Reputation: 1528
I have hit a brick wall with this... I know my flaw is in the logic somehow but I'll explain where I'm at.
I'm (trying) to write code to dynamically create a square (75x75) pixel thumbnail for a navigation bar along the bottom of my Silverlight app. When I debug my code below I keep getting an "Array index out of bounds" error during the scaling nested loops (I've only debugged with srcWidth > srcHeight, one step at a time)
The source image I'm testing with is 307x162 (49734 pixels) The dest size (per current logic) for this is 150x75 (11250 pixels) Of course after this then I intend to crop the result down to 75x75
It's only getting about half through the source image before hitting 11251 as the index of the destination. I know it's my logic, I just don't know if I went wrong in the sizes I used to construct the destination, or in the approximations used from casting the floats to ints, or maybe it's neither
I don't know, which is why I'm posting here... anyhow, here's my source:
private void ResizeNavBarImage(Image src)
{
const int SizeOfRGB = 4;
WriteableBitmap bmpSource = new WriteableBitmap((BitmapSource)src.Source);
int[] iSourcePixels = bmpSource.Pixels;
int srcWidth = bmpSource.PixelWidth;
int srcHeight = bmpSource.PixelHeight;
int destWidth = 75;
int destHeight = 75;
float xFactor = srcWidth / destWidth;
float yFactor = srcHeight / destHeight;
float xSource, ySource;
int iApprox;
int iIndex = 0;
if (srcWidth > srcHeight)
{
WriteableBitmap bmpDest = new WriteableBitmap((int)(destWidth * yFactor), destHeight);
int[] iDestPixels = bmpDest.Pixels;
// Resize srcHeight to destHeight, srcWidth to same ratio
for (int i = 0; i < srcHeight; i++)
{
for (int j = 0; j < srcWidth; j++)
{
xSource = j * yFactor;
ySource = i * yFactor;
iApprox = (int)(ySource * srcWidth + xSource);
iDestPixels[iIndex++] = iSourcePixels[iApprox];
}
}
// Crop half of difference from each side of the width
srcWidth = bmpDest.PixelWidth;
srcHeight = bmpDest.PixelHeight;
int xLeftOffset = (srcWidth - srcHeight) / 2;
WriteableBitmap bmpFinalDest = new WriteableBitmap(destWidth, destHeight);
for (int iPixelRow = 0; iPixelRow < destHeight; iPixelRow++)
{
int srcOffset = (iPixelRow * srcWidth + xLeftOffset) * SizeOfRGB;
int destOffset = iPixelRow * destWidth * SizeOfRGB;
Buffer.BlockCopy(bmpDest.Pixels, srcOffset, bmpFinalDest.Pixels, destOffset, destWidth * SizeOfRGB);
}
src.Source = (ImageSource)bmpFinalDest;
}
else if (srcWidth < srcHeight)
{
WriteableBitmap bmpDest = new WriteableBitmap(destWidth, (int)(destHeight * xFactor));
int[] iDestPixels = bmpDest.Pixels;
// Resize srcWidth to destWidth, srcHeight to same ratio
for (int i = 0; i < srcHeight; i++)
{
for (int j = 0; j < srcWidth; j++)
{
xSource = j * xFactor;
ySource = i * xFactor;
iApprox = (int)(ySource * srcWidth + xSource);
iDestPixels[iIndex++] = iSourcePixels[iApprox];
}
}
// Crop half of difference from each side of the height
srcWidth = bmpDest.PixelWidth;
srcHeight = bmpDest.PixelHeight;
int yTopOffset = (srcHeight - srcWidth) / 2;
WriteableBitmap bmpFinalDest = new WriteableBitmap(destWidth, destHeight);
for (int iPixelRow = yTopOffset; iPixelRow < (destHeight - (yTopOffset * 2)); iPixelRow++)
{
int srcOffset = iPixelRow * srcWidth * SizeOfRGB;
int destOffset = iPixelRow * destWidth * SizeOfRGB;
Buffer.BlockCopy(bmpDest.Pixels, srcOffset, bmpFinalDest.Pixels, destOffset, destWidth * SizeOfRGB);
}
src.Source = (ImageSource)bmpFinalDest;
}
else // (srcWidth == srcHeight)
{
WriteableBitmap bmpDest = new WriteableBitmap(destWidth, destHeight);
int[] iDestPixels = bmpDest.Pixels;
// Resize and set source
for (var i = 0; i < srcHeight; i++)
{
for (var j = 0; j < srcWidth; j++)
{
xSource = j * xFactor;
ySource = i * yFactor;
iApprox = (int)(ySource * srcWidth + xSource);
iDestPixels[iIndex++] = iSourcePixels[iApprox];
}
}
src.Source = (ImageSource)bmpDest;
}
}
===============================================================================
Here's my working code (with WriteableBitmapEx) for posterity:
private void ResizeNavBarImage(Image src)
{
WriteableBitmap bmpSource = new WriteableBitmap((BitmapSource)src.Source);
int srcWidth = bmpSource.PixelWidth;
int srcHeight = bmpSource.PixelHeight;
int finalDestWidth = 75;
int finalDestHeight = 75;
// Resize
float xFactor = ((float)finalDestWidth / (float)srcWidth);
float yFactor = ((float)finalDestHeight / (float)srcHeight);
float Factor = 0;
if (xFactor < yFactor)
Factor = yFactor;
else
Factor = xFactor;
int destWidth = (int)(srcWidth * Factor);
int destHeight = (int)(srcHeight * Factor);
if (destWidth < destHeight && destWidth != finalDestWidth)
destWidth = finalDestWidth;
else if (destWidth > destHeight && destHeight != finalDestHeight)
destHeight = finalDestHeight;
WriteableBitmap bmpDest = bmpSource.Resize(destWidth, destHeight, WriteableBitmapExtensions.Interpolation.Bilinear);
// Crop
int Offset;
WriteableBitmap bmpFinalDest = new WriteableBitmap(finalDestWidth, finalDestHeight);
if (destWidth > destHeight)
{
Offset = (bmpDest.PixelWidth - bmpDest.PixelHeight) / 2;
if (finalDestWidth % 2 != 0 && Offset % 2 == 0)
Offset -= 1;
bmpFinalDest = bmpDest.Crop(Offset, 0, finalDestWidth, finalDestHeight);
}
else if (destWidth < destHeight)
{
Offset = (bmpDest.PixelHeight - bmpDest.PixelWidth) / 2;
if (finalDestHeight % 2 != 0 && Offset % 2 == 0)
Offset -= 1;
bmpFinalDest = bmpDest.Crop(0, Offset, finalDestWidth, finalDestHeight);
}
else
bmpFinalDest = bmpDest;
src.Source = (ImageSource)bmpFinalDest;
}
Upvotes: 0
Views: 3038
Reputation: 1062
Why not use the built-in scale functionality of WriteableBitmap?
WriteableBitmap wb = new WriteableBitmap(src, new ScaleTransform() { ScaleX = 0.25, ScaleY = 0.25 });
wb.Invalidate();
src.Source = wb;
Upvotes: 0
Reputation: 10906
I agree with Mark Ransom, the correct way to resize the image would be to draw into another image using a Graphics object.
http://www.switchonthecode.com/tutorials/csharp-tutorial-image-editing-saving-cropping-and-resizing
This will allow you to use different, better filters to get good image quality, not to mention speed.
Upvotes: 0
Reputation: 308402
Your loops for i
and j
are going to srcHeight
and srcWidth
instead of destHeight
and destWidth
.
That's probably not the last bug in this code either.
Upvotes: 1