This is how you can do it without using a pixel shader or the content reader importer. I did this just using get set data. Regardless most of these steps are needed to do it in the other ways.
You can load your images via the pipeline tool shown at the bottom of the post if you don’t know how to do so already.
In the below resulting image i picked a random texture and did the following.
* 1) loaded it and drawn it as it is.
* 2) colored it to grayscale.
* 3) reduced the grayscale image to a set of values matching the 21 colors in your array but in no specific order. then drew the image data that contains grayscale values from 0 to 21 hence it is nearly black.
* 4) reassigned the colors from your palatte array to the corresponding indexs within a reduced grayscaled image. The color table can be swapped.
* 5) the function can be called on the texture again with a different color palatte table at run time.
Here is the actual .Cs File in full.
Please see the two primary GrayScale functions especially the parameters they take.
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
namespace GLCrossPlatDesktop_GrayScale_Ex01
{
/// <summary>
/// This is the main type for your game.
/// </summary>
public class Game1 : Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
Texture2D texture, textureAsGrayScale, textureAsPalette, textureAsPaletteImage;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
graphics.PreferredBackBufferWidth = 800;
graphics.PreferredBackBufferHeight = 800;
}
protected override void Initialize()
{
base.Initialize();
}
protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
texture = Content.Load<Texture2D>("QuadDraw"); // <<< load any image of your own here that you have added thru the content pipeline tool.
textureAsGrayScale = ToGrayScaledPalettable(texture, false, 16);
textureAsPalette = ToGrayScaledPalettable(texture, true, 16);
textureAsPaletteImage = ToGrayScaledPaletteSet(texture, palColorArray);
}
protected override void UnloadContent()
{
textureAsGrayScale.Dispose();
textureAsPalette.Dispose();
textureAsPaletteImage.Dispose();
}
public static Color[] palColorArray = new Color[]
{
new Color(191, 191, 191),
new Color(000, 000, 000),
new Color(255, 255, 255),
new Color(255, 000, 000),
new Color(191, 000, 000),
new Color(255, 191, 191),
new Color(255, 255, 000),
new Color(191, 191, 000),
new Color(255, 255, 191),
new Color(000, 255, 000),
new Color(000, 191, 000),
new Color(191, 255, 191),
new Color(000, 255, 255),
new Color(000, 191, 191),
new Color(191, 255, 255),
new Color(000, 000, 255),
new Color(000, 000, 191),
new Color(191, 191, 255),
new Color(255, 000, 255),
new Color(191, 000, 191),
new Color(255, 191, 255)
};
public Texture2D ToGrayScaledPalettable(Texture2D original, bool makePaletted, int numberOfPalColors)
{
//make an empty bitmap the same size as original
Color[] colors = new Color[original.Width * original.Height];
original.GetData<Color>(colors);
Color[] destColors = new Color[original.Width * original.Height];
Texture2D newTexture = new Texture2D(GraphicsDevice, original.Width, original.Height);
for (int i = 0; i < original.Width; i++)
{
for (int j = 0; j < original.Height; j++)
{
//get the pixel from the original image
int index = i + j * original.Width;
Color originalColor = colors[index];
//create the grayscale version of the pixel
float maxval = .3f + .59f + .11f + .79f;
float grayScale = (((originalColor.R / 255f) * .3f) + ((originalColor.G / 255f) * .59f) + ((originalColor.B / 255f) * .11f) + ((originalColor.A / 255f) * .79f));
grayScale = grayScale / maxval;
if (makePaletted)
{
var val = (int)((grayScale -.0001f) * numberOfPalColors);
destColors[index] = new Color(val, val, val, 255);
}
else
{
destColors[index] = new Color(grayScale, grayScale, grayScale, 1f);
}
}
}
newTexture.SetData<Color>(destColors);
return newTexture;
}
public Texture2D ToGrayScaledPaletteSet(Texture2D original, Color[] palColorArray)
{
var numberOfPalColors = palColorArray.Length;
//make an empty bitmap the same size as original
Color[] colors = new Color[original.Width * original.Height];
original.GetData<Color>(colors);
Color[] destColors = new Color[original.Width * original.Height];
Texture2D newTexture = new Texture2D(GraphicsDevice, original.Width, original.Height);
for (int i = 0; i < original.Width; i++)
{
for (int j = 0; j < original.Height; j++)
{
//get the pixel from the original image
int index = i + j * original.Width;
Color originalColor = colors[index];
//create the grayscale version of the pixel
float maxval = .3f + .59f + .11f + .79f;
float grayScale = (((originalColor.R / 255f) * .3f) + ((originalColor.G / 255f) * .59f) + ((originalColor.B / 255f) * .11f) + ((originalColor.A / 255f) * .79f));
grayScale = grayScale / maxval;
var val = (int)((grayScale - .0001f) * numberOfPalColors);
destColors[index] = palColorArray[val];
}
}
newTexture.SetData<Color>(destColors);
return newTexture;
}
protected override void Update(GameTime gameTime)
{
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
Exit();
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin();
// the original texture
spriteBatch.Draw(texture, new Rectangle(20, 5, 400, 140), Color.White);
// the original texture in grayscale.
spriteBatch.Draw(textureAsGrayScale, new Rectangle(20, 150, 400, 140), Color.White);
// the original texture data compressed to palatted index relative values.
spriteBatch.Draw(textureAsPalette, new Rectangle(20, 300, 400, 140), Color.White);
// the original texture thru all the above steps and then recolored to the palette.
spriteBatch.Draw(textureAsPaletteImage, new Rectangle(20, 450, 400, 140), Color.White);
spriteBatch.End();
base.Draw(gameTime);
}
}
}
Here is the resulting output.
Loading your images into the content pipeline bmp i don’t think will work.
png jpeg will.
Steps 1 is valid below then use add “existing item” for step 2 browse to your images add them save the content build the content. Go back to game1 and load them as i have with the first (texture) in the above game1 project within the load method.
If you wish to change the palette texture on the shader at runtime.
Though you really shouldn’t have to unless this is to change dynamically and a lot at runtime.
You should make a pixel shader that takes the palatte array of colors. On load you should turn your textures into the 3rd image type and send them to spritebatch using your own shader when you draw them.
The below is not the best examples to start with that while not directly related it is related. You should be able to infer how to create and use a shader with spritebatch via study of the above and the related parts of the below.
There maybe better examples if you search.

