Sign in to follow this  
Haytil

Extra Border Around ID3DXSprite [SOLVED]

Recommended Posts

Extra Border Around ID3DXSprite I'm trying to use ID3DXSprite in DirectX 9.0 to draw animated sprites. However, when I try and draw sprites from my texture, I get an odd black border around my sprites. When drawing normally, there is a black line at the top. When rotated, the border extends all around the sprite. Let me demonstrate: This is my texture: Each sprite is 32 pixels wide and 57 pixels high (with a 1 pixel border extending around each pixel). Here is my code for loading:
// Initialization
LPD3DXSPRITE pD3DXSprite;
LPDIRECT3D9 pD3D9;
LPDIRECT3DDEVICE9 pD3DDevice9;

pD3D9 = Direct3DCreate9(D3D_SDK_VERSION);

// get the display mode
	D3DDISPLAYMODE d3ddm;
	pD3D9->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &d3ddm);

	// set the presentation parameters
	D3DPRESENT_PARAMETERS d3dpp;
	ZeroMemory(&d3dpp, sizeof(d3dpp));
	d3dpp.BackBufferWidth = 1024;
	d3dpp.BackBufferHeight = 768;
	d3dpp.BackBufferCount = 1;
	d3dpp.BackBufferFormat = d3ddm.Format;
	d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
	d3dpp.Windowed = false;
	d3dpp.EnableAutoDepthStencil = true;
	d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
	d3dpp.Flags = D3DPRESENTFLAG_LOCKABLE_BACKBUFFER;
	d3dpp.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
	d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;	// for frame rate detection

pD3D9->CreateDevice(D3DADAPTER_DEFAULT, 
                                 D3DDEVTYPE_HAL, m_HWND,
                                 behavior_flags,
                                 &d3dpp, &pD3DDevice9);

D3DXCreateSprite(pD3DDevice9, &pD3DXSprite);


// Load texture
D3DXIMAGE_INFO d3dxImageInfo;
LPDIRECT3DTEXTURE9 m_pTexture;
D3DCOLOR colorkey = 0xFFFF00FF;

D3DXCreateTextureFromFileEx(Graphics_System->pD3DDevice9, "test.bmp", 128, 128, 1,
					D3DPOOL_DEFAULT, D3DFMT_UNKNOWN, D3DPOOL_DEFAULT, D3DX_DEFAULT, 
					D3DX_DEFAULT, colorkey,	// Use a magenta color key
					&d3dxImageInfo, NULL, &m_pTexture)))

m_src_Rect.top = 1 + ((m_curr_frame / m_x_cells) * (m_height + 1));
		m_src_Rect.left = 1 + ((m_curr_frame % m_x_cells) * (m_width + 1));
		m_src_Rect.bottom = m_src_Rect.top + m_height;
		m_src_Rect.right = m_src_Rect.left + m_width;



Here is my code for drawing:
// Texture coordinates:

RECT m_src_Rect;
int m_x_cells = 3;	// Number of cells in the horizontal direction (3 x 2 cells means width is 3 cells)
int m_curr_frame = 0;	// First frame, though this code has the same problem no matter what the frame
int m_height = 57;
int m_width = 32;

m_src_Rect.top = 1 + ((m_curr_frame / m_x_cells) * (m_height + 1));
		m_src_Rect.left = 1 + ((m_curr_frame % m_x_cells) * (m_width + 1));
		m_src_Rect.bottom = m_src_Rect.top + m_height;
		m_src_Rect.right = m_src_Rect.left + m_width;


// Red background so it's easy to see what's going on
pD3DDevice9->Clear(0, NULL, D3DCLEAR_TARGET,
		                 D3DCOLOR_XRGB(255, 0, 0), 1.0f, 0);
pD3DDevice9->BeginScene();
pD3DXSprite->Begin();

D3DXVECTOR2 m_vScale_Vector;
m_vScale_Vector.x = 1.0f;
m_vScale_Vector.y = 1.0f;

D3DXVECTOR2 m_vRotation_Center;
m_vRotation_Center.x = 0.5f * (float)m_width;
m_vRotation_Center.y = 0.5f * (float)m_height;

float m_Angle = 0;	// Can set to whatever angle we desire

D3DXVECTOR2 m_vPosition;
m_vPosition.x = 300;
m_vPosition.x = 400;

DWORD m_Tint = D3DCOLOR_RGBA(255, 255, 255, 255);

m_pSprite->Draw(m_pTexture, &m_src_Rect, &m_vScale_Vector, &m_vRotation_Center, m_Angle,
				&m_vPosition, m_Tint);

pD3DXSprite->End();
pD3DDevice9->EndScene();
pD3DDevice9->Present(NULL, NULL, NULL, NULL);



I don't understand why the black line exists at the top - my texture coordinates (in m_src_rect) specify that the top of the sprite begins at pixel.top = 1, which should be fine, because the top pixel of the texture (pixel.top = 0) is black, but underneath it's magenta. Similarly, why is the border visible everywhere when I rotate the sprite? Thanks. EDIT: This problem's now been solved. As discussed below, ID3DXSprite uses bilinear texture filtering. So either a transparent border must be used around all the frames, or point filtering must be used. Thanks guys. [Edited by - Gauvir_Mucca on March 7, 2010 2:27:33 AM]

Share this post


Link to post
Share on other sites
My m_width and m_height variables are correct.

If I try reducing them by 1 for purposes of calculating m_src_rect.bottom and m_src_rect.right, three things happen:

A) The top line is still always visible

B) The border still appears around the left and top of the sprite when rotated (though the right and bottom borders are no longer visible)

C) The right edge of the image is clipped by 1 pixel (hard to see with the all-blue rocket ship, but if I add surface details, you can see the right wing is clipped when I perform the above operation).

Based on my calculations, and the above evidence, I believe I can say that my m_width and m_height variables are correct. Even if they weren't, reducing them didn't solve anything.

I also included the original texture above so you can check for yourself.

Any other ideas?

[Edited by - Gauvir_Mucca on March 5, 2010 10:45:56 PM]

Share this post


Link to post
Share on other sites
Quote:
Original post by LancerSolurus
Simple fix.. DX sprites act weird at the edges, it overlaps, top to bottom and left to right. Simply create a 1 pixel wide fully transparent border around the entire image and the offending lines will go away...


Is this a well-documented bug/feature that MS acknowledges and suggests as the fix for?

What you suggest seems to work, but it seems like a dirty hack, designed to get around actual behavior that does not match up with the suggested behavior of the documentation.

It does not seem to me that every image SHOULD have to have a transparent border - this means, for instance, that you can't have sprite images bordering one another without any kind of border (transparent or not) between them. Additionally, a custom-written D3D-based Sprite wrapper using textured quads wouldn't have an arbitrary requirement like this, would it?

I'm just concerned that the reason this is happening is because I'm doing something wrong, and not because MS's DirectX 9 is inherently buggy and does not work entirely as documented. It seems to me that if DirectX 9 is fundamentally flawed in such a manner, then MS would at least acknowledge that (and warn their developers)...

Share this post


Link to post
Share on other sites
Quote:
If I try reducing them by 1 for purposes of calculating m_src_rect.bottom and m_src_rect.top, three things happen:

Quote:
B) The border still appears around the left and top of the sprite when rotated (though the right and bottom borders are no longer visible)


I think you're forgetting that you added +1 to the left and top. You should reduce m_src_Rect.bottom and m_src_Rect.right by 2. If that doesn't help, make sure your rectangle(the area enclosed in the outer border) is not wider that 98 pixels(rect.right-rect.left) and that it's not higher than 115 pixels.

Share this post


Link to post
Share on other sites
Quote:
Original post by Gauvir_Mucca
Is this a well-documented bug/feature that MS acknowledges and suggests as the fix for?


It's not a "bug", it's a natural result of bilinear filtering. The only way to make sure that only the texels within your rectangle get sampled is to use POINT for your MagFilter and MinFilter. If you want bilinear filtering, you'll need to add a gutter around your image as others have suggested.

Share this post


Link to post
Share on other sites
Quote:
Original post by Mussi
Quote:
If I try reducing them by 1 for purposes of calculating m_src_rect.bottom and m_src_rect.top, three things happen:

B) The border still appears around the left and top of the sprite when rotated (though the right and bottom borders are no longer visible)


A) Sorry, my earlier post was incorrect in describing my attempt at a fix. What I meant to write was:

Quote:
If I try reducing them by 1 for purposes of calculating m_src_rect.bottom and m_src_rect.right, three things happen:


So what I'm saying is, I changed the code to the following, per your suggestion:


m_src_Rect.top = 1 + ((m_curr_frame / m_x_cells) * (m_height + 1));
m_src_Rect.left = 1 + ((m_curr_frame % m_x_cells) * (m_width + 1));
m_src_Rect.bottom = m_src_Rect.top + m_height - 1;
m_src_Rect.right = m_src_Rect.left + m_width - 1;






Quote:
I think you're forgetting that you added +1 to the left and top. You should reduce m_src_Rect.bottom and m_src_Rect.right by 2.


I need to add +1 to the left and top because the border surrounds all the frames. Reducing m_src_Rect.bottom and m_src_Rect.right by 2 not only does not make sense, but does not help either - the clipping on the right is even more severe (since I'm essentially reducing the width and height by 2, instead of 1 or 0).


Quote:
If that doesn't help, make sure your rectangle(the area enclosed in the outer border) is not wider that 98 pixels(rect.right-rect.left) and that it's not higher than 115 pixels.


First, m_src_rect should only encompass one cell (i.e., one frame), not all six cells - so if by "rect.right-rect.left" you mean "m_src_rect.right-m_src_rect.left", then it should be 33 x 57.

I counted and made sure the entire six cells with their borders are 100 x 117 pixels. If I discount the outer borders (but keep the separating borders in the middle), the six cells are 98 pixels by 115 pixels.

That is, for a rectangle of 3 x 2 cells:

width = 1 (outer border) + 32 (first cell width) + 1 (first middle border) + 32 (second cell width) + 1 (second middle border) + 32 (third cell width) + 1 (outer border) = 100

height = 1 (outer border) + 57 (first cell height) + 1 (middle border) + 57 (second cell height) + 1 (outer border) = 117

Like I said, the image linked above is the actual texture I use - you can check for yourself if you don't believe me, but I've checked and re-checked that my sizes and numbers are correct.

Quote:
Original post by Aiwendil
Any reason why you can't just get rid of the border in the image itself?


Like I said, I COULD simply use transparent borders - but that seems like an awkward requirement and workaround. I specified a rectangle with top-left coordinates of (1, 1) to DirectX - so DirectX should draw my image with no regard for what kind of pixels are at (0, 0) or (1, 0) or anything else above (1, 1), since y = 1 should be the highest row of pixels it's using.

What if I had my sprite cells all next to each other without ANY border? In principle, it seems like I should be able to do that. But with this problem, the images will overlap slightly. If the top three rocket cells were blue and the bottom three were red, then attempting to draw the fourth cell, for example, would draw a mostly red rocket with a blue tip at the top (since the blue rocket in the first cell's position is bordering the top of the red rocket in the fourth cell's position).

Nothing in the documentation says your images cannot border one another in the texture. Likewise, nothing suggests your images must not only have a border, but that it be transparent as well.

Which leads me to believe that it isn't a requirement for accurate images, and that something wrong must be occurring. Otherwise, wouldn't this problem be mentioned somewhere by MS?

Share this post


Link to post
Share on other sites
Quote:
Original post by MJP
Quote:
Original post by Gauvir_Mucca
Is this a well-documented bug/feature that MS acknowledges and suggests as the fix for?


It's not a "bug", it's a natural result of bilinear filtering. The only way to make sure that only the texels within your rectangle get sampled is to use POINT for your MagFilter and MinFilter. If you want bilinear filtering, you'll need to add a gutter around your image as others have suggested.


Ok, so I don't want bilinear filtering. I try calling this before rendering:


pD3DDevice9->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT);
pD3DDevice9->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT);



But my problem still remains. What am I doing wrong?

Share this post


Link to post
Share on other sites
Quote:
Original post by Gauvir_Mucca
Quote:
Original post by MJP
Quote:
Original post by Gauvir_Mucca
Is this a well-documented bug/feature that MS acknowledges and suggests as the fix for?


It's not a "bug", it's a natural result of bilinear filtering. The only way to make sure that only the texels within your rectangle get sampled is to use POINT for your MagFilter and MinFilter. If you want bilinear filtering, you'll need to add a gutter around your image as others have suggested.


Ok, so I don't want bilinear filtering. I try calling this before rendering:

*** Source Snippet Removed ***

But my problem still remains. What am I doing wrong?


Never mind, I need to make those calls AFTER the call to pD3DXSprite->Begin(). Any reason why? It seems like making the call to SetSamplerState() should affect anything afterwards (unless a later call contradicts it). Does pD3DXSprite->Begin() automatically set bilinear filtering?

Share this post


Link to post
Share on other sites
Quote:
Original post by Gauvir_Mucca
Never mind, I need to make those calls AFTER the call to pD3DXSprite->Begin(). Any reason why? It seems like making the call to SetSamplerState() should affect anything afterwards (unless a later call contradicts it). Does pD3DXSprite->Begin() automatically set bilinear filtering?


The reason why is because ID3DXSprite->Begin() sets sampler states of its own, including enabling bilinear blending.

Share this post


Link to post
Share on other sites
Quote:
Original post by Gauvir_Mucca
Quote:
Original post by Gauvir_Mucca
Quote:
Original post by MJP
Quote:
Original post by Gauvir_Mucca
Is this a well-documented bug/feature that MS acknowledges and suggests as the fix for?


It's not a "bug", it's a natural result of bilinear filtering. The only way to make sure that only the texels within your rectangle get sampled is to use POINT for your MagFilter and MinFilter. If you want bilinear filtering, you'll need to add a gutter around your image as others have suggested.


Ok, so I don't want bilinear filtering. I try calling this before rendering:

*** Source Snippet Removed ***

But my problem still remains. What am I doing wrong?


Never mind, I need to make those calls AFTER the call to pD3DXSprite->Begin(). Any reason why? It seems like making the call to SetSamplerState() should affect anything afterwards (unless a later call contradicts it). Does pD3DXSprite->Begin() automatically set bilinear filtering?


It sure does. There's a list of all sampler/render/texture states it sets in the documentation.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this