ReadPixel WritePixel speed

Started by Hardcoal, February 28, 2020, 15:22:10

Previous topic - Next topic

Hardcoal

Ill simply post the code
But again it's in xors
Code

Derron

Thought you were using TPixmap (Max2D) ?

local img:TPixmap = LoadPixmap("your.png")
local otherImg:TPixmap = CreatePixmap(tileW, tileH, img.format)
otherImg.WritePixel(x,y, img.ReadPixel(x - offsetX, y - offsetY))


bye
Ron

Hardcoal

ok here is part of my code.

Derron Hi.. I dont need it to be blazing fast just normally fast :)

i didnt read all replies well im on a hurry ill go through them later..

mean while here is part of my code..

also a new link to my demo https://drive.google.com/file/d/1kugyDoI-uF_XwkHDkiu_lE2yWnJkOvzU/view?usp=sharing

Method ReadPNG(ImageHandle, CurrentX = 0, CurrentY = 0, Pixel[,] Var)
Local X, Y, StartX, StartY, NewPixel[ximagewidth(imagehandle), ximageheight(imagehandle)]
Local LeftX, RightX, UpY, DownY

   'Start
Local Coords:Coords_Class = New Coords_Class
Coords = GetFirstEmptyPoint(ImageHandle, Pixel)
If Coords.X = -1 Then Return 'End Of Trace
StartX = Coords.X
StartY = Coords.Y
LeftX = StartX
RightX = StartX
UpY = StartY
DownY = StartY
    NewPixel[StartX, StartY] = OccupiedPix 'A Must
    Pixel[StartX, StartY] = OccupiedPix    'Not Certain
Coords = Null

   'Left To Right
For Y = StartY To xImageHeight(ImageHandle) - 1

For X = 0 To xImageWidth(ImageHandle) - 1

   'Is UnChecked
If Pixel[X, Y] = UnChecked Then
If HasAnOccupiedPointAroundHim(ImageHandle, X, Y, NewPixel) = True Then
If X < LeftX Then LeftX = X
If X > RightX Then RightX = X
If Y < UpY Then UpY = Y
If Y > DownY Then DownY = Y
Pixel[X, Y] = OccupiedPix
NewPixel[X, Y] = OccupiedPix
End If
End If

   'ForceStop
If ForceStopFlg = True Then Return

TimedUpdateGraphics()

Next

Next

   'Right To Left And Down To Up
For Y = xImageHeight(ImageHandle) - 1 To 0 Step - 1

For X = xImageWidth(ImageHandle) - 1 To 0 Step - 1

   'Is Empty
If Pixel[X, Y] = UnChecked Then
If HasAnOccupiedPointAroundHim(ImageHandle, X, Y, NewPixel) = True Then
If X < LeftX Then LeftX = X
If X > RightX Then RightX = X
If Y < UpY Then UpY = Y
If Y > DownY Then DownY = Y
Pixel[X, Y] = OccupiedPix
NewPixel[X, Y] = OccupiedPix
End If
End If

   'ForceStop
If ForceStopFlg = True Then Return
TimedUpdateGraphics()

Next

Next

   'Up To Down  [All Seems To Work]
For X = 0 To xImageWidth(ImageHandle) - 1

For Y = 0 To xImageHeight(ImageHandle) - 1

   'Is Empty
If Pixel[X, Y] = UnChecked Then
If HasAnOccupiedPointAroundHim(ImageHandle, X, Y, NewPixel) = True Then
If X < LeftX Then LeftX = X
If X > RightX Then RightX = X
If Y < UpY Then UpY = Y
If Y > DownY Then DownY = Y
Pixel[X, Y] = OccupiedPix
NewPixel[X, Y] = OccupiedPix
End If
End If

   'ForceStop
If ForceStopFlg = True Then Return
TimedUpdateGraphics()

Next

Next

   'Down To Up
For X = 0 To xImageWidth(ImageHandle) - 1

For Y = xImageHeight(ImageHandle) - 1 To 0 Step - 1

   'Is Empty
If Pixel[X, Y] = UnChecked Then
If HasAnOccupiedPointAroundHim(ImageHandle, X, Y, NewPixel) = True Then
If X < LeftX Then LeftX = X
If X > RightX Then RightX = X
If Y < UpY Then UpY = Y
If Y > DownY Then DownY = Y
Pixel[X, Y] = OccupiedPix
NewPixel[X, Y] = OccupiedPix
End If
End If

   'ForceStop
If ForceStopFlg = True Then Return
TimedUpdateGraphics()

Next

Next

   'Write The Image
Local NewImage = Xcreateimage (RightX - LeftX, DownY - UpY)
For Y = UpY To DownY
For X = LeftX To RightX

If NewPixel[X, Y] = OccupiedPix Then
  xWritePixel(X - LeftX, Y - UpY, xReadPixel(X, Y, xImageBuffer(ImageHandle)), xImageBuffer(NewImage))
  End If

   'ForceStop
If ForceStopFlg = True Then Return
TimedUpdateGraphics()

Next
Next

Return NewImage
End Method
Code

Derron

#18
So ... you want to split tiles from a tilemap. What happens if you have "islands" in the tile?.
Eg. There is a "sky tile" which has some separate clouds.

How to handle this?

You can only automate this if then the tiles are in a "grid" layout - so you define tile width and other stuff (like grid spacing). If you have dynamically sized tiles then you cannot have "islands" (or they will end up becoming separate sprites).

Edit: also important - certain image editors (GIMP!) do not set RGB information when deleting stuff - they just set the alpha value to 0. So when exporting your tileset and then compare against a color like "0" (rgba(0,0,0,0)) this will ignore these "only made transparent" pixels. So better compare for the alpha value.


Edit2: http://www.spritecow.com/ (js sources available) might be interesting to check out. So the interesting part is this: https://github.com/jakearchibald/sprite-cow/blob/master/www/assets/script/SpriteCanvas.js

If your tilemaps are always in a grid layout it is just a matter of cutting out the tiles and then removing all rows/cols with all pixels' alpha values being 0 ("auto cropping").


Edit3: https://github.com/bmarwane/spriteSplitter/blob/master/app/js/loader.js this does it a bit more "verbose".


bye
Ron



Hardcoal

My program does exactly what I desired it to do..
In some cases it may not be suitable for some people, but in 99% of the times It will be fine..
Code

Derron

I still do not know what it should do - compared to what it actually does (there is a missing function in your sample too).

This is why I asked for an abstract of what it should do - not an implementation of what you do.


bye
Ron

Hardcoal

#21
Sorry Derron.. I was so busy today in so many things.. i didnt have time to really look through the posts.. into details..

Ive added a Tileset in the folder of the Program like this one..
Press LoadTileSet Button and choose this png.. than press ConverToPNGS
After a while it will turn from a single Image to a multiple PNG Images.
It was made for my Editor use at first..
So I wont need to use photo shop to seperate tiles and save them one by one.

Yea its a work in progress..
so I dont blame you if youre confused..

Anyway.. its all going fine for me besides the speed thing..
but ill fix it later.. for now all is ok..
I prefer to deal with progressing this , instead of messing with the speed issue atm.

im gonna open a new thread soon . only for this little software.. and add the latest Demo

Thanks Derron for your Care

Code

Matty

That looks nice Hardcoal.

However, if I understand you want a program to extract the individual square tiles from that tileset, or any tileset.

Perhaps a faster way would be the following algorithm.

1. Get width and height of image.
2. Find first non transparent pixel in a column.
3. Find first non transparent pixel in a row.
4. Find shortest distance in rows and columns to next transparent pixel.
5. Combining knowledge from points 2 to 4 you have the size of each tile plus its initial offset.
6. Grab rectangles of size defined in 5 and spit them out.
7. If there is transparency issues with resulting image then store transparency in greyscale image and recombine later.

Ignore if I'm completely off base.

Hardcoal

Matty its all working fine.. I just tried to make it faster lol thats all..
Ill put the latest version of my App Soon.
Code

Matty

Exactly, hence I suggested a faster algorithm than reading and writing every individual pixel.

Derron

What happens if you replace that xors commands with some pure tpixmap commands? Maybe this is the speed culprit?


Bye
Ron

TomToad

Quote from: Hardcoal on March 02, 2020, 22:07:47

Ive added a Tileset in the folder of the Program like this one..
Press LoadTileSet Button and choose this png.. than press ConverToPNGS
After a while it will turn from a single Image to a multiple PNG Images.

Challenge Accepted, and accomplished https://www.syntaxbomb.com/index.php/topic,7474.msg347040328.html#msg347040328 :)

Uses Pixmaps and directly accesses the pixels instead of using Read/WritePixel for faster access.  You can save the resulting tiles with SavePixmapPNG command if you want.
------------------------------------------------
8 rabbits equals 1 rabbyte.

Derron

#27
@TomToad
Nice job - I changed the "show" portion to quickly draw all sprites ... which exposes a flaw in the dark green tile at the bottom right (above the "plus") of the original (see following picture):


result of your algo:


This is what I by the way was talking about ("island detection")


You _could_ somehow circumvent it by increasing the "lookup" range for blank lines ("end of tile"). So if you knew that all tiles are at least X pixels away from each other (padding?) then you could use this to keep these tiles intact. But I do not see a way to "automate" this in all situations. So eg. your algorithm could check distance betwen tiles - and after this quick scan uses the most used distance as "minimum distance" between tiles.

bye
Ron

TomToad

Quote from: Derron on March 03, 2020, 12:59:21
@TomToad
Nice job - I changed the "show" portion to quickly draw all sprites ... which exposes a flaw in the dark green tile at the bottom right (above the "plus") of the original (see following picture):

Are you refering to the four corner square part?  My version requires that all pixels be connected continuously.  There are 0 alpha pixels between each corner, so each one is extracted into its own tile.  I guess you could create a "reach" variable, defined how close a pixel needs to be to be part of the same tile, and search through a grid of pixels instead of just left/right/up/down.
------------------------------------------------
8 rabbits equals 1 rabbyte.

Derron

#29
I did not want to say your code is not working - it just seems to not be what Hardcoal is trying to achieve (at least not 100%).

So yes, I was talking about it - and also knew in advance why it fails and that your code does not try to cope with it.


This aside your code is very helpful and I am tempted to add it to my Dig framework (once we matured/optimized it even more :D - maybe one could add borders to sprites and the extractor then just skips the border)

bye
Ron