For my game that I'm developing I want to have my cards dynamically drawn so if there is any changes done to them it could be drawn on the card itself. I'm currently using a CPU method as I'm not sure if there is a way to do it via GPU as I'm new to drawing and textures.
What I have currently is that the cards are drawn to a RenderTarget at their originally designed size of 2100x1480 px. (I'll probably for game purposes have to scale that down to 1050x740 for its native size, but was designed that way to have 600 dpi for physical versions).
Anyway the way I want to do it is that after drawing everything to the RenderTarget I scale it down and store that scaled down texture to the card's texture (which will be 164 x 115 for the playing size, the 1050 x 740 is the zoomed in size that I want so players can view what the card does and stats). The issue that I'm facing is that the way I currently tried to do it was far too slow taking over 9 seconds to scale it down after drawing the original size to the RenderTarget.
The code that I'm currently using:
protected void DrawCardTest(decimal cardScale)
{
startTime = DateTime.Now;
// Set the device to the render target
graphics.GraphicsDevice.SetRenderTarget(cardRenderTarget);
graphics.GraphicsDevice.Clear(Color.Transparent);
spriteBatch.Begin();
Vector2 pos = Vector2.Zero;
spriteBatch.Draw(cardDrawables.Texture, pos, cardDrawables.SourceRectangle("Blank Card Front"), Color.White, 0.0F, pos, new Vector2(1.0F, 1.0F), SpriteEffects.None, 0.0F);
spriteBatch.End();
// Reset the device to the back buffer so can grab the texture from the rendertarget
graphics.GraphicsDevice.SetRenderTarget(null);
colorArrayForTexture = new Color[cardDrawables.SourceRectangle("Blank Card Front").Width * cardDrawables.SourceRectangle("Blank Card Front").Height];
cardRenderTarget.GetData(colorArrayForTexture);
cardTest = new Texture2D(graphics.GraphicsDevice, (int)(cardRenderTarget.Width * (decimal)cardScale), (int)(cardRenderTarget.Height * (decimal)cardScale));
Color[] arrayAfterScale = new Color[(int)(cardRenderTarget.Width * (decimal)cardScale) * (int)(cardRenderTarget.Height * (decimal)cardScale)];
cardTest.SetData(ScaleTexture(colorArrayForTexture, cardRenderTarget.Width, cardRenderTarget.Height, (decimal)cardScale));
endTime = DateTime.Now;
}
public Color[] ScaleTextureNew(Color[] textureData, int width, int height, decimal scaleAmount)
{
#region ScaleTexture Variables and Setup
decimal scaleX = scaleAmount;
decimal scaleY = scaleAmount;
int widthAfterScale = (int)(width * scaleX);
int heightAfterScale = (int)(height * scaleY);
//create a pixel array that will hold the scaled texture data
Color[] scaledTextureData = new Color[widthAfterScale * heightAfterScale];
ColorStorage colorToAverage = new ColorStorage();
decimal widthStep = (decimal)width / (decimal)widthAfterScale;
decimal heightStep = (decimal)height / (decimal)heightAfterScale;
decimal widthTotal = widthStep;
decimal heightTotal = heightStep;
//check to see if width sample total or height sample total will be repeating
if ((widthStep * widthAfterScale) != width) // this should equal out if not there is a repeating issue
widthTotal += repeatingFix;
if ((heightStep * heightAfterScale) != height) // this should equal out if not there is a repeating issue
heightTotal += repeatingFix;
decimal sampledWidth = 0.0M;
decimal sampledHeight = 0.0M;
decimal widthSampling = 0.0M;
decimal heightSampling = 0.0M;
decimal widthSamplingTotal = 0.0M;
decimal heightSamplingTotal = 0.0M;
decimal widthIncrement = 0.0M;
decimal heightIncrement = 0.0M;
decimal widthRemainder = 0.0M;
decimal heightRemainder = 0.0M;
decimal transparentPixelArea = 0.0M;
decimal areaSampled = 1.0M;
decimal totalAreaSampled = 0.0M;
decimal currentHeight = 0.0M;
decimal currentWidth = 0.0M;
decimal alpha = 0.0M;
if (widthStep > 1.0M)
widthIncrement = 1.0M;
else if (widthStep < 1.0M)
widthIncrement = widthStep;
if (heightStep > 1.0M)
heightIncrement = 1.0M;
else if (heightStep < 1.0M)
heightIncrement = heightStep;
Index widthIndexUnscaled = new Index(0);
Index heightIndexUnscaled = new Index(0);
Index widthIndexScaled = new Index(0);
Index heightIndexScaled = new Index(0);
Index widthIndexToUse;
Index heightIndexToUse;
int indexIntoArray = 0;
decimal[] samplingArrayWidth; // use the bigger between width and widthafterscale
if (width > widthAfterScale)
{
samplingArrayWidth = new decimal[width];
widthIndexToUse = widthIndexUnscaled;
}
else
{
samplingArrayWidth = new decimal[widthAfterScale];
widthIndexToUse = widthIndexScaled;
}
CalculateSampling(ref samplingArrayWidth, widthStep);
decimal[] samplingArrayHeight; // use the bigger between width and widthafterscale
if (height > heightAfterScale)
{
samplingArrayHeight = new decimal[height];
heightIndexToUse = heightIndexUnscaled;
}
else
{
samplingArrayHeight = new decimal[heightAfterScale];
heightIndexToUse = heightIndexScaled;
}
CalculateSampling(ref samplingArrayHeight, heightStep);
bool doneScaling = false;
#endregion
//will iterate top left to right bottom
while (!doneScaling)
{
while (sampledHeight < heightStep)
{
if (heightRemainder == 0.0M)
{
heightRemainder = heightStep - samplingArrayHeight[heightIndexToUse.Point];
heightSampling = heightStep - heightRemainder;
}
else if (heightRemainder >= 1.0M)
{
heightRemainder -= samplingArrayHeight[heightIndexToUse.Point];
heightSampling = 1.0M;
}
else
heightSampling = heightRemainder;
while (sampledWidth < widthStep)
{
if (widthRemainder == 0.0M)
{
widthRemainder = widthStep - samplingArrayWidth[widthIndexToUse.Point];
widthSampling = widthStep - widthRemainder;
}
else if (widthRemainder >= 1.0M)
{
widthRemainder -= samplingArrayWidth[widthIndexToUse.Point];
widthSampling = 1.0M;
}
else
widthSampling = widthRemainder;
areaSampled *= heightSampling * widthSampling;
// check to see if the pixel is blank to correctly sample
indexIntoArray = width * heightIndexUnscaled.Point + widthIndexUnscaled.Point; // so don't have to calculate index every time
if (textureData[indexIntoArray].A == 0)
transparentPixelArea += areaSampled;
alpha = textureData[indexIntoArray].A;
colorToAverage.Alpha += alpha * areaSampled;
alpha /= 255; //get the oppacity percentage to reverse the multiplyied alpha onto the colors
if (alpha == 0.0M)
alpha = 1.0M; // this is so colors don't get created where there were none.
// undivide the colors by alpha since its been done and needs to be corrected
colorToAverage.Red += textureData[indexIntoArray].R * areaSampled / alpha;// *alpha * areaSampled;
colorToAverage.Green += textureData[indexIntoArray].G * areaSampled / alpha;// *alpha * areaSampled;
colorToAverage.Blue += textureData[indexIntoArray].B * areaSampled / alpha;// *alpha * areaSampled;
//add the area sampled to total and reset area to sample
totalAreaSampled += areaSampled;
areaSampled = 1.0M;
sampledWidth += widthSampling;
widthSamplingTotal += widthSampling;
widthIndexUnscaled.Point = (int)widthSamplingTotal; // have to do this here since counter has to change to sample properly
}
widthSampling = 0.0M;
widthRemainder = 0.0M;
sampledWidth = 0.0M;
widthSamplingTotal = currentWidth;
widthIndexUnscaled.Point = (int)widthSamplingTotal; // have to do this here since counter has to change to sample properly
sampledHeight += heightSampling;
heightSamplingTotal += heightSampling;
heightIndexUnscaled.Point = (int)heightSamplingTotal; // have to do this here since counter has to change to sample properly
}
heightSampling = 0.0M;
heightRemainder = 0.0M;
sampledHeight = 0.0M;
//calculate the scaled texture's value here
scaledTextureData[heightIndexScaled.Point * widthAfterScale + widthIndexScaled.Point] = ColorAveraged(colorToAverage,
(totalAreaSampled - transparentPixelArea), totalAreaSampled);
//reset colorToAverage for next pass
colorToAverage.Alpha = 0.0M;
colorToAverage.Red = 0.0M;
colorToAverage.Blue = 0.0M;
colorToAverage.Green = 0.0M;
totalAreaSampled = 0.0M;
transparentPixelArea = 0.0M;
if (widthTotal >= width) // at the right of the texture
{
if (heightTotal >= height) // at the bottom of the texture
doneScaling = true;
else
{
//increment height stuff for next line of texture
currentHeight = heightTotal;
heightSamplingTotal = currentHeight;
heightIndexUnscaled.Point = (int)heightSamplingTotal; // have to do this here since counter has to change to sample properly
heightTotal += heightStep;
heightIndexScaled.Point++;
//reset width stuff for next line of texture
widthTotal = widthStep;
if ((widthStep * widthAfterScale) != width) // this should equal out if not there is a repeating issue
widthTotal += repeatingFix;
widthIndexScaled.Point = 0;
widthIndexUnscaled.Point = 0;
}
}
else
{
currentWidth = widthTotal;
widthSamplingTotal = currentWidth;
widthIndexUnscaled.Point = (int)widthSamplingTotal; // have to do this here since counter has to change to sample properly
widthTotal += widthStep;
widthIndexScaled.Point++;
heightSamplingTotal = currentHeight;
heightIndexUnscaled.Point = (int)heightSamplingTotal; // have to do this here since counter has to change to sample properly
}
}
decimal widthToBlur;
if (widthAfterScale > width) // only blur width if its been scaled up
widthToBlur = ((decimal)widthAfterScale / (decimal)width) / 2.0M;
else
widthToBlur = 0.0M;
decimal heightToBlur;
if (heightAfterScale > height) // only blur height if its been scaled up
heightToBlur = ((decimal)heightAfterScale / (decimal)height) / 2.0M;
else
heightToBlur = 0.0M;
//if ((height < heightAfterScale) || (width < widthAfterScale)) //texture was scaled up so need to blur it
// scaledTextureData = Blur(scaledTextureData, widthAfterScale, heightAfterScale,
// widthToBlur, heightToBlur);
return scaledTextureData;
}
}
What the function is supposed to do. Example1: if scaling down a 4x4 image to a 2x2 image each pixel for the 2x2 image will be a composition of a 2x2 pixel sampling area of the original image.
Example2: if scaling down a 66x66 image to a 10x10 image each pixel for the 10x10 image will be a composition of a 6.6x6.6 pixel sampling area of the original image.
EDIT: Not sure how to do code for this site so it shows up more readable. :S