transparent png to polygon converter - solved

Monkey Forums/Monkey Programming/transparent png to polygon converter - solved

GC-Martijn(Posted 2015) [#1]
edit: solved and created a module here:
http://www.monkey-x.com/Community/posts.php?topic=9748

H!

I'm now using a simple RectColl() detection but for several objects in my game I need a other Coll detection.

For example, take this road sign picture:

The mouse over is only allowed when the cursor is on the sign, and not outside it.
(with a rect detection it's not oke)

There are several options to solve this problem, and I thought this is the best that I need.
- Load the image in monkey and create a polygon outline using the png alpha.

Then after that, store that polygon inside the memory and use that when needed, for example to check if the mouse is inside the polygon.

I'm searching and find some java things, but I don't understand how to convert that to monkey code.

Here is the fastest example.
(it don't have to be accurate)

http://stackoverflow.com/questions/7052422/image-graphic-into-a-shape

http://stackoverflow.com/questions/15342083/algorithm-to-make-a-polygon-from-transparent-png-sprite

When the polygon is inside the memory, than its possible to rotate/scale it with the image if needed :)

I'm wondering if this is possible in monkey.


Nobuyuki(Posted 2015) [#2]
You can bake SVG polygons using a vector trace algo built into a program like Illustrator, then do some fudging with the data to bake polygons from it. This can be automated into your build chain using an illustrator script or something of that nature. I'm not sure how it's done in other programs, but once you have that data, it can be used in your other projects.

That's a lot of effort, however. Since you're only looking for mouse hover data, it sounds like you only need to text against one pixel for a collision mask. If that's the case, a polygon type collission is not necessary -- you can do a pixel test on the mouse's underlying pixel either by siphoning raw alpha data from the image (say using nDrawExts2), or by baking raw alpha masks from your sprites and incorporating that into your build chain. The latter would allow you to do a threshold test without any serious magic voodoo, just load the raw data into a DataBuffer and use ReadByte() to test if the value is >0. (Signed ints mean this is a simple >127 alpha threshold)


GC-Martijn(Posted 2015) [#3]
you talk voodoo to me haha, I know the only fact is that I want to extract/use the alpha to be ignored when mouse over.

So its possible to read bytes() to detect if its transparent ?
every byte is a pixel I assume ?

I will check you nDrawExts2 later, to figure this out.
Thanks for the info.

SVG polygons sounds overkill indeed, and I don't want to use extra program's if not needed.
Btw is nDrawExts2 for desktop/ios and android ?


Nobuyuki(Posted 2015) [#4]
desktop, ios, and android yes. All OpenGL targets. I do not provide support for this module, however.

You can read bytes from a specially-crafted alpha mask. You have to create it yourself using photoshop or another tool, or pull the alpha bits directly from the image at runtime using something like nDrawExts2. I recommend not creating the mask at runtime, since it could be slow depending on how many pixels you'd have to read.


SLotman(Posted 2015) [#5]
I did this once, for a mini HOG. What I did? I took all images, opened them up in PSP7, and with the drawing tool I plotted a polygon on top of it, keeping nots of every x,y pair I placed a vertex.

Then, used those vertex in code, and with a "point inside polygon" function checked the collision. Worked well and it was fast enough on the dev-side for me :)

If you don't want to do it manually, just write a tool to do that instead - should be simple to record MouseHit() and record positions.


Powelly(Posted 2015) [#6]
You could try my pixel perfect collision detection module "Collision Mask Module":
http://www.loadcode.co.uk/software/monkey-x-software/collision-mask-module/


Jesse(Posted 2015) [#7]
Maybe this can help you:
http://www.monkey-x.com/Community/posts.php?topic=2571


GC-Martijn(Posted 2015) [#8]
@Powelly
That was the first 'thing' I found, but first i'm trying to do this in monkey only with-out extra tools

@Jesse
I will have a look


GC-Martijn(Posted 2015) [#9]
Update.

2 hours later I think its fast and possible to create this using monkey.

What my code does:
- scanning an image from top left to bottom right
- when its alpha is 0 then it remembers the x,y coordinate till the alpha is not 0
- so at the end I have some outlines
- part 2 is to compress the data and create only the necessary x,y dots (the green dots)
- part 3 is to display a polygon using that coordinates (fails here)

I have all the coordinates (green dots) from top left to bottom right and create the polygon like this
' to poly verts
		' Function DrawPoly:Int( verts:Float[] )
		Local tmpI:Int = 0
		For Local point:Point = Eachin points2
			verts[tmpI] = point.x
			tmpI=tmpI+1
			verts[tmpI] = point.y
			tmpI=tmpI+1
		Next



See what happens.

http://postimg.org/image/5ihhjt6mx/

http://postimg.org/image/5sh1n60x3/

Using this image:

All this is possible using gless and using parts from the nDrawExts2 class (thanks Nobuyuki)

Strict

Import mojo
Import opengl.gles11

Function Main:Int()
	New MyApp()
	Return 0
End

Class MyApp Extends App
	Field thing:Sprite
		
	Method OnCreate:Int()			
		SetUpdateRate(60)
		thing = New Sprite()
		Return 0
	End
	
	Method OnUpdate:Int()
		Return 0
	End
	
	Method OnRender:Int()
		Cls(255,255,255)

		thing.Draw(0,0)
		Return 0
	End
End



Class Sprite
	Field image:Image
	Field tmp:Image
	Field points:Stack<Point> = New Stack<Point>()
	Field points2:Stack<Point> = New Stack<Point>()
	Field verts:Float[60]

	Method New()
		image = LoadImage("test-col.png")

		Local size:Int[2]
		Local data:Int[] = PixelArray.Get("test-col.png", size)

		tmp = CreateImage(size[0], size[1])
		Local x:Int = 0
		Local y:Int = 0

		Local prevX:Int = -1
		Local prevY:Int = -1
		Local testVertical:Bool = False
		Local testHorzontal:Bool = True

		For Local j:Int = 0 Until data.Length
			Local color:Int[] = PixelArray.GetARGB(data[j])

			If color[0]>0 Then
				Local r:Int = 0
				Local g:Int = 0
				Local b:Int = 255
				Local a:Int = 255
								
				Local color:Int
				color |= (a & $FF) Shl 24
				color |= (r & $FF) Shl 16
				color |= (g & $FF) Shl 8
				color |= (b & $FF)

				data[j] = color

				' transparant END

				If testHorzontal Then
					points.Push(New Point(x,y))
					testVertical = True
					testHorzontal = False
				end
			Else
				' transparant START

				If testVertical Then
					points.Push(New Point(x-1,y))
					testVertical = False
					testHorzontal = True
				End
			End

			If x=size[0]-1 Then
				x = 0
				y = y+1
			Else
				x = x+1
			End
		Next

		' REMOVE STRAIGT LINES... 
		Local xyMap:FloatMap<Float> = New FloatMap<Float>()
		Local endedWithTheSame:Bool = False
		Local lastY:Float
		Local xMap:FloatMap<Float> = New FloatMap<Float>()

		For Local point:Point = Eachin points
			If xyMap.Contains(point.x) Then
				xMap.Add(point.x,point.y)
				endedWithTheSame = True
				lastY = point.y
			Else
				For Local it:=EachIn xMap
					Print it.Key+","+it.Value
					points2.Push(New Point(it.Key,lastY))
				Next

				xyMap.Add(point.x,point.y)
				points2.Push(New Point(point.x,point.y))

				xMap.Clear()
				Print ""
			End
		Next
		If endedWithTheSame Then
			For Local it:=EachIn xMap
				points2.Push(New Point(it.Key,lastY))
			Next
		End

		' to poly verts
		' Function DrawPoly:Int( verts:Float[] )
		Local tmpI:Int = 0
		For Local point:Point = Eachin points2
			verts[tmpI] = point.x
			tmpI=tmpI+1
			verts[tmpI] = point.y
			tmpI=tmpI+1
		Next


		' Print size[1]
		tmp.WritePixels(data, 0, 0, size[0], size[1])
	End
	


	Method Draw:Void(px:Float, py:Float)
		DrawImage(tmp,250,250)

		SetColor(255,0,0)
		For Local point:Point = Eachin points
			DrawPoint(point.x,point.y)
		Next

		SetColor(0,255,0)
		For Local point:Point = Eachin points2
			DrawCircle(point.x,point.y,3)
		Next


		DrawPoly(verts)
	End
	
End

Class Point
	Field x:Float
	Field y:Float
	Method New(_x:Float,_y:Float)
		x=_x
		y=_y
	End
End

Class PixelArray
	'Summary:  Loads an ARGB image into an array of ints.
	Function Get:Int[] (path:String, info:Int[])
		Local data:Int[]
		Local db:DataBuffer = LoadImageData("monkey://data/" + path, info)
			
		'Local timeSpent:Int = Millisecs()
		
		'Copy the data buffer into an array.
		 data = data.Resize(db.Length / 4)  '32-bits = 4 bytes
	
		'We need to swap bytes of R and B channels around.
		For Local i:Int = 0 Until db.Length Step 4
			Local j:Int = db.PeekInt(i)
			
			data[i / 4] = (j & $ff000000) | ((j & $00ff0000) Shr 16) | (j & $0000ff00) | ((j & $000000ff) Shl 16)
		Next
	
		'Print "Operation took " + (Millisecs() -timeSpent) + "ms"
		
		Return data
	End Function
	
	'Summary:  Loads a section of an ARGB image into an array of ints.
	Function Get:Int[] (path:String, startX:Int, startY:Int, w:Int, h:Int)
		Local data:Int[]
		Local info:Int[2]
		Local db:DataBuffer = LoadImageData("monkey://data/" + path, info)
			
		'Local timeSpent:Int = Millisecs()
		
		'Copy the data buffer into an array.
		 data = data.Resize(w * h)
	
		'We need to swap bytes of R and B channels around.
		For Local y:Int = 0 Until h
			For Local x:Int = 0 Until w
				Local j:Int = db.PeekInt( ( (startY + y) * info[0] + (startX + x)) * 4)
				
				data[y * w + x] = (j & $ff000000) | ( (j & $00ff0000) Shr 16) | (j & $0000ff00) | ( (j & $000000ff) Shl 16)
			Next
		Next
		
		'Print "Operation took " + (Millisecs() -timeSpent) + "ms"
		
		Return data
	End Function

	'Summary:  Crops a 1d pixel array to the specified rectangle.  Width of image in data must be specified.	
	Function Crop:Int[] (pixeldata:Int[], strideWidth:Int, startX:Int, startY:Int, w:Int, h:Int)
		Local output:Int[w * h]
	
		For Local y:Int = 0 Until h
			For Local x:Int = 0 Until w
				output[y * w + x] = pixeldata[ (y + startY) * strideWidth + (x + startX)]
			Next
		Next
		
		Return output
	End Function

	'Summary:  Grabs a color from an ARGB value and returns an array [A,R,G,B].	
	Function GetARGB:Int[] (argb:Int)
		Local out:Int[4]
		out[3] = (argb) & $FF
		out[2] = (argb Shr 8) & $FF
		out[1] = (argb shr 16) & $FF
		out[0] = (argb shr 24) & $FF
		
		Return out
	End Function
	
	'Summary:  Returns an ARGB value from the colors specified.
''	Function ToARGB:Int(a, r, g, b)
''		Return (r Shl 24) + (g Shl 16) + (b Shl 8) + a
		
	'	Local color:Int
	''	color |= (a & $FF) Shl 24
	''	color |= (r & $FF) Shl 16
	''	color |= (g & $FF) Shl 8
	''	color |= (b & $FF)
	''	Return color
''	End Function
	
	Function ToARGB:Int(color:Int[])
		Local out:Int
		out |= (color[0] & $FF) Shl 24
		out |= (color[1] & $FF) Shl 16
		out |= (color[2] & $FF) Shl 8
		out |= (color[3] & $FF)
		Return color		
	End Function
End Class



Raph(Posted 2015) [#10]
Dunno if you saw this:

http://www.monkey-x.com/Community/posts.php?topic=8626


GC-Martijn(Posted 2015) [#11]
nope haha, but where can i find your code ?


Raph(Posted 2015) [#12]
Doh! Haha, I thought I had posted it up back then! I'll see if I can find it and get it on that thread.