summaryrefslogtreecommitdiff
path: root/fluid/app/Fluid_Image.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'fluid/app/Fluid_Image.cxx')
-rw-r--r--fluid/app/Fluid_Image.cxx412
1 files changed, 412 insertions, 0 deletions
diff --git a/fluid/app/Fluid_Image.cxx b/fluid/app/Fluid_Image.cxx
new file mode 100644
index 000000000..d25f17023
--- /dev/null
+++ b/fluid/app/Fluid_Image.cxx
@@ -0,0 +1,412 @@
+//
+// Pixmap (and other images) label support for the Fast Light Tool Kit (FLTK).
+//
+// 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
+// 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
+//
+
+#include "app/Fluid_Image.h"
+
+#include "app/fluid.h"
+#include "io/file.h"
+#include "io/code.h"
+#include "nodes/Fl_Group_Type.h"
+#include "nodes/Fl_Window_Type.h"
+#include "tools/fluid_filename.h"
+
+#include <FL/Fl.H>
+#include <FL/Fl_Widget.H>
+#include <FL/Fl_Window.H>
+#include <FL/fl_string_functions.h>
+#include <FL/fl_utf8.h> // fl_fopen()
+#include <FL/Fl_File_Chooser.H>
+#include <FL/Fl_SVG_Image.H>
+#include <FL/Fl_Anim_GIF_Image.H>
+#include "../src/flstring.h"
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+void Fluid_Image::image(Fl_Widget *o) {
+ if (o->window() != o) o->image(img);
+}
+
+void Fluid_Image::deimage(Fl_Widget *o) {
+ if (o->window() != o) o->deimage(img);
+}
+
+/** Write the contents of the name() file as binary source code.
+ \param fmt short name of file contents for error message
+ \return 0 if the file could not be opened or read */
+size_t Fluid_Image::write_static_binary(Fd_Code_Writer& f, const char* fmt) {
+ size_t nData = 0;
+ enter_project_dir();
+ FILE *in = fl_fopen(name(), "rb");
+ leave_project_dir();
+ if (!in) {
+ write_file_error(f, fmt);
+ return 0;
+ } else {
+ fseek(in, 0, SEEK_END);
+ nData = ftell(in);
+ fseek(in, 0, SEEK_SET);
+ if (nData) {
+ char *data = (char*)calloc(nData, 1);
+ if (fread(data, nData, 1, in)==0) { /* ignore */ }
+ f.write_cdata(data, (int)nData);
+ free(data);
+ }
+ fclose(in);
+ }
+ return nData;
+}
+
+/** Write the contents of the name() file as textual source code.
+ \param fmt short name of file contents for error message
+ \return 0 if the file could not be opened or read */
+size_t Fluid_Image::write_static_text(Fd_Code_Writer& f, const char* fmt) {
+ size_t nData = 0;
+ enter_project_dir();
+ FILE *in = fl_fopen(name(), "rb");
+ leave_project_dir();
+ if (!in) {
+ write_file_error(f, fmt);
+ return 0;
+ } else {
+ fseek(in, 0, SEEK_END);
+ nData = ftell(in);
+ fseek(in, 0, SEEK_SET);
+ if (nData) {
+ char *data = (char*)calloc(nData+1, 1);
+ if (fread(data, nData, 1, in)==0) { /* ignore */ }
+ f.write_cstring(data, (int)nData);
+ free(data);
+ }
+ fclose(in);
+ }
+ return nData;
+}
+
+void Fluid_Image::write_static_rgb(Fd_Code_Writer& f, const char* idata_name) {
+ // Write image data...
+ f.write_c("\n");
+ f.write_c_once("#include <FL/Fl_Image.H>\n");
+ f.write_c("static const unsigned char %s[] =\n", idata_name);
+ const int extra_data = img->ld() ? (img->ld()-img->w()*img->d()) : 0;
+ f.write_cdata(img->data()[0], (img->w() * img->d() + extra_data) * img->h());
+ f.write_c(";\n");
+ write_initializer(f, "Fl_RGB_Image", "%s, %d, %d, %d, %d", idata_name, img->w(), img->h(), img->d(), img->ld());
+}
+
+/**
+ Write the static image data into the soutrce file.
+
+ If \p compressed is set, write the original image format, which requires
+ linking the matching image reader at runtime, or if we want to store the raw
+ uncompressed pixels, which makes images fast, needs no reader, but takes a
+ lot of memory (current default for PNG)
+
+ \param compressed write data in the original compressed file format
+ */
+void Fluid_Image::write_static(Fd_Code_Writer& f, int compressed) {
+ if (!img) return;
+ const char *idata_name = f.unique_id(this, "idata", fl_filename_name(name()), 0);
+ function_name_ = f.unique_id(this, "image", fl_filename_name(name()), 0);
+
+ if (is_animated_gif_) {
+ // Write animated gif image data...
+ f.write_c("\n");
+ f.write_c_once("#include <FL/Fl_Anim_GIF_Image.H>\n");
+ f.write_c("static const unsigned char %s[] =\n", idata_name);
+ size_t nData = write_static_binary(f, "AnimGIF");
+ f.write_c(";\n");
+ write_initializer(f, "Fl_Anim_GIF_Image", "\"%s\", %s, %d", fl_filename_name(name()), idata_name, nData);
+ } else if (compressed && fl_ascii_strcasecmp(fl_filename_ext(name()), ".gif")==0) {
+ // Write gif image data...
+ f.write_c("\n");
+ f.write_c_once("#include <FL/Fl_GIF_Image.H>\n");
+ f.write_c("static const unsigned char %s[] =\n", idata_name);
+ size_t nData = write_static_binary(f, "GIF");
+ f.write_c(";\n");
+ write_initializer(f, "Fl_GIF_Image", "\"%s\", %s, %d", fl_filename_name(name()), idata_name, nData);
+ } else if (compressed && fl_ascii_strcasecmp(fl_filename_ext(name()), ".bmp")==0) {
+ // Write bmp image data...
+ f.write_c("\n");
+ f.write_c_once("#include <FL/Fl_BMP_Image.H>\n");
+ f.write_c("static const unsigned char %s[] =\n", idata_name);
+ size_t nData = write_static_binary(f, "BMP");
+ f.write_c(";\n");
+ write_initializer(f, "Fl_BMP_Image", "\"%s\", %s, %d", fl_filename_name(name()), idata_name, nData);
+ } else if (img->count() > 1) {
+ // Write Pixmap data...
+ f.write_c("\n");
+ f.write_c_once("#include <FL/Fl_Pixmap.H>\n");
+ f.write_c("static const char *%s[] = {\n", idata_name);
+ f.write_cstring(img->data()[0], (int)strlen(img->data()[0]));
+
+ int i;
+ int ncolors, chars_per_color;
+ sscanf(img->data()[0], "%*d%*d%d%d", &ncolors, &chars_per_color);
+
+ if (ncolors < 0) {
+ f.write_c(",\n");
+ f.write_cstring(img->data()[1], ncolors * -4);
+ i = 2;
+ } else {
+ for (i = 1; i <= ncolors; i ++) {
+ f.write_c(",\n");
+ f.write_cstring(img->data()[i], (int)strlen(img->data()[i]));
+ }
+ }
+ for (; i < img->count(); i ++) {
+ f.write_c(",\n");
+ f.write_cstring(img->data()[i], img->w() * chars_per_color);
+ }
+ f.write_c("\n};\n");
+ write_initializer(f, "Fl_Pixmap", "%s", idata_name);
+ } else if (img->d() == 0) {
+ // Write Bitmap data...
+ f.write_c("\n");
+ f.write_c_once("#include <FL/Fl_Bitmap.H>\n");
+ f.write_c("static const unsigned char %s[] =\n", idata_name);
+ f.write_cdata(img->data()[0], ((img->w() + 7) / 8) * img->h());
+ f.write_c(";\n");
+ write_initializer(f, "Fl_Bitmap", "%s, %d, %d, %d", idata_name, ((img->w() + 7) / 8) * img->h(), img->w(), img->h());
+ } else if (compressed && fl_ascii_strcasecmp(fl_filename_ext(name()), ".jpg")==0) {
+ // Write jpeg image data...
+ f.write_c("\n");
+ f.write_c_once("#include <FL/Fl_JPEG_Image.H>\n");
+ f.write_c("static const unsigned char %s[] =\n", idata_name);
+ size_t nData = write_static_binary(f, "JPEG");
+ f.write_c(";\n");
+ write_initializer(f, "Fl_JPEG_Image", "\"%s\", %s, %d", fl_filename_name(name()), idata_name, nData);
+ } else if (compressed && fl_ascii_strcasecmp(fl_filename_ext(name()), ".png")==0) {
+ // Write png image data...
+ f.write_c("\n");
+ f.write_c_once("#include <FL/Fl_PNG_Image.H>\n");
+ f.write_c("static const unsigned char %s[] =\n", idata_name);
+ size_t nData = write_static_binary(f, "PNG");
+ f.write_c(";\n");
+ write_initializer(f, "Fl_PNG_Image", "\"%s\", %s, %d", fl_filename_name(name()), idata_name, nData);
+ }
+#ifdef FLTK_USE_SVG
+ else if (fl_ascii_strcasecmp(fl_filename_ext(name()), ".svg")==0 || fl_ascii_strcasecmp(fl_filename_ext(name()), ".svgz")==0) {
+ bool gzipped = (strcmp(fl_filename_ext(name()), ".svgz") == 0);
+ // Write svg image data...
+ if (compressed) {
+ f.write_c("\n");
+ f.write_c_once("#include <FL/Fl_SVG_Image.H>\n");
+ if (gzipped) {
+ f.write_c("static const unsigned char %s[] =\n", idata_name);
+ size_t nData = write_static_binary(f, "SVGZ");
+ f.write_c(";\n");
+ write_initializer(f, "Fl_SVG_Image", "\"%s\", %s, %ld", fl_filename_name(name()), idata_name, nData);
+ } else {
+ f.write_c("static const char %s[] =\n", idata_name);
+ write_static_text(f, "SVG");
+ f.write_c(";\n");
+ write_initializer(f, "Fl_SVG_Image", "\"%s\", %s", fl_filename_name(name()), idata_name);
+ }
+ } else {
+ // if FLUID runs from the command line, make sure that the image is not
+ // only loaded but also rasterized, so we can write the RGB image data
+ Fl_RGB_Image* rgb_image = NULL;
+ Fl_SVG_Image* svg_image = NULL;
+ if (img->d()>0)
+ rgb_image = (Fl_RGB_Image*)img->image();
+ if (rgb_image)
+ svg_image = rgb_image->as_svg_image();
+ if (svg_image) {
+ svg_image->resize(svg_image->w(), svg_image->h());
+ write_static_rgb(f, idata_name);
+ } else {
+ write_file_error(f, "RGB_from_SVG");
+ }
+ }
+ }
+#endif // FLTK_USE_SVG
+ else {
+ write_static_rgb(f, idata_name);
+ }
+}
+
+void Fluid_Image::write_file_error(Fd_Code_Writer& f, const char *fmt) {
+ f.write_c("#warning Cannot read %s file \"%s\": %s\n", fmt, name(), strerror(errno));
+ enter_project_dir();
+ f.write_c("// Searching in path \"%s\"\n", fl_getcwd(0, FL_PATH_MAX));
+ leave_project_dir();
+}
+
+void Fluid_Image::write_initializer(Fd_Code_Writer& f, 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 = NULL;
+ if (!image)
+ image = new 'type_name'('product of format and remaining args');
+ return image;
+ } */
+ va_list ap;
+ va_start(ap, format);
+ f.write_c("static Fl_Image *%s() {\n", function_name_);
+ if (is_animated_gif_)
+ f.write_c("%sFl_GIF_Image::animate = true;\n", f.indent(1));
+ f.write_c("%sstatic Fl_Image *image = NULL;\n", f.indent(1));
+ f.write_c("%sif (!image)\n", f.indent(1));
+ f.write_c("%simage = new %s(", f.indent(2), type_name);
+ f.vwrite_c(format, ap);
+ f.write_c(");\n");
+ f.write_c("%sreturn image;\n", f.indent(1));
+ f.write_c("}\n");
+ va_end(ap);
+}
+
+void Fluid_Image::write_code(Fd_Code_Writer& f, int bind, const char *var, int inactive) {
+ /* Outputs code that attaches an image to an Fl_Widget or Fl_Menu_Item.
+ This code calls a function output before by Fluid_Image::write_initializer() */
+ if (img) {
+ f.write_c("%s%s->%s%s( %s() );\n", f.indent(), var, bind ? "bind_" : "", inactive ? "deimage" : "image", function_name_);
+ if (is_animated_gif_)
+ f.write_c("%s((Fl_Anim_GIF_Image*)(%s()))->canvas(%s, Fl_Anim_GIF_Image::DONT_RESIZE_CANVAS);\n", f.indent(), function_name_, var);
+ }
+}
+
+void Fluid_Image::write_inline(Fd_Code_Writer& f, int inactive) {
+ if (img)
+ f.write_c("%s()", function_name_);
+}
+
+
+////////////////////////////////////////////////////////////////
+
+static Fluid_Image** images = 0; // sorted list
+static int numimages = 0;
+static int tablesize = 0;
+
+Fluid_Image* Fluid_Image::find(const char *iname) {
+ if (!iname || !*iname) return 0;
+
+ // first search to see if it exists already:
+ int a = 0;
+ int b = numimages;
+ while (a < b) {
+ int c = (a+b)/2;
+ int i = strcmp(iname,images[c]->name_);
+ if (i < 0) b = c;
+ else if (i > 0) a = c+1;
+ else return images[c];
+ }
+
+ // no, so now see if the file exists:
+
+ enter_project_dir();
+ FILE *f = fl_fopen(iname,"rb");
+ if (!f) {
+ if (batch_mode)
+ fprintf(stderr, "Can't open image file:\n%s\n%s",iname,strerror(errno));
+ else
+ fl_message("Can't open image file:\n%s\n%s",iname,strerror(errno));
+ leave_project_dir();
+ return 0;
+ }
+ fclose(f);
+
+ Fluid_Image *ret = new Fluid_Image(iname);
+
+ if (!ret->img || !ret->img->w() || !ret->img->h()) {
+ delete ret;
+ ret = 0;
+ if (batch_mode)
+ fprintf(stderr, "Can't read image file:\n%s\nunrecognized image format",iname);
+ else
+ fl_message("Can't read image file:\n%s\nunrecognized image format",iname);
+ }
+ leave_project_dir();
+ if (!ret) return 0;
+
+ // make a new entry in the table:
+ numimages++;
+ if (numimages > tablesize) {
+ tablesize = tablesize ? 2*tablesize : 16;
+ if (images) images = (Fluid_Image**)realloc(images, tablesize*sizeof(Fluid_Image*));
+ else images = (Fluid_Image**)malloc(tablesize*sizeof(Fluid_Image*));
+ }
+ for (b = numimages-1; b > a; b--) images[b] = images[b-1];
+ images[a] = ret;
+
+ return ret;
+}
+
+Fluid_Image::Fluid_Image(const char *iname)
+ : is_animated_gif_(false)
+{
+ name_ = fl_strdup(iname);
+ written = 0;
+ refcount = 0;
+ img = Fl_Shared_Image::get(iname);
+ if (img && iname) {
+ const char *ext = fl_filename_ext(iname);
+ if (fl_ascii_strcasecmp(ext, ".gif")==0) {
+ int fc = Fl_Anim_GIF_Image::frame_count(iname);
+ if (fc > 0) is_animated_gif_ = true;
+ }
+ }
+ function_name_ = NULL;
+}
+
+void Fluid_Image::increment() {
+ ++refcount;
+}
+
+void Fluid_Image::decrement() {
+ --refcount;
+ if (refcount > 0) return;
+ delete this;
+}
+
+Fluid_Image::~Fluid_Image() {
+ int a;
+ if (images) {
+ 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_);
+}
+
+////////////////////////////////////////////////////////////////
+
+const char *ui_find_image_name;
+Fluid_Image *ui_find_image(const char *oldname) {
+ enter_project_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"
+#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;
+ leave_project_dir();
+ return ret;
+}