diff --git a/CHANGES.txt b/CHANGES.txt index 29048f478e20004f62c5f6856d7000f40e778528..147aa7ffff30cbf579dabad8038cffa8a989f42d 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -18,6 +18,8 @@ Changes in FLTK 1.4.0 Released: ??? ?? 2020 New Features and Extensions - (add new items here) + - New classes Fl_SVG_File_Surface and Fl_EPS_File_Surface to save any FLTK + graphics to SVG or EPS files, respectively. - New fl_putenv() is a cross-platform putenv() wrapper (see docs). - New Fl::keyboard_screen_scaling(0) call stops recognition of ctrl/+/-/0/ keystrokes as scaling all windows of a screen. @@ -87,9 +89,6 @@ Changes in FLTK 1.4.0 Released: ??? ?? 2020 of Fl_Clock, Fl_Clock_Output, and derived widgets. - New method Fl_Tabs::tab_align() allows to set alignment of tab labels, particularly to support icons on tab labels (STR #3076). - - Added '--enable-print' option to configure effective under X11 platforms - and with 'yes' default value. Using '--enable-print=no' removes print - and PostScript support from the FLTK library, thus reducing its size. - Added Fl_Surface_Device::push_current(new_surface) and Fl_Surface_Device::pop_current() to set/unset the current surface receiving graphics commands. @@ -101,8 +100,13 @@ Changes in FLTK 1.4.0 Released: ??? ?? 2020 New Configuration Options (ABI Version) - - The library can be built without support for SVG images using the - --disable-nanosvg configure option or turning off OPTION_USE_NANOSVG in CMake. + - The library can be built without support for reading SVG images or writing + graphics in SVG format using the --disable-svg configure option + or turning off OPTION_USE_SVG in CMake. + - The library can be built without support for PostScript, thus reducing + its size, using the --disable-print configure option or turning off + OPTION_PRINT_SUPPORT in CMake. That makes classes Fl_PostScript_File_Device, + Fl_EPS_File_Surface and Fl_Printer (under X11 platform only) ineffective. - FLTK's ABI version can be configured with 'configure' and CMake. See documentation in README.abi-version.txt. @@ -119,6 +123,7 @@ Changes in FLTK 1.4.0 Released: ??? ?? 2020 Other Improvements - (add new items here) + - Support for building for the arm64 architecture used by macOS 11.0 "Big Sur". - Add optional argument to Fl_Printer::begin_job() to receive a string describing the error when an error occurs. - Fix Windows-specific bug when the program tries to enlarge a diff --git a/CMake/options.cmake b/CMake/options.cmake index 559b68683e34c1150a0ed911b0dd48bfc4dd7474..70128080684bb6534e9c6957d9d5c62fb7aef528 100644 --- a/CMake/options.cmake +++ b/CMake/options.cmake @@ -179,11 +179,11 @@ else () endif (PKG_CAIRO_FOUND) ####################################################################### -option(OPTION_USE_NANOSVG "support SVG images" ON) +option(OPTION_USE_SVG "read/write SVG files" ON) -if(OPTION_USE_NANOSVG) - set(FLTK_USE_NANOSVG 1) -endif(OPTION_USE_NANOSVG) +if(OPTION_USE_SVG) + set(FLTK_USE_SVG 1) +endif(OPTION_USE_SVG) ####################################################################### set(HAVE_GL LIB_GL OR LIB_MesaGL) diff --git a/FL/Fl_PostScript.H b/FL/Fl_PostScript.H index 6d26490dccf559bd0aa033df15eccd6787fa5547..ad6a6cd4af9fb462847228de3712fc12da87fa6e 100644 --- a/FL/Fl_PostScript.H +++ b/FL/Fl_PostScript.H @@ -3,7 +3,7 @@ // // Support for graphics output to PostScript file for the Fast Light Tool Kit (FLTK). // -// Copyright 2010-2019 by Bill Spitzak and others. +// Copyright 2010-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 @@ -206,6 +206,11 @@ public: // --- Fl_Bitmask create_bitmask(int w, int h, const uchar *array) { return 0L; } virtual int has_feature(driver_feature feature_mask) { return feature_mask & PRINTER; } + + int start_eps(int width, int height); + void ps_origin(int x, int y); + void ps_translate(int, int); + void ps_untranslate(); }; /** @@ -310,6 +315,58 @@ public: static const char *file_chooser_title; }; +/** Encapsulated PostScript drawing surface. + This drawing surface allows to store any FLTK graphics in vectorial form in an "Encapsulated PostScript" file. + \n Usage example: + \code + Fl_Window *win = ...// Window to draw to an .eps file + int ww = win->decorated_w(); + int wh = win->decorated_h(); + FILE *eps = fl_fopen("/path/to/mywindow.eps", "w"); + if (eps) { + Fl_EPS_File_Surface *surface = new Fl_EPS_File_Surface(ww, wh, eps, win->color()); + Fl_Surface_Device::push_current(surface); + surface->draw_decorated_window(win); + Fl_Surface_Device::pop_current(); + delete surface; // the .eps file is not complete until the destructor was run + fclose(eps); + } + \endcode + */ +class FL_EXPORT Fl_EPS_File_Surface : public Fl_Widget_Surface { +private: + void complete_(); +protected: + /** Returns the PostScript driver of this drawing surface. */ + inline Fl_PostScript_Graphics_Driver *driver() { return (Fl_PostScript_Graphics_Driver*)Fl_Surface_Device::driver(); } +public: + /** + Constructor. + \param width,height Width and height of the EPS drawing area + \param eps A writable FILE pointer where the Encapsulated PostScript data will be sent + \param background Color expected to cover the background of the EPS drawing area. + This parameter affects only the drawing of transparent Fl_RGB_Image objects: + transparent areas of RGB images are blended with the \p background color. + */ + Fl_EPS_File_Surface(int width, int height, FILE *eps, Fl_Color background = FL_WHITE); + /** + Destructor. + The underlying FILE pointer remains open after destruction of the Fl_EPS_File_Surface object + unless close() was called. + */ + ~Fl_EPS_File_Surface(); + virtual int printable_rect(int *w, int *h); + /** Returns the underlying FILE pointer */ + FILE *file() { return driver()->output; } + virtual void origin(int x, int y); + virtual void origin(int *px, int *py); + virtual void translate(int x, int y); + virtual void untranslate(); + /** Closes using fclose() the underlying FILE pointer. + The only operation possible with the Fl_EPS_File_Surface object after calling close() is its destruction. */ + int close(); +}; + #endif // Fl_PostScript_H // diff --git a/FL/Fl_SVG_File_Surface.H b/FL/Fl_SVG_File_Surface.H new file mode 100644 index 0000000000000000000000000000000000000000..272f7db1f23bd565f2794de689ff7f591d09f837 --- /dev/null +++ b/FL/Fl_SVG_File_Surface.H @@ -0,0 +1,73 @@ +// +// Declaration of 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 +// + +#ifndef Fl_SVG_File_Surface_H +#define Fl_SVG_File_Surface_H + +#include <FL/Fl_Widget_Surface.H> +#include <stdio.h> + +/** A drawing surface producing a Scalable Vector Graphics (SVG) file. + This drawing surface allows to store any FLTK graphics in vectorial form in a "Scalable Vector Graphics" file. + \n Usage example: + \code + Fl_Window *win = ...// Window to draw to a .svg file + int ww = win->decorated_w(); + int wh = win->decorated_h(); + FILE *svg = fl_fopen("/path/to/mywindow.svg", "w"); + if (svg) { + Fl_SVG_File_Surface *surface = new Fl_SVG_File_Surface(ww, wh, svg); + Fl_Surface_Device::push_current(surface); + fl_color(FL_WHITE); + fl_rectf(0, 0, ww, wh); + surface->draw_decorated_window(win); + Fl_Surface_Device::pop_current(); + delete surface; // the .svg file is not complete until the destructor was run + fclose(svg); + } + \endcode + \note FLTK uses the PNG and JPEG libraries to encode images to the SVG format. If JPEG is + not available at application build time, PNG is enough (but produces a quite larger output). + If PNG isn't available either, images don't appear in the SVG output. +*/ +class FL_EXPORT Fl_SVG_File_Surface : public Fl_Widget_Surface { + int width_, height_; +public: + /** + Constructor of the SVG drawing surface. + \param width,height Width and height of the graphics area in FLTK drawing units + \param svg A writable FILE pointer where the SVG data are to be sent. The resulting SVG data are not complete until after destruction of the Fl_SVG_File_Surface object or after calling close(). + */ + Fl_SVG_File_Surface(int width, int height, FILE *svg); + /** + Destructor. + The underlying FILE pointer remains open after destruction of the Fl_SVG_File_Surface object + unless close() was called. + */ + ~Fl_SVG_File_Surface(); + /** Returns the underlying FILE pointer */ + FILE *file(); + virtual void origin(int x, int y); + virtual void translate(int x, int y); + virtual void untranslate(); + virtual int printable_rect(int *w, int *h); + /** Closes with function fclose() the FILE pointer where SVG data is output. + The only operation possible after this on the Fl_SVG_File_Surface object is its destruction. + \return The value returned by fclose(). */ + int close(); +}; + +#endif /* Fl_SVG_File_Surface_H */ diff --git a/configh.cmake.in b/configh.cmake.in index f00bad328817783bbb2480b06417e83fb3758fff..8b6a86985171eb8452c1c254acb16a84db90afbc 100644 --- a/configh.cmake.in +++ b/configh.cmake.in @@ -320,12 +320,12 @@ #cmakedefine HAVE_PNG_SET_TRNS_TO_ALPHA 1 /* -* FLTK_USE_NANOSVG +* FLTK_USE_SVG * -* Do we want FLTK to support SVG images with nanosvg ? +* Do we want FLTK to read and write SVG-formatted files ? */ -#cmakedefine FLTK_USE_NANOSVG 1 +#cmakedefine FLTK_USE_SVG 1 /* * Do we have POSIX threading? diff --git a/configh.in b/configh.in index dcb8b5b8c3fe6579d0164bb4b0c356f0dcfdba51..65d90dc6d0b3a941174bdee08c603fe4801b32f2 100644 --- a/configh.in +++ b/configh.in @@ -287,7 +287,7 @@ #undef HAVE_LIBPNG #undef HAVE_LIBZ #undef HAVE_LIBJPEG -#undef FLTK_USE_NANOSVG +#undef FLTK_USE_SVG /* * FLTK_USE_CAIRO diff --git a/configure.ac b/configure.ac index fda55a21d73c84c4b7c0356b7b405a05dd3b5b9b..20fcbe05ad93268ab60d83d7ed36db2a62828587 100644 --- a/configure.ac +++ b/configure.ac @@ -789,10 +789,10 @@ AC_SUBST(PNGINC) AC_SUBST(ZLIB) AC_SUBST(ZLIBINC) -# Control the usage of the nanosvg lib -AC_ARG_ENABLE(nanosvg, [ --enable-nanosvg use nanosvg to support SVG images [[default=yes]]]) -if test x$enable_nanosvg != xno; then - AC_DEFINE(FLTK_USE_NANOSVG) +# Control the usage of the nanosvg lib and SVG output +AC_ARG_ENABLE(svg, [ --enable-svg read/write SVG files [[default=yes]]]) +if test x$enable_svg != xno; then + AC_DEFINE(FLTK_USE_SVG) fi dnl Restore original LIBS settings... diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0205cb9963b9dc5093cb35c6cf96534871a9b301..3c1f0f0d68fb12b2deeec89bef175222e1021268 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 b0fb9dd9d47ad4426f29126e8790abb563bede17..c010aa2cf20f10a82d8436449c69b4b45ff25644 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 5274a6a6cdf7786b51dbba3e751e0c24f7ee2942..7fde7e8c967498e54ea66d247de3c34905df6e68 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 f182720b811ef2bfceb6e80cd71620d36bd06bf2..d04b27af3f1215cb9a9cbc0022b7f194a5e0abd7 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(<)+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 0000000000000000000000000000000000000000..d6cf7f4b711a4bfa8c32df0e0d109b83e39cf652 --- /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("&", out_); + else if (str[i] == '<') fputs("<", out_); + else if (str[i] == '>') fputs(">", 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 624b6f4c37270ae2fabc1c41242f59d6c5bab36a..3df7fcd27fc17c77d5de4a759b2727c85e82dec5 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; } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 886d18813d93d853f33cbeae38732824375f1dad..6e996a5a3ecea51da357e1c5eb096e949c80476d 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -59,7 +59,7 @@ CREATE_EXAMPLE(color_chooser color_chooser.cxx fltk ANDROID_OK) CREATE_EXAMPLE(cursor cursor.cxx fltk ANDROID_OK) CREATE_EXAMPLE(curve curve.cxx fltk ANDROID_OK) CREATE_EXAMPLE(demo demo.cxx fltk) -CREATE_EXAMPLE(device device.cxx fltk) +CREATE_EXAMPLE(device device.cxx "fltk;fltk_images") CREATE_EXAMPLE(doublebuffer doublebuffer.cxx fltk ANDROID_OK) CREATE_EXAMPLE(editor editor.cxx fltk ANDROID_OK) CREATE_EXAMPLE(fast_slow fast_slow.fl fltk ANDROID_OK) diff --git a/test/Makefile b/test/Makefile index 66a25825c126258d9a50d8ea2ed456439091ca1f..4dceb9d45c4d23efa82b503a0d59af8fa22cc094 100644 --- a/test/Makefile +++ b/test/Makefile @@ -370,9 +370,9 @@ demo$(EXEEXT): demo.o $(OSX_ONLY) mkdir -p demo.app/Contents/Resources $(OSX_ONLY) cp -f demo.menu demo.app/Contents/Resources/ -device$(EXEEXT): device.o $(IMGLIBNAME) +device$(EXEEXT): device.o echo Linking $@... - $(CXX) $(ARCHFLAGS) $(CXXFLAGS) $(LDFLAGS) device.o -o $@ $(LINKFLTKIMG) $(LDLIBS) + $(CXX) $(ARCHFLAGS) $(CXXFLAGS) $(LDFLAGS) device.o -o $@ $(LINKFLTK) $(IMAGELIBS) $(LDLIBS) $(OSX_ONLY) ../fltk-config --post $@ doublebuffer$(EXEEXT): doublebuffer.o diff --git a/test/device.cxx b/test/device.cxx index 6ddda876127e436943dcb7473eb5c558ba535a42..e242f6ae687b37c914a0a4f60540f1cf3bd93302 100644 --- a/test/device.cxx +++ b/test/device.cxx @@ -3,7 +3,7 @@ // // Device test program for the Fast Light Tool Kit (FLTK). // -// Copyright 1998-2016 by Roman Kantor and others. +// Copyright 1998-2020 by Roman Kantor 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 @@ -18,7 +18,6 @@ #include <math.h> #include <FL/Fl.H> - #include <FL/Fl_Overlay_Window.H> #include <FL/Fl_Light_Button.H> #include <FL/Fl_Radio_Round_Button.H> @@ -28,15 +27,12 @@ #include <FL/Fl_Pixmap.H> #include <FL/Fl_Bitmap.H> #include <FL/Fl_Round_Button.H> - - #include <FL/Fl_Printer.H> #include <FL/Fl_PostScript.H> #include <FL/Fl_Copy_Surface.H> #include <FL/Fl_Image_Surface.H> - -#include <FL/Fl_File_Chooser.H> -#include <FL/fl_draw.H> +#include <FL/Fl_Native_File_Chooser.H> +#include <FL/Fl_SVG_File_Surface.H> #define sorceress_width 75 @@ -630,6 +626,59 @@ void copy(Fl_Widget *, void *data) { } else if (err > 1 && err_message) {fl_alert("%s", err_message); delete[] err_message;} delete p; } + + if (strcmp(operation, "Fl_EPS_File_Surface") == 0) { + Fl_Native_File_Chooser fnfc; + fnfc.title("Save a .eps file"); + fnfc.type(Fl_Native_File_Chooser::BROWSE_SAVE_FILE); + fnfc.filter("EPS\t*.eps\n"); + fnfc.options(Fl_Native_File_Chooser::SAVEAS_CONFIRM | Fl_Native_File_Chooser::USE_FILTER_EXT); + if (!fnfc.show() ) { + FILE *eps = fl_fopen(fnfc.filename(), "w"); + if (eps) { + int ww, wh; + if (target->as_window()) { + ww = target->as_window()->decorated_w(); + wh = target->as_window()->decorated_h(); + } else { + ww = target->w(); + wh = target->h(); + } + Fl_EPS_File_Surface p(ww, wh, eps); + if (target->as_window()) p.draw_decorated_window(target->as_window()); + else p.draw(target); + //p.close(); + } + fclose(eps); + } + } + + if (strcmp(operation, "Fl_SVG_File_Surface") == 0) { + Fl_Native_File_Chooser fnfc; + fnfc.title("Save a .svg file"); + fnfc.type(Fl_Native_File_Chooser::BROWSE_SAVE_FILE); + fnfc.filter("SVG\t*.svg\n"); + fnfc.options(Fl_Native_File_Chooser::SAVEAS_CONFIRM | Fl_Native_File_Chooser::USE_FILTER_EXT); + if (!fnfc.show() ) { + FILE *svg = fl_fopen(fnfc.filename(), "w"); + if (svg) { + int ww, wh; + if (target->as_window()) { + ww = target->as_window()->decorated_w(); + wh = target->as_window()->decorated_h(); + } else { + ww = target->w(); + wh = target->h(); + } + Fl_SVG_File_Surface surface(ww, wh, svg); + if (surface.file()) { + if (target->as_window()) surface.draw_decorated_window(target->as_window()); + else surface.draw(target); + surface.close(); + } + } + } + } } @@ -664,59 +713,59 @@ void operation_cb(Fl_Widget* wid, void *data) int main(int argc, char ** argv) { - //Fl::scheme("plastic"); + Fl::scheme("plastic"); - Fl_Window * w2 = new Fl_Window(500,560,"Graphics test"); + Fl_Window * w2 = new Fl_Window(500,568,"Graphics test"); - Fl_Group *c2 =new Fl_Group(3, 43, 494, 514 ); + Fl_Group *c2 =new Fl_Group(3, 56, 494, 514 ); - new MyWidget(10,140); - new MyWidget2(110,80); - new MyWidget3(220,140); - new MyWidget4(330,70); - new MyWidget5(140,270); + new MyWidget(10,140+16); + new MyWidget2(110,80+16); + new MyWidget3(220,140+16); + new MyWidget4(330,70+16); + new MyWidget5(140,270+16); make_image(); Fl_RGB_Image *rgb = new Fl_RGB_Image(image, width, height, 4); - My_Button b_rgb(10,245,100,100,"RGB with alpha"); + My_Button b_rgb(10,245+16,100,100,"RGB with alpha"); b_rgb.image(rgb); - My_Button b_pixmap(10,345,100,100,"Pixmap"); + My_Button b_pixmap(10,345+16,100,100,"Pixmap"); Fl_Pixmap *pixmap = new Fl_Pixmap(porsche_xpm); b_pixmap.image(pixmap); - My_Button b_bitmap(10,445,100,100,"Bitmap"); + My_Button b_bitmap(10,445+16,100,100,"Bitmap"); b_bitmap.labelcolor(FL_GREEN); b_bitmap.image(new Fl_Bitmap(sorceress_bits,sorceress_width,sorceress_height)); - new Fl_Clock(360,230,120,120); + new Fl_Clock(360,230+16,120,120); Fl_Return_Button * ret = new Fl_Return_Button (360, 360, 120,30, "Return"); ret->deactivate(); - Fl_Button but1(360, 390, 30, 30, "@->|"); + Fl_Button but1(360, 390+16, 30, 30, "@->|"); but1.labelcolor(FL_DARK3); - Fl_Button but2(390, 390, 30, 30, "@UpArrow"); + Fl_Button but2(390, 390+16, 30, 30, "@UpArrow"); but2.labelcolor(FL_DARK3); - Fl_Button but3(420, 390, 30, 30, "@DnArrow"); + Fl_Button but3(420, 390+16, 30, 30, "@DnArrow"); but3.labelcolor(FL_DARK3); - Fl_Button but4(450, 390, 30, 30, "@+"); + Fl_Button but4(450, 390+16, 30, 30, "@+"); but4.labelcolor(FL_DARK3); - Fl_Button but5(360, 425, 120, 30, "Hello, World"); + Fl_Button but5(360, 425+16, 120, 30, "Hello, World"); but5.labelfont(FL_BOLD|FL_ITALIC); but5.labeltype(FL_SHADOW_LABEL); but5.box(FL_ROUND_UP_BOX); - Fl_Button but6(360, 460, 120, 30, "Plastic"); + Fl_Button but6(360, 460+16, 120, 30, "Plastic"); but6.box(FL_PLASTIC_UP_BOX); Fl_Group *group; - { Fl_Group* o = new Fl_Group(360, 495, 120, 40); group=o; + { Fl_Group* o = new Fl_Group(360, 495+16, 120, 40); group=o; o->box(FL_UP_BOX); - { Fl_Group* o = new Fl_Group(365, 500, 110, 30); + { Fl_Group* o = new Fl_Group(365, 500+16, 110, 30); o->box(FL_THIN_UP_FRAME); - { Fl_Round_Button* o = new Fl_Round_Button(365, 500, 40, 30, "rad"); + { Fl_Round_Button* o = new Fl_Round_Button(365, 500+16, 40, 30, "rad"); o->value(1); } - { Fl_Check_Button* o = new Fl_Check_Button(410, 500, 60, 30, "check"); + { Fl_Check_Button* o = new Fl_Check_Button(410, 500+16, 60, 30, "check"); o->value(1); } @@ -725,7 +774,7 @@ int main(int argc, char ** argv) { o->end(); o->deactivate(); } - Fl_Box tx(120,492,230,50,"Background is not printed because\nencapsulating group, which we are\n printing, has not set the box type"); + Fl_Box tx(120,492+16,230,50,"Background is not printed because\nencapsulating group, which we are\n printing, has not set the box type"); tx.box(FL_SHADOW_BOX); tx.labelsize(12); @@ -734,7 +783,7 @@ int main(int argc, char ** argv) { c2->end(); Fl_Radio_Round_Button *rb; - Fl_Window *w3 = new Fl_Window(2,5,w2->w()-10,60); + Fl_Window *w3 = new Fl_Window(2,5,w2->w()-10,73); w3->box(FL_DOWN_BOX); Fl_Group *g1 = new Fl_Group(w3->x(),w3->y(),w3->w(),w3->h()); rb = new Fl_Radio_Round_Button(5,5,150,12, "Fl_Image_Surface"); @@ -742,6 +791,8 @@ int main(int argc, char ** argv) { rb = new Fl_Radio_Round_Button(5,18,150,12, "Fl_Copy_Surface"); rb->callback(operation_cb, NULL); rb->labelsize(12); rb = new Fl_Radio_Round_Button(5,31,150,12, "Fl_Printer"); rb->callback(operation_cb, NULL); rb->labelsize(12); rb = new Fl_Radio_Round_Button(5,44,150,12, "Fl_PostScript_File_Device"); rb->callback(operation_cb, NULL); rb->labelsize(12); + rb = new Fl_Radio_Round_Button(5,57,150,12, "Fl_EPS_File_Surface"); rb->callback(operation_cb, NULL); rb->labelsize(12); + rb = new Fl_Radio_Round_Button(170,57,150,12, "Fl_SVG_File_Surface"); rb->callback(operation_cb, NULL); rb->labelsize(12); g1->end(); Fl_Group *g2 = new Fl_Group(w3->x(),w3->y(),w3->w(),w3->h()); diff --git a/test/pixmap_browser.cxx b/test/pixmap_browser.cxx index 7d2ea3c3c1a01982e747f8ddbefe34ccef53329c..b2611703b461bb53948dc5f7d4ab9ebc89509aea 100644 --- a/test/pixmap_browser.cxx +++ b/test/pixmap_browser.cxx @@ -27,7 +27,8 @@ #include <errno.h> #include <FL/Fl_File_Chooser.H> #include <FL/fl_message.H> - +#include <FL/Fl_SVG_File_Surface.H> +#include <FL/Fl_Native_File_Chooser.H> Fl_Box *b; Fl_Double_Window *w; Fl_Shared_Image *img; @@ -77,16 +78,17 @@ void file_cb(const char *n) { void button_cb(Fl_Widget *,void *) { fl_file_chooser_callback(file_cb); const char *fname = fl_file_chooser("Image file?","*.{bm,bmp,gif,jpg,pbm,pgm,png,ppm,xbm,xpm" -#ifdef FLTK_USE_NANOSVG +#ifdef FLTK_USE_SVG ",svg" #ifdef HAVE_LIBZ ",svgz" -#endif -#endif +#endif // HAVE_LIBZ +#endif // FLTK_USE_SVG "}", name); puts(fname ? fname : "(null)"); fflush(stdout); fl_file_chooser_callback(0); } + void print_cb(Fl_Widget *widget, void *) { Fl_Printer printer; int width, height; @@ -101,6 +103,19 @@ void print_cb(Fl_Widget *widget, void *) { printer.end_page(); printer.end_job(); } + +void svg_cb(Fl_Widget *widget, void *) { + Fl_Native_File_Chooser fnfc; + fnfc.title("Pick a .svg file"); + fnfc.type(Fl_Native_File_Chooser::BROWSE_SAVE_FILE); + fnfc.filter("SVG\t*.svg\n"); + fnfc.options(Fl_Native_File_Chooser::SAVEAS_CONFIRM | Fl_Native_File_Chooser::USE_FILTER_EXT); + if (fnfc.show() ) return; + FILE *svg = fl_fopen(fnfc.filename(), "w"); + Fl_SVG_File_Surface surf(widget->window()->decorated_w(), widget->window()->decorated_h(), svg); + surf.draw_decorated_window(widget->window()); + surf.close(); +} int dvisual = 0; int arg(int, char **argv, int &i) { @@ -126,6 +141,8 @@ int main(int argc, char **argv) { window.resizable(b); Fl_Button print(300,425,50,25,"Print"); print.callback(print_cb); + Fl_Button svg(190,425,100,25,"save as SVG"); + svg.callback(svg_cb); window.show(argc,argv); return Fl::run();