gecko-dev/image/decoders/nsGIFDecoder2.h
Andrew Osmond d70025073e Bug 1901077 - Switch GIF decoder to yield new frames at terminating block. r=tnikkel
Originally we would yield a new frame when the next frame was available,
or the decoder finished. This is problematic for some WPTs used by
WebCodecs image decoding. There should be no functional difference
besides yielding a new frame slightly earlier.

Differential Revision: https://phabricator.services.mozilla.com/D212832
2024-07-17 11:39:13 +00:00

168 lines
6.0 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_image_decoders_nsGIFDecoder2_h
#define mozilla_image_decoders_nsGIFDecoder2_h
#include "Decoder.h"
#include "GIF2.h"
#include "StreamingLexer.h"
#include "SurfacePipe.h"
#include "mozilla/gfx/Swizzle.h"
namespace mozilla {
namespace image {
class RasterImage;
//////////////////////////////////////////////////////////////////////
// nsGIFDecoder2 Definition
class nsGIFDecoder2 final : public Decoder {
public:
~nsGIFDecoder2();
DecoderType GetType() const override { return DecoderType::GIF; }
protected:
LexerResult DoDecode(SourceBufferIterator& aIterator,
IResumable* aOnResume) override;
nsresult FinishWithErrorInternal() override;
nsresult FinishInternal() override;
Maybe<Telemetry::HistogramID> SpeedHistogram() const override;
private:
friend class DecoderFactory;
// Decoders should only be instantiated via DecoderFactory.
explicit nsGIFDecoder2(RasterImage* aImage);
/// Called when we begin decoding the image.
void BeginGIF();
/**
* Called when we begin decoding a frame.
*
* @param aFrameRect The region of the image that contains data. The region
* outside this rect is transparent.
* @param aDepth The palette depth of this frame.
* @param aIsInterlaced If true, this frame is an interlaced frame.
*/
nsresult BeginImageFrame(const OrientedIntRect& aFrameRect, uint16_t aDepth,
bool aIsInterlaced);
/// Called when we finish decoding a frame.
void EndImageFrame();
/// Called when we finish decoding the entire image.
void FlushImageData();
/// Convert color map to BGRA, applying any necessary CMS transforms.
void ConvertColormap(uint32_t* aColormap, uint32_t aColors);
/// Transforms a palette index into a pixel.
template <typename PixelSize>
PixelSize ColormapIndexToPixel(uint8_t aIndex);
/// A generator function that performs LZW decompression and yields pixels.
template <typename PixelSize>
std::tuple<int32_t, Maybe<WriteState>> YieldPixels(const uint8_t* aData,
size_t aLength,
size_t* aBytesReadOut,
PixelSize* aPixelBlock,
int32_t aBlockSize);
/// Checks if we have transparency, either because the header indicates that
/// there's alpha, or because the frame rect doesn't cover the entire image.
bool CheckForTransparency(const OrientedIntRect& aFrameRect);
// @return the clear code used for LZW decompression.
int ClearCode() const {
MOZ_ASSERT(mGIFStruct.datasize <= MAX_LZW_BITS);
return 1 << mGIFStruct.datasize;
}
enum class State {
FAILURE,
SUCCESS,
GIF_HEADER,
SCREEN_DESCRIPTOR,
GLOBAL_COLOR_TABLE,
FINISHED_GLOBAL_COLOR_TABLE,
BLOCK_HEADER,
EXTENSION_HEADER,
GRAPHIC_CONTROL_EXTENSION,
APPLICATION_IDENTIFIER,
NETSCAPE_EXTENSION_SUB_BLOCK,
NETSCAPE_EXTENSION_DATA,
IMAGE_DESCRIPTOR,
LOCAL_COLOR_TABLE,
FINISHED_LOCAL_COLOR_TABLE,
IMAGE_DATA_BLOCK,
IMAGE_DATA_SUB_BLOCK,
LZW_DATA,
SKIP_LZW_DATA,
FINISHED_LZW_DATA,
FINISH_END_IMAGE_FRAME,
SKIP_SUB_BLOCKS,
SKIP_DATA_THEN_SKIP_SUB_BLOCKS,
FINISHED_SKIPPING_DATA
};
LexerTransition<State> ReadGIFHeader(const char* aData);
LexerTransition<State> ReadScreenDescriptor(const char* aData);
LexerTransition<State> ReadGlobalColorTable(const char* aData,
size_t aLength);
LexerTransition<State> FinishedGlobalColorTable();
LexerTransition<State> ReadBlockHeader(const char* aData);
LexerTransition<State> ReadExtensionHeader(const char* aData);
LexerTransition<State> ReadGraphicControlExtension(const char* aData);
LexerTransition<State> ReadApplicationIdentifier(const char* aData);
LexerTransition<State> ReadNetscapeExtensionSubBlock(const char* aData);
LexerTransition<State> ReadNetscapeExtensionData(const char* aData);
LexerTransition<State> ReadImageDescriptor(const char* aData);
LexerTransition<State> FinishImageDescriptor(const char* aData);
LexerTransition<State> ReadLocalColorTable(const char* aData, size_t aLength);
LexerTransition<State> FinishedLocalColorTable();
LexerTransition<State> ReadImageDataBlock(const char* aData);
LexerTransition<State> ReadImageDataSubBlock(const char* aData);
LexerTransition<State> ReadLZWData(const char* aData, size_t aLength);
LexerTransition<State> SkipSubBlocks(const char* aData);
// The StreamingLexer used to manage input. The initial size of the buffer is
// chosen as a little larger than the maximum size of any fixed-length data we
// have to read for a state. We read variable-length data in unbuffered mode
// so the buffer shouldn't have to be resized during decoding.
StreamingLexer<State, 16> mLexer;
uint32_t mOldColor; // The old value of the transparent pixel
// The frame number of the currently-decoding frame when we're in the middle
// of decoding it, and -1 otherwise.
int32_t mCurrentFrameIndex;
// When we're reading in the global or local color table, this records our
// current position - i.e., the offset into which the next byte should be
// written.
size_t mColorTablePos;
uint32_t* mColormap; // Current colormap to be used in Cairo format
uint32_t mColormapSize;
uint8_t mColorMask; // Apply this to the pixel to keep within colormap
bool mGIFOpen;
bool mSawTransparency;
gif_struct mGIFStruct;
gfx::SwizzleRowFn mSwizzleFn; /// Method to unpack color tables from RGB.
SurfacePipe mPipe; /// The SurfacePipe used to write to the output surface.
};
} // namespace image
} // namespace mozilla
#endif // mozilla_image_decoders_nsGIFDecoder2_h