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