669 lines
18 KiB
C++
669 lines
18 KiB
C++
/****************************************************************************
|
|
** 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;
|
|
}
|