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