1
/***************************************************************************
2
* Copyright (C) 2009 by Valerio Pilo *
5
* Copyright (C) 2009 by Adam Goossens *
7
***************************************************************************/
9
/***************************************************************************
10
* This program is free software; you can redistribute it and/or modify *
11
* it under the terms of the GNU Lesser General Public License as *
12
* published by the Free Software Foundation; either version 2.1 of the *
13
* License, or (at your option) any later version. *
15
* This program is distributed in the hope that it will be useful, *
16
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
17
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
18
* GNU General Public License for more details. *
20
* You should have received a copy of the GNU Lesser General Public *
21
* License along with this program; if not, write to the *
22
* Free Software Foundation, Inc., *
23
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
24
***************************************************************************/
26
#include "isfqt-internal.h"
28
#include "data/datasource.h"
29
#include "data/multibytecoding.h"
30
#include "tagsparser.h"
31
#include "tagswriter.h"
33
#include <IsfQtDrawing>
38
#if ISFQT_GIF_ENABLED == 1
39
#include "gif-support.h"
44
using namespace Compress;
47
/// Supported ISF version number
48
#define SUPPORTED_ISF_VERSION 0
53
* Convert a raw ISF data stream into a drawing.
55
* If the ISF data is invalid, a null Drawing is returned.
57
* @param rawData Source byte array with an ISF stream
58
* @param decodeFromBase64 Whether the bytes are in the Base64 format and
59
* need to be decoded first
60
* @return an Isf::Drawing, with null contents on error
62
Drawing &Stream::reader( const QByteArray &rawData, bool decodeFromBase64 )
64
// Create a new drawing on the heap to ensure it will keep
65
// living after this method returns
66
Drawing *drawing = new Drawing;
67
DataSource isfData( decodeFromBase64
68
? QByteArray::fromBase64( rawData )
70
int size = isfData.size();
77
ParserState state = ISF_PARSER_START;
79
while( state != ISF_PARSER_FINISH )
83
case ISF_PARSER_START:
85
// step 1: read ISF version.
86
quint8 version = decodeUInt( isfData );
87
#ifdef ISFQT_DEBUG_VERBOSE
88
qDebug() << "Version:" << version;
90
if ( version != SUPPORTED_ISF_VERSION )
92
drawing->error_ = ISF_ERROR_BAD_VERSION;
93
drawing->isNull_ = true;
94
state = ISF_PARSER_FINISH;
98
// version is OK. find ISF stream size next.
99
state = ISF_PARSER_STREAMSIZE;
105
case ISF_PARSER_STREAMSIZE:
107
// read ISF stream size.
108
// check it matches the length of the data array.
109
quint64 streamSize = decodeUInt( isfData );
111
if ( streamSize != (quint64)( isfData.size() - isfData.pos() ) )
114
qDebug() << "Invalid stream size" << streamSize
115
<< ", expected" << ( isfData.size() - isfData.pos() );
117
// streamsize is bad.
118
drawing->error_ = ISF_ERROR_BAD_STREAMSIZE;
119
state = ISF_PARSER_FINISH;
124
qDebug() << "Reading ISF stream of size:" << streamSize << "...";
126
// Validate the drawing
127
drawing->isNull_ = false;
129
// Fill up the default properties
130
drawing->currentMetrics_ = &drawing->defaultMetrics_;
131
drawing->currentAttributeSet_ = &drawing->defaultAttributeSet_;
132
drawing->currentStrokeInfo_ = &drawing->defaultStrokeInfo_;
133
drawing->currentTransform_ = &drawing->defaultTransform_;
135
// start looking for ISF tags.
136
state = ISF_PARSER_TAG;
142
// ******************
143
// This is the key point of the state machine. This will continually loop looking for ISF
144
// tags and farming off to the appropriate method.
145
// *******************
148
if( isfData.atEnd() )
150
state = ISF_PARSER_FINISH;
154
QString place( "0x" + QString::number( isfData.pos(), 16 ).toUpper() );
155
quint64 tagIndex = decodeUInt( isfData );
159
case TAG_INK_SPACE_RECT:
160
#ifdef ISFQT_DEBUG_VERBOSE
161
qDebug() << "Got tag (@" << place << "): TAG_INK_SPACE_RECT";
163
drawing->error_ = TagsParser::parseInkSpaceRectangle( isfData, *drawing );
167
#ifdef ISFQT_DEBUG_VERBOSE
168
qDebug() << "Got tag (@" << place << "): TAG_GUID_TABLE";
170
drawing->error_ = TagsParser::parseGuidTable( isfData, *drawing );
173
case TAG_DRAW_ATTRS_TABLE:
174
#ifdef ISFQT_DEBUG_VERBOSE
175
qDebug() << "Got tag (@" << place << "): TAG_DRAW_ATTRS_TABLE";
177
drawing->error_ = TagsParser::parseAttributeTable( isfData, *drawing );
180
case TAG_DRAW_ATTRS_BLOCK:
181
#ifdef ISFQT_DEBUG_VERBOSE
182
qDebug() << "Got tag (@" << place << "): TAG_DRAW_ATTRS_BLOCK";
184
drawing->error_ = TagsParser::parseAttributeBlock( isfData, *drawing );
187
case TAG_STROKE_DESC_TABLE:
188
#ifdef ISFQT_DEBUG_VERBOSE
189
qDebug() << "Got tag (@" << place << "): TAG_STROKE_DESC_TABLE";
191
drawing->error_ = TagsParser::parseStrokeDescTable( isfData, *drawing );
194
case TAG_STROKE_DESC_BLOCK:
195
#ifdef ISFQT_DEBUG_VERBOSE
196
qDebug() << "Got tag (@" << place << "): TAG_STROKE_DESC_BLOCK";
198
drawing->error_ = TagsParser::parseStrokeDescBlock( isfData, *drawing );
202
#ifdef ISFQT_DEBUG_VERBOSE
203
qDebug() << "Got tag (@" << place << "): TAG_BUTTONS";
205
drawing->error_ = TagsParser::parseUnsupported( isfData, "TAG_BUTTONS" );
209
#ifdef ISFQT_DEBUG_VERBOSE
210
qDebug() << "Got tag (@" << place << "): TAG_NO_X";
212
drawing->error_ = ISF_ERROR_NONE;
214
drawing->hasXData_ = false;
218
#ifdef ISFQT_DEBUG_VERBOSE
219
qDebug() << "Got tag (@" << place << "): TAG_NO_Y";
221
drawing->hasYData_ = false;
226
#ifdef ISFQT_DEBUG_VERBOSE
227
qDebug() << "Got tag (@" << place << "): TAG_DIDX";
230
quint64 value = decodeUInt( isfData );
232
if( value < (uint)drawing->attributeSets_.count() )
234
drawing->currentAttributeSet_ = drawing->attributeSets_[ value ];
235
#ifdef ISFQT_DEBUG_VERBOSE
236
qDebug() << "- Next strokes will use drawing attributes #" << value;
242
qWarning() << "Invalid drawing attribute ID!";
249
#ifdef ISFQT_DEBUG_VERBOSE
250
qDebug() << "Got tag (@" << place << "): TAG_STROKE";
252
drawing->error_ = TagsParser::parseStroke( isfData, *drawing );
255
case TAG_STROKE_PROPERTY_LIST:
256
#ifdef ISFQT_DEBUG_VERBOSE
257
qDebug() << "Got tag (@" << place << "): TAG_STROKE_PROPERTY_LIST";
259
drawing->error_ = TagsParser::parseUnsupported( isfData, "TAG_STROKE_PROPERTY_LIST" );
262
case TAG_POINT_PROPERTY:
263
#ifdef ISFQT_DEBUG_VERBOSE
264
qDebug() << "Got tag (@" << place << "): TAG_POINT_PROPERTY";
266
drawing->error_ = TagsParser::parseUnsupported( isfData, "TAG_POINT_PROPERTY" );
271
#ifdef ISFQT_DEBUG_VERBOSE
272
qDebug() << "Got tag (@" << place << "): TAG_SIDX";
275
quint64 value = decodeUInt( isfData );
277
if( value < (uint)drawing->strokeInfo_.count() )
279
drawing->currentStrokeInfo_ = drawing->strokeInfo_[ value ];
280
#ifdef ISFQT_DEBUG_VERBOSE
281
qDebug() << "- Next strokes will use stroke info #" << value;
287
qWarning() << "Invalid stroke ID!";
293
case TAG_COMPRESSION_HEADER:
294
#ifdef ISFQT_DEBUG_VERBOSE
295
qDebug() << "Got tag (@" << place << "): TAG_COMPRESSION_HEADER";
297
drawing->error_ = TagsParser::parseUnsupported( isfData, "TAG_COMPRESSION_HEADER" );
300
case TAG_TRANSFORM_TABLE:
301
#ifdef ISFQT_DEBUG_VERBOSE
302
qDebug() << "Got tag (@" << place << "): TAG_TRANSFORM_TABLE";
304
drawing->error_ = TagsParser::parseTransformationTable( isfData, *drawing );
308
#ifdef ISFQT_DEBUG_VERBOSE
309
qDebug() << "Got tag (@" << place << "): TAG_TRANSFORM";
311
drawing->error_ = TagsParser::parseTransformation( isfData, *drawing, tagIndex );
314
case TAG_TRANSFORM_ISOTROPIC_SCALE:
315
#ifdef ISFQT_DEBUG_VERBOSE
316
qDebug() << "Got tag (@" << place << "): TAG_TRANSFORM_ISOTROPIC_SCALE";
318
drawing->error_ = TagsParser::parseTransformation( isfData, *drawing, tagIndex );
321
case TAG_TRANSFORM_ANISOTROPIC_SCALE:
322
#ifdef ISFQT_DEBUG_VERBOSE
323
qDebug() << "Got tag (@" << place << "): TAG_TRANSFORM_ANISOTROPIC_SCALE";
325
drawing->error_ = TagsParser::parseTransformation( isfData, *drawing, tagIndex );
328
case TAG_TRANSFORM_ROTATE:
329
#ifdef ISFQT_DEBUG_VERBOSE
330
qDebug() << "Got tag (@" << place << "): TAG_TRANSFORM_ROTATE";
332
drawing->error_ = TagsParser::parseTransformation( isfData, *drawing, tagIndex );
335
case TAG_TRANSFORM_TRANSLATE:
336
#ifdef ISFQT_DEBUG_VERBOSE
337
qDebug() << "Got tag (@" << place << "): TAG_TRANSFORM_TRANSLATE";
339
drawing->error_ = TagsParser::parseTransformation( isfData, *drawing, tagIndex );
342
case TAG_TRANSFORM_SCALE_AND_TRANSLATE:
343
#ifdef ISFQT_DEBUG_VERBOSE
344
qDebug() << "Got tag (@" << place << "): TAG_TRANSFORM_SCALE_AND_TRANSLATE";
346
drawing->error_ = TagsParser::parseTransformation( isfData, *drawing, tagIndex );
349
case TAG_TRANSFORM_QUAD:
350
#ifdef ISFQT_DEBUG_VERBOSE
351
qDebug() << "Got tag (@" << place << "): TAG_TRANSFORM_QUAD";
353
drawing->error_ = TagsParser::parseTransformation( isfData, *drawing, tagIndex );
358
#ifdef ISFQT_DEBUG_VERBOSE
359
qDebug() << "Got tag (@" << place << "): TAG_TIDX";
362
quint64 value = decodeUInt( isfData );
364
if( value < (uint)drawing->transforms_.count() )
366
drawing->currentTransform_ = drawing->transforms_[ value ];
367
#ifdef ISFQT_DEBUG_VERBOSE
368
qDebug() << "- Next strokes will use transform #" << value;
374
qWarning() << "Invalid transform ID!";
381
case TAG_METRIC_TABLE:
382
#ifdef ISFQT_DEBUG_VERBOSE
383
qDebug() << "Got tag (@" << place << "): TAG_METRIC_TABLE";
385
drawing->error_ = TagsParser::parseMetricTable( isfData, *drawing );
388
case TAG_METRIC_BLOCK:
389
#ifdef ISFQT_DEBUG_VERBOSE
390
qDebug() << "Got tag (@" << place << "): TAG_METRIC_BLOCK";
392
drawing->error_ = TagsParser::parseMetricBlock( isfData, *drawing );
397
#ifdef ISFQT_DEBUG_VERBOSE
398
qDebug() << "Got tag (@" << place << "): TAG_MIDX";
401
quint64 value = decodeUInt( isfData );
403
if( value < (uint)drawing->metrics_.count() )
405
drawing->currentMetrics_ = drawing->metrics_[ value ];
406
#ifdef ISFQT_DEBUG_VERBOSE
407
qDebug() << "- Next strokes will use metrics #" << value;
413
qWarning() << "Invalid metrics ID!";
421
#ifdef ISFQT_DEBUG_VERBOSE
422
qDebug() << "Got tag (@" << place << "): TAG_MANTISSA";
424
drawing->error_ = TagsParser::parseUnsupported( isfData, "TAG_MANTISSA" );
427
case TAG_PERSISTENT_FORMAT:
428
#ifdef ISFQT_DEBUG_VERBOSE
429
qDebug() << "Got tag (@" << place << "): TAG_PERSISTENT_FORMAT";
431
drawing->error_ = TagsParser::parsePersistentFormat( isfData, *drawing );
434
case TAG_HIMETRIC_SIZE:
435
#ifdef ISFQT_DEBUG_VERBOSE
436
qDebug() << "Got tag (@" << place << "): TAG_HIMETRIC_SIZE";
438
drawing->error_ = TagsParser::parseHiMetricSize( isfData, *drawing );
442
#ifdef ISFQT_DEBUG_VERBOSE
443
qDebug() << "Got tag (@" << place << "): TAG_STROKE_IDS";
446
drawing->error_ = TagsParser::parseUnsupported( isfData, "TAG_STROKE_IDS" );
451
// If the tagIndex is known from the GUID table, show it differently
452
if( drawing->maxGuid_ > 0
453
&& tagIndex >= DEFAULT_TAGS_NUMBER && tagIndex <= drawing->maxGuid_ )
455
#ifdef ISFQT_DEBUG_VERBOSE
456
qDebug() << "Got tag (@" << place << "): TAG_CUSTOM:" << tagIndex;
458
TagsParser::parseCustomTag( isfData, *drawing, tagIndex );
462
TagsParser::parseUnsupported( isfData, "Unknown " + QString::number( tagIndex ) );
466
} // End of tagIndex switch
468
if( drawing->error_ != ISF_ERROR_NONE )
470
#ifdef ISFQT_DEBUG_VERBOSE
471
qWarning() << "Error in last operation, stopping.";
473
state = ISF_PARSER_FINISH;
479
// Should never arrive here! It's here only to avoid compiler warnings.
480
case ISF_PARSER_FINISH:
488
qDebug() << "Finished with" << ( drawing->error_ == ISF_ERROR_NONE ? "success" : "error" );
492
if( drawing->error_ != ISF_ERROR_NONE )
497
// Perform the last operations on the drawing
499
// Adjust the bounding rectangle to include the strokes borders
500
QSize penSize( drawing->maxPenSize_.toSize() );
501
drawing->boundingRect_.adjust( -penSize.width() - 1, -penSize.height() - 1,
502
+penSize.width() + 1, +penSize.height() + 1 );
505
#ifdef ISFQT_DEBUG_VERBOSE
506
qDebug() << "Drawing bounding rectangle:" << drawing->boundingRect_;
507
qDebug() << "Maximum thickness:" << drawing->maxPenSize_;
516
* Convert a Fortified-GIF image into a drawing.
518
* If the GIF image or the ISF data within it are invalid, or if the GIF did not
519
* have any ISF stream within, then a null Drawing is returned.
521
* Please note that this method does nothing if Isf-Qt is compiled
522
* withous GIF support. Use Stream::supportsGif() to verify whether
523
* GIF was compiled in or not.
526
* @param gifRawBytes Source byte array with a Fortified GIF image
527
* @param decodeFromBase64 True if the bytes are in the Base64 format and
528
* need to be decoded first
529
* @return an Isf::Drawing, with null contents on error
531
Drawing &Stream::readerGif( const QByteArray &gifRawBytes, bool decodeFromBase64 )
535
#if ISFQT_GIF_ENABLED == 1
537
QByteArray gifBytes( decodeFromBase64
538
? QByteArray::fromBase64( gifRawBytes )
542
* With the commented code below, it would all have been so easy, but no!
543
* DGifGetComment is NOT PRESENT IN THE LIBRARY despite being in the giflib
544
* header file. And EGifPutComment is present!
545
* It doesn't work, but hey, at least it's there. (see below)
548
QBuffer gifData( &gifBytes );
549
gifData.open( QIODevice::ReadOnly );
552
GifFileType *gifImage = DGifOpen( (void*)&gifData, GifReadFromByteArray );
555
DGifGetComment( gifImage, data?? );
559
qWarning() << "Couldn't initialize GIF library!";
562
DGifCloseFile( gifImage );
566
// Find the last 'comment' type tag: it should be the last thing in the file...
569
qint32 maxDataPosition = gifBytes.size() - 2; // comment and gif stream ending bytes
571
#ifdef ISFQT_DEBUG_VERBOSE
572
qDebug() << "Searching for a stream. Last valid position:" << maxDataPosition;
575
while( size == 0 && ( position = gifBytes.lastIndexOf( COMMENT_EXT_FUNC_CODE, position - 1 ) ) >= 0 )
577
// Skip the comment tag, to have the size byte as current char
578
qint32 lastPosition = position + 1;
580
// The next character after the tag can't be an ISF stream start, skip
581
if( gifBytes[ position + 2 ] != '\0' )
586
// Try to read the stream
590
sizeByte = gifBytes[ lastPosition ];
592
// Skip the size byte
595
isfData.append( gifBytes.mid( lastPosition, sizeByte ) );
596
lastPosition += sizeByte;
598
while( sizeByte == MAX_GIF_BYTE && ( lastPosition <= maxDataPosition ) );
600
// We found the ISF stream!
601
if( lastPosition == maxDataPosition )
603
#ifdef ISFQT_DEBUG_VERBOSE
604
qDebug() << "Found an ISF stream of size" << isfData.size();
610
#ifdef ISFQT_DEBUG_VERBOSE
611
qDebug() << "Stream not found at position:" << position
612
<< "size:" << ( lastPosition - position );
618
#endif // ISFQT_GIF_ENABLED == 1
620
return reader( isfData );
626
* Return whether the library was built with Fortified GIF support or not.
630
bool Stream::supportsGif()
632
return ( ISFQT_GIF_ENABLED == true );
638
* Convert a drawing into a raw ISF data stream.
640
* The resulting byte array will be empty if the drawing is not valid.
642
* @param drawing Source drawing
643
* @param encodeToBase64 Whether the converted ISF stream should be
644
* encoded with Base64 or not
645
* @return Byte array with an ISF data stream
647
QByteArray Stream::writer( const Drawing &drawing, bool encodeToBase64 )
649
if( &drawing == 0 || drawing.isNull() || drawing.error() != ISF_ERROR_NONE )
652
qDebug() << "The drawing was not valid!";
659
// Write the persistent format tag
660
TagsWriter::addPersistentFormat( isfData, drawing );
662
// Write the drawing size
663
TagsWriter::addHiMetricSize( isfData, drawing );
665
// Write the attributes
666
TagsWriter::addAttributeTable( isfData, drawing );
669
TagsWriter::addMetricsTable( isfData, drawing );
671
// Write the transforms
672
TagsWriter::addTransformationTable( isfData, drawing );
675
TagsWriter::addStrokes( isfData, drawing );
677
// Write the stream size (at the start of the stream)
678
encodeUInt( isfData, isfData.size(), true/*prepend*/ );
680
// Write the version number (at the start of the stream)
681
encodeUInt( isfData, SUPPORTED_ISF_VERSION, true/*prepend*/ );
684
// Convert to Base64 if needed
687
return isfData.data().toBase64();
691
return isfData.data();
698
* Convert a drawing into a Fortified-GIF image.
700
* The resulting byte array will be empty if the drawing is not valid.
702
* Please note that this method does nothing if Isf-Qt is compiled
703
* withous GIF support. Use Stream::supportsGif() to verify whether
704
* GIF was compiled in or not.
707
* @param drawing Source drawing
708
* @param encodeToBase64 Whether the converted ISF stream should be
709
* encoded with Base64 or not
710
* @return Byte array with an ISF data stream
712
QByteArray Stream::writerGif( const Drawing &drawing, bool encodeToBase64 )
714
QByteArray imageBytes;
716
#if ISFQT_GIF_ENABLED == 1
717
Drawing source( drawing );
719
// Get the ISF data stream
720
QByteArray isfData( writer( source ) );
722
#ifdef ISFQT_DEBUG_VERBOSE
723
qDebug() << "GIF-Fortifying an ISF stream of size" << isfData.size();
726
// Get the ISF pixmap and copy the pixels to an 8bpp image
727
QImage isfImage( source.pixmap().toImage()
728
.convertToFormat( QImage::Format_Indexed8,
729
Qt::ThresholdDither ) );
731
// Initialise the gif variables
733
GifFileType *gifImage = NULL;
734
ColorMapObject *cmap = NULL;
735
int height = isfImage.height();
736
int width = isfImage.width();
738
bool gifError = true;
740
// Convert the image to GIF using libgif
743
gifData.open( QIODevice::WriteOnly );
744
gifImage = EGifOpen( (void*)&gifData, GifWriteToByteArray );
747
qWarning() << "Couldn't initialize gif library!";
751
// Create the color map
752
numColors = ( isfImage.numColors() << 2 );
753
if( numColors > 256 )
758
cmap = MakeMapObject( numColors, NULL );
759
if( cmap == 0 && isfImage.numColors() > 1 )
761
qWarning() << "Couldn't create map object for gif conversion (colors:" << isfImage.numColors() << ")!";
765
// Fill in the color map with the colors in the image color table
766
for( int i = 0; i < isfImage.numColors(); ++i )
768
const QRgb &color( isfImage.color( i ) );
769
cmap->Colors[i].Red = qRed ( color );
770
cmap->Colors[i].Green = qGreen( color );
771
cmap->Colors[i].Blue = qBlue ( color );
774
// Save the file properties
775
if( EGifPutScreenDesc( gifImage, width, height, 8, 0, cmap ) == GIF_ERROR )
777
qWarning() << "EGifPutScreenDesc() failed!";
781
// Save the image format
782
if( EGifPutImageDesc( gifImage, 0, 0, width, height, 0, NULL ) == GIF_ERROR )
784
qWarning() << "EGifPutImageDesc() failed!";
790
* FIXME: If to write the scanlines you use
791
* EGifPutLine( gifImage, isfImage.bits(), isfImage.width() * isfImage.height() )
792
* i.e. convert the complete image in one call, then the resulting image is mangled.
793
* Something is wrong with the width or so, it seems to be off by about two pixels.
795
// Write every scanline
796
for( int line = 0; line < height; ++line )
798
if( EGifPutLine( gifImage, isfImage.scanLine( line ), width ) == GIF_ERROR )
800
qWarning() << "EGifPutLine failed at scanline" << line
801
<< "(height:" << isfImage.height()
802
<< ", width:" << isfImage.width()
803
<< ", bytesperline:" << isfImage.bytesPerLine() << ")";
810
* Completing the funny theater that is giflib, EGifPutComment() doesn't
811
* work, or I've overlooked something Extremely Obvious(tm).
812
* Googling didn't help: I rewrote it (from the giflib source) with
816
if( EGifPutComment( gifImage, isfData.constData() ) == GIF_ERROR )
818
qWarning() << "EGifPutComment has failed!";
823
// Write the ISF stream into the Comment Extension field
824
if( isfData.size() < MAX_GIF_BYTE )
826
EGifPutExtension( gifImage, COMMENT_EXT_FUNC_CODE, isfData.size(), isfData.constData() );
830
// Write the extension
831
if( EGifPutExtensionFirst( gifImage, COMMENT_EXT_FUNC_CODE, MAX_GIF_BYTE, isfData.left( MAX_GIF_BYTE ).data() ) == GIF_ERROR )
833
qWarning() << "EGifPutExtensionFirst failed!";
837
// The first MAX_GIF_BYTE bytes have been written already
838
quint32 pos = MAX_GIF_BYTE;
840
quint32 length = ( isfData.size() - pos );
842
// Write all the full data blocks
843
while( length >= MAX_GIF_BYTE )
845
if( EGifPutExtensionNext( gifImage, 0, MAX_GIF_BYTE, isfData.mid( pos, MAX_GIF_BYTE ).data() ) == GIF_ERROR )
847
qWarning() << "EGifPutExtensionNext failed!";
852
length -= MAX_GIF_BYTE;
855
// Write the last block
858
if( EGifPutExtensionLast( gifImage, 0, length, isfData.mid( pos, MAX_GIF_BYTE ).data() ) == GIF_ERROR )
860
qWarning() << "EGifPutExtensionLast (n) failed!";
866
if( EGifPutExtensionLast( gifImage, 0, 0, 0 ) == GIF_ERROR )
868
qWarning() << "EGifPutExtensionLast (0) failed!";
877
// Clean up the GIF converter etc
878
EGifCloseFile( gifImage );
879
FreeMapObject( cmap );
884
qWarning() << "GIF error code:" << GifLastError();
888
// Return the GIF data
889
imageBytes = gifData.data();
891
#ifdef ISFQT_DEBUG_VERBOSE
892
qDebug() << "Converted a" << isfData.size() << "bytes Ink to GIF:" << isfImage.height() << "x" << isfImage.width() << "->" << imageBytes.size() << "bytes";
896
#endif // ISFQT_GIF_ENABLED == 1
899
// Convert to Base64 if needed
902
return imageBytes.toBase64();