blob: 214c888d5f37cf81e218d4492131b9b2cf433c68 [file] [log] [blame]
The Android Open Source Project34a25642009-03-03 19:30:03 -08001//--------------------------------------------------------------------------
2// Program to pull the information out of various types of EXIF digital
3// camera files and show it in a reasonably consistent way
4//
5// This module handles basic Jpeg file handling
6//
7// Matthias Wandel
8//--------------------------------------------------------------------------
Ray Chen5d50b9a2010-06-23 15:14:30 -07009//#define LOG_NDEBUG 0
10#define LOG_TAG "JHEAD"
The Android Open Source Project34a25642009-03-03 19:30:03 -080011#include <utils/Log.h>
12#include "jhead.h"
13
14// Storage for simplified info extracted from file.
15ImageInfo_t ImageInfo;
16
17
18static Section_t * Sections = NULL;
19static int SectionsAllocated;
20static int SectionsRead;
21static int HaveAll;
22
23// Define the line below to turn on poor man's debugging output
24#undef SUPERDEBUG
25
26#ifdef SUPERDEBUG
Steve Block7a314da2012-01-06 19:10:19 +000027#define printf ALOGE
The Android Open Source Project34a25642009-03-03 19:30:03 -080028#endif
29
30
31
32#define PSEUDO_IMAGE_MARKER 0x123; // Extra value.
33//--------------------------------------------------------------------------
34// Get 16 bits motorola order (always) for jpeg header stuff.
35//--------------------------------------------------------------------------
36static int Get16m(const void * Short)
37{
38 return (((uchar *)Short)[0] << 8) | ((uchar *)Short)[1];
39}
40
41
42//--------------------------------------------------------------------------
43// Process a COM marker.
44// We want to print out the marker contents as legible text;
45// we must guard against random junk and varying newline representations.
46//--------------------------------------------------------------------------
47static void process_COM (const uchar * Data, int length)
48{
49 int ch;
Chih-Chung Changd6a02c32009-03-24 20:34:28 -070050 char Comment[MAX_COMMENT_SIZE+1];
The Android Open Source Project34a25642009-03-03 19:30:03 -080051 int nch;
52 int a;
53
54 nch = 0;
55
Chih-Chung Changd6a02c32009-03-24 20:34:28 -070056 if (length > MAX_COMMENT_SIZE) length = MAX_COMMENT_SIZE; // Truncate if it won't fit in our structure.
The Android Open Source Project34a25642009-03-03 19:30:03 -080057
58 for (a=2;a<length;a++){
59 ch = Data[a];
60
61 if (ch == '\r' && Data[a+1] == '\n') continue; // Remove cr followed by lf.
62
63 if (ch >= 32 || ch == '\n' || ch == '\t'){
64 Comment[nch++] = (char)ch;
65 }else{
66 Comment[nch++] = '?';
67 }
68 }
69
70 Comment[nch] = '\0'; // Null terminate
71
72 if (ShowTags){
73 printf("COM marker comment: %s\n",Comment);
74 }
75
76 strcpy(ImageInfo.Comments,Comment);
Chih-Chung Changd6a02c32009-03-24 20:34:28 -070077 ImageInfo.CommentWidchars = 0;
The Android Open Source Project34a25642009-03-03 19:30:03 -080078}
79
80
81//--------------------------------------------------------------------------
82// Process a SOFn marker. This is useful for the image dimensions
83//--------------------------------------------------------------------------
84static void process_SOFn (const uchar * Data, int marker)
85{
86 int data_precision, num_components;
87
88 data_precision = Data[2];
89 ImageInfo.Height = Get16m(Data+3);
90 ImageInfo.Width = Get16m(Data+5);
91 num_components = Data[7];
92
93 if (num_components == 3){
94 ImageInfo.IsColor = 1;
95 }else{
96 ImageInfo.IsColor = 0;
97 }
98
99 ImageInfo.Process = marker;
100
101 if (ShowTags){
102 printf("JPEG image is %uw * %uh, %d color components, %d bits per sample\n",
103 ImageInfo.Width, ImageInfo.Height, num_components, data_precision);
104 }
105}
106
107
108//--------------------------------------------------------------------------
109// Check sections array to see if it needs to be increased in size.
110//--------------------------------------------------------------------------
111void CheckSectionsAllocated(void)
112{
113 if (SectionsRead > SectionsAllocated){
114 ErrFatal("allocation screwup");
115 }
116 if (SectionsRead >= SectionsAllocated){
117 SectionsAllocated += SectionsAllocated/2;
118 Sections = (Section_t *)realloc(Sections, sizeof(Section_t)*SectionsAllocated);
119 if (Sections == NULL){
120 ErrFatal("could not allocate data for entire image");
121 }
122 }
123}
124
125
126//--------------------------------------------------------------------------
127// Parse the marker stream until SOS or EOI is seen;
128//--------------------------------------------------------------------------
129int ReadJpegSections (FILE * infile, ReadMode_t ReadMode)
130{
131 int a;
132 int HaveCom = FALSE;
133
134 a = fgetc(infile);
135
136 if (a != 0xff || fgetc(infile) != M_SOI){
137 return FALSE;
138 }
139 for(;;){
140 int itemlen;
141 int marker = 0;
142 int ll,lh, got;
143 uchar * Data;
144
145 CheckSectionsAllocated();
146
Chih-Chung Changd6a02c32009-03-24 20:34:28 -0700147 for (a=0;a<=16;a++){
The Android Open Source Project34a25642009-03-03 19:30:03 -0800148 marker = fgetc(infile);
149 if (marker != 0xff) break;
150
Chih-Chung Changd6a02c32009-03-24 20:34:28 -0700151 if (a >= 16){
The Android Open Source Project34a25642009-03-03 19:30:03 -0800152 fprintf(stderr,"too many padding bytes\n");
153 return FALSE;
154 }
155 }
156
The Android Open Source Project34a25642009-03-03 19:30:03 -0800157
158 Sections[SectionsRead].Type = marker;
Jeff Sharkey31b17e62013-08-21 11:27:36 -0700159 Sections[SectionsRead].Offset = ftell(infile);
The Android Open Source Project34a25642009-03-03 19:30:03 -0800160
161 // Read the length of the section.
162 lh = fgetc(infile);
163 ll = fgetc(infile);
164
165 itemlen = (lh << 8) | ll;
166
167 if (itemlen < 2){
168// ErrFatal("invalid marker");
Steve Block7a314da2012-01-06 19:10:19 +0000169 ALOGE("invalid marker");
The Android Open Source Project34a25642009-03-03 19:30:03 -0800170 return FALSE;
171 }
172
173 Sections[SectionsRead].Size = itemlen;
174
175 Data = (uchar *)malloc(itemlen);
176 if (Data == NULL){
177 // ErrFatal("Could not allocate memory");
Steve Block7a314da2012-01-06 19:10:19 +0000178 ALOGE("Could not allocate memory");
The Android Open Source Project34a25642009-03-03 19:30:03 -0800179 return 0;
180 }
181 Sections[SectionsRead].Data = Data;
182
183 // Store first two pre-read bytes.
184 Data[0] = (uchar)lh;
185 Data[1] = (uchar)ll;
186
187 got = fread(Data+2, 1, itemlen-2, infile); // Read the whole section.
188 if (got != itemlen-2){
189// ErrFatal("Premature end of file?");
Steve Block7a314da2012-01-06 19:10:19 +0000190 ALOGE("Premature end of file?");
The Android Open Source Project34a25642009-03-03 19:30:03 -0800191 return FALSE;
192 }
193 SectionsRead += 1;
194
195 printf("reading marker %d", marker);
196 switch(marker){
197
198 case M_SOS: // stop before hitting compressed data
199 // If reading entire image is requested, read the rest of the data.
200 if (ReadMode & READ_IMAGE){
201 int cp, ep, size;
202 // Determine how much file is left.
203 cp = ftell(infile);
204 fseek(infile, 0, SEEK_END);
205 ep = ftell(infile);
206 fseek(infile, cp, SEEK_SET);
207
208 size = ep-cp;
209 Data = (uchar *)malloc(size);
210 if (Data == NULL){
211 // ErrFatal("could not allocate data for entire image");
Steve Block7a314da2012-01-06 19:10:19 +0000212 ALOGE("could not allocate data for entire image");
The Android Open Source Project34a25642009-03-03 19:30:03 -0800213 return FALSE;
214 }
215
216 got = fread(Data, 1, size, infile);
217 if (got != size){
218 // ErrFatal("could not read the rest of the image");
Steve Block7a314da2012-01-06 19:10:19 +0000219 ALOGE("could not read the rest of the image");
The Android Open Source Project34a25642009-03-03 19:30:03 -0800220 return FALSE;
221 }
222
223 CheckSectionsAllocated();
224 Sections[SectionsRead].Data = Data;
Jeff Sharkey31b17e62013-08-21 11:27:36 -0700225 Sections[SectionsRead].Offset = cp;
The Android Open Source Project34a25642009-03-03 19:30:03 -0800226 Sections[SectionsRead].Size = size;
227 Sections[SectionsRead].Type = PSEUDO_IMAGE_MARKER;
228 SectionsRead ++;
229 HaveAll = 1;
230 }
231 return TRUE;
232
233 case M_EOI: // in case it's a tables-only JPEG stream
234 fprintf(stderr,"No image in jpeg!\n");
235 return FALSE;
236
237 case M_COM: // Comment section
238 if (HaveCom || ((ReadMode & READ_METADATA) == 0)){
239 // Discard this section.
240 free(Sections[--SectionsRead].Data);
241 }else{
242 process_COM(Data, itemlen);
243 HaveCom = TRUE;
244 }
245 break;
246
247 case M_JFIF:
248 // Regular jpegs always have this tag, exif images have the exif
249 // marker instead, althogh ACDsee will write images with both markers.
250 // this program will re-create this marker on absence of exif marker.
251 // hence no need to keep the copy from the file.
252 free(Sections[--SectionsRead].Data);
253 break;
254
255 case M_EXIF:
Chih-Chung Changd6a02c32009-03-24 20:34:28 -0700256 // There can be different section using the same marker.
257 if (ReadMode & READ_METADATA){
258 if (memcmp(Data+2, "Exif", 4) == 0){
259 process_EXIF(Data, itemlen);
260 break;
261 }else if (memcmp(Data+2, "http:", 5) == 0){
262 Sections[SectionsRead-1].Type = M_XMP; // Change tag for internal purposes.
263 if (ShowTags){
264 printf("Image cotains XMP section, %d bytes long\n", itemlen);
265 if (ShowTags){
266 ShowXmp(Sections[SectionsRead-1]);
267 }
268 }
269 break;
270 }
The Android Open Source Project34a25642009-03-03 19:30:03 -0800271 }
Chih-Chung Changd6a02c32009-03-24 20:34:28 -0700272 // Oterwise, discard this section.
273 free(Sections[--SectionsRead].Data);
The Android Open Source Project34a25642009-03-03 19:30:03 -0800274 break;
275
276 case M_IPTC:
277 if (ReadMode & READ_METADATA){
278 if (ShowTags){
279 printf("Image cotains IPTC section, %d bytes long\n", itemlen);
280 }
281 // Note: We just store the IPTC section. Its relatively straightforward
282 // and we don't act on any part of it, so just display it at parse time.
283 }else{
284 free(Sections[--SectionsRead].Data);
285 }
286 break;
287
288 case M_SOF0:
289 case M_SOF1:
290 case M_SOF2:
291 case M_SOF3:
292 case M_SOF5:
293 case M_SOF6:
294 case M_SOF7:
295 case M_SOF9:
296 case M_SOF10:
297 case M_SOF11:
298 case M_SOF13:
299 case M_SOF14:
300 case M_SOF15:
301 process_SOFn(Data, marker);
302 break;
303 default:
304 // Skip any other sections.
305 if (ShowTags){
306 printf("Jpeg section marker 0x%02x size %d\n",marker, itemlen);
307 }
308 break;
309 }
310 }
311 return TRUE;
312}
313
314//--------------------------------------------------------------------------
Tyler Luu24757b42011-08-24 20:53:34 -0500315// Parse the marker buffer until SOS or EOI is seen;
316//--------------------------------------------------------------------------
317int ReadJpegSectionsFromBuffer (unsigned char* buffer, unsigned int buffer_size, ReadMode_t ReadMode)
318{
319 int a;
320 unsigned int pos = 0;
321 int HaveCom = FALSE;
322
323 if (!buffer) {
324 return FALSE;
325 }
326
327 if (buffer_size < 1) {
328 return FALSE;
329 }
330
331 a = (int) buffer[pos++];
332
333 if (a != 0xff || buffer[pos++] != M_SOI){
334 return FALSE;
335 }
336
337 for(;;){
338 int itemlen;
339 int marker = 0;
340 int ll,lh, got;
341 uchar * Data;
342
343 CheckSectionsAllocated();
344
345 for (a=0;a<=16;a++){
346 marker = buffer[pos++];
347 if (marker != 0xff) break;
348
349 if (a >= 16){
350 fprintf(stderr,"too many padding bytes\n");
351 return FALSE;
352 }
353 }
354
355 Sections[SectionsRead].Type = marker;
Jeff Sharkey31b17e62013-08-21 11:27:36 -0700356 Sections[SectionsRead].Offset = pos;
Tyler Luu24757b42011-08-24 20:53:34 -0500357
358 // Read the length of the section.
359 lh = buffer[pos++];
360 ll = buffer[pos++];
361
362 itemlen = (lh << 8) | ll;
363
364 if (itemlen < 2) {
Steve Block7a314da2012-01-06 19:10:19 +0000365 ALOGE("invalid marker");
Tyler Luu24757b42011-08-24 20:53:34 -0500366 return FALSE;
367 }
368
369 Sections[SectionsRead].Size = itemlen;
370
371 Data = (uchar *)malloc(itemlen);
372 if (Data == NULL) {
Steve Block7a314da2012-01-06 19:10:19 +0000373 ALOGE("Could not allocate memory");
Tyler Luu24757b42011-08-24 20:53:34 -0500374 return 0;
375 }
376 Sections[SectionsRead].Data = Data;
377
378 // Store first two pre-read bytes.
379 Data[0] = (uchar)lh;
380 Data[1] = (uchar)ll;
381
382 if (pos+itemlen-2 > buffer_size) {
Steve Block7a314da2012-01-06 19:10:19 +0000383 ALOGE("Premature end of file?");
Tyler Luu24757b42011-08-24 20:53:34 -0500384 return FALSE;
385 }
386
387 memcpy(Data+2, buffer+pos, itemlen-2); // Read the whole section.
388 pos += itemlen-2;
389
390 SectionsRead += 1;
391
392 printf("reading marker %d", marker);
393 switch(marker){
394
395 case M_SOS: // stop before hitting compressed data
396 // If reading entire image is requested, read the rest of the data.
397 if (ReadMode & READ_IMAGE){
398 int size;
399 // Determine how much file is left.
400 size = buffer_size - pos;
401
402 if (size < 1) {
Steve Block7a314da2012-01-06 19:10:19 +0000403 ALOGE("could not read the rest of the image");
Tyler Luu24757b42011-08-24 20:53:34 -0500404 return FALSE;
405 }
406 Data = (uchar *)malloc(size);
407 if (Data == NULL) {
Steve Block7a314da2012-01-06 19:10:19 +0000408 ALOGE("%d: could not allocate data for entire image size: %d", __LINE__, size);
Tyler Luu24757b42011-08-24 20:53:34 -0500409 return FALSE;
410 }
411
412 memcpy(Data, buffer+pos, size);
413
414 CheckSectionsAllocated();
415 Sections[SectionsRead].Data = Data;
Jeff Sharkey31b17e62013-08-21 11:27:36 -0700416 Sections[SectionsRead].Offset = pos;
Tyler Luu24757b42011-08-24 20:53:34 -0500417 Sections[SectionsRead].Size = size;
418 Sections[SectionsRead].Type = PSEUDO_IMAGE_MARKER;
419 SectionsRead ++;
420 HaveAll = 1;
421 }
422 return TRUE;
423
424 case M_EOI: // in case it's a tables-only JPEG stream
Steve Block7a314da2012-01-06 19:10:19 +0000425 ALOGE("No image in jpeg!\n");
Tyler Luu24757b42011-08-24 20:53:34 -0500426 return FALSE;
427
428 case M_COM: // Comment section
429 if (HaveCom || ((ReadMode & READ_METADATA) == 0)){
430 // Discard this section.
431 free(Sections[--SectionsRead].Data);
432 }else{
433 process_COM(Data, itemlen);
434 HaveCom = TRUE;
435 }
436 break;
437
438 case M_JFIF:
439 // Regular jpegs always have this tag, exif images have the exif
440 // marker instead, althogh ACDsee will write images with both markers.
441 // this program will re-create this marker on absence of exif marker.
442 // hence no need to keep the copy from the file.
443 free(Sections[--SectionsRead].Data);
444 break;
445
446 case M_EXIF:
447 // There can be different section using the same marker.
448 if (ReadMode & READ_METADATA){
449 if (memcmp(Data+2, "Exif", 4) == 0){
450 process_EXIF(Data, itemlen);
451 break;
452 }else if (memcmp(Data+2, "http:", 5) == 0){
453 Sections[SectionsRead-1].Type = M_XMP; // Change tag for internal purposes.
454 if (ShowTags){
Steve Block84f254d2011-12-20 16:20:38 +0000455 ALOGD("Image cotains XMP section, %d bytes long\n", itemlen);
Tyler Luu24757b42011-08-24 20:53:34 -0500456 if (ShowTags){
457 ShowXmp(Sections[SectionsRead-1]);
458 }
459 }
460 break;
461 }
462 }
463 // Oterwise, discard this section.
464 free(Sections[--SectionsRead].Data);
465 break;
466
467 case M_IPTC:
468 if (ReadMode & READ_METADATA){
469 if (ShowTags){
Steve Block84f254d2011-12-20 16:20:38 +0000470 ALOGD("Image cotains IPTC section, %d bytes long\n", itemlen);
Tyler Luu24757b42011-08-24 20:53:34 -0500471 }
472 // Note: We just store the IPTC section. Its relatively straightforward
473 // and we don't act on any part of it, so just display it at parse time.
474 }else{
475 free(Sections[--SectionsRead].Data);
476 }
477 break;
478
479 case M_SOF0:
480 case M_SOF1:
481 case M_SOF2:
482 case M_SOF3:
483 case M_SOF5:
484 case M_SOF6:
485 case M_SOF7:
486 case M_SOF9:
487 case M_SOF10:
488 case M_SOF11:
489 case M_SOF13:
490 case M_SOF14:
491 case M_SOF15:
492 process_SOFn(Data, marker);
493 break;
494 default:
495 // Skip any other sections.
496 if (ShowTags){
Steve Block84f254d2011-12-20 16:20:38 +0000497 ALOGD("Jpeg section marker 0x%02x size %d\n",marker, itemlen);
Tyler Luu24757b42011-08-24 20:53:34 -0500498 }
499 break;
500 }
501 }
502 return TRUE;
503}
504
505//--------------------------------------------------------------------------
The Android Open Source Project34a25642009-03-03 19:30:03 -0800506// Discard read data.
507//--------------------------------------------------------------------------
508void DiscardData(void)
509{
510 int a;
511
512 for (a=0;a<SectionsRead;a++){
513 free(Sections[a].Data);
514 }
515
516 memset(&ImageInfo, 0, sizeof(ImageInfo));
517 SectionsRead = 0;
518 HaveAll = 0;
519}
520
521//--------------------------------------------------------------------------
522// Read image data.
523//--------------------------------------------------------------------------
524int ReadJpegFile(const char * FileName, ReadMode_t ReadMode)
525{
526 FILE * infile;
527 int ret;
528
529 infile = fopen(FileName, "rb"); // Unix ignores 'b', windows needs it.
530
531 if (infile == NULL) {
Steve Block7a314da2012-01-06 19:10:19 +0000532 ALOGE("can't open '%s'", FileName);
The Android Open Source Project34a25642009-03-03 19:30:03 -0800533 fprintf(stderr, "can't open '%s'\n", FileName);
534 return FALSE;
535 }
536
537 // Scan the JPEG headers.
538 printf("ReadJpegSections");
539 ret = ReadJpegSections(infile, ReadMode);
540 if (!ret){
Steve Block56c2f1e2011-10-20 14:24:07 +0100541 ALOGV("Cannot parse JPEG sections for file: %s", FileName);
The Android Open Source Project34a25642009-03-03 19:30:03 -0800542 fprintf(stderr,"Not JPEG: %s\n",FileName);
543 }
544
545 fclose(infile);
546
547 if (ret == FALSE){
548 DiscardData();
549 }
550 return ret;
551}
552
553
554//--------------------------------------------------------------------------
555// Replace or remove exif thumbnail
556//--------------------------------------------------------------------------
557int SaveThumbnail(char * ThumbFileName)
558{
559 FILE * ThumbnailFile;
560
561 if (ImageInfo.ThumbnailOffset == 0 || ImageInfo.ThumbnailSize == 0){
562 fprintf(stderr,"Image contains no thumbnail\n");
563 return FALSE;
564 }
565
566 if (strcmp(ThumbFileName, "-") == 0){
567 // A filename of '-' indicates thumbnail goes to stdout.
568 // This doesn't make much sense under Windows, so this feature is unix only.
569 ThumbnailFile = stdout;
570 }else{
571 ThumbnailFile = fopen(ThumbFileName,"wb");
572 }
573
574 if (ThumbnailFile){
575 uchar * ThumbnailPointer;
576 Section_t * ExifSection;
577 ExifSection = FindSection(M_EXIF);
578 ThumbnailPointer = ExifSection->Data+ImageInfo.ThumbnailOffset+8;
579
580 fwrite(ThumbnailPointer, ImageInfo.ThumbnailSize ,1, ThumbnailFile);
581 fclose(ThumbnailFile);
582 return TRUE;
583 }else{
584 // ErrFatal("Could not write thumbnail file");
Steve Block7a314da2012-01-06 19:10:19 +0000585 ALOGE("Could not write thumbnail file");
The Android Open Source Project34a25642009-03-03 19:30:03 -0800586 return FALSE;
587 }
588}
589
590//--------------------------------------------------------------------------
591// Replace or remove exif thumbnail
592//--------------------------------------------------------------------------
Tyler Luu13714f22011-09-27 20:04:35 -0500593int ReplaceThumbnailFromBuffer(const char * Thumb, int ThumbLen)
594{
595 int NewExifSize;
596 Section_t * ExifSection;
597 uchar * ThumbnailPointer;
598
599 if (ImageInfo.ThumbnailOffset == 0 || ImageInfo.ThumbnailAtEnd == FALSE){
600 if (Thumb == NULL){
601 // Delete of nonexistent thumbnail (not even pointers present)
602 // No action, no error.
603 return FALSE;
604 }
605
606 // Adding or removing of thumbnail is not possible - that would require rearranging
607 // of the exif header, which is risky, and jhad doesn't know how to do.
608 fprintf(stderr,"Image contains no thumbnail to replace - add is not possible\n");
609#ifdef SUPERDEBUG
Steve Block7a314da2012-01-06 19:10:19 +0000610 ALOGE("Image contains no thumbnail to replace - add is not possible\n");
Tyler Luu13714f22011-09-27 20:04:35 -0500611#endif
612 return FALSE;
613 }
614
615 if (Thumb) {
616 if (ThumbLen + ImageInfo.ThumbnailOffset > 0x10000-20){
617 //ErrFatal("Thumbnail is too large to insert into exif header");
Steve Block7a314da2012-01-06 19:10:19 +0000618 ALOGE("Thumbnail is too large to insert into exif header");
Tyler Luu13714f22011-09-27 20:04:35 -0500619 return FALSE;
620 }
621 } else {
622 if (ImageInfo.ThumbnailSize == 0){
623 return FALSE;
624 }
625
626 ThumbLen = 0;
627 }
628
629 ExifSection = FindSection(M_EXIF);
630
631 NewExifSize = ImageInfo.ThumbnailOffset+8+ThumbLen;
632 ExifSection->Data = (uchar *)realloc(ExifSection->Data, NewExifSize);
633
634 ThumbnailPointer = ExifSection->Data+ImageInfo.ThumbnailOffset+8;
635
636 if (Thumb){
637 memcpy(ThumbnailPointer, Thumb, ThumbLen);
638 }
639
640 ImageInfo.ThumbnailSize = ThumbLen;
641
642 Put32u(ExifSection->Data+ImageInfo.ThumbnailSizeOffset+8, ThumbLen);
643
644 ExifSection->Data[0] = (uchar)(NewExifSize >> 8);
645 ExifSection->Data[1] = (uchar)NewExifSize;
646 ExifSection->Size = NewExifSize;
647
648#ifdef SUPERDEBUG
Steve Block7a314da2012-01-06 19:10:19 +0000649 ALOGE("ReplaceThumbnail successful thumblen %d", ThumbLen);
Tyler Luu13714f22011-09-27 20:04:35 -0500650#endif
651 return TRUE;
652}
653
654//--------------------------------------------------------------------------
655// Replace or remove exif thumbnail
656//--------------------------------------------------------------------------
The Android Open Source Project34a25642009-03-03 19:30:03 -0800657int ReplaceThumbnail(const char * ThumbFileName)
658{
659 FILE * ThumbnailFile;
660 int ThumbLen, NewExifSize;
661 Section_t * ExifSection;
662 uchar * ThumbnailPointer;
663
664 if (ImageInfo.ThumbnailOffset == 0 || ImageInfo.ThumbnailAtEnd == FALSE){
Chih-Chung Changd6a02c32009-03-24 20:34:28 -0700665 if (ThumbFileName == NULL){
666 // Delete of nonexistent thumbnail (not even pointers present)
667 // No action, no error.
668 return FALSE;
669 }
670
The Android Open Source Project34a25642009-03-03 19:30:03 -0800671 // Adding or removing of thumbnail is not possible - that would require rearranging
672 // of the exif header, which is risky, and jhad doesn't know how to do.
The Android Open Source Project34a25642009-03-03 19:30:03 -0800673 fprintf(stderr,"Image contains no thumbnail to replace - add is not possible\n");
674#ifdef SUPERDEBUG
Steve Block7a314da2012-01-06 19:10:19 +0000675 ALOGE("Image contains no thumbnail to replace - add is not possible\n");
The Android Open Source Project34a25642009-03-03 19:30:03 -0800676#endif
677 return FALSE;
678 }
679
680 if (ThumbFileName){
681 ThumbnailFile = fopen(ThumbFileName,"rb");
682
683 if (ThumbnailFile == NULL){
684 //ErrFatal("Could not read thumbnail file");
Steve Block7a314da2012-01-06 19:10:19 +0000685 ALOGE("Could not read thumbnail file");
The Android Open Source Project34a25642009-03-03 19:30:03 -0800686 return FALSE;
687 }
688
689 // get length
690 fseek(ThumbnailFile, 0, SEEK_END);
691
692 ThumbLen = ftell(ThumbnailFile);
693 fseek(ThumbnailFile, 0, SEEK_SET);
694
695 if (ThumbLen + ImageInfo.ThumbnailOffset > 0x10000-20){
696 //ErrFatal("Thumbnail is too large to insert into exif header");
Steve Block7a314da2012-01-06 19:10:19 +0000697 ALOGE("Thumbnail is too large to insert into exif header");
The Android Open Source Project34a25642009-03-03 19:30:03 -0800698 return FALSE;
699 }
700 }else{
Chih-Chung Changd6a02c32009-03-24 20:34:28 -0700701 if (ImageInfo.ThumbnailSize == 0){
702 return FALSE;
703 }
704
The Android Open Source Project34a25642009-03-03 19:30:03 -0800705 ThumbLen = 0;
706 ThumbnailFile = NULL;
707 }
708
709 ExifSection = FindSection(M_EXIF);
710
711 NewExifSize = ImageInfo.ThumbnailOffset+8+ThumbLen;
712 ExifSection->Data = (uchar *)realloc(ExifSection->Data, NewExifSize);
713
714 ThumbnailPointer = ExifSection->Data+ImageInfo.ThumbnailOffset+8;
715
716 if (ThumbnailFile){
717 fread(ThumbnailPointer, ThumbLen, 1, ThumbnailFile);
718 fclose(ThumbnailFile);
719 }
720
721 ImageInfo.ThumbnailSize = ThumbLen;
722
723 Put32u(ExifSection->Data+ImageInfo.ThumbnailSizeOffset+8, ThumbLen);
724
725 ExifSection->Data[0] = (uchar)(NewExifSize >> 8);
726 ExifSection->Data[1] = (uchar)NewExifSize;
727 ExifSection->Size = NewExifSize;
728
729#ifdef SUPERDEBUG
Steve Block7a314da2012-01-06 19:10:19 +0000730 ALOGE("ReplaceThumbnail successful thumblen %d", ThumbLen);
The Android Open Source Project34a25642009-03-03 19:30:03 -0800731#endif
732 return TRUE;
733}
734
735
736//--------------------------------------------------------------------------
737// Discard everything but the exif and comment sections.
738//--------------------------------------------------------------------------
739void DiscardAllButExif(void)
740{
741 Section_t ExifKeeper;
742 Section_t CommentKeeper;
743 Section_t IptcKeeper;
Chih-Chung Changd6a02c32009-03-24 20:34:28 -0700744 Section_t XmpKeeper;
The Android Open Source Project34a25642009-03-03 19:30:03 -0800745 int a;
746
747 memset(&ExifKeeper, 0, sizeof(ExifKeeper));
748 memset(&CommentKeeper, 0, sizeof(CommentKeeper));
749 memset(&IptcKeeper, 0, sizeof(IptcKeeper));
Chih-Chung Changd6a02c32009-03-24 20:34:28 -0700750 memset(&XmpKeeper, 0, sizeof(IptcKeeper));
The Android Open Source Project34a25642009-03-03 19:30:03 -0800751
752 for (a=0;a<SectionsRead;a++){
753 if (Sections[a].Type == M_EXIF && ExifKeeper.Type == 0){
Chih-Chung Changd6a02c32009-03-24 20:34:28 -0700754 ExifKeeper = Sections[a];
755 }else if (Sections[a].Type == M_XMP && XmpKeeper.Type == 0){
756 XmpKeeper = Sections[a];
The Android Open Source Project34a25642009-03-03 19:30:03 -0800757 }else if (Sections[a].Type == M_COM && CommentKeeper.Type == 0){
758 CommentKeeper = Sections[a];
759 }else if (Sections[a].Type == M_IPTC && IptcKeeper.Type == 0){
760 IptcKeeper = Sections[a];
761 }else{
762 free(Sections[a].Data);
763 }
764 }
765 SectionsRead = 0;
766 if (ExifKeeper.Type){
767 CheckSectionsAllocated();
768 Sections[SectionsRead++] = ExifKeeper;
769 }
770 if (CommentKeeper.Type){
771 CheckSectionsAllocated();
772 Sections[SectionsRead++] = CommentKeeper;
773 }
774 if (IptcKeeper.Type){
775 CheckSectionsAllocated();
776 Sections[SectionsRead++] = IptcKeeper;
777 }
Chih-Chung Changd6a02c32009-03-24 20:34:28 -0700778
779 if (XmpKeeper.Type){
780 CheckSectionsAllocated();
781 Sections[SectionsRead++] = XmpKeeper;
782 }
The Android Open Source Project34a25642009-03-03 19:30:03 -0800783}
784
785//--------------------------------------------------------------------------
786// Write image data back to disk.
787//--------------------------------------------------------------------------
788int WriteJpegFile(const char * FileName)
789{
790 FILE * outfile;
791 int a;
792
793 if (!HaveAll){
Steve Block7a314da2012-01-06 19:10:19 +0000794 ALOGE("Can't write back - didn't read all");
The Android Open Source Project34a25642009-03-03 19:30:03 -0800795 return FALSE;
796 }
797
798 outfile = fopen(FileName,"wb");
799 if (outfile == NULL){
Steve Block7a314da2012-01-06 19:10:19 +0000800 ALOGE("Could not open file for write");
The Android Open Source Project34a25642009-03-03 19:30:03 -0800801 return FALSE;
802 }
803
804 // Initial static jpeg marker.
805 fputc(0xff,outfile);
806 fputc(0xd8,outfile);
807
808 if (Sections[0].Type != M_EXIF && Sections[0].Type != M_JFIF){
809 // The image must start with an exif or jfif marker. If we threw those away, create one.
810 static uchar JfifHead[18] = {
811 0xff, M_JFIF,
812 0x00, 0x10, 'J' , 'F' , 'I' , 'F' , 0x00, 0x01,
813 0x01, 0x01, 0x01, 0x2C, 0x01, 0x2C, 0x00, 0x00
814 };
815 fwrite(JfifHead, 18, 1, outfile);
816 }
817
Benny Wong37f509c2009-09-10 13:29:41 -0500818 int writeOk = FALSE;
819 int nWrite = 0;
The Android Open Source Project34a25642009-03-03 19:30:03 -0800820 // Write all the misc sections
821 for (a=0;a<SectionsRead-1;a++){
822 fputc(0xff,outfile);
Chih-Chung Changd6a02c32009-03-24 20:34:28 -0700823 fputc((unsigned char)Sections[a].Type, outfile);
Benny Wong37f509c2009-09-10 13:29:41 -0500824 nWrite = fwrite(Sections[a].Data, 1, Sections[a].Size, outfile);
825 writeOk = (nWrite == Sections[a].Size);
826 if(!writeOk){
Steve Block7a314da2012-01-06 19:10:19 +0000827 ALOGE("write section %d failed expect %d actual %d",a,Sections[a].Size,nWrite);
Benny Wong37f509c2009-09-10 13:29:41 -0500828 break;
829 }
The Android Open Source Project34a25642009-03-03 19:30:03 -0800830 }
831
832 // Write the remaining image data.
Benny Wong37f509c2009-09-10 13:29:41 -0500833 if (writeOk){
834 nWrite = fwrite(Sections[a].Data, 1,Sections[a].Size, outfile);
835 writeOk = (nWrite == Sections[a].Size);
836 if (!writeOk){
Steve Block7a314da2012-01-06 19:10:19 +0000837 ALOGE("write section %d failed expect %d actual %d",a,Sections[a].Size,nWrite);
Benny Wong37f509c2009-09-10 13:29:41 -0500838 }
839 }
The Android Open Source Project34a25642009-03-03 19:30:03 -0800840
841 fclose(outfile);
Benny Wong37f509c2009-09-10 13:29:41 -0500842 return writeOk;
The Android Open Source Project34a25642009-03-03 19:30:03 -0800843}
844
Tyler Luu24757b42011-08-24 20:53:34 -0500845//--------------------------------------------------------------------------
846// Write image to a buffer
847//--------------------------------------------------------------------------
848int WriteJpegToBuffer(unsigned char* buffer, unsigned int buffer_size)
849{
850 unsigned int pos = 0;
851 int a;
852
853 if (!buffer) {
854 return FALSE;
855 }
856
857 if (buffer_size < 1) {
858 return FALSE;
859 }
860
861 if (!HaveAll){
Steve Block7a314da2012-01-06 19:10:19 +0000862 ALOGE("Can't write back - didn't read all");
Tyler Luu24757b42011-08-24 20:53:34 -0500863 return FALSE;
864 }
865
866 // Initial static jpeg marker.
867 buffer[pos++] = 0xff;
868 buffer[pos++] = 0xd8;
869
870 if (Sections[0].Type != M_EXIF && Sections[0].Type != M_JFIF){
871 // The image must start with an exif or jfif marker. If we threw those away, create one.
872 static uchar JfifHead[18] = {
873 0xff, M_JFIF,
874 0x00, 0x10, 'J' , 'F' , 'I' , 'F' , 0x00, 0x01,
875 0x01, 0x01, 0x01, 0x2C, 0x01, 0x2C, 0x00, 0x00
876 };
877 memcpy(buffer+pos, JfifHead, 18);
878 pos+= 18;
879 }
880
881 int writeOk = FALSE;
882 int nWrite = 0;
883 // Write all the misc sections
884 for (a=0;a<SectionsRead-1;a++){
885 buffer[pos++] = 0xff;
886 buffer[pos++] = (unsigned char) Sections[a].Type;
887 if (pos+Sections[a].Size > buffer_size) {
888 writeOk = FALSE;
889 break;
890 }
891 memcpy(buffer+pos, Sections[a].Data, Sections[a].Size);
892 pos += Sections[a].Size;
893 writeOk = TRUE;
894 }
895
896 // Write the remaining image data.
897 if (writeOk){
898 if (pos+Sections[a].Size > buffer_size) {
899 writeOk = FALSE;
900 } else {
901 memcpy(buffer+pos, Sections[a].Data, Sections[a].Size);
902 pos += Sections[a].Size;
903 writeOk = TRUE;
904 }
905 }
906 return writeOk;
907}
908
The Android Open Source Project34a25642009-03-03 19:30:03 -0800909
910//--------------------------------------------------------------------------
911// Check if image has exif header.
912//--------------------------------------------------------------------------
913Section_t * FindSection(int SectionType)
914{
915 int a;
916
917 for (a=0;a<SectionsRead;a++){
918 if (Sections[a].Type == SectionType){
919 return &Sections[a];
920 }
921 }
922 // Could not be found.
923 return NULL;
924}
925
926//--------------------------------------------------------------------------
927// Remove a certain type of section.
928//--------------------------------------------------------------------------
929int RemoveSectionType(int SectionType)
930{
931 int a;
932 for (a=0;a<SectionsRead-1;a++){
933 if (Sections[a].Type == SectionType){
934 // Free up this section
935 free (Sections[a].Data);
936 // Move succeding sections back by one to close space in array.
937 memmove(Sections+a, Sections+a+1, sizeof(Section_t) * (SectionsRead-a));
938 SectionsRead -= 1;
939 return TRUE;
940 }
941 }
942 return FALSE;
943}
944
945//--------------------------------------------------------------------------
946// Remove sectons not part of image and not exif or comment sections.
947//--------------------------------------------------------------------------
948int RemoveUnknownSections(void)
949{
950 int a;
951 int Modified = FALSE;
952 for (a=0;a<SectionsRead-1;){
953 switch(Sections[a].Type){
954 case M_SOF0:
955 case M_SOF1:
956 case M_SOF2:
957 case M_SOF3:
958 case M_SOF5:
959 case M_SOF6:
960 case M_SOF7:
961 case M_SOF9:
962 case M_SOF10:
963 case M_SOF11:
964 case M_SOF13:
965 case M_SOF14:
966 case M_SOF15:
967 case M_SOI:
968 case M_EOI:
969 case M_SOS:
970 case M_JFIF:
971 case M_EXIF:
Chih-Chung Changd6a02c32009-03-24 20:34:28 -0700972 case M_XMP:
The Android Open Source Project34a25642009-03-03 19:30:03 -0800973 case M_COM:
974 case M_DQT:
975 case M_DHT:
976 case M_DRI:
977 case M_IPTC:
978 // keep.
979 a++;
980 break;
981 default:
982 // Unknown. Delete.
983 free (Sections[a].Data);
984 // Move succeding sections back by one to close space in array.
985 memmove(Sections+a, Sections+a+1, sizeof(Section_t) * (SectionsRead-a));
986 SectionsRead -= 1;
987 Modified = TRUE;
988 }
989 }
990 return Modified;
991}
992
993//--------------------------------------------------------------------------
994// Add a section (assume it doesn't already exist) - used for
995// adding comment sections and exif sections
996//--------------------------------------------------------------------------
997Section_t * CreateSection(int SectionType, unsigned char * Data, int Size)
998{
999 Section_t * NewSection;
1000 int a;
1001 int NewIndex;
1002 NewIndex = 2;
1003
1004 if (SectionType == M_EXIF) NewIndex = 0; // Exif alwas goes first!
1005
1006 // Insert it in third position - seems like a safe place to put
1007 // things like comments.
1008
1009 if (SectionsRead < NewIndex){
1010 // ErrFatal("Too few sections!");
Steve Block7a314da2012-01-06 19:10:19 +00001011 ALOGE("Too few sections!");
The Android Open Source Project34a25642009-03-03 19:30:03 -08001012 return FALSE;
1013 }
1014
1015 CheckSectionsAllocated();
1016 for (a=SectionsRead;a>NewIndex;a--){
1017 Sections[a] = Sections[a-1];
1018 }
1019 SectionsRead += 1;
1020
1021 NewSection = Sections+NewIndex;
1022
1023 NewSection->Type = SectionType;
1024 NewSection->Size = Size;
1025 NewSection->Data = Data;
1026
1027 return NewSection;
1028}
1029
1030
1031//--------------------------------------------------------------------------
1032// Initialisation.
1033//--------------------------------------------------------------------------
1034void ResetJpgfile(void)
1035{
1036 if (Sections == NULL){
1037 Sections = (Section_t *)malloc(sizeof(Section_t)*5);
1038 SectionsAllocated = 5;
1039 }
1040
1041 SectionsRead = 0;
1042 HaveAll = 0;
1043}