c++resizedib

Quick code to resize DIB image and maintain good img quality


There is many algorithms to do image resizing - lancorz, bicubic, bilinear, e.g. But most of them are pretty complex and therefore consume too much CPU.

What I need is fast relatively simple C++ code to resize images with acceptable quality.

Here is an example of what I'm currently doing:

for (int y = 0; y < height; y ++)
{
    int         srcY1Coord = int((double)(y * srcHeight) / height);
    int         srcY2Coord = min(srcHeight - 1, max(srcY1Coord, int((double)((y + 1) * srcHeight) / height) - 1));

    for (int x = 0; x < width; x ++)
    {
        int     srcX1Coord = int((double)(x * srcWidth) / width);
        int     srcX2Coord = min(srcWidth - 1, max(srcX1Coord, int((double)((x + 1) * srcWidth) / width) - 1));
        int     srcPixelsCount = (srcX2Coord - srcX1Coord + 1) * (srcY2Coord - srcY1Coord + 1);
        RGB32       color32;
        UINT32      r(0), g(0), b(0), a(0);

        for (int xSrc = srcX1Coord; xSrc <= srcX2Coord; xSrc ++)
            for (int ySrc = srcY1Coord; ySrc <= srcY2Coord; ySrc ++)
            {
                RGB32   curSrcColor32 = pSrcDIB->GetDIBPixel(xSrc, ySrc);
                r += curSrcColor32.r; g += curSrcColor32.g; b += curSrcColor32.b; a += curSrcColor32.alpha;
            }

            color32.r = BYTE(r / srcPixelsCount); color32.g = BYTE(g / srcPixelsCount); color32.b = BYTE(b / srcPixelsCount); color32.alpha = BYTE(a / srcPixelsCount);

            SetDIBPixel(x, y, color32);
    }
}

The code above is fast enough, but the quality is not ok on scaling pictures up.

Therefore, possibly someone already has fast and good C++ code sample for scaling DIBs?

Note: I was using StretchDIBits before - it was super-slow when was needed to downsize 10000x10000 picture down to 100x100 size, my code is much, much faster, I just want to have a bit higher quality

P.S. I'm using my own SetPixel/GetPixel functions, to work directly with data array and fast, that's not device context!


Solution

  • Allright, here is the answer, had to do it myself... It works perfectly well for scaling pictures up (for scaling down my initial code works perfectly well too). Hope someone will find a good use for it, it's fast enough and produced very good picture quality.

    for (int y = 0; y < height; y ++)
    {
        double      srcY1Coord = (y * srcHeight) / (double)height;
        int         srcY1CoordInt = (int)(srcY1Coord);
        double      srcY2Coord = ((y + 1) * srcHeight) / (double)height - 0.00000000001;
        int         srcY2CoordInt = min(maxSrcYcoord, (int)(srcY2Coord));
        double      yMultiplierForFirstCoord = (0.5 * (1 - (srcY1Coord - srcY1CoordInt)));
        double      yMultiplierForLastCoord = (0.5 * (srcY2Coord - srcY2CoordInt));
    
        for (int x = 0; x < width; x ++)
        {
            double  srcX1Coord = (x * srcWidth) / (double)width;
            int     srcX1CoordInt = (int)(srcX1Coord);
            double  srcX2Coord = ((x + 1) * srcWidth) / (double)width - 0.00000000001;
            int     srcX2CoordInt = min(maxSrcXcoord, (int)(srcX2Coord));
            RGB32   color32;
    
            ASSERT(srcX1Coord < srcWidth && srcY1Coord < srcHeight);
            double  r(0), g(0), b(0), a(0), multiplier(0);
    
            for (int xSrc = srcX1CoordInt; xSrc <= srcX2CoordInt; xSrc ++)
                for (int ySrc = srcY1CoordInt; ySrc <= srcY2CoordInt; ySrc ++)
                {
                    RGB32   curSrcColor32 = pSrcDIB->GetDIBPixel(xSrc, ySrc);
                    double  xMultiplier = xSrc < srcX1Coord ? (0.5 * (1 - (srcX1Coord - srcX1CoordInt))) : (xSrc >= srcX2Coord ? (0.5 * (srcX2Coord - srcX2CoordInt)) : 0.5);
                    double  yMultiplier = ySrc < srcY1Coord ? yMultiplierForFirstCoord : (ySrc >= srcY2Coord ? yMultiplierForLastCoord : 0.5);
                    double  curPixelMultiplier = xMultiplier + yMultiplier;
    
                    if (curPixelMultiplier > 0)
                    {
                        r += (curSrcColor32.r * curPixelMultiplier); g += (curSrcColor32.g * curPixelMultiplier); b += (curSrcColor32.b * curPixelMultiplier); a += (curSrcColor32.alpha * curPixelMultiplier);
                        multiplier += curPixelMultiplier;
                    }
                }
                color32.r = BYTE(r / multiplier); color32.g = BYTE(g / multiplier); color32.b = BYTE(b / multiplier); color32.alpha = BYTE(a / multiplier);
    
                SetDIBPixel(x, y, color32);
        }
    }
    

    P.S. Please don't ask why I’m not using StretchDIBits - leave comments for these who understand that not always system api is available or acceptable.