Sgt. Conker We are "absolutely fine"

22Jun/104

Windows Phone 7 – XNA Brightness and Contrast

With the addition of Windows Phone 7 support to XNA comes some limitations. If one wanted to have brightness and contrast controls for their game, previous methods may not be available. Considering features such as programmable shaders or device gamma ramp to achieve such a task will leave you out of luck. However there is a simple (and perhaps old school) way of doing it, and that is blend states.

Click Continue reading to see how!

If you look around for information on how to adjust brightness and contrast with blend states, you might find this:

Draw a full screen quad and use these color blend states:

Brightness: source = ONE, dest = ONE
Contrast: source = DESTCOLOR, dest = ZERO

While these would work, they might not be as you expect. Drawing with a 24/32bit per texel texture, the first would only allow you to adjust the brightness up from white, and the latter would only let you reduce contrast. That is probably not what people want. I suggest different settings:

Brightness: source = ZERO, dest = SOURCECOLOR
Contrast: source = DESTCOLOR, dest = SOURCECOLOR

This will allow brightness to be reduced instead of raised, and contrast can be increased as well as reduced.

So how do you actually do this in XNA 4 on Windows Phone 7?

First you will need a 1x1 texel white texture either through the content pipeline, or procedurally.

Add this member to your game class:

Texture2D whiteTexture;

In the LoadContent override method, add this to create the texture procedurally:

whiteTexture = new Texture2D(GraphicsDevice, 1, 1);
whiteTexture.SetData<Color>(new Color[] { Color.White });

You will also need a couple of integer members to hold brightness and contrast state:

int brightness;
int contrast;

Also, two blend states, one for brightness, and one for contrast:

BlendState brightnessBlend;
BlendState contrastBlend;

In the Initialize override method, we can set these other members (note that alpha blend states must match color blend states on WP7/Reach):

brightness = 255;
contrast = 128;

brightnessBlend = new BlendState();
brightnessBlend.ColorSourceBlend = brightnessBlend.AlphaSourceBlend =  Blend.Zero;
brightnessBlend.ColorDestinationBlend = brightnessBlend.AlphaDestinationBlend = Blend.SourceColor;

contrastBlend = new BlendState();
contrastBlend.ColorSourceBlend = contrastBlend.AlphaSourceBlend = Blend.DestinationColor;
contrastBlend.ColorDestinationBlend = contrastBlend.AlphaDestinationBlend = Blend.SourceColor;

Finally, in the Draw override method, after you have drawn to the screen, do the following (this assumes spriteBatch is initialized and that the default blend state is wanted next frame):

spriteBatch.Begin(SpriteSortMode.Immediate, brightnessBlend);
spriteBatch.Draw(whiteTexture, new Rectangle(0, 0, 480, 800), new Color (brightness, brightness, brightness, 255));
spriteBatch.End();

spriteBatch.Begin(SpriteSortMode.Immediate, contrastBlend);
spriteBatch.Draw(whiteTexture, new Rectangle(0, 0, 480, 800), new Color(contrast, contrast, contrast, 255));
spriteBatch.End();

GraphicsDevice.BlendState = BlendState.Opaque;

That's it, but there are a couple more things to mention. First is that the color values (the brightness and contrast integers) should be between 0 and 255, although you could use floats too. To clamp an integer between these values use:

value = Math.Max(Math.Min(value, 255), 0);

To test runtime adjustment of the brightness and contrast, I used input from the 4 quadrants of the screen. The top half adjusts brightness, the bottom contrast. The left side reduces the value and the right side increases it. To do this, add the following code to the Update override method:

foreach (TouchLocation t in TouchPanel.GetState())
{
   if (t.Position.Y <= 400)
      Adjust(t, ref brightness);
   else
      Adjust(t, ref contrast);
}

And the Adjust method (here it adjusts the value by 3 for each touch change at your pleasure, it also includes the clamping mentioned above):

private void Adjust(TouchLocation t, ref int adjustee)
{
   if (t.Position.X < 240)
      adjustee -= 3;
   else
      adjustee += 3;

   adjustee = Math.Max(Math.Min(adjustee, 255), 0);
}

You could expose this via sliders in an options page. The brightness would be particularly useful as you don't want people exiting the game to adjust screen brightness.

About CorporalX

Professional XNA/.NET developer.
Comments (4) Trackbacks (1)
  1. For brightness, you suggest using zero and source color, but then in the code sample you use one and one.

  2. You should also mention the overdraw cost. You are drawing two full-screen quads over the scene every frame and this will have a measurable GPU cost.

  3. Nice solution, cheers for the post


Leave a comment


*