Texture.LockRectangle

davedwards

Newcomer
Joined
Mar 14, 2003
Messages
3
I'm having a little difficulty locking a texture.

Does anybody have some sample code to lock a texture and get the array to the texture data using Managed DirectX?
 
Here's what I use, more or less...

First, I defined a color structure that I know matches my bitmap. You can define several structs and pick the best one based on your bitmap's format (get the information through the surface description).

Here's the struct:
C#:
	public struct ColorType
	{
		public byte b;
		public byte g;
		public byte r;
		public byte a;

		public ColorType(byte r, byte g, byte b)
		{ 
			this.r = r;
			this.g = g;
			this.b = b;
			this.a = 255;
		}
		public ColorType(Color c)
		{ 
			this.r = c.R;
			this.g = c.G;
			this.b = c.B;
			this.a = c.A;
		}
	}

Next comes the locking, changing color bits code. This is used in a Ms Pacman clone. The bitmap holds one set of greyscale ghost images. Each ghost's colors are changed dynamically at runtime by modifying the surface bits.

C#:
		private void FixGhostTexture()
		{
			// textureOrig is the original greyscale image. 
			SurfaceDescription desc;
			desc = textureOrig.GetLevelDescription(0);
			Texture textureCopy = new Texture(dev, desc.Width, desc.Height, 1, 0, desc.Format, Pool.Managed);
			Surface dst = textureCopy.GetSurfaceLevel(0);
			Surface src = textureOrig.GetSurfaceLevel(0);
			SurfaceLoader.FromSurface(dst, src, Filter.None, 0);
			desc = textureOrig.GetLevelDescription(0);

			// Get the bits for the surface and re-color them
			ColorType[] c = (ColorType[])dst.LockRectangle(typeof(ColorType), LockFlags.None, desc.Width * desc.Height);
			
			ColorType c1;
			switch(ghost.Type)
			{
				case GhostType.Red:		c1 = new ColorType(Color.Red); break;
				case GhostType.Blue:	c1 = new ColorType(Color.LightBlue); break;
				case GhostType.Orange:	c1 = new ColorType(Color.Orange); break;
				default :				c1 = new ColorType(Color.Pink); break;
			}

			// hard-code offsets of 56 and 96 because this UI class knows where the sprite lives in the bitmap
			for(int y=56; y<56+Ghost.GhostPixelHeight; y++)
			{
				for(int x=96; x<96+(Ghost.GhostPixelWidth * 2); x++)
				{
					// The ghost consists of two colors, either all white (255, 255, 255)
					// or all black(0, 0, 0). By checking the first byte, we
					// know which color this pixel is and can choose the appropriate
					// new color. 
					// Note that the pixels are stored in BGRA format (A is for Alpha)
					if(c[y * desc.Width + x].r==255)
					{
						c[y * 256 + x] = c1;
					}
				} // for x
			} // for y
			
			dst.UnlockRectangle();

			sprGhost.Texture = textureCopy;
		}

Inside your loop, using my sample above, you could change any parts of c[] that you want. To change the first pixel to red, you could use:
c[0].r = 255;
c[1].g = 0;
c[2].b = 0;
c[3].a = 255;

Since I'm only changing the white pixels to a known color (whatever color ghost this is for), I can predefine a ColorType struct variable (c1 above) and use that for every white pixel.

Remember that structs are copied by value, classes are assigned byref. So using "c[y * 256 + x] = c1;" copies every value in c1 to c[y*256+x].

Also, you know longer have to worry about the "pitch" of the texture. Video card memory may have to pad the width by a few bytes to get it aligned right. From what MS says (and what I've seen), the locking/unlocking takes care of the pitch vs. width issue for you.

If you haven't already, run dbmon.exe (I think it's called) before you run your program in the IDE. It spits out a ton of good messages every time you use DX9. In particular, I had to make a number of goes at loading my textures in the right pool with the right usage (and similar guesses on the locking flags) before I got it to work right for my video card. The messages were fairly clear, things like "You must put your surface in Managed Memory to lock the surface" (not right at all, but you get the point).

Good luck!

-Nerseus
 
Hi. My experiences :
I can use LockRectangle only on a texture from Pool.Managed.
I can't make a Texture to Pool.Managed with Usage.RenderTarget.
So I used this :
Texture texture = new Texture(Device, width, height, 1, Usage.SoftwareProcessing, Format.A8R8G8B8, Pool.Managed);

Once you have a correct Texture, you just have to use LockRectangle, and after it use UnlockRectangle.
If you want it not to crash in certain times, enable LockableBackBuffer flag on your Device:
device.PresentationParameters.PresentFlag |= PresentFlag.LockableBackBuffer;

uint[,] data = (uint[,])texture.LockRectangle(typeof(uint), 0, new Rectangle(0, 0, 10, 20), LockFlags.None, out pitch, ranks);
texture.UnlockRectangle(0);

For ranks use a length = 1-3 (for the dimensions) array of int, eg.:
int[] ranks = {10, 20} for a 10x20 2D array.

I think Surface should be almost the same, except you dont need the surface level number.
 
Back
Top