|
|
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.2 ! paf 8: $Id: image.C,v 1.1 2001/04/10 13:22:55 paf Exp $
1.1 paf 9: */
10:
11: #include "pa_config_includes.h"
12:
13: #ifdef WIN32
14: # include "smtp/smtp.h"
15: #endif
16:
17: #include "pa_common.h"
18: #include "pa_request.h"
19: #include "pa_vfile.h"
20: #include "pa_vimage.h"
21:
22: // global var
23:
24: VStateless_class *image_class;
25:
26: // helpers
27:
28: class Measure_reader {
29: public:
30: enum { READ_CHUNK_SIZE=10*0x400 }; // 10K
31: typedef size_t (*Func)(void *& buf, size_t limit, void *info);
32:
33: Measure_reader(Func afunc, void *ainfo) :
34: func(afunc), info(ainfo),
35: chunk(0), offset(0), size(0) {
36: }
37:
38: size_t read(unsigned char *& buf, size_t limit) {
39: if(!size) // nothing left
40: size=(*func)(chunk, READ_CHUNK_SIZE, info);
41: if(!size) // EOF
42: return 0;
43:
44: // something left
45: size_t read_size=min(offset+limit, size)-offset;
46: buf=((unsigned char *)chunk)+offset;
47: offset+=read_size;
48: return read_size;
49: }
50:
51: private:
52: Func func;
53: void *info;
54:
55: void *chunk;
56: size_t offset;
57: size_t size;
58: };
59:
60: // GIF
61: struct GIF_Header {
62: char type[3]; // 'GIF'
63: char version[3];
64: unsigned char width[2];
65: unsigned char height[2];
66: char dif;
67: char fonColor;
68: char nulls;
69: };
70:
71: // JPEG
72: struct JFIF_Header {
73: char length[2]; // length of JFIF segment marker
74: char identifier[5]; // JFIF identifier
75: char version[2]; // version
76: char units; // units X of Y pixel density
77: char xdensity[2]; // X pixel density
78: char ydensity[2]; // X pixel density
79: char xthumbnails; // width of thumbnails
80: char ythumbnails; // height of thumbnails
81: char reserved; // reserved
82: };
83: struct JPG_Frame {
84: char length[2]; // length of image marker
85: char data; // data precision of bits/sample
86: char height[2]; // image height
87: char width[2]; // image width
88: char numComponents; // number of color components
89: };
90:
91: //
92:
93: inline short bytes_to_int(unsigned char HI, unsigned char LO) {
94: return (short)((HI<<8) + LO);
95: }
96:
97: void measure_gif(Pool& pool, const String *origin_string,
98: Measure_reader& reader, int& width, int& height) {
99:
100: unsigned char *buf;
101: const int head_size=sizeof(GIF_Header);
102: if(reader.read(buf, head_size)<head_size)
103: PTHROW(0, 0,
104: origin_string,
105: "broken GIF header - file size is less then %d bytes", head_size);
106:
107: GIF_Header& screenD=*reinterpret_cast<GIF_Header *>(buf);
108: if(strncmp(screenD.type, "GIF", 3)!=0)
109: PTHROW(0, 0,
110: origin_string,
111: "bad image file - GIF signature not found");
112:
113: width=bytes_to_int(screenD.width[1], screenD.width[0]);
114: height=bytes_to_int(screenD.height[1], screenD.height[0]);
115: }
116:
117: void measure_jpeg(Pool& pool, const String *origin_string,
118: Measure_reader& reader, int& width, int& height) {
1.2 ! paf 119: // JFIF format markers
! 120: const unsigned char SOI=0xD8;
! 121: const unsigned char EOI=0xD9;
! 122: const unsigned char APP0=0xE0;
! 123: const unsigned char SOF0=0xC0;
! 124: const unsigned char SOF2=0xC2;
! 125: const unsigned char COM=0xFE;
! 126:
1.1 paf 127: unsigned char *screenD_buf;
128: unsigned char *h_buf;
129:
130: bool flag=false;
131:
132: unsigned char *prefix;
133: const prefix_size=2;
134: if(reader.read(prefix, prefix_size)<prefix_size)
135: PTHROW(0, 0,
136: origin_string,
137: "broken JPEG file - size is less then %d bytes", prefix_size);
138:
139: if(((unsigned char *)prefix)[1]!=SOI)
140: PTHROW(0, 0,
141: origin_string,
142: "broken JPEG file - second byte of header is not 0x%02X", SOI);
143:
144: unsigned char zero=0;
145: unsigned char *marker=&zero;
146:
147: do {
148: while((*marker)!=0xFF)
149: if(reader.read(marker, sizeof(char))<=0) break;
150: if(reader.read(marker, sizeof(char))<=0) break;
151: switch(*marker) {
152: case EOI:
153: marker=&zero;
154: break;
155: case APP0:
156: if(!flag) {
157: flag=true;
158: if(reader.read(screenD_buf, sizeof(JFIF_Header)) < sizeof(JFIF_Header))
159: break;
160: JFIF_Header& screenD=*reinterpret_cast<JFIF_Header *>(screenD_buf);
161: if((bytes_to_int(screenD.length[0], screenD.length[1]) < 16) ||
1.2 ! paf 162: strcasecmp(screenD.identifier, "JFIF")) flag=false;
1.1 paf 163: }
164: break;
165: case SOF0:
166: case SOF2:
167: if(reader.read(h_buf, sizeof(JPG_Frame))<sizeof(JPG_Frame))
168: flag=false;
169: break;
170: default: break;
171: }
172: } while(*marker!=EOI);
173:
174: if(flag) {
175: JPG_Frame& h=*reinterpret_cast<JPG_Frame *>(h_buf);
176: width=bytes_to_int(h.width[0], h.width[1]);
177: height=bytes_to_int(h.height[0], h.height[1]);
178: } else
179: PTHROW(0, 0,
180: origin_string,
181: "broken JPEG file - APP0 frame not found");
182: }
183:
184: // measure center
185:
186: void measure(Pool& pool, const String& file_name,
187: Measure_reader& reader, int& width, int& height) {
188: if(const char *cext=strrchr(file_name.cstr(), '.')) {
189: cext++;
190: if(strcasecmp(cext, "GIF")==0)
191: measure_gif(pool, &file_name, reader, width, height);
192: else if(strcasecmp(cext, "JPG")==0 || strcasecmp(cext, "JPEG")==0)
193: measure_jpeg(pool, &file_name, reader, width, height);
194: else
195: PTHROW(0, 0,
196: &file_name,
197: "unhandled image file name extension '%s'", cext);
198: } else
199: PTHROW(0, 0,
200: &file_name,
201: "can not determine image type - no file name extension");
202: }
203:
204: // read from somewhere
205:
206: struct Read_mem_info {
207: unsigned char *ptr;
208: unsigned char *eof;
209: };
210: static size_t read_mem(void*& buf, size_t limit, void *info) {
211: Read_mem_info& rmi=*static_cast<Read_mem_info *>(info);
212: buf=rmi.ptr;
213: size_t read_size=min(limit, (size_t)(rmi.eof-rmi.ptr));
214: rmi.ptr+=read_size;
215: return read_size;
216: }
217:
218: struct Read_disk_info {
219: const String *file_spec;
220: size_t offset;
221: };
222: static size_t read_disk(void*& buf, size_t limit, void *info) {
223: Read_disk_info& rdi=*static_cast<Read_disk_info *>(info);
224: Pool& pool=rdi.file_spec->pool();
225:
226: size_t read_size;
227: file_read(pool, *rdi.file_spec,
228: buf, read_size,
229: false/*as_text*/,
230: true/*fail_on_read_problem*/,
231: rdi.offset, limit);
232:
233: rdi.offset+=read_size;
234: return read_size;
235: }
236:
237: // methods
238:
239: /// ^image:measure[DATA]
240: static void _measure(Request& r, const String& method_name, Array *params) {
241: Pool& pool=r.pool();
242:
243: Value& data=*static_cast<Value *>(params->get(0));
244: // forcing [this body type]
245: r.fail_if_junction_(true, data, method_name, "data must not be code");
246:
247: void *info; Measure_reader::Func read_func;
248: Read_mem_info read_mem_info;
249: Read_disk_info read_disk_info;
250: const String *file_name;
251: if(data.is_string()) {
252: file_name=data.get_string();
253: read_disk_info.file_spec=&r.absolute(*file_name);
254: read_disk_info.offset=0;
255: info=&read_disk_info; read_func=read_disk;
256: } else {
257: const VFile& vfile=*data.as_vfile();
258: file_name=&static_cast<Value *>(vfile.fields().get(*name_name))->as_string();
259: read_mem_info.ptr=(unsigned char *)vfile.value_ptr();
260: read_mem_info.eof=read_mem_info.ptr+vfile.value_size();
261: info=&read_mem_info; read_func=read_mem;
262: }
263:
264: Measure_reader reader(read_func, info);
265: int width, height;
266: measure(pool, *file_name, reader, width, height);
267:
268: static_cast<VImage *>(r.self)->set(*file_name, width, height);
269: }
270:
271: // initialize
272: void initialize_image_class(Pool& pool, VStateless_class& vclass) {
273: // ^image:measure[DATA]
274: vclass.add_native_method("measure", Method::CT_DYNAMIC, _measure, 1, 1);
275: }