github.com/cellofellow/gopkg@v0.0.0-20140722061823-eec0544a62ad/image/webp/libwebp/examples/jpegdec.c (about) 1 // Copyright 2012 Google Inc. All Rights Reserved. 2 // 3 // Use of this source code is governed by a BSD-style license 4 // that can be found in the COPYING file in the root of the source 5 // tree. An additional intellectual property rights grant can be found 6 // in the file PATENTS. All contributing project authors may 7 // be found in the AUTHORS file in the root of the source tree. 8 // ----------------------------------------------------------------------------- 9 // 10 // JPEG decode. 11 12 #include "./jpegdec.h" 13 14 #ifdef HAVE_CONFIG_H 15 #include "config.h" 16 #endif 17 18 #include <stdio.h> 19 20 #ifdef WEBP_HAVE_JPEG 21 #include <jpeglib.h> 22 #include <setjmp.h> 23 #include <stdlib.h> 24 #include <string.h> 25 26 #include "webp/encode.h" 27 #include "./metadata.h" 28 29 // ----------------------------------------------------------------------------- 30 // Metadata processing 31 32 #ifndef JPEG_APP1 33 # define JPEG_APP1 (JPEG_APP0 + 1) 34 #endif 35 #ifndef JPEG_APP2 36 # define JPEG_APP2 (JPEG_APP0 + 2) 37 #endif 38 39 typedef struct { 40 const uint8_t* data; 41 size_t data_length; 42 int seq; // this segment's sequence number [1, 255] for use in reassembly. 43 } ICCPSegment; 44 45 static void SaveMetadataMarkers(j_decompress_ptr dinfo) { 46 const unsigned int max_marker_length = 0xffff; 47 jpeg_save_markers(dinfo, JPEG_APP1, max_marker_length); // Exif/XMP 48 jpeg_save_markers(dinfo, JPEG_APP2, max_marker_length); // ICC profile 49 } 50 51 static int CompareICCPSegments(const void* a, const void* b) { 52 const ICCPSegment* s1 = (const ICCPSegment*)a; 53 const ICCPSegment* s2 = (const ICCPSegment*)b; 54 return s1->seq - s2->seq; 55 } 56 57 // Extract ICC profile segments from the marker list in 'dinfo', reassembling 58 // and storing them in 'iccp'. 59 // Returns true on success and false for memory errors and corrupt profiles. 60 static int StoreICCP(j_decompress_ptr dinfo, MetadataPayload* const iccp) { 61 // ICC.1:2010-12 (4.3.0.0) Annex B.4 Embedding ICC Profiles in JPEG files 62 static const char kICCPSignature[] = "ICC_PROFILE"; 63 static const size_t kICCPSignatureLength = 12; // signature includes '\0' 64 static const size_t kICCPSkipLength = 14; // signature + seq & count 65 int expected_count = 0; 66 int actual_count = 0; 67 int seq_max = 0; 68 size_t total_size = 0; 69 ICCPSegment iccp_segments[255]; 70 jpeg_saved_marker_ptr marker; 71 72 memset(iccp_segments, 0, sizeof(iccp_segments)); 73 for (marker = dinfo->marker_list; marker != NULL; marker = marker->next) { 74 if (marker->marker == JPEG_APP2 && 75 marker->data_length > kICCPSkipLength && 76 !memcmp(marker->data, kICCPSignature, kICCPSignatureLength)) { 77 // ICC_PROFILE\0<seq><count>; 'seq' starts at 1. 78 const int seq = marker->data[kICCPSignatureLength]; 79 const int count = marker->data[kICCPSignatureLength + 1]; 80 const size_t segment_size = marker->data_length - kICCPSkipLength; 81 ICCPSegment* segment; 82 83 if (segment_size == 0 || count == 0 || seq == 0) { 84 fprintf(stderr, "[ICCP] size (%d) / count (%d) / sequence number (%d)" 85 " cannot be 0!\n", 86 (int)segment_size, seq, count); 87 return 0; 88 } 89 90 if (expected_count == 0) { 91 expected_count = count; 92 } else if (expected_count != count) { 93 fprintf(stderr, "[ICCP] Inconsistent segment count (%d / %d)!\n", 94 expected_count, count); 95 return 0; 96 } 97 98 segment = iccp_segments + seq - 1; 99 if (segment->data_length != 0) { 100 fprintf(stderr, "[ICCP] Duplicate segment number (%d)!\n" , seq); 101 return 0; 102 } 103 104 segment->data = marker->data + kICCPSkipLength; 105 segment->data_length = segment_size; 106 segment->seq = seq; 107 total_size += segment_size; 108 if (seq > seq_max) seq_max = seq; 109 ++actual_count; 110 } 111 } 112 113 if (actual_count == 0) return 1; 114 if (seq_max != actual_count) { 115 fprintf(stderr, "[ICCP] Discontinuous segments, expected: %d actual: %d!\n", 116 actual_count, seq_max); 117 return 0; 118 } 119 if (expected_count != actual_count) { 120 fprintf(stderr, "[ICCP] Segment count: %d does not match expected: %d!\n", 121 actual_count, expected_count); 122 return 0; 123 } 124 125 // The segments may appear out of order in the file, sort them based on 126 // sequence number before assembling the payload. 127 qsort(iccp_segments, actual_count, sizeof(*iccp_segments), 128 CompareICCPSegments); 129 130 iccp->bytes = (uint8_t*)malloc(total_size); 131 if (iccp->bytes == NULL) return 0; 132 iccp->size = total_size; 133 134 { 135 int i; 136 size_t offset = 0; 137 for (i = 0; i < seq_max; ++i) { 138 memcpy(iccp->bytes + offset, 139 iccp_segments[i].data, iccp_segments[i].data_length); 140 offset += iccp_segments[i].data_length; 141 } 142 } 143 return 1; 144 } 145 146 // Returns true on success and false for memory errors and corrupt profiles. 147 // The caller must use MetadataFree() on 'metadata' in all cases. 148 static int ExtractMetadataFromJPEG(j_decompress_ptr dinfo, 149 Metadata* const metadata) { 150 static const struct { 151 int marker; 152 const char* signature; 153 size_t signature_length; 154 size_t storage_offset; 155 } kJPEGMetadataMap[] = { 156 // Exif 2.2 Section 4.7.2 Interoperability Structure of APP1 ... 157 { JPEG_APP1, "Exif\0", 6, METADATA_OFFSET(exif) }, 158 // XMP Specification Part 3 Section 3 Embedding XMP Metadata ... #JPEG 159 // TODO(jzern) Add support for 'ExtendedXMP' 160 { JPEG_APP1, "http://ns.adobe.com/xap/1.0/", 29, METADATA_OFFSET(xmp) }, 161 { 0, NULL, 0, 0 }, 162 }; 163 jpeg_saved_marker_ptr marker; 164 // Treat ICC profiles separately as they may be segmented and out of order. 165 if (!StoreICCP(dinfo, &metadata->iccp)) return 0; 166 167 for (marker = dinfo->marker_list; marker != NULL; marker = marker->next) { 168 int i; 169 for (i = 0; kJPEGMetadataMap[i].marker != 0; ++i) { 170 if (marker->marker == kJPEGMetadataMap[i].marker && 171 marker->data_length > kJPEGMetadataMap[i].signature_length && 172 !memcmp(marker->data, kJPEGMetadataMap[i].signature, 173 kJPEGMetadataMap[i].signature_length)) { 174 MetadataPayload* const payload = 175 (MetadataPayload*)((uint8_t*)metadata + 176 kJPEGMetadataMap[i].storage_offset); 177 178 if (payload->bytes == NULL) { 179 const char* marker_data = (const char*)marker->data + 180 kJPEGMetadataMap[i].signature_length; 181 const size_t marker_data_length = 182 marker->data_length - kJPEGMetadataMap[i].signature_length; 183 if (!MetadataCopy(marker_data, marker_data_length, payload)) return 0; 184 } else { 185 fprintf(stderr, "Ignoring additional '%s' marker\n", 186 kJPEGMetadataMap[i].signature); 187 } 188 } 189 } 190 } 191 return 1; 192 } 193 194 #undef JPEG_APP1 195 #undef JPEG_APP2 196 197 // ----------------------------------------------------------------------------- 198 // JPEG decoding 199 200 struct my_error_mgr { 201 struct jpeg_error_mgr pub; 202 jmp_buf setjmp_buffer; 203 }; 204 205 static void my_error_exit(j_common_ptr dinfo) { 206 struct my_error_mgr* myerr = (struct my_error_mgr*)dinfo->err; 207 dinfo->err->output_message(dinfo); 208 longjmp(myerr->setjmp_buffer, 1); 209 } 210 211 int ReadJPEG(FILE* in_file, WebPPicture* const pic, Metadata* const metadata) { 212 int ok = 0; 213 int stride, width, height; 214 struct jpeg_decompress_struct dinfo; 215 struct my_error_mgr jerr; 216 uint8_t* rgb = NULL; 217 JSAMPROW buffer[1]; 218 219 dinfo.err = jpeg_std_error(&jerr.pub); 220 jerr.pub.error_exit = my_error_exit; 221 222 if (setjmp(jerr.setjmp_buffer)) { 223 Error: 224 MetadataFree(metadata); 225 jpeg_destroy_decompress(&dinfo); 226 goto End; 227 } 228 229 jpeg_create_decompress(&dinfo); 230 jpeg_stdio_src(&dinfo, in_file); 231 if (metadata != NULL) SaveMetadataMarkers(&dinfo); 232 jpeg_read_header(&dinfo, TRUE); 233 234 dinfo.out_color_space = JCS_RGB; 235 dinfo.do_fancy_upsampling = TRUE; 236 237 jpeg_start_decompress(&dinfo); 238 239 if (dinfo.output_components != 3) { 240 goto Error; 241 } 242 243 width = dinfo.output_width; 244 height = dinfo.output_height; 245 stride = dinfo.output_width * dinfo.output_components * sizeof(*rgb); 246 247 rgb = (uint8_t*)malloc(stride * height); 248 if (rgb == NULL) { 249 goto End; 250 } 251 buffer[0] = (JSAMPLE*)rgb; 252 253 while (dinfo.output_scanline < dinfo.output_height) { 254 if (jpeg_read_scanlines(&dinfo, buffer, 1) != 1) { 255 goto End; 256 } 257 buffer[0] += stride; 258 } 259 260 if (metadata != NULL) { 261 ok = ExtractMetadataFromJPEG(&dinfo, metadata); 262 if (!ok) { 263 fprintf(stderr, "Error extracting JPEG metadata!\n"); 264 goto Error; 265 } 266 } 267 268 jpeg_finish_decompress(&dinfo); 269 jpeg_destroy_decompress(&dinfo); 270 271 // WebP conversion. 272 pic->width = width; 273 pic->height = height; 274 ok = WebPPictureImportRGB(pic, rgb, stride); 275 if (!ok) goto Error; 276 277 End: 278 free(rgb); 279 return ok; 280 } 281 #else // !WEBP_HAVE_JPEG 282 int ReadJPEG(FILE* in_file, struct WebPPicture* const pic, 283 struct Metadata* const metadata) { 284 (void)in_file; 285 (void)pic; 286 (void)metadata; 287 fprintf(stderr, "JPEG support not compiled. Please install the libjpeg " 288 "development package before building.\n"); 289 return 0; 290 } 291 #endif // WEBP_HAVE_JPEG 292 293 // -----------------------------------------------------------------------------