diff --git a/forms/mainwindow.ui b/forms/mainwindow.ui index 941a3179..f752e85d 100644 --- a/forms/mainwindow.ui +++ b/forms/mainwindow.ui @@ -831,8 +831,8 @@ 0 0 - 443 - 74 + 428 + 77 @@ -1019,10 +1019,10 @@ - 8 + 0 0 - 431 - 341 + 411 + 449 @@ -1172,8 +1172,8 @@ 0 0 - 434 - 643 + 428 + 704 @@ -1471,8 +1471,8 @@ 0 0 - 430 - 521 + 98 + 28 @@ -1516,8 +1516,8 @@ 0 0 - 430 - 521 + 98 + 28 @@ -1561,8 +1561,8 @@ 0 0 - 430 - 521 + 98 + 28 @@ -1606,8 +1606,8 @@ 0 0 - 430 - 521 + 98 + 28 @@ -1651,8 +1651,8 @@ 0 0 - 430 - 521 + 98 + 28 @@ -1702,8 +1702,8 @@ 0 0 - 430 - 521 + 98 + 28 @@ -2543,7 +2543,7 @@ true - + @@ -2593,7 +2593,7 @@ 0 0 1287 - 22 + 21 @@ -2607,6 +2607,7 @@ + @@ -2938,6 +2939,11 @@ Edit Shortcuts... + + + Export Map Timelapse Image... + + @@ -2950,6 +2956,7 @@ AdjustingStackedWidget QStackedWidget
adjustingstackedwidget.h
+ 1 GraphicsView diff --git a/forms/mapimageexporter.ui b/forms/mapimageexporter.ui index c25f6cad..d97099a7 100644 --- a/forms/mapimageexporter.ui +++ b/forms/mapimageexporter.ui @@ -33,8 +33,8 @@ 0 0 - 373 - 334 + 403 + 343 @@ -262,6 +262,61 @@
+ + + + Timelapse + + + + + + + + + ms + + + 1 + + + 2000 + + + 200 + + + + + + + Frame Delay + + + + + + + + + + 1 + + + 999 + + + + + + + Edit Frame Skip + + + + + + @@ -317,6 +372,13 @@
+ + + NoScrollComboBox + QComboBox +
noscrollcombobox.h
+
+
diff --git a/include/mainwindow.h b/include/mainwindow.h index d1631b1b..3d09c5e7 100644 --- a/include/mainwindow.h +++ b/include/mainwindow.h @@ -187,6 +187,7 @@ private slots: void on_action_Export_Map_Image_triggered(); void on_actionExport_Stitched_Map_Image_triggered(); + void on_actionExport_Map_Timelapse_Image_triggered(); void on_comboBox_ConnectionDirection_currentIndexChanged(const QString &arg1); void on_spinBox_ConnectionOffset_valueChanged(int offset); @@ -323,7 +324,7 @@ private: void connectSubEditorsToShortcutsEditor(); bool isProjectOpen(); - void showExportMapImageWindow(bool stitchMode); + void showExportMapImageWindow(ImageExporterMode mode); void redrawMetatileSelection(); QObjectList shortcutableObjects() const; diff --git a/include/ui/mapimageexporter.h b/include/ui/mapimageexporter.h index 0dedb204..3ab482c7 100644 --- a/include/ui/mapimageexporter.h +++ b/include/ui/mapimageexporter.h @@ -10,12 +10,18 @@ namespace Ui { class MapImageExporter; } +enum ImageExporterMode { + Normal, + Stitch, + Timelapse, +}; + class MapImageExporter : public QDialog { Q_OBJECT public: - explicit MapImageExporter(QWidget *parent, Editor *editor, bool stitchMode); + explicit MapImageExporter(QWidget *parent, Editor *editor, ImageExporterMode mode); ~MapImageExporter(); private: @@ -39,7 +45,9 @@ private: bool showGrid = false; bool showBorder = false; bool showCollision = false; - bool stitchMode = false; + int timelapseSkipAmount = 1; + int timelapseDelayMs = 200; + ImageExporterMode mode = ImageExporterMode::Normal; void updatePreview(); void saveImage(); @@ -65,6 +73,8 @@ private slots: void on_pushButton_Save_pressed(); void on_pushButton_Reset_pressed(); void on_pushButton_Cancel_pressed(); + void on_spinBox_TimelapseDelay_valueChanged(int delayMs); + void on_spinBox_FrameSkip_valueChanged(int skip); }; #endif // MAPIMAGEEXPORTER_H diff --git a/porymap.pro b/porymap.pro index a741f69a..1c3b2fac 100644 --- a/porymap.pro +++ b/porymap.pro @@ -176,3 +176,5 @@ INCLUDEPATH += include INCLUDEPATH += include/core INCLUDEPATH += include/ui INCLUDEPATH += include/lib + +include(src/vendor/QtGifImage/gifimage/qtgifimage.pri) diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 79c9d61b..dd7f71bf 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -2442,20 +2442,24 @@ void MainWindow::moveEvent(QMoveEvent *event) { } void MainWindow::on_action_Export_Map_Image_triggered() { - showExportMapImageWindow(false); + showExportMapImageWindow(ImageExporterMode::Normal); } void MainWindow::on_actionExport_Stitched_Map_Image_triggered() { - showExportMapImageWindow(true); + showExportMapImageWindow(ImageExporterMode::Stitch); } -void MainWindow::showExportMapImageWindow(bool stitchMode) { +void MainWindow::on_actionExport_Map_Timelapse_Image_triggered() { + showExportMapImageWindow(ImageExporterMode::Timelapse); +} + +void MainWindow::showExportMapImageWindow(ImageExporterMode mode) { if (!editor->project) return; if (this->mapImageExporter) delete this->mapImageExporter; - this->mapImageExporter = new MapImageExporter(this, this->editor, stitchMode); + this->mapImageExporter = new MapImageExporter(this, this->editor, mode); connect(this->mapImageExporter, &QObject::destroyed, [=](QObject *) { this->mapImageExporter = nullptr; }); this->mapImageExporter->setAttribute(Qt::WA_DeleteOnClose); diff --git a/src/ui/mapimageexporter.cpp b/src/ui/mapimageexporter.cpp index bb4878bd..9ee69ce0 100644 --- a/src/ui/mapimageexporter.cpp +++ b/src/ui/mapimageexporter.cpp @@ -1,5 +1,6 @@ #include "mapimageexporter.h" #include "ui_mapimageexporter.h" +#include "qgifimage.h" #include #include @@ -9,17 +10,30 @@ #define STITCH_MODE_BORDER_DISTANCE 2 -MapImageExporter::MapImageExporter(QWidget *parent_, Editor *editor_, bool stitchMode) : +QString getTitle(ImageExporterMode mode) { + switch (mode) + { + case ImageExporterMode::Normal: + return "Export Map Image"; + case ImageExporterMode::Stitch: + return "Export Map Stitch Image"; + case ImageExporterMode::Timelapse: + return "Export Map Timelapse Image"; + } + return ""; +} + +MapImageExporter::MapImageExporter(QWidget *parent_, Editor *editor_, ImageExporterMode mode) : QDialog(parent_), ui(new Ui::MapImageExporter) { ui->setupUi(this); this->map = editor_->map; this->editor = editor_; - this->stitchMode = stitchMode; - - this->setWindowTitle(this->stitchMode ? "Export Map Stitch Image" : "Export Map Image"); - this->ui->groupBox_Connections->setVisible(!this->stitchMode); + this->mode = mode; + this->setWindowTitle(getTitle(this->mode)); + this->ui->groupBox_Connections->setVisible(this->mode == ImageExporterMode::Normal); + this->ui->groupBox_Timelapse->setVisible(this->mode == ImageExporterMode::Timelapse); this->ui->comboBox_MapSelection->addItems(*editor->project->mapNames); this->ui->comboBox_MapSelection->setCurrentText(map->name); @@ -34,26 +48,118 @@ MapImageExporter::~MapImageExporter() { } void MapImageExporter::saveImage() { - QString title = this->stitchMode ? "Export Map Stitch Image" : "Export Map Image"; - QString defaultFilename = this->stitchMode ? QString("Stitch_From_%1").arg(map->name) : map->name; - QString defaultFilepath = QString("%1/%2.png").arg(editor->project->root).arg(defaultFilename); - QString filepath = QFileDialog::getSaveFileName(this, title, defaultFilepath, - "Image Files (*.png *.jpg *.bmp)"); + QString title = getTitle(this->mode); + QString defaultFilename; + switch (this->mode) + { + case ImageExporterMode::Normal: + defaultFilename = map->name; + break; + case ImageExporterMode::Stitch: + defaultFilename = QString("Stitch_From_%1").arg(map->name); + break; + case ImageExporterMode::Timelapse: + defaultFilename = QString("Timelapse_%1").arg(map->name); + break; + } + + QString defaultFilepath = QString("%1/%2.%3") + .arg(editor->project->root) + .arg(defaultFilename) + .arg(this->mode == ImageExporterMode::Timelapse ? "gif" : "png"); + QString filter = this->mode == ImageExporterMode::Timelapse ? "Image Files (*.gif)" : "Image Files (*.png *.jpg *.bmp)"; + QString filepath = QFileDialog::getSaveFileName(this, title, defaultFilepath, filter); if (!filepath.isEmpty()) { - if (this->stitchMode) { - QProgressDialog progress("Building map stitch...", "Cancel", 0, 1, this); - progress.setAutoClose(true); - progress.setWindowModality(Qt::WindowModal); - progress.setModal(true); - QPixmap pixmap = this->getStitchedImage(&progress, this->showBorder); - if (progress.wasCanceled()) { + switch (this->mode) { + case ImageExporterMode::Normal: + this->preview.save(filepath); + break; + case ImageExporterMode::Stitch: { + QProgressDialog progress("Building map stitch...", "Cancel", 0, 1, this); + progress.setAutoClose(true); + progress.setWindowModality(Qt::WindowModal); + progress.setModal(true); + QPixmap pixmap = this->getStitchedImage(&progress, this->showBorder); + if (progress.wasCanceled()) { + progress.close(); + return; + } + pixmap.save(filepath); progress.close(); - return; + break; } - pixmap.save(filepath); - progress.close(); - } else { - this->preview.save(filepath); + case ImageExporterMode::Timelapse: + QProgressDialog progress("Building map timelapse...", "Cancel", 0, 1, this); + progress.setAutoClose(true); + progress.setWindowModality(Qt::WindowModal); + progress.setModal(true); + progress.setMaximum(1); + progress.setValue(0); + + int maxWidth = this->map->getWidth() * 16; + int maxHeight = this->map->getHeight() * 16; + if (showBorder) { + maxWidth += STITCH_MODE_BORDER_DISTANCE * 16; + maxHeight += STITCH_MODE_BORDER_DISTANCE * 16; + } + // Rewind to the specified start of the map edit history. + int i = 0; + while (this->map->editHistory.canUndo()) { + progress.setValue(i); + this->map->editHistory.undo(); + int width = this->map->getWidth() * 16; + int height = this->map->getHeight() * 16; + if (showBorder) { + width += STITCH_MODE_BORDER_DISTANCE * 16; + height += STITCH_MODE_BORDER_DISTANCE * 16; + } + if (width > maxWidth) { + maxWidth = width; + } + if (height > maxHeight) { + maxHeight = height; + } + i++; + } + QGifImage timelapseImg(QSize(maxWidth, maxHeight)); + timelapseImg.setDefaultDelay(timelapseDelayMs); + timelapseImg.setDefaultTransparentColor(QColor(0, 0, 0)); + // Draw each frame, skpping the specified number of map edits in + // the undo history. + progress.setMaximum(i); + while (i > 0) { + if (progress.wasCanceled()) { + progress.close(); + while (i > 0 && this->map->editHistory.canRedo()) { + i--; + this->map->editHistory.redo(); + } + return; + } + progress.setValue(progress.maximum() - i); + QPixmap pixmap = this->getFormattedMapPixmap(this->map, !this->showBorder); + if (pixmap.width() < maxWidth || pixmap.height() < maxHeight) { + QPixmap pixmap2 = QPixmap(maxWidth, maxHeight); + QPainter painter(&pixmap2); + pixmap2.fill(QColor(0, 0, 0)); + painter.drawPixmap(0, 0, pixmap.width(), pixmap.height(), pixmap); + painter.end(); + pixmap = pixmap2; + } + timelapseImg.addFrame(pixmap.toImage()); + for (int j = 0; j < timelapseSkipAmount; j++) { + if (i > 0) { + i--; + this->map->editHistory.redo(); + } + } + } + // The latest map state is the last animated frame. + QPixmap pixmap = this->getFormattedMapPixmap(this->map, !this->showBorder); + timelapseImg.addFrame(pixmap.toImage()); + timelapseImg.save(filepath); + progress.close(); + break; } this->close(); } @@ -236,7 +342,7 @@ QPixmap MapImageExporter::getFormattedMapPixmap(Map *map, bool ignoreBorder) { int borderHeight = 0, borderWidth = 0; bool forceDrawBorder = showUpConnections || showDownConnections || showLeftConnections || showRightConnections; if (!ignoreBorder && (showBorder || forceDrawBorder)) { - int borderDistance = this->stitchMode ? STITCH_MODE_BORDER_DISTANCE : BORDER_DISTANCE; + int borderDistance = this->mode ? STITCH_MODE_BORDER_DISTANCE : BORDER_DISTANCE; map->renderBorder(); int borderHorzDist = editor->getBorderDrawDistance(map->getBorderWidth()); int borderVertDist = editor->getBorderDrawDistance(map->getBorderHeight()); @@ -255,7 +361,7 @@ QPixmap MapImageExporter::getFormattedMapPixmap(Map *map, bool ignoreBorder) { pixmap = newPixmap; } - if (!this->stitchMode) { + if (!this->mode) { // if showing connections, draw on outside of image QPainter connectionPainter(&pixmap); for (auto connectionItem : editor->connection_edit_items) { @@ -365,3 +471,11 @@ void MapImageExporter::on_pushButton_Reset_pressed() { void MapImageExporter::on_pushButton_Cancel_pressed() { this->close(); } + +void MapImageExporter::on_spinBox_TimelapseDelay_valueChanged(int delayMs) { + timelapseDelayMs = delayMs; +} + +void MapImageExporter::on_spinBox_FrameSkip_valueChanged(int skip) { + timelapseSkipAmount = skip; +} diff --git a/src/vendor/QtGifImage/3rdParty/giflib.pri b/src/vendor/QtGifImage/3rdParty/giflib.pri new file mode 100644 index 00000000..1a9f43f7 --- /dev/null +++ b/src/vendor/QtGifImage/3rdParty/giflib.pri @@ -0,0 +1,9 @@ + +INCLUDEPATH += $$PWD/giflib + +SOURCES += $$PWD/giflib/dgif_lib.c \ + $$PWD/giflib/egif_lib.c \ + $$PWD/giflib/gif_err.c \ + $$PWD/giflib/gif_hash.c \ + $$PWD/giflib/gifalloc.c \ + $$PWD/giflib/quantize.c diff --git a/src/vendor/QtGifImage/3rdParty/giflib/AUTHORS b/src/vendor/QtGifImage/3rdParty/giflib/AUTHORS new file mode 100644 index 00000000..78611cac --- /dev/null +++ b/src/vendor/QtGifImage/3rdParty/giflib/AUTHORS @@ -0,0 +1,36 @@ +Michael Brown + callbacks to write data via user defined function + +Daniel Eisenbud + Fixes for crashes with invalid gif files and double freeing of + colormaps + +Gershon Elber + original giflib code + +Marc Ewing + spec file (for rpms) updates + +Toshio Kuratomi + uncompressed gif writing code + autoconf/automake process + former maintainer + +marek + Gif initialization fix + windows build code + +Peter Mehlitz + callbacks to read data from arbitrary sources (like libjpeg/libpng) + +Dick Porter + int/pointer fixes for Alpha + +Eric Raymond + current as well as long time former maintainer of giflib code + +Petter Reinholdtsen + Tru64 build fixs + +Georg Schwarz + IRIX fixes diff --git a/src/vendor/QtGifImage/3rdParty/giflib/COPYING b/src/vendor/QtGifImage/3rdParty/giflib/COPYING new file mode 100644 index 00000000..b9c0b501 --- /dev/null +++ b/src/vendor/QtGifImage/3rdParty/giflib/COPYING @@ -0,0 +1,19 @@ +The GIFLIB distribution is Copyright (c) 1997 Eric S. Raymond + +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. diff --git a/src/vendor/QtGifImage/3rdParty/giflib/README b/src/vendor/QtGifImage/3rdParty/giflib/README new file mode 100644 index 00000000..38a9233c --- /dev/null +++ b/src/vendor/QtGifImage/3rdParty/giflib/README @@ -0,0 +1,27 @@ += GIFLIB = + +This is the README file of GIFLIB, a library for manipulating GIF files. + +Latest versions of GIFLIB are currently hosted at: + http://sourceforge.net/projects/giflib + +== Overview == + +GIF is a legacy format; we recommend against generating new images in +it. For a cleaner, more extensible design with better color support +and compression, look up PNG. + +giflib provides code for reading GIF files and transforming them into +RGB bitmaps, and for writing RGB bitmaps as GIF files. + +The (permissive) open-source license is in the file COPYING. + +You will find build instructions in build.asc + +You will find full documentation of the API in doc/ and on the +project website. + +The project has a long and confusing history, described in history.asc + +The project to-do list is in TODO. + diff --git a/src/vendor/QtGifImage/3rdParty/giflib/dgif_lib.c b/src/vendor/QtGifImage/3rdParty/giflib/dgif_lib.c new file mode 100644 index 00000000..7c5ca295 --- /dev/null +++ b/src/vendor/QtGifImage/3rdParty/giflib/dgif_lib.c @@ -0,0 +1,1169 @@ +/****************************************************************************** + +dgif_lib.c - GIF decoding + +The functions here and in egif_lib.c are partitioned carefully so that +if you only require one of read and write capability, only one of these +two modules will be linked. Preserve this property! + +*****************************************************************************/ + +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#else +#include +#endif /* _WIN32 */ + +#include "gif_lib.h" +#include "gif_lib_private.h" + +/* compose unsigned little endian value */ +#define UNSIGNED_LITTLE_ENDIAN(lo, hi) ((lo) | ((hi) << 8)) + +/* avoid extra function call in case we use fread (TVT) */ +#define READ(_gif,_buf,_len) \ + (((GifFilePrivateType*)_gif->Private)->Read ? \ + ((GifFilePrivateType*)_gif->Private)->Read(_gif,_buf,_len) : \ + fread(_buf,1,_len,((GifFilePrivateType*)_gif->Private)->File)) + +static int DGifGetWord(GifFileType *GifFile, GifWord *Word); +static int DGifSetupDecompress(GifFileType *GifFile); +static int DGifDecompressLine(GifFileType *GifFile, GifPixelType *Line, + int LineLen); +static int DGifGetPrefixChar(GifPrefixType *Prefix, int Code, int ClearCode); +static int DGifDecompressInput(GifFileType *GifFile, int *Code); +static int DGifBufferedInput(GifFileType *GifFile, GifByteType *Buf, + GifByteType *NextByte); + +/****************************************************************************** + Open a new GIF file for read, given by its name. + Returns dynamically allocated GifFileType pointer which serves as the GIF + info record. +******************************************************************************/ +GifFileType * +DGifOpenFileName(const char *FileName, int *Error) +{ + int FileHandle; + GifFileType *GifFile; + + if ((FileHandle = open(FileName, O_RDONLY)) == -1) { + if (Error != NULL) + *Error = D_GIF_ERR_OPEN_FAILED; + return NULL; + } + + GifFile = DGifOpenFileHandle(FileHandle, Error); + return GifFile; +} + +/****************************************************************************** + Update a new GIF file, given its file handle. + Returns dynamically allocated GifFileType pointer which serves as the GIF + info record. +******************************************************************************/ +GifFileType * +DGifOpenFileHandle(int FileHandle, int *Error) +{ + char Buf[GIF_STAMP_LEN + 1]; + GifFileType *GifFile; + GifFilePrivateType *Private; + FILE *f; + + GifFile = (GifFileType *)malloc(sizeof(GifFileType)); + if (GifFile == NULL) { + if (Error != NULL) + *Error = D_GIF_ERR_NOT_ENOUGH_MEM; + (void)close(FileHandle); + return NULL; + } + + /*@i1@*/memset(GifFile, '\0', sizeof(GifFileType)); + + /* Belt and suspenders, in case the null pointer isn't zero */ + GifFile->SavedImages = NULL; + GifFile->SColorMap = NULL; + + Private = (GifFilePrivateType *)malloc(sizeof(GifFilePrivateType)); + if (Private == NULL) { + if (Error != NULL) + *Error = D_GIF_ERR_NOT_ENOUGH_MEM; + (void)close(FileHandle); + free((char *)GifFile); + return NULL; + } +#ifdef _WIN32 + _setmode(FileHandle, O_BINARY); /* Make sure it is in binary mode. */ +#endif /* _WIN32 */ + + f = fdopen(FileHandle, "rb"); /* Make it into a stream: */ + + /*@-mustfreeonly@*/ + GifFile->Private = (void *)Private; + Private->FileHandle = FileHandle; + Private->File = f; + Private->FileState = FILE_STATE_READ; + Private->Read = NULL; /* don't use alternate input method (TVT) */ + GifFile->UserData = NULL; /* TVT */ + /*@=mustfreeonly@*/ + + /* Let's see if this is a GIF file: */ + if (READ(GifFile, (unsigned char *)Buf, GIF_STAMP_LEN) != GIF_STAMP_LEN) { + if (Error != NULL) + *Error = D_GIF_ERR_READ_FAILED; + (void)fclose(f); + free((char *)Private); + free((char *)GifFile); + return NULL; + } + + /* Check for GIF prefix at start of file */ + Buf[GIF_STAMP_LEN] = 0; + if (strncmp(GIF_STAMP, Buf, GIF_VERSION_POS) != 0) { + if (Error != NULL) + *Error = D_GIF_ERR_NOT_GIF_FILE; + (void)fclose(f); + free((char *)Private); + free((char *)GifFile); + return NULL; + } + + if (DGifGetScreenDesc(GifFile) == GIF_ERROR) { + (void)fclose(f); + free((char *)Private); + free((char *)GifFile); + return NULL; + } + + GifFile->Error = 0; + + /* What version of GIF? */ + Private->gif89 = (Buf[GIF_VERSION_POS] == '9'); + + return GifFile; +} + +/****************************************************************************** + GifFileType constructor with user supplied input function (TVT) +******************************************************************************/ +GifFileType * +DGifOpen(void *userData, InputFunc readFunc, int *Error) +{ + char Buf[GIF_STAMP_LEN + 1]; + GifFileType *GifFile; + GifFilePrivateType *Private; + + GifFile = (GifFileType *)malloc(sizeof(GifFileType)); + if (GifFile == NULL) { + if (Error != NULL) + *Error = D_GIF_ERR_NOT_ENOUGH_MEM; + return NULL; + } + + memset(GifFile, '\0', sizeof(GifFileType)); + + /* Belt and suspenders, in case the null pointer isn't zero */ + GifFile->SavedImages = NULL; + GifFile->SColorMap = NULL; + + Private = (GifFilePrivateType *)malloc(sizeof(GifFilePrivateType)); + if (!Private) { + if (Error != NULL) + *Error = D_GIF_ERR_NOT_ENOUGH_MEM; + free((char *)GifFile); + return NULL; + } + + GifFile->Private = (void *)Private; + Private->FileHandle = 0; + Private->File = NULL; + Private->FileState = FILE_STATE_READ; + + Private->Read = readFunc; /* TVT */ + GifFile->UserData = userData; /* TVT */ + + /* Lets see if this is a GIF file: */ + if (READ(GifFile, (unsigned char *)Buf, GIF_STAMP_LEN) != GIF_STAMP_LEN) { + if (Error != NULL) + *Error = D_GIF_ERR_READ_FAILED; + free((char *)Private); + free((char *)GifFile); + return NULL; + } + + /* Check for GIF prefix at start of file */ + Buf[GIF_STAMP_LEN] = '\0'; + if (strncmp(GIF_STAMP, Buf, GIF_VERSION_POS) != 0) { + if (Error != NULL) + *Error = D_GIF_ERR_NOT_GIF_FILE; + free((char *)Private); + free((char *)GifFile); + return NULL; + } + + if (DGifGetScreenDesc(GifFile) == GIF_ERROR) { + free((char *)Private); + free((char *)GifFile); + *Error = D_GIF_ERR_NO_SCRN_DSCR; + return NULL; + } + + GifFile->Error = 0; + + /* What version of GIF? */ + Private->gif89 = (Buf[GIF_VERSION_POS] == '9'); + + return GifFile; +} + +/****************************************************************************** + This routine should be called before any other DGif calls. Note that + this routine is called automatically from DGif file open routines. +******************************************************************************/ +int +DGifGetScreenDesc(GifFileType *GifFile) +{ + int BitsPerPixel; + BOOL SortFlag; + GifByteType Buf[3]; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + if (!IS_READABLE(Private)) { + /* This file was NOT open for reading: */ + GifFile->Error = D_GIF_ERR_NOT_READABLE; + return GIF_ERROR; + } + + /* Put the screen descriptor into the file: */ + if (DGifGetWord(GifFile, &GifFile->SWidth) == GIF_ERROR || + DGifGetWord(GifFile, &GifFile->SHeight) == GIF_ERROR) + return GIF_ERROR; + + if (READ(GifFile, Buf, 3) != 3) { + GifFile->Error = D_GIF_ERR_READ_FAILED; + GifFreeMapObject(GifFile->SColorMap); + GifFile->SColorMap = NULL; + return GIF_ERROR; + } + GifFile->SColorResolution = (((Buf[0] & 0x70) + 1) >> 4) + 1; + SortFlag = (Buf[0] & 0x08) != 0; + BitsPerPixel = (Buf[0] & 0x07) + 1; + GifFile->SBackGroundColor = Buf[1]; + GifFile->AspectByte = Buf[2]; + if (Buf[0] & 0x80) { /* Do we have global color map? */ + int i; + + GifFile->SColorMap = GifMakeMapObject(1 << BitsPerPixel, NULL); + if (GifFile->SColorMap == NULL) { + GifFile->Error = D_GIF_ERR_NOT_ENOUGH_MEM; + return GIF_ERROR; + } + + /* Get the global color map: */ + GifFile->SColorMap->SortFlag = SortFlag; + for (i = 0; i < GifFile->SColorMap->ColorCount; i++) { + if (READ(GifFile, Buf, 3) != 3) { + GifFreeMapObject(GifFile->SColorMap); + GifFile->SColorMap = NULL; + GifFile->Error = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + GifFile->SColorMap->Colors[i].Red = Buf[0]; + GifFile->SColorMap->Colors[i].Green = Buf[1]; + GifFile->SColorMap->Colors[i].Blue = Buf[2]; + } + } else { + GifFile->SColorMap = NULL; + } + + return GIF_OK; +} + +/****************************************************************************** + This routine should be called before any attempt to read an image. +******************************************************************************/ +int +DGifGetRecordType(GifFileType *GifFile, GifRecordType* Type) +{ + GifByteType Buf; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + if (!IS_READABLE(Private)) { + /* This file was NOT open for reading: */ + GifFile->Error = D_GIF_ERR_NOT_READABLE; + return GIF_ERROR; + } + + if (READ(GifFile, &Buf, 1) != 1) { + GifFile->Error = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + + switch (Buf) { + case DESCRIPTOR_INTRODUCER: + *Type = IMAGE_DESC_RECORD_TYPE; + break; + case EXTENSION_INTRODUCER: + *Type = EXTENSION_RECORD_TYPE; + break; + case TERMINATOR_INTRODUCER: + *Type = TERMINATE_RECORD_TYPE; + break; + default: + *Type = UNDEFINED_RECORD_TYPE; + GifFile->Error = D_GIF_ERR_WRONG_RECORD; + return GIF_ERROR; + } + + return GIF_OK; +} + +/****************************************************************************** + This routine should be called before any attempt to read an image. + Note it is assumed the Image desc. header has been read. +******************************************************************************/ +int +DGifGetImageDesc(GifFileType *GifFile) +{ + unsigned int BitsPerPixel; + GifByteType Buf[3]; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + SavedImage *sp; + + if (!IS_READABLE(Private)) { + /* This file was NOT open for reading: */ + GifFile->Error = D_GIF_ERR_NOT_READABLE; + return GIF_ERROR; + } + + if (DGifGetWord(GifFile, &GifFile->Image.Left) == GIF_ERROR || + DGifGetWord(GifFile, &GifFile->Image.Top) == GIF_ERROR || + DGifGetWord(GifFile, &GifFile->Image.Width) == GIF_ERROR || + DGifGetWord(GifFile, &GifFile->Image.Height) == GIF_ERROR) + return GIF_ERROR; + if (READ(GifFile, Buf, 1) != 1) { + GifFile->Error = D_GIF_ERR_READ_FAILED; + GifFreeMapObject(GifFile->Image.ColorMap); + GifFile->Image.ColorMap = NULL; + return GIF_ERROR; + } + BitsPerPixel = (Buf[0] & 0x07) + 1; + GifFile->Image.Interlace = (Buf[0] & 0x40) ? TRUE : FALSE; + + /* Setup the colormap */ + if (GifFile->Image.ColorMap) { + GifFreeMapObject(GifFile->Image.ColorMap); + GifFile->Image.ColorMap = NULL; + } + /* Does this image have local color map? */ + if (Buf[0] & 0x80) { + unsigned int i; + + GifFile->Image.ColorMap = GifMakeMapObject(1 << BitsPerPixel, NULL); + if (GifFile->Image.ColorMap == NULL) { + GifFile->Error = D_GIF_ERR_NOT_ENOUGH_MEM; + return GIF_ERROR; + } + + /* Get the image local color map: */ + for (i = 0; i < GifFile->Image.ColorMap->ColorCount; i++) { + if (READ(GifFile, Buf, 3) != 3) { + GifFreeMapObject(GifFile->Image.ColorMap); + GifFile->Error = D_GIF_ERR_READ_FAILED; + GifFile->Image.ColorMap = NULL; + return GIF_ERROR; + } + GifFile->Image.ColorMap->Colors[i].Red = Buf[0]; + GifFile->Image.ColorMap->Colors[i].Green = Buf[1]; + GifFile->Image.ColorMap->Colors[i].Blue = Buf[2]; + } + } + + if (GifFile->SavedImages) { + if ((GifFile->SavedImages = (SavedImage *)realloc(GifFile->SavedImages, + sizeof(SavedImage) * + (GifFile->ImageCount + 1))) == NULL) { + GifFile->Error = D_GIF_ERR_NOT_ENOUGH_MEM; + return GIF_ERROR; + } + } else { + if ((GifFile->SavedImages = + (SavedImage *) malloc(sizeof(SavedImage))) == NULL) { + GifFile->Error = D_GIF_ERR_NOT_ENOUGH_MEM; + return GIF_ERROR; + } + } + + sp = &GifFile->SavedImages[GifFile->ImageCount]; + memcpy(&sp->ImageDesc, &GifFile->Image, sizeof(GifImageDesc)); + if (GifFile->Image.ColorMap != NULL) { + sp->ImageDesc.ColorMap = GifMakeMapObject( + GifFile->Image.ColorMap->ColorCount, + GifFile->Image.ColorMap->Colors); + if (sp->ImageDesc.ColorMap == NULL) { + GifFile->Error = D_GIF_ERR_NOT_ENOUGH_MEM; + return GIF_ERROR; + } + } + sp->RasterBits = (unsigned char *)NULL; + sp->ExtensionBlockCount = 0; + sp->ExtensionBlocks = (ExtensionBlock *) NULL; + + GifFile->ImageCount++; + + Private->PixelCount = (long)GifFile->Image.Width * + (long)GifFile->Image.Height; + + /* Reset decompress algorithm parameters. */ + (void)DGifSetupDecompress(GifFile); + + return GIF_OK; +} + +/****************************************************************************** + Get one full scanned line (Line) of length LineLen from GIF file. +******************************************************************************/ +int +DGifGetLine(GifFileType *GifFile, GifPixelType *Line, int LineLen) +{ + GifByteType *Dummy; + GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private; + + if (!IS_READABLE(Private)) { + /* This file was NOT open for reading: */ + GifFile->Error = D_GIF_ERR_NOT_READABLE; + return GIF_ERROR; + } + + if (!LineLen) + LineLen = GifFile->Image.Width; + + if ((Private->PixelCount -= LineLen) > 0xffff0000UL) { + GifFile->Error = D_GIF_ERR_DATA_TOO_BIG; + return GIF_ERROR; + } + + if (DGifDecompressLine(GifFile, Line, LineLen) == GIF_OK) { + if (Private->PixelCount == 0) { + /* We probably won't be called any more, so let's clean up + * everything before we return: need to flush out all the + * rest of image until an empty block (size 0) + * detected. We use GetCodeNext. + */ + do + if (DGifGetCodeNext(GifFile, &Dummy) == GIF_ERROR) + return GIF_ERROR; + while (Dummy != NULL) ; + } + return GIF_OK; + } else + return GIF_ERROR; +} + +/****************************************************************************** + Put one pixel (Pixel) into GIF file. +******************************************************************************/ +int +DGifGetPixel(GifFileType *GifFile, GifPixelType Pixel) +{ + GifByteType *Dummy; + GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private; + + if (!IS_READABLE(Private)) { + /* This file was NOT open for reading: */ + GifFile->Error = D_GIF_ERR_NOT_READABLE; + return GIF_ERROR; + } + if (--Private->PixelCount > 0xffff0000UL) + { + GifFile->Error = D_GIF_ERR_DATA_TOO_BIG; + return GIF_ERROR; + } + + if (DGifDecompressLine(GifFile, &Pixel, 1) == GIF_OK) { + if (Private->PixelCount == 0) { + /* We probably won't be called any more, so let's clean up + * everything before we return: need to flush out all the + * rest of image until an empty block (size 0) + * detected. We use GetCodeNext. + */ + do + if (DGifGetCodeNext(GifFile, &Dummy) == GIF_ERROR) + return GIF_ERROR; + while (Dummy != NULL) ; + } + return GIF_OK; + } else + return GIF_ERROR; +} + +/****************************************************************************** + Get an extension block (see GIF manual) from GIF file. This routine only + returns the first data block, and DGifGetExtensionNext should be called + after this one until NULL extension is returned. + The Extension should NOT be freed by the user (not dynamically allocated). + Note it is assumed the Extension description header has been read. +******************************************************************************/ +int +DGifGetExtension(GifFileType *GifFile, int *ExtCode, GifByteType **Extension) +{ + GifByteType Buf; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + if (!IS_READABLE(Private)) { + /* This file was NOT open for reading: */ + GifFile->Error = D_GIF_ERR_NOT_READABLE; + return GIF_ERROR; + } + + if (READ(GifFile, &Buf, 1) != 1) { + GifFile->Error = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + *ExtCode = Buf; + + return DGifGetExtensionNext(GifFile, Extension); +} + +/****************************************************************************** + Get a following extension block (see GIF manual) from GIF file. This + routine should be called until NULL Extension is returned. + The Extension should NOT be freed by the user (not dynamically allocated). +******************************************************************************/ +int +DGifGetExtensionNext(GifFileType *GifFile, GifByteType ** Extension) +{ + GifByteType Buf; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + if (READ(GifFile, &Buf, 1) != 1) { + GifFile->Error = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + if (Buf > 0) { + *Extension = Private->Buf; /* Use private unused buffer. */ + (*Extension)[0] = Buf; /* Pascal strings notation (pos. 0 is len.). */ + /* coverity[tainted_data] */ + if (READ(GifFile, &((*Extension)[1]), Buf) != Buf) { + GifFile->Error = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + } else + *Extension = NULL; + + return GIF_OK; +} + +/****************************************************************************** + Extract a Graphics Control Block from raw extension data +******************************************************************************/ + +int DGifExtensionToGCB(const size_t GifExtensionLength, + const GifByteType *GifExtension, + GraphicsControlBlock *GCB) +{ + if (GifExtensionLength != 4) { + return GIF_ERROR; + } + + GCB->DisposalMode = (GifExtension[0] >> 2) & 0x07; + GCB->UserInputFlag = (GifExtension[0] & 0x02) != 0; + GCB->DelayTime = UNSIGNED_LITTLE_ENDIAN(GifExtension[1], GifExtension[2]); + if (GifExtension[0] & 0x01) + GCB->TransparentColor = (int)GifExtension[3]; + else + GCB->TransparentColor = NO_TRANSPARENT_COLOR; + + return GIF_OK; +} + +/****************************************************************************** + Extract the Graphics Control Block for a saved image, if it exists. +******************************************************************************/ + +int DGifSavedExtensionToGCB(GifFileType *GifFile, + int ImageIndex, GraphicsControlBlock *GCB) +{ + int i; + + if (ImageIndex < 0 || ImageIndex > GifFile->ImageCount - 1) + return GIF_ERROR; + + GCB->DisposalMode = DISPOSAL_UNSPECIFIED; + GCB->UserInputFlag = FALSE; + GCB->DelayTime = 0; + GCB->TransparentColor = NO_TRANSPARENT_COLOR; + + for (i = 0; i < GifFile->SavedImages[ImageIndex].ExtensionBlockCount; i++) { + ExtensionBlock *ep = &GifFile->SavedImages[ImageIndex].ExtensionBlocks[i]; + if (ep->Function == GRAPHICS_EXT_FUNC_CODE) + return DGifExtensionToGCB(ep->ByteCount, ep->Bytes, GCB); + } + + return GIF_ERROR; +} + +/****************************************************************************** + This routine should be called last, to close the GIF file. +******************************************************************************/ +int +DGifCloseFile(GifFileType *GifFile) +{ + GifFilePrivateType *Private; + + if (GifFile == NULL || GifFile->Private == NULL) + return GIF_ERROR; + + if (GifFile->Image.ColorMap) { + GifFreeMapObject(GifFile->Image.ColorMap); + GifFile->Image.ColorMap = NULL; + } + + if (GifFile->SColorMap) { + GifFreeMapObject(GifFile->SColorMap); + GifFile->SColorMap = NULL; + } + + if (GifFile->SavedImages) { + GifFreeSavedImages(GifFile); + GifFile->SavedImages = NULL; + } + + GifFreeExtensions(&GifFile->ExtensionBlockCount, &GifFile->ExtensionBlocks); + + Private = (GifFilePrivateType *) GifFile->Private; + + if (!IS_READABLE(Private)) { + /* This file was NOT open for reading: */ + GifFile->Error = D_GIF_ERR_NOT_READABLE; + return GIF_ERROR; + } + + if (Private->File && (fclose(Private->File) != 0)) { + GifFile->Error = D_GIF_ERR_CLOSE_FAILED; + return GIF_ERROR; + } + + free((char *)GifFile->Private); + + /* + * Without the #ifndef, we get spurious warnings because Coverity mistakenly + * thinks the GIF structure is freed on an error return. + */ +#ifndef __COVERITY__ + free(GifFile); +#endif /* __COVERITY__ */ + + return GIF_OK; +} + +/****************************************************************************** + Get 2 bytes (word) from the given file: +******************************************************************************/ +static int +DGifGetWord(GifFileType *GifFile, GifWord *Word) +{ + unsigned char c[2]; + + if (READ(GifFile, c, 2) != 2) { + GifFile->Error = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + + *Word = (GifWord)UNSIGNED_LITTLE_ENDIAN(c[0], c[1]); + return GIF_OK; +} + +/****************************************************************************** + Get the image code in compressed form. This routine can be called if the + information needed to be piped out as is. Obviously this is much faster + than decoding and encoding again. This routine should be followed by calls + to DGifGetCodeNext, until NULL block is returned. + The block should NOT be freed by the user (not dynamically allocated). +******************************************************************************/ +int +DGifGetCode(GifFileType *GifFile, int *CodeSize, GifByteType **CodeBlock) +{ + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + if (!IS_READABLE(Private)) { + /* This file was NOT open for reading: */ + GifFile->Error = D_GIF_ERR_NOT_READABLE; + return GIF_ERROR; + } + + *CodeSize = Private->BitsPerPixel; + + return DGifGetCodeNext(GifFile, CodeBlock); +} + +/****************************************************************************** + Continue to get the image code in compressed form. This routine should be + called until NULL block is returned. + The block should NOT be freed by the user (not dynamically allocated). +******************************************************************************/ +int +DGifGetCodeNext(GifFileType *GifFile, GifByteType **CodeBlock) +{ + GifByteType Buf; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + /* coverity[tainted_data_argument] */ + if (READ(GifFile, &Buf, 1) != 1) { + GifFile->Error = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + + /* coverity[lower_bounds] */ + if (Buf > 0) { + *CodeBlock = Private->Buf; /* Use private unused buffer. */ + (*CodeBlock)[0] = Buf; /* Pascal strings notation (pos. 0 is len.). */ + /* coverity[tainted_data] */ + if (READ(GifFile, &((*CodeBlock)[1]), Buf) != Buf) { + GifFile->Error = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + } else { + *CodeBlock = NULL; + Private->Buf[0] = 0; /* Make sure the buffer is empty! */ + Private->PixelCount = 0; /* And local info. indicate image read. */ + } + + return GIF_OK; +} + +/****************************************************************************** + Setup the LZ decompression for this image: +******************************************************************************/ +static int +DGifSetupDecompress(GifFileType *GifFile) +{ + int i, BitsPerPixel; + GifByteType CodeSize; + GifPrefixType *Prefix; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + READ(GifFile, &CodeSize, 1); /* Read Code size from file. */ + BitsPerPixel = CodeSize; + + Private->Buf[0] = 0; /* Input Buffer empty. */ + Private->BitsPerPixel = BitsPerPixel; + Private->ClearCode = (1 << BitsPerPixel); + Private->EOFCode = Private->ClearCode + 1; + Private->RunningCode = Private->EOFCode + 1; + Private->RunningBits = BitsPerPixel + 1; /* Number of bits per code. */ + Private->MaxCode1 = 1 << Private->RunningBits; /* Max. code + 1. */ + Private->StackPtr = 0; /* No pixels on the pixel stack. */ + Private->LastCode = NO_SUCH_CODE; + Private->CrntShiftState = 0; /* No information in CrntShiftDWord. */ + Private->CrntShiftDWord = 0; + + Prefix = Private->Prefix; + for (i = 0; i <= LZ_MAX_CODE; i++) + Prefix[i] = NO_SUCH_CODE; + + return GIF_OK; +} + +/****************************************************************************** + The LZ decompression routine: + This version decompress the given GIF file into Line of length LineLen. + This routine can be called few times (one per scan line, for example), in + order the complete the whole image. +******************************************************************************/ +static int +DGifDecompressLine(GifFileType *GifFile, GifPixelType *Line, int LineLen) +{ + int i = 0; + int j, CrntCode, EOFCode, ClearCode, CrntPrefix, LastCode, StackPtr; + GifByteType *Stack, *Suffix; + GifPrefixType *Prefix; + GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private; + + StackPtr = Private->StackPtr; + Prefix = Private->Prefix; + Suffix = Private->Suffix; + Stack = Private->Stack; + EOFCode = Private->EOFCode; + ClearCode = Private->ClearCode; + LastCode = Private->LastCode; + + if (StackPtr > LZ_MAX_CODE) { + return GIF_ERROR; + } + + if (StackPtr != 0) { + /* Let pop the stack off before continueing to read the GIF file: */ + while (StackPtr != 0 && i < LineLen) + Line[i++] = Stack[--StackPtr]; + } + + while (i < LineLen) { /* Decode LineLen items. */ + if (DGifDecompressInput(GifFile, &CrntCode) == GIF_ERROR) + return GIF_ERROR; + + if (CrntCode == EOFCode) { + /* Note however that usually we will not be here as we will stop + * decoding as soon as we got all the pixel, or EOF code will + * not be read at all, and DGifGetLine/Pixel clean everything. */ + GifFile->Error = D_GIF_ERR_EOF_TOO_SOON; + return GIF_ERROR; + } else if (CrntCode == ClearCode) { + /* We need to start over again: */ + for (j = 0; j <= LZ_MAX_CODE; j++) + Prefix[j] = NO_SUCH_CODE; + Private->RunningCode = Private->EOFCode + 1; + Private->RunningBits = Private->BitsPerPixel + 1; + Private->MaxCode1 = 1 << Private->RunningBits; + LastCode = Private->LastCode = NO_SUCH_CODE; + } else { + /* Its regular code - if in pixel range simply add it to output + * stream, otherwise trace to codes linked list until the prefix + * is in pixel range: */ + if (CrntCode < ClearCode) { + /* This is simple - its pixel scalar, so add it to output: */ + Line[i++] = CrntCode; + } else { + /* Its a code to needed to be traced: trace the linked list + * until the prefix is a pixel, while pushing the suffix + * pixels on our stack. If we done, pop the stack in reverse + * (thats what stack is good for!) order to output. */ + if (Prefix[CrntCode] == NO_SUCH_CODE) { + /* Only allowed if CrntCode is exactly the running code: + * In that case CrntCode = XXXCode, CrntCode or the + * prefix code is last code and the suffix char is + * exactly the prefix of last code! */ + if (CrntCode == Private->RunningCode - 2) { + CrntPrefix = LastCode; + Suffix[Private->RunningCode - 2] = + Stack[StackPtr++] = DGifGetPrefixChar(Prefix, + LastCode, + ClearCode); + } else { + GifFile->Error = D_GIF_ERR_IMAGE_DEFECT; + return GIF_ERROR; + } + } else + CrntPrefix = CrntCode; + + /* Now (if image is O.K.) we should not get a NO_SUCH_CODE + * during the trace. As we might loop forever, in case of + * defective image, we use StackPtr as loop counter and stop + * before overflowing Stack[]. */ + while (StackPtr < LZ_MAX_CODE && + CrntPrefix > ClearCode && CrntPrefix <= LZ_MAX_CODE) { + Stack[StackPtr++] = Suffix[CrntPrefix]; + CrntPrefix = Prefix[CrntPrefix]; + } + if (StackPtr >= LZ_MAX_CODE || CrntPrefix > LZ_MAX_CODE) { + GifFile->Error = D_GIF_ERR_IMAGE_DEFECT; + return GIF_ERROR; + } + /* Push the last character on stack: */ + Stack[StackPtr++] = CrntPrefix; + + /* Now lets pop all the stack into output: */ + while (StackPtr != 0 && i < LineLen) + Line[i++] = Stack[--StackPtr]; + } + if (LastCode != NO_SUCH_CODE && Prefix[Private->RunningCode - 2] == NO_SUCH_CODE) { + Prefix[Private->RunningCode - 2] = LastCode; + + if (CrntCode == Private->RunningCode - 2) { + /* Only allowed if CrntCode is exactly the running code: + * In that case CrntCode = XXXCode, CrntCode or the + * prefix code is last code and the suffix char is + * exactly the prefix of last code! */ + Suffix[Private->RunningCode - 2] = + DGifGetPrefixChar(Prefix, LastCode, ClearCode); + } else { + Suffix[Private->RunningCode - 2] = + DGifGetPrefixChar(Prefix, CrntCode, ClearCode); + } + } + LastCode = CrntCode; + } + } + + Private->LastCode = LastCode; + Private->StackPtr = StackPtr; + + return GIF_OK; +} + +/****************************************************************************** + Routine to trace the Prefixes linked list until we get a prefix which is + not code, but a pixel value (less than ClearCode). Returns that pixel value. + If image is defective, we might loop here forever, so we limit the loops to + the maximum possible if image O.k. - LZ_MAX_CODE times. +******************************************************************************/ +static int +DGifGetPrefixChar(GifPrefixType *Prefix, int Code, int ClearCode) +{ + int i = 0; + + while (Code > ClearCode && i++ <= LZ_MAX_CODE) { + if (Code > LZ_MAX_CODE) { + return NO_SUCH_CODE; + } + Code = Prefix[Code]; + } + return Code; +} + +/****************************************************************************** + Interface for accessing the LZ codes directly. Set Code to the real code + (12bits), or to -1 if EOF code is returned. +******************************************************************************/ +int +DGifGetLZCodes(GifFileType *GifFile, int *Code) +{ + GifByteType *CodeBlock; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + if (!IS_READABLE(Private)) { + /* This file was NOT open for reading: */ + GifFile->Error = D_GIF_ERR_NOT_READABLE; + return GIF_ERROR; + } + + if (DGifDecompressInput(GifFile, Code) == GIF_ERROR) + return GIF_ERROR; + + if (*Code == Private->EOFCode) { + /* Skip rest of codes (hopefully only NULL terminating block): */ + do { + if (DGifGetCodeNext(GifFile, &CodeBlock) == GIF_ERROR) + return GIF_ERROR; + } while (CodeBlock != NULL) ; + + *Code = -1; + } else if (*Code == Private->ClearCode) { + /* We need to start over again: */ + Private->RunningCode = Private->EOFCode + 1; + Private->RunningBits = Private->BitsPerPixel + 1; + Private->MaxCode1 = 1 << Private->RunningBits; + } + + return GIF_OK; +} + +/****************************************************************************** + The LZ decompression input routine: + This routine is responsable for the decompression of the bit stream from + 8 bits (bytes) packets, into the real codes. + Returns GIF_OK if read successfully. +******************************************************************************/ +static int +DGifDecompressInput(GifFileType *GifFile, int *Code) +{ + static const unsigned short CodeMasks[] = { + 0x0000, 0x0001, 0x0003, 0x0007, + 0x000f, 0x001f, 0x003f, 0x007f, + 0x00ff, 0x01ff, 0x03ff, 0x07ff, + 0x0fff + }; + + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + GifByteType NextByte; + + /* The image can't contain more than LZ_BITS per code. */ + if (Private->RunningBits > LZ_BITS) { + GifFile->Error = D_GIF_ERR_IMAGE_DEFECT; + return GIF_ERROR; + } + + while (Private->CrntShiftState < Private->RunningBits) { + /* Needs to get more bytes from input stream for next code: */ + if (DGifBufferedInput(GifFile, Private->Buf, &NextByte) == GIF_ERROR) { + return GIF_ERROR; + } + Private->CrntShiftDWord |= + ((unsigned long)NextByte) << Private->CrntShiftState; + Private->CrntShiftState += 8; + } + *Code = Private->CrntShiftDWord & CodeMasks[Private->RunningBits]; + + Private->CrntShiftDWord >>= Private->RunningBits; + Private->CrntShiftState -= Private->RunningBits; + + /* If code cannot fit into RunningBits bits, must raise its size. Note + * however that codes above 4095 are used for special signaling. + * If we're using LZ_BITS bits already and we're at the max code, just + * keep using the table as it is, don't increment Private->RunningCode. + */ + if (Private->RunningCode < LZ_MAX_CODE + 2 && + ++Private->RunningCode > Private->MaxCode1 && + Private->RunningBits < LZ_BITS) { + Private->MaxCode1 <<= 1; + Private->RunningBits++; + } + return GIF_OK; +} + +/****************************************************************************** + This routines read one GIF data block at a time and buffers it internally + so that the decompression routine could access it. + The routine returns the next byte from its internal buffer (or read next + block in if buffer empty) and returns GIF_OK if succesful. +******************************************************************************/ +static int +DGifBufferedInput(GifFileType *GifFile, GifByteType *Buf, GifByteType *NextByte) +{ + if (Buf[0] == 0) { + /* Needs to read the next buffer - this one is empty: */ + if (READ(GifFile, Buf, 1) != 1) { + GifFile->Error = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + /* There shouldn't be any empty data blocks here as the LZW spec + * says the LZW termination code should come first. Therefore we + * shouldn't be inside this routine at that point. + */ + if (Buf[0] == 0) { + GifFile->Error = D_GIF_ERR_IMAGE_DEFECT; + return GIF_ERROR; + } + /* There shouldn't be any empty data blocks here as the LZW spec + * says the LZW termination code should come first. Therefore we + * shouldn't be inside this routine at that point. + */ + if (Buf[0] == 0) { + GifFile->Error = D_GIF_ERR_IMAGE_DEFECT; + return GIF_ERROR; + } + if (READ(GifFile, &Buf[1], Buf[0]) != Buf[0]) { + GifFile->Error = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + *NextByte = Buf[1]; + Buf[1] = 2; /* We use now the second place as last char read! */ + Buf[0]--; + } else { + *NextByte = Buf[Buf[1]++]; + Buf[0]--; + } + + return GIF_OK; +} + +/****************************************************************************** + This routine reads an entire GIF into core, hanging all its state info off + the GifFileType pointer. Call DGifOpenFileName() or DGifOpenFileHandle() + first to initialize I/O. Its inverse is EGifSpew(). +*******************************************************************************/ +int +DGifSlurp(GifFileType *GifFile) +{ + size_t ImageSize; + GifRecordType RecordType; + SavedImage *sp; + GifByteType *ExtData; + int ExtFunction; + + GifFile->ExtensionBlocks = NULL; + GifFile->ExtensionBlockCount = 0; + + do { + if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR) + return (GIF_ERROR); + + switch (RecordType) { + case IMAGE_DESC_RECORD_TYPE: + if (DGifGetImageDesc(GifFile) == GIF_ERROR) + return (GIF_ERROR); + + sp = &GifFile->SavedImages[GifFile->ImageCount - 1]; + /* Allocate memory for the image */ + if (sp->ImageDesc.Width < 0 && sp->ImageDesc.Height < 0 && + sp->ImageDesc.Width > (INT_MAX / sp->ImageDesc.Height)) { + return GIF_ERROR; + } + ImageSize = sp->ImageDesc.Width * sp->ImageDesc.Height; + + if (ImageSize > (SIZE_MAX / sizeof(GifPixelType))) { + return GIF_ERROR; + } + sp->RasterBits = (unsigned char *)malloc(ImageSize * + sizeof(GifPixelType)); + + if (sp->RasterBits == NULL) { + return GIF_ERROR; + } + + if (sp->ImageDesc.Interlace) { + int i, j; + /* + * The way an interlaced image should be read - + * offsets and jumps... + */ + int InterlacedOffset[] = { 0, 4, 2, 1 }; + int InterlacedJumps[] = { 8, 8, 4, 2 }; + /* Need to perform 4 passes on the image */ + for (i = 0; i < 4; i++) + for (j = InterlacedOffset[i]; + j < sp->ImageDesc.Height; + j += InterlacedJumps[i]) { + if (DGifGetLine(GifFile, + sp->RasterBits+j*sp->ImageDesc.Width, + sp->ImageDesc.Width) == GIF_ERROR) + return GIF_ERROR; + } + } + else { + if (DGifGetLine(GifFile,sp->RasterBits,ImageSize)==GIF_ERROR) + return (GIF_ERROR); + } + + if (GifFile->ExtensionBlocks) { + sp->ExtensionBlocks = GifFile->ExtensionBlocks; + sp->ExtensionBlockCount = GifFile->ExtensionBlockCount; + + GifFile->ExtensionBlocks = NULL; + GifFile->ExtensionBlockCount = 0; + } + break; + + case EXTENSION_RECORD_TYPE: + if (DGifGetExtension(GifFile,&ExtFunction,&ExtData) == GIF_ERROR) + return (GIF_ERROR); + /* Create an extension block with our data */ + if (ExtData != NULL) { + if (GifAddExtensionBlock(&GifFile->ExtensionBlockCount, + &GifFile->ExtensionBlocks, + ExtFunction, ExtData[0], &ExtData[1]) + == GIF_ERROR) + return (GIF_ERROR); + } + while (ExtData != NULL) { + if (DGifGetExtensionNext(GifFile, &ExtData) == GIF_ERROR) + return (GIF_ERROR); + /* Continue the extension block */ + if (ExtData != NULL) + if (GifAddExtensionBlock(&GifFile->ExtensionBlockCount, + &GifFile->ExtensionBlocks, + CONTINUE_EXT_FUNC_CODE, + ExtData[0], &ExtData[1]) == GIF_ERROR) + return (GIF_ERROR); + } + break; + + case TERMINATE_RECORD_TYPE: + break; + + default: /* Should be trapped by DGifGetRecordType */ + break; + } + } while (RecordType != TERMINATE_RECORD_TYPE); + + return (GIF_OK); +} + +/* end */ diff --git a/src/vendor/QtGifImage/3rdParty/giflib/egif_lib.c b/src/vendor/QtGifImage/3rdParty/giflib/egif_lib.c new file mode 100644 index 00000000..e4251046 --- /dev/null +++ b/src/vendor/QtGifImage/3rdParty/giflib/egif_lib.c @@ -0,0 +1,1147 @@ +/****************************************************************************** + +egif_lib.c - GIF encoding + +The functions here and in dgif_lib.c are partitioned carefully so that +if you only require one of read and write capability, only one of these +two modules will be linked. Preserve this property! + +*****************************************************************************/ + +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#else +#include +#include +#endif /* _WIN32 */ +#include + +#include "gif_lib.h" +#include "gif_lib_private.h" + +/* Masks given codes to BitsPerPixel, to make sure all codes are in range: */ +/*@+charint@*/ +static const GifPixelType CodeMask[] = { + 0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff +}; +/*@-charint@*/ + +static int EGifPutWord(int Word, GifFileType * GifFile); +static int EGifSetupCompress(GifFileType * GifFile); +static int EGifCompressLine(GifFileType * GifFile, GifPixelType * Line, + int LineLen); +static int EGifCompressOutput(GifFileType * GifFile, int Code); +static int EGifBufferedOutput(GifFileType * GifFile, GifByteType * Buf, + int c); + +/* extract bytes from an unsigned word */ +#define LOBYTE(x) ((x) & 0xff) +#define HIBYTE(x) (((x) >> 8) & 0xff) + +/****************************************************************************** + Open a new GIF file for write, specified by name. If TestExistance then + if the file exists this routines fails (returns NULL). + Returns a dynamically allocated GifFileType pointer which serves as the GIF + info record. The Error member is cleared if successful. +******************************************************************************/ +GifFileType * +EGifOpenFileName(const char *FileName, const BOOL TestExistence, int *Error) +{ + + int FileHandle; + GifFileType *GifFile; + + if (TestExistence) + FileHandle = open(FileName, O_WRONLY | O_CREAT | O_EXCL, + S_IREAD | S_IWRITE); + else + FileHandle = open(FileName, O_WRONLY | O_CREAT | O_TRUNC, + S_IREAD | S_IWRITE); + + if (FileHandle == -1) { + if (Error != NULL) + *Error = E_GIF_ERR_OPEN_FAILED; + return NULL; + } + GifFile = EGifOpenFileHandle(FileHandle, Error); + if (GifFile == (GifFileType *) NULL) + (void)close(FileHandle); + return GifFile; +} + +/****************************************************************************** + Update a new GIF file, given its file handle, which must be opened for + write in binary mode. + Returns dynamically allocated a GifFileType pointer which serves as the GIF + info record. + Only fails on a memory allocation error. +******************************************************************************/ +GifFileType * +EGifOpenFileHandle(const int FileHandle, int *Error) +{ + GifFileType *GifFile; + GifFilePrivateType *Private; + FILE *f; + + GifFile = (GifFileType *) malloc(sizeof(GifFileType)); + if (GifFile == NULL) { + return NULL; + } + + memset(GifFile, '\0', sizeof(GifFileType)); + + Private = (GifFilePrivateType *)malloc(sizeof(GifFilePrivateType)); + if (Private == NULL) { + free(GifFile); + if (Error != NULL) + *Error = E_GIF_ERR_NOT_ENOUGH_MEM; + return NULL; + } + if ((Private->HashTable = _InitHashTable()) == NULL) { + free(GifFile); + free(Private); + if (Error != NULL) + *Error = E_GIF_ERR_NOT_ENOUGH_MEM; + return NULL; + } + +#ifdef _WIN32 + _setmode(FileHandle, O_BINARY); /* Make sure it is in binary mode. */ +#endif /* _WIN32 */ + + f = fdopen(FileHandle, "wb"); /* Make it into a stream: */ + + GifFile->Private = (void *)Private; + Private->FileHandle = FileHandle; + Private->File = f; + Private->FileState = FILE_STATE_WRITE; + + Private->Write = (OutputFunc) 0; /* No user write routine (MRB) */ + GifFile->UserData = (void *)NULL; /* No user write handle (MRB) */ + + GifFile->Error = 0; + + return GifFile; +} + +/****************************************************************************** + Output constructor that takes user supplied output function. + Basically just a copy of EGifOpenFileHandle. (MRB) +******************************************************************************/ +GifFileType * +EGifOpen(void *userData, OutputFunc writeFunc, int *Error) +{ + GifFileType *GifFile; + GifFilePrivateType *Private; + + GifFile = (GifFileType *)malloc(sizeof(GifFileType)); + if (GifFile == NULL) { + if (Error != NULL) + *Error = E_GIF_ERR_NOT_ENOUGH_MEM; + return NULL; + } + + memset(GifFile, '\0', sizeof(GifFileType)); + + Private = (GifFilePrivateType *)malloc(sizeof(GifFilePrivateType)); + if (Private == NULL) { + free(GifFile); + if (Error != NULL) + *Error = E_GIF_ERR_NOT_ENOUGH_MEM; + return NULL; + } + + Private->HashTable = _InitHashTable(); + if (Private->HashTable == NULL) { + free (GifFile); + free (Private); + if (Error != NULL) + *Error = E_GIF_ERR_NOT_ENOUGH_MEM; + return NULL; + } + + GifFile->Private = (void *)Private; + Private->FileHandle = 0; + Private->File = (FILE *) 0; + Private->FileState = FILE_STATE_WRITE; + + Private->Write = writeFunc; /* User write routine (MRB) */ + GifFile->UserData = userData; /* User write handle (MRB) */ + + Private->gif89 = FALSE; /* initially, write GIF87 */ + + GifFile->Error = 0; + + return GifFile; +} + +/****************************************************************************** + Routine to compute the GIF version that will be written on output. +******************************************************************************/ +char * +EGifGetGifVersion(GifFileType *GifFile) +{ + GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private; + int i, j; + + /* + * Bulletproofing - always write GIF89 if we need to. + * Note, we don't clear the gif89 flag here because + * users of the sequential API might have called EGifSetGifVersion() + * in order to set that flag. + */ + for (i = 0; i < GifFile->ImageCount; i++) { + for (j = 0; j < GifFile->SavedImages[i].ExtensionBlockCount; j++) { + int function = + GifFile->SavedImages[i].ExtensionBlocks[j].Function; + + if (function == COMMENT_EXT_FUNC_CODE + || function == GRAPHICS_EXT_FUNC_CODE + || function == PLAINTEXT_EXT_FUNC_CODE + || function == APPLICATION_EXT_FUNC_CODE) + Private->gif89 = TRUE; + } + } + for (i = 0; i < GifFile->ExtensionBlockCount; i++) { + int function = GifFile->ExtensionBlocks[i].Function; + + if (function == COMMENT_EXT_FUNC_CODE + || function == GRAPHICS_EXT_FUNC_CODE + || function == PLAINTEXT_EXT_FUNC_CODE + || function == APPLICATION_EXT_FUNC_CODE) + Private->gif89 = TRUE; + } + + if (Private->gif89) + return GIF89_STAMP; + else + return GIF87_STAMP; +} + +/****************************************************************************** + Set the GIF version. In the extremely unlikely event that there is ever + another version, replace the bool argument with an enum in which the + GIF87 value is 0 (numerically the same as bool false) and the GIF89 value + is 1 (numerically the same as bool true). That way we'll even preserve + object-file compatibility! +******************************************************************************/ +void EGifSetGifVersion(GifFileType *GifFile, const BOOL gif89) +{ + GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private; + + Private->gif89 = gif89; +} + +/****************************************************************************** + All writes to the GIF should go through this. +******************************************************************************/ +static int InternalWrite(GifFileType *GifFileOut, + const unsigned char *buf, size_t len) +{ + GifFilePrivateType *Private = (GifFilePrivateType*)GifFileOut->Private; + if (Private->Write) + return Private->Write(GifFileOut,buf,len); + else + return fwrite(buf, 1, len, Private->File); +} + +/****************************************************************************** + This routine should be called before any other EGif calls, immediately + following the GIF file opening. +******************************************************************************/ +int +EGifPutScreenDesc(GifFileType *GifFile, + const int Width, + const int Height, + const int ColorRes, + const int BackGround, + const ColorMapObject *ColorMap) +{ + GifByteType Buf[3]; + GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private; + char *write_version; + + if (Private->FileState & FILE_STATE_SCREEN) { + /* If already has screen descriptor - something is wrong! */ + GifFile->Error = E_GIF_ERR_HAS_SCRN_DSCR; + return GIF_ERROR; + } + if (!IS_WRITEABLE(Private)) { + /* This file was NOT open for writing: */ + GifFile->Error = E_GIF_ERR_NOT_WRITEABLE; + return GIF_ERROR; + } + + write_version = EGifGetGifVersion(GifFile); + + /* First write the version prefix into the file. */ + if (InternalWrite(GifFile, (unsigned char *)write_version, + strlen(write_version)) != strlen(write_version)) { + GifFile->Error = E_GIF_ERR_WRITE_FAILED; + return GIF_ERROR; + } + + GifFile->SWidth = Width; + GifFile->SHeight = Height; + GifFile->SColorResolution = ColorRes; + GifFile->SBackGroundColor = BackGround; + if (ColorMap) { + GifFile->SColorMap = GifMakeMapObject(ColorMap->ColorCount, + ColorMap->Colors); + if (GifFile->SColorMap == NULL) { + GifFile->Error = E_GIF_ERR_NOT_ENOUGH_MEM; + return GIF_ERROR; + } + } else + GifFile->SColorMap = NULL; + + /* + * Put the logical screen descriptor into the file: + */ + /* Logical Screen Descriptor: Dimensions */ + (void)EGifPutWord(Width, GifFile); + (void)EGifPutWord(Height, GifFile); + + /* Logical Screen Descriptor: Packed Fields */ + /* Note: We have actual size of the color table default to the largest + * possible size (7+1 == 8 bits) because the decoder can use it to decide + * how to display the files. + */ + Buf[0] = (ColorMap ? 0x80 : 0x00) | /* Yes/no global colormap */ + ((ColorRes - 1) << 4) | /* Bits allocated to each primary color */ + (ColorMap ? ColorMap->BitsPerPixel - 1 : 0x07 ); /* Actual size of the + color table. */ + if (ColorMap != NULL && ColorMap->SortFlag) + Buf[0] |= 0x08; + Buf[1] = BackGround; /* Index into the ColorTable for background color */ + Buf[2] = GifFile->AspectByte; /* Pixel Aspect Ratio */ + InternalWrite(GifFile, Buf, 3); + + /* If we have Global color map - dump it also: */ + if (ColorMap != NULL) { + int i; + for (i = 0; i < ColorMap->ColorCount; i++) { + /* Put the ColorMap out also: */ + Buf[0] = ColorMap->Colors[i].Red; + Buf[1] = ColorMap->Colors[i].Green; + Buf[2] = ColorMap->Colors[i].Blue; + if (InternalWrite(GifFile, Buf, 3) != 3) { + GifFile->Error = E_GIF_ERR_WRITE_FAILED; + return GIF_ERROR; + } + } + } + + /* Mark this file as has screen descriptor, and no pixel written yet: */ + Private->FileState |= FILE_STATE_SCREEN; + + return GIF_OK; +} + +/****************************************************************************** + This routine should be called before any attempt to dump an image - any + call to any of the pixel dump routines. +******************************************************************************/ +int +EGifPutImageDesc(GifFileType *GifFile, + const int Left, + const int Top, + const int Width, + const int Height, + const BOOL Interlace, + const ColorMapObject *ColorMap) +{ + GifByteType Buf[3]; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + if (Private->FileState & FILE_STATE_IMAGE && + Private->PixelCount > 0xffff0000UL) { + /* If already has active image descriptor - something is wrong! */ + GifFile->Error = E_GIF_ERR_HAS_IMAG_DSCR; + return GIF_ERROR; + } + if (!IS_WRITEABLE(Private)) { + /* This file was NOT open for writing: */ + GifFile->Error = E_GIF_ERR_NOT_WRITEABLE; + return GIF_ERROR; + } + GifFile->Image.Left = Left; + GifFile->Image.Top = Top; + GifFile->Image.Width = Width; + GifFile->Image.Height = Height; + GifFile->Image.Interlace = Interlace; + if (ColorMap) { + GifFile->Image.ColorMap = GifMakeMapObject(ColorMap->ColorCount, + ColorMap->Colors); + if (GifFile->Image.ColorMap == NULL) { + GifFile->Error = E_GIF_ERR_NOT_ENOUGH_MEM; + return GIF_ERROR; + } + } else { + GifFile->Image.ColorMap = NULL; + } + + /* Put the image descriptor into the file: */ + Buf[0] = DESCRIPTOR_INTRODUCER; /* Image separator character. */ + InternalWrite(GifFile, Buf, 1); + (void)EGifPutWord(Left, GifFile); + (void)EGifPutWord(Top, GifFile); + (void)EGifPutWord(Width, GifFile); + (void)EGifPutWord(Height, GifFile); + Buf[0] = (ColorMap ? 0x80 : 0x00) | + (Interlace ? 0x40 : 0x00) | + (ColorMap ? ColorMap->BitsPerPixel - 1 : 0); + InternalWrite(GifFile, Buf, 1); + + /* If we have Global color map - dump it also: */ + if (ColorMap != NULL) { + int i; + for (i = 0; i < ColorMap->ColorCount; i++) { + /* Put the ColorMap out also: */ + Buf[0] = ColorMap->Colors[i].Red; + Buf[1] = ColorMap->Colors[i].Green; + Buf[2] = ColorMap->Colors[i].Blue; + if (InternalWrite(GifFile, Buf, 3) != 3) { + GifFile->Error = E_GIF_ERR_WRITE_FAILED; + return GIF_ERROR; + } + } + } + if (GifFile->SColorMap == NULL && GifFile->Image.ColorMap == NULL) { + GifFile->Error = E_GIF_ERR_NO_COLOR_MAP; + return GIF_ERROR; + } + + /* Mark this file as has screen descriptor: */ + Private->FileState |= FILE_STATE_IMAGE; + Private->PixelCount = (long)Width *(long)Height; + + /* Reset compress algorithm parameters. */ + (void)EGifSetupCompress(GifFile); + + return GIF_OK; +} + +/****************************************************************************** + Put one full scanned line (Line) of length LineLen into GIF file. +******************************************************************************/ +int +EGifPutLine(GifFileType * GifFile, GifPixelType *Line, int LineLen) +{ + int i; + GifPixelType Mask; + GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private; + + if (!IS_WRITEABLE(Private)) { + /* This file was NOT open for writing: */ + GifFile->Error = E_GIF_ERR_NOT_WRITEABLE; + return GIF_ERROR; + } + + if (!LineLen) + LineLen = GifFile->Image.Width; + if (Private->PixelCount < (unsigned)LineLen) { + GifFile->Error = E_GIF_ERR_DATA_TOO_BIG; + return GIF_ERROR; + } + Private->PixelCount -= LineLen; + + /* Make sure the codes are not out of bit range, as we might generate + * wrong code (because of overflow when we combine them) in this case: */ + Mask = CodeMask[Private->BitsPerPixel]; + for (i = 0; i < LineLen; i++) + Line[i] &= Mask; + + return EGifCompressLine(GifFile, Line, LineLen); +} + +/****************************************************************************** + Put one pixel (Pixel) into GIF file. +******************************************************************************/ +int +EGifPutPixel(GifFileType *GifFile, GifPixelType Pixel) +{ + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + if (!IS_WRITEABLE(Private)) { + /* This file was NOT open for writing: */ + GifFile->Error = E_GIF_ERR_NOT_WRITEABLE; + return GIF_ERROR; + } + + if (Private->PixelCount == 0) { + GifFile->Error = E_GIF_ERR_DATA_TOO_BIG; + return GIF_ERROR; + } + --Private->PixelCount; + + /* Make sure the code is not out of bit range, as we might generate + * wrong code (because of overflow when we combine them) in this case: */ + Pixel &= CodeMask[Private->BitsPerPixel]; + + return EGifCompressLine(GifFile, &Pixel, 1); +} + +/****************************************************************************** + Put a comment into GIF file using the GIF89 comment extension block. +******************************************************************************/ +int +EGifPutComment(GifFileType *GifFile, const char *Comment) +{ + unsigned int length; + char *buf; + + length = strlen(Comment); + if (length <= 255) { + return EGifPutExtension(GifFile, COMMENT_EXT_FUNC_CODE, + length, Comment); + } else { + buf = (char *)Comment; + if (EGifPutExtensionLeader(GifFile, COMMENT_EXT_FUNC_CODE) + == GIF_ERROR) { + return GIF_ERROR; + } + + /* Break the comment into 255 byte sub blocks */ + while (length > 255) { + if (EGifPutExtensionBlock(GifFile, 255, buf) == GIF_ERROR) { + return GIF_ERROR; + } + buf = buf + 255; + length -= 255; + } + /* Output any partial block and the clear code. */ + if (length > 0) { + if (EGifPutExtensionBlock(GifFile, length, buf) == GIF_ERROR) { + return GIF_ERROR; + } + } + if (EGifPutExtensionTrailer(GifFile) == GIF_ERROR) { + return GIF_ERROR; + } + } + return GIF_OK; +} + +/****************************************************************************** + Begin an extension block (see GIF manual). More + extensions can be dumped using EGifPutExtensionBlock until + EGifPutExtensionTrailer is invoked. +******************************************************************************/ +int +EGifPutExtensionLeader(GifFileType *GifFile, const int ExtCode) +{ + GifByteType Buf[3]; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + if (!IS_WRITEABLE(Private)) { + /* This file was NOT open for writing: */ + GifFile->Error = E_GIF_ERR_NOT_WRITEABLE; + return GIF_ERROR; + } + + Buf[0] = EXTENSION_INTRODUCER; + Buf[1] = ExtCode; + InternalWrite(GifFile, Buf, 2); + + return GIF_OK; +} + +/****************************************************************************** + Put extension block data (see GIF manual) into a GIF file. +******************************************************************************/ +int +EGifPutExtensionBlock(GifFileType *GifFile, + const int ExtLen, + const void *Extension) +{ + GifByteType Buf; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + if (!IS_WRITEABLE(Private)) { + /* This file was NOT open for writing: */ + GifFile->Error = E_GIF_ERR_NOT_WRITEABLE; + return GIF_ERROR; + } + + Buf = ExtLen; + InternalWrite(GifFile, &Buf, 1); + InternalWrite(GifFile, Extension, ExtLen); + + return GIF_OK; +} + +/****************************************************************************** + Put a terminating block (see GIF manual) into a GIF file. +******************************************************************************/ +int +EGifPutExtensionTrailer(GifFileType *GifFile) { + + GifByteType Buf; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + if (!IS_WRITEABLE(Private)) { + /* This file was NOT open for writing: */ + GifFile->Error = E_GIF_ERR_NOT_WRITEABLE; + return GIF_ERROR; + } + + /* Write the block terminator */ + Buf = 0; + InternalWrite(GifFile, &Buf, 1); + + return GIF_OK; +} + +/****************************************************************************** + Put an extension block (see GIF manual) into a GIF file. + Warning: This function is only useful for Extension blocks that have at + most one subblock. Extensions with more than one subblock need to use the + EGifPutExtension{Leader,Block,Trailer} functions instead. +******************************************************************************/ +int +EGifPutExtension(GifFileType *GifFile, + const int ExtCode, + const int ExtLen, + const void *Extension) { + + GifByteType Buf[3]; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + if (!IS_WRITEABLE(Private)) { + /* This file was NOT open for writing: */ + GifFile->Error = E_GIF_ERR_NOT_WRITEABLE; + return GIF_ERROR; + } + + if (ExtCode == 0) + InternalWrite(GifFile, (GifByteType *)&ExtLen, 1); + else { + Buf[0] = EXTENSION_INTRODUCER; + Buf[1] = ExtCode; /* Extension Label */ + Buf[2] = ExtLen; /* Extension length */ + InternalWrite(GifFile, Buf, 3); + } + InternalWrite(GifFile, Extension, ExtLen); + Buf[0] = 0; + InternalWrite(GifFile, Buf, 1); + + return GIF_OK; +} + +/****************************************************************************** + Render a Graphics Control Block as raw extension data +******************************************************************************/ + +size_t EGifGCBToExtension(const GraphicsControlBlock *GCB, + GifByteType *GifExtension) +{ + GifExtension[0] = 0; + GifExtension[0] |= (GCB->TransparentColor == NO_TRANSPARENT_COLOR) ? 0x00 : 0x01; + GifExtension[0] |= GCB->UserInputFlag ? 0x02 : 0x00; + GifExtension[0] |= ((GCB->DisposalMode & 0x07) << 2); + GifExtension[1] = LOBYTE(GCB->DelayTime); + GifExtension[2] = HIBYTE(GCB->DelayTime); + GifExtension[3] = (char)GCB->TransparentColor; + return 4; +} + +/****************************************************************************** + Replace the Graphics Control Block for a saved image, if it exists. +******************************************************************************/ + +int EGifGCBToSavedExtension(const GraphicsControlBlock *GCB, + GifFileType *GifFile, int ImageIndex) +{ + int i; + size_t Len; + GifByteType buf[sizeof(GraphicsControlBlock)]; /* a bit dodgy... */ + + if (ImageIndex < 0 || ImageIndex > GifFile->ImageCount - 1) + return GIF_ERROR; + + for (i = 0; i < GifFile->SavedImages[ImageIndex].ExtensionBlockCount; i++) { + ExtensionBlock *ep = &GifFile->SavedImages[ImageIndex].ExtensionBlocks[i]; + if (ep->Function == GRAPHICS_EXT_FUNC_CODE) { + EGifGCBToExtension(GCB, ep->Bytes); + return GIF_OK; + } + } + + Len = EGifGCBToExtension(GCB, (GifByteType *)buf); + if (GifAddExtensionBlock(&GifFile->SavedImages[ImageIndex].ExtensionBlockCount, + &GifFile->SavedImages[ImageIndex].ExtensionBlocks, + GRAPHICS_EXT_FUNC_CODE, + Len, + (unsigned char *)buf) == GIF_ERROR) + return (GIF_ERROR); + + return (GIF_OK); +} + +/****************************************************************************** + Put the image code in compressed form. This routine can be called if the + information needed to be piped out as is. Obviously this is much faster + than decoding and encoding again. This routine should be followed by calls + to EGifPutCodeNext, until NULL block is given. + The block should NOT be freed by the user (not dynamically allocated). +******************************************************************************/ +int +EGifPutCode(GifFileType *GifFile, int CodeSize, const GifByteType *CodeBlock) +{ + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + if (!IS_WRITEABLE(Private)) { + /* This file was NOT open for writing: */ + GifFile->Error = E_GIF_ERR_NOT_WRITEABLE; + return GIF_ERROR; + } + + /* No need to dump code size as Compression set up does any for us: */ + /* + * Buf = CodeSize; + * if (InternalWrite(GifFile, &Buf, 1) != 1) { + * GifFile->Error = E_GIF_ERR_WRITE_FAILED; + * return GIF_ERROR; + * } + */ + + return EGifPutCodeNext(GifFile, CodeBlock); +} + +/****************************************************************************** + Continue to put the image code in compressed form. This routine should be + called with blocks of code as read via DGifGetCode/DGifGetCodeNext. If + given buffer pointer is NULL, empty block is written to mark end of code. +******************************************************************************/ +int +EGifPutCodeNext(GifFileType *GifFile, const GifByteType *CodeBlock) +{ + GifByteType Buf; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + if (CodeBlock != NULL) { + if (InternalWrite(GifFile, CodeBlock, CodeBlock[0] + 1) + != (unsigned)(CodeBlock[0] + 1)) { + GifFile->Error = E_GIF_ERR_WRITE_FAILED; + return GIF_ERROR; + } + } else { + Buf = 0; + if (InternalWrite(GifFile, &Buf, 1) != 1) { + GifFile->Error = E_GIF_ERR_WRITE_FAILED; + return GIF_ERROR; + } + Private->PixelCount = 0; /* And local info. indicate image read. */ + } + + return GIF_OK; +} + +/****************************************************************************** + This routine should be called last, to close the GIF file. +******************************************************************************/ +int +EGifCloseFile(GifFileType *GifFile) +{ + GifByteType Buf; + GifFilePrivateType *Private; + FILE *File; + + if (GifFile == NULL) + return GIF_ERROR; + + Private = (GifFilePrivateType *) GifFile->Private; + if (Private == NULL) + return GIF_ERROR; + if (!IS_WRITEABLE(Private)) { + /* This file was NOT open for writing: */ + GifFile->Error = E_GIF_ERR_NOT_WRITEABLE; + return GIF_ERROR; + } + + File = Private->File; + + Buf = TERMINATOR_INTRODUCER; + InternalWrite(GifFile, &Buf, 1); + + if (GifFile->Image.ColorMap) { + GifFreeMapObject(GifFile->Image.ColorMap); + GifFile->Image.ColorMap = NULL; + } + if (GifFile->SColorMap) { + GifFreeMapObject(GifFile->SColorMap); + GifFile->SColorMap = NULL; + } + if (Private) { + if (Private->HashTable) { + free((char *) Private->HashTable); + } + free((char *) Private); + } + + if (File && fclose(File) != 0) { + GifFile->Error = E_GIF_ERR_CLOSE_FAILED; + return GIF_ERROR; + } + + /* + * Without the #ifndef, we get spurious warnings because Coverity mistakenly + * thinks the GIF structure is freed on an error return. + */ +#ifndef __COVERITY__ + free(GifFile); +#endif /* __COVERITY__ */ + + return GIF_OK; +} + +/****************************************************************************** + Put 2 bytes (a word) into the given file in little-endian order: +******************************************************************************/ +static int +EGifPutWord(int Word, GifFileType *GifFile) +{ + unsigned char c[2]; + + c[0] = LOBYTE(Word); + c[1] = HIBYTE(Word); + if (InternalWrite(GifFile, c, 2) == 2) + return GIF_OK; + else + return GIF_ERROR; +} + +/****************************************************************************** + Setup the LZ compression for this image: +******************************************************************************/ +static int +EGifSetupCompress(GifFileType *GifFile) +{ + int BitsPerPixel; + GifByteType Buf; + GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private; + + /* Test and see what color map to use, and from it # bits per pixel: */ + if (GifFile->Image.ColorMap) + BitsPerPixel = GifFile->Image.ColorMap->BitsPerPixel; + else if (GifFile->SColorMap) + BitsPerPixel = GifFile->SColorMap->BitsPerPixel; + else { + GifFile->Error = E_GIF_ERR_NO_COLOR_MAP; + return GIF_ERROR; + } + + Buf = BitsPerPixel = (BitsPerPixel < 2 ? 2 : BitsPerPixel); + InternalWrite(GifFile, &Buf, 1); /* Write the Code size to file. */ + + Private->Buf[0] = 0; /* Nothing was output yet. */ + Private->BitsPerPixel = BitsPerPixel; + Private->ClearCode = (1 << BitsPerPixel); + Private->EOFCode = Private->ClearCode + 1; + Private->RunningCode = Private->EOFCode + 1; + Private->RunningBits = BitsPerPixel + 1; /* Number of bits per code. */ + Private->MaxCode1 = 1 << Private->RunningBits; /* Max. code + 1. */ + Private->CrntCode = FIRST_CODE; /* Signal that this is first one! */ + Private->CrntShiftState = 0; /* No information in CrntShiftDWord. */ + Private->CrntShiftDWord = 0; + + /* Clear hash table and send Clear to make sure the decoder do the same. */ + _ClearHashTable(Private->HashTable); + + if (EGifCompressOutput(GifFile, Private->ClearCode) == GIF_ERROR) { + GifFile->Error = E_GIF_ERR_DISK_IS_FULL; + return GIF_ERROR; + } + return GIF_OK; +} + +/****************************************************************************** + The LZ compression routine: + This version compresses the given buffer Line of length LineLen. + This routine can be called a few times (one per scan line, for example), in + order to complete the whole image. +******************************************************************************/ +static int +EGifCompressLine(GifFileType *GifFile, + GifPixelType *Line, + const int LineLen) +{ + int i = 0, CrntCode, NewCode; + unsigned long NewKey; + GifPixelType Pixel; + GifHashTableType *HashTable; + GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private; + + HashTable = Private->HashTable; + + if (Private->CrntCode == FIRST_CODE) /* Its first time! */ + CrntCode = Line[i++]; + else + CrntCode = Private->CrntCode; /* Get last code in compression. */ + + while (i < LineLen) { /* Decode LineLen items. */ + Pixel = Line[i++]; /* Get next pixel from stream. */ + /* Form a new unique key to search hash table for the code combines + * CrntCode as Prefix string with Pixel as postfix char. + */ + NewKey = (((uint32_t) CrntCode) << 8) + Pixel; + if ((NewCode = _ExistsHashTable(HashTable, NewKey)) >= 0) { + /* This Key is already there, or the string is old one, so + * simple take new code as our CrntCode: + */ + CrntCode = NewCode; + } else { + /* Put it in hash table, output the prefix code, and make our + * CrntCode equal to Pixel. + */ + if (EGifCompressOutput(GifFile, CrntCode) == GIF_ERROR) { + GifFile->Error = E_GIF_ERR_DISK_IS_FULL; + return GIF_ERROR; + } + CrntCode = Pixel; + + /* If however the HashTable if full, we send a clear first and + * Clear the hash table. + */ + if (Private->RunningCode >= LZ_MAX_CODE) { + /* Time to do some clearance: */ + if (EGifCompressOutput(GifFile, Private->ClearCode) + == GIF_ERROR) { + GifFile->Error = E_GIF_ERR_DISK_IS_FULL; + return GIF_ERROR; + } + Private->RunningCode = Private->EOFCode + 1; + Private->RunningBits = Private->BitsPerPixel + 1; + Private->MaxCode1 = 1 << Private->RunningBits; + _ClearHashTable(HashTable); + } else { + /* Put this unique key with its relative Code in hash table: */ + _InsertHashTable(HashTable, NewKey, Private->RunningCode++); + } + } + + } + + /* Preserve the current state of the compression algorithm: */ + Private->CrntCode = CrntCode; + + if (Private->PixelCount == 0) { + /* We are done - output last Code and flush output buffers: */ + if (EGifCompressOutput(GifFile, CrntCode) == GIF_ERROR) { + GifFile->Error = E_GIF_ERR_DISK_IS_FULL; + return GIF_ERROR; + } + if (EGifCompressOutput(GifFile, Private->EOFCode) == GIF_ERROR) { + GifFile->Error = E_GIF_ERR_DISK_IS_FULL; + return GIF_ERROR; + } + if (EGifCompressOutput(GifFile, FLUSH_OUTPUT) == GIF_ERROR) { + GifFile->Error = E_GIF_ERR_DISK_IS_FULL; + return GIF_ERROR; + } + } + + return GIF_OK; +} + +/****************************************************************************** + The LZ compression output routine: + This routine is responsible for the compression of the bit stream into + 8 bits (bytes) packets. + Returns GIF_OK if written successfully. +******************************************************************************/ +static int +EGifCompressOutput(GifFileType *GifFile, + const int Code) +{ + GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private; + int retval = GIF_OK; + + if (Code == FLUSH_OUTPUT) { + while (Private->CrntShiftState > 0) { + /* Get Rid of what is left in DWord, and flush it. */ + if (EGifBufferedOutput(GifFile, Private->Buf, + Private->CrntShiftDWord & 0xff) == GIF_ERROR) + retval = GIF_ERROR; + Private->CrntShiftDWord >>= 8; + Private->CrntShiftState -= 8; + } + Private->CrntShiftState = 0; /* For next time. */ + if (EGifBufferedOutput(GifFile, Private->Buf, + FLUSH_OUTPUT) == GIF_ERROR) + retval = GIF_ERROR; + } else { + Private->CrntShiftDWord |= ((long)Code) << Private->CrntShiftState; + Private->CrntShiftState += Private->RunningBits; + while (Private->CrntShiftState >= 8) { + /* Dump out full bytes: */ + if (EGifBufferedOutput(GifFile, Private->Buf, + Private->CrntShiftDWord & 0xff) == GIF_ERROR) + retval = GIF_ERROR; + Private->CrntShiftDWord >>= 8; + Private->CrntShiftState -= 8; + } + } + + /* If code cannt fit into RunningBits bits, must raise its size. Note */ + /* however that codes above 4095 are used for special signaling. */ + if (Private->RunningCode >= Private->MaxCode1 && Code <= 4095) { + Private->MaxCode1 = 1 << ++Private->RunningBits; + } + + return retval; +} + +/****************************************************************************** + This routines buffers the given characters until 255 characters are ready + to be output. If Code is equal to -1 the buffer is flushed (EOF). + The buffer is Dumped with first byte as its size, as GIF format requires. + Returns GIF_OK if written successfully. +******************************************************************************/ +static int +EGifBufferedOutput(GifFileType *GifFile, + GifByteType *Buf, + int c) +{ + if (c == FLUSH_OUTPUT) { + /* Flush everything out. */ + if (Buf[0] != 0 + && InternalWrite(GifFile, Buf, Buf[0] + 1) != (unsigned)(Buf[0] + 1)) { + GifFile->Error = E_GIF_ERR_WRITE_FAILED; + return GIF_ERROR; + } + /* Mark end of compressed data, by an empty block (see GIF doc): */ + Buf[0] = 0; + if (InternalWrite(GifFile, Buf, 1) != 1) { + GifFile->Error = E_GIF_ERR_WRITE_FAILED; + return GIF_ERROR; + } + } else { + if (Buf[0] == 255) { + /* Dump out this buffer - it is full: */ + if (InternalWrite(GifFile, Buf, Buf[0] + 1) != (unsigned)(Buf[0] + 1)) { + GifFile->Error = E_GIF_ERR_WRITE_FAILED; + return GIF_ERROR; + } + Buf[0] = 0; + } + Buf[++Buf[0]] = c; + } + + return GIF_OK; +} + +/****************************************************************************** + This routine writes to disk an in-core representation of a GIF previously + created by DGifSlurp(). +******************************************************************************/ + +static int +EGifWriteExtensions(GifFileType *GifFileOut, + ExtensionBlock *ExtensionBlocks, + int ExtensionBlockCount) +{ + if (ExtensionBlocks) { + ExtensionBlock *ep; + int j; + + for (j = 0; j < ExtensionBlockCount; j++) { + ep = &ExtensionBlocks[j]; + if (ep->Function != CONTINUE_EXT_FUNC_CODE) + if (EGifPutExtensionLeader(GifFileOut, ep->Function) == GIF_ERROR) + return (GIF_ERROR); + if (EGifPutExtensionBlock(GifFileOut, ep->ByteCount, ep->Bytes) == GIF_ERROR) + return (GIF_ERROR); + if (j == ExtensionBlockCount - 1 || (ep+1)->Function != CONTINUE_EXT_FUNC_CODE) + if (EGifPutExtensionTrailer(GifFileOut) == GIF_ERROR) + return (GIF_ERROR); + } + } + + return (GIF_OK); +} + +int +EGifSpew(GifFileType *GifFileOut) +{ + int i, j; + + if (EGifPutScreenDesc(GifFileOut, + GifFileOut->SWidth, + GifFileOut->SHeight, + GifFileOut->SColorResolution, + GifFileOut->SBackGroundColor, + GifFileOut->SColorMap) == GIF_ERROR) { + return (GIF_ERROR); + } + + for (i = 0; i < GifFileOut->ImageCount; i++) { + SavedImage *sp = &GifFileOut->SavedImages[i]; + int SavedHeight = sp->ImageDesc.Height; + int SavedWidth = sp->ImageDesc.Width; + + /* this allows us to delete images by nuking their rasters */ + if (sp->RasterBits == NULL) + continue; + + if (EGifWriteExtensions(GifFileOut, + sp->ExtensionBlocks, + sp->ExtensionBlockCount) == GIF_ERROR) + return (GIF_ERROR); + + if (EGifPutImageDesc(GifFileOut, + sp->ImageDesc.Left, + sp->ImageDesc.Top, + SavedWidth, + SavedHeight, + sp->ImageDesc.Interlace, + sp->ImageDesc.ColorMap) == GIF_ERROR) + return (GIF_ERROR); + + if (sp->ImageDesc.Interlace) { + /* + * The way an interlaced image should be written - + * offsets and jumps... + */ + int InterlacedOffset[] = { 0, 4, 2, 1 }; + int InterlacedJumps[] = { 8, 8, 4, 2 }; + int k; + /* Need to perform 4 passes on the images: */ + for (k = 0; k < 4; k++) + for (j = InterlacedOffset[k]; + j < SavedHeight; + j += InterlacedJumps[k]) { + if (EGifPutLine(GifFileOut, + sp->RasterBits + j * SavedWidth, + SavedWidth) == GIF_ERROR) + return (GIF_ERROR); + } + } else { + for (j = 0; j < SavedHeight; j++) { + if (EGifPutLine(GifFileOut, + sp->RasterBits + j * SavedWidth, + SavedWidth) == GIF_ERROR) + return (GIF_ERROR); + } + } + } + + if (EGifWriteExtensions(GifFileOut, + GifFileOut->ExtensionBlocks, + GifFileOut->ExtensionBlockCount) == GIF_ERROR) + return (GIF_ERROR); + + if (EGifCloseFile(GifFileOut) == GIF_ERROR) + return (GIF_ERROR); + + return (GIF_OK); +} + +/* end */ diff --git a/src/vendor/QtGifImage/3rdParty/giflib/gif_err.c b/src/vendor/QtGifImage/3rdParty/giflib/gif_err.c new file mode 100644 index 00000000..8de72a00 --- /dev/null +++ b/src/vendor/QtGifImage/3rdParty/giflib/gif_err.c @@ -0,0 +1,97 @@ +/***************************************************************************** + +gif_err.c - handle error reporting for the GIF library. + +****************************************************************************/ + +#include + +#include "gif_lib.h" +#include "gif_lib_private.h" + +/***************************************************************************** + Return a string description of the last GIF error +*****************************************************************************/ +char * +GifErrorString(int ErrorCode) +{ + char *Err; + + switch (ErrorCode) { + case E_GIF_ERR_OPEN_FAILED: + Err = "Failed to open given file"; + break; + case E_GIF_ERR_WRITE_FAILED: + Err = "Failed to write to given file"; + break; + case E_GIF_ERR_HAS_SCRN_DSCR: + Err = "Screen descriptor has already been set"; + break; + case E_GIF_ERR_HAS_IMAG_DSCR: + Err = "Image descriptor is still active"; + break; + case E_GIF_ERR_NO_COLOR_MAP: + Err = "Neither global nor local color map"; + break; + case E_GIF_ERR_DATA_TOO_BIG: + Err = "Number of pixels bigger than width * height"; + break; + case E_GIF_ERR_NOT_ENOUGH_MEM: + Err = "Failed to allocate required memory"; + break; + case E_GIF_ERR_DISK_IS_FULL: + Err = "Write failed (disk full?)"; + break; + case E_GIF_ERR_CLOSE_FAILED: + Err = "Failed to close given file"; + break; + case E_GIF_ERR_NOT_WRITEABLE: + Err = "Given file was not opened for write"; + break; + case D_GIF_ERR_OPEN_FAILED: + Err = "Failed to open given file"; + break; + case D_GIF_ERR_READ_FAILED: + Err = "Failed to read from given file"; + break; + case D_GIF_ERR_NOT_GIF_FILE: + Err = "Data is not in GIF format"; + break; + case D_GIF_ERR_NO_SCRN_DSCR: + Err = "No screen descriptor detected"; + break; + case D_GIF_ERR_NO_IMAG_DSCR: + Err = "No Image Descriptor detected"; + break; + case D_GIF_ERR_NO_COLOR_MAP: + Err = "Neither global nor local color map"; + break; + case D_GIF_ERR_WRONG_RECORD: + Err = "Wrong record type detected"; + break; + case D_GIF_ERR_DATA_TOO_BIG: + Err = "Number of pixels bigger than width * height"; + break; + case D_GIF_ERR_NOT_ENOUGH_MEM: + Err = "Failed to allocate required memory"; + break; + case D_GIF_ERR_CLOSE_FAILED: + Err = "Failed to close given file"; + break; + case D_GIF_ERR_NOT_READABLE: + Err = "Given file was not opened for read"; + break; + case D_GIF_ERR_IMAGE_DEFECT: + Err = "Image is defective, decoding aborted"; + break; + case D_GIF_ERR_EOF_TOO_SOON: + Err = "Image EOF detected before image complete"; + break; + default: + Err = NULL; + break; + } + return Err; +} + +/* end */ diff --git a/src/vendor/QtGifImage/3rdParty/giflib/gif_font.c b/src/vendor/QtGifImage/3rdParty/giflib/gif_font.c new file mode 100644 index 00000000..ba47b29e --- /dev/null +++ b/src/vendor/QtGifImage/3rdParty/giflib/gif_font.c @@ -0,0 +1,252 @@ +/***************************************************************************** + +gif_font.c - utility font handling and simple drawing for the GIF library + +****************************************************************************/ + +#include + +#include "gif_lib.h" + +/***************************************************************************** + Ascii 8 by 8 regular font - only first 128 characters are supported. +*****************************************************************************/ + +/* + * Each array entry holds the bits for 8 horizontal scan lines, topmost + * first. The most significant bit of each constant is the leftmost bit of + * the scan line. + */ +/*@+charint@*/ +const unsigned char GifAsciiTable8x8[][GIF_FONT_WIDTH] = { + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* Ascii 0 */ + {0x3c, 0x42, 0xa5, 0x81, 0xbd, 0x42, 0x3c, 0x00}, /* Ascii 1 */ + {0x3c, 0x7e, 0xdb, 0xff, 0xc3, 0x7e, 0x3c, 0x00}, /* Ascii 2 */ + {0x00, 0xee, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00}, /* Ascii 3 */ + {0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00}, /* Ascii 4 */ + {0x00, 0x3c, 0x18, 0xff, 0xff, 0x08, 0x18, 0x00}, /* Ascii 5 */ + {0x10, 0x38, 0x7c, 0xfe, 0xfe, 0x10, 0x38, 0x00}, /* Ascii 6 */ + {0x00, 0x00, 0x18, 0x3c, 0x18, 0x00, 0x00, 0x00}, /* Ascii 7 */ + {0xff, 0xff, 0xe7, 0xc3, 0xe7, 0xff, 0xff, 0xff}, /* Ascii 8 */ + {0x00, 0x3c, 0x42, 0x81, 0x81, 0x42, 0x3c, 0x00}, /* Ascii 9 */ + {0xff, 0xc3, 0xbd, 0x7e, 0x7e, 0xbd, 0xc3, 0xff}, /* Ascii 10 */ + {0x1f, 0x07, 0x0d, 0x7c, 0xc6, 0xc6, 0x7c, 0x00}, /* Ascii 11 */ + {0x00, 0x7e, 0xc3, 0xc3, 0x7e, 0x18, 0x7e, 0x18}, /* Ascii 12 */ + {0x04, 0x06, 0x07, 0x04, 0x04, 0xfc, 0xf8, 0x00}, /* Ascii 13 */ + {0x0c, 0x0a, 0x0d, 0x0b, 0xf9, 0xf9, 0x1f, 0x1f}, /* Ascii 14 */ + {0x00, 0x92, 0x7c, 0x44, 0xc6, 0x7c, 0x92, 0x00}, /* Ascii 15 */ + {0x00, 0x00, 0x60, 0x78, 0x7e, 0x78, 0x60, 0x00}, /* Ascii 16 */ + {0x00, 0x00, 0x06, 0x1e, 0x7e, 0x1e, 0x06, 0x00}, /* Ascii 17 */ + {0x18, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x18}, /* Ascii 18 */ + {0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x00}, /* Ascii 19 */ + {0xff, 0xb6, 0x76, 0x36, 0x36, 0x36, 0x36, 0x00}, /* Ascii 20 */ + {0x7e, 0xc1, 0xdc, 0x22, 0x22, 0x1f, 0x83, 0x7e}, /* Ascii 21 */ + {0x00, 0x00, 0x00, 0x7e, 0x7e, 0x00, 0x00, 0x00}, /* Ascii 22 */ + {0x18, 0x7e, 0x18, 0x18, 0x7e, 0x18, 0x00, 0xff}, /* Ascii 23 */ + {0x18, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00}, /* Ascii 24 */ + {0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x18, 0x00}, /* Ascii 25 */ + {0x00, 0x04, 0x06, 0xff, 0x06, 0x04, 0x00, 0x00}, /* Ascii 26 */ + {0x00, 0x20, 0x60, 0xff, 0x60, 0x20, 0x00, 0x00}, /* Ascii 27 */ + {0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xff, 0x00}, /* Ascii 28 */ + {0x00, 0x24, 0x66, 0xff, 0x66, 0x24, 0x00, 0x00}, /* Ascii 29 */ + {0x00, 0x00, 0x10, 0x38, 0x7c, 0xfe, 0x00, 0x00}, /* Ascii 30 */ + {0x00, 0x00, 0x00, 0xfe, 0x7c, 0x38, 0x10, 0x00}, /* Ascii 31 */ + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* */ + {0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x30, 0x00}, /* ! */ + {0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* " */ + {0x6c, 0x6c, 0xfe, 0x6c, 0xfe, 0x6c, 0x6c, 0x00}, /* # */ + {0x10, 0x7c, 0xd2, 0x7c, 0x86, 0x7c, 0x10, 0x00}, /* $ */ + {0xf0, 0x96, 0xfc, 0x18, 0x3e, 0x72, 0xde, 0x00}, /* % */ + {0x30, 0x48, 0x30, 0x78, 0xce, 0xcc, 0x78, 0x00}, /* & */ + {0x0c, 0x0c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00}, /* ' */ + {0x10, 0x60, 0xc0, 0xc0, 0xc0, 0x60, 0x10, 0x00}, /* ( */ + {0x10, 0x0c, 0x06, 0x06, 0x06, 0x0c, 0x10, 0x00}, /* ) */ + {0x00, 0x54, 0x38, 0xfe, 0x38, 0x54, 0x00, 0x00}, /* * */ + {0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00}, /* + */ + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x70}, /* , */ + {0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00}, /* - */ + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00}, /* . */ + {0x02, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x00}, /* / */ + {0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00}, /* 0 */ + {0x18, 0x38, 0x78, 0x18, 0x18, 0x18, 0x3c, 0x00}, /* 1 */ + {0x7c, 0xc6, 0x06, 0x0c, 0x30, 0x60, 0xfe, 0x00}, /* 2 */ + {0x7c, 0xc6, 0x06, 0x3c, 0x06, 0xc6, 0x7c, 0x00}, /* 3 */ + {0x0e, 0x1e, 0x36, 0x66, 0xfe, 0x06, 0x06, 0x00}, /* 4 */ + {0xfe, 0xc0, 0xc0, 0xfc, 0x06, 0x06, 0xfc, 0x00}, /* 5 */ + {0x7c, 0xc6, 0xc0, 0xfc, 0xc6, 0xc6, 0x7c, 0x00}, /* 6 */ + {0xfe, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x60, 0x00}, /* 7 */ + {0x7c, 0xc6, 0xc6, 0x7c, 0xc6, 0xc6, 0x7c, 0x00}, /* 8 */ + {0x7c, 0xc6, 0xc6, 0x7e, 0x06, 0xc6, 0x7c, 0x00}, /* 9 */ + {0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00}, /* : */ + {0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x20, 0x00}, /* }, */ + {0x00, 0x1c, 0x30, 0x60, 0x30, 0x1c, 0x00, 0x00}, /* < */ + {0x00, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x00, 0x00}, /* = */ + {0x00, 0x70, 0x18, 0x0c, 0x18, 0x70, 0x00, 0x00}, /* > */ + {0x7c, 0xc6, 0x0c, 0x18, 0x30, 0x00, 0x30, 0x00}, /* ? */ + {0x7c, 0x82, 0x9a, 0xaa, 0xaa, 0x9e, 0x7c, 0x00}, /* @ */ + {0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0x00}, /* A */ + {0xfc, 0xc6, 0xc6, 0xfc, 0xc6, 0xc6, 0xfc, 0x00}, /* B */ + {0x7c, 0xc6, 0xc6, 0xc0, 0xc0, 0xc6, 0x7c, 0x00}, /* C */ + {0xf8, 0xcc, 0xc6, 0xc6, 0xc6, 0xcc, 0xf8, 0x00}, /* D */ + {0xfe, 0xc0, 0xc0, 0xfc, 0xc0, 0xc0, 0xfe, 0x00}, /* E */ + {0xfe, 0xc0, 0xc0, 0xfc, 0xc0, 0xc0, 0xc0, 0x00}, /* F */ + {0x7c, 0xc6, 0xc0, 0xce, 0xc6, 0xc6, 0x7e, 0x00}, /* G */ + {0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00}, /* H */ + {0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00}, /* I */ + {0x1e, 0x06, 0x06, 0x06, 0xc6, 0xc6, 0x7c, 0x00}, /* J */ + {0xc6, 0xcc, 0xd8, 0xf0, 0xd8, 0xcc, 0xc6, 0x00}, /* K */ + {0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xfe, 0x00}, /* L */ + {0xc6, 0xee, 0xfe, 0xd6, 0xc6, 0xc6, 0xc6, 0x00}, /* M */ + {0xc6, 0xe6, 0xf6, 0xde, 0xce, 0xc6, 0xc6, 0x00}, /* N */ + {0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00}, /* O */ + {0xfc, 0xc6, 0xc6, 0xfc, 0xc0, 0xc0, 0xc0, 0x00}, /* P */ + {0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x06}, /* Q */ + {0xfc, 0xc6, 0xc6, 0xfc, 0xc6, 0xc6, 0xc6, 0x00}, /* R */ + {0x78, 0xcc, 0x60, 0x30, 0x18, 0xcc, 0x78, 0x00}, /* S */ + {0xfc, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00}, /* T */ + {0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00}, /* U */ + {0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00}, /* V */ + {0xc6, 0xc6, 0xc6, 0xd6, 0xfe, 0xee, 0xc6, 0x00}, /* W */ + {0xc6, 0xc6, 0x6c, 0x38, 0x6c, 0xc6, 0xc6, 0x00}, /* X */ + {0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x00}, /* Y */ + {0xfe, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0xfe, 0x00}, /* Z */ + {0x3c, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3c, 0x00}, /* [ */ + {0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x03, 0x00}, /* \ */ + {0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x3c, 0x00}, /* ] */ + {0x00, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00}, /* ^ */ + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff}, /* _ */ + {0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00}, /* ` */ + {0x00, 0x00, 0x7c, 0x06, 0x7e, 0xc6, 0x7e, 0x00}, /* a */ + {0xc0, 0xc0, 0xfc, 0xc6, 0xc6, 0xe6, 0xdc, 0x00}, /* b */ + {0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0x7e, 0x00}, /* c */ + {0x06, 0x06, 0x7e, 0xc6, 0xc6, 0xce, 0x76, 0x00}, /* d */ + {0x00, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0x7e, 0x00}, /* e */ + {0x1e, 0x30, 0x7c, 0x30, 0x30, 0x30, 0x30, 0x00}, /* f */ + {0x00, 0x00, 0x7e, 0xc6, 0xce, 0x76, 0x06, 0x7c}, /* g */ + {0xc0, 0xc0, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0x00}, /* */ + {0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x3c, 0x00}, /* i */ + {0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0xf0}, /* j */ + {0xc0, 0xc0, 0xcc, 0xd8, 0xf0, 0xd8, 0xcc, 0x00}, /* k */ + {0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00}, /* l */ + {0x00, 0x00, 0xcc, 0xfe, 0xd6, 0xc6, 0xc6, 0x00}, /* m */ + {0x00, 0x00, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0x00}, /* n */ + {0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0x00}, /* o */ + {0x00, 0x00, 0xfc, 0xc6, 0xc6, 0xe6, 0xdc, 0xc0}, /* p */ + {0x00, 0x00, 0x7e, 0xc6, 0xc6, 0xce, 0x76, 0x06}, /* q */ + {0x00, 0x00, 0x6e, 0x70, 0x60, 0x60, 0x60, 0x00}, /* r */ + {0x00, 0x00, 0x7c, 0xc0, 0x7c, 0x06, 0xfc, 0x00}, /* s */ + {0x30, 0x30, 0x7c, 0x30, 0x30, 0x30, 0x1c, 0x00}, /* t */ + {0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x00}, /* u */ + {0x00, 0x00, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00}, /* v */ + {0x00, 0x00, 0xc6, 0xc6, 0xd6, 0xfe, 0x6c, 0x00}, /* w */ + {0x00, 0x00, 0xc6, 0x6c, 0x38, 0x6c, 0xc6, 0x00}, /* x */ + {0x00, 0x00, 0xc6, 0xc6, 0xce, 0x76, 0x06, 0x7c}, /* y */ + {0x00, 0x00, 0xfc, 0x18, 0x30, 0x60, 0xfc, 0x00}, /* z */ + {0x0e, 0x18, 0x18, 0x70, 0x18, 0x18, 0x0e, 0x00}, /* { */ + {0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00}, /* | */ + {0xe0, 0x30, 0x30, 0x1c, 0x30, 0x30, 0xe0, 0x00}, /* } */ + {0x00, 0x00, 0x70, 0x9a, 0x0e, 0x00, 0x00, 0x00}, /* ~ */ + {0x00, 0x00, 0x18, 0x3c, 0x66, 0xff, 0x00, 0x00} /* Ascii 127 */ +}; +/*@=charint@*/ + +void +GifDrawText8x8(SavedImage *Image, + const int x, const int y, + const char *legend, + const int color) +{ + int i, j; + int base; + const char *cp; + + for (i = 0; i < GIF_FONT_HEIGHT; i++) { + base = Image->ImageDesc.Width * (y + i) + x; + + for (cp = legend; *cp; cp++) + for (j = 0; j < GIF_FONT_WIDTH; j++) { + if (GifAsciiTable8x8[(short)(*cp)][i] & (1 << (GIF_FONT_WIDTH - j))) + Image->RasterBits[base] = color; + base++; + } + } +} + +void +GifDrawBox(SavedImage *Image, + const int x, const int y, + const int w, const int d, + const int color) +{ + int j, base = Image->ImageDesc.Width * y + x; + + for (j = 0; j < w; j++) + Image->RasterBits[base + j] = + Image->RasterBits[base + (d * Image->ImageDesc.Width) + j] = color; + + for (j = 0; j < d; j++) + Image->RasterBits[base + j * Image->ImageDesc.Width] = + Image->RasterBits[base + j * Image->ImageDesc.Width + w] = color; +} + +void +GifDrawRectangle(SavedImage *Image, + const int x, const int y, + const int w, const int d, + const int color) +{ + unsigned char *bp = Image->RasterBits + Image->ImageDesc.Width * y + x; + int i; + + for (i = 0; i < d; i++) + memset(bp + (i * Image->ImageDesc.Width), color, (size_t)w); +} + +void +GifDrawBoxedText8x8(SavedImage *Image, + const int x, const int y, + const char *legend, + const int border, + const int bg, const int fg) +{ + int i, j = 0, LineCount = 0, TextWidth = 0; + const char *cp; + + /* compute size of text to box */ + for (cp = legend; *cp; cp++) + if (*cp == '\r') { + if (j > TextWidth) + TextWidth = j; + j = 0; + LineCount++; + } else if (*cp != '\t') + ++j; + LineCount++; /* count last line */ + if (j > TextWidth) /* last line might be longer than any previous */ + TextWidth = j; + + /* fill the box */ + GifDrawRectangle(Image, x + 1, y + 1, + border + TextWidth * GIF_FONT_WIDTH + border - 1, + border + LineCount * GIF_FONT_HEIGHT + border - 1, bg); + + /* draw the text */ + i = 0; + cp = strtok((char *)legend, "\r\n"); + do { + int leadspace = 0; + + if (cp[0] == '\t') + leadspace = (TextWidth - strlen(++cp)) / 2; + + GifDrawText8x8(Image, x + border + (leadspace * GIF_FONT_WIDTH), + y + border + (GIF_FONT_HEIGHT * i++), cp, fg); + cp = strtok((char *)NULL, "\r\n"); + } while (cp); + + /* outline the box */ + GifDrawBox(Image, x, y, border + TextWidth * GIF_FONT_WIDTH + border, + border + LineCount * GIF_FONT_HEIGHT + border, fg); +} + +/* end */ diff --git a/src/vendor/QtGifImage/3rdParty/giflib/gif_hash.c b/src/vendor/QtGifImage/3rdParty/giflib/gif_hash.c new file mode 100644 index 00000000..ed7fd904 --- /dev/null +++ b/src/vendor/QtGifImage/3rdParty/giflib/gif_hash.c @@ -0,0 +1,131 @@ +/***************************************************************************** + +gif_hash.c -- module to support the following operations: + +1. InitHashTable - initialize hash table. +2. ClearHashTable - clear the hash table to an empty state. +2. InsertHashTable - insert one item into data structure. +3. ExistsHashTable - test if item exists in data structure. + +This module is used to hash the GIF codes during encoding. + +*****************************************************************************/ + +#include +#include +#include +#include +#include + +#include "gif_lib.h" +#include "gif_hash.h" +#include "gif_lib_private.h" + +/* #define DEBUG_HIT_RATE Debug number of misses per hash Insert/Exists. */ + +#ifdef DEBUG_HIT_RATE +static long NumberOfTests = 0, + NumberOfMisses = 0; +#endif /* DEBUG_HIT_RATE */ + +static int KeyItem(uint32_t Item); + +/****************************************************************************** + Initialize HashTable - allocate the memory needed and clear it. * +******************************************************************************/ +GifHashTableType *_InitHashTable(void) +{ + GifHashTableType *HashTable; + + if ((HashTable = (GifHashTableType *) malloc(sizeof(GifHashTableType))) + == NULL) + return NULL; + + _ClearHashTable(HashTable); + + return HashTable; +} + +/****************************************************************************** + Routine to clear the HashTable to an empty state. * + This part is a little machine depended. Use the commented part otherwise. * +******************************************************************************/ +void _ClearHashTable(GifHashTableType *HashTable) +{ + memset(HashTable -> HTable, 0xFF, HT_SIZE * sizeof(uint32_t)); +} + +/****************************************************************************** + Routine to insert a new Item into the HashTable. The data is assumed to be * + new one. * +******************************************************************************/ +void _InsertHashTable(GifHashTableType *HashTable, uint32_t Key, int Code) +{ + int HKey = KeyItem(Key); + uint32_t *HTable = HashTable -> HTable; + +#ifdef DEBUG_HIT_RATE + NumberOfTests++; + NumberOfMisses++; +#endif /* DEBUG_HIT_RATE */ + + while (HT_GET_KEY(HTable[HKey]) != 0xFFFFFL) { +#ifdef DEBUG_HIT_RATE + NumberOfMisses++; +#endif /* DEBUG_HIT_RATE */ + HKey = (HKey + 1) & HT_KEY_MASK; + } + HTable[HKey] = HT_PUT_KEY(Key) | HT_PUT_CODE(Code); +} + +/****************************************************************************** + Routine to test if given Key exists in HashTable and if so returns its code * + Returns the Code if key was found, -1 if not. * +******************************************************************************/ +int _ExistsHashTable(GifHashTableType *HashTable, uint32_t Key) +{ + int HKey = KeyItem(Key); + uint32_t *HTable = HashTable -> HTable, HTKey; + +#ifdef DEBUG_HIT_RATE + NumberOfTests++; + NumberOfMisses++; +#endif /* DEBUG_HIT_RATE */ + + while ((HTKey = HT_GET_KEY(HTable[HKey])) != 0xFFFFFL) { +#ifdef DEBUG_HIT_RATE + NumberOfMisses++; +#endif /* DEBUG_HIT_RATE */ + if (Key == HTKey) return HT_GET_CODE(HTable[HKey]); + HKey = (HKey + 1) & HT_KEY_MASK; + } + + return -1; +} + +/****************************************************************************** + Routine to generate an HKey for the hashtable out of the given unique key. * + The given Key is assumed to be 20 bits as follows: lower 8 bits are the * + new postfix character, while the upper 12 bits are the prefix code. * + Because the average hit ratio is only 2 (2 hash references per entry), * + evaluating more complex keys (such as twin prime keys) does not worth it! * +******************************************************************************/ +static int KeyItem(uint32_t Item) +{ + return ((Item >> 12) ^ Item) & HT_KEY_MASK; +} + +#ifdef DEBUG_HIT_RATE +/****************************************************************************** + Debugging routine to print the hit ratio - number of times the hash table * + was tested per operation. This routine was used to test the KeyItem routine * +******************************************************************************/ +void HashTablePrintHitRatio(void) +{ + printf("Hash Table Hit Ratio is %ld/%ld = %ld%%.\n", + NumberOfMisses, NumberOfTests, + NumberOfMisses * 100 / NumberOfTests); +} +#endif /* DEBUG_HIT_RATE */ + +/* end */ diff --git a/src/vendor/QtGifImage/3rdParty/giflib/gif_hash.h b/src/vendor/QtGifImage/3rdParty/giflib/gif_hash.h new file mode 100644 index 00000000..7c10071b --- /dev/null +++ b/src/vendor/QtGifImage/3rdParty/giflib/gif_hash.h @@ -0,0 +1,41 @@ +/****************************************************************************** + +gif_hash.h - magfic constants and declarations for GIF LZW + +******************************************************************************/ + +#ifndef _GIF_HASH_H_ +#define _GIF_HASH_H_ + +#ifndef _WIN32 +#include +#endif +#include + +#define HT_SIZE 8192 /* 12bits = 4096 or twice as big! */ +#define HT_KEY_MASK 0x1FFF /* 13bits keys */ +#define HT_KEY_NUM_BITS 13 /* 13bits keys */ +#define HT_MAX_KEY 8191 /* 13bits - 1, maximal code possible */ +#define HT_MAX_CODE 4095 /* Biggest code possible in 12 bits. */ + +/* The 32 bits of the long are divided into two parts for the key & code: */ +/* 1. The code is 12 bits as our compression algorithm is limited to 12bits */ +/* 2. The key is 12 bits Prefix code + 8 bit new char or 20 bits. */ +/* The key is the upper 20 bits. The code is the lower 12. */ +#define HT_GET_KEY(l) (l >> 12) +#define HT_GET_CODE(l) (l & 0x0FFF) +#define HT_PUT_KEY(l) (l << 12) +#define HT_PUT_CODE(l) (l & 0x0FFF) + +typedef struct GifHashTableType { + uint32_t HTable[HT_SIZE]; +} GifHashTableType; + +GifHashTableType *_InitHashTable(void); +void _ClearHashTable(GifHashTableType *HashTable); +void _InsertHashTable(GifHashTableType *HashTable, uint32_t Key, int Code); +int _ExistsHashTable(GifHashTableType *HashTable, uint32_t Key); + +#endif /* _GIF_HASH_H_ */ + +/* end */ diff --git a/src/vendor/QtGifImage/3rdParty/giflib/gif_lib.h b/src/vendor/QtGifImage/3rdParty/giflib/gif_lib.h new file mode 100644 index 00000000..6624cbef --- /dev/null +++ b/src/vendor/QtGifImage/3rdParty/giflib/gif_lib.h @@ -0,0 +1,316 @@ +/****************************************************************************** + +gif_lib.h - service library for decoding and encoding GIF images + +*****************************************************************************/ + +#ifndef _GIF_LIB_H_ +#define _GIF_LIB_H_ 1 + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define GIFLIB_MAJOR 5 +#define GIFLIB_MINOR 0 +#define GIFLIB_RELEASE 5 + +#define GIF_ERROR 0 +#define GIF_OK 1 + +#include +#ifdef _MSC_VER +#define BOOL int +#define TRUE 1 +#define FALSE 0 +#else +#include +#define BOOL _Bool +#define TRUE 1 +#define FALSE 0 +#endif + +#define GIF_STAMP "GIFVER" /* First chars in file - GIF stamp. */ +#define GIF_STAMP_LEN sizeof(GIF_STAMP) - 1 +#define GIF_VERSION_POS 3 /* Version first character in stamp. */ +#define GIF87_STAMP "GIF87a" /* First chars in file - GIF stamp. */ +#define GIF89_STAMP "GIF89a" /* First chars in file - GIF stamp. */ + +typedef unsigned char GifPixelType; +typedef unsigned char *GifRowType; +typedef unsigned char GifByteType; +typedef unsigned int GifPrefixType; +typedef int GifWord; + +typedef struct GifColorType { + GifByteType Red, Green, Blue; +} GifColorType; + +typedef struct ColorMapObject { + int ColorCount; + int BitsPerPixel; + BOOL SortFlag; + GifColorType *Colors; /* on malloc(3) heap */ +} ColorMapObject; + +typedef struct GifImageDesc { + GifWord Left, Top, Width, Height; /* Current image dimensions. */ + BOOL Interlace; /* Sequential/Interlaced lines. */ + ColorMapObject *ColorMap; /* The local color map */ +} GifImageDesc; + +typedef struct ExtensionBlock { + int ByteCount; + GifByteType *Bytes; /* on malloc(3) heap */ + int Function; /* The block function code */ +#define CONTINUE_EXT_FUNC_CODE 0x00 /* continuation subblock */ +#define COMMENT_EXT_FUNC_CODE 0xfe /* comment */ +#define GRAPHICS_EXT_FUNC_CODE 0xf9 /* graphics control (GIF89) */ +#define PLAINTEXT_EXT_FUNC_CODE 0x01 /* plaintext */ +#define APPLICATION_EXT_FUNC_CODE 0xff /* application block */ +} ExtensionBlock; + +typedef struct SavedImage { + GifImageDesc ImageDesc; + GifByteType *RasterBits; /* on malloc(3) heap */ + int ExtensionBlockCount; /* Count of extensions before image */ + ExtensionBlock *ExtensionBlocks; /* Extensions before image */ +} SavedImage; + +typedef struct GifFileType { + GifWord SWidth, SHeight; /* Size of virtual canvas */ + GifWord SColorResolution; /* How many colors can we generate? */ + GifWord SBackGroundColor; /* Background color for virtual canvas */ + GifByteType AspectByte; /* Used to compute pixel aspect ratio */ + ColorMapObject *SColorMap; /* Global colormap, NULL if nonexistent. */ + int ImageCount; /* Number of current image (both APIs) */ + GifImageDesc Image; /* Current image (low-level API) */ + SavedImage *SavedImages; /* Image sequence (high-level API) */ + int ExtensionBlockCount; /* Count extensions past last image */ + ExtensionBlock *ExtensionBlocks; /* Extensions past last image */ + int Error; /* Last error condition reported */ + void *UserData; /* hook to attach user data (TVT) */ + void *Private; /* Don't mess with this! */ +} GifFileType; + +#define GIF_ASPECT_RATIO(n) ((n)+15.0/64.0) + +typedef enum { + UNDEFINED_RECORD_TYPE, + SCREEN_DESC_RECORD_TYPE, + IMAGE_DESC_RECORD_TYPE, /* Begin with ',' */ + EXTENSION_RECORD_TYPE, /* Begin with '!' */ + TERMINATE_RECORD_TYPE /* Begin with ';' */ +} GifRecordType; + +/* func type to read gif data from arbitrary sources (TVT) */ +typedef int (*InputFunc) (GifFileType *, GifByteType *, int); + +/* func type to write gif data to arbitrary targets. + * Returns count of bytes written. (MRB) + */ +typedef int (*OutputFunc) (GifFileType *, const GifByteType *, int); + +/****************************************************************************** + GIF89 structures +******************************************************************************/ + +typedef struct GraphicsControlBlock { + int DisposalMode; +#define DISPOSAL_UNSPECIFIED 0 /* No disposal specified. */ +#define DISPOSE_DO_NOT 1 /* Leave image in place */ +#define DISPOSE_BACKGROUND 2 /* Set area too background color */ +#define DISPOSE_PREVIOUS 3 /* Restore to previous content */ + BOOL UserInputFlag; /* User confirmation required before disposal */ + int DelayTime; /* pre-display delay in 0.01sec units */ + int TransparentColor; /* Palette index for transparency, -1 if none */ +#define NO_TRANSPARENT_COLOR -1 +} GraphicsControlBlock; + +/****************************************************************************** + GIF encoding routines +******************************************************************************/ + +/* Main entry points */ +GifFileType *EGifOpenFileName(const char *GifFileName, + const BOOL GifTestExistence, int *Error); +GifFileType *EGifOpenFileHandle(const int GifFileHandle, int *Error); +GifFileType *EGifOpen(void *userPtr, OutputFunc writeFunc, int *Error); +int EGifSpew(GifFileType * GifFile); +char *EGifGetGifVersion(GifFileType *GifFile); /* new in 5.x */ +int EGifCloseFile(GifFileType * GifFile); + +#define E_GIF_ERR_OPEN_FAILED 1 /* And EGif possible errors. */ +#define E_GIF_ERR_WRITE_FAILED 2 +#define E_GIF_ERR_HAS_SCRN_DSCR 3 +#define E_GIF_ERR_HAS_IMAG_DSCR 4 +#define E_GIF_ERR_NO_COLOR_MAP 5 +#define E_GIF_ERR_DATA_TOO_BIG 6 +#define E_GIF_ERR_NOT_ENOUGH_MEM 7 +#define E_GIF_ERR_DISK_IS_FULL 8 +#define E_GIF_ERR_CLOSE_FAILED 9 +#define E_GIF_ERR_NOT_WRITEABLE 10 + +/* These are legacy. You probably do not want to call them directly */ +int EGifPutScreenDesc(GifFileType *GifFile, + const int GifWidth, const int GifHeight, + const int GifColorRes, + const int GifBackGround, + const ColorMapObject *GifColorMap); +int EGifPutImageDesc(GifFileType *GifFile, + const int GifLeft, const int GifTop, + const int GifWidth, const int GifHeight, + const BOOL GifInterlace, + const ColorMapObject *GifColorMap); +void EGifSetGifVersion(GifFileType *GifFile, const BOOL gif89); +int EGifPutLine(GifFileType *GifFile, GifPixelType *GifLine, + int GifLineLen); +int EGifPutPixel(GifFileType *GifFile, const GifPixelType GifPixel); +int EGifPutComment(GifFileType *GifFile, const char *GifComment); +int EGifPutExtensionLeader(GifFileType *GifFile, const int GifExtCode); +int EGifPutExtensionBlock(GifFileType *GifFile, + const int GifExtLen, const void *GifExtension); +int EGifPutExtensionTrailer(GifFileType *GifFile); +int EGifPutExtension(GifFileType *GifFile, const int GifExtCode, + const int GifExtLen, + const void *GifExtension); +int EGifPutCode(GifFileType *GifFile, int GifCodeSize, + const GifByteType *GifCodeBlock); +int EGifPutCodeNext(GifFileType *GifFile, + const GifByteType *GifCodeBlock); + +/****************************************************************************** + GIF decoding routines +******************************************************************************/ + +/* Main entry points */ +GifFileType *DGifOpenFileName(const char *GifFileName, int *Error); +GifFileType *DGifOpenFileHandle(int GifFileHandle, int *Error); +int DGifSlurp(GifFileType * GifFile); +GifFileType *DGifOpen(void *userPtr, InputFunc readFunc, int *Error); /* new one (TVT) */ +int DGifCloseFile(GifFileType * GifFile); + +#define D_GIF_ERR_OPEN_FAILED 101 /* And DGif possible errors. */ +#define D_GIF_ERR_READ_FAILED 102 +#define D_GIF_ERR_NOT_GIF_FILE 103 +#define D_GIF_ERR_NO_SCRN_DSCR 104 +#define D_GIF_ERR_NO_IMAG_DSCR 105 +#define D_GIF_ERR_NO_COLOR_MAP 106 +#define D_GIF_ERR_WRONG_RECORD 107 +#define D_GIF_ERR_DATA_TOO_BIG 108 +#define D_GIF_ERR_NOT_ENOUGH_MEM 109 +#define D_GIF_ERR_CLOSE_FAILED 110 +#define D_GIF_ERR_NOT_READABLE 111 +#define D_GIF_ERR_IMAGE_DEFECT 112 +#define D_GIF_ERR_EOF_TOO_SOON 113 + +/* These are legacy. You probably do not want to call them directly */ +int DGifGetScreenDesc(GifFileType *GifFile); +int DGifGetRecordType(GifFileType *GifFile, GifRecordType *GifType); +int DGifGetImageDesc(GifFileType *GifFile); +int DGifGetLine(GifFileType *GifFile, GifPixelType *GifLine, int GifLineLen); +int DGifGetPixel(GifFileType *GifFile, GifPixelType GifPixel); +int DGifGetComment(GifFileType *GifFile, char *GifComment); +int DGifGetExtension(GifFileType *GifFile, int *GifExtCode, + GifByteType **GifExtension); +int DGifGetExtensionNext(GifFileType *GifFile, GifByteType **GifExtension); +int DGifGetCode(GifFileType *GifFile, int *GifCodeSize, + GifByteType **GifCodeBlock); +int DGifGetCodeNext(GifFileType *GifFile, GifByteType **GifCodeBlock); +int DGifGetLZCodes(GifFileType *GifFile, int *GifCode); + + +/****************************************************************************** + Color table quantization (deprecated) +******************************************************************************/ +int GifQuantizeBuffer(unsigned int Width, unsigned int Height, + int *ColorMapSize, GifByteType * RedInput, + GifByteType * GreenInput, GifByteType * BlueInput, + GifByteType * OutputBuffer, + GifColorType * OutputColorMap); + +/****************************************************************************** + Error handling and reporting. +******************************************************************************/ +extern char *GifErrorString(int ErrorCode); /* new in 2012 - ESR */ + +/***************************************************************************** + Everything below this point is new after version 1.2, supporting `slurp + mode' for doing I/O in two big belts with all the image-bashing in core. +******************************************************************************/ + +/****************************************************************************** + Color map handling from gif_alloc.c +******************************************************************************/ + +extern ColorMapObject *GifMakeMapObject(int ColorCount, + const GifColorType *ColorMap); +extern void GifFreeMapObject(ColorMapObject *Object); +extern ColorMapObject *GifUnionColorMap(const ColorMapObject *ColorIn1, + const ColorMapObject *ColorIn2, + GifPixelType ColorTransIn2[]); +extern int GifBitSize(int n); + +/****************************************************************************** + Support for the in-core structures allocation (slurp mode). +******************************************************************************/ + +extern void GifApplyTranslation(SavedImage *Image, GifPixelType Translation[]); +extern int GifAddExtensionBlock(int *ExtensionBlock_Count, + ExtensionBlock **ExtensionBlocks, + int Function, + unsigned int Len, unsigned char ExtData[]); +extern void GifFreeExtensions(int *ExtensionBlock_Count, + ExtensionBlock **ExtensionBlocks); +extern SavedImage *GifMakeSavedImage(GifFileType *GifFile, + const SavedImage *CopyFrom); +extern void GifFreeSavedImages(GifFileType *GifFile); + +/****************************************************************************** + 5.x functions for GIF89 graphics control blocks +******************************************************************************/ + +int DGifExtensionToGCB(const size_t GifExtensionLength, + const GifByteType *GifExtension, + GraphicsControlBlock *GCB); +size_t EGifGCBToExtension(const GraphicsControlBlock *GCB, + GifByteType *GifExtension); + +int DGifSavedExtensionToGCB(GifFileType *GifFile, + int ImageIndex, + GraphicsControlBlock *GCB); +int EGifGCBToSavedExtension(const GraphicsControlBlock *GCB, + GifFileType *GifFile, + int ImageIndex); + +/****************************************************************************** + The library's internal utility font +******************************************************************************/ + +#define GIF_FONT_WIDTH 8 +#define GIF_FONT_HEIGHT 8 +extern const unsigned char GifAsciiTable8x8[][GIF_FONT_WIDTH]; + +extern void GifDrawText8x8(SavedImage *Image, + const int x, const int y, + const char *legend, const int color); + +extern void GifDrawBox(SavedImage *Image, + const int x, const int y, + const int w, const int d, const int color); + +extern void GifDrawRectangle(SavedImage *Image, + const int x, const int y, + const int w, const int d, const int color); + +extern void GifDrawBoxedText8x8(SavedImage *Image, + const int x, const int y, + const char *legend, + const int border, const int bg, const int fg); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* _GIF_LIB_H */ + +/* end */ diff --git a/src/vendor/QtGifImage/3rdParty/giflib/gif_lib_private.h b/src/vendor/QtGifImage/3rdParty/giflib/gif_lib_private.h new file mode 100644 index 00000000..4022a3f9 --- /dev/null +++ b/src/vendor/QtGifImage/3rdParty/giflib/gif_lib_private.h @@ -0,0 +1,59 @@ +/**************************************************************************** + +gif_lib_private.h - internal giflib routines and structures + +****************************************************************************/ + +#ifndef _GIF_LIB_PRIVATE_H +#define _GIF_LIB_PRIVATE_H + +#include "gif_lib.h" +#include "gif_hash.h" + +#define EXTENSION_INTRODUCER 0x21 +#define DESCRIPTOR_INTRODUCER 0x2c +#define TERMINATOR_INTRODUCER 0x3b + +#define LZ_MAX_CODE 4095 /* Biggest code possible in 12 bits. */ +#define LZ_BITS 12 + +#define FLUSH_OUTPUT 4096 /* Impossible code, to signal flush. */ +#define FIRST_CODE 4097 /* Impossible code, to signal first. */ +#define NO_SUCH_CODE 4098 /* Impossible code, to signal empty. */ + +#define FILE_STATE_WRITE 0x01 +#define FILE_STATE_SCREEN 0x02 +#define FILE_STATE_IMAGE 0x04 +#define FILE_STATE_READ 0x08 + +#define IS_READABLE(Private) (Private->FileState & FILE_STATE_READ) +#define IS_WRITEABLE(Private) (Private->FileState & FILE_STATE_WRITE) + +typedef struct GifFilePrivateType { + GifWord FileState, FileHandle, /* Where all this data goes to! */ + BitsPerPixel, /* Bits per pixel (Codes uses at least this + 1). */ + ClearCode, /* The CLEAR LZ code. */ + EOFCode, /* The EOF LZ code. */ + RunningCode, /* The next code algorithm can generate. */ + RunningBits, /* The number of bits required to represent RunningCode. */ + MaxCode1, /* 1 bigger than max. possible code, in RunningBits bits. */ + LastCode, /* The code before the current code. */ + CrntCode, /* Current algorithm code. */ + StackPtr, /* For character stack (see below). */ + CrntShiftState; /* Number of bits in CrntShiftDWord. */ + unsigned long CrntShiftDWord; /* For bytes decomposition into codes. */ + unsigned long PixelCount; /* Number of pixels in image. */ + FILE *File; /* File as stream. */ + InputFunc Read; /* function to read gif input (TVT) */ + OutputFunc Write; /* function to write gif output (MRB) */ + GifByteType Buf[256]; /* Compressed input is buffered here. */ + GifByteType Stack[LZ_MAX_CODE]; /* Decoded pixels are stacked here. */ + GifByteType Suffix[LZ_MAX_CODE + 1]; /* So we can trace the codes. */ + GifPrefixType Prefix[LZ_MAX_CODE + 1]; + GifHashTableType *HashTable; + BOOL gif89; +} GifFilePrivateType; + +#endif /* _GIF_LIB_PRIVATE_H */ + +/* end */ diff --git a/src/vendor/QtGifImage/3rdParty/giflib/gifalloc.c b/src/vendor/QtGifImage/3rdParty/giflib/gifalloc.c new file mode 100644 index 00000000..5726e768 --- /dev/null +++ b/src/vendor/QtGifImage/3rdParty/giflib/gifalloc.c @@ -0,0 +1,400 @@ +/***************************************************************************** + + GIF construction tools + +****************************************************************************/ + +#include +#include +#include + +#include "gif_lib.h" + +#define MAX(x, y) (((x) > (y)) ? (x) : (y)) + +/****************************************************************************** + Miscellaneous utility functions +******************************************************************************/ + +/* return smallest bitfield size n will fit in */ +int +GifBitSize(int n) +{ + register int i; + + for (i = 1; i <= 8; i++) + if ((1 << i) >= n) + break; + return (i); +} + +/****************************************************************************** + Color map object functions +******************************************************************************/ + +/* + * Allocate a color map of given size; initialize with contents of + * ColorMap if that pointer is non-NULL. + */ +ColorMapObject * +GifMakeMapObject(int ColorCount, const GifColorType *ColorMap) +{ + ColorMapObject *Object; + + /*** FIXME: Our ColorCount has to be a power of two. Is it necessary to + * make the user know that or should we automatically round up instead? */ + if (ColorCount != (1 << GifBitSize(ColorCount))) { + return ((ColorMapObject *) NULL); + } + + Object = (ColorMapObject *)malloc(sizeof(ColorMapObject)); + if (Object == (ColorMapObject *) NULL) { + return ((ColorMapObject *) NULL); + } + + Object->Colors = (GifColorType *)calloc(ColorCount, sizeof(GifColorType)); + if (Object->Colors == (GifColorType *) NULL) { + free(Object); + return ((ColorMapObject *) NULL); + } + + Object->ColorCount = ColorCount; + Object->BitsPerPixel = GifBitSize(ColorCount); + + if (ColorMap != NULL) { + memcpy((char *)Object->Colors, + (char *)ColorMap, ColorCount * sizeof(GifColorType)); + } + + return (Object); +} + +/******************************************************************************* +Free a color map object +*******************************************************************************/ +void +GifFreeMapObject(ColorMapObject *Object) +{ + if (Object != NULL) { + (void)free(Object->Colors); + (void)free(Object); + } +} + +#ifdef DEBUG +void +DumpColorMap(ColorMapObject *Object, + FILE * fp) +{ + if (Object != NULL) { + int i, j, Len = Object->ColorCount; + + for (i = 0; i < Len; i += 4) { + for (j = 0; j < 4 && j < Len; j++) { + (void)fprintf(fp, "%3d: %02x %02x %02x ", i + j, + Object->Colors[i + j].Red, + Object->Colors[i + j].Green, + Object->Colors[i + j].Blue); + } + (void)fprintf(fp, "\n"); + } + } +} +#endif /* DEBUG */ + +/******************************************************************************* + Compute the union of two given color maps and return it. If result can't + fit into 256 colors, NULL is returned, the allocated union otherwise. + ColorIn1 is copied as is to ColorUnion, while colors from ColorIn2 are + copied iff they didn't exist before. ColorTransIn2 maps the old + ColorIn2 into the ColorUnion color map table./ +*******************************************************************************/ +ColorMapObject * +GifUnionColorMap(const ColorMapObject *ColorIn1, + const ColorMapObject *ColorIn2, + GifPixelType ColorTransIn2[]) +{ + int i, j, CrntSlot, RoundUpTo, NewGifBitSize; + ColorMapObject *ColorUnion; + + /* + * We don't worry about duplicates within either color map; if + * the caller wants to resolve those, he can perform unions + * with an empty color map. + */ + + /* Allocate table which will hold the result for sure. */ + ColorUnion = GifMakeMapObject(MAX(ColorIn1->ColorCount, + ColorIn2->ColorCount) * 2, NULL); + + if (ColorUnion == NULL) + return (NULL); + + /* + * Copy ColorIn1 to ColorUnion. + */ + for (i = 0; i < ColorIn1->ColorCount; i++) + ColorUnion->Colors[i] = ColorIn1->Colors[i]; + CrntSlot = ColorIn1->ColorCount; + + /* + * Potentially obnoxious hack: + * + * Back CrntSlot down past all contiguous {0, 0, 0} slots at the end + * of table 1. This is very useful if your display is limited to + * 16 colors. + */ + while (ColorIn1->Colors[CrntSlot - 1].Red == 0 + && ColorIn1->Colors[CrntSlot - 1].Green == 0 + && ColorIn1->Colors[CrntSlot - 1].Blue == 0) + CrntSlot--; + + /* Copy ColorIn2 to ColorUnion (use old colors if they exist): */ + for (i = 0; i < ColorIn2->ColorCount && CrntSlot <= 256; i++) { + /* Let's see if this color already exists: */ + for (j = 0; j < ColorIn1->ColorCount; j++) + if (memcmp (&ColorIn1->Colors[j], &ColorIn2->Colors[i], + sizeof(GifColorType)) == 0) + break; + + if (j < ColorIn1->ColorCount) + ColorTransIn2[i] = j; /* color exists in Color1 */ + else { + /* Color is new - copy it to a new slot: */ + ColorUnion->Colors[CrntSlot] = ColorIn2->Colors[i]; + ColorTransIn2[i] = CrntSlot++; + } + } + + if (CrntSlot > 256) { + GifFreeMapObject(ColorUnion); + return ((ColorMapObject *) NULL); + } + + NewGifBitSize = GifBitSize(CrntSlot); + RoundUpTo = (1 << NewGifBitSize); + + if (RoundUpTo != ColorUnion->ColorCount) { + register GifColorType *Map = ColorUnion->Colors; + + /* + * Zero out slots up to next power of 2. + * We know these slots exist because of the way ColorUnion's + * start dimension was computed. + */ + for (j = CrntSlot; j < RoundUpTo; j++) + Map[j].Red = Map[j].Green = Map[j].Blue = 0; + + /* perhaps we can shrink the map? */ + if (RoundUpTo < ColorUnion->ColorCount) + ColorUnion->Colors = (GifColorType *)realloc(Map, + sizeof(GifColorType) * RoundUpTo); + } + + ColorUnion->ColorCount = RoundUpTo; + ColorUnion->BitsPerPixel = NewGifBitSize; + + return (ColorUnion); +} + +/******************************************************************************* + Apply a given color translation to the raster bits of an image +*******************************************************************************/ +void +GifApplyTranslation(SavedImage *Image, GifPixelType Translation[]) +{ + register int i; + register int RasterSize = Image->ImageDesc.Height * Image->ImageDesc.Width; + + for (i = 0; i < RasterSize; i++) + Image->RasterBits[i] = Translation[Image->RasterBits[i]]; +} + +/****************************************************************************** + Extension record functions +******************************************************************************/ +int +GifAddExtensionBlock(int *ExtensionBlockCount, + ExtensionBlock **ExtensionBlocks, + int Function, + unsigned int Len, + unsigned char ExtData[]) +{ + ExtensionBlock *ep; + + if (*ExtensionBlocks == NULL) + *ExtensionBlocks=(ExtensionBlock *)malloc(sizeof(ExtensionBlock)); + else + *ExtensionBlocks = (ExtensionBlock *)realloc(*ExtensionBlocks, + sizeof(ExtensionBlock) * + (*ExtensionBlockCount + 1)); + + if (*ExtensionBlocks == NULL) + return (GIF_ERROR); + + ep = &(*ExtensionBlocks)[(*ExtensionBlockCount)++]; + + ep->Function = Function; + ep->ByteCount=Len; + ep->Bytes = (GifByteType *)malloc(ep->ByteCount); + if (ep->Bytes == NULL) + return (GIF_ERROR); + + if (ExtData != NULL) { + memcpy(ep->Bytes, ExtData, Len); + } + + return (GIF_OK); +} + +void +GifFreeExtensions(int *ExtensionBlockCount, + ExtensionBlock **ExtensionBlocks) +{ + ExtensionBlock *ep; + + if (*ExtensionBlocks == NULL) + return; + + for (ep = *ExtensionBlocks; + ep < (*ExtensionBlocks + *ExtensionBlockCount); + ep++) + (void)free((char *)ep->Bytes); + (void)free((char *)*ExtensionBlocks); + *ExtensionBlocks = NULL; + *ExtensionBlockCount = 0; +} + +/****************************************************************************** + Image block allocation functions +******************************************************************************/ + +/* Private Function: + * Frees the last image in the GifFile->SavedImages array + */ +void +FreeLastSavedImage(GifFileType *GifFile) +{ + SavedImage *sp; + + if ((GifFile == NULL) || (GifFile->SavedImages == NULL)) + return; + + /* Remove one SavedImage from the GifFile */ + GifFile->ImageCount--; + sp = &GifFile->SavedImages[GifFile->ImageCount]; + + /* Deallocate its Colormap */ + if (sp->ImageDesc.ColorMap != NULL) { + GifFreeMapObject(sp->ImageDesc.ColorMap); + sp->ImageDesc.ColorMap = NULL; + } + + /* Deallocate the image data */ + if (sp->RasterBits != NULL) + free((char *)sp->RasterBits); + + /* Deallocate any extensions */ + GifFreeExtensions(&sp->ExtensionBlockCount, &sp->ExtensionBlocks); + + /*** FIXME: We could realloc the GifFile->SavedImages structure but is + * there a point to it? Saves some memory but we'd have to do it every + * time. If this is used in GifFreeSavedImages then it would be inefficient + * (The whole array is going to be deallocated.) If we just use it when + * we want to free the last Image it's convenient to do it here. + */ +} + +/* + * Append an image block to the SavedImages array + */ +SavedImage * +GifMakeSavedImage(GifFileType *GifFile, const SavedImage *CopyFrom) +{ + if (GifFile->SavedImages == NULL) + GifFile->SavedImages = (SavedImage *)malloc(sizeof(SavedImage)); + else + GifFile->SavedImages = (SavedImage *)realloc(GifFile->SavedImages, + sizeof(SavedImage) * (GifFile->ImageCount + 1)); + + if (GifFile->SavedImages == NULL) + return ((SavedImage *)NULL); + else { + SavedImage *sp = &GifFile->SavedImages[GifFile->ImageCount++]; + memset((char *)sp, '\0', sizeof(SavedImage)); + + if (CopyFrom != NULL) { + memcpy((char *)sp, CopyFrom, sizeof(SavedImage)); + + /* + * Make our own allocated copies of the heap fields in the + * copied record. This guards against potential aliasing + * problems. + */ + + /* first, the local color map */ + if (sp->ImageDesc.ColorMap != NULL) { + sp->ImageDesc.ColorMap = GifMakeMapObject( + CopyFrom->ImageDesc.ColorMap->ColorCount, + CopyFrom->ImageDesc.ColorMap->Colors); + if (sp->ImageDesc.ColorMap == NULL) { + FreeLastSavedImage(GifFile); + return (SavedImage *)(NULL); + } + } + + /* next, the raster */ + sp->RasterBits = (unsigned char *)malloc(sizeof(GifPixelType) * + CopyFrom->ImageDesc.Height * + CopyFrom->ImageDesc.Width); + if (sp->RasterBits == NULL) { + FreeLastSavedImage(GifFile); + return (SavedImage *)(NULL); + } + memcpy(sp->RasterBits, CopyFrom->RasterBits, + sizeof(GifPixelType) * CopyFrom->ImageDesc.Height * + CopyFrom->ImageDesc.Width); + + /* finally, the extension blocks */ + if (sp->ExtensionBlocks != NULL) { + sp->ExtensionBlocks = (ExtensionBlock *)malloc( + sizeof(ExtensionBlock) * + CopyFrom->ExtensionBlockCount); + if (sp->ExtensionBlocks == NULL) { + FreeLastSavedImage(GifFile); + return (SavedImage *)(NULL); + } + memcpy(sp->ExtensionBlocks, CopyFrom->ExtensionBlocks, + sizeof(ExtensionBlock) * CopyFrom->ExtensionBlockCount); + } + } + + return (sp); + } +} + +void +GifFreeSavedImages(GifFileType *GifFile) +{ + SavedImage *sp; + + if ((GifFile == NULL) || (GifFile->SavedImages == NULL)) { + return; + } + for (sp = GifFile->SavedImages; + sp < GifFile->SavedImages + GifFile->ImageCount; sp++) { + if (sp->ImageDesc.ColorMap != NULL) { + GifFreeMapObject(sp->ImageDesc.ColorMap); + sp->ImageDesc.ColorMap = NULL; + } + + if (sp->RasterBits != NULL) + free((char *)sp->RasterBits); + + GifFreeExtensions(&sp->ExtensionBlockCount, &sp->ExtensionBlocks); + } + free((char *)GifFile->SavedImages); + GifFile->SavedImages = NULL; +} + +/* end */ diff --git a/src/vendor/QtGifImage/3rdParty/giflib/quantize.c b/src/vendor/QtGifImage/3rdParty/giflib/quantize.c new file mode 100644 index 00000000..fc31f36e --- /dev/null +++ b/src/vendor/QtGifImage/3rdParty/giflib/quantize.c @@ -0,0 +1,309 @@ +/***************************************************************************** + + quantize.c - quantize a high resolution image into lower one + + Based on: "Color Image Quantization for frame buffer Display", by + Paul Heckbert SIGGRAPH 1982 page 297-307. + + This doesn't really belong in the core library, was undocumented, + and was removed in 4.2. Then it turned out some client apps were + actually using it, so it was restored in 5.0. + +******************************************************************************/ + +#include +#include +#include "gif_lib.h" +#include "gif_lib_private.h" + +#define ABS(x) ((x) > 0 ? (x) : (-(x))) + +#define COLOR_ARRAY_SIZE 32768 +#define BITS_PER_PRIM_COLOR 5 +#define MAX_PRIM_COLOR 0x1f + +static int SortRGBAxis; + +typedef struct QuantizedColorType { + GifByteType RGB[3]; + GifByteType NewColorIndex; + long Count; + struct QuantizedColorType *Pnext; +} QuantizedColorType; + +typedef struct NewColorMapType { + GifByteType RGBMin[3], RGBWidth[3]; + unsigned int NumEntries; /* # of QuantizedColorType in linked list below */ + unsigned long Count; /* Total number of pixels in all the entries */ + QuantizedColorType *QuantizedColors; +} NewColorMapType; + +static int SubdivColorMap(NewColorMapType * NewColorSubdiv, + unsigned int ColorMapSize, + unsigned int *NewColorMapSize); +static int SortCmpRtn(const void *Entry1, const void *Entry2); + +/****************************************************************************** + Quantize high resolution image into lower one. Input image consists of a + 2D array for each of the RGB colors with size Width by Height. There is no + Color map for the input. Output is a quantized image with 2D array of + indexes into the output color map. + Note input image can be 24 bits at the most (8 for red/green/blue) and + the output has 256 colors at the most (256 entries in the color map.). + ColorMapSize specifies size of color map up to 256 and will be updated to + real size before returning. + Also non of the parameter are allocated by this routine. + This function returns GIF_OK if successful, GIF_ERROR otherwise. +******************************************************************************/ +int +GifQuantizeBuffer(unsigned int Width, + unsigned int Height, + int *ColorMapSize, + GifByteType * RedInput, + GifByteType * GreenInput, + GifByteType * BlueInput, + GifByteType * OutputBuffer, + GifColorType * OutputColorMap) { + + unsigned int Index, NumOfEntries; + int i, j, MaxRGBError[3]; + unsigned int NewColorMapSize; + long Red, Green, Blue; + NewColorMapType NewColorSubdiv[256]; + QuantizedColorType *ColorArrayEntries, *QuantizedColor; + + ColorArrayEntries = (QuantizedColorType *)malloc( + sizeof(QuantizedColorType) * COLOR_ARRAY_SIZE); + if (ColorArrayEntries == NULL) { + return GIF_ERROR; + } + + for (i = 0; i < COLOR_ARRAY_SIZE; i++) { + ColorArrayEntries[i].RGB[0] = i >> (2 * BITS_PER_PRIM_COLOR); + ColorArrayEntries[i].RGB[1] = (i >> BITS_PER_PRIM_COLOR) & + MAX_PRIM_COLOR; + ColorArrayEntries[i].RGB[2] = i & MAX_PRIM_COLOR; + ColorArrayEntries[i].Count = 0; + } + + /* Sample the colors and their distribution: */ + for (i = 0; i < (int)(Width * Height); i++) { + Index = ((RedInput[i] >> (8 - BITS_PER_PRIM_COLOR)) << + (2 * BITS_PER_PRIM_COLOR)) + + ((GreenInput[i] >> (8 - BITS_PER_PRIM_COLOR)) << + BITS_PER_PRIM_COLOR) + + (BlueInput[i] >> (8 - BITS_PER_PRIM_COLOR)); + ColorArrayEntries[Index].Count++; + } + + /* Put all the colors in the first entry of the color map, and call the + * recursive subdivision process. */ + for (i = 0; i < 256; i++) { + NewColorSubdiv[i].QuantizedColors = NULL; + NewColorSubdiv[i].Count = NewColorSubdiv[i].NumEntries = 0; + for (j = 0; j < 3; j++) { + NewColorSubdiv[i].RGBMin[j] = 0; + NewColorSubdiv[i].RGBWidth[j] = 255; + } + } + + /* Find the non empty entries in the color table and chain them: */ + for (i = 0; i < COLOR_ARRAY_SIZE; i++) + if (ColorArrayEntries[i].Count > 0) + break; + QuantizedColor = NewColorSubdiv[0].QuantizedColors = &ColorArrayEntries[i]; + NumOfEntries = 1; + while (++i < COLOR_ARRAY_SIZE) + if (ColorArrayEntries[i].Count > 0) { + QuantizedColor->Pnext = &ColorArrayEntries[i]; + QuantizedColor = &ColorArrayEntries[i]; + NumOfEntries++; + } + QuantizedColor->Pnext = NULL; + + NewColorSubdiv[0].NumEntries = NumOfEntries; /* Different sampled colors */ + NewColorSubdiv[0].Count = ((long)Width) * Height; /* Pixels */ + NewColorMapSize = 1; + if (SubdivColorMap(NewColorSubdiv, *ColorMapSize, &NewColorMapSize) != + GIF_OK) { + free((char *)ColorArrayEntries); + return GIF_ERROR; + } + if (NewColorMapSize < *ColorMapSize) { + /* And clear rest of color map: */ + for (i = NewColorMapSize; i < *ColorMapSize; i++) + OutputColorMap[i].Red = OutputColorMap[i].Green = + OutputColorMap[i].Blue = 0; + } + + /* Average the colors in each entry to be the color to be used in the + * output color map, and plug it into the output color map itself. */ + for (i = 0; i < NewColorMapSize; i++) { + if ((j = NewColorSubdiv[i].NumEntries) > 0) { + QuantizedColor = NewColorSubdiv[i].QuantizedColors; + Red = Green = Blue = 0; + while (QuantizedColor) { + QuantizedColor->NewColorIndex = i; + Red += QuantizedColor->RGB[0]; + Green += QuantizedColor->RGB[1]; + Blue += QuantizedColor->RGB[2]; + QuantizedColor = QuantizedColor->Pnext; + } + OutputColorMap[i].Red = (Red << (8 - BITS_PER_PRIM_COLOR)) / j; + OutputColorMap[i].Green = (Green << (8 - BITS_PER_PRIM_COLOR)) / j; + OutputColorMap[i].Blue = (Blue << (8 - BITS_PER_PRIM_COLOR)) / j; + } + } + + /* Finally scan the input buffer again and put the mapped index in the + * output buffer. */ + MaxRGBError[0] = MaxRGBError[1] = MaxRGBError[2] = 0; + for (i = 0; i < (int)(Width * Height); i++) { + Index = ((RedInput[i] >> (8 - BITS_PER_PRIM_COLOR)) << + (2 * BITS_PER_PRIM_COLOR)) + + ((GreenInput[i] >> (8 - BITS_PER_PRIM_COLOR)) << + BITS_PER_PRIM_COLOR) + + (BlueInput[i] >> (8 - BITS_PER_PRIM_COLOR)); + Index = ColorArrayEntries[Index].NewColorIndex; + OutputBuffer[i] = Index; + if (MaxRGBError[0] < ABS(OutputColorMap[Index].Red - RedInput[i])) + MaxRGBError[0] = ABS(OutputColorMap[Index].Red - RedInput[i]); + if (MaxRGBError[1] < ABS(OutputColorMap[Index].Green - GreenInput[i])) + MaxRGBError[1] = ABS(OutputColorMap[Index].Green - GreenInput[i]); + if (MaxRGBError[2] < ABS(OutputColorMap[Index].Blue - BlueInput[i])) + MaxRGBError[2] = ABS(OutputColorMap[Index].Blue - BlueInput[i]); + } + +#ifdef DEBUG + fprintf(stderr, + "Quantization L(0) errors: Red = %d, Green = %d, Blue = %d.\n", + MaxRGBError[0], MaxRGBError[1], MaxRGBError[2]); +#endif /* DEBUG */ + + free((char *)ColorArrayEntries); + + *ColorMapSize = NewColorMapSize; + + return GIF_OK; +} + +/****************************************************************************** + Routine to subdivide the RGB space recursively using median cut in each + axes alternatingly until ColorMapSize different cubes exists. + The biggest cube in one dimension is subdivide unless it has only one entry. + Returns GIF_ERROR if failed, otherwise GIF_OK. +*******************************************************************************/ +static int +SubdivColorMap(NewColorMapType * NewColorSubdiv, + unsigned int ColorMapSize, + unsigned int *NewColorMapSize) { + + int MaxSize; + unsigned int i, j, Index = 0, NumEntries, MinColor, MaxColor; + long Sum, Count; + QuantizedColorType *QuantizedColor, **SortArray; + + while (ColorMapSize > *NewColorMapSize) { + /* Find candidate for subdivision: */ + MaxSize = -1; + for (i = 0; i < *NewColorMapSize; i++) { + for (j = 0; j < 3; j++) { + if ((((int)NewColorSubdiv[i].RGBWidth[j]) > MaxSize) && + (NewColorSubdiv[i].NumEntries > 1)) { + MaxSize = NewColorSubdiv[i].RGBWidth[j]; + Index = i; + SortRGBAxis = j; + } + } + } + + if (MaxSize == -1) + return GIF_OK; + + /* Split the entry Index into two along the axis SortRGBAxis: */ + + /* Sort all elements in that entry along the given axis and split at + * the median. */ + SortArray = (QuantizedColorType **)malloc( + sizeof(QuantizedColorType *) * + NewColorSubdiv[Index].NumEntries); + if (SortArray == NULL) + return GIF_ERROR; + for (j = 0, QuantizedColor = NewColorSubdiv[Index].QuantizedColors; + j < NewColorSubdiv[Index].NumEntries && QuantizedColor != NULL; + j++, QuantizedColor = QuantizedColor->Pnext) + SortArray[j] = QuantizedColor; + + qsort(SortArray, NewColorSubdiv[Index].NumEntries, + sizeof(QuantizedColorType *), SortCmpRtn); + + /* Relink the sorted list into one: */ + for (j = 0; j < NewColorSubdiv[Index].NumEntries - 1; j++) + SortArray[j]->Pnext = SortArray[j + 1]; + SortArray[NewColorSubdiv[Index].NumEntries - 1]->Pnext = NULL; + NewColorSubdiv[Index].QuantizedColors = QuantizedColor = SortArray[0]; + free((char *)SortArray); + + /* Now simply add the Counts until we have half of the Count: */ + Sum = NewColorSubdiv[Index].Count / 2 - QuantizedColor->Count; + NumEntries = 1; + Count = QuantizedColor->Count; + while (QuantizedColor->Pnext != NULL && + (Sum -= QuantizedColor->Pnext->Count) >= 0 && + QuantizedColor->Pnext->Pnext != NULL) { + QuantizedColor = QuantizedColor->Pnext; + NumEntries++; + Count += QuantizedColor->Count; + } + /* Save the values of the last color of the first half, and first + * of the second half so we can update the Bounding Boxes later. + * Also as the colors are quantized and the BBoxes are full 0..255, + * they need to be rescaled. + */ + MaxColor = QuantizedColor->RGB[SortRGBAxis]; /* Max. of first half */ + /* coverity[var_deref_op] */ + MinColor = QuantizedColor->Pnext->RGB[SortRGBAxis]; /* of second */ + MaxColor <<= (8 - BITS_PER_PRIM_COLOR); + MinColor <<= (8 - BITS_PER_PRIM_COLOR); + + /* Partition right here: */ + NewColorSubdiv[*NewColorMapSize].QuantizedColors = + QuantizedColor->Pnext; + QuantizedColor->Pnext = NULL; + NewColorSubdiv[*NewColorMapSize].Count = Count; + NewColorSubdiv[Index].Count -= Count; + NewColorSubdiv[*NewColorMapSize].NumEntries = + NewColorSubdiv[Index].NumEntries - NumEntries; + NewColorSubdiv[Index].NumEntries = NumEntries; + for (j = 0; j < 3; j++) { + NewColorSubdiv[*NewColorMapSize].RGBMin[j] = + NewColorSubdiv[Index].RGBMin[j]; + NewColorSubdiv[*NewColorMapSize].RGBWidth[j] = + NewColorSubdiv[Index].RGBWidth[j]; + } + NewColorSubdiv[*NewColorMapSize].RGBWidth[SortRGBAxis] = + NewColorSubdiv[*NewColorMapSize].RGBMin[SortRGBAxis] + + NewColorSubdiv[*NewColorMapSize].RGBWidth[SortRGBAxis] - MinColor; + NewColorSubdiv[*NewColorMapSize].RGBMin[SortRGBAxis] = MinColor; + + NewColorSubdiv[Index].RGBWidth[SortRGBAxis] = + MaxColor - NewColorSubdiv[Index].RGBMin[SortRGBAxis]; + + (*NewColorMapSize)++; + } + + return GIF_OK; +} + +/**************************************************************************** + Routine called by qsort to compare two entries. +*****************************************************************************/ +static int +SortCmpRtn(const void *Entry1, + const void *Entry2) { + + return (*((QuantizedColorType **) Entry1))->RGB[SortRGBAxis] - + (*((QuantizedColorType **) Entry2))->RGB[SortRGBAxis]; +} + +/* end */ diff --git a/src/vendor/QtGifImage/gifimage/doc/qtgifimage.qdocconf b/src/vendor/QtGifImage/gifimage/doc/qtgifimage.qdocconf new file mode 100644 index 00000000..7b8f9f7a --- /dev/null +++ b/src/vendor/QtGifImage/gifimage/doc/qtgifimage.qdocconf @@ -0,0 +1,75 @@ +include($QT_INSTALL_DOCS/global/qt-html-templates-offline.qdocconf) +include($QT_INSTALL_DOCS/global/qt-module-defaults.qdocconf) + +project = QtGifImage +description = Qt GifImage Reference Documentation +url = http://qtgifimage.debao.me +version = $QT_VERSION + +qhp.projects = QtGifImage + +qhp.QtGifImage.file = qtgifimage.qhp +qhp.QtGifImage.namespace = me.debao.qtgifimage.$QT_VERSION_TAG +qhp.QtGifImage.virtualFolder = qtgifimage +qhp.QtGifImage.indexTitle = Qt GifImage +qhp.QtGifImage.indexRoot = + +qhp.QtGifImage.filterAttributes = qtgifimage $QT_VERSION qtrefdoc +qhp.QtGifImage.customFilters.Qt.name = QtGifImage $QT_VERSION +qhp.QtGifImage.customFilters.Qt.filterAttributes = qtgifimage $QT_VERSION +qhp.QtGifImage.subprojects = overviews classes qmltypes examples +qhp.QtGifImage.subprojects.overviews.title = Overview +qhp.QtGifImage.subprojects.overviews.indexTitle = Qt GifImage +qhp.QtGifImage.subprojects.overviews.selectors = fake:page,group,module +qhp.QtGifImage.subprojects.classes.title = C++ Classes +qhp.QtGifImage.subprojects.classes.indexTitle = Qt GifImage C++ Classes +qhp.QtGifImage.subprojects.classes.selectors = class fake:headerfile +qhp.QtGifImage.subprojects.classes.sortPages = true +qhp.QtGifImage.subprojects.examples.title = Examples +qhp.QtGifImage.subprojects.examples.indexTitle = Qt GifImage Examples +qhp.QtGifImage.subprojects.examples.selectors = fake:example + +tagfile = ../../../doc/qtgifimage/qtgifimage.tags + +headerdirs += .. + +sourcedirs += .. + +exampledirs += ../../../examples/gifimage \ + snippets/ + +# Specify the install path under QT_INSTALL_EXAMPLES +examplesinstallpath = gifimage + +imagedirs += images + +depends += qtcore qtdoc qtgui + +HTML.footer = \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + "\n" \ + "
\n" \ + "
\n" \ + "
\n" \ + "

\n" \ + " © 2013 Debao Zhang. \n" \ + " Documentation contributions included herein are the copyrights of\n" \ + " their respective owners.

\n" \ + "

\n" \ + " The documentation provided herein is licensed under the terms of the\n" \ + " GNU Free Documentation\n" \ + " License version 1.3 as published by the Free Software Foundation.

\n" \ + "

\n" \ + " Documentation sources may be obtained from \n" \ + " github.com/dbzhang800.

\n" \ + "

\n" \ + " Qt and their respective logos are trademarks of Digia Plc \n" \ + " in Finland and/or other countries worldwide. All other trademarks are property\n" \ + " of their respective owners. Privacy Policy

\n" \ + "
\n" \ + "
\n" \ + "
\n" \ diff --git a/src/vendor/QtGifImage/gifimage/doc/snippets/doc_src_qtgifimage.cpp b/src/vendor/QtGifImage/gifimage/doc/snippets/doc_src_qtgifimage.cpp new file mode 100644 index 00000000..ecad0da8 --- /dev/null +++ b/src/vendor/QtGifImage/gifimage/doc/snippets/doc_src_qtgifimage.cpp @@ -0,0 +1,8 @@ + +//! [0] +#include +//! [0] + +//! [1] +#include +//! [1] diff --git a/src/vendor/QtGifImage/gifimage/doc/snippets/doc_src_qtgifimage.pro b/src/vendor/QtGifImage/gifimage/doc/snippets/doc_src_qtgifimage.pro new file mode 100644 index 00000000..85a9d4aa --- /dev/null +++ b/src/vendor/QtGifImage/gifimage/doc/snippets/doc_src_qtgifimage.pro @@ -0,0 +1,3 @@ +#! [1] +QT += gifimage +#! [1] diff --git a/src/vendor/QtGifImage/gifimage/doc/src/examples.qdoc b/src/vendor/QtGifImage/gifimage/doc/src/examples.qdoc new file mode 100644 index 00000000..7dfa06fe --- /dev/null +++ b/src/vendor/QtGifImage/gifimage/doc/src/examples.qdoc @@ -0,0 +1,8 @@ +/*! + \group qtgifimage-examples + \title Qt GifImage Examples + \brief Examples for the Qt GifImage module + \ingroup all-examples + + Qt GifImage comes with the following examples: +*/ diff --git a/src/vendor/QtGifImage/gifimage/doc/src/index.qdoc b/src/vendor/QtGifImage/gifimage/doc/src/index.qdoc new file mode 100644 index 00000000..59540a23 --- /dev/null +++ b/src/vendor/QtGifImage/gifimage/doc/src/index.qdoc @@ -0,0 +1,71 @@ +/**************************************************************************** +** Copyright (c) 2013 Debao Zhang +** 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. +** +****************************************************************************/ + +/*! + \title Qt GifImage + \page index.html + \brief Qt GifImage provides functionality for handling .gif files. + + The \l{Qt GifImage C++ Classes}{Qt GifImage Module} provides a set of classes to read and write .gif files. + The library can be used to + + \list + \li Generate a new .gif file from a list of QImage files. + \li Extract frames to a list of QImage files. + \endlist + + \image demo1.gif + + \table + \row + \li Source code: \li \l{https://github.com/dbzhang800/QtGifImage} + \row + \li Issures: \li \l{https://github.com/dbzhang800/QtGifImage/issues} + \row + \li License: \li MIT + \endtable + + \section1 Getting Started + + To include the definitions of the module's classes, using the following directive: + + \code + #include + \endcode + + To link against the module, add this line to your qmake .pro file: + + \code + QT += gifimage + \endcode + + More information can be found in \l{Qt GifImage Build} page. + + \section1 Related information + \list + \li \l{Qt GifImage C++ Classes} + \li \l{Qt GifImage Examples} + \endlist +*/ diff --git a/src/vendor/QtGifImage/gifimage/doc/src/qtgifimage.qdoc b/src/vendor/QtGifImage/gifimage/doc/src/qtgifimage.qdoc new file mode 100644 index 00000000..e5730745 --- /dev/null +++ b/src/vendor/QtGifImage/gifimage/doc/src/qtgifimage.qdoc @@ -0,0 +1,34 @@ +/**************************************************************************** +** Copyright (c) 2013 Debao Zhang +** 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. +** +****************************************************************************/ + +/*! + \module QtGifImage + \title Qt GifImage C++ Classes + \ingroup modules + + \brief The Qt GifImage module provides functionality for handling .gif files. + +*/ + diff --git a/src/vendor/QtGifImage/gifimage/doc/src/usage.qdoc b/src/vendor/QtGifImage/gifimage/doc/src/usage.qdoc new file mode 100644 index 00000000..28290417 --- /dev/null +++ b/src/vendor/QtGifImage/gifimage/doc/src/usage.qdoc @@ -0,0 +1,53 @@ +/*! + \page building + \title Qt GifImage Build + + \section1 Usage(1): Use Qt GifImage as Qt5's addon module + + 1. Download the source code from \l {https://github.com/dbzhang800/QtGifImage/archive/master.zip} {github.com}. + + 2. Put the source code in any directory you like. At the toplevel directory run + + \note Perl is needed in this step. + + \code + qmake + make + make install + \endcode + + The library, the header files, and others will be installed to your system. + + 3. Add following line to your qmake's project file: + + \code + QT += gifimage + \endcode + + 4. Then, using Qt Qt GifImage in your code + + \section1 Usage(2): Use source code directly + + The package contains a qtgifimage.pri file that allows you to integrate + the component into applications that use qmake for the build step. + + 1. Download the source code from \l {https://github.com/dbzhang800/QtGifImage/archive/master.zip} {github.com} + + 2. Put the source code in any directory you like. For example, 3rdparty: + + \code + |-- project.pro + |-- .... + |-- 3rdparty\ + | |-- qtgifimage\ + | | + \endcode + + 3. Add following line to your qmake project file: + + \code + include(3rdparty/qtgifimage/src/gifimage/qtgifimage.pri) + \endcode + + 4. Then, using Qt GifImage in your code +*/ diff --git a/src/vendor/QtGifImage/gifimage/gifimage.pro b/src/vendor/QtGifImage/gifimage/gifimage.pro new file mode 100644 index 00000000..e34c8835 --- /dev/null +++ b/src/vendor/QtGifImage/gifimage/gifimage.pro @@ -0,0 +1,13 @@ +TARGET = QtGifImage + +QMAKE_DOCS = $$PWD/doc/qtgifimage.qdocconf + +load(qt_module) + +CONFIG += build_gifimage_lib +include(qtgifimage.pri) + +QMAKE_TARGET_COMPANY = "Debao Zhang" +QMAKE_TARGET_COPYRIGHT = "Copyright (C) 2013 Debao Zhang " +QMAKE_TARGET_DESCRIPTION = ".gif file reader and wirter for Qt" + diff --git a/src/vendor/QtGifImage/gifimage/qgifglobal.h b/src/vendor/QtGifImage/gifimage/qgifglobal.h new file mode 100644 index 00000000..4b7c26fd --- /dev/null +++ b/src/vendor/QtGifImage/gifimage/qgifglobal.h @@ -0,0 +1,39 @@ +/**************************************************************************** +** Copyright (c) 2013 Debao Zhang +** 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 QGIFGLOBAL_H +#define QGIFGLOBAL_H +#include + +#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 // QGIFGLOBAL_H diff --git a/src/vendor/QtGifImage/gifimage/qgifimage.cpp b/src/vendor/QtGifImage/gifimage/qgifimage.cpp new file mode 100644 index 00000000..46748d94 --- /dev/null +++ b/src/vendor/QtGifImage/gifimage/qgifimage.cpp @@ -0,0 +1,678 @@ +/**************************************************************************** +** Copyright (c) 2013 Debao Zhang +** 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 "qgifimage.h" +#include "qgifimage_p.h" +#include +#include +#include +#include + +namespace +{ +int writeToIODevice(GifFileType *gifFile, const GifByteType *data, int maxSize) +{ + return static_cast(gifFile->UserData)->write(reinterpret_cast(data), maxSize); +} + +int readFromIODevice(GifFileType *gifFile, GifByteType *data, int maxSize) +{ + return static_cast(gifFile->UserData)->read(reinterpret_cast(data), maxSize); +} +} + +QGifImagePrivate::QGifImagePrivate(QGifImage *p) + : loopCount(0), defaultDelayTime(1000), q_ptr(p) +{ + +} + +QGifImagePrivate::~QGifImagePrivate() +{ + +} + +QVector QGifImagePrivate::colorTableFromColorMapObject(ColorMapObject *colorMap, int transColorIndex) const +{ + QVector colorTable; + if (colorMap) { + for (int idx=0; idxColorCount; ++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 colorTable) const +{ + if (colorTable.isEmpty()) + return 0; + + ColorMapObject *cmap = (ColorMapObject *)malloc(sizeof(ColorMapObject)); + // num of colors must be a power of 2 + int numColors = 1 << GifBitSize(colorTable.size()); + cmap->ColorCount = numColors; + //Maybe a bug of giflib, BitsPerPixel is used as size of the color table size. + cmap->BitsPerPixel = GifBitSize(colorTable.size()); //Todo! + cmap->SortFlag = false; + + GifColorType* 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 QSize(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 = frameInfo.image.colorTable().indexOf(transColor.rgb()); + else if (!globalColorTable.isEmpty()) + index = 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) { + qWarning(GifErrorString(error)); + 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; idxImageCount; ++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 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 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) { + qWarning(GifErrorString(error)); + 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 = globalColorTable.indexOf(bgColor.rgba()); + gifFile->SBackGroundColor = idx == -1 ? 0 : idx; + } + + gifFile->ImageCount = 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 = 0; + + GifByteType *data = (GifByteType *)malloc(image.width() * image.height() * sizeof(GifByteType)); + for (int row=0; rowRasterBits = data; + + if (idx == 0) { + uchar data8[12] = "NETSCAPE2.0"; + GifAddExtensionBlock(&gifImage->ExtensionBlockCount, &gifImage->ExtensionBlocks + , APPLICATION_EXT_FUNC_CODE, 11, data8); + uchar data[3]; + data[0] = 0x01; + data[1] = loopCount & 0xFF; + data[2] = (loopCount >> 8) & 0xFF; + GifAddExtensionBlock(&gifImage->ExtensionBlockCount, &gifImage->ExtensionBlocks + , CONTINUE_EXT_FUNC_CODE, 3, data); + } + + 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); + //EGifCloseFile(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 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 &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 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 QImage(); + + 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 QPoint(); + + 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 QColor(); + + 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)) + return d->save(&file); + + 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; +} diff --git a/src/vendor/QtGifImage/gifimage/qgifimage.h b/src/vendor/QtGifImage/gifimage/qgifimage.h new file mode 100644 index 00000000..f81a28ae --- /dev/null +++ b/src/vendor/QtGifImage/gifimage/qgifimage.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** Copyright (c) 2013 Debao Zhang +** 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 QGIFIMAGE_H +#define QGIFIMAGE_H + +#include "qgifglobal.h" +#include +#include +#include +#include + +class QGifImagePrivate; +class Q_GIFIMAGE_EXPORT QGifImage +{ + Q_DECLARE_PRIVATE(QGifImage) +public: + QGifImage(); + QGifImage(const QString &fileName); + QGifImage(const QSize &size); + ~QGifImage(); + + QVector globalColorTable() const; + QColor backgroundColor() const; + void setGlobalColorTable(const QVector &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 // QGIFIMAGE_H diff --git a/src/vendor/QtGifImage/gifimage/qgifimage_p.h b/src/vendor/QtGifImage/gifimage/qgifimage_p.h new file mode 100644 index 00000000..bed22320 --- /dev/null +++ b/src/vendor/QtGifImage/gifimage/qgifimage_p.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** Copyright (c) 2013 Debao Zhang +** 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 QGIFIMAGE_P_H +#define QGIFIMAGE_P_H + +#include "qgifimage.h" +#include "gif_lib.h" + +#include +#include + +class QGifFrameInfoData +{ +public: + QGifFrameInfoData() + :delayTime(-1), interlace(false) + { + + } + 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: + QGifImagePrivate(QGifImage *p); + ~QGifImagePrivate(); + bool load(QIODevice *device); + bool save(QIODevice *device) const; + QVector colorTableFromColorMapObject(ColorMapObject *object, int transColorIndex=-1) const; + ColorMapObject * colorTableToColorMapObject(QVector colorTable) const; + QSize getCanvasSize() const; + int getFrameTransparentColorIndex(const QGifFrameInfoData &info) const; + + QSize canvasSize; + int loopCount; + int defaultDelayTime; + QColor defaultTransparentColor; + + QVector globalColorTable; + QColor bgColor; + QList frameInfos; + + QGifImage *q_ptr; +}; + +#endif // QGIFIMAGE_P_H diff --git a/src/vendor/QtGifImage/gifimage/qtgifimage.pri b/src/vendor/QtGifImage/gifimage/qtgifimage.pri new file mode 100644 index 00000000..261e5e29 --- /dev/null +++ b/src/vendor/QtGifImage/gifimage/qtgifimage.pri @@ -0,0 +1,15 @@ +INCLUDEPATH += $$PWD +DEPENDPATH += $$PWD + +QT += core gui +!build_gifimage_lib:DEFINES += GIFIMAGE_NO_LIB + +include($$PWD/../3rdParty/giflib.pri) + +HEADERS += \ + $$PWD/qgifglobal.h \ + $$PWD/qgifimage.h \ + $$PWD/qgifimage_p.h + +SOURCES += \ + $$PWD/qgifimage.cpp diff --git a/src/vendor/QtGifImage/src.pro b/src/vendor/QtGifImage/src.pro new file mode 100644 index 00000000..479dcabd --- /dev/null +++ b/src/vendor/QtGifImage/src.pro @@ -0,0 +1,3 @@ +TEMPLATE = subdirs + +SUBDIRS = gifimage