1
0
mirror of https://github.com/opencv/opencv.git synced 2026-07-29 15:23:05 +04:00
Files
Kumataro 3e43d0cfca Merge pull request #26971 from Kumataro:fix26970
imgcodecs: gif: support animated gif without loop #26971

Close #26970

### Pull Request Readiness Checklist

See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request

- [x] I agree to contribute to the project under Apache 2 License.
- [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV
- [x] The PR is proposed to the proper branch
- [x] There is a reference to the original bug report and related work
- [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable
      Patch to opencv_extra has the same branch name.
- [x] The feature is well documented and sample code can be built with the project CMake
2025-03-19 14:24:08 +03:00

196 lines
5.5 KiB
C++

// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level
// directory of this distribution and at http://opencv.org/license.html
#ifndef OPENCV_GRFMT_GIF_HPP
#define OPENCV_GRFMT_GIF_HPP
#ifdef HAVE_IMGCODEC_GIF
#include "grfmt_base.hpp"
namespace cv
{
// See https://www.w3.org/Graphics/GIF/spec-gif89a.txt
// 23. Graphic Control Extension.
// <Packed Fields>
// Reserved : 3 bits
// Disposal Method : 3 bits
// User Input Flag : 1 bit
// Transparent Color Flag : 1 bit
constexpr int GIF_DISPOSE_METHOD_SHIFT = 2;
constexpr int GIF_DISPOSE_METHOD_MASK = 7; // 0b111
constexpr int GIF_TRANS_COLOR_FLAG_MASK = 1; // 0b1
enum GifDisposeMethod {
GIF_DISPOSE_NA = 0,
GIF_DISPOSE_NONE = 1,
GIF_DISPOSE_RESTORE_BACKGROUND = 2,
GIF_DISPOSE_RESTORE_PREVIOUS = 3,
// 4-7 are reserved/undefined.
GIF_DISPOSE_MAX = GIF_DISPOSE_RESTORE_PREVIOUS,
};
enum GifTransparentColorFlag {
GIF_TRANSPARENT_INDEX_NOT_GIVEN = 0,
GIF_TRANSPARENT_INDEX_GIVEN = 1,
GIF_TRANSPARENT_INDEX_MAX = GIF_TRANSPARENT_INDEX_GIVEN,
};
//////////////////////////////////////////////////////////////////////
//// GIF Decoder ////
//////////////////////////////////////////////////////////////////////
class GifDecoder CV_FINAL : public BaseImageDecoder
{
public:
GifDecoder();
~GifDecoder() CV_OVERRIDE;
bool readHeader() CV_OVERRIDE;
bool readData(Mat& img) CV_OVERRIDE;
bool nextPage() CV_OVERRIDE;
void close();
ImageDecoder newDecoder() const CV_OVERRIDE;
protected:
RLByteStream m_strm;
int bgColor;
int depth;
int idx;
bool hasTransparentColor;
uchar transparentColor;
int top, left, width, height;
bool hasRead;
std::vector<uchar> globalColorTable;
std::vector<uchar> localColorTable;
int lzwMinCodeSize;
int globalColorTableSize;
int localColorTableSize;
Mat lastImage;
std::vector<uchar> imgCodeStream;
struct lzwNodeD
{
int length;
uchar suffix;
std::vector<uchar> prefix;
};
GifDisposeMethod readExtensions();
void code2pixel(Mat& img, int start, int k);
bool lzwDecode();
bool getFrameCount_();
bool skipHeader();
};
//////////////////////////////////////////////////////////////////////
//// GIF Encoder ////
//////////////////////////////////////////////////////////////////////
class GifEncoder CV_FINAL : public BaseImageEncoder {
public:
GifEncoder();
~GifEncoder() CV_OVERRIDE;
bool writeanimation(const Animation& animation, const std::vector<int>& params) CV_OVERRIDE;
ImageEncoder newEncoder() const CV_OVERRIDE;
private:
/** Color Quantization **/
class OctreeColorQuant
{
struct OctreeNode
{
bool isLeaf;
std::shared_ptr<OctreeNode> children[8]{};
int level;
uchar index;
int leaf;
int pixelCount;
size_t redSum, greenSum, blueSum;
OctreeNode();
};
std::shared_ptr<OctreeNode> root;
std::vector<std::shared_ptr<OctreeNode>> m_nodeList[8];
int32_t m_bitLength;
int32_t m_maxColors;
int32_t m_leafCount;
uchar m_criticalTransparency;
uchar r, g, b; // color under transparent color
public:
explicit OctreeColorQuant(int maxColors = 256, int bitLength = 8, uchar criticalTransparency = 1);
int getPalette(uchar* colorTable);
uchar getLeaf(uchar red, uchar green, uchar blue);
void addMat(const Mat& img);
void addMats(const std::vector<Mat>& img_vec);
void addColor(int red, int green, int blue);
void reduceTree();
void recurseReduce(const std::shared_ptr<OctreeNode>& node);
};
enum GifDithering // normal dithering level is -1 to 2
{
GRFMT_GIF_None = 3,
GRFMT_GIF_FloydSteinberg = 4
};
WLByteStream strm;
int m_width, m_height;
int globalColorTableSize;
int localColorTableSize;
uchar criticalTransparency;
uchar transparentColor;
Vec3b transparentRGB;
int top, left, width, height;
OctreeColorQuant quantG;
OctreeColorQuant quantL;
std::vector<int16_t> lzwTable;
std::vector<uchar> imgCodeStream;
std::vector<uchar> globalColorTable;
std::vector<uchar> localColorTable;
// params
int colorNum;
int bitDepth;
int dithering;
int lzwMinCodeSize, lzwMaxCodeSize;
bool fast;
bool writeFrames(const std::vector<Mat>& img_vec, const std::vector<int>& params);
bool writeHeader(const std::vector<Mat>& img_vec, const int loopCount);
bool writeFrame(const Mat& img, const int frameDelay);
bool pixel2code(const Mat& img);
void getColorTable(const std::vector<Mat>& img_vec, bool isGlobal);
static int ditheringKernel(const Mat &img, Mat &img_, int depth, uchar transparency);
bool lzwEncode();
void close();
};
} // namespace cv
#endif // HAVE_IMGCODEC_GIF
#endif //OPENCV_GRFMT_GIF_HPP