Browse Source

auto-scale textures at load time, without loading full-res and then downrezzing

David Rose 18 years ago
parent
commit
161e2cd5b3

+ 42 - 11
panda/src/gobj/texture.cxx

@@ -1979,6 +1979,7 @@ do_read_one(const Filename &fullpath, const Filename &alpha_fullpath,
       x_size = 1;
       y_size = 1;
     }
+
     image = PNMImage(x_size, y_size, image.get_num_channels(), 
                      image.get_maxval(), image.get_type());
     image.fill(0.2, 0.3, 1.0);
@@ -1987,6 +1988,19 @@ do_read_one(const Filename &fullpath, const Filename &alpha_fullpath,
     }
 
   } else {
+    if (!image.read_header(fullpath, NULL, false)) {
+      gobj_cat.error()
+        << "Texture::read() - couldn't read: " << fullpath << endl;
+      return false;
+    }
+
+    if (z == 0 && n == 0) {
+      consider_rescale(image, fullpath.get_basename());
+    } else {
+      image.set_read_size(get_expected_mipmap_x_size(n),
+                          get_expected_mipmap_y_size(n));
+    }
+
     if (!image.read(fullpath, NULL, false)) {
       gobj_cat.error()
         << "Texture::read() - couldn't read: " << fullpath << endl;
@@ -2021,6 +2035,22 @@ do_read_one(const Filename &fullpath, const Filename &alpha_fullpath,
       }
       
     } else {
+      if (!alpha_image.read_header(alpha_fullpath, NULL, true)) {
+        gobj_cat.error()
+          << "Texture::read() - couldn't read (alpha): " << alpha_fullpath << endl;
+        return false;
+      }
+
+      if (image.get_x_size() != alpha_image.get_x_size() ||
+          image.get_y_size() != alpha_image.get_y_size()) {
+        gobj_cat.info()
+          << "Implicitly rescaling " << alpha_fullpath.get_basename()
+          << " from " << alpha_image.get_x_size() << " by "
+          << alpha_image.get_y_size() << " to " << image.get_x_size()
+          << " by " << image.get_y_size() << "\n";
+        alpha_image.set_read_size(image.get_x_size(), image.get_y_size());
+      }
+
       if (!alpha_image.read(alpha_fullpath, NULL, true)) {
         gobj_cat.error()
           << "Texture::read() - couldn't read (alpha): " << alpha_fullpath << endl;
@@ -2045,13 +2075,12 @@ do_read_one(const Filename &fullpath, const Filename &alpha_fullpath,
     
     set_fullpath(fullpath);
     set_alpha_fullpath(alpha_fullpath);
-    
-    consider_rescale(image, fullpath.get_basename());
   }
 
   if (!alpha_fullpath.empty()) {
-    // The grayscale (alpha channel) image must be the same size as the
-    // main image.
+    // The grayscale (alpha channel) image must be the same size as
+    // the main image.  This should really have been already
+    // guaranteed by the above.
     if (image.get_x_size() != alpha_image.get_x_size() ||
         image.get_y_size() != alpha_image.get_y_size()) {
       gobj_cat.info()
@@ -2818,8 +2847,13 @@ clear_prepared(PreparedGraphicsObjects *prepared_objects) {
 ////////////////////////////////////////////////////////////////////
 //     Function: Texture::consider_rescale
 //       Access: Private
-//  Description: Scales the PNMImage according to the whims of the
-//               Config.prc file.
+//  Description: Asks the PNMImage to change its scale when it reads
+//               the image, according to the whims of the Config.prc
+//               file.
+//
+//               This method should be called after
+//               pnmimage.read_header() has been called, but before
+//               pnmimage.read().
 ////////////////////////////////////////////////////////////////////
 void Texture::
 consider_rescale(PNMImage &pnmimage, const string &name) {
@@ -2877,14 +2911,11 @@ consider_rescale(PNMImage &pnmimage, const string &name) {
   if (pnmimage.get_x_size() != new_x_size ||
       pnmimage.get_y_size() != new_y_size) {
     gobj_cat.info()
-      << "Automatically rescaling " << name << " from "
+      << "Implicitly rescaling " << name << " from "
       << pnmimage.get_x_size() << " by " << pnmimage.get_y_size() << " to "
       << new_x_size << " by " << new_y_size << "\n";
 
-    PNMImage scaled(new_x_size, new_y_size, pnmimage.get_num_channels(),
-                    pnmimage.get_maxval(), pnmimage.get_type());
-    scaled.quick_filter_from(pnmimage);
-    pnmimage = scaled;
+    pnmimage.set_read_size(new_x_size, new_y_size);
   }
 }
 

+ 35 - 0
panda/src/pnmimage/pnmImage.I

@@ -25,6 +25,8 @@ INLINE PNMImage::
 PNMImage() {
   _array = NULL;
   _alpha = NULL;
+
+  clear();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -155,6 +157,39 @@ alpha_fill(double alpha) {
   alpha_fill_val((xelval)(alpha * get_maxval()));
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PNMImage::set_read_size
+//       Access: Published
+//  Description: Specifies the size to we'd like to scale the image
+//               upon reading it.  This will affect the next call to
+//               read().  This is usually used to reduce the image
+//               size, e.g. for a thumbnail.
+//
+//               If the file type reader supports it (e.g. JPEG), then
+//               this will scale the image during the read operation,
+//               consequently reducing memory and CPU utilization.  If
+//               the file type reader does not support it, this will
+//               load the image normally, and them perform a linear
+//               scale after it has been loaded.
+////////////////////////////////////////////////////////////////////
+INLINE void PNMImage::
+set_read_size(int x_size, int y_size) {
+  _read_x_size = x_size;
+  _read_y_size = y_size;
+  _has_read_size = true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PNMImage::clear_read_size
+//       Access: Published
+//  Description: Undoes the effect of a previous call to
+//               set_read_size().
+////////////////////////////////////////////////////////////////////
+INLINE void PNMImage::
+clear_read_size() {
+  _has_read_size = false;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PNMImage::is_valid
 //       Access: Published

+ 29 - 9
panda/src/pnmimage/pnmImage.cxx

@@ -31,6 +31,7 @@ PNMImage::
 PNMImage(const Filename &filename, PNMFileType *type) {
   _array = NULL;
   _alpha = NULL;
+  _has_read_size = false;
 
   bool result = read(filename, type);
   if (!result) {
@@ -61,6 +62,7 @@ clear() {
   _maxval = 255;
   _comment.clear();
   _type = (PNMFileType *)NULL;
+  _has_read_size = false;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -81,6 +83,7 @@ clear(int x_size, int y_size, int num_channels,
   _maxval = maxval;
   _comment.clear();
   _type = type;
+  _has_read_size = false;
 
   if (has_alpha()) {
     allocate_alpha();
@@ -220,13 +223,12 @@ remix_channels(const LMatrix4f &conv) {
 ////////////////////////////////////////////////////////////////////
 bool PNMImage::
 read(const Filename &filename, PNMFileType *type, bool report_unknown_type) {
-  clear();
-
-  PNMReader *reader = PNMImageHeader::make_reader(filename, type,
-                                                  report_unknown_type);
+  PNMReader *reader = make_reader(filename, type, report_unknown_type);
   if (reader == (PNMReader *)NULL) {
+    clear();
     return false;
   }
+
   return read(reader);
 }
 
@@ -247,11 +249,10 @@ read(const Filename &filename, PNMFileType *type, bool report_unknown_type) {
 bool PNMImage::
 read(istream &data, const string &filename, PNMFileType *type,
      bool report_unknown_type) {
-  clear();
-
   PNMReader *reader = PNMImageHeader::make_reader
     (&data, false, filename, string(), type, report_unknown_type);
   if (reader == (PNMReader *)NULL) {
+    clear();
     return false;
   }
   return read(reader);
@@ -272,6 +273,10 @@ read(istream &data, const string &filename, PNMFileType *type,
 ////////////////////////////////////////////////////////////////////
 bool PNMImage::
 read(PNMReader *reader) {
+  bool has_read_size = _has_read_size;
+  int read_x_size = _read_x_size;
+  int read_y_size = _read_y_size;
+
   clear();
 
   if (reader == NULL) {
@@ -283,6 +288,11 @@ read(PNMReader *reader) {
     return false;
   }
 
+  if (has_read_size) {
+    reader->set_read_size(read_x_size, read_y_size);
+  }
+  reader->prepare_read();
+
   copy_header_from(*reader);
 
   // We reassign y_size after reading because we might have read a
@@ -293,10 +303,20 @@ read(PNMReader *reader) {
   if (_y_size == 0) {
     clear();
     return false;
-  } else {
-    setup_rc();
-    return true;
   }
+
+  setup_rc();
+
+  if (has_read_size && (_x_size != read_x_size || _y_size != read_y_size)) {
+    // The Reader didn't comply with our size request.  Do the sizing
+    // explicitly, then.
+    PNMImage new_image(read_x_size, read_y_size, get_num_channels(),
+                       get_maxval(), get_type());
+    new_image.quick_filter_from(*this);
+    take_from(new_image);
+  }
+
+  return true;
 }
 
 ////////////////////////////////////////////////////////////////////

+ 6 - 0
panda/src/pnmimage/pnmImage.h

@@ -91,6 +91,9 @@ PUBLISHED:
   INLINE void alpha_fill(double alpha = 0.0);
   void alpha_fill_val(xelval alpha = 0);
 
+  INLINE void set_read_size(int x_size, int y_size);
+  INLINE void clear_read_size();
+
   bool read(const Filename &filename, PNMFileType *type = NULL,
             bool report_unknown_type = true);
   bool read(istream &data, const string &filename = string(), 
@@ -239,6 +242,9 @@ private:
   xel *_array;
   xelval *_alpha;
   double _default_rc, _default_gc, _default_bc;
+
+  int _read_x_size, _read_y_size;
+  bool _has_read_size;
 };
 
 #include "pnmImage.I"

+ 3 - 2
panda/src/pnmimage/pnmImageHeader.cxx

@@ -35,8 +35,9 @@
 //               false.
 ////////////////////////////////////////////////////////////////////
 bool PNMImageHeader::
-read_header(const Filename &filename, PNMFileType *type) {
-  PNMReader *reader = make_reader(filename, type);
+read_header(const Filename &filename, PNMFileType *type,
+            bool report_unknown_type) {
+  PNMReader *reader = make_reader(filename, type, report_unknown_type);
   if (reader != (PNMReader *)NULL) {
     (*this) = (*reader);
     delete reader;

+ 2 - 1
panda/src/pnmimage/pnmImageHeader.h

@@ -82,7 +82,8 @@ PUBLISHED:
   INLINE PNMFileType *get_type() const;
   INLINE void set_type(PNMFileType *type);
 
-  bool read_header(const Filename &filename, PNMFileType *type = NULL);
+  bool read_header(const Filename &filename, PNMFileType *type = NULL,
+                   bool report_unknown_type = true);
 
   PNMReader *make_reader(const Filename &filename,
                          PNMFileType *type = NULL,

+ 19 - 1
panda/src/pnmimage/pnmReader.I

@@ -26,10 +26,28 @@ PNMReader(PNMFileType *type, istream *file, bool owns_file) :
   _type(type),
   _owns_file(owns_file),
   _file(file),
-  _is_valid(true)
+  _is_valid(true),
+  _has_read_size(false)
 {
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PNMReader::set_read_size
+//       Access: Public
+//  Description: Instructs the reader to attempt to scale the image to
+//               the indicated size while reading it.  The reader may
+//               or may not follow this suggestion, or may follow it
+//               only partially (e.g. by reading a file which is
+//               slightly reduced in size, but not the precise size
+//               requested).
+////////////////////////////////////////////////////////////////////
+void PNMReader::
+set_read_size(int x_size, int y_size) {
+  _read_x_size = x_size;
+  _read_y_size = y_size;
+  _has_read_size = true;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PNMReader::get_type
 //       Access: Public

+ 172 - 5
panda/src/pnmimage/pnmReader.cxx

@@ -39,6 +39,40 @@ PNMReader::
   _file = (istream *)NULL;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PNMReader::prepare_read
+//       Access: Public, Virtual
+//  Description: This method will be called before read_data() or
+//               read_row() is called.  It instructs the reader to
+//               initialize its data structures as necessary to
+//               actually perform the read operation.  
+//
+//               After this call, _x_size and _y_size should reflect
+//               the actual size that will be filled by read_data()
+//               (as possibly modified by set_read_size()).
+////////////////////////////////////////////////////////////////////
+void PNMReader::
+prepare_read() {
+  if (!_is_valid) {
+    return;
+  }
+
+  _x_shift = 0;
+  _y_shift = 0;
+  _orig_x_size = _x_size;
+  _orig_y_size = _y_size;
+
+  if (supports_read_row()) {
+    // Maybe we can quick-filter each row down as we go.
+    if (_has_read_size) {
+      _x_shift = get_reduction_shift(_x_size, _read_x_size);
+      _x_size = _x_size / (1 << _x_shift);
+      _y_shift = get_reduction_shift(_y_size, _read_y_size);
+      _y_size = _y_size / (1 << _y_shift);
+    }
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PNMReader::read_data
 //       Access: Public, Virtual
@@ -58,12 +92,102 @@ read_data(xel *array, xelval *alpha) {
     return 0;
   }
 
-  int y;
-  for (y = 0; y < _y_size; y++) {
-    if (!read_row(array + y * _x_size, alpha + y * _x_size)) {
-      return y;
+  if (_x_shift == 0 && _y_shift == 0) {
+    // Read with no reduction.
+    int y;
+    for (y = 0; y < _y_size; ++y) {
+      if (!read_row(array + y * _x_size, alpha + y * _x_size, _x_size, _y_size)) {
+        return y;
+      }
+    }
+
+  } else {
+    int x_reduction = (1 << _x_shift);
+    int y_reduction = (1 << _y_shift);
+    
+    int shift = _x_shift + _y_shift;
+
+    // We need a temporary buffer, at least one row wide, with
+    // full-width integers, for accumulating pixel data.
+    int *accum_row_array = (int *)alloca(_orig_x_size * sizeof(int) * 3);
+    int *accum_row_alpha = (int *)alloca(_orig_x_size * sizeof(int));
+
+    // Each time we read a row, we will actually read the full row
+    // here, before we filter it down into the above.
+    xel *orig_row_array = (xel *)alloca(_orig_x_size * sizeof(xel));
+    xelval *orig_row_alpha = (xelval *)alloca(_orig_x_size * sizeof(xelval));
+    
+    int y;
+    for (y = 0; y < _y_size; ++y) {
+      // Zero out the accumulation data, in preparation for
+      // holding the results of the below.
+      memset(accum_row_array, 0, _x_size * sizeof(int) * 3);
+      if (has_alpha()) {
+        memset(accum_row_alpha, 0, _x_size * sizeof(int));
+      }
+
+      for (int yi = 0; yi < y_reduction; ++yi) {
+        // OK, read a row.  This reads the original, full-size row.
+        if (!read_row(orig_row_array, orig_row_alpha, _orig_x_size, _orig_y_size)) {
+          return y;
+        }
+
+        // Boil that row down to its proper, reduced size, and
+        // accumulate it into the target row.
+        xel *p = orig_row_array;
+        int *q = accum_row_array;
+        int *qstop = q + _x_size * 3;
+        while (q < qstop) {
+          for (int xi = 0; xi < x_reduction; ++xi) {
+            q[0] += (*p).r;
+            q[1] += (*p).g;
+            q[2] += (*p).b;
+            ++p;
+          }
+          q += 3;
+        }
+        if (has_alpha()) {
+          // Now do it again for the alpha channel.
+          xelval *p = orig_row_alpha;
+          int *q = accum_row_alpha;
+          int *qstop = q + _x_size;
+          while (q < qstop) {
+            for (int xi = 0; xi < x_reduction; ++xi) {
+              (*q) += (*p);
+              ++p;
+            }
+            ++q;
+          }
+        }
+      }
+
+      // OK, now copy the accumulated pixel data into the final
+      // result.
+      xel *target_row_array = array + y * _x_size;
+      xelval *target_row_alpha = alpha + y * _x_size;
+
+      int *p = accum_row_array;
+      xel *q = target_row_array;
+      xel *qstop = q + _x_size;
+      while (q < qstop) {
+        (*q).r = (*p++) >> shift;
+        (*q).g = (*p++) >> shift;
+        (*q).b = (*p++) >> shift;
+        ++q;
+      }
+
+      if (has_alpha()) {
+        int *p = accum_row_alpha;
+        xelval *q = target_row_alpha;
+        xelval *qstop = q + _x_size;
+        while (q < qstop) {
+          (*q) = (*p++) >> shift;
+          ++q;
+        }
+      }
     }
   }
+    
 
   return _y_size;
 }
@@ -91,9 +215,17 @@ supports_read_row() const {
 //               one horizontal row at a time, beginning from the top.
 //               Returns true if the row is successfully read, false
 //               if there is an error or end of file.
+//
+//               The x_size and y_size parameters are the value of
+//               _x_size and _y_size as originally filled in by the
+//               constructor; it is the actual number of pixels in the
+//               image.  (The _x_size and _y_size members may have
+//               been automatically modified by the time this method
+//               is called if we are scaling on load, so should not be
+//               used.)
 ////////////////////////////////////////////////////////////////////
 bool PNMReader::
-read_row(xel *, xelval *) {
+read_row(xel *, xelval *, int, int) {
   return false;
 }
 
@@ -111,3 +243,38 @@ bool PNMReader::
 supports_stream_read() const {
   return false;
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: PNMReader::get_reduction_shift
+//       Access: Private
+//  Description: Determines the reduction factor between the original
+//               size and the requested size, returned as an exponent
+//               of power of 2 (that is, a bit shift).  
+//
+//               Only power-of-two reductions are supported, since
+//               those are common, easy, and fast.  Other reductions
+//               will be handled in the higher level code.
+////////////////////////////////////////////////////////////////////
+int PNMReader::
+get_reduction_shift(int orig_size, int new_size) {
+  if (new_size == 0) {
+    return 0;
+  }
+
+  int reduction = max(orig_size / new_size, 1);
+
+  int shift = 0;
+
+  int r = 2;
+  while (r <= reduction) {
+    shift += 1;
+    r <<= 1;
+  }
+
+  if ((orig_size % r) != 0) {
+    // If the reduction isn't even, never mind.
+    shift = 0;
+  }
+
+  return shift;
+}

+ 12 - 1
panda/src/pnmimage/pnmReader.h

@@ -38,22 +38,33 @@ protected:
 
 public:
   virtual ~PNMReader();
+  INLINE void set_read_size(int x_size, int y_size);
 
   INLINE PNMFileType *get_type() const;
 
+  virtual void prepare_read();
   virtual int read_data(xel *array, xelval *alpha);
   virtual bool supports_read_row() const;
-  virtual bool read_row(xel *array, xelval *alpha);
+  virtual bool read_row(xel *array, xelval *alpha, int x_size, int y_size);
 
   virtual bool supports_stream_read() const;
 
   INLINE bool is_valid() const;
 
+private:
+  int get_reduction_shift(int orig_size, int new_size);
+
 protected:
   PNMFileType *_type;
   bool _owns_file;
   istream *_file;
   bool _is_valid;
+
+  int _read_x_size, _read_y_size;
+  bool _has_read_size;
+
+  int _x_shift, _y_shift;
+  int _orig_x_size, _orig_y_size;
 };
 
 #include "pnmReader.I"

+ 0 - 12
panda/src/pnmimagetypes/config_pnmimagetypes.cxx

@@ -90,18 +90,6 @@ ConfigVariableInt jpeg_quality
           "size)."));
 
 
-// These control the scaling that is automatically performed on a JPEG
-// file for decompression.  You might specify to scale down by a
-// fraction, e.g. 1/8, by specifying jpeg_scale_num = 1 and
-// jpeg_scale_denom = 8.  This will reduce decompression time
-// correspondingly.  Attempting to use this to scale up, or to scale
-// by any fraction other than an even power of two, may not be
-// supported.
-ConfigVariableInt jpeg_scale_num
-("jpeg-scale-num", 1);
-ConfigVariableInt jpeg_scale_denom
-("jpeg-scale-denom", 1);
-
 ConfigVariableInt bmp_bpp
 ("bmp-bpp", 0,
  PRC_DESC("This controls how many bits per pixel are written out for BMP "

+ 0 - 2
panda/src/pnmimagetypes/config_pnmimagetypes.h

@@ -52,8 +52,6 @@ extern ConfigVariableBool tga_colormap;
 extern ConfigVariableBool tga_grayscale;
 
 extern ConfigVariableInt jpeg_quality;
-extern ConfigVariableInt jpeg_scale_num;
-extern ConfigVariableInt jpeg_scale_denom;
 
 extern ConfigVariableInt bmp_bpp;
 

+ 3 - 3
panda/src/pnmimagetypes/pnmFileTypeAlias.cxx

@@ -229,7 +229,7 @@ supports_read_row() const {
 //               if there is an error or end of file.
 ////////////////////////////////////////////////////////////////////
 bool PNMFileTypeAlias::Reader::
-read_row(xel *row_data, xelval *) {
+read_row(xel *row_data, xelval *, int x_size, int) {
   if (!is_valid()) {
     return false;
   }
@@ -239,9 +239,9 @@ read_row(xel *row_data, xelval *) {
   unsigned char red, grn, blu;
 
   x = 0;
-  while (x < _x_size) {
+  while (x < x_size) {
     num = read_uchar_ALIAS(_file);
-    if (num==0 || x+num > _x_size) {
+    if (num==0 || x+num > x_size) {
       return false;
     }
     blu = read_uchar_ALIAS(_file);

+ 1 - 1
panda/src/pnmimagetypes/pnmFileTypeAlias.h

@@ -49,7 +49,7 @@ public:
     Reader(PNMFileType *type, istream *file, bool owns_file, string magic_number);
 
     virtual bool supports_read_row() const;
-    virtual bool read_row(xel *array, xelval *alpha);
+    virtual bool read_row(xel *array, xelval *alpha, int x_size, int y_size);
   };
 
   class Writer : public PNMWriter {

+ 2 - 2
panda/src/pnmimagetypes/pnmFileTypeIMG.cxx

@@ -254,10 +254,10 @@ supports_read_row() const {
 //               if there is an error or end of file.
 ////////////////////////////////////////////////////////////////////
 bool PNMFileTypeIMG::Reader::
-read_row(xel *row_data, xelval *) {
+read_row(xel *row_data, xelval *, int x_size, int) {
   int x;
   xelval red, grn, blu;
-  for (x = 0; x < _x_size; x++) {
+  for (x = 0; x < x_size; x++) {
     red = read_uchar_IMG(_file);
     grn = read_uchar_IMG(_file);
     blu = read_uchar_IMG(_file);

+ 1 - 1
panda/src/pnmimagetypes/pnmFileTypeIMG.h

@@ -49,7 +49,7 @@ public:
     Reader(PNMFileType *type, istream *file, bool owns_file, string magic_number);
 
     virtual bool supports_read_row() const;
-    virtual bool read_row(xel *array, xelval *alpha);
+    virtual bool read_row(xel *array, xelval *alpha, int x_size, int y_size);
   };
 
   class Writer : public PNMWriter {

+ 1 - 0
panda/src/pnmimagetypes/pnmFileTypeJPG.h

@@ -73,6 +73,7 @@ public:
     Reader(PNMFileType *type, istream *file, bool owns_file, string magic_number);
     ~Reader();
 
+    virtual void prepare_read();
     virtual int read_data(xel *array, xelval *alpha);
 
   private:

+ 29 - 3
panda/src/pnmimagetypes/pnmFileTypeJPGReader.cxx

@@ -295,9 +295,36 @@ Reader(PNMFileType *type, istream *file, bool owns_file, string magic_number) :
    * See libjpeg.doc for more info.
    */
 
+  _num_channels = _cinfo.num_components;
+  _x_size = (int)_cinfo.image_width;
+  _y_size = (int)_cinfo.image_height;
+  _maxval = MAXJSAMPLE;
+
   /* Step 6: set parameters for decompression */
-  _cinfo.scale_num = jpeg_scale_num;
-  _cinfo.scale_denom = jpeg_scale_denom;
+  _cinfo.scale_num = 1;
+  _cinfo.scale_denom = 1;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PNMFileTypeJPG::Reader::prepare_read
+//       Access: Public, Virtual
+//  Description: This method will be called before read_data() or
+//               read_row() is called.  It instructs the reader to
+//               initialize its data structures as necessary to
+//               actually perform the read operation.  
+//
+//               After this call, _x_size and _y_size should reflect
+//               the actual size that will be filled by read_data()
+//               (as possibly modified by set_read_size()).
+////////////////////////////////////////////////////////////////////
+void PNMFileTypeJPG::Reader::
+prepare_read() {
+  if (_has_read_size && _read_x_size != 0 && _read_y_size != 0) {
+    // Attempt to get the scale close to our target scale.
+    int x_reduction = _cinfo.image_width / _read_x_size;
+    int y_reduction = _cinfo.image_height / _read_y_size;
+    _cinfo.scale_denom = max(min(x_reduction, y_reduction), 1);
+  }
 
   /* Step 7: Start decompressor */
 
@@ -309,7 +336,6 @@ Reader(PNMFileType *type, istream *file, bool owns_file, string magic_number) :
   _num_channels = _cinfo.output_components;
   _x_size = (int)_cinfo.output_width;
   _y_size = (int)_cinfo.output_height;
-  _maxval = MAXJSAMPLE;
 }
 
 ////////////////////////////////////////////////////////////////////

+ 1 - 1
panda/src/pnmimagetypes/pnmFileTypeSGI.h

@@ -53,7 +53,7 @@ public:
     virtual ~Reader();
 
     virtual bool supports_read_row() const;
-    virtual bool read_row(xel *array, xelval *alpha);
+    virtual bool read_row(xel *array, xelval *alpha, int x_size, int y_size);
 
     typedef struct {
       long start;     /* offset in file */

+ 10 - 10
panda/src/pnmimagetypes/pnmFileTypeSGIReader.cxx

@@ -182,33 +182,33 @@ supports_read_row() const {
 //               if there is an error or end of file.
 ////////////////////////////////////////////////////////////////////
 bool PNMFileTypeSGI::Reader::
-read_row(xel *row_data, xelval *alpha_data) {
+read_row(xel *row_data, xelval *alpha_data, int x_size, int y_size) {
   if (!is_valid()) {
     return false;
   }
   nassertr(current_row >= 0, false);
 
-  ScanElem *red = (ScanElem *)alloca(_x_size * sizeof(ScanElem));
-  ScanElem *grn = (ScanElem *)alloca(_x_size * sizeof(ScanElem));
-  ScanElem *blu = (ScanElem *)alloca(_x_size * sizeof(ScanElem));
-  ScanElem *alpha = (ScanElem *)alloca(_x_size * sizeof(ScanElem));
+  ScanElem *red = (ScanElem *)alloca(x_size * sizeof(ScanElem));
+  ScanElem *grn = (ScanElem *)alloca(x_size * sizeof(ScanElem));
+  ScanElem *blu = (ScanElem *)alloca(x_size * sizeof(ScanElem));
+  ScanElem *alpha = (ScanElem *)alloca(x_size * sizeof(ScanElem));
 
-  read_channel(_file, _x_size, _y_size, _num_channels, bpc, table, red,
+  read_channel(_file, x_size, y_size, _num_channels, bpc, table, red,
                table_start, 0, current_row);
 
   if (!is_grayscale()) {
-    read_channel(_file, _x_size, _y_size, _num_channels, bpc, table, grn,
+    read_channel(_file, x_size, y_size, _num_channels, bpc, table, grn,
                  table_start, 1, current_row);
-    read_channel(_file, _x_size, _y_size, _num_channels, bpc, table, blu,
+    read_channel(_file, x_size, y_size, _num_channels, bpc, table, blu,
                  table_start, 2, current_row);
   }
 
   if (has_alpha()) {
-    read_channel(_file, _x_size, _y_size, _num_channels, bpc, table, alpha,
+    read_channel(_file, x_size, y_size, _num_channels, bpc, table, alpha,
                  table_start, _num_channels - 1, current_row);
   }
 
-  for (int x = 0; x < _x_size; x++) {
+  for (int x = 0; x < x_size; x++) {
     if (is_grayscale()) {
       PPM_PUTB(row_data[x], (xelval)red[x]);
     } else {

+ 5 - 5
panda/src/pnmimagetypes/pnmFileTypeSoftImage.cxx

@@ -454,31 +454,31 @@ supports_read_row() const {
 //               if there is an error or end of file.
 ////////////////////////////////////////////////////////////////////
 bool PNMFileTypeSoftImage::Reader::
-read_row(xel *row_data, xelval *alpha_data) {
+read_row(xel *row_data, xelval *alpha_data, int x_size, int) {
   if (!is_valid()) {
     return false;
   }
   switch (soft_color) {
   case rgb:
-    if (!read_scanline(row_data, alpha_data, _x_size, _file,
+    if (!read_scanline(row_data, alpha_data, x_size, _file,
                        read_rgb, rgb_ctype)) {
       return false;
     }
     break;
 
   case rgba:
-    if (!read_scanline(row_data, alpha_data, _x_size, _file,
+    if (!read_scanline(row_data, alpha_data, x_size, _file,
                        read_rgba, rgb_ctype)) {
       return false;
     }
     break;
 
   case rgb_a:
-    if (!read_scanline(row_data, alpha_data, _x_size, _file,
+    if (!read_scanline(row_data, alpha_data, x_size, _file,
                        read_rgb, rgb_ctype)) {
       return false;
     }
-    if (!read_scanline(row_data, alpha_data, _x_size, _file,
+    if (!read_scanline(row_data, alpha_data, x_size, _file,
                        read_alpha, alpha_ctype)) {
       return false;
     }

+ 1 - 1
panda/src/pnmimagetypes/pnmFileTypeSoftImage.h

@@ -52,7 +52,7 @@ public:
     Reader(PNMFileType *type, istream *file, bool owns_file, string magic_number);
 
     virtual bool supports_read_row() const;
-    virtual bool read_row(xel *array, xelval *alpha);
+    virtual bool read_row(xel *array, xelval *alpha, int x_size, int y_size);
 
   private:
     enum { unknown, rgb, rgba, rgb_a } soft_color;

+ 5 - 5
panda/src/pnmimagetypes/pnmFileTypeTIFF.cxx

@@ -592,7 +592,7 @@ supports_read_row() const {
 //               if there is an error or end of file.
 ////////////////////////////////////////////////////////////////////
 bool PNMFileTypeTIFF::Reader::
-read_row(xel *row_data, xelval *alpha_data) {
+read_row(xel *row_data, xelval *alpha_data, int x_size, int) {
   if (!is_valid()) {
     return false;
   }
@@ -639,7 +639,7 @@ read_row(xel *row_data, xelval *alpha_data) {
 
   switch ( photomet ) {
   case PHOTOMETRIC_MINISBLACK:
-    for ( col = 0; col < _x_size; ++col )
+    for ( col = 0; col < x_size; ++col )
       {
         sample = (this->*next_sample)(buf_ptr, bits_left);
         gray = sample;
@@ -661,7 +661,7 @@ read_row(xel *row_data, xelval *alpha_data) {
     break;
 
   case PHOTOMETRIC_MINISWHITE:
-    for ( col = 0; col < _x_size; ++col )
+    for ( col = 0; col < x_size; ++col )
       {
         sample = (this->*next_sample)(buf_ptr, bits_left);
         gray = _maxval - sample;
@@ -685,7 +685,7 @@ read_row(xel *row_data, xelval *alpha_data) {
     break;
 
   case PHOTOMETRIC_PALETTE:
-    for ( col = 0; col < _x_size; ++col )
+    for ( col = 0; col < x_size; ++col )
       {
         sample = (this->*next_sample)(buf_ptr, bits_left);
         row_data[col] = colormap[sample];
@@ -712,7 +712,7 @@ read_row(xel *row_data, xelval *alpha_data) {
     break;
 
   case PHOTOMETRIC_RGB:
-    for ( col = 0; col < _x_size; ++col ) {
+    for ( col = 0; col < x_size; ++col ) {
       sample = (this->*next_sample)(buf_ptr, bits_left);
       r = sample;
       sample = (this->*next_sample)(buf_ptr, bits_left);

+ 1 - 1
panda/src/pnmimagetypes/pnmFileTypeTIFF.h

@@ -60,7 +60,7 @@ public:
     virtual ~Reader();
 
     virtual bool supports_read_row() const;
-    virtual bool read_row(xel *array, xelval *alpha);
+    virtual bool read_row(xel *array, xelval *alpha, int x_size, int y_size);
 
   private:
     xelval next_sample_lt_8(unsigned char *&buf_ptr, int &bits_left) const;