mirror of https://github.com/fltk/fltk
#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:
parent
994b5824dc
commit
332a87aa43
|
@ -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'.
|
||||
*/
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue