diff options
| author | ManoloFLTK <41016272+ManoloFLTK@users.noreply.github.com> | 2022-11-15 10:09:01 +0100 |
|---|---|---|
| committer | ManoloFLTK <41016272+ManoloFLTK@users.noreply.github.com> | 2022-11-15 10:09:01 +0100 |
| commit | 7f8f7c5b851f4e15cf95c6e819bff284b7fda7ca (patch) | |
| tree | 48a51de221358f977c494a955b73ceca6cd679be /src | |
| parent | 433a8e71e81c51a96f1d617fae5a3a4aac4efaeb (diff) | |
Add support of .svgz image files to fluid
The prototype of the public Fl_SVG_Image constructor is expanded
to allow construction from in-memory, gzip'ed binary data.
Diffstat (limited to 'src')
| -rw-r--r-- | src/Fl_SVG_Image.cxx | 111 | ||||
| -rw-r--r-- | src/Fl_System_Driver.H | 6 | ||||
| -rw-r--r-- | src/drivers/Posix/Fl_Posix_System_Driver.H | 4 | ||||
| -rw-r--r-- | src/drivers/Posix/Fl_Posix_System_Driver.cxx | 29 | ||||
| -rw-r--r-- | src/drivers/WinAPI/Fl_WinAPI_System_Driver.H | 4 | ||||
| -rw-r--r-- | src/drivers/WinAPI/Fl_WinAPI_System_Driver.cxx | 54 |
6 files changed, 173 insertions, 35 deletions
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); +} |
