Tiling a timage to another Timage.
BlitzMax Forums/BlitzMax Beginners Area/Tiling a timage to another Timage.
| ||
Recently I've started work on a project that deals with timages. I was wondering if anyone could provide a function. I'm looking to tile one timage across another Timage. Lets call them image1 and image2. Image2 must be at least the size of image1. If it is not image2 must be resized to the size of image1. Now image1 has 17 subimages in the animation. I hope I'm making sense. Image1 is a dither pattern that is 4x4 pixels with 17 different fill patterns and image2 is a base picture. Basically I want to iterate through each 4x4 pixel section of the base image and replace it with one of the 17 dither patterns measuring 4x4 pixels. The problem comes in the dither pattern going "off the edge" of the base picture. The pattern chosen is determined by the average of all pixels in the 4x4 square. The grayscale version of each pixel in the 4x4 square is simply (red+green+blue)/3. This value is set for the red,green and blue. The 16 values then are averaged and divided by 255 to get the brush image pattern for the 4x4 square. Here is the dither pattern it goes from pure black 0 to pure white 16. This is the 17 image dither brush pattern. The function should not modify the original image but rather return a new timage that is the original image with the 4x4 dithering applied. I cannot work from the back buffer for this project. Image data must be done with pixel pointers. I'm really sorry if you are lost. I tried to help as I asked for help. Please try if you think that you can help me with this. Take a base picture that is atleast 4x4 pixels. Divide that up into 4x4 pixel blocks. Convert those blocks to grayscale by averaging their RGB components then setting all RGB values to that averaged color. Choose the proper subimage of the gradient pattern. Replace that block with the gradient pattern subimage. |
| ||
1. There is no pixel pointer + TImage -> you must use lockimage which returns a TPixmap and use that 2. Rest is either memory arithmetic, readpixel / writepixel or TPixmap.paste and do exactly what you say "work block by block". The rest shouldn't be hard. if you have distinct keys for the patterns you could store them in a TMap and simply lookup the needed one when replacing. |
| ||
Ok I've got some code working. It returns a dithered image in about 30-40 milliseconds. My main problem is adjusting for images that are not even divisable by 4. Function round:Float(n:Float) Local r:Float = n Mod 2.0 If r >=.5 Return Ceil(n) Else Return Floor(n) EndIf End Function Function ditherfilter:timage(source:TImage, brush:TImage) Local sourcePixmap:TPixmap = LockImage(source) Local sourcePixmapPtr:Byte Ptr = PixmapPixelPtr(sourcePixmap, 0, 0) Local tmpPixmap:TPixmap = CopyPixmap(sourcePixmap) Local tmpPixmapPtr:Byte Ptr = PixmapPixelPtr(tmpPixmap,0,0) Local x:Int,y:Int Local w:Int = tmpPixmap.width Local h:Int = tmpPixmap.height Local extra_width:Float = w - (Floor(w / 4) * 4) Local extra_height:Float = h - (Floor(h / 4) * 4) For x = 0 To w - 1 Step 4' this is a 4 pixel jump thgouth the image For y = 0 To h - 1 Step 4 ' this is a 4 pixel jump thgouth the image ' set the component totals to be averaged upon completion Local total_red:Float = 0 Local total_green:Float = 0 Local total_blue:Float = 0 Local pixels_scanned:Float = 0 Local gray:Float = 0 Local subimage:Float = 0 'average the color of each 4x4 square For Local x2:Int = 0 To 3 For Local y2:Int = 0 To 3 ' get the color of each pixel of 16 Local red:Float = sourcePixmapPtr[(x + x2) * 4 + (y + y2) * 4 * w] Local green:Float = sourcePixmapPtr[(x + x2) * 4 + (y + y2) * 4 * w + 1] Local blue:Float = sourcePixmapPtr[(x + x2) * 4 + (y + y2) * 4 * w + 2] total_red:+red total_green:+green total_blue:+blue pixels_scanned:+1 Next Next 'draw the 4x4 fill total_red:/pixels_scanned total_green:/pixels_scanned total_blue:/pixels_scanned gray = (total_red + total_green + total_blue) / 3 For Local x2:Int = 0 To 3 For Local y2:Int = 0 To 3 subimage = round(gray / 255 * 16) Local brush:TPixmap = LockImage(brush, subimage, 0, 0) Local brushPixmapPtr:Byte Ptr = PixmapPixelPtr(brush, 0, 0) Local w2:Int = brush.width tmpPixmapPtr[(x + x2) * 4 + (y + y2) * 4 * w] = brushPixmapPTr[x2 * 4 + y2 * 4 * w2] tmpPixmapPtr[(x + x2) * 4 + (y + y2) * 4 * w + 1] = brushPixmapPTr[x2 * 4 + y2 * 4 * w2 + 1] tmpPixmapPtr[(x + x2) * 4 + (y + y2) * 4 * w + 2] = brushPixmapPTr[x2 * 4 + y2 * 4 * w2 + 2] 'alpha Next Next Next Next Local tmpImage:TImage = LoadImage(tmpPixmap) Return tmpImage End Function Could sombody patch up my work please? I would like the dither pattern to extend over the edges and keep the original size of the image. |
| ||
To get around images not divisable by 4, you need to check to make sure, that the pixel are not outside the pixmap.Function round:Float(n:Float) Local r:Float = n Mod 2.0 If r >=.5 Return Ceil(n) Else Return Floor(n) EndIf End Function Function ditherfilter:timage(source:TImage, brush:TImage) Local sourcePixmap:TPixmap = LockImage(source) Local sourcePixmapPtr:Byte Ptr = PixmapPixelPtr(sourcePixmap, 0, 0) Local tmpPixmap:TPixmap = CopyPixmap(sourcePixmap) Local tmpPixmapPtr:Byte Ptr = PixmapPixelPtr(tmpPixmap,0,0) Local x:Int,y:Int Local w:Int = tmpPixmap.width Local h:Int = tmpPixmap.height Local extra_width:Float = w - (Floor(w / 4) * 4) Local extra_height:Float = h - (Floor(h / 4) * 4) For x = 0 To w - 1 Step 4' this is a 4 pixel jump thgouth the image For y = 0 To h - 1 Step 4 ' this is a 4 pixel jump thgouth the image ' set the component totals to be averaged upon completion Local total_red:Float = 0 Local total_green:Float = 0 Local total_blue:Float = 0 Local pixels_scanned:Float = 0 Local gray:Float = 0 Local subimage:Float = 0 'average the color of each 4x4 square For Local x2:Int = 0 To 3 If x+x2 < w Then For Local y2:Int = 0 To 3 If y+y2 < h Then ' get the color of each pixel of 16 Local red:Float = sourcePixmapPtr[(x + x2) * 4 + (y + y2) * 4 * w] Local green:Float = sourcePixmapPtr[(x + x2) * 4 + (y + y2) * 4 * w + 1] Local blue:Float = sourcePixmapPtr[(x + x2) * 4 + (y + y2) * 4 * w + 2] total_red:+red total_green:+green total_blue:+blue pixels_scanned:+1 End If Next End If Next 'draw the 4x4 fill total_red:/pixels_scanned total_green:/pixels_scanned total_blue:/pixels_scanned gray = (total_red + total_green + total_blue) / 3 For Local x2:Int = 0 To 3 If x+x2 < w Then For Local y2:Int = 0 To 3 If y+y2 < h Then subimage = round(gray / 255 * 16) Local brush:TPixmap = LockImage(brush, subimage, 0, 0) Local brushPixmapPtr:Byte Ptr = PixmapPixelPtr(brush, 0, 0) Local w2:Int = brush.width tmpPixmapPtr[(x + x2) * 4 + (y + y2) * 4 * w] = brushPixmapPTr[x2 * 4 + y2 * 4 * w2] tmpPixmapPtr[(x + x2) * 4 + (y + y2) * 4 * w + 1] = brushPixmapPTr[x2 * 4 + y2 * 4 * w2 + 1] tmpPixmapPtr[(x + x2) * 4 + (y + y2) * 4 * w + 2] = brushPixmapPTr[x2 * 4 + y2 * 4 * w2 + 2] 'alpha End If Next End If Next Next Next Local tmpImage:TImage = LoadImage(tmpPixmap) Return tmpImage End Function |
| ||
the case that an image is not divideable by 4 does not exist for images > 2x2 images must always be power of 2 on hardware and you sure have >= 3x3 pixels :) you can load other images but internally they get expanded to next power of two and then UV modified to only show "your area" so for memory and performance efficiency make the images power of two directly |
| ||
Thanks for everything. |