mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1194058 (Part 2) - Add downscale-during-decode support for the GIF decoder. r=tn
This commit is contained in:
parent
c8ccf76b56
commit
dcfc39c17a
@ -39,7 +39,8 @@ ShouldDownscaleDuringDecode(const nsCString& aMimeType)
|
||||
return type == DecoderType::JPEG ||
|
||||
type == DecoderType::ICON ||
|
||||
type == DecoderType::PNG ||
|
||||
type == DecoderType::BMP;
|
||||
type == DecoderType::BMP ||
|
||||
type == DecoderType::GIF;
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
|
@ -99,6 +99,54 @@ nsGIFDecoder2::~nsGIFDecoder2()
|
||||
free(mGIFStruct.hold);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsGIFDecoder2::SetTargetSize(const nsIntSize& aSize)
|
||||
{
|
||||
// Make sure the size is reasonable.
|
||||
if (MOZ_UNLIKELY(aSize.width <= 0 || aSize.height <= 0)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// Create a downscaler that we'll filter our output through.
|
||||
mDownscaler.emplace(aSize);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
uint8_t*
|
||||
nsGIFDecoder2::GetCurrentRowBuffer()
|
||||
{
|
||||
if (!mDownscaler) {
|
||||
MOZ_ASSERT(!mDeinterlacer, "Deinterlacer without downscaler?");
|
||||
uint32_t bpp = mGIFStruct.images_decoded == 0 ? sizeof(uint32_t)
|
||||
: sizeof(uint8_t);
|
||||
return mImageData + mGIFStruct.irow * mGIFStruct.width * bpp;
|
||||
}
|
||||
|
||||
if (!mDeinterlacer) {
|
||||
return mDownscaler->RowBuffer();
|
||||
}
|
||||
|
||||
return mDeinterlacer->RowBuffer(mGIFStruct.irow);
|
||||
}
|
||||
|
||||
uint8_t*
|
||||
nsGIFDecoder2::GetRowBuffer(uint32_t aRow)
|
||||
{
|
||||
MOZ_ASSERT(mGIFStruct.images_decoded == 0,
|
||||
"Calling GetRowBuffer on a frame other than the first suggests "
|
||||
"we're deinterlacing animated frames");
|
||||
MOZ_ASSERT(!mDownscaler || mDeinterlacer,
|
||||
"Can't get buffer for a specific row if downscaling "
|
||||
"but not deinterlacing");
|
||||
|
||||
if (mDownscaler) {
|
||||
return mDeinterlacer->RowBuffer(aRow);
|
||||
}
|
||||
|
||||
return mImageData + aRow * mGIFStruct.width * sizeof(uint32_t);
|
||||
}
|
||||
|
||||
void
|
||||
nsGIFDecoder2::FinishInternal()
|
||||
{
|
||||
@ -128,6 +176,15 @@ nsGIFDecoder2::FlushImageData(uint32_t fromRow, uint32_t rows)
|
||||
void
|
||||
nsGIFDecoder2::FlushImageData()
|
||||
{
|
||||
if (mDownscaler) {
|
||||
if (mDownscaler->HasInvalidation()) {
|
||||
DownscalerInvalidRect invalidRect = mDownscaler->TakeInvalidRect();
|
||||
PostInvalidation(invalidRect.mOriginalSizeRect,
|
||||
Some(invalidRect.mTargetSizeRect));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
switch (mCurrentPass - mLastFlushedPass) {
|
||||
case 0: // same pass
|
||||
if (mCurrentRow - mLastFlushedRow) {
|
||||
@ -202,21 +259,44 @@ nsGIFDecoder2::BeginImageFrame(uint16_t aDepth)
|
||||
|
||||
CheckForTransparency(frameRect);
|
||||
|
||||
// Make sure there's no animation if we're downscaling.
|
||||
MOZ_ASSERT_IF(mDownscaler, !GetImageMetadata().HasAnimation());
|
||||
|
||||
IntSize targetSize = mDownscaler ? mDownscaler->TargetSize()
|
||||
: GetSize();
|
||||
|
||||
// Rescale the frame rect for the target size.
|
||||
IntRect targetFrameRect = frameRect;
|
||||
if (mDownscaler) {
|
||||
IntSize originalSize = GetSize();
|
||||
targetFrameRect.ScaleRoundOut(double(targetSize.width) / originalSize.width,
|
||||
double(targetSize.height) / originalSize.height);
|
||||
}
|
||||
|
||||
// Use correct format, RGB for first frame, PAL for following frames
|
||||
// and include transparency to allow for optimization of opaque images
|
||||
nsresult rv = NS_OK;
|
||||
if (mGIFStruct.images_decoded) {
|
||||
// Image data is stored with original depth and palette.
|
||||
rv = AllocateFrame(mGIFStruct.images_decoded, GetSize(),
|
||||
frameRect, format, aDepth);
|
||||
rv = AllocateFrame(mGIFStruct.images_decoded, targetSize,
|
||||
targetFrameRect, format, aDepth);
|
||||
} else {
|
||||
// Regardless of depth of input, the first frame is decoded into 24bit RGB.
|
||||
rv = AllocateFrame(mGIFStruct.images_decoded, GetSize(),
|
||||
frameRect, format);
|
||||
rv = AllocateFrame(mGIFStruct.images_decoded, targetSize,
|
||||
targetFrameRect, format);
|
||||
}
|
||||
|
||||
mCurrentFrameIndex = mGIFStruct.images_decoded;
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (mDownscaler) {
|
||||
rv = mDownscaler->BeginFrame(frameRect.Size(), mImageData,
|
||||
mGIFStruct.is_transparent);
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
@ -237,10 +317,15 @@ nsGIFDecoder2::EndImageFrame()
|
||||
// This will clear the remaining bits of the placeholder. (Bug 37589)
|
||||
const uint32_t realFrameHeight = mGIFStruct.height + mGIFStruct.y_offset;
|
||||
if (realFrameHeight < mGIFStruct.screen_height) {
|
||||
nsIntRect r(0, realFrameHeight,
|
||||
mGIFStruct.screen_width,
|
||||
mGIFStruct.screen_height - realFrameHeight);
|
||||
PostInvalidation(r);
|
||||
if (mDownscaler) {
|
||||
IntRect targetRect = IntRect(IntPoint(), mDownscaler->TargetSize());
|
||||
PostInvalidation(IntRect(IntPoint(), GetSize()), Some(targetRect));
|
||||
} else {
|
||||
nsIntRect r(0, realFrameHeight,
|
||||
mGIFStruct.screen_width,
|
||||
mGIFStruct.screen_height - realFrameHeight);
|
||||
PostInvalidation(r);
|
||||
}
|
||||
}
|
||||
|
||||
// The first frame was preallocated with alpha; if it wasn't transparent, we
|
||||
@ -291,8 +376,11 @@ nsGIFDecoder2::EndImageFrame()
|
||||
uint32_t
|
||||
nsGIFDecoder2::OutputRow()
|
||||
{
|
||||
int drow_start, drow_end;
|
||||
drow_start = drow_end = mGIFStruct.irow;
|
||||
// Initialize the region in which we're duplicating rows (for the
|
||||
// Haeberli-inspired hack below) to |irow|, which is the row we're writing to
|
||||
// now.
|
||||
int drow_start = mGIFStruct.irow;
|
||||
int drow_end = mGIFStruct.irow;
|
||||
|
||||
// Protect against too much image data
|
||||
if ((unsigned)drow_start >= mGIFStruct.height) {
|
||||
@ -329,8 +417,7 @@ nsGIFDecoder2::OutputRow()
|
||||
}
|
||||
|
||||
// Row to process
|
||||
const uint32_t bpr = sizeof(uint32_t) * mGIFStruct.width;
|
||||
uint8_t* rowp = mImageData + (mGIFStruct.irow * bpr);
|
||||
uint8_t* rowp = GetCurrentRowBuffer();
|
||||
|
||||
// Convert color indices to Cairo pixels
|
||||
uint8_t* from = rowp + mGIFStruct.width;
|
||||
@ -351,12 +438,24 @@ nsGIFDecoder2::OutputRow()
|
||||
}
|
||||
}
|
||||
|
||||
// Duplicate rows
|
||||
// If we're downscaling but not deinterlacing, we're done with this row and
|
||||
// can commit it now. Otherwise, we'll let Deinterlacer do the committing
|
||||
// when we call PropagatePassToDownscaler() at the end of this pass.
|
||||
if (mDownscaler && !mDeinterlacer) {
|
||||
mDownscaler->CommitRow();
|
||||
}
|
||||
|
||||
if (drow_end > drow_start) {
|
||||
// irow is the current row filled
|
||||
// Duplicate rows if needed to reduce the "venetian blind" effect mentioned
|
||||
// above. This writes out scanlines of the image in a way that isn't ordered
|
||||
// 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;
|
||||
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)) {
|
||||
memcpy(mImageData + (r * bpr), rowp, bpr);
|
||||
memcpy(GetRowBuffer(r), rowp, bpr);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -369,9 +468,12 @@ nsGIFDecoder2::OutputRow()
|
||||
}
|
||||
|
||||
if (!mGIFStruct.interlaced) {
|
||||
MOZ_ASSERT(!mDeinterlacer);
|
||||
mGIFStruct.irow++;
|
||||
} else {
|
||||
static const uint8_t kjump[5] = { 1, 8, 8, 4, 2 };
|
||||
int currentPass = mGIFStruct.ipass;
|
||||
|
||||
do {
|
||||
// Row increments resp. per 8,8,4,2 rows
|
||||
mGIFStruct.irow += kjump[mGIFStruct.ipass];
|
||||
@ -381,6 +483,15 @@ nsGIFDecoder2::OutputRow()
|
||||
mGIFStruct.ipass++;
|
||||
}
|
||||
} while (mGIFStruct.irow >= mGIFStruct.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.
|
||||
if (mGIFStruct.ipass > currentPass && mDownscaler) {
|
||||
MOZ_ASSERT(mDeinterlacer);
|
||||
mDeinterlacer->PropagatePassToDownscaler(*mDownscaler);
|
||||
FlushImageData();
|
||||
mDownscaler->ResetForNextProgressivePass();
|
||||
}
|
||||
}
|
||||
|
||||
return --mGIFStruct.rows_remaining;
|
||||
@ -413,17 +524,13 @@ nsGIFDecoder2::DoLzw(const uint8_t* q)
|
||||
uint8_t* stack = mGIFStruct.stack;
|
||||
uint8_t* rowp = mGIFStruct.rowp;
|
||||
|
||||
uint32_t bpr = mGIFStruct.width;
|
||||
if (!mGIFStruct.images_decoded) {
|
||||
bpr *= sizeof(uint32_t);
|
||||
}
|
||||
uint8_t* rowend = mImageData + (bpr * mGIFStruct.irow) + mGIFStruct.width;
|
||||
uint8_t* rowend = GetCurrentRowBuffer() + mGIFStruct.width;
|
||||
|
||||
#define OUTPUT_ROW() \
|
||||
PR_BEGIN_MACRO \
|
||||
if (!OutputRow()) \
|
||||
goto END; \
|
||||
rowp = mImageData + mGIFStruct.irow * bpr; \
|
||||
rowp = GetCurrentRowBuffer(); \
|
||||
rowend = rowp + mGIFStruct.width; \
|
||||
PR_END_MACRO
|
||||
|
||||
@ -934,12 +1041,19 @@ nsGIFDecoder2::WriteInternal(const char* aBuffer, uint32_t aCount)
|
||||
// below, so that RasterImage can detect that this happened.
|
||||
PostIsAnimated(/* aFirstFrameTimeout = */ 0);
|
||||
}
|
||||
|
||||
if (IsFirstFrameDecode()) {
|
||||
// We're about to get a second frame, but we only want the first. Stop
|
||||
// decoding now.
|
||||
mGIFStruct.state = gif_done;
|
||||
break;
|
||||
}
|
||||
|
||||
if (mDownscaler) {
|
||||
MOZ_ASSERT_UNREACHABLE("Doing downscale-during-decode "
|
||||
"for an animated image?");
|
||||
mDownscaler.reset();
|
||||
}
|
||||
}
|
||||
|
||||
// Get image offsets, with respect to the screen origin
|
||||
@ -1025,14 +1139,22 @@ nsGIFDecoder2::WriteInternal(const char* aBuffer, uint32_t aCount)
|
||||
// offset. Otherwise, the area may never be refreshed and the
|
||||
// placeholder will remain on the screen. (Bug 37589)
|
||||
if (mGIFStruct.y_offset > 0) {
|
||||
nsIntRect r(0, 0, mGIFStruct.screen_width, mGIFStruct.y_offset);
|
||||
PostInvalidation(r);
|
||||
if (mDownscaler) {
|
||||
IntRect targetRect = IntRect(IntPoint(), mDownscaler->TargetSize());
|
||||
PostInvalidation(IntRect(IntPoint(), GetSize()), Some(targetRect));
|
||||
} else {
|
||||
nsIntRect r(0, 0, mGIFStruct.screen_width, mGIFStruct.y_offset);
|
||||
PostInvalidation(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (q[8] & 0x40) {
|
||||
mGIFStruct.interlaced = true;
|
||||
mGIFStruct.ipass = 1;
|
||||
if (mDownscaler) {
|
||||
mDeinterlacer.emplace(mDownscaler->OriginalSize());
|
||||
}
|
||||
} else {
|
||||
mGIFStruct.interlaced = false;
|
||||
mGIFStruct.ipass = 0;
|
||||
@ -1044,7 +1166,7 @@ nsGIFDecoder2::WriteInternal(const char* aBuffer, uint32_t aCount)
|
||||
// Clear state from last image
|
||||
mGIFStruct.irow = 0;
|
||||
mGIFStruct.rows_remaining = mGIFStruct.height;
|
||||
mGIFStruct.rowp = mImageData;
|
||||
mGIFStruct.rowp = GetCurrentRowBuffer();
|
||||
|
||||
// Depth of colors is determined by colormap
|
||||
// (q[8] & 0x80) indicates local colormap
|
||||
|
@ -8,6 +8,7 @@
|
||||
#define mozilla_image_decoders_nsGIFDecoder2_h
|
||||
|
||||
#include "Decoder.h"
|
||||
#include "Downscaler.h"
|
||||
|
||||
#include "GIF2.h"
|
||||
#include "nsCOMPtr.h"
|
||||
@ -24,6 +25,8 @@ class nsGIFDecoder2 : public Decoder
|
||||
public:
|
||||
~nsGIFDecoder2();
|
||||
|
||||
nsresult SetTargetSize(const nsIntSize& aSize) override;
|
||||
|
||||
virtual void WriteInternal(const char* aBuffer, uint32_t aCount) override;
|
||||
virtual void FinishInternal() override;
|
||||
virtual Telemetry::ID SpeedHistogram() override;
|
||||
@ -34,6 +37,9 @@ private:
|
||||
// Decoders should only be instantiated via DecoderFactory.
|
||||
explicit nsGIFDecoder2(RasterImage* aImage);
|
||||
|
||||
uint8_t* GetCurrentRowBuffer();
|
||||
uint8_t* GetRowBuffer(uint32_t aRow);
|
||||
|
||||
// These functions will be called when the decoder has a decoded row,
|
||||
// frame size information, etc.
|
||||
void BeginGIF();
|
||||
@ -67,6 +73,8 @@ private:
|
||||
bool mSawTransparency;
|
||||
|
||||
gif_struct mGIFStruct;
|
||||
Maybe<Downscaler> mDownscaler;
|
||||
Maybe<Deinterlacer> mDeinterlacer;
|
||||
};
|
||||
|
||||
} // namespace image
|
||||
|
Loading…
Reference in New Issue
Block a user