Project

General

Profile

Feature #1199 » exiv2-160810-3.diff

Ben Touchette, 10 Aug 2016 16:17

View differences:

trunk/include/exiv2/webpimage.hpp (revision 4359) → trunk/include/exiv2/webpimage.hpp (working copy)
73 73
        //@{
74 74
        void readMetadata();
75 75
        void writeMetadata();
76
        void printStructure(std::ostream& out, PrintStructureOption option);
76 77
        //@}
77 78

  
78 79
        /*!
......
92 93
        //@{
93 94
        bool equalsWebPTag(Exiv2::DataBuf& buf ,const char* str);
94 95
        void decodeChunks(uint64_t filesize);
96
        void inject_VP8X(BasicIo& iIo, bool has_xmp, bool has_exif,
97
                         bool has_alpha, bool has_icc);
98

  
95 99
        //! Copy constructor
96 100
        WebPImage(const WebPImage& rhs);
97 101
        //! Assignment operator
trunk/src/webpimage.cpp (revision 4359) → trunk/src/webpimage.cpp (working copy)
22 22
  File:      webpimage.cpp
23 23
  Version:   $Rev: 3845 $
24 24
  Author(s): Ben Touchette <draekko.software+exiv2@gmail.com>
25
  History:   29-Jul-16
25
  History:   10-Aug-16
26 26
  Credits:   See header file
27 27
 */
28 28
// *****************************************************************************
......
54 54

  
55 55
#define CHECK_BIT(var,pos) ((var) & (1<<(pos)))
56 56

  
57

  
57 58
// *****************************************************************************
58 59
// class member definitions
59 60
namespace Exiv2 {
......
114 115
        byte data[12];
115 116
        DataBuf chunkId(5);
116 117
        const int TAG_SIZE = 4;
117
        chunkId.pData_[4] = '\0' ;
118
        chunkId.pData_[4] = '\0';
118 119

  
119 120
        io_->read(data, TAG_SIZE * 3);
120 121
        uint64_t filesize = Exiv2::getULong(data + 4, littleEndian);
......
127 128
        /* Parse Chunks */
128 129
        bool has_xmp = false;
129 130
        bool has_exif = false;
131
        bool has_vp8x = false;
132
        bool has_alpha = false;
133
        bool has_icc = false;
134

  
130 135
        byte size_buff[4];
131 136
        std::string xmpData;
132 137
        Blob blob;
133 138

  
139
#ifdef SVN_VERSION
140
        if (iccProfile_.count() > 0) {
141
            has_icc = true;
142
        }
143
#endif
144

  
134 145
        if (iptcData_.count() > 0) {
135
            std::cout << "Found iptc data\n";
146
            // do nothing for now
136 147
        }
137 148

  
138 149
        if (exifData_.count() > 0) {
......
154 165
            }
155 166
        }
156 167

  
168
        /* Verify for a VP8X Chunk First before writing in
169
           case we have any exif or xmp data, also check
170
           for any chunks with alpha frame/layer set */
171
        if (has_xmp || has_exif) {
172
            while (!io_->eof()) {
173
                io_->read(chunkId.pData_, 4);
174
                io_->read(size_buff, 4);
175
                uint64_t size = Exiv2::getULong(size_buff, littleEndian);
176
                DataBuf payload(size);
177
                io_->read(payload.pData_, payload.size_);
178
                if (equalsWebPTag(chunkId, "ICCP") && !has_alpha) {
179
                    has_icc = true;
180
                }
181
                if (equalsWebPTag(chunkId, "VP8X")) {
182
                    has_vp8x = true;
183
                }
184
#if 0 // May need to verify for alpha for these chunks in the future
185
                if (equalsWebPTag(chunkId, "VP8 ") && !has_alpha) {
186
                    has_alpha = true;
187
                }
188
                if (equalsWebPTag(chunkId, "ANIM") && !has_alpha) {
189
                    has_alpha = true;
190
                }
191
#endif
192
                if (equalsWebPTag(chunkId, "VP8L") && !has_alpha) {
193
                    if ((payload.pData_[5] & 0x10) == 0x10) {
194
                      has_alpha = true;
195
                    }
196
                }
197
                if (equalsWebPTag(chunkId, "ANMF") && !has_alpha) {
198
                    if ((payload.pData_[5] & 0x2) == 0x2) {
199
                      has_alpha = true;
200
                    }
201
                }
202
                if (equalsWebPTag(chunkId, "ALPH") && !has_alpha) {
203
                    has_alpha = true;
204
                }
205
            }
206

  
207
            if (!has_vp8x) {
208
                inject_VP8X(outIo, has_xmp, has_exif, has_alpha, has_icc);
209
            }
210
        }
211

  
212
        io_->seek(12, BasicIo::beg);
213

  
157 214
        while (!io_->eof()) {
215
            uint64_t offset = io_->tell();
216
            if (offset >= filesize) {
217
                break;
218
            }
219

  
158 220
            io_->read(chunkId.pData_, 4);
159 221
            io_->read(size_buff, 4);
160 222

  
161
            if (endoffile >= filesize) {
162
                break;
163
            }
164

  
165 223
            uint64_t size = Exiv2::getULong(size_buff, littleEndian);
166 224

  
167 225
            DataBuf payload(size);
168
            io_->read(payload.pData_, payload.size_);
226
            io_->read(payload.pData_, size);
169 227

  
170 228
            if (equalsWebPTag(chunkId, "VP8X")) {
171
              if (has_xmp){
172
                payload.pData_[0] |= 0x4;
173
              } else {
174
                payload.pData_[0] &= ~0x4;
175
              }
176
              if (has_exif) {
177
                payload.pData_[0] |= 0x8;
178
              } else {
179
                payload.pData_[0] &= ~0x8;
180
              }
181
              if (outIo.write(chunkId.pData_, TAG_SIZE) != TAG_SIZE)
182
                  throw Error(21);
183
              if (outIo.write(size_buff, 4) != 4)
184
                  throw Error(21);
185
              if (outIo.write(payload.pData_, payload.size_) != payload.size_)
186
                  throw Error(21);
229

  
230
                if (has_icc){
231
                    payload.pData_[0] |= 0x20;
232
                }
233

  
234
                if (has_xmp){
235
                    payload.pData_[0] |= 0x4;
236
                }
237

  
238
                if (has_exif) {
239
                    payload.pData_[0] |= 0x8;
240
                }
241

  
242
                if (outIo.write(chunkId.pData_, TAG_SIZE) != TAG_SIZE)
243
                    throw Error(21);
244
                if (outIo.write(size_buff, 4) != 4)
245
                    throw Error(21);
246
                if (outIo.write(payload.pData_, payload.size_) != payload.size_)
247
                    throw Error(21);
248
#ifdef SVN_VERSION
249
            } else if (equalsWebPTag(chunkId, "ICCP") && has_icc) {
250
                ul2Data(size_buff, iptcData_.size(), littleEndian);
251
                if (outIo.write(chunkId.pData_, TAG_SIZE) != TAG_SIZE)
252
                    throw Error(21);
253
                if (outIo.write(size_buff, 4) != 4)
254
                    throw Error(21);
255
                if (outIo.write(iccProfile_, iccProfile_.size_) != iccProfile_.size_)
256
                    throw Error(21);
257
#endif
187 258
            } else if (equalsWebPTag(chunkId, "EXIF")) {
188
              // Skip and add new data afterwards
259
                // Skip and add new data afterwards
189 260
            } else if (equalsWebPTag(chunkId, "XMP ")) {
190
              // Skip and add new data afterwards
261
                // Skip and add new data afterwards
191 262
            } else {
192
              if (outIo.write(chunkId.pData_, TAG_SIZE) != TAG_SIZE)
263
                if (outIo.write(chunkId.pData_, TAG_SIZE) != TAG_SIZE)
264
                    throw Error(21);
265
                if (outIo.write(size_buff, 4) != 4)
266
                    throw Error(21);
267
                if (outIo.write(payload.pData_, payload.size_) != payload.size_)
268
                    throw Error(21);
269
            }
270

  
271
            offset = io_->tell();
272

  
273
            // Check for extra \0 padding
274
            byte one_character[1];
275
            bool has_zero = false;
276
            while (!io_->eof() && !has_zero && offset < filesize) {
277
                io_->read(one_character, 1);
278
                if (one_character[0] != 0) {
279
                    io_->seek(-1, BasicIo::cur);
280
                    break;
281
                } else {
282
                    has_zero = true;
283
                    if (outIo.write(one_character, 1) != 1)
284
                        throw Error(21);
285
                }
286
            }
287

  
288
            // Encoder seems to pad on odd sized data.
289
            one_character[0] = 0x0;
290
            if (size % 2 != 0 && !has_zero) {
291
              if (outIo.write(one_character, 1) != 1)
193 292
                  throw Error(21);
194
              if (outIo.write(size_buff, 4) != 4)
195
                  throw Error(21);
196
              if (outIo.write(payload.pData_, payload.size_) != payload.size_)
197
                  throw Error(21);
198 293
            }
199 294

  
200
            endoffile = io_->tell();
201
            if (endoffile >= filesize) {
295
            if (offset >= filesize) {
202 296
                break;
203 297
            }
204 298
        }
......
218 312
            {
219 313
                throw Error(21);
220 314
            }
315
            //for (int lp=0; lp<12; lp++)
316
            //    data[0] = 0;
317
            //if (outIo.write(data, 1) != 1) throw Error(21);
221 318
        }
222 319

  
223 320
        if (has_xmp) {
......
228 325
            if (outIo.write((const byte*)xmpData.data(), static_cast<long>(xmpData.size())) != (long)xmpData.size()) {
229 326
                throw Error(21);
230 327
            }
231
            for (int lp=0; lp<12; lp++)
232
                data[0] = 0;
233
            if (outIo.write(data, 1) != 1) throw Error(21);
328
            //for (int lp=0; lp<12; lp++)
329
            //    data[0] = 0;
330
            //if (outIo.write(data, 1) != 1) throw Error(21);
234 331
        }
235 332

  
236 333
        // Fix File Size Payload Data
......
246 343

  
247 344
    /* =========================================== */
248 345

  
346
    void WebPImage::printStructure(std::ostream& out, PrintStructureOption option)
347
    {
348
        if (io_->open() != 0) {
349
            throw Error(9, io_->path(), strError());
350
        }
351
        IoCloser closer(*io_);
352
        // Ensure that this is the correct image type
353
        if (!isWebPType(*io_, true)) {
354
            if (io_->error() || io_->eof()) throw Error(14);
355
            throw Error(3, "WEBP");
356
        }
357

  
358
        if ( option == kpsBasic || option == kpsXMP ) {
359

  
360
            char output[255];
361
            char showdata[16];
362
            byte header[12];
363
            byte size_buff[4];
364
            DataBuf chunkId(5);
365
            uint64_t offset = 0;
366
            int length = 0;
367
            const int TAG_SIZE = 4;
368
            chunkId.pData_[4] = '\0';
369

  
370
            if ( option == kpsBasic ) {
371
                std::cout << "STRUCTURE OF WEBP FILE: " << io_->path() << std::endl;
372
                std::cout << "    offset  | chunk_type |   length   | data" << std::endl;
373
            }
374

  
375
            /* Get up header */
376
            length = TAG_SIZE * 3;
377
            io_->read(header, length);
378

  
379
            for (int clean = 0; clean < 16; clean++)
380
                showdata[clean] = 0;
381

  
382
            for (int loop = 0; loop < length; loop++) {
383
                if (header[loop] >= 0x20 && header[loop] <= 0x7F) {
384
                    showdata[loop] = header[loop];
385
                } else {
386
                    showdata[loop] = '.';
387
                }
388
            }
389
            showdata[length] = 0;
390
            sprintf((char*)&output, " %10d |    WEBP    | %10d | %s ",
391
                    offset, length, showdata);
392
            std::cout << output << std::endl;
393

  
394
            /* Loop through chunks */
395
            while( !io_->eof() ) {
396
                offset = io_->tell();
397
                if ((offset + 2) >= io_->size()){
398
                    break;
399
                }
400
                io_->read(chunkId.pData_, 4);
401
                io_->read(size_buff, 4);
402
                length = Exiv2::getULong(size_buff, littleEndian);
403

  
404
                DataBuf payload(length);
405
                io_->read(payload.pData_, payload.size_);
406

  
407
                for (int clean = 0; clean < 16; clean++)
408
                    showdata[clean] = 0;
409

  
410
                int max = length;
411
                if (max > 15) max = 15;
412
                for (int loop = 0; loop < max; loop++) {
413
                    if (payload.pData_[loop] >= 0x20 && payload.pData_[loop] <= 0x7F) {
414
                        showdata[loop] = payload.pData_[loop];
415
                    } else {
416
                        showdata[loop] = '.';
417
                    }
418
                }
419
                showdata[max] = 0;
420
                sprintf((char*)&output, " %10d |    %s    | %10d | %s ",
421
                        offset, chunkId.pData_, length, showdata);
422
                std::cout << output << std::endl;
423

  
424
                if (length == 0) {
425
                    std::cout << "length to short aborting\n";
426
                    break;
427
                }
428

  
429
                // Check for extra \0 padding
430
                byte one_character[1];
431
                int count = 0;
432
                while (1) {
433
                    io_->read(one_character, 1);
434
                    if (one_character[0] != 0 || io_->eof()) {
435
                        if (count > 0)
436
                          std::cout << "offset output null [" << count << "]\n";
437
                        io_->seek(-1, BasicIo::cur);
438
                        break;
439
                    }
440
                    count++;
441
                }
442
            }
443
        }
444
    }
445

  
446
    /* =========================================== */
447

  
249 448
    void WebPImage::readMetadata()
250 449
    {
251
    	std::cout << "1\n";
252 450
        if (io_->open() != 0) throw Error(9, io_->path(), strError());
253 451
        IoCloser closer(*io_);
254 452
        // Ensure that this is the correct image type
......
271 469

  
272 470
    void WebPImage::decodeChunks(uint64_t filesize)
273 471
    {
274
    	std::cout << "2\n";
275
        DataBuf  chunkId(5);
276
        byte     size_buff[4];
277
        uint64_t size;
278
        uint64_t endoffile = 12;
472
        DataBuf   chunkId(5);
473
        byte      size_buff[4];
474
        uint64_t  offset;
475
        uint64_t  size;
476
        uint64_t  endoffile = 12;
477
        bool       has_canvas_data = false;
279 478

  
280 479
        chunkId.pData_[4] = '\0' ;
281 480

  
282 481
        while (!io_->eof()) {
482
            offset = io_->tell();
483
            if ((offset + 2) >= io_->size()){
484
                break;
485
            }
283 486
            io_->read(chunkId.pData_, 4);
284 487
            io_->read(size_buff, 4);
285 488
            size = Exiv2::getULong(size_buff, littleEndian);
286 489

  
287 490
            DataBuf payload(size);
288 491

  
289
            if (equalsWebPTag(chunkId, "VP8X")) {
492
            if (equalsWebPTag(chunkId, "VP8X") && !has_canvas_data) {
493
                has_canvas_data = true;
290 494
                io_->read(payload.pData_, payload.size_);
291 495
                byte size_buf[4];
292 496
                memcpy(&size_buf, &payload.pData_[4], 3);
......
295 499
                memcpy(&size_buf, &payload.pData_[7], 3);
296 500
                size_buf[3] = 0;
297 501
                pixelHeight_ = Exiv2::getULong(size_buf, littleEndian) + 1;
502
            } else if (equalsWebPTag(chunkId, "VP8 ") && !has_canvas_data) {
503
                has_canvas_data = true;
504
                io_->read(payload.pData_, payload.size_);
505
                byte size_buf[4];
506
                memcpy(&size_buf, &payload.pData_[6], 2);
507
                size_buf[2] = 0;
508
                size_buf[3] = 0;
509
                pixelWidth_ = Exiv2::getULong(size_buf, littleEndian) & 0x3fff;
510
                memcpy(&size_buf, &payload.pData_[8], 2);
511
                size_buf[2] = 0;
512
                size_buf[3] = 0;
513
                pixelHeight_ = Exiv2::getULong(size_buf, littleEndian) & 0x3fff;
514
            } else if (equalsWebPTag(chunkId, "VP8L") && !has_canvas_data) {
515
                has_canvas_data = true;
516
                io_->read(payload.pData_, payload.size_);
517
                byte size_buf_w[2];
518
                memcpy(&size_buf_w, &payload.pData_[1], 2);
519
                size_buf_w[1] &= 0x3F;
520
                pixelWidth_ = Exiv2::getUShort(size_buf_w, littleEndian) + 1;
521
                byte size_buf_h[3];
522
                memcpy(&size_buf_h, &payload.pData_[2], 3);
523
                size_buf_h[0] = ((size_buf_h[0] >> 6) & 0x3) | ((size_buf_h[1] & 0x3F) << 0x2);
524
                size_buf_h[1] = ((size_buf_h[1] >> 6) & 0x3) | ((size_buf_h[2] & 0x3F) << 0x2);
525
                pixelHeight_ = Exiv2::getUShort(size_buf_h, littleEndian) + 1;
526
            } else if (equalsWebPTag(chunkId, "ANMF") && !has_canvas_data) {
527
                has_canvas_data = true;
528
                io_->read(payload.pData_, payload.size_);
529
                byte size_buf[4];
530
                memcpy(&size_buf, &payload.pData_[6], 3);
531
                size_buf[3] = 0;
532
                pixelWidth_ = Exiv2::getULong(size_buf, littleEndian) + 1;
533
                memcpy(&size_buf, &payload.pData_[9], 3);
534
                size_buf[3] = 0;
535
                pixelHeight_ = Exiv2::getULong(size_buf, littleEndian) + 1;
298 536
            } else if (equalsWebPTag(chunkId, "ICCP")) {
299
#ifdef __SVN__ /* COULD BE ENABLED FOR SVN VERSION */
537
#ifdef SVN_VERSION
300 538
                io_->read(payload.pData_, payload.size_);
301 539
                this->setIccProfile(payload);
302 540
#else
......
349 587
                io_->seek(size, BasicIo::cur);
350 588
            }
351 589

  
590
            // Check for extra \0 padding
591
            byte one_character[1];
592
            int count = 0;
593
            while (1) {
594
                io_->read(one_character, 1);
595
                if (one_character[0] != 0 || io_->eof()) {
596
                    io_->seek(-1, BasicIo::cur);
597
                    break;
598
                }
599
                count++;
600
            }
601

  
352 602
            endoffile = io_->tell();
353 603
            if (endoffile >= filesize) {
354 604
                break;
......
367 617
        return image;
368 618
    }
369 619

  
370
    bool isWebPType(BasicIo& iIo, bool /*advance*/)
620
    bool isWebPType(BasicIo& iIo, bool  /*advance*/)
371 621
    {
372 622
        const int32_t len = 4;
373 623
        const unsigned char RiffImageId[4] = { 'R', 'I', 'F' ,'F'};
......
381 631
        bool matched_riff = (memcmp(riff, RiffImageId, len) == 0);
382 632
        bool matched_webp = (memcmp(webp, WebPImageId, len) == 0);
383 633
        iIo.seek(-12, BasicIo::cur);
384
        bool result = matched_riff && matched_webp;
385
    	return result;
634
        return matched_riff && matched_webp;
386 635
    }
387 636

  
388 637
    /*!
......
399 648
        return true;
400 649
    }
401 650

  
651

  
652
    /*!
653
      @brief Function used to add missing EXIF & XMP flags
654
          to the feature section.
655
      @param  iIo get BasicIo pointer to inject data
656
      @param has_xmp Verify if we have xmp data and set required flag
657
      @param has_exif Verify if we have exif data and set required flag
658
      @return Returns void
659
     */
660
    void WebPImage::inject_VP8X(BasicIo& iIo, bool has_xmp,
661
                                bool has_exif, bool has_alpha,
662
                                bool has_icc) {
663
        byte header[4];
664
        byte size[4] = { 0x0A, 0x00, 0x00, 0x00 };
665
        byte data[10] = { 0x00, 0x00, 0x00, 0x00, 0x00,
666
                          0x00, 0x00, 0x00, 0x00, 0x00 };
667
        strncpy((char*)&header, "VP8X", 4);
668
        iIo.write(header, 4);
669
        iIo.write(size, 4);
670

  
671
        if (has_alpha) {
672
            data[0] |= 0x10;
673
        }
674

  
675
        if (has_icc) {
676
            data[0] |= 0x20;
677
        }
678

  
679
        if (has_xmp) {
680
            data[0] |= 0x4;
681
        }
682

  
683
        if (has_exif) {
684
            data[0] |= 0x8;
685
        }
686

  
687
        /* set width */
688
        int w = pixelWidth_- 1;
689
        data[4] = w & 0xFF;
690
        data[5] = (w >> 8) & 0xFF;
691
        data[6] = (w >> 16) & 0xFF;
692

  
693
        /* set width */
694
        int h = pixelHeight_- 1;
695
        data[7] = h & 0xFF;
696
        data[8] = (h >> 8) & 0xFF;
697
        data[9] = (h >> 16) & 0xFF;
698

  
699
        iIo.write(data, 10);
700
    }
701

  
402 702
} // namespace Exiv2
(8-8/12)