Annotation of parser3/src/classes/image.C, revision 1.77

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

E-mail: