void GifFrame_nativeRenderFrame()

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