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
|