Add export to animated GIF, wrt GH-9
This commit is contained in:
40
3rdparty/QtGifImage/include/gifimage/qgifglobal.h
vendored
Normal file
40
3rdparty/QtGifImage/include/gifimage/qgifglobal.h
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
/****************************************************************************
|
||||
** Copyright (c) 2013 Debao Zhang <hello@debao.me>
|
||||
** All right reserved.
|
||||
**
|
||||
** Permission is hereby granted, free of charge, to any person obtaining
|
||||
** a copy of this software and associated documentation files (the
|
||||
** "Software"), to deal in the Software without restriction, including
|
||||
** without limitation the rights to use, copy, modify, merge, publish,
|
||||
** distribute, sublicense, and/or sell copies of the Software, and to
|
||||
** permit persons to whom the Software is furnished to do so, subject to
|
||||
** the following conditions:
|
||||
**
|
||||
** The above copyright notice and this permission notice shall be
|
||||
** included in all copies or substantial portions of the Software.
|
||||
**
|
||||
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
**
|
||||
****************************************************************************/
|
||||
#ifndef GIF_IMAGE_Q_GIF_GLOBAL_H_
|
||||
#define GIF_IMAGE_Q_GIF_GLOBAL_H_
|
||||
|
||||
#include <QtGlobal>
|
||||
|
||||
#if !defined(QT_STATIC) && !defined(GIFIMAGE_NO_LIB)
|
||||
#if defined(QT_BUILD_GIFIMAGE_LIB)
|
||||
#define Q_GIFIMAGE_EXPORT Q_DECL_EXPORT
|
||||
#else
|
||||
#define Q_GIFIMAGE_EXPORT Q_DECL_IMPORT
|
||||
#endif
|
||||
#else
|
||||
#define Q_GIFIMAGE_EXPORT
|
||||
#endif
|
||||
|
||||
#endif // GIF_IMAGE_Q_GIF_GLOBAL_H_
|
||||
668
3rdparty/QtGifImage/include/gifimage/qgifimage.cpp
vendored
Normal file
668
3rdparty/QtGifImage/include/gifimage/qgifimage.cpp
vendored
Normal file
@@ -0,0 +1,668 @@
|
||||
/****************************************************************************
|
||||
** Copyright (c) 2013 Debao Zhang <hello@debao.me>
|
||||
** All right reserved.
|
||||
**
|
||||
** Permission is hereby granted, free of charge, to any person obtaining
|
||||
** a copy of this software and associated documentation files (the
|
||||
** "Software"), to deal in the Software without restriction, including
|
||||
** without limitation the rights to use, copy, modify, merge, publish,
|
||||
** distribute, sublicense, and/or sell copies of the Software, and to
|
||||
** permit persons to whom the Software is furnished to do so, subject to
|
||||
** the following conditions:
|
||||
**
|
||||
** The above copyright notice and this permission notice shall be
|
||||
** included in all copies or substantial portions of the Software.
|
||||
**
|
||||
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include <gifimage/qgifimage.h>
|
||||
#include <gifimage/qgifimage_p.h>
|
||||
|
||||
#include <QFile>
|
||||
#include <QImage>
|
||||
#include <QDebug>
|
||||
#include <QScopedPointer>
|
||||
|
||||
namespace {
|
||||
|
||||
int writeToIODevice(GifFileType *gifFile, const GifByteType *data,
|
||||
int maxSize) {
|
||||
return static_cast<int>(
|
||||
static_cast<QIODevice *>(
|
||||
gifFile->UserData)->write(
|
||||
reinterpret_cast<const char *>(data), maxSize));
|
||||
}
|
||||
|
||||
int readFromIODevice(GifFileType *gifFile, GifByteType *data, int maxSize) {
|
||||
return static_cast<int>(
|
||||
static_cast<QIODevice *>(
|
||||
gifFile->UserData)->read(
|
||||
reinterpret_cast<char *>(data), maxSize));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
QGifImagePrivate::QGifImagePrivate(QGifImage *p)
|
||||
: loopCount(0), defaultDelayTime(1000), q_ptr(p) {}
|
||||
|
||||
QVector<QRgb> QGifImagePrivate::colorTableFromColorMapObject(
|
||||
ColorMapObject *colorMap, int transColorIndex) const {
|
||||
QVector<QRgb> colorTable;
|
||||
if (colorMap) {
|
||||
for (int idx = 0; idx < colorMap->ColorCount; ++idx) {
|
||||
GifColorType gifColor = colorMap->Colors[idx];
|
||||
QRgb color = gifColor.Blue | (gifColor.Green << 8) | (gifColor.Red << 16);
|
||||
// For non-transparent color, set the alpha to opaque.
|
||||
if (idx != transColorIndex)
|
||||
color |= 0xff << 24;
|
||||
colorTable.append(color);
|
||||
}
|
||||
}
|
||||
return colorTable;
|
||||
}
|
||||
|
||||
ColorMapObject *QGifImagePrivate::colorTableToColorMapObject(
|
||||
QVector<QRgb> colorTable) const {
|
||||
if (colorTable.isEmpty())
|
||||
return nullptr;
|
||||
|
||||
auto cmap = (ColorMapObject *)malloc(sizeof(ColorMapObject));
|
||||
// num of colors must be a power of 2
|
||||
int numColors = 1 << GifBitSize(static_cast<int>(colorTable.size()));
|
||||
cmap->ColorCount = numColors;
|
||||
// Maybe a bug of giflib, BitsPerPixel is used as size of the color table
|
||||
// size.
|
||||
cmap->BitsPerPixel = GifBitSize(static_cast<int>(colorTable.size())); // Todo!
|
||||
cmap->SortFlag = false;
|
||||
|
||||
auto colorValues =
|
||||
(GifColorType *)calloc(numColors, sizeof(GifColorType));
|
||||
for (int idx = 0; idx < colorTable.size(); ++idx) {
|
||||
colorValues[idx].Red = qRed(colorTable[idx]);
|
||||
colorValues[idx].Green = qGreen(colorTable[idx]);
|
||||
colorValues[idx].Blue = qBlue(colorTable[idx]);
|
||||
}
|
||||
|
||||
cmap->Colors = colorValues;
|
||||
|
||||
return cmap;
|
||||
}
|
||||
|
||||
QSize QGifImagePrivate::getCanvasSize() const {
|
||||
// If canvasSize has been set by user.
|
||||
if (canvasSize.isValid())
|
||||
return canvasSize;
|
||||
|
||||
// Calc the right canvasSize from the frame size.
|
||||
int width = -1;
|
||||
int height = -1;
|
||||
foreach (QGifFrameInfoData info, frameInfos) {
|
||||
int w = info.image.width() + info.offset.x();
|
||||
int h = info.image.height() + info.offset.y();
|
||||
if (w > width)
|
||||
width = w;
|
||||
if (h > height)
|
||||
height = h;
|
||||
}
|
||||
return {width, height};
|
||||
}
|
||||
|
||||
int QGifImagePrivate::getFrameTransparentColorIndex(
|
||||
const QGifFrameInfoData &frameInfo) const {
|
||||
int index = -1;
|
||||
|
||||
QColor transColor = frameInfo.transparentColor.isValid()
|
||||
? frameInfo.transparentColor
|
||||
: defaultTransparentColor;
|
||||
|
||||
if (transColor.isValid()) {
|
||||
if (!frameInfo.image.colorTable().isEmpty())
|
||||
index = static_cast<int>(frameInfo.image.colorTable().indexOf(transColor.rgb()));
|
||||
else if (!globalColorTable.isEmpty())
|
||||
index = static_cast<int>(globalColorTable.indexOf(transColor.rgb()));
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
bool QGifImagePrivate::load(QIODevice *device) {
|
||||
static int interlacedOffset[] = {0, 4, 2,
|
||||
1}; /* The way Interlaced image should. */
|
||||
static int interlacedJumps[] = {8, 8, 4,
|
||||
2}; /* be read - offsets and jumps... */
|
||||
|
||||
int error;
|
||||
GifFileType *gifFile = DGifOpen(device, readFromIODevice, &error);
|
||||
if (!gifFile) {
|
||||
char *temp = GifErrorString(error);
|
||||
qWarning("%s", temp);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (DGifSlurp(gifFile) == GIF_ERROR)
|
||||
return false;
|
||||
|
||||
canvasSize.setWidth(gifFile->SWidth);
|
||||
canvasSize.setHeight(gifFile->SHeight);
|
||||
if (gifFile->SColorMap) {
|
||||
globalColorTable = colorTableFromColorMapObject(gifFile->SColorMap);
|
||||
if (gifFile->SBackGroundColor < globalColorTable.size())
|
||||
bgColor = QColor(globalColorTable[gifFile->SBackGroundColor]);
|
||||
}
|
||||
|
||||
for (int idx = 0; idx < gifFile->ImageCount; ++idx) {
|
||||
SavedImage gifImage = gifFile->SavedImages[idx];
|
||||
int top = gifImage.ImageDesc.Top;
|
||||
int left = gifImage.ImageDesc.Left;
|
||||
int width = gifImage.ImageDesc.Width;
|
||||
int height = gifImage.ImageDesc.Height;
|
||||
|
||||
QGifFrameInfoData frameInfo;
|
||||
GraphicsControlBlock gcb;
|
||||
DGifSavedExtensionToGCB(gifFile, idx, &gcb);
|
||||
int transColorIndex = gcb.TransparentColor;
|
||||
|
||||
QVector<QRgb> colorTable;
|
||||
if (gifImage.ImageDesc.ColorMap)
|
||||
colorTable = colorTableFromColorMapObject(gifImage.ImageDesc.ColorMap,
|
||||
transColorIndex);
|
||||
else if (transColorIndex != -1)
|
||||
colorTable =
|
||||
colorTableFromColorMapObject(gifFile->SColorMap, transColorIndex);
|
||||
else
|
||||
colorTable = globalColorTable;
|
||||
|
||||
if (transColorIndex != -1)
|
||||
frameInfo.transparentColor = colorTable[transColorIndex];
|
||||
frameInfo.delayTime = gcb.DelayTime * 10; // convert to milliseconds
|
||||
frameInfo.interlace = gifImage.ImageDesc.Interlace;
|
||||
frameInfo.offset = QPoint(left, top);
|
||||
|
||||
QImage image(width, height, QImage::Format_Indexed8);
|
||||
image.setOffset(QPoint(left, top)); // Maybe useful for some users.
|
||||
image.setColorTable(colorTable);
|
||||
if (transColorIndex != -1)
|
||||
image.fill(transColorIndex);
|
||||
else if (!globalColorTable.isEmpty())
|
||||
image.fill(gifFile->SBackGroundColor); //! ToDo
|
||||
|
||||
if (gifImage.ImageDesc.Interlace) {
|
||||
int line = 0;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
for (int row = interlacedOffset[i]; row < height;
|
||||
row += interlacedJumps[i]) {
|
||||
memcpy(image.scanLine(row), gifImage.RasterBits + line * width,
|
||||
width);
|
||||
line++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int row = 0; row < height; row++) {
|
||||
memcpy(image.scanLine(row), gifImage.RasterBits + row * width, width);
|
||||
}
|
||||
}
|
||||
|
||||
// Extract other data for the image.
|
||||
if (idx == 0) {
|
||||
if (gifImage.ExtensionBlockCount > 2) {
|
||||
ExtensionBlock *extBlock = gifImage.ExtensionBlocks;
|
||||
if (extBlock->Function == APPLICATION_EXT_FUNC_CODE &&
|
||||
extBlock->ByteCount == 8) {
|
||||
if (QByteArray((char *)extBlock->Bytes) ==
|
||||
QByteArray("NETSCAPE2.0")) {
|
||||
ExtensionBlock *block = gifImage.ExtensionBlocks + 1;
|
||||
if (block->ByteCount == 3) {
|
||||
loopCount =
|
||||
uchar(block->Bytes[1]) + uchar((block->Bytes[2]) << 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
frameInfo.image = image;
|
||||
frameInfos.append(frameInfo);
|
||||
}
|
||||
|
||||
DGifCloseFile(gifFile);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool QGifImagePrivate::save(QIODevice *device) const {
|
||||
int error;
|
||||
GifFileType *gifFile = EGifOpen(device, writeToIODevice, &error);
|
||||
if (!gifFile) {
|
||||
char *temp = GifErrorString(error);
|
||||
qWarning("%s", temp);
|
||||
return false;
|
||||
}
|
||||
|
||||
QSize _canvasSize = getCanvasSize();
|
||||
gifFile->SWidth = _canvasSize.width();
|
||||
gifFile->SHeight = _canvasSize.height();
|
||||
gifFile->SColorResolution = 8;
|
||||
if (!globalColorTable.isEmpty()) {
|
||||
gifFile->SColorMap = colorTableToColorMapObject(globalColorTable);
|
||||
int idx = static_cast<int>(globalColorTable.indexOf(bgColor.rgba()));
|
||||
gifFile->SBackGroundColor = idx == -1 ? 0 : idx;
|
||||
}
|
||||
|
||||
gifFile->ImageCount = static_cast<int>(frameInfos.size());
|
||||
gifFile->SavedImages =
|
||||
(SavedImage *)calloc(frameInfos.size(), sizeof(SavedImage));
|
||||
for (int idx = 0; idx < frameInfos.size(); ++idx) {
|
||||
const QGifFrameInfoData frameInfo = frameInfos.at(idx);
|
||||
QImage image = frameInfo.image;
|
||||
if (image.format() != QImage::Format_Indexed8) {
|
||||
if (!globalColorTable.isEmpty())
|
||||
image =
|
||||
image.convertToFormat(QImage::Format_Indexed8, globalColorTable);
|
||||
else
|
||||
image = image.convertToFormat(QImage::Format_Indexed8);
|
||||
}
|
||||
|
||||
SavedImage *gifImage = gifFile->SavedImages + idx;
|
||||
|
||||
gifImage->ImageDesc.Left = frameInfo.offset.x();
|
||||
gifImage->ImageDesc.Top = frameInfo.offset.y();
|
||||
gifImage->ImageDesc.Width = image.width();
|
||||
gifImage->ImageDesc.Height = image.height();
|
||||
gifImage->ImageDesc.Interlace = frameInfo.interlace;
|
||||
|
||||
if (!image.colorTable().isEmpty() &&
|
||||
(image.colorTable() != globalColorTable))
|
||||
gifImage->ImageDesc.ColorMap =
|
||||
colorTableToColorMapObject(image.colorTable());
|
||||
else
|
||||
gifImage->ImageDesc.ColorMap = nullptr;
|
||||
|
||||
auto data = (GifByteType *)malloc(image.width() * image.height() *
|
||||
sizeof(GifByteType));
|
||||
for (int row = 0; row < image.height(); ++row) {
|
||||
memcpy(data + row * image.width(), image.scanLine(row), image.width());
|
||||
}
|
||||
gifImage->RasterBits = data;
|
||||
|
||||
if (idx == 0) {
|
||||
uchar data8[12] = "NETSCAPE2.0";
|
||||
GifAddExtensionBlock(&gifImage->ExtensionBlockCount,
|
||||
&gifImage->ExtensionBlocks,
|
||||
APPLICATION_EXT_FUNC_CODE, 11, data8);
|
||||
uchar data_char[3];
|
||||
data_char[0] = 0x01;
|
||||
data_char[1] = loopCount & 0xFF;
|
||||
data_char[2] = (loopCount >> 8) & 0xFF;
|
||||
GifAddExtensionBlock(&gifImage->ExtensionBlockCount,
|
||||
&gifImage->ExtensionBlocks, CONTINUE_EXT_FUNC_CODE,
|
||||
3, data_char);
|
||||
}
|
||||
|
||||
GraphicsControlBlock gcbBlock;
|
||||
gcbBlock.DisposalMode = 0;
|
||||
gcbBlock.UserInputFlag = false;
|
||||
gcbBlock.TransparentColor = getFrameTransparentColorIndex(frameInfo);
|
||||
|
||||
if (frameInfo.delayTime != -1)
|
||||
gcbBlock.DelayTime =
|
||||
frameInfo.delayTime / 10; // convert from milliseconds
|
||||
else
|
||||
gcbBlock.DelayTime = defaultDelayTime / 10;
|
||||
|
||||
EGifGCBToSavedExtension(&gcbBlock, gifFile, idx);
|
||||
}
|
||||
EGifSpew(gifFile);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*!
|
||||
\class QGifImage
|
||||
\inmodule QtGifImage
|
||||
\brief Class used to read/wirte .gif files.
|
||||
*/
|
||||
|
||||
/*!
|
||||
Constructs a gif image
|
||||
*/
|
||||
QGifImage::QGifImage() : d_ptr(new QGifImagePrivate(this)) {}
|
||||
|
||||
/*!
|
||||
Constructs a gif image and tries to load the image from the
|
||||
file with the given \a fileName
|
||||
*/
|
||||
QGifImage::QGifImage(const QString &fileName)
|
||||
: d_ptr(new QGifImagePrivate(this)) {
|
||||
load(fileName);
|
||||
}
|
||||
|
||||
/*!
|
||||
Constructs a gif image with the given \a size
|
||||
*/
|
||||
QGifImage::QGifImage(const QSize &size) : d_ptr(new QGifImagePrivate(this)) {
|
||||
d_ptr->canvasSize = size;
|
||||
}
|
||||
|
||||
/*!
|
||||
Destroys the gif image and cleans up.
|
||||
*/
|
||||
QGifImage::~QGifImage() { delete d_ptr; }
|
||||
|
||||
/*!
|
||||
Return global color table.
|
||||
*/
|
||||
QVector<QRgb> QGifImage::globalColorTable() const {
|
||||
Q_D(const QGifImage);
|
||||
return d->globalColorTable;
|
||||
}
|
||||
|
||||
/*!
|
||||
Return background color of the gif canvas. It only makes sense when
|
||||
global color table is not empty.
|
||||
*/
|
||||
QColor QGifImage::backgroundColor() const {
|
||||
Q_D(const QGifImage);
|
||||
return d->bgColor;
|
||||
}
|
||||
|
||||
/*!
|
||||
Set the global color table \a colors and background color \a bgColor.
|
||||
\a bgColor must be one the color in \a colors.
|
||||
|
||||
Unlike other image formats that support alpha (e.g. png), GIF does not
|
||||
support semi-transparent pixels. So the alpha channel of the color table
|
||||
will be ignored.
|
||||
*/
|
||||
void QGifImage::setGlobalColorTable(const QVector<QRgb> &colors,
|
||||
const QColor &bgColor) {
|
||||
Q_D(QGifImage);
|
||||
d->globalColorTable = colors;
|
||||
d->bgColor = bgColor;
|
||||
}
|
||||
|
||||
/*!
|
||||
Return the default delay in milliseconds. The default value is 1000 ms.
|
||||
|
||||
The time delay can be different for every frame.
|
||||
*/
|
||||
int QGifImage::defaultDelay() const {
|
||||
Q_D(const QGifImage);
|
||||
return d->defaultDelayTime;
|
||||
}
|
||||
|
||||
/*!
|
||||
Set the default \a delay in milliseconds.
|
||||
*/
|
||||
void QGifImage::setDefaultDelay(int delay) {
|
||||
Q_D(QGifImage);
|
||||
d->defaultDelayTime = delay;
|
||||
}
|
||||
|
||||
/*!
|
||||
Return the default transparent color.
|
||||
|
||||
The transparent color can be different for every frame.
|
||||
*/
|
||||
QColor QGifImage::defaultTransparentColor() const {
|
||||
Q_D(const QGifImage);
|
||||
return d->defaultTransparentColor;
|
||||
}
|
||||
|
||||
/*!
|
||||
Set the default transparent \a color.
|
||||
|
||||
Unlike other image formats that support alpha (e.g. png), GIF does
|
||||
not support semi-transparent pixels. The way to achieve transparency
|
||||
is to set a color that will be transparent when rendering the GIF.
|
||||
So, if you set the transparent color to black, the black pixels in
|
||||
the gif file will be transparent.
|
||||
*/
|
||||
void QGifImage::setDefaultTransparentColor(const QColor &color) {
|
||||
Q_D(QGifImage);
|
||||
d->defaultTransparentColor = color;
|
||||
}
|
||||
|
||||
/*!
|
||||
Return the loop count.
|
||||
*/
|
||||
int QGifImage::loopCount() const {
|
||||
Q_D(const QGifImage);
|
||||
return d->loopCount;
|
||||
}
|
||||
|
||||
/*!
|
||||
Set the loop count. The default value of \a loop is 0, which means loop
|
||||
forever.
|
||||
*/
|
||||
void QGifImage::setLoopCount(int loop) {
|
||||
Q_D(QGifImage);
|
||||
d->loopCount = loop;
|
||||
}
|
||||
|
||||
/*!
|
||||
Insert the QImage object \a frame at position \a index with \a delay.
|
||||
|
||||
As gif file only support indexed image, so all the \a frame will be
|
||||
converted to the QImage::Format_Indexed8 format. Global color table will be
|
||||
used in the convertion if it has been set.
|
||||
|
||||
QImage::offset() will be used when insert the QImage to the gif canvas.
|
||||
*/
|
||||
void QGifImage::insertFrame(int index, const QImage &frame, int delay) {
|
||||
Q_D(QGifImage);
|
||||
|
||||
QGifFrameInfoData data;
|
||||
data.image = frame;
|
||||
data.delayTime = delay;
|
||||
data.offset = frame.offset();
|
||||
|
||||
d->frameInfos.insert(index, data);
|
||||
}
|
||||
|
||||
/*!
|
||||
\overload
|
||||
|
||||
Insert the QImage object \a frame at position \a index with the given \a
|
||||
offset and \a delay.
|
||||
|
||||
As gif file only support indexed image, so all the \a frame will be
|
||||
converted to the QImage::Format_Indexed8 format. Global color table will be
|
||||
used in the convertion if it has been set.
|
||||
*/
|
||||
void QGifImage::insertFrame(int index, const QImage &frame,
|
||||
const QPoint &offset, int delay) {
|
||||
Q_D(QGifImage);
|
||||
QGifFrameInfoData data;
|
||||
data.image = frame;
|
||||
data.delayTime = delay;
|
||||
data.offset = offset;
|
||||
|
||||
d->frameInfos.insert(index, data);
|
||||
}
|
||||
|
||||
/*!
|
||||
Append the QImage object \a frame with \a delay.
|
||||
|
||||
As gif file only support indexed image, so all the \a frame will be
|
||||
converted to the QImage::Format_Indexed8 format. Global color table will be
|
||||
used in the convertion if it has been set.
|
||||
|
||||
QImage::offset() will be used when insert the QImage to the gif canvas.
|
||||
*/
|
||||
void QGifImage::addFrame(const QImage &frame, int delay) {
|
||||
Q_D(QGifImage);
|
||||
|
||||
QGifFrameInfoData data;
|
||||
data.image = frame;
|
||||
data.delayTime = delay;
|
||||
data.offset = frame.offset();
|
||||
|
||||
d->frameInfos.append(data);
|
||||
}
|
||||
|
||||
/*!
|
||||
\overload
|
||||
Append the QImage object \a frame with the given \a offset and \a delay.
|
||||
*/
|
||||
void QGifImage::addFrame(const QImage &frame, const QPoint &offset, int delay) {
|
||||
Q_D(QGifImage);
|
||||
|
||||
QGifFrameInfoData data;
|
||||
data.image = frame;
|
||||
data.delayTime = delay;
|
||||
data.offset = offset;
|
||||
|
||||
d->frameInfos.append(data);
|
||||
}
|
||||
|
||||
/*!
|
||||
Return frame count contained in the gif file.
|
||||
*/
|
||||
int QGifImage::frameCount() const {
|
||||
Q_D(const QGifImage);
|
||||
return static_cast<int>(d->frameInfos.count());
|
||||
}
|
||||
|
||||
/*!
|
||||
Return the image at \a index.
|
||||
*/
|
||||
QImage QGifImage::frame(int index) const {
|
||||
Q_D(const QGifImage);
|
||||
if (index < 0 || index >= d->frameInfos.size())
|
||||
return {};
|
||||
|
||||
return d->frameInfos[index].image;
|
||||
}
|
||||
|
||||
/*!
|
||||
Return the offset value of the frame at \a index
|
||||
*/
|
||||
QPoint QGifImage::frameOffset(int index) const {
|
||||
Q_D(const QGifImage);
|
||||
if (index < 0 || index >= d->frameInfos.size())
|
||||
return {};
|
||||
|
||||
return d->frameInfos[index].offset;
|
||||
}
|
||||
|
||||
/*!
|
||||
Set the \a offset value for the frame at \a index
|
||||
*/
|
||||
void QGifImage::setFrameOffset(int index, const QPoint &offset) {
|
||||
Q_D(QGifImage);
|
||||
if (index < 0 || index >= d->frameInfos.size())
|
||||
return;
|
||||
d->frameInfos[index].offset = offset;
|
||||
}
|
||||
|
||||
/*!
|
||||
Return the delay value of the frame at \a index
|
||||
*/
|
||||
int QGifImage::frameDelay(int index) const {
|
||||
Q_D(const QGifImage);
|
||||
if (index < 0 || index >= d->frameInfos.size())
|
||||
return -1;
|
||||
|
||||
return d->frameInfos[index].delayTime;
|
||||
}
|
||||
|
||||
/*!
|
||||
Set the \a delay value for the frame at \a index
|
||||
*/
|
||||
void QGifImage::setFrameDelay(int index, int delay) {
|
||||
Q_D(QGifImage);
|
||||
if (index < 0 || index >= d->frameInfos.size())
|
||||
return;
|
||||
d->frameInfos[index].delayTime = delay;
|
||||
}
|
||||
|
||||
/*!
|
||||
Return the transparent color of the frame at \a index
|
||||
*/
|
||||
QColor QGifImage::frameTransparentColor(int index) const {
|
||||
Q_D(const QGifImage);
|
||||
if (index < 0 || index >= d->frameInfos.size())
|
||||
return {};
|
||||
|
||||
return d->frameInfos[index].transparentColor;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the transparent \a color of the frame \a index. Unlike other image
|
||||
formats that support alpha (e.g. PNG), GIF does not support semi-transparent
|
||||
pixels. The way to achieve transparency is to set a color that will be
|
||||
transparent when rendering the GIF. So, if you set the transparent color to
|
||||
black, the black pixels in your gif file will be transparent.
|
||||
*/
|
||||
void QGifImage::setFrameTransparentColor(int index, const QColor &color) {
|
||||
Q_D(QGifImage);
|
||||
if (index < 0 || index >= d->frameInfos.size())
|
||||
return;
|
||||
d->frameInfos[index].transparentColor = color;
|
||||
}
|
||||
|
||||
/*!
|
||||
Saves the gif image to the file with the given \a fileName.
|
||||
Returns \c true if the image was successfully saved; otherwise
|
||||
returns \c false.
|
||||
*/
|
||||
bool QGifImage::save(const QString &fileName) const {
|
||||
Q_D(const QGifImage);
|
||||
QFile file(fileName);
|
||||
if (file.open(QIODevice::WriteOnly)) {
|
||||
bool res = d->save(&file);
|
||||
// d->~QGifImagePrivate();
|
||||
// delete d;
|
||||
return res;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*!
|
||||
\overload
|
||||
|
||||
This function writes a QImage to the given \a device.
|
||||
*/
|
||||
bool QGifImage::save(QIODevice *device) const {
|
||||
Q_D(const QGifImage);
|
||||
if (device->openMode() | QIODevice::WriteOnly)
|
||||
return d->save(device);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*!
|
||||
Loads an gif image from the file with the given \a fileName. Returns \c true
|
||||
if the image was successfully loaded; otherwise invalidates the image and
|
||||
returns \c false.
|
||||
*/
|
||||
bool QGifImage::load(const QString &fileName) {
|
||||
Q_D(QGifImage);
|
||||
QFile file(fileName);
|
||||
if (file.open(QIODevice::ReadOnly))
|
||||
return d->load(&file);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*!
|
||||
\overload
|
||||
|
||||
This function reads a gif image from the given \a device. This can,
|
||||
for example, be used to load an image directly into a QByteArray.
|
||||
*/
|
||||
bool QGifImage::load(QIODevice *device) {
|
||||
Q_D(QGifImage);
|
||||
if (device->openMode() | QIODevice::ReadOnly)
|
||||
return d->load(device);
|
||||
|
||||
return false;
|
||||
}
|
||||
89
3rdparty/QtGifImage/include/gifimage/qgifimage.h
vendored
Normal file
89
3rdparty/QtGifImage/include/gifimage/qgifimage.h
vendored
Normal file
@@ -0,0 +1,89 @@
|
||||
/****************************************************************************
|
||||
** Copyright (c) 2013 Debao Zhang <hello@debao.me>
|
||||
** All right reserved.
|
||||
**
|
||||
** Permission is hereby granted, free of charge, to any person obtaining
|
||||
** a copy of this software and associated documentation files (the
|
||||
** "Software"), to deal in the Software without restriction, including
|
||||
** without limitation the rights to use, copy, modify, merge, publish,
|
||||
** distribute, sublicense, and/or sell copies of the Software, and to
|
||||
** permit persons to whom the Software is furnished to do so, subject to
|
||||
** the following conditions:
|
||||
**
|
||||
** The above copyright notice and this permission notice shall be
|
||||
** included in all copies or substantial portions of the Software.
|
||||
**
|
||||
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
**
|
||||
****************************************************************************/
|
||||
#ifndef GIF_IMAGE_Q_GIF_IMAGE_H_
|
||||
#define GIF_IMAGE_Q_GIF_IMAGE_H_
|
||||
|
||||
#include <gifimage/qgifglobal.h>
|
||||
|
||||
#include <QList>
|
||||
#include <QImage>
|
||||
#include <QColor>
|
||||
#include <QVector>
|
||||
|
||||
class QGifImagePrivate;
|
||||
|
||||
class QGifImage {
|
||||
Q_DECLARE_PRIVATE(QGifImage)
|
||||
|
||||
public:
|
||||
QGifImage();
|
||||
explicit QGifImage(const QString &fileName);
|
||||
explicit QGifImage(const QSize &size);
|
||||
|
||||
~QGifImage();
|
||||
|
||||
public:
|
||||
QVector<QRgb> globalColorTable() const;
|
||||
QColor backgroundColor() const;
|
||||
|
||||
void setGlobalColorTable(const QVector<QRgb> &colors, const QColor &bgColor = QColor());
|
||||
int defaultDelay() const;
|
||||
|
||||
void setDefaultDelay(int internal);
|
||||
QColor defaultTransparentColor() const;
|
||||
void setDefaultTransparentColor(const QColor &color);
|
||||
|
||||
int loopCount() const;
|
||||
void setLoopCount(int loop);
|
||||
|
||||
int frameCount() const;
|
||||
QImage frame(int index) const;
|
||||
|
||||
void addFrame(const QImage &frame, int delay = -1);
|
||||
void addFrame(const QImage &frame, const QPoint &offset, int delay = -1);
|
||||
|
||||
void insertFrame(int index, const QImage &frame, int delay = -1);
|
||||
void insertFrame(int index, const QImage &frame, const QPoint &offset, int delay = -1);
|
||||
|
||||
QPoint frameOffset(int index) const;
|
||||
void setFrameOffset(int index, const QPoint &offset);
|
||||
|
||||
int frameDelay(int index) const;
|
||||
void setFrameDelay(int index, int delay);
|
||||
|
||||
QColor frameTransparentColor(int index) const;
|
||||
void setFrameTransparentColor(int index, const QColor &color);
|
||||
|
||||
bool load(QIODevice *device);
|
||||
bool load(const QString &fileName);
|
||||
|
||||
bool save(QIODevice *device) const;
|
||||
bool save(const QString &fileName) const;
|
||||
|
||||
private:
|
||||
QGifImagePrivate *const d_ptr;
|
||||
};
|
||||
|
||||
#endif // GIF_IMAGE_Q_GIF_IMAGE_H_
|
||||
75
3rdparty/QtGifImage/include/gifimage/qgifimage_p.h
vendored
Normal file
75
3rdparty/QtGifImage/include/gifimage/qgifimage_p.h
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
/****************************************************************************
|
||||
** Copyright (c) 2013 Debao Zhang <hello@debao.me>
|
||||
** All right reserved.
|
||||
**
|
||||
** Permission is hereby granted, free of charge, to any person obtaining
|
||||
** a copy of this software and associated documentation files (the
|
||||
** "Software"), to deal in the Software without restriction, including
|
||||
** without limitation the rights to use, copy, modify, merge, publish,
|
||||
** distribute, sublicense, and/or sell copies of the Software, and to
|
||||
** permit persons to whom the Software is furnished to do so, subject to
|
||||
** the following conditions:
|
||||
**
|
||||
** The above copyright notice and this permission notice shall be
|
||||
** included in all copies or substantial portions of the Software.
|
||||
**
|
||||
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
**
|
||||
****************************************************************************/
|
||||
#ifndef GIF_IMAGE_Q_GIF_IMAGE_P_H_
|
||||
#define GIF_IMAGE_Q_GIF_IMAGE_P_H_
|
||||
|
||||
#include <giflib/gif_lib.h>
|
||||
#include <gifimage/qgifimage.h>
|
||||
|
||||
#include <QVector>
|
||||
#include <QColor>
|
||||
|
||||
struct QGifFrameInfoData {
|
||||
QGifFrameInfoData() : delayTime(-1), interlace(false) {}
|
||||
|
||||
public:
|
||||
QImage image;
|
||||
QPoint offset; //offset info of QImage will lost when convert from One format to another.
|
||||
int delayTime;
|
||||
bool interlace;
|
||||
QColor transparentColor;
|
||||
};
|
||||
|
||||
class QGifImagePrivate {
|
||||
Q_DECLARE_PUBLIC(QGifImage)
|
||||
|
||||
public:
|
||||
explicit QGifImagePrivate(QGifImage *p);
|
||||
~QGifImagePrivate() = default;
|
||||
|
||||
public:
|
||||
bool load(QIODevice *device);
|
||||
bool save(QIODevice *device) const;
|
||||
|
||||
QVector<QRgb> colorTableFromColorMapObject(ColorMapObject *object, int transColorIndex = -1) const;
|
||||
[[nodiscard]] ColorMapObject *colorTableToColorMapObject(QVector<QRgb> colorTable) const;
|
||||
|
||||
[[nodiscard]] QSize getCanvasSize() const;
|
||||
[[nodiscard]] int getFrameTransparentColorIndex(const QGifFrameInfoData &info) const;
|
||||
|
||||
public:
|
||||
QSize canvasSize;
|
||||
int loopCount;
|
||||
int defaultDelayTime;
|
||||
QColor defaultTransparentColor;
|
||||
|
||||
QVector<QRgb> globalColorTable;
|
||||
QColor bgColor;
|
||||
QList<QGifFrameInfoData> frameInfos;
|
||||
|
||||
QGifImage *q_ptr;
|
||||
};
|
||||
|
||||
#endif // GIF_IMAGE_Q_GIF_IMAGE_P_H_
|
||||
Reference in New Issue
Block a user