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