Strange behavior with LockRect and scaling a sprite.

Started by
16 comments, last by valleyman86 13 years, 10 months ago
If I scale a sprite and use the texture I edited the pixels of with LockRect then my changes don't seem to show up. In fact depending on how much I scale the sprite the worse it gets. So at a scale of 1 (100%) then it works fine anything less not so much.

void XenStats::RenderArc(float angle, bool top, bool left, byte r, byte g, byte b, D3DXVECTOR2 arcPos, float scale)
{
	if(!top)
		angle = 90 - angle;

	float angleRatio = tan(angle * PI / 180);

	D3DSURFACE_DESC arcDesc;
	arcTexture->GetLevelDesc(0, &arcDesc);

	D3DXVECTOR2 arcScale;
	arcScale.x =  0.5; //This doesn't seem to work but 1.0 does.
	arcScale.y = 0.5;

	
	D3DLOCKED_RECT lockedRect;
	arcTexture->LockRect(0, &lockedRect, NULL, 0);
	byte *pixelBuffer = (byte*)(lockedRect.pBits);

	//Utils::Log(Utils::ToString<int>(lockedRect.Pitch).c_str());

	for (int x = 0; x < arcDesc.Width; ++x) {
		for (int y = 0; y < arcDesc.Height; ++y) {
			//int currentPixel = (arcDesc.Width * y + x) * 4;
			DWORD currentPixel=(x*4+(y*(lockedRect.Pitch)));

//Change all pixels to be blue. These become less and less blue the more I scale the sprite.
				pixelBuffer[currentPixel] = 255;
				pixelBuffer[currentPixel+1] = 0;
				pixelBuffer[currentPixel+2] = 0;
			
		}
	}

	arcTexture->UnlockRect(0);

	D3DXMATRIX arcMatrix;
	D3DXMatrixTransformation2D(&arcMatrix, NULL, 0.0, &arcScale, NULL, 0, &arcPos);


	statsBgSprite->Begin(D3DXSPRITE_ALPHABLEND);
	statsBgSprite->SetTransform(&arcMatrix);
	statsBgSprite->Draw(arcTexture, NULL, NULL, NULL, D3DCOLOR_RGBA(255, 255, 255, 255));
	statsBgSprite->End();
	
}

-------------------------------------------
http://www.MurderDev.com - Developing Killer Software
http://www.MobileCellOut.com - Cell Phone Contract Exchange
Advertisement
1. How do you scale the sprite?
2. How do you draw the sprite?
3. Is this not just because of texture filtering? What if you set the texture filtering to point with:
pDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT);pDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT);

(Assuming you're not using pixel shaders)
I did try what you suggested. That did not seem to make a difference. Im not exactly sure how to answer the other 2 questions. The drawing and the scaling are done in the code above.
-------------------------------------------
http://www.MurderDev.com - Developing Killer Software
http://www.MobileCellOut.com - Cell Phone Contract Exchange
Unless you understand how the drawing and scaling is done, then you won't have an idea as to how it should work. Without knowing how the code works at least at an higher level then its pointless to try and trouble shoot. Did you write the code/ or did you cut and paste it from somewhere. Scaling an sprite(image) is just a resampling problem. By that I mean for a scale factor of 1( 1 to 1) mapping, each pixel in the target it mapped to the same pixel in the destination. If you scale up by a factor on n then for each pixel in the the input, you have 3n pixels written to the destination. If you scale down by n then for for every n pixels in the input you have 1/n pixels in the destination. This is just a simplification and does not factor in filtering, but you can see that just by understanding how scaling works, you should be able to double check you code and find the error in your logic (if there is one) and fix it.
I wrote the code. It uses a matrix and scales it using the line

D3DXMatrixTransformation2D(&arcMatrix, NULL, 0.0, &arcScale, NULL, 0, &arcPos);

as seen above. The reason I don't know how to answer your question is because all the code used for drawing and scaling is shown in my snippet. I am using built in D3DXSprites and thats it.

The lockrect stuff is because I change the color of each pixel in the texture based on a percentage to create a rounded health bar. That works fine but when I scale this it does exactly as you mentioned (re-sampling) and messes it up. Now I would guess the best way to do this would be to modify the texture or sprite after it has been scaled except that I'm not sure how this would be done exactly. I don't think textures get scaled and instead may get mapped to the sprite. I have no clue how that actually works to be honest. So If I could modify the texture after its been applied to the sprite this would probably work fine.
-------------------------------------------
http://www.MurderDev.com - Developing Killer Software
http://www.MobileCellOut.com - Cell Phone Contract Exchange
From what I can tell your application of D3DXMatrixTransformation2D is fine, i.e. scaling and translating the sprite. I'm not quite sure if you ought to SetTransform before Sprite->Begin() though, but probably not.

But, and please apologize, I still don't understand what you want to achieve:

Quote:The lockrect stuff is because I change the color of each pixel in the texture based on a percentage to create a rounded health bar


As I read your double loop, you set all pixels to blue (leaving alpha as it is), independant of method arguments or globals . Apart from being rather useless and time-consuming - such a texture can be setup/loaded once - I doubt that's what you want.

I suggest you make a screenshot and a drawing/explanation what you really want.
Lucky for me I have a screenshot already for what this accomplishes. It works great but I haven't figured out a more efficient way to do this or support scaling.

http://xenergy.murderdev.com/images/itemcompass.png

The hp/mp/xp bars in the center of the screen is this code in action and working. I have a black and white png image that contains half of one bar. I then duplicate that texture to make the other half. To get the color I use the method above to change the pixels blue element to 255.
-------------------------------------------
http://www.MurderDev.com - Developing Killer Software
http://www.MobileCellOut.com - Cell Phone Contract Exchange
(Off topic: Nice work! The screenshot looks promising)

Ok, still not sure whats going wrong. Can you send the health bar texture and a screenshot with wrong/undesired output (i.e. the scaling), too, please. And the code actually calling XenStats::RenderArc might be enlightening, too.

But here some hints:
Quote:...to change the pixels blue element to 255


You can achieve this far cheaper with a tint. Currently, you are using a tint of opaque white, which will leave the texture unchanged. Try using a blue tint:

statsBgSprite->Draw(arcTexture, NULL, NULL, NULL, D3DCOLOR_RGBA(0, 0, 255, 255));


Maybe you need to set the texture stage states accordingly but AFAIR the Sprite class will do that for you in this case. This way you can also fade in/out sprites if you use a alpha other than 255.

You probably will want to change the method signature to reflect that, i.e. provide a additional parameter for color.

Quote:I then duplicate that texture to make the other half.


How do you do that ? Drawing two or more sprites and put them together like tiles and flip some ? And if so: How do you flip ?

If not using sprites, i.e. working with vertex buffers, you normally achieve this by properly setting the texture wrapping modes and the tex-coords. With sprites this is probably not possible that easy, but I suggest having a look at the parameters of sprite->Draw you are currently ignoring, especially pSrcRect which allows you to only take a part of the texture. Flipping can then be done again with an appropriate transformation.

Hope that helps
I am not currently home at the moment but I will take some screenshots later showing this in more detail as well as the issue with scaling.

That said wouldn't

statsBgSprite->Draw(arcTexture, NULL, NULL, NULL, D3DCOLOR_RGBA(0, 0, 255, 255));

color the entire sprite blue? That is not exactly what I want to happen. If you notice in this screenshot (below) the mp is less than full. This also shows what I am accomplishing. This is done using some math and drawing a line from one corner of the texture (which is half of one of those bars) at an angle across the texture. Everything below that line is colored.

http://xenergy.murderdev.com/Images/XenergyNewBars.png

void XenStats::RenderArc(float angle, bool top, bool left, byte r, byte g, byte b, D3DXVECTOR2 arcPos, float scale)

As far as this goes angle is the angle of the line (which is calculated from a health/mana percentage elsewhere). Top indicates whether this half of the arc is top or bottom because since they are rotated differently they need to be aware of what is the bottom for coloring purposes. Left is the same idea as the top. r g and b is the color of the bar. arcPos and scale is the position and scale of the bar respectively.

To answer your question about how I duplicate the textures, I used to just use one texture and modify it every frame and apply it to the sprite. That worked but was inefficient since the HP/MP bars do not always change. I then created 4 textures and modified them separately and only when they changed. I don't remember exactly off hand why my design required me to create a texture for each sprite. Something to do with remembering the previous state or color? Not sure. I may have overlooked that and should go back to it but its not a big deal really.
-------------------------------------------
http://www.MurderDev.com - Developing Killer Software
http://www.MobileCellOut.com - Cell Phone Contract Exchange
Quote:That said wouldn't ... color the entire sprite blue?

Yes it would. But actually, isn't that what you actually do in the double loop ? And you do have a comment saying so (//Change all pixels to be blue..).

Finally I somehow understand the approach, though, i.e. masking pixels with that line defined by an arbitrary angle. But either I'm totally blind or the code you provided does not do that.

Yes, XenStats::RenderArc got arguments for the angle and the quadrant (bool top, bool left), but in that code snipped they are ignored. Well you do a little calculation, but neither of them has an impact on the texture update or the sprite drawing (Transformation). You sure this is (all) the code responsible for those health bars ?

This topic is closed to new replies.

Advertisement