Diff for /parser3/src/classes/image.C between versions 1.170 and 1.184

version 1.170, 2020/12/01 00:50:06 version 1.184, 2023/09/26 20:49:05
Line 1 Line 1
 /** @file  /** @file
         Parser: @b image parser class.          Parser: @b image parser class.
   
         Copyright (c) 2001-2017 Art. Lebedev Studio (http://www.artlebedev.com)          Copyright (c) 2001-2023 Art. Lebedev Studio (http://www.artlebedev.com)
         Author: Alexandr Petrosian <paf@design.ru> (http://paf.design.ru)          Authors: Konstantin Morshnev <moko@design.ru>, Alexandr Petrosian <paf@design.ru>
 */  */
   
 /*  /*
Line 234  public: Line 234  public:
 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 258  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 286  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 314  struct Measure_info { Line 308  struct Measure_info {
         Value** exif;          Value** exif;
         Value** xmp;          Value** xmp;
         Charset* xmp_charset;          Charset* xmp_charset;
           bool    video;
 };  };
   
   
Line 426  static Value* parse_IFD_entry_formatted_ Line 421  static Value* parse_IFD_entry_formatted_
                 size_t length=components_count;                  size_t length=components_count;
                 // Data format is "YYYY:MM:DD HH:MM:SS"+0x00, total 20bytes                  // Data format is "YYYY:MM:DD HH:MM:SS"+0x00, total 20bytes
                 if(length==JPEG_EXIF_DATE_CHARS && isdigit((unsigned char)cstr[0]) && cstr[length-1]==0) {                  if(length==JPEG_EXIF_DATE_CHARS && isdigit((unsigned char)cstr[0]) && cstr[length-1]==0) {
                         char cstr_writable[JPEG_EXIF_DATE_CHARS];  
                         strcpy(cstr_writable, cstr);  
   
                         try {                          try {
                                 tm tmIn=cstr_to_time_t(cstr_writable, 0);                                  tm tmIn=cstr_to_time_t(pa_strdup(cstr), 0);
                                 return new VDate(tmIn);                                  return new VDate(tmIn);
                         }                          }
                         catch(...) { /*ignore bad date times*/ }                          catch(...) { /*ignore bad date times*/ }
Line 453  static Value* parse_IFD_entry_formatted_ Line 445  static Value* parse_IFD_entry_formatted_
         return result;          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[]={          size_t format2component_size[]={
                 0, // undefined                  0, // undefined
                 1, // unsigned byte                  1, // unsigned byte
Line 490  static Value* parse_IFD_entry_value(bool Line 482  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;
 }  }
   
 static void parse_IFD(HashStringValue& hash, bool is_big, Measure_reader& reader, long tiff_base, bool gps=false);  static void parse_IFD(HashStringValue& hash, bool is_big, Measure_reader& reader, uint64_t tiff_base, bool gps=false);
   
 static void parse_IFD_entry(HashStringValue& hash, bool is_big, Measure_reader& reader, long tiff_base, JPG_Exif_IFD_entry& entry, bool gps=false) {  static void parse_IFD_entry(HashStringValue& hash, bool is_big, Measure_reader& reader, uint64_t tiff_base, JPG_Exif_IFD_entry& entry, bool gps=false) {
         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 527  static void parse_IFD_entry(HashStringVa Line 519  static void parse_IFD_entry(HashStringVa
         }          }
 }  }
   
 static void parse_IFD(HashStringValue& hash, bool is_big, Measure_reader& reader, long tiff_base, bool gps) {  static void parse_IFD(HashStringValue& hash, bool is_big, Measure_reader& reader, uint64_t tiff_base, bool gps) {
         const char* buf;          const char* buf;
         if(reader.read(buf, sizeof(JPG_Exif_IFD_begin))<sizeof(JPG_Exif_IFD_begin))          if(reader.read(buf, sizeof(JPG_Exif_IFD_begin))<sizeof(JPG_Exif_IFD_begin))
                 return;                  return;
Line 546  static void parse_IFD(HashStringValue& h Line 538  static void parse_IFD(HashStringValue& h
 static Value* parse_exif(Measure_reader& reader) {  static Value* parse_exif(Measure_reader& reader) {
         const char* buf;          const char* buf;
   
         uint tiff_base=reader.tell();          uint64_t tiff_base=reader.tell();
         if(reader.read(buf, sizeof(JPG_Exif_TIFF_header))<sizeof(JPG_Exif_TIFF_header))          if(reader.read(buf, sizeof(JPG_Exif_TIFF_header))<sizeof(JPG_Exif_TIFF_header))
                 return 0;                  return 0;
   
Line 554  static Value* parse_exif(Measure_reader& Line 546  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 592  static void measure_jpeg(const String& o Line 584  static void measure_jpeg(const String& o
                 throw Exception(IMAGE_FORMAT, &origin_string, "not JPEG file - wrong signature");                  throw Exception(IMAGE_FORMAT, &origin_string, "not JPEG file - wrong signature");
   
         while(true) {          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))<sizeof(JPG_Segment_head))                  if(reader.read(buf, sizeof(JPG_Segment_head))<sizeof(JPG_Segment_head))
                         break;                          break;
                 JPG_Segment_head *head=(JPG_Segment_head *)buf;                  JPG_Segment_head *head=(JPG_Segment_head *)buf;
Line 646  static void measure_jpeg(const String& o Line 638  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 673  static bool parse_tiff_IFD(bool is_big, Line 665  static bool parse_tiff_IFD(bool is_big,
                         if(entry_format != 3 && entry_format != 4 || endian_to_uint(is_big, entry->components_count) != 1)                          if(entry_format != 3 && entry_format != 4 || endian_to_uint(is_big, entry->components_count) != 1)
                                 return false;                                  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);                          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)                          if(info.width && info.height)
                                 return true;                                  return true;
                 }                  }
Line 699  static void measure_tiff(const String& o Line 691  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 785  static void measure_bmp(const String& or Line 777  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 875  struct MP4_Header { Line 866  struct MP4_Header {
         char signature[4];   // 'ftyp' in first chunk          char signature[4];   // 'ftyp' in first chunk
 };  };
   
 static bool measure_mp4(const String& origin_string, Measure_reader& reader, ushort& width, ushort& height, long 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;;){          for(bool first=anext==0;;){
                 const char* buf;                  const char* buf;
                 const size_t head_size=sizeof(MP4_Header);                  uint64_t next=reader.tell();
                 if(reader.read(buf, head_size)<head_size)  
                         throw Exception(IMAGE_FORMAT, &origin_string, first ? "not MP4 file - too small" : "broken MP4 file - small chunk header");                  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;                  MP4_Header *head=(MP4_Header *)buf;
                 uint size=endian_to_uint(true, head->size);                  uint64_t size=endian_to_uint(true, head->size);
                 long next=reader.tell() + size - 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))<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(first){
                         if(strncmp(head->signature, "ftyp", 4)!=0)                          if(strncmp(head->signature, "ftyp", 4)!=0)
                                 throw Exception(IMAGE_FORMAT, &origin_string, "not MP4 file - wrong signature");                                  throw Exception(IMAGE_FORMAT, &origin_string, "not MP4 file - wrong signature");
                         first=false;                          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) {                  } 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))                          if(measure_mp4(origin_string, reader, width, height, next, lastTkhd))
                                 return true;                                  return true;
                 } else if(strncmp(head->signature, "tkhd", 4)==0) {                  } else if(strncmp(head->signature, "tkhd", 4)==0) {
                         if(size>8){                          if(size>8){
                                 reader.seek(next-8, SEEK_SET);                                  reader.seek(next-8);
                                 if(reader.read(lastTkhd, 8)<8)                                  if(reader.read(lastTkhd, sizeof(MP4_Tkhd))<sizeof(MP4_Tkhd))
                                         throw Exception(IMAGE_FORMAT, &origin_string, "broken MP4 file - bad tkhd chunk");                                          throw Exception(IMAGE_FORMAT, &origin_string, "broken MP4 file - bad tkhd chunk");
                         }                          }
                 } else if (strncmp(head->signature, "hdlr", 4)==0) {                  } else if (strncmp(head->signature, "hdlr", 4)==0) {
Line 906  static bool measure_mp4(const String& or Line 914  static bool measure_mp4(const String& or
                                 const char* hdlr;                                  const char* hdlr;
                                 if(reader.read(hdlr, 12)<12)                                  if(reader.read(hdlr, 12)<12)
                                         throw Exception(IMAGE_FORMAT, &origin_string, "broken MP4 file - bad hdlr chunk");                                          throw Exception(IMAGE_FORMAT, &origin_string, "broken MP4 file - bad hdlr chunk");
 //                              if(strncmp(hdlr+8, "vide", 4)==0)                                  if(lastTkhd && strncmp(hdlr+8, "vide", 4)==0) {
 //                                      printf("vide found\n");                                          MP4_Tkhd *tkhd=(MP4_Tkhd *)lastTkhd;
                                 if( lastTkhd && strncmp(hdlr+8, "vide", 4)==0) {                                          width=endian_to_ushort(true, tkhd->width);
                                         width=endian_to_ushort(true, (const unsigned char*)(lastTkhd));                                          height=endian_to_ushort(true, tkhd->height);
                                         height=endian_to_ushort(true, (const unsigned char*)(lastTkhd+4));  
 //                                      printf("wh %d %d\n",width, height);  
                                         return true;                                          return true;
                                 }                                  }
                         }                          }
                 }                  }
                 if(anext && next>=anext)                  if(anext && next>=anext)
                         break;                          break;
                 reader.seek(next, SEEK_SET);                  reader.seek(next);
         }          }
         return false;          return false;
 }  }
Line 946  static void measure(const String& file_n Line 952  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)                  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                  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          } 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  // methods
Line 966  static void _measure(Request& r, MethodP Line 975  static void _measure(Request& r, MethodP
   
         Value* exif=0;          Value* exif=0;
         Value* xmp=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(params.count()>1)
                 if(HashStringValue* options=params.as_hash(1, "methods options")) {                  if(HashStringValue* options=params.as_hash(1, "methods options")) {
Line 988  static void _measure(Request& r, MethodP Line 997  static void _measure(Request& r, MethodP
                                         info.xmp_charset=&pa_charsets.get(value->as_string());                                          info.xmp_charset=&pa_charsets.get(value->as_string());
                                         valid_options++;                                          valid_options++;
                                 }                                  }
                                   if(key == "video") {
                                           info.video=r.process(*value).as_bool();
                                           valid_options++;
                                   }
                         }                          }
                         if(valid_options!=options->count())                          if(valid_options!=options->count())
                                 throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION);                                  throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION);
Line 998  static void _measure(Request& r, MethodP Line 1011  static void _measure(Request& r, MethodP
         if(file_name=data.get_string()) {          if(file_name=data.get_string()) {
                 file_read_action_under_lock(r.full_disk_path(*file_name), "measure", file_measure_action, &info);                  file_read_action_under_lock(r.full_disk_path(*file_name), "measure", file_measure_action, &info);
         } else {          } else {
                 VFile* vfile=data.as_vfile(String::L_AS_IS);                  VFile* vfile=data.as_vfile();
                 file_name=&vfile->fields().get(name_name)->as_string();                  file_name=&vfile->fields().get(name_name)->as_string();
                 Measure_buf_reader reader(vfile->value_ptr(), vfile->value_size(), *file_name);                  Measure_buf_reader reader(vfile->value_ptr(), vfile->value_size(), *file_name);
                 measure(*file_name, reader, info);                  measure(*file_name, reader, info);
Line 1167  static void _replace(Request& r, MethodP Line 1180  static void _replace(Request& r, MethodP
         gdImage::Point* all_p=0;          gdImage::Point* all_p=0;
         size_t count=0;          size_t count=0;
         if(params.count() == 3){          if(params.count() == 3){
                 Table* table=params.as_table(2, "coordinates");                  if(Table* table=params.as_table(2, "coordinates")){
                 count=table->count();                          count=table->count();
                 all_p=new(PointerFreeGC) gdImage::Point[count];                          all_p=new(PointerFreeGC) gdImage::Point[count];
                 gdImage::Point* add_p=all_p;                          gdImage::Point* add_p=all_p;
                 table->for_each(add_point, &add_p);                          table->for_each(add_point, &add_p);
                   }
         } else {          } else {
                 int max_x=image.SX()-1;                  int max_x=image.SX()-1;
                 int max_y=image.SY()-1;                  int max_y=image.SY()-1;
Line 1193  static void _replace(Request& r, MethodP Line 1207  static void _replace(Request& r, MethodP
 static void _polyline(Request& r, MethodParams& params) {  static void _polyline(Request& r, MethodParams& params) {
         gdImage& image=GET_SELF(r, VImage).image();          gdImage& image=GET_SELF(r, VImage).image();
   
         Table* table=params.as_table(1, "coordinates");          if(Table* table=params.as_table(1, "coordinates")){
                   gdImage::Point* all_p=new(PointerFreeGC) gdImage::Point[table->count()];
         gdImage::Point* all_p=new(PointerFreeGC) gdImage::Point[table->count()];                  gdImage::Point *add_p=all_p;
         gdImage::Point *add_p=all_p;                      table->for_each(add_point, &add_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*/);
         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) {  static void _polygon(Request& r, MethodParams& params) {
         gdImage& image=GET_SELF(r, VImage).image();          gdImage& image=GET_SELF(r, VImage).image();
   
         Table* table=(Table*)params.as_table(1, "coordinates");          if(Table* table=(Table*)params.as_table(1, "coordinates")){
                   gdImage::Point* all_p=new(PointerFreeGC) gdImage::Point[table->count()];
         gdImage::Point* all_p=new(PointerFreeGC) gdImage::Point[table->count()];                  gdImage::Point *add_p=all_p;
         gdImage::Point *add_p=all_p;                      table->for_each(add_point, &add_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)));
         image.Polygon(all_p, table->count(),           }
                 image.Color(params.as_int(0, "color must be int", r)));  
 }  }
   
 static void _polybar(Request& r, MethodParams& params) {  static void _polybar(Request& r, MethodParams& params) {
         gdImage& image=GET_SELF(r, VImage).image();          gdImage& image=GET_SELF(r, VImage).image();
   
         Table* table=(Table*)params.as_table(1, "coordinates");          if(Table* table=(Table*)params.as_table(1, "coordinates")){
                   gdImage::Point* all_p=new(PointerFreeGC) gdImage::Point[table->count()];
         gdImage::Point* all_p=new(PointerFreeGC) gdImage::Point[table->count()];                  gdImage::Point *add_p=all_p;
         gdImage::Point *add_p=all_p;                      table->for_each(add_point, &add_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)));
         image.FilledPolygon(all_p, table->count(),           }
                 image.Color(params.as_int(0, "color must be int", r)));  
 }  }
   
 // font  // font

Removed from v.1.170  
changed lines
  Added in v.1.184


E-mail: