// // Implementation of classes Fl_SVG_Graphics_Driver and Fl_SVG_File_Surface in the Fast Light Tool Kit (FLTK). // // Copyright 2020-2022 by Bill Spitzak and others. // // This library is free software. Distribution and use rights are outlined in // the file "COPYING" which should have been included with this file. If this // file is missing or damaged, see the license at: // // https://www.fltk.org/COPYING.php // // Please see the following page on how to report bugs and issues: // // https://www.fltk.org/bugs.php // // Complete implementation to draw into an SVG file using the standard FLTK drawing API. #include #include #ifdef FLTK_USE_SVG #include #include #include #include #include #include "../../Fl_System_Driver.H" #include #include #include #include #include #include #include extern "C" { #if defined(HAVE_LIBPNG) # ifdef HAVE_PNG_H # include # else # include # endif // HAVE_PNG_H #endif // HAVE_LIBPNG #ifdef HAVE_LIBJPEG # include #endif // HAVE_LIBJPEG } class Fl_SVG_Graphics_Driver : public Fl_Graphics_Driver { FILE *out_; int width_; int line_style_; const char *linecap_; const char *linejoin_; uchar red_, green_, blue_; char *dasharray_; // the dash array as SVG needs it char *user_dash_array_; // the dash array as FLTK needs it class Clip { public: int x, y, w, h; // the clip rectangle char Id[12]; // "none" or SVG Id of this clip rectangle Clip *prev; // previous in pile of clips }; Clip * clip_; // top of pile of clips int clip_count_; // to generate distinct SVG clip Ids char *last_rgb_name_; // NULL or SVG Id of last defined RGB image const char *family_; const char *bold_; const char *style_; public: Fl_SVG_Graphics_Driver(FILE*); ~Fl_SVG_Graphics_Driver(); FILE* file() {return out_;} protected: int clocale_printf(const char *format, ...); void rect(int x, int y, int w, int h) FL_OVERRIDE; void rectf(int x, int y, int w, int h) FL_OVERRIDE; virtual void compute_dasharray(float s, char *dashes=0); void line_style(int style, int width, char *dashes=0) FL_OVERRIDE; void line(int x1, int y1, int x2, int y2) FL_OVERRIDE; void line(int x1, int y1, int x2, int y2, int x3, int y3) FL_OVERRIDE; void font_(int f, int s); void font(int f, int s) FL_OVERRIDE; Fl_Font font() FL_OVERRIDE; void draw(const char *str, int n, int x, int y) FL_OVERRIDE; void draw(const char*, int, float, float) FL_OVERRIDE; void draw(int, const char*, int, int, int) FL_OVERRIDE; void rtl_draw(const char *str, int n, int x, int y) FL_OVERRIDE; void color(uchar r, uchar g, uchar b) FL_OVERRIDE; void color(Fl_Color c) FL_OVERRIDE; Fl_Color color() FL_OVERRIDE; double width(const char*, int) FL_OVERRIDE; double width(unsigned int c) FL_OVERRIDE; void text_extents(const char*, int n, int& dx, int& dy, int& w, int& h) FL_OVERRIDE; int height() FL_OVERRIDE; int descent() FL_OVERRIDE; void draw_rgb(Fl_RGB_Image *rgb, int XP, int YP, int WP, int HP, int cx, int cy) FL_OVERRIDE; void define_rgb_png(Fl_RGB_Image *rgb, const char *name, int x, int y); void define_rgb_jpeg(Fl_RGB_Image *rgb, const char *name, int x, int y); void draw_pixmap(Fl_Pixmap *pxm,int XP, int YP, int WP, int HP, int cx, int cy) FL_OVERRIDE; void draw_bitmap(Fl_Bitmap *bm,int XP, int YP, int WP, int HP, int cx, int cy) FL_OVERRIDE; void draw_image(const uchar* buf, int x, int y, int w, int h, int d, int l) FL_OVERRIDE; void draw_image(Fl_Draw_Image_Cb cb, void* data, int x, int y, int w, int h, int d) FL_OVERRIDE; void draw_image_mono(const uchar* buf, int x, int y, int w, int h, int d, int l) FL_OVERRIDE; void draw_image_mono(Fl_Draw_Image_Cb cb, void* data, int x, int y, int w, int h, int d) FL_OVERRIDE; void push_clip(int x, int y, int w, int h) FL_OVERRIDE; void push_no_clip() FL_OVERRIDE; void pop_clip() FL_OVERRIDE; int clip_box(int x, int y, int w, int h, int& X, int& Y, int& W, int& H) FL_OVERRIDE; int not_clipped(int x, int y, int w, int h) FL_OVERRIDE; void polygon(int x0, int y0, int x1, int y1, int x2, int y2) FL_OVERRIDE; void polygon(int x0, int y0, int x1, int y1, int x2, int y2, int x3, int y3) FL_OVERRIDE; void loop(int x0, int y0, int x1, int y1, int x2, int y2, int x3, int y3) FL_OVERRIDE; void loop(int x0, int y0, int x1, int y1, int x2, int y2) FL_OVERRIDE; void point(int x, int y) FL_OVERRIDE; void end_points() FL_OVERRIDE; void end_line() FL_OVERRIDE; void end_polygon() FL_OVERRIDE; void end_complex_polygon() FL_OVERRIDE; void circle(double x, double y,double r) FL_OVERRIDE; void arc(int x,int y,int w,int h,double a1,double a2) FL_OVERRIDE; void arc(double x, double y, double r, double start, double end) FL_OVERRIDE; void pie(int x,int y,int w,int h,double a1,double a2) FL_OVERRIDE; void arc_pie(char AorP, int x, int y, int w, int h, double a1, double a2); }; Fl_SVG_Graphics_Driver::Fl_SVG_Graphics_Driver(FILE *f) { out_ = f; width_ = 1; line_style_ = 0; linecap_ = "butt"; linejoin_ = "miter"; family_ = ""; bold_ = ""; style_ = ""; red_ = green_ = blue_ = 0; clip_count_ = 0; clip_ = NULL; user_dash_array_ = 0; dasharray_ = fl_strdup("none"); p_size = 0; last_rgb_name_ = NULL; } Fl_SVG_Graphics_Driver::~Fl_SVG_Graphics_Driver() { if (user_dash_array_) free(user_dash_array_); if (dasharray_) free(dasharray_); while (clip_){ Clip * c= clip_; clip_= clip_->prev; delete c; } if (last_rgb_name_) free(last_rgb_name_); } int Fl_SVG_Graphics_Driver::clocale_printf(const char *format, ...) { va_list args; va_start(args, format); int retval = Fl::system_driver()->clocale_vprintf(out_, format, args); va_end(args); return retval; } static int clocale_fprintf(FILE *f, const char *format, ...) { va_list args; va_start(args, format); int retval = Fl::system_driver()->clocale_vprintf(f, format, args); va_end(args); return retval; } void Fl_SVG_Graphics_Driver::rect(int x, int y, int w, int h) { fprintf(out_, "\n", x, y, w-1, h-1, red_, green_, blue_, width_, dasharray_, linecap_, linejoin_); } void Fl_SVG_Graphics_Driver::rectf(int x, int y, int w, int h) { clocale_fprintf(out_, "\n", x-.5, y-.5, w, h, red_, green_, blue_); } void Fl_SVG_Graphics_Driver::point(int x, int y) { rectf(x,y,1,1); } void Fl_SVG_Graphics_Driver::line(int x1, int y1, int x2, int y2) { fprintf(out_, "\n", x1,y1,x2,y2, red_, green_, blue_, width_, linecap_, linejoin_, dasharray_); } void Fl_SVG_Graphics_Driver::line(int x1, int y1, int x2, int y2, int x3, int y3) { fprintf(out_, "\n", x1, y1, x2, y2, x3, y3, red_, green_, blue_, width_, linecap_, linejoin_, dasharray_); } void Fl_SVG_Graphics_Driver::font_(int ft, int s) { Fl_Graphics_Driver::font(ft, s); int famnum = ft/4; if (famnum == 0) family_ = "Helvetica"; else if (famnum == 1) family_ = "Courier"; else family_ = "Times"; int modulo = ft % 4; int use_bold = modulo == 1 || modulo == 3; int use_italic = modulo >= 2; bold_ = ( use_bold ? " font-weight=\"bold\"" : "" ); style_ = ( use_italic ? " font-style=\"italic\"" : "" ); if (use_italic && famnum != 2) style_ = " font-style=\"oblique\""; } void Fl_SVG_Graphics_Driver::font(int ft, int s) { Fl_Display_Device::display_device()->driver()->font(ft, s); font_(ft, s); } Fl_Font Fl_SVG_Graphics_Driver::font() { return Fl_Graphics_Driver::font(); } void Fl_SVG_Graphics_Driver::compute_dasharray(float s, char *dashes) { if (user_dash_array_ && user_dash_array_ != dashes) {free(user_dash_array_); user_dash_array_ = NULL;} if (dashes && *dashes) { if (dasharray_) free(dasharray_); int array_len = int(10*strlen(dashes) + 1); dasharray_ = (char*)calloc(array_len, 1); for (char *p = dashes; *p; p++) { int c = snprintf(dasharray_+strlen(dasharray_), array_len, "%.3f,", (*p)/s); array_len -= c; } dasharray_[strlen(dasharray_) - 1] = 0; if (user_dash_array_ != dashes) user_dash_array_ = fl_strdup(dashes); return; } int dash_part = line_style_ & 0xFF; if (dash_part == FL_SOLID) { if (strcmp(dasharray_, "none")) { free(dasharray_); dasharray_ = fl_strdup("none"); } } else { int cap_part = (line_style_ & 0xF00); bool is_flat = (cap_part == FL_CAP_FLAT || cap_part == 0); float dot = (is_flat ? width_/s : width_*0.6f/s); float gap = (is_flat ? width_/s : width_*1.5f/s); float big = (is_flat ? 3*width_/s : width_*2.5f/s); if (dasharray_) free(dasharray_); dasharray_ = (char*)malloc(61); if (dash_part == FL_DOT) snprintf(dasharray_, 61, "%.3f,%.3f", dot, gap); else if (dash_part == FL_DASH) snprintf(dasharray_, 61, "%.3f,%.3f", big, gap); else if (dash_part == FL_DASHDOT) snprintf(dasharray_, 61, "%.3f,%.3f,%.3f,%.3f", big, gap, dot, gap); else snprintf(dasharray_, 61, "%.3f,%.3f,%.3f,%.3f,%.3f,%.3f", big, gap, dot, gap, dot, gap); } } void Fl_SVG_Graphics_Driver::line_style(int style, int width, char *dashes) { line_style_ = style; if (width == 0) width = 1; width_ = width; int cap_part = style & 0xF00; if (cap_part == FL_CAP_SQUARE) linecap_ = "square"; else if (cap_part == FL_CAP_ROUND) linecap_ = "round"; else linecap_ = "butt"; int join_part = style & 0xF000; if (join_part == FL_JOIN_BEVEL) linejoin_ = "bevel"; else if (join_part == FL_JOIN_MITER) linejoin_ = "miter"; else if (join_part == FL_JOIN_ROUND) linejoin_ = "round"; else linejoin_ = "miter"; compute_dasharray(1., dashes); } void Fl_SVG_Graphics_Driver::draw(const char *str, int n, int x, int y) { // Caution: Internet Explorer ignores the xml:space="preserve" attribute // work-around: replace all spaces by no-break space = U+00A0 = 0xC2-0xA0 (UTF-8) before sending to IE fprintf(out_, "", x, y, family_, bold_, style_, size(), red_, green_, blue_, (int)width(str, n)); for (int i = 0; i < n; i++) { if (str[i] == '&') fputs("&", out_); else if (str[i] == '<') fputs("<", out_); else if (str[i] == '>') fputs(">", out_); else fputc(str[i], out_); } fputs("\n", out_); } void Fl_SVG_Graphics_Driver::draw(const char* str, int n, float fx, float fy) { return draw(str, n, (int)fx, (int)fy); } void Fl_SVG_Graphics_Driver::draw(int angle, const char* str, int n, int x, int y) { fprintf(out_, "", x, y, -angle); draw(str, n, 0, 0); fputs("\n", out_); } void Fl_SVG_Graphics_Driver::rtl_draw(const char *str, int n, int x, int y) { int w = (int)width(str, n); draw(str, n, x - w, y); } void Fl_SVG_Graphics_Driver::color(Fl_Color c) { Fl_Graphics_Driver::color(c); Fl::get_color(c, red_, green_, blue_); } void Fl_SVG_Graphics_Driver::color(uchar r, uchar g, uchar b) { red_ = r; green_ = g; blue_ = b; } Fl_Color Fl_SVG_Graphics_Driver::color() { return Fl_Graphics_Driver::color(); } double Fl_SVG_Graphics_Driver::width(const char* str, int l) { return Fl_Display_Device::display_device()->driver()->width(str, l); } double Fl_SVG_Graphics_Driver::width(unsigned int c) { return Fl_Display_Device::display_device()->driver()->width(c); } void Fl_SVG_Graphics_Driver::text_extents(const char *c, int n, int& dx, int& dy, int& w, int& h) { Fl::first_window()->make_current(); // to get a valid drawing gc Fl_Display_Device::display_device()->driver()->text_extents(c, n, dx, dy, w, h); } int Fl_SVG_Graphics_Driver::height() { return Fl_Display_Device::display_device()->driver()->height(); } int Fl_SVG_Graphics_Driver::descent() { return Fl_Display_Device::display_device()->driver()->descent(); } Fl_SVG_File_Surface::Fl_SVG_File_Surface(int w, int h, FILE *f, int (*closef)(FILE*)) : Fl_Widget_Surface(new Fl_SVG_Graphics_Driver(f)) { closef_ = closef; Fl_Window *win = Fl::first_window(); float s = (win ? Fl::screen_scale(win->screen_num()) : 1); int sw = int(w * s), sh = int(h * s); fprintf(f, "\n" "\n" "\n", sw, sh, sw, sh); width_ = w; height_ = h; clocale_fprintf(f, "\n", s); fputs("\n", f); } Fl_SVG_File_Surface::~Fl_SVG_File_Surface() { if (driver()) close(); } FILE *Fl_SVG_File_Surface::file() { Fl_SVG_Graphics_Driver *driver = (Fl_SVG_Graphics_Driver*)this->driver(); return driver->file(); } int Fl_SVG_File_Surface::close() { Fl_SVG_Graphics_Driver *driver = (Fl_SVG_Graphics_Driver*)this->driver(); fputs("\n", driver->file()); int retval = (closef_ ? closef_(driver->file()) : fclose(driver->file())); delete driver; this->driver(NULL); return retval; } void Fl_SVG_File_Surface::translate(int x, int y) { Fl_SVG_Graphics_Driver *driver = (Fl_SVG_Graphics_Driver*)this->driver(); fprintf(driver->file(), "\n", x, y); } void Fl_SVG_File_Surface::untranslate() { Fl_SVG_Graphics_Driver *driver = (Fl_SVG_Graphics_Driver*)this->driver(); fputs("\n", driver->file()); } void Fl_SVG_File_Surface::origin(int x, int y) { Fl_SVG_Graphics_Driver *driver = (Fl_SVG_Graphics_Driver*)this->driver(); fprintf(driver->file(), "\n", x, y); Fl_Widget_Surface::origin(x, y); } int Fl_SVG_File_Surface::printable_rect(int *w, int *h) { *w = width_; *h = height_; return 0; } struct svg_base64_t { // holds data useful to perform base64-encoding of a stream of bytes FILE *svg; // where base64-encoded data is output int lline; // follows length of current line in svg file uchar buff[3]; // holds up to 3 bytes that still need encoding int lbuf; // # of valid bytes in buff }; // Performs base64 encoding of up to 3 bytes. // To be called successively with 3 consecutive bytes (l=3), // and possibly with l=1 or l=2 only at the end of the byte stream. // Always writes 4 printable characters to the output FILE. static void to_base64(uchar *p, int l, svg_base64_t *svg_base64) { static char base64_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; uchar B0 = *p++; uchar B1 = (l == 1 ? 0 : *p++); uchar B2 = (l <= 2 ? 0 : *p); fputc(base64_table[ B0 >> 2 ], svg_base64->svg); fputc(base64_table[ ((B0 & 0x3) << 4) + (B1 >> 4) ], svg_base64->svg); fputc( (l == 1 ? '=' : base64_table[ ((B1 & 0xF) << 2) + (B2 >> 6) ]), svg_base64->svg ); fputc( (l < 3 ? '=' : base64_table[ B2 & 0x3F ]), svg_base64->svg ); svg_base64->lline += 4; if (svg_base64->lline >= 80) { fputc('\n', svg_base64->svg); svg_base64->lline = 0; } } // Writes to the svg file, in base64-encoded form, a block of length bytes. // 1 or 2 bytes may remain unprocessed after return. // Returns the number of remaining unprocessed bytes. static size_t write_by_3(uchar *data, size_t length, svg_base64_t *svg_base64) { while (length >= 3) { to_base64(data, 3, svg_base64); data += 3; length -= 3; } return length; } #ifdef HAVE_LIBPNG // processes length bytes of the png stream under construction static void user_write_data(png_structp png_ptr, png_bytep data, png_size_t length) { svg_base64_t *svg_base64_data = (svg_base64_t*)png_get_io_ptr(png_ptr); if (svg_base64_data->lbuf == 1 && length >= 2) { svg_base64_data->buff[1] = *data++; length--; svg_base64_data->buff[2] = *data++; length--; write_by_3(svg_base64_data->buff, 3, svg_base64_data); } else if (svg_base64_data->lbuf == 2 && length >= 1) { svg_base64_data->buff[2] = *data++; length--; write_by_3(svg_base64_data->buff, 3, svg_base64_data); } size_t new_l = length; if (length >= 3) { new_l = write_by_3(data, length, svg_base64_data); } svg_base64_data->lbuf = (int)new_l; if (new_l) { memcpy(svg_base64_data->buff, data + length - new_l, new_l); } } // processes last bytes to be base64 encoded static void user_flush_data(png_structp png_ptr) { svg_base64_t *svg_base64_data = (svg_base64_t*)png_get_io_ptr(png_ptr); if (svg_base64_data->lbuf) to_base64(svg_base64_data->buff, svg_base64_data->lbuf, svg_base64_data); } /* How to define first the image data and next use it, possibly several times: */ /* Specify image data and draw it in one go: */ void Fl_SVG_Graphics_Driver::define_rgb_png(Fl_RGB_Image *rgb, const char *name, int x, int y) { png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (!png_ptr) return; png_infop info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { png_destroy_write_struct(&png_ptr, (png_infopp)NULL); return; } if (name) { if (last_rgb_name_) free(last_rgb_name_); last_rgb_name_ = fl_strdup(name); } float f = rgb->data_w() > rgb->data_h() ? float(rgb->w()) / rgb->data_w(): float(rgb->h()) / rgb->data_h(); if (name) fprintf(out_, "data_w(), f*rgb->data_h()); // Transforms the image into a stream of bytes in PNG format, // base64-encode this byte stream, and outputs the result to the svg FILE. svg_base64_t svg_base64_data; svg_base64_data.svg = out_; svg_base64_data.lline = 0; svg_base64_data.lbuf = 0; // user_write_data is a function repetitively called by libpng which receives blocks of bytes. png_set_write_fn(png_ptr, &svg_base64_data, user_write_data, user_flush_data); int color_type; switch (rgb->d()) { case 1: color_type = PNG_COLOR_TYPE_GRAY; break; case 2: color_type = PNG_COLOR_TYPE_GRAY_ALPHA; break; case 3: color_type = PNG_COLOR_TYPE_RGB; break; case 4: default: color_type = PNG_COLOR_TYPE_RGB_ALPHA; } png_set_IHDR(png_ptr, info_ptr, rgb->data_w(), rgb->data_h(), 8, color_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); const uchar **row_pointers = new const uchar*[rgb->data_h()]; int ld = rgb->ld() ? rgb->ld() : rgb->d() * rgb->data_w(); for (int i=0; i < rgb->data_h(); i++) row_pointers[i] = (rgb->array + i*ld); png_set_rows(png_ptr, info_ptr, (png_bytepp)row_pointers); png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL); png_write_end(png_ptr, NULL); user_flush_data(png_ptr); png_destroy_write_struct(&png_ptr, &info_ptr); delete[] row_pointers; if (name) fputs("\"/>\n", out_); else fputs("\"/>\n", out_); } #endif // HAVE_LIBPNG #ifdef HAVE_LIBJPEG struct jpeg_client_data_struct { JOCTET JPEG_BUFFER[50000]; size_t size; svg_base64_t base64_data; }; static void init_destination(jpeg_compress_struct *cinfo) { jpeg_client_data_struct *client_data = (jpeg_client_data_struct*)(cinfo->client_data); cinfo->dest->next_output_byte = client_data->JPEG_BUFFER; cinfo->dest->free_in_buffer = client_data->size; } static size_t process_jpeg_chunk(jpeg_compress_struct *cinfo, size_t length) { jpeg_client_data_struct *client_data = (jpeg_client_data_struct*)(cinfo->client_data); JOCTET *data = client_data->JPEG_BUFFER; size_t new_l = length; if (length >= 3) { new_l = write_by_3(data, length, &client_data->base64_data); if (new_l) memmove(client_data->JPEG_BUFFER, data + length - new_l, new_l); } cinfo->dest->next_output_byte = client_data->JPEG_BUFFER + new_l; cinfo->dest->free_in_buffer = client_data->size - new_l; return new_l; } static boolean empty_output_buffer(jpeg_compress_struct *cinfo) { jpeg_client_data_struct *client_data = (jpeg_client_data_struct*)(cinfo->client_data); process_jpeg_chunk(cinfo, client_data->size); return TRUE; } static void term_destination(jpeg_compress_struct *cinfo) { jpeg_client_data_struct *client_data = (jpeg_client_data_struct*)(cinfo->client_data); size_t new_l = process_jpeg_chunk(cinfo, client_data->size - cinfo->dest->free_in_buffer); if (new_l) { to_base64(client_data->JPEG_BUFFER, (int)new_l, &client_data->base64_data); } } void Fl_SVG_Graphics_Driver::define_rgb_jpeg(Fl_RGB_Image *rgb, const char *name, int x, int y) { if (name) { if (last_rgb_name_) free(last_rgb_name_); last_rgb_name_ = fl_strdup(name); } float f = rgb->data_w() > rgb->data_h() ? float(rgb->w()) / rgb->data_w(): float(rgb->h()) / rgb->data_h(); if (name) fprintf(out_, "data_w(), f*rgb->data_h()); // Transforms the image into a stream of bytes in JPEG format, // base64-encode this byte stream, and outputs the result to the svg FILE. jpeg_compress_struct cinfo; jpeg_error_mgr jerr; jpeg_client_data_struct jpeg_client_data; jpeg_client_data.size = sizeof(jpeg_client_data.JPEG_BUFFER); cinfo.client_data = &jpeg_client_data; cinfo.err = jpeg_std_error(&jerr); jpeg_create_compress(&cinfo); jpeg_destination_mgr jpeg_mgr; jpeg_mgr.init_destination = init_destination; jpeg_mgr.empty_output_buffer = empty_output_buffer; jpeg_mgr.term_destination = term_destination; cinfo.dest = &jpeg_mgr; cinfo.image_width = rgb->data_w(); cinfo.image_height = rgb->data_h(); cinfo.input_components = rgb->d(); // 1 or 3 cinfo.in_color_space = rgb->d() == 3 ? JCS_RGB : JCS_GRAYSCALE; jpeg_set_defaults(&cinfo); jpeg_client_data.base64_data.svg = out_; jpeg_client_data.base64_data.lline = 0; jpeg_client_data.base64_data.lbuf = 0; jpeg_start_compress(&cinfo, TRUE); int ld = rgb->ld() ? rgb->ld() : rgb->data_w() * rgb->d(); JSAMPROW row_pointer[1]; while (cinfo.next_scanline < cinfo.image_height) { row_pointer[0] = (uchar*)rgb->array + ld*cinfo.next_scanline; jpeg_write_scanlines(&cinfo, row_pointer, 1); } jpeg_finish_compress(&cinfo); jpeg_destroy_compress(&cinfo); if (name) fputs("\"/>\n", out_); else fputs("\"/>\n", out_); } #endif // HAVE_LIBJPEG void Fl_SVG_Graphics_Driver::draw_rgb(Fl_RGB_Image *rgb, int XP, int YP, int WP, int HP, int cx, int cy) { #if defined(HAVE_LIBPNG) char name[24]; bool need_clip = (cx || cy || WP != rgb->w() || HP != rgb->h()); void *p = (void*)*Fl_Graphics_Driver::id(rgb); if (p) snprintf(name, 24, "FLrgb%p", p); else name[0] = 0; if (!p || !last_rgb_name_ || strcmp(name, last_rgb_name_) != 0) { if (*name==0 && need_clip) push_clip(XP, YP, WP, HP); #if defined(HAVE_LIBJPEG) if (rgb->d() == 3 || rgb->d() == 1) define_rgb_jpeg(rgb, *name ? name : NULL, XP-cx, YP-cy); else #endif // HAVE_LIBJPEG define_rgb_png(rgb, *name ? name : NULL, XP-cx, YP-cy); if (*name==0 && need_clip) pop_clip(); } if (*name) { if (need_clip) push_clip(XP, YP, WP, HP); fprintf(out_, "\n", last_rgb_name_, XP-cx, YP-cy); if (need_clip) pop_clip(); } #endif // HAVE_LIBPNG } void Fl_SVG_Graphics_Driver::draw_pixmap(Fl_Pixmap *pxm, int XP, int YP, int WP, int HP, int cx, int cy) { #if defined(HAVE_LIBPNG) char name[24]; bool need_clip = (cx || cy || WP != pxm->w() || HP != pxm->h()); void *p = (void*)*Fl_Graphics_Driver::id(pxm); if (p) snprintf(name, 24, "FLpx%p", p); else name[0] = 0; if (!p || !last_rgb_name_ || strcmp(name, last_rgb_name_) != 0) { Fl_RGB_Image *rgb = new Fl_RGB_Image(pxm); if (*name==0 && need_clip) push_clip(XP, YP, WP, HP); define_rgb_png(rgb, *name ? name : NULL, XP-cx, YP-cy); if (*name==0 && need_clip) pop_clip(); delete rgb; } if (*name) { if (need_clip) push_clip(XP, YP, WP, HP); fprintf(out_, "\n", last_rgb_name_, XP-cx, YP-cy); if (need_clip) pop_clip(); } #endif // HAVE_LIBPNG } void Fl_SVG_Graphics_Driver::draw_bitmap(Fl_Bitmap *bm, int XP, int YP, int WP, int HP, int cx, int cy) { #if defined(HAVE_LIBPNG) char name[45]; bool need_clip = (cx || cy || WP != bm->w() || HP != bm->h()); void *p = (void*)*Fl_Graphics_Driver::id(bm); if (p) snprintf(name, 45, "FLbm%p%X", p, fl_color()); else name[0] = 0; if (!p || !last_rgb_name_ || strcmp(name, last_rgb_name_) != 0) { uchar R, G, B; Fl::get_color(fl_color(), R, G, B); uchar *data = new uchar[bm->data_w() * bm->data_h() * 4]; memset(data, 0, bm->data_w() * bm->data_h() * 4); Fl_RGB_Image *rgb = new Fl_RGB_Image(data, bm->data_w(), bm->data_h(), 4); rgb->alloc_array = 1; int rowBytes = (bm->data_w()+7)>>3 ; for (int j = 0; j < bm->data_h(); j++) { const uchar *p = bm->array + j*rowBytes; for (int i = 0; i < rowBytes; i++) { uchar q = *p; int last = bm->data_w() - 8*i; if (last > 8) last = 8; for (int k=0; k < last; k++) { if (q&1) { uchar *r = (uchar*)rgb->array + j*bm->data_w()*4 + i*8*4 + k*4; *r++ = R; *r++ = G; *r++ = B; *r = ~0; } q >>= 1; } p++; } } if (*name==0 && need_clip) push_clip(XP, YP, WP, HP); define_rgb_png(rgb, *name ? name : NULL, XP-cx, YP-cy); if (*name==0 && need_clip) pop_clip(); delete rgb; } if (*name) { if (need_clip) push_clip(XP, YP, WP, HP); fprintf(out_, "\n", last_rgb_name_, XP-cx, YP-cy); if (need_clip) pop_clip(); } #endif // HAVE_LIBPNG } void Fl_SVG_Graphics_Driver::draw_image(const uchar* buf, int x, int y, int w, int h, int d, int l) { if (d < 0) { fprintf(out_, "\n", x, y); x = -w; y = 0; buf -= (w-1)*abs(d); } if (l < 0) { fprintf(out_, "\n", x, y); x = 0; y = -h; buf -= (h-1)*abs(l); } Fl_RGB_Image *rgb = new Fl_RGB_Image(buf, w, h, abs(d), abs(l)); rgb->draw(x, y); delete rgb; if (d < 0) fprintf(out_, "\n"); if (l < 0) fprintf(out_, "\n"); } void Fl_SVG_Graphics_Driver::draw_image(Fl_Draw_Image_Cb cb, void* data, int x, int y, int w, int h, int d) { uchar *buf = new uchar[w*h*d]; for (int j = 0; j < h; j++) { cb(data, 0, j, w, buf + j*w*d); } draw_image(buf, x, y, w, h, d, 0); delete [] buf; } struct mono_image_data { const uchar *buf; int d; int l; }; static void mono_image_cb(mono_image_data* data, int x, int y, int w, uchar* buf) { for (int i = 0; i < w; i++) *buf++ = *(data->buf + y*data->l + (x++)*data->d); } void Fl_SVG_Graphics_Driver::draw_image_mono(const uchar* buf, int x, int y, int w, int h, int d, int l) { mono_image_data data; data.buf = buf; data.d = d; data.l = (l?l:w*d); draw_image((Fl_Draw_Image_Cb)mono_image_cb, (void*)&data, x, y, w, h, 1); } void Fl_SVG_Graphics_Driver::draw_image_mono(Fl_Draw_Image_Cb cb, void* data, int x, int y, int w, int h, int d) { uchar *buf = new uchar[w*h*d]; for (int j = 0; j < h; j++) { cb(data, 0, j, w, buf + j*w*d); } draw_image_mono(buf, x, y, w, h, d, 0); delete[] buf; } void Fl_SVG_Graphics_Driver::push_clip(int x, int y, int w, int h) { Clip * c=new Clip(); clip_box(x,y,w,h,c->x,c->y,c->w,c->h); c->prev=clip_; snprintf(c->Id, sizeof(c->Id), "FLclip%d", clip_count_++); clip_=c; fprintf(out_, "\n", c->Id, clip_->x , clip_->y , clip_->w, clip_->h, c->Id); } void Fl_SVG_Graphics_Driver::push_no_clip() { Clip * c=clip_; while (c) { fprintf(out_, ""); c = c->prev; } c=new Clip(); c->prev=clip_; strcpy(c->Id, "none"); // mark of no_clip clip_=c; fprintf(out_, "\n"); } void Fl_SVG_Graphics_Driver::pop_clip() { Clip *c; bool was_no_clip = clip_ && (strcmp(clip_->Id, "none") == 0); fprintf(out_, ""); if (clip_) { c = clip_; clip_ = clip_->prev; delete c; } if (was_no_clip) { Clip *next = NULL; c=clip_; while (c) { Clip *c2 = new Clip(*c); c2->prev = next; next = c2; c = c->prev; } while (next) { fprintf(out_, "", next->Id); c = next->prev; delete next; next = c; } } fprintf(out_, "\n"); } int Fl_SVG_Graphics_Driver::clip_box(int x, int y, int w, int h, int& X, int& Y, int& W, int& H) { if (!clip_) { X = x; Y = y; W = w; H = h; return 0; } if (clip_->w < 0) { X = x; Y = y; W = w; H = h; return 1; } int ret = 0; if (x > (X=clip_->x)) {X=x; ret=1;} if (y > (Y=clip_->y)) {Y=y; ret=1;} if ((x+w) < (clip_->x+clip_->w)) { W=x+w-X; ret=1; }else W = clip_->x + clip_->w - X; if(W<0){ W=0; return 1; } if ((y+h) < (clip_->y+clip_->h)) { H=y+h-Y; ret=1; }else H = clip_->y + clip_->h - Y; if(H<0){ W=0; H=0; return 1; } return ret; } int Fl_SVG_Graphics_Driver::not_clipped(int x, int y, int w, int h) { if (!clip_) return 1; if (clip_->w < 0) return 1; int X = 0, Y = 0, W = 0, H = 0; clip_box(x, y, w, h, X, Y, W, H); if (W) return 1; return 0; } void Fl_SVG_Graphics_Driver::polygon(int x0, int y0, int x1, int y1, int x2, int y2) { fprintf(out_, "\n", x0, y0, x1, y1, x2, y2, red_, green_, blue_); } void Fl_SVG_Graphics_Driver::polygon(int x0, int y0, int x1, int y1, int x2, int y2, int x3, int y3) { fprintf(out_, "\n", x0, y0, x1, y1, x2, y2, x3, y3, red_, green_, blue_); } void Fl_SVG_Graphics_Driver::loop(int x0, int y0, int x1, int y1, int x2, int y2, int x3, int y3) { fprintf(out_, "\n", x0, y0, x1, y1, x2, y2, x3, y3, red_, green_, blue_, width_, linejoin_, linecap_, dasharray_); } void Fl_SVG_Graphics_Driver::loop(int x0, int y0, int x1, int y1, int x2, int y2) { fprintf(out_, "\n", x0, y0, x1, y1, x2, y2, red_, green_, blue_, width_, linejoin_, linecap_, dasharray_); } void Fl_SVG_Graphics_Driver::end_points() { for (int i=0; i\n", xpoint[i].x, xpoint[i].y, xpoint[i].x, xpoint[i].y, red_, green_, blue_, width_); } } void Fl_SVG_Graphics_Driver::end_line() { if (n < 2) { end_points(); return; } if (n<=1) return; clocale_printf("\n", red_, green_, blue_, width_, dasharray_, linecap_, linejoin_); } void Fl_SVG_Graphics_Driver::end_polygon() { fixloop(); if (n < 3) { end_line(); return; } if (n<=1) return; clocale_printf("\n", red_, green_, blue_); } void Fl_SVG_Graphics_Driver::circle(double x, double y, double r) { double xt = transform_x(x,y); double yt = transform_y(x,y); double rx = r * (m.c ? sqrt(m.a*m.a+m.c*m.c) : fabs(m.a)); double ry = r * (m.b ? sqrt(m.b*m.b+m.d*m.d) : fabs(m.d)); int llx = (int)rint(xt-rx); int w = (int)rint(xt+rx)-llx; int lly = (int)rint(yt-ry); int h = (int)rint(yt+ry)-lly; clocale_printf("\n", red_, green_, blue_); } void Fl_SVG_Graphics_Driver::end_complex_polygon() { gap(); if (n < 3) { end_line(); return; } if (n<=1) return; clocale_printf("\n", red_, green_, blue_); } void Fl_SVG_Graphics_Driver::arc(double x, double y, double r, double start, double end) { Fl_Graphics_Driver::arc(x, y, r, start, end); } void Fl_SVG_Graphics_Driver::arc(int x, int y, int w, int h, double a1, double a2) { arc_pie('A', x, y, w, h, a1, a2); } void Fl_SVG_Graphics_Driver::pie(int x, int y, int w, int h, double a1, double a2) { arc_pie('P', x, y, w, h, a1, a2); } void Fl_SVG_Graphics_Driver::arc_pie(char AorP, int x, int y, int w, int h, double a1, double a2) { // This implementation was constructed as follows: // - follow Fl_Quartz_Graphics_Driver::arc(int x,...). // which applies a translation, a scaling, and then calls // CGContextAddArc(gc_, 0, 0, 0.5, a1, a2, 1); // to draw an arc of a circle given its center, its radius, and starting and ending angles // - consider https://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes // which gives the equations that transform the center parameterization used by // CGContextAddArc() to the endpoint parameterization used by the "elliptical arc curve" command of SVG (A). if (w <= 0 || h <= 0) return; bool full = fabs(a1-a2) == 360; // case of full circle/disk a1 = (-a1)/180.0f*M_PI; a2 = (-a2)/180.0f*M_PI; float cx = x + 0.5f*w /*- 0.5f*/, cy = y + 0.5f*h - 0.5f; double r = (w!=h ? 0.5 : (w+h)*0.25f-0.5f); float stroke_width = float(width_); float sx, sy; if (w != h) { sx = float(w-1); sy = float(h-1); stroke_width /= ((sx+sy)/2); } else { sx = sy = float(2*r); stroke_width /= sx; } clocale_printf("\n", cx, cy, sx, sy); if (AorP == 'A') compute_dasharray((sx+sy)/2, user_dash_array_); if (full) { fprintf(out_, " M_PI ? 1 : 0; if (AorP == 'A') clocale_printf("\n\n", red_, green_, blue_); if (AorP == 'A') compute_dasharray(1., user_dash_array_); } #else Fl_SVG_File_Surface::Fl_SVG_File_Surface(int w, int h, FILE *f, int (*closef)(FILE*)) : Fl_Widget_Surface(NULL) { closef_ = NULL; width_ = height_ = 0; } Fl_SVG_File_Surface::~Fl_SVG_File_Surface() {} int Fl_SVG_File_Surface::close() {return 0;} FILE *Fl_SVG_File_Surface::file() {return NULL;} void Fl_SVG_File_Surface::origin(int x, int y) {} void Fl_SVG_File_Surface::translate(int x, int y) {} void Fl_SVG_File_Surface::untranslate() {} int Fl_SVG_File_Surface::printable_rect(int *w, int *h) {return 0;} #endif // FLTK_USE_SVG void Fl_SVG_File_Surface::origin(int *x, int *y) { Fl_Widget_Surface::origin(x, y);}