text: //
  // "$Id: Fl_Shared_Image.cxx 7903 2010-11-28 21:06:39Z matt $"
  //
  // Shared image code for the Fast Light Tool Kit (FLTK).
  //
  // Copyright 1998-2010 by Bill Spitzak and others.
  //
  // This library is free software; you can redistribute it and/or
  // modify it under the terms of the GNU Library General Public
  // License as published by the Free Software Foundation; either
  // version 2 of the License, or (at your option) any later version.
  //
  // This library is distributed in the hope that it will be useful,
  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  // Library General Public License for more details.
  //
  // You should have received a copy of the GNU Library General Public
  // License along with this library; if not, write to the Free Software
  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
  // USA.
  //
  // Please report all bugs and problems on the following page:
  //
  //     http://www.fltk.org/str.php
  //
  
  #include <stdio.h>
  #include <stdlib.h>
  #include <FL/fl_utf8.h>
  #include "flstring.h"
  
  #include <FL/Fl.H>
  #include <FL/Fl_Shared_Image.H>
  #include <FL/Fl_XBM_Image.H>
  #include <FL/Fl_XPM_Image.H>
  
  
  //
  // Global class vars...
  //
  
  Fl_Shared_Image **Fl_Shared_Image::images_ = 0;	// Shared images
  int	Fl_Shared_Image::num_images_ = 0;	// Number of shared images
  int	Fl_Shared_Image::alloc_images_ = 0;	// Allocated shared images
  
  Fl_Shared_Handler *Fl_Shared_Image::handlers_ = 0;// Additional format handlers
  int	Fl_Shared_Image::num_handlers_ = 0;	// Number of format handlers
  int	Fl_Shared_Image::alloc_handlers_ = 0;	// Allocated format handlers
  
  
  //
  // Typedef the C API sort function type the only way I know how...
  //
  
  extern "C" {
    typedef int (*compare_func_t)(const void *, const void *);
  }
  
  
  /** Returns the Fl_Shared_Image* array */
  Fl_Shared_Image **Fl_Shared_Image::images() {
    return images_;
  }
  /** Returns the total number of shared images in the array. */
  int Fl_Shared_Image::num_images() {
    return num_images_;
  }
  
  
  //
  // 'Fl_Shared_Image::compare()' - Compare two shared images...
  //
  
  int
  Fl_Shared_Image::compare(Fl_Shared_Image **i0,		// I - First image
                           Fl_Shared_Image **i1) {	// I - Second image
    int i = strcmp((*i0)->name(), (*i1)->name());
  
    if (i) return i;
    else if (((*i0)->w() == 0 && (*i1)->original_) ||
             ((*i1)->w() == 0 && (*i0)->original_)) return 0;
    else if ((*i0)->w() != (*i1)->w()) return (*i0)->w() - (*i1)->w();
    else return (*i0)->h() - (*i1)->h();
  }
  
  
  /** 
    Creates an empty shared image.
    The constructors create a new shared image record in the image cache.
    
    <P>The constructors are protected and cannot be used directly
    from a program. Use the get() method instead.
  */
  Fl_Shared_Image::Fl_Shared_Image() : Fl_Image(0,0,0) {
    name_        = 0;
    refcount_    = 1;
    original_    = 0;
    image_       = 0;
    alloc_image_ = 0;
  }
  
  
  /** 
    Creates a shared image from its filename and its corresponding Fl_Image* img.
    The constructors create a new shared image record in the image cache.
    
    <P>The constructors are protected and cannot be used directly
    from a program. Use the get() method instead.
  */
  Fl_Shared_Image::Fl_Shared_Image(const char *n,		// I - Filename
                                   Fl_Image   *img)	// I - Image
    : Fl_Image(0,0,0) {
    name_ = new char[strlen(n) + 1];
    strcpy((char *)name_, n);
  
    refcount_    = 1;
    image_       = img;
    alloc_image_ = !img;
    original_    = 1;
  
    if (!img) reload();
    else update();
  }
  
  
  //
  // 'Fl_Shared_Image::add()' - Add a shared image to the array.
  //
  
  void
  Fl_Shared_Image::add() {
    Fl_Shared_Image	**temp;		// New image pointer array...
  
    if (num_images_ >= alloc_images_) {
      // Allocate more memory...
      temp = new Fl_Shared_Image *[alloc_images_ + 32];
  
      if (alloc_images_) {
        memcpy(temp, images_, alloc_images_ * sizeof(Fl_Shared_Image *));
  
        delete[] images_;
      }
  
      images_       = temp;
      alloc_images_ += 32;
    }
  
    images_[num_images_] = this;
    num_images_ ++;
  
    if (num_images_ > 1) {
      qsort(images_, num_images_, sizeof(Fl_Shared_Image *),
            (compare_func_t)compare);
    }
  }
  
  
  //
  // 'Fl_Shared_Image::update()' - Update the dimensions of the shared images.
  //
  
  void
  Fl_Shared_Image::update() {
    if (image_) {
      w(image_->w());
      h(image_->h());
      d(image_->d());
      data(image_->data(), image_->count());
    }
  }
  
  /**
    The destructor free all memory and server resources that are
    used by the image. The destructor is protected and cannot be
    used directly from a program. Use the Fl_Shared_Image::release() method
    instead.
  */
  Fl_Shared_Image::~Fl_Shared_Image() {
    if (name_) delete[] (char *)name_;
    if (alloc_image_) delete image_;
  }
  
  
  //
  /** 
    Releases and possibly destroys (if refcount <=0) a shared image. 
    In the latter case, it will reorganize the shared image array so that no hole will occur.
  */
  void Fl_Shared_Image::release() {
    int	i;	// Looping var...
  
    refcount_ --;
    if (refcount_ > 0) return;
  
    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 *));
        }
  
        break;
      }
  
    delete this;
  
    if (num_images_ == 0 && images_) {
      delete[] images_;
  
      images_       = 0;
      alloc_images_ = 0;
    }
  }
  
  
  //
  /** Reloads the shared image from disk */
  void Fl_Shared_Image::reload() {
    // Load image from disk...
    int		i;		// Looping var
    FILE		*fp;		// File pointer
    uchar		header[64];	// Buffer for auto-detecting files
    Fl_Image	*img;		// New image
  
    if (!name_) return;
  
    if ((fp = fl_fopen(name_, "rb")) != NULL) {
      if (fread(header, 1, sizeof(header), fp)==0) { /* ignore */ }
      fclose(fp);
    } else {
      return;
    }
  
    // Load the image as appropriate...
    if (memcmp(header, "#define", 7) == 0) // XBM file
      img = new Fl_XBM_Image(name_);
    else if (memcmp(header, "/* XPM */", 9) == 0) // XPM file
      img = new Fl_XPM_Image(name_);
    else {
      // Not a standard format; try an image handler...
      for (i = 0, img = 0; i < num_handlers_; i ++) {
        img = (handlers_[i])(name_, header, sizeof(header));
  
        if (img) break;
      }
    }
  
    if (img) {
      if (alloc_image_) delete image_;
  
      alloc_image_ = 1;
  
      if ((img->w() != w() && w()) || (img->h() != h() && h())) {
        // Make sure the reloaded image is the same size as the existing one.
        Fl_Image *temp = img->copy(w(), h());
        delete img;
        image_ = temp;
      } else {
        image_ = img;
      }
  
      update();
    }
  }
  
  
  //
  // 'Fl_Shared_Image::copy()' - Copy and resize a shared image...
  //
  
  Fl_Image *
  Fl_Shared_Image::copy(int W, int H) {
    Fl_Image		*temp_image;	// New image file
    Fl_Shared_Image	*temp_shared;	// New shared image
  
    // Make a copy of the image we're sharing...
    if (!image_) temp_image = 0;
    else temp_image = image_->copy(W, H);
  
    // Then make a new shared image...
    temp_shared = new Fl_Shared_Image();
  
    temp_shared->name_ = new char[strlen(name_) + 1];
    strcpy((char *)temp_shared->name_, name_);
  
    temp_shared->refcount_    = 1;
    temp_shared->image_       = temp_image;
    temp_shared->alloc_image_ = 1;
  
    temp_shared->update();
  
    return temp_shared;
  }
  
  
  //
  // 'Fl_Shared_Image::color_average()' - Blend colors...
  //
  
  void
  Fl_Shared_Image::color_average(Fl_Color c,	// I - Color to blend with
                                 float    i) {	// I - Blend fraction
    if (!image_) return;
  
    image_->color_average(c, i);
    update();
  }
  
  
  //
  // 'Fl_Shared_Image::desaturate()' - Convert the image to grayscale...
  //
  
  void
  Fl_Shared_Image::desaturate() {
    if (!image_) return;
  
    image_->desaturate();
    update();
  }
  
  
  //
  // 'Fl_Shared_Image::draw()' - Draw a shared image...
  //
  
  void
  Fl_Shared_Image::draw(int X, int Y, int W, int H, int cx, int cy) {
    if (image_) image_->draw(X, Y, W, H, cx, cy);
    else Fl_Image::draw(X, Y, W, H, cx, cy);
  }
  
  
  //
  // 'Fl_Shared_Image::uncache()' - Uncache the shared image...
  //
  
  void Fl_Shared_Image::uncache()
  {
    if (image_) image_->uncache();
  }
  
  
  
  /** Finds a shared image from its named and size specifications */
  Fl_Shared_Image* Fl_Shared_Image::find(const char *n, int W, int H) {
    Fl_Shared_Image	*key,		// Image key
  			**match;	// Matching image
  
    if (num_images_) {
      key = new Fl_Shared_Image();
      key->name_ = new char[strlen(n) + 1];
      strcpy((char *)key->name_, n);
      key->w(W);
      key->h(H);
  
      match = (Fl_Shared_Image **)bsearch(&key, images_, num_images_,
                                          sizeof(Fl_Shared_Image *),
                                          (compare_func_t)compare);
  
      delete key;
  
      if (match) {
        (*match)->refcount_ ++;
        return *match;
      }
    }
  
    return 0;
  }
  
  
  /** 
    Gets a shared image, if it exists already ; it will return it.
    If it does not exist or if it exist but with other size, 
    then the existing image is deleted and replaced
    by a new image from the n filename of the proper dimension.
    If n is not a valid image filename, then get() will return NULL.
  */
  Fl_Shared_Image* Fl_Shared_Image::get(const char *n, int W, int H) {
    Fl_Shared_Image	*temp;		// Image
  
    if ((temp = find(n, W, H)) != NULL) return temp;
  
    if ((temp = find(n)) == NULL) {
      temp = new Fl_Shared_Image(n);
  
      if (!temp->image_) {
        delete temp;
        return NULL;
      }
  
      temp->add();
    }
  
    if ((temp->w() != W || temp->h() != H) && W && H) {
      temp = (Fl_Shared_Image *)temp->copy(W, H);
      temp->add();
    }
  
    return temp;
  }
  
  
  
  /** Adds a shared image handler, which is basically a test function for adding new formats */
  void Fl_Shared_Image::add_handler(Fl_Shared_Handler f) {
    int			i;		// Looping var...
    Fl_Shared_Handler	*temp;		// New image handler array...
  
    // First see if we have already added the handler...
    for (i = 0; i < num_handlers_; i ++) {
      if (handlers_[i] == f) return;
    }
  
    if (num_handlers_ >= alloc_handlers_) {
      // Allocate more memory...
      temp = new Fl_Shared_Handler [alloc_handlers_ + 32];
  
      if (alloc_handlers_) {
        memcpy(temp, handlers_, alloc_handlers_ * sizeof(Fl_Shared_Handler));
  
        delete[] handlers_;
      }
  
      handlers_       = temp;
      alloc_handlers_ += 32;
    }
  
    handlers_[num_handlers_] = f;
    num_handlers_ ++;
  }
  
  
  
  /** Removes a shared image handler */
  void Fl_Shared_Image::remove_handler(Fl_Shared_Handler f) {
    int	i;				// Looping var...
  
    // First see if the handler has been added...
    for (i = 0; i < num_handlers_; i ++) {
      if (handlers_[i] == f) break;
    }
  
    if (i >= num_handlers_) return;
  
    // OK, remove the handler from the array...
    num_handlers_ --;
  
    if (i < num_handlers_) {
      // Shift later handlers down 1...
      memmove(handlers_ + i, handlers_ + i + 1,
             (num_handlers_ - i) * sizeof(Fl_Shared_Handler ));
    }
  }
  
  
  //
  // End of "$Id: Fl_Shared_Image.cxx 7903 2010-11-28 21:06:39Z matt $".
  //
  