summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--FL/Fl_SVG_Image.H7
-rw-r--r--fluid/Fluid_Image.cxx30
-rw-r--r--src/Fl_SVG_Image.cxx111
-rw-r--r--src/Fl_System_Driver.H6
-rw-r--r--src/drivers/Posix/Fl_Posix_System_Driver.H4
-rw-r--r--src/drivers/Posix/Fl_Posix_System_Driver.cxx29
-rw-r--r--src/drivers/WinAPI/Fl_WinAPI_System_Driver.H4
-rw-r--r--src/drivers/WinAPI/Fl_WinAPI_System_Driver.cxx54
8 files changed, 200 insertions, 45 deletions
diff --git a/FL/Fl_SVG_Image.H b/FL/Fl_SVG_Image.H
index d09f3882b..530c51a82 100644
--- a/FL/Fl_SVG_Image.H
+++ b/FL/Fl_SVG_Image.H
@@ -1,7 +1,7 @@
//
// SVG Image header file for the Fast Light Tool Kit (FLTK).
//
-// Copyright 2017-2020 by Bill Spitzak and others.
+// Copyright 2017-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
@@ -147,13 +147,14 @@ private:
float svg_scaling_(int W, int H);
void rasterize_(int W, int H);
virtual void cache_size_(int &width, int &height);
- void init_(const char *filename, const char *filedata, const Fl_SVG_Image *copy_source);
+ void init_(const char *filename, const unsigned char *filedata, const Fl_SVG_Image *copy_source,
+ size_t length);
Fl_SVG_Image(const Fl_SVG_Image *source);
public:
/** Set this to \c false to allow image re-scaling that alters the image aspect ratio.
Upon object creation, proportional is set to \c true, and the aspect ratio is kept constant.*/
bool proportional;
- Fl_SVG_Image(const char *filename, const char *svg_data = NULL);
+ Fl_SVG_Image(const char *filename, const char *svg_data = NULL, size_t length = 0);
virtual ~Fl_SVG_Image();
virtual Fl_Image *copy(int W, int H) const;
Fl_Image *copy() const {
diff --git a/fluid/Fluid_Image.cxx b/fluid/Fluid_Image.cxx
index e2ae5a1bd..f76a88d25 100644
--- a/fluid/Fluid_Image.cxx
+++ b/fluid/Fluid_Image.cxx
@@ -1,7 +1,7 @@
//
// Pixmap (and other images) label support for the Fast Light Tool Kit (FLTK).
//
-// Copyright 1998-2021 by Bill Spitzak and others.
+// Copyright 1998-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
@@ -124,35 +124,45 @@ void Fluid_Image::write_static() {
write_c(";\n");
write_initializer("Fl_JPEG_Image", "\"%s\", %s", fl_filename_name(name()), idata_name);
- } else if (strcmp(fl_filename_ext(name()), ".svg")==0) {
+ } else if (strcmp(fl_filename_ext(name()), ".svg")==0 || strcmp(fl_filename_ext(name()), ".svgz")==0) {
+ bool gzipped = (strcmp(fl_filename_ext(name()), ".svgz") == 0);
// Write svg image data...
write_c("\n");
if (svg_header_written != write_number) {
write_c("#include <FL/Fl_SVG_Image.H>\n");
svg_header_written = write_number;
}
- write_c("static const char %s[] =\n", idata_name);
+ write_c(
+ (gzipped ? "static const unsigned char %s[] =\n" : "static const char %s[] =\n"),
+ idata_name);
goto_designfile_dir();
FILE *f = fl_fopen(name(), "rb");
leave_designfile_dir();
+ size_t nData = 0;
if (!f) {
write_file_error("SVG");
} else {
fseek(f, 0, SEEK_END);
- size_t nData = ftell(f);
+ nData = ftell(f);
fseek(f, 0, SEEK_SET);
if (nData) {
char *data = (char*)calloc(nData+1, 1);
if (fread(data, nData, 1, f)==0) { /* ignore */ }
- write_cstring(data, (int)nData);
+ if (gzipped)
+ write_cdata(data, (int)nData);
+ else
+ write_cstring(data, (int)nData);
free(data);
}
fclose(f);
}
write_c(";\n");
- write_initializer("Fl_SVG_Image", "NULL, %s", idata_name);
+ if (gzipped)
+ write_initializer("Fl_SVG_Image", "NULL, (const char*)%s, %ld", idata_name, nData);
+ else
+ write_initializer("Fl_SVG_Image", "NULL, %s", idata_name);
} else {
// Write image data...
write_c("\n");
@@ -291,7 +301,13 @@ const char *ui_find_image_name;
Fluid_Image *ui_find_image(const char *oldname) {
goto_designfile_dir();
fl_file_chooser_ok_label("Use Image");
- const char *name = fl_file_chooser("Image?","Image Files (*.{bm,bmp,gif,jpg,pbm,pgm,png,ppm,xbm,xpm,svg})",oldname,1);
+ const char *name = fl_file_chooser("Image?",
+ "Image Files (*.{bm,bmp,gif,jpg,pbm,pgm,png,ppm,xbm,xpm,svg"
+#ifdef HAVE_LIBZ
+ ",svgz"
+#endif
+ "})",
+ oldname,1);
fl_file_chooser_ok_label(NULL);
ui_find_image_name = name;
Fluid_Image *ret = (name && *name) ? Fluid_Image::find(name) : 0;
diff --git a/src/Fl_SVG_Image.cxx b/src/Fl_SVG_Image.cxx
index 58dd48b34..ccecfc373 100644
--- a/src/Fl_SVG_Image.cxx
+++ b/src/Fl_SVG_Image.cxx
@@ -1,7 +1,7 @@
//
// SVG image code for the Fast Light Tool Kit (FLTK).
//
-// Copyright 2017 by Bill Spitzak and others.
+// Copyright 2017-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
@@ -23,6 +23,7 @@
#include <FL/fl_draw.H>
#include <FL/fl_string_functions.h>
#include "Fl_Screen_Driver.H"
+#include "Fl_System_Driver.H"
#include <stdio.h>
#include <stdlib.h>
@@ -56,16 +57,19 @@ static double strtoll(const char *str, char **endptr, int base) {
\param filename Name of a .svg or .svgz file, or NULL.
\param svg_data A pointer to the memory location of the SVG image data.
This parameter allows to load an SVG image from in-memory data, and is used when \p filename is NULL.
+ \param length When 0, indicates that \p svg_data contains SVG text, otherwise \p svg_data is
+ a buffer of \p length bytes containing GZ-compressed SVG data.
\note In-memory SVG data is parsed by the object constructor and is not used after construction.
+ When \p length > 0, parameter \p svg_data may safely be cast from data of type <em>const unsigned char *</em>.
*/
-Fl_SVG_Image::Fl_SVG_Image(const char *filename, const char *svg_data) : Fl_RGB_Image(NULL, 0, 0, 4) {
- init_(filename, svg_data, NULL);
+Fl_SVG_Image::Fl_SVG_Image(const char *filename, const char *svg_data, size_t length) : Fl_RGB_Image(NULL, 0, 0, 4) {
+ init_(filename, (const unsigned char *)svg_data, NULL, length);
}
// private constructor
Fl_SVG_Image::Fl_SVG_Image(const Fl_SVG_Image *source) : Fl_RGB_Image(NULL, 0, 0, 4) {
- init_(NULL, NULL, source);
+ init_(NULL, NULL, source, 0);
}
@@ -86,47 +90,63 @@ float Fl_SVG_Image::svg_scaling_(int W, int H) {
#if defined(HAVE_LIBZ)
-static char *svg_inflate(const char *fname) {
- FILE *in = fl_fopen(fname, "r");
- if (!in) return NULL;
- unsigned char header[2];
- if (fread(header, 2, 1, in) < 1) {
- fclose(in);
- return NULL;
- }
- int direct = (header[0] != 0x1f || header[1] != 0x8b);
- fseek(in, 0, SEEK_END);
- long size = ftell(in);
- fclose(in);
- int fd = fl_open_ext(fname, 1, 0);
- if (fd < 0) return NULL;
- gzFile gzf = gzdopen(fd, "r");
- if (!gzf) return NULL;
+/* Implementation note about decompression of svgz file or in-memory data.
+ It seems necessary to use the gzdopen()/gzread() API to inflate a gzip'ed
+ file or byte buffer. Writing the in-memory gzip'ed data to an anonymous pipe
+ and calling gzread() on the read end of this pipe is a solution for the in-memory case.
+ But non-blocking write to the pipe is needed to do that in the main thread,
+ and that seems impossible with Windows anonymous pipes.
+ Therefore, the anonymous pipe is handled in 2 ways:
+ 1) Under Windows, a child thread writes to the write end of the pipe and
+ the main thread reads from the read end with gzread().
+ 2) Under Posix systems, the write end of the pipe is made non-blocking
+ with a fcntl() call, and the main thread successively writes to the write
+ end and reads from the read end with gzread(). This allows to not have
+ libfltk_images requiring a threading library.
+ */
+
+static char *svg_inflate(gzFile gzf, // can be a file or the read end of a pipe
+ size_t size, // size of compressed data or of file
+ bool is_compressed, // true when file or byte buffer is gzip'ed
+ int fdwrite, // write end of pipe if >= 0
+ const unsigned char *bytes // byte buffer to write to pipe
+ ) {
+ size_t rest_bytes = size;
int l;
- long out_size = direct ? size + 1 : 3*size + 1;
+ size_t out_size = is_compressed ? 3 * size + 1 : size + 1;
char *out = (char*)malloc(out_size);
char *p = out;
do {
- if ((!direct) && p + size > out + out_size) {
+ if (is_compressed && p + size > out + out_size) {
out_size += size;
char *tmp = (char*)realloc(out, out_size + 1);
p = tmp + (p - out);
out = tmp;
}
+ if ( fdwrite >= 0 && Fl::system_driver()->write_nonblocking_fd(fdwrite, bytes, rest_bytes) ) {
+ free(out);
+ out = NULL;
+ is_compressed = false;
+ break;
+ }
+
l = gzread(gzf, p, size);
if (l > 0) {
p += l; *p = 0;
}
- } while ((!direct) && l >0);
+ } while (is_compressed && l >0);
gzclose(gzf);
- if (!direct) out = (char*)realloc(out, (p-out)+1);
+ if (is_compressed) out = (char*)realloc(out, (p-out)+1);
return out;
}
-#endif
-void Fl_SVG_Image::init_(const char *filename, const char *in_filedata, const Fl_SVG_Image *copy_source) {
+#endif // defined(HAVE_LIBZ)
+
+
+void Fl_SVG_Image::init_(const char *filename, const unsigned char *in_filedata, const Fl_SVG_Image *copy_source, size_t length) {
if (copy_source) {
- filename = in_filedata = NULL;
+ filename = NULL;
+ in_filedata = NULL;
counted_svg_image_ = copy_source->counted_svg_image_;
counted_svg_image_->ref_count++;
} else {
@@ -138,10 +158,35 @@ void Fl_SVG_Image::init_(const char *filename, const char *in_filedata, const Fl
to_desaturate_ = false;
average_weight_ = 1;
proportional = true;
- if (filename) {
+ bool is_compressed = true;
+
+ if (filename || length) { // process file or byte buffer
#if defined(HAVE_LIBZ)
- filedata = svg_inflate(filename);
-#else
+ int fdread, fdwrite = -1;
+ if (length) { // process gzip'ed byte buffer
+ // Pipe gzip'ed byte buffer into gzlib inflate algorithm.
+ // Under Windows, gzip'ed byte buffer is written to pipe by child thread.
+ // Under Posix, gzip'ed byte buffer is written to pipe by non-blocking write
+ // done by main thread.
+ Fl::system_driver()->pipe_support(fdread, fdwrite, in_filedata, length);
+ } else { // read or decompress a .svg or .svgz file
+ struct stat svg_file_stat;
+ fl_stat(filename, &svg_file_stat); // get file size
+ fdread = fl_open_ext(filename, 1, 0);
+ // read a possibly gzip'ed file and return result as char string
+ length = svg_file_stat.st_size;
+ is_compressed = (strcmp(filename + strlen(filename) - 5, ".svgz") == 0);
+ }
+ gzFile gzf = (fdread >= 0 ? gzdopen(fdread, "rb") : NULL);
+ if (gzf) {
+ filedata = svg_inflate(gzf, length, is_compressed, fdwrite, in_filedata);
+ } else {
+ if (fdread >= 0) Fl::system_driver()->close_fd(fdread);
+ if (fdwrite >= 0) Fl::system_driver()->close_fd(fdwrite);
+ }
+
+#else // ! HAVE_LIBZ
+ // without libz, read .svg file
FILE *fp = fl_fopen(filename, "rb");
if (fp) {
fseek(fp, 0, SEEK_END);
@@ -160,10 +205,12 @@ void Fl_SVG_Image::init_(const char *filename, const char *in_filedata, const Fl
}
#endif // HAVE_LIBZ
if (!filedata) ld(ERR_FILE_ACCESS);
- } else {
+ } else { // handle non-gzip'ed svg data as a char string
// XXX: Make internal copy -- nsvgParse() modifies filedata during parsing (!)
- filedata = in_filedata ? fl_strdup(in_filedata) : NULL;
+ filedata = in_filedata ? fl_strdup((const char *)in_filedata) : NULL;
}
+
+ // filedata is NULL or contains SVG data as a char string
if (filedata) {
counted_svg_image_->svg_image = nsvgParse(filedata, "px", 96);
free(filedata); // made with svg_inflate|malloc|strdup
diff --git a/src/Fl_System_Driver.H b/src/Fl_System_Driver.H
index bba6c77ff..b1901b133 100644
--- a/src/Fl_System_Driver.H
+++ b/src/Fl_System_Driver.H
@@ -221,6 +221,12 @@ public:
virtual void unlock_ring() {}
virtual double wait(double); // must override
virtual int ready() { return 0; } // must override
+ virtual int close_fd(int) {return -1;} // to close a file descriptor
+ // next 2 for support of Fl_SVG_Image
+ virtual int write_nonblocking_fd(int , const unsigned char *&, size_t &) {return 0;}
+ virtual void pipe_support(int &fdread, int &fdwrite, const unsigned char *, size_t ) {
+ fdread = fdwrite = -1;
+ }
};
#endif // FL_SYSTEM_DRIVER_H
diff --git a/src/drivers/Posix/Fl_Posix_System_Driver.H b/src/drivers/Posix/Fl_Posix_System_Driver.H
index df9407c17..5712f6be2 100644
--- a/src/drivers/Posix/Fl_Posix_System_Driver.H
+++ b/src/drivers/Posix/Fl_Posix_System_Driver.H
@@ -82,6 +82,10 @@ public:
virtual int dot_file_hidden() {return 1;}
virtual void gettime(time_t *sec, int *usec);
virtual char* strdup(const char *s) {return ::strdup(s);}
+ virtual int close_fd(int fd);
+ // next 2 for support of Fl_SVG_Image
+ virtual int write_nonblocking_fd(int , const unsigned char *&, size_t &);
+ virtual void pipe_support(int &, int &, const unsigned char *, size_t );
#if defined(HAVE_PTHREAD)
virtual void lock_ring();
virtual void unlock_ring();
diff --git a/src/drivers/Posix/Fl_Posix_System_Driver.cxx b/src/drivers/Posix/Fl_Posix_System_Driver.cxx
index dcf9e8f1a..ec43ea741 100644
--- a/src/drivers/Posix/Fl_Posix_System_Driver.cxx
+++ b/src/drivers/Posix/Fl_Posix_System_Driver.cxx
@@ -1,7 +1,7 @@
//
// Definition of Posix system driver (used by the X11, Wayland and macOS platforms).
//
-// Copyright 1998-2021 by Bill Spitzak and others.
+// Copyright 1998-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
@@ -308,6 +308,33 @@ bool Fl_Posix_System_Driver::probe_for_GTK(int major, int minor, void **p_ptr_gt
#endif // HAVE_DLSYM && HAVE_DLFCN_H
+int Fl_Posix_System_Driver::close_fd(int fd) { return close(fd); }
+
+int Fl_Posix_System_Driver::write_nonblocking_fd(int fdwrite, const unsigned char *&bytes, size_t &rest_bytes) {
+ if (rest_bytes > 0) {
+ ssize_t nw = write(fdwrite, bytes, rest_bytes);
+ if (nw == -1) {
+ close(fdwrite);
+ return 1; // error
+ }
+ bytes += nw;
+ rest_bytes -= nw;
+ if (rest_bytes == 0) close(fdwrite);
+ }
+ return 0; // success
+}
+
+void Fl_Posix_System_Driver::pipe_support(int &fdread, int &fdwrite, const unsigned char *unused, size_t unused_s) {
+ int fds[2];
+ if (pipe(fds)) { // create anonymous pipe
+ Fl_System_Driver::pipe_support(fdread, fdwrite, NULL, 0); // indicates error
+ } else {
+ fdread = fds[0];
+ fdwrite = fds[1];
+ fcntl(fdwrite, F_SETFL, O_NONBLOCK); // make pipe's write end non-blocking
+ }
+}
+
////////////////////////////////////////////////////////////////
// POSIX threading...
#if defined(HAVE_PTHREAD)
diff --git a/src/drivers/WinAPI/Fl_WinAPI_System_Driver.H b/src/drivers/WinAPI/Fl_WinAPI_System_Driver.H
index 730d9320d..d05b580d6 100644
--- a/src/drivers/WinAPI/Fl_WinAPI_System_Driver.H
+++ b/src/drivers/WinAPI/Fl_WinAPI_System_Driver.H
@@ -1,7 +1,7 @@
//
// Windows system driver for the Fast Light Tool Kit (FLTK).
//
-// Copyright 2010-2021 by Bill Spitzak and others.
+// Copyright 2010-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
@@ -114,6 +114,8 @@ public:
virtual void unlock_ring();
virtual double wait(double time_to_wait);
virtual int ready();
+ virtual void pipe_support(int &, int &, const unsigned char *, size_t );
+ virtual int close_fd(int fd);
};
#endif // FL_WINAPI_SYSTEM_DRIVER_H
diff --git a/src/drivers/WinAPI/Fl_WinAPI_System_Driver.cxx b/src/drivers/WinAPI/Fl_WinAPI_System_Driver.cxx
index 237243975..cdb0805eb 100644
--- a/src/drivers/WinAPI/Fl_WinAPI_System_Driver.cxx
+++ b/src/drivers/WinAPI/Fl_WinAPI_System_Driver.cxx
@@ -1,7 +1,7 @@
//
// Definition of Windows system driver for the Fast Light Tool Kit (FLTK).
//
-// Copyright 1998-2021 by Bill Spitzak and others.
+// Copyright 1998-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
@@ -1026,3 +1026,55 @@ void Fl_WinAPI_System_Driver::unlock() {
void Fl_WinAPI_System_Driver::awake(void* msg) {
PostThreadMessage( main_thread, fl_wake_msg, (WPARAM)msg, 0);
}
+
+// create anonymous pipe in the form of 2 unix-style file descriptors
+static void pipe_win32(int fds[2]) {
+ HANDLE read_h, write_h;
+ SECURITY_ATTRIBUTES sa;
+ sa.nLength = sizeof(sa);
+ sa.lpSecurityDescriptor = NULL;
+ sa.bInheritHandle = true;
+ if (CreatePipe(&read_h, &write_h, &sa, 0) == 0) {
+ fds[0] = fds[1] = -1; // indicates error
+ } else { // create unix-style file descriptors from handles
+ fds[0] = _open_osfhandle((fl_intptr_t)read_h, _O_RDONLY | _O_BINARY);
+ fds[1] = _open_osfhandle((fl_intptr_t)write_h, _O_WRONLY | _O_BINARY);
+ }
+}
+
+struct gunz_data { // data transmitted to thread
+ const unsigned char *data;
+ unsigned length;
+ int fd;
+};
+
+static void __cdecl write_func(struct gunz_data *pdata) { // will run in child thread
+ //const unsigned char *from = pdata->data;
+ while (pdata->length > 0) {
+ int done = _write(pdata->fd, pdata->data, pdata->length);
+ if (done == -1) break;
+ pdata->length -= done;
+ pdata->data += done;
+ }
+ _close(pdata->fd);
+ free(pdata);
+}
+
+int Fl_WinAPI_System_Driver::close_fd(int fd) {
+ return _close(fd);
+}
+
+void Fl_WinAPI_System_Driver::pipe_support(int &fdread, int &fdwrite, const unsigned char *bytes, size_t length) {
+ int fds[2];
+ pipe_win32(fds); // create anonymous pipe
+ if (fds[0] == -1) return Fl_System_Driver::pipe_support(fdread, fdwrite, NULL, 0);
+ fdread = fds[0];
+ fdwrite = -1;
+ // prepare data transmitted to child thread
+ struct gunz_data *thread_data = (struct gunz_data*)malloc(sizeof(struct gunz_data));
+ thread_data->data = bytes;
+ thread_data->length = (unsigned)length;
+ thread_data->fd = fds[1];
+ // launch child thread that will write byte buffer to pipe's write end
+ _beginthread((void( __cdecl * )( void * ))write_func, 0, thread_data);
+}