|
|
| version 1.169, 2020/11/30 19:50:47 | version 1.174, 2020/12/03 22:48:09 |
|---|---|
| Line 228 public: | Line 228 public: |
| } | } |
| } exif_gps_tag_value2name; | } exif_gps_tag_value2name; |
| ///*********************************************** support functions | ///*********************************************** support functions |
| class Measure_reader { | class Measure_reader { |
| public: | public: |
| virtual size_t read(const char* &buf, size_t limit)=0; | virtual size_t read(const char* &buf, size_t limit)=0; |
| virtual void seek(long value, int whence)=0; | virtual void seek(uint64_t value)=0; |
| virtual long tell()=0; | virtual uint64_t tell()=0; |
| virtual uint64_t length()=0; | |
| }; | }; |
| class Measure_file_reader: public Measure_reader { | class Measure_file_reader: public Measure_reader { |
| Line 257 public: | Line 259 public: |
| return read_size; | return read_size; |
| } | } |
| override void seek(long value, int whence) { | override void seek(uint64_t value) { |
| if(lseek(f, value, whence)<0) | if(pa_lseek(f, value, SEEK_SET)<0) |
| throw Exception(IMAGE_FORMAT, &file_name, "seek(value=%ld, whence=%d) failed: %s (%d)", value, whence, strerror(errno), errno); | throw Exception(IMAGE_FORMAT, &file_name, "seek to %.15g failed: %s (%d)", (double)value, strerror(errno), errno); |
| } | } |
| override long tell() { return lseek(f, 0, SEEK_CUR); } | override uint64_t tell() { return pa_lseek(f, 0, SEEK_CUR); } |
| override uint64_t length() { return pa_lseek(f, 0, SEEK_END); } | |
| }; | }; |
| Line 285 public: | Line 289 public: |
| return to_read; | return to_read; |
| } | } |
| override void seek(long value, int whence) { | override void seek(uint64_t value) { |
| size_t new_offset; | if(value>(uint64_t)size) |
| switch(whence) { | throw Exception(IMAGE_FORMAT, &file_name, "seek to %.15g failed: out of buffer (%.15g)", value, size); |
| case SEEK_CUR: new_offset=offset+value; break; | offset=(size_t)value; |
| case SEEK_SET: new_offset=(size_t)value; break; | |
| case SEEK_END: new_offset=size; break; | |
| default: | |
| throw Exception(0, 0, "whence #%d not supported", 0, whence); | |
| break; // never | |
| } | |
| if((ssize_t)new_offset<0 || new_offset>size) | |
| throw Exception(IMAGE_FORMAT, &file_name, "seek(value=%l, whence=%d) failed: out of buffer, new_offset>size (%l>%l) or new_offset<0", | |
| value, whence, new_offset, size); | |
| offset=new_offset; | |
| } | } |
| override long tell() { return offset; } | override uint64_t tell() { return offset; } |
| override uint64_t length() { return size; } | |
| }; | }; |
| Line 332 inline uint endian_to_uint(bool is_big, | Line 327 inline uint endian_to_uint(bool is_big, |
| return is_big ? x_endian_to_uint(b[3], b[2], b[1], b[0]) : x_endian_to_uint(b[0], b[1], b[2], b[3]); | return is_big ? x_endian_to_uint(b[3], b[2], b[1], b[0]) : x_endian_to_uint(b[0], b[1], b[2], b[3]); |
| } | } |
| ///*********************************************** JPEG | ///*********************************************** JPEG |
| struct JPG_Segment_head { | struct JPG_Segment_head { |
| uchar marker; | uchar marker; |
| uchar code; | uchar code; |
| Line 487 static Value* parse_IFD_entry_value(bool | Line 484 static Value* parse_IFD_entry_value(bool |
| if(value_size<=4) | if(value_size<=4) |
| result=parse_IFD_entry_formatted_value(is_big, format, component_size, components_count, entry.value_or_offset_to_it); | result=parse_IFD_entry_formatted_value(is_big, format, component_size, components_count, entry.value_or_offset_to_it); |
| else { | else { |
| long remembered=reader.tell(); | uint64_t remembered=reader.tell(); |
| { | { |
| reader.seek(tiff_base+endian_to_uint(is_big, entry.value_or_offset_to_it), SEEK_SET); | reader.seek(tiff_base+endian_to_uint(is_big, entry.value_or_offset_to_it)); |
| const char* value; | const char* value; |
| if(reader.read(value, value_size)<value_size) | if(reader.read(value, value_size)<value_size) |
| return 0; | return 0; |
| result=parse_IFD_entry_formatted_value(is_big, format, component_size, components_count, (const uchar*)value); | result=parse_IFD_entry_formatted_value(is_big, format, component_size, components_count, (const uchar*)value); |
| } | } |
| reader.seek(remembered, SEEK_SET); | reader.seek(remembered); |
| } | } |
| return result; | return result; |
| Line 507 static void parse_IFD_entry(HashStringVa | Line 504 static void parse_IFD_entry(HashStringVa |
| ushort tag=endian_to_ushort(is_big, entry.tag); | ushort tag=endian_to_ushort(is_big, entry.tag); |
| if(tag==JPG_IFD_TAG_EXIF_OFFSET || tag==JPG_IFD_TAG_EXIF_GPS_OFFSET){ | if(tag==JPG_IFD_TAG_EXIF_OFFSET || tag==JPG_IFD_TAG_EXIF_GPS_OFFSET){ |
| long remembered=reader.tell(); | uint64_t remembered=reader.tell(); |
| { | { |
| reader.seek(tiff_base+endian_to_uint(is_big, entry.value_or_offset_to_it), SEEK_SET); | reader.seek(tiff_base+endian_to_uint(is_big, entry.value_or_offset_to_it)); |
| parse_IFD(hash, is_big, reader, tiff_base, (tag==JPG_IFD_TAG_EXIF_GPS_OFFSET)?true:gps); | parse_IFD(hash, is_big, reader, tiff_base, (tag==JPG_IFD_TAG_EXIF_GPS_OFFSET)?true:gps); |
| } | } |
| reader.seek(remembered, SEEK_SET); | reader.seek(remembered); |
| return; | return; |
| } | } |
| Line 551 static Value* parse_exif(Measure_reader& | Line 548 static Value* parse_exif(Measure_reader& |
| bool is_big=head->byte_align_identifier[0]=='M'; // [M]otorola vs [I]ntel | bool is_big=head->byte_align_identifier[0]=='M'; // [M]otorola vs [I]ntel |
| uint first_IFD_offset=endian_to_uint(is_big, head->first_IFD_offset); | uint first_IFD_offset=endian_to_uint(is_big, head->first_IFD_offset); |
| reader.seek(tiff_base+first_IFD_offset, SEEK_SET); | reader.seek(tiff_base+first_IFD_offset); |
| VHash* vhash=new VHash; | VHash* vhash=new VHash; |
| Line 643 static void measure_jpeg(const String& o | Line 640 static void measure_jpeg(const String& o |
| return; | return; |
| }; | }; |
| reader.seek(segment_base + segment_length, SEEK_SET); | reader.seek(segment_base + segment_length); |
| } | } |
| throw Exception(IMAGE_FORMAT, &origin_string, "broken JPEG file - size frame not found"); | throw Exception(IMAGE_FORMAT, &origin_string, "broken JPEG file - size frame not found"); |
| Line 696 static void measure_tiff(const String& o | Line 693 static void measure_tiff(const String& o |
| if(endian_to_ushort(is_big, head->signature) != 42) | if(endian_to_ushort(is_big, head->signature) != 42) |
| throw Exception(IMAGE_FORMAT, &origin_string, "not TIFF file - wrong signature"); | throw Exception(IMAGE_FORMAT, &origin_string, "not TIFF file - wrong signature"); |
| reader.seek(endian_to_uint(is_big, head->first_IFD_offset), SEEK_SET); | reader.seek(endian_to_uint(is_big, head->first_IFD_offset)); |
| if(!parse_tiff_IFD(is_big, reader, info)) | if(!parse_tiff_IFD(is_big, reader, info)) |
| throw Exception(IMAGE_FORMAT, &origin_string, "broken TIFF file - size field entry not found"); | throw Exception(IMAGE_FORMAT, &origin_string, "broken TIFF file - size field entry not found"); |
| } | } |
| Line 782 static void measure_bmp(const String& or | Line 779 static void measure_bmp(const String& or |
| if(strncmp(head->signature, "BM", 2)!=0) | if(strncmp(head->signature, "BM", 2)!=0) |
| throw Exception(IMAGE_FORMAT, &origin_string, "not BMP file - wrong signature"); | throw Exception(IMAGE_FORMAT, &origin_string, "not BMP file - wrong signature"); |
| reader.seek(0, SEEK_END); | if((uint)reader.length() != endian_to_uint(false, head->file_size)) |
| if((uint)reader.tell() != endian_to_uint(false, head->file_size)) | |
| throw Exception(IMAGE_FORMAT, &origin_string, "not BMP file - length header and file size do not match"); | throw Exception(IMAGE_FORMAT, &origin_string, "not BMP file - length header and file size do not match"); |
| width=endian_to_ushort(false, head->width); | width=endian_to_ushort(false, head->width); |
| Line 864 static void measure_webp(const String& o | Line 860 static void measure_webp(const String& o |
| } else throw Exception(IMAGE_FORMAT, &origin_string, "broken WEBP file - invalid chunk signature"); | } else throw Exception(IMAGE_FORMAT, &origin_string, "broken WEBP file - invalid chunk signature"); |
| } | } |
| ///*********************************************** MP4 | |
| struct MP4_Header { | |
| uchar size[4]; | |
| char signature[4]; // 'ftyp' in first chunk | |
| }; | |
| struct MP4_ExtSize { | |
| uchar high[4]; | |
| uchar low[4]; | |
| }; | |
| struct MP4_Tkhd { | |
| uchar width[4]; | |
| uchar height[4]; | |
| }; | |
| static bool measure_mp4(const String& origin_string, Measure_reader& reader, ushort& width, ushort& height, uint64_t anext, const char* lastTkhd=NULL) { | |
| for(bool first=anext==0;;){ | |
| const char* buf; | |
| uint64_t next=reader.tell(); | |
| if(reader.read(buf, sizeof(MP4_Header))<sizeof(MP4_Header)) | |
| throw Exception(IMAGE_FORMAT, &origin_string, first ? "not MP4 file - too small" : "broken MP4 file - truncated chunk header"); | |
| MP4_Header *head=(MP4_Header *)buf; | |
| uint64_t size=endian_to_uint(true, head->size); | |
| if(size==1){ | |
| if(reader.read(buf, sizeof(MP4_ExtSize))<sizeof(MP4_ExtSize)) | |
| throw Exception(IMAGE_FORMAT, &origin_string, "broken MP4 file - truncated chunk extended size header"); | |
| MP4_ExtSize *ext_size=(MP4_ExtSize *)buf; | |
| size=((uint64_t)endian_to_uint(true, ext_size->high) << 32) + endian_to_uint(true, ext_size->low); | |
| } | |
| next+=size; | |
| if(first){ | |
| if(strncmp(head->signature, "ftyp", 4)!=0) | |
| throw Exception(IMAGE_FORMAT, &origin_string, "not MP4 file - wrong signature"); | |
| first=false; | |
| anext=reader.length(); // to avoid reading beyond EOF | |
| } else if(strncmp(head->signature, "moov", 4)==0 || strncmp(head->signature, "mdia", 4)==0 || strncmp(head->signature, "trak", 4)==0) { | |
| if(measure_mp4(origin_string, reader, width, height, next, lastTkhd)) | |
| return true; | |
| } else if(strncmp(head->signature, "tkhd", 4)==0) { | |
| if(size>8){ | |
| reader.seek(next-8); | |
| if(reader.read(lastTkhd, sizeof(MP4_Tkhd))<sizeof(MP4_Tkhd)) | |
| throw Exception(IMAGE_FORMAT, &origin_string, "broken MP4 file - bad tkhd chunk"); | |
| } | |
| } else if (strncmp(head->signature, "hdlr", 4)==0) { | |
| if(size>12){ | |
| const char* hdlr; | |
| if(reader.read(hdlr, 12)<12) | |
| throw Exception(IMAGE_FORMAT, &origin_string, "broken MP4 file - bad hdlr chunk"); | |
| if(lastTkhd && strncmp(hdlr+8, "vide", 4)==0) { | |
| MP4_Tkhd *tkhd=(MP4_Tkhd *)lastTkhd; | |
| width=endian_to_ushort(true, tkhd->width); | |
| height=endian_to_ushort(true, tkhd->height); | |
| return true; | |
| } | |
| } | |
| } | |
| if(anext && next>=anext) | |
| break; | |
| reader.seek(next); | |
| } | |
| return false; | |
| } | |
| static void measure_mp4(const String& origin_string, Measure_reader& reader, ushort& width, ushort& height) { | |
| if(!measure_mp4(origin_string, reader, width, height, 0)) | |
| throw Exception(IMAGE_FORMAT, &origin_string, "unsupported MP4 file - size not found"); | |
| } | |
| ///*********************************************** measure center | ///*********************************************** measure center |
| static void measure(const String& file_name, Measure_reader& reader, Measure_info &info) { | static void measure(const String& file_name, Measure_reader& reader, Measure_info &info) { |
| Line 882 static void measure(const String& file_n | Line 954 static void measure(const String& file_n |
| measure_webp(file_name, reader, info.width, info.height); | measure_webp(file_name, reader, info.width, info.height); |
| else if(strcasecmp(cext, "TIF")==0 || strcasecmp(cext, "TIFF")==0) | else if(strcasecmp(cext, "TIF")==0 || strcasecmp(cext, "TIFF")==0) |
| measure_tiff(file_name, reader, info); | measure_tiff(file_name, reader, info); |
| else if(strcasecmp(cext, "MP4")==0 || strcasecmp(cext, "MOV")==0) | |
| measure_mp4(file_name, reader, info.width, info.height); | |
| else | else |
| throw Exception(IMAGE_FORMAT, &file_name, "unhandled image file name extension '%s'", cext); | throw Exception(IMAGE_FORMAT, &file_name, "unhandled image file name extension '%s'", cext); |
| } else | } else |