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