mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1223465 - Clamp GIF frame rects to their screen rects - r=seth
This commit is contained in:
parent
fccade91ff
commit
b7082af263
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user