views:

149

answers:

4

If I use the following

for(int i = 255; i > 0; i--)
{
    Color transparentBlack = new Color(0, 0, 0, i);
}

I have the effect of the object using this color to draw with going from black to a light grey and then invisible when the alpha value goes to zero. However if I start with a white value:

new Color(255, 255, 255, i);

The objects never becomes invisible and only stays white. I've also noticed that if I use a value that is a bit lighter than black (say 50, 50, 50) the drawing goes from Dark, to invisible, to White.

I assume that I just don't understand how alpha blending mixing works but is there a way of making a white color fade to translucency?

Edit: The background I am drawing on is Color.CornflowerBlue (100,149,237,255)

Edit: Sample XNA program reproducing the explanation. To use; create a new XNA Game Studio 4.0 project - Windows Game (4.0), call it AlphaBlendTest - & In the content project add a new SpriteFont and call it testfont

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace AlphaBlendTest
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;


    SpriteFont font;

    public Game1()
    {
        graphics = new GraphicsDeviceManager(this);
        Content.RootDirectory = "Content";
    }

    /// <summary>
    /// Allows the game to perform any initialization it needs to before starting to run.
    /// This is where it can query for any required services and load any non-graphic
    /// related content.  Calling base.Initialize will enumerate through any components
    /// and initialize them as well.
    /// </summary>
    protected override void Initialize()
    {
        // TODO: Add your initialization logic here

        base.Initialize();
    }

    /// <summary>
    /// LoadContent will be called once per game and is the place to load
    /// all of your content.
    /// </summary>
    protected override void LoadContent()
    {
        // Create a new SpriteBatch, which can be used to draw textures.
        spriteBatch = new SpriteBatch(GraphicsDevice);

        font = Content.Load<SpriteFont>("testfont");
    }

    /// <summary>
    /// UnloadContent will be called once per game and is the place to unload
    /// all content.
    /// </summary>
    protected override void UnloadContent()
    {
        // TODO: Unload any non ContentManager content here
    }

    /// <summary>
    /// Allows the game to run logic such as updating the world,
    /// checking for collisions, gathering input, and playing audio.
    /// </summary>
    /// <param name="gameTime">Provides a snapshot of timing values.</param>
    protected override void Update(GameTime gameTime)
    {
        // Allows the game to exit
        if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
            this.Exit();

        // TODO: Add your update logic here

        base.Update(gameTime);
    }

    /// <summary>
    /// This is called when the game should draw itself.
    /// </summary>
    /// <param name="gameTime">Provides a snapshot of timing values.</param>
    protected override void Draw(GameTime gameTime)
    {
        GraphicsDevice.Clear(Color.CornflowerBlue);
        spriteBatch.Begin();

        //spriteBatch.Draw(tR, new Rectangle(100, 100, 100, 100), Color.Red);

        Vector2 v2 = new Vector2(0, 0);
        spriteBatch.DrawString(font, "Test - White", v2, Color.White);

        v2.Y = v2.Y + 50;

        spriteBatch.DrawString(font, "Test - Black", v2, Color.Black);

        v2.Y = v2.Y + 50;

        Color BlackTransparent = new Color(0, 0, 0, 0);
        spriteBatch.DrawString(font, "Test - Black Transparent", v2, BlackTransparent);

        v2.Y = v2.Y + 50;
        Color WhiteTransparent = new Color(255, 255, 255, 0);
        spriteBatch.DrawString(font, "Test - White Transparent", v2, WhiteTransparent);

        spriteBatch.End();

        base.Draw(gameTime);
    }
}
}

Edit: This is the image this code draws: alt text

Edit: One of the comments is concerned that this is a Text only related 'feature' of windows. I used text as an example to keep the demo program small; testing with an image gives the same result. alt text

To add the square box in to the demo program; create a square white PNG image and add it to the content directory (use the default values for the content pipeline). Then add this to the class:

Texture2D tWhiteBox;

Add this in to the load method:

tWhiteBox = Content.Load<Texture2D>("whitebox");

Then in the draw add the following below the other draw statements:

v2.Y = v2.Y + 50;
spriteBatch.Draw(tWhiteBox, v2, WhiteTransparent);
A: 

If black becomes "light grey" when it's half-transparent, that suggests that you've got a white background... at which point, a fully transparent white colour put on top will still be white.

Try using your white object against a different coloured background - red, say - which should make it go from white (alpha=255) through pink (alpha=127) to just red (alpha=0).

Jon Skeet
For testing the background I am drawing on is Color.CornflowerBlue (100,149,237,255). I am trying to get something working that I can use on a multi colored background through.
Sebastian Gray
Also I tested on a red background and used a fixed color value of Color(255, 255, 255, 0); The color for the object being drawn was white.
Sebastian Gray
@Sebastian: I would have *expected* that to be okay then. Could you provide a short but complete test program so we can check there's nothing else going on? If you could try it on a simpler-to-test API (e.g. WPF) first, that would be helpful too... although I understand that obviously we'll need to get it working on WP7 too.
Jon Skeet
@Jon Skeet: I've created the smallest Windows XNA Game project that I could to demonstrate what I am talking about - this isn't dependent on WP7 but it's probably not as simple as a WPF app. I'm just not sure if I would be able to reproduce the same result with a WPF app.
Sebastian Gray
@Sebastian: Thanks - I'll give it a try this evening.
Jon Skeet
+2  A: 

This is a historical limitation, at least on Windows. The GDI primitives, like DrawTextEx don't support transparency. They will simply ignore the alpha channel. This restriction is solved by graphics libraries like GDI+ and Milcore. But there's only one TrueType renderer and it does a job too untrivial to be easily replaced. Your screenshot shows it running inside a regular Windows window so the restriction is very likely to apply. Not sure if this is different on a console.

A possible workaround is to use a function similar to GraphicsPath.AddString(). You will however lose the ClearType anti-aliasing effect. Not so sure that matters in XNA. Nor do I know it well enough to suggest the equivalent of GraphicsPath.

Hans Passant
I don't think the limitation you're referring to is present in XNA. I've added an updated screen shot and the code for the test to use an image as well. Originally I noticed this behavior on an image but a text example is the smallest demo project I could quickly pull together, thanks anyway.
Sebastian Gray
+6  A: 

Grey is what you'll get in XNA 4.0 (or full white if you try to just adjust the alpha channel without adjusting the RGB channels as well). Transparency is done (by default) using pre-multiplied alpha. If you're looking for the old non-pre-mul XNA 3.1 behavior, see this post from Shawn Hargreaves: http://blogs.msdn.com/b/shawnhar/archive/2010/04/08/premultiplied-alpha-in-xna-game-studio-4-0.aspx . At the bottom of the post it tells you how to do it.

I'd recommend reading all of his posts on pre-multiplied alpha before doing so though (you can use the "Blog Index" link on the left side near the top of his page) - pre-mul really is a lot better and a lot more logically consistent.

MikeBMcL
Thanks, that post had the answer. If I replace the code I'm using with this: WhiteTransparent = Color.White * 0.5f; I get a 50% transparent white image. 0f fully transparent and 1f completely white. From the post Shawn indicates that the code to define transparency colors has changed and the old code should throw a compiler error; however no compiler error is thrown when defining the alpha value with the rest of the color, as per the code I had an issue with. Thanks for you're help.
Sebastian Gray
A: 

I think it has something to do with Alpha bleding settings for SpriteBatch.

Default is BlendState.AplhaBlend. That has formula BackgroundColor*(1-Alpha)+ForegroundColor. This would explain different bahaviour with black and white.

Try using different spriteBatch.Begin method with BlendState.NonPremultiplied to get desired result. This will result in : BackgroundColor*(1-Alpha)+ForegroundColor*Aplha, making even white completly transparent.

Euphoric