Faster Pixmap

Started by Ryszard, August 12, 2023, 08:13:07

Previous topic - Next topic

Ryszard

Dzień dobry

Mój przyjaciel poprosił mnie o opublikowanie tego tekstu. Dał blitzforum.de ale nie ma nawet czytelników.
Jestem początkującym użytkownikiem Blitmax. Mam wiele rysunków w wysokiej rozdzielczości. Muszę usunąć z nich niektóre elementy. Wszystko działa dobrze dla mnie, ale bardzo powoli. Chcę przyspieszyć program. Usuwam elementy z Pixmap za pomocą polecenia WritePixel. Jak mogę to zrobić, aby zamiast 1 piksela można było użyć znaczka 10x10 pikseli lub nawet większego. Najprostszy sposób na włączenie wskaźników. Do zapisu bezpośrednio do znaczków pamięci 10x10 lub 20x20 pikseli.

Dzięki za wskazówki

Pozdrowienia Simon

Midimaster

google auto-translation:
QuoteMy friend asked me to publish this text. He gave blitzforum.de but he doesn't even have readers.
I am a beginner Blitmax user. I have many high resolution drawings. I need to remove some elements from them. Everything works fine for me, but very slowly. I want to speed up the program. I'm removing elements from a Pixmap using the WritePixel command. How can I make it so that instead of 1 pixel I can use a stamp of 10x10 pixels or even larger. The easiest way to enable indicators. For writing directly to 10x10 or 20x20 pixel memory stamps.
Thanks for the tips
Regards Simon
...back from North Pole.

Midimaster

I have done this some years ago for my game "Ballerburg". Speed can be improved!

There are several ways to do this. But they are all related on the kind of area you want to clear.


A Rectangle Area

You can create a second "inside" Pixmap with PixMapWindow(), which defines a rectangle area of the original Pixmap, then use ClearPixels() on this area:


A Non rectangle area

At the moment you use WritePixel() to do the job. When you follow the command WritePixel() in the BlitzMax sources you will find this function:

Type TPixMap
    ....
    Method WritePixel(x:Int, y:Int, col:SColor8)
        Assert x>=0 And x<width And y>=0 And y<height Else "Pixmap coordinates out of bounds"
        Local p:Byte Ptr=PixelPtr(x,y)
        Select format
        Case PF_A8
            p[0]=col.a
        Case PF_I8
            p[0]=(col.r + col.g + col.b)/3
        Case PF_RGB888
            p[0]=col.r ; p[1]=col.g ; p[2]=col.b
        Case PF_BGR888
            p[0]=col.b ; p[1]=col.g ; p[2]=col.r
        Case PF_RGBA8888
            p[0]=col.r ; p[1]=col.g ; p[2]=col.b; p[3]=col.a
        Case PF_BGRA8888
            p[0]=col.b ; p[1]=col.g ; p[2]=col.r ; p[3]=col.a
        End Select   
    End Method

As you see it has a lot of lines to set the colors and single byte operations.

But in your case you only need a ClearPixel() command,that does not care about colors and PixMap-format. So you could write your very short own function:

Type TPixMap
    ....

    Method ClearPixel(x:Int, y:Int)
        'Assert x>=0 And x<width And y>=0 And y<height Else "Pixmap coordinates out of bounds"
        Local p:Int Ptr = PixelPtr(x,y)
        p[0]=0
    End Method

This works if you only use 32bit ARGB-TPixmap ( which is mostly the common). And you can save additional time when you remove the Image-boundary-check (Assert....)



A Combination of both


If the area you want to destroy is non rectangle but massiv (no wholes), you could clear the area line by line.
1. Scan each line of the area. For i%=y to y+n
2. Define the left starting pixel
3. Define the right (last) pixel
4. Now create a 1 pixel high PixMapWindow() with parameters left,i,(right-left),1
5. Use ClearPixels() for each line window
...back from North Pole.

Ryszard

Good morning

Thank you very much for your reply. As always reliable and brilliant Midimaster. Technical drawings are duplicated - copy from copy from copy from copy. Any copying adds garbage. Even the first copy is usually of mediocre quality (worn scanner, untrained staff).  I longed to remove this garbage. I was surprised how such simple measures turned out to be so effective. Unfortunately, it takes far too long. Large drawings, many drawings. The program searches the drawing for frames with 10x10, 20x20 single-pixel walls. If the frame walls in the drawing have only a background then I remove the rectangle or square bounded by these frames. The drawings are binary. Everything works great only the speed is unsatisfactory. I am thinking to use pointers for stamping. It would be faster for sure.

Midimaster

The original WritePixel() already< uses pointer operations. But it need 4 operations to clear one pixel. My idea Method ClearPixel() of using INT PTR needs only 1 operation and already rises the speed with factor x4.

More speed is only possible if you minimize the security checks (image borders and format check). This would work if you check the security only once for a complete block, then clear it without additional checks with ClearPixel(). Again a speed factor x2 is possible.

A little more speed improvement is possible if you are sure, that you will clear 2 neighbor pixels. Them you can use a LONG PTR operation, which will clear 8 Bytes in one operation. Could be +50% speed acceleration... perhaps.


Didn't we have this discussion some years ago? https://www.syntaxbomb.com/blitzmax-blitzmax-ng/pointers-pixmapa/
...back from North Pole.

Midimaster

With the new BlitzMax NG you could use more THREADS. That means more than one CPU core can work at the same image. Divide the image in 4 parts and let each CPU CORE work a quarter. At the end you mount them together.

You write, that your process is slow... How slow? Are we talking about some seconds or minutes?
 
...back from North Pole.

Ryszard

Tak rozmawialiśmy. Jeszcze raz dziękuję za pomoc. Niektóre rzeczy się przydały.
Najpierw usuwam pojedyncze piksele, czarne kwadraty 2x2, 2x3, 3x2, 3x3. Jak wiadomo, na rysunkach technicznych jest więcej pikseli białego tła niż pikseli czarnych rysunków. Przesuwam biały znaczek o 10x10 pikseli. I sprawdzam, czy wokół znaczka jest biała ramka.
Jeden czarny w kadrze, przechodzę do następnego piksela w stemplu. Jeśli w kadrze nie ma czerni, odbijam biały znaczek 10x10. I przechodzę do następnego piksela 10 w prawo. I jeden piksel na raz do końca rzędu po prawej stronie i do następnego rzędu. Sprawdzam, czy choć jeden stempel został odbity. Jeśli tak, powtarzam procedurę. Później stempluję 20x20. Teraz ,,rozgryzę" kod podany powyżej. W pracy nie mam nic wspólnego z programowaniem. Jest to dla mnie po prostu przyjemna czynność. A ja nie mam czasu na dogłębne zajmowanie się Blitzmaxem. Literatury też nie ma. A sposób, w jaki to wyjaśniono, nie jest dla laika. Jeśli czegoś potrzebuję, staram się spróbować. Z pomocą kolegów z forum udaje mi się. Dzięki

Ryszard

Kilka minut. To jest stan surowy bez żadnej optymalizacji. Mam w sumie dwa komputery po 6 rdzeni każdy - 12 wątków dałoby się okiełznać oba. Są one połączone przez serwer. Jest trzeci, ale bez sieci. Ale myślę, że po optymalizacji jedna wystarczy. Jak inaczej można wykorzystać więcej rdzeni. Nikt mnie nie goni, nie wymaga ode mnie. Robię to dla siebie.

Derron

#8
Please write in English here ... it should not be the job of a reader to translate your language (looks like Czech) into English.


Regarding Stamping and Speed:
As Midimaster indirectly pointed out, a "window" into the pixmap speeds up clearing already. Parsing eg a "circle" into such windows (scan line approach) means creating a lot of "TPixmap" objects ... which should be avoided (as it is slower than not doing so).

Why are these cleared windows faster than single writePixels? Because they clear contiguous blocks of memory.
Pixmaps are stored in memory like a 2D-Array. And thus means we have "horizontal" lines data next to each other. y0 (0 to width), y1 (0 to width), ...
This leads to the deletion of a whole horizontal line (eg the top or bottom line) to be way way faster than deleting a vertical line (as this means a delete-command for each single affected y position)

This informative part aside:
Code (Blitzmax) Select
Method ClearPixels( argb )
If Not argb And width*BytesPerPixel[format]=pitch
MemClear pixels,Size_T(pitch*height)
Return
EndIf
For Local y=0 Until height
Local p:Byte Ptr=PixelPtr(0,y)
If Not argb
MemClear p,Size_T(width*BytesPerPixel[format])
Continue
EndIf
....

This MemClear accesses "BytesPerPixel[]" for each y (width * bytesperpixel could be precalcultated for a potential very slight speedup). It also checks is the for loop is needed at all ..
Simply doing your own "ClearPixelsRect(argb, x,y,w,h)" will already be faster (you might even consider only using PixelPtr() once and then increasing the pointer by "y*pitch" ... but I doubt you save overly much time there.

For now this only works with setting the pixel data to "0" (black + full transparency, so ARGB 0,0,0,0). I will raise an issue to add a kind of exposed "MemSet()" which would allow to set a specific value.
Internally MemClear already uses MemSet - with the value 0 :)

Code (C) Select
void bbMemClear( void *dst,size_t size ){
memset( dst,0,size );
}

But it would only allow to set "bytes" which is not much of use for ARGB (4 bytes) except very specific combinations :)

bye
Ron

Ryszard

Good morning

I apologize for the lack of English in the post. I had automatic translation enabled and didn't even notice that the text was in Polish. Thank you for the expert help on the questions I asked on the forum. I'm not likely to have problems laying out the algorithm for the task I want to solve with Blitzmax. The problem is precisely how to translate the algorithm into understandable for Blitzmax. With this I have a problem, but reading the forum step by step I come to the final.