Annotation of parser3/src/classes/image.C, revision 1.90.2.2
1.1 paf 1: /** @file
2: Parser: @b image parser class.
3:
1.90 paf 4: Copyright(c) 2001, 2003 ArtLebedev Group (http://www.artlebedev.com)
1.66 paf 5: Author: Alexandr Petrosian <paf@design.ru> (http://paf.design.ru)
1.73 paf 6: */
1.1 paf 7:
1.90.2.2! paf 8: static const char* IDENT_IMAGE_C="$Date: 2003/01/23 15:38:04 $";
1.31 parser 9:
10: /*
11: jpegsize: gets the width and height (in pixels) of a jpeg file
12: Andrew Tong, werdna@ugcs.caltech.edu February 14, 1995
13: modified slightly by alex@ed.ac.uk
14: and further still by rjray@uswest.com
15: optimization and general re-write from tmetro@vl.com
16: from perl by paf@design.ru
1.1 paf 17: */
18:
19: #include "pa_config_includes.h"
20:
1.8 paf 21: #include "gif.h"
1.6 paf 22:
1.1 paf 23: #include "pa_common.h"
24: #include "pa_request.h"
25: #include "pa_vfile.h"
26: #include "pa_vimage.h"
1.89 paf 27: #include "pa_vdate.h"
1.1 paf 28:
1.22 paf 29: // class
30:
31: class MImage : public Methoded {
32: public: // VStateless_class
1.90.2.2! paf 33: ValuePtr create_new_value() { return ValuePtr(new VImage()); }
1.22 paf 34:
35: public:
36: MImage(Pool& pool);
1.24 paf 37:
38: public: // Methoded
1.22 paf 39: bool used_directly() { return true; }
40:
41: };
1.1 paf 42:
43: // helpers
44:
1.90.2.1 paf 45: /// value of exif tag -> it's value
46: Hash<int, const char *> exif_tag_value2name;
47:
48:
1.43 parser 49: #ifndef DOXYGEN
1.1 paf 50: class Measure_reader {
51: public:
1.77 paf 52: virtual size_t read(const void *&buf, size_t limit)=0;
1.79 paf 53: virtual void seek(long value, int whence)=0;
54: virtual long tell()=0;
1.77 paf 55: };
1.1 paf 56:
1.77 paf 57: class Measure_file_reader: public Measure_reader {
58: public:
59: Measure_file_reader(Pool& apool, int af, const String& afile_name, const char *afname):
60: pool(apool), file_name(afile_name), fname(afname), f(af) {
1.1 paf 61: }
62:
1.77 paf 63: /*override*/size_t read(const void *&abuf, size_t limit) {
64: if(limit==0)
1.1 paf 65: return 0;
1.77 paf 66:
67: void *lbuf=pool.malloc(limit);
68: size_t read_size=(size_t)::read(f, lbuf, limit); abuf=lbuf;
69: if(ssize_t(read_size)<0 || read_size>limit)
70: throw Exception(0,
71: &file_name,
72: "measure failed: actually read %lu bytes count not in [0..%lu] valid range",
73: read_size, limit);
74:
1.1 paf 75: return read_size;
76: }
77:
1.79 paf 78: /*override*/void seek(long value, int whence) {
79: if(lseek(f, value, whence)<0)
1.88 paf 80: throw Exception("image.format",
1.77 paf 81: &file_name,
1.79 paf 82: "seek(value=%ld, whence=%d) failed: %s (%d), actual filename '%s'",
83: value, whence, strerror(errno), errno, fname);
1.77 paf 84: }
85:
1.81 paf 86: /*override*/long tell() { return lseek(f, 0, SEEK_CUR); }
1.79 paf 87:
1.1 paf 88: private:
1.77 paf 89: Pool& pool;
90: const String& file_name; const char *fname;
91: int f;
92: };
93:
94: class Measure_buf_reader: public Measure_reader {
95: public:
96: Measure_buf_reader(const void *abuf, size_t asize, const String& afile_name):
97: buf(abuf), size(asize), file_name(afile_name), offset(0) {
98: }
99:
100: /*override*/size_t read(const void *&abuf, size_t limit) {
101: size_t to_read=min(limit, size-offset);
102: abuf=(const char*)buf+offset;
103: offset+=to_read;
104: return to_read;
105: }
106:
1.79 paf 107: /*override*/void seek(long value, int whence) {
108: size_t new_offset;
109: switch(whence) {
110: case SEEK_CUR: new_offset=offset+value; break;
111: case SEEK_SET: new_offset=(size_t)value; break;
1.88 paf 112: default: throw Exception(0, 0, "whence #%d not supported", 0, whence); break; // never
1.79 paf 113: }
114:
1.77 paf 115: if((ssize_t)new_offset<0 || new_offset>size)
1.88 paf 116: throw Exception("image.format",
1.77 paf 117: &file_name,
1.79 paf 118: "seek(value=%l, whence=%d) failed: out of buffer, new_offset>size (%l>%l) or new_offset<0",
119: value, whence, new_offset, size);
1.77 paf 120: offset=new_offset;
121: }
122:
1.79 paf 123: /*override*/long tell() { return offset; }
124:
1.77 paf 125: private:
126:
127: const void *buf; size_t size;
128: const String& file_name;
1.1 paf 129:
130: size_t offset;
131: };
1.77 paf 132:
1.43 parser 133: #endif
1.1 paf 134:
1.72 paf 135: /// PNG file header
136: struct PNG_Header {
137: char dummy[12];
138: char signature[4]; //< must be "IHDR"
1.80 paf 139: uchar high_width[2]; //< image width high bytes [we ignore for now]
140: uchar width[2]; //< image width low bytes
141: uchar high_height[2]; //< image height high bytes [we ignore for now]
142: uchar height[4]; //< image height
1.72 paf 143: };
144:
1.21 paf 145: /// GIF file header
1.1 paf 146: struct GIF_Header {
1.72 paf 147: char signature[3]; // 'GIF'
1.1 paf 148: char version[3];
1.80 paf 149: uchar width[2];
150: uchar height[2];
1.1 paf 151: char dif;
152: char fonColor;
153: char nulls;
154: };
155:
1.31 parser 156: /// JPEG record head
157: struct JPG_Segment_head {
1.80 paf 158: uchar marker;
159: uchar code;
160: uchar length[2];
1.1 paf 161: };
1.21 paf 162: /// JPEG frame header
1.31 parser 163: struct JPG_Size_segment_body {
1.27 parser 164: char data; //< data precision of bits/sample
1.80 paf 165: uchar height[2]; //< image height
166: uchar width[2]; //< image width
1.27 parser 167: char numComponents; //< number of color components
1.1 paf 168: };
169:
1.79 paf 170: /// JPEG frame header
171: struct JPG_Exif_segment_start {
172: char signature[6]; // Exif\0\0
173: };
174:
1.80 paf 175: /// JPEG Exif TIFF Header
176: struct JPG_Exif_TIFF_header {
177: uchar byte_align_identifier[2];
178: char dummy[2]; // always 000A [or 0A00]
179: uchar first_IFD_offset[4]; // Usually the first IFD starts immediately next to TIFF header, so this offset has value '0x00000008'.
180: };
181:
182: // JPEG Exif IFD start
183: struct JPG_Exif_IFD_start {
184: uchar directory_entry_count[2]; // the number of directory entry contains in this IFD
185: };
186:
187: // TTTT ffff NNNNNNNN DDDDDDDD
188: struct JPG_Exif_IFD_entry {
189: uchar tag[2]; // Tag number, this shows a kind of data
190: uchar format[2]; // data format
191: uchar components_count[4]; // number of components
192: uchar value_or_offset_to_it[4]; // data value or offset to data value
193: };
194:
195: #define JPG_IFD_TAG_EXIF_OFFSET 0x8769
196:
1.89 paf 197: #define JPEG_EXIF_DATE_CHARS 20
198:
1.1 paf 199: //
200:
1.80 paf 201: inline ushort x_endian_to_ushort(uchar b0, uchar b1) {
202: return (ushort)((b1<<8) + b0);
1.33 parser 203: }
204:
1.80 paf 205: inline uint x_endian_to_uint(uchar b0, uchar b1, uchar b2, uchar b3) {
206: return (uint)(((((b3<<8) + b2)<<8)+b1)<<8)+b0;
1.33 parser 207: }
208:
1.80 paf 209: inline ushort endian_to_ushort(bool is_big, const uchar *b/* [2] */) {
210: return is_big?x_endian_to_ushort(b[1], b[0]):
211: x_endian_to_ushort(b[0], b[1]);
1.1 paf 212: }
213:
1.80 paf 214: inline uint endian_to_uint(bool is_big, const uchar *b /* [4] */) {
215: return is_big?x_endian_to_uint(b[3], b[2], b[1], b[0]):
216: x_endian_to_uint(b[0], b[1], b[2], b[3]);
217: }
218:
219: static void measure_gif(Pool& pool, const String *origin_string,
1.78 paf 220: Measure_reader& reader, ushort& width, ushort& height) {
1.1 paf 221:
1.83 paf 222: const void *buf;
1.1 paf 223: const int head_size=sizeof(GIF_Header);
224: if(reader.read(buf, head_size)<head_size)
1.68 paf 225: throw Exception("image.format",
1.1 paf 226: origin_string,
1.34 parser 227: "not GIF file - too small");
1.31 parser 228: GIF_Header *head=(GIF_Header *)buf;
1.1 paf 229:
1.72 paf 230: if(strncmp(head->signature, "GIF", 3)!=0)
1.68 paf 231: throw Exception("image.format",
1.1 paf 232: origin_string,
1.44 parser 233: "not GIF file - wrong signature");
1.1 paf 234:
1.80 paf 235: width=endian_to_ushort(false, head->width);
236: height=endian_to_ushort(false, head->height);
237: }
238:
239: static Value *parse_IFD_entry_formatted_one_value(Pool& pool,
240: bool is_big,
241: ushort format,
242: size_t component_size,
243: const uchar *value) {
244: switch(format) {
245: case 1: // unsigned byte
246: return new(pool) VInt(pool, (uchar)value[0]);
247: case 3: // unsigned short
248: return new(pool) VInt(pool, endian_to_ushort(is_big, value));
249: case 4: // unsigned long
250: // 'double' because parser's Int is signed
251: return new(pool) VDouble(pool, endian_to_uint(is_big, value));
252: case 5: // unsigned rational
253: {
254: uint numerator=endian_to_uint(is_big, value); value+=component_size/2;
255: uint denominator=endian_to_uint(is_big, value);
256: if(!denominator)
257: return 0;
258: return new(pool) VDouble(pool, ((double)numerator)/denominator);
259: }
260: case 6: // signed byte
261: return new(pool) VInt(pool, (signed char)value[0]);
262: case 8: // signed short
263: return new(pool) VInt(pool, (signed short)endian_to_ushort(is_big, value));
264: case 9: // signed long
265: return new(pool) VInt(pool, (signed int)endian_to_uint(is_big, value));
266: case 10: // signed rational
267: {
268: signed int numerator=(signed int)endian_to_uint(is_big, value); value+=component_size/2;
269: uint denominator=endian_to_uint(is_big, value);
270: if(!denominator)
271: return 0;
272: return new(pool) VDouble(pool, numerator/denominator);
273: }
274: /*
275: case 11: // single float
276: todo
277: case 12: // double float
278: todo
279: */
280: };
281:
282: return 0;
283: }
284:
1.89 paf 285: // date.C
286: time_t cstr_to_time_t(char *cstr, const String *report_error_origin);
287:
1.80 paf 288: static Value *parse_IFD_entry_formatted_value(Pool& pool,
289: bool is_big, ushort format,
290: size_t component_size, uint components_count,
291: const uchar *value) {
292: if(format==2) { // ascii string, exception: the only type with varying size
293: const char *cstr=(const char *)value;
294: size_t size=components_count;
1.89 paf 295: // Data format is "YYYY:MM:DD HH:MM:SS"+0x00, total 20bytes
296: if(size==JPEG_EXIF_DATE_CHARS
297: && isdigit(cstr[0])
298: && cstr[JPEG_EXIF_DATE_CHARS-1]==0) {
299: char cstr_writable[JPEG_EXIF_DATE_CHARS];
300: strcpy(cstr_writable, cstr);
301:
302: time_t t=cstr_to_time_t(cstr_writable, 0/* do not throw exception, just return bad result */);
303: if(t>=0)
304: return new(pool) VDate(pool, t);
305: }
306:
1.80 paf 307: if(const char *premature_zero_pos=(const char *)memchr(cstr, 0, size))
308: size=premature_zero_pos-cstr;
309: return new(pool) VString(*new(pool) String(pool, cstr, size, true/*tainted*/));
310: }
311:
312: if(components_count==1)
313: return parse_IFD_entry_formatted_one_value(pool, is_big, format, component_size, value);
314:
315: VHash& result=*new(pool) VHash(pool);
316: Hash& hash=result.hash(0);
317: for(uint i=0; i<components_count; i++, value+=component_size) {
318: String& skey=*new(pool) String(pool);
319: {
320: char *buf=(char *)pool.malloc(MAX_NUMBER);
321: snprintf(buf, MAX_NUMBER, "%d", i);
322: skey << buf;
323: }
324: hash.put(skey, parse_IFD_entry_formatted_one_value(pool, is_big, format, component_size, value));
325: }
326:
327: return &result;
328: }
329:
330: static Value *parse_IFD_entry_value(Pool& pool,
331: bool is_big, Measure_reader& reader, long tiff_base,
332: JPG_Exif_IFD_entry& entry) {
333: size_t format2component_size[]={
334: 0, // undefined
335: 1, // unsigned byte
336: 1, // ascii string
337: 2, // unsigned short
338: 4, // unsigned long
339: 8, // unsigned rational
340: 1, // signed byte
341: 0, // undefined
342: 2, // signed short
343: 4, // signed long
344: 8, // signed rational
345: /*
346: 4, // single float
347: 8, // double float
348: */
349: };
350:
351: ushort format=endian_to_ushort(is_big, entry.format);
352: if(format>=sizeof(format2component_size)/sizeof(format2component_size[0]))
353: return 0; // format out of range, ignoring
354:
355: size_t component_size=format2component_size[format];
356: if(component_size==0)
357: return 0; // undefined format
358:
359: // You can get the total data byte length by multiplies
360: // a 'bytes/components' value (see above chart) by number of components stored 'NNNNNNNN' area
361: uint components_count=endian_to_uint(is_big, entry.components_count);
362: size_t value_size=component_size*components_count;
363: // If its size is over 4bytes, 'DDDDDDDD' contains the offset to data stored address
364: Value *result;
365:
366: if(value_size<=4)
367: result=parse_IFD_entry_formatted_value(pool,
368: is_big, format,
369: component_size, components_count,
370: entry.value_or_offset_to_it);
371: else {
372: long remembered=reader.tell();
373: {
374: reader.seek(tiff_base+endian_to_uint(is_big, entry.value_or_offset_to_it), SEEK_SET);
375: const void *value;
376: if(reader.read(value, value_size)<sizeof(value_size))
377: return 0;
378: result=parse_IFD_entry_formatted_value(pool,
379: is_big, format,
380: component_size, components_count,
381: (const uchar*)value);
382: }
383: reader.seek(remembered, SEEK_SET);
384: }
385:
386: return result;
387: }
388:
389: static void parse_IFD(Pool& pool,
390: Hash& hash,
391: bool is_big, Measure_reader& reader, long tiff_base);
392:
393: static void parse_IFD_entry(Pool& pool, Hash& hash,
394: bool is_big, Measure_reader& reader, long tiff_base,
395: JPG_Exif_IFD_entry& entry) {
396: ushort tag=endian_to_ushort(is_big, entry.tag);
397: if(tag==JPG_IFD_TAG_EXIF_OFFSET) {
398: long remembered=reader.tell();
399: {
400: reader.seek(tiff_base+endian_to_uint(is_big, entry.value_or_offset_to_it), SEEK_SET);
401: parse_IFD(pool, hash, is_big, reader, tiff_base);
402: }
403: reader.seek(remembered, SEEK_SET);
404: return;
405: }
406:
407: if(Value *value=parse_IFD_entry_value(pool, is_big, reader, tiff_base, entry)) {
408: String& skey=*new(pool) String(pool);
409: {
410: char *buf=(char *)pool.malloc(MAX_NUMBER);
411: snprintf(buf, MAX_NUMBER, "%u", tag);
412: skey << buf;
413: }
414:
415: if(const char *name=(const char *)exif_tag_value2name->get(skey))
416: hash.put(*new(pool) String(pool, name), value);
417: else
418: hash.put(skey, value);
419: }
1.1 paf 420: }
421:
1.80 paf 422: static void parse_IFD(Pool& pool,
423: Hash& hash,
424: bool is_big, Measure_reader& reader, long tiff_base) {
425: const void *buf;
426: if(reader.read(buf, sizeof(JPG_Exif_IFD_start))<sizeof(JPG_Exif_IFD_start))
427: return;
428: JPG_Exif_IFD_start *start=(JPG_Exif_IFD_start *)buf;
429:
430: ushort directory_entry_count=endian_to_ushort(is_big, start->directory_entry_count);
431: for(int i=0; i<directory_entry_count; i++) {
432: if(reader.read(buf, sizeof(JPG_Exif_IFD_entry))<sizeof(JPG_Exif_IFD_entry))
433: return;
434:
435: parse_IFD_entry(pool, hash, is_big, reader, tiff_base, *(JPG_Exif_IFD_entry *)buf);
436: }
437: // then goes: LLLLLLLL Offset to next IFD [not going there]
438: }
439:
440: static Value *parse_exif(Pool& pool,
441: Measure_reader& reader,
442: const String *origin_string) {
443: const void *buf;
444: if(reader.read(buf, sizeof(JPG_Exif_segment_start))<sizeof(JPG_Exif_segment_start))
445: throw Exception("image.format",
446: origin_string,
447: "not JPEG file - can not fully read Exif segment start");
448:
449: JPG_Exif_segment_start *start=(JPG_Exif_segment_start *)buf;
450: if(memcmp(start->signature, "Exif\0\0", 4+2)!=0) //signature invalid?
451: return 0; // ignore invalid block
452:
453: uint tiff_base=reader.tell();
454: if(reader.read(buf, sizeof(JPG_Exif_TIFF_header))<sizeof(JPG_Exif_TIFF_header))
455: return 0;
456:
457: JPG_Exif_TIFF_header *head=(JPG_Exif_TIFF_header *)buf;
458: bool is_big=head->byte_align_identifier[0]=='M'; // [M]otorola vs [I]ntel
459:
460: uint first_IFD_offset=endian_to_uint(is_big, head->first_IFD_offset);
461: reader.seek(tiff_base+first_IFD_offset, SEEK_SET);
462:
463: VHash& vhash=*new(pool) VHash(pool);
464:
465: // IFD
466: parse_IFD(pool, vhash.hash(0), is_big, reader, tiff_base);
467:
468: return &vhash;
469: }
470:
471: static void measure_jpeg(Pool& pool, const String *origin_string,
472: Measure_reader& reader, ushort& width, ushort& height, Value ** exif) {
1.2 paf 473: // JFIF format markers
1.80 paf 474: const uchar MARKER=0xFF;
475: const uchar CODE_SIZE_A=0xC0;
476: const uchar CODE_SIZE_B=0xC1;
477: const uchar CODE_SIZE_C=0xC2;
478: const uchar CODE_SIZE_D=0xC3;
479: const uchar CODE_EXIF=0xE1;
1.2 paf 480:
1.83 paf 481: const void *buf;
1.18 paf 482: const size_t prefix_size=2;
1.31 parser 483: if(reader.read(buf, prefix_size)<prefix_size)
1.68 paf 484: throw Exception("image.format",
1.1 paf 485: origin_string,
1.34 parser 486: "not JPEG file - too small");
1.80 paf 487: uchar *signature=(uchar *)buf;
1.1 paf 488:
1.31 parser 489: if(!(signature[0]==0xFF && signature[1]==0xD8))
1.68 paf 490: throw Exception("image.format",
1.1 paf 491: origin_string,
1.44 parser 492: "not JPEG file - wrong signature");
1.31 parser 493:
494: while(true) {
1.80 paf 495: uint segment_base=reader.tell()+2/*marker,code*/;
1.31 parser 496: if(reader.read(buf, sizeof(JPG_Segment_head))<sizeof(JPG_Segment_head))
1.79 paf 497: break;
1.31 parser 498: JPG_Segment_head *head=(JPG_Segment_head *)buf;
499:
500: // Verify that it's a valid segment.
501: if(head->marker!=MARKER)
1.79 paf 502: throw Exception("image.format",
503: origin_string,
504: "not JPEG file - marker not found");
505:
506: switch(head->code) {
507: // http://www.ba.wakwak.com/~tsuruzoh/Computer/Digicams/exif-e.html
508: case CODE_EXIF:
1.80 paf 509: if(exif && !*exif) // seen .jpg with some xml under EXIF tag, after real exif block :)
510: *exif=parse_exif(pool, reader, origin_string);
1.1 paf 511: break;
1.31 parser 512:
1.79 paf 513: case CODE_SIZE_A:
514: case CODE_SIZE_B:
515: case CODE_SIZE_C:
516: case CODE_SIZE_D:
517: {
518: // Segments that contain size info
519: if(reader.read(buf, sizeof(JPG_Size_segment_body))<sizeof(JPG_Size_segment_body))
520: throw Exception("image.format",
521: origin_string,
522: "not JPEG file - can not fully read Size segment");
523: JPG_Size_segment_body *body=(JPG_Size_segment_body *)buf;
524:
1.80 paf 525: width=endian_to_ushort(true, body->width);
526: height=endian_to_ushort(true, body->height);
1.79 paf 527: }
1.80 paf 528: return;
1.79 paf 529: };
530:
1.80 paf 531: reader.seek(segment_base+endian_to_ushort(true, head->length), SEEK_SET);
1.31 parser 532: }
533:
1.79 paf 534: throw Exception("image.format",
535: origin_string,
536: "broken JPEG file - size frame not found");
1.1 paf 537: }
538:
1.80 paf 539: static void measure_png(Pool& pool, const String *origin_string,
1.78 paf 540: Measure_reader& reader, ushort& width, ushort& height) {
1.72 paf 541:
1.83 paf 542: const void *buf;
1.72 paf 543: const int head_size=sizeof(PNG_Header);
544: if(reader.read(buf, head_size)<head_size)
545: throw Exception("image.format",
546: origin_string,
547: "not PNG file - too small");
548: PNG_Header *head=(PNG_Header *)buf;
549:
550: if(strncmp(head->signature, "IHDR", 4)!=0)
551: throw Exception("image.format",
552: origin_string,
553: "not PNG file - wrong signature");
554:
1.80 paf 555: width=endian_to_ushort(true, head->width);
556: height=endian_to_ushort(true, head->height);
1.72 paf 557: }
558:
1.1 paf 559: // measure center
560:
1.80 paf 561: static void measure(Pool& pool, const String& file_name,
562: Measure_reader& reader, ushort& width, ushort& height, Value ** exif) {
1.60 paf 563: if(const char *cext=strrchr(file_name.cstr(String::UL_FILE_SPEC), '.')) {
1.1 paf 564: cext++;
565: if(strcasecmp(cext, "GIF")==0)
566: measure_gif(pool, &file_name, reader, width, height);
567: else if(strcasecmp(cext, "JPG")==0 || strcasecmp(cext, "JPEG")==0)
1.80 paf 568: measure_jpeg(pool, &file_name, reader, width, height, exif);
1.72 paf 569: else if(strcasecmp(cext, "PNG")==0)
570: measure_png(pool, &file_name, reader, width, height);
1.1 paf 571: else
1.68 paf 572: throw Exception("image.format",
1.1 paf 573: &file_name,
574: "unhandled image file name extension '%s'", cext);
575: } else
1.68 paf 576: throw Exception("image.format",
1.1 paf 577: &file_name,
578: "can not determine image type - no file name extension");
579: }
580:
1.77 paf 581: // methods
1.1 paf 582:
1.40 parser 583: #ifndef DOXYGEN
1.77 paf 584: struct File_measure_action_info {
1.78 paf 585: ushort *width;
586: ushort *height;
1.80 paf 587: Value ** exif;
1.77 paf 588: const String *file_name;
1.1 paf 589: };
1.40 parser 590: #endif
1.77 paf 591: static void file_measure_action(Pool& pool,
1.80 paf 592: struct stat& finfo, int f,
1.77 paf 593: const String& file_spec, const char *fname, bool as_text,
594: void *context) {
595: File_measure_action_info& info=*static_cast<File_measure_action_info *>(context);
1.1 paf 596:
1.77 paf 597: Measure_file_reader reader(pool, f, *info.file_name, fname);
1.80 paf 598: measure(pool, *info.file_name, reader, *info.width, *info.height, info.exif);
1.1 paf 599: }
600:
1.17 paf 601: static void _measure(Request& r, const String& method_name, MethodParams *params) {
1.1 paf 602: Pool& pool=r.pool();
603:
1.30 parser 604: Value& data=params->as_no_junction(0, "data must not be code");
1.1 paf 605:
1.78 paf 606: ushort width=0;
607: ushort height=0;
1.80 paf 608: Value *exif=0;
1.1 paf 609: const String *file_name;
1.77 paf 610: if(file_name=data.get_string()) {
1.80 paf 611: File_measure_action_info info={&width, &height, &exif, file_name};
1.77 paf 612: file_read_action_under_lock(pool, r.absolute(*file_name),
613: "measure", file_measure_action, &info);
1.1 paf 614: } else {
615: const VFile& vfile=*data.as_vfile();
616: file_name=&static_cast<Value *>(vfile.fields().get(*name_name))->as_string();
1.77 paf 617: Measure_buf_reader reader(
618: vfile.value_ptr(),
619: vfile.value_size(),
620: *file_name
621: );
1.80 paf 622: measure(pool, *file_name, reader, width, height, &exif);
1.1 paf 623: }
624:
1.80 paf 625: VImage &vimage=*static_cast<VImage *>(r.get_self());
626: vimage.set(file_name, width, height, 0, exif);
1.1 paf 627: }
628:
1.40 parser 629: #ifndef DOXYGEN
1.4 paf 630: struct Attrib_info {
1.21 paf 631: String *tag; ///< html tag being constructed
632: Hash *skip; ///< tag attributes not to append to tag string [to skip]
1.4 paf 633: };
1.40 parser 634: #endif
1.3 paf 635: static void append_attrib_pair(const Hash::Key& key, Hash::Val *val, void *info) {
1.4 paf 636: Attrib_info& ai=*static_cast<Attrib_info *>(info);
637:
1.49 parser 638: // skip user-specified and internal(starting with "line-") attributes
639: if(ai.skip && ai.skip->get(key) || key.pos("line-")==0)
1.4 paf 640: return;
641:
1.3 paf 642: Value& value=*static_cast<Value *>(val);
643: // src="a.gif" width=123 ismap[=-1]
1.4 paf 644: *ai.tag << " " << key;
1.26 parser 645: if(value.is_string() || value.as_int()>=0)
1.6 paf 646: *ai.tag << "=\"" << value.as_string() << "\"";
1.3 paf 647: }
1.17 paf 648: static void _html(Request& r, const String& method_name, MethodParams *params) {
1.3 paf 649: Pool& pool=r.pool();
650:
651: String tag(pool);
652: tag << "<img";
1.4 paf 653:
1.76 paf 654: const Hash& fields=static_cast<VImage *>(r.get_self())->fields();
1.5 paf 655: Hash *attribs=0;
1.4 paf 656:
1.39 parser 657: if(params->size()) {
1.69 paf 658: // for backward compatibility: someday was ^html{}
659: Value &vattribs=r.process_to_value(params->get(0),
1.70 paf 660: /*0/*no name* /,*/
1.52 parser 661: false/*don't intercept string*/);
1.82 paf 662: if(!vattribs.is_string()) // allow empty
1.59 parser 663: if(attribs=vattribs.get_hash(&method_name)) {
1.39 parser 664: Attrib_info attrib_info={&tag, 0};
665: attribs->for_each(append_attrib_pair, &attrib_info);
666: } else
1.68 paf 667: throw Exception("parser.runtime",
1.39 parser 668: &method_name,
669: "attributes must be hash");
670: }
1.4 paf 671:
1.5 paf 672: Attrib_info attrib_info={&tag, attribs};
1.4 paf 673: fields.for_each(append_attrib_pair, &attrib_info);
1.6 paf 674: tag << " />";
1.3 paf 675: r.write_pass_lang(tag);
676: }
1.8 paf 677:
1.68 paf 678: /// @test wrap FILE to auto-object
1.16 paf 679: static gdImage *load(Request& r, const String& method_name,
680: const String& file_name){
681: Pool& pool=r.pool();
682:
1.42 parser 683: const char *file_name_cstr=r.absolute(file_name).cstr(String::UL_FILE_SPEC);
1.16 paf 684: if(FILE *f=fopen(file_name_cstr, "rb")) {
685: gdImage& image=*new(pool) gdImage(pool);
1.23 paf 686: bool ok=image.CreateFromGif(f);
1.16 paf 687: fclose(f);
1.23 paf 688: if(!ok)
1.68 paf 689: throw Exception("image.format",
1.23 paf 690: &file_name,
691: "is not in GIF format");
1.16 paf 692: return ℑ
693: } else {
1.68 paf 694: throw Exception("file.missing",
1.16 paf 695: &method_name,
696: "can not open '%s'", file_name_cstr);
697: return 0;
698: }
699: }
700:
701:
1.17 paf 702: static void _load(Request& r, const String& method_name, MethodParams *params) {
1.6 paf 703: Pool& pool=r.pool();
704:
1.44 parser 705: const String& file_name=params->as_string(0, "file name must not be code");
1.6 paf 706:
1.16 paf 707: gdImage& image=*load(r, method_name, file_name);
708: int width=image.SX();
709: int height=image.SY();
1.76 paf 710: static_cast<VImage *>(r.get_self())->set(&file_name, width, height, &image);
1.6 paf 711: }
712:
1.17 paf 713: static void _create(Request& r, const String& method_name, MethodParams *params) {
1.6 paf 714: Pool& pool=r.pool();
715:
1.51 parser 716: int width=params->as_int(0, "width must be int", r);
717: int height=params->as_int(1, "height must be int", r);
1.8 paf 718: int bgcolor_value=0xffFFff;
1.6 paf 719: if(params->size()>2)
1.51 parser 720: bgcolor_value=params->as_int(2, "color must be int", r);
1.15 paf 721: gdImage& image=*new(pool) gdImage(pool);
722: image.Create(width, height);
723: image.FilledRectangle(0, 0, width-1, height-1, image.Color(bgcolor_value));
1.76 paf 724: static_cast<VImage *>(r.get_self())->set(0, width, height, &image);
1.6 paf 725: }
726:
1.17 paf 727: static void _gif(Request& r, const String& method_name, MethodParams *params) {
1.6 paf 728: Pool& pool=r.pool();
729:
1.76 paf 730: gdImage *image=static_cast<VImage *>(r.get_self())->image;
1.8 paf 731: if(!image)
1.68 paf 732: throw Exception(0,
1.16 paf 733: &method_name,
1.12 paf 734: "does not contain an image");
1.6 paf 735:
736: // could _ but don't thing it's wise to use $image.src for vfile.name
1.10 paf 737:
1.16 paf 738: String out(pool); image->Gif(out);
1.6 paf 739:
740: VFile& vfile=*new(pool) VFile(pool);
1.41 parser 741: Value *content_type=new(pool) VString(*new(pool) String(pool, "image/gif"));
1.20 paf 742: vfile.set(false/*not tainted*/,
1.61 paf 743: out.cstr(), out.size(), 0, content_type);
1.6 paf 744:
745: r.write_no_lang(vfile);
746: }
747:
1.17 paf 748: static void _line(Request& r, const String& method_name, MethodParams *params) {
1.12 paf 749: Pool& pool=r.pool();
750:
1.76 paf 751: gdImage *image=static_cast<VImage *>(r.get_self())->image;
1.12 paf 752: if(!image)
1.68 paf 753: throw Exception(0,
1.16 paf 754: &method_name,
1.12 paf 755: "does not contain an image");
756:
757: image->Line(
1.51 parser 758: params->as_int(0, "x0 must be int", r),
759: params->as_int(1, "y0 must be int", r),
760: params->as_int(2, "x1 must be int", r),
761: params->as_int(3, "y1 must be int", r),
762: image->Color(params->as_int(4, "color must be int", r)));
1.13 paf 763: }
764:
1.17 paf 765: static void _fill(Request& r, const String& method_name, MethodParams *params) {
1.13 paf 766: Pool& pool=r.pool();
767:
1.76 paf 768: gdImage *image=static_cast<VImage *>(r.get_self())->image;
1.13 paf 769: if(!image)
1.68 paf 770: throw Exception(0,
1.16 paf 771: &method_name,
1.13 paf 772: "does not contain an image");
1.12 paf 773:
1.13 paf 774: image->Fill(
1.51 parser 775: params->as_int(0, "x must be int", r),
776: params->as_int(1, "y must be int", r),
777: image->Color(params->as_int(2, "color must be int", r)));
1.13 paf 778: }
779:
1.17 paf 780: static void _rectangle(Request& r, const String& method_name, MethodParams *params) {
1.13 paf 781: Pool& pool=r.pool();
782:
1.76 paf 783: gdImage *image=static_cast<VImage *>(r.get_self())->image;
1.13 paf 784: if(!image)
1.68 paf 785: throw Exception(0,
1.16 paf 786: &method_name,
1.13 paf 787: "does not contain an image");
788:
789: image->Rectangle(
1.51 parser 790: params->as_int(0, "x0 must be int", r),
791: params->as_int(1, "y0 must be int", r),
792: params->as_int(2, "x1 must be int", r),
793: params->as_int(3, "y1 must be int", r),
794: image->Color(params->as_int(4, "color must be int", r)));
1.13 paf 795: }
796:
1.17 paf 797: static void _bar(Request& r, const String& method_name, MethodParams *params) {
1.13 paf 798: Pool& pool=r.pool();
799:
1.76 paf 800: gdImage *image=static_cast<VImage *>(r.get_self())->image;
1.13 paf 801: if(!image)
1.68 paf 802: throw Exception(0,
1.16 paf 803: &method_name,
1.13 paf 804: "does not contain an image");
805:
806: image->FilledRectangle(
1.51 parser 807: params->as_int(0, "x0 must be int", r),
808: params->as_int(1, "y0 must be int", r),
809: params->as_int(2, "x1 must be int", r),
810: params->as_int(3, "y1 must be int", r),
811: image->Color(params->as_int(4, "color must be int", r)));
1.13 paf 812: }
813:
1.44 parser 814: #ifndef DOXYGEN
815: static void add_point(Array::Item *value, void *info) {
816: Array& row=*static_cast<Array *>(value);
817: gdImage::Point **p=static_cast<gdImage::Point **>(info);
818:
819: (**p).x=row.get_string(0)->as_int();
820: (**p).y=row.get_string(1)->as_int();
821: (*p)++;
822: }
823: #endif
1.17 paf 824: static void _replace(Request& r, const String& method_name, MethodParams *params) {
1.13 paf 825: Pool& pool=r.pool();
826:
1.76 paf 827: gdImage *image=static_cast<VImage *>(r.get_self())->image;
1.13 paf 828: if(!image)
1.68 paf 829: throw Exception(0,
1.16 paf 830: &method_name,
1.13 paf 831: "does not contain an image");
832:
1.44 parser 833: Table *table=params->as_no_junction(2, "coordinates must not be code").get_table();
834: if(!table)
1.68 paf 835: throw Exception(0,
1.44 parser 836: &method_name,
837: "coordinates must be table");
1.13 paf 838:
1.44 parser 839: gdImage::Point *all_p=(gdImage::Point *)pool.malloc(sizeof(gdImage::Point)*table->size());
840: gdImage::Point *add_p=all_p;
841: table->for_each(add_point, &add_p);
842: image->FilledPolygonReplaceColor(all_p, table->size(),
1.51 parser 843: image->Color(params->as_int(0, "src color must be int", r)),
844: image->Color(params->as_int(1, "dest color must be int", r)));
1.13 paf 845: }
846:
1.44 parser 847: static void _polyline(Request& r, const String& method_name, MethodParams *params) {
1.13 paf 848: Pool& pool=r.pool();
849:
1.76 paf 850: gdImage *image=static_cast<VImage *>(r.get_self())->image;
1.13 paf 851: if(!image)
1.68 paf 852: throw Exception(0,
1.16 paf 853: &method_name,
1.13 paf 854: "does not contain an image");
855:
1.44 parser 856: Table *table=params->as_no_junction(1, "coordinates must not be code").get_table();
857: if(!table)
1.68 paf 858: throw Exception(0,
1.44 parser 859: &method_name,
860: "coordinates must be table");
861:
862: gdImage::Point *all_p=(gdImage::Point *)pool.malloc(sizeof(gdImage::Point)*table->size());
863: gdImage::Point *add_p=all_p;
864: table->for_each(add_point, &add_p);
865: image->Polygon(all_p, table->size(),
1.51 parser 866: image->Color(params->as_int(0, "color must be int", r)),
1.44 parser 867: false/*not closed*/);
868: }
869:
870: static void _polygon(Request& r, const String& method_name, MethodParams *params) {
871: Pool& pool=r.pool();
872:
1.76 paf 873: gdImage *image=static_cast<VImage *>(r.get_self())->image;
1.44 parser 874: if(!image)
1.68 paf 875: throw Exception(0,
1.16 paf 876: &method_name,
1.44 parser 877: "does not contain an image");
1.13 paf 878:
1.44 parser 879: Table *table=params->as_no_junction(1, "coordinates must not be code").get_table();
880: if(!table)
1.68 paf 881: throw Exception(0,
1.44 parser 882: &method_name,
883: "coordinates must be table");
884:
885: gdImage::Point *all_p=(gdImage::Point *)pool.malloc(sizeof(gdImage::Point)*table->size());
886: gdImage::Point *add_p=all_p;
887: table->for_each(add_point, &add_p);
888: image->Polygon(all_p, table->size(),
1.51 parser 889: image->Color(params->as_int(0, "color must be int", r)));
1.13 paf 890: }
891:
1.17 paf 892: static void _polybar(Request& r, const String& method_name, MethodParams *params) {
1.13 paf 893: Pool& pool=r.pool();
894:
1.76 paf 895: gdImage *image=static_cast<VImage *>(r.get_self())->image;
1.13 paf 896: if(!image)
1.68 paf 897: throw Exception(0,
1.16 paf 898: &method_name,
1.13 paf 899: "does not contain an image");
900:
1.44 parser 901: Table *table=params->as_no_junction(1, "coordinates must not be code").get_table();
902: if(!table)
1.68 paf 903: throw Exception("parser.runtime",
1.44 parser 904: &method_name,
905: "coordinates must be table");
1.13 paf 906:
1.44 parser 907: gdImage::Point *all_p=(gdImage::Point *)pool.malloc(sizeof(gdImage::Point)*table->size());
908: gdImage::Point *add_p=all_p;
909: table->for_each(add_point, &add_p);
910: image->FilledPolygon(all_p, table->size(),
1.51 parser 911: image->Color(params->as_int(0, "color must be int", r)));
1.12 paf 912: }
913:
1.16 paf 914: // font
915:
1.85 paf 916: #define Y(y)(y+index*height)
1.21 paf 917:
918: /// simple gdImage-based font storage & text output
1.85 paf 919: class Font: public Pooled {
1.16 paf 920: public:
921:
1.38 parser 922: const static int letter_spacing;
1.35 parser 923: int height; ///< Font heigth
924: int monospace; ///< Default char width
925: int spacebarspace; ///< spacebar width
1.16 paf 926: gdImage& ifont;
927: const String& alphabet;
928:
929: Font(Pool& pool,
930: const String& aalphabet,
1.85 paf 931: gdImage& aifont, int aheight, int amonospace, int aspacebarspace): Pooled(pool),
1.16 paf 932: alphabet(aalphabet),
1.35 parser 933: height(aheight), monospace(amonospace), spacebarspace(aspacebarspace),
1.16 paf 934: ifont(aifont) {
935: }
936:
937: /* ******************************** char ********************************** */
938:
939: int index_of(char ch) {
940: if(ch==' ') return -1;
941: return alphabet.pos(&ch, 1);
942: }
943:
944: int index_width(int index) {
945: if(index<0)
1.35 parser 946: return spacebarspace;
1.16 paf 947: int tr=ifont.GetTransparent();
1.35 parser 948: for(int x=ifont.SX()-1; x>=0; x--) {
1.86 paf 949: for(int y=0; y<height; y++)
1.16 paf 950: if(ifont.GetPixel(x, Y(y))!=tr)
1.35 parser 951: return x+1;
1.16 paf 952: }
953: return 0;
954: }
955:
956: void index_display(gdImage& image, int x, int y, int index){
957: if(index>=0)
1.85 paf 958: ifont.Copy(image, x, y, 0, Y(0), index_width(index), height);
1.16 paf 959: }
960:
961: /* ******************************** string ********************************** */
1.47 parser 962:
1.87 paf 963: int step_width(int index) {
964: return letter_spacing + (monospace ? monospace : index_width(index));
965: }
966:
967: // counts trailing letter_spacing, consider this OK. useful for contiuations
1.47 parser 968: int string_width(const String& s){
1.61 paf 969: const char *cstr=s.cstr();
1.16 paf 970: int result=0;
971: for(; *cstr; cstr++)
1.87 paf 972: result+=step_width(index_of(*cstr));
1.16 paf 973: return result;
974: }
975:
976: void string_display(gdImage& image, int x, int y, const String& s){
1.61 paf 977: const char *cstr=s.cstr();
1.16 paf 978: if(cstr) for(; *cstr; cstr++) {
979: int index=index_of(*cstr);
980: index_display(image, x, y, index);
1.87 paf 981: x+=step_width(index);
1.16 paf 982: }
983: }
984:
985: };
1.38 parser 986: const int Font::letter_spacing=1;
1.35 parser 987:
1.17 paf 988: static void _font(Request& r, const String& method_name, MethodParams *params) {
1.16 paf 989: Pool& pool=r.pool();
990:
1.37 parser 991: const String& alphabet=params->as_string(0, "alphabet must not be code");
992: gdImage& image=*load(r, method_name, params->as_string(1, "file_name must not be code"));
1.51 parser 993: int spacebar_width=params->as_int(2, "spacebar_width must be int", r);
1.37 parser 994: int monospace_width;
995: if(params->size()>3) {
1.51 parser 996: monospace_width=params->as_int(3, "monospace_width must be int", r);
1.37 parser 997: if(!monospace_width)
998: monospace_width=image.SX();
999: } else
1000: monospace_width=0;
1.16 paf 1001:
1.37 parser 1002: if(!alphabet.size())
1.68 paf 1003: throw Exception("parser.runtime",
1.37 parser 1004: &method_name,
1005: "alphabet must not be empty");
1.84 paf 1006:
1007: if(int remainder=image.SY() % alphabet.size())
1008: throw Exception("parser.runtime",
1009: &method_name,
1010: "font-file height(%d) not divisable by alphabet size(%d), remainder=%d",
1011: image.SY(), alphabet.size(), remainder);
1.37 parser 1012:
1.76 paf 1013: static_cast<VImage *>(r.get_self())->font=new(pool) Font(pool,
1.37 parser 1014: alphabet,
1.36 parser 1015: image,
1.37 parser 1016: image.SY() / alphabet.size(), monospace_width, spacebar_width);
1.16 paf 1017: }
1018:
1.17 paf 1019: static void _text(Request& r, const String& method_name, MethodParams *params) {
1.16 paf 1020: Pool& pool=r.pool();
1021:
1.51 parser 1022: int x=params->as_int(0, "x must be int", r);
1023: int y=params->as_int(1, "y must be int", r);
1.36 parser 1024: const String& s=params->as_string(2, "text must not be code");
1.16 paf 1025:
1.76 paf 1026: VImage& vimage=*static_cast<VImage *>(r.get_self());
1.16 paf 1027: if(vimage.image)
1028: if(vimage.font)
1029: vimage.font->string_display(*vimage.image, x, y, s);
1030: else
1.68 paf 1031: throw Exception("parser.runtime",
1.16 paf 1032: &method_name,
1033: "set the font first");
1034: else
1.68 paf 1035: throw Exception(0,
1.16 paf 1036: &method_name,
1037: "does not contain an image");
1038: }
1039:
1.47 parser 1040: static void _length(Request& r, const String& method_name, MethodParams *params) {
1041: Pool& pool=r.pool();
1042:
1043: const String& s=params->as_string(0, "text must not be code");
1044:
1.76 paf 1045: VImage& vimage=*static_cast<VImage *>(r.get_self());
1.47 parser 1046: if(vimage.image)
1047: if(vimage.font) {
1.71 paf 1048: r.write_no_lang(*new(pool) VInt(pool, vimage.font->string_width(s)));
1.47 parser 1049: } else
1.68 paf 1050: throw Exception("parser.runtime",
1.47 parser 1051: &method_name,
1052: "set the font first");
1053: else
1.68 paf 1054: throw Exception(0,
1.47 parser 1055: &method_name,
1056: "does not contain an image");
1057: }
1058:
1.48 parser 1059: static void _arc(Request& r, const String& method_name, MethodParams *params) {
1060: Pool& pool=r.pool();
1061:
1.76 paf 1062: gdImage *image=static_cast<VImage *>(r.get_self())->image;
1.48 parser 1063: if(!image)
1.68 paf 1064: throw Exception(0,
1.48 parser 1065: &method_name,
1066: "does not contain an image");
1067:
1068: image->Arc(
1.51 parser 1069: params->as_int(0, "center_x must be int", r),
1070: params->as_int(1, "center_y must be int", r),
1071: params->as_int(2, "width must be int", r),
1072: params->as_int(3, "height must be int", r),
1073: params->as_int(4, "start degrees must be int", r),
1074: params->as_int(5, "end degrees must be int", r),
1075: image->Color(params->as_int(6, "cx must be int", r)));
1.48 parser 1076: }
1077:
1.49 parser 1078: static void _sector(Request& r, const String& method_name, MethodParams *params) {
1079: Pool& pool=r.pool();
1080:
1.76 paf 1081: gdImage *image=static_cast<VImage *>(r.get_self())->image;
1.49 parser 1082: if(!image)
1.68 paf 1083: throw Exception(0,
1.49 parser 1084: &method_name,
1085: "does not contain an image");
1086:
1087: image->Sector(
1.51 parser 1088: params->as_int(0, "center_x must be int", r),
1089: params->as_int(1, "center_y must be int", r),
1090: params->as_int(2, "width must be int", r),
1091: params->as_int(3, "height must be int", r),
1092: params->as_int(4, "start degrees must be int", r),
1093: params->as_int(5, "end degrees must be int", r),
1094: image->Color(params->as_int(6, "color must be int", r)));
1.49 parser 1095: }
1096:
1.48 parser 1097: static void _circle(Request& r, const String& method_name, MethodParams *params) {
1098: Pool& pool=r.pool();
1099:
1.76 paf 1100: gdImage *image=static_cast<VImage *>(r.get_self())->image;
1.48 parser 1101: if(!image)
1.68 paf 1102: throw Exception(0,
1.48 parser 1103: &method_name,
1104: "does not contain an image");
1105:
1.51 parser 1106: int size=params->as_int(2, "radius must be int", r)*2;
1.48 parser 1107: image->Arc(
1.51 parser 1108: params->as_int(0, "center_x must be int", r),
1109: params->as_int(1, "center_y must be int", r),
1.50 parser 1110: size, //w
1111: size, //h
1.48 parser 1112: 0, //s
1113: 360, //e
1.51 parser 1114: image->Color(params->as_int(3, "color must be int", r)));
1.48 parser 1115: }
1116:
1.53 parser 1117: gdImage& as_image(Pool& pool, const String& method_name, MethodParams *params,
1118: int index, const char *msg) {
1.75 paf 1119: gdImage *src=0;
1120:
1.53 parser 1121: Value& value=params->as_no_junction(index, msg);
1122:
1.75 paf 1123: if(Value *vimage=value.as(VIMAGE_TYPE, false)) {
1124: src=static_cast<VImage *>(vimage)->image;
1125: if(!src)
1126: throw Exception("parser.runtime",
1127: &method_name,
1128: msg);
1129: } else
1.68 paf 1130: throw Exception("parser.runtime",
1.53 parser 1131: &method_name,
1132: msg);
1133:
1134: return *src;
1135: }
1136:
1137: static void _copy(Request& r, const String& method_name, MethodParams *params) {
1138: Pool& pool=r.pool();
1139:
1.76 paf 1140: gdImage *dest=static_cast<VImage *>(r.get_self())->image;
1.53 parser 1141: if(!dest)
1.68 paf 1142: throw Exception(0,
1.53 parser 1143: &method_name,
1144: "self does not contain an image");
1145:
1146: gdImage& src=as_image(pool, method_name, params, 0, "src must be image");
1147:
1148: int sx=params->as_int(1, "src_x must be int", r);
1149: int sy=params->as_int(2, "src_y must be int", r);
1150: int sw=params->as_int(3, "src_w must be int", r);
1151: int sh=params->as_int(4, "src_h must be int", r);
1152: int dx=params->as_int(5, "dest_x must be int", r);
1153: int dy=params->as_int(6, "dest_y must be int", r);
1154: if(params->size()>1+2+2+2) {
1155: int dw=params->as_int(1+2+2+2, "dest_w must be int", r);
1.56 parser 1156: int dh=(int)(params->size()>1+2+2+2+1?
1157: params->as_int(1+2+2+2+1, "dest_h must be int", r):sh*(((double)dw)/((double)sw)));
1158: int tolerance=params->size()>1+2+2+2+2?
1159: params->as_int(1+2+2+2+2, "tolerance must be int", r):150;
1.53 parser 1160:
1.56 parser 1161: src.CopyResampled(*dest, dx, dy, sx, sy, dw, dh, sw, sh, tolerance);
1.53 parser 1162: } else
1.54 parser 1163: src.Copy(*dest, dx, dy, sx, sy, sw, sh);
1.53 parser 1164: }
1165:
1166:
1.22 paf 1167: // constructor
1168:
1.71 paf 1169: MImage::MImage(Pool& apool) : Methoded(apool, "image") {
1.1 paf 1170: // ^image:measure[DATA]
1.22 paf 1171: add_native_method("measure", Method::CT_DYNAMIC, _measure, 1, 1);
1.3 paf 1172:
1.25 paf 1173: // ^image.html[]
1174: // ^image.html[hash]
1.22 paf 1175: add_native_method("html", Method::CT_DYNAMIC, _html, 0, 1);
1.6 paf 1176:
1.25 paf 1177: // ^image.load[background.gif]
1.22 paf 1178: add_native_method("load", Method::CT_DYNAMIC, _load, 1, 1);
1.6 paf 1179:
1.25 paf 1180: // ^image.create[width;height] bgcolor=white
1181: // ^image.create[width;height;bgcolor]
1.22 paf 1182: add_native_method("create", Method::CT_DYNAMIC, _create, 2, 3);
1.6 paf 1183:
1.25 paf 1184: // ^image.gif[]
1.22 paf 1185: add_native_method("gif", Method::CT_DYNAMIC, _gif, 0, 0);
1.12 paf 1186:
1.25 paf 1187: // ^image.line(x0;y0;x1;y1;color)
1.22 paf 1188: add_native_method("line", Method::CT_DYNAMIC, _line, 5, 5);
1.13 paf 1189:
1.25 paf 1190: // ^image.fill(x;y;color)
1.22 paf 1191: add_native_method("fill", Method::CT_DYNAMIC, _fill, 3, 3);
1.13 paf 1192:
1.25 paf 1193: // ^image.rectangle(x0;y0;x1;y1;color)
1.22 paf 1194: add_native_method("rectangle", Method::CT_DYNAMIC, _rectangle, 5, 5);
1.13 paf 1195:
1.25 paf 1196: // ^image.bar(x0;y0;x1;y1;color)
1.22 paf 1197: add_native_method("bar", Method::CT_DYNAMIC, _bar, 5, 5);
1.13 paf 1198:
1.44 parser 1199: // ^image.replace(color-source;color-dest)[table x:y]
1200: add_native_method("replace", Method::CT_DYNAMIC, _replace, 3, 3);
1201:
1202: // ^image.polyline(color)[table x:y]
1203: add_native_method("polyline", Method::CT_DYNAMIC, _polyline, 2, 2);
1.13 paf 1204:
1.44 parser 1205: // ^image.polygon(color)[table x:y]
1206: add_native_method("polygon", Method::CT_DYNAMIC, _polygon, 2, 2);
1.13 paf 1207:
1.44 parser 1208: // ^image.polybar(color)[table x:y]
1209: add_native_method("polybar", Method::CT_DYNAMIC, _polybar, 2, 2);
1.13 paf 1210:
1.36 parser 1211: // ^image.font[alPHAbet;font-file-name.gif](spacebar_width)
1212: // ^image.font[alPHAbet;font-file-name.gif](spacebar_width;width)
1213: add_native_method("font", Method::CT_DYNAMIC, _font, 3, 4);
1.16 paf 1214:
1.25 paf 1215: // ^image.text(x;y)[text]
1.22 paf 1216: add_native_method("text", Method::CT_DYNAMIC, _text, 3, 3);
1.47 parser 1217:
1218: // ^image.ngth[text]
1219: add_native_method("length", Method::CT_DYNAMIC, _length, 1, 1);
1.16 paf 1220:
1.48 parser 1221: // ^image.arc(center x;center y;width;height;start in degrees;end in degrees;color)
1222: add_native_method("arc", Method::CT_DYNAMIC, _arc, 7, 7);
1.49 parser 1223:
1224: // ^image.sector(center x;center y;width;height;start in degrees;end in degrees;color)
1225: add_native_method("sector", Method::CT_DYNAMIC, _sector, 7, 7);
1.48 parser 1226:
1227: // ^image.circle(center x;center y;r;color)
1228: add_native_method("circle", Method::CT_DYNAMIC, _circle, 4, 4);
1229:
1.56 parser 1230: // ^image.copy[source](src x;src y;src w;src h;dst x;dst y[;dest w[;dest h[;tolerance]]])
1231: add_native_method("copy", Method::CT_DYNAMIC, _copy, 1+2+2+2, (1+2+2+2)+2+1);
1.22 paf 1232: }
1233:
1234: // global variable
1235:
1236: Methoded *image_class;
1237:
1238: // creator
1239:
1240: Methoded *MImage_create(Pool& pool) {
1.90.2.1 paf 1241: // image JPEG Exif
1242: #define EXIF_TAG(tag, name) \
1243: exif_tag_value2name.put(tag, #name);
1244: // Tags used by IFD0 (main image)
1245: EXIF_TAG(0x010e, ImageDescription);
1246: EXIF_TAG(0x010f, Make);
1247: EXIF_TAG(0x0110, Model);
1248: EXIF_TAG(0x0112, Orientation);
1249: EXIF_TAG(0x011a, XResolution);
1250: EXIF_TAG(0x011b, YResolution);
1251: EXIF_TAG(0x0128, ResolutionUnit);
1252: EXIF_TAG(0x0131, Software);
1253: EXIF_TAG(0x0132, DateTime);
1254: EXIF_TAG(0x013e, WhitePoint);
1255: EXIF_TAG(0x013f, PrimaryChromaticities);
1256: EXIF_TAG(0x0211, YCbCrCoefficients);
1257: EXIF_TAG(0x0213, YCbCrPositioning);
1258: EXIF_TAG(0x0214, ReferenceBlackWhite);
1259: EXIF_TAG(0x8298, Copyright);
1260: EXIF_TAG(0x8769, ExifOffset);
1261: // Tags used by Exif SubIFD
1262: EXIF_TAG(0x829a, ExposureTime);
1263: EXIF_TAG(0x829d, FNumber);
1264: EXIF_TAG(0x8822, ExposureProgram);
1265: EXIF_TAG(0x8827, ISOSpeedRatings);
1266: EXIF_TAG(0x9000, ExifVersion);
1267: EXIF_TAG(0x9003, DateTimeOriginal);
1268: EXIF_TAG(0x9004, DateTimeDigitized);
1269: EXIF_TAG(0x9101, ComponentsConfiguration);
1270: EXIF_TAG(0x9102, CompressedBitsPerPixel);
1271: EXIF_TAG(0x9201, ShutterSpeedValue);
1272: EXIF_TAG(0x9202, ApertureValue);
1273: EXIF_TAG(0x9203, BrightnessValue);
1274: EXIF_TAG(0x9204, ExposureBiasValue);
1275: EXIF_TAG(0x9205, MaxApertureValue);
1276: EXIF_TAG(0x9206, SubjectDistance);
1277: EXIF_TAG(0x9207, MeteringMode);
1278: EXIF_TAG(0x9208, LightSource);
1279: EXIF_TAG(0x9209, Flash);
1280: EXIF_TAG(0x920a, FocalLength);
1281: EXIF_TAG(0x927c, MakerNote);
1282: EXIF_TAG(0x9286, UserComment);
1283: EXIF_TAG(0x9290, SubsecTime);
1284: EXIF_TAG(0x9291, SubsecTimeOriginal);
1285: EXIF_TAG(0x9292, SubsecTimeDigitized);
1286: EXIF_TAG(0xa000, FlashPixVersion);
1287: EXIF_TAG(0xa001, ColorSpace);
1288: EXIF_TAG(0xa002, ExifImageWidth);
1289: EXIF_TAG(0xa003, ExifImageHeight);
1290: EXIF_TAG(0xa004, RelatedSoundFile);
1291: EXIF_TAG(0xa005, ExifInteroperabilityOffset);
1292: EXIF_TAG(0xa20e, FocalPlaneXResolution);
1293: EXIF_TAG(0xa20f, FocalPlaneYResolution);
1294: EXIF_TAG(0xa210, FocalPlaneResolutionUnit);
1295: EXIF_TAG(0xa215, ExposureIndex);
1296: EXIF_TAG(0xa217, SensingMethod);
1297: EXIF_TAG(0xa300, FileSource);
1298: EXIF_TAG(0xa301, SceneType);
1299: EXIF_TAG(0xa302, CFAPattern);
1300: // Misc Tags
1301: EXIF_TAG(0x00fe, NewSubfileType);
1302: EXIF_TAG(0x00ff, SubfileType);
1303: EXIF_TAG(0x012d, TransferFunction);
1304: EXIF_TAG(0x013b, Artist);
1305: EXIF_TAG(0x013d, Predictor);
1306: EXIF_TAG(0x0142, TileWidth);
1307: EXIF_TAG(0x0143, TileLength);
1308: EXIF_TAG(0x0144, TileOffsets);
1309: EXIF_TAG(0x0145, TileByteCounts);
1310: EXIF_TAG(0x014a, SubIFDs);
1311: EXIF_TAG(0x015b, JPEGTables);
1312: EXIF_TAG(0x828d, CFARepeatPatternDim);
1313: EXIF_TAG(0x828e, CFAPattern);
1314: EXIF_TAG(0x828f, BatteryLevel);
1315: EXIF_TAG(0x83bb, IPTC/NAA);
1316: EXIF_TAG(0x8773, InterColorProfile);
1317: EXIF_TAG(0x8824, SpectralSensitivity);
1318: EXIF_TAG(0x8825, GPSInfo);
1319: EXIF_TAG(0x8828, OECF);
1320: EXIF_TAG(0x8829, Interlace);
1321: EXIF_TAG(0x882a, TimeZoneOffset);
1322: EXIF_TAG(0x882b, SelfTimerMode);
1323: EXIF_TAG(0x920b, FlashEnergy);
1324: EXIF_TAG(0x920c, SpatialFrequencyResponse);
1325: EXIF_TAG(0x920d, Noise);
1326: EXIF_TAG(0x9211, ImageNumber);
1327: EXIF_TAG(0x9212, SecurityClassification);
1328: EXIF_TAG(0x9213, ImageHistory);
1329: EXIF_TAG(0x9214, SubjectLocation);
1330: EXIF_TAG(0x9215, ExposureIndex);
1331: EXIF_TAG(0x9216, TIFF/EPStandardID);
1332: EXIF_TAG(0xa20b, FlashEnergy);
1333: EXIF_TAG(0xa20c, SpatialFrequencyResponse);
1334: EXIF_TAG(0xa214, SubjectLocation);
1335: #undef EXIF_TAG
1336:
1.22 paf 1337: return image_class=new(pool) MImage(pool);
1.1 paf 1338: }
E-mail: