Add new Fl_SVG_Image class to support scalable vector graphics images using the (modified) nanosvg software.

git-svn-id: file:///fltk/svn/fltk/branches/branch-1.4@12413 ea41ed52-d2ee-0310-a9c1-e6b18d33e121
This commit is contained in:
Manolo Gouy 2017-09-03 13:14:25 +00:00
parent da31ff4569
commit 1a28d85dc3
13 changed files with 4865 additions and 3 deletions

View File

@ -148,6 +148,13 @@ else()
set(FLTK_CAIRO_FOUND FALSE) set(FLTK_CAIRO_FOUND FALSE)
endif(LIB_CAIRO AND OPTION_CAIROEXT AND PKG_CAIRO_FOUND) endif(LIB_CAIRO AND OPTION_CAIROEXT AND PKG_CAIRO_FOUND)
#######################################################################
option(OPTION_USE_NANOSVG "support SVG images" ON)
if(OPTION_USE_NANOSVG)
set(FLTK_USE_NANOSVG 1)
endif(OPTION_USE_NANOSVG)
####################################################################### #######################################################################
set(HAVE_GL LIB_GL OR LIB_MesaGL) set(HAVE_GL LIB_GL OR LIB_MesaGL)

82
FL/Fl_SVG_Image.H Normal file
View File

@ -0,0 +1,82 @@
//
// "$Id: Fl_SVG_Image.H 12239 2017-05-17 11:54:18Z manolo $"
//
// SVG Image header file for the Fast Light Tool Kit (FLTK).
//
// Copyright 2017 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:
//
// http://www.fltk.org/COPYING.php
//
// Please report all bugs and problems on the following page:
//
// http://www.fltk.org/str.php
//
#ifndef FL_SVG_IMAGE_H
#define FL_SVG_IMAGE_H
#include <FL/Fl_Image.H>
struct NSVGimage;
/** The Fl_SVG_Image class supports loading, caching and drawing of scalable vector graphics (SVG) images.
The FLTK library performs parsing and rasterization of SVG data using a modified version
of the \c nanosvg software (https://github.com/memononen/nanosvg) © 2013-14 Mikko Mononen
(memon@inside.org). The software modification allows the option to change the image ratio
while performing rasterization.
Use Fl_Image::fail() to check if the Fl_SVG_Image failed to load. fail() returns ERR_FILE_ACCESS
if the file could not be opened or read, and ERR_FORMAT if the SVG format could not be decoded.
If the image has loaded correctly, w(), h(), and d() should return values greater than zero.
Rasterization is not done until the image is first drawn or resize() is called. Therefore,
array() is NULL until then. The delayed rasterization ensures an Fl_Shared_Image based on
an SVG image and scaled to its display size by calling Fl_Shared_Image::scale() will be
always rasterized to the exact screen resolution.
The Fl_SVG_Image class draws images computed by \c nanosvg: one known limitation is that text
within \c <text\></text\> blocks is not rendered.
The FLTK library can optionally be built without SVG support; in that case,
class Fl_SVG_Image is unavailable.
*/
class FL_EXPORT Fl_SVG_Image : public Fl_RGB_Image {
private:
typedef struct {
NSVGimage* svg_image;
int ref_count;
} counted_NSVGimage;
counted_NSVGimage* counted_svg_image_;
bool rasterized_;
int raster_w_, raster_h_;
bool to_desaturate_;
Fl_Color average_color_;
float average_weight_;
float svg_scaling_(int W, int H);
void rasterize_(int W, int H);
void init_(const char *filename, char *filedata, Fl_SVG_Image *copy_source);
virtual int draw_scaled_(int X, int Y, int W, int H);
Fl_SVG_Image(Fl_SVG_Image *source);
public:
/** Set this to \c false to allow image re-scaling that alters the image aspect ratio.
Upon object creation, \c proportional is set to \c true, and the aspect ratio is kept constant.*/
bool proportional;
Fl_SVG_Image(const char *filename, char *filedata = NULL);
virtual ~Fl_SVG_Image();
virtual Fl_Image *copy(int W, int H);
void resize(int width, int height);
virtual void desaturate();
virtual void color_average(Fl_Color c, float i);
virtual void draw(int X, int Y, int W, int H, int cx = 0, int cy = 0);
void draw(int X, int Y) { draw(X, Y, w(), h(), 0, 0); }
};
#endif // FL_SVG_IMAGE_H
//
// End of "$Id: Fl_SVG_Image.H 12239 2017-05-17 11:54:18Z manolo $".
//

View File

@ -328,6 +328,14 @@
#cmakedefine HAVE_PNG_GET_VALID 1 #cmakedefine HAVE_PNG_GET_VALID 1
#cmakedefine HAVE_PNG_SET_TRNS_TO_ALPHA 1 #cmakedefine HAVE_PNG_SET_TRNS_TO_ALPHA 1
/*
* FLTK_USE_NANOSVG
*
* Do we want FLTK to support SVG images with nanosvg ?
*/
#cmakedefine FLTK_USE_NANOSVG 1
/* /*
* Do we have POSIX threading? * Do we have POSIX threading?
*/ */

View File

@ -297,6 +297,7 @@
#undef HAVE_LIBPNG #undef HAVE_LIBPNG
#undef HAVE_LIBZ #undef HAVE_LIBZ
#undef HAVE_LIBJPEG #undef HAVE_LIBJPEG
#undef FLTK_USE_NANOSVG
/* /*
* FLTK_USE_CAIRO * FLTK_USE_CAIRO

View File

@ -766,6 +766,12 @@ AC_SUBST(PNGINC)
AC_SUBST(ZLIB) AC_SUBST(ZLIB)
AC_SUBST(ZLIBINC) 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)
fi
dnl Restore original LIBS settings... dnl Restore original LIBS settings...
LIBS="$SAVELIBS" LIBS="$SAVELIBS"

136
nanosvg/altsvgrast.diff Normal file
View File

@ -0,0 +1,136 @@
1,6d0
< //
< // "$Id: altsvgrast.h 12239 2017-05-17 11:54:18Z XX $"
< //
<
< /* Modified by FLTK from original sources to support non-square X,Y axes scaling */
<
31d24
<
51,53d43
<
< // For non-square X,Y scaling, use
< nsvgAltRasterize(rast, image, 0,0,1,1, img, w, h, w*4);
63c53
< // scale - image scale (assumes square aspect ratio)
---
> // scale - image scale
72,77d61
< // As above, but allow X and Y axes to scale independently for non-square aspects
< void nsvgAltRasterize(NSVGrasterizer* r,
< NSVGimage* image, float tx, float ty,
< float sx, float sy,
< unsigned char* dst, int w, int h, int stride);
<
382c366
< static void nsvg__flattenShape(NSVGrasterizer* r, NSVGshape* shape, float sx, float sy)
---
> static void nsvg__flattenShape(NSVGrasterizer* r, NSVGshape* shape, float scale)
390c374
< nsvg__addPathPoint(r, path->pts[0]*sx, path->pts[1]*sy, 0);
---
> nsvg__addPathPoint(r, path->pts[0]*scale, path->pts[1]*scale, 0);
393c377
< nsvg__flattenCubicBez(r, p[0]*sx,p[1]*sy, p[2]*sx,p[3]*sy, p[4]*sx,p[5]*sy, p[6]*sx,p[7]*sy, 0, 0);
---
> nsvg__flattenCubicBez(r, p[0]*scale,p[1]*scale, p[2]*scale,p[3]*scale, p[4]*scale,p[5]*scale, p[6]*scale,p[7]*scale, 0, 0);
396c380
< nsvg__addPathPoint(r, path->pts[0]*sx, path->pts[1]*sy, 0);
---
> nsvg__addPathPoint(r, path->pts[0]*scale, path->pts[1]*scale, 0);
746c730
< static void nsvg__flattenShapeStroke(NSVGrasterizer* r, NSVGshape* shape, float sx, float sy)
---
> static void nsvg__flattenShapeStroke(NSVGrasterizer* r, NSVGshape* shape, float scale)
754,755c738
< const float sw = (sx + sy) / 2; // average scaling factor
< const float lineWidth = shape->strokeWidth * sw; // FIXME (?)
---
> float lineWidth = shape->strokeWidth * scale;
760c743
< nsvg__addPathPoint(r, path->pts[0]*sx, path->pts[1]*sy, NSVG_PT_CORNER);
---
> nsvg__addPathPoint(r, path->pts[0]*scale, path->pts[1]*scale, NSVG_PT_CORNER);
763c746
< nsvg__flattenCubicBez(r, p[0]*sx,p[1]*sy, p[2]*sx,p[3]*sy, p[4]*sx,p[5]*sy, p[6]*sx,p[7]*sy, 0, NSVG_PT_CORNER);
---
> nsvg__flattenCubicBez(r, p[0]*scale,p[1]*scale, p[2]*scale,p[3]*scale, p[4]*scale,p[5]*scale, p[6]*scale,p[7]*scale, 0, NSVG_PT_CORNER);
809c792
< dashLen = (shape->strokeDashArray[idash] - dashOffset) * sw;
---
> dashLen = (shape->strokeDashArray[idash] - dashOffset) * scale;
831c814
< dashLen = shape->strokeDashArray[idash] * sw;
---
> dashLen = shape->strokeDashArray[idash] * scale;
1000c983
< float tx, float ty, float sx, float sy, NSVGcachedPaint* cache)
---
> float tx, float ty, float scale, NSVGcachedPaint* cache)
1041,1043c1024,1026
< fx = ((float)x - tx) / sx;
< fy = ((float)y - ty) / sy;
< dx = 1.0f / sx;
---
> fx = ((float)x - tx) / scale;
> fy = ((float)y - ty) / scale;
> dx = 1.0f / scale;
1086,1088c1069,1071
< fx = ((float)x - tx) / sx;
< fy = ((float)y - ty) / sy;
< dx = 1.0f / sx;
---
> fx = ((float)x - tx) / scale;
> fy = ((float)y - ty) / scale;
> dx = 1.0f / scale;
1127c1110
< static void nsvg__rasterizeSortedEdges(NSVGrasterizer *r, float tx, float ty, float sx, float sy, NSVGcachedPaint* cache, char fillRule)
---
> static void nsvg__rasterizeSortedEdges(NSVGrasterizer *r, float tx, float ty, float scale, NSVGcachedPaint* cache, char fillRule)
1209c1192
< nsvg__scanlineSolid(&r->bitmap[y * r->stride] + xmin*4, xmax-xmin+1, &r->scanline[xmin], xmin, y, tx,ty, sx, sy, cache);
---
> nsvg__scanlineSolid(&r->bitmap[y * r->stride] + xmin*4, xmax-xmin+1, &r->scanline[xmin], xmin, y, tx,ty, scale, cache);
1377,1379c1360,1361
< void nsvgAltRasterize(NSVGrasterizer* r,
< NSVGimage* image, float tx, float ty,
< float sx, float sy,
---
> void nsvgRasterize(NSVGrasterizer* r,
> NSVGimage* image, float tx, float ty, float scale,
1410c1392
< nsvg__flattenShape(r, shape, sx, sy);
---
> nsvg__flattenShape(r, shape, scale);
1427c1409
< nsvg__rasterizeSortedEdges(r, tx,ty, sx, sy, &cache, shape->fillRule);
---
> nsvg__rasterizeSortedEdges(r, tx,ty,scale, &cache, shape->fillRule);
1429c1411
< if (shape->stroke.type != NSVG_PAINT_NONE && (shape->strokeWidth * sx) > 0.01f) {
---
> if (shape->stroke.type != NSVG_PAINT_NONE && (shape->strokeWidth * scale) > 0.01f) {
1434c1416
< nsvg__flattenShapeStroke(r, shape, sx, sy);
---
> nsvg__flattenShapeStroke(r, shape, scale);
1453c1435
< nsvg__rasterizeSortedEdges(r, tx,ty,sx, sy, &cache, NSVG_FILLRULE_NONZERO);
---
> nsvg__rasterizeSortedEdges(r, tx,ty,scale, &cache, NSVG_FILLRULE_NONZERO);
1465,1477c1447
< void nsvgRasterize(NSVGrasterizer* r,
< NSVGimage* image, float tx, float ty, float scale,
< unsigned char* dst, int w, int h, int stride)
< {
< nsvgAltRasterize(r,image, tx, ty, scale, scale, dst, w, h, stride);
< }
<
< #endif // NANOSVGRAST_IMPLEMENTATION
<
<
< //
< // End of "$Id: altsvgrast.h 12239 2017-05-17 11:54:18Z XX $".
< //
---
> #endif

1477
nanosvg/altsvgrast.h Normal file

File diff suppressed because it is too large Load Diff

2925
nanosvg/nanosvg.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -367,6 +367,7 @@ set (IMGCPPFILES
Fl_JPEG_Image.cxx Fl_JPEG_Image.cxx
Fl_PNG_Image.cxx Fl_PNG_Image.cxx
Fl_PNM_Image.cxx Fl_PNM_Image.cxx
Fl_SVG_Image.cxx
) )
set (CFILES set (CFILES

206
src/Fl_SVG_Image.cxx Normal file
View File

@ -0,0 +1,206 @@
//
// "$Id: Fl_SVG_Image.cxx 12239 2017-05-17 11:54:18Z manolo $"
//
// SVG image code for the Fast Light Tool Kit (FLTK).
//
// Copyright 2017 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:
//
// http://www.fltk.org/COPYING.php
//
// Please report all bugs and problems on the following page:
//
// http://www.fltk.org/str.php
//
#include <config.h>
#if defined(FLTK_USE_NANOSVG) || defined(FL_DOXYGEN)
#include <FL/Fl_SVG_Image.H>
#include <stdio.h>
#define NANOSVG_ALL_COLOR_KEYWORDS // Include full list of color keywords.
#define NANOSVG_IMPLEMENTATION // Expands implementation
#include "../nanosvg/nanosvg.h"
#define NANOSVGRAST_IMPLEMENTATION // Expands implementation
#include "../nanosvg/altsvgrast.h"
/** The constructor loads the SVG image from the given .svg filename or in-memory data.
\param filename A full path and name pointing to an SVG file.
\param filedata A pointer to the memory location of the SVG image data.
This parameter allows to load an SVG image from in-memory data, and requires that \p filename is NULL.
\note In-memory SVG data is modified by the object constructor and is no longer used after construction.
*/
Fl_SVG_Image::Fl_SVG_Image(const char *filename, char *filedata) : Fl_RGB_Image(NULL, 0, 0, 4) {
init_(filename, filedata, NULL);
}
// private constructor
Fl_SVG_Image::Fl_SVG_Image(Fl_SVG_Image *source) : Fl_RGB_Image(NULL, 0, 0, 4) {
init_(NULL, NULL, source);
}
/** The destructor frees all memory and server resources that are used by the SVG image. */
Fl_SVG_Image::~Fl_SVG_Image() {
if ( --counted_svg_image_->ref_count <= 0) {
nsvgDelete(counted_svg_image_->svg_image);
delete counted_svg_image_;
}
}
float Fl_SVG_Image::svg_scaling_(int W, int H) {
float f1 = float(W) / int(counted_svg_image_->svg_image->width+0.5);
float f2 = float(H) / int(counted_svg_image_->svg_image->height+0.5);
return (f1 < f2) ? f1 : f2;
}
void Fl_SVG_Image::init_(const char *filename, char *filedata, Fl_SVG_Image *copy_source) {
if (copy_source) {
filename = filedata = NULL;
counted_svg_image_ = copy_source->counted_svg_image_;
counted_svg_image_->ref_count++;
} else {
counted_svg_image_ = new counted_NSVGimage;
counted_svg_image_->svg_image = NULL;
counted_svg_image_->ref_count = 1;
}
to_desaturate_ = false;
average_weight_ = 1;
proportional = true;
if (filename) {
filedata = NULL;
FILE *fp = fopen(filename, "rb");
if (fp) {
fseek(fp, 0, SEEK_END);
size_t size = ftell(fp);
fseek(fp, 0, SEEK_SET);
filedata = (char*)malloc(size+1);
if (filedata) {
if (fread(filedata, 1, size, fp) == size) filedata[size] = '\0';
else {
free(filedata);
filedata = NULL;
}
}
fclose(fp);
} else ld(ERR_FILE_ACCESS);
}
if (filedata) {
counted_svg_image_->svg_image = nsvgParse(filedata, "px", 96);
if (filename) free(filedata);
if (counted_svg_image_->svg_image->width == 0 || counted_svg_image_->svg_image->height == 0) {
d(-1);
ld(ERR_FORMAT);
} else {
w(counted_svg_image_->svg_image->width + 0.5);
h(counted_svg_image_->svg_image->height + 0.5);
}
} else if (copy_source) {
w(copy_source->w());
h(copy_source->h());
}
rasterized_ = false;
}
void Fl_SVG_Image::rasterize_(int W, int H) {
static NSVGrasterizer *rasterizer = nsvgCreateRasterizer();
double fx, fy;
if (proportional) {
fx = svg_scaling_(W, H);
fy = fx;
} else {
fx = (double)W / counted_svg_image_->svg_image->width;
fy = (double)H / counted_svg_image_->svg_image->height;
}
array = new uchar[W*H*4];
nsvgAltRasterize(rasterizer, counted_svg_image_->svg_image, 0, 0, fx, fy, (uchar* )array, W, H, W*4);
alloc_array = 1;
data((const char * const *)&array, 1);
d(4);
if (to_desaturate_) Fl_RGB_Image::desaturate();
if (average_weight_ < 1) Fl_RGB_Image::color_average(average_color_, average_weight_);
rasterized_ = true;
raster_w_ = W;
raster_h_ = H;
}
Fl_Image *Fl_SVG_Image::copy(int W, int H) {
Fl_SVG_Image *svg2 = new Fl_SVG_Image(this);
svg2->to_desaturate_ = to_desaturate_;
svg2->average_weight_ = average_weight_;
svg2->average_color_ = average_color_;
svg2->w(W); svg2->h(H);
return svg2;
}
/** Have the svg data (re-)rasterized using the given width and height values.
By default, the resulting image width and height will preserve the width/height ratio
of the SVG data.
If \ref proportional was set to false, the image is rasterized to the given \c width
and \v height values.*/
void Fl_SVG_Image::resize(int width, int height) {
if (ld() < 0) {
return;
}
int w1 = width, h1 = height;
if (proportional) {
float f = svg_scaling_(width, height);
w1 = int( int(counted_svg_image_->svg_image->width+0.5)*f + 0.5 );
h1 = int( int(counted_svg_image_->svg_image->height+0.5)*f + 0.5 );
}
w(w1); h(h1);
if (rasterized_ && w1 == raster_w_ && h1 == raster_h_) return;
if (array) {
delete[] array;
array = NULL;
}
uncache();
rasterize_(w1, h1);
}
void Fl_SVG_Image::draw(int X, int Y, int W, int H, int cx, int cy) {
resize(w(), h());
Fl_RGB_Image::draw(X, Y, W, H, cx, cy);
}
void Fl_SVG_Image::desaturate() {
to_desaturate_ = true;
Fl_RGB_Image::desaturate();
}
void Fl_SVG_Image::color_average(Fl_Color c, float i) {
average_color_ = c;
average_weight_ = i;
Fl_RGB_Image::color_average(c, i);
}
int Fl_SVG_Image::draw_scaled_(int X, int Y, int W, int H) {
w(W);
h(H);
draw(X, Y, W, H, 0, 0);
return 1;
}
#endif // FLTK_USE_NANOSVG
//
// End of "$Id: Fl_SVG_Image.cxx 12239 2017-05-17 11:54:18Z manolo $".
//

View File

@ -209,7 +209,8 @@ IMGCPPFILES = \
Fl_Help_Dialog.cxx \ Fl_Help_Dialog.cxx \
Fl_JPEG_Image.cxx \ Fl_JPEG_Image.cxx \
Fl_PNG_Image.cxx \ Fl_PNG_Image.cxx \
Fl_PNM_Image.cxx Fl_PNM_Image.cxx \
Fl_SVG_Image.cxx
CFILES = fl_call_main.c flstring.c numericsort.c vsnprintf.c CFILES = fl_call_main.c flstring.c numericsort.c vsnprintf.c

View File

@ -31,6 +31,7 @@
#include <FL/Fl_JPEG_Image.H> #include <FL/Fl_JPEG_Image.H>
#include <FL/Fl_PNG_Image.H> #include <FL/Fl_PNG_Image.H>
#include <FL/Fl_PNM_Image.H> #include <FL/Fl_PNM_Image.H>
#include <FL/Fl_SVG_Image.H>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include "flstring.h" #include "flstring.h"
@ -63,7 +64,7 @@ void fl_register_images() {
Fl_Image * // O - Image, if found Fl_Image * // O - Image, if found
fl_check_images(const char *name, // I - Filename fl_check_images(const char *name, // I - Filename
uchar *header, // I - Header data from file uchar *header, // I - Header data from file
int) { // I - Amount of data (not used) int headerlen) { // I - Amount of data
if (memcmp(header, "GIF87a", 6) == 0 || if (memcmp(header, "GIF87a", 6) == 0 ||
memcmp(header, "GIF89a", 6) == 0) // GIF file memcmp(header, "GIF89a", 6) == 0) // GIF file
return new Fl_GIF_Image(name); return new Fl_GIF_Image(name);
@ -88,6 +89,12 @@ fl_check_images(const char *name, // I - Filename
return new Fl_JPEG_Image(name); return new Fl_JPEG_Image(name);
#endif // HAVE_LIBJPEG #endif // HAVE_LIBJPEG
#ifdef FLTK_USE_NANOSVG
if ( (headerlen > 5 && memcmp(header, "<?xml", 5) == 0) ||
memcmp(header, "<svg", 4) == 0)
return new Fl_SVG_Image(name);
#endif // FLTK_USE_NANOSVG
return 0; return 0;
} }

View File

@ -16,6 +16,7 @@
// http://www.fltk.org/str.php // http://www.fltk.org/str.php
// //
#include <config.h>
#include <FL/Fl.H> #include <FL/Fl.H>
#include <FL/Fl_Box.H> #include <FL/Fl_Box.H>
#include <FL/Fl_Double_Window.H> #include <FL/Fl_Double_Window.H>
@ -75,7 +76,11 @@ void file_cb(const char *n) {
void button_cb(Fl_Widget *,void *) { void button_cb(Fl_Widget *,void *) {
fl_file_chooser_callback(file_cb); fl_file_chooser_callback(file_cb);
const char *fname = fl_file_chooser("Image file?","*.{bm,bmp,gif,jpg,pbm,pgm,png,ppm,xbm,xpm}", name); const char *fname = fl_file_chooser("Image file?","*.{bm,bmp,gif,jpg,pbm,pgm,png,ppm,xbm,xpm"
#ifdef FLTK_USE_NANOSVG
",svg"
#endif
"}", name);
puts(fname ? fname : "(null)"); fflush(stdout); puts(fname ? fname : "(null)"); fflush(stdout);
fl_file_chooser_callback(0); fl_file_chooser_callback(0);
} }