Index: FL/Fl_Device.H
===================================================================
--- FL/Fl_Device.H	(revision 10483)
+++ FL/Fl_Device.H	(working copy)
@@ -397,6 +397,10 @@
   inline Fl_Font_Descriptor *font_descriptor() { return font_descriptor_;}
   /** Sets the current Fl_Font_Descriptor for the graphics driver */
   inline void font_descriptor(Fl_Font_Descriptor *d) { font_descriptor_ = d;}
+#if FLTK_ABI_VERSION >= 10304
+  virtual
+#endif
+  int draw_scaled(Fl_RGB_Image *img, int X, int Y, int W, int H);
   /** \brief The destructor */
   virtual ~Fl_Graphics_Driver() { if (p) free(p); }
 };
@@ -423,6 +427,7 @@
   void draw(Fl_Pixmap *pxm, int XP, int YP, int WP, int HP, int cx, int cy);
   void draw(Fl_Bitmap *pxm, int XP, int YP, int WP, int HP, int cx, int cy);
   void draw(Fl_RGB_Image *img, int XP, int YP, int WP, int HP, int cx, int cy);
+  int draw_scaled(Fl_RGB_Image *img, int XP, int YP, int WP, int HP);
   void draw_image(const uchar* buf, int X,int Y,int W,int H, int D=3, int L=0);
   void draw_image(Fl_Draw_Image_Cb cb, void* data, int X,int Y,int W,int H, int D=3);
   void draw_image_mono(const uchar* buf, int X,int Y,int W,int H, int D=1, int L=0);
@@ -483,6 +488,7 @@
   const char *class_name() {return class_id;};
   void draw(Fl_Pixmap *pxm, int XP, int YP, int WP, int HP, int cx, int cy);
   void draw(Fl_Bitmap *bm, int XP, int YP, int WP, int HP, int cx, int cy);
+  int draw_scaled(Fl_RGB_Image *img, int X, int Y, int W, int H);
 };
 #endif
 #if !(defined(__APPLE__) || defined(WIN32))
Index: FL/Fl_Image.H
===================================================================
--- FL/Fl_Image.H	(revision 10483)
+++ FL/Fl_Image.H	(working copy)
@@ -183,6 +183,7 @@
 class FL_EXPORT Fl_RGB_Image : public Fl_Image {
   friend class Fl_Quartz_Graphics_Driver;
   friend class Fl_GDI_Graphics_Driver;
+  friend class Fl_GDI_Printer_Graphics_Driver;
   friend class Fl_Xlib_Graphics_Driver;
   static size_t max_size_;
 public:
@@ -247,6 +248,54 @@
   static size_t max_size() {return max_size_;}
 };
 
+class Fl_Shared_Image;
+
+/** The Fl_Scalable_Image class can re-scale a source image when drawing it.
+ 
+ An Fl_Scalable_Image can be useful to draw an image on a drawing surface whose resolution is higher 
+ than the drawing unit for this surface. Examples of such drawing surfaces: laser printers, 
+ PostScript files, PDF printers, retina displays on Apple hardware.
+ 
+ The Fl_Scalable_Image class allows to draw within a rectangle of the drawing surface a source
+ image that has its own size, independently from the rectangle size. The source image is typically larger
+ than the drawing rectangle, so its pixels can 'fill' high resolution drawing surfaces.
+ 
+ Example code: use an Fl_Scalable_Image as the background image of a box.
+ \code
+ Fl_Shared_Image *source = ...; // the source image. It could also be an Fl_RGB_Image.
+ Fl_Box *box = ...;    // a box
+ // create the scalable image from the source image, give it the size of the box,
+ // and keep it proportionally scaled
+ Fl_Scalable_Image *scalable = new Fl_Scalable_Image(source, box->w(), box->h(), 1);
+ box->image(scalable); // make the scalable image the box's image
+ source->release();  // if image 'source' is used only by image 'scalable'
+ \endcode
+ */
+class Fl_Scalable_Image : public Fl_Image {
+private:
+  void Fl_Scalable_Image_(Fl_Image *base, int W, int H, int proportional, int can_expand);
+  Fl_Image* image_; // the source image containing the pixel data
+  Fl_Image* scaled_image_; // auxiliary scaled copy of image_ when needed
+  Fl_Shared_Image *from_shared_; // when !=0, the scalable was constructed from a shared image
+public:
+  /** indicates whether the scalable image should delete its source image when itself is deleted.
+   Set this to 1 only when the scalable image was constructed from an Fl_RGB_Image source,
+   and if you want the scalable image to own its source image.
+   */
+  int	alloc_image;
+  Fl_Scalable_Image(Fl_Shared_Image *source, int width = 0, int height = 0, int proportional = 1, int can_expand = 0);
+  Fl_Scalable_Image(Fl_RGB_Image *source, int width = 0, int height = 0, int proportional = 1, int can_expand = 0);
+  void draw(int X, int Y, int W, int H, int cx=0, int cy=0);
+  void draw(int X, int Y) {draw(X, Y, w(), h(), 0, 0);}
+  Fl_Image *copy(int W, int H);
+  void color_average(Fl_Color c, float  i);
+  void desaturate();
+  void uncache();
+  void size(int width, int height, int proportional = 1, int can_expand = 0);
+  ~Fl_Scalable_Image();
+};
+
+
 #endif // !Fl_Image_H
 
 //
Index: FL/Fl_PostScript.H
===================================================================
--- FL/Fl_PostScript.H	(revision 10549)
+++ FL/Fl_PostScript.H	(working copy)
@@ -205,6 +205,7 @@
   void draw(Fl_Pixmap * pxm,int XP, int YP, int WP, int HP, int cx, int cy);
   void draw(Fl_Bitmap * bitmap,int XP, int YP, int WP, int HP, int cx, int cy);
   void draw(Fl_RGB_Image * rgb,int XP, int YP, int WP, int HP, int cx, int cy);
+  int draw_scaled(Fl_RGB_Image *img, int XP, int YP, int WP, int HP);
   int clocale_printf(const char *format, ...);
   ~Fl_PostScript_Graphics_Driver();
 };
Index: FL/Fl_Shared_Image.H
===================================================================
--- FL/Fl_Shared_Image.H	(revision 10483)
+++ FL/Fl_Shared_Image.H	(working copy)
@@ -41,6 +41,7 @@
   
   friend class Fl_JPEG_Image;
   friend class Fl_PNG_Image;
+  friend class Fl_Scalable_Image;
 
 protected:
 
Index: src/Fl_Image.cxx
===================================================================
--- src/Fl_Image.cxx	(revision 10571)
+++ src/Fl_Image.cxx	(working copy)
@@ -22,7 +22,9 @@
 #include <FL/Fl_Widget.H>
 #include <FL/Fl_Menu_Item.H>
 #include <FL/Fl_Image.H>
+#include <FL/Fl_Shared_Image.H>
 #include <FL/Fl_Printer.H>
+#include <FL/Fl_PostScript.H>
 #include "flstring.h"
 
 #ifdef WIN32
@@ -548,6 +550,14 @@
   return 0;
 }
 
+/** Draws an Fl_RGB_Image scaled to width \p W & height \p H with top-left corner at \em X,Y
+ \return non-zero when the graphics driver doesn't implement scaled drawing
+ */
+int Fl_Graphics_Driver::draw_scaled(Fl_RGB_Image *img, int X, int Y, int W, int H) {
+  return 1;
+}
+
+
 #ifdef __APPLE__
 static void imgProviderReleaseData (void *info, const void *data, size_t size)
 {
@@ -612,7 +622,41 @@
   }
 }
 
+int Fl_Quartz_Graphics_Driver::draw_scaled(Fl_RGB_Image *img, int XP, int YP, int WP, int HP) {
+  int X, Y, W, H;
+  fl_clip_box(XP,YP,WP,HP,X,Y,W,H); // X,Y,W,H will give the unclipped area of XP,YP,WP,HP
+  if (W == 0 || H == 0) return 0;
+  fl_push_no_clip(); // remove the FLTK clip that can't be rescaled
+  CGContextSaveGState(fl_gc);
+  CGContextClipToRect(fl_gc, CGRectMake(X, Y, W, H)); // this clip path will be rescaled & translated
+  CGContextTranslateCTM(fl_gc, XP, YP);
+  CGContextScaleCTM(fl_gc, float(WP)/img->w(), float(HP)/img->h());
+  draw(img, 0, 0, img->w(), img->h(), 0, 0);
+  CGContextRestoreGState(fl_gc);
+  fl_pop_clip(); // restore FLTK's clip
+  return 0;
+}
+
+
 #elif defined(WIN32)
+static Fl_Offscreen build_id(Fl_RGB_Image *img, void **pmask)
+{
+  Fl_Offscreen offs = fl_create_offscreen(img->w(), img->h());
+  if ((img->d() == 2 || img->d() == 4) && fl_can_do_alpha_blending()) {
+    fl_begin_offscreen(offs);
+    fl_draw_image(img->array, 0, 0, img->w(), img->h(), img->d()|FL_IMAGE_WITH_ALPHA, img->ld());
+    fl_end_offscreen();
+  } else {
+    fl_begin_offscreen(offs);
+    fl_draw_image(img->array, 0, 0, img->w(), img->h(), img->d(), img->ld());
+    fl_end_offscreen();
+    if (img->d() == 2 || img->d() == 4) {
+      *pmask = fl_create_alphamask(img->w(), img->h(), img->d(), img->ld(), img->array);
+    }
+  }
+  return offs;
+}
+
 void Fl_GDI_Graphics_Driver::draw(Fl_RGB_Image *img, int XP, int YP, int WP, int HP, int cx, int cy) {
   int X, Y, W, H;
   // Don't draw an empty image...
@@ -623,21 +667,7 @@
   if (start(img, XP, YP, WP, HP, img->w(), img->h(), cx, cy, X, Y, W, H)) {
     return;
   }
-  if (!img->id_) {
-    img->id_ = fl_create_offscreen(img->w(), img->h());
-    if ((img->d() == 2 || img->d() == 4) && fl_can_do_alpha_blending()) {
-      fl_begin_offscreen((Fl_Offscreen)img->id_);
-      fl_draw_image(img->array, 0, 0, img->w(), img->h(), img->d()|FL_IMAGE_WITH_ALPHA, img->ld());
-      fl_end_offscreen();
-    } else {
-      fl_begin_offscreen((Fl_Offscreen)img->id_);
-      fl_draw_image(img->array, 0, 0, img->w(), img->h(), img->d(), img->ld());
-      fl_end_offscreen();
-      if (img->d() == 2 || img->d() == 4) {
-        img->mask_ = fl_create_alphamask(img->w(), img->h(), img->d(), img->ld(), img->array);
-      }
-    }
-  }
+  if (!img->id_) img->id_ = build_id(img, &(img->mask_));
   if (img->mask_) {
     HDC new_gc = CreateCompatibleDC(fl_gc);
     int save = SaveDC(new_gc);
@@ -654,6 +684,25 @@
   }
 }
 
+int Fl_GDI_Printer_Graphics_Driver::draw_scaled(Fl_RGB_Image *img, int XP, int YP, int WP, int HP) {
+  // Don't draw an empty image...
+  if (!img->d() || !img->array) {
+    push_clip(XP, YP, WP, HP);
+    img->draw_empty(XP, YP);
+    pop_clip();
+    return 0;
+  }
+  if (!img->id_) img->id_ = build_id(img, &(img->mask_));
+  HDC new_gc = CreateCompatibleDC(fl_gc);
+  int save = SaveDC(new_gc);
+  SelectObject(new_gc, img->id_);
+  StretchBlt(fl_gc, XP, YP, WP, HP, new_gc, 0, 0, img->w(), img->h(), SRCCOPY);
+  RestoreDC(new_gc, save);
+  DeleteDC(new_gc);
+  return 0;
+}
+
+
 #else
 void Fl_Xlib_Graphics_Driver::draw(Fl_RGB_Image *img, int XP, int YP, int WP, int HP, int cx, int cy) {
   int X, Y, W, H;
@@ -711,6 +760,160 @@
   m->label(_FL_IMAGE_LABEL, (const char*)this);
 }
 
+
+void Fl_Scalable_Image::Fl_Scalable_Image_(Fl_Image *base, int W, int H, int proportional, int can_expand) {
+  alloc_image = 0;
+  image_ = 0;
+  scaled_image_ = NULL;
+  from_shared_ = NULL;
+  if (!base || base->w() <= 0 || base->h() <= 0) return;
+  image_ = base;
+  d(base->d());
+  size(W, H, proportional, can_expand);
+}
+
+/** Constructs an Fl_Scalable_Image that will scale its Fl_Shared_Image \p source to \p width and \p height drawing units.
+ 
+ \param   source   an Fl_Shared_Image used as source image for the scalable image
+ \param width, height, proportional, can_expand    see size(int width, int height, int proportional, int can_expand)
+  */
+Fl_Scalable_Image::Fl_Scalable_Image(Fl_Shared_Image *source, int width, int height,
+                                     int proportional, int can_expand) : Fl_Image(width, height, 0) {
+  Fl_Scalable_Image_(source->image_, width, height, proportional, can_expand);
+  from_shared_ = Fl_Shared_Image::find(source->name(), source->w(), source->h());
+}
+
+/** Constructs an Fl_Scalable_Image that will scale its Fl_RGB_Image \p source to \p width and \p height drawing units.
+ 
+ \param   source   an Fl_RGB_Image used as source image for the scalable image
+ \param width, height, proportional, can_expand    see size(int width, int height, int proportional, int can_expand)
+  */
+Fl_Scalable_Image::Fl_Scalable_Image(Fl_RGB_Image *source, int width, int height,
+                                     int proportional, int can_expand) : Fl_Image(width, height, 0) {
+  Fl_Scalable_Image_(source, width, height, proportional, can_expand);
+  from_shared_ = NULL;
+}
+
+/** Sets the drawing width and height of the scalable image 
+ \param width,height   maximum width and height (in drawing units) to which the source image is rescaled when drawn
+ \param proportional   if not nul, keep the width and height of the scalable image proportional
+ to those of the source image
+ \param can_expand  if nul, the width and height of the scalable image will not exceed those of the source image
+*/
+void Fl_Scalable_Image::size(int width, int height, int proportional, int can_expand)
+{
+  w(width);
+  h(height);
+  if (!image_) return;
+  float fw = image_->w() / float(width);
+  float fh = image_->h() / float(height);
+  if (proportional) {
+    if (fh > fw) fw = fh;
+    else fh = fw;
+  }
+  if (!can_expand) {
+    if (fw < 1) fw = 1;
+    if (fh < 1) fh = 1;
+  }
+  w(image_->w() / fw);
+  h(image_->h() / fh);
+}
+
+/** Draws the scalable image scaling it to its own size.
+ The image is scaled to the width \em w() and the height \em h(), 
+ drawn with its top-left corner at <em>X-cx, Y-cy</em>, and clipped to the rectangle <em>X, Y, W, H</em>.
+ */
+void Fl_Scalable_Image::draw(int X, int Y, int W, int H, int cx, int cy) {
+  int use_scaled_image = 0;
+  if (!image_) return;
+  fl_push_clip(X, Y, W, H);
+  if (image_->d() == 0 || image_->count() >= 2) { // handles Fl_Bitmap or Fl_Pixmap
+    if (w() == image_->w() && h() == image_->h()) image_->draw(X-cx, Y-cy);
+    else use_scaled_image = 1;
+    }
+  else { // handles Fl_RGB_Image
+#if FLTK_ABI_VERSION >= 10304
+    int err = fl_graphics_driver->draw_scaled((Fl_RGB_Image*)image_, X-cx, Y-cy, w(), h());
+    if (err) use_scaled_image = 1;
+#else
+    Fl_RGB_Image *rgb = (Fl_RGB_Image*)image_;
+    const char *name = fl_graphics_driver->class_name();
+#ifdef WIN32
+    if (name == Fl_GDI_Printer_Graphics_Driver::class_id) {
+      ((Fl_GDI_Printer_Graphics_Driver*)fl_graphics_driver)->draw_scaled(rgb, X-cx, Y-cy, w(), h());
+    } else
+#elif defined(__APPLE__)
+    if (name == Fl_Quartz_Graphics_Driver::class_id) {
+      ((Fl_Quartz_Graphics_Driver*)fl_graphics_driver)->draw_scaled(rgb, X-cx, Y-cy, w(), h());
+    } else
+#endif // WIN32
+    if (name == Fl_PostScript_Graphics_Driver::class_id) {
+      ((Fl_PostScript_Graphics_Driver*)fl_graphics_driver)->draw_scaled(rgb, X-cx, Y-cy, w(), h());
+    } else {
+      use_scaled_image = 1;
+    }
+#endif // FLTK_ABI_VERSION
+  }
+  if (use_scaled_image) {
+    if (scaled_image_ && (scaled_image_->w() != w() || scaled_image_->h() != h())) {
+      delete scaled_image_;
+      scaled_image_ = NULL;
+    }
+    if (!scaled_image_) scaled_image_ = image_->copy(w(), h());
+    scaled_image_->draw(X-cx, Y-cy);
+  }
+  fl_pop_clip();
+}
+
+/** Copies the scalable image to a new one with new size <em>W, H</em>
+ 
+ The source image of the copy is the same as that of the initial source image.
+ */
+Fl_Image *Fl_Scalable_Image::copy(int W, int H) {
+  Fl_Scalable_Image *copy = new Fl_Scalable_Image((Fl_RGB_Image*)NULL, W, H);
+  copy->image_ = image_;
+  if (from_shared_) copy->from_shared_ =
+    Fl_Shared_Image::find(from_shared_->name(), from_shared_->w(), from_shared_->h());
+  else copy->from_shared_ = NULL;
+  return copy;
+}
+
+void Fl_Scalable_Image::color_average(Fl_Color c,	// I - Color to blend with
+                                      float    i) {	// I - Blend fraction
+  if (from_shared_) {
+    from_shared_->color_average(c, i);
+    image_ = from_shared_->image_;
+  }
+  else {
+    image_->color_average(c, i);
+  }
+}
+
+void Fl_Scalable_Image::desaturate() {
+  if (from_shared_) {
+    from_shared_->desaturate();
+    image_ = from_shared_->image_;
+  }
+  else {
+    image_->desaturate();
+  }
+}
+
+void Fl_Scalable_Image::uncache()
+{
+  if (image_) image_->uncache();
+  if (!scaled_image_) delete scaled_image_;
+}
+
+/** Destructor.
+ Deleting a scalable image obtained with any of the two constructors will not delete its \p source image
+ */
+Fl_Scalable_Image::~Fl_Scalable_Image() {
+  if (from_shared_) from_shared_->release();
+  else if (alloc_image) delete image_;
+  if (scaled_image_) delete scaled_image_;
+}
+
 //
 // End of "$Id$".
 //
Index: src/ps_image.cxx
===================================================================
--- src/ps_image.cxx	(revision 10483)
+++ src/ps_image.cxx	(working copy)
@@ -456,6 +456,19 @@
   mask=0;
 }
 
+int Fl_PostScript_Graphics_Driver::draw_scaled(Fl_RGB_Image *rgb, int XP, int YP, int WP, int HP){
+  int X, Y, W, H;
+  clip_box(XP,YP,WP,HP,X,Y,W,H); // X,Y,W,H will give the unclipped area of XP,YP,WP,HP
+  if (W == 0 || H == 0) return 0;
+  push_no_clip(); // remove the FLTK clip that can't be rescaled
+  clocale_printf("%d %d %i %i CL\n", X, Y, W, H);
+  clocale_printf("GS %d %d TR  %f %f SC GS\n", XP, YP, float(WP)/rgb->w(), float(HP)/rgb->h());
+  draw(rgb, 0, 0, rgb->w(), rgb->h(), 0, 0);
+  clocale_printf("GR GR\n");
+  pop_clip(); // restore FLTK's clip
+  return 0;
+}
+
 void Fl_PostScript_Graphics_Driver::draw(Fl_Bitmap * bitmap,int XP, int YP, int WP, int HP, int cx, int cy){
   const uchar  * di = bitmap->array;
   int w,h;
Index: test/pixmap_browser.cxx
===================================================================
--- test/pixmap_browser.cxx	(revision 10483)
+++ test/pixmap_browser.cxx	(working copy)
@@ -3,7 +3,7 @@
 //
 // A shared image test program for the Fast Light Tool Kit (FLTK).
 //
-// Copyright 1998-2010 by Bill Spitzak and others.
+// Copyright 1998-2015 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
@@ -21,6 +21,7 @@
 #include <FL/Fl_Double_Window.H>
 #include <FL/Fl_Button.H>
 #include <FL/Fl_Shared_Image.H>
+#include <FL/Fl_Printer.H>
 #include <string.h>
 #include <errno.h>
 #include <FL/Fl_File_Chooser.H>
@@ -35,8 +36,8 @@
 
 void load_file(const char *n) {
   if (img) {
-    img->release();
     img = 0L;
+    delete b->image();
   }
   if (fl_filename_isdir(n)) {
     b->label("@fileopen"); // show a generic folder
@@ -46,8 +47,8 @@
     b->redraw();
     return;
   }
-  img = Fl_Shared_Image::get(n);
-  if (!img) {
+  Fl_Shared_Image *img2 = Fl_Shared_Image::get(n);
+  if (!img2) {
     b->label("@filenew"); // show an empty document
     b->labelsize(64);
     b->labelcolor(FL_LIGHT2);
@@ -55,18 +56,12 @@
     b->redraw();
     return;
   }
-  if (img->w() > b->w() || img->h() > b->h()) {
-    Fl_Image *temp;
-    if (img->w() > img->h()) temp = img->copy(b->w(), b->h() * img->h() / img->w());
-    else temp = img->copy(b->w() * img->w() / img->h(), b->h());
-
-    img->release();
-    img = (Fl_Shared_Image *)temp;
-  }
-  b->label(name);
+  img = img2;
   b->labelsize(14);
   b->labelcolor(FL_FOREGROUND_COLOR);
-  b->image(img);
+  b->image(new Fl_Scalable_Image(img, b->w(), b->h()));
+  img->release();
+  b->label(NULL);
   b->redraw();
 }
 
@@ -84,6 +79,21 @@
   fl_file_chooser_callback(0);
 }
 
+void print_cb(Fl_Widget *widget, void *) {
+  Fl_Printer printer;
+  int width, height;
+  printer.start_job(1);
+  printer.start_page();
+  printer.printable_rect(&width, &height);
+  float fw = widget->window()->decorated_w() / float(width);
+  float fh = widget->window()->decorated_h() / float(height);
+  if (fh > fw) fw = fh;
+  printer.scale(1/fw);
+  printer.print_window(widget->window());
+  printer.end_page();
+  printer.end_job();
+}
+
 int dvisual = 0;
 int arg(int, char **argv, int &i) {
   if (argv[i][1] == '8') {dvisual = 1; i++; return 1;}
@@ -97,15 +107,18 @@
 
   Fl::args(argc,argv,i,arg);
 
-  Fl_Double_Window window(400,435); ::w = &window;
+  Fl_Double_Window window(400,450); ::w = &window;
   Fl_Box b(10,45,380,380); ::b = &b;
   b.box(FL_THIN_DOWN_BOX);
-  b.align(FL_ALIGN_INSIDE|FL_ALIGN_CENTER);
+  b.align(FL_ALIGN_INSIDE|FL_ALIGN_CENTER|FL_ALIGN_CLIP);
   Fl_Button button(150,5,100,30,"load");
   button.callback(button_cb);
   if (!dvisual) Fl::visual(FL_RGB);
   if (argv[1]) load_file(argv[1]);
-  window.resizable(window);
+  window.resizable(b);
+  Fl_Button print(300,425,50,25,"Print");
+  print.callback(print_cb);
+
   window.show(argc,argv);
   return Fl::run();
 }
