in animated-gif/src/main/jni/gifimage/gif.cpp [1256:1346]
void GifFrame_nativeRenderFrame(
JNIEnv* pEnv,
jobject thiz,
jint width,
jint height,
jobject bitmap) {
auto spNativeContext = getGifFrameNativeContext(pEnv, thiz);
if (!spNativeContext) {
throwIllegalStateException(pEnv, "Already disposed");
return;
}
AndroidBitmapInfo bitmapInfo;
if (AndroidBitmap_getInfo(pEnv, bitmap, &bitmapInfo) != ANDROID_BITMAP_RESULT_SUCCESS) {
throwIllegalStateException(pEnv, "Bad bitmap");
return;
}
if (width < 0 || height < 0) {
throwIllegalArgumentException(pEnv, "Width or height is negative");
return;
}
if (bitmapInfo.width < (unsigned) width || bitmapInfo.height < (unsigned) height) {
throwIllegalStateException(pEnv, "Width or height is too small");
return;
}
if (bitmapInfo.format != ANDROID_BITMAP_FORMAT_RGBA_8888) {
throwIllegalStateException(pEnv, "Wrong color format");
return;
}
GifWrapper *pGifWrapper = spNativeContext->spGifWrapper.get();
// Note, there is some major hackery below since giflib is not intended to incrementally decode
// arbitrary frames on demand.
// We need to lock because the raster data and the data offset are shared resources and only
// one thread can use them at a time.
std::unique_lock<std::mutex> lock(pGifWrapper->getRasterMutex());
// We set the data buffer that giflib will read from to be at the beginning of where the encoded
// data for the frame starts. We know this offset because we stored it when we originally decoded
// the GIF.
int frameNum = spNativeContext->frameNum;
int byteOffset = pGifWrapper->getFrameByteOffset(frameNum);
if (!pGifWrapper->getData()->setPosition(byteOffset)) {
// Unable to position to frame, ignore it
return;
}
// Now we kick off the decoding process.
int readRes = readSingleFrame(pGifWrapper,
true, // Decode frame pixels
false, // Don't add frame to saved images
INT_MAX // Don't limit the size, it was checked in modifiedDGifSlurp
);
if (readRes != GIF_OK) {
// Probably, broken canvas, and we can ignore it
return;
}
// Get the right color table to use.
ColorMapObject* pColorMap = spNativeContext->spGifWrapper->get()->SColorMap;
ReaderLock rlock_{pGifWrapper->getSavedImagesRWLock()};
SavedImage* pSavedImage = &pGifWrapper->get()->SavedImages[frameNum];
if (pSavedImage->ImageDesc.ColorMap != NULL) {
// use local color table
pColorMap = pSavedImage->ImageDesc.ColorMap;
if (pColorMap->ColorCount != (1 << pColorMap->BitsPerPixel)) {
pColorMap = sDefaultColorMap;
}
}
uint8_t* pixels;
if (AndroidBitmap_lockPixels(pEnv, bitmap, (void**) &pixels) != ANDROID_BITMAP_RESULT_SUCCESS) {
throwIllegalStateException(pEnv, "Bad bitmap");
return;
}
blitNormal(
pixels,
width,
height,
bitmapInfo.stride,
pSavedImage,
spNativeContext->spGifWrapper->getRasterBits(),
pColorMap,
spNativeContext->transparentIndex);
AndroidBitmap_unlockPixels(pEnv, bitmap);
}