Browse Source

PNMImage sRGB support, use floats instead of doubles

rdb 10 years ago
parent
commit
22120524a2

+ 23 - 22
panda/src/gobj/texture.cxx

@@ -1893,7 +1893,8 @@ consider_rescale(PNMImage &pnmimage, const string &name, AutoTextureScale auto_t
     if (pnmimage.is_valid()) {
       // The image is already loaded.  Rescale on the spot.
       PNMImage new_image(new_x_size, new_y_size, pnmimage.get_num_channels(),
-                         pnmimage.get_maxval());
+                         pnmimage.get_maxval(), pnmimage.get_type(),
+                         pnmimage.get_color_space());
       new_image.quick_filter_from(pnmimage);
       pnmimage.take_from(new_image);
     } else {
@@ -2928,7 +2929,8 @@ do_read_one(CData *cdata, const Filename &fullpath, const Filename &alpha_fullpa
       pfm.clear(x_size, y_size, image.get_num_channels());
     } else {
       image = PNMImage(x_size, y_size, image.get_num_channels(),
-                       image.get_maxval(), image.get_type());
+                       image.get_maxval(), image.get_type(),
+                       image.get_color_space());
       image.fill(0.2, 0.3, 1.0);
       if (image.has_alpha()) {
         image.alpha_fill(1.0);
@@ -2988,7 +2990,8 @@ do_read_one(CData *cdata, const Filename &fullpath, const Filename &alpha_fullpa
       int x_size = image.get_x_size();
       int y_size = image.get_y_size();
       alpha_image = PNMImage(x_size, y_size, alpha_image.get_num_channels(),
-                             alpha_image.get_maxval(), alpha_image.get_type());
+                             alpha_image.get_maxval(), alpha_image.get_type(),
+                             alpha_image.get_color_space());
       alpha_image.fill(1.0);
       if (alpha_image.has_alpha()) {
         alpha_image.alpha_fill(1.0);
@@ -3047,7 +3050,8 @@ do_read_one(CData *cdata, const Filename &fullpath, const Filename &alpha_fullpa
 
       PNMImage scaled(image.get_x_size(), image.get_y_size(),
                       alpha_image.get_num_channels(),
-                      alpha_image.get_maxval(), alpha_image.get_type());
+                      alpha_image.get_maxval(), alpha_image.get_type(),
+                      alpha_image.get_color_space());
       scaled.quick_filter_from(alpha_image);
       Thread::consider_yield();
       alpha_image = scaled;
@@ -3063,21 +3067,14 @@ do_read_one(CData *cdata, const Filename &fullpath, const Filename &alpha_fullpa
   if (!alpha_fullpath.empty()) {
     // Make the original image a 4-component image by taking the
     // grayscale value from the second image.
-
     image.add_alpha();
 
     if (alpha_file_channel == 4 ||
         (alpha_file_channel == 2 && alpha_image.get_num_channels() == 2)) {
-
-      if (!alpha_image.has_alpha()) {
-        gobj_cat.error()
-          << alpha_fullpath.get_basename() << " has no channel " << alpha_file_channel << ".\n";
-      } else {
-        // Use the alpha channel.
-        for (int x = 0; x < image.get_x_size(); x++) {
-          for (int y = 0; y < image.get_y_size(); y++) {
-            image.set_alpha(x, y, alpha_image.get_alpha(x, y));
-          }
+      // Use the alpha channel.
+      for (int x = 0; x < image.get_x_size(); x++) {
+        for (int y = 0; y < image.get_y_size(); y++) {
+          image.set_alpha(x, y, alpha_image.get_alpha(x, y));
         }
       }
       cdata->_alpha_file_channel = alpha_image.get_num_channels();
@@ -3119,7 +3116,8 @@ do_read_one(CData *cdata, const Filename &fullpath, const Filename &alpha_fullpa
         pad_x_size = new_x_size - image.get_x_size();
         pad_y_size = new_y_size - image.get_y_size();
         PNMImage new_image(new_x_size, new_y_size, image.get_num_channels(),
-                           image.get_maxval());
+                           image.get_maxval(), image.get_type(),
+                           image.get_color_space());
         new_image.copy_sub_image(image, 0, new_y_size - image.get_y_size());
         image.take_from(new_image);
       }
@@ -3189,7 +3187,8 @@ do_load_one(CData *cdata, const PNMImage &pnmimage, const string &name, int z, i
       << y_size << "\n";
 
     PNMImage scaled(x_size, y_size, pnmimage.get_num_channels(),
-                    pnmimage.get_maxval(), pnmimage.get_type());
+                    pnmimage.get_maxval(), pnmimage.get_type(),
+                    pnmimage.get_color_space());
     scaled.quick_filter_from(pnmimage);
     Thread::consider_yield();
 
@@ -3580,7 +3579,6 @@ do_read_dds(CData *cdata, istream &in, const string &filename, bool header_only)
         format = F_luminance;
       }
     }
-
   }
 
   do_setup_texture(cdata, texture_type, header.width, header.height, header.depth,
@@ -4912,7 +4910,8 @@ do_rescale_texture(CData *cdata) {
       << "Resizing " << get_name() << " to " << new_x_size << " x "
       << new_y_size << "\n";
     PNMImage new_image(new_x_size, new_y_size, orig_image.get_num_channels(),
-                       orig_image.get_maxval());
+                       orig_image.get_maxval(), orig_image.get_type(),
+                       orig_image.get_color_space());
     new_image.quick_filter_from(orig_image);
 
     do_clear_ram_image(cdata);
@@ -4943,7 +4942,8 @@ do_rescale_texture(CData *cdata) {
         return false;
       }
       PNMImage new_image(new_x_size, new_y_size, orig_image.get_num_channels(),
-                         orig_image.get_maxval());
+                         orig_image.get_maxval(), orig_image.get_type(),
+                         orig_image.get_color_space());
       new_image.copy_sub_image(orig_image, 0, new_y_size - orig_image.get_y_size());
 
       do_clear_ram_image(cdata);
@@ -5021,8 +5021,9 @@ do_clear(CData *cdata) {
 //  Description:
 ////////////////////////////////////////////////////////////////////
 void Texture::
-do_setup_texture(CData *cdata, Texture::TextureType texture_type, int x_size, int y_size,
-                 int z_size, Texture::ComponentType component_type,
+do_setup_texture(CData *cdata, Texture::TextureType texture_type,
+                 int x_size, int y_size, int z_size,
+                 Texture::ComponentType component_type,
                  Texture::Format format) {
   switch (texture_type) {
   case TT_1d_texture:

+ 2 - 0
panda/src/gobj/texture.h

@@ -40,6 +40,8 @@
 #include "cycleDataStageWriter.h"
 #include "pipelineCycler.h"
 #include "samplerState.h"
+#include "pnmImage.h"
+#include "colorSpace.h"
 
 class PNMImage;
 class PfmFile;

+ 2 - 2
panda/src/grutil/geoMipTerrain.cxx

@@ -113,11 +113,11 @@ generate_block(unsigned short mx,
     for (int y = 0; y <= _block_size; y++) {
       if ((x % level) == 0 && (y % level) == 0) {
         if (_has_color_map) {
-          LVecBase4d color = _color_map.get_xel_a(int((mx * _block_size + x)
+          LVecBase4f color = _color_map.get_xel_a(int((mx * _block_size + x)
                                   / double(_xsize) * _color_map.get_x_size()),
                                                       int((my * _block_size + y)
                                   / double(_ysize) * _color_map.get_y_size()));
-          cwriter.add_data4(LCAST(PN_stdfloat, color));
+          cwriter.add_data4f(color);
         }
         vwriter.add_data3(x - 0.5 * _block_size, y - 0.5 * _block_size, get_pixel_value(mx, my, x, y));
         twriter.add_data2((mx * _block_size + x) / double(_xsize - 1),

+ 1 - 1
panda/src/pgraphnodes/spotlight.cxx

@@ -194,7 +194,7 @@ make_spot(int pixel_width, PN_stdfloat full_radius, LColor &fg, LColor &bg) {
     ++num_channels;
   }
   PNMImage image(pixel_width, pixel_width, num_channels);
-  image.render_spot(LCAST(double, fg), LCAST(double, bg), full_radius, 1.0);
+  image.render_spot(LCAST(float, fg), LCAST(float, bg), full_radius, 1.0);
 
   PT(Texture) tex = new Texture("spot");
   tex->load(image);

+ 16 - 17
panda/src/pnmimage/pfmFile.cxx

@@ -346,7 +346,7 @@ load(const PNMImage &pnmimage) {
       for (int yi = 0; yi < pnmimage.get_y_size(); ++yi) {
         for (int xi = 0; xi < pnmimage.get_x_size(); ++xi) {
           PN_float32 *point = &_table[(yi * _x_size + xi) * _num_channels];
-          LRGBColord xel = pnmimage.get_xel(xi, yi);
+          LRGBColorf xel = pnmimage.get_xel(xi, yi);
           point[0] = xel[0];
           point[1] = xel[1];
           point[2] = xel[2];
@@ -360,11 +360,11 @@ load(const PNMImage &pnmimage) {
       for (int yi = 0; yi < pnmimage.get_y_size(); ++yi) {
         for (int xi = 0; xi < pnmimage.get_x_size(); ++xi) {
           PN_float32 *point = &_table[(yi * _x_size + xi) * _num_channels];
-          LRGBColord xel = pnmimage.get_xel(xi, yi);
+          LColorf xel = pnmimage.get_xel_a(xi, yi);
           point[0] = xel[0];
           point[1] = xel[1];
           point[2] = xel[2];
-          point[3] = pnmimage.get_alpha(xi, yi);
+          point[3] = xel[3];
         }
       }
     }
@@ -431,8 +431,7 @@ store(PNMImage &pnmimage) const {
       for (int yi = 0; yi < get_y_size(); ++yi) {
         for (int xi = 0; xi < get_x_size(); ++xi) {
           const LPoint4f &point = get_point4(xi, yi);
-          pnmimage.set_xel(xi, yi, point[0], point[1], point[2]);
-          pnmimage.set_alpha(xi, yi, point[3]);
+          pnmimage.set_xel_a(xi, yi, point[0], point[1], point[2], point[3]);
         }
       }
     }
@@ -1584,8 +1583,8 @@ clear_to_texcoords(int x_size, int y_size) {
 //               the number of points affected.
 ////////////////////////////////////////////////////////////////////
 int PfmFile::
-pull_spot(const LPoint4f &delta, double xc, double yc, 
-          double xr, double yr, double exponent) {
+pull_spot(const LPoint4f &delta, float xc, float yc,
+          float xr, float yr, float exponent) {
   int minx = max((int)cceil(xc - xr), 0);
   int maxx = min((int)cfloor(xc + xr), _x_size - 1);
   int miny = max((int)cceil(yc - yr), 0);
@@ -1594,9 +1593,9 @@ pull_spot(const LPoint4f &delta, double xc, double yc,
   int count = 0;
   for (int yi = miny; yi <= maxy; ++yi) {
     for (int xi = minx; xi <= maxx; ++xi) {
-      double xd = ((double)xi - xc) / xr;
-      double yd = ((double)yi - yc) / yr;
-      double r2 = xd * xd + yd * yd;
+      float xd = ((float)xi - xc) / xr;
+      float yd = ((float)yi - yc) / yr;
+      float r2 = xd * xd + yd * yd;
       if (r2 >= 1.0) {
         continue;
       }
@@ -1955,7 +1954,7 @@ copy_sub_image(const PfmFile &copy, int xto, int yto,
 void PfmFile::
 add_sub_image(const PfmFile &copy, int xto, int yto,
               int xfrom, int yfrom, int x_size, int y_size,
-              double pixel_scale) {
+              float pixel_scale) {
   int xmin, ymin, xmax, ymax;
   setup_sub_image(copy, xto, yto, xfrom, yfrom, x_size, y_size,
                   xmin, ymin, xmax, ymax);
@@ -2022,7 +2021,7 @@ add_sub_image(const PfmFile &copy, int xto, int yto,
 void PfmFile::
 mult_sub_image(const PfmFile &copy, int xto, int yto,
                int xfrom, int yfrom, int x_size, int y_size,
-               double pixel_scale) {
+               float pixel_scale) {
   int xmin, ymin, xmax, ymax;
   setup_sub_image(copy, xto, yto, xfrom, yfrom, x_size, y_size,
                   xmin, ymin, xmax, ymax);
@@ -2076,7 +2075,7 @@ mult_sub_image(const PfmFile &copy, int xto, int yto,
       }
     }
     break;
-  } 
+  }
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -2084,13 +2083,13 @@ mult_sub_image(const PfmFile &copy, int xto, int yto,
 //       Access: Published
 //  Description: Behaves like copy_sub_image(), except the copy pixels
 //               are divided into the pixels of the destination, after
-//               scaling by the specified pixel_scale. 
+//               scaling by the specified pixel_scale.
 //               dest(x, y) = dest(x, y) / (copy(x, y) * pixel_scale).
 ////////////////////////////////////////////////////////////////////
 void PfmFile::
 divide_sub_image(const PfmFile &copy, int xto, int yto,
                  int xfrom, int yfrom, int x_size, int y_size,
-                 double pixel_scale) {
+                 float pixel_scale) {
   int xmin, ymin, xmax, ymax;
   setup_sub_image(copy, xto, yto, xfrom, yfrom, x_size, y_size,
                   xmin, ymin, xmax, ymax);
@@ -2156,7 +2155,7 @@ divide_sub_image(const PfmFile &copy, int xto, int yto,
       }
     }
     break;
-  } 
+  }
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -2166,7 +2165,7 @@ divide_sub_image(const PfmFile &copy, int xto, int yto,
 //               a constant floating-point multiplier value.
 ////////////////////////////////////////////////////////////////////
 void PfmFile::
-operator *= (double multiplier) {
+operator *= (float multiplier) {
   nassertv(is_valid());
 
   switch (_num_channels) {

+ 8 - 8
panda/src/pnmimage/pfmFile.h

@@ -112,8 +112,8 @@ PUBLISHED:
   INLINE const LPoint4f &get_no_data_value() const;
 
   BLOCKING void resize(int new_x_size, int new_y_size);
-  BLOCKING void box_filter_from(double radius, const PfmFile &copy);
-  BLOCKING void gaussian_filter_from(double radius, const PfmFile &copy);
+  BLOCKING void box_filter_from(float radius, const PfmFile &copy);
+  BLOCKING void gaussian_filter_from(float radius, const PfmFile &copy);
   BLOCKING void quick_filter_from(const PfmFile &copy);
 
   BLOCKING void reverse_rows();
@@ -128,8 +128,8 @@ PUBLISHED:
   BLOCKING void apply_crop(int x_begin, int x_end, int y_begin, int y_end);
   BLOCKING void clear_to_texcoords(int x_size, int y_size);
 
-  BLOCKING int pull_spot(const LPoint4f &delta, double xc, double yc, 
-                         double xr, double yr, double exponent);
+  BLOCKING int pull_spot(const LPoint4f &delta, float xc, float yc,
+                         float xr, float yr, float exponent);
 
   bool calc_tight_bounds(LPoint3f &min_point, LPoint3f &max_point) const;
   BLOCKING PT(BoundingHexahedron) compute_planar_bounds(const LPoint2f &center, PN_float32 point_dist, PN_float32 sample_radius, bool points_only) const;
@@ -143,17 +143,17 @@ PUBLISHED:
   void add_sub_image(const PfmFile &copy, int xto, int yto,
                      int xfrom = 0, int yfrom = 0,
                      int x_size = -1, int y_size = -1,
-                     double pixel_scale = 1.0);
+                     float pixel_scale = 1.0);
   void mult_sub_image(const PfmFile &copy, int xto, int yto,
                       int xfrom = 0, int yfrom = 0,
                       int x_size = -1, int y_size = -1,
-                      double pixel_scale = 1.0);
+                      float pixel_scale = 1.0);
   void divide_sub_image(const PfmFile &copy, int xto, int yto,
                         int xfrom = 0, int yfrom = 0,
                         int x_size = -1, int y_size = -1,
-                        double pixel_scale = 1.0);
+                        float pixel_scale = 1.0);
 
-  void operator *= (double multiplier);
+  void operator *= (float multiplier);
 
   void output(ostream &out) const;
 

+ 6 - 6
panda/src/pnmimage/pnm-image-filter-core.cxx

@@ -19,7 +19,7 @@
 
 static void
 FUNCTION_NAME(IMAGETYPE &dest, const IMAGETYPE &source,
-              double width, FilterFunction *make_filter, int channel) {
+              float width, FilterFunction *make_filter, int channel) {
   if (!dest.is_valid() || !source.is_valid()) {
     return;
   }
@@ -38,15 +38,15 @@ FUNCTION_NAME(IMAGETYPE &dest, const IMAGETYPE &source,
   }
 
   // First, scale the image in the A direction.
-  double scale;
+  float scale;
   StoreType *temp_source, *temp_dest;
 
-  scale = (double)dest.ASIZE() / (double)source.ASIZE();
+  scale = (float)dest.ASIZE() / (float)source.ASIZE();
   temp_source = (StoreType *)PANDA_MALLOC_ARRAY(source.ASIZE() * sizeof(StoreType));
   temp_dest = (StoreType *)PANDA_MALLOC_ARRAY(dest.ASIZE() * sizeof(StoreType));
 
   WorkType *filter;
-  double filter_width;
+  float filter_width;
 
   make_filter(scale, width, filter, filter_width);
 
@@ -70,7 +70,7 @@ FUNCTION_NAME(IMAGETYPE &dest, const IMAGETYPE &source,
   PANDA_FREE_ARRAY(filter);
 
   // Now, scale the image in the B direction.
-  scale = (double)dest.BSIZE() / (double)source.BSIZE();
+  scale = (float)dest.BSIZE() / (float)source.BSIZE();
   temp_dest = (StoreType *)PANDA_MALLOC_ARRAY(dest.BSIZE() * sizeof(StoreType));
 
   make_filter(scale, width, filter, filter_width);
@@ -82,7 +82,7 @@ FUNCTION_NAME(IMAGETYPE &dest, const IMAGETYPE &source,
                filter, filter_width);
 
     for (b = 0; b < dest.BSIZE(); b++) {
-      dest.SETVAL(a, b, channel, (double)temp_dest[b]/(double)source_max);
+      dest.SETVAL(a, b, channel, (float)temp_dest[b]/(float)source_max);
     }
   }
 

+ 6 - 6
panda/src/pnmimage/pnm-image-filter-sparse-core.cxx

@@ -19,7 +19,7 @@
 
 static void
 FUNCTION_NAME(IMAGETYPE &dest, const IMAGETYPE &source,
-              double width, FilterFunction *make_filter, int channel) {
+              float width, FilterFunction *make_filter, int channel) {
   if (!dest.is_valid() || !source.is_valid()) {
     return;
   }
@@ -40,17 +40,17 @@ FUNCTION_NAME(IMAGETYPE &dest, const IMAGETYPE &source,
   }
 
   // First, scale the image in the A direction.
-  double scale;
+  float scale;
   StoreType *temp_source, *temp_source_weight, *temp_dest, *temp_dest_weight;
 
-  scale = (double)dest.ASIZE() / (double)source.ASIZE();
+  scale = (float)dest.ASIZE() / (float)source.ASIZE();
   temp_source = (StoreType *)PANDA_MALLOC_ARRAY(source.ASIZE() * sizeof(StoreType));
   temp_source_weight = (StoreType *)PANDA_MALLOC_ARRAY(source.ASIZE() * sizeof(StoreType));
   temp_dest = (StoreType *)PANDA_MALLOC_ARRAY(dest.ASIZE() * sizeof(StoreType));
   temp_dest_weight = (StoreType *)PANDA_MALLOC_ARRAY(dest.ASIZE() * sizeof(StoreType));
 
   WorkType *filter;
-  double filter_width;
+  float filter_width;
 
   make_filter(scale, width, filter, filter_width);
 
@@ -81,7 +81,7 @@ FUNCTION_NAME(IMAGETYPE &dest, const IMAGETYPE &source,
   PANDA_FREE_ARRAY(filter);
 
   // Now, scale the image in the B direction.
-  scale = (double)dest.BSIZE() / (double)source.BSIZE();
+  scale = (float)dest.BSIZE() / (float)source.BSIZE();
   temp_dest = (StoreType *)PANDA_MALLOC_ARRAY(dest.BSIZE() * sizeof(StoreType));
   temp_dest_weight = (StoreType *)PANDA_MALLOC_ARRAY(dest.BSIZE() * sizeof(StoreType));
 
@@ -95,7 +95,7 @@ FUNCTION_NAME(IMAGETYPE &dest, const IMAGETYPE &source,
 
     for (b = 0; b < dest.BSIZE(); b++) {
       if (temp_dest_weight[b] != 0) {
-        dest.SETVAL(a, b, channel, (double)temp_dest[b]/(double)source_max);
+        dest.SETVAL(a, b, channel, (float)temp_dest[b]/(float)source_max);
       }
     }
   }

+ 76 - 97
panda/src/pnmimage/pnm-image-filter.cxx

@@ -65,18 +65,18 @@
 // use shorts, and not very much to use chars.
 
 // To use double-precision floating point, 8 bytes: (strictly for the neurotic)
+/*
 typedef double WorkType;
 typedef double StoreType;
 static const WorkType source_max = 1.0;
 static const WorkType filter_max = 1.0;
+*/
 
-/*
 // To use single-precision floating point, 4 bytes:
-typedef double WorkType;
+typedef float WorkType;
 typedef float StoreType;
-static const WorkType source_max = 1.0;
-static const WorkType filter_max = 1.0;
-*/
+static const WorkType source_max = 1.0f;
+static const WorkType filter_max = 1.0f;
 
 /*
 // To use 16-bit integer arithmetic, 2 bytes:
@@ -94,8 +94,6 @@ static const WorkType source_max = 255;
 static const WorkType filter_max = 255;
 */
 
-
-
 // filter_row() filters a single row by convolving with a one-dimensional
 // kernel filter.  The kernel is defined by an array of weights in filter[],
 // where the ith element of filter corresponds to abs(d * scale), if scale>1.0,
@@ -109,17 +107,17 @@ static const WorkType filter_max = 255;
 static void
 filter_row(StoreType dest[], int dest_len,
            const StoreType source[], int source_len,
-           double scale,                    //  == dest_len / source_len
+           float scale,                    //  == dest_len / source_len
            const WorkType filter[],
-           double filter_width) {
+           float filter_width) {
   // If we are expanding the row (scale > 1.0), we need to look at a
   // fractional granularity.  Hence, we scale our filter index by
   // scale.  If we are compressing (scale < 1.0), we don't need to
   // fiddle with the filter index, so we leave it at one.
 
-  double iscale;
-  if (scale < 1.0) {
-    iscale = 1.0;
+  float iscale;
+  if (scale < 1.0f) {
+    iscale = 1.0f;
     filter_width /= scale;
   } else {
     iscale = scale;
@@ -127,7 +125,7 @@ filter_row(StoreType dest[], int dest_len,
 
   for (int dest_x = 0; dest_x < dest_len; dest_x++) {
     // The additional offset of 0.5 keeps the pixel centered.
-    double center = (dest_x + 0.5) / scale - 0.5;
+    float center = (dest_x + 0.5f) / scale - 0.5f;
 
     // left and right are the starting and ending ranges of the radius of
     // interest of the filter function.  We need to apply the filter to each
@@ -148,13 +146,13 @@ filter_row(StoreType dest[], int dest_len,
     // of center--so we don't have to incur the overhead of calling fabs()
     // each time through the loop.
     for (source_x = left; source_x < right_center; source_x++) {
-      index = (int)(iscale * (center - source_x) + 0.5);
+      index = (int)(iscale * (center - source_x) + 0.5f);
       net_value += filter[index] * source[source_x];
       net_weight += filter[index];
     }
 
     for (; source_x <= right; source_x++) {
-      index = (int)(iscale * (source_x - center) + 0.5);
+      index = (int)(iscale * (source_x - center) + 0.5f);
       net_value += filter[index] * source[source_x];
       net_weight += filter[index];
     }
@@ -173,15 +171,15 @@ filter_row(StoreType dest[], int dest_len,
 static void
 filter_sparse_row(StoreType dest[], StoreType dest_weight[], int dest_len,
                   const StoreType source[], const StoreType source_weight[], int source_len,
-                  double scale,                    //  == dest_len / source_len
+                  float scale,                    //  == dest_len / source_len
                   const WorkType filter[],
-                  double filter_width) {
+                  float filter_width) {
   // If we are expanding the row (scale > 1.0), we need to look at a
   // fractional granularity.  Hence, we scale our filter index by
   // scale.  If we are compressing (scale < 1.0), we don't need to
   // fiddle with the filter index, so we leave it at one.
 
-  double iscale;
+  float iscale;
   if (scale < 1.0) {
     iscale = 1.0;
     filter_width /= scale;
@@ -191,7 +189,7 @@ filter_sparse_row(StoreType dest[], StoreType dest_weight[], int dest_len,
 
   for (int dest_x = 0; dest_x < dest_len; dest_x++) {
     // The additional offset of 0.5 keeps the pixel centered.
-    double center = (dest_x + 0.5) / scale - 0.5;
+    float center = (dest_x + 0.5f) / scale - 0.5f;
 
     // left and right are the starting and ending ranges of the radius of
     // interest of the filter function.  We need to apply the filter to each
@@ -212,13 +210,13 @@ filter_sparse_row(StoreType dest[], StoreType dest_weight[], int dest_len,
     // of center--so we don't have to incur the overhead of calling fabs()
     // each time through the loop.
     for (source_x = left; source_x < right_center; source_x++) {
-      index = (int)(iscale * (center - source_x) + 0.5);
+      index = (int)(iscale * (center - source_x) + 0.5f);
       net_value += filter[index] * source[source_x] * source_weight[source_x];
       net_weight += filter[index] * source_weight[source_x];
     }
 
     for (; source_x <= right; source_x++) {
-      index = (int)(iscale * (source_x - center) + 0.5);
+      index = (int)(iscale * (source_x - center) + 0.5f);
       net_value += filter[index] * source[source_x] * source_weight[source_x];
       net_weight += filter[index] * source_weight[source_x];
     }
@@ -244,13 +242,13 @@ filter_sparse_row(StoreType dest[], StoreType dest_weight[], int dest_len,
 // 0..filter_max; the array must have enough elements to include all indices
 // corresponding to values in the range -filter_width to filter_width.
 
-typedef void FilterFunction(double scale, double width,
-                            WorkType *&filter, double &filter_width);
+typedef void FilterFunction(float scale, float width,
+                            WorkType *&filter, float &filter_width);
 
 static void
-box_filter_impl(double scale, double width,
-                WorkType *&filter, double &filter_width) {
-  double fscale;
+box_filter_impl(float scale, float width,
+                WorkType *&filter, float &filter_width) {
+  float fscale;
   if (scale < 1.0) {
     // If we are compressing the image, we want to expand the range of
     // the filter function to prevent dropping below the Nyquist rate.
@@ -274,9 +272,9 @@ box_filter_impl(double scale, double width,
 }
 
 static void
-gaussian_filter_impl(double scale, double width,
-                     WorkType *&filter, double &filter_width) {
-  double fscale;
+gaussian_filter_impl(float scale, float width,
+                     WorkType *&filter, float &filter_width) {
+  float fscale;
   if (scale < 1.0) {
     // If we are compressing the image, we want to expand the range of
     // the filter function to prevent dropping below the Nyquist rate.
@@ -290,7 +288,7 @@ gaussian_filter_impl(double scale, double width,
     fscale = scale;
   }
 
-  double sigma = width/2;
+  float sigma = width/2;
   filter_width = 3.0 * sigma;
   int actual_width = (int)cceil((filter_width + 1) * fscale);
 
@@ -301,10 +299,10 @@ gaussian_filter_impl(double scale, double width,
   // so we can ignore the y^2.)
 
   filter = (WorkType *)PANDA_MALLOC_ARRAY(actual_width * sizeof(WorkType));
-  double div = 2 * sigma * sigma;
+  float div = 2 * sigma * sigma;
 
   for (int i = 0; i < actual_width; i++) {
-    double x = i / fscale;
+    float x = i / fscale;
     filter[i] = (WorkType)(filter_max * exp(-x*x / div));
     // The highest value of the exp function in this range is always 1.0,
     // at index value 0.  Thus, we scale the whole range by filter_max,
@@ -485,7 +483,7 @@ gaussian_filter_impl(double scale, double width,
 // another.  Both images can be the same with no ill effects.
 static void
 filter_image(PNMImage &dest, const PNMImage &source,
-             double width, FilterFunction *make_filter) {
+             float width, FilterFunction *make_filter) {
 
   // We want to scale by the smallest destination axis first, for a
   // slight performance gain.
@@ -523,8 +521,6 @@ filter_image(PNMImage &dest, const PNMImage &source,
   }
 }
 
-
-
 ////////////////////////////////////////////////////////////////////
 //     Function: PNMImage::box_filter_from
 //       Access: Public
@@ -535,7 +531,7 @@ filter_image(PNMImage &dest, const PNMImage &source,
 //               appropriate filter to perform the stretching.
 ////////////////////////////////////////////////////////////////////
 void PNMImage::
-box_filter_from(double width, const PNMImage &copy) {
+box_filter_from(float width, const PNMImage &copy) {
   filter_image(*this, copy, width, &box_filter_impl);
 }
 
@@ -549,7 +545,7 @@ box_filter_from(double width, const PNMImage &copy) {
 //               appropriate filter to perform the stretching.
 ////////////////////////////////////////////////////////////////////
 void PNMImage::
-gaussian_filter_from(double width, const PNMImage &copy) {
+gaussian_filter_from(float width, const PNMImage &copy) {
   filter_image(*this, copy, width, &gaussian_filter_impl);
 }
 
@@ -624,7 +620,7 @@ gaussian_filter_from(double width, const PNMImage &copy) {
 // another.  Both images can be the same with no ill effects.
 static void
 filter_image(PfmFile &dest, const PfmFile &source,
-             double width, FilterFunction *make_filter) {
+             float width, FilterFunction *make_filter) {
   int num_channels = min(dest.get_num_channels(), source.get_num_channels());
 
   if (source.has_no_data_value()) {
@@ -654,8 +650,6 @@ filter_image(PfmFile &dest, const PfmFile &source,
   }
 }
 
-
-
 ////////////////////////////////////////////////////////////////////
 //     Function: PfmFile::box_filter_from
 //       Access: Public
@@ -666,7 +660,7 @@ filter_image(PfmFile &dest, const PfmFile &source,
 //               appropriate filter to perform the stretching.
 ////////////////////////////////////////////////////////////////////
 void PfmFile::
-box_filter_from(double width, const PfmFile &copy) {
+box_filter_from(float width, const PfmFile &copy) {
   filter_image(*this, copy, width, &box_filter_impl);
 }
 
@@ -680,99 +674,86 @@ box_filter_from(double width, const PfmFile &copy) {
 //               appropriate filter to perform the stretching.
 ////////////////////////////////////////////////////////////////////
 void PfmFile::
-gaussian_filter_from(double width, const PfmFile &copy) {
+gaussian_filter_from(float width, const PfmFile &copy) {
   filter_image(*this, copy, width, &gaussian_filter_impl);
 }
 
-
 //
 // The following functions are support for quick_box_filter().
 //
 
-INLINE void
-box_filter_xel(const PNMImage &image,
-               int x, int y, double x_contrib, double y_contrib,
-               double &red, double &grn, double &blu, double &alpha,
-               double &pixel_count) {
-  double contrib = x_contrib * y_contrib;
-  red += image.get_red_val(x, y) * contrib;
-  grn += image.get_green_val(x, y) * contrib;
-  blu += image.get_blue_val(x, y) * contrib;
-  if (image.has_alpha()) {
-    alpha += image.get_alpha_val(x, y) * contrib;
-  }
+static INLINE void
+box_filter_xel(const PNMImage &img,
+               int x, int y, float x_contrib, float y_contrib,
+               LColorf &color, float &pixel_count) {
 
+  float contrib = x_contrib * y_contrib;
+  color += img.get_xel_a(x, y) * contrib;
   pixel_count += contrib;
 }
 
-
-INLINE void
+static INLINE void
 box_filter_line(const PNMImage &image,
-                double x0, int y, double x1, double y_contrib,
-                double &red, double &grn, double &blu, double &alpha,
-                double &pixel_count) {
+                float x0, int y, float x1, float y_contrib,
+                LColorf &color, float &pixel_count) {
   int x = (int)x0;
   // Get the first (partial) xel
-  box_filter_xel(image, x, y, (double)(x+1)-x0, y_contrib,
-                 red, grn, blu, alpha, pixel_count);
+  box_filter_xel(image, x, y, (float)(x+1)-x0, y_contrib,
+                 color, pixel_count);
 
   int x_last = (int)x1;
   if (x < x_last) {
     x++;
     while (x < x_last) {
       // Get each consecutive (complete) xel
-      box_filter_xel(image, x, y, 1.0, y_contrib,
-                     red, grn, blu, alpha, pixel_count);
+      box_filter_xel(image, x, y, 1.0f, y_contrib,
+                     color, pixel_count);
       x++;
     }
 
     // Get the final (partial) xel
-    double x_contrib = x1 - (double)x_last;
-    if (x_contrib > 0.0001) {
+    float x_contrib = x1 - (float)x_last;
+    if (x_contrib > 0.0001f) {
       box_filter_xel(image, x, y, x_contrib, y_contrib,
-                     red, grn, blu, alpha, pixel_count);
+                     color, pixel_count);
     }
   }
 }
 
-static void
+static LColorf
 box_filter_region(const PNMImage &image,
-                  double x0, double y0, double x1, double y1,
-                  xel &result, xelval &alpha_result) {
-  double red = 0.0, grn = 0.0, blu = 0.0, alpha = 0.0;
-  double pixel_count = 0.0;
+                  float x0, float y0, float x1, float y1) {
+  LColorf color = LColorf::zero();
+  float pixel_count = 0.0f;
 
-  assert(y0 >=0 && y1 >=0);
+  assert(y0 >= 0 && y1 >= 0);
 
   int y = (int)y0;
   // Get the first (partial) row
-  box_filter_line(image, x0, y, x1, (double)(y+1)-y0,
-                  red, grn, blu, alpha, pixel_count);
+  box_filter_line(image, x0, y, x1, (float)(y+1)-y0,
+                  color, pixel_count);
 
   int y_last = (int)y1;
   if (y < y_last) {
     y++;
     while (y < y_last) {
       // Get each consecutive (complete) row
-      box_filter_line(image, x0, y, x1, 1.0,
-                      red, grn, blu, alpha, pixel_count);
+      box_filter_line(image, x0, y, x1, 1.0f,
+                      color, pixel_count);
       y++;
     }
 
     // Get the final (partial) row
-    double y_contrib = y1 - (double)y_last;
-    if (y_contrib > 0.0001) {
+    float y_contrib = y1 - (float)y_last;
+    if (y_contrib > 0.0001f) {
       box_filter_line(image, x0, y, x1, y_contrib,
-                      red, grn, blu, alpha, pixel_count);
+                      color, pixel_count);
     }
   }
 
-  PPM_ASSIGN(result,
-             (xelval)(red / pixel_count + 0.5),
-             (xelval)(grn / pixel_count + 0.5),
-             (xelval)(blu / pixel_count + 0.5));
-
-  alpha_result = (xelval)(alpha / pixel_count + 0.5);
+  //cerr << pixel_count << "\n";
+  color /= pixel_count;
+  return color;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -798,11 +779,13 @@ quick_filter_from(const PNMImage &from, int xborder, int yborder) {
   int to_xoff = xborder / 2;
   int to_yoff = yborder / 2;
 
-  double from_x0, from_x1, from_y0, from_y1;
+  float from_x0, from_x1, from_y0, from_y1;
   int to_x, to_y;
 
-  double x_scale = (double)from_xs / (double)to_xs;
-  double y_scale = (double)from_ys / (double)to_ys;
+  float x_scale = (float)from_xs / (float)to_xs;
+  float y_scale = (float)from_ys / (float)to_ys;
+
+  LColorf color;
 
   from_y0 = max(0, -to_yoff) * y_scale;
   for (to_y = max(0, -to_yoff);
@@ -818,14 +801,10 @@ quick_filter_from(const PNMImage &from, int xborder, int yborder) {
 
       // Now the box from (from_x0, from_y0) - (from_x1, from_y1)
       // but not including (from_x1, from_y1) maps to the pixel (to_x, to_y).
-      xelval alpha_result;
-      box_filter_region(from,
-                        from_x0, from_y0, from_x1, from_y1,
-                        (*this)[to_yoff + to_y][to_xoff + to_x],
-                        alpha_result);
-      if (has_alpha()) {
-        set_alpha_val(to_xoff+to_x, to_yoff+to_y, alpha_result);
-      }
+      color = box_filter_region(from,
+                                from_x0, from_y0, from_x1, from_y1);
+
+      set_xel_a(to_xoff + to_x, to_yoff + to_y, color);
 
       from_x0 = from_x1;
     }

+ 3 - 3
panda/src/pnmimage/pnmBrush.I

@@ -19,7 +19,7 @@
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 INLINE PNMBrush::
-PNMBrush(double xc, double yc) : _xc(xc), _yc(yc) {
+PNMBrush(float xc, float yc) : _xc(xc), _yc(yc) {
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -31,7 +31,7 @@ PNMBrush(double xc, double yc) : _xc(xc), _yc(yc) {
 //               (1.0, 1.0); for a centered three-pixel brush, this
 //               will be (1.5, 1.5); and so on.
 ////////////////////////////////////////////////////////////////////
-INLINE double PNMBrush::
+INLINE float PNMBrush::
 get_xc() const {
   return _xc;
 }
@@ -45,7 +45,7 @@ get_xc() const {
 //               (1.0, 1.0); for a centered three-pixel brush, this
 //               will be (1.5, 1.5); and so on.
 ////////////////////////////////////////////////////////////////////
-INLINE double PNMBrush::
+INLINE float PNMBrush::
 get_yc() const {
   return _yc;
 }

+ 54 - 101
panda/src/pnmimage/pnmBrush.cxx

@@ -20,10 +20,10 @@
 // A PNMTransparentBrush doesn't draw or fill anything.
 class EXPCL_PANDA_PNMIMAGE PNMTransparentBrush : public PNMBrush {
 public:
-  PNMTransparentBrush() : 
+  PNMTransparentBrush() :
     PNMBrush(0.0, 0.0) { }
 
-  virtual void draw(PNMImage &, int, int, double) {
+  virtual void draw(PNMImage &, int, int, float) {
   }
 
   virtual void fill(PNMImage &, int, int, int, int, int) {
@@ -33,26 +33,22 @@ public:
 // A PNMPixelBrush is a family of brushes that draw one pixel at a time.
 class EXPCL_PANDA_PNMIMAGE PNMPixelBrush : public PNMBrush {
 protected:
-  PNMPixelBrush(const LColord &color) : 
-    PNMBrush(0.5, 0.5), _rgb(color[0], color[1], color[2]), _a(color[3]) { }
+  PNMPixelBrush(const LColorf &color) :
+    PNMBrush(0.5, 0.5), _color(color) { }
 
-  LRGBColord _rgb;
-  double _a;
+  LColorf _color;
 };
 
 // Arbitrarily sets the pixel to a particular color, with no antialiasing.
 class EXPCL_PANDA_PNMIMAGE PNMSetPixelBrush : public PNMPixelBrush {
 public:
-  PNMSetPixelBrush(const LColord &color) : PNMPixelBrush(color) { }
+  PNMSetPixelBrush(const LColorf &color) : PNMPixelBrush(color) { }
 
-  virtual void draw(PNMImage &image, int x, int y, double pixel_scale) {
-    if (x >= 0 && x < image.get_x_size() && 
+  virtual void draw(PNMImage &image, int x, int y, float pixel_scale) {
+    if (x >= 0 && x < image.get_x_size() &&
         y >= 0 && y < image.get_y_size() &&
         pixel_scale >= 0.5) {
-      image.set_xel(x, y, _rgb);
-      if (image.has_alpha()) {
-        image.set_alpha(x, y, _a);
-      }
+      image.set_xel_a(x, y, _color);
     }
   }
 
@@ -62,12 +58,7 @@ public:
       xfrom = max(xfrom, 0);
       xto = min(xto, image.get_x_size() - 1);
       for (int x = xfrom; x <= xto; ++x) {
-        image.set_xel(x, y, _rgb);
-      }
-      if (image.has_alpha()) {
-        for (int x = xfrom; x <= xto; ++x) {
-          image.set_alpha(x, y, _a);
-        }
+        image.set_xel_a(x, y, _color);
       }
     }
   }
@@ -76,12 +67,12 @@ public:
 // Blends the pixel in to the existing background.
 class EXPCL_PANDA_PNMIMAGE PNMBlendPixelBrush : public PNMPixelBrush {
 public:
-  PNMBlendPixelBrush(const LColord &color) : PNMPixelBrush(color) { }
+  PNMBlendPixelBrush(const LColorf &color) : PNMPixelBrush(color) { }
 
-  virtual void draw(PNMImage &image, int x, int y, double pixel_scale) {
+  virtual void draw(PNMImage &image, int x, int y, float pixel_scale) {
     if (x >= 0 && x < image.get_x_size() && 
         y >= 0 && y < image.get_y_size()) {
-      image.blend(x, y, _rgb, _a * pixel_scale);
+      image.blend(x, y, _color[0], _color[1], _color[2], _color[3] * pixel_scale);
     }
   }
 
@@ -91,7 +82,7 @@ public:
       xfrom = max(xfrom, 0);
       xto = min(xto, image.get_x_size() - 1);
       for (int x = xfrom; x <= xto; ++x) {
-        image.blend(x, y, _rgb, _a);
+        image.blend(x, y, _color[0], _color[1], _color[2], _color[3]);
       }
     }
   }
@@ -100,22 +91,14 @@ public:
 // Darkens the pixel in the existing background.
 class EXPCL_PANDA_PNMIMAGE PNMDarkenPixelBrush : public PNMPixelBrush {
 public:
-  PNMDarkenPixelBrush(const LColord &color) : PNMPixelBrush(color) { }
+  PNMDarkenPixelBrush(const LColorf &color) : PNMPixelBrush(color) { }
 
-  virtual void draw(PNMImage &image, int x, int y, double pixel_scale) {
-    if (x >= 0 && x < image.get_x_size() && 
+  virtual void draw(PNMImage &image, int x, int y, float pixel_scale) {
+    if (x >= 0 && x < image.get_x_size() &&
         y >= 0 && y < image.get_y_size()) {
-      LRGBColord rgb = image.get_xel(x, y);
-      LRGBColord p;
-      p.set(min(1.0 - (1.0 - _rgb[0]) * pixel_scale, rgb[0]), 
-            min(1.0 - (1.0 - _rgb[1]) * pixel_scale, rgb[1]), 
-            min(1.0 - (1.0 - _rgb[2]) * pixel_scale, rgb[2]));
-      image.set_xel(x, y, p);
-
-      if (image.has_alpha()) {
-        double a = image.get_alpha(x, y);
-        image.set_alpha(x, y, min(1.0 - (1.0 - _a) * pixel_scale, a));
-      }
+
+      LColorf p = (_color - 1.0f) * pixel_scale + 1.0f;
+      image.set_xel_a(x, y, p.fmin(image.get_xel_a(x, y)));
     }
   }
 
@@ -125,18 +108,7 @@ public:
       xfrom = max(xfrom, 0);
       xto = min(xto, image.get_x_size() - 1);
       for (int x = xfrom; x <= xto; ++x) {
-        LRGBColord rgb = image.get_xel(x, y);
-        LRGBColord p;
-        p.set(min(_rgb[0], rgb[0]), 
-              min(_rgb[1], rgb[1]), 
-              min(_rgb[2], rgb[2]));
-        image.set_xel(x, y, p);
-      }
-      if (image.has_alpha()) {
-        for (int x = xfrom; x <= xto; ++x) {
-          double a = image.get_alpha(x, y);
-          image.set_alpha(x, y, min(_a, a));
-        }
+        image.set_xel_a(x, y, _color.fmin(image.get_xel_a(x, y)));
       }
     }
   }
@@ -145,22 +117,13 @@ public:
 // Lightens the pixel in the existing background.
 class EXPCL_PANDA_PNMIMAGE PNMLightenPixelBrush : public PNMPixelBrush {
 public:
-  PNMLightenPixelBrush(const LColord &color) : PNMPixelBrush(color) { }
+  PNMLightenPixelBrush(const LColorf &color) : PNMPixelBrush(color) { }
 
-  virtual void draw(PNMImage &image, int x, int y, double pixel_scale) {
-    if (x >= 0 && x < image.get_x_size() && 
+  virtual void draw(PNMImage &image, int x, int y, float pixel_scale) {
+    if (x >= 0 && x < image.get_x_size() &&
         y >= 0 && y < image.get_y_size()) {
-      LRGBColord rgb = image.get_xel(x, y);
-      LRGBColord p;
-      p.set(max(_rgb[0] * pixel_scale, rgb[0]), 
-            max(_rgb[1] * pixel_scale, rgb[1]), 
-            max(_rgb[2] * pixel_scale, rgb[2]));
-      image.set_xel(x, y, p);
-
-      if (image.has_alpha()) {
-        double a = image.get_alpha(x, y);
-        image.set_alpha(x, y, max(_a * pixel_scale, a));
-      }
+      image.set_xel_a(x, y,
+        image.get_xel_a(x, y).fmax(_color * pixel_scale));
     }
   }
 
@@ -170,18 +133,8 @@ public:
       xfrom = max(xfrom, 0);
       xto = max(xto, image.get_x_size() - 1);
       for (int x = xfrom; x <= xto; ++x) {
-        LRGBColord rgb = image.get_xel(x, y);
-        LRGBColord p;
-        p.set(max(_rgb[0], rgb[0]), 
-              max(_rgb[1], rgb[1]), 
-              max(_rgb[2], rgb[2]));
-        image.set_xel(x, y, p);
-      }
-      if (image.has_alpha()) {
-        for (int x = xfrom; x <= xto; ++x) {
-          double a = image.get_alpha(x, y);
-          image.set_alpha(x, y, max(_a, a));
-        }
+        image.set_xel_a(x, y,
+          image.get_xel_a(x, y).fmax(_color));
       }
     }
   }
@@ -190,9 +143,9 @@ public:
 // A PNMImageBrush is a family of brushes that draw an image at a time.
 class EXPCL_PANDA_PNMIMAGE PNMImageBrush : public PNMBrush {
 protected:
-  PNMImageBrush(const PNMImage &image, double xc, double yc) : 
+  PNMImageBrush(const PNMImage &image, float xc, float yc) :
     PNMBrush(xc, yc),
-    _image(image) 
+    _image(image)
   {
   }
 
@@ -226,16 +179,16 @@ protected:
 // Sets the pixels from the rectangular image, with no antialiasing.
 class EXPCL_PANDA_PNMIMAGE PNMSetImageBrush : public PNMImageBrush {
 public:
-  PNMSetImageBrush(const PNMImage &image, double xc, double yc) : 
+  PNMSetImageBrush(const PNMImage &image, float xc, float yc) :
     PNMImageBrush(image, xc, yc) { }
 
-  virtual void draw(PNMImage &image, int x, int y, double pixel_scale) {
+  virtual void draw(PNMImage &image, int x, int y, float pixel_scale) {
     if (pixel_scale >= 0.5) {
       image.copy_sub_image(_image, x, y);
     }
   }
 
-  virtual void do_scanline(PNMImage &image, int xto, int yto, 
+  virtual void do_scanline(PNMImage &image, int xto, int yto,
                            int xfrom, int yfrom, int x_size, int y_size) {
     image.copy_sub_image(_image, xto, yto, xfrom, yfrom, x_size, y_size);
   }
@@ -244,14 +197,14 @@ public:
 // Blends the pixels in using alpha.
 class EXPCL_PANDA_PNMIMAGE PNMBlendImageBrush : public PNMImageBrush {
 public:
-  PNMBlendImageBrush(const PNMImage &image, double xc, double yc) : 
+  PNMBlendImageBrush(const PNMImage &image, float xc, float yc) :
     PNMImageBrush(image, xc, yc) { }
 
-  virtual void draw(PNMImage &image, int x, int y, double pixel_scale) {
+  virtual void draw(PNMImage &image, int x, int y, float pixel_scale) {
     image.blend_sub_image(_image, x, y, 0, 0, -1, -1, pixel_scale);
   }
 
-  virtual void do_scanline(PNMImage &image, int xto, int yto, 
+  virtual void do_scanline(PNMImage &image, int xto, int yto,
                            int xfrom, int yfrom, int x_size, int y_size) {
     image.blend_sub_image(_image, xto, yto, xfrom, yfrom, x_size, y_size);
   }
@@ -260,14 +213,14 @@ public:
 // Darkens the pixels
 class EXPCL_PANDA_PNMIMAGE PNMDarkenImageBrush : public PNMImageBrush {
 public:
-  PNMDarkenImageBrush(const PNMImage &image, double xc, double yc) : 
+  PNMDarkenImageBrush(const PNMImage &image, float xc, float yc) :
     PNMImageBrush(image, xc, yc) { }
 
-  virtual void draw(PNMImage &image, int x, int y, double pixel_scale) {
+  virtual void draw(PNMImage &image, int x, int y, float pixel_scale) {
     image.darken_sub_image(_image, x, y, 0, 0, -1, -1, pixel_scale);
   }
 
-  virtual void do_scanline(PNMImage &image, int xto, int yto, 
+  virtual void do_scanline(PNMImage &image, int xto, int yto,
                            int xfrom, int yfrom, int x_size, int y_size) {
     image.darken_sub_image(_image, xto, yto, xfrom, yfrom, x_size, y_size);
   }
@@ -276,14 +229,14 @@ public:
 // Lightens the pixels
 class EXPCL_PANDA_PNMIMAGE PNMLightenImageBrush : public PNMImageBrush {
 public:
-  PNMLightenImageBrush(const PNMImage &image, double xc, double yc) : 
+  PNMLightenImageBrush(const PNMImage &image, float xc, float yc) :
     PNMImageBrush(image, xc, yc) { }
 
-  virtual void draw(PNMImage &image, int x, int y, double pixel_scale) {
+  virtual void draw(PNMImage &image, int x, int y, float pixel_scale) {
     image.lighten_sub_image(_image, x, y, 0, 0, -1, -1, pixel_scale);
   }
 
-  virtual void do_scanline(PNMImage &image, int xto, int yto, 
+  virtual void do_scanline(PNMImage &image, int xto, int yto,
                            int xfrom, int yfrom, int x_size, int y_size) {
     image.lighten_sub_image(_image, xto, yto, xfrom, yfrom, x_size, y_size);
   }
@@ -292,7 +245,7 @@ public:
 ////////////////////////////////////////////////////////////////////
 //     Function: PNMBrush::Destructor
 //       Access: Published, Virtual
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 PNMBrush::
 ~PNMBrush() {
@@ -309,7 +262,7 @@ PT(PNMBrush) PNMBrush::
 make_transparent() {
   return new PNMTransparentBrush();
 }
-  
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PNMBrush::make_pixel
 //       Access: Published, Static
@@ -318,7 +271,7 @@ make_transparent() {
 //               in an interior.
 ////////////////////////////////////////////////////////////////////
 PT(PNMBrush) PNMBrush::
-make_pixel(const LColord &color, PNMBrush::BrushEffect effect) {
+make_pixel(const LColorf &color, PNMBrush::BrushEffect effect) {
   switch (effect) {
   case BE_set:
     return new PNMSetPixelBrush(color);
@@ -346,9 +299,9 @@ make_pixel(const LColord &color, PNMBrush::BrushEffect effect) {
 //               spot is fuzzy; otherwise, it is hard-edged.
 ////////////////////////////////////////////////////////////////////
 PT(PNMBrush) PNMBrush::
-make_spot(const LColord &color, double radius, bool fuzzy,
+make_spot(const LColorf &color, float radius, bool fuzzy,
           BrushEffect effect) {
-  LColord bg;
+  LColorf bg;
 
   switch (effect) {
   case BE_set:
@@ -356,7 +309,7 @@ make_spot(const LColord &color, double radius, bool fuzzy,
     break;
 
   case BE_blend:
-    bg.set(color[0], color[1], color[2], 0.0);
+    bg.set(color[0], color[1], color[2], 0.0f);
     break;
 
   case BE_darken:
@@ -372,19 +325,19 @@ make_spot(const LColord &color, double radius, bool fuzzy,
       << "**Invalid BrushEffect (" << (int)effect << ")**\n";
   }
 
-  int size = (int)cceil(radius * 2.0);
-  double half_size = (double)size * 0.5;
+  int size = (int)cceil(radius * 2.0f);
+  float half_size = (float)size * 0.5f;
   PNMImage spot(size, size, 4);
-  double r = half_size / radius;
+  float r = half_size / radius;
 
   if (fuzzy) {
-    spot.render_spot(color, bg, 0.0, r);
+    spot.render_spot(color, bg, 0.0f, r);
   } else {
     spot.render_spot(color, bg, r, r);
   }
   return make_image(spot, half_size, half_size, effect);
 }
-  
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PNMBrush::make_image
 //       Access: Published, Static
@@ -397,7 +350,7 @@ make_spot(const LColord &color, double radius, bool fuzzy,
 //               call.
 ////////////////////////////////////////////////////////////////////
 PT(PNMBrush) PNMBrush::
-make_image(const PNMImage &image, double xc, double yc,
+make_image(const PNMImage &image, float xc, float yc,
            PNMBrush::BrushEffect effect) {
   switch (effect) {
   case BE_set:

+ 8 - 8
panda/src/pnmimage/pnmBrush.h

@@ -40,7 +40,7 @@ class PNMImage;
 ////////////////////////////////////////////////////////////////////
 class EXPCL_PANDA_PNMIMAGE PNMBrush : public ReferenceCount {
 protected:
-  INLINE PNMBrush(double xc, double yc);
+  INLINE PNMBrush(float xc, float yc);
 
 PUBLISHED:
   virtual ~PNMBrush();
@@ -53,22 +53,22 @@ PUBLISHED:
   };
 
   static PT(PNMBrush) make_transparent();
-  static PT(PNMBrush) make_pixel(const LColord &color, BrushEffect effect = BE_blend);
-  static PT(PNMBrush) make_spot(const LColord &color, double radius, bool fuzzy,
+  static PT(PNMBrush) make_pixel(const LColorf &color, BrushEffect effect = BE_blend);
+  static PT(PNMBrush) make_spot(const LColorf &color, float radius, bool fuzzy,
                                 BrushEffect effect = BE_blend);
-  static PT(PNMBrush) make_image(const PNMImage &image, double xc, double yc,
+  static PT(PNMBrush) make_image(const PNMImage &image, float xc, float yc,
                                  BrushEffect effect = BE_blend);
 
 public:
-  INLINE double get_xc() const;
-  INLINE double get_yc() const;
+  INLINE float get_xc() const;
+  INLINE float get_yc() const;
 
-  virtual void draw(PNMImage &image, int x, int y, double pixel_scale)=0;
+  virtual void draw(PNMImage &image, int x, int y, float pixel_scale)=0;
   virtual void fill(PNMImage &image, int xfrom, int xto, int y,
                     int xo, int yo)=0;
 
 protected:
-  double _xc, _yc;
+  float _xc, _yc;
 };
 
 #include "pnmBrush.I"

+ 408 - 159
panda/src/pnmimage/pnmImage.I

@@ -32,11 +32,11 @@ PNMImage() {
 ////////////////////////////////////////////////////////////////////
 INLINE PNMImage::
 PNMImage(int x_size, int y_size, int num_channels, xelval maxval,
-         PNMFileType *type) {
+         PNMFileType *type, ColorSpace color_space) {
   _array = NULL;
   _alpha = NULL;
 
-  clear(x_size, y_size, num_channels, maxval, type);
+  clear(x_size, y_size, num_channels, maxval, type, color_space);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -74,7 +74,6 @@ INLINE PNMImage::
   clear();
 }
 
-
 ////////////////////////////////////////////////////////////////////
 //     Function: PNMImage::clamp_val
 //       Access: Published
@@ -89,23 +88,86 @@ clamp_val(int input_value) const {
 ////////////////////////////////////////////////////////////////////
 //     Function: PNMImage::to_val
 //       Access: Published
-//  Description: A handy function to scale values from [0..1] to
-//               [0..get_maxval()].
+//  Description: A handy function to scale non-alpha values from
+//               [0..1] to [0..get_maxval()].  Do not use this for
+//               alpha values, see to_alpha_val.
+////////////////////////////////////////////////////////////////////
+INLINE xelval PNMImage::
+to_val(float input_value) const {
+  switch (_xel_encoding) {
+  case XE_generic:
+  case XE_generic_alpha:
+    return clamp_val((int)(input_value * get_maxval() + 0.5f));
+
+  case XE_generic_sRGB:
+  case XE_generic_sRGB_alpha:
+    return clamp_val((int)
+      (encode_sRGB_float(input_value) * get_maxval() + 0.5f));
+
+  case XE_uchar_sRGB:
+  case XE_uchar_sRGB_alpha:
+    return encode_sRGB_uchar(input_value);
+
+  case XE_uchar_sRGB_sse2:
+  case XE_uchar_sRGB_alpha_sse2:
+    return encode_sRGB_uchar_sse2(input_value);
+
+  case XE_scRGB:
+  case XE_scRGB_alpha:
+    return min(max(0, (int)((8192 * input_value) + 4096.5f)), 65535);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PNMImage::to_alpha_val
+//       Access: Published
+//  Description: A handy function to scale alpha values from [0..1]
+//               to [0..get_maxval()].
 ////////////////////////////////////////////////////////////////////
 INLINE xelval PNMImage::
-to_val(double input_value) const {
+to_alpha_val(float input_value) const {
   return clamp_val((int)(input_value * get_maxval() + 0.5));
 }
 
 ////////////////////////////////////////////////////////////////////
 //     Function: PNMImage::from_val
 //       Access: Published
-//  Description: A handy function to scale values from
-//               [0..get_maxval()] to [0..1].
+//  Description: A handy function to scale non-alpha values from
+//               [0..get_maxval()] to [0..1].  Do not use this for
+//               alpha values, see from_alpha_val.
 ////////////////////////////////////////////////////////////////////
-INLINE double PNMImage::
+INLINE float PNMImage::
 from_val(xelval input_value) const {
-  return (double)input_value / (double)get_maxval();
+  switch (_xel_encoding) {
+  case XE_generic:
+  case XE_generic_alpha:
+    return (float)input_value * _inv_maxval;
+
+  case XE_generic_sRGB:
+  case XE_generic_sRGB_alpha:
+    return decode_sRGB_float((float)input_value * _inv_maxval);
+
+  case XE_uchar_sRGB:
+  case XE_uchar_sRGB_alpha:
+  case XE_uchar_sRGB_sse2:
+  case XE_uchar_sRGB_alpha_sse2:
+    return decode_sRGB_float((unsigned char)input_value);
+
+  case XE_scRGB:
+  case XE_scRGB_alpha:
+    return (input_value - 4096) * (1.f / 8192.f);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PNMImage::from_alpha_val
+//       Access: Published
+//  Description: A handy function to scale alpha values from
+//               [0..get_maxval()] to [0..1].
+////////////////////////////////////////////////////////////////////
+INLINE float PNMImage::
+from_alpha_val(xelval input_value) const {
+  return (float)input_value * _inv_maxval;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -115,7 +177,7 @@ from_val(xelval input_value) const {
 //               the given color.
 ////////////////////////////////////////////////////////////////////
 INLINE void PNMImage::
-fill(double red, double green, double blue) {
+fill(float red, float green, float blue) {
   fill_val(to_val(red), to_val(green), to_val(blue));
 }
 
@@ -126,7 +188,7 @@ fill(double red, double green, double blue) {
 //               the given grayscale level.
 ////////////////////////////////////////////////////////////////////
 INLINE void PNMImage::
-fill(double gray) {
+fill(float gray) {
   fill(gray, gray, gray);
 }
 
@@ -147,8 +209,8 @@ fill_val(xelval gray) {
 //  Description: Sets the entire alpha channel to the given level.
 ////////////////////////////////////////////////////////////////////
 INLINE void PNMImage::
-alpha_fill(double alpha) {
-  alpha_fill_val(to_val(alpha));
+alpha_fill(float alpha) {
+  alpha_fill_val(to_alpha_val(alpha));
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -218,6 +280,17 @@ get_read_y_size() const {
   return _has_read_size ? _read_y_size : get_y_size();
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PNMImage::get_color_space
+//       Access: Published
+//  Description: Returns the color space in which the image is
+//               encoded.
+////////////////////////////////////////////////////////////////////
+INLINE ColorSpace PNMImage::
+get_color_space() const {
+  return _color_space;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PNMImage::is_valid
 //       Access: Published
@@ -306,7 +379,10 @@ get_xel_val(int x, int y) const {
 //     Function: PNMImage::set_xel_val
 //       Access: Published
 //  Description: Changes the RGB color at the indicated pixel.  Each
-//               component is in the range 0..maxval.
+//               component is in the range 0..maxval, encoded in
+//               the configured color space.  See set_xel if you
+//               instead have a linearized and normalized
+//               floating-point value.
 ////////////////////////////////////////////////////////////////////
 INLINE void PNMImage::
 set_xel_val(int x, int y, const xel &value) {
@@ -318,7 +394,10 @@ set_xel_val(int x, int y, const xel &value) {
 //     Function: PNMImage::set_xel_val
 //       Access: Published
 //  Description: Changes the RGB color at the indicated pixel.  Each
-//               component is in the range 0..maxval.
+//               component is in the range 0..maxval, encoded in
+//               the configured color space.  See set_xel if you
+//               instead have a linearized and normalized
+//               floating-point value.
 ////////////////////////////////////////////////////////////////////
 INLINE void PNMImage::
 set_xel_val(int x, int y, xelval r, xelval g, xelval b) {
@@ -331,7 +410,10 @@ set_xel_val(int x, int y, xelval r, xelval g, xelval b) {
 //       Access: Published
 //  Description: Changes all three color components at the indicated
 //               pixel to the same value.  The value is in the range
-//               0..maxval.
+//               component is in the range 0..maxval, encoded in
+//               the configured color space.  See set_xel if you
+//               instead have a linearized and normalized
+//               floating-point value.
 ////////////////////////////////////////////////////////////////////
 INLINE void PNMImage::
 set_xel_val(int x, int y, xelval gray) {
@@ -343,7 +425,8 @@ set_xel_val(int x, int y, xelval gray) {
 //     Function: PNMImage::get_red_val
 //       Access: Published
 //  Description: Returns the red component color at the indicated
-//               pixel.  The value returned is in the range 0..maxval.
+//               pixel.  The value returned is in the range 0..maxval
+//               and encoded in the configured color space.
 ////////////////////////////////////////////////////////////////////
 INLINE xelval PNMImage::
 get_red_val(int x, int y) const {
@@ -354,7 +437,8 @@ get_red_val(int x, int y) const {
 //     Function: PNMImage::get_green_val
 //       Access: Published
 //  Description: Returns the green component color at the indicated
-//               pixel.  The value returned is in the range 0..maxval.
+//               pixel.  The value returned is in the range 0..maxval
+//               and encoded in the configured color space.
 ////////////////////////////////////////////////////////////////////
 INLINE xelval PNMImage::
 get_green_val(int x, int y) const {
@@ -365,7 +449,8 @@ get_green_val(int x, int y) const {
 //     Function: PNMImage::get_blue_val
 //       Access: Published
 //  Description: Returns the blue component color at the indicated
-//               pixel.  The value returned is in the range 0..maxval.
+//               pixel.  The value returned is in the range 0..maxval
+//               and encoded in the configured color space.
 ////////////////////////////////////////////////////////////////////
 INLINE xelval PNMImage::
 get_blue_val(int x, int y) const {
@@ -379,8 +464,9 @@ get_blue_val(int x, int y) const {
 //               pixel.  This only has a meaningful value for
 //               grayscale images; for other image types, this returns
 //               the value of the blue channel only.  However, also
-//               see the get_bright() function.  The value returned is
-//               in the range 0..maxval.
+//               see the get_bright() function.
+//               The value returned is in the range 0..maxval and
+//               encoded in the configured color space.
 ////////////////////////////////////////////////////////////////////
 INLINE xelval PNMImage::
 get_gray_val(int x, int y) const {
@@ -393,7 +479,7 @@ get_gray_val(int x, int y) const {
 //  Description: Returns the alpha component color at the indicated
 //               pixel.  It is an error to call this unless
 //               has_alpha() is true.  The value returned is in the
-//               range 0..maxval.
+//               range 0..maxval and always linear.
 ////////////////////////////////////////////////////////////////////
 INLINE xelval PNMImage::
 get_alpha_val(int x, int y) const {
@@ -406,7 +492,9 @@ get_alpha_val(int x, int y) const {
 //       Access: Published
 //  Description: Sets the red component color only at the indicated
 //               pixel.  The value given should be in the range
-//               0..maxval.
+//               0..maxval, encoded in the configured color space.
+//               See set_red if you instead have a linearized and
+//               normalized floating-point value.
 ////////////////////////////////////////////////////////////////////
 INLINE void PNMImage::
 set_red_val(int x, int y, xelval r) {
@@ -419,7 +507,9 @@ set_red_val(int x, int y, xelval r) {
 //       Access: Published
 //  Description: Sets the green component color only at the indicated
 //               pixel.  The value given should be in the range
-//               0..maxval.
+//               0..maxval, encoded in the configured color space.
+//               See set_green if you instead have a linearized and
+//               normalized floating-point value.
 ////////////////////////////////////////////////////////////////////
 INLINE void PNMImage::
 set_green_val(int x, int y, xelval g) {
@@ -432,7 +522,9 @@ set_green_val(int x, int y, xelval g) {
 //       Access: Published
 //  Description: Sets the blue component color only at the indicated
 //               pixel.  The value given should be in the range
-//               0..maxval.
+//               0..maxval, encoded in the configured color space.
+//               See set_blue if you instead have a linearized and
+//               normalized floating-point value.
 ////////////////////////////////////////////////////////////////////
 INLINE void PNMImage::
 set_blue_val(int x, int y, xelval b) {
@@ -450,7 +542,9 @@ set_blue_val(int x, int y, xelval b) {
 //               which can set all the component colors to the same
 //               grayscale level, and hence works correctly both for
 //               grayscale and color images.  The value given should
-//               be in the range 0..maxval.
+//               be in the range 0..maxval, encoded in the configured
+//               color space.  See set_gray if you instead have a
+//               linearized normalized floating-point value.
 ////////////////////////////////////////////////////////////////////
 INLINE void PNMImage::
 set_gray_val(int x, int y, xelval gray) {
@@ -465,6 +559,9 @@ set_gray_val(int x, int y, xelval gray) {
 //               pixel.  It is an error to call this unless
 //               has_alpha() is true.  The value given should be in
 //               the range 0..maxval.
+//
+//               This value is always linearly encoded, even if the
+//               image is set to the sRGB color space.
 ////////////////////////////////////////////////////////////////////
 INLINE void PNMImage::
 set_alpha_val(int x, int y, xelval a) {
@@ -476,67 +573,185 @@ set_alpha_val(int x, int y, xelval a) {
 //     Function: PNMImage::get_xel
 //       Access: Published
 //  Description: Returns the RGB color at the indicated pixel.  Each
-//               component is a double in the range 0..1.
+//               component is a linearized float in the range 0..1.
 ////////////////////////////////////////////////////////////////////
-INLINE LRGBColord PNMImage::
+INLINE LRGBColorf PNMImage::
 get_xel(int x, int y) const {
-  return LRGBColord(from_val(get_red_val(x, y)),
-                   from_val(get_green_val(x, y)),
-                   from_val(get_blue_val(x, y)));
+  nassertr(x >= 0 && x < _x_size && y >= 0 && y < _y_size, LRGBColorf::zero());
+
+  const xel &col = row(y)[x];
+
+  switch (_xel_encoding) {
+  case XE_generic:
+  case XE_generic_alpha:
+    return LRGBColorf(col.r, col.g, col.b) * _inv_maxval;
+
+  case XE_generic_sRGB:
+  case XE_generic_sRGB_alpha:
+    return LRGBColorf(
+      decode_sRGB_float(col.r * _inv_maxval),
+      decode_sRGB_float(col.g * _inv_maxval),
+      decode_sRGB_float(col.b * _inv_maxval));
+
+  case XE_uchar_sRGB:
+  case XE_uchar_sRGB_alpha:
+  case XE_uchar_sRGB_sse2:
+  case XE_uchar_sRGB_alpha_sse2:
+    return LRGBColorf(
+      decode_sRGB_float((unsigned char)col.r),
+      decode_sRGB_float((unsigned char)col.g),
+      decode_sRGB_float((unsigned char)col.b));
+
+  case XE_scRGB:
+  case XE_scRGB_alpha:
+    return LRGBColorf((int)col.r - 4096,
+                      (int)col.g - 4096,
+                      (int)col.b - 4096) * (1.f / 8192.f);
+  }
 }
 
 ////////////////////////////////////////////////////////////////////
 //     Function: PNMImage::set_xel
 //       Access: Published
 //  Description: Changes the RGB color at the indicated pixel.  Each
-//               component is a double in the range 0..1.
+//               component is a linearized float in the range 0..1.
 ////////////////////////////////////////////////////////////////////
 INLINE void PNMImage::
-set_xel(int x, int y, const LRGBColord &value) {
-  set_xel_val(x, y, to_val(value[0]), to_val(value[1]), to_val(value[2]));
+set_xel(int x, int y, const LRGBColorf &value) {
+  nassertv(x >= 0 && x < _x_size && y >= 0 && y < _y_size);
+
+  xel &col = row(y)[x];
+
+  switch (_xel_encoding) {
+  case XE_generic:
+  case XE_generic_alpha:
+    {
+      LRGBColorf scaled = value * get_maxval() + 0.5f;
+      col.r = clamp_val((int)scaled[0]);
+      col.g = clamp_val((int)scaled[1]);
+      col.b = clamp_val((int)scaled[2]);
+    }
+    break;
+
+  case XE_generic_sRGB:
+  case XE_generic_sRGB_alpha:
+    col.r = clamp_val((int)
+      (encode_sRGB_float(value[0]) * get_maxval() + 0.5f));
+    col.g = clamp_val((int)
+      (encode_sRGB_float(value[1]) * get_maxval() + 0.5f));
+    col.b = clamp_val((int)
+      (encode_sRGB_float(value[2]) * get_maxval() + 0.5f));
+    break;
+
+  case XE_uchar_sRGB:
+  case XE_uchar_sRGB_alpha:
+    encode_sRGB_uchar(LColorf(value, 0.0f), col);
+    break;
+
+  case XE_uchar_sRGB_sse2:
+  case XE_uchar_sRGB_alpha_sse2:
+    encode_sRGB_uchar_sse2(LColorf(value, 0.0f), col);
+    break;
+
+  case XE_scRGB:
+  case XE_scRGB_alpha:
+    {
+      LRGBColorf scaled = value * 8192.f + 4096.5f;
+      col.r = min(max(0, (int)scaled[0]), 65535);
+      col.g = min(max(0, (int)scaled[1]), 65535);
+      col.b = min(max(0, (int)scaled[2]), 65535);
+    }
+    break;
+  }
 }
 
 ////////////////////////////////////////////////////////////////////
 //     Function: PNMImage::set_xel
 //       Access: Published
 //  Description: Changes the RGB color at the indicated pixel.  Each
-//               component is a double in the range 0..1.
+//               component is a linearized float in the range 0..1.
 ////////////////////////////////////////////////////////////////////
 INLINE void PNMImage::
-set_xel(int x, int y, double r, double g, double b) {
-  set_xel_val(x, y, to_val(r), to_val(g), to_val(b));
+set_xel(int x, int y, float r, float g, float b) {
+  set_xel(x, y, LRGBColorf(r, g, b));
 }
 
 ////////////////////////////////////////////////////////////////////
 //     Function: PNMImage::set_xel
 //       Access: Published
 //  Description: Changes all three color components at the indicated
-//               pixel to the same value.  The value is a double in
-//               the range 0..1.
+//               pixel to the same value.  The value is a linearized
+//               float in the range 0..1.
 ////////////////////////////////////////////////////////////////////
 INLINE void PNMImage::
-set_xel(int x, int y, double gray) {
-  set_xel_val(x, y, to_val(gray), to_val(gray), to_val(gray));
+set_xel(int x, int y, float gray) {
+  xelval val = to_val(gray);
+  set_xel_val(x, y, val);
 }
 
 ////////////////////////////////////////////////////////////////////
 //     Function: PNMImage::get_xel_a
 //       Access: Published
 //  Description: Returns the RGBA color at the indicated pixel.  Each
-//               component is a double in the range 0..1.
+//               component is a linearized float in the range 0..1.
 ////////////////////////////////////////////////////////////////////
-INLINE LColord PNMImage::
+INLINE LColorf PNMImage::
 get_xel_a(int x, int y) const {
-  if (has_alpha()) {
-    return LColord(from_val(get_red_val(x, y)),
-                  from_val(get_green_val(x, y)),
-                  from_val(get_blue_val(x, y)),
-                  from_val(get_alpha_val(x, y)));
-  } else {
-    return LColord(from_val(get_red_val(x, y)),
-                  from_val(get_green_val(x, y)),
-                  from_val(get_blue_val(x, y)),
-                  0.0);
+  const xel &col = row(y)[x];
+
+  switch (_xel_encoding) {
+  case XE_generic:
+    return LColorf(col.r, col.g, col.b, 0.0f) * _inv_maxval;
+
+  case XE_generic_alpha:
+    return LColorf(col.r, col.g, col.b, alpha_row(y)[x]) * _inv_maxval;
+
+  case XE_generic_sRGB:
+    return LColorf(
+      decode_sRGB_float(col.r * _inv_maxval),
+      decode_sRGB_float(col.g * _inv_maxval),
+      decode_sRGB_float(col.b * _inv_maxval),
+      0.0f);
+
+  case XE_generic_sRGB_alpha:
+    return LColorf(
+      decode_sRGB_float(col.r * _inv_maxval),
+      decode_sRGB_float(col.g * _inv_maxval),
+      decode_sRGB_float(col.b * _inv_maxval),
+      alpha_row(y)[x] * _inv_maxval);
+
+  case XE_uchar_sRGB:
+  case XE_uchar_sRGB_sse2:
+    return LColorf(
+      decode_sRGB_float((unsigned char)col.r),
+      decode_sRGB_float((unsigned char)col.g),
+      decode_sRGB_float((unsigned char)col.b),
+      0.0f);
+
+  case XE_uchar_sRGB_alpha:
+  case XE_uchar_sRGB_alpha_sse2:
+    return LColorf(
+      decode_sRGB_float((unsigned char)col.r),
+      decode_sRGB_float((unsigned char)col.g),
+      decode_sRGB_float((unsigned char)col.b),
+      alpha_row(y)[x] * (1.f / 255.f));
+
+  case XE_scRGB:
+    return LColorf((int)col.r - 4096,
+                   (int)col.g - 4096,
+                   (int)col.b - 4096,
+                   0) * (1.f / 8192.f);
+
+  case XE_scRGB_alpha:
+    {
+      static const LColorf scale(1.f / 8192.f, 1.f / 8192.f, 1.f / 8192.f, 1.f / 65535.f);
+      LColorf color((int)col.r - 4096,
+                    (int)col.g - 4096,
+                    (int)col.b - 4096,
+                    alpha_row(y)[x]);
+      color.componentwise_mult(scale);
+      return color;
+    }
   }
 }
 
@@ -544,13 +759,87 @@ get_xel_a(int x, int y) const {
 //     Function: PNMImage::set_xel_a
 //       Access: Published
 //  Description: Changes the RGBA color at the indicated pixel.  Each
-//               component is a double in the range 0..1.
+//               component is a linearized float in the range 0..1.
 ////////////////////////////////////////////////////////////////////
 INLINE void PNMImage::
-set_xel_a(int x, int y, const LColord &value) {
-  set_xel_val(x, y, to_val(value[0]), to_val(value[1]), to_val(value[2]));
-  if (has_alpha()) {
-    set_alpha_val(x, y, to_val(value[3]));
+set_xel_a(int x, int y, const LColorf &value) {
+  nassertv(x >= 0 && x < _x_size && y >= 0 && y < _y_size);
+
+  xel &col = row(y)[x];
+
+  switch (_xel_encoding) {
+  case XE_generic:
+    {
+      LColorf scaled = value * get_maxval() + 0.5f;
+      col.r = clamp_val((int)scaled[0]);
+      col.g = clamp_val((int)scaled[1]);
+      col.b = clamp_val((int)scaled[2]);
+    }
+    break;
+
+  case XE_generic_alpha:
+    {
+      LColorf scaled = value * get_maxval() + 0.5f;
+      col.r = clamp_val((int)scaled[0]);
+      col.g = clamp_val((int)scaled[1]);
+      col.b = clamp_val((int)scaled[2]);
+      alpha_row(y)[x] = clamp_val((int)scaled[3]);
+    }
+    break;
+
+  case XE_generic_sRGB:
+    col.r = clamp_val((int)
+      (encode_sRGB_float(value[0]) * get_maxval() + 0.5f));
+    col.g = clamp_val((int)
+      (encode_sRGB_float(value[1]) * get_maxval() + 0.5f));
+    col.b = clamp_val((int)
+      (encode_sRGB_float(value[2]) * get_maxval() + 0.5f));
+    break;
+
+  case XE_generic_sRGB_alpha:
+    col.r = clamp_val((int)
+      (encode_sRGB_float(value[0]) * get_maxval() + 0.5f));
+    col.g = clamp_val((int)
+      (encode_sRGB_float(value[1]) * get_maxval() + 0.5f));
+    col.b = clamp_val((int)
+      (encode_sRGB_float(value[2]) * get_maxval() + 0.5f));
+    alpha_row(y)[x] = clamp_val((int)(value[3] * get_maxval() + 0.5f));
+    break;
+
+  case XE_uchar_sRGB:
+    encode_sRGB_uchar(value, col);
+    break;
+
+  case XE_uchar_sRGB_alpha:
+    encode_sRGB_uchar(value, col, alpha_row(y)[x]);
+    break;
+
+  case XE_uchar_sRGB_sse2:
+    encode_sRGB_uchar_sse2(value, col);
+    break;
+
+  case XE_uchar_sRGB_alpha_sse2:
+    encode_sRGB_uchar_sse2(value, col, alpha_row(y)[x]);
+    break;
+
+  case XE_scRGB:
+    {
+      LColorf scaled = value * 8192.0f + 4096.5f;
+      col.r = min(max(0, (int)scaled[0]), 65535);
+      col.g = min(max(0, (int)scaled[1]), 65535);
+      col.b = min(max(0, (int)scaled[2]), 65535);
+    }
+    break;
+
+  case XE_scRGB_alpha:
+    {
+      LColorf scaled = value * 8192.0f + 4096.5f;
+      col.r = min(max(0, (int)scaled[0]), 65535);
+      col.g = min(max(0, (int)scaled[1]), 65535);
+      col.b = min(max(0, (int)scaled[2]), 65535);
+      alpha_row(y)[x] = min(max(0, (int)(value[3] * 65535 + 0.5f)), 65535);
+    }
+    break;
   }
 }
 
@@ -558,24 +847,21 @@ set_xel_a(int x, int y, const LColord &value) {
 //     Function: PNMImage::set_xel_a
 //       Access: Published
 //  Description: Changes the RGBA color at the indicated pixel.  Each
-//               component is a double in the range 0..1.
+//               component is a linearized float in the range 0..1.
 ////////////////////////////////////////////////////////////////////
 INLINE void PNMImage::
-set_xel_a(int x, int y, double r, double g, double b, double a) {
-  set_xel_val(x, y, to_val(r), to_val(g), to_val(b));
-  if (has_alpha()) {
-    set_alpha_val(x, y, to_val(a));
-  }
+set_xel_a(int x, int y, float r, float g, float b, float a) {
+  set_xel_a(x, y, LColorf(r, g, b, a));
 }
 
 ////////////////////////////////////////////////////////////////////
 //     Function: PNMImage::get_red
 //       Access: Published
 //  Description: Returns the red component color at the indicated
-//               pixel.  The value returned is a double in the range
-//               0..1.
+//               pixel.  The value returned is a linearized float
+//               in the range 0..1.
 ////////////////////////////////////////////////////////////////////
-INLINE double PNMImage::
+INLINE float PNMImage::
 get_red(int x, int y) const {
   return from_val(get_red_val(x, y));
 }
@@ -584,10 +870,10 @@ get_red(int x, int y) const {
 //     Function: PNMImage::get_green
 //       Access: Published
 //  Description: Returns the green component color at the indicated
-//               pixel.  The value returned is a double in the range
-//               0..1.
+//               pixel.  The value returned is a linearized float
+//               in the range 0..1.
 ////////////////////////////////////////////////////////////////////
-INLINE double PNMImage::
+INLINE float PNMImage::
 get_green(int x, int y) const {
   return from_val(get_green_val(x, y));
 }
@@ -596,10 +882,10 @@ get_green(int x, int y) const {
 //     Function: PNMImage::get_blue
 //       Access: Published
 //  Description: Returns the blue component color at the indicated
-//               pixel.  The value returned is a double in the range
-//               0..1.
+//               pixel.  The value returned is a linearized float
+//               in the range 0..1.
 ////////////////////////////////////////////////////////////////////
-INLINE double PNMImage::
+INLINE float PNMImage::
 get_blue(int x, int y) const {
   return from_val(get_blue_val(x, y));
 }
@@ -611,10 +897,10 @@ get_blue(int x, int y) const {
 //               pixel.  This only has a meaningful value for
 //               grayscale images; for other image types, this returns
 //               the value of the blue channel only.  However, also
-//               see the get_bright() function.  The value returned is
-//               a double in the range 0..1.
+//               see the get_bright() function.  The value returned
+//               is a linearized float in the range 0..1.
 ////////////////////////////////////////////////////////////////////
-INLINE double PNMImage::
+INLINE float PNMImage::
 get_gray(int x, int y) const {
   return from_val(get_gray_val(x, y));
 }
@@ -624,23 +910,23 @@ get_gray(int x, int y) const {
 //       Access: Published
 //  Description: Returns the alpha component color at the indicated
 //               pixel.  It is an error to call this unless
-//               has_alpha() is true.  The value returned is a double
+//               has_alpha() is true.  The value returned is a float
 //               in the range 0..1.
 ////////////////////////////////////////////////////////////////////
-INLINE double PNMImage::
+INLINE float PNMImage::
 get_alpha(int x, int y) const {
-  return from_val(get_alpha_val(x, y));
+  return from_alpha_val(get_alpha_val(x, y));
 }
 
 ////////////////////////////////////////////////////////////////////
 //     Function: PNMImage::set_red
 //       Access: Published
 //  Description: Sets the red component color only at the indicated
-//               pixel.  The value given should be a double in the
-//               range 0..1.
+//               pixel.  The value given should be a linearized
+//               float in the range 0..1.
 ////////////////////////////////////////////////////////////////////
 INLINE void PNMImage::
-set_red(int x, int y, double r) {
+set_red(int x, int y, float r) {
   set_red_val(x, y, to_val(r));
 }
 
@@ -648,24 +934,24 @@ set_red(int x, int y, double r) {
 //     Function: PNMImage::set_green
 //       Access: Published
 //  Description: Sets the green component color only at the indicated
-//               pixel.  The value given should be a double in the
-//               range 0..1.
+//               pixel.  The value given should be a linearized
+//               float in the range 0..1.
 ////////////////////////////////////////////////////////////////////
 INLINE void PNMImage::
-set_green(int x, int y, double r) {
-  set_green_val(x, y, to_val(r));
+set_green(int x, int y, float g) {
+  set_green_val(x, y, to_val(g));
 }
 
 ////////////////////////////////////////////////////////////////////
 //     Function: PNMImage::set_blue
 //       Access: Published
 //  Description: Sets the blue component color only at the indicated
-//               pixel.  The value given should be a double in the
-//               range 0..1.
+//               pixel.  The value given should be a linearized
+//               float in the range 0..1.
 ////////////////////////////////////////////////////////////////////
 INLINE void PNMImage::
-set_blue(int x, int y, double r) {
-  set_blue_val(x, y, to_val(r));
+set_blue(int x, int y, float b) {
+  set_blue_val(x, y, to_val(b));
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -678,11 +964,11 @@ set_blue(int x, int y, double r) {
 //               can set all the component colors to the same
 //               grayscale level, and hence works correctly both for
 //               grayscale and color images.  The value given should
-//               be a double in the range 0..1.
+//               be a linearized float in the range 0..1.
 ////////////////////////////////////////////////////////////////////
 INLINE void PNMImage::
-set_gray(int x, int y, double r) {
-  set_gray_val(x, y, to_val(r));
+set_gray(int x, int y, float gray) {
+  set_gray_val(x, y, to_val(gray));
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -694,51 +980,19 @@ set_gray(int x, int y, double r) {
 //               the range 0..1.
 ////////////////////////////////////////////////////////////////////
 INLINE void PNMImage::
-set_alpha(int x, int y, double r) {
-  set_alpha_val(x, y, to_val(r));
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PNMImage::get_channel
-//       Access: Published
-//  Description: Returns the nth component color at the indicated
-//               pixel.  The channel index should be in the range
-//               0..(get_num_channels()-1).  The channels are ordered B,
-//               G, R, A.  This is slightly less optimal than
-//               accessing the component values directly by named
-//               methods.  The value returned is a double in the range
-//               0..1.
-////////////////////////////////////////////////////////////////////
-INLINE double PNMImage::
-get_channel(int x, int y, int channel) const {
-  return from_val(get_channel_val(x, y, channel));
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PNMImage::set_channel_val
-//       Access: Published
-//  Description: Sets the nth component color at the indicated
-//               pixel.  The channel index should be in the range
-//               0..(get_num_channels()-1).  The channels are ordered B,
-//               G, R, A.  This is slightly less optimal than
-//               setting the component values directly by named
-//               methods.  The value given should be a double in the
-//               range 0..1.
-////////////////////////////////////////////////////////////////////
-INLINE void PNMImage::
-set_channel(int x, int y, int channel, double value) {
-  set_channel_val(x, y, channel, to_val(value));
+set_alpha(int x, int y, float a) {
+  set_alpha_val(x, y, to_alpha_val(a));
 }
 
 ////////////////////////////////////////////////////////////////////
 //     Function: PNMImage::get_bright
 //       Access: Published
 //  Description: Returns the linear brightness of the given xel, as a
-//               double in the range 0..1.  This flavor of
+//               linearized float in the range 0..1.  This flavor of
 //               get_bright() returns the correct grayscale brightness
 //               level for both full-color and grayscale images.
 ////////////////////////////////////////////////////////////////////
-INLINE double PNMImage::
+INLINE float PNMImage::
 get_bright(int x, int y) const {
   return get_bright(x, y, _default_rc, _default_gc, _default_bc);
 }
@@ -751,11 +1005,9 @@ get_bright(int x, int y) const {
 //               for the RGB color at the indicated pixel, based on
 //               the supplied weights for each component.
 ////////////////////////////////////////////////////////////////////
-INLINE double PNMImage::
-get_bright(int x, int y, double rc, double gc, double bc) const {
-  return from_val((int)(rc * get_red_val(x, y) +
-                        gc * get_green_val(x, y) +
-                        bc * get_blue_val(x, y)));
+INLINE float PNMImage::
+get_bright(int x, int y, float rc, float gc, float bc) const {
+  return get_xel(x, y).dot(LVecBase3f(rc, gc, bc));
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -766,12 +1018,9 @@ get_bright(int x, int y, double rc, double gc, double bc) const {
 //               value for the RGBA color at the indicated pixel,
 //               based on the supplied weights for each component.
 ////////////////////////////////////////////////////////////////////
-INLINE double PNMImage::
-get_bright(int x, int y, double rc, double gc, double bc, double ac) const {
-  return from_val((int)(rc * get_red_val(x, y) +
-                        gc * get_green_val(x, y) +
-                        bc * get_blue_val(x, y) +
-                        ac * get_alpha_val(x, y)));
+INLINE float PNMImage::
+get_bright(int x, int y, float rc, float gc, float bc, float ac) const {
+  return get_xel_a(x, y).dot(LVecBase4f(rc, gc, bc, ac));
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -784,7 +1033,7 @@ get_bright(int x, int y, double rc, double gc, double bc, double ac) const {
 //               alpha of 0.0 is fully transparent and does nothing.
 ////////////////////////////////////////////////////////////////////
 INLINE void PNMImage::
-blend(int x, int y, const LRGBColord &val, double alpha) {
+blend(int x, int y, const LRGBColorf &val, float alpha) {
   blend(x, y, val[0], val[1], val[2], alpha);
 }
 
@@ -818,7 +1067,7 @@ operator [] (int y) const {
 //               the effect is that of a blur operation.
 ////////////////////////////////////////////////////////////////////
 INLINE void PNMImage::
-box_filter(double radius) {
+box_filter(float radius) {
   box_filter_from(radius, *this);
 }
 
@@ -830,7 +1079,7 @@ box_filter(double radius) {
 //               the effect is that of a blur operation.
 ////////////////////////////////////////////////////////////////////
 INLINE void PNMImage::
-gaussian_filter(double radius) {
+gaussian_filter(float radius) {
   gaussian_filter_from(radius, *this);
 }
 
@@ -843,7 +1092,7 @@ gaussian_filter(double radius) {
 //               channels.  Does not affect the alpha channel.
 ////////////////////////////////////////////////////////////////////
 INLINE void PNMImage::
-gamma_correct(double from_gamma, double to_gamma) {
+gamma_correct(float from_gamma, float to_gamma) {
   apply_exponent(from_gamma / to_gamma);
 }
 
@@ -856,7 +1105,7 @@ gamma_correct(double from_gamma, double to_gamma) {
 //               channel.  Does not affect the RGB channels.
 ////////////////////////////////////////////////////////////////////
 INLINE void PNMImage::
-gamma_correct_alpha(double from_gamma, double to_gamma) {
+gamma_correct_alpha(float from_gamma, float to_gamma) {
   apply_exponent(1.0, from_gamma / to_gamma);
 }
 
@@ -868,7 +1117,7 @@ gamma_correct_alpha(double from_gamma, double to_gamma) {
 //               exponent, such that L' = L ^ exponent.
 ////////////////////////////////////////////////////////////////////
 INLINE void PNMImage::
-apply_exponent(double gray_exponent) {
+apply_exponent(float gray_exponent) {
   apply_exponent(gray_exponent, gray_exponent, gray_exponent, 1.0);
 }
 
@@ -880,7 +1129,7 @@ apply_exponent(double gray_exponent) {
 //               exponent, such that L' = L ^ exponent.
 ////////////////////////////////////////////////////////////////////
 INLINE void PNMImage::
-apply_exponent(double gray_exponent, double alpha_exponent) {
+apply_exponent(float gray_exponent, float alpha_exponent) {
   apply_exponent(gray_exponent, gray_exponent, gray_exponent, alpha_exponent);
 }
 
@@ -895,7 +1144,7 @@ apply_exponent(double gray_exponent, double alpha_exponent) {
 //               green_exponent are unused.
 ////////////////////////////////////////////////////////////////////
 INLINE void PNMImage::
-apply_exponent(double red_exponent, double green_exponent, double blue_exponent) {
+apply_exponent(float red_exponent, float green_exponent, float blue_exponent) {
   apply_exponent(red_exponent, green_exponent, blue_exponent, 1.0);
 }
 
@@ -1080,16 +1329,16 @@ setup_sub_image(const PNMImage &copy, int &xto, int &yto,
 //               from the center.
 ////////////////////////////////////////////////////////////////////
 INLINE void PNMImage::
-compute_spot_pixel(LColord &c, double d2,
-                   double min_radius, double max_radius,
-                   const LColord &fg, const LColord &bg) {
-  double d = sqrt(d2);
+compute_spot_pixel(LColorf &c, float d2,
+                   float min_radius, float max_radius,
+                   const LColorf &fg, const LColorf &bg) {
+  float d = sqrt(d2);
   if (d > max_radius) {
     c = bg;
   } else if (d > min_radius) {
     d = (d - min_radius) / (max_radius - min_radius);
-    double d2 = d * d;
-    double t = (3.0 * d2) - (2.0 * d * d2);
+    float d2 = d * d;
+    float t = (3.0 * d2) - (2.0 * d * d2);
     c = fg + t * (bg - fg);
   } else {
     c = fg;
@@ -1118,7 +1367,7 @@ operator + (const PNMImage &other) const {
 //               is added to each pixel in the provided image.
 ////////////////////////////////////////////////////////////////////
 INLINE PNMImage PNMImage::
-operator + (const LColord &other) const {
+operator + (const LColorf &other) const {
   PNMImage target (*this);
   target += other;
   return target;
@@ -1146,7 +1395,7 @@ operator - (const PNMImage &other) const {
 //               is subtracted from each pixel in the provided image.
 ////////////////////////////////////////////////////////////////////
 INLINE PNMImage PNMImage::
-operator - (const LColord &other) const {
+operator - (const LColorf &other) const {
   PNMImage target (*this);
   target -= other;
   return target;
@@ -1176,7 +1425,7 @@ operator * (const PNMImage &other) const {
 //               a constant floating-point multiplier value.
 ////////////////////////////////////////////////////////////////////
 INLINE PNMImage PNMImage::
-operator * (double multiplier) const {
+operator * (float multiplier) const {
   PNMImage target (*this);
   target *= multiplier;
   return target;
@@ -1189,7 +1438,7 @@ operator * (double multiplier) const {
 //               is multiplied to each pixel in the provided image.
 ////////////////////////////////////////////////////////////////////
 INLINE PNMImage PNMImage::
-operator * (const LColord &other) const {
+operator * (const LColorf &other) const {
   PNMImage target (*this);
   target *= other;
   return target;

File diff suppressed because it is too large
+ 388 - 120
panda/src/pnmimage/pnmImage.cxx


+ 123 - 83
panda/src/pnmimage/pnmImage.h

@@ -19,13 +19,13 @@
 
 #include "pnmImageHeader.h"
 #include "pnmBrush.h"
-
+#include "stackedPerlinNoise2.h"
+#include "convert_srgb.h"
 #include "luse.h"
 
 class PNMReader;
 class PNMWriter;
 class PNMFileType;
-class StackedPerlinNoise2;
 
 ////////////////////////////////////////////////////////////////////
 //       Class : PNMImage
@@ -47,44 +47,59 @@ class StackedPerlinNoise2;
 //               xels manipulated, and written out again, or a black
 //               image may be constructed from scratch.
 //
+//               A PNMImage has a color space and a maxval, the
+//               combination of which defines how a floating-point
+//               linear color value is encoded as an integer value in
+//               memory.  The functions ending in _val operate on
+//               encoded colors, whereas the regular ones work with
+//               linear floating-point values.  All operations are
+//               color space correct unless otherwise specified.
+//
 //               The image is of size XSize() by YSize() xels,
 //               numbered from top to bottom, left to right, beginning
 //               at zero.
 //
 //               Files can be specified by filename, or by an iostream
 //               pointer.  The filename "-" refers to stdin or stdout.
+//
+//               This class is not inherently thread-safe; use it
+//               from a single thread or protect access using a mutex.
 ////////////////////////////////////////////////////////////////////
 class EXPCL_PANDA_PNMIMAGE PNMImage : public PNMImageHeader {
 PUBLISHED:
   INLINE PNMImage();
   PNMImage(const Filename &filename, PNMFileType *type = NULL);
   INLINE PNMImage(int x_size, int y_size, int num_channels = 3,
-                  xelval maxval = 255, PNMFileType *type = NULL);
+                  xelval maxval = 255, PNMFileType *type = NULL,
+                  ColorSpace color_space = CS_linear);
   INLINE PNMImage(const PNMImage &copy);
   INLINE void operator = (const PNMImage &copy);
 
   INLINE ~PNMImage();
 
   INLINE xelval clamp_val(int input_value) const;
-  INLINE xelval to_val(double input_value) const;
-  INLINE double from_val(xelval input_value) const;
+  INLINE xelval to_val(float input_value) const;
+  INLINE xelval to_alpha_val(float input_value) const;
+  INLINE float from_val(xelval input_value) const;
+  INLINE float from_alpha_val(xelval input_value) const;
 
   void clear();
   void clear(int x_size, int y_size, int num_channels = 3,
-             xelval maxval = 255, PNMFileType *type = NULL);
+             xelval maxval = 255, PNMFileType *type = NULL,
+             ColorSpace color_space = CS_linear);
 
   void copy_from(const PNMImage &copy);
   void copy_channel(const PNMImage &copy, int src_channel, int dest_channel);
   void copy_header_from(const PNMImageHeader &header);
   void take_from(PNMImage &orig);
 
-  INLINE void fill(double red, double green, double blue);
-  INLINE void fill(double gray = 0.0);
+  INLINE void fill(float red, float green, float blue);
+  INLINE void fill(float gray = 0.0);
 
   void fill_val(xelval red, xelval green, xelval blue);
   INLINE void fill_val(xelval gray = 0);
 
-  INLINE void alpha_fill(double alpha = 0.0);
+  INLINE void alpha_fill(float alpha = 0.0);
   void alpha_fill_val(xelval alpha = 0);
 
   INLINE void set_read_size(int x_size, int y_size);
@@ -92,6 +107,7 @@ PUBLISHED:
   INLINE bool has_read_size() const;
   INLINE int get_read_x_size() const;
   INLINE int get_read_y_size() const;
+  INLINE ColorSpace get_color_space() const;
 
   BLOCKING bool read(const Filename &filename, PNMFileType *type = NULL,
                      bool report_unknown_type = true);
@@ -109,11 +125,12 @@ PUBLISHED:
 
   INLINE void set_num_channels(int num_channels);
   void set_color_type(ColorType color_type);
+  void set_color_space(ColorSpace color_space);
 
   INLINE void add_alpha();
   INLINE void remove_alpha();
   INLINE void make_grayscale();
-  void make_grayscale(double rc, double gc, double bc);
+  void make_grayscale(float rc, float gc, float bc);
   INLINE void make_rgb();
 
   BLOCKING void reverse_rows();
@@ -124,7 +141,8 @@ PUBLISHED:
   // The *_val() functions return or set the color values in the range
   // [0..get_maxval()].  This range may be different for different
   // images!  Use the corresponding functions (without _val()) to work
-  // in the normalized range [0..1].
+  // in the normalized range [0..1].  These return values in the
+  // image's stored color space.
 
   INLINE const xel &get_xel_val(int x, int y) const;
   INLINE void set_xel_val(int x, int y, const xel &value);
@@ -145,46 +163,45 @@ PUBLISHED:
 
   xelval get_channel_val(int x, int y, int channel) const;
   void set_channel_val(int x, int y, int channel, xelval value);
+  float get_channel(int x, int y, int channel) const;
+  void set_channel(int x, int y, int channel, float value);
 
   PixelSpec get_pixel(int x, int y) const;
   void set_pixel(int x, int y, const PixelSpec &pixel);
 
   // The corresponding get_xel(), set_xel(), get_red(), etc. functions
   // automatically scale their values by get_maxval() into the range
-  // [0..1].
-
-  INLINE LRGBColord get_xel(int x, int y) const;
-  INLINE void set_xel(int x, int y, const LRGBColord &value);
-  INLINE void set_xel(int x, int y, double r, double g, double b);
-  INLINE void set_xel(int x, int y, double gray);
-
-  INLINE LColord get_xel_a(int x, int y) const;
-  INLINE void set_xel_a(int x, int y, const LColord &value);
-  INLINE void set_xel_a(int x, int y, double r, double g, double b, double a);
-
-  INLINE double get_red(int x, int y) const;
-  INLINE double get_green(int x, int y) const;
-  INLINE double get_blue(int x, int y) const;
-  INLINE double get_gray(int x, int y) const;
-  INLINE double get_alpha(int x, int y) const;
-
-  INLINE void set_red(int x, int y, double r);
-  INLINE void set_green(int x, int y, double g);
-  INLINE void set_blue(int x, int y, double b);
-  INLINE void set_gray(int x, int y, double gray);
-  INLINE void set_alpha(int x, int y, double a);
-
-  INLINE double get_channel(int x, int y, int channel) const;
-  INLINE void set_channel(int x, int y, int channel, double value);
-
-  INLINE double get_bright(int x, int y) const;
-  INLINE double get_bright(int x, int y, double rc, double gc,
-                           double bc) const;
-  INLINE double get_bright(int x, int y, double rc, double gc,
-                           double bc, double ac) const;
-
-  INLINE void blend(int x, int y, const LRGBColord &val, double alpha);
-  void blend(int x, int y, double r, double g, double b, double alpha);
+  // [0..1], and into the linear color space.
+
+  INLINE LRGBColorf get_xel(int x, int y) const;
+  INLINE void set_xel(int x, int y, const LRGBColorf &value);
+  INLINE void set_xel(int x, int y, float r, float g, float b);
+  INLINE void set_xel(int x, int y, float gray);
+
+  INLINE LColorf get_xel_a(int x, int y) const;
+  INLINE void set_xel_a(int x, int y, const LColorf &value);
+  INLINE void set_xel_a(int x, int y, float r, float g, float b, float a);
+
+  INLINE float get_red(int x, int y) const;
+  INLINE float get_green(int x, int y) const;
+  INLINE float get_blue(int x, int y) const;
+  INLINE float get_gray(int x, int y) const;
+  INLINE float get_alpha(int x, int y) const;
+
+  INLINE void set_red(int x, int y, float r);
+  INLINE void set_green(int x, int y, float g);
+  INLINE void set_blue(int x, int y, float b);
+  INLINE void set_gray(int x, int y, float gray);
+  INLINE void set_alpha(int x, int y, float a);
+
+  INLINE float get_bright(int x, int y) const;
+  INLINE float get_bright(int x, int y, float rc, float gc,
+                           float bc) const;
+  INLINE float get_bright(int x, int y, float rc, float gc,
+                           float bc, float ac) const;
+
+  INLINE void blend(int x, int y, const LRGBColorf &val, float alpha);
+  void blend(int x, int y, float r, float g, float b, float alpha);
 
   // If you're used to the NetPBM library and like working with a 2-d
   // array of xels, and using the PNM macros to access their components,
@@ -199,68 +216,68 @@ PUBLISHED:
   void blend_sub_image(const PNMImage &copy, int xto, int yto,
                        int xfrom = 0, int yfrom = 0,
                        int x_size = -1, int y_size = -1,
-                       double pixel_scale = 1.0);
+                       float pixel_scale = 1.0);
   void add_sub_image(const PNMImage &copy, int xto, int yto,
                      int xfrom = 0, int yfrom = 0,
                      int x_size = -1, int y_size = -1,
-                     double pixel_scale = 1.0);
+                     float pixel_scale = 1.0);
   void mult_sub_image(const PNMImage &copy, int xto, int yto,
                       int xfrom = 0, int yfrom = 0,
                       int x_size = -1, int y_size = -1,
-                      double pixel_scale = 1.0);
+                      float pixel_scale = 1.0);
   void darken_sub_image(const PNMImage &copy, int xto, int yto,
                         int xfrom = 0, int yfrom = 0,
                         int x_size = -1, int y_size = -1,
-                        double pixel_scale = 1.0);
+                        float pixel_scale = 1.0);
   void lighten_sub_image(const PNMImage &copy, int xto, int yto,
                          int xfrom = 0, int yfrom = 0,
                          int x_size = -1, int y_size = -1,
-                         double pixel_scale = 1.0);
-  void threshold(const PNMImage &select_image, int channel, double threshold,
+                         float pixel_scale = 1.0);
+  void threshold(const PNMImage &select_image, int channel, float threshold,
                  const PNMImage &lt, const PNMImage &ge);
-  BLOCKING void fill_distance_inside(const PNMImage &mask, double threshold, int radius, bool shrink_from_border);
-  BLOCKING void fill_distance_outside(const PNMImage &mask, double threshold, int radius);
+  BLOCKING void fill_distance_inside(const PNMImage &mask, float threshold, int radius, bool shrink_from_border);
+  BLOCKING void fill_distance_outside(const PNMImage &mask, float threshold, int radius);
 
-  void rescale(double min_val, double max_val);
+  void rescale(float min_val, float max_val);
 
   void copy_channel(const PNMImage &copy, int xto, int yto, int cto,
                     int xfrom = 0, int yfrom = 0, int cfrom = 0,
                     int x_size = -1, int y_size = -1);
 
-  void render_spot(const LColord &fg, const LColord &bg,
-                   double min_radius, double max_radius);
+  void render_spot(const LColorf &fg, const LColorf &bg,
+                   float min_radius, float max_radius);
 
   void expand_border(int left, int right, int bottom, int top,
-                     const LColord &color);
+                     const LColorf &color);
 
   // The bodies for the non-inline *_filter() functions can be found
   // in the file pnm-image-filter.cxx.
 
-  INLINE void box_filter(double radius = 1.0);
-  INLINE void gaussian_filter(double radius = 1.0);
+  INLINE void box_filter(float radius = 1.0);
+  INLINE void gaussian_filter(float radius = 1.0);
 
   void unfiltered_stretch_from(const PNMImage &copy);
-  void box_filter_from(double radius, const PNMImage &copy);
-  void gaussian_filter_from(double radius, const PNMImage &copy);
+  void box_filter_from(float radius, const PNMImage &copy);
+  void gaussian_filter_from(float radius, const PNMImage &copy);
   void quick_filter_from(const PNMImage &copy,
                          int xborder = 0, int yborder = 0);
 
   void make_histogram(Histogram &hist);
-  void perlin_noise_fill(double sx, double sy, int table_size = 256,
+  void perlin_noise_fill(float sx, float sy, int table_size = 256,
                          unsigned long seed = 0);
   void perlin_noise_fill(StackedPerlinNoise2 &perlin);
 
   void remix_channels(const LMatrix4 &conv);
-  INLINE void gamma_correct(double from_gamma, double to_gamma);
-  INLINE void gamma_correct_alpha(double from_gamma, double to_gamma);
-  INLINE void apply_exponent(double gray_exponent);
-  INLINE void apply_exponent(double gray_exponent, double alpha_exponent);
-  INLINE void apply_exponent(double red_exponent, double green_exponent, double blue_exponent);
-  void apply_exponent(double red_exponent, double green_exponent, double blue_exponent, double alpha_exponent);
+  INLINE void gamma_correct(float from_gamma, float to_gamma);
+  INLINE void gamma_correct_alpha(float from_gamma, float to_gamma);
+  INLINE void apply_exponent(float gray_exponent);
+  INLINE void apply_exponent(float gray_exponent, float alpha_exponent);
+  INLINE void apply_exponent(float red_exponent, float green_exponent, float blue_exponent);
+  void apply_exponent(float red_exponent, float green_exponent, float blue_exponent, float alpha_exponent);
 
-  LRGBColord get_average_xel() const;
-  LColord get_average_xel_a() const;
-  double get_average_gray() const;
+  LRGBColorf get_average_xel() const;
+  LColorf get_average_xel_a() const;
+  float get_average_gray() const;
 
   void do_fill_distance(int xi, int yi, int d);
 
@@ -288,37 +305,60 @@ private:
                               int &xfrom, int &yfrom, int &x_size, int &y_size,
                               int &xmin, int &ymin, int &xmax, int &ymax);
 
-  INLINE static void compute_spot_pixel(LColord &c, double d2,
-                                        double min_radius, double max_radius,
-                                        const LColord &fg, const LColord &bg);
+  INLINE static void compute_spot_pixel(LColorf &c, float d2,
+                                        float min_radius, float max_radius,
+                                        const LColorf &fg, const LColorf &bg);
 
   void setup_rc();
+  void setup_encoding();
 
 PUBLISHED:
   PNMImage operator ~() const;
 
   INLINE PNMImage operator + (const PNMImage &other) const;
-  INLINE PNMImage operator + (const LColord &other) const;
+  INLINE PNMImage operator + (const LColorf &other) const;
   INLINE PNMImage operator - (const PNMImage &other) const;
-  INLINE PNMImage operator - (const LColord &other) const;
+  INLINE PNMImage operator - (const LColorf &other) const;
   INLINE PNMImage operator * (const PNMImage &other) const;
-  INLINE PNMImage operator * (double multiplier) const;
-  INLINE PNMImage operator * (const LColord &other) const;
+  INLINE PNMImage operator * (float multiplier) const;
+  INLINE PNMImage operator * (const LColorf &other) const;
   void operator += (const PNMImage &other);
-  void operator += (const LColord &other);
+  void operator += (const LColorf &other);
   void operator -= (const PNMImage &other);
-  void operator -= (const LColord &other);
+  void operator -= (const LColorf &other);
   void operator *= (const PNMImage &other);
-  void operator *= (double multiplier);
-  void operator *= (const LColord &other);
+  void operator *= (float multiplier);
+  void operator *= (const LColorf &other);
 
 private:
   xel *_array;
   xelval *_alpha;
-  double _default_rc, _default_gc, _default_bc;
+  float _default_rc, _default_gc, _default_bc;
 
   int _read_x_size, _read_y_size;
   bool _has_read_size;
+
+  // The reciprocal of _maxval, as an optimization for from_val.
+  float _inv_maxval;
+
+  // These method pointers contain the implementation for to_val and
+  // from_val, respectively, dependent on the maxval and color space.
+  ColorSpace _color_space;
+
+  // The following enum determines which code path we should take in
+  // the set_xel and get_xel methods.
+  enum XelEncoding {
+    XE_generic,
+    XE_generic_alpha,
+    XE_generic_sRGB,
+    XE_generic_sRGB_alpha,
+    XE_uchar_sRGB,
+    XE_uchar_sRGB_alpha,
+    XE_uchar_sRGB_sse2,
+    XE_uchar_sRGB_alpha_sse2,
+    XE_scRGB,
+    XE_scRGB_alpha
+  } _xel_encoding;
 };
 
 #include "pnmImage.I"

+ 14 - 0
panda/src/pnmimage/pnmImageHeader.I

@@ -23,6 +23,7 @@ PNMImageHeader() {
   _y_size = 0;
   _num_channels = 0;
   _maxval = 255;
+  _color_space = CS_unspecified;
   _type = (PNMFileType *)NULL;
 }
 
@@ -37,6 +38,7 @@ PNMImageHeader(const PNMImageHeader &copy) :
   _y_size(copy._y_size),
   _num_channels(copy._num_channels),
   _maxval(copy._maxval),
+  _color_space(copy._color_space),
   _type(copy._type)
 {
 }
@@ -52,6 +54,7 @@ operator = (const PNMImageHeader &copy) {
   _y_size = copy._y_size;
   _num_channels = copy._num_channels;
   _maxval = copy._maxval;
+  _color_space = copy._color_space;
   _comment = copy._comment;
   _type = copy._type;
 }
@@ -153,6 +156,17 @@ get_maxval() const {
   return _maxval;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PNMImageHeader::get_color_space
+//       Access: Published
+//  Description: Returns the color space that the image is encoded
+//               in, or CS_unspecified if unknown.
+////////////////////////////////////////////////////////////////////
+INLINE ColorSpace PNMImageHeader::
+get_color_space() const {
+  return _color_space;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PNMImageHeader::get_x_size
 //       Access: Published

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

@@ -24,6 +24,7 @@
 #include "pnotify.h"
 #include "pmap.h"
 #include "pvector.h"
+#include "colorSpace.h"
 
 class PNMFileType;
 class PNMReader;
@@ -67,6 +68,7 @@ PUBLISHED:
   INLINE bool has_alpha() const;
 
   INLINE xelval get_maxval() const;
+  INLINE ColorSpace get_color_space() const;
 
   INLINE int get_x_size() const;
   INLINE int get_y_size() const;
@@ -134,7 +136,7 @@ PUBLISHED:
 
     INLINE xelval operator [](int n) const;
     INLINE static int size();
-    
+
     void output(ostream &out) const;
 
   public:
@@ -175,7 +177,7 @@ PUBLISHED:
   private:
     PixelCount _pixels;
     HistMap _hist_map;
-  };    
+  };
 
 protected:
   bool compute_histogram(HistMap &hist, xel *array, xelval *alpha,
@@ -187,6 +189,7 @@ protected:
   int _x_size, _y_size;
   int _num_channels;
   xelval _maxval;
+  ColorSpace _color_space;
   string _comment;
   PNMFileType *_type;
 };

+ 7 - 7
panda/src/pnmimage/pnmPainter.I

@@ -82,7 +82,7 @@ get_fill() const {
 //               current pen.
 ////////////////////////////////////////////////////////////////////
 INLINE void PNMPainter::
-draw_point(double x, double y) {
+draw_point(float x, float y) {
   draw_line(x, y, x, y);
 }
 
@@ -93,9 +93,9 @@ draw_point(double x, double y) {
 //               mostly-horizontal line.
 ////////////////////////////////////////////////////////////////////
 INLINE void PNMPainter::
-draw_hline_point(int x, double xa, double ya, double xd, double yd,
-                 double pixel_scale) {
-  double y = (yd * (x - xa) / xd) + ya;
+draw_hline_point(int x, float xa, float ya, float xd, float yd,
+                 float pixel_scale) {
+  float y = (yd * (x - xa) / xd) + ya;
   int ymax = (int)cceil(y);
   int ymin = (int)cfloor(y);
   if (ymax == ymin) {
@@ -113,9 +113,9 @@ draw_hline_point(int x, double xa, double ya, double xd, double yd,
 //               mostly-vertical line.
 ////////////////////////////////////////////////////////////////////
 INLINE void PNMPainter::
-draw_vline_point(int y, double xa, double ya, double xd, double yd,
-                 double pixel_scale) {
-  double x = (xd * (y - ya) / yd) + xa;
+draw_vline_point(int y, float xa, float ya, float xd, float yd,
+                 float pixel_scale) {
+  float x = (xd * (y - ya) / yd) + xa;
   int xmax = (int)cceil(x);
   int xmin = (int)cfloor(x);
   if (xmax == xmin) {

+ 8 - 8
panda/src/pnmimage/pnmPainter.cxx

@@ -35,8 +35,8 @@ PNMPainter(PNMImage &image, int xo, int yo) :
   _image(image),
   _xo(xo), _yo(yo)
 {
-  _pen = PNMBrush::make_pixel(LColord(0, 0, 0, 1));
-  _fill = PNMBrush::make_pixel(LColord(1, 1, 1, 1));
+  _pen = PNMBrush::make_pixel(LColorf(0, 0, 0, 1));
+  _fill = PNMBrush::make_pixel(LColorf(1, 1, 1, 1));
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -46,7 +46,7 @@ PNMPainter(PNMImage &image, int xo, int yo) :
 //               current pen.
 ////////////////////////////////////////////////////////////////////
 void PNMPainter::
-draw_line(double xa, double ya, double xb, double yb) {
+draw_line(float xa, float ya, float xb, float yb) {
   // Shift the line coordinates to position the center of the pen on
   // the line.
   xa -= (_pen->get_xc() - 0.5);
@@ -55,8 +55,8 @@ draw_line(double xa, double ya, double xb, double yb) {
   yb -= (_pen->get_yc() - 0.5);
 
   // Compute the line delta.
-  double xd = xb - xa;
-  double yd = yb - ya;
+  float xd = xb - xa;
+  float yd = yb - ya;
 
   if (xa == xb && ya == yb) {
     // Just a single point.  Treat it as a very short horizontal line.
@@ -154,16 +154,16 @@ draw_line(double xa, double ya, double xb, double yb) {
 //               opposite corners.
 ////////////////////////////////////////////////////////////////////
 void PNMPainter::
-draw_rectangle(double xa, double ya, double xb, double yb) {
+draw_rectangle(float xa, float ya, float xb, float yb) {
   // Make (xa, ya) be the upper-left corner, and (xb, yb) the
   // lower-right.
   if (xa > xb) {
-    double t = xa;
+    float t = xa;
     xa = xb;
     xb = t;
   }
   if (ya > yb) {
-    double t = ya;
+    float t = ya;
     ya = yb;
     yb = t;
   }

+ 9 - 9
panda/src/pnmimage/pnmPainter.h

@@ -41,17 +41,17 @@ PUBLISHED:
   INLINE void set_fill(PNMBrush *fill);
   INLINE PNMBrush *get_fill() const;
 
-  INLINE void draw_point(double x, double y);
-  void draw_line(double xa, double ya, double xb, double yb);
-  void draw_rectangle(double xa, double ya, double xb, double yb);
+  INLINE void draw_point(float x, float y);
+  void draw_line(float xa, float ya, float xb, float yb);
+  void draw_rectangle(float xa, float ya, float xb, float yb);
 
 private:
-  INLINE void draw_hline_point(int x, double xa, double ya, 
-                               double xd, double yd,
-                               double pixel_scale);
-  INLINE void draw_vline_point(int y, double xa, double ya, 
-                               double xd, double yd,
-                               double pixel_scale);
+  INLINE void draw_hline_point(int x, float xa, float ya,
+                               float xd, float yd,
+                               float pixel_scale);
+  INLINE void draw_vline_point(int y, float xa, float ya,
+                               float xd, float yd,
+                               float pixel_scale);
 
 private:
   PNMImage &_image;

+ 3 - 3
panda/src/pnmimage/pnmimage_base.h

@@ -113,9 +113,9 @@ EXPCL_PANDA_PNMIMAGE int pm_writelittlelong(ostream *out, long l);
 
 // These ratios are used to compute the brightness of a colored pixel; they
 // define the relative contributions of each of the components.
-static const double lumin_red = 0.299;
-static const double lumin_grn = 0.587;
-static const double lumin_blu = 0.114;
+static const float lumin_red = 0.299f;
+static const float lumin_grn = 0.587f;
+static const float lumin_blu = 0.114f;
 
 
 #endif

+ 50 - 11
panda/src/pnmimagetypes/pnmFileTypePNG.cxx

@@ -227,17 +227,40 @@ Reader(PNMFileType *type, istream *file, bool owns_file, string magic_number) :
   png_uint_32 height;
   int bit_depth;
   int color_type;
+  int srgb_intent;
+  double gamma;
 
   png_get_IHDR(_png, _info, &width, &height,
                &bit_depth, &color_type, NULL, NULL, NULL);
 
+  // Look for an sRGB chunk.
+  if (png_get_sRGB(_png, _info, &srgb_intent) == PNG_INFO_sRGB) {
+    _color_space = CS_sRGB;
+
+  } else if (png_get_gAMA(_png, _info, &gamma) == PNG_INFO_gAMA) {
+    // File specifies a gamma.
+    if (gamma >= 0.99 && gamma <= 1.01) {
+      _color_space = CS_linear;
+
+    } else if (gamma >= 0.44999 && gamma <= 0.455001) {
+      // It's probably close enough to sRGB.
+      _color_space = CS_sRGB;
+
+    } else {
+      pnmimage_png_cat.warning()
+        << "Unsupported image gamma " << gamma << ", "
+        << "please re-export image as sRGB or linear.\n";
+    }
+  }
+
   pnmimage_png_cat.debug()
     << "width = " << width << " height = " << height << " bit_depth = "
-    << bit_depth << " color_type = " << color_type << "\n";
+    << bit_depth << " color_type = " << color_type
+    << " color_space = " << _color_space << "\n";
 
   _x_size = width;
   _y_size = height;
-  _maxval = ( 1 << bit_depth ) - 1;
+  _maxval = (1 << bit_depth) - 1;
 
   if (bit_depth < 8) {
     png_set_packing(_png);
@@ -342,7 +365,7 @@ read_data(xel *array, xelval *alpha_data) {
       << "Allocating " << num_rows << " rows of " << row_byte_length
       << " bytes each.\n";
   }
-    
+
   // We need to read a full copy of the image in first, in libpng's
   // 2-d array format, mainly because we keep array and alpha data
   // separately, and there doesn't appear to be good support to get
@@ -384,7 +407,7 @@ read_data(xel *array, xelval *alpha_data) {
           alpha = (source[0] << 8) | source[1];
           source += 2;
         }
-        
+
       } else {
         if (get_color) {
           red = *source;
@@ -402,7 +425,7 @@ read_data(xel *array, xelval *alpha_data) {
           source++;
         }
       }
-      
+
       PPM_ASSIGN(array[pi], red, green, blue);
       if (get_alpha) {
         alpha_data[pi] = alpha;
@@ -593,9 +616,9 @@ write_data(xel *array, xelval *alpha_data) {
       if (compute_palette(palette, array, alpha_data, png_max_palette)) {
         pnmimage_png_cat.debug()
           << palette.size() << " colors found.\n";
-        
+
         int palette_bit_depth = make_png_bit_depth(pm_maxvaltobits(palette.size() - 1));
-        
+
         int total_bits = png_bit_depth;
         if (!is_grayscale()) {
           total_bits *= 3;
@@ -603,7 +626,7 @@ write_data(xel *array, xelval *alpha_data) {
         if (has_alpha()) {
           total_bits += png_bit_depth;
         }
-        
+
         if (palette_bit_depth < total_bits ||
             _maxval != (1 << true_bit_depth) - 1) {
           pnmimage_png_cat.debug()
@@ -616,7 +639,7 @@ write_data(xel *array, xelval *alpha_data) {
           // Re-sort the palette to put the semitransparent pixels at the
           // beginning.
           sort(palette.begin(), palette.end(), LowAlphaCompare());
-        
+
           double palette_scale = 255.0 / _maxval;
 
           int num_alpha = 0;
@@ -628,7 +651,7 @@ write_data(xel *array, xelval *alpha_data) {
             if (palette[i]._alpha != _maxval) {
               num_alpha = i + 1;
             }
-          
+
             // Also build a reverse-lookup from color to palette index in
             // the "histogram" structure.
             palette_lookup[palette[i]] = i;
@@ -664,7 +687,8 @@ write_data(xel *array, xelval *alpha_data) {
   pnmimage_png_cat.debug()
     << "width = " << _x_size << " height = " << _y_size
     << " maxval = " << _maxval << " bit_depth = "
-    << png_bit_depth << " color_type = " << color_type << "\n";
+    << png_bit_depth << " color_type = " << color_type
+    << " color_space = " << _color_space << "\n";
 
   png_set_IHDR(_png, _info, _x_size, _y_size, png_bit_depth,
                color_type, PNG_INTERLACE_NONE,
@@ -675,6 +699,21 @@ write_data(xel *array, xelval *alpha_data) {
     png_set_sBIT(_png, _info, &sig_bit);
   }
 
+  // Set the color space, if we know it.
+  switch (_color_space) {
+  case CS_linear:
+    png_set_gAMA(_png, _info, 1.0);
+    // Not sure if we should set cHRM to anything.
+    break;
+
+  case CS_sRGB:
+    png_set_sRGB_gAMA_and_cHRM(_png, _info, PNG_sRGB_INTENT_RELATIVE);
+    break;
+
+  default:
+    break;
+  }
+
   png_write_info(_png, _info);
 
 

+ 3 - 3
panda/src/pnmtext/config_pnmtext.cxx

@@ -25,11 +25,11 @@ ConfigureFn(config_pnmtext) {
 }
 
 ConfigVariableDouble text_point_size
-("text-point-size", 10.0f);
+("text-point-size", 10.0);
 ConfigVariableDouble text_pixels_per_unit
-("text-pixels-per-unit", 40.0f);
+("text-pixels-per-unit", 40.0);
 ConfigVariableDouble text_scale_factor
-("text-scale-factor", 2.0f);
+("text-scale-factor", 2.0);
 ConfigVariableBool text_native_antialias
 ("text-native-antialias", true);
 

+ 10 - 39
panda/src/pnmtext/pnmTextGlyph.cxx

@@ -54,8 +54,6 @@ place(PNMImage &dest_image, int xp, int yp, const LColor &fg) {
     // If we have no image, do nothing.
     return;
   }
-  LRGBColord fg_rgb(fg[0], fg[1], fg[2]);
-  double fg_alpha = fg[3];
 
   int left = xp + _left;
   int top = yp - _top;
@@ -72,18 +70,11 @@ place(PNMImage &dest_image, int xp, int yp, const LColor &fg) {
     for (int x = cleft; x < cright; x++) {
       double gval = get_value(x - left, y - top);
       if (gval == 1.0) {
-        dest_image.set_xel(x, y, fg_rgb);
-        if (dest_image.has_alpha()) {
-          dest_image.set_alpha(x, y, fg_alpha);
-        }
+        dest_image.set_xel_a(x, y, fg);
 
       } else if (gval > 0.0) {
-        LRGBColord bg_rgb = dest_image.get_xel(x, y);
-        dest_image.set_xel(x, y, fg_rgb * gval + bg_rgb * (1.0 - gval));
-        if (dest_image.has_alpha()) {
-          double bg_alpha = dest_image.get_alpha(x, y);
-          dest_image.set_alpha(x, y, fg_alpha * gval + bg_alpha * (1.0 - gval));
-        }
+        LColorf bg = dest_image.get_xel_a(x, y);
+        dest_image.set_xel_a(x, y, fg * gval + bg * (1.0 - gval));
       }
     }
   }
@@ -97,16 +88,12 @@ place(PNMImage &dest_image, int xp, int yp, const LColor &fg) {
 //               called earlier.
 ////////////////////////////////////////////////////////////////////
 void PNMTextGlyph::
-place(PNMImage &dest_image, int xp, int yp, const LColor &fg, 
+place(PNMImage &dest_image, int xp, int yp, const LColor &fg,
       const LColor &interior) {
   if (!_image.is_valid()) {
     // If we have no image, do nothing.
     return;
   }
-  LRGBColord fg_rgb(fg[0], fg[1], fg[2]);
-  double fg_alpha = fg[3];
-  LRGBColord interior_rgb(interior[0], interior[1], interior[2]);
-  double interior_alpha = interior[3];
 
   int left = xp + _left;
   int top = yp - _top;
@@ -123,38 +110,22 @@ place(PNMImage &dest_image, int xp, int yp, const LColor &fg,
     for (int x = cleft; x < cright; x++) {
       double gval = get_value(x - left, y - top);
       if (gval == 1.0) {
-        dest_image.set_xel(x, y, fg_rgb);
-        if (dest_image.has_alpha()) {
-          dest_image.set_alpha(x, y, fg_alpha);
-        }
+        dest_image.set_xel_a(x, y, fg);
 
       } else if (gval > 0.0) {
         bool is_interior = get_interior_flag(x - left, y - top);
-        LRGBColord bg_rgb;
+        LColorf bg;
         if (is_interior) {
-          bg_rgb = interior_rgb;
+          bg = interior;
         } else {
-          bg_rgb = dest_image.get_xel(x, y);
+          bg = dest_image.get_xel_a(x, y);
         }
 
-        dest_image.set_xel(x, y, fg_rgb * gval + bg_rgb * (1.0 - gval));
-        if (dest_image.has_alpha()) {
-          double bg_alpha;
-
-          if (is_interior) {
-            bg_alpha = interior_alpha;
-          } else {
-            bg_alpha = dest_image.get_alpha(x, y);
-          }
-          dest_image.set_alpha(x, y, fg_alpha * gval + bg_alpha * (1.0 - gval));
-        }
+        dest_image.set_xel_a(x, y, fg * gval + bg * (1.0 - gval));
       } else { // gval == 0.0
         bool is_interior = get_interior_flag(x - left, y - top);
         if (is_interior) {
-          dest_image.set_xel(x, y, interior_rgb);
-          if (dest_image.has_alpha()) {
-            dest_image.set_alpha(x, y, interior_alpha);
-          }
+          dest_image.set_xel_a(x, y, interior);
         }
       }
     }

+ 86 - 0
panda/src/putil/colorSpace.cxx

@@ -0,0 +1,86 @@
+// Filename: colorSpace.cxx
+// Created by:  rdb (02Jun14)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#include "colorSpace.h"
+#include "config_util.h"
+#include "configVariableEnum.h"
+#include "string_utils.h"
+
+#include "dconfig.h"
+#include "pnotify.h"
+
+#include <ctype.h>
+
+ColorSpace
+parse_color_space_string(const string &str) {
+  if (cmp_nocase_uh(str, "linear") == 0 ||
+             cmp_nocase_uh(str, "linear-rgb") == 0 ||
+             cmp_nocase_uh(str, "lrgb") == 0) {
+    return CS_linear;
+
+  } else if (cmp_nocase_uh(str, "srgb") == 0) {
+    return CS_sRGB;
+
+  } else if (cmp_nocase_uh(str, "scrgb") == 0) {
+    return CS_scRGB;
+
+  } else if (cmp_nocase_uh(str, "unspecified") == 0) {
+    return CS_unspecified;
+
+  } else if (cmp_nocase_uh(str, "non-color") == 0) {
+    // In case we want to add this as an enum value in the future.
+    return CS_linear;
+  }
+
+  util_cat->error()
+    << "Invalid color_space string: " << str << "\n";
+  return CS_linear;
+}
+
+string
+format_color_space(ColorSpace cs) {
+  ostringstream strm;
+  strm << cs;
+  return strm.str();
+}
+
+ostream &
+operator << (ostream &out, ColorSpace cs) {
+  switch (cs) {
+  case CS_linear:
+    return out << "linear";
+
+  case CS_sRGB:
+    return out << "sRGB";
+
+  case CS_scRGB:
+    return out << "scRGB";
+
+  case CS_unspecified:
+    return out << "unspecified";
+  }
+
+  util_cat->error()
+    << "Invalid color_space value: " << (int)cs << "\n";
+  nassertr(false, out);
+  return out;
+}
+
+istream &
+operator >> (istream &in, ColorSpace &cs) {
+  string word;
+  in >> word;
+  cs = parse_color_space_string(word);
+  return in;
+}

+ 54 - 0
panda/src/putil/colorSpace.h

@@ -0,0 +1,54 @@
+// Filename: colorSpace.h
+// Created by:  rdb (02Jun14)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef COLORSPACE_H
+#define COLORSPACE_H
+
+#include "pandabase.h"
+
+#include "typedef.h"
+
+BEGIN_PUBLISH
+
+enum ColorSpace {
+  // This value is not a color space, but is used to indicate that
+  // a color space has not been specified.
+  CS_unspecified = 0,
+
+  // CS_linear is not a color space per se, but represents the
+  // working color space of graphics APIs, which is linearized.  Since
+  // the conversion from sRGB to linear is defined, one could posit
+  // that it has the ITU-R BT.709 primaries, but this isn't meaningful
+  // as modern graphics APIs do not perform color management.
+  // All colors in Panda3D are linear unless otherwise specified.
+  CS_linear,
+
+  // This is the standard, gamma-2.2-corrected sRGB color space, as
+  // used by the majority of image formats.
+  CS_sRGB,
+
+  // This is a 16-bit encoded linear color space capable of encoding
+  // color values in the -0.5...7.4999 range.
+  CS_scRGB,
+};
+
+EXPCL_PANDA_PUTIL ColorSpace parse_color_space_string(const string &str);
+EXPCL_PANDA_PUTIL string format_color_space(ColorSpace cs);
+
+END_PUBLISH
+
+EXPCL_PANDA_PUTIL ostream &operator << (ostream &out, ColorSpace cs);
+EXPCL_PANDA_PUTIL istream &operator >> (istream &in, ColorSpace &cs);
+
+#endif

+ 1 - 0
panda/src/putil/p3putil_composite1.cxx

@@ -16,6 +16,7 @@
 #include "callbackData.cxx"
 #include "callbackObject.cxx"
 #include "clockObject.cxx"
+#include "colorSpace.cxx"
 #include "config_util.cxx"
 #include "configurable.cxx"
 #include "copyOnWriteObject.cxx"

+ 3 - 3
pandatool/src/imageprogs/imageTrans.cxx

@@ -85,7 +85,7 @@ run() {
   case C_rgba:
     _image.set_num_channels((int)_channels);
     break;
-    
+
   case C_r:
     _image.make_grayscale(1.0, 0.0, 0.0);
     _image.remove_alpha();
@@ -112,8 +112,8 @@ run() {
         _color_scale[2] != 1.0f) {
       for (int yi = 0; yi < _image.get_y_size(); ++yi) {
         for (int xi = 0; xi < _image.get_x_size(); ++xi) {
-          LRGBColord rgb = _image.get_xel(xi, yi);
-          _image.set_xel(xi, yi, 
+          LRGBColorf rgb = _image.get_xel(xi, yi);
+          _image.set_xel(xi, yi,
                          rgb[0] * _color_scale[0],
                          rgb[1] * _color_scale[1],
                          rgb[2] * _color_scale[2]);

+ 2 - 2
pandatool/src/palettizer/paletteImage.cxx

@@ -93,8 +93,8 @@ operator = (const PaletteImage::ClearedRegion &copy) {
 ////////////////////////////////////////////////////////////////////
 void PaletteImage::ClearedRegion::
 clear(PNMImage &image) {
-  LRGBColord rgb(pal->_background[0], pal->_background[1], pal->_background[2]);
-  double alpha = pal->_background[3];
+  LRGBColorf rgb(pal->_background[0], pal->_background[1], pal->_background[2]);
+  float alpha = pal->_background[3];
 
   for (int y = _y; y < _y + _y_size; y++) {
     for (int x = _x; x < _x + _x_size; x++) {

Some files were not shown because too many files changed in this diff