Idea: DrawPadding and CreateTransparentPadding

Monkey Targets Forums/Desktop/Idea: DrawPadding and CreateTransparentPadding

nori(Posted 2014) [#1]
I've got the following problem: Keeping a padding in the texture still gives jaggies on rotated sprites. No matter if the padding was copied exactly or with alpha = 0. And it's not depending on texture sizes and clamping either, the filter simply makes jaggies on all sides of a rotated Image.


----------------


This is the (4x enlarged) output of the code below:



It looks the same on Intel HD Graphics 4600, ATI Radeon 5450 and Nvidia FX 5500.

I don't know what OpenGL modes I might be missing, the filtering is actually there, just not on polygon edges.

DrawImageRect a... does the same (see Mojo code) as using XYPadding with even 4 pixels of copied padding, still it's jaggy.
DrawImageRect b... draws it with the transparent padding.


#MOJO_IMAGE_FILTERING_ENABLED = True

Import mojo

Class Test Extends App
	Field a:Image
	Field b:Image

	Method OnCreate()
		a = LoadImage("20x20_red.png")
		b = LoadImage("8x8_red_on_20x20_transparent.png")
	End

	Method OnUpdate()
	End

	Method OnRender()
		Cls 255,255,255

		DrawImageRect a, 100.0, 100.0, 4, 4, 12, 12, 25.0, 1.0, 1.0, 0

		DrawImageRect b, 140.0, 98.0, 0, 0, 20, 20, 25.0, 1.0, 1.0, 0
	End
End

Function Main()
	New Test()
End


https://www.sendspace.com/file/fku58k


----------------


What fixes it is drawing padded images at x-1, y-1 with their padding, which therefore needs to have alpha = 0. So, if you don't use XYPadding you will need one pixel padding, if you use XYPadding you will need two pixels of padding. No matter what, it's not good this way, you would need to work with modified coordinates and dimensions and can't simply design your game without knowing about this or creating some helper class. So it would be very nice if Mojo could do this for me. I mean, there should be flag called Image.DrawPadding that makes DrawImage draw the image with the padding at the extended coordinates.

Another thing is, I don't want to keep the padding in the image files myself. XYPadding is ok the way it is and it can't be changed to work differently anyway. But in general, when you export images from a graphics software (e.g. Xara), they don't contain a padding, yet I want it to look nice. So if you could give Mojo a flag called Image.CreateTransparentPadding, that would be neat. Mojo would then create a transparent padding at LoadImage.

----------------

So here are two patches for Mojo GLFW and I would be glad if you could consider implementing this or something simmilar.

This is the patch for the DrawPadding flag.

In modules/mojo/graphics.monkey:

At the consts add:
...
	Const DrawPadding=8


At the fields add:
...
	Field px#,py#


The beginning of ApplyFlags must be this:
...
		flags=iflags

		If flags & DrawPadding
			px = 2
			py = 2
		Endif


SetHandle Method must be this:
...
	Method SetHandle( tx#,ty# )
		Self.tx=tx+px*0.5
		Self.ty=ty+px*0.5
		Self.flags=Self.flags & ~MidHandle
	End


HandleX and HandleY must be this:
...
	Method HandleX#()
		Return tx-px*0.5
	End
	
	Method HandleY#()
		Return ty-py*0.5
	End


Width and Height must be this:
...
	Method Width()
		Return width-px
	End

	Method Height()
		Return height-py
	End


The end of ApplyFlags must be:
...
		If flags & Image.MidHandle
			SetHandle (width-px)/2.0,(height-py)/2.0
		Endif
		
		If frames.Length=1 And frames[0].x=0 And frames[0].y=0 And width=surface.Width And height=surface.Height And Not (flags & DrawPadding)
			flags|=FullFrame
		Endif
	End


And the modified DrawSurface2 calls will must be, from first to last occurance:

1)
...
		renderDevice.DrawSurface2 image.surface,x-image.tx,y-image.ty,f.x,f.y,image.width,image.height

2)
...
		renderDevice.DrawSurface2 image.surface,0,0,f.x,f.y,image.width,image.height

3)
...
	renderDevice.DrawSurface2 image.surface,-image.tx+x,-image.ty+y,srcX+f.x,srcY+f.y,srcWidth+image.px,srcHeight+image.py

4)
...
	renderDevice.DrawSurface2 image.surface,0,0,srcX+f.x,srcY+f.y,srcWidth+image.px,srcHeight+image.py


----------------------

This is the patch for CreateTransparentPadding for GLFW

In graphics.monkey:

At the Image consts, add the new flag:

...
	Const CreateTransparentPadding=16


The LoadImage functions must be this:

...
Function LoadImage:Image( path$,frameCount=1,flags=Image.DefaultFlags )
	#If TARGET = "glfw"
	Local createTransparentPadding% = 0
	If flags & Image.CreateTransparentPadding
		createTransparentPadding = 2
	Endif

	Local surf:=device.LoadSurface( FixDataPath(path), createTransparentPadding, frameCount )
	#Else
	Local surf:=device.LoadSurface( FixDataPath(path) )	
	#Endif
	If surf Return (New Image).Init( surf,frameCount,flags )
End

Function LoadImage:Image( path$,frameWidth,frameHeight,frameCount,flags=Image.DefaultFlags )
	#If TARGET = "glfw"
	Local surf:=device.LoadSurface( FixDataPath(path), 0, 0 )
	#Else
	Local surf:=device.LoadSurface( FixDataPath(path) )
	#Endif
	If surf Return (New Image).Init( surf,0,0,frameWidth,frameHeight,frameCount,flags,Null,0,0,surf.Width,surf.Height )
End




In graphicsdevice.monkey the declaration must be this:
...
	#If TARGET = "glfw"
	Method LoadSurface:Surface( path$, createTransparentPadding#, frameCount# )
	#Else
	Method LoadSurface:Surface( path$ )
	#Endif




In mojo.glfw.cpp:


The declarations must be this:
...
	virtual gxtkSurface *LoadSurface( String path,int createTransparentPadding,int frameCount );
	virtual gxtkSurface *CreateSurface( int width,int height );
	virtual bool LoadSurface__UNSAFE__( gxtkSurface *surface,String path,int createTransparentPadding,int frameCount );


The new function and modified LoadSurface methods must be this:
...
static void _createTransparentPadding( unsigned char **_data,int *_width,int *_height,int depth,int padding,int frameCount ){

	if(padding==0) return;
	if(frameCount==0) return;

	unsigned char* data   = *_data;
	int            width  = *_width;
	int            height = *_height;

	int frameWidth       = width / frameCount;
	int paddedFrameWidth = frameWidth + padding;
	int paddedWidth      = frameCount * paddedFrameWidth;

	int paddedHeight = height + padding;

	unsigned char* paddedData = (unsigned char*) calloc(1, paddedWidth * paddedHeight * depth);

	int srcStride = width       * depth;
	int dstStride = paddedWidth * depth;

	for(int frame=0; frame<frameCount; ++frame)
	{
		unsigned char *src = data + (frame * frameWidth) * depth;
		unsigned char *dst = paddedData + (padding/2 * paddedWidth + padding/2 + frame * paddedFrameWidth) * depth;

		for(int y=0; y<height; ++y)
		{
			memcpy(dst, src, frameWidth*depth);
			src += srcStride;
			dst += dstStride;
		}
	}

	*_data = paddedData;
	free(data);

	*_width  = paddedWidth;
	*_height = paddedHeight;
}

bool gxtkGraphics::LoadSurface__UNSAFE__( gxtkSurface *surface,String path,int createTransparentPadding,int frameCount ){

	int width,height,depth;
	unsigned char *data=BBGlfwGame::GlfwGame()->LoadImageData( path,&width,&height,&depth );
	if( !data ) return false;

	_createTransparentPadding(&data,&width,&height,depth,createTransparentPadding,frameCount);

	surface->SetData( data,width,height,depth );
	return true;
}

gxtkSurface *gxtkGraphics::LoadSurface( String path,int createTransparentPadding,int frameCount ){
	gxtkSurface *surf=new gxtkSurface();
	if( !LoadSurface__UNSAFE__( surf,path,createTransparentPadding,frameCount ) ) return 0;
	surf->Bind();
	return surf;
}


----------------------

Usage:
...
Local image = LoadImage("unpadded.png", 1, Image.DrawPadding | Image.CreateTransparentPadding)