1 //--------------------------------------------------------------------------
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 //--------------------------------------------------------------------------
9 //#define LOG_NDEBUG 0
10 #define LOG_TAG "JHEAD"
11 #include <utils/Log.h>
12 #include "jhead.h"
13
14 // Storage for simplified info extracted from file.
15 ImageInfo_t ImageInfo;
16
17
18 static Section_t * Sections = NULL;
19 static int SectionsAllocated;
20 static int SectionsRead;
21 static int HaveAll;
22
23 // Define the line below to turn on poor man's debugging output
24 #undef SUPERDEBUG
25
26 #ifdef SUPERDEBUG
27 #define printf ALOGE
28 #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 //--------------------------------------------------------------------------
Get16m(const void * Short)36 static 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 //--------------------------------------------------------------------------
process_COM(const uchar * Data,int length)47 static void process_COM (const uchar * Data, int length)
48 {
49 int ch;
50 char Comment[MAX_COMMENT_SIZE+1];
51 int nch;
52 int a;
53
54 nch = 0;
55
56 if (length > MAX_COMMENT_SIZE) length = MAX_COMMENT_SIZE; // Truncate if it won't fit in our structure.
57
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);
77 ImageInfo.CommentWidchars = 0;
78 }
79
80
81 //--------------------------------------------------------------------------
82 // Process a SOFn marker. This is useful for the image dimensions
83 //--------------------------------------------------------------------------
process_SOFn(const uchar * Data,int marker)84 static 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 //--------------------------------------------------------------------------
CheckSectionsAllocated(void)111 void 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 //--------------------------------------------------------------------------
ReadJpegSections(FILE * infile,ReadMode_t ReadMode)129 int 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
147 for (a=0;a<=16;a++){
148 marker = fgetc(infile);
149 if (marker != 0xff) break;
150
151 if (a >= 16){
152 fprintf(stderr,"too many padding bytes\n");
153 return FALSE;
154 }
155 }
156
157
158 Sections[SectionsRead].Type = marker;
159 Sections[SectionsRead].Offset = ftell(infile);
160
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");
169 ALOGE("invalid marker");
170 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");
178 ALOGE("Could not allocate memory");
179 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?");
190 ALOGE("Premature end of file?");
191 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");
212 ALOGE("could not allocate data for entire image");
213 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");
219 ALOGE("could not read the rest of the image");
220 return FALSE;
221 }
222
223 CheckSectionsAllocated();
224 Sections[SectionsRead].Data = Data;
225 Sections[SectionsRead].Offset = cp;
226 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:
256 // 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 }
271 }
272 // Oterwise, discard this section.
273 free(Sections[--SectionsRead].Data);
274 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 //--------------------------------------------------------------------------
315 // Parse the marker buffer until SOS or EOI is seen;
316 //--------------------------------------------------------------------------
ReadJpegSectionsFromBuffer(unsigned char * buffer,unsigned int buffer_size,ReadMode_t ReadMode)317 int 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;
356 Sections[SectionsRead].Offset = pos;
357
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) {
365 ALOGE("invalid marker");
366 return FALSE;
367 }
368
369 Sections[SectionsRead].Size = itemlen;
370
371 Data = (uchar *)malloc(itemlen);
372 if (Data == NULL) {
373 ALOGE("Could not allocate memory");
374 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) {
383 ALOGE("Premature end of file?");
384 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) {
403 ALOGE("could not read the rest of the image");
404 return FALSE;
405 }
406 Data = (uchar *)malloc(size);
407 if (Data == NULL) {
408 ALOGE("%d: could not allocate data for entire image size: %d", __LINE__, size);
409 return FALSE;
410 }
411
412 memcpy(Data, buffer+pos, size);
413
414 CheckSectionsAllocated();
415 Sections[SectionsRead].Data = Data;
416 Sections[SectionsRead].Offset = pos;
417 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
425 ALOGE("No image in jpeg!\n");
426 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){
455 ALOGD("Image cotains XMP section, %d bytes long\n", itemlen);
456 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){
470 ALOGD("Image cotains IPTC section, %d bytes long\n", itemlen);
471 }
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){
497 ALOGD("Jpeg section marker 0x%02x size %d\n",marker, itemlen);
498 }
499 break;
500 }
501 }
502 return TRUE;
503 }
504
505 //--------------------------------------------------------------------------
506 // Discard read data.
507 //--------------------------------------------------------------------------
DiscardData(void)508 void 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 //--------------------------------------------------------------------------
ReadJpegFile(const char * FileName,ReadMode_t ReadMode)524 int 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) {
532 ALOGE("can't open '%s'", FileName);
533 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){
541 ALOGV("Cannot parse JPEG sections for file: %s", FileName);
542 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 //--------------------------------------------------------------------------
SaveThumbnail(char * ThumbFileName)557 int 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");
585 ALOGE("Could not write thumbnail file");
586 return FALSE;
587 }
588 }
589
590 //--------------------------------------------------------------------------
591 // Replace or remove exif thumbnail
592 //--------------------------------------------------------------------------
ReplaceThumbnailFromBuffer(const char * Thumb,int ThumbLen)593 int 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
610 ALOGE("Image contains no thumbnail to replace - add is not possible\n");
611 #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");
618 ALOGE("Thumbnail is too large to insert into exif header");
619 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
649 ALOGE("ReplaceThumbnail successful thumblen %d", ThumbLen);
650 #endif
651 return TRUE;
652 }
653
654 //--------------------------------------------------------------------------
655 // Replace or remove exif thumbnail
656 //--------------------------------------------------------------------------
ReplaceThumbnail(const char * ThumbFileName)657 int 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){
665 if (ThumbFileName == NULL){
666 // Delete of nonexistent thumbnail (not even pointers present)
667 // No action, no error.
668 return FALSE;
669 }
670
671 // 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.
673 fprintf(stderr,"Image contains no thumbnail to replace - add is not possible\n");
674 #ifdef SUPERDEBUG
675 ALOGE("Image contains no thumbnail to replace - add is not possible\n");
676 #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");
685 ALOGE("Could not read thumbnail file");
686 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");
697 ALOGE("Thumbnail is too large to insert into exif header");
698 return FALSE;
699 }
700 }else{
701 if (ImageInfo.ThumbnailSize == 0){
702 return FALSE;
703 }
704
705 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
730 ALOGE("ReplaceThumbnail successful thumblen %d", ThumbLen);
731 #endif
732 return TRUE;
733 }
734
735
736 //--------------------------------------------------------------------------
737 // Discard everything but the exif and comment sections.
738 //--------------------------------------------------------------------------
DiscardAllButExif(void)739 void DiscardAllButExif(void)
740 {
741 Section_t ExifKeeper;
742 Section_t CommentKeeper;
743 Section_t IptcKeeper;
744 Section_t XmpKeeper;
745 int a;
746
747 memset(&ExifKeeper, 0, sizeof(ExifKeeper));
748 memset(&CommentKeeper, 0, sizeof(CommentKeeper));
749 memset(&IptcKeeper, 0, sizeof(IptcKeeper));
750 memset(&XmpKeeper, 0, sizeof(IptcKeeper));
751
752 for (a=0;a<SectionsRead;a++){
753 if (Sections[a].Type == M_EXIF && ExifKeeper.Type == 0){
754 ExifKeeper = Sections[a];
755 }else if (Sections[a].Type == M_XMP && XmpKeeper.Type == 0){
756 XmpKeeper = Sections[a];
757 }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 }
778
779 if (XmpKeeper.Type){
780 CheckSectionsAllocated();
781 Sections[SectionsRead++] = XmpKeeper;
782 }
783 }
784
785 //--------------------------------------------------------------------------
786 // Write image data back to disk.
787 //--------------------------------------------------------------------------
WriteJpegFile(const char * FileName)788 int WriteJpegFile(const char * FileName)
789 {
790 FILE * outfile;
791 int a;
792
793 if (!HaveAll){
794 ALOGE("Can't write back - didn't read all");
795 return FALSE;
796 }
797
798 outfile = fopen(FileName,"wb");
799 if (outfile == NULL){
800 ALOGE("Could not open file for write");
801 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
818 int writeOk = FALSE;
819 int nWrite = 0;
820 // Write all the misc sections
821 for (a=0;a<SectionsRead-1;a++){
822 fputc(0xff,outfile);
823 fputc((unsigned char)Sections[a].Type, outfile);
824 nWrite = fwrite(Sections[a].Data, 1, Sections[a].Size, outfile);
825 writeOk = (nWrite == Sections[a].Size);
826 if(!writeOk){
827 ALOGE("write section %d failed expect %d actual %d",a,Sections[a].Size,nWrite);
828 break;
829 }
830 }
831
832 // Write the remaining image data.
833 if (writeOk){
834 nWrite = fwrite(Sections[a].Data, 1,Sections[a].Size, outfile);
835 writeOk = (nWrite == Sections[a].Size);
836 if (!writeOk){
837 ALOGE("write section %d failed expect %d actual %d",a,Sections[a].Size,nWrite);
838 }
839 }
840
841 fclose(outfile);
842 return writeOk;
843 }
844
845 //--------------------------------------------------------------------------
846 // Write image to a buffer
847 //--------------------------------------------------------------------------
WriteJpegToBuffer(unsigned char * buffer,unsigned int buffer_size)848 int 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){
862 ALOGE("Can't write back - didn't read all");
863 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
909
910 //--------------------------------------------------------------------------
911 // Check if image has exif header.
912 //--------------------------------------------------------------------------
FindSection(int SectionType)913 Section_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 //--------------------------------------------------------------------------
RemoveSectionType(int SectionType)929 int 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 //--------------------------------------------------------------------------
RemoveUnknownSections(void)948 int 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:
972 case M_XMP:
973 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 //--------------------------------------------------------------------------
CreateSection(int SectionType,unsigned char * Data,int Size)997 Section_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!");
1011 ALOGE("Too few sections!");
1012 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 //--------------------------------------------------------------------------
ResetJpgfile(void)1034 void 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 }
1044