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

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

E-mail: