Annotation of parser3/src/classes/image.C, revision 1.7
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.7 ! paf 8: $Id: image.C,v 1.6 2001/04/11 13:03:39 paf Exp $
1.1 paf 9: */
10:
11: #include "pa_config_includes.h"
12:
1.6 paf 13: #include <stdio.h>
14:
1.7 ! paf 15: #include "Imaging.h"
! 16: #include "Gif.h"
1.6 paf 17:
1.1 paf 18: #include "pa_common.h"
19: #include "pa_request.h"
20: #include "pa_vfile.h"
21: #include "pa_vimage.h"
22:
23: // global var
24:
25: VStateless_class *image_class;
26:
27: // helpers
28:
29: class Measure_reader {
30: public:
1.3 paf 31: enum { READ_CHUNK_SIZE=0x400 }; // 1K
1.1 paf 32: typedef size_t (*Func)(void *& buf, size_t limit, void *info);
33:
34: Measure_reader(Func afunc, void *ainfo) :
35: func(afunc), info(ainfo),
36: chunk(0), offset(0), size(0) {
37: }
38:
39: size_t read(unsigned char *& buf, size_t limit) {
1.3 paf 40: if(offset+limit>size) // nothing left
41: if(offset==0 || limit==1) { // only one-byte continuations allowed
42: size=(*func)(chunk, READ_CHUNK_SIZE, info);
43: offset=0;
44: } else
45: return 0; // as if EOF
1.1 paf 46: if(!size) // EOF
47: return 0;
48:
49: // something left
50: size_t read_size=min(offset+limit, size)-offset;
51: buf=((unsigned char *)chunk)+offset;
52: offset+=read_size;
53: return read_size;
54: }
55:
56: private:
57: Func func;
58: void *info;
59:
60: void *chunk;
61: size_t offset;
62: size_t size;
63: };
64:
65: // GIF
66: struct GIF_Header {
67: char type[3]; // 'GIF'
68: char version[3];
69: unsigned char width[2];
70: unsigned char height[2];
71: char dif;
72: char fonColor;
73: char nulls;
74: };
75:
76: // JPEG
77: struct JFIF_Header {
78: char length[2]; // length of JFIF segment marker
79: char identifier[5]; // JFIF identifier
80: char version[2]; // version
81: char units; // units X of Y pixel density
82: char xdensity[2]; // X pixel density
83: char ydensity[2]; // X pixel density
84: char xthumbnails; // width of thumbnails
85: char ythumbnails; // height of thumbnails
86: char reserved; // reserved
87: };
88: struct JPG_Frame {
89: char length[2]; // length of image marker
90: char data; // data precision of bits/sample
91: char height[2]; // image height
92: char width[2]; // image width
93: char numComponents; // number of color components
94: };
95:
96: //
97:
98: inline short bytes_to_int(unsigned char HI, unsigned char LO) {
99: return (short)((HI<<8) + LO);
100: }
101:
102: void measure_gif(Pool& pool, const String *origin_string,
103: Measure_reader& reader, int& width, int& height) {
104:
105: unsigned char *buf;
106: const int head_size=sizeof(GIF_Header);
107: if(reader.read(buf, head_size)<head_size)
108: PTHROW(0, 0,
109: origin_string,
110: "broken GIF header - file size is less then %d bytes", head_size);
111:
112: GIF_Header& screenD=*reinterpret_cast<GIF_Header *>(buf);
113: if(strncmp(screenD.type, "GIF", 3)!=0)
114: PTHROW(0, 0,
115: origin_string,
116: "bad image file - GIF signature not found");
117:
118: width=bytes_to_int(screenD.width[1], screenD.width[0]);
119: height=bytes_to_int(screenD.height[1], screenD.height[0]);
120: }
121:
122: void measure_jpeg(Pool& pool, const String *origin_string,
123: Measure_reader& reader, int& width, int& height) {
1.2 paf 124: // JFIF format markers
125: const unsigned char SOI=0xD8;
126: const unsigned char EOI=0xD9;
127: const unsigned char APP0=0xE0;
128: const unsigned char SOF0=0xC0;
129: const unsigned char SOF2=0xC2;
130: const unsigned char COM=0xFE;
131:
1.1 paf 132: unsigned char *screenD_buf;
1.3 paf 133: unsigned char *h_buf=0;
1.1 paf 134:
135: bool flag=false;
136:
137: unsigned char *prefix;
138: const prefix_size=2;
139: if(reader.read(prefix, prefix_size)<prefix_size)
140: PTHROW(0, 0,
141: origin_string,
142: "broken JPEG file - size is less then %d bytes", prefix_size);
143:
144: if(((unsigned char *)prefix)[1]!=SOI)
145: PTHROW(0, 0,
146: origin_string,
147: "broken JPEG file - second byte of header is not 0x%02X", SOI);
148:
149: unsigned char zero=0;
150: unsigned char *marker=&zero;
151:
152: do {
153: while((*marker)!=0xFF)
154: if(reader.read(marker, sizeof(char))<=0) break;
155: if(reader.read(marker, sizeof(char))<=0) break;
156: switch(*marker) {
157: case EOI:
158: marker=&zero;
159: break;
160: case APP0:
161: if(!flag) {
162: flag=true;
163: if(reader.read(screenD_buf, sizeof(JFIF_Header)) < sizeof(JFIF_Header))
164: break;
165: JFIF_Header& screenD=*reinterpret_cast<JFIF_Header *>(screenD_buf);
166: if((bytes_to_int(screenD.length[0], screenD.length[1]) < 16) ||
1.2 paf 167: strcasecmp(screenD.identifier, "JFIF")) flag=false;
1.1 paf 168: }
169: break;
170: case SOF0:
171: case SOF2:
172: if(reader.read(h_buf, sizeof(JPG_Frame))<sizeof(JPG_Frame))
173: flag=false;
174: break;
175: default: break;
176: }
177: } while(*marker!=EOI);
178:
1.3 paf 179: if(flag && h_buf) {
1.1 paf 180: JPG_Frame& h=*reinterpret_cast<JPG_Frame *>(h_buf);
181: width=bytes_to_int(h.width[0], h.width[1]);
182: height=bytes_to_int(h.height[0], h.height[1]);
183: } else
184: PTHROW(0, 0,
185: origin_string,
186: "broken JPEG file - APP0 frame not found");
187: }
188:
189: // measure center
190:
191: void measure(Pool& pool, const String& file_name,
192: Measure_reader& reader, int& width, int& height) {
193: if(const char *cext=strrchr(file_name.cstr(), '.')) {
194: cext++;
195: if(strcasecmp(cext, "GIF")==0)
196: measure_gif(pool, &file_name, reader, width, height);
197: else if(strcasecmp(cext, "JPG")==0 || strcasecmp(cext, "JPEG")==0)
198: measure_jpeg(pool, &file_name, reader, width, height);
199: else
200: PTHROW(0, 0,
201: &file_name,
202: "unhandled image file name extension '%s'", cext);
203: } else
204: PTHROW(0, 0,
205: &file_name,
206: "can not determine image type - no file name extension");
207: }
208:
209: // read from somewhere
210:
211: struct Read_mem_info {
212: unsigned char *ptr;
213: unsigned char *eof;
214: };
215: static size_t read_mem(void*& buf, size_t limit, void *info) {
216: Read_mem_info& rmi=*static_cast<Read_mem_info *>(info);
217: buf=rmi.ptr;
218: size_t read_size=min(limit, (size_t)(rmi.eof-rmi.ptr));
219: rmi.ptr+=read_size;
220: return read_size;
221: }
222:
223: struct Read_disk_info {
224: const String *file_spec;
225: size_t offset;
226: };
227: static size_t read_disk(void*& buf, size_t limit, void *info) {
228: Read_disk_info& rdi=*static_cast<Read_disk_info *>(info);
229: Pool& pool=rdi.file_spec->pool();
230:
231: size_t read_size;
232: file_read(pool, *rdi.file_spec,
233: buf, read_size,
234: false/*as_text*/,
235: true/*fail_on_read_problem*/,
236: rdi.offset, limit);
237:
238: rdi.offset+=read_size;
239: return read_size;
240: }
241:
242: // methods
243:
244: /// ^image:measure[DATA]
245: static void _measure(Request& r, const String& method_name, Array *params) {
246: Pool& pool=r.pool();
247:
248: Value& data=*static_cast<Value *>(params->get(0));
249: // forcing [this body type]
250: r.fail_if_junction_(true, data, method_name, "data must not be code");
251:
252: void *info; Measure_reader::Func read_func;
253: Read_mem_info read_mem_info;
254: Read_disk_info read_disk_info;
255: const String *file_name;
256: if(data.is_string()) {
257: file_name=data.get_string();
258: read_disk_info.file_spec=&r.absolute(*file_name);
259: read_disk_info.offset=0;
260: info=&read_disk_info; read_func=read_disk;
261: } else {
262: const VFile& vfile=*data.as_vfile();
263: file_name=&static_cast<Value *>(vfile.fields().get(*name_name))->as_string();
264: read_mem_info.ptr=(unsigned char *)vfile.value_ptr();
265: read_mem_info.eof=read_mem_info.ptr+vfile.value_size();
266: info=&read_mem_info; read_func=read_mem;
267: }
268:
269: Measure_reader reader(read_func, info);
270: int width, height;
271: measure(pool, *file_name, reader, width, height);
272:
1.6 paf 273: static_cast<VImage *>(r.self)->set(file_name, width, height);
1.1 paf 274: }
275:
1.4 paf 276: struct Attrib_info {
277: String *tag;
278: Hash *skip;
279: };
1.3 paf 280: static void append_attrib_pair(const Hash::Key& key, Hash::Val *val, void *info) {
1.4 paf 281: Attrib_info& ai=*static_cast<Attrib_info *>(info);
282:
283: if(ai.skip && ai.skip->get(key))
284: return;
285:
1.3 paf 286: Value& value=*static_cast<Value *>(val);
287: // src="a.gif" width=123 ismap[=-1]
1.4 paf 288: *ai.tag << " " << key;
1.6 paf 289: if(value.is_string() || value.as_double()>=0)
290: *ai.tag << "=\"" << value.as_string() << "\"";
1.3 paf 291: }
292: /// ^image.html[]
293: /// ^image.html[hash]
294: static void _html(Request& r, const String& method_name, Array *params) {
295: Pool& pool=r.pool();
296:
297: String tag(pool);
298: tag << "<img";
1.4 paf 299:
300: Hash& fields=static_cast<VImage *>(r.self)->fields();
1.5 paf 301: Hash *attribs=0;
1.4 paf 302:
1.7 ! paf 303: if(params->size())
1.5 paf 304: if(attribs=static_cast<Value *>(params->get(0))->get_hash()) {
1.4 paf 305: Attrib_info attrib_info={&tag, 0};
1.5 paf 306: attribs->for_each(append_attrib_pair, &attrib_info);
1.4 paf 307: } else
308: PTHROW(0, 0,
309: &method_name,
1.5 paf 310: "attributes must be must be hash");
1.4 paf 311:
1.5 paf 312: Attrib_info attrib_info={&tag, attribs};
1.4 paf 313: fields.for_each(append_attrib_pair, &attrib_info);
1.6 paf 314: tag << " />";
1.3 paf 315: r.write_pass_lang(tag);
316: }
1.7 ! paf 317: /*
1.6 paf 318: /// ^image.load[background.gif]
319: static void _load(Request& r, const String& method_name, Array *params) {
320: Pool& pool=r.pool();
321:
322: Value& vfile_name=*static_cast<Value *>(params->get(0));
323: // forcing [this body type]
324: r.fail_if_junction_(true, vfile_name, method_name, "file name must not be code");
325: const String& file_name=vfile_name.as_string();
326:
327: const char *file_name_cstr=r.absolute(file_name).cstr(String::UL_FILE_NAME);
328: if(FILE *f=fopen(file_name_cstr, "rb")) {
329: gdImagePtr image=gdImageCreateFromGif(f);
330: int width=gdImageSX(image);
331: int height=gdImageSY(image);
332: fclose(f);
333:
334: static_cast<VImage *>(r.self)->set(&file_name, width, height, image);
335: } else
336: PTHROW(0, 0,
337: &method_name,
338: "can not open background image '%s'", file_name_cstr);
339: }
1.7 ! paf 340: */
1.6 paf 341:
342: inline void v2rgb(int v, int& r, int& g, int& b) {
343: r=v>>8*2 & 0xFF;
344: g=v>>8*1 & 0xFF;
345: b=v & 0xFF;
346: }
347: /// ^image.create[width;height] bgcolor=white
348: /// ^image.create[width;height;bgcolor]
1.7 ! paf 349: /// @test ImagingDelete
1.6 paf 350: static void _create(Request& r, const String& method_name, Array *params) {
351: Pool& pool=r.pool();
352:
353: int width=(int)r.process(*static_cast<Value *>(params->get(0))).as_double();
354: int height=(int)r.process(*static_cast<Value *>(params->get(1))).as_double();
355: int bgcolor_value=0xFFFFFF;
356: if(params->size()>2)
357: bgcolor_value=
358: (int)r.process(*static_cast<Value *>(params->get(2))).as_double();
1.7 ! paf 359: Imaging im=ImagingNew("P", width, height);
! 360: ImagingPalette palette=ImagingPaletteNew("RGB");
! 361: ImagingPaletteCachePrepare(palette);
1.6 paf 362: {
363: int r,g,b; v2rgb(bgcolor_value, r,g,b);
1.7 ! paf 364: ImagingPaletteCacheUpdate(palette, r,g,b);
! 365: int bgcolor_index=ImagingPaletteCache(palette, r,g,b);
! 366: ImagingDrawRectangle(im, 0, 0, width-1, height-1,
! 367: bgcolor_index, bgcolor_index);
1.6 paf 368: }
1.7 ! paf 369: static_cast<VImage *>(r.self)->set(0, width, height, im);
1.6 paf 370: }
371:
372: /// ^image.gif[]
373: /// ^image.gif[user-file-name]
374: static void _gif(Request& r, const String& method_name, Array *params) {
375: Pool& pool=r.pool();
376:
1.7 ! paf 377: Imaging im=static_cast<VImage *>(r.self)->im;
! 378: if(!im)
1.6 paf 379: PTHROW(0, 0,
380: &method_name,
381: "does not contain image");
382:
383: char *file_name_cstr=0;
1.7 ! paf 384: if(params->size()) {
1.6 paf 385: Value& vfile_name=*static_cast<Value *>(params->get(0));
386: // forcing [this body type]
387: r.fail_if_junction_(true, vfile_name, method_name, "file name must not be code");
388:
389: file_name_cstr=vfile_name.as_string().cstr(String::UL_FILE_NAME);
390: }
391: // could _ but don't thing it's wise to use $image.src for vfile.name
392:
1.7 ! paf 393: ImagingCodecStateInstance codec_state={0};
! 394: GIFENCODERSTATE encoder_state={0};
! 395: int bits;
! 396: codec_state.shuffle=ImagingFindPacker("P", &bits);
! 397: codec_state.bits=codec_state.bits=bits;
! 398: codec_state.xsize = im->xsize;
! 399: codec_state.ysize = im->ysize;
! 400: /* Allocate memory buffer (if bits field is set) */
! 401: codec_state.bytes = (codec_state.bits * codec_state.xsize+7)/8;
! 402: codec_state.buffer = (UINT8*) malloc(codec_state.bytes);
! 403: codec_state.context=&encoder_state;
! 404:
! 405: String result(pool);
! 406: do {
! 407: const int buf_size=10*0x400;
! 408: UINT8 *buf=(UINT8 *)pool.malloc(buf_size);
! 409: int encoded_size=ImagingGifEncode(im, &codec_state, buf, buf_size);
! 410: if(encoded_size>0)
! 411: result.APPEND_CLEAN((const char *)buf, encoded_size, 0, 0);
! 412: } while(codec_state.errcode==0);
! 413:
1.6 paf 414: VFile& vfile=*new(pool) VFile(pool);
415: String& image_gif=*new(pool) String(pool, "image/gif");
1.7 ! paf 416: vfile.set(false/*not tainted*/, result.cstr(), result.size(),
! 417: file_name_cstr, &image_gif);
1.6 paf 418:
419: r.write_no_lang(vfile);
420: }
421:
1.1 paf 422: // initialize
423: void initialize_image_class(Pool& pool, VStateless_class& vclass) {
424: // ^image:measure[DATA]
425: vclass.add_native_method("measure", Method::CT_DYNAMIC, _measure, 1, 1);
1.3 paf 426:
427: /// ^image.html[]
428: /// ^image.html[hash]
429: vclass.add_native_method("html", Method::CT_DYNAMIC, _html, 0, 1);
1.6 paf 430:
431: /// ^image.load[background.gif]
1.7 ! paf 432: // vclass.add_native_method("load", Method::CT_DYNAMIC, _load, 1, 1);
1.6 paf 433:
434: /// ^image.create[width;height] bgcolor=white
435: /// ^image.create[width;height;bgcolor]
436: vclass.add_native_method("create", Method::CT_DYNAMIC, _create, 2, 3);
437:
438: /// ^image.gif[]
439: /// ^image.gif[user-file-name]
440: vclass.add_native_method("gif", Method::CT_DYNAMIC, _gif, 0, 1);
1.1 paf 441: }
E-mail: