--- parser3/src/classes/image.C 2020/12/01 18:53:01 1.172 +++ parser3/src/classes/image.C 2020/12/23 15:01:13 1.181 @@ -1,7 +1,7 @@ /** @file Parser: @b image parser class. - Copyright (c) 2001-2017 Art. Lebedev Studio (http://www.artlebedev.com) + Copyright (c) 2001-2020 Art. Lebedev Studio (http://www.artlebedev.com) Author: Alexandr Petrosian (http://paf.design.ru) */ @@ -26,7 +26,7 @@ #include "pa_table.h" #include "pa_charsets.h" -volatile const char * IDENT_IMAGE_C="$Id: image.C,v 1.172 2020/12/01 18:53:01 moko Exp $"; +volatile const char * IDENT_IMAGE_C="$Id: image.C,v 1.181 2020/12/23 15:01:13 moko Exp $"; // defines @@ -234,8 +234,9 @@ public: class Measure_reader { public: virtual size_t read(const char* &buf, size_t limit)=0; - virtual void seek(off_t value, int whence)=0; - virtual off_t tell()=0; + virtual void seek(uint64_t value)=0; + virtual uint64_t tell()=0; + virtual uint64_t length()=0; }; class Measure_file_reader: public Measure_reader { @@ -258,12 +259,14 @@ public: return read_size; } - override void seek(off_t value, int whence) { - if(lseek(f, value, whence)<0) - throw Exception(IMAGE_FORMAT, &file_name, "seek(value=%ld, whence=%d) failed: %s (%d)", value, whence, strerror(errno), errno); + override void seek(uint64_t value) { + if(pa_lseek(f, value, SEEK_SET)<0) + throw Exception(IMAGE_FORMAT, &file_name, "seek to %.15g failed: %s (%d)", (double)value, strerror(errno), errno); } - override off_t 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); } }; @@ -286,24 +289,15 @@ public: return to_read; } - override void seek(off_t value, int whence) { - size_t new_offset; - switch(whence) { - case SEEK_CUR: new_offset=offset+value; break; - 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 void seek(uint64_t value) { + if(value>(uint64_t)size) + throw Exception(IMAGE_FORMAT, &file_name, "seek to %.15g failed: out of buffer (%.15g)", value, size); + offset=(size_t)value; } - override off_t tell() { return offset; } + override uint64_t tell() { return offset; } + + override uint64_t length() { return size; } }; @@ -314,6 +308,7 @@ struct Measure_info { Value** exif; Value** xmp; Charset* xmp_charset; + bool video; }; @@ -453,7 +448,7 @@ static Value* parse_IFD_entry_formatted_ return result; } -static Value* parse_IFD_entry_value(bool is_big, Measure_reader& reader, long tiff_base, JPG_Exif_IFD_entry& entry) { +static Value* parse_IFD_entry_value(bool is_big, Measure_reader& reader, uint64_t tiff_base, JPG_Exif_IFD_entry& entry) { size_t format2component_size[]={ 0, // undefined 1, // unsigned byte @@ -490,32 +485,32 @@ static Value* parse_IFD_entry_value(bool if(value_size<=4) result=parse_IFD_entry_formatted_value(is_big, format, component_size, components_count, entry.value_or_offset_to_it); else { - off_t 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; if(reader.read(value, value_size)byte_align_identifier[0]=='M'; // [M]otorola vs [I]ntel 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; @@ -592,7 +587,7 @@ static void measure_jpeg(const String& o throw Exception(IMAGE_FORMAT, &origin_string, "not JPEG file - wrong signature"); while(true) { - uint segment_base=reader.tell()+2/*marker,code*/; + uint64_t segment_base=reader.tell()+2/*marker,code*/; if(reader.read(buf, sizeof(JPG_Segment_head))components_count) != 1) return false; uint value = (entry_format == 3) ? endian_to_ushort(is_big, entry->value_or_offset_to_it) : endian_to_uint(is_big, entry->value_or_offset_to_it); - (entry_tag == 256) ? info.width=value : info.height=value; + (entry_tag == 256) ? info.width=(short)value : info.height=(short)value; if(info.width && info.height) return true; } @@ -699,7 +694,7 @@ static void measure_tiff(const String& o if(endian_to_ushort(is_big, head->signature) != 42) 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)) throw Exception(IMAGE_FORMAT, &origin_string, "broken TIFF file - size field entry not found"); } @@ -785,8 +780,7 @@ static void measure_bmp(const String& or if(strncmp(head->signature, "BM", 2)!=0) throw Exception(IMAGE_FORMAT, &origin_string, "not BMP file - wrong signature"); - reader.seek(0, SEEK_END); - if((uint)reader.tell() != endian_to_uint(false, head->file_size)) + if((uint)reader.length() != endian_to_uint(false, head->file_size)) throw Exception(IMAGE_FORMAT, &origin_string, "not BMP file - length header and file size do not match"); width=endian_to_ushort(false, head->width); @@ -875,32 +869,47 @@ struct MP4_Header { char signature[4]; // 'ftyp' in first chunk }; -static bool measure_mp4(const String& origin_string, Measure_reader& reader, ushort& width, ushort& height, off_t anext, const char* lastTkhd=NULL) { +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; - const size_t head_size=sizeof(MP4_Header); - if(reader.read(buf, head_size)size); - off_t next=reader.tell() + size - head_size; + uint64_t size=endian_to_uint(true, head->size); -// printf("%d processing chunk size %d signature '%c%c%c%c %p'\n", anext, size, head->signature[0], head->signature[1], head->signature[2], head->signature[3], lastTkhd); + if(size==1){ + if(reader.read(buf, sizeof(MP4_ExtSize))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; - reader.seek(0, SEEK_END); - anext=reader.tell(); // to avoid reading beyond EOF + 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, SEEK_SET); - if(reader.read(lastTkhd, 8)<8) + reader.seek(next-8); + if(reader.read(lastTkhd, sizeof(MP4_Tkhd))signature, "hdlr", 4)==0) { @@ -908,19 +917,17 @@ static bool measure_mp4(const String& or const char* hdlr; if(reader.read(hdlr, 12)<12) throw Exception(IMAGE_FORMAT, &origin_string, "broken MP4 file - bad hdlr chunk"); -// if(strncmp(hdlr+8, "vide", 4)==0) -// printf("vide found\n"); - if( lastTkhd && strncmp(hdlr+8, "vide", 4)==0) { - width=endian_to_ushort(true, (const unsigned char*)(lastTkhd)); - height=endian_to_ushort(true, (const unsigned char*)(lastTkhd+4)); -// printf("wh %d %d\n",width, height); + 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, SEEK_SET); + reader.seek(next); } return false; } @@ -949,11 +956,14 @@ static void measure(const String& file_n else if(strcasecmp(cext, "TIF")==0 || strcasecmp(cext, "TIFF")==0) 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); + if(info.video) + measure_mp4(file_name, reader, info.width, info.height); + else + throw Exception(IMAGE_FORMAT, &file_name, "handling disabled for file name extension '%s'", cext); else - throw Exception(IMAGE_FORMAT, &file_name, "unhandled image file name extension '%s'", cext); + throw Exception(IMAGE_FORMAT, &file_name, "unhandled file name extension '%s'", cext); } else - throw Exception(IMAGE_FORMAT, &file_name, "can not determine image type - no file name extension"); + throw Exception(IMAGE_FORMAT, &file_name, "can not determine file type - no file name extension"); } // methods @@ -968,7 +978,7 @@ static void _measure(Request& r, MethodP Value* exif=0; Value* xmp=0; - Measure_info info={ 0, 0, 0, 0, &pa_UTF8_charset }; + Measure_info info={ 0, 0, 0, 0, &pa_UTF8_charset, false }; if(params.count()>1) if(HashStringValue* options=params.as_hash(1, "methods options")) { @@ -990,6 +1000,10 @@ static void _measure(Request& r, MethodP info.xmp_charset=&pa_charsets.get(value->as_string()); valid_options++; } + if(key == "video") { + info.video=r.process(*value).as_bool(); + valid_options++; + } } if(valid_options!=options->count()) throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION); @@ -1169,11 +1183,12 @@ static void _replace(Request& r, MethodP gdImage::Point* all_p=0; size_t count=0; if(params.count() == 3){ - Table* table=params.as_table(2, "coordinates"); - count=table->count(); - all_p=new(PointerFreeGC) gdImage::Point[count]; - gdImage::Point* add_p=all_p; - table->for_each(add_point, &add_p); + if(Table* table=params.as_table(2, "coordinates")){ + count=table->count(); + all_p=new(PointerFreeGC) gdImage::Point[count]; + gdImage::Point* add_p=all_p; + table->for_each(add_point, &add_p); + } } else { int max_x=image.SX()-1; int max_y=image.SY()-1; @@ -1195,38 +1210,34 @@ static void _replace(Request& r, MethodP static void _polyline(Request& r, MethodParams& params) { gdImage& image=GET_SELF(r, VImage).image(); - Table* table=params.as_table(1, "coordinates"); - - gdImage::Point* all_p=new(PointerFreeGC) gdImage::Point[table->count()]; - gdImage::Point *add_p=all_p; - table->for_each(add_point, &add_p); - image.Polygon(all_p, table->count(), - image.Color(params.as_int(0, "color must be int", r)), - false/*not closed*/); + if(Table* table=params.as_table(1, "coordinates")){ + gdImage::Point* all_p=new(PointerFreeGC) gdImage::Point[table->count()]; + gdImage::Point *add_p=all_p; + table->for_each(add_point, &add_p); + image.Polygon(all_p, table->count(), image.Color(params.as_int(0, "color must be int", r)), false/*not closed*/); + } } static void _polygon(Request& r, MethodParams& params) { gdImage& image=GET_SELF(r, VImage).image(); - Table* table=(Table*)params.as_table(1, "coordinates"); - - gdImage::Point* all_p=new(PointerFreeGC) gdImage::Point[table->count()]; - gdImage::Point *add_p=all_p; - table->for_each(add_point, &add_p); - image.Polygon(all_p, table->count(), - image.Color(params.as_int(0, "color must be int", r))); + if(Table* table=(Table*)params.as_table(1, "coordinates")){ + gdImage::Point* all_p=new(PointerFreeGC) gdImage::Point[table->count()]; + gdImage::Point *add_p=all_p; + table->for_each(add_point, &add_p); + image.Polygon(all_p, table->count(), image.Color(params.as_int(0, "color must be int", r))); + } } static void _polybar(Request& r, MethodParams& params) { gdImage& image=GET_SELF(r, VImage).image(); - Table* table=(Table*)params.as_table(1, "coordinates"); - - gdImage::Point* all_p=new(PointerFreeGC) gdImage::Point[table->count()]; - gdImage::Point *add_p=all_p; - table->for_each(add_point, &add_p); - image.FilledPolygon(all_p, table->count(), - image.Color(params.as_int(0, "color must be int", r))); + if(Table* table=(Table*)params.as_table(1, "coordinates")){ + gdImage::Point* all_p=new(PointerFreeGC) gdImage::Point[table->count()]; + gdImage::Point *add_p=all_p; + table->for_each(add_point, &add_p); + image.FilledPolygon(all_p, table->count(), image.Color(params.as_int(0, "color must be int", r))); + } } // font