#887: Fixes Fl_Shared_Image reference counting

- adds documentation to all calls
- changes implementation details on ::copy()
and copy(w,h)
This commit is contained in:
Matthias Melcher 2024-01-13 19:32:24 +01:00
parent 994b5824dc
commit 332a87aa43
3 changed files with 184 additions and 59 deletions

View File

@ -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'.
*/

View File

@ -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;

View File

@ -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; i<num_images_; i++) {
Fl_Shared_Image *img = images_[i];
printf("%3d: %3d(%c) %4dx%4d: %s\n",
i,
img->refcount_,
img->original_ ? 'O' : '_',
img->w(), img->h(),
img->name()
);
}
}
#endif