1018 lines
37 KiB
C++
1018 lines
37 KiB
C++
//
|
|
// Implementation of classes Fl_SVG_Graphics_Driver and Fl_SVG_File_Surface in the Fast Light Tool Kit (FLTK).
|
|
//
|
|
// Copyright 2020-2022 by Bill Spitzak and others.
|
|
//
|
|
// This library is free software. Distribution and use rights are outlined in
|
|
// the file "COPYING" which should have been included with this file. If this
|
|
// file is missing or damaged, see the license at:
|
|
//
|
|
// https://www.fltk.org/COPYING.php
|
|
//
|
|
// Please see the following page on how to report bugs and issues:
|
|
//
|
|
// https://www.fltk.org/bugs.php
|
|
//
|
|
|
|
// Complete implementation to draw into an SVG file using the standard FLTK drawing API.
|
|
|
|
#include <config.h>
|
|
#include <FL/Fl_SVG_File_Surface.H>
|
|
#ifdef 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_System_Driver.H"
|
|
#include <FL/Fl.H>
|
|
#include <FL/Fl_RGB_Image.H>
|
|
#include <FL/Fl_Pixmap.H>
|
|
#include <FL/Fl_Bitmap.H>
|
|
#include <FL/fl_string_functions.h>
|
|
#include <stdlib.h>
|
|
#include <stdarg.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
|
|
class Clip {
|
|
public:
|
|
int x, y, w, h; // the clip rectangle
|
|
char Id[12]; // "none" or SVG Id of this clip rectangle
|
|
Clip *prev; // previous in pile of clips
|
|
};
|
|
Clip * clip_; // top of pile of clips
|
|
int clip_count_; // to generate distinct SVG clip Ids
|
|
char *last_rgb_name_; // NULL or SVG Id of last defined RGB image
|
|
const char *family_;
|
|
const char *bold_;
|
|
const char *style_;
|
|
public:
|
|
Fl_SVG_Graphics_Driver(FILE*);
|
|
~Fl_SVG_Graphics_Driver();
|
|
FILE* file() {return out_;}
|
|
protected:
|
|
int clocale_printf(const char *format, ...);
|
|
void rect(int x, int y, int w, int h) FL_OVERRIDE;
|
|
void rectf(int x, int y, int w, int h) FL_OVERRIDE;
|
|
virtual void compute_dasharray(float s, char *dashes=0);
|
|
void line_style(int style, int width, char *dashes=0) FL_OVERRIDE;
|
|
void line(int x1, int y1, int x2, int y2) FL_OVERRIDE;
|
|
void line(int x1, int y1, int x2, int y2, int x3, int y3) FL_OVERRIDE;
|
|
void font_(int f, int s);
|
|
void font(int f, int s) FL_OVERRIDE;
|
|
Fl_Font font() FL_OVERRIDE;
|
|
void draw(const char *str, int n, int x, int y) FL_OVERRIDE;
|
|
void draw(const char*, int, float, float) FL_OVERRIDE;
|
|
void draw(int, const char*, int, int, int) FL_OVERRIDE;
|
|
void rtl_draw(const char *str, int n, int x, int y) FL_OVERRIDE;
|
|
void color(uchar r, uchar g, uchar b) FL_OVERRIDE;
|
|
void color(Fl_Color c) FL_OVERRIDE;
|
|
Fl_Color color() FL_OVERRIDE;
|
|
double width(const char*, int) FL_OVERRIDE;
|
|
double width(unsigned int c) FL_OVERRIDE;
|
|
void text_extents(const char*, int n, int& dx, int& dy, int& w, int& h) FL_OVERRIDE;
|
|
int height() FL_OVERRIDE;
|
|
int descent() FL_OVERRIDE;
|
|
void draw_rgb(Fl_RGB_Image *rgb, int XP, int YP, int WP, int HP, int cx, int cy) FL_OVERRIDE;
|
|
void define_rgb_png(Fl_RGB_Image *rgb, const char *name, int x, int y);
|
|
void define_rgb_jpeg(Fl_RGB_Image *rgb, const char *name, int x, int y);
|
|
void draw_pixmap(Fl_Pixmap *pxm,int XP, int YP, int WP, int HP, int cx, int cy) FL_OVERRIDE;
|
|
void draw_bitmap(Fl_Bitmap *bm,int XP, int YP, int WP, int HP, int cx, int cy) FL_OVERRIDE;
|
|
void draw_image(const uchar* buf, int x, int y, int w, int h, int d, int l) FL_OVERRIDE;
|
|
void draw_image(Fl_Draw_Image_Cb cb, void* data, int x, int y, int w, int h, int d) FL_OVERRIDE;
|
|
void draw_image_mono(const uchar* buf, int x, int y, int w, int h, int d, int l) FL_OVERRIDE;
|
|
void draw_image_mono(Fl_Draw_Image_Cb cb, void* data, int x, int y, int w, int h, int d) FL_OVERRIDE;
|
|
void push_clip(int x, int y, int w, int h) FL_OVERRIDE;
|
|
void push_no_clip() FL_OVERRIDE;
|
|
void pop_clip() FL_OVERRIDE;
|
|
int clip_box(int x, int y, int w, int h, int& X, int& Y, int& W, int& H) FL_OVERRIDE;
|
|
int not_clipped(int x, int y, int w, int h) FL_OVERRIDE;
|
|
void polygon(int x0, int y0, int x1, int y1, int x2, int y2) FL_OVERRIDE;
|
|
void polygon(int x0, int y0, int x1, int y1, int x2, int y2, int x3, int y3) FL_OVERRIDE;
|
|
void loop(int x0, int y0, int x1, int y1, int x2, int y2, int x3, int y3) FL_OVERRIDE;
|
|
void loop(int x0, int y0, int x1, int y1, int x2, int y2) FL_OVERRIDE;
|
|
void point(int x, int y) FL_OVERRIDE;
|
|
void end_points() FL_OVERRIDE;
|
|
void end_line() FL_OVERRIDE;
|
|
void end_polygon() FL_OVERRIDE;
|
|
void end_complex_polygon() FL_OVERRIDE;
|
|
void circle(double x, double y,double r) FL_OVERRIDE;
|
|
void arc(int x,int y,int w,int h,double a1,double a2) FL_OVERRIDE;
|
|
void arc(double x, double y, double r, double start, double end) FL_OVERRIDE;
|
|
void pie(int x,int y,int w,int h,double a1,double a2) FL_OVERRIDE;
|
|
void arc_pie(char AorP, int x, int y, int w, int h, double a1, double a2);
|
|
};
|
|
|
|
Fl_SVG_Graphics_Driver::Fl_SVG_Graphics_Driver(FILE *f) {
|
|
out_ = f;
|
|
width_ = 1;
|
|
line_style_ = 0;
|
|
linecap_ = "butt";
|
|
linejoin_ = "miter";
|
|
family_ = "";
|
|
bold_ = "";
|
|
style_ = "";
|
|
red_ = green_ = blue_ = 0;
|
|
clip_count_ = 0;
|
|
clip_ = NULL;
|
|
user_dash_array_ = 0;
|
|
dasharray_ = fl_strdup("none");
|
|
p_size = 0;
|
|
last_rgb_name_ = NULL;
|
|
}
|
|
|
|
Fl_SVG_Graphics_Driver::~Fl_SVG_Graphics_Driver()
|
|
{
|
|
if (user_dash_array_) free(user_dash_array_);
|
|
if (dasharray_) free(dasharray_);
|
|
while (clip_){
|
|
Clip * c= clip_;
|
|
clip_= clip_->prev;
|
|
delete c;
|
|
}
|
|
if (last_rgb_name_) free(last_rgb_name_);
|
|
}
|
|
|
|
|
|
int Fl_SVG_Graphics_Driver::clocale_printf(const char *format, ...)
|
|
{
|
|
va_list args;
|
|
va_start(args, format);
|
|
int retval = Fl::system_driver()->clocale_vprintf(out_, format, args);
|
|
va_end(args);
|
|
return retval;
|
|
}
|
|
|
|
|
|
static int clocale_fprintf(FILE *f, const char *format, ...)
|
|
{
|
|
va_list args;
|
|
va_start(args, format);
|
|
int retval = Fl::system_driver()->clocale_vprintf(f, format, args);
|
|
va_end(args);
|
|
return retval;
|
|
}
|
|
|
|
|
|
void Fl_SVG_Graphics_Driver::rect(int x, int y, int w, int h) {
|
|
fprintf(out_, "<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::line(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 \" "
|
|
"style=\"stroke:rgb(%u,%u,%u);fill:none;stroke-width:%d;stroke-linecap:%s;stroke-linejoin:%s;stroke-dasharray:%s\" />\n",
|
|
x1, y1, x2, y2, x3, y3, red_, green_, blue_, width_, linecap_, linejoin_, dasharray_);
|
|
}
|
|
|
|
void Fl_SVG_Graphics_Driver::font_(int ft, int s) {
|
|
Fl_Graphics_Driver::font(ft, s);
|
|
int famnum = ft/4;
|
|
if (famnum == 0) family_ = "Helvetica";
|
|
else if (famnum == 1) family_ = "Courier";
|
|
else family_ = "Times";
|
|
int modulo = ft % 4;
|
|
int use_bold = modulo == 1 || modulo == 3;
|
|
int use_italic = modulo >= 2;
|
|
bold_ = ( use_bold ? " font-weight=\"bold\"" : "" );
|
|
style_ = ( use_italic ? " font-style=\"italic\"" : "" );
|
|
if (use_italic && famnum != 2) style_ = " font-style=\"oblique\"";
|
|
}
|
|
|
|
void Fl_SVG_Graphics_Driver::font(int ft, int s) {
|
|
Fl_Display_Device::display_device()->driver()->font(ft, s);
|
|
font_(ft, s);
|
|
}
|
|
|
|
Fl_Font Fl_SVG_Graphics_Driver::font() { return Fl_Graphics_Driver::font(); }
|
|
|
|
void Fl_SVG_Graphics_Driver::compute_dasharray(float s, char *dashes) {
|
|
if (user_dash_array_ && user_dash_array_ != dashes) {free(user_dash_array_); user_dash_array_ = NULL;}
|
|
if (dashes && *dashes) {
|
|
if (dasharray_) free(dasharray_);
|
|
int array_len = int(10*strlen(dashes) + 1);
|
|
dasharray_ = (char*)calloc(array_len, 1);
|
|
for (char *p = dashes; *p; p++) {
|
|
int c = snprintf(dasharray_+strlen(dasharray_), array_len, "%.3f,", (*p)/s);
|
|
array_len -= c;
|
|
}
|
|
dasharray_[strlen(dasharray_) - 1] = 0;
|
|
if (user_dash_array_ != dashes) user_dash_array_ = fl_strdup(dashes);
|
|
return;
|
|
}
|
|
int dash_part = line_style_ & 0xFF;
|
|
if (dash_part == FL_SOLID) {
|
|
if (strcmp(dasharray_, "none")) {
|
|
free(dasharray_);
|
|
dasharray_ = fl_strdup("none");
|
|
}
|
|
} else {
|
|
int cap_part = (line_style_ & 0xF00);
|
|
bool is_flat = (cap_part == FL_CAP_FLAT || cap_part == 0);
|
|
float dot = (is_flat ? width_/s : width_*0.6f/s);
|
|
float gap = (is_flat ? width_/s : width_*1.5f/s);
|
|
float big = (is_flat ? 3*width_/s : width_*2.5f/s);
|
|
if (dasharray_) free(dasharray_);
|
|
dasharray_ = (char*)malloc(61);
|
|
if (dash_part == FL_DOT) snprintf(dasharray_, 61, "%.3f,%.3f", dot, gap);
|
|
else if (dash_part == FL_DASH) snprintf(dasharray_, 61, "%.3f,%.3f", big, gap);
|
|
else if (dash_part == FL_DASHDOT) snprintf(dasharray_, 61, "%.3f,%.3f,%.3f,%.3f", big, gap, dot, gap);
|
|
else snprintf(dasharray_, 61, "%.3f,%.3f,%.3f,%.3f,%.3f,%.3f", big, gap, dot, gap, dot, gap);
|
|
}
|
|
}
|
|
|
|
void Fl_SVG_Graphics_Driver::line_style(int style, int width, char *dashes) {
|
|
line_style_ = style;
|
|
if (width == 0) width = 1;
|
|
width_ = width;
|
|
int cap_part = style & 0xF00;
|
|
if (cap_part == FL_CAP_SQUARE) linecap_ = "square";
|
|
else if (cap_part == FL_CAP_ROUND) linecap_ = "round";
|
|
else linecap_ = "butt";
|
|
int join_part = style & 0xF000;
|
|
if (join_part == FL_JOIN_BEVEL) linejoin_ = "bevel";
|
|
else if (join_part == FL_JOIN_MITER) linejoin_ = "miter";
|
|
else if (join_part == FL_JOIN_ROUND) linejoin_ = "round";
|
|
else linejoin_ = "miter";
|
|
compute_dasharray(1., dashes);
|
|
}
|
|
|
|
void Fl_SVG_Graphics_Driver::draw(const char *str, int n, int x, int y) {
|
|
// Caution: Internet Explorer ignores the xml:space="preserve" attribute
|
|
// work-around: replace all spaces by no-break space = U+00A0 = 0xC2-0xA0 (UTF-8) before sending to IE
|
|
fprintf(out_, "<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;
|
|
}
|
|
|
|
Fl_Color Fl_SVG_Graphics_Driver::color() { return Fl_Graphics_Driver::color(); }
|
|
|
|
double Fl_SVG_Graphics_Driver::width(const char* str, int l) {
|
|
return Fl_Display_Device::display_device()->driver()->width(str, l);
|
|
}
|
|
|
|
double Fl_SVG_Graphics_Driver::width(unsigned int c) {
|
|
return Fl_Display_Device::display_device()->driver()->width(c);
|
|
}
|
|
|
|
void Fl_SVG_Graphics_Driver::text_extents(const char *c, int n, int& dx, int& dy, int& w, int& h) {
|
|
Fl::first_window()->make_current(); // to get a valid drawing gc
|
|
Fl_Display_Device::display_device()->driver()->text_extents(c, n, dx, dy, w, h);
|
|
}
|
|
|
|
int Fl_SVG_Graphics_Driver::height() {
|
|
return Fl_Display_Device::display_device()->driver()->height();
|
|
}
|
|
|
|
int Fl_SVG_Graphics_Driver::descent() {
|
|
return Fl_Display_Device::display_device()->driver()->descent();
|
|
}
|
|
|
|
Fl_SVG_File_Surface::Fl_SVG_File_Surface(int w, int h, FILE *f, int (*closef)(FILE*)) : Fl_Widget_Surface(new Fl_SVG_Graphics_Driver(f)) {
|
|
closef_ = closef;
|
|
Fl_Window *win = Fl::first_window();
|
|
float s = (win ? Fl::screen_scale(win->screen_num()) : 1);
|
|
int sw = int(w * s), sh = int(h * s);
|
|
fprintf(f,
|
|
"<?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;
|
|
clocale_fprintf(f, "<g transform=\"scale(%f)\">\n", s);
|
|
fputs("<g transform=\"translate(0,0)\">\n", f);
|
|
}
|
|
|
|
Fl_SVG_File_Surface::~Fl_SVG_File_Surface() {
|
|
if (driver()) close();
|
|
}
|
|
|
|
FILE *Fl_SVG_File_Surface::file() {
|
|
Fl_SVG_Graphics_Driver *driver = (Fl_SVG_Graphics_Driver*)this->driver();
|
|
return driver->file();
|
|
}
|
|
|
|
int Fl_SVG_File_Surface::close() {
|
|
Fl_SVG_Graphics_Driver *driver = (Fl_SVG_Graphics_Driver*)this->driver();
|
|
fputs("</g></g></svg>\n", driver->file());
|
|
int retval = (closef_ ? closef_(driver->file()) : fclose(driver->file()));
|
|
delete driver;
|
|
this->driver(NULL);
|
|
return retval;
|
|
}
|
|
|
|
void Fl_SVG_File_Surface::translate(int x, int y) {
|
|
Fl_SVG_Graphics_Driver *driver = (Fl_SVG_Graphics_Driver*)this->driver();
|
|
fprintf(driver->file(), "<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 = (int)new_l;
|
|
if (new_l) {
|
|
memcpy(svg_base64_data->buff, data + length - new_l, new_l);
|
|
}
|
|
}
|
|
|
|
// processes last bytes to be base64 encoded
|
|
static void user_flush_data(png_structp png_ptr) {
|
|
svg_base64_t *svg_base64_data = (svg_base64_t*)png_get_io_ptr(png_ptr);
|
|
if (svg_base64_data->lbuf) to_base64(svg_base64_data->buff, svg_base64_data->lbuf, svg_base64_data);
|
|
}
|
|
|
|
/* How to define first the image data and next use it, possibly several times:
|
|
<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_ = fl_strdup(name);
|
|
}
|
|
float f = rgb->data_w() > rgb->data_h() ? float(rgb->w()) / rgb->data_w(): float(rgb->h()) / rgb->data_h();
|
|
if (name) fprintf(out_, "<defs><image id=\"%s\" ", name);
|
|
else fprintf(out_, "<image x=\"%d\" y=\"%d\" ", x, y);
|
|
clocale_printf("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, (int)new_l, &client_data->base64_data);
|
|
}
|
|
}
|
|
|
|
void Fl_SVG_Graphics_Driver::define_rgb_jpeg(Fl_RGB_Image *rgb, const char *name, int x, int y) {
|
|
if (name) {
|
|
if (last_rgb_name_) free(last_rgb_name_);
|
|
last_rgb_name_ = fl_strdup(name);
|
|
}
|
|
float f = rgb->data_w() > rgb->data_h() ? float(rgb->w()) / rgb->data_w(): float(rgb->h()) / rgb->data_h();
|
|
if (name) fprintf(out_, "<defs><image id=\"%s\" ", name);
|
|
else fprintf(out_, "<image x=\"%d\" y=\"%d\" ", x, y);
|
|
clocale_printf("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) snprintf(name, 24, "FLrgb%p", p); else name[0] = 0;
|
|
if (!p || !last_rgb_name_ || strcmp(name, last_rgb_name_) != 0) {
|
|
if (*name==0 && need_clip) push_clip(XP, YP, WP, HP);
|
|
#if defined(HAVE_LIBJPEG)
|
|
if (rgb->d() == 3 || rgb->d() == 1) define_rgb_jpeg(rgb, *name ? name : NULL, XP-cx, YP-cy);
|
|
else
|
|
#endif // HAVE_LIBJPEG
|
|
define_rgb_png(rgb, *name ? name : NULL, XP-cx, YP-cy);
|
|
if (*name==0 && need_clip) pop_clip();
|
|
}
|
|
if (*name) {
|
|
if (need_clip) push_clip(XP, YP, WP, HP);
|
|
fprintf(out_, "<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) snprintf(name, 24, "FLpx%p", p); else name[0] = 0;
|
|
if (!p || !last_rgb_name_ || strcmp(name, last_rgb_name_) != 0) {
|
|
Fl_RGB_Image *rgb = new Fl_RGB_Image(pxm);
|
|
if (*name==0 && need_clip) push_clip(XP, YP, WP, HP);
|
|
define_rgb_png(rgb, *name ? name : NULL, XP-cx, YP-cy);
|
|
if (*name==0 && need_clip) pop_clip();
|
|
delete rgb;
|
|
}
|
|
if (*name) {
|
|
if (need_clip) push_clip(XP, YP, WP, HP);
|
|
fprintf(out_, "<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) snprintf(name, 45, "FLbm%p%X", p, fl_color()); else name[0] = 0;
|
|
if (!p || !last_rgb_name_ || strcmp(name, last_rgb_name_) != 0) {
|
|
uchar R, G, B;
|
|
Fl::get_color(fl_color(), R, G, B);
|
|
uchar *data = new uchar[bm->data_w() * bm->data_h() * 4];
|
|
memset(data, 0, bm->data_w() * bm->data_h() * 4);
|
|
Fl_RGB_Image *rgb = new Fl_RGB_Image(data, bm->data_w(), bm->data_h(), 4);
|
|
rgb->alloc_array = 1;
|
|
int rowBytes = (bm->data_w()+7)>>3 ;
|
|
for (int j = 0; j < bm->data_h(); j++) {
|
|
const uchar *p = bm->array + j*rowBytes;
|
|
for (int i = 0; i < rowBytes; i++) {
|
|
uchar q = *p;
|
|
int last = bm->data_w() - 8*i; if (last > 8) last = 8;
|
|
for (int k=0; k < last; k++) {
|
|
if (q&1) {
|
|
uchar *r = (uchar*)rgb->array + j*bm->data_w()*4 + i*8*4 + k*4;
|
|
*r++ = R; *r++ = G; *r++ = B; *r = ~0;
|
|
}
|
|
q >>= 1;
|
|
}
|
|
p++;
|
|
}
|
|
}
|
|
if (*name==0 && need_clip) push_clip(XP, YP, WP, HP);
|
|
define_rgb_png(rgb, *name ? name : NULL, XP-cx, YP-cy);
|
|
if (*name==0 && need_clip) pop_clip();
|
|
delete rgb;
|
|
}
|
|
if (*name) {
|
|
if (need_clip) push_clip(XP, YP, WP, HP);
|
|
fprintf(out_, "<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_;
|
|
snprintf(c->Id, sizeof(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::end_points() {
|
|
for (int i=0; i<n; i++) {
|
|
clocale_printf("<path d=\"M %f %f L %f %f\" fill=\"none\" stroke=\"rgb(%u,%u,%u)\" stroke-width=\"%d\" />\n",
|
|
xpoint[i].x, xpoint[i].y, xpoint[i].x, xpoint[i].y, red_, green_, blue_, width_);
|
|
}
|
|
}
|
|
|
|
void Fl_SVG_Graphics_Driver::end_line() {
|
|
if (n < 2) {
|
|
end_points();
|
|
return;
|
|
}
|
|
if (n<=1) return;
|
|
clocale_printf("<path d=\"M %f %f", xpoint[0].x, xpoint[0].y);
|
|
for (int i=1; i<n; i++)
|
|
clocale_printf(" L %f %f", xpoint[i].x, xpoint[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::end_polygon() {
|
|
fixloop();
|
|
if (n < 3) {
|
|
end_line();
|
|
return;
|
|
}
|
|
if (n<=1) return;
|
|
clocale_printf("<path d=\"M %f %f", xpoint[0].x, xpoint[0].y);
|
|
for (int i=1; i<n; i++)
|
|
clocale_printf(" L %f %f", xpoint[i].x, xpoint[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;
|
|
clocale_printf("<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::end_complex_polygon() {
|
|
gap();
|
|
if (n < 3) {
|
|
end_line();
|
|
return;
|
|
}
|
|
if (n<=1) return;
|
|
clocale_printf("<path d=\"M %f %f", xpoint[0].x, xpoint[0].y);
|
|
for (int i=1; i<n; i++)
|
|
clocale_printf(" L %f %f", xpoint[i].x, xpoint[i].y);
|
|
fprintf(out_, " z\" fill=\"rgb(%u,%u,%u)\" />\n", red_, green_, blue_);
|
|
}
|
|
|
|
void Fl_SVG_Graphics_Driver::arc(double x, double y, double r, double start, double end) {
|
|
Fl_Graphics_Driver::arc(x, y, r, start, end);
|
|
}
|
|
|
|
void Fl_SVG_Graphics_Driver::arc(int x, int y, int w, int h, double a1, double a2) {
|
|
arc_pie('A', x, y, w, h, a1, a2);
|
|
}
|
|
|
|
void Fl_SVG_Graphics_Driver::pie(int x, int y, int w, int h, double a1, double a2) {
|
|
arc_pie('P', x, y, w, h, a1, a2);
|
|
}
|
|
|
|
void Fl_SVG_Graphics_Driver::arc_pie(char AorP, int x, int y, int w, int h, double a1, double a2) {
|
|
// This implementation was constructed as follows:
|
|
// - follow Fl_Quartz_Graphics_Driver::arc(int x,...).
|
|
// which applies a translation, a scaling, and then calls
|
|
// CGContextAddArc(gc_, 0, 0, 0.5, a1, a2, 1);
|
|
// to draw an arc of a circle given its center, its radius, and starting and ending angles
|
|
// - consider https://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
|
|
// which gives the equations that transform the center parameterization used by
|
|
// CGContextAddArc() to the endpoint parameterization used by the "elliptical arc curve" command of SVG (A).
|
|
if (w <= 0 || h <= 0) return;
|
|
bool full = fabs(a1-a2) == 360; // case of full circle/disk
|
|
a1 = (-a1)/180.0f*M_PI; a2 = (-a2)/180.0f*M_PI;
|
|
float cx = x + 0.5f*w /*- 0.5f*/, cy = y + 0.5f*h - 0.5f;
|
|
double r = (w!=h ? 0.5 : (w+h)*0.25f-0.5f);
|
|
float stroke_width = float(width_);
|
|
float sx, sy;
|
|
if (w != h) {
|
|
sx = float(w-1); sy = float(h-1);
|
|
stroke_width /= ((sx+sy)/2);
|
|
} else {
|
|
sx = sy = float(2*r);
|
|
stroke_width /= sx;
|
|
}
|
|
clocale_printf("<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')
|
|
clocale_printf(":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')
|
|
clocale_printf("<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
|
|
clocale_printf("<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, int (*closef)(FILE*)) : Fl_Widget_Surface(NULL) {
|
|
closef_ = NULL;
|
|
width_ = height_ = 0;
|
|
}
|
|
Fl_SVG_File_Surface::~Fl_SVG_File_Surface() {}
|
|
int Fl_SVG_File_Surface::close() {return 0;}
|
|
FILE *Fl_SVG_File_Surface::file() {return NULL;}
|
|
void Fl_SVG_File_Surface::origin(int x, int y) {}
|
|
void Fl_SVG_File_Surface::translate(int x, int y) {}
|
|
void Fl_SVG_File_Surface::untranslate() {}
|
|
int Fl_SVG_File_Surface::printable_rect(int *w, int *h) {return 0;}
|
|
|
|
#endif // FLTK_USE_SVG
|
|
|
|
void Fl_SVG_File_Surface::origin(int *x, int *y) { Fl_Widget_Surface::origin(x, y);}
|