Попытка конвертировать билинейный код интерполяции с Java на C/С++ на Android
Фон
Я создал крошечную библиотеку Android для обработки растровых изображений с использованием JNI (ссылка здесь)
В прошлом я сделал код билинейной интерполяции как возможный алгоритм масштабирования изображений. Алгоритм немного сложный и использует пиксели вокруг, чтобы сформировать целевой пиксель.
Проблема
Несмотря на то, что ошибок нет (нет ошибок компиляции и ошибок во время выполнения), выходное изображение выглядит так (масштабируется по x2):
Код
В основном исходный код Java использовал SWT и поддерживал только RGB, но он одинаковый для альфа-канала. Он работал до того, как просто отлично (хотя теперь, когда я смотрю на него, он создает много объектов на пути).
Здесь код Java:
/** class for resizing imageData using the Bilinear Interpolation method */
public class BilinearInterpolation
{
/** the method for resizing the imageData using the Bilinear Interpolation algorithm */
public static void resize(final ImageData inputImageData,final ImageData newImageData,final int oldWidth,final int oldHeight,final int newWidth,final int newHeight)
{
// position of the top left pixel of the 4 pixels to use interpolation on
int xTopLeft,yTopLeft;
int x,y,lastTopLefty;
final float xRatio=(float)newWidth/(float)oldWidth,yratio=(float)newHeight/(float)oldHeight;
// Y color ratio to use on left and right pixels for interpolation
float ycRatio2=0,ycRatio1=0;
// pixel target in the src
float xt,yt;
// X color ratio to use on left and right pixels for interpolation
float xcRatio2=0,xcratio1=0;
// copy data from source image to RGB values:
RGB rgbTopLeft,rgbTopRight,rgbBottomLeft=null,rgbBottomRight=null,rgbTopMiddle=null,rgbBottomMiddle=null;
RGB[][] startingImageData;
startingImageData=new RGB[oldWidth][oldHeight];
for(x=0;x<oldWidth;++x)
for(y=0;y<oldHeight;++y)
{
rgbTopLeft=inputImageData.palette.getRGB(inputImageData.getPixel(x,y));
startingImageData[x][y]=new RGB(rgbTopLeft.red,rgbTopLeft.green,rgbTopLeft.blue);
}
// do the resizing:
for(x=0;x<newWidth;x++)
{
xTopLeft=(int)(xt=x/xRatio);
// when meeting the most right edge, move left a little
if(xTopLeft>=oldWidth-1)
xTopLeft--;
if(xt<=xTopLeft+1)
{
// we are between the left and right pixel
xcratio1=xt-xTopLeft;
// color ratio in favor of the right pixel color
xcRatio2=1-xcratio1;
}
for(y=0,lastTopLefty=Integer.MIN_VALUE;y<newHeight;y++)
{
yTopLeft=(int)(yt=y/yratio);
// when meeting the most bottom edge, move up a little
if(yTopLeft>=oldHeight-1)
yTopLeft--;
// we went down only one rectangle
if(lastTopLefty==yTopLeft-1)
{
rgbTopLeft=rgbBottomLeft;
rgbTopRight=rgbBottomRight;
rgbTopMiddle=rgbBottomMiddle;
rgbBottomLeft=startingImageData[xTopLeft][yTopLeft+1];
rgbBottomRight=startingImageData[xTopLeft+1][yTopLeft+1];
rgbBottomMiddle=new RGB((int)(rgbBottomLeft.red*xcRatio2+rgbBottomRight.red*xcratio1),(int)(rgbBottomLeft.green*xcRatio2+rgbBottomRight.green*xcratio1),(int)(rgbBottomLeft.blue*xcRatio2+rgbBottomRight.blue*xcratio1));
}
else if(lastTopLefty!=yTopLeft)
{
// we went to a totally different rectangle (happens in every loop start,and might happen more when making the picture smaller)
rgbTopLeft=startingImageData[xTopLeft][yTopLeft];
rgbTopRight=startingImageData[xTopLeft+1][yTopLeft];
rgbTopMiddle=new RGB((int)(rgbTopLeft.red*xcRatio2+rgbTopRight.red*xcratio1),(int)(rgbTopLeft.green*xcRatio2+rgbTopRight.green*xcratio1),(int)(rgbTopLeft.blue*xcRatio2+rgbTopRight.blue*xcratio1));
rgbBottomLeft=startingImageData[xTopLeft][yTopLeft+1];
rgbBottomRight=startingImageData[xTopLeft+1][yTopLeft+1];
rgbBottomMiddle=new RGB((int)(rgbBottomLeft.red*xcRatio2+rgbBottomRight.red*xcratio1),(int)(rgbBottomLeft.green*xcRatio2+rgbBottomRight.green*xcratio1),(int)(rgbBottomLeft.blue*xcRatio2+rgbBottomRight.blue*xcratio1));
}
lastTopLefty=yTopLeft;
if(yt<=yTopLeft+1)
{
// color ratio in favor of the bottom pixel color
ycRatio1=yt-yTopLeft;
ycRatio2=1-ycRatio1;
}
// prepared all pixels to look at, so finally set the new pixel data
newImageData.setPixel(x,y,inputImageData.palette.getPixel(new RGB((int)(rgbTopMiddle.red*ycRatio2+rgbBottomMiddle.red*ycRatio1),(int)(rgbTopMiddle.green*ycRatio2+rgbBottomMiddle.green*ycRatio1),(int)(rgbTopMiddle.blue*ycRatio2+rgbBottomMiddle.blue*ycRatio1))));
}
}
}
}
И вот код C/С++, который я пытался сделать из него:
typedef struct
{
uint8_t alpha, red, green, blue;
} ARGB;
int32_t convertArgbToInt(ARGB argb)
{
return (argb.alpha) | (argb.red << 16) | (argb.green << 8)
| (argb.blue << 24);
}
void convertIntToArgb(uint32_t pixel, ARGB* argb)
{
argb->red = ((pixel >> 24) & 0xff);
argb->green = ((pixel >> 16) & 0xff);
argb->blue = ((pixel >> 8) & 0xff);
argb->alpha = (pixel & 0xff);
}
...
/**scales the image using a high-quality algorithm called "Bilinear Interpolation" */ //
JNIEXPORT void JNICALL Java_com_jni_bitmap_1operations_JniBitmapHolder_jniScaleBIBitmap(
JNIEnv * env, jobject obj, jobject handle, uint32_t newWidth,
uint32_t newHeight)
{
JniBitmap* jniBitmap = (JniBitmap*) env->GetDirectBufferAddress(handle);
if (jniBitmap->_storedBitmapPixels == NULL)
return;
uint32_t oldWidth = jniBitmap->_bitmapInfo.width;
uint32_t oldHeight = jniBitmap->_bitmapInfo.height;
uint32_t* previousData = jniBitmap->_storedBitmapPixels;
uint32_t* newBitmapPixels = new uint32_t[newWidth * newHeight];
// position of the top left pixel of the 4 pixels to use interpolation on
int xTopLeft, yTopLeft;
int x, y, lastTopLefty;
float xRatio = (float) newWidth / (float) oldWidth, yratio =
(float) newHeight / (float) oldHeight;
// Y color ratio to use on left and right pixels for interpolation
float ycRatio2 = 0, ycRatio1 = 0;
// pixel target in the src
float xt, yt;
// X color ratio to use on left and right pixels for interpolation
float xcRatio2 = 0, xcratio1 = 0;
ARGB rgbTopLeft, rgbTopRight, rgbBottomLeft, rgbBottomRight, rgbTopMiddle,
rgbBottomMiddle, result;
for (x = 0; x < newWidth; ++x)
{
xTopLeft = (int) (xt = x / xRatio);
// when meeting the most right edge, move left a little
if (xTopLeft >= oldWidth - 1)
xTopLeft--;
if (xt <= xTopLeft + 1)
{
// we are between the left and right pixel
xcratio1 = xt - xTopLeft;
// color ratio in favor of the right pixel color
xcRatio2 = 1 - xcratio1;
}
for (y = 0, lastTopLefty = -30000; y < newHeight; ++y)
{
yTopLeft = (int) (yt = y / yratio);
// when meeting the most bottom edge, move up a little
if (yTopLeft >= oldHeight - 1)
--yTopLeft;
if (lastTopLefty == yTopLeft - 1)
{
// we went down only one rectangle
rgbTopLeft = rgbBottomLeft;
rgbTopRight = rgbBottomRight;
rgbTopMiddle = rgbBottomMiddle;
//rgbBottomLeft=startingImageData[xTopLeft][yTopLeft+1];
convertIntToArgb(
previousData[((yTopLeft + 1) * oldWidth) + xTopLeft],
&rgbBottomLeft);
//rgbBottomRight=startingImageData[xTopLeft+1][yTopLeft+1];
convertIntToArgb(
previousData[((yTopLeft + 1) * oldWidth)
+ (xTopLeft + 1)], &rgbBottomRight);
rgbBottomMiddle.alpha = rgbBottomLeft.alpha * xcRatio2
+ rgbBottomRight.alpha * xcratio1;
rgbBottomMiddle.red = rgbBottomLeft.red * xcRatio2
+ rgbBottomRight.red * xcratio1;
rgbBottomMiddle.green = rgbBottomLeft.green * xcRatio2
+ rgbBottomRight.green * xcratio1;
rgbBottomMiddle.blue = rgbBottomLeft.blue * xcRatio2
+ rgbBottomRight.blue * xcratio1;
}
else if (lastTopLefty != yTopLeft)
{
// we went to a totally different rectangle (happens in every loop start,and might happen more when making the picture smaller)
//rgbTopLeft=startingImageData[xTopLeft][yTopLeft];
convertIntToArgb(previousData[(yTopLeft * oldWidth) + xTopLeft],
&rgbTopLeft);
//rgbTopRight=startingImageData[xTopLeft+1][yTopLeft];
convertIntToArgb(
previousData[((yTopLeft + 1) * oldWidth) + xTopLeft],
&rgbTopRight);
rgbTopMiddle.alpha = rgbTopLeft.alpha * xcRatio2
+ rgbTopRight.alpha * xcratio1;
rgbTopMiddle.red = rgbTopLeft.red * xcRatio2
+ rgbTopRight.red * xcratio1;
rgbTopMiddle.green = rgbTopLeft.green * xcRatio2
+ rgbTopRight.green * xcratio1;
rgbTopMiddle.blue = rgbTopLeft.blue * xcRatio2
+ rgbTopRight.blue * xcratio1;
//rgbBottomLeft=startingImageData[xTopLeft][yTopLeft+1];
convertIntToArgb(
previousData[((yTopLeft + 1) * oldWidth) + xTopLeft],
&rgbBottomLeft);
//rgbBottomRight=startingImageData[xTopLeft+1][yTopLeft+1];
convertIntToArgb(
previousData[((yTopLeft + 1) * oldWidth)
+ (xTopLeft + 1)], &rgbBottomRight);
rgbBottomMiddle.alpha = rgbBottomLeft.alpha * xcRatio2
+ rgbBottomRight.alpha * xcratio1;
rgbBottomMiddle.red = rgbBottomLeft.red * xcRatio2
+ rgbBottomRight.red * xcratio1;
rgbBottomMiddle.green = rgbBottomLeft.green * xcRatio2
+ rgbBottomRight.green * xcratio1;
rgbBottomMiddle.blue = rgbBottomLeft.blue * xcRatio2
+ rgbBottomRight.blue * xcratio1;
}
lastTopLefty = yTopLeft;
if (yt <= yTopLeft + 1)
{
// color ratio in favor of the bottom pixel color
ycRatio1 = yt - yTopLeft;
ycRatio2 = 1 - ycRatio1;
}
// prepared all pixels to look at, so finally set the new pixel data
result.alpha = rgbTopMiddle.alpha * ycRatio2
+ rgbBottomMiddle.alpha * ycRatio1;
result.blue = rgbTopMiddle.blue * ycRatio2
+ rgbBottomMiddle.blue * ycRatio1;
result.red = rgbTopMiddle.red * ycRatio2
+ rgbBottomMiddle.red * ycRatio1;
result.green = rgbTopMiddle.green * ycRatio2
+ rgbBottomMiddle.green * ycRatio1;
newBitmapPixels[(y * newWidth) + x] = convertArgbToInt(result);
}
}
//get rid of old data, and replace it with new one
delete[] previousData;
jniBitmap->_storedBitmapPixels = newBitmapPixels;
jniBitmap->_bitmapInfo.width = newWidth;
jniBitmap->_bitmapInfo.height = newHeight;
}
Вопрос
Что я делаю неправильно?
Возможно ли сделать код более читаемым? Я немного ржавый на C/С++, и я был больше разработчиком C, чем разработчиком С++.
EDIT: теперь он работает нормально. Я отредактировал и исправил код.
Единственное, что вы, ребята, можете помочь, это дать советы о том, как сделать это лучше.
Ответы
Ответ 1
ОК, все началось с плохого преобразования цветов, затем оно перешло к использованию указателей, а затем к основному из которых можно было разместить пиксели.
Код, который я написал сейчас, отлично работает (добавлены все необходимые исправления).
Вскоре вы сможете использовать новый код в проекте Github.