Pergunta

Background

I've made a tiny Android library for handling bitmaps using JNI (link here)

In the long past, I've made some code of Bilinear Interpolation as a possible algorithm for scaling of images. The algorithm is a bit complex and uses pixels around to form the target pixel.

The problem

Even though there are no errors (no compilation errors and no runtime errors), the output image look like this (scaled the width by x2) :

enter image description here

The code

Basically the original Java code used SWT and supported only RGB, but it's the same for the Alpha channel. It worked before just perfectly (though now that I look at it, it seems to create a lot of objects on the way) .

Here's the Java code:

/** 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))));
        }
      }
    }
  }

And here's the C/C++ code I've tried to make from it:

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;
    }

The question

What am I doing wrong?

Is it also possible to make the code a bit more readable? I'm a bit rusty on C/C++ and I was more of a C developer than a C++ developer.

EDIT: now it works fine. I've edited and fixed the code.

Only thing that you guys can help is giving tips about how to make it better.

Foi útil?

Solução

OK, it all started with a bad conversion of the colors, then it went to the usage of pointers , and then to the basic one of where to put the pixels.

The code I've written now works fine (added all of the needed fixes).

Soon you will all be able to use the new code on the Github project.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top