Bug 1223465 - Clamp GIF frame rects to their screen rects - r=seth

This commit is contained in:
Edwin Flores 2015-11-25 13:50:13 +00:00
parent fccade91ff
commit b7082af263
2 changed files with 40 additions and 19 deletions

View File

@ -75,6 +75,8 @@ typedef struct gif_struct {
// Parameters for image frame currently being decoded
unsigned x_offset, y_offset; // With respect to "screen" origin
unsigned height, width;
unsigned clamped_height; // Size of the frame rectangle clamped to the
unsigned clamped_width; // global logical size after x_ and y_offset.
int tpixel; // Index of transparent pixel
int32_t disposal_method; // Restore to background, leave in place, etc.
uint32_t* local_colormap; // Per-image colormap

View File

@ -181,11 +181,11 @@ nsGIFDecoder2::FlushImageData()
case 1: // one pass on - need to handle bottom & top rects
FlushImageData(0, mCurrentRow + 1);
FlushImageData(mLastFlushedRow + 1,
mGIFStruct.height - (mLastFlushedRow + 1));
mGIFStruct.clamped_height - (mLastFlushedRow + 1));
break;
default: // more than one pass on - push the whole frame
FlushImageData(0, mGIFStruct.height);
FlushImageData(0, mGIFStruct.clamped_height);
}
}
@ -338,11 +338,11 @@ nsGIFDecoder2::EndImageFrame()
mCurrentPass = mLastFlushedPass = 0;
// Only add frame if we have any rows at all
if (mGIFStruct.rows_remaining != mGIFStruct.height) {
if (mGIFStruct.rows_remaining != mGIFStruct.clamped_height) {
if (mGIFStruct.rows_remaining && mGIFStruct.images_decoded) {
// Clear the remaining rows (only needed for the animation frames)
uint8_t* rowp =
mImageData + ((mGIFStruct.height - mGIFStruct.rows_remaining) *
mImageData + ((mGIFStruct.clamped_height - mGIFStruct.rows_remaining) *
mGIFStruct.width);
memset(rowp, 0, mGIFStruct.rows_remaining * mGIFStruct.width);
}
@ -381,7 +381,7 @@ nsGIFDecoder2::OutputRow()
int drow_end = mGIFStruct.irow;
// Protect against too much image data
if ((unsigned)drow_start >= mGIFStruct.height) {
if ((unsigned)drow_start >= mGIFStruct.clamped_height) {
NS_WARNING("GIF2.cpp::OutputRow - too much image data");
return 0;
}
@ -401,16 +401,16 @@ nsGIFDecoder2::OutputRow()
drow_end = drow_start + row_dup;
// Extend if bottom edge isn't covered because of the shift upward.
if (((mGIFStruct.height - 1) - drow_end) <= row_shift) {
drow_end = mGIFStruct.height - 1;
if (((mGIFStruct.clamped_height - 1) - drow_end) <= row_shift) {
drow_end = mGIFStruct.clamped_height - 1;
}
// Clamp first and last rows to upper and lower edge of image.
if (drow_start < 0) {
drow_start = 0;
}
if ((unsigned)drow_end >= mGIFStruct.height) {
drow_end = mGIFStruct.height - 1;
if ((unsigned)drow_end >= mGIFStruct.clamped_height) {
drow_end = mGIFStruct.clamped_height - 1;
}
}
@ -418,17 +418,17 @@ nsGIFDecoder2::OutputRow()
uint8_t* rowp = GetCurrentRowBuffer();
// Convert color indices to Cairo pixels
uint8_t* from = rowp + mGIFStruct.width;
uint32_t* to = ((uint32_t*)rowp) + mGIFStruct.width;
uint8_t* from = rowp + mGIFStruct.clamped_width;
uint32_t* to = ((uint32_t*)rowp) + mGIFStruct.clamped_width;
uint32_t* cmap = mColormap;
for (uint32_t c = mGIFStruct.width; c > 0; c--) {
for (uint32_t c = mGIFStruct.clamped_width; c > 0; c--) {
*--to = cmap[*--from];
}
// check for alpha (only for first frame)
if (mGIFStruct.is_transparent && !mSawTransparency) {
const uint32_t* rgb = (uint32_t*)rowp;
for (uint32_t i = mGIFStruct.width; i > 0; i--) {
for (uint32_t i = mGIFStruct.clamped_width; i > 0; i--) {
if (*rgb++ == 0) {
mSawTransparency = true;
break;
@ -449,7 +449,7 @@ nsGIFDecoder2::OutputRow()
// vertically, which is incompatible with the filter that we use for
// downscale-during-decode, so we can't do this if we're downscaling.
MOZ_ASSERT_IF(mDownscaler, mDeinterlacer);
const uint32_t bpr = sizeof(uint32_t) * mGIFStruct.width;
const uint32_t bpr = sizeof(uint32_t) * mGIFStruct.clamped_width;
for (int r = drow_start; r <= drow_end; r++) {
// Skip the row we wrote to above; that's what we're copying *from*.
if (r != int(mGIFStruct.irow)) {
@ -475,12 +475,12 @@ nsGIFDecoder2::OutputRow()
do {
// Row increments resp. per 8,8,4,2 rows
mGIFStruct.irow += kjump[mGIFStruct.ipass];
if (mGIFStruct.irow >= mGIFStruct.height) {
if (mGIFStruct.irow >= mGIFStruct.clamped_height) {
// Next pass starts resp. at row 4,2,1,0
mGIFStruct.irow = 8 >> mGIFStruct.ipass;
mGIFStruct.ipass++;
}
} while (mGIFStruct.irow >= mGIFStruct.height);
} while (mGIFStruct.irow >= mGIFStruct.clamped_height);
// We've finished a pass. If we're downscaling, it's time to propagate the
// rows we've decoded so far from our Deinterlacer to our Downscaler.
@ -525,14 +525,14 @@ nsGIFDecoder2::DoLzw(const uint8_t* q)
uint8_t* stack = mGIFStruct.stack;
uint8_t* rowp = mGIFStruct.rowp;
uint8_t* rowend = GetCurrentRowBuffer() + mGIFStruct.width;
uint8_t* rowend = GetCurrentRowBuffer() + mGIFStruct.clamped_width;
#define OUTPUT_ROW() \
PR_BEGIN_MACRO \
if (!OutputRow()) \
goto END; \
rowp = GetCurrentRowBuffer(); \
rowend = rowp + mGIFStruct.width; \
rowend = rowp + mGIFStruct.clamped_width; \
PR_END_MACRO
for (const uint8_t* ch = q; count-- > 0; ch++) {
@ -625,6 +625,10 @@ nsGIFDecoder2::DoLzw(const uint8_t* q)
*rowp++ = *--stackp & mColorMask; // ensure index is within colormap
if (rowp == rowend) {
OUTPUT_ROW();
// Consume decoded data that falls past the end of the clamped width.
stackp -= mGIFStruct.width - mGIFStruct.clamped_width;
stackp = std::max(stackp, stack);
}
} while (stackp > stack);
}
@ -1108,6 +1112,21 @@ nsGIFDecoder2::WriteInternal(const char* aBuffer, uint32_t aCount)
}
}
// Hack around GIFs with frame rects outside the given screen bounds.
IntRect clampedRect =
ClampToImageRect(IntRect(mGIFStruct.x_offset, mGIFStruct.y_offset,
mGIFStruct.width, mGIFStruct.height));
if (clampedRect.IsEmpty()) {
// XXX Bug 1227546 - Maybe we should treat this as valid?
mGIFStruct.state = gif_error;
break;
}
mGIFStruct.clamped_width = clampedRect.width;
mGIFStruct.clamped_height = clampedRect.height;
MOZ_ASSERT(mGIFStruct.clamped_width <= mGIFStruct.width);
MOZ_ASSERT(mGIFStruct.clamped_height <= mGIFStruct.height);
// Depth of colors is determined by colormap
// (q[8] & 0x80) indicates local colormap
// bits per pixel is (q[8]&0x07 + 1) when local colormap is set
@ -1170,7 +1189,7 @@ nsGIFDecoder2::WriteInternal(const char* aBuffer, uint32_t aCount)
// Clear state from last image
mGIFStruct.irow = 0;
mGIFStruct.rows_remaining = mGIFStruct.height;
mGIFStruct.rows_remaining = mGIFStruct.clamped_height;
mGIFStruct.rowp = GetCurrentRowBuffer();
// Depth of colors is determined by colormap