diff --git a/FL/Fl_Image.H b/FL/Fl_Image.H index a8f514f36..8b8b433e8 100644 --- a/FL/Fl_Image.H +++ b/FL/Fl_Image.H @@ -259,7 +259,7 @@ public: \note Since FLTK 1.4.0 this method is 'const'. If you derive your own class from Fl_Image or any subclass your overridden methods of 'Fl_Image::copy() const' - and 'Fl_Image::copy(int, int) const' \b must also be 'const' for inheritage + and 'Fl_Image::copy(int, int) const' \b must also be 'const' for inheritance to work properly. This is different than in FLTK 1.3.x and earlier where these methods have not been 'const'. */ diff --git a/FL/Fl_Shared_Image.H b/FL/Fl_Shared_Image.H index 554197396..acecebf83 100644 --- a/FL/Fl_Shared_Image.H +++ b/FL/Fl_Shared_Image.H @@ -22,6 +22,7 @@ # include "Fl_Image.H" +#undef SHIM_DEBUG /** Test function (typedef) for adding new shared image formats. @@ -120,8 +121,13 @@ protected: virtual ~Fl_Shared_Image(); void add(); void update(); + Fl_Shared_Image *copy_(int W, int H) const; public: +#ifdef SHIM_DEBUG + static void print_pool(); +#endif + /** Returns the filename of the shared image */ const char *name() { return name_; } @@ -147,9 +153,9 @@ public: } Fl_Image *copy(int W, int H) const FL_OVERRIDE; - Fl_Image *copy() const { - return Fl_Image::copy(); - } + Fl_Image *copy() const; + Fl_Image *copy(); + void color_average(Fl_Color c, float i) FL_OVERRIDE; void desaturate() FL_OVERRIDE; void draw(int X, int Y, int W, int H, int cx = 0, int cy = 0) FL_OVERRIDE; diff --git a/src/Fl_Shared_Image.cxx b/src/Fl_Shared_Image.cxx index 62c06079a..d3f76cce9 100644 --- a/src/Fl_Shared_Image.cxx +++ b/src/Fl_Shared_Image.cxx @@ -48,18 +48,26 @@ extern "C" { } -/** Returns the Fl_Shared_Image* array */ +/** + Returns the Fl_Shared_Image* array. + + \return a pointer to an array of shared image pointers, sorted by name and size + \see Fl_Shared_Image::num_images() + */ Fl_Shared_Image **Fl_Shared_Image::images() { return images_; } +/** + Number of shared images in their various cached sizes. -/** Returns the total number of shared images in the array. */ + \return number of entries in the array + \see Fl_Shared_Image::images() + */ int Fl_Shared_Image::num_images() { return num_images_; } - /** Compares two shared images. @@ -77,6 +85,7 @@ int Fl_Shared_Image::num_images() { and the original_ flags set. This is not implemented via binary search, but by a simple run of the array inside Fl_Shared_Image::find(). + \param[in] i0, i1 image pointer pointer for sorting \returns Whether the images match or their relative sort order (see text). \retval 0 the images match \retval <0 Image \p i0 is \e less than image \p i1 @@ -95,7 +104,6 @@ Fl_Shared_Image::compare(Fl_Shared_Image **i0, // I - First image } } - /** Creates an empty shared image. The constructors create a new shared image record in the image cache. @@ -118,9 +126,12 @@ Fl_Shared_Image::Fl_Shared_Image() : Fl_Image(0,0,0) { The constructors are protected and cannot be used directly from a program. Use the get() method instead. + + \param[in] n filename or pool name of the image, must be unique among shared images + \param[in] img the image that is made available using the name */ -Fl_Shared_Image::Fl_Shared_Image(const char *n, // I - Filename - Fl_Image *img) // I - Image +Fl_Shared_Image::Fl_Shared_Image(const char *n, + Fl_Image *img) : Fl_Image(0,0,0) { name_ = new char[strlen(n) + 1]; strcpy((char *)name_, n); @@ -134,14 +145,15 @@ Fl_Shared_Image::Fl_Shared_Image(const char *n, // I - Filename else update(); } - /** - Adds a shared image to the image cache. + Adds a shared image to the image pool. - This \b protected method adds an image to the cache, an ordered list - of shared images. The cache is searched for a matching image whenever + This \b protected method adds an image to the pool, an ordered list + of shared images. The pool is searched for a matching image whenever one is requested, for instance with Fl_Shared_Image::get() or Fl_Shared_Image::find(). + + This method does not increase or decrease reference counts! */ void Fl_Shared_Image::add() { @@ -170,11 +182,11 @@ Fl_Shared_Image::add() { } } +/** + Update the dimensions of the shared images. -// -// 'Fl_Shared_Image::update()' - Update the dimensions of the shared images. -// - + Internal method to synchronize shared image data with the actual image data. + */ void Fl_Shared_Image::update() { if (image_) { @@ -199,7 +211,6 @@ Fl_Shared_Image::~Fl_Shared_Image() { if (alloc_image_) delete image_; } - /** Releases and possibly destroys (if refcount <= 0) a shared image. @@ -208,29 +219,39 @@ Fl_Shared_Image::~Fl_Shared_Image() { */ void Fl_Shared_Image::release() { int i; // Looping var... + Fl_Shared_Image *the_original = NULL; +#ifdef SHIM_DEBUG + printf("----> Fl_Shared_Image::release() %016x\n", this); + print_pool(); +#endif + + if (refcount_ <= 0) return; // assert(refcount_>0); refcount_ --; if (refcount_ > 0) return; if (!original()) { Fl_Shared_Image *o = find(name()); if (o && o->original() && o!=this) { - o->release(); // release as a reference to this copy of the image + the_original = o; + } + if (o) { o->release(); // release from find() operation } } - for (i = 0; i < num_images_; i ++) + for (i = 0; i < num_images_; i ++) { if (images_[i] == this) { num_images_ --; if (i < num_images_) { memmove(images_ + i, images_ + i + 1, - (num_images_ - i) * sizeof(Fl_Shared_Image *)); + (num_images_ - i) * sizeof(Fl_Shared_Image *)); } break; } + } delete this; @@ -238,11 +259,17 @@ void Fl_Shared_Image::release() { delete[] images_; images_ = 0; - alloc_images_ = 0; + alloc_images_ = NULL; } +#ifdef SHIM_DEBUG + printf("<---- Fl_Shared_Image::release() %016x\n", this); + print_pool(); + printf("\n"); +#endif + if (the_original) + the_original->release(); // release as a reference to this copy of the image } - /** Reloads the shared image from disk. */ void Fl_Shared_Image::reload() { // Load image from disk... @@ -290,15 +317,19 @@ void Fl_Shared_Image::reload() { } } +/** + Create a resized copy of the image and wrap it into the share image class. -// -// 'Fl_Shared_Image::copy()' - Copy and resize a shared image... -// -// Note: intentionally no doxygen docs here. -// For doxygen docs see Fl_Image::copy(). + This function is usually followed by a call to `returned_image->add() to add + the image to the pool, and `this->refcounter_++` to make sure that the original + shared image keeps a reference to the copy. Don't call this function if + an image of the given size is already in the pool. -Fl_Image * -Fl_Shared_Image::copy(int W, int H) const { + \param[in] W, H new image size + \return a new shared image pointer that is not yet in the pool + */ +Fl_Shared_Image * +Fl_Shared_Image::copy_(int W, int H) const { Fl_Image *temp_image; // New image file Fl_Shared_Image *temp_shared; // New shared image @@ -321,25 +352,80 @@ Fl_Shared_Image::copy(int W, int H) const { return temp_shared; } +/** + Return a shared image of this image with the requested size. -// -// 'Fl_Shared_Image::color_average()' - Blend colors... -// + This is the same as calling `Fl_Shared_Image::get(this->name(), W, H)`. + If a shared image of the desired size already exists in the shared image + pool, the existing image is returned and no copy is made. But the reference + counter is incremented. When the image is no longer used, call + `Fl_Shared_Image::release()`. + + To get a copy of the image data, call `this->image()->copy(W, H)` instead. + + \param[in] W, H size of requested image + \return pointer to an `Fl_Shared_Image` that can be safely cast, or NULL if + the image can't be found and can't be created. + */ +Fl_Image *Fl_Shared_Image::copy(int W, int H) const { + if (name_) // should always be set + return Fl_Shared_Image::get(name_, W, H); + else + return NULL; +} + +/** + Increments the reference counter and returns a pointer to itself. + + When the image is no longer used, call `Fl_Shared_Image::release()`. + + To get a copy of the image data, call `this->image()->copy()` instead. + + \return pointer to an `Fl_Shared_Image` that can be safely cast + */ +Fl_Image *Fl_Shared_Image::copy() { + refcount_++; + return this; +} + +Fl_Image *Fl_Shared_Image::copy() const { + if (name_) // should always be set + return Fl_Shared_Image::get(name_); + else + return NULL; +} + +/** + Averages the colors in the image with the provided FLTK color value. + + This method changes the pixel data of this specific image. + + \note It does not change any of the resized copies of this image, nor does it + necessarily apply the color changes if this image is resized later. + + \param[in] c blend with this color + \param[in] bland fraction + \see Fl_Image::color_average(Fl_Color c, float i) + */ void -Fl_Shared_Image::color_average(Fl_Color c, // I - Color to blend with - float i) { // I - Blend fraction +Fl_Shared_Image::color_average(Fl_Color c, float i) { if (!image_) return; image_->color_average(c, i); update(); } +/** + Convert the image to gray scale. -// -// 'Fl_Shared_Image::desaturate()' - Convert the image to grayscale... -// + This method changes the pixel data of this specific image. + \note It does not change any of the resized copies of this image, nor does it + necessarily apply the color changes if this image is resized later. + + \see Fl_Image::desaturate() + */ void Fl_Shared_Image::desaturate() { if (!image_) return; @@ -348,9 +434,12 @@ Fl_Shared_Image::desaturate() { update(); } -// -// 'Fl_Shared_Image::draw()' - Draw a shared image... -// +/** + Draw this image to the current graphics context. + + \param[in] X, Y, W, H draw at this position and size + \param[in] cx, cy image origin + */ void Fl_Shared_Image::draw(int X, int Y, int W, int H, int cx, int cy) { if (!image_) { Fl_Image::draw(X, Y, W, H, cx, cy); @@ -363,20 +452,16 @@ void Fl_Shared_Image::draw(int X, int Y, int W, int H, int cx, int cy) { image_->scale(width, height, 0, 1); } +/** + Remove the cached device specific image data. - - -// -// 'Fl_Shared_Image::uncache()' - Uncache the shared image... -// - + \see Fl_Image::uncache() + */ void Fl_Shared_Image::uncache() { if (image_) image_->uncache(); } - - /** Finds a shared image from its name and size specifications. This uses a binary search in the image cache. @@ -451,7 +536,6 @@ Fl_Shared_Image* Fl_Shared_Image::find(const char *name, int W, int H) { return NULL; } - /** Find or load an image that can be shared by multiple widgets. @@ -481,6 +565,8 @@ Fl_Shared_Image* Fl_Shared_Image::find(const char *name, int W, int H) { \param name name of the image \param W, H desired size + \return the image at the requested size, or NULL if the image could not be + found or generated \see Fl_Shared_Image::find(const char *name, int W, int H) \see Fl_Shared_Image::release() @@ -488,26 +574,42 @@ Fl_Shared_Image* Fl_Shared_Image::find(const char *name, int W, int H) { \see Fl_PNG_Image::Fl_PNG_Image (const char *name_png, const unsigned char *buffer, int maxsize) */ Fl_Shared_Image* Fl_Shared_Image::get(const char *name, int W, int H) { - Fl_Shared_Image *temp; // Image + Fl_Shared_Image *temp; + bool temp_referenced = false; + // Find an image by the requested size // ::find() increments the ref count for us if ((temp = find(name, W, H)) != NULL) return temp; - if ((temp = find(name)) == NULL) { + // Find the original image, size does not matter + temp = find(name); + if (temp) { + temp_referenced = true; + } else { + // No original found, so we generate it by loading the file temp = new Fl_Shared_Image(name); - + // We can't load the file or create the image, so return fail if (!temp->image_) { delete temp; return NULL; } - + // Add the new image to the pool, refcount is already at 1 temp->add(); } + // At this point, temp is an original image + // But if the size is wrong, generate a resized copy if ((temp->w() != W || temp->h() != H) && W && H) { - temp = (Fl_Shared_Image *)temp->copy(W, H); - temp->add(); + // Generate a copy with the new size, the copy gets refcount 1 + Fl_Shared_Image *new_temp = temp->copy_(W, H); + if (!new_temp) return NULL; + // Also increment the refcount of the original image + if (!temp_referenced) + temp->refcount_++; + // add the newly created image to the pool and return it + new_temp->add(); + return new_temp; } return temp; @@ -529,7 +631,6 @@ Fl_Shared_Image *Fl_Shared_Image::get(Fl_RGB_Image *rgb, int own_it) return shared; } - /** Adds a shared image handler, which is basically a test function for adding new image formats. @@ -572,7 +673,6 @@ void Fl_Shared_Image::add_handler(Fl_Shared_Handler f) { num_handlers_ ++; } - /** Removes a shared image handler. */ void Fl_Shared_Image::remove_handler(Fl_Shared_Handler f) { int i; // Looping var... @@ -593,3 +693,22 @@ void Fl_Shared_Image::remove_handler(Fl_Shared_Handler f) { (num_handlers_ - i) * sizeof(Fl_Shared_Handler )); } } + +#ifdef SHIM_DEBUG +/** + Print the contents of the shared image pool. + */ +void Fl_Shared_Image::print_pool() { + printf("Fl_Shared_Image: %d images stored in a pool of %d\n", num_images_, alloc_images_); + for (int i=0; irefcount_, + img->original_ ? 'O' : '_', + img->w(), img->h(), + img->name() + ); + } +} +#endif