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