Annotation of parser3/src/classes/image.C, revision 1.79
1.1 paf 1: /** @file
2: Parser: @b image parser class.
3:
1.67 paf 4: Copyright(c) 2001, 2002 ArtLebedev Group (http://www.artlebedev.com)
1.66 paf 5: Author: Alexandr Petrosian <paf@design.ru> (http://paf.design.ru)
1.73 paf 6: */
1.1 paf 7:
1.79 ! paf 8: static const char* IDENT_IMAGE_C="$Date: 2002/11/21 09:47:34 $";
1.31 parser 9:
10: /*
11: jpegsize: gets the width and height (in pixels) of a jpeg file
12: Andrew Tong, werdna@ugcs.caltech.edu February 14, 1995
13: modified slightly by alex@ed.ac.uk
14: and further still by rjray@uswest.com
15: optimization and general re-write from tmetro@vl.com
16: from perl by paf@design.ru
1.1 paf 17: */
18:
19: #include "pa_config_includes.h"
20:
1.8 paf 21: #include "gif.h"
1.6 paf 22:
1.1 paf 23: #include "pa_common.h"
24: #include "pa_request.h"
25: #include "pa_vfile.h"
26: #include "pa_vimage.h"
27:
1.22 paf 28: // class
29:
30: class MImage : public Methoded {
31: public: // VStateless_class
32: Value *create_new_value(Pool& pool) { return new(pool) VImage(pool); }
33:
34: public:
35: MImage(Pool& pool);
1.24 paf 36:
37: public: // Methoded
1.22 paf 38: bool used_directly() { return true; }
39:
40: };
1.1 paf 41:
42: // helpers
43:
1.43 parser 44: #ifndef DOXYGEN
1.1 paf 45: class Measure_reader {
46: public:
1.77 paf 47: virtual size_t read(const void *&buf, size_t limit)=0;
1.79 ! paf 48: virtual void seek(long value, int whence)=0;
! 49: virtual long tell()=0;
1.77 paf 50: };
1.1 paf 51:
1.77 paf 52: class Measure_file_reader: public Measure_reader {
53: public:
54: Measure_file_reader(Pool& apool, int af, const String& afile_name, const char *afname):
55: pool(apool), file_name(afile_name), fname(afname), f(af) {
1.1 paf 56: }
57:
1.77 paf 58: /*override*/size_t read(const void *&abuf, size_t limit) {
59: if(limit==0)
1.1 paf 60: return 0;
1.77 paf 61:
62: void *lbuf=pool.malloc(limit);
63: size_t read_size=(size_t)::read(f, lbuf, limit); abuf=lbuf;
64: if(ssize_t(read_size)<0 || read_size>limit)
65: throw Exception(0,
66: &file_name,
67: "measure failed: actually read %lu bytes count not in [0..%lu] valid range",
68: read_size, limit);
69:
1.1 paf 70: return read_size;
71: }
72:
1.79 ! paf 73: /*override*/void seek(long value, int whence) {
! 74: if(lseek(f, value, whence)<0)
1.77 paf 75: throw Exception("file.seek",
76: &file_name,
1.79 ! paf 77: "seek(value=%ld, whence=%d) failed: %s (%d), actual filename '%s'",
! 78: value, whence, strerror(errno), errno, fname);
1.77 paf 79: }
80:
1.79 ! paf 81: /*override*/long tell() { return ::tell(f); }
! 82:
1.1 paf 83: private:
1.77 paf 84: Pool& pool;
85: const String& file_name; const char *fname;
86: int f;
87: };
88:
89: class Measure_buf_reader: public Measure_reader {
90: public:
91: Measure_buf_reader(const void *abuf, size_t asize, const String& afile_name):
92: buf(abuf), size(asize), file_name(afile_name), offset(0) {
93: }
94:
95: /*override*/size_t read(const void *&abuf, size_t limit) {
96: size_t to_read=min(limit, size-offset);
97: abuf=(const char*)buf+offset;
98: offset+=to_read;
99: return to_read;
100: }
101:
1.79 ! paf 102: /*override*/void seek(long value, int whence) {
! 103: size_t new_offset;
! 104: switch(whence) {
! 105: case SEEK_CUR: new_offset=offset+value; break;
! 106: case SEEK_SET: new_offset=(size_t)value; break;
! 107: default: throw Exception("file.seek", 0, "whence #%d not supported", 0, whence); break;
! 108: }
! 109:
1.77 paf 110: if((ssize_t)new_offset<0 || new_offset>size)
111: throw Exception("file.seek",
112: &file_name,
1.79 ! paf 113: "seek(value=%l, whence=%d) failed: out of buffer, new_offset>size (%l>%l) or new_offset<0",
! 114: value, whence, new_offset, size);
1.77 paf 115: offset=new_offset;
116: }
117:
1.79 ! paf 118: /*override*/long tell() { return offset; }
! 119:
1.77 paf 120: private:
121:
122: const void *buf; size_t size;
123: const String& file_name;
1.1 paf 124:
125: size_t offset;
126: };
1.77 paf 127:
1.43 parser 128: #endif
1.1 paf 129:
1.72 paf 130: /// PNG file header
131: struct PNG_Header {
132: char dummy[12];
133: char signature[4]; //< must be "IHDR"
134: unsigned char high_width[2]; //< image width high bytes [we ignore for now]
135: unsigned char width[2]; //< image width low bytes
136: unsigned char high_height[2]; //< image height high bytes [we ignore for now]
137: unsigned char height[4]; //< image height
138: };
139:
1.21 paf 140: /// GIF file header
1.1 paf 141: struct GIF_Header {
1.72 paf 142: char signature[3]; // 'GIF'
1.1 paf 143: char version[3];
144: unsigned char width[2];
145: unsigned char height[2];
146: char dif;
147: char fonColor;
148: char nulls;
149: };
150:
1.31 parser 151: /// JPEG record head
152: struct JPG_Segment_head {
153: unsigned char marker;
154: unsigned char code;
155: unsigned char length[2];
1.1 paf 156: };
1.21 paf 157: /// JPEG frame header
1.31 parser 158: struct JPG_Size_segment_body {
1.27 parser 159: char data; //< data precision of bits/sample
1.31 parser 160: unsigned char height[2]; //< image height
161: unsigned char width[2]; //< image width
1.27 parser 162: char numComponents; //< number of color components
1.1 paf 163: };
164:
1.79 ! paf 165: /// JPEG frame header
! 166: struct JPG_Exif_segment_start {
! 167: char signature[6]; // Exif\0\0
! 168: };
! 169:
1.1 paf 170: //
171:
1.78 paf 172: inline ushort x_endian_to_ushort(unsigned char L, unsigned char H) {
1.33 parser 173: return(short)((H<<8) + L);
174: }
175:
1.78 paf 176: inline ushort big_endian_to_ushort(unsigned char b[2]) {
177: return x_endian_to_ushort(b[1], b[0]);
1.33 parser 178: }
179:
1.78 paf 180: inline ushort little_endian_to_ushort(unsigned char b[2]) {
181: return x_endian_to_ushort(b[0], b[1]);
1.1 paf 182: }
183:
184: void measure_gif(Pool& pool, const String *origin_string,
1.78 paf 185: Measure_reader& reader, ushort& width, ushort& height) {
1.1 paf 186:
1.31 parser 187: void *buf;
1.1 paf 188: const int head_size=sizeof(GIF_Header);
189: if(reader.read(buf, head_size)<head_size)
1.68 paf 190: throw Exception("image.format",
1.1 paf 191: origin_string,
1.34 parser 192: "not GIF file - too small");
1.31 parser 193: GIF_Header *head=(GIF_Header *)buf;
1.1 paf 194:
1.72 paf 195: if(strncmp(head->signature, "GIF", 3)!=0)
1.68 paf 196: throw Exception("image.format",
1.1 paf 197: origin_string,
1.44 parser 198: "not GIF file - wrong signature");
1.1 paf 199:
1.78 paf 200: width=little_endian_to_ushort(head->width);
201: height=little_endian_to_ushort(head->height);
1.1 paf 202: }
203:
1.58 parser 204: /// @test remove ugly mech in reader - 20K limit
1.1 paf 205: void measure_jpeg(Pool& pool, const String *origin_string,
1.78 paf 206: Measure_reader& reader, ushort& width, ushort& height) {
1.2 paf 207: // JFIF format markers
1.31 parser 208: const unsigned char MARKER=0xFF;
1.79 ! paf 209: const unsigned char CODE_SIZE_A=0xC0;
! 210: const unsigned char CODE_SIZE_B=0xC1;
! 211: const unsigned char CODE_SIZE_C=0xC2;
! 212: const unsigned char CODE_SIZE_D=0xC3;
! 213: const unsigned char CODE_EXIF=0xE1;
1.2 paf 214:
1.31 parser 215: void *buf;
1.18 paf 216: const size_t prefix_size=2;
1.31 parser 217: if(reader.read(buf, prefix_size)<prefix_size)
1.68 paf 218: throw Exception("image.format",
1.1 paf 219: origin_string,
1.34 parser 220: "not JPEG file - too small");
1.31 parser 221: unsigned char *signature=(unsigned char *)buf;
1.1 paf 222:
1.31 parser 223: if(!(signature[0]==0xFF && signature[1]==0xD8))
1.68 paf 224: throw Exception("image.format",
1.1 paf 225: origin_string,
1.44 parser 226: "not JPEG file - wrong signature");
1.31 parser 227:
228: while(true) {
1.79 ! paf 229: long segment_base_offset=reader.tell()+2/*marker,code*/;
1.31 parser 230: if(reader.read(buf, sizeof(JPG_Segment_head))<sizeof(JPG_Segment_head))
1.79 ! paf 231: break;
1.31 parser 232: JPG_Segment_head *head=(JPG_Segment_head *)buf;
233:
234: // Verify that it's a valid segment.
235: if(head->marker!=MARKER)
1.79 ! paf 236: throw Exception("image.format",
! 237: origin_string,
! 238: "not JPEG file - marker not found");
! 239:
! 240: switch(head->code) {
! 241: // http://www.ba.wakwak.com/~tsuruzoh/Computer/Digicams/exif-e.html
! 242: case CODE_EXIF:
! 243: {
! 244: if(reader.read(buf, sizeof(JPG_Exif_segment_start))<sizeof(JPG_Exif_segment_start))
! 245: throw Exception("image.format",
! 246: origin_string,
! 247: "not JPEG file - can not fully read Exif segment start");
! 248:
! 249: JPG_Exif_segment_start *start=(JPG_Exif_segment_start *)buf;
! 250: if(memcmp(start->signature, "Exif\0\0", 4+2)!=0) //signature invalid?
! 251: break; // ignore invalid block
! 252:
! 253: // parse Exif block
! 254: }
1.1 paf 255: break;
1.31 parser 256:
1.79 ! paf 257: case CODE_SIZE_A:
! 258: case CODE_SIZE_B:
! 259: case CODE_SIZE_C:
! 260: case CODE_SIZE_D:
! 261: {
! 262: // Segments that contain size info
! 263: if(reader.read(buf, sizeof(JPG_Size_segment_body))<sizeof(JPG_Size_segment_body))
! 264: throw Exception("image.format",
! 265: origin_string,
! 266: "not JPEG file - can not fully read Size segment");
! 267: JPG_Size_segment_body *body=(JPG_Size_segment_body *)buf;
! 268:
! 269: width=big_endian_to_ushort(body->width);
! 270: height=big_endian_to_ushort(body->height);
! 271: return;
! 272: }
! 273: };
! 274:
! 275: reader.seek(segment_base_offset+big_endian_to_ushort(head->length), SEEK_SET);
1.31 parser 276: }
277:
1.79 ! paf 278: throw Exception("image.format",
! 279: origin_string,
! 280: "broken JPEG file - size frame not found");
1.1 paf 281: }
282:
1.72 paf 283: void measure_png(Pool& pool, const String *origin_string,
1.78 paf 284: Measure_reader& reader, ushort& width, ushort& height) {
1.72 paf 285:
286: void *buf;
287: const int head_size=sizeof(PNG_Header);
288: if(reader.read(buf, head_size)<head_size)
289: throw Exception("image.format",
290: origin_string,
291: "not PNG file - too small");
292: PNG_Header *head=(PNG_Header *)buf;
293:
294: if(strncmp(head->signature, "IHDR", 4)!=0)
295: throw Exception("image.format",
296: origin_string,
297: "not PNG file - wrong signature");
298:
1.78 paf 299: width=big_endian_to_ushort(head->width);
300: height=big_endian_to_ushort(head->height);
1.72 paf 301: }
302:
1.1 paf 303: // measure center
304:
305: void measure(Pool& pool, const String& file_name,
1.78 paf 306: Measure_reader& reader, ushort& width, ushort& height) {
1.60 paf 307: if(const char *cext=strrchr(file_name.cstr(String::UL_FILE_SPEC), '.')) {
1.1 paf 308: cext++;
309: if(strcasecmp(cext, "GIF")==0)
310: measure_gif(pool, &file_name, reader, width, height);
311: else if(strcasecmp(cext, "JPG")==0 || strcasecmp(cext, "JPEG")==0)
312: measure_jpeg(pool, &file_name, reader, width, height);
1.72 paf 313: else if(strcasecmp(cext, "PNG")==0)
314: measure_png(pool, &file_name, reader, width, height);
1.1 paf 315: else
1.68 paf 316: throw Exception("image.format",
1.1 paf 317: &file_name,
318: "unhandled image file name extension '%s'", cext);
319: } else
1.68 paf 320: throw Exception("image.format",
1.1 paf 321: &file_name,
322: "can not determine image type - no file name extension");
323: }
324:
1.77 paf 325: // methods
1.1 paf 326:
1.40 parser 327: #ifndef DOXYGEN
1.77 paf 328: struct File_measure_action_info {
1.78 paf 329: ushort *width;
330: ushort *height;
1.77 paf 331: const String *file_name;
1.1 paf 332: };
1.40 parser 333: #endif
1.77 paf 334: static void file_measure_action(Pool& pool,
335: int f,
336: const String& file_spec, const char *fname, bool as_text,
337: void *context) {
338: File_measure_action_info& info=*static_cast<File_measure_action_info *>(context);
1.1 paf 339:
1.77 paf 340: Measure_file_reader reader(pool, f, *info.file_name, fname);
341: measure(pool, *info.file_name, reader, *info.width, *info.height);
1.1 paf 342: }
343:
1.17 paf 344: static void _measure(Request& r, const String& method_name, MethodParams *params) {
1.1 paf 345: Pool& pool=r.pool();
346:
1.30 parser 347: Value& data=params->as_no_junction(0, "data must not be code");
1.1 paf 348:
1.78 paf 349: ushort width=0;
350: ushort height=0;
1.1 paf 351: const String *file_name;
1.77 paf 352: if(file_name=data.get_string()) {
353: File_measure_action_info info={&width, &height, file_name};
354: file_read_action_under_lock(pool, r.absolute(*file_name),
355: "measure", file_measure_action, &info);
1.1 paf 356: } else {
357: const VFile& vfile=*data.as_vfile();
358: file_name=&static_cast<Value *>(vfile.fields().get(*name_name))->as_string();
1.77 paf 359: Measure_buf_reader reader(
360: vfile.value_ptr(),
361: vfile.value_size(),
362: *file_name
363: );
364: measure(pool, *file_name, reader, width, height);
1.1 paf 365: }
366:
1.76 paf 367: static_cast<VImage *>(r.get_self())->set(file_name, width, height);
1.1 paf 368: }
369:
1.40 parser 370: #ifndef DOXYGEN
1.4 paf 371: struct Attrib_info {
1.21 paf 372: String *tag; ///< html tag being constructed
373: Hash *skip; ///< tag attributes not to append to tag string [to skip]
1.4 paf 374: };
1.40 parser 375: #endif
1.3 paf 376: static void append_attrib_pair(const Hash::Key& key, Hash::Val *val, void *info) {
1.4 paf 377: Attrib_info& ai=*static_cast<Attrib_info *>(info);
378:
1.49 parser 379: // skip user-specified and internal(starting with "line-") attributes
380: if(ai.skip && ai.skip->get(key) || key.pos("line-")==0)
1.4 paf 381: return;
382:
1.3 paf 383: Value& value=*static_cast<Value *>(val);
384: // src="a.gif" width=123 ismap[=-1]
1.4 paf 385: *ai.tag << " " << key;
1.26 parser 386: if(value.is_string() || value.as_int()>=0)
1.6 paf 387: *ai.tag << "=\"" << value.as_string() << "\"";
1.3 paf 388: }
1.17 paf 389: static void _html(Request& r, const String& method_name, MethodParams *params) {
1.3 paf 390: Pool& pool=r.pool();
391:
392: String tag(pool);
393: tag << "<img";
1.4 paf 394:
1.76 paf 395: const Hash& fields=static_cast<VImage *>(r.get_self())->fields();
1.5 paf 396: Hash *attribs=0;
1.4 paf 397:
1.39 parser 398: if(params->size()) {
1.69 paf 399: // for backward compatibility: someday was ^html{}
400: Value &vattribs=r.process_to_value(params->get(0),
1.70 paf 401: /*0/*no name* /,*/
1.52 parser 402: false/*don't intercept string*/);
1.39 parser 403: if(vattribs.is_defined()) // allow 'void'
1.59 parser 404: if(attribs=vattribs.get_hash(&method_name)) {
1.39 parser 405: Attrib_info attrib_info={&tag, 0};
406: attribs->for_each(append_attrib_pair, &attrib_info);
407: } else
1.68 paf 408: throw Exception("parser.runtime",
1.39 parser 409: &method_name,
410: "attributes must be hash");
411: }
1.4 paf 412:
1.5 paf 413: Attrib_info attrib_info={&tag, attribs};
1.4 paf 414: fields.for_each(append_attrib_pair, &attrib_info);
1.6 paf 415: tag << " />";
1.3 paf 416: r.write_pass_lang(tag);
417: }
1.8 paf 418:
1.68 paf 419: /// @test wrap FILE to auto-object
1.16 paf 420: static gdImage *load(Request& r, const String& method_name,
421: const String& file_name){
422: Pool& pool=r.pool();
423:
1.42 parser 424: const char *file_name_cstr=r.absolute(file_name).cstr(String::UL_FILE_SPEC);
1.16 paf 425: if(FILE *f=fopen(file_name_cstr, "rb")) {
426: gdImage& image=*new(pool) gdImage(pool);
1.23 paf 427: bool ok=image.CreateFromGif(f);
1.16 paf 428: fclose(f);
1.23 paf 429: if(!ok)
1.68 paf 430: throw Exception("image.format",
1.23 paf 431: &file_name,
432: "is not in GIF format");
1.16 paf 433: return ℑ
434: } else {
1.68 paf 435: throw Exception("file.missing",
1.16 paf 436: &method_name,
437: "can not open '%s'", file_name_cstr);
438: return 0;
439: }
440: }
441:
442:
1.17 paf 443: static void _load(Request& r, const String& method_name, MethodParams *params) {
1.6 paf 444: Pool& pool=r.pool();
445:
1.44 parser 446: const String& file_name=params->as_string(0, "file name must not be code");
1.6 paf 447:
1.16 paf 448: gdImage& image=*load(r, method_name, file_name);
449: int width=image.SX();
450: int height=image.SY();
1.76 paf 451: static_cast<VImage *>(r.get_self())->set(&file_name, width, height, &image);
1.6 paf 452: }
453:
1.17 paf 454: static void _create(Request& r, const String& method_name, MethodParams *params) {
1.6 paf 455: Pool& pool=r.pool();
456:
1.51 parser 457: int width=params->as_int(0, "width must be int", r);
458: int height=params->as_int(1, "height must be int", r);
1.8 paf 459: int bgcolor_value=0xffFFff;
1.6 paf 460: if(params->size()>2)
1.51 parser 461: bgcolor_value=params->as_int(2, "color must be int", r);
1.15 paf 462: gdImage& image=*new(pool) gdImage(pool);
463: image.Create(width, height);
464: image.FilledRectangle(0, 0, width-1, height-1, image.Color(bgcolor_value));
1.76 paf 465: static_cast<VImage *>(r.get_self())->set(0, width, height, &image);
1.6 paf 466: }
467:
1.17 paf 468: static void _gif(Request& r, const String& method_name, MethodParams *params) {
1.6 paf 469: Pool& pool=r.pool();
470:
1.76 paf 471: gdImage *image=static_cast<VImage *>(r.get_self())->image;
1.8 paf 472: if(!image)
1.68 paf 473: throw Exception(0,
1.16 paf 474: &method_name,
1.12 paf 475: "does not contain an image");
1.6 paf 476:
477: // could _ but don't thing it's wise to use $image.src for vfile.name
1.10 paf 478:
1.16 paf 479: String out(pool); image->Gif(out);
1.6 paf 480:
481: VFile& vfile=*new(pool) VFile(pool);
1.41 parser 482: Value *content_type=new(pool) VString(*new(pool) String(pool, "image/gif"));
1.20 paf 483: vfile.set(false/*not tainted*/,
1.61 paf 484: out.cstr(), out.size(), 0, content_type);
1.6 paf 485:
486: r.write_no_lang(vfile);
487: }
488:
1.17 paf 489: static void _line(Request& r, const String& method_name, MethodParams *params) {
1.12 paf 490: Pool& pool=r.pool();
491:
1.76 paf 492: gdImage *image=static_cast<VImage *>(r.get_self())->image;
1.12 paf 493: if(!image)
1.68 paf 494: throw Exception(0,
1.16 paf 495: &method_name,
1.12 paf 496: "does not contain an image");
497:
498: image->Line(
1.51 parser 499: params->as_int(0, "x0 must be int", r),
500: params->as_int(1, "y0 must be int", r),
501: params->as_int(2, "x1 must be int", r),
502: params->as_int(3, "y1 must be int", r),
503: image->Color(params->as_int(4, "color must be int", r)));
1.13 paf 504: }
505:
1.17 paf 506: static void _fill(Request& r, const String& method_name, MethodParams *params) {
1.13 paf 507: Pool& pool=r.pool();
508:
1.76 paf 509: gdImage *image=static_cast<VImage *>(r.get_self())->image;
1.13 paf 510: if(!image)
1.68 paf 511: throw Exception(0,
1.16 paf 512: &method_name,
1.13 paf 513: "does not contain an image");
1.12 paf 514:
1.13 paf 515: image->Fill(
1.51 parser 516: params->as_int(0, "x must be int", r),
517: params->as_int(1, "y must be int", r),
518: image->Color(params->as_int(2, "color must be int", r)));
1.13 paf 519: }
520:
1.17 paf 521: static void _rectangle(Request& r, const String& method_name, MethodParams *params) {
1.13 paf 522: Pool& pool=r.pool();
523:
1.76 paf 524: gdImage *image=static_cast<VImage *>(r.get_self())->image;
1.13 paf 525: if(!image)
1.68 paf 526: throw Exception(0,
1.16 paf 527: &method_name,
1.13 paf 528: "does not contain an image");
529:
530: image->Rectangle(
1.51 parser 531: params->as_int(0, "x0 must be int", r),
532: params->as_int(1, "y0 must be int", r),
533: params->as_int(2, "x1 must be int", r),
534: params->as_int(3, "y1 must be int", r),
535: image->Color(params->as_int(4, "color must be int", r)));
1.13 paf 536: }
537:
1.17 paf 538: static void _bar(Request& r, const String& method_name, MethodParams *params) {
1.13 paf 539: Pool& pool=r.pool();
540:
1.76 paf 541: gdImage *image=static_cast<VImage *>(r.get_self())->image;
1.13 paf 542: if(!image)
1.68 paf 543: throw Exception(0,
1.16 paf 544: &method_name,
1.13 paf 545: "does not contain an image");
546:
547: image->FilledRectangle(
1.51 parser 548: params->as_int(0, "x0 must be int", r),
549: params->as_int(1, "y0 must be int", r),
550: params->as_int(2, "x1 must be int", r),
551: params->as_int(3, "y1 must be int", r),
552: image->Color(params->as_int(4, "color must be int", r)));
1.13 paf 553: }
554:
1.44 parser 555: #ifndef DOXYGEN
556: static void add_point(Array::Item *value, void *info) {
557: Array& row=*static_cast<Array *>(value);
558: gdImage::Point **p=static_cast<gdImage::Point **>(info);
559:
560: (**p).x=row.get_string(0)->as_int();
561: (**p).y=row.get_string(1)->as_int();
562: (*p)++;
563: }
564: #endif
1.17 paf 565: static void _replace(Request& r, const String& method_name, MethodParams *params) {
1.13 paf 566: Pool& pool=r.pool();
567:
1.76 paf 568: gdImage *image=static_cast<VImage *>(r.get_self())->image;
1.13 paf 569: if(!image)
1.68 paf 570: throw Exception(0,
1.16 paf 571: &method_name,
1.13 paf 572: "does not contain an image");
573:
1.44 parser 574: Table *table=params->as_no_junction(2, "coordinates must not be code").get_table();
575: if(!table)
1.68 paf 576: throw Exception(0,
1.44 parser 577: &method_name,
578: "coordinates must be table");
1.13 paf 579:
1.44 parser 580: gdImage::Point *all_p=(gdImage::Point *)pool.malloc(sizeof(gdImage::Point)*table->size());
581: gdImage::Point *add_p=all_p;
582: table->for_each(add_point, &add_p);
583: image->FilledPolygonReplaceColor(all_p, table->size(),
1.51 parser 584: image->Color(params->as_int(0, "src color must be int", r)),
585: image->Color(params->as_int(1, "dest color must be int", r)));
1.13 paf 586: }
587:
1.44 parser 588: static void _polyline(Request& r, const String& method_name, MethodParams *params) {
1.13 paf 589: Pool& pool=r.pool();
590:
1.76 paf 591: gdImage *image=static_cast<VImage *>(r.get_self())->image;
1.13 paf 592: if(!image)
1.68 paf 593: throw Exception(0,
1.16 paf 594: &method_name,
1.13 paf 595: "does not contain an image");
596:
1.44 parser 597: Table *table=params->as_no_junction(1, "coordinates must not be code").get_table();
598: if(!table)
1.68 paf 599: throw Exception(0,
1.44 parser 600: &method_name,
601: "coordinates must be table");
602:
603: gdImage::Point *all_p=(gdImage::Point *)pool.malloc(sizeof(gdImage::Point)*table->size());
604: gdImage::Point *add_p=all_p;
605: table->for_each(add_point, &add_p);
606: image->Polygon(all_p, table->size(),
1.51 parser 607: image->Color(params->as_int(0, "color must be int", r)),
1.44 parser 608: false/*not closed*/);
609: }
610:
611: static void _polygon(Request& r, const String& method_name, MethodParams *params) {
612: Pool& pool=r.pool();
613:
1.76 paf 614: gdImage *image=static_cast<VImage *>(r.get_self())->image;
1.44 parser 615: if(!image)
1.68 paf 616: throw Exception(0,
1.16 paf 617: &method_name,
1.44 parser 618: "does not contain an image");
1.13 paf 619:
1.44 parser 620: Table *table=params->as_no_junction(1, "coordinates must not be code").get_table();
621: if(!table)
1.68 paf 622: throw Exception(0,
1.44 parser 623: &method_name,
624: "coordinates must be table");
625:
626: gdImage::Point *all_p=(gdImage::Point *)pool.malloc(sizeof(gdImage::Point)*table->size());
627: gdImage::Point *add_p=all_p;
628: table->for_each(add_point, &add_p);
629: image->Polygon(all_p, table->size(),
1.51 parser 630: image->Color(params->as_int(0, "color must be int", r)));
1.13 paf 631: }
632:
1.17 paf 633: static void _polybar(Request& r, const String& method_name, MethodParams *params) {
1.13 paf 634: Pool& pool=r.pool();
635:
1.76 paf 636: gdImage *image=static_cast<VImage *>(r.get_self())->image;
1.13 paf 637: if(!image)
1.68 paf 638: throw Exception(0,
1.16 paf 639: &method_name,
1.13 paf 640: "does not contain an image");
641:
1.44 parser 642: Table *table=params->as_no_junction(1, "coordinates must not be code").get_table();
643: if(!table)
1.68 paf 644: throw Exception("parser.runtime",
1.44 parser 645: &method_name,
646: "coordinates must be table");
1.13 paf 647:
1.44 parser 648: gdImage::Point *all_p=(gdImage::Point *)pool.malloc(sizeof(gdImage::Point)*table->size());
649: gdImage::Point *add_p=all_p;
650: table->for_each(add_point, &add_p);
651: image->FilledPolygon(all_p, table->size(),
1.51 parser 652: image->Color(params->as_int(0, "color must be int", r)));
1.12 paf 653: }
654:
1.16 paf 655: // font
656:
657: #define Y(y)(y+index*height+1)
1.21 paf 658:
659: /// simple gdImage-based font storage & text output
1.16 paf 660: class Font : public Pooled {
661: public:
662:
1.38 parser 663: const static int letter_spacing;
1.35 parser 664: int height; ///< Font heigth
665: int monospace; ///< Default char width
666: int spacebarspace; ///< spacebar width
1.16 paf 667: gdImage& ifont;
668: const String& alphabet;
669:
670: Font(Pool& pool,
671: const String& aalphabet,
1.35 parser 672: gdImage& aifont, int aheight, int amonospace, int aspacebarspace) : Pooled(pool),
1.16 paf 673: alphabet(aalphabet),
1.35 parser 674: height(aheight), monospace(amonospace), spacebarspace(aspacebarspace),
1.16 paf 675: ifont(aifont) {
676: }
677:
678: /* ******************************** char ********************************** */
679:
680: int index_of(char ch) {
681: if(ch==' ') return -1;
682: return alphabet.pos(&ch, 1);
683: }
684:
685: int index_width(int index) {
686: if(index<0)
1.35 parser 687: return spacebarspace;
1.16 paf 688: int tr=ifont.GetTransparent();
1.35 parser 689: for(int x=ifont.SX()-1; x>=0; x--) {
1.16 paf 690: for(int y=0; y<height-1; y++)
691: if(ifont.GetPixel(x, Y(y))!=tr)
1.35 parser 692: return x+1;
1.16 paf 693: }
694: return 0;
695: }
696:
697: void index_display(gdImage& image, int x, int y, int index){
698: if(index>=0)
699: ifont.Copy(image, x, y, 0, Y(0), index_width(index), height-1);
700: }
701:
702: /* ******************************** string ********************************** */
1.47 parser 703:
704: int string_width(const String& s){
1.61 paf 705: const char *cstr=s.cstr();
1.16 paf 706: int result=0;
707: for(; *cstr; cstr++)
708: result+=index_width(index_of(*cstr));
709: return result;
710: }
711:
712: void string_display(gdImage& image, int x, int y, const String& s){
1.61 paf 713: const char *cstr=s.cstr();
1.16 paf 714: if(cstr) for(; *cstr; cstr++) {
715: int index=index_of(*cstr);
716: index_display(image, x, y, index);
1.38 parser 717: x+=letter_spacing + (monospace ? monospace : index_width(index));
1.16 paf 718: }
719: }
720:
721: };
1.38 parser 722: const int Font::letter_spacing=1;
1.35 parser 723:
1.17 paf 724: static void _font(Request& r, const String& method_name, MethodParams *params) {
1.16 paf 725: Pool& pool=r.pool();
726:
1.37 parser 727: const String& alphabet=params->as_string(0, "alphabet must not be code");
728: gdImage& image=*load(r, method_name, params->as_string(1, "file_name must not be code"));
1.51 parser 729: int spacebar_width=params->as_int(2, "spacebar_width must be int", r);
1.37 parser 730: int monospace_width;
731: if(params->size()>3) {
1.51 parser 732: monospace_width=params->as_int(3, "monospace_width must be int", r);
1.37 parser 733: if(!monospace_width)
734: monospace_width=image.SX();
735: } else
736: monospace_width=0;
1.16 paf 737:
1.37 parser 738: if(!alphabet.size())
1.68 paf 739: throw Exception("parser.runtime",
1.37 parser 740: &method_name,
741: "alphabet must not be empty");
742:
1.76 paf 743: static_cast<VImage *>(r.get_self())->font=new(pool) Font(pool,
1.37 parser 744: alphabet,
1.36 parser 745: image,
1.37 parser 746: image.SY() / alphabet.size(), monospace_width, spacebar_width);
1.16 paf 747: }
748:
1.17 paf 749: static void _text(Request& r, const String& method_name, MethodParams *params) {
1.16 paf 750: Pool& pool=r.pool();
751:
1.51 parser 752: int x=params->as_int(0, "x must be int", r);
753: int y=params->as_int(1, "y must be int", r);
1.36 parser 754: const String& s=params->as_string(2, "text must not be code");
1.16 paf 755:
1.76 paf 756: VImage& vimage=*static_cast<VImage *>(r.get_self());
1.16 paf 757: if(vimage.image)
758: if(vimage.font)
759: vimage.font->string_display(*vimage.image, x, y, s);
760: else
1.68 paf 761: throw Exception("parser.runtime",
1.16 paf 762: &method_name,
763: "set the font first");
764: else
1.68 paf 765: throw Exception(0,
1.16 paf 766: &method_name,
767: "does not contain an image");
768: }
769:
1.47 parser 770: static void _length(Request& r, const String& method_name, MethodParams *params) {
771: Pool& pool=r.pool();
772:
773: const String& s=params->as_string(0, "text must not be code");
774:
1.76 paf 775: VImage& vimage=*static_cast<VImage *>(r.get_self());
1.47 parser 776: if(vimage.image)
777: if(vimage.font) {
1.71 paf 778: r.write_no_lang(*new(pool) VInt(pool, vimage.font->string_width(s)));
1.47 parser 779: } else
1.68 paf 780: throw Exception("parser.runtime",
1.47 parser 781: &method_name,
782: "set the font first");
783: else
1.68 paf 784: throw Exception(0,
1.47 parser 785: &method_name,
786: "does not contain an image");
787: }
788:
1.48 parser 789: static void _arc(Request& r, const String& method_name, MethodParams *params) {
790: Pool& pool=r.pool();
791:
1.76 paf 792: gdImage *image=static_cast<VImage *>(r.get_self())->image;
1.48 parser 793: if(!image)
1.68 paf 794: throw Exception(0,
1.48 parser 795: &method_name,
796: "does not contain an image");
797:
798: image->Arc(
1.51 parser 799: params->as_int(0, "center_x must be int", r),
800: params->as_int(1, "center_y must be int", r),
801: params->as_int(2, "width must be int", r),
802: params->as_int(3, "height must be int", r),
803: params->as_int(4, "start degrees must be int", r),
804: params->as_int(5, "end degrees must be int", r),
805: image->Color(params->as_int(6, "cx must be int", r)));
1.48 parser 806: }
807:
1.49 parser 808: static void _sector(Request& r, const String& method_name, MethodParams *params) {
809: Pool& pool=r.pool();
810:
1.76 paf 811: gdImage *image=static_cast<VImage *>(r.get_self())->image;
1.49 parser 812: if(!image)
1.68 paf 813: throw Exception(0,
1.49 parser 814: &method_name,
815: "does not contain an image");
816:
817: image->Sector(
1.51 parser 818: params->as_int(0, "center_x must be int", r),
819: params->as_int(1, "center_y must be int", r),
820: params->as_int(2, "width must be int", r),
821: params->as_int(3, "height must be int", r),
822: params->as_int(4, "start degrees must be int", r),
823: params->as_int(5, "end degrees must be int", r),
824: image->Color(params->as_int(6, "color must be int", r)));
1.49 parser 825: }
826:
1.48 parser 827: static void _circle(Request& r, const String& method_name, MethodParams *params) {
828: Pool& pool=r.pool();
829:
1.76 paf 830: gdImage *image=static_cast<VImage *>(r.get_self())->image;
1.48 parser 831: if(!image)
1.68 paf 832: throw Exception(0,
1.48 parser 833: &method_name,
834: "does not contain an image");
835:
1.51 parser 836: int size=params->as_int(2, "radius must be int", r)*2;
1.48 parser 837: image->Arc(
1.51 parser 838: params->as_int(0, "center_x must be int", r),
839: params->as_int(1, "center_y must be int", r),
1.50 parser 840: size, //w
841: size, //h
1.48 parser 842: 0, //s
843: 360, //e
1.51 parser 844: image->Color(params->as_int(3, "color must be int", r)));
1.48 parser 845: }
846:
1.53 parser 847: gdImage& as_image(Pool& pool, const String& method_name, MethodParams *params,
848: int index, const char *msg) {
1.75 paf 849: gdImage *src=0;
850:
1.53 parser 851: Value& value=params->as_no_junction(index, msg);
852:
1.75 paf 853: if(Value *vimage=value.as(VIMAGE_TYPE, false)) {
854: src=static_cast<VImage *>(vimage)->image;
855: if(!src)
856: throw Exception("parser.runtime",
857: &method_name,
858: msg);
859: } else
1.68 paf 860: throw Exception("parser.runtime",
1.53 parser 861: &method_name,
862: msg);
863:
864: return *src;
865: }
866:
867: static void _copy(Request& r, const String& method_name, MethodParams *params) {
868: Pool& pool=r.pool();
869:
1.76 paf 870: gdImage *dest=static_cast<VImage *>(r.get_self())->image;
1.53 parser 871: if(!dest)
1.68 paf 872: throw Exception(0,
1.53 parser 873: &method_name,
874: "self does not contain an image");
875:
876: gdImage& src=as_image(pool, method_name, params, 0, "src must be image");
877:
878: int sx=params->as_int(1, "src_x must be int", r);
879: int sy=params->as_int(2, "src_y must be int", r);
880: int sw=params->as_int(3, "src_w must be int", r);
881: int sh=params->as_int(4, "src_h must be int", r);
882: int dx=params->as_int(5, "dest_x must be int", r);
883: int dy=params->as_int(6, "dest_y must be int", r);
884: if(params->size()>1+2+2+2) {
885: int dw=params->as_int(1+2+2+2, "dest_w must be int", r);
1.56 parser 886: int dh=(int)(params->size()>1+2+2+2+1?
887: params->as_int(1+2+2+2+1, "dest_h must be int", r):sh*(((double)dw)/((double)sw)));
888: int tolerance=params->size()>1+2+2+2+2?
889: params->as_int(1+2+2+2+2, "tolerance must be int", r):150;
1.53 parser 890:
1.56 parser 891: src.CopyResampled(*dest, dx, dy, sx, sy, dw, dh, sw, sh, tolerance);
1.53 parser 892: } else
1.54 parser 893: src.Copy(*dest, dx, dy, sx, sy, sw, sh);
1.53 parser 894: }
895:
896:
1.22 paf 897: // constructor
898:
1.71 paf 899: MImage::MImage(Pool& apool) : Methoded(apool, "image") {
1.1 paf 900: // ^image:measure[DATA]
1.22 paf 901: add_native_method("measure", Method::CT_DYNAMIC, _measure, 1, 1);
1.3 paf 902:
1.25 paf 903: // ^image.html[]
904: // ^image.html[hash]
1.22 paf 905: add_native_method("html", Method::CT_DYNAMIC, _html, 0, 1);
1.6 paf 906:
1.25 paf 907: // ^image.load[background.gif]
1.22 paf 908: add_native_method("load", Method::CT_DYNAMIC, _load, 1, 1);
1.6 paf 909:
1.25 paf 910: // ^image.create[width;height] bgcolor=white
911: // ^image.create[width;height;bgcolor]
1.22 paf 912: add_native_method("create", Method::CT_DYNAMIC, _create, 2, 3);
1.6 paf 913:
1.25 paf 914: // ^image.gif[]
1.22 paf 915: add_native_method("gif", Method::CT_DYNAMIC, _gif, 0, 0);
1.12 paf 916:
1.25 paf 917: // ^image.line(x0;y0;x1;y1;color)
1.22 paf 918: add_native_method("line", Method::CT_DYNAMIC, _line, 5, 5);
1.13 paf 919:
1.25 paf 920: // ^image.fill(x;y;color)
1.22 paf 921: add_native_method("fill", Method::CT_DYNAMIC, _fill, 3, 3);
1.13 paf 922:
1.25 paf 923: // ^image.rectangle(x0;y0;x1;y1;color)
1.22 paf 924: add_native_method("rectangle", Method::CT_DYNAMIC, _rectangle, 5, 5);
1.13 paf 925:
1.25 paf 926: // ^image.bar(x0;y0;x1;y1;color)
1.22 paf 927: add_native_method("bar", Method::CT_DYNAMIC, _bar, 5, 5);
1.13 paf 928:
1.44 parser 929: // ^image.replace(color-source;color-dest)[table x:y]
930: add_native_method("replace", Method::CT_DYNAMIC, _replace, 3, 3);
931:
932: // ^image.polyline(color)[table x:y]
933: add_native_method("polyline", Method::CT_DYNAMIC, _polyline, 2, 2);
1.13 paf 934:
1.44 parser 935: // ^image.polygon(color)[table x:y]
936: add_native_method("polygon", Method::CT_DYNAMIC, _polygon, 2, 2);
1.13 paf 937:
1.44 parser 938: // ^image.polybar(color)[table x:y]
939: add_native_method("polybar", Method::CT_DYNAMIC, _polybar, 2, 2);
1.13 paf 940:
1.36 parser 941: // ^image.font[alPHAbet;font-file-name.gif](spacebar_width)
942: // ^image.font[alPHAbet;font-file-name.gif](spacebar_width;width)
943: add_native_method("font", Method::CT_DYNAMIC, _font, 3, 4);
1.16 paf 944:
1.25 paf 945: // ^image.text(x;y)[text]
1.22 paf 946: add_native_method("text", Method::CT_DYNAMIC, _text, 3, 3);
1.47 parser 947:
948: // ^image.ngth[text]
949: add_native_method("length", Method::CT_DYNAMIC, _length, 1, 1);
1.16 paf 950:
1.48 parser 951: // ^image.arc(center x;center y;width;height;start in degrees;end in degrees;color)
952: add_native_method("arc", Method::CT_DYNAMIC, _arc, 7, 7);
1.49 parser 953:
954: // ^image.sector(center x;center y;width;height;start in degrees;end in degrees;color)
955: add_native_method("sector", Method::CT_DYNAMIC, _sector, 7, 7);
1.48 parser 956:
957: // ^image.circle(center x;center y;r;color)
958: add_native_method("circle", Method::CT_DYNAMIC, _circle, 4, 4);
959:
1.56 parser 960: // ^image.copy[source](src x;src y;src w;src h;dst x;dst y[;dest w[;dest h[;tolerance]]])
961: add_native_method("copy", Method::CT_DYNAMIC, _copy, 1+2+2+2, (1+2+2+2)+2+1);
1.22 paf 962: }
963:
964: // global variable
965:
966: Methoded *image_class;
967:
968: // creator
969:
970: Methoded *MImage_create(Pool& pool) {
971: return image_class=new(pool) MImage(pool);
1.1 paf 972: }
E-mail: