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 //-------------------------------------------------------------------------- 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 //-------------------------------------------------------------------------- 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 //-------------------------------------------------------------------------- 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 //-------------------------------------------------------------------------- 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 //-------------------------------------------------------------------------- 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 //-------------------------------------------------------------------------- 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 //-------------------------------------------------------------------------- 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 //-------------------------------------------------------------------------- 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 //-------------------------------------------------------------------------- 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 //-------------------------------------------------------------------------- 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 //-------------------------------------------------------------------------- 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 //-------------------------------------------------------------------------- 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 //-------------------------------------------------------------------------- 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 //-------------------------------------------------------------------------- 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 //-------------------------------------------------------------------------- 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 //-------------------------------------------------------------------------- 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 //-------------------------------------------------------------------------- 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 //-------------------------------------------------------------------------- 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 //-------------------------------------------------------------------------- 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