SVG is now decompressed in memory Bitmap invalid array length handling to return an error RGB Image data reader to return error if image data is too short FLUID: Add size argument to bitmap and JPEG data
This commit is contained in:
parent
08f6741d7b
commit
12da87ba0c
@ -43,12 +43,16 @@ private:
|
||||
int cache_w_, cache_h_; // size of bitmap when cached
|
||||
|
||||
public:
|
||||
/** The constructors create a new bitmap from the specified bitmap data */
|
||||
/** The constructors create a new bitmap from the specified bitmap data.
|
||||
\see Fl_Bitmap(const uchar *bits, int bits_length, int W, int H) */
|
||||
Fl_Bitmap(const uchar *bits, int W, int H) :
|
||||
Fl_Image(W,H,0), array(bits), alloc_array(0), id_(0), cache_w_(0),cache_h_(0) {data((const char **)&array, 1);}
|
||||
/** The constructors create a new bitmap from the specified bitmap data */
|
||||
/** The constructors create a new bitmap from the specified bitmap data.
|
||||
\see Fl_Bitmap(const char *bits, int bits_length, int W, int H) */
|
||||
Fl_Bitmap(const char *bits, int W, int H) :
|
||||
Fl_Image(W,H,0), array((const uchar *)bits), alloc_array(0), id_(0), cache_w_(0),cache_h_(0) {data((const char **)&array, 1);}
|
||||
Fl_Bitmap(const uchar *bits, int bits_length, int W, int H);
|
||||
Fl_Bitmap(const char *bits, int bits_length, int W, int H);
|
||||
virtual ~Fl_Bitmap();
|
||||
virtual Fl_Image *copy(int W, int H) const;
|
||||
Fl_Image *copy() const { return Fl_Image::copy(); }
|
||||
|
@ -60,9 +60,10 @@ enum Fl_RGB_Scaling {
|
||||
class FL_EXPORT Fl_Image {
|
||||
friend class Fl_Graphics_Driver;
|
||||
public:
|
||||
static const int ERR_NO_IMAGE = -1;
|
||||
static const int ERR_FILE_ACCESS = -2;
|
||||
static const int ERR_FORMAT = -3;
|
||||
static const int ERR_NO_IMAGE = -1;
|
||||
static const int ERR_FILE_ACCESS = -2;
|
||||
static const int ERR_FORMAT = -3;
|
||||
static const int ERR_MEMORY_ACCESS = -4;
|
||||
|
||||
private:
|
||||
int w_, h_, d_, ld_, count_;
|
||||
@ -353,6 +354,7 @@ private:
|
||||
public:
|
||||
|
||||
Fl_RGB_Image(const uchar *bits, int W, int H, int D=3, int LD=0);
|
||||
Fl_RGB_Image(const uchar *bits, int bits_length, int W, int H, int D, int LD);
|
||||
Fl_RGB_Image(const Fl_Pixmap *pxm, Fl_Color bg=FL_GRAY);
|
||||
virtual ~Fl_RGB_Image();
|
||||
virtual Fl_Image *copy(int W, int H) const;
|
||||
|
@ -32,11 +32,11 @@ class FL_EXPORT Fl_JPEG_Image : public Fl_RGB_Image {
|
||||
public:
|
||||
|
||||
Fl_JPEG_Image(const char *filename);
|
||||
Fl_JPEG_Image(const char *name, const unsigned char *data);
|
||||
Fl_JPEG_Image(const char *name, const unsigned char *data, int data_length=-1);
|
||||
|
||||
protected:
|
||||
|
||||
void load_jpg_(const char *filename, const char *sharename, const unsigned char *data);
|
||||
void load_jpg_(const char *filename, const char *sharename, const unsigned char *data, int data_length=-1);
|
||||
|
||||
};
|
||||
|
||||
|
@ -147,14 +147,15 @@ 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 unsigned char *filedata, const Fl_SVG_Image *copy_source,
|
||||
size_t length);
|
||||
void init_(const char *name, const unsigned char *filedata, 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, size_t length = 0);
|
||||
Fl_SVG_Image(const char *filename);
|
||||
Fl_SVG_Image(const char *sharedname, const char *svg_data);
|
||||
Fl_SVG_Image(const char *sharedname, const unsigned char *svg_data, size_t length);
|
||||
virtual ~Fl_SVG_Image();
|
||||
virtual Fl_Image *copy(int W, int H) const;
|
||||
Fl_Image *copy() const {
|
||||
|
@ -94,6 +94,7 @@ class FL_EXPORT Fl_Shared_Image : public Fl_Image {
|
||||
|
||||
friend class Fl_JPEG_Image;
|
||||
friend class Fl_PNG_Image;
|
||||
friend class Fl_SVG_Image;
|
||||
friend class Fl_Graphics_Driver;
|
||||
|
||||
protected:
|
||||
|
@ -94,7 +94,7 @@ void Fluid_Image::write_static() {
|
||||
write_c("static const unsigned char %s[] =\n", idata_name);
|
||||
write_cdata(img->data()[0], ((img->w() + 7) / 8) * img->h());
|
||||
write_c(";\n");
|
||||
write_initializer( "Fl_Bitmap", "%s, %d, %d", idata_name, img->w(), img->h());
|
||||
write_initializer( "Fl_Bitmap", "%s, %d, %d, %d", idata_name, ((img->w() + 7) / 8) * img->h(), img->w(), img->h());
|
||||
} else if (strcmp(fl_filename_ext(name()), ".jpg")==0) {
|
||||
// Write jpeg image data...
|
||||
write_c("\n");
|
||||
@ -104,6 +104,7 @@ void Fluid_Image::write_static() {
|
||||
}
|
||||
write_c("static const unsigned char %s[] =\n", idata_name);
|
||||
|
||||
size_t nData = 0;
|
||||
enter_project_dir();
|
||||
FILE *f = fl_fopen(name(), "rb");
|
||||
leave_project_dir();
|
||||
@ -111,7 +112,7 @@ void Fluid_Image::write_static() {
|
||||
write_file_error("JPEG");
|
||||
} 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);
|
||||
@ -123,7 +124,7 @@ void Fluid_Image::write_static() {
|
||||
}
|
||||
|
||||
write_c(";\n");
|
||||
write_initializer("Fl_JPEG_Image", "\"%s\", %s", fl_filename_name(name()), idata_name);
|
||||
write_initializer("Fl_JPEG_Image", "\"%s\", %s, %d", fl_filename_name(name()), idata_name, nData);
|
||||
} 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...
|
||||
@ -160,9 +161,9 @@ void Fluid_Image::write_static() {
|
||||
|
||||
write_c(";\n");
|
||||
if (gzipped)
|
||||
write_initializer("Fl_SVG_Image", "NULL, (const char*)%s, %ld", idata_name, nData);
|
||||
write_initializer("Fl_SVG_Image", "\"%s\", (const char*)%s, %ld", fl_filename_name(name()), idata_name, nData);
|
||||
else
|
||||
write_initializer("Fl_SVG_Image", "NULL, %s", idata_name);
|
||||
write_initializer("Fl_SVG_Image", "\"%s\", %s", fl_filename_name(name()), idata_name);
|
||||
} else {
|
||||
// Write image data...
|
||||
write_c("\n");
|
||||
@ -188,15 +189,21 @@ void Fluid_Image::write_file_error(const char *fmt) {
|
||||
void Fluid_Image::write_initializer(const char *type_name, const char *format, ...) {
|
||||
/* Outputs code that returns (and initializes if needed) an Fl_Image as follows:
|
||||
static Fl_Image *'function_name_'() {
|
||||
static Fl_Image *image = new 'type_name'('product of format and remaining args');
|
||||
static Fl_Image *image = NULL;
|
||||
if (!image)
|
||||
image = new 'type_name'('product of format and remaining args');
|
||||
return image;
|
||||
} */
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
write_c("static Fl_Image *%s() {\n%sstatic Fl_Image *image = new %s(",
|
||||
function_name_, indent(1), type_name);
|
||||
write_c("static Fl_Image *%s() {\n", function_name_);
|
||||
write_c("%sstatic Fl_Image *image = NULL;\n", indent(1));
|
||||
write_c("%sif (!image)\n", indent(1));
|
||||
write_c("%simage = new %s(", indent(2), type_name);
|
||||
vwrite_c(format, ap);
|
||||
write_c(");\n%sreturn image;\n}\n", indent(1));
|
||||
write_c(");\n");
|
||||
write_c("%sreturn image;\n", indent(1));
|
||||
write_c("}\n");
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
@ -287,9 +294,15 @@ void Fluid_Image::decrement() {
|
||||
Fluid_Image::~Fluid_Image() {
|
||||
int a;
|
||||
if (images) {
|
||||
for (a = 0;; a++) if (images[a] == this) break;
|
||||
numimages--;
|
||||
for (; a < numimages; a++) images[a] = images[a+1];
|
||||
for (a = 0; a<numimages; a++) {
|
||||
if (images[a] == this) {
|
||||
numimages--;
|
||||
for (; a < numimages; a++) {
|
||||
images[a] = images[a+1];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (img) img->release();
|
||||
free((void*)name_);
|
||||
|
@ -26,11 +26,75 @@
|
||||
#include <FL/Fl_Menu_Item.H>
|
||||
#include <FL/Fl_Bitmap.H>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
void Fl_Bitmap::draw(int XP, int YP, int WP, int HP, int cx, int cy) {
|
||||
fl_graphics_driver->draw_bitmap(this, XP, YP, WP, HP, cx, cy);
|
||||
}
|
||||
|
||||
|
||||
/** The constructors create a new bitmap from the specified bitmap data.
|
||||
If the provided array is too small to contain all the image data, the
|
||||
constructor will not generate the bitmap to avoid illegal memory read
|
||||
access and instead set \c data to NULL and \c ld to \c ERR_MEMORY_ACCESS.
|
||||
\param bits bitmap data, one pixel per bit, rows are rounded to the next byte
|
||||
\param bit_length length of the \p bits array in bytes
|
||||
\param W image width in pixels
|
||||
\param H image height in pixels
|
||||
\see Fl_Bitmap(const char *bits, int bits_length, int W, int H),
|
||||
Fl_Bitmap(const uchar *bits, int W, int H)
|
||||
*/
|
||||
Fl_Bitmap::Fl_Bitmap(const uchar *bits, int bits_length, int W, int H) :
|
||||
Fl_Image(W,H,0),
|
||||
array((const uchar *)bits),
|
||||
alloc_array(0),
|
||||
id_(0),
|
||||
cache_w_(0),
|
||||
cache_h_(0)
|
||||
{
|
||||
int rowBytes = (W+7)>>3;
|
||||
int min_length = rowBytes * H;
|
||||
if (bits_length >= min_length) {
|
||||
data((const char **)&array, 1);
|
||||
} else {
|
||||
array = NULL;
|
||||
data(NULL, 0);
|
||||
ld(ERR_MEMORY_ACCESS);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** The constructors create a new bitmap from the specified bitmap data.
|
||||
If the provided array is too small to contain all the image data, the
|
||||
constructor will not generate the bitmap to avoid illegal memory read
|
||||
access and instead set \c data to NULL and \c ld to \c ERR_MEMORY_ACCESS.
|
||||
\param bits bitmap data, one pixel per bit, rows are rounded to the next byte
|
||||
\param bit_length length of the \p bits array in bytes
|
||||
\param W image width in pixels
|
||||
\param H image height in pixels
|
||||
\see Fl_Bitmap(const uchar *bits, int bits_length, int W, int H),
|
||||
Fl_Bitmap(const char *bits, int W, int H)
|
||||
*/
|
||||
Fl_Bitmap::Fl_Bitmap(const char *bits, int bits_length, int W, int H) :
|
||||
Fl_Image(W,H,0),
|
||||
array((const uchar *)bits),
|
||||
alloc_array(0),
|
||||
id_(0),
|
||||
cache_w_(0),
|
||||
cache_h_(0)
|
||||
{
|
||||
int rowBytes = (W+7)>>3;
|
||||
int min_length = rowBytes * H;
|
||||
if (bits_length >= min_length) {
|
||||
data((const char **)&array, 1);
|
||||
} else {
|
||||
array = NULL;
|
||||
data(NULL, 0);
|
||||
ld(ERR_MEMORY_ACCESS);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
The destructor frees all memory and server resources that are used by
|
||||
the bitmap.
|
||||
|
@ -22,6 +22,8 @@
|
||||
#include <FL/Fl_Image.H>
|
||||
#include "flstring.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
void fl_restore_clip(); // from fl_rect.cxx
|
||||
|
||||
//
|
||||
@ -187,11 +189,12 @@ void Fl_Image::label(Fl_Menu_Item* m) {
|
||||
box.image(jpg);
|
||||
\endcode
|
||||
|
||||
\returns Image load failure if non-zero
|
||||
\retval 0 the image was loaded successfully
|
||||
\retval ERR_NO_IMAGE no image was found
|
||||
\retval ERR_FILE_ACCESS there was a file access related error (errno should be set)
|
||||
\retval ERR_FORMAT image decoding failed
|
||||
\returns Image load failure if non-zero
|
||||
\retval 0 the image was loaded successfully
|
||||
\retval ERR_NO_IMAGE no image was found
|
||||
\retval ERR_FILE_ACCESS there was a file access related error (errno should be set)
|
||||
\retval ERR_FORMAT image decoding failed
|
||||
\retval ERR_MEMORY_ACCESS image decoder tried to access memory outside of given memory block
|
||||
*/
|
||||
int Fl_Image::fail() const {
|
||||
// if no image exists, ld_ may contain a simple error code
|
||||
@ -377,6 +380,45 @@ Fl_RGB_Image::Fl_RGB_Image(const uchar *bits, int W, int H, int D, int LD) :
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
The constructor creates a new image from the specified data.
|
||||
|
||||
If the provided array is too small to contain all the image data, the
|
||||
constructor will not generate the image to avoid illegal memory read
|
||||
access and instead set \c data to NULL and \c ld to \c ERR_MEMORY_ACCESS.
|
||||
|
||||
\param bits image data
|
||||
\param bit_length length of the \p bits array in bytes
|
||||
\param W image width in pixels
|
||||
\param H image height in pixels
|
||||
\param D image depth in bytes, 1 for gray scale, 2 for gray with alpha,
|
||||
3 for RGB, and 4 for RGB plus alpha
|
||||
\param LD line length in bytes, or 0 to use W*D.
|
||||
|
||||
\see Fl_RGB_Image(const uchar *bits, int W, int H, int D, int LD)
|
||||
*/
|
||||
Fl_RGB_Image::Fl_RGB_Image(const uchar *bits, int bits_length, int W, int H, int D, int LD) :
|
||||
Fl_Image(W,H,D),
|
||||
array(bits),
|
||||
alloc_array(0),
|
||||
id_(0),
|
||||
mask_(0),
|
||||
cache_w_(0), cache_h_(0)
|
||||
{
|
||||
if (D == 0) D = 3;
|
||||
if (LD == 0) LD = W*D;
|
||||
int min_length = LD*(H-1) + W*D;
|
||||
if (bits_length >= min_length) {
|
||||
data((const char **)&array, 1);
|
||||
ld(LD);
|
||||
} else {
|
||||
array = NULL;
|
||||
data(NULL, 0);
|
||||
ld(ERR_MEMORY_ACCESS);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
The constructor creates a new RGBA image from the specified Fl_Pixmap.
|
||||
|
||||
|
@ -123,14 +123,16 @@ Fl_JPEG_Image::Fl_JPEG_Image(const char *filename)
|
||||
|
||||
\param name A unique name or NULL
|
||||
\param data A pointer to the memory location of the JPEG image
|
||||
\param data_length optional length of \c data. This will protect memory outside
|
||||
of the \c data array from illegal read operations
|
||||
|
||||
\see Fl_JPEG_Image::Fl_JPEG_Image(const char *filename)
|
||||
\see Fl_Shared_Image
|
||||
*/
|
||||
Fl_JPEG_Image::Fl_JPEG_Image(const char *name, const unsigned char *data)
|
||||
Fl_JPEG_Image::Fl_JPEG_Image(const char *name, const unsigned char *data, int data_length)
|
||||
: Fl_RGB_Image(0,0,0)
|
||||
{
|
||||
load_jpg_(0L, name, data);
|
||||
load_jpg_(0L, name, data, data_length);
|
||||
}
|
||||
|
||||
|
||||
@ -188,7 +190,7 @@ extern "C" {
|
||||
|
||||
} // extern "C"
|
||||
|
||||
static void jpeg_mem_src(j_decompress_ptr cinfo, const unsigned char *data)
|
||||
static void jpeg_unprotected_mem_src(j_decompress_ptr cinfo, const unsigned char *data)
|
||||
{
|
||||
my_src_ptr src = (my_source_mgr*)malloc(sizeof(my_source_mgr));
|
||||
cinfo->src = &(src->pub);
|
||||
@ -209,9 +211,9 @@ static void jpeg_mem_src(j_decompress_ptr cinfo, const unsigned char *data)
|
||||
This method reads JPEG image data and creates an RGB or grayscale image.
|
||||
To avoid code duplication, we set filename if we want to read form a file or
|
||||
data to read from memory instead. Sharename can be set if the image is
|
||||
supposed to be added to teh Fl_Shared_Image list.
|
||||
supposed to be added to the Fl_Shared_Image list.
|
||||
*/
|
||||
void Fl_JPEG_Image::load_jpg_(const char *filename, const char *sharename, const unsigned char *data)
|
||||
void Fl_JPEG_Image::load_jpg_(const char *filename, const char *sharename, const unsigned char *data, int data_length)
|
||||
{
|
||||
#ifdef HAVE_LIBJPEG
|
||||
jpeg_decompress_struct dinfo; // Decompressor info
|
||||
@ -299,7 +301,10 @@ void Fl_JPEG_Image::load_jpg_(const char *filename, const char *sharename, const
|
||||
if (*fp) {
|
||||
jpeg_stdio_src(&dinfo, *fp);
|
||||
} else {
|
||||
jpeg_mem_src(&dinfo, data);
|
||||
if (data_length==-1)
|
||||
jpeg_unprotected_mem_src(&dinfo, data);
|
||||
else
|
||||
jpeg_mem_src(&dinfo, data, (size_t)data_length);
|
||||
}
|
||||
jpeg_read_header(&dinfo, TRUE);
|
||||
|
||||
|
@ -19,6 +19,7 @@
|
||||
#if defined(FLTK_USE_SVG) || defined(FL_DOXYGEN)
|
||||
|
||||
#include <FL/Fl_SVG_Image.H>
|
||||
#include <FL/Fl_Shared_Image.H>
|
||||
#include <FL/fl_utf8.h>
|
||||
#include <FL/fl_draw.H>
|
||||
#include <FL/fl_string_functions.h>
|
||||
@ -53,23 +54,76 @@ static double strtoll(const char *str, char **endptr, int base) {
|
||||
#include <zlib.h>
|
||||
#endif
|
||||
|
||||
/** The constructor loads the SVG image from the given .svg/.svgz filename or in-memory data.
|
||||
\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>.
|
||||
|
||||
/** Load an SVG image from a file.
|
||||
|
||||
This constructor loads the SVG image from a .svg or .svgz file. The reader
|
||||
recognizes if the data is compressed, and decompresses it if zlib is available
|
||||
(HAVE_LIBZ).
|
||||
|
||||
\param filename the filename for a .svg or .svgz file
|
||||
*/
|
||||
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);
|
||||
Fl_SVG_Image::Fl_SVG_Image(const char *filename) :
|
||||
Fl_RGB_Image(NULL, 0, 0, 4)
|
||||
{
|
||||
init_(filename, NULL, 0);
|
||||
}
|
||||
|
||||
|
||||
/** Load an SVG image from memory.
|
||||
|
||||
This constructor loads the SVG image from a block of memory. This version is
|
||||
commonly used for uncompressed text data, but the reader recognizes if the data
|
||||
is compressed, and decompresses it if zlib is available (HAVE_LIBZ).
|
||||
|
||||
\param sharedname if not \c NULL, a shared image will be generated with this name
|
||||
\param svg_data a pointer to the memory location of the SVG image data
|
||||
|
||||
\note In-memory SVG data is parsed by the object constructor and is no longer
|
||||
needed after construction.
|
||||
*/
|
||||
Fl_SVG_Image::Fl_SVG_Image(const char *sharedname, const char *svg_data) :
|
||||
Fl_RGB_Image(NULL, 0, 0, 4)
|
||||
{
|
||||
init_(sharedname, (const unsigned char*)svg_data, 0);
|
||||
}
|
||||
|
||||
|
||||
/** Load an SVG image from memory.
|
||||
|
||||
This constructor loads the SVG image from a block of memory. This version is
|
||||
commonly used for compressed binary data, but the reader recognizes if the data
|
||||
is uncompressed, and reads it as a text block.
|
||||
|
||||
\param sharedname if not \c NULL, a shared image will be generated with this name
|
||||
\param svg_data a pointer to the memory location of the SVG image data
|
||||
\param length optional length of \p svg_data or \c 0. This will protect memory
|
||||
outside of the \p svg_data array from illegal read operations for
|
||||
compressed SVG data
|
||||
|
||||
\note In-memory SVG data is parsed by the object constructor and is no longer
|
||||
needed after construction.
|
||||
*/
|
||||
Fl_SVG_Image::Fl_SVG_Image(const char *name, const unsigned char *svg_data, size_t length) :
|
||||
Fl_RGB_Image(NULL, 0, 0, 4)
|
||||
{
|
||||
init_(name, svg_data, 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, 0);
|
||||
Fl_SVG_Image::Fl_SVG_Image(const Fl_SVG_Image *source) :
|
||||
Fl_RGB_Image(NULL, 0, 0, 4)
|
||||
{
|
||||
counted_svg_image_ = source->counted_svg_image_;
|
||||
counted_svg_image_->ref_count++;
|
||||
to_desaturate_ = false;
|
||||
average_weight_ = 1;
|
||||
proportional = true;
|
||||
w(source->w());
|
||||
h(source->h());
|
||||
rasterized_ = false;
|
||||
raster_w_ = raster_h_ = 0;
|
||||
}
|
||||
|
||||
|
||||
@ -90,143 +144,182 @@ float Fl_SVG_Image::svg_scaling_(int W, int H) {
|
||||
|
||||
#if defined(HAVE_LIBZ)
|
||||
|
||||
/* 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.
|
||||
*/
|
||||
// Decompress gzip data in memory
|
||||
#define CHUNK_SIZE (2048)
|
||||
static int svg_inflate(uchar *src, size_t src_length, uchar *&dst, size_t &dst_length) {
|
||||
// allocate space for decompressed data in chunks
|
||||
typedef struct Chunk {
|
||||
Chunk() { next = NULL; }
|
||||
struct Chunk *next;
|
||||
uchar data[CHUNK_SIZE];
|
||||
} Chunk;
|
||||
Chunk *first = NULL;
|
||||
Chunk *chunk = NULL, *next_chunk;
|
||||
|
||||
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;
|
||||
size_t out_size = is_compressed ? 3 * size + 1 : size + 1;
|
||||
char *out = (char*)malloc(out_size);
|
||||
char *p = out;
|
||||
z_stream stream = { };
|
||||
int err = Z_OK;
|
||||
const uInt max = (uInt)-1;
|
||||
|
||||
dst = 0;
|
||||
dst_length = 0;
|
||||
|
||||
stream.next_in = (z_const Bytef *)src;
|
||||
stream.avail_in = 0;
|
||||
stream.zalloc = (alloc_func)0;
|
||||
stream.zfree = (free_func)0;
|
||||
stream.opaque = (voidpf)0;
|
||||
|
||||
// initialize zlib for inflating compressed data
|
||||
err = inflateInit2(&stream, 31);
|
||||
if (err != Z_OK) return err;
|
||||
gz_header header;
|
||||
err = inflateGetHeader(&stream, &header);
|
||||
if (err != Z_OK) return err;
|
||||
|
||||
stream.avail_out = 0;
|
||||
stream.avail_in = (uInt)(src_length ? src_length : -1);
|
||||
|
||||
// inflate into as many chunks as needed
|
||||
do {
|
||||
if (is_compressed && p + size > out + out_size) {
|
||||
out_size += size;
|
||||
size_t delta = (p - out);
|
||||
out = (char*)realloc(out, out_size + 1);
|
||||
p = out + delta;
|
||||
}
|
||||
if ( fdwrite >= 0 && Fl::system_driver()->write_nonblocking_fd(fdwrite, bytes, rest_bytes) ) {
|
||||
free(out);
|
||||
out = NULL;
|
||||
is_compressed = false;
|
||||
break;
|
||||
if (stream.avail_out == 0) {
|
||||
next_chunk = new Chunk;
|
||||
if (!first) first = next_chunk; else chunk->next = next_chunk;
|
||||
chunk = next_chunk;
|
||||
stream.avail_out = CHUNK_SIZE;
|
||||
stream.next_out = chunk->data;
|
||||
}
|
||||
err = inflate(&stream, Z_NO_FLUSH);
|
||||
} while (err == Z_OK);
|
||||
|
||||
l = gzread(gzf, p, (unsigned int)size);
|
||||
if (l > 0) {
|
||||
p += l; *p = 0;
|
||||
inflateEnd(&stream);
|
||||
|
||||
// copy chunk data into a new continuous data block
|
||||
if (err == Z_STREAM_END) {
|
||||
size_t nn = dst_length = stream.total_out;
|
||||
dst = (uchar*)malloc(dst_length+1); // leave room for a trailing NUL
|
||||
uchar *d = dst;
|
||||
chunk = first;
|
||||
while (chunk && nn>0) {
|
||||
size_t n = nn > CHUNK_SIZE ? CHUNK_SIZE : nn;
|
||||
memcpy(d, chunk->data, n);
|
||||
d += n;
|
||||
nn -= n;
|
||||
chunk = chunk->next;
|
||||
}
|
||||
} while (is_compressed && l >0);
|
||||
gzclose(gzf);
|
||||
if (is_compressed) out = (char*)realloc(out, (p-out)+1);
|
||||
return out;
|
||||
}
|
||||
|
||||
// delete all the chunks that we allocated
|
||||
chunk = first;
|
||||
while (chunk) {
|
||||
next_chunk = chunk->next;
|
||||
delete chunk;
|
||||
chunk = next_chunk;
|
||||
}
|
||||
|
||||
return (err == Z_STREAM_END)
|
||||
? Z_OK
|
||||
: (err == Z_NEED_DICT)
|
||||
? Z_DATA_ERROR
|
||||
: ((err == Z_BUF_ERROR) && stream.avail_out)
|
||||
? Z_DATA_ERROR
|
||||
: err;
|
||||
}
|
||||
|
||||
|
||||
#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 = NULL;
|
||||
in_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;
|
||||
}
|
||||
char *filedata = NULL;
|
||||
void Fl_SVG_Image::init_(const char *name, const unsigned char *in_data, size_t length) {
|
||||
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;
|
||||
bool is_compressed = true;
|
||||
|
||||
if (filename || length) { // process file or byte buffer
|
||||
#if defined(HAVE_LIBZ)
|
||||
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);
|
||||
}
|
||||
// yes, this is a const cast to avoid duplicating user supplied data
|
||||
uchar *data = const_cast<uchar*>(in_data); // 🤨 careful with this, don't overwrite user supplied data in nsvgParse()
|
||||
|
||||
#else // ! HAVE_LIBZ
|
||||
// without libz, read .svg file
|
||||
FILE *fp = fl_fopen(filename, "rb");
|
||||
if (fp) {
|
||||
fseek(fp, 0, SEEK_END);
|
||||
long 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);
|
||||
}
|
||||
#endif // HAVE_LIBZ
|
||||
if (!filedata) ld(ERR_FILE_ACCESS);
|
||||
} 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((const char *)in_filedata) : NULL;
|
||||
}
|
||||
// this is to make it clear what we are doing
|
||||
const char *sharedname = data ? name : NULL;
|
||||
const char *filename = data ? NULL : name;
|
||||
|
||||
// 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
|
||||
if (counted_svg_image_->svg_image->width == 0 || counted_svg_image_->svg_image->height == 0) {
|
||||
d(-1);
|
||||
ld(ERR_FORMAT);
|
||||
} else {
|
||||
w(int(counted_svg_image_->svg_image->width + 0.5));
|
||||
h(int(counted_svg_image_->svg_image->height + 0.5));
|
||||
}
|
||||
} else if (copy_source) {
|
||||
w(copy_source->w());
|
||||
h(copy_source->h());
|
||||
}
|
||||
// prepare with error data, so we can just return if an error occurs
|
||||
d(-1);
|
||||
ld(ERR_FORMAT);
|
||||
rasterized_ = false;
|
||||
raster_w_ = raster_h_ = 0;
|
||||
|
||||
// if we are reading from a file, just read the entire file into a memory block
|
||||
if (!data) {
|
||||
FILE *f = fl_fopen(filename, "rb");
|
||||
if (f) {
|
||||
fseek(f, 0, SEEK_END);
|
||||
length = ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
data = (uchar*)malloc(length+1);
|
||||
if (data) {
|
||||
if (fread((void*)data, 1, length, f) == length) {
|
||||
data[length] = 0;
|
||||
} else {
|
||||
free((void*)data);
|
||||
data = NULL;
|
||||
}
|
||||
}
|
||||
fclose(f);
|
||||
}
|
||||
if (!data) return;
|
||||
}
|
||||
|
||||
// now if our data is compressed, we use zlib to infalte it
|
||||
if (length==0 || length>10) {
|
||||
if (data[0] == 0x1f && data[1] == 0x8b) {
|
||||
#if defined(HAVE_LIBZ)
|
||||
// this is gzip compressed data, so we decompress it and preplace the data array
|
||||
uchar *uncompressed_data = NULL;
|
||||
size_t uncompressed_data_length = 0;
|
||||
int err = svg_inflate(data, length, uncompressed_data, uncompressed_data_length);
|
||||
if (err == Z_OK) {
|
||||
// replace compressed data with uncompressed data
|
||||
if (in_data == NULL) free(data);
|
||||
length = (size_t)uncompressed_data_length;
|
||||
data = (uchar*)uncompressed_data;
|
||||
data[length] = 0;
|
||||
} else {
|
||||
if (in_data != data) free(data);
|
||||
return;
|
||||
}
|
||||
#else
|
||||
if (in_data != data) free(data);
|
||||
return;
|
||||
#endif // HAVE_LIBZ
|
||||
}
|
||||
}
|
||||
|
||||
// now our SVG data should be in text format in `data`, terminated by a NUL
|
||||
// nsvgParse is destructive, so if in_data was set, we must duplicate the data first!
|
||||
if (in_data == data) {
|
||||
if (length) {
|
||||
data = (uchar*)malloc(length+1);
|
||||
memcpy(data, in_data, length);
|
||||
data[length] = 0;
|
||||
} else {
|
||||
data = (uchar*)fl_strdup((char*)in_data);
|
||||
}
|
||||
}
|
||||
counted_svg_image_->svg_image = nsvgParse((char*)data, "px", 96);
|
||||
if (in_data != data) free(data);
|
||||
if (counted_svg_image_->svg_image->width != 0 && counted_svg_image_->svg_image->height != 0) {
|
||||
w(int(counted_svg_image_->svg_image->width + 0.5));
|
||||
h(int(counted_svg_image_->svg_image->height + 0.5));
|
||||
d(4);
|
||||
ld(0);
|
||||
}
|
||||
|
||||
if (sharedname && w() && h()) {
|
||||
Fl_Shared_Image *si = new Fl_Shared_Image(sharedname, this);
|
||||
si->add();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user