问题
I load my textures using
Texture2D.FromFile()
then draw them using
spriteBatch.Draw()
But here's the point: I want to change some colors of the image to another ones. So my questions:
How to change single color of the image to another single color (eg. blue to red).
In fact, what I really want to do is changing group of colors to another group of colors. For example red and similar hues to red to blue and similar hues to blue. You can do this for example in Corel PHOTO-PAINT ("Replace Color").
Please have in mind, that I'm a beginner in XNA. Best regards, Jack
EDIT:
Thank you very much for help, guys. Callum's answer is very helpful indeed. But I'm wondering is there a built-in function to solve my second problem, because writing your own may be time-consuming. And I think, that kind of function may be very useful. Something like:
color.SetNewColor(Color color_from, Color color_to, int range)
That kind of function, as I've said before, is built in Corel PHOTO-PAINT. To explain it better, here is the example of what I'm talking about:
link text
So, I only set color_from, color_to and range. I think it works like that: it checks every color of the image, if it is in range of color_from, it is changed to adequate color in hue of color_to.
回答1:
I assume you mean change individual pixels? In that case use the GetData()
and SetData()
methods of the Texture2D
class.
For example, you can get an array containing the colours of the individual pixels by doing this:
// Assume you have a Texture2D called texture
Color[] data = new Color[texture.Width * texture.Height];
texutre.GetData(data);
// You now have a packed array of Colors.
// So, change the 3rd pixel from the right which is the 4th pixel from the top do:
data[4*texture.Width+3] = Color.Red;
// Once you have finished changing data, set it back to the texture:
texture.SetData(data);
Note you can use the other overloads of GetData()
to select only a section.
So, to replace each pixel of a specified colour to another colour:
// Assume you have a Texture2D called texture, Colors called colorFrom, colorTo
Color[] data = new Color[texture.Width * texture.Height];
texutre.GetData(data);
for(int i = 0; i < data.Length; i++)
if(data[i] == colorFrom)
data[i] = colorTo;
texture.SetData(data);
To see if hues are similar, try this method:
private bool IsSimilar(Color original, Color test, int redDelta, int blueDelta, int greenDelta)
{
return Math.Abs(original.R - test.R) < redDelta && Math.Abs(original.G - test.G) < greenDelta && Math.Abs(original.B - test.B) < blueDelta;
}
where *delta is the tolerance of change for each colour channel that you want to accept.
To answer your edit, no there is a built in function, but you can just use a mixture of ideas from the two sections above:
Color[] data = new Color[texture.Width * texture.Height];
texutre.GetData(data);
for(int i = 0; i < data.Length; i++)
if(IsSimilar(data[i], colorFrom, range, range, range))
data[i] = colorTo;
texture.SetData(data);
回答2:
Moving data between the GPU and CPU by using GetData and SetData is an expensive operation. If there are a limited number of colors, you could use a pixel shader effect when rendering to the screen. You can pass an effect to SpriteBatch.Begin:
sampler2D input : register(s0);
/// <summary>The color used to tint the input.</summary>
/// <defaultValue>White</defaultValue>
float4 FromColor : register(C0);
/// <summary>The color used to tint the input.</summary>
/// <defaultValue>Red</defaultValue>
float4 ToColor : register(C1);
/// <summary>Explain the purpose of this variable.</summary>
/// <minValue>05/minValue>
/// <maxValue>10</maxValue>
/// <defaultValue>3.5</defaultValue>
float4 main(float2 uv : TEXCOORD) : COLOR
{
float4 Color;
Color= tex2D(input , uv.xy);
if (Color.r == FromColor.r && Color.g == FromColor.g && Color.b == FromColor.b)
return ToColor;
return Color;
}
technique Technique1
{
pass Pass1
{
PixelShader = compile ps_2_0 main();
}
}
Create your effect in your LoadContent method:
colorSwapEffect = Content.Load<Effect>(@"Effects\ColorSwap");
colorSwapEffect.Parameters["FromColor"].SetValue(Color.White);
colorSwapEffect.Parameters["ToColor"].SetValue(Color.Red);
And pass the effect to your call to SpriteBatch.Begin():
sprite.Begin(0, BlendState.Opaque, SamplerState.PointWrap,
DepthStencilState.Default, RasterizerState.CullNone, colorSwapEffect);
For what you really want to do, you can swap the red and blue channels even more easily. Change your pixel shader's main() function to this, which swaps b (blue) and r (red):
float4 main(float2 uv : TEXCOORD) : COLOR
{
float4 Color;
Color= tex2D(input , uv.xy);
return float4(Color.b, Color.g, Color.r, Color.a);
}
回答3:
Callum's solution is powerful and flexible.
A more limited solution that is slightly easier to implement is to leverage the spriteBatch color parameter.
The variables
Texture2D sprite; //Assuming you have loaded this somewhere
Color color = Color.Red; //The color you want to use
Vector2 position = new Vector2(0f, 0f); //the position to draw the sprite
The drawing code
//Start the spriteBatch segment, enable alpha blending for transparency
spriteBatch.Begin(SpriteBlendMode.AlphaBlend);
//Draw our sprite at the specified position using a specified color
spriteBatch.Draw(sprite, position, color);
//end the spritebatch
spriteBatch.End();
If your sprite is all white, then using this method will turn your sprite red. Also, make sure you are using a file format with transparency in it, PNG is a favorite.
回答4:
Callum hit it on the head if you are changing the color of 2D images as it seems you are - but as you can see you actually need to determine the actual pixel you want to modify and edit it rather than "replace yellow with green" for example.
The same logic could be used to do this replacement (simply loop through the pixels of the image and check the color - I can say that be wary when editing textures like this though as they seemed to cause some pretty serious spikes in performance depending on what was done and how often. I didn't fully investigate but I think it was causing quite a bit of garbage collection.
回答5:
this works for me:
protected override void Initialize()
{
sprite = Content.Load<Texture2D>("Parado");
Color[] data = new Color[sprite.Width * sprite.Height];
sprite.GetData(data);
// new color
Color novaCor =Color.Blue;
for (int i = 0; i < data.Length; i++)
{
// cor roxa no desenho
if (data[i].R == 142
&& data[i].G == 24
&& data[i].B == 115)
{
data[i] = novaCor;
}
}
sprite.SetData<Color>(data);
posicaoNinja = new Vector2(0, 200);
base.Initialize();
}
来源:https://stackoverflow.com/questions/3255311/color-replacement-in-xna-c-sharp