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.
This commit is contained in:
parent
433a8e71e8
commit
7f8f7c5b85
@ -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 {
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user