summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorManoloFLTK <41016272+ManoloFLTK@users.noreply.github.com>2020-06-27 09:56:00 +0200
committerManoloFLTK <41016272+ManoloFLTK@users.noreply.github.com>2020-06-27 09:56:00 +0200
commit26e6c3f930a052a1e729d160b0e8e5b6042b5018 (patch)
treea8d9ced3f2c8eac6414394a9b2930a1757a1ee50 /src
parent93f19c3a2457f1c87f3c0781481f0bcc1602a514 (diff)
Add classes Fl_SVG_File_Surface and Fl_EPS_File_Surface to draw to SVG and EPS.
Test programs device and pixmap_browser use these new classes. Class Fl_SVG_File_Surface can be optionally made non functional using the --disable-svg configure option or turning off OPTION_USE_SVG in CMake. Class Fl_EPS_File_Surface can be optionally made non functional using the --disable-print configure option or turning off OPTION_PRINT_SUPPORT in CMake.
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt1
-rw-r--r--src/Fl_SVG_Image.cxx4
-rw-r--r--src/Makefile4
-rw-r--r--src/drivers/PostScript/Fl_PostScript.cxx145
-rw-r--r--src/drivers/SVG/Fl_SVG_File_Surface.cxx1023
-rw-r--r--src/fl_images_core.cxx4
6 files changed, 1165 insertions, 16 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 0205cb996..3c1f0f0d6 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -161,6 +161,7 @@ set (CPPFILES
fl_utf8.cxx
fl_encoding_latin1.cxx
fl_encoding_mac_roman.cxx
+ drivers/SVG/Fl_SVG_File_Surface.cxx
)
# find all header files in source directory <FL/...>
diff --git a/src/Fl_SVG_Image.cxx b/src/Fl_SVG_Image.cxx
index b0fb9dd9d..c010aa2cf 100644
--- a/src/Fl_SVG_Image.cxx
+++ b/src/Fl_SVG_Image.cxx
@@ -18,7 +18,7 @@
#include <config.h>
-#if defined(FLTK_USE_NANOSVG) || defined(FL_DOXYGEN)
+#if defined(FLTK_USE_SVG) || defined(FL_DOXYGEN)
#include <FL/Fl_SVG_Image.H>
#include <FL/fl_utf8.h>
@@ -272,7 +272,7 @@ void Fl_SVG_Image::normalize() {
if (!array) resize(w(), h());
}
-#endif // FLTK_USE_NANOSVG
+#endif // FLTK_USE_SVG
//
// End of "$Id$".
diff --git a/src/Makefile b/src/Makefile
index 5274a6a6c..7fde7e8c9 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -214,7 +214,6 @@ IMGCPPFILES = \
Fl_Image_Reader.cxx \
Fl_SVG_Image.cxx
-
CFILES = fl_call_main.c flstring.c numericsort.c vsnprintf.c
UTF8CFILES = \
@@ -306,7 +305,8 @@ GDICFILES = \
PSCPPFILES = \
drivers/PostScript/Fl_PostScript.cxx \
- drivers/PostScript/Fl_PostScript_image.cxx
+ drivers/PostScript/Fl_PostScript_image.cxx \
+ drivers/SVG/Fl_SVG_File_Surface.cxx
################################################################
FLTKFLAGS = -DFL_LIBRARY
diff --git a/src/drivers/PostScript/Fl_PostScript.cxx b/src/drivers/PostScript/Fl_PostScript.cxx
index f182720b8..d04b27af3 100644
--- a/src/drivers/PostScript/Fl_PostScript.cxx
+++ b/src/drivers/PostScript/Fl_PostScript.cxx
@@ -26,6 +26,7 @@
#include <FL/Fl_Native_File_Chooser.H>
#include "../../Fl_System_Driver.H"
#include <stdarg.h>
+#include <time.h>
const char *Fl_PostScript_File_Device::file_chooser_title = "Select a .ps file";
@@ -137,7 +138,6 @@ int Fl_PostScript_Graphics_Driver::clocale_printf(const char *format, ...)
// Prolog string
static const char * prolog =
-"%%BeginProlog\n"
"/L { /y2 exch def\n"
"/x2 exch def\n"
"/y1 exch def\n"
@@ -554,7 +554,7 @@ int Fl_PostScript_Graphics_Driver::start_postscript (int pagecount,
if (lang_level_ == 3 && (layout & Fl_Paged_Device::LANDSCAPE) ) { x = w; w = h; h = x; }
fprintf(output, "<</PageSize[%d %d]>>setpagedevice\n", w, h );
fputs("%%EndFeature\n", output);
- fputs("%%EndComments\n", output);
+ fputs("%%EndComments\n%%BeginProlog\n", output);
fputs(prolog, output);
if (lang_level_ > 1) {
fputs(prolog_2, output);
@@ -583,6 +583,41 @@ int Fl_PostScript_Graphics_Driver::start_postscript (int pagecount,
return 0;
}
+int Fl_PostScript_Graphics_Driver::start_eps (int width, int height) {
+ width_ = width;
+ height_ = height;
+ fputs("%!PS-Adobe-3.0 EPSF-3.0\n", output);
+ fputs("%%Creator: (FLTK)\n", output);
+ fprintf(output,"%%%%BoundingBox: 1 1 %d %d\n", width, height);
+ if (ps_filename_) fprintf(output,"%%%%Title: (%s)\n", fl_filename_name(ps_filename_));
+ time_t lt = time(NULL);
+ fprintf(output,"%%%%CreationDate: %s", ctime(&lt)+4);
+ lang_level_= 2;
+ fprintf(output, "%%%%LanguageLevel: 2\n");
+ fputs("%%Pages: 1\n%%EndComments\n", output);
+ fputs("%%BeginProlog\n", output);
+ fputs("%%EndProlog\n",output);
+ fprintf(output, "save\n");
+ fputs("/FLTK 20 dict def FLTK begin\n"
+ "/x1 0 def /x2 0 def /y1 0 def /y2 0 def /x 0 def /y 0 def /dx 0 def /dy 0 def\n"
+ "/px 0 def /py 0 def /sx 0 def /sy 0 def /inter 0 def\n"
+ "/pixmap_sx 0 def /pixmap_sy 0 def /pixmap_w 0 def /pixmap_h 0 def\n", output);
+ fputs(prolog, output);
+ fputs(prolog_2, output);
+ fputs(prolog_2_pixmap, output);
+ fputs("/CS { GS } bind def\n", output);
+ fputs("/CR { GR } bind def\n", output);
+ page_policy_ = 1;
+ reset();
+ nPages=0;
+ fprintf(output, "GS\n");
+ clocale_printf( "%g %g TR\n", (double)0, height_);
+ fprintf(output, "1 -1 SC\n");
+ line_style(0);
+ fprintf(output, "GS GS\n");
+ return 0;
+}
+
void Fl_PostScript_Graphics_Driver::recover(){
color(cr_,cg_,cb_);
line_style(linestyle_,linewidth_,linedash_);
@@ -1386,6 +1421,22 @@ int Fl_PostScript_Graphics_Driver::not_clipped(int x, int y, int w, int h) {
return 0;
}
+void Fl_PostScript_Graphics_Driver::ps_origin(int x, int y)
+{
+ clocale_printf("GR GR GS %d %d TR %f %f SC %d %d TR %f rotate GS\n",
+ left_margin, top_margin, scale_x, scale_y, x, y, angle);
+}
+
+void Fl_PostScript_Graphics_Driver::ps_translate(int x, int y)
+{
+ fprintf(output, "GS %d %d translate GS\n", x, y);
+}
+
+void Fl_PostScript_Graphics_Driver::ps_untranslate(void)
+{
+ fprintf(output, "GR GR\n");
+}
+
void Fl_PostScript_File_Device::margins(int *left, int *top, int *right, int *bottom) // to implement
{
Fl_PostScript_Graphics_Driver *ps = driver();
@@ -1413,9 +1464,7 @@ void Fl_PostScript_File_Device::origin(int x, int y)
{
x_offset = x;
y_offset = y;
- Fl_PostScript_Graphics_Driver *ps = driver();
- ps->clocale_printf("GR GR GS %d %d TR %f %f SC %d %d TR %f rotate GS\n",
- ps->left_margin, ps->top_margin, ps->scale_x, ps->scale_y, x, y, ps->angle);
+ driver()->ps_origin(x, y);
}
void Fl_PostScript_File_Device::scale (float s_x, float s_y)
@@ -1438,12 +1487,12 @@ void Fl_PostScript_File_Device::rotate (float rot_angle)
void Fl_PostScript_File_Device::translate(int x, int y)
{
- fprintf(driver()->output, "GS %d %d translate GS\n", x, y);
+ driver()->ps_translate(x, y);
}
void Fl_PostScript_File_Device::untranslate(void)
{
- fprintf(driver()->output, "GR GR\n");
+ driver()->ps_untranslate();
}
int Fl_PostScript_File_Device::begin_page (void)
@@ -1495,9 +1544,85 @@ void Fl_PostScript_File_Device::end_job (void)
}
/**
- \}
- \endcond
- */
+\}
+\endcond
+*/
+
+Fl_EPS_File_Surface::Fl_EPS_File_Surface(int width, int height, FILE *eps, Fl_Color background) :
+ Fl_Widget_Surface(new Fl_PostScript_Graphics_Driver()) {
+ Fl_PostScript_Graphics_Driver *ps = driver();
+ ps->output = eps;
+ if (ps->output) {
+ float s = Fl::screen_scale(0);
+ ps->start_eps(width*s, height*s);
+ if (s != 1) {
+ ps->clocale_printf("GR GR GS %f %f SC GS\n", s, s);
+ ps->scale_x = ps->scale_y = s;
+ }
+ Fl::get_color(background, ps->bg_r, ps->bg_g, ps->bg_b);
+ }
+}
+
+void Fl_EPS_File_Surface::complete_() {
+ Fl_PostScript_Graphics_Driver *ps = driver();
+ if(ps->output) {
+ fputs("GR\nend %matches begin of FLTK dict\n", ps->output);
+ fputs("restore\n", ps->output);
+ fputs("%%EOF\n", ps->output);
+ ps->reset();
+ fflush(ps->output);
+ if(ferror(ps->output)) {
+ fl_alert ("Error during PostScript data output.");
+ }
+ }
+ while (ps->clip_){
+ Fl_PostScript_Graphics_Driver::Clip * c= ps->clip_;
+ ps->clip_= ps->clip_->prev;
+ delete c;
+ }
+}
+
+Fl_EPS_File_Surface::~Fl_EPS_File_Surface() {
+ Fl_PostScript_Graphics_Driver *ps = driver();
+ if(ps->output) complete_();
+ delete ps;
+}
+
+int Fl_EPS_File_Surface::close() {
+ complete_();
+ Fl_PostScript_Graphics_Driver *ps = driver();
+ int retval = fclose(ps->output);
+ ps->output = NULL;
+ return retval;
+}
+
+int Fl_EPS_File_Surface::printable_rect(int *w, int *h) {
+ Fl_PostScript_Graphics_Driver *ps = driver();
+ *w = ps->width_;
+ *h = ps->height_;
+ return 0;
+}
+
+void Fl_EPS_File_Surface::origin(int x, int y)
+{
+ x_offset = x;
+ y_offset = y;
+ driver()->ps_origin(x, y);
+}
+
+void Fl_EPS_File_Surface::origin(int *px, int *py) {
+ Fl_Widget_Surface::origin(px, py);
+}
+
+void Fl_EPS_File_Surface::translate(int x, int y)
+{
+ driver()->ps_translate(x, y);
+}
+
+void Fl_EPS_File_Surface::untranslate()
+{
+ driver()->ps_untranslate();
+}
#endif // !defined(FL_NO_PRINT_SUPPORT)
diff --git a/src/drivers/SVG/Fl_SVG_File_Surface.cxx b/src/drivers/SVG/Fl_SVG_File_Surface.cxx
new file mode 100644
index 000000000..d6cf7f4b7
--- /dev/null
+++ b/src/drivers/SVG/Fl_SVG_File_Surface.cxx
@@ -0,0 +1,1023 @@
+//
+// Implementation of classes Fl_SVG_Graphics_Driver and Fl_SVG_File_Surface in the Fast Light Tool Kit (FLTK).
+//
+// Copyright 2020 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 report all bugs and problems on the following page:
+//
+// https://www.fltk.org/str.php
+//
+
+// Complete implementation to draw into an SVG file using the standard FLTK drawing API.
+
+#include <config.h>
+#include <FL/Fl_SVG_File_Surface.H>
+#if FLTK_USE_SVG
+#include <FL/fl_draw.H>
+#include <stdio.h>
+#include <FL/math.h>
+#include <FL/Fl_Widget_Surface.H>
+#include <FL/Fl_Graphics_Driver.H>
+#include <FL/Fl.H>
+#include <FL/Fl_RGB_Image.H>
+#include <FL/Fl_Pixmap.H>
+#include <FL/Fl_Bitmap.H>
+extern "C" {
+#if defined(HAVE_LIBPNG)
+# ifdef HAVE_PNG_H
+# include <png.h>
+# else
+# include <libpng/png.h>
+# endif // HAVE_PNG_H
+#endif // HAVE_LIBPNG
+
+#ifdef HAVE_LIBJPEG
+# include <jpeglib.h>
+#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
+ int p_size;
+ typedef struct { float x; float y; } XPOINT;
+ XPOINT *p;
+ 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:
+ void rect(int x, int y, int w, int h);
+ void rectf(int x, int y, int w, int h);
+ void compute_dasharray(float s, char *dashes=0);
+ void line_style(int style, int width, char *dashes=0);
+ void line(int x1, int y1, int x2, int y2);
+ void font_(int f, int s);
+ void font(int f, int s);
+ void draw(const char *str, int n, int x, int y);
+ void draw(const char*, int, float, float) ;
+ void draw(int, const char*, int, int, int) ;
+ void rtl_draw(const char *str, int n, int x, int y);
+ void color(uchar r, uchar g, uchar b);
+ void color(Fl_Color c);
+ double width(const char*, int) ;
+ void text_extents(const char*, int n, int& dx, int& dy, int& w, int& h);
+ int height() ;
+ int descent() ;
+ void draw_rgb(Fl_RGB_Image *rgb, int XP, int YP, int WP, int HP, int cx, int cy);
+ 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);
+ void draw_bitmap(Fl_Bitmap *bm,int XP, int YP, int WP, int HP, int cx, int cy);
+ void draw_image(const uchar* buf, int x, int y, int w, int h, int d, int l);
+ void draw_image(Fl_Draw_Image_Cb cb, void* data, int x, int y, int w, int h, int d);
+ void draw_image_mono(const uchar* buf, int x, int y, int w, int h, int d, int l);
+ void draw_image_mono(Fl_Draw_Image_Cb cb, void* data, int x, int y, int w, int h, int d);
+ void push_clip(int x, int y, int w, int h);
+ void push_no_clip();
+ void pop_clip();
+ int clip_box(int x, int y, int w, int h, int& X, int& Y, int& W, int& H);
+ int not_clipped(int x, int y, int w, int h);
+ void polygon(int x0, int y0, int x1, int y1, int x2, int y2);
+ void polygon(int x0, int y0, int x1, int y1, int x2, int y2, int x3, int y3);
+ void loop(int x0, int y0, int x1, int y1, int x2, int y2, int x3, int y3);
+ void loop(int x0, int y0, int x1, int y1, int x2, int y2);
+ void point(int x, int y);
+ void transformed_vertex0(float x, float y);
+ void transformed_vertex(double xf, double yf);
+ void vertex(double x,double y);
+ void end_points();
+ void end_line();
+ void fixloop();
+ void end_loop();
+ void end_polygon();
+ void begin_complex_polygon();
+ void gap();
+ void end_complex_polygon();
+ void circle(double x, double y,double r);
+ void arc(int x,int y,int w,int h,double a1,double a2);
+ void pie(int x,int y,int w,int h,double a1,double a2);
+ 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_ = strdup("none");
+ p_size = 0;
+ p = NULL;
+ 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_);
+}
+
+void Fl_SVG_Graphics_Driver::rect(int x, int y, int w, int h) {
+ fprintf(out_, "<rect x=\"%d\" y=\"%d\" width=\"%d\" height=\"%d\" "
+ "fill=\"none\" stroke=\"rgb(%u,%u,%u)\" stroke-width=\"%d\" stroke-dasharray=\"%s\""
+ " stroke-linecap=\"%s\" stroke-linejoin=\"%s\"/>\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) {
+ fprintf(out_, "<rect x=\"%.3f\" y=\"%.3f\" width=\"%d\" height=\"%d\" "
+ "fill=\"rgb(%u,%u,%u)\" />\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_,
+ "<line x1=\"%d\" y1=\"%d\" x2=\"%d\" y2=\"%d\" "
+ "style=\"stroke:rgb(%u,%u,%u);stroke-width:%d;stroke-linecap:%s;stroke-linejoin:%s;stroke-dasharray:%s\" />\n",
+ x1,y1,x2,y2, 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);
+}
+
+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_);
+ dasharray_ = (char*)calloc(10*strlen(dashes) + 1, 1);
+ for (char *p = dashes; *p; p++) {
+ sprintf(dasharray_+strlen(dasharray_), "%.3f,", (*p)/s);
+ }
+ dasharray_[strlen(dasharray_) - 1] = 0;
+ if (user_dash_array_ != dashes) user_dash_array_ = strdup(dashes);
+ return;
+ }
+ int dash_part = line_style_ & 0xFF;
+ if (dash_part == FL_SOLID) {
+ if (dasharray_ && strcmp(dasharray_, "none")) free(dasharray_);
+ dasharray_ = 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.6/s);
+ float gap = (is_flat ? width_/s : width_*1.5/s);
+ float big = (is_flat ? 3*width_/s : width_*2.5/s);
+ if (dasharray_) free(dasharray_);
+ dasharray_ = (char*)malloc(61);
+ if (dash_part == FL_DOT) sprintf(dasharray_, "%.3f,%.3f", dot, gap);
+ else if (dash_part == FL_DASH) sprintf(dasharray_, "%.3f,%.3f", big, gap);
+ else if (dash_part == FL_DASHDOT) sprintf(dasharray_, "%.3f,%.3f,%.3f,%.3f", big, gap, dot, gap);
+ else sprintf(dasharray_, "%.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_, "<text x=\"%d\" y=\"%d\" font-family=\"%s\"%s%s font-size=\"%d\" "
+ "xml:space=\"preserve\" "
+ " fill=\"rgb(%u,%u,%u)\" textLength=\"%d\">", x, y, family_, bold_, style_, size(), red_, green_, blue_, (int)width(str, n));
+ for (int i = 0; i < n; i++) {
+ if (str[i] == '&') fputs("&amp;", out_);
+ else if (str[i] == '<') fputs("&lt;", out_);
+ else if (str[i] == '>') fputs("&gt;", out_);
+ else fputc(str[i], out_);
+ }
+ fputs("</text>\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_, "<g transform=\"translate(%d,%d) rotate(%d)\">", x, y, -angle);
+ draw(str, n, 0, 0);
+ fputs("</g>\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;
+}
+
+double Fl_SVG_Graphics_Driver::width(const char* str, int l) {
+ return Fl_Display_Device::display_device()->driver()->width(str, l);
+}
+
+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) : Fl_Widget_Surface(new Fl_SVG_Graphics_Driver(f)) {
+ Fl_Window *win = Fl::first_window();
+ float s = (win ? Fl::screen_scale(win->screen_num()) : 1);
+ int sw = w * s, sh = h * s;
+ fprintf(f,
+ "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"no\"?>\n"
+ "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \n"
+ "\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n"
+ "<svg width=\"%dpx\" height=\"%dpx\" viewBox=\"0 0 %d %d\"\n"
+ "xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">\n", sw, sh, sw, sh);
+ width_ = w; height_ = h;
+ fprintf(f, "<g transform=\"scale(%f)\">\n", s);
+ fputs("<g transform=\"translate(0,0)\">\n", f);
+}
+
+Fl_SVG_File_Surface::~Fl_SVG_File_Surface() {
+ Fl_SVG_Graphics_Driver *driver = (Fl_SVG_Graphics_Driver*)this->driver();
+ if (driver) {
+ fputs("</g></g></svg>\n", driver->file());
+ fflush(driver->file());
+ delete driver;
+ }
+}
+
+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("</g></g></svg>\n", driver->file());
+ int retval = 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(), "<g transform=\"translate(%d,%d) \">\n", x, y);
+}
+
+void Fl_SVG_File_Surface::untranslate() {
+ Fl_SVG_Graphics_Driver *driver = (Fl_SVG_Graphics_Driver*)this->driver();
+ fputs("</g>\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(), "</g><g transform=\"translate(%d,%d) \">\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 = 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:
+<defs><image id="myimage" width="64" height="64" href="data:image/png;base64,
+iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAnElEQVR4nO3UsQ3EIADAwEQgsf+S7ECB
+/sdwkfMEru7de/+eDzfvvfVD2hxj1A9pc61VP6TNc079kMYABjCAAfVDGgMYwAAG1A9pDGAAAxhQP6Qx
+gAEMYED9kMYABjCAAfVDGgMYwAAG1A9pDGAAAxhQP6QxgAEMYED9kMYABjCAAfVDGgMYwAAG1A9pDGAA
+AxhQP6QxgAEM+LYBf9sdYcTRmp6pAAAAAElFTkSuQmCCAAAAAElFTkSuQmCC"/>
+</defs>
+<use href="#myimage" x="xxx" y="yyy"/>
+<use href="#myimage" x="xxx2" y="yyy2"/>
+*/
+/* Specify image data and draw it in one go:
+ <image x="xxx" y="yyy" width="64" height="64" href="data:image/png;base64,
+ iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAnElEQVR4nO3UsQ3EIADAwEQgsf+S7ECB
+ /sdwkfMEru7de/+eDzfvvfVD2hxj1A9pc61VP6TNc079kMYABjCAAfVDGgMYwAAG1A9pDGAAAxhQP6Qx
+ gAEMYED9kMYABjCAAfVDGgMYwAAG1A9pDGAAAxhQP6QxgAEMYED9kMYABjCAAfVDGgMYwAAG1A9pDGAA
+ AxhQP6QxgAEM+LYBf9sdYcTRmp6pAAAAAElFTkSuQmCCAAAAAElFTkSuQmCC"/>
+ */
+
+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_ = 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_, "<defs><image id=\"%s\" ", name);
+ else fprintf(out_, "<image x=\"%d\" y=\"%d\" ", x, y);
+ fprintf(out_, "width=\"%f\" height=\"%f\" href=\"data:image/png;base64,\n", f*rgb->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("\"/></defs>\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, 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_ = 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_, "<defs><image id=\"%s\" ", name);
+ else fprintf(out_, "<image x=\"%d\" y=\"%d\" ", x, y);
+ fprintf(out_, "width=\"%f\" height=\"%f\" href=\"data:image/jpeg;base64,\n", f*rgb->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("\"/></defs>\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) sprintf(name, "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_, "<use href=\"#%s\" x=\"%d\" y=\"%d\"/>\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) sprintf(name, "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_, "<use href=\"#%s\" x=\"%d\" y=\"%d\"/>\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) sprintf(name, "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_, "<use href=\"#%s\" x=\"%d\" y=\"%d\"/>\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_, "<g transform=\"translate(%d,%d) scale(-1,1)\">\n", x, y);
+ x = -w; y = 0; buf -= (w-1)*abs(d);
+ }
+ if (l < 0) {
+ fprintf(out_, "<g transform=\"translate(%d,%d) scale(1,-1)\">\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_, "</g>\n");
+ if (l < 0) fprintf(out_, "</g>\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_;
+ sprintf(c->Id, "FLclip%d", clip_count_++);
+ clip_=c;
+ fprintf(out_, "<clipPath id=\"%s\"><rect x=\"%d\" y=\"%d\" width=\"%d\" height=\"%d\"/></clipPath><g clip-path=\"url(#%s)\">\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_, "</g>");
+ c = c->prev;
+ }
+ c=new Clip();
+ c->prev=clip_;
+ strcpy(c->Id, "none"); // mark of no_clip
+ clip_=c;
+ fprintf(out_, "<g clip-path=\"none\">\n");
+}
+
+void Fl_SVG_Graphics_Driver::pop_clip() {
+ Clip *c;
+ bool was_no_clip = clip_ && (strcmp(clip_->Id, "none") == 0);
+ fprintf(out_, "</g>");
+ 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_, "<g clip-path=\"url(#%s)\">", 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_, "<path d=\"M %d %d L %d %d L %d %d z\" fill=\"rgb(%u,%u,%u)\" />\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_, "<path d=\"M %d %d L %d %d L %d %d L %d %d z\" fill=\"rgb(%u,%u,%u)\" />\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_, "<path d=\"M %d %d L %d %d L %d %d L %d %d z\" fill=\"none\" stroke=\"rgb(%u,%u,%u)\" "
+ "stroke-width=\"%d\" stroke-linejoin=\"%s\" stroke-linecap=\"%s\" stroke-dasharray=\"%s\"/>\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_, "<path d=\"M %d %d L %d %d L %d %d z\" fill=\"none\" stroke=\"rgb(%u,%u,%u)\" "
+ "stroke-width=\"%d\" stroke-linejoin=\"%s\" stroke-linecap=\"%s\" stroke-dasharray=\"%s\"/>\n",
+ x0, y0, x1, y1, x2, y2, red_, green_, blue_, width_, linejoin_, linecap_, dasharray_);
+}
+
+void Fl_SVG_Graphics_Driver::transformed_vertex0(float x, float y) {
+ if (!n || x != p[n-1].x || y != p[n-1].y) {
+ if (n >= p_size) {
+ p_size = p ? 2*p_size : 16;
+ p = (XPOINT*)realloc((void*)p, p_size*sizeof(*p));
+ }
+ p[n].x = x;
+ p[n].y = y;
+ n++;
+ }
+}
+
+void Fl_SVG_Graphics_Driver::transformed_vertex(double xf, double yf) {
+ transformed_vertex0(float(xf), float(yf));
+}
+
+void Fl_SVG_Graphics_Driver::vertex(double x,double y) {
+ transformed_vertex0(float(x*m.a + y*m.c + m.x), float(x*m.b + y*m.d + m.y));
+}
+
+void Fl_SVG_Graphics_Driver::end_points() {
+ for (int i=0; i<n; i++) {
+ fprintf(out_, "<path d=\"M %f %f L %f %f\" fill=\"none\" stroke=\"rgb(%u,%u,%u)\" stroke-width=\"%d\" />\n",
+ p[i].x, p[i].y, p[i].x, p[i].y, red_, green_, blue_, width_);
+ }
+}
+
+void Fl_SVG_Graphics_Driver::end_line() {
+ if (n < 2) {
+ end_points();
+ return;
+ }
+ if (n<=1) return;
+ fprintf(out_, "<path d=\"M %f %f", p[0].x, p[0].y);
+ for (int i=1; i<n; i++)
+ fprintf(out_, " L %f %f", p[i].x, p[i].y);
+ fprintf(out_, "\" fill=\"none\" stroke=\"rgb(%u,%u,%u)\" stroke-width=\"%d\" stroke-dasharray=\"%s\" stroke-linecap=\"%s\" stroke-linejoin=\"%s\" />\n",
+ red_, green_, blue_, width_, dasharray_, linecap_, linejoin_);
+}
+
+void Fl_SVG_Graphics_Driver::fixloop() { // remove equal points from closed path
+ while (n>2 && p[n-1].x == p[0].x && p[n-1].y == p[0].y) n--;
+}
+
+void Fl_SVG_Graphics_Driver::end_loop() {
+ fixloop();
+ if (n>2) transformed_vertex((float)p[0].x, (float)p[0].y);
+ end_line();
+}
+
+void Fl_SVG_Graphics_Driver::end_polygon() {
+ fixloop();
+ if (n < 3) {
+ end_line();
+ return;
+ }
+ if (n<=1) return;
+ fprintf(out_, "<path d=\"M %f %f", p[0].x, p[0].y);
+ for (int i=1; i<n; i++)
+ fprintf(out_, " L %f %f", p[i].x, p[i].y);
+ fprintf(out_, " z\" fill=\"rgb(%u,%u,%u)\" />\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;
+ fprintf(out_, "<circle cx=\"%g\" cy=\"%g\" r=\"%g\"", xt, yt, (w+h)*0.25f);
+ if (what == POLYGON)
+ fprintf(out_, " fill");
+ else
+ fprintf(out_, " fill=\"none\" stroke-width=\"%d\" stroke-dasharray=\"%s\" stroke-linecap=\"%s\" stroke", width_, dasharray_,linecap_);
+ fprintf(out_, "=\"rgb(%u,%u,%u)\" />\n", red_, green_, blue_);
+}
+
+void Fl_SVG_Graphics_Driver::begin_complex_polygon() {
+ begin_polygon();
+ gap_ = 0;
+}
+
+void Fl_SVG_Graphics_Driver::gap() {
+ while (n>gap_+2 && p[n-1].x == p[gap_].x && p[n-1].y == p[gap_].y) n--;
+ if (n > gap_+2) {
+ transformed_vertex((float)p[gap_].x, (float)p[gap_].y);
+ gap_ = n;
+ } else {
+ n = gap_;
+ }
+}
+
+void Fl_SVG_Graphics_Driver::end_complex_polygon() {
+ gap();
+ if (n < 3) {
+ end_line();
+ return;
+ }
+ if (n<=1) return;
+ fprintf(out_, "<path d=\"M %f %f", p[0].x, p[0].y);
+ for (int i=1; i<n; i++)
+ fprintf(out_, " L %f %f", p[i].x, p[i].y);
+ fprintf(out_, " z\" fill=\"rgb(%u,%u,%u)\" />\n", red_, green_, blue_);
+}
+
+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 = width_;
+ float sx, sy;
+ if (w != h) {
+ sx = w-1; sy = h-1;
+ stroke_width /= ((sx+sy)/2);
+ } else {
+ sx = sy = 2*r;
+ stroke_width /= sx;
+ }
+ fprintf(out_, "<g transform=\"translate(%f,%f) scale(%f,%f)\">\n", cx, cy, sx, sy);
+ if (AorP == 'A') compute_dasharray((sx+sy)/2, user_dash_array_);
+ if (full) {
+ fprintf(out_, "<circle cx=\"0\" cy=\"0\" r=\"0.5\" style=\"fill");
+ if (AorP == 'A')
+ fprintf(out_, ":none;stroke-width:%f;stroke-linecap:%s;stroke-dasharray:%s;stroke", stroke_width, linecap_, dasharray_);
+ } else {
+ double x1 = 0.5*cos(a1), y1 = 0.5 * sin(a1);
+ double x2 = 0.5*cos(a2), y2 = 0.5 * sin(a2);
+ int fA = fabs(a2-a1) > M_PI ? 1 : 0;
+ if (AorP == 'A')
+ fprintf(out_, "<path d=\"M %f,%f A 0.5,0.5 0 %d,0 %f,%f\" "
+ "style=\"fill:none;stroke-width:%f;stroke-linecap:%s;stroke-dasharray:%s;stroke",
+ x1, y1, fA, x2, y2, stroke_width, linecap_, dasharray_);
+ else
+ fprintf(out_, "<path d=\"M 0,0 L %f,%f A 0.5,0.5 0 %d,0 %f,%f z\" style=\"fill",
+ x1, y1, fA, x2, y2);
+ }
+ fprintf(out_, ":rgb(%u,%u,%u)\"/>\n</g>\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) : Fl_Widget_Surface(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
+
+//
+// End of "$Id$".
+//
diff --git a/src/fl_images_core.cxx b/src/fl_images_core.cxx
index 624b6f4c3..3df7fcd27 100644
--- a/src/fl_images_core.cxx
+++ b/src/fl_images_core.cxx
@@ -91,7 +91,7 @@ fl_check_images(const char *name, // I - Filename
return new Fl_JPEG_Image(name);
#endif // HAVE_LIBJPEG
-#ifdef FLTK_USE_NANOSVG
+#ifdef FLTK_USE_SVG
# if defined(HAVE_LIBZ)
if (header[0] == 0x1f && header[1] == 0x8b) { // denotes gzip'ed data
int fd = fl_open_ext(name, 1, 0);
@@ -106,7 +106,7 @@ fl_check_images(const char *name, // I - Filename
if ( (headerlen > 5 && memcmp(header, "<?xml", 5) == 0) ||
memcmp(header, "<svg", 4) == 0)
return new Fl_SVG_Image(name);
-#endif // FLTK_USE_NANOSVG
+#endif // FLTK_USE_SVG
return 0;
}