About Monkey 2 › Forums › Monkey 2 Programming Help › Change color in a sprite
This topic contains 17 replies, has 6 voices, and was last updated by
ArtemKuchin 1 year, 7 months ago.
-
AuthorPosts
-
September 7, 2017 at 9:36 pm #10322
hello!
I am very new to Monkey. I learned the languages Prtty easy (i already have C,ASM,C++,C#,perl,php etc… in my work, so, this is nothing new), but i am not good with graphics. Mostly done data processing all my life.
So, i need to have several sprites, mostluy monochrome, but some can have several colors. And before drawng them i need to change a certain color to other one in a sprite. I have no clue where to start for this task. Everything is strictly 2d.
For exaple, i need to draw 50 monochrome sprites. I load them from file Then i need to change thge color of a sprte to some other color. For example, there are white by default and i change to red. Then draw 50 red sprites. The colro is actually user selectable, if it matters.
September 7, 2017 at 10:53 pm #10325Just draw the sprites as normal, and when you want to switch colour you do canvas.color=Color.Red or whatever and draw all the red sprites.
You can also do canvas.color=New Color( r:float, g:float, b:float) to set your own custom colour.I don’t have Monkey 2 installed right now, so the syntax is bound to be incorrect, but the general idea will work.
September 8, 2017 at 1:57 am #10329Also there is an Image.Color property.
September 8, 2017 at 5:22 am #10333Thank you
I tried mojotest app from bananas
did this:
image=Image.Load( “asset::RedbrushAlpha.png” ) image=Image.Load( “asset::RedbrushAlpha.png” ) image.Color=Color.Grey
The result is strange. Seems .color does not replacem pixel colors, but mixes with pixels in image.
See attached file.
Also, this way i do not have any way to choose what color to replace. But it is secondary task.
Attachments:
September 8, 2017 at 5:57 am #10335Yes, colors mixes. To get clean custom color your source image should be white.
September 8, 2017 at 6:02 am #10336Aha, found this in image class description
-
<li class=”mx2docs”>Color – when rendering an image to a canvas, this property is multiplied by the current canvas color and the result is multiplied by actual image pixel colors to achieve the final color to be rendered.
So, color does not do it.
I guess i need a way to directly manipulate pixels in image or on canvas in ARGB fomat.
I don’t see anyt ReadPixels/WritePixels in mojo2 for images and buffers.
Is it possible?
September 8, 2017 at 8:06 am #10339For exaple, i need to draw 50 monochrome sprites. I load them from file Then i need to change thge color of a sprte to some other color. For example, there are white by default and i change to red. Then draw 50 red sprites. The colro is actually user selectable, if it matters.
If you start with white images you can use the .Color stuff because white multiplied by a color is the color…
I made a small sample (attached zip) that shows that the white dot is always getting the correct color.I don’t see anyt ReadPixels/WritePixels in mojo2 for images and buffers.
There’s something with Image.Texture and Pixmaps.GetPixel but I don’t remember and the link to Texture in the docs is broken.. Can’t find the example I made at the time.
I made a small sample (attached zip) that shows that the white dot is always getting the correct color.
EDIT: Texture is hidden from docs.. At the time I did some modifications to play around with monkey2 itself so I don’t really know how you can “extract” the pixmap of an Image. Maybe create a new Canvas with the Image then myCanvas.CopyPixmap then myPixmap.Getpixel. But this is so complicated. Image.GetPixel would be nicer..
Attachments:
September 8, 2017 at 9:20 am #10341here’s an Image Extension that will do Image.GetPixel (very slow!). There’s also an Image.GetPixmap (faster to scan the whole image (untested)) but i’m not quite sure how the resource will be managed..
I’m not sure it’s the right/good way to do it though! It’s so complex for such a simple thing…
[/crayon]Monkey123456789101112131415161718192021222324252627282930313233343536[crayon-5cba85cab8465885813351 inline="true" ]Class Image ExtensionMethod GetPixel:Color(x:Int,y:Int)Local myCustomImage:=New Image (Self.Width,Self.Height)Local myCanvas:=New Canvas(myCustomImage)myCanvas.Clear(Color.None)myCanvas.DrawImage(Self,0,0)myCanvas.Flush()Local myRecti:=New Recti(0,0,Self.Width-1,Self.Height-1)Local myPixmap:=myCanvas.CopyPixmap(myRecti)Local c:=myPixmap.GetPixel(x,y)myPixmap.Discard()myCustomImage.Discard()Return cEndMethod GetPixmap:Pixmap()Local myCustomImage:=New Image (Self.Width,Self.Height)Local myCanvas:=New Canvas(myCustomImage)myCanvas.Clear(Color.None)myCanvas.DrawImage(Self,0,0)myCanvas.Flush()Local myRecti:=New Recti(0,0,Self.Width-1,Self.Height-1)Local myPixmap:=myCanvas.CopyPixmap(myRecti)myCustomImage.Discard()Return myPixmapEndEndSeptember 8, 2017 at 10:23 am #10343You could always keep a copy of the image in a pixmap, then you can alter the pixmap with pixmap.SetPixel() when you need to and use New Image(pixmap) to transfer the altered pixmap to an image for rendering
Monkey123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081Namespace myapp#Import "<std>"#Import "<mojo>"Using std..Using mojo..Class MyWindow Extends WindowField pixmap:Pixmap = New Pixmap(64,64)Field image:ImageField currentColor:Color = Color.WhiteMethod New( title:String="Simple mojo app",width:Int=640,height:Int=480,flags:WindowFlags=Null )Super.New( title,width,height,flags )'let's fill the pixmap with a red circleFor Local x:Int = -32 To 31For Local y:Int = -32 To 31If Sqrt(x*x+y*y) < 32pixmap.SetPixel(x+32,y+32,Color.Red)Elsepixmap.SetPixel(x+32,y+32,Color.Black)End IfNextNext'Now insert white squareFor Local x:Int = 24 To 40For Local y:Int = 24 To 40pixmap.SetPixel(x,y,Color.White)NextNext'now load the pixmap into an imageimage = New Image(pixmap)EndMethod OnRender( canvas:Canvas ) OverrideApp.RequestRender()'if space key is pressed, change the color to the next oneIf Keyboard.KeyPressed(Key.Space)'get the next colorSelect currentColor.ToARGB()Case Color.White.ToARGB()currentColor = Color.BlueCase Color.Blue.ToARGB()currentColor = Color.GreenCase Color.Green.ToARGB()currentColor = Color.YellowCase Color.Yellow.ToARGB()currentColor = Color.WhiteEnd Select'draw a square in the pixmapFor Local x:Int = 24 To 40For Local y:Int = 24 To 40pixmap.SetPixel(x,y,currentColor)NextNext'transfer the pixmap to imageimage = New Image(pixmap)End If'draw the imagecanvas.DrawImage(image,100,100)EndEndFunction Main()New AppInstanceNew MyWindowApp.Run()EndSeptember 8, 2017 at 11:00 am #10345Or you can write own fragment shader to process images.
Little more info: http://monkeycoder.co.nz/forums/topic/image-with-texture-mask/
My idea is
- pass R1 G1 B1 / R2 G2 B2 (to get by-component control) integer parameters into shader using Image.Material.SetInt( “R1”,200 ) method,
- inside of shader get color components, translate them into integers (color has ‘float vec4’ type in shader) and compare with your R1 G1 B1
- if there is a match – replace color with R2 G2 B2, converted back to float
September 8, 2017 at 11:51 am #10347You could always keep a copy of the image in a pixmap
To me the problem is more how to get the pixmap out of a loaded .png image. Once you have the pixmap you can use Pixmap.Getpixel() or GetARGBpixel().
Nerobot’s solution is of course top performance but needs more work/knowledge.
September 8, 2017 at 7:35 pm #10351aha, so i neex pixelmap, not image
pixelmap has load method which load from png and save which saves to png
that’s very good, i will play with it
September 8, 2017 at 9:45 pm #10353pixelmap has load method
how can I have missed that!
September 8, 2017 at 10:56 pm #10354Okay, i have done it. See code below. HOWEVER one thing i don’t understand. if ALPHA channel is ZERO i can still the color. To fully hide pixel i need to set ALPHA to zero and RGB to zero also. This i don’t understand.
Monkey1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374Namespace pmtest#Import "<std>"#Import "<mojo>"#Import "assets/"Using std..Using mojo..Class MyWindow Extends WindowField pixmap:std.graphics.PixmapField image:ImageField k:IntField colors:UInt[]Field currentColor:Color = Color.WhiteMethod New( title:String="Simple mojo app",width:Int=640,height:Int=480,flags:WindowFlags=Null )Super.New( title,width,height,flags )colors=New UInt[5]colors[0]=$FF0000FFcolors[1]=$FF00FF00colors[2]=$FFFF0000colors[3]=$FF00FFFFcolors[4]=$FFFFFF00pixmap= std.graphics.Pixmap.Load("asset::test.png",std.graphics.PixelFormat.RGBA32,True)Local p:UInt Ptrp=Cast<UInt Ptr>(pixmap.Data)For Local i:Int=0 To 100*100' ABGRIf p[i]=$FF000000 Thenp[i]=colors[0]EndifNextimage = New Image(pixmap)EndMethod OnRender( canvas:Canvas ) OverrideApp.RequestRender()If Keyboard.KeyPressed(Key.Space)k=k+1If k>4 Then k=0Local p:UInt Ptrp=Cast<UInt Ptr>(pixmap.Data)For Local i:Int=0 To 100*100' if anythung non transparentIf p[i] & $FF000000 Thenp[i]=colors[k]EndifNextimage = New Image(pixmap)End Ifcanvas.Clear(Color.Grey)canvas.Color=Color.Bluecanvas.DrawOval(100,100,100,50)canvas.Color=Color.Whitecanvas.DrawImage(image,100,100)EndEndFunction Main()New AppInstanceNew MyWindowApp.Run()EndAttachments:
September 9, 2017 at 1:58 am #10356if ALPHA channel is ZERO i can still the color. To fully hide pixel i need to set ALPHA to zero and RGB to zero also.
This is because image rendering uses pre-multiplied alpha. This is a technique where image colors are assumed to be pre-multiplied by image alpha – more information here (and much more via goggle):
You can premultiply pixmap data like this by setting the pmAlpha parameter of Pixmap.Load to true when loading a pixmap, or using the Pixmap.PremultiplyAlpha method.
Note that Image.Load is really just a ‘wrapper’ around something like this:
Monkey1234Local pixmap:=Pixmap.Load( path,Null,True )Local image:=New Image( pixmap )Return imageIf you want to modify image pixels when loading, the best way to do it is to use Pixmap.Load as above, modify the pixmap, then use New Image to create anew image with the modified pixmap.
Image are not really intended to be ‘read’ by the CPU. In fact, the more HW evolves, the harder this gets – in Open GL ES there aren’t even any commands for reading texture data, so you need to render and then copy from the backbuffer! The has the potential to lose lots of precision along the way, as textures/backbuffers masy be in a crappy format behind the scenes on some taregets and texture data may be compressed etc etc.
The only way to guarantee ‘clean’ pixele data is by using Pixmap.Load and using the pixel data *before* it reaches the image/texture. Once things reach the GPU, they should ideally only be read by the GPU – which includes shaders, which can easily access texture data (although again it may not be exactly the same as the original pixmap data on some targets).
-
AuthorPosts
You must be logged in to reply to this topic.

