~ubuntu-branches/ubuntu/lucid/kmess/lucid

« back to all changes in this revision

Viewing changes to contrib/isf-qt/src/isfqt.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Mark Purcell
  • Date: 2009-12-05 21:19:26 UTC
  • mfrom: (1.1.7 upstream) (0.1.8 sid)
  • Revision ID: james.westby@ubuntu.com-20091205211926-r26u8j38kysf6o2p
Tags: 2.0.2-1
* New upstream release 
  - Fixes friendly names (LP: #485640)
* Update Homepage: http://kmess.org
* Add Build-Depends: libphonon-dev | libqt4-phonon-dev (ubuntu friendly)
* kmess.1 fix lintian:hyphen-used-as-minus-sign

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/***************************************************************************
 
2
 *   Copyright (C) 2009 by Valerio Pilo                                    *
 
3
 *   valerio@kmess.org                                                     *
 
4
 *                                                                         *
 
5
 *   Copyright (C) 2009 by Adam Goossens                                   *
 
6
 *   adam@kmess.org                                                        *
 
7
 ***************************************************************************/
 
8
 
 
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.                       *
 
14
 *                                                                         *
 
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.                          *
 
19
 *                                                                         *
 
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
 ***************************************************************************/
 
25
 
 
26
#include "isfqt-internal.h"
 
27
 
 
28
#include "data/datasource.h"
 
29
#include "data/multibytecoding.h"
 
30
#include "tagsparser.h"
 
31
#include "tagswriter.h"
 
32
 
 
33
#include <IsfQtDrawing>
 
34
 
 
35
#include <QPainter>
 
36
#include <QPixmap>
 
37
 
 
38
#if ISFQT_GIF_ENABLED == 1
 
39
  #include "gif-support.h"
 
40
#endif
 
41
 
 
42
 
 
43
using namespace Isf;
 
44
using namespace Compress;
 
45
 
 
46
 
 
47
/// Supported ISF version number
 
48
#define SUPPORTED_ISF_VERSION       0
 
49
 
 
50
 
 
51
 
 
52
/**
 
53
 * Convert a raw ISF data stream into a drawing.
 
54
 *
 
55
 * If the ISF data is invalid, a null Drawing is returned.
 
56
 *
 
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
 
61
 */
 
62
Drawing &Stream::reader( const QByteArray &rawData, bool decodeFromBase64 )
 
63
{
 
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 )
 
69
                        : rawData );
 
70
  int size = isfData.size();
 
71
 
 
72
  if( size == 0 )
 
73
  {
 
74
    return *drawing;
 
75
  }
 
76
 
 
77
  ParserState state = ISF_PARSER_START;
 
78
 
 
79
  while( state != ISF_PARSER_FINISH )
 
80
  {
 
81
    switch( state )
 
82
    {
 
83
      case ISF_PARSER_START:
 
84
      {
 
85
        // step 1: read ISF version.
 
86
        quint8 version = decodeUInt( isfData );
 
87
#ifdef ISFQT_DEBUG_VERBOSE
 
88
        qDebug() << "Version:" << version;
 
89
#endif
 
90
        if ( version != SUPPORTED_ISF_VERSION )
 
91
        {
 
92
          drawing->error_ = ISF_ERROR_BAD_VERSION;
 
93
          drawing->isNull_ = true;
 
94
          state = ISF_PARSER_FINISH;
 
95
        }
 
96
        else
 
97
        {
 
98
          // version is OK. find ISF stream size next.
 
99
          state = ISF_PARSER_STREAMSIZE;
 
100
        }
 
101
 
 
102
        break;
 
103
      }
 
104
 
 
105
      case ISF_PARSER_STREAMSIZE:
 
106
      {
 
107
        // read ISF stream size.
 
108
        // check it matches the length of the data array.
 
109
        quint64 streamSize = decodeUInt( isfData );
 
110
 
 
111
        if ( streamSize != (quint64)( isfData.size() - isfData.pos() ) )
 
112
        {
 
113
#ifdef ISFQT_DEBUG
 
114
          qDebug() << "Invalid stream size" << streamSize
 
115
                   << ", expected" << ( isfData.size() - isfData.pos() );
 
116
#endif
 
117
          // streamsize is bad.
 
118
          drawing->error_ = ISF_ERROR_BAD_STREAMSIZE;
 
119
          state = ISF_PARSER_FINISH;
 
120
        }
 
121
        else
 
122
        {
 
123
#ifdef ISFQT_DEBUG
 
124
          qDebug() << "Reading ISF stream of size:" << streamSize << "...";
 
125
#endif
 
126
          // Validate the drawing
 
127
          drawing->isNull_ = false;
 
128
 
 
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_;
 
134
 
 
135
          // start looking for ISF tags.
 
136
          state = ISF_PARSER_TAG;
 
137
        }
 
138
 
 
139
        break;
 
140
      }
 
141
 
 
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
      // *******************
 
146
      case ISF_PARSER_TAG:
 
147
      {
 
148
        if( isfData.atEnd() )
 
149
        {
 
150
          state = ISF_PARSER_FINISH;
 
151
          break;
 
152
        }
 
153
 
 
154
        QString place( "0x" + QString::number( isfData.pos(), 16 ).toUpper() );
 
155
        quint64 tagIndex = decodeUInt( isfData );
 
156
 
 
157
        switch( tagIndex )
 
158
        {
 
159
          case TAG_INK_SPACE_RECT:
 
160
#ifdef ISFQT_DEBUG_VERBOSE
 
161
            qDebug() << "Got tag (@" << place << "): TAG_INK_SPACE_RECT";
 
162
#endif
 
163
            drawing->error_ = TagsParser::parseInkSpaceRectangle( isfData, *drawing );
 
164
            break;
 
165
 
 
166
          case TAG_GUID_TABLE:
 
167
#ifdef ISFQT_DEBUG_VERBOSE
 
168
            qDebug() << "Got tag (@" << place << "): TAG_GUID_TABLE";
 
169
#endif
 
170
            drawing->error_ = TagsParser::parseGuidTable( isfData, *drawing );
 
171
            break;
 
172
 
 
173
          case TAG_DRAW_ATTRS_TABLE:
 
174
#ifdef ISFQT_DEBUG_VERBOSE
 
175
            qDebug() << "Got tag (@" << place << "): TAG_DRAW_ATTRS_TABLE";
 
176
#endif
 
177
            drawing->error_ = TagsParser::parseAttributeTable( isfData, *drawing );
 
178
            break;
 
179
 
 
180
          case TAG_DRAW_ATTRS_BLOCK:
 
181
#ifdef ISFQT_DEBUG_VERBOSE
 
182
            qDebug() << "Got tag (@" << place << "): TAG_DRAW_ATTRS_BLOCK";
 
183
#endif
 
184
            drawing->error_ = TagsParser::parseAttributeBlock( isfData, *drawing );
 
185
            break;
 
186
 
 
187
          case TAG_STROKE_DESC_TABLE:
 
188
#ifdef ISFQT_DEBUG_VERBOSE
 
189
            qDebug() << "Got tag (@" << place << "): TAG_STROKE_DESC_TABLE";
 
190
#endif
 
191
            drawing->error_ = TagsParser::parseStrokeDescTable( isfData, *drawing );
 
192
            break;
 
193
 
 
194
          case TAG_STROKE_DESC_BLOCK:
 
195
#ifdef ISFQT_DEBUG_VERBOSE
 
196
            qDebug() << "Got tag (@" << place << "): TAG_STROKE_DESC_BLOCK";
 
197
#endif
 
198
            drawing->error_ = TagsParser::parseStrokeDescBlock( isfData, *drawing );
 
199
            break;
 
200
 
 
201
          case TAG_BUTTONS:
 
202
#ifdef ISFQT_DEBUG_VERBOSE
 
203
            qDebug() << "Got tag (@" << place << "): TAG_BUTTONS";
 
204
#endif
 
205
            drawing->error_ = TagsParser::parseUnsupported( isfData, "TAG_BUTTONS" );
 
206
            break;
 
207
 
 
208
          case TAG_NO_X:
 
209
#ifdef ISFQT_DEBUG_VERBOSE
 
210
            qDebug() << "Got tag (@" << place << "): TAG_NO_X";
 
211
#endif
 
212
            drawing->error_ = ISF_ERROR_NONE;
 
213
 
 
214
            drawing->hasXData_ = false;
 
215
            break;
 
216
 
 
217
          case TAG_NO_Y:
 
218
#ifdef ISFQT_DEBUG_VERBOSE
 
219
            qDebug() << "Got tag (@" << place << "): TAG_NO_Y";
 
220
#endif
 
221
            drawing->hasYData_ = false;
 
222
            break;
 
223
 
 
224
          case TAG_DIDX:
 
225
          {
 
226
#ifdef ISFQT_DEBUG_VERBOSE
 
227
            qDebug() << "Got tag (@" << place << "): TAG_DIDX";
 
228
#endif
 
229
 
 
230
            quint64 value = decodeUInt( isfData );
 
231
 
 
232
            if( value < (uint)drawing->attributeSets_.count() )
 
233
            {
 
234
              drawing->currentAttributeSet_ = drawing->attributeSets_[ value ];
 
235
#ifdef ISFQT_DEBUG_VERBOSE
 
236
              qDebug() << "- Next strokes will use drawing attributes #" << value;
 
237
#endif
 
238
            }
 
239
            else
 
240
            {
 
241
#ifdef ISFQT_DEBUG
 
242
              qWarning() << "Invalid drawing attribute ID!";
 
243
#endif
 
244
            }
 
245
            break;
 
246
          }
 
247
 
 
248
          case TAG_STROKE:
 
249
#ifdef ISFQT_DEBUG_VERBOSE
 
250
            qDebug() << "Got tag (@" << place << "): TAG_STROKE";
 
251
#endif
 
252
            drawing->error_ = TagsParser::parseStroke( isfData, *drawing );
 
253
            break;
 
254
 
 
255
          case TAG_STROKE_PROPERTY_LIST:
 
256
#ifdef ISFQT_DEBUG_VERBOSE
 
257
            qDebug() << "Got tag (@" << place << "): TAG_STROKE_PROPERTY_LIST";
 
258
#endif
 
259
            drawing->error_ = TagsParser::parseUnsupported( isfData, "TAG_STROKE_PROPERTY_LIST" );
 
260
            break;
 
261
 
 
262
          case TAG_POINT_PROPERTY:
 
263
#ifdef ISFQT_DEBUG_VERBOSE
 
264
            qDebug() << "Got tag (@" << place << "): TAG_POINT_PROPERTY";
 
265
#endif
 
266
            drawing->error_ = TagsParser::parseUnsupported( isfData, "TAG_POINT_PROPERTY" );
 
267
            break;
 
268
 
 
269
          case TAG_SIDX:
 
270
          {
 
271
#ifdef ISFQT_DEBUG_VERBOSE
 
272
            qDebug() << "Got tag (@" << place << "): TAG_SIDX";
 
273
#endif
 
274
 
 
275
            quint64 value = decodeUInt( isfData );
 
276
 
 
277
            if( value < (uint)drawing->strokeInfo_.count() )
 
278
            {
 
279
              drawing->currentStrokeInfo_ = drawing->strokeInfo_[ value ];
 
280
#ifdef ISFQT_DEBUG_VERBOSE
 
281
              qDebug() << "- Next strokes will use stroke info #" << value;
 
282
#endif
 
283
            }
 
284
            else
 
285
            {
 
286
#ifdef ISFQT_DEBUG
 
287
              qWarning() << "Invalid stroke ID!";
 
288
#endif
 
289
            }
 
290
            break;
 
291
          }
 
292
 
 
293
          case TAG_COMPRESSION_HEADER:
 
294
#ifdef ISFQT_DEBUG_VERBOSE
 
295
            qDebug() << "Got tag (@" << place << "): TAG_COMPRESSION_HEADER";
 
296
#endif
 
297
            drawing->error_ = TagsParser::parseUnsupported( isfData, "TAG_COMPRESSION_HEADER" );
 
298
            break;
 
299
 
 
300
          case TAG_TRANSFORM_TABLE:
 
301
#ifdef ISFQT_DEBUG_VERBOSE
 
302
            qDebug() << "Got tag (@" << place << "): TAG_TRANSFORM_TABLE";
 
303
#endif
 
304
            drawing->error_ = TagsParser::parseTransformationTable( isfData, *drawing );
 
305
            break;
 
306
 
 
307
          case TAG_TRANSFORM:
 
308
#ifdef ISFQT_DEBUG_VERBOSE
 
309
            qDebug() << "Got tag (@" << place << "): TAG_TRANSFORM";
 
310
#endif
 
311
            drawing->error_ = TagsParser::parseTransformation( isfData, *drawing, tagIndex );
 
312
            break;
 
313
 
 
314
          case TAG_TRANSFORM_ISOTROPIC_SCALE:
 
315
#ifdef ISFQT_DEBUG_VERBOSE
 
316
            qDebug() << "Got tag (@" << place << "): TAG_TRANSFORM_ISOTROPIC_SCALE";
 
317
#endif
 
318
            drawing->error_ = TagsParser::parseTransformation( isfData, *drawing, tagIndex );
 
319
            break;
 
320
 
 
321
          case TAG_TRANSFORM_ANISOTROPIC_SCALE:
 
322
#ifdef ISFQT_DEBUG_VERBOSE
 
323
            qDebug() << "Got tag (@" << place << "): TAG_TRANSFORM_ANISOTROPIC_SCALE";
 
324
#endif
 
325
            drawing->error_ = TagsParser::parseTransformation( isfData, *drawing, tagIndex );
 
326
            break;
 
327
 
 
328
          case TAG_TRANSFORM_ROTATE:
 
329
#ifdef ISFQT_DEBUG_VERBOSE
 
330
            qDebug() << "Got tag (@" << place << "): TAG_TRANSFORM_ROTATE";
 
331
#endif
 
332
            drawing->error_ = TagsParser::parseTransformation( isfData, *drawing, tagIndex );
 
333
            break;
 
334
 
 
335
          case TAG_TRANSFORM_TRANSLATE:
 
336
#ifdef ISFQT_DEBUG_VERBOSE
 
337
            qDebug() << "Got tag (@" << place << "): TAG_TRANSFORM_TRANSLATE";
 
338
#endif
 
339
            drawing->error_ = TagsParser::parseTransformation( isfData, *drawing, tagIndex );
 
340
            break;
 
341
 
 
342
          case TAG_TRANSFORM_SCALE_AND_TRANSLATE:
 
343
#ifdef ISFQT_DEBUG_VERBOSE
 
344
            qDebug() << "Got tag (@" << place << "): TAG_TRANSFORM_SCALE_AND_TRANSLATE";
 
345
#endif
 
346
            drawing->error_ = TagsParser::parseTransformation( isfData, *drawing, tagIndex );
 
347
            break;
 
348
 
 
349
          case TAG_TRANSFORM_QUAD:
 
350
#ifdef ISFQT_DEBUG_VERBOSE
 
351
            qDebug() << "Got tag (@" << place << "): TAG_TRANSFORM_QUAD";
 
352
#endif
 
353
            drawing->error_ = TagsParser::parseTransformation( isfData, *drawing, tagIndex );
 
354
            break;
 
355
 
 
356
          case TAG_TIDX:
 
357
          {
 
358
#ifdef ISFQT_DEBUG_VERBOSE
 
359
            qDebug() << "Got tag (@" << place << "): TAG_TIDX";
 
360
#endif
 
361
 
 
362
            quint64 value = decodeUInt( isfData );
 
363
 
 
364
            if( value < (uint)drawing->transforms_.count() )
 
365
            {
 
366
              drawing->currentTransform_ = drawing->transforms_[ value ];
 
367
#ifdef ISFQT_DEBUG_VERBOSE
 
368
              qDebug() << "- Next strokes will use transform #" << value;
 
369
#endif
 
370
            }
 
371
            else
 
372
            {
 
373
#ifdef ISFQT_DEBUG
 
374
              qWarning() << "Invalid transform ID!";
 
375
#endif
 
376
            }
 
377
 
 
378
            break;
 
379
          }
 
380
 
 
381
          case TAG_METRIC_TABLE:
 
382
#ifdef ISFQT_DEBUG_VERBOSE
 
383
            qDebug() << "Got tag (@" << place << "): TAG_METRIC_TABLE";
 
384
#endif
 
385
            drawing->error_ = TagsParser::parseMetricTable( isfData, *drawing );
 
386
            break;
 
387
 
 
388
          case TAG_METRIC_BLOCK:
 
389
#ifdef ISFQT_DEBUG_VERBOSE
 
390
            qDebug() << "Got tag (@" << place << "): TAG_METRIC_BLOCK";
 
391
#endif
 
392
            drawing->error_ = TagsParser::parseMetricBlock( isfData, *drawing );
 
393
            break;
 
394
 
 
395
          case TAG_MIDX:
 
396
          {
 
397
#ifdef ISFQT_DEBUG_VERBOSE
 
398
            qDebug() << "Got tag (@" << place << "): TAG_MIDX";
 
399
#endif
 
400
 
 
401
            quint64 value = decodeUInt( isfData );
 
402
 
 
403
            if( value < (uint)drawing->metrics_.count() )
 
404
            {
 
405
              drawing->currentMetrics_ = drawing->metrics_[ value ];
 
406
#ifdef ISFQT_DEBUG_VERBOSE
 
407
              qDebug() << "- Next strokes will use metrics #" << value;
 
408
#endif
 
409
            }
 
410
            else
 
411
            {
 
412
#ifdef ISFQT_DEBUG
 
413
              qWarning() << "Invalid metrics ID!";
 
414
#endif
 
415
            }
 
416
 
 
417
            break;
 
418
          }
 
419
 
 
420
          case TAG_MANTISSA:
 
421
#ifdef ISFQT_DEBUG_VERBOSE
 
422
            qDebug() << "Got tag (@" << place << "): TAG_MANTISSA";
 
423
#endif
 
424
            drawing->error_ = TagsParser::parseUnsupported( isfData, "TAG_MANTISSA" );
 
425
            break;
 
426
 
 
427
          case TAG_PERSISTENT_FORMAT:
 
428
#ifdef ISFQT_DEBUG_VERBOSE
 
429
            qDebug() << "Got tag (@" << place << "): TAG_PERSISTENT_FORMAT";
 
430
#endif
 
431
            drawing->error_ = TagsParser::parsePersistentFormat( isfData, *drawing );
 
432
            break;
 
433
 
 
434
          case TAG_HIMETRIC_SIZE:
 
435
#ifdef ISFQT_DEBUG_VERBOSE
 
436
            qDebug() << "Got tag (@" << place << "): TAG_HIMETRIC_SIZE";
 
437
#endif
 
438
            drawing->error_ = TagsParser::parseHiMetricSize( isfData, *drawing );
 
439
            break;
 
440
 
 
441
          case TAG_STROKE_IDS:
 
442
#ifdef ISFQT_DEBUG_VERBOSE
 
443
            qDebug() << "Got tag (@" << place << "): TAG_STROKE_IDS";
 
444
#endif
 
445
 
 
446
            drawing->error_ = TagsParser::parseUnsupported( isfData, "TAG_STROKE_IDS" );
 
447
            break;
 
448
 
 
449
          default:
 
450
 
 
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_ )
 
454
            {
 
455
#ifdef ISFQT_DEBUG_VERBOSE
 
456
              qDebug() << "Got tag (@" << place << "): TAG_CUSTOM:" << tagIndex;
 
457
#endif
 
458
              TagsParser::parseCustomTag( isfData, *drawing, tagIndex );
 
459
            }
 
460
            else
 
461
            {
 
462
              TagsParser::parseUnsupported( isfData, "Unknown " + QString::number( tagIndex ) );
 
463
            }
 
464
            break;
 
465
 
 
466
        } // End of tagIndex switch
 
467
 
 
468
        if( drawing->error_ != ISF_ERROR_NONE )
 
469
        {
 
470
#ifdef ISFQT_DEBUG_VERBOSE
 
471
          qWarning() << "Error in last operation, stopping.";
 
472
#endif
 
473
          state = ISF_PARSER_FINISH;
 
474
        }
 
475
 
 
476
        break;
 
477
      }
 
478
 
 
479
      // Should never arrive here! It's here only to avoid compiler warnings.
 
480
      case ISF_PARSER_FINISH:
 
481
        break;
 
482
 
 
483
      break;
 
484
    }
 
485
  }
 
486
 
 
487
#ifdef ISFQT_DEBUG
 
488
  qDebug() << "Finished with" << ( drawing->error_ == ISF_ERROR_NONE ? "success" : "error" );
 
489
  qDebug();
 
490
#endif
 
491
 
 
492
  if( drawing->error_ != ISF_ERROR_NONE )
 
493
  {
 
494
    return *drawing;
 
495
  }
 
496
 
 
497
  // Perform the last operations on the drawing
 
498
 
 
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 );
 
503
 
 
504
 
 
505
#ifdef ISFQT_DEBUG_VERBOSE
 
506
  qDebug() << "Drawing bounding rectangle:" << drawing->boundingRect_;
 
507
  qDebug() << "Maximum thickness:" << drawing->maxPenSize_;
 
508
#endif
 
509
 
 
510
  return *drawing;
 
511
}
 
512
 
 
513
 
 
514
 
 
515
/**
 
516
 * Convert a Fortified-GIF image into a drawing.
 
517
 *
 
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.
 
520
 *
 
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.
 
524
 *
 
525
 * @see supportsGif()
 
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
 
530
 */
 
531
Drawing &Stream::readerGif( const QByteArray &gifRawBytes, bool decodeFromBase64 )
 
532
{
 
533
  QByteArray isfData;
 
534
 
 
535
#if ISFQT_GIF_ENABLED == 1
 
536
 
 
537
  QByteArray gifBytes( decodeFromBase64
 
538
                        ? QByteArray::fromBase64( gifRawBytes )
 
539
                        : gifRawBytes );
 
540
 
 
541
/**
 
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)
 
546
 */
 
547
/*
 
548
  QBuffer gifData( &gifBytes );
 
549
  gifData.open( QIODevice::ReadOnly );
 
550
 
 
551
  // Open the gif file
 
552
  GifFileType *gifImage = DGifOpen( (void*)&gifData, GifReadFromByteArray );
 
553
  if( gifImage != 0 )
 
554
  {
 
555
    DGifGetComment( gifImage, data?? );
 
556
  }
 
557
  else
 
558
  {
 
559
    qWarning() << "Couldn't initialize GIF library!";
 
560
  }
 
561
 
 
562
  DGifCloseFile( gifImage );
 
563
  gifData.close();
 
564
*/
 
565
 
 
566
  // Find the last 'comment' type tag: it should be the last thing in the file...
 
567
  qint32 size = 0;
 
568
  qint32 position = 0;
 
569
  qint32 maxDataPosition = gifBytes.size() - 2; // comment and gif stream ending bytes
 
570
 
 
571
#ifdef ISFQT_DEBUG_VERBOSE
 
572
  qDebug() << "Searching for a stream. Last valid position:" << maxDataPosition;
 
573
#endif
 
574
 
 
575
  while( size == 0 && ( position = gifBytes.lastIndexOf( COMMENT_EXT_FUNC_CODE, position - 1 ) ) >= 0 )
 
576
  {
 
577
    // Skip the comment tag, to have the size byte as current char
 
578
    qint32 lastPosition = position + 1;
 
579
 
 
580
    // The next character after the tag can't be an ISF stream start, skip
 
581
    if( gifBytes[ position + 2 ] != '\0' )
 
582
    {
 
583
      continue;
 
584
    }
 
585
 
 
586
    // Try to read the stream
 
587
    quint8 sizeByte;
 
588
    do
 
589
    {
 
590
      sizeByte = gifBytes[ lastPosition ];
 
591
 
 
592
      // Skip the size byte
 
593
      lastPosition++;
 
594
 
 
595
      isfData.append( gifBytes.mid( lastPosition, sizeByte ) );
 
596
      lastPosition += sizeByte;
 
597
    }
 
598
    while( sizeByte == MAX_GIF_BYTE && ( lastPosition <= maxDataPosition ) );
 
599
 
 
600
    // We found the ISF stream!
 
601
    if( lastPosition == maxDataPosition )
 
602
    {
 
603
#ifdef ISFQT_DEBUG_VERBOSE
 
604
      qDebug() << "Found an ISF stream of size" << isfData.size();
 
605
#endif
 
606
      break;
 
607
    }
 
608
    else
 
609
    {
 
610
#ifdef ISFQT_DEBUG_VERBOSE
 
611
      qDebug() << "Stream not found at position:" << position
 
612
               << "size:" << ( lastPosition - position );
 
613
#endif
 
614
      isfData.clear();
 
615
    }
 
616
  }
 
617
 
 
618
#endif // ISFQT_GIF_ENABLED == 1
 
619
 
 
620
  return reader( isfData );
 
621
}
 
622
 
 
623
 
 
624
 
 
625
/**
 
626
 * Return whether the library was built with Fortified GIF support or not.
 
627
 *
 
628
 * @return bool
 
629
 */
 
630
bool Stream::supportsGif()
 
631
{
 
632
  return ( ISFQT_GIF_ENABLED == true );
 
633
}
 
634
 
 
635
 
 
636
 
 
637
/**
 
638
 * Convert a drawing into a raw ISF data stream.
 
639
 *
 
640
 * The resulting byte array will be empty if the drawing is not valid.
 
641
 *
 
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
 
646
 */
 
647
QByteArray Stream::writer( const Drawing &drawing, bool encodeToBase64 )
 
648
{
 
649
  if( &drawing == 0 || drawing.isNull() || drawing.error() != ISF_ERROR_NONE )
 
650
  {
 
651
#ifdef ISFQT_DEBUG
 
652
    qDebug() << "The drawing was not valid!";
 
653
#endif
 
654
    return QByteArray();
 
655
  }
 
656
 
 
657
  DataSource isfData;
 
658
 
 
659
  // Write the persistent format tag
 
660
  TagsWriter::addPersistentFormat( isfData, drawing );
 
661
 
 
662
  // Write the drawing size
 
663
  TagsWriter::addHiMetricSize( isfData, drawing );
 
664
 
 
665
  // Write the attributes
 
666
  TagsWriter::addAttributeTable( isfData, drawing );
 
667
 
 
668
  // Write the metrics
 
669
  TagsWriter::addMetricsTable( isfData, drawing );
 
670
 
 
671
  // Write the transforms
 
672
  TagsWriter::addTransformationTable( isfData, drawing );
 
673
 
 
674
  // Write the strokes
 
675
  TagsWriter::addStrokes( isfData, drawing );
 
676
 
 
677
  // Write the stream size (at the start of the stream)
 
678
  encodeUInt( isfData, isfData.size(), true/*prepend*/ );
 
679
 
 
680
  // Write the version number (at the start of the stream)
 
681
  encodeUInt( isfData, SUPPORTED_ISF_VERSION, true/*prepend*/ );
 
682
 
 
683
 
 
684
  // Convert to Base64 if needed
 
685
  if( encodeToBase64 )
 
686
  {
 
687
    return isfData.data().toBase64();
 
688
  }
 
689
  else
 
690
  {
 
691
    return isfData.data();
 
692
  }
 
693
}
 
694
 
 
695
 
 
696
 
 
697
/**
 
698
 * Convert a drawing into a Fortified-GIF image.
 
699
 *
 
700
 * The resulting byte array will be empty if the drawing is not valid.
 
701
 *
 
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.
 
705
 *
 
706
 * @see supportsGif()
 
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
 
711
 */
 
712
QByteArray Stream::writerGif( const Drawing &drawing, bool encodeToBase64 )
 
713
{
 
714
  QByteArray imageBytes;
 
715
 
 
716
#if ISFQT_GIF_ENABLED == 1
 
717
  Drawing source( drawing );
 
718
 
 
719
  // Get the ISF data stream
 
720
  QByteArray isfData( writer( source ) );
 
721
 
 
722
#ifdef ISFQT_DEBUG_VERBOSE
 
723
  qDebug() << "GIF-Fortifying an ISF stream of size" << isfData.size();
 
724
#endif
 
725
 
 
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 ) );
 
730
 
 
731
  // Initialise the gif variables
 
732
  QBuffer         gifData;
 
733
  GifFileType    *gifImage  = NULL;
 
734
  ColorMapObject *cmap      = NULL;
 
735
  int             height    = isfImage.height();
 
736
  int             width     = isfImage.width();
 
737
  int             numColors = 0;
 
738
  bool            gifError  = true;
 
739
 
 
740
  // Convert the image to GIF using libgif
 
741
 
 
742
  // Open the gif file
 
743
  gifData.open( QIODevice::WriteOnly );
 
744
  gifImage = EGifOpen( (void*)&gifData, GifWriteToByteArray );
 
745
  if( gifImage == 0 )
 
746
  {
 
747
    qWarning() << "Couldn't initialize gif library!";
 
748
    goto writeError;
 
749
  }
 
750
 
 
751
  // Create the color map
 
752
  numColors = ( isfImage.numColors() << 2 );
 
753
  if( numColors > 256 )
 
754
  {
 
755
    numColors = 256;
 
756
  }
 
757
 
 
758
  cmap = MakeMapObject( numColors, NULL );
 
759
  if( cmap == 0 && isfImage.numColors() > 1 )
 
760
  {
 
761
    qWarning() << "Couldn't create map object for gif conversion (colors:" << isfImage.numColors() << ")!";
 
762
    goto writeError;
 
763
  }
 
764
 
 
765
  // Fill in the color map with the colors in the image color table
 
766
  for( int i = 0; i < isfImage.numColors(); ++i )
 
767
  {
 
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 );
 
772
  }
 
773
 
 
774
  // Save the file properties
 
775
  if( EGifPutScreenDesc( gifImage, width, height, 8, 0, cmap ) == GIF_ERROR )
 
776
  {
 
777
    qWarning() << "EGifPutScreenDesc() failed!";
 
778
    goto writeError;
 
779
  }
 
780
 
 
781
  // Save the image format
 
782
  if( EGifPutImageDesc( gifImage, 0, 0, width, height, 0, NULL ) == GIF_ERROR )
 
783
  {
 
784
    qWarning() << "EGifPutImageDesc() failed!";
 
785
    goto writeError;
 
786
  }
 
787
 
 
788
 
 
789
  /**
 
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.
 
794
   */
 
795
  // Write every scanline
 
796
  for( int line = 0; line < height; ++line )
 
797
  {
 
798
    if( EGifPutLine( gifImage, isfImage.scanLine( line ), width ) == GIF_ERROR )
 
799
    {
 
800
      qWarning() << "EGifPutLine failed at scanline" << line
 
801
                 << "(height:" << isfImage.height()
 
802
                 << ", width:" << isfImage.width()
 
803
                 << ", bytesperline:" << isfImage.bytesPerLine() << ")";
 
804
      goto writeError;
 
805
    }
 
806
  }
 
807
 
 
808
 
 
809
/**
 
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
 
813
 * Qt.
 
814
 */
 
815
/*
 
816
  if( EGifPutComment( gifImage, isfData.constData() ) == GIF_ERROR )
 
817
  {
 
818
    qWarning() << "EGifPutComment has failed!";
 
819
    goto writeError;
 
820
  }
 
821
*/
 
822
 
 
823
  // Write the ISF stream into the Comment Extension field
 
824
  if( isfData.size() < MAX_GIF_BYTE )
 
825
  {
 
826
    EGifPutExtension( gifImage, COMMENT_EXT_FUNC_CODE, isfData.size(), isfData.constData() );
 
827
  }
 
828
  else
 
829
  {
 
830
    // Write the extension
 
831
    if( EGifPutExtensionFirst( gifImage, COMMENT_EXT_FUNC_CODE, MAX_GIF_BYTE, isfData.left( MAX_GIF_BYTE ).data() ) == GIF_ERROR )
 
832
    {
 
833
      qWarning() << "EGifPutExtensionFirst failed!";
 
834
      goto writeError;
 
835
    }
 
836
 
 
837
    // The first MAX_GIF_BYTE bytes have been written already
 
838
    quint32 pos = MAX_GIF_BYTE;
 
839
 
 
840
    quint32 length = ( isfData.size() - pos );
 
841
 
 
842
    // Write all the full data blocks
 
843
    while( length >= MAX_GIF_BYTE )
 
844
    {
 
845
      if( EGifPutExtensionNext( gifImage, 0, MAX_GIF_BYTE, isfData.mid( pos, MAX_GIF_BYTE ).data() ) == GIF_ERROR )
 
846
      {
 
847
        qWarning() << "EGifPutExtensionNext failed!";
 
848
        goto writeError;
 
849
      }
 
850
 
 
851
      pos += MAX_GIF_BYTE;
 
852
      length -= MAX_GIF_BYTE;
 
853
    }
 
854
 
 
855
    // Write the last block
 
856
    if( length > 0 )
 
857
    {
 
858
      if( EGifPutExtensionLast( gifImage, 0, length, isfData.mid( pos, MAX_GIF_BYTE ).data() ) == GIF_ERROR )
 
859
      {
 
860
        qWarning() << "EGifPutExtensionLast (n) failed!";
 
861
        goto writeError;
 
862
      }
 
863
    }
 
864
    else
 
865
    {
 
866
      if( EGifPutExtensionLast( gifImage, 0, 0, 0 ) == GIF_ERROR )
 
867
      {
 
868
        qWarning() << "EGifPutExtensionLast (0) failed!";
 
869
        goto writeError;
 
870
      }
 
871
    }
 
872
  }
 
873
 
 
874
  gifError = false;
 
875
 
 
876
writeError:
 
877
  // Clean up the GIF converter etc
 
878
  EGifCloseFile( gifImage );
 
879
  FreeMapObject( cmap );
 
880
  gifData.close();
 
881
 
 
882
  if( gifError )
 
883
  {
 
884
    qWarning() << "GIF error code:" << GifLastError();
 
885
  }
 
886
  else
 
887
  {
 
888
    // Return the GIF data
 
889
    imageBytes = gifData.data();
 
890
 
 
891
#ifdef ISFQT_DEBUG_VERBOSE
 
892
    qDebug() << "Converted a" << isfData.size() << "bytes Ink to GIF:" << isfImage.height() << "x" << isfImage.width() << "->" << imageBytes.size() << "bytes";
 
893
#endif
 
894
  }
 
895
 
 
896
#endif // ISFQT_GIF_ENABLED == 1
 
897
 
 
898
 
 
899
  // Convert to Base64 if needed
 
900
  if( encodeToBase64 )
 
901
  {
 
902
    return imageBytes.toBase64();
 
903
  }
 
904
  else
 
905
  {
 
906
    return imageBytes;
 
907
  }
 
908
}
 
909
 
 
910