Annotation of parser3/src/classes/image.C, revision 1.14
1.1 paf 1: /** @file
2: Parser: @b image parser class.
3:
4: Copyright (c) 2001 ArtLebedev Group (http://www.artlebedev.com)
5:
6: Author: Alexander Petrosyan <paf@design.ru> (http://design.ru/paf)
7:
1.14 ! paf 8: $Id: image.C,v 1.13 2001/04/12 14:07:33 paf Exp $
1.1 paf 9: */
10:
11: #include "pa_config_includes.h"
12:
1.6 paf 13: #include <stdio.h>
14:
1.8 paf 15: #ifdef WIN32
16: # include "smtp/smtp.h"
17: #endif
18:
19: #include "gif.h"
1.6 paf 20:
1.1 paf 21: #include "pa_common.h"
22: #include "pa_request.h"
23: #include "pa_vfile.h"
24: #include "pa_vimage.h"
25:
26: // global var
27:
28: VStateless_class *image_class;
29:
30: // helpers
31:
32: class Measure_reader {
33: public:
1.3 paf 34: enum { READ_CHUNK_SIZE=0x400 }; // 1K
1.1 paf 35: typedef size_t (*Func)(void *& buf, size_t limit, void *info);
36:
37: Measure_reader(Func afunc, void *ainfo) :
38: func(afunc), info(ainfo),
39: chunk(0), offset(0), size(0) {
40: }
41:
42: size_t read(unsigned char *& buf, size_t limit) {
1.3 paf 43: if(offset+limit>size) // nothing left
44: if(offset==0 || limit==1) { // only one-byte continuations allowed
45: size=(*func)(chunk, READ_CHUNK_SIZE, info);
46: offset=0;
47: } else
48: return 0; // as if EOF
1.1 paf 49: if(!size) // EOF
50: return 0;
51:
52: // something left
53: size_t read_size=min(offset+limit, size)-offset;
54: buf=((unsigned char *)chunk)+offset;
55: offset+=read_size;
56: return read_size;
57: }
58:
59: private:
60: Func func;
61: void *info;
62:
63: void *chunk;
64: size_t offset;
65: size_t size;
66: };
67:
68: // GIF
69: struct GIF_Header {
70: char type[3]; // 'GIF'
71: char version[3];
72: unsigned char width[2];
73: unsigned char height[2];
74: char dif;
75: char fonColor;
76: char nulls;
77: };
78:
79: // JPEG
80: struct JFIF_Header {
81: char length[2]; // length of JFIF segment marker
82: char identifier[5]; // JFIF identifier
83: char version[2]; // version
84: char units; // units X of Y pixel density
85: char xdensity[2]; // X pixel density
86: char ydensity[2]; // X pixel density
87: char xthumbnails; // width of thumbnails
88: char ythumbnails; // height of thumbnails
89: char reserved; // reserved
90: };
91: struct JPG_Frame {
92: char length[2]; // length of image marker
93: char data; // data precision of bits/sample
94: char height[2]; // image height
95: char width[2]; // image width
96: char numComponents; // number of color components
97: };
98:
99: //
100:
101: inline short bytes_to_int(unsigned char HI, unsigned char LO) {
102: return (short)((HI<<8) + LO);
103: }
104:
105: void measure_gif(Pool& pool, const String *origin_string,
106: Measure_reader& reader, int& width, int& height) {
107:
108: unsigned char *buf;
109: const int head_size=sizeof(GIF_Header);
110: if(reader.read(buf, head_size)<head_size)
111: PTHROW(0, 0,
112: origin_string,
113: "broken GIF header - file size is less then %d bytes", head_size);
114:
115: GIF_Header& screenD=*reinterpret_cast<GIF_Header *>(buf);
116: if(strncmp(screenD.type, "GIF", 3)!=0)
117: PTHROW(0, 0,
118: origin_string,
119: "bad image file - GIF signature not found");
120:
121: width=bytes_to_int(screenD.width[1], screenD.width[0]);
122: height=bytes_to_int(screenD.height[1], screenD.height[0]);
123: }
124:
125: void measure_jpeg(Pool& pool, const String *origin_string,
126: Measure_reader& reader, int& width, int& height) {
1.2 paf 127: // JFIF format markers
128: const unsigned char SOI=0xD8;
129: const unsigned char EOI=0xD9;
130: const unsigned char APP0=0xE0;
131: const unsigned char SOF0=0xC0;
132: const unsigned char SOF2=0xC2;
133: const unsigned char COM=0xFE;
134:
1.1 paf 135: unsigned char *screenD_buf;
1.3 paf 136: unsigned char *h_buf=0;
1.1 paf 137:
138: bool flag=false;
139:
140: unsigned char *prefix;
141: const prefix_size=2;
142: if(reader.read(prefix, prefix_size)<prefix_size)
143: PTHROW(0, 0,
144: origin_string,
145: "broken JPEG file - size is less then %d bytes", prefix_size);
146:
147: if(((unsigned char *)prefix)[1]!=SOI)
148: PTHROW(0, 0,
149: origin_string,
150: "broken JPEG file - second byte of header is not 0x%02X", SOI);
151:
152: unsigned char zero=0;
153: unsigned char *marker=&zero;
154:
155: do {
156: while((*marker)!=0xFF)
157: if(reader.read(marker, sizeof(char))<=0) break;
158: if(reader.read(marker, sizeof(char))<=0) break;
159: switch(*marker) {
160: case EOI:
161: marker=&zero;
162: break;
163: case APP0:
164: if(!flag) {
165: flag=true;
166: if(reader.read(screenD_buf, sizeof(JFIF_Header)) < sizeof(JFIF_Header))
167: break;
168: JFIF_Header& screenD=*reinterpret_cast<JFIF_Header *>(screenD_buf);
169: if((bytes_to_int(screenD.length[0], screenD.length[1]) < 16) ||
1.2 paf 170: strcasecmp(screenD.identifier, "JFIF")) flag=false;
1.1 paf 171: }
172: break;
173: case SOF0:
174: case SOF2:
175: if(reader.read(h_buf, sizeof(JPG_Frame))<sizeof(JPG_Frame))
176: flag=false;
177: break;
178: default: break;
179: }
180: } while(*marker!=EOI);
181:
1.3 paf 182: if(flag && h_buf) {
1.1 paf 183: JPG_Frame& h=*reinterpret_cast<JPG_Frame *>(h_buf);
184: width=bytes_to_int(h.width[0], h.width[1]);
185: height=bytes_to_int(h.height[0], h.height[1]);
186: } else
187: PTHROW(0, 0,
188: origin_string,
189: "broken JPEG file - APP0 frame not found");
190: }
191:
192: // measure center
193:
194: void measure(Pool& pool, const String& file_name,
195: Measure_reader& reader, int& width, int& height) {
196: if(const char *cext=strrchr(file_name.cstr(), '.')) {
197: cext++;
198: if(strcasecmp(cext, "GIF")==0)
199: measure_gif(pool, &file_name, reader, width, height);
200: else if(strcasecmp(cext, "JPG")==0 || strcasecmp(cext, "JPEG")==0)
201: measure_jpeg(pool, &file_name, reader, width, height);
202: else
203: PTHROW(0, 0,
204: &file_name,
205: "unhandled image file name extension '%s'", cext);
206: } else
207: PTHROW(0, 0,
208: &file_name,
209: "can not determine image type - no file name extension");
210: }
211:
212: // read from somewhere
213:
214: struct Read_mem_info {
215: unsigned char *ptr;
216: unsigned char *eof;
217: };
218: static size_t read_mem(void*& buf, size_t limit, void *info) {
219: Read_mem_info& rmi=*static_cast<Read_mem_info *>(info);
220: buf=rmi.ptr;
221: size_t read_size=min(limit, (size_t)(rmi.eof-rmi.ptr));
222: rmi.ptr+=read_size;
223: return read_size;
224: }
225:
226: struct Read_disk_info {
227: const String *file_spec;
228: size_t offset;
229: };
230: static size_t read_disk(void*& buf, size_t limit, void *info) {
231: Read_disk_info& rdi=*static_cast<Read_disk_info *>(info);
232: Pool& pool=rdi.file_spec->pool();
233:
234: size_t read_size;
235: file_read(pool, *rdi.file_spec,
236: buf, read_size,
237: false/*as_text*/,
238: true/*fail_on_read_problem*/,
239: rdi.offset, limit);
240:
241: rdi.offset+=read_size;
242: return read_size;
243: }
244:
245: // methods
246:
247: /// ^image:measure[DATA]
248: static void _measure(Request& r, const String& method_name, Array *params) {
249: Pool& pool=r.pool();
250:
251: Value& data=*static_cast<Value *>(params->get(0));
252: // forcing [this body type]
253: r.fail_if_junction_(true, data, method_name, "data must not be code");
254:
255: void *info; Measure_reader::Func read_func;
256: Read_mem_info read_mem_info;
257: Read_disk_info read_disk_info;
258: const String *file_name;
259: if(data.is_string()) {
260: file_name=data.get_string();
261: read_disk_info.file_spec=&r.absolute(*file_name);
262: read_disk_info.offset=0;
263: info=&read_disk_info; read_func=read_disk;
264: } else {
265: const VFile& vfile=*data.as_vfile();
266: file_name=&static_cast<Value *>(vfile.fields().get(*name_name))->as_string();
267: read_mem_info.ptr=(unsigned char *)vfile.value_ptr();
268: read_mem_info.eof=read_mem_info.ptr+vfile.value_size();
269: info=&read_mem_info; read_func=read_mem;
270: }
271:
272: Measure_reader reader(read_func, info);
273: int width, height;
274: measure(pool, *file_name, reader, width, height);
275:
1.6 paf 276: static_cast<VImage *>(r.self)->set(file_name, width, height);
1.1 paf 277: }
278:
1.4 paf 279: struct Attrib_info {
280: String *tag;
281: Hash *skip;
282: };
1.3 paf 283: static void append_attrib_pair(const Hash::Key& key, Hash::Val *val, void *info) {
1.4 paf 284: Attrib_info& ai=*static_cast<Attrib_info *>(info);
285:
286: if(ai.skip && ai.skip->get(key))
287: return;
288:
1.3 paf 289: Value& value=*static_cast<Value *>(val);
290: // src="a.gif" width=123 ismap[=-1]
1.4 paf 291: *ai.tag << " " << key;
1.6 paf 292: if(value.is_string() || value.as_double()>=0)
293: *ai.tag << "=\"" << value.as_string() << "\"";
1.3 paf 294: }
295: /// ^image.html[]
296: /// ^image.html[hash]
297: static void _html(Request& r, const String& method_name, Array *params) {
298: Pool& pool=r.pool();
299:
300: String tag(pool);
301: tag << "<img";
1.4 paf 302:
303: Hash& fields=static_cast<VImage *>(r.self)->fields();
1.5 paf 304: Hash *attribs=0;
1.4 paf 305:
1.11 paf 306: if(params->size())
1.5 paf 307: if(attribs=static_cast<Value *>(params->get(0))->get_hash()) {
1.4 paf 308: Attrib_info attrib_info={&tag, 0};
1.5 paf 309: attribs->for_each(append_attrib_pair, &attrib_info);
1.4 paf 310: } else
311: PTHROW(0, 0,
312: &method_name,
1.5 paf 313: "attributes must be must be hash");
1.4 paf 314:
1.5 paf 315: Attrib_info attrib_info={&tag, attribs};
1.4 paf 316: fields.for_each(append_attrib_pair, &attrib_info);
1.6 paf 317: tag << " />";
1.3 paf 318: r.write_pass_lang(tag);
319: }
1.8 paf 320:
1.6 paf 321: /// ^image.load[background.gif]
322: static void _load(Request& r, const String& method_name, Array *params) {
323: Pool& pool=r.pool();
324:
325: Value& vfile_name=*static_cast<Value *>(params->get(0));
326: // forcing [this body type]
327: r.fail_if_junction_(true, vfile_name, method_name, "file name must not be code");
328: const String& file_name=vfile_name.as_string();
329:
330: const char *file_name_cstr=r.absolute(file_name).cstr(String::UL_FILE_NAME);
1.9 paf 331: gdImage image(pool);
1.6 paf 332: if(FILE *f=fopen(file_name_cstr, "rb")) {
1.9 paf 333: image.CreateFromGif(f);
334: int width=image.SX();
335: int height=image.SY();
1.6 paf 336: fclose(f);
337:
1.9 paf 338: static_cast<VImage *>(r.self)->set(&file_name, width, height, &image);
1.6 paf 339: } else
340: PTHROW(0, 0,
341: &method_name,
342: "can not open background image '%s'", file_name_cstr);
343: }
344:
345: /// ^image.create[width;height] bgcolor=white
346: /// ^image.create[width;height;bgcolor]
347: static void _create(Request& r, const String& method_name, Array *params) {
348: Pool& pool=r.pool();
349:
350: int width=(int)r.process(*static_cast<Value *>(params->get(0))).as_double();
351: int height=(int)r.process(*static_cast<Value *>(params->get(1))).as_double();
1.8 paf 352: int bgcolor_value=0xffFFff;
1.6 paf 353: if(params->size()>2)
354: bgcolor_value=
355: (int)r.process(*static_cast<Value *>(params->get(2))).as_double();
1.8 paf 356: gdImage *image=new(pool) gdImage(pool);
357: image->Create(width, height);
358: image->FilledRectangle(0, 0, width-1, height-1, image->Color(bgcolor_value));
359: static_cast<VImage *>(r.self)->set(0, width, height, image);
1.6 paf 360: }
361:
362: /// ^image.gif[]
363: static void _gif(Request& r, const String& method_name, Array *params) {
364: Pool& pool=r.pool();
365:
1.9 paf 366: gdImage *image=static_cast<VImage *>(r.self)->image;
1.8 paf 367: if(!image)
1.6 paf 368: PTHROW(0, 0,
369: &method_name,
1.12 paf 370: "does not contain an image");
1.6 paf 371:
372: // could _ but don't thing it's wise to use $image.src for vfile.name
1.10 paf 373:
374: String out(pool); image->Gif(out);
1.6 paf 375:
376: VFile& vfile=*new(pool) VFile(pool);
377: String& image_gif=*new(pool) String(pool, "image/gif");
1.14 ! paf 378: vfile.set(false/*not tainted*/, out.cstr(), out.size(), 0, &image_gif);
1.6 paf 379:
380: r.write_no_lang(vfile);
381: }
382:
1.12 paf 383: /// ^image.line(x0;y0;x1;y1;color)
384: static void _line(Request& r, const String& method_name, Array *params) {
385: Pool& pool=r.pool();
386:
387: gdImage *image=static_cast<VImage *>(r.self)->image;
388: if(!image)
389: PTHROW(0, 0,
390: &method_name,
391: "does not contain an image");
392:
393: image->Line(
394: (int)r.process(*static_cast<Value *>(params->get(0))).as_double(),
395: (int)r.process(*static_cast<Value *>(params->get(1))).as_double(),
396: (int)r.process(*static_cast<Value *>(params->get(2))).as_double(),
397: (int)r.process(*static_cast<Value *>(params->get(3))).as_double(),
398: image->Color((int)r.process(*static_cast<Value *>(params->get(4))).as_double()));
1.13 paf 399: }
400:
401: /// ^image.fill(x;y;color)
402: static void _fill(Request& r, const String& method_name, Array *params) {
403: Pool& pool=r.pool();
404:
405: gdImage *image=static_cast<VImage *>(r.self)->image;
406: if(!image)
407: PTHROW(0, 0,
408: &method_name,
409: "does not contain an image");
1.12 paf 410:
1.13 paf 411: image->Fill(
412: (int)r.process(*static_cast<Value *>(params->get(0))).as_double(),
413: (int)r.process(*static_cast<Value *>(params->get(1))).as_double(),
414: image->Color((int)r.process(*static_cast<Value *>(params->get(2))).as_double()));
415: }
416:
417: /// ^image.rectangle(x0;y0;x1;y1;color)
418: static void _rectangle(Request& r, const String& method_name, Array *params) {
419: Pool& pool=r.pool();
420:
421: gdImage *image=static_cast<VImage *>(r.self)->image;
422: if(!image)
423: PTHROW(0, 0,
424: &method_name,
425: "does not contain an image");
426:
427: image->Rectangle(
428: (int)r.process(*static_cast<Value *>(params->get(0))).as_double(),
429: (int)r.process(*static_cast<Value *>(params->get(1))).as_double(),
430: (int)r.process(*static_cast<Value *>(params->get(2))).as_double(),
431: (int)r.process(*static_cast<Value *>(params->get(3))).as_double(),
432: image->Color((int)r.process(*static_cast<Value *>(params->get(4))).as_double()));
433: }
434:
435: /// ^image.bar(x0;y0;x1;y1;color)
436: static void _bar(Request& r, const String& method_name, Array *params) {
437: Pool& pool=r.pool();
438:
439: gdImage *image=static_cast<VImage *>(r.self)->image;
440: if(!image)
441: PTHROW(0, 0,
442: &method_name,
443: "does not contain an image");
444:
445: image->FilledRectangle(
446: (int)r.process(*static_cast<Value *>(params->get(0))).as_double(),
447: (int)r.process(*static_cast<Value *>(params->get(1))).as_double(),
448: (int)r.process(*static_cast<Value *>(params->get(2))).as_double(),
449: (int)r.process(*static_cast<Value *>(params->get(3))).as_double(),
450: image->Color((int)r.process(*static_cast<Value *>(params->get(4))).as_double()));
451: }
452:
453: /// ^image.replace(color-source;color-dest)(x;y)... point coord pairs
454: static void _replace(Request& r, const String& method_name, Array *params) {
455: Pool& pool=r.pool();
456:
457: gdImage *image=static_cast<VImage *>(r.self)->image;
458: if(!image)
459: PTHROW(0, 0,
460: &method_name,
461: "does not contain an image");
462:
463: if((params->size()-2)%2) // I see your thoughts, but that's more readable
464: PTHROW(0, 0,
465: &method_name,
466: "y coordinate missing");
467:
468: int n=(params->size()-2)/2;
469:
470: gdImage::Point *p=(gdImage::Point *)pool.malloc(sizeof(gdImage::Point)*n);
471: for(int i=0; i<n; i++) {
472: p[i].x=(int)r.process(*static_cast<Value *>(params->get(2+i*2+0))).as_double();
473: p[i].y=(int)r.process(*static_cast<Value *>(params->get(2+i*2+1))).as_double();
474: }
475: image->FilledPolygonReplaceColor(p, n,
476: image->Color((int)r.process(*static_cast<Value *>(params->get(0))).as_double()), // src color
477: image->Color((int)r.process(*static_cast<Value *>(params->get(1))).as_double())); // dest color
478: }
479:
480: /// ^image.polygon(color)(x;y)... point coord pairs
481: static void _polygon(Request& r, const String& method_name, Array *params) {
482: Pool& pool=r.pool();
483:
484: gdImage *image=static_cast<VImage *>(r.self)->image;
485: if(!image)
486: PTHROW(0, 0,
487: &method_name,
488: "does not contain an image");
489:
490: if((params->size()-1)%2) // [I see..] see now?
491: PTHROW(0, 0,
492: &method_name,
493: "y coordinate missing");
494:
495: int n=(params->size()-1)/2;
496:
497: gdImage::Point *p=(gdImage::Point *)pool.malloc(sizeof(gdImage::Point)*n);
498: for(int i=0; i<n; i++) {
499: p[i].x=(int)r.process(*static_cast<Value *>(params->get(1+i*2+0))).as_double();
500: p[i].y=(int)r.process(*static_cast<Value *>(params->get(1+i*2+1))).as_double();
501: }
502: image->Polygon(p, n,
503: image->Color((int)r.process(*static_cast<Value *>(params->get(0))).as_double()));
504: }
505:
506: /// ^image.polybar(color)(x;y)... point coord pairs
507: static void _polybar(Request& r, const String& method_name, Array *params) {
508: Pool& pool=r.pool();
509:
510: gdImage *image=static_cast<VImage *>(r.self)->image;
511: if(!image)
512: PTHROW(0, 0,
513: &method_name,
514: "does not contain an image");
515:
516: if((params->size()-1)%2) // [I see..] see now?
517: PTHROW(0, 0,
518: &method_name,
519: "y coordinate missing");
520:
521: int n=(params->size()-1)/2;
522:
523: gdImage::Point *p=(gdImage::Point *)pool.malloc(sizeof(gdImage::Point)*n);
524: for(int i=0; i<n; i++) {
525: p[i].x=(int)r.process(*static_cast<Value *>(params->get(1+i*2+0))).as_double();
526: p[i].y=(int)r.process(*static_cast<Value *>(params->get(1+i*2+1))).as_double();
527: }
528: image->FilledPolygon(p, n,
529: image->Color((int)r.process(*static_cast<Value *>(params->get(0))).as_double()));
1.12 paf 530: }
531:
1.1 paf 532: // initialize
533: void initialize_image_class(Pool& pool, VStateless_class& vclass) {
534: // ^image:measure[DATA]
535: vclass.add_native_method("measure", Method::CT_DYNAMIC, _measure, 1, 1);
1.3 paf 536:
537: /// ^image.html[]
538: /// ^image.html[hash]
539: vclass.add_native_method("html", Method::CT_DYNAMIC, _html, 0, 1);
1.6 paf 540:
541: /// ^image.load[background.gif]
1.8 paf 542: vclass.add_native_method("load", Method::CT_DYNAMIC, _load, 1, 1);
1.6 paf 543:
544: /// ^image.create[width;height] bgcolor=white
545: /// ^image.create[width;height;bgcolor]
546: vclass.add_native_method("create", Method::CT_DYNAMIC, _create, 2, 3);
547:
548: /// ^image.gif[]
1.14 ! paf 549: vclass.add_native_method("gif", Method::CT_DYNAMIC, _gif, 0, 0);
1.12 paf 550:
551: /// ^image.line(x0;y0;x1;y1;color)
552: vclass.add_native_method("line", Method::CT_DYNAMIC, _line, 5, 5);
1.13 paf 553:
554: /// ^image.fill(x;y;color)
555: vclass.add_native_method("fill", Method::CT_DYNAMIC, _fill, 3, 3);
556:
557: /// ^image.rectangle(x0;y0;x1;y1;color)
558: vclass.add_native_method("rectangle", Method::CT_DYNAMIC, _rectangle, 5, 5);
559:
560: /// ^image.bar(x0;y0;x1;y1;color)
561: vclass.add_native_method("bar", Method::CT_DYNAMIC, _bar, 5, 5);
562:
563: /// ^image.replace(color-source;color-dest)(x;y)... point coord pairs
564: vclass.add_native_method("replace", Method::CT_DYNAMIC, _replace, 2+3*2, 2+100*2);
565:
566: /// ^image.polygon(color)(x;y)... point coord pairs
567: vclass.add_native_method("polygon", Method::CT_DYNAMIC, _polygon, 1+3*2, 1+100*2);
568:
569: /// ^image.polybar(color)(x;y)... point coord pairs
570: vclass.add_native_method("polybar", Method::CT_DYNAMIC, _polybar, 1+3*2, 1+100*2);
571:
1.1 paf 572: }
E-mail: