Question

I'm using the following code to scale jpeg, I've got ZoomOutJpeg() function from a example on internet.

//JpegLib Error Handing - Begin
struct my_error_mgr
{
    struct jpeg_error_mgr pub;  /* "public" fields */

    jmp_buf setjmp_buffer;  /* for return to caller */
};

typedef struct my_error_mgr * my_error_ptr;

/*
* Here's the routine that will replace the standard error_exit method:
*/

METHODDEF(void)
my_error_exit(j_common_ptr cinfo)
{
    /* cinfo->err really points to a my_error_mgr struct, so coerce pointer */
    my_error_ptr myerr = (my_error_ptr)cinfo->err;

    /* Always display the message. */
    /* We could postpone this until after returning, if we chose. */
    (*cinfo->err->output_message) (cinfo);

    /* Return control to the setjmp point */
    longjmp(myerr->setjmp_buffer, 1);
}
//JpegLib Error Handing - End

/*
* zoom out picture. the factor should between 0.5 and 1.0. It can be 0.5, but can not be 1.0.
*/
bool ZoomOutJpeg(UINT8 *inBuff, UINT8 *outBuff, UINT32 inSize, UINT32* _outSize, float factor)
{   
    //if (factor > 1 || factor < 0.5f)
    //  return false;

    // init decompress struct
    struct jpeg_decompress_struct in;
    JSAMPROW inRowPointer[1];
    // init compress struct
    struct jpeg_compress_struct out;    
    JSAMPROW outRowPointer[1];
    struct my_error_mgr jerrIn;
    struct my_error_mgr jerrOut;

    /* We set up the normal JPEG error routines, then override error_exit. */
    in.err = jpeg_std_error(&jerrIn.pub);   
    jerrIn.pub.error_exit = my_error_exit;
    /* Establish the setjmp return context for my_error_exit to use. */
    if (setjmp(jerrIn.setjmp_buffer))
    {
        jpeg_destroy_decompress(&in);
        return false;
    }
    jpeg_create_decompress(&in);
    jpeg_mem_src(&in, inBuff, inSize);
    jpeg_read_header(&in, TRUE);
    jpeg_start_decompress(&in);

    out.err = jpeg_std_error(&jerrOut.pub);
    jerrOut.pub.error_exit = my_error_exit;
    /* Establish the setjmp return context for my_error_exit to use. */
    if (setjmp(jerrOut.setjmp_buffer))
    {
        jpeg_destroy_decompress(&in);
        jpeg_destroy_compress(&out);
        return false;
    }
    jpeg_create_compress(&out);
    jpeg_mem_dest(&out, &outBuff, (unsigned long*)_outSize);

    int width = in.output_width;
    int height = in.output_height;
    int bytesPerPixel = in.num_components;

    int destWidth = (int)(width * factor);
    int destHeight = (int)(height * factor);
    _outFrameSize->resX = destWidth;
    _outFrameSize->resY = destHeight;

    out.image_width = destWidth;
    out.image_height = destHeight;
    out.input_components = bytesPerPixel;
    out.in_color_space = JCS_RGB;

    jpeg_set_defaults(&out);
    jpeg_start_compress(&out, TRUE);

    // Process RGB data.
    int outRowStride = destWidth * bytesPerPixel;
    int inRowStride = width * bytesPerPixel;
    outRowPointer[0] = (unsigned char *)malloc(outRowStride);
    inRowPointer[0] = (unsigned char *)malloc(inRowStride);

    JSAMPROW baseInRowPointer[1];
    baseInRowPointer[0] = (unsigned char *)malloc(inRowStride);

    unsigned char bUpLeft, bUpRight, bDownLeft, bDownRight;
    unsigned char gUpLeft, gUpRight, gDownLeft, gDownRight;
    unsigned char rUpLeft, rUpRight, rDownLeft, rDownRight;
    unsigned char b, g, r;

    float fX, fY;
    int iX, iY;
    int i, j;

    int currentBaseLocation = -1;
    int count = 0;

    // Process the first line.
    jpeg_read_scanlines(&in, inRowPointer, 1);
    for (j = 0; j < destWidth; j++)
    {
        fX = ((float)j) / factor;
        iX = (int)fX;

        bUpLeft = inRowPointer[0][iX * 3 + 0];
        bUpRight = inRowPointer[0][(iX + 1) * 3 + 0];

        gUpLeft = inRowPointer[0][iX * 3 + 1];
        gUpRight = inRowPointer[0][(iX + 1) * 3 + 1];

        rUpLeft = inRowPointer[0][iX * 3 + 2];
        rUpRight = inRowPointer[0][(iX + 1) * 3 + 2];

        b = bUpLeft * (iX + 1 - fX) + bUpRight * (fX - iX);
        g = gUpLeft * (iX + 1 - fX) + gUpRight * (fX - iX);
        r = rUpLeft * (iX + 1 - fX) + rUpRight * (fX - iX);

        outRowPointer[0][j * 3 + 0] = b;
        outRowPointer[0][j * 3 + 1] = g;
        outRowPointer[0][j * 3 + 2] = r;
    }
    jpeg_write_scanlines(&out, outRowPointer, 1);

    currentBaseLocation = 0;

    //Process the other lines between the first and last.
    for (i = 1; i < destHeight - 1; i++)
    {
        fY = ((float)i) / factor;
        iY = (int)fY;

        if (iY == currentBaseLocation)
        {
            in.output_scanline = iY;
            SwapJsampRow(inRowPointer[0], baseInRowPointer[0]);
            jpeg_read_scanlines(&in, baseInRowPointer, 1);
        }
        else
        {
            in.output_scanline = iY - 1;
            jpeg_read_scanlines(&in, inRowPointer, 1);
            jpeg_read_scanlines(&in, baseInRowPointer, 1);
        }

        currentBaseLocation = iY + 1;

        for (j = 0; j < destWidth; j++)
        {
            fX = ((float)j) / factor;
            iX = (int)fX;

            bUpLeft = inRowPointer[0][iX * 3 + 0];
            bUpRight = inRowPointer[0][(iX + 1) * 3 + 0];
            bDownLeft = baseInRowPointer[0][iX * 3 + 0];
            bDownRight = baseInRowPointer[0][(iX + 1) * 3 + 0];

            gUpLeft = inRowPointer[0][iX * 3 + 1];
            gUpRight = inRowPointer[0][(iX + 1) * 3 + 1];
            gDownLeft = baseInRowPointer[0][iX * 3 + 1];
            gDownRight = baseInRowPointer[0][(iX + 1) * 3 + 1];

            rUpLeft = inRowPointer[0][iX * 3 + 2];
            rUpRight = inRowPointer[0][(iX + 1) * 3 + 2];
            rDownLeft = baseInRowPointer[0][iX * 3 + 2];
            rDownRight = baseInRowPointer[0][(iX + 1) * 3 + 2];

            b = bUpLeft * (iX + 1 - fX) * (iY + 1 - fY) + bUpRight * (fX - iX) * (iY + 1 - fY) + bDownLeft * (iX + 1 - fX) * (fY - iY) + bDownRight * (fX - iX) * (fY - iY);
            g = gUpLeft * (iX + 1 - fX) * (iY + 1 - fY) + gUpRight * (fX - iX) * (iY + 1 - fY) + gDownLeft * (iX + 1 - fX) * (fY - iY) + gDownRight * (fX - iX) * (fY - iY);
            r = rUpLeft * (iX + 1 - fX) * (iY + 1 - fY) + rUpRight * (fX - iX) * (iY + 1 - fY) + rDownLeft * (iX + 1 - fX) * (fY - iY) + rDownRight * (fX - iX) * (fY - iY);

            outRowPointer[0][j * 3 + 0] = b;
            outRowPointer[0][j * 3 + 1] = g;
            outRowPointer[0][j * 3 + 2] = r;
        }

        jpeg_write_scanlines(&out, outRowPointer, 1);
    }

    //Process the last line.
    in.output_scanline = height - 1;
    jpeg_read_scanlines(&in, inRowPointer, 1);
    for (j = 0; j < destWidth; j++)
    {
        fX = ((float)j) / factor;
        iX = (int)fX;

        bUpLeft = inRowPointer[0][iX * 3 + 0];
        bUpRight = inRowPointer[0][(iX + 1) * 3 + 0];

        gUpLeft = inRowPointer[0][iX * 3 + 1];
        gUpRight = inRowPointer[0][(iX + 1) * 3 + 1];

        rUpLeft = inRowPointer[0][iX * 3 + 2];
        rUpRight = inRowPointer[0][(iX + 1) * 3 + 2];

        b = bUpLeft * (iX + 1 - fX) + bUpRight * (fX - iX);
        g = gUpLeft * (iX + 1 - fX) + gUpRight * (fX - iX);
        r = rUpLeft * (iX + 1 - fX) + rUpRight * (fX - iX);

        outRowPointer[0][j * 3 + 0] = b;
        outRowPointer[0][j * 3 + 1] = g;
        outRowPointer[0][j * 3 + 2] = r;
    }
    jpeg_write_scanlines(&out, outRowPointer, 1);

    //free memory
    free(inRowPointer[0]);
    free(baseInRowPointer[0]);
    free(outRowPointer[0]);

    // close resource
    jpeg_finish_decompress(&in);
    jpeg_destroy_decompress(&in);
    jpeg_finish_compress(&out);
    jpeg_destroy_compress(&out);

    return true;
}

I have some questions about above code:

  1. I don't understand why the author said "zoom out picture. the factor should between 0.5 and 1.0. It can be 0.5, but can not be 1.0." What happen if I use this code with any other factors as 0.2 or 2 (zoom in)?

  2. Are there any potential problems when I use jpeg_destroy_decompress(&in); jpeg_destroy_compress(&out); in my error handing below?

  3. When I'm using ZoomOutJpeg() with the factor is 0.23, it often occur errors, and the result image is damaged. How can I fix it, I really want to Scale with any factor.

  4. Finaly, if someone have any other algorithm or library to scale jpeg, that give the best performance, please introduce me.

Thank you very much!

T&T

Was it helpful?

Solution

  1. Output pixel values are based on at most a 2x2 input neighborhood. This automatically means scale factors are limited to 1/2. Scale factors above 1 require interpolation, which this code doesn't do.
  2. That type of error handling and cleanup is extremely fragile. Just use RAII.
  3. Scaling by 0.23 requires 5x5 input pixels per output pixel, which this code can't do.
  4. Library requests are off-topic on SO, just use a search engine.
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top