Little black lines around images?

Monkey Targets Forums/Android/Little black lines around images?

Yahfree(Posted 2011) [#1]
Hey, I'm testing my game on my Android phone, and I get little black lines around moving images.. sort of like 1px borders.

Is this because I'm not using a Pow 2 image size? Or what?

Thanks!


xlsior(Posted 2011) [#2]
Are you using int or float to position your images?

It may be entirely unrelated, but I've seen similar issues in Blitzmax when drawing images at float locations on certain low-end graphics adapters. It's worth trying a float -> int conversion before drawing and see if that makes any difference.


Yahfree(Posted 2011) [#3]
Float position.. maybe that's it.. I'll report back (:


Pudsy(Posted 2011) [#4]
Hi,

Just wondered if you got to the bottom of this?

I'm noticing what sounds like the same issue, on both real devices & the emulator, certainly in the latest Monkey v45c (not sure since what version). Only on Android though.

I'm hoping it's not a "float position" thing as it's tough to avoid those with the various display dimensions. Scaling the output to suit (with the transform matrix) will no doubt cause float positions behind the scenes.

It doesn't seem to be the usual culprit for this kind of issue, where an adjacent image in an anim/spritesheet is too close and bleeds over.

Instead, I've seen this happen on the edges of single images and on edges of images where there is nothing on either that side of the image or the opposite side. Sometimes it even appears all the way around a single image.

It kind of looks like the u,v texture coords are just fractionally off. But the lines don't appear to be any part of the image wrapping round or similar. The lines just look like weird dark (or sometimes light) streaks.

Strangely, it doesn't always seem to be the same images each time - or even to happen consistently. Will let you know if I can narrow it down to a specific case.

Thanks!


Pudsy(Posted 2011) [#5]
I've managed to narrow this down to the most consistent test case I've had so far. And so far, the issue seems specific to Android (both emulator + real devices).

For reference, for this test app I'm using Android SDK Tools revision 11 (since R12 broke the snapshot feature).
I'm using a test device in the emulator with settings...
- Android 2.1-update1 - API Level 7
- Skin = built-in WVGA800
- LCD density = 240
- Max VM Heap = 24

But like I said, I've seen the same thing elsewhere on various real devices.

See below for the code.
You'll need a very specific image too, so place this in the "data" folder and name it "image.png" ==>

I've seen this happen with other images of various dimensions (including power-of-2) some anims, some single images. For whatever reason, this image seems to show the issue most consistently in this test app.

Even having said that, sometimes when I build this app & run it in the emulator, the weird lines won't be there. So if the lines aren't appearing, you may need to rebuild until they do. Really seems hard to track down what it might be.

I'm starting to think it may be some memory alignment issue or similar. Sometimes even just adding an irrelevant line of code or changing the package name in the CONFIG.TXT and rebuilding can make the problem appear or go away.

This is the reason I say to use that exact image. Also probably wise to just use the default "Monkey Game" etc. in CONFIG.TXT

Instructions are on the screen in the app. Basically it lets you cycle through several slightly different methods of drawing 3 separately-loaded copies of the same image.

The "line" seems to appear down the RHS of the image that is loaded first, regardless of the order the images are drawn in.

I've tried having 3 differently-named copies "image0.png", "image1.png", "image2.png" and that gives the same result.

If the image positions are rounded to Int's (as xlsior suggested above) the issue does indeed seem to go away. Unfortunately that's not very practical once you start transforming the output to suit different resolutions. The matrix transformations are bound to introduce float positions.

And I can't imagine why the issue would only affect the 1st image in this test app since they're all identical?

Here's a grab from the emulator, and I've enlarged the glitchy part to show the issue. There's nothing in "image.png" to suggest where that weird line might come from, other than the line seems to match the tallest part of the green arrow. But there's nothing down the LHS that could be eg. "wrapping around" the u/v-coords. And it's in such an odd range of colours too!


Here's the code anyway - any help much appreciated.
Thanks!
Strict
Import mojo


Const NUM_TESTS:Int = 5
Global TEST_NUMBER:Int = 1

Global x:Float=10, y:Float=10
Global dx:Float=0.11, dy:Float=0.13
Global img:Image[3]


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


Class MyApp Extends App

	Method OnCreate:Int()
		SetUpdateRate(60)
		
		For Local i:Int = 0 Until img.Length
			img[i] = LoadImage("image.png")
			If img[i] = Null Then Error "Can't load: image.png"
		Next
		
		Return 0
	End Method
	
	Method OnUpdate:Int()
		x += dx
		y += dy
		If (x < 0) Or (x > DeviceWidth()) Then dx = -dx
		If (y < 0) Or (y > DeviceHeight()) Then dy = -dy
		
		If TouchHit(0) > 0 Then TEST_NUMBER = (TEST_NUMBER Mod NUM_TESTS) + 1
		
		Return 0
	End Method
	
	Method OnRender:Int()
		Cls 192,64,192
		
		Local testDescr:String = ""
		
		Local px:Float = x
		Local py:Float = y
		Local stepx:Float = img[0].Width() + 4
		Local stepy:Float = img[0].Height() + 4
		
		Select TEST_NUMBER
			
			Case 1
				testDescr = "DRAW ALL IMAGES - FIRST ONE HAS LINE DOWN RHS"
				For Local i:Int = 0 Until img.Length
					DrawImage img[i], px,py
					py += stepy
				Next
				
			Case 2
				testDescr = "REVERSE ORDER - SAME ONE HAS LINE DOWN RHS"
				For Local i:Int = img.Length-1 To 0 Step -1
					DrawImage img[i], px,py
					py += stepy
				Next
				
			Case 3
				testDescr = "ROUND POSITIONS TO INTS - LINE IS GONE!"
				For Local i:Int = 0 Until img.Length
					DrawImage img[i], Int(px),Int(py)
					py += stepy
				Next
				
			Case 4
				testDescr = "DRAW FIRST IMAGE MULTIPLE TIMES - ALL HAVE LINE"
				For Local i:Int = 0 Until img.Length
					DrawImage img[0], px,py
					py += stepy
				Next
				
			Case 5
				testDescr = "DRAW SECOND IMAGE MULTIPLE TIMES - NONE HAVE LINE"
				For Local i:Int = 0 Until img.Length
					DrawImage img[1], px,py
					py += stepy
				Next
				
		End Select
		
		DrawText "Tap screen to cycle through tests", 0,200
		DrawText "Test number: "+TEST_NUMBER, 0,216
		DrawText testDescr, 0,232
		
		Return 0
	End Method
	
End Class



OvineByDesign(Posted 2011) [#6]
have you tried this thread ?

http://monkeycoder.co.nz/Community/posts.php?topic=993


Pudsy(Posted 2011) [#7]
Thanks for the suggestion. Disabling the filtering is not exactly the end-result I'm after (especially via back-end Monkey hacks), but I guess the effect could have it's uses, so I'm all for adding it as a feature :)

I've just given it a go anyway...

At first I thought that had resolved it, but it seems to just disguise some of the occurences. I still get the occasional line appearing on the edges of some images - only now they're more obvious solid 1-pixel-wide lines rather than the thinner/semi-transparent smears like in the screen grab above.

I'm still puzzled by how it only affects the first image in my example above. And how it sometimes goes away when I rebuild the app.

Proving to be a tough one to nail down this.

Is anyone else able to reproduce this issue?
Is it worth moving this thread to "Bug Reports" ?

Thanks!


Uncle(Posted 2011) [#8]
Usually issues like this are caused when you don't have enough transparent space around your solid image. Try just making your the transparent border around your image a few pixels wider. Also google 'Defringe' as there are few tools out there that help remove these artifects.

more info here...

http://www.blitzbasic.com/Community/posts.php?topic=81818

http://www.blitzbasic.com/Community/posts.php?topic=62967

download a defringe tool here...

http://sites.google.com/site/yanbloke/


Pudsy(Posted 2011) [#9]
Thanks Uncle!

I've tried both of the following images with my test app...

One with a larger transparent border:
And the same one after using "defringe.exe":

Unfortunately I get the same results...


It seems fairly clear now that the weird edge has nothing to do with the size of the green part of the image. Beyond that, I'm still clueless...

I keep wondering if the slight dithering of the drawn image in the screen grab from the emulator is just a result of the emulator using a lower bit-depth for output, or if it is somehow is related to the issue. I've checked that "HICOLOR_TEXTURES=true" in MonkeyGame.java, but I guess that doesn't prevent the device using a lower depth somewhere along the way.

FWIW I used Paint Shop Pro 8 for all the images in the original app where I noticed the issue, and for the first "green arrow" example image I posted. I've seen various other apps (one I remember being Fireworks MX 2004) which save images with dodgy transparent fringes, so understand that problem. Haven't had it before with PSP8, and it doesn't look like that's the issue here either.

I guess I would have been surprised if defringing the image did solve it, because that wouldn't really explain why it only affects the first-loaded copy of the image. I can't understand why the other 2 copies display just fine, unless it's some weird bug with how the images are loaded (or perhaps how they are aligned in memory, as I suggested above).

Similarly, if the image itself was causing it, I might expect to see similar visible fringes when compiled for other targets - but all of HTML5, Flash, iOS & XNA seem to display all the images in my original app just fine.

One bonus - interestingly, "defringe.exe" managed to compress my image better than PSP8, so I may have to use it again elsewhere :)

Still leaves me stumped though... :(


benmc(Posted 2011) [#10]
I'm just getting started, but I am experiencing the exact same thing.

It appears to be completely related to scaling effects on the images you are loading and the size of the loaded images.

In my onRender, I'm scaling based on screen size, and if I don't do any scaling, the images are perfect, but if I apply scaling (up), I get the line.

I can reduce this from happening a little by not stretching, but scaling proportionally:

SCALE_RATIO_Y = DeviceHeight()/HEIGHT
SCALE_RATIO_X = ((DeviceHeight()*WIDTH)/HEIGHT)/WIDTH

HOWEVER, I still get the line sometimes.

The fix appears to be making my images squares. So, a 24x32 image will always show the line, but a 32x32 will not.

So, I would suggest changing your images to squares and using proportional scaling.

All tests for me were done on Android.


benmc(Posted 2011) [#11]
Actually, the proportional scaling seems to mean nothing in further tests. But just making my images squares, I get around this.

I'm sure this has to do with texturing. I know from some OpenGL experiences that images needed to be loaded as 128x128, 256x256, 512x512, etc, or they wouldn't work properly.


Pudsy(Posted 2011) [#12]
Thanks benmc.

I also originally got the feeling that scaling images made the issue occur more frequently, or perhaps just made it more noticeable. But I couldn't tie anything down in that regard, so I made sure my test app above could demo the effect without using any scaling.

Similarly, using power-of-2 images appears to reduce the number of occurences, but I'm not 100% certain it completely eliminates the problem. Image size is definitely a factor though, and some non-power-of-2 image sizes work fine in my test app. I tried a few sizes before hitting upon the 19x16 image that gave the line in my test app.

But it still puzzles me that if an image is problematic because of it's dimensions, how does loading the same image 3 times in a row only give the problem on the first one? Perhaps I should try loading every image twice and discarding the first one! ;)

I haven't tried sticking to square images yet. That's quite a strict limitation if we need to do that... and if it's needed for Android, perhaps it would be useful if eg. Monkey automatically allocated square images behind the scenes or something? Perhaps as an optional flag with LoadImage?


benmc(Posted 2011) [#13]
I can't seem to completely get rid of this problem when scaling on Android. It is very frustrating.

I'm now thinking of designing for 960x480 and then scaling down to see if that helps.


Pudsy(Posted 2011) [#14]
Me either :(

And scaling can't be avoided if you want to support different resolutions - although I'm not 100% certain it completely goes away if you don't scale anything anyway.

Best solution I've managed so far is by packing all my images into larger "atlas" images, and do a DrawRect (or GrabImage) from those. It seems to help if those atlas images are power-of-2 size, and square (not sure how much being square helps).

That seems to have got rid of most of the problem, but still not perfect.

[ EDIT: Having said that, one Android device I tested struggles with images over 512x512, so there's probably a limit to how far we can push that ]

Doesn't seem to be restricted to any particular devices either. It's starting to feel more like either a bug, or something that needs tweaking for Android - all other targets appear to be fine.

I'm a little surprised there aren't more people reporting it.

I did notice when grabbing sub-images from my atlas images, in one case where I wanted to draw a row of the same solid (no transparency) sub-image, there were small (maybe 1-2 pixels) transparent gaps appearing between them. I kind of got the feeling that Image.GrabImage and/or DrawRect might be "off-by-1-pixel" or something on the Android target.

It's just occured to me that this could be related.
May need to investigate further...


benmc(Posted 2011) [#15]
I have been able to completely avoid this problem with a few tweaks to my image assets.

It all came down to padding of frames in my images with transparency.

So, in my code, I will define a sprite as 32x32, but the graphic itself will be 34x34 and each frame will contain a pixel or two of transparent padding and I don't have this problem at all anymore for stand-alone sprites.

In a tile-set, where sprites may need to butt-up against each other and I don't want to see any spacing around them, I create a sprite sheet where each tile is 32x32, but the tile where I never want to see spacing on the left or right, I make the tiles on the left and right act as the "padding" and those tiles typically match the "center" tile. It's hard to explain, and it makes for messy sprite sheets, but I've scaled to many sizes now, and tested on various android and iOS devices, and no longer have this problem.

The last trick I learned is that if a sprite sheet as 5 frames, I make it 6 frames wide so the last frame has that padding on the right as well.

I don't seem to have the problem with the first frame.

I am using DrawImage by Frame, not GrabImage or DrawRect.


benmc(Posted 2011) [#16]
In another thread, therevills, pointed out the thread below to me:

http://www.monkeycoder.co.nz/Community/posts.php?topic=993

After implementing this change to mojo, this problem has gone away on my Android devices.


Pudsy(Posted 2011) [#17]
Thanks for the update.

OvineByDesign suggested the same solution somewhere above. When I tried this "disable image filtering" hack previously, it did seem to *almost* fix the problem (still got occasional lines appearing on some images).

Unfortunately, the blocky unfiltered output isn't really the end-result I'm after - but I'd love to see it added as an official option/command in Monkey as I'm sure it would have it's uses :)

For now the only solution that is working for me is to pack all my images into large "atlas" images (and make sure they're square & power-of-2 sizes) - which is probably good practice & wise for performance anyway.

I'd still like to see this investigated in case something can be done about it on the Monkey side of things, because AFAIK the problem only occurs on Android.

As I mentioned above, I got the feeling it may even be some kind of "off-by-1" bug that is only visible when texture coords & image scaling come together in certain ways. Sometimes increasing/decreasing an image width or height by 1 pixel would make it go away.

Perhaps it is rendering garbage data from memory adjacent to the image data, just for that 1 pixel line. That might explain why I could load the same image multiple times, but only the first one would have the weird line down the right edge.


AdamRedwoods(Posted 2011) [#18]
Could be a GL_CLAMP_TO_EDGE/GL_LINEAR sampling issue or pixel alignment problem (or both).

GL_CLAMP_TO_EDGE tells opengl to grab to the edge, so if we do our GL_LINEAR sampling, we will sample pixels off the edge (garbage). OpenGL should clamp these values, not sure why it isn't. Using GL_CLAMP will prevent this, but you have to assume a 1-pixel border for your textures.

Even the man page will state you'll get black edges appear on an invalid read:
http://www.khronos.org/opengles/sdk/docs/man/xhtml/glTexParameter.xml

Similarly, if the width or height of a texture image are not powers of two and either the
GL_TEXTURE_MIN_FILTER is set to one of the functions that requires mipmaps
or the GL_TEXTURE_WRAP_S or GL_TEXTURE_WRAP_T is not
set to GL_CLAMP_TO_EDGE, then the texture image unit will return
(R, G, B, A) = (0, 0, 0, 1).





The Second problem it could be is with glSubTexImage2D() wanting power-of-two. This is noted here:
http://stackoverflow.com/questions/5086145/what-differences-in-opengl-rendering-are-there-between-the-atrix-4g-and-other-an

That would be either an opengl implementation bug or an alignment issue. Mojo has pixel unpacking set to 1, and sets textures to power-of-two automatically-- so this should be ok.

I wonder if GL_PACK_ALIGNMENT should be set to 1 as well? It shouldn't need to be, but I wonder if some opengl drivers get messed up.


benmc(Posted 2011) [#19]
AdamRedwoods - thank you for the information, I don't know enough about OpenGL to understand, but I can add that changing all GL_LINEAR to GL_NEAREST fixes the problem for me, but as seen above in this thread, it doesn't provide ideal results for everyone.

Would I just change GL_LINEAR/NEAREST to GL_CLAMP_TO_EDGE or somewhere set the GL_PACK_ALIGNMENT to 1 to test the results you suggest? I would like to get it a go.


AdamRedwoods(Posted 2011) [#20]
It's under the mojo native/Bind() function. (Note: do this at your own risk, etc.)

So in that function you'll see that there's two lines with glTexParameterx() with GL_CLAMP_TO_EDGE. You can try GL_CLAMP instead to see if it adds in the border.


Secondly, you can try adding the line:
gl.glPixelStorei( GL11.GL_UNPACK_ALIGNMENT,1 ); //this line already is in there
gl.glPixelStorei( GL11.GL_PACK_ALIGNMENT,1 ); //add this line


Again, I don't expect these to solve the problems, but it may. If it does, then I'd say theres a weird implementation in the device drivers. These lines shouldn't effect anything else other than shaving a pixel on the border of some images.


teremochek(Posted 2012) [#21]
Thank you. I too was worried about this problemma.
GL_CLAMP get an error.
-compile:
    [javac] C:\Programs\android-sdk-windows\tools\ant\build.xml:600: warning: 'includeantruntime' was not set, defaulting to build.sysclasspath=last; set to false for repeatable builds
    [javac] Compiling 2 source files to D:\MonkeyPro50\bananas\teremochek\electromon\electromon.build\android\bin\classes
    [javac] D:\MonkeyPro50\bananas\teremochek\electromon\electromon.build\android\src\com\teremochek\emmc\MonkeyGame.java:1288: cannot find symbol
    [javac] symbol  : variable GL_CLAMP
    [javac] location: interface javax.microedition.khronos.opengles.GL11
    [javac] 		gl.glTexParameterx( GL11.GL_TEXTURE_2D,GL11.GL_TEXTURE_WRAP_S,GL11.GL_CLAMP );
    [javac] 		                                                                  ^
    [javac] D:\MonkeyPro50\bananas\teremochek\electromon\electromon.build\android\src\com\teremochek\emmc\MonkeyGame.java:1289: cannot find symbol
    [javac] symbol  : variable GL_CLAMP
    [javac] location: interface javax.microedition.khronos.opengles.GL11
    [javac] 		gl.glTexParameterx( GL11.GL_TEXTURE_2D,GL11.GL_TEXTURE_WRAP_T,GL11.GL_CLAMP );
    [javac] 		                                                                  ^
    [javac] Note: D:\MonkeyPro50\bananas\teremochek\electromon\electromon.build\android\src\com\teremochek\emmc\MonkeyGame.java uses unchecked or unsafe operations.
    [javac] Note: Recompile with -Xlint:unchecked for details.
    [javac] 2 errors
BUILD FAILED
C:\Programs\android-sdk-windows\tools\ant\build.xml:580: The following error occurred while executing this line:
C:\Programs\android-sdk-windows\tools\ant\build.xml:600: Compile failed; see the compiler error output for details.
Total time: 1 second
Android build failed.

GL_PACK_ALIGNMENT not get the desired result.
...
I had a similar problemma on "BlitzMax". And it solved way - SetMaskColor().


marksibly(Posted 2012) [#22]
HI,

Ok, it's not happening here but I think I know what the problem is.

It's basically the same as the 'padding' problem - see: http://www.monkeycoder.co.nz/Community/posts.php?topic=2134

In this case though it's because the image isn't a power-of-2 size, but the texture is. This means the left/top pixels are being correctly clamped by the hardware, but the right/bottom ones aren't (because the hardware can only clamp to the texture size, not an arbitrary rect). And currently, there is only 'garbage' in the texture outside the image area.

If you make the image a power of 2, or use XYPadding, it should fix it - but you really shouldn't have to pad 1 frame images so I'll do something about that.


teremochek(Posted 2012) [#23]
Thanks
It seems to happen.

1) in the file \modules\mojo\native\mojo.android.java

change
gl.glTexParameterx( GL11.GL_TEXTURE_2D,GL11.GL_TEXTURE_MAG_FILTER,GL11.GL_LINEAR );
gl.glTexParameterx( GL11.GL_TEXTURE_2D,GL11.GL_TEXTURE_MIN_FILTER,GL11.GL_LINEAR );

on
gl.glTexParameterx( GL11.GL_TEXTURE_2D,GL11.GL_TEXTURE_MAG_FILTER,GL11.GL_NEAREST );
gl.glTexParameterx( GL11.GL_TEXTURE_2D,GL11.GL_TEXTURE_MIN_FILTER,GL11.GL_NEAREST );

2) changed in the code

DrawImageRect( image, x*zoomX, y*zoomY, srcX, srcY, srcWidth, srcHeight, rotation, scaleX*zoomX, scaleY*zoomY, 0 )

on
DrawImageRect( image, Int(x*zoomX), Int(y*zoomY), srcX, srcY, srcWidth, srcHeight, rotation, scaleX*zoomX, scaleY*zoomY, 0 )