[JNI Memory Leak] Help please

The MagickWand interface is a new high-level C API interface to ImageMagick core methods. We discourage the use of the core methods and encourage the use of this API instead. Post MagickWand questions, bug reports, and suggestions to this forum.
Post Reply
ring8595
Posts: 1
Joined: 2014-11-06T20:27:59-07:00
Authentication code: 6789

[JNI Memory Leak] Help please

Post by ring8595 »

I use MagickWand to build a JNI to transcoding images from JAVA.
our version is ImageMagick-devel-6.5.4.7-6.el6_2.x86_64

I find that the program is continuing consuming memory. I think that there is memory leak in the JNI part.
I debug it for a week. I got nothing.
can anyone help me. thanks

Code: Select all

#include <algorithm>
#include <vector>
#include <string.h>
#include <stdint.h>
#include <arpa/inet.h>

#include <jni.h>
#include <magick/ImageMagick.h>

void encodeAlpha(unsigned char *rawAlphaBytes,
       std::vector<unsigned char> &encodedAlphaBytes, size_t rawAlphaLength);

extern "C" JNIEXPORT void JNICALL ImageTranscoderJNIImpl_init(
        JNIEnv *env, jclass javaClass) {
    MagickCoreGenesis(NULL, (MagickBooleanType) false);
}


extern "C" JNIEXPORT jbyteArray JNICALL ImageTranscoderJNIImpl_transcode(
        JNIEnv *env, jclass javaClass, jint width, jint height,
        jboolean cropToFit, jboolean keepRatio, jint quality,
        jfloat blur, jbyteArray input) {
    ExceptionInfo exception;
    size_t inputSize = env->GetArrayLength(input);
    jbyte *inputBytes = env->GetByteArrayElements(input, 0);

    // Decode the input byte array to a Magick image
    GetExceptionInfo(&exception);
    ImageInfo *inputImageInfo = AcquireImageInfo();
    Image *inputImage = BlobToImage(inputImageInfo, inputBytes, inputSize,
        &exception);
    DestroyImageInfo(inputImageInfo);
    env->ReleaseByteArrayElements(input, inputBytes, 0);

    if (inputImage == NULL) {
        DestroyExceptionInfo(&exception);
        return NULL;
    }
    DestroyExceptionInfo(&exception);

    // Calculate image scale
    int origWidth = inputImage->columns;
    int origHeight = inputImage->rows;
    int scaleWidth = width;
    int scaleHeight = height;

    bool widthRelativelyShort =
      scaleWidth * origHeight / origWidth > scaleHeight;

    if( keepRatio ) {
        if( widthRelativelyShort == cropToFit ) {
            scaleHeight = scaleWidth * origHeight / origWidth;
        } else {
            scaleWidth = scaleHeight * origWidth / origHeight;
        }
    }

    // Rescale image
    GetExceptionInfo(&exception);
    Image *scaledImage = ResizeImage(inputImage, scaleWidth, scaleHeight,
        LanczosFilter, blur, &exception);
    DestroyImages(inputImage);

    if (scaledImage == NULL) {
        DestroyExceptionInfo(&exception);
        return NULL;
    }
    DestroyExceptionInfo(&exception);

    // Calculate final dimensions
    int finalWidth = std::min(width, scaleWidth);
    int finalHeight = std::min(height, scaleHeight);

    RectangleInfo ri = {
        finalWidth,
        finalHeight,
        (scaleWidth - finalWidth) / 2,
        (scaleHeight - finalHeight) / 2,
    };

    // Crop image
    GetExceptionInfo(&exception);
    Image *croppedAndScaledImage = CropImage(scaledImage, &ri, &exception);
    DestroyImages(scaledImage);

    if (croppedAndScaledImage == NULL) {
      DestroyExceptionInfo(&exception);
      return NULL;
    }
    DestroyExceptionInfo(&exception);

    // Handle image transparency if needed
    std::vector<unsigned char> encodedAlphaBytes;
    if (croppedAndScaledImage->matte) {
      // Allocate space for raw alpha
      unsigned char *rawAlphaBytes =
          new unsigned char[finalWidth * finalHeight];

      GetExceptionInfo(&exception);
      DispatchImage(croppedAndScaledImage, 0, 0, finalWidth, finalHeight, "A",
          CharPixel, rawAlphaBytes, &exception);

      encodeAlpha(rawAlphaBytes, encodedAlphaBytes, finalWidth * finalHeight);

      DestroyExceptionInfo(&exception);
      delete [] rawAlphaBytes;
    }
    int encodedAlphaSize = encodedAlphaBytes.size();

    // Encode transformed image as a JPEG
    ImageInfo *outputImageInfo = AcquireImageInfo();
    strcpy(outputImageInfo->magick, "JPEG");
    outputImageInfo->magick[4] = '\0';
    if (croppedAndScaledImage->colorspace != RGBColorspace) {
      TransformRGBImage(croppedAndScaledImage, RGBColorspace);
    }
    outputImageInfo->type = TrueColorType;

    // This is stupid - when writing an image to a blob we need to set the
    // quality parameter on the input image, not on the ImageInfo :(
    croppedAndScaledImage->quality = quality;

    // Remove color profile info, comments, etc.
    StripImage(croppedAndScaledImage);

    GetExceptionInfo(&exception);
    size_t outputSize = 0;
    unsigned char *outputBytes = ImageToBlob(outputImageInfo,
        croppedAndScaledImage, &outputSize, &exception);
    DestroyImages(croppedAndScaledImage);
    DestroyImageInfo(outputImageInfo);

    if (outputBytes == NULL) {
      DestroyExceptionInfo(&exception);
      return NULL;
    }
    DestroyExceptionInfo(&exception);

    // Note: Allocating 18 extra bytes for transparency + position info
    jbyteArray output = env->NewByteArray(outputSize + encodedAlphaSize + 18);
    if (output == NULL) {
        RelinquishMagickMemory(outputBytes);
        return NULL;
    }

    // Write encoded image
    env->SetByteArrayRegion(output, 0, outputSize, (jbyte *) outputBytes);

    // Write transparency
    if (!encodedAlphaBytes.empty()) {
        env->SetByteArrayRegion(output, outputSize, encodedAlphaSize,
            (const jbyte *) &encodedAlphaBytes[0]);
    }
    encodedAlphaBytes.clear();
    // Write transparency and position info
    uint16_t outAlphaLength = htons(encodedAlphaSize);
    uint32_t outWidth = htonl(finalWidth);
    uint32_t outHeight = htonl(finalHeight);
    uint32_t imageXPos = htonl(-1);
    uint32_t imageYPos = htonl(-1);
    // Transparency size
    env->SetByteArrayRegion(output, outputSize + encodedAlphaSize, 2,
        (jbyte *) &outAlphaLength);
    // Dimensions
    env->SetByteArrayRegion(output, outputSize + encodedAlphaSize + 2, 4,
        (jbyte *) &outWidth);
    env->SetByteArrayRegion(output, outputSize + encodedAlphaSize + 6, 4,
        (jbyte *) &outHeight);
    // Position
    env->SetByteArrayRegion(output, outputSize + encodedAlphaSize + 10, 4,
        (jbyte *) &imageXPos);
    env->SetByteArrayRegion(output, outputSize + encodedAlphaSize + 14, 4,
        (jbyte *) &imageYPos);

    RelinquishMagickMemory(outputBytes);

    return output;
}


void encodeAlpha(unsigned char *rawAlphaBytes,
        std::vector<unsigned char> &encodedAlphaBytes, size_t rawAlphaLength) {
    if( rawAlphaLength > 1 ) {
        int currentCount = 1;
        // Note: we use alpha between 0-63 and use the extra bits for
        // encoding
        int prevAlpha = ( rawAlphaBytes[ 0 ] >> 2 ) & 0x3f;

        for( int i = 1; i < rawAlphaLength; i++ ) {
            int alpha = ( rawAlphaBytes[ i ] >> 2 ) & 0x3f;

            if( prevAlpha == alpha && currentCount < 255 ) {
                currentCount++;
            } else {
                if( currentCount == 1 ) {
                    // we use 0-63 to code one alpha value
                    encodedAlphaBytes.push_back( prevAlpha );
                } else {
                    // and 64-127 to code a series of an alpha value
                    encodedAlphaBytes.push_back( prevAlpha | 0x40 );
                    encodedAlphaBytes.push_back( currentCount );
                    currentCount = 1;
                }

                prevAlpha = alpha;
            }
        }

        if( currentCount == 1 ) {
            // we use 0-63 to code one alpha value
            encodedAlphaBytes.push_back( prevAlpha );
        } else {
            // and 64-127 to code a series of an alpha value
            encodedAlphaBytes.push_back( prevAlpha | 0x40 );
            encodedAlphaBytes.push_back( currentCount );
        }
    }
}

User avatar
fmw42
Posts: 25562
Joined: 2007-07-02T17:14:51-07:00
Authentication code: 1152
Location: Sunnyvale, California, USA

Re: [JNI Memory Leak] Help please

Post by fmw42 »

I cannot help, but I would point out that your version of IM at 6.5.4.7 is ancient (about 350 versions old). Perhaps an upgrade might help?
Post Reply