int readSingleFrame()

in animated-gif/src/main/jni/gifimage/gif.cpp [411:508]


int readSingleFrame(
    GifWrapper* pGifWrapper,
    bool decodeFramePixels,
    bool addToSavedImages,
    int maxDimension) {

  GifFileType *pGifFile = pGifWrapper->get();

  int imageCount = pGifFile->ImageCount;
  int imageDescResult = GIF_ERROR;
  {
    WriterLock wlock_{pGifWrapper->getSavedImagesRWLock()};
    imageDescResult = DGifGetImageDesc(pGifFile);
  }

  // DGifGetImageDesc may have changed the count, temporarily restoring until we know whether
  // the frame was read successfully.
  pGifFile->ImageCount = imageCount;

  if (imageDescResult == GIF_ERROR) {
    return GIF_ERROR;
  }

  ReaderLock rlock_{pGifWrapper->getSavedImagesRWLock()};
  SavedImage* pSavedImage = &pGifFile->SavedImages[imageCount];

  // Check size of image. Note: Frames with 0 width or height should be allowed.
  if (pSavedImage->ImageDesc.Width < 0 || pSavedImage->ImageDesc.Height < 0 ||
      pSavedImage->ImageDesc.Width > maxDimension || pSavedImage->ImageDesc.Height > maxDimension) {
    return GIF_ERROR;
  }

  // Check for image size overflow.
  if (pSavedImage->ImageDesc.Height != 0 &&
      pSavedImage->ImageDesc.Width > (INT_MAX / pSavedImage->ImageDesc.Height)) {
    return GIF_ERROR;
  }

  if (decodeFramePixels) {
    // Reserve larger raster bits buffer if needed
    size_t imageSize = pSavedImage->ImageDesc.Width * pSavedImage->ImageDesc.Height;
    pGifWrapper->resizeRasterBuffer(imageSize);

    // Decode frame image and save it to temporary raster bits buffer
    uint8_t* pRasterBits = pGifWrapper->getRasterBits();
    if (pSavedImage->ImageDesc.Interlace) {
      // The way an interlaced image should be read - offsets and jumps...
      int interlacedOffset[] = { 0, 4, 2, 1 };
      int interlacedJumps[] = { 8, 8, 4, 2 };
      // Need to perform 4 passes on the image.
      for (int i = 0; i < 4; i++) {
        for (int j = interlacedOffset[i];
             j < pSavedImage->ImageDesc.Height;
             j += interlacedJumps[i]) {
          GifPixelType* pLine = pRasterBits + j * pSavedImage->ImageDesc.Width;
          int lineLength = pSavedImage->ImageDesc.Width;
          if (DGifGetLine(pGifFile, pLine, lineLength) == GIF_ERROR) {
            return GIF_ERROR;
          }
        }
      }
    } else {
      if (DGifGetLine(pGifFile, pRasterBits, imageSize) == GIF_ERROR) {
        return GIF_ERROR;
      }
    }
  } else {
    // Don't decode. Just read the encoded data to skip past it.
    int codeSize;
    GifByteType* pCodeBlock;
    if (DGifGetCode(pGifFile, &codeSize, &pCodeBlock) == GIF_ERROR) {
      return GIF_ERROR;
    }
    while (pCodeBlock != NULL) {
      if (DGifGetCodeNext(pGifFile, &pCodeBlock) == GIF_ERROR) {
        return GIF_ERROR;
      }
    }
  }

  if (pGifFile->ExtensionBlocks) {
    pSavedImage->ExtensionBlocks = pGifFile->ExtensionBlocks;
    pSavedImage->ExtensionBlockCount = pGifFile->ExtensionBlockCount;

    pGifFile->ExtensionBlocks = nullptr;
    pGifFile->ExtensionBlockCount = 0;
  }

  if (addToSavedImages) {
    // giflib wasn't designed to work with decoding arbitrary frames on the fly. By default, it
    // keeps adding more images to the SavedImages array, and we reset the value after calling
    // DGifGetImageDesc. Now, as the result of decoding is known to be successful, we can increment
    // the value to represent correct number of images.
    pGifFile->ImageCount = imageCount + 1;
  }

  return GIF_OK;
}