From 22120524a2f9a1f9330cdfa2b35bbcdf15d721d4 Mon Sep 17 00:00:00 2001 From: rdb Date: Sun, 15 Mar 2015 17:22:28 +0100 Subject: [PATCH] PNMImage sRGB support, use floats instead of doubles --- panda/src/gobj/texture.cxx | 45 +- panda/src/gobj/texture.h | 2 + panda/src/grutil/geoMipTerrain.cxx | 4 +- panda/src/pgraphnodes/spotlight.cxx | 2 +- panda/src/pnmimage/pfmFile.cxx | 33 +- panda/src/pnmimage/pfmFile.h | 16 +- panda/src/pnmimage/pnm-image-filter-core.cxx | 12 +- .../pnmimage/pnm-image-filter-sparse-core.cxx | 12 +- panda/src/pnmimage/pnm-image-filter.cxx | 177 ++-- panda/src/pnmimage/pnmBrush.I | 6 +- panda/src/pnmimage/pnmBrush.cxx | 153 ++-- panda/src/pnmimage/pnmBrush.h | 16 +- panda/src/pnmimage/pnmImage.I | 567 +++++++++---- panda/src/pnmimage/pnmImage.cxx | 798 ++++++++++++------ panda/src/pnmimage/pnmImage.h | 194 +++-- panda/src/pnmimage/pnmImageHeader.I | 14 + panda/src/pnmimage/pnmImageHeader.h | 7 +- panda/src/pnmimage/pnmPainter.I | 14 +- panda/src/pnmimage/pnmPainter.cxx | 16 +- panda/src/pnmimage/pnmPainter.h | 18 +- panda/src/pnmimage/pnmimage_base.h | 6 +- panda/src/pnmimagetypes/pnmFileTypePNG.cxx | 61 +- panda/src/pnmtext/config_pnmtext.cxx | 6 +- panda/src/pnmtext/pnmTextGlyph.cxx | 49 +- panda/src/putil/colorSpace.cxx | 86 ++ panda/src/putil/colorSpace.h | 54 ++ panda/src/putil/p3putil_composite1.cxx | 1 + pandatool/src/imageprogs/imageTrans.cxx | 6 +- pandatool/src/palettizer/paletteImage.cxx | 4 +- 29 files changed, 1539 insertions(+), 840 deletions(-) create mode 100644 panda/src/putil/colorSpace.cxx create mode 100644 panda/src/putil/colorSpace.h diff --git a/panda/src/gobj/texture.cxx b/panda/src/gobj/texture.cxx index f5b3e8a051..eedf79cc1e 100644 --- a/panda/src/gobj/texture.cxx +++ b/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: diff --git a/panda/src/gobj/texture.h b/panda/src/gobj/texture.h index 66e52ed7da..ea06f34e91 100644 --- a/panda/src/gobj/texture.h +++ b/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; diff --git a/panda/src/grutil/geoMipTerrain.cxx b/panda/src/grutil/geoMipTerrain.cxx index 964d13e295..f5ff072fca 100644 --- a/panda/src/grutil/geoMipTerrain.cxx +++ b/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), diff --git a/panda/src/pgraphnodes/spotlight.cxx b/panda/src/pgraphnodes/spotlight.cxx index 28548f919e..15ce5a7d98 100644 --- a/panda/src/pgraphnodes/spotlight.cxx +++ b/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); diff --git a/panda/src/pnmimage/pfmFile.cxx b/panda/src/pnmimage/pfmFile.cxx index 2a756faaff..e030ba73bc 100644 --- a/panda/src/pnmimage/pfmFile.cxx +++ b/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 ©, int xto, int yto, void PfmFile:: add_sub_image(const PfmFile ©, 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 ©, int xto, int yto, void PfmFile:: mult_sub_image(const PfmFile ©, 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 ©, int xto, int yto, } } break; - } + } } //////////////////////////////////////////////////////////////////// @@ -2084,13 +2083,13 @@ mult_sub_image(const PfmFile ©, 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 ©, 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 ©, int xto, int yto, } } break; - } + } } //////////////////////////////////////////////////////////////////// @@ -2166,7 +2165,7 @@ divide_sub_image(const PfmFile ©, int xto, int yto, // a constant floating-point multiplier value. //////////////////////////////////////////////////////////////////// void PfmFile:: -operator *= (double multiplier) { +operator *= (float multiplier) { nassertv(is_valid()); switch (_num_channels) { diff --git a/panda/src/pnmimage/pfmFile.h b/panda/src/pnmimage/pfmFile.h index d08ebaf17e..0249b71331 100644 --- a/panda/src/pnmimage/pfmFile.h +++ b/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 ©); - BLOCKING void gaussian_filter_from(double radius, const PfmFile ©); + BLOCKING void box_filter_from(float radius, const PfmFile ©); + BLOCKING void gaussian_filter_from(float radius, const PfmFile ©); BLOCKING void quick_filter_from(const PfmFile ©); 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 ¢er, PN_float32 point_dist, PN_float32 sample_radius, bool points_only) const; @@ -143,17 +143,17 @@ PUBLISHED: void add_sub_image(const PfmFile ©, 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 ©, 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 ©, 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; diff --git a/panda/src/pnmimage/pnm-image-filter-core.cxx b/panda/src/pnmimage/pnm-image-filter-core.cxx index c9817da095..af918a8c2c 100644 --- a/panda/src/pnmimage/pnm-image-filter-core.cxx +++ b/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); } } diff --git a/panda/src/pnmimage/pnm-image-filter-sparse-core.cxx b/panda/src/pnmimage/pnm-image-filter-sparse-core.cxx index 9201ca9ee7..70e1514c07 100755 --- a/panda/src/pnmimage/pnm-image-filter-sparse-core.cxx +++ b/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); } } } diff --git a/panda/src/pnmimage/pnm-image-filter.cxx b/panda/src/pnmimage/pnm-image-filter.cxx index 0de628050a..0e304e33f8 100644 --- a/panda/src/pnmimage/pnm-image-filter.cxx +++ b/panda/src/pnmimage/pnm-image-filter.cxx @@ -65,19 +65,19 @@ // 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 StoreType; -static const WorkType source_max = 1.0; -static const WorkType filter_max = 1.0; */ +// To use single-precision floating point, 4 bytes: +typedef float WorkType; +typedef float StoreType; +static const WorkType source_max = 1.0f; +static const WorkType filter_max = 1.0f; + /* // To use 16-bit integer arithmetic, 2 bytes: typedef unsigned long WorkType; @@ -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 ©) { +box_filter_from(float width, const PNMImage ©) { filter_image(*this, copy, width, &box_filter_impl); } @@ -549,7 +545,7 @@ box_filter_from(double width, const PNMImage ©) { // appropriate filter to perform the stretching. //////////////////////////////////////////////////////////////////// void PNMImage:: -gaussian_filter_from(double width, const PNMImage ©) { +gaussian_filter_from(float width, const PNMImage ©) { filter_image(*this, copy, width, &gaussian_filter_impl); } @@ -624,7 +620,7 @@ gaussian_filter_from(double width, const PNMImage ©) { // 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 ©) { +box_filter_from(float width, const PfmFile ©) { filter_image(*this, copy, width, &box_filter_impl); } @@ -680,99 +674,86 @@ box_filter_from(double width, const PfmFile ©) { // appropriate filter to perform the stretching. //////////////////////////////////////////////////////////////////// void PfmFile:: -gaussian_filter_from(double width, const PfmFile ©) { +gaussian_filter_from(float width, const PfmFile ©) { 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; } diff --git a/panda/src/pnmimage/pnmBrush.I b/panda/src/pnmimage/pnmBrush.I index af74b64151..0bc457d58e 100644 --- a/panda/src/pnmimage/pnmBrush.I +++ b/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; } diff --git a/panda/src/pnmimage/pnmBrush.cxx b/panda/src/pnmimage/pnmBrush.cxx index d6105c030f..cc9d41aeae 100644 --- a/panda/src/pnmimage/pnmBrush.cxx +++ b/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: diff --git a/panda/src/pnmimage/pnmBrush.h b/panda/src/pnmimage/pnmBrush.h index 376ec544ae..82f816e190 100644 --- a/panda/src/pnmimage/pnmBrush.h +++ b/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" diff --git a/panda/src/pnmimage/pnmImage.I b/panda/src/pnmimage/pnmImage.I index 6c8b3e28ba..328eabbfc4 100644 --- a/panda/src/pnmimage/pnmImage.I +++ b/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(double input_value) const { +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_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 +// 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 float PNMImage:: +from_val(xelval input_value) const { + 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 double PNMImage:: -from_val(xelval input_value) const { - return (double)input_value / (double)get_maxval(); +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 ©, 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; diff --git a/panda/src/pnmimage/pnmImage.cxx b/panda/src/pnmimage/pnmImage.cxx index cb969f473e..bab069db84 100644 --- a/panda/src/pnmimage/pnmImage.cxx +++ b/panda/src/pnmimage/pnmImage.cxx @@ -60,9 +60,12 @@ clear() { _y_size = 0; _num_channels = 0; _maxval = 255; + _inv_maxval = 1.0 / 255.0; + _color_space = CS_linear; _comment.clear(); _type = (PNMFileType *)NULL; _has_read_size = false; + _xel_encoding = XE_generic; } //////////////////////////////////////////////////////////////////// @@ -73,18 +76,26 @@ clear() { //////////////////////////////////////////////////////////////////// void PNMImage:: clear(int x_size, int y_size, int num_channels, - xelval maxval, PNMFileType *type) { + xelval maxval, PNMFileType *type, ColorSpace color_space) { clear(); nassertv(num_channels >= 1 && num_channels <= 4); + nassertv(color_space != CS_unspecified); _x_size = x_size; _y_size = y_size; _num_channels = num_channels; _maxval = maxval; + _color_space = color_space; _comment.clear(); _type = type; _has_read_size = false; + if (maxval == 0) { + _inv_maxval = 0.0f; + } else { + _inv_maxval = 1.0f / (float)maxval; + } + if (has_alpha()) { allocate_alpha(); memset(_alpha, 0, sizeof(xelval) * _y_size * _x_size); @@ -93,6 +104,7 @@ clear(int x_size, int y_size, int num_channels, allocate_array(); memset(_array, 0, sizeof(xel) * _y_size * _x_size); + setup_encoding(); setup_rc(); } @@ -138,15 +150,14 @@ copy_channel(const PNMImage ©, int src_channel, int dest_channel) { // Do the actual copying for (int x = 0; x < _x_size; x++) { for (int y = 0; y < _y_size; y++) { - LVecBase4d t = get_xel_a(x, y); - LVecBase4d o = copy.get_xel_a(x, y); - t.set_cell(dest_channel,o.get_cell(src_channel)); + LColorf t = get_xel_a(x, y); + LColorf o = copy.get_xel_a(x, y); + t.set_cell(dest_channel, o.get_cell(src_channel)); set_xel_a(x, y, t); } } } - //////////////////////////////////////////////////////////////////// // Function: PNMImage::copy_header_from // Access: Published @@ -165,6 +176,7 @@ copy_header_from(const PNMImageHeader &header) { } allocate_array(); + setup_encoding(); setup_rc(); } @@ -178,6 +190,7 @@ void PNMImage:: take_from(PNMImage &orig) { clear(); PNMImageHeader::operator = (orig); + setup_encoding(); setup_rc(); if (has_alpha()) { @@ -329,13 +342,14 @@ read(PNMReader *reader) { return false; } + setup_encoding(); setup_rc(); if (has_read_size && (_x_size != read_x_size || _y_size != read_y_size)) { // The Reader didn't comply with our size request. Do the sizing // explicitly, then. PNMImage new_image(read_x_size, read_y_size, get_num_channels(), - get_maxval(), get_type()); + get_maxval(), get_type(), get_color_space()); new_image.quick_filter_from(*this); take_from(new_image); } @@ -441,8 +455,6 @@ write(PNMWriter *writer) const { return (result == _y_size); } - - //////////////////////////////////////////////////////////////////// // Function: PNMImage::set_color_type // Access: Published @@ -461,7 +473,7 @@ set_color_type(PNMImage::ColorType color_type) { } if (!is_grayscale() && is_grayscale(color_type)) { - // convert to grayscale from color + // Convert to grayscale from color for (int y = 0; y < get_y_size(); y++) { for (int x = 0; x < get_x_size(); x++) { set_gray(x, y, get_bright(x, y)); @@ -469,31 +481,128 @@ set_color_type(PNMImage::ColorType color_type) { } } else if (is_grayscale() && !is_grayscale(color_type)) { - // convert to color from grayscale + // Convert to color from grayscale for (int y = 0; y < get_y_size(); y++) { - for (int x = 0; x 0); if (maxval != _maxval) { - double ratio = (double)maxval / (double)_maxval; + float ratio = (float)maxval / (float)_maxval; if (is_grayscale()) { for (int y = 0; y < get_y_size(); y++) { @@ -671,6 +781,13 @@ set_maxval(xelval maxval) { } } _maxval = maxval; + if (maxval == 0) { + _inv_maxval = 0.0f; + } else { + _inv_maxval = 1.0f / (float)maxval; + } + + setup_encoding(); } } @@ -748,6 +865,80 @@ set_channel_val(int x, int y, int channel, xelval value) { } } +//////////////////////////////////////////////////////////////////// +// 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 float in the range +// 0..1. +//////////////////////////////////////////////////////////////////// +float PNMImage:: +get_channel(int x, int y, int channel) const { + switch (channel) { + case 0: + return get_blue(x, y); + + case 1: + return (_num_channels == 2) ? get_alpha(x, y) : get_green(x, y); + + case 2: + return get_red(x, y); + + case 3: + return get_alpha(x, y); + + default: + pnmimage_cat.error() + << "Invalid request for channel " << channel << " in " + << get_num_channels() << "-channel image.\n"; + nassertr(false, 0); + return 0; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::set_channel +// 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 float in the +// range 0..1. +//////////////////////////////////////////////////////////////////// +void PNMImage:: +set_channel(int x, int y, int channel, float value) { + switch (channel) { + case 0: + set_blue(x, y, value); + break; + + case 1: + if (_num_channels == 2) { + set_alpha(x, y, value); + } else { + set_green(x, y, value); + } + break; + + case 2: + set_red(x, y, value); + break; + + case 3: + set_alpha(x, y, value); + break; + + default: + nassertv(false); + } +} + //////////////////////////////////////////////////////////////////// // Function: PNMImage::get_pixel // Access: Published @@ -796,7 +987,7 @@ set_pixel(int x, int y, const PixelSpec &pixel) { // alpha of 0.0 is fully transparent and does nothing. //////////////////////////////////////////////////////////////////// void PNMImage:: -blend(int x, int y, double r, double g, double b, double alpha) { +blend(int x, int y, float r, float g, float b, float alpha) { if (alpha >= 1.0) { // Completely replace the previous color. if (has_alpha()) { @@ -804,22 +995,22 @@ blend(int x, int y, double r, double g, double b, double alpha) { } set_xel(x, y, r, g, b); - } else if (alpha > 0.0) { + } else if (alpha > 0.0f) { // Blend with the previous color. - double prev_alpha = has_alpha() ? get_alpha(x, y) : 1.0; + float prev_alpha = has_alpha() ? get_alpha(x, y) : 1.0f; - if (prev_alpha == 0.0) { + if (prev_alpha == 0.0f) { // Nothing there previously; replace with this new color. set_alpha(x, y, alpha); set_xel(x, y, r, g, b); } else { // Blend the color with the previous color. - LRGBColord prev_rgb = get_xel(x, y); - r = r + (1.0 - alpha) * (get_red(x, y) - r); - g = g + (1.0 - alpha) * (get_green(x, y) - g); - b = b + (1.0 - alpha) * (get_blue(x, y) - b); - alpha = prev_alpha + alpha * (1.0 - prev_alpha); + LRGBColorf prev_rgb = get_xel(x, y); + r = r + (1.0f - alpha) * (get_red(x, y) - r); + g = g + (1.0f - alpha) * (get_green(x, y) - g); + b = b + (1.0f - alpha) * (get_blue(x, y) - b); + alpha = prev_alpha + alpha * (1.0f - prev_alpha); if (has_alpha()) { set_alpha(x, y, alpha); @@ -887,7 +1078,8 @@ copy_sub_image(const PNMImage ©, int xto, int yto, setup_sub_image(copy, xto, yto, xfrom, yfrom, x_size, y_size, xmin, ymin, xmax, ymax); - if (get_maxval() == copy.get_maxval()) { + if (get_maxval() == copy.get_maxval() && + get_color_space() == copy.get_color_space()) { // The simple case: no pixel value rescaling is required. int x, y; for (y = ymin; y < ymax; y++) { @@ -941,7 +1133,7 @@ copy_sub_image(const PNMImage ©, int xto, int yto, void PNMImage:: blend_sub_image(const PNMImage ©, int xto, int yto, int xfrom, int yfrom, int x_size, int y_size, - double pixel_scale) { + float pixel_scale) { if (!copy.has_alpha() && pixel_scale == 1.0) { copy_sub_image(copy, xto, yto, xfrom, yfrom, x_size, y_size); return; @@ -981,7 +1173,7 @@ blend_sub_image(const PNMImage ©, int xto, int yto, void PNMImage:: add_sub_image(const PNMImage ©, 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); @@ -997,9 +1189,9 @@ add_sub_image(const PNMImage ©, int xto, int yto, for (y = ymin; y < ymax; y++) { for (x = xmin; x < xmax; x++) { - LRGBColord rgb1 = get_xel(x, y); - LRGBColord rgb2 = copy.get_xel(x, y); - set_xel(x, y, + LRGBColorf rgb1 = get_xel(x, y); + LRGBColorf rgb2 = copy.get_xel(x, y); + set_xel(x, y, rgb1[0] + rgb2[0] * pixel_scale, rgb1[1] + rgb2[1] * pixel_scale, rgb1[2] + rgb2[2] * pixel_scale); @@ -1019,7 +1211,7 @@ add_sub_image(const PNMImage ©, int xto, int yto, void PNMImage:: mult_sub_image(const PNMImage ©, 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); @@ -1035,9 +1227,9 @@ mult_sub_image(const PNMImage ©, int xto, int yto, for (y = ymin; y < ymax; y++) { for (x = xmin; x < xmax; x++) { - LRGBColord rgb1 = get_xel(x, y); - LRGBColord rgb2 = copy.get_xel(x, y); - set_xel(x, y, + LRGBColorf rgb1 = get_xel(x, y); + LRGBColorf rgb2 = copy.get_xel(x, y); + set_xel(x, y, rgb1[0] * rgb2[0] * pixel_scale, rgb1[1] * rgb2[1] * pixel_scale, rgb1[2] * rgb2[2] * pixel_scale); @@ -1062,12 +1254,13 @@ mult_sub_image(const PNMImage ©, int xto, int yto, void PNMImage:: darken_sub_image(const PNMImage ©, 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); - if (get_maxval() == copy.get_maxval() && pixel_scale == 1.0) { + if (get_maxval() == copy.get_maxval() && pixel_scale == 1.0f && + get_color_space() == CS_linear && copy.get_color_space() == CS_linear) { // The simple case: no pixel value rescaling is required. int x, y; for (y = ymin; y < ymax; y++) { @@ -1095,12 +1288,12 @@ darken_sub_image(const PNMImage ©, int xto, int yto, int x, y; for (y = ymin; y < ymax; y++) { for (x = xmin; x < xmax; x++) { - LRGBColord c = copy.get_xel(x - xmin + xfrom, y - ymin + yfrom); - LRGBColord o = get_xel(x, y); - LRGBColord p; - p.set(min(1.0 - ((1.0 - c[0]) * pixel_scale), o[0]), - min(1.0 - ((1.0 - c[1]) * pixel_scale), o[1]), - min(1.0 - ((1.0 - c[2]) * pixel_scale), o[2])); + LRGBColorf c = copy.get_xel(x - xmin + xfrom, y - ymin + yfrom); + LRGBColorf o = get_xel(x, y); + LRGBColorf p; + p.set(min(1.0f - ((1.0f - c[0]) * pixel_scale), o[0]), + min(1.0f - ((1.0f - c[1]) * pixel_scale), o[1]), + min(1.0f - ((1.0f - c[2]) * pixel_scale), o[2])); set_xel(x, y, p); } } @@ -1108,9 +1301,9 @@ darken_sub_image(const PNMImage ©, int xto, int yto, if (has_alpha() && copy.has_alpha()) { for (y = ymin; y < ymax; y++) { for (x = xmin; x < xmax; x++) { - double c = copy.get_alpha(x - xmin + xfrom, y - ymin + yfrom); - double o = get_alpha(x, y); - set_alpha(x, y, min(1.0 - ((1.0 - c) * pixel_scale), o)); + float c = copy.get_alpha(x - xmin + xfrom, y - ymin + yfrom); + float o = get_alpha(x, y); + set_alpha(x, y, min(1.0f - ((1.0f - c) * pixel_scale), o)); } } } @@ -1132,12 +1325,13 @@ darken_sub_image(const PNMImage ©, int xto, int yto, void PNMImage:: lighten_sub_image(const PNMImage ©, 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); - if (get_maxval() == copy.get_maxval() && pixel_scale == 1.0) { + if (get_maxval() == copy.get_maxval() && pixel_scale == 1.0f && + get_color_space() == CS_linear && copy.get_color_space() == CS_linear) { // The simple case: no pixel value rescaling is required. int x, y; for (y = ymin; y < ymax; y++) { @@ -1165,9 +1359,9 @@ lighten_sub_image(const PNMImage ©, int xto, int yto, int x, y; for (y = ymin; y < ymax; y++) { for (x = xmin; x < xmax; x++) { - LRGBColord c = copy.get_xel(x - xmin + xfrom, y - ymin + yfrom); - LRGBColord o = get_xel(x, y); - LRGBColord p; + LRGBColorf c = copy.get_xel(x - xmin + xfrom, y - ymin + yfrom); + LRGBColorf o = get_xel(x, y); + LRGBColorf p; p.set(max(c[0] * pixel_scale, o[0]), max(c[1] * pixel_scale, o[1]), max(c[2] * pixel_scale, o[2])); @@ -1178,8 +1372,8 @@ lighten_sub_image(const PNMImage ©, int xto, int yto, if (has_alpha() && copy.has_alpha()) { for (y = ymin; y < ymax; y++) { for (x = xmin; x < xmax; x++) { - double c = copy.get_alpha(x - xmin + xfrom, y - ymin + yfrom); - double o = get_alpha(x, y); + float c = copy.get_alpha(x - xmin + xfrom, y - ymin + yfrom); + float o = get_alpha(x, y); set_alpha(x, y, max(c * pixel_scale, o)); } } @@ -1210,15 +1404,16 @@ lighten_sub_image(const PNMImage ©, int xto, int yto, // both be 1x1 images instead of the source image size. //////////////////////////////////////////////////////////////////// void PNMImage:: -threshold(const PNMImage &select_image, int channel, double threshold, +threshold(const PNMImage &select_image, int channel, float threshold, const PNMImage <, const PNMImage &ge) { nassertv(get_x_size() <= select_image.get_x_size() && get_y_size() <= select_image.get_y_size()); nassertv(channel >= 0 && channel < select_image.get_num_channels()); - xelval threshold_val = (xelval)(select_image.get_maxval() * threshold + 0.5); + xelval threshold_val = select_image.to_val(threshold); - if (lt.get_x_size() == 1 && lt.get_y_size() == 1 && + if (lt.get_x_size() == 1 && lt.get_y_size() == 1 && ge.get_x_size() == 1 && ge.get_y_size() == 1) { + // FIXME: what if select_image has different color space? // 1x1 source images. xel lt_val = lt.get_xel_val(0, 0); xelval lt_alpha = 0; @@ -1226,10 +1421,10 @@ threshold(const PNMImage &select_image, int channel, double threshold, lt_alpha = lt.get_alpha_val(0, 0); } if (lt.get_maxval() != get_maxval()) { - double scale = (double)get_maxval() / (double)lt.get_maxval(); - PPM_ASSIGN(lt_val, - (xelval)(PPM_GETR(lt_val) * scale + 0.5), - (xelval)(PPM_GETG(lt_val) * scale + 0.5), + float scale = (float)get_maxval() / (float)lt.get_maxval(); + PPM_ASSIGN(lt_val, + (xelval)(PPM_GETR(lt_val) * scale + 0.5), + (xelval)(PPM_GETG(lt_val) * scale + 0.5), (xelval)(PPM_GETB(lt_val) * scale + 0.5)); lt_alpha = (xelval)(lt_alpha * scale + 0.5); } @@ -1240,16 +1435,16 @@ threshold(const PNMImage &select_image, int channel, double threshold, ge_alpha = ge.get_alpha_val(0, 0); } if (ge.get_maxval() != get_maxval()) { - double scale = (double)get_maxval() / (double)ge.get_maxval(); - PPM_ASSIGN(ge_val, - (xelval)(PPM_GETR(ge_val) * scale + 0.5), - (xelval)(PPM_GETG(ge_val) * scale + 0.5), + float scale = (float)get_maxval() / (float)ge.get_maxval(); + PPM_ASSIGN(ge_val, + (xelval)(PPM_GETR(ge_val) * scale + 0.5), + (xelval)(PPM_GETG(ge_val) * scale + 0.5), (xelval)(PPM_GETB(ge_val) * scale + 0.5)); ge_alpha = (xelval)(ge_alpha * scale + 0.5); } int x, y; - + if (channel == 3) { // Further special case: the alpha channel. if (has_alpha() && lt.has_alpha() && ge.has_alpha()) { @@ -1265,7 +1460,7 @@ threshold(const PNMImage &select_image, int channel, double threshold, } } } - + } else { // Don't copy alpha channel. for (y = 0; y < get_y_size(); y++) { @@ -1293,7 +1488,7 @@ threshold(const PNMImage &select_image, int channel, double threshold, } } } - + } else { // Don't copy alpha channel. for (y = 0; y < get_y_size(); y++) { @@ -1307,13 +1502,15 @@ threshold(const PNMImage &select_image, int channel, double threshold, } } } - + } else { // Same-sized source images. nassertv(get_x_size() <= lt.get_x_size() && get_y_size() <= lt.get_y_size()); nassertv(get_x_size() <= ge.get_x_size() && get_y_size() <= ge.get_y_size()); - if (get_maxval() == lt.get_maxval() && get_maxval() == ge.get_maxval()) { + if (get_maxval() == lt.get_maxval() && get_maxval() == ge.get_maxval() && + get_color_space() == lt.get_color_space() && + get_color_space() == ge.get_color_space()) { // Simple case: the maxvals are all equal. Copy by integer value. int x, y; @@ -1430,12 +1627,12 @@ threshold(const PNMImage &select_image, int channel, double threshold, // process. //////////////////////////////////////////////////////////////////// void PNMImage:: -fill_distance_inside(const PNMImage &mask, double threshold, int radius, bool shrink_from_border) { +fill_distance_inside(const PNMImage &mask, float threshold, int radius, bool shrink_from_border) { nassertv(radius <= PNM_MAXMAXVAL); - PNMImage dist(mask.get_x_size(), mask.get_y_size(), 1, radius); + PNMImage dist(mask.get_x_size(), mask.get_y_size(), 1, radius, NULL, CS_linear); dist.fill_val(radius); - xelval threshold_val = (xelval)(mask.get_maxval() * threshold + 0.5); + xelval threshold_val = mask.to_val(threshold); for (int yi = 0; yi < mask.get_y_size(); ++yi) { for (int xi = 0; xi < mask.get_x_size(); ++xi) { @@ -1480,12 +1677,12 @@ fill_distance_inside(const PNMImage &mask, double threshold, int radius, bool sh // process. //////////////////////////////////////////////////////////////////// void PNMImage:: -fill_distance_outside(const PNMImage &mask, double threshold, int radius) { +fill_distance_outside(const PNMImage &mask, float threshold, int radius) { nassertv(radius <= PNM_MAXMAXVAL); - PNMImage dist(mask.get_x_size(), mask.get_y_size(), 1, radius); + PNMImage dist(mask.get_x_size(), mask.get_y_size(), 1, radius, NULL, CS_linear); dist.fill_val(radius); - xelval threshold_val = (xelval)(mask.get_maxval() * threshold + 0.5); + xelval threshold_val = mask.to_val(threshold); for (int yi = 0; yi < mask.get_y_size(); ++yi) { for (int xi = 0; xi < mask.get_x_size(); ++xi) { @@ -1508,14 +1705,14 @@ fill_distance_outside(const PNMImage &mask, double threshold, int radius) { // Does not affect the alpha channel, if any. //////////////////////////////////////////////////////////////////// void PNMImage:: -rescale(double min_val, double max_val) { - double scale = max_val - min_val; +rescale(float min_val, float max_val) { + float scale = max_val - min_val; if (_num_channels <= 2) { // Grayscale. for (int y = 0; y < get_y_size(); y++) { for (int x = 0; x < get_x_size(); x++) { - double val = get_gray(x, y); + float val = get_gray(x, y); set_gray(x, y, (val - min_val) / scale); } } @@ -1523,7 +1720,7 @@ rescale(double min_val, double max_val) { // RGB(A). for (int y = 0; y < get_y_size(); y++) { for (int x = 0; x < get_x_size(); x++) { - LRGBColord xel = get_xel(x, y); + LRGBColorf xel = get_xel(x, y); set_xel(x, y, (xel[0] - min_val) / scale, (xel[1] - min_val) / scale, @@ -1548,7 +1745,8 @@ copy_channel(const PNMImage ©, int xto, int yto, int cto, setup_sub_image(copy, xto, yto, xfrom, yfrom, x_size, y_size, xmin, ymin, xmax, ymax); - if (get_maxval() == copy.get_maxval()) { + if (get_maxval() == copy.get_maxval() && + get_color_space() == copy.get_color_space()) { // The simple case: no pixel value rescaling is required. int x, y; for (y = ymin; y < ymax; y++) { @@ -1601,14 +1799,14 @@ copy_channel(const PNMImage ©, int xto, int yto, int cto, // between fg and bg colors. //////////////////////////////////////////////////////////////////// void PNMImage:: -render_spot(const LColord &fg, const LColord &bg, - double min_radius, double max_radius) { +render_spot(const LColorf &fg, const LColorf &bg, + float min_radius, float max_radius) { if (_x_size == 0 || _y_size == 0) { return; } - double x_scale = 2.0 / _x_size; - double y_scale = 2.0 / _y_size; + float x_scale = 2.0 / _x_size; + float y_scale = 2.0 / _y_size; // If the width is even, x_center1 == x_center0. If the width is // odd, x_center1 == x_center0 + 1. @@ -1617,19 +1815,19 @@ render_spot(const LColord &fg, const LColord &bg, int x_center1 = (_x_size + 1) / 2; int y_center1 = (_y_size + 1) / 2; - double min_r2 = min_radius * min_radius; - double max_r2 = max_radius * max_radius; + float min_r2 = min_radius * min_radius; + float max_r2 = max_radius * max_radius; for (int yi = 0; yi < y_center1; ++yi) { - double y = yi * y_scale; - double y2_inner = y * y; - double y2_outer = (y + y_scale) * (y + y_scale); + float y = yi * y_scale; + float y2_inner = y * y; + float y2_outer = (y + y_scale) * (y + y_scale); for (int xi = 0; xi < x_center1; ++xi) { - double x = xi * x_scale; - double d2_inner = (x * x + y2_inner); - double d2_outer = ((x + x_scale) * (x + x_scale) + y2_outer); - double d2_a = ((x + x_scale) * (x + x_scale) + y2_inner); - double d2_b = (x * x + y2_outer); + float x = xi * x_scale; + float d2_inner = (x * x + y2_inner); + float d2_outer = ((x + x_scale) * (x + x_scale) + y2_outer); + float d2_a = ((x + x_scale) * (x + x_scale) + y2_inner); + float d2_b = (x * x + y2_outer); if ((d2_inner <= min_r2) && (d2_outer <= min_r2) && @@ -1653,14 +1851,14 @@ render_spot(const LColord &fg, const LColord &bg, } else { // This pixel is in a feathered area or along the antialiased edge. - LColord c_outer, c_inner, c_a, c_b; + LColorf c_outer, c_inner, c_a, c_b; compute_spot_pixel(c_outer, d2_outer, min_radius, max_radius, fg, bg); compute_spot_pixel(c_inner, d2_inner, min_radius, max_radius, fg, bg); compute_spot_pixel(c_a, d2_a, min_radius, max_radius, fg, bg); compute_spot_pixel(c_b, d2_b, min_radius, max_radius, fg, bg); // Now average all four pixels for the antialiased result. - LColord c; + LColorf c; c = (c_outer + c_inner + c_a + c_b) * 0.25; set_xel_a(x_center1 - 1 - xi, y_center1 - 1 - yi, c); @@ -1684,10 +1882,11 @@ render_spot(const LColord &fg, const LColord &bg, //////////////////////////////////////////////////////////////////// void PNMImage:: expand_border(int left, int right, int bottom, int top, - const LColord &color) { + const LColorf &color) { PNMImage new_image(get_x_size() + left + right, get_y_size() + bottom + top, - get_num_channels(), get_maxval(), get_type()); + get_num_channels(), get_maxval(), + get_type(), get_color_space()); new_image.fill(color[0], color[1], color[2]); if (has_alpha()) { new_image.alpha_fill(color[3]); @@ -1762,9 +1961,9 @@ make_histogram(PNMImage::Histogram &histogram) { // See also the PerlinNoise2 class in mathutil. //////////////////////////////////////////////////////////////////// void PNMImage:: -perlin_noise_fill(double sx, double sy, int table_size, unsigned long seed) { - double x, y; - double noise; +perlin_noise_fill(float sx, float sy, int table_size, unsigned long seed) { + float x, y; + float noise; PerlinNoise2 perlin (sx * _x_size, sy * _y_size, table_size, seed); for (x = 0; x < _x_size; ++x) { for (y = 0; y < _y_size; ++y) { @@ -1782,11 +1981,11 @@ perlin_noise_fill(double sx, double sy, int table_size, unsigned long seed) { //////////////////////////////////////////////////////////////////// void PNMImage:: perlin_noise_fill(StackedPerlinNoise2 &perlin) { - double x, y; - double noise; + float x, y; + float noise; for (x = 0; x < _x_size; ++x) { for (y = 0; y < _y_size; ++y) { - noise = perlin.noise(x / (double) _x_size, y / (double) _y_size); + noise = perlin.noise(x / (float) _x_size, y / (float) _y_size); set_xel(x, y, 0.5 * (noise + 1.0)); } } @@ -1823,17 +2022,17 @@ remix_channels(const LMatrix4 &conv) { // green_exponent are unused. //////////////////////////////////////////////////////////////////// void PNMImage:: -apply_exponent(double red_exponent, double green_exponent, double blue_exponent, - double alpha_exponent) { +apply_exponent(float red_exponent, float green_exponent, float blue_exponent, + float alpha_exponent) { int num_channels = _num_channels; - if (has_alpha() && alpha_exponent == 1.0) { + if (has_alpha() && alpha_exponent == 1.0f) { // If the alpha_exponent is 1, don't bother to apply it. --num_channels; } int x, y; - if (red_exponent == 1.0 && green_exponent == 1.0 && blue_exponent == 1.0) { + if (red_exponent == 1.0f && green_exponent == 1.0f && blue_exponent == 1.0f) { // If the RGB components are all 1, apply only to the alpha channel. switch (num_channels) { case 1: @@ -1844,7 +2043,7 @@ apply_exponent(double red_exponent, double green_exponent, double blue_exponent, case 4: for (y = 0; y < _y_size; ++y) { for (x = 0; x < _x_size; ++x) { - double alpha = get_alpha(x, y); + float alpha = get_alpha(x, y); alpha = cpow(alpha, blue_exponent); set_alpha(x, y, alpha); } @@ -1859,7 +2058,7 @@ apply_exponent(double red_exponent, double green_exponent, double blue_exponent, case 1: for (y = 0; y < _y_size; ++y) { for (x = 0; x < _x_size; ++x) { - double gray = get_gray(x, y); + float gray = get_gray(x, y); gray = cpow(gray, blue_exponent); set_gray(x, y, gray); } @@ -1869,11 +2068,11 @@ apply_exponent(double red_exponent, double green_exponent, double blue_exponent, case 2: for (y = 0; y < _y_size; ++y) { for (x = 0; x < _x_size; ++x) { - double gray = get_gray(x, y); + float gray = get_gray(x, y); gray = cpow(gray, blue_exponent); set_gray(x, y, gray); - double alpha = get_alpha(x, y); + float alpha = get_alpha(x, y); alpha = cpow(alpha, blue_exponent); set_alpha(x, y, alpha); } @@ -1883,7 +2082,7 @@ apply_exponent(double red_exponent, double green_exponent, double blue_exponent, case 3: for (y = 0; y < _y_size; ++y) { for (x = 0; x < _x_size; ++x) { - LRGBColord color = get_xel(x, y); + LRGBColorf color = get_xel(x, y); color[0] = cpow(color[0], red_exponent); color[1] = cpow(color[1], green_exponent); color[2] = cpow(color[2], blue_exponent); @@ -1895,7 +2094,7 @@ apply_exponent(double red_exponent, double green_exponent, double blue_exponent, case 4: for (y = 0; y < _y_size; ++y) { for (x = 0; x < _x_size; ++x) { - LColord color = get_xel_a(x, y); + LColorf color = get_xel_a(x, y); color[0] = cpow(color[0], red_exponent); color[1] = cpow(color[1], green_exponent); color[2] = cpow(color[2], blue_exponent); @@ -1929,6 +2128,74 @@ setup_rc() { } } +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::setup_encoding +// Access: Private +// Description: Sets the _xel_encoding value apppropriately +// according to the color space, maxval and whether +// the image has an alpha channel, so that to_val and +// from_val will work correctly (and possibly more +// efficiently). +//////////////////////////////////////////////////////////////////// +void PNMImage:: +setup_encoding() { + if (has_alpha()) { + switch (_color_space) { + case CS_linear: + _xel_encoding = XE_generic_alpha; + break; + + case CS_sRGB: + if (get_maxval() == 255) { + if (has_sse2_sRGB_encode()) { + _xel_encoding = XE_uchar_sRGB_alpha_sse2; + } else { + _xel_encoding = XE_uchar_sRGB_alpha; + } + } else { + _xel_encoding = XE_generic_sRGB_alpha; + } + break; + + case CS_scRGB: + _xel_encoding = XE_scRGB_alpha; + nassertv(get_maxval() == 65535); + break; + + default: + nassertv(false); + break; + } + } else { + switch (_color_space) { + case CS_linear: + _xel_encoding = XE_generic; + break; + + case CS_sRGB: + if (get_maxval() == 255) { + if (has_sse2_sRGB_encode()) { + _xel_encoding = XE_uchar_sRGB_sse2; + } else { + _xel_encoding = XE_uchar_sRGB; + } + } else { + _xel_encoding = XE_generic_sRGB; + } + break; + + case CS_scRGB: + _xel_encoding = XE_scRGB; + nassertv(get_maxval() == 65535); + break; + + default: + nassertv(false); + break; + } + } +} + //////////////////////////////////////////////////////////////////// // Function: PNMImage::do_fill_distance // Access: Private @@ -1937,7 +2204,7 @@ setup_rc() { //////////////////////////////////////////////////////////////////// void PNMImage:: do_fill_distance(int xi, int yi, int d) { - if (xi < 0 || xi >= get_x_size() || + if (xi < 0 || xi >= get_x_size() || yi < 0 || yi >= get_y_size()) { return; } @@ -1958,21 +2225,22 @@ do_fill_distance(int xi, int yi, int d) { // Description: Returns the average color of all of the pixels // in the image. //////////////////////////////////////////////////////////////////// -LRGBColord PNMImage:: +LRGBColorf PNMImage:: get_average_xel() const { - LRGBColord color (LRGBColord::zero()); + LRGBColorf color (LRGBColorf::zero()); if (_x_size == 0 || _y_size == 0) { return color; } + float factor = 1.0f / (float)(_x_size * _y_size); + int x, y; for (x = 0; x < _x_size; ++x) { for (y = 0; y < _y_size; ++y) { - color += get_xel(x, y); + color += get_xel(x, y) * factor; } } - color *= 1.0 / (_x_size * _y_size); return color; } @@ -1982,21 +2250,22 @@ get_average_xel() const { // Description: Returns the average color of all of the pixels // in the image, including the alpha channel. //////////////////////////////////////////////////////////////////// -LColord PNMImage:: +LColorf PNMImage:: get_average_xel_a() const { - LColord color (LColord::zero()); + LColorf color (LColorf::zero()); if (_x_size == 0 || _y_size == 0) { return color; } + float factor = 1.0f / (float)(_x_size * _y_size); + int x, y; for (x = 0; x < _x_size; ++x) { for (y = 0; y < _y_size; ++y) { - color += get_xel_a(x, y); + color += get_xel_a(x, y) * factor; } } - color *= 1.0 / (_x_size * _y_size); return color; } @@ -2006,29 +2275,30 @@ get_average_xel_a() const { // Description: Returns the average grayscale component of all of // the pixels in the image. //////////////////////////////////////////////////////////////////// -double PNMImage:: +float PNMImage:: get_average_gray() const { - double color = 0.0; + float gray = 0.0; if (_x_size == 0 || _y_size == 0) { - return color; + return gray; } int x, y; for (x = 0; x < _x_size; ++x) { for (y = 0; y < _y_size; ++y) { - color += get_gray(x, y); + gray += get_gray(x, y); } } - color *= 1.0 / (_x_size * _y_size); - return color; + gray /= (float)(_x_size * _y_size); + return gray; } //////////////////////////////////////////////////////////////////// // Function: PNMImage::operator ~ // Access: Published -// Description: Returns a new PNMImage that is the -// complement of the current PNMImage. +// Description: Returns a new PNMImage that is the complement of +// this PNMImage. This operation is not color-space +// correct. //////////////////////////////////////////////////////////////////// PNMImage PNMImage:: operator ~ () const { @@ -2065,26 +2335,36 @@ operator ~ () const { //////////////////////////////////////////////////////////////////// void PNMImage:: operator += (const PNMImage &other) { + nassertv(is_valid() && other.is_valid()); nassertv(_x_size == other._x_size && _y_size == other._y_size); - nassertv(_maxval == other._maxval && _maxval == other._maxval); - size_t array_size = _x_size * _y_size; - if (_array != NULL && _alpha != NULL) { - for (size_t i = 0; i < array_size; ++i) { - _array[i].r = clamp_val(_array[i].r + other._array[i].r); - _array[i].g = clamp_val(_array[i].g + other._array[i].g); - _array[i].b = clamp_val(_array[i].b + other._array[i].b); - _alpha[i] = clamp_val(_alpha[i] + other._alpha[i]); + if (get_maxval() == other.get_maxval() && + get_color_space() == CS_linear && + other.get_color_space() == CS_linear) { + size_t array_size = _x_size * _y_size; + + // Simple case: add vals directly. + if (_alpha != NULL && other._alpha != NULL) { + for (size_t i = 0; i < array_size; ++i) { + _array[i].r = clamp_val((int)_array[i].r + (int)other._array[i].r); + _array[i].g = clamp_val((int)_array[i].g + (int)other._array[i].g); + _array[i].b = clamp_val((int)_array[i].b + (int)other._array[i].b); + _alpha[i] = clamp_val((int)_alpha[i] + (int)other._alpha[i]); + } + } else { + for (size_t i = 0; i < array_size; ++i) { + _array[i].r = clamp_val((int)_array[i].r + (int)other._array[i].r); + _array[i].g = clamp_val((int)_array[i].g + (int)other._array[i].g); + _array[i].b = clamp_val((int)_array[i].b + (int)other._array[i].b); + } } - } else if (_array != NULL) { - for (size_t i = 0; i < array_size; ++i) { - _array[i].r = clamp_val(_array[i].r + other._array[i].r); - _array[i].g = clamp_val(_array[i].g + other._array[i].g); - _array[i].b = clamp_val(_array[i].b + other._array[i].b); - } - } else if (_alpha != NULL) { - for (size_t i = 0; i < array_size; ++i) { - _alpha[i] = clamp_val(_alpha[i] + other._alpha[i]); + } else { + // Not a linear color space: convert back and forth. + int x, y; + for (x = 0; x < _x_size; ++x) { + for (y = 0; y < _y_size; ++y) { + set_xel_a(x, y, get_xel_a(x, y) + other.get_xel_a(x, y)); + } } } } @@ -2095,30 +2375,40 @@ operator += (const PNMImage &other) { // Description: Adds the provided color to each pixel in this image. //////////////////////////////////////////////////////////////////// void PNMImage:: -operator += (const LColord &other) { - size_t array_size = _x_size * _y_size; - // Note: don't use to_val here because it clamps values below 0 - int add_r = (int)(other.get_x() * get_maxval() + 0.5); - int add_g = (int)(other.get_y() * get_maxval() + 0.5); - int add_b = (int)(other.get_z() * get_maxval() + 0.5); - int add_a = (int)(other.get_w() * get_maxval() + 0.5); +operator += (const LColorf &other) { + nassertv(is_valid()); - if (_array != NULL && _alpha != NULL) { - for (size_t i = 0; i < array_size; ++i) { - _array[i].r = clamp_val(_array[i].r + add_r); - _array[i].g = clamp_val(_array[i].g + add_g); - _array[i].b = clamp_val(_array[i].b + add_b); - _alpha[i] = clamp_val(_alpha[i] + add_a); + if (get_color_space() == CS_linear) { + size_t array_size = _x_size * _y_size; + + // Note: don't use to_val here because it clamps values below 0 + int add_r = (int)(other.get_x() * get_maxval() + 0.5); + int add_g = (int)(other.get_y() * get_maxval() + 0.5); + int add_b = (int)(other.get_z() * get_maxval() + 0.5); + int add_a = (int)(other.get_w() * get_maxval() + 0.5); + + if (_alpha != NULL) { + for (size_t i = 0; i < array_size; ++i) { + _array[i].r = clamp_val((int)_array[i].r + add_r); + _array[i].g = clamp_val((int)_array[i].g + add_g); + _array[i].b = clamp_val((int)_array[i].b + add_b); + _alpha[i] = clamp_val((int)_alpha[i] + add_a); + } + + } else { + for (size_t i = 0; i < array_size; ++i) { + _array[i].r = clamp_val((int)_array[i].r + add_r); + _array[i].g = clamp_val((int)_array[i].g + add_g); + _array[i].b = clamp_val((int)_array[i].b + add_b); + } } - } else if (_array != NULL) { - for (size_t i = 0; i < array_size; ++i) { - _array[i].r = clamp_val(_array[i].r + add_r); - _array[i].g = clamp_val(_array[i].g + add_g); - _array[i].b = clamp_val(_array[i].b + add_b); - } - } else if (_alpha != NULL) { - for (size_t i = 0; i < array_size; ++i) { - _alpha[i] = clamp_val(_alpha[i] + add_a); + } else { + // Not a linear color space: convert back and forth. + int x, y; + for (x = 0; x < _x_size; ++x) { + for (y = 0; y < _y_size; ++y) { + set_xel_a(x, y, get_xel_a(x, y) + other); + } } } } @@ -2132,26 +2422,36 @@ operator += (const LColord &other) { //////////////////////////////////////////////////////////////////// void PNMImage:: operator -= (const PNMImage &other) { + nassertv(is_valid() && other.is_valid()); nassertv(_x_size == other._x_size && _y_size == other._y_size); - nassertv(_maxval == other._maxval && _maxval == other._maxval); - size_t array_size = _x_size * _y_size; - if (_array != NULL && _alpha != NULL) { - for (size_t i = 0; i < array_size; ++i) { - _array[i].r = clamp_val(_array[i].r - other._array[i].r); - _array[i].g = clamp_val(_array[i].g - other._array[i].g); - _array[i].b = clamp_val(_array[i].b - other._array[i].b); - _alpha[i] = clamp_val(_alpha[i] - other._alpha[i]); + if (get_maxval() == other.get_maxval() && + get_color_space() == CS_linear && + other.get_color_space() == CS_linear) { + size_t array_size = _x_size * _y_size; + + // Simple case: subtract vals directly. + if (_alpha != NULL && other._alpha != NULL) { + for (size_t i = 0; i < array_size; ++i) { + _array[i].r = clamp_val((int)_array[i].r - (int)other._array[i].r); + _array[i].g = clamp_val((int)_array[i].g - (int)other._array[i].g); + _array[i].b = clamp_val((int)_array[i].b - (int)other._array[i].b); + _alpha[i] = clamp_val((int)_alpha[i] - (int)other._alpha[i]); + } + } else { + for (size_t i = 0; i < array_size; ++i) { + _array[i].r = clamp_val((int)_array[i].r - (int)other._array[i].r); + _array[i].g = clamp_val((int)_array[i].g - (int)other._array[i].g); + _array[i].b = clamp_val((int)_array[i].b - (int)other._array[i].b); + } } - } else if (_array != NULL) { - for (size_t i = 0; i < array_size; ++i) { - _array[i].r = clamp_val(_array[i].r - other._array[i].r); - _array[i].g = clamp_val(_array[i].g - other._array[i].g); - _array[i].b = clamp_val(_array[i].b - other._array[i].b); - } - } else if (_alpha != NULL) { - for (size_t i = 0; i < array_size; ++i) { - _alpha[i] = clamp_val(_alpha[i] - other._alpha[i]); + } else { + // Not a linear color space: convert back and forth. + int x, y; + for (x = 0; x < _x_size; ++x) { + for (y = 0; y < _y_size; ++y) { + set_xel_a(x, y, get_xel_a(x, y) - other.get_xel_a(x, y)); + } } } } @@ -2163,8 +2463,8 @@ operator -= (const PNMImage &other) { // image. //////////////////////////////////////////////////////////////////// void PNMImage:: -operator -= (const LColord &other) { - (*this) += (-other); +operator -= (const LColorf &other) { + (*this) += -other; } //////////////////////////////////////////////////////////////////// @@ -2178,25 +2478,13 @@ operator -= (const LColord &other) { //////////////////////////////////////////////////////////////////// void PNMImage:: operator *= (const PNMImage &other) { + nassertv(is_valid() && other.is_valid()); nassertv(_x_size == other._x_size && _y_size == other._y_size); - size_t array_size = _x_size * _y_size; - if (_array != NULL && _alpha != NULL) { - for (size_t i = 0; i < array_size; ++i) { - _array[i].r = to_val(from_val(_array[i].r) * other.from_val(other._array[i].r)); - _array[i].g = to_val(from_val(_array[i].g) * other.from_val(other._array[i].g)); - _array[i].b = to_val(from_val(_array[i].b) * other.from_val(other._array[i].b)); - _alpha[i] = to_val(from_val(_alpha[i]) * other.from_val(other._alpha[i])); - } - } else if (_array != NULL) { - for (size_t i = 0; i < array_size; ++i) { - _array[i].r = to_val(from_val(_array[i].r) * other.from_val(other._array[i].r)); - _array[i].g = to_val(from_val(_array[i].g) * other.from_val(other._array[i].g)); - _array[i].b = to_val(from_val(_array[i].b) * other.from_val(other._array[i].b)); - } - } else if (_alpha != NULL) { - for (size_t i = 0; i < array_size; ++i) { - _alpha[i] = to_val(from_val(_alpha[i]) * other.from_val(other._alpha[i])); + int x, y; + for (x = 0; x < _x_size; ++x) { + for (y = 0; y < _y_size; ++y) { + set_xel_a(x, y, get_xel_a(x, y) - other.get_xel_a(x, y)); } } } @@ -2205,28 +2493,38 @@ operator *= (const PNMImage &other) { // Function: PNMImage::operator *= // Access: Published // Description: Multiplies every pixel value in the image by -// a constant floating-point multiplier value. +// a constant floating-point multiplier value. This +// affects all channels. //////////////////////////////////////////////////////////////////// void PNMImage:: -operator *= (double multiplier) { - size_t array_size = _x_size * _y_size; +operator *= (float multiplier) { + nassertv(is_valid()); - if (_array != NULL && _alpha != NULL) { - for (size_t i = 0; i < array_size; ++i) { - _array[i].r = clamp_val(_array[i].r * multiplier); - _array[i].g = clamp_val(_array[i].g * multiplier); - _array[i].b = clamp_val(_array[i].b * multiplier); - _alpha[i] = clamp_val(_alpha[i] * multiplier); + if (get_color_space() == CS_linear) { + size_t array_size = _x_size * _y_size; + + if (_alpha != NULL) { + for (size_t i = 0; i < array_size; ++i) { + _array[i].r = clamp_val((int)(_array[i].r * multiplier + 0.5f)); + _array[i].g = clamp_val((int)(_array[i].g * multiplier + 0.5f)); + _array[i].b = clamp_val((int)(_array[i].b * multiplier + 0.5f)); + _alpha[i] = clamp_val((int)(_alpha[i] * multiplier + 0.5f)); + } + + } else { + for (size_t i = 0; i < array_size; ++i) { + _array[i].r = clamp_val((int)(_array[i].r * multiplier + 0.5f)); + _array[i].g = clamp_val((int)(_array[i].g * multiplier + 0.5f)); + _array[i].b = clamp_val((int)(_array[i].b * multiplier + 0.5f)); + } } - } else if (_array != NULL) { - for (size_t i = 0; i < array_size; ++i) { - _array[i].r = clamp_val(_array[i].r * multiplier); - _array[i].g = clamp_val(_array[i].g * multiplier); - _array[i].b = clamp_val(_array[i].b * multiplier); - } - } else if (_alpha != NULL) { - for (size_t i = 0; i < array_size; ++i) { - _alpha[i] = clamp_val(_alpha[i] * multiplier); + } else { + // Not a linear color space: convert back and forth. + int x, y; + for (x = 0; x < _x_size; ++x) { + for (y = 0; y < _y_size; ++y) { + set_xel_a(x, y, get_xel_a(x, y) * multiplier); + } } } } @@ -2234,30 +2532,40 @@ operator *= (double multiplier) { //////////////////////////////////////////////////////////////////// // Function: PNMImage::operator *= // Access: Published -// Description: Multiples the provided color to each pixel in this -// image. +// Description: Multiplies the provided color to each pixel in this +// image. This is a component-wise multiplication. //////////////////////////////////////////////////////////////////// void PNMImage:: -operator *= (const LColord &other) { - size_t array_size = _x_size * _y_size; +operator *= (const LColorf &other) { + nassertv(is_valid()); - if (_array != NULL && _alpha != NULL) { - for (size_t i = 0; i < array_size; ++i) { - _array[i].r = clamp_val(_array[i].r * other[0]); - _array[i].g = clamp_val(_array[i].g * other[1]); - _array[i].b = clamp_val(_array[i].b * other[2]); - _alpha[i] = clamp_val(_alpha[i] * other[3]); + if (get_color_space() == CS_linear) { + size_t array_size = _x_size * _y_size; + + if (_alpha != NULL) { + for (size_t i = 0; i < array_size; ++i) { + _array[i].r = clamp_val((int)(_array[i].r * other[0] + 0.5f)); + _array[i].g = clamp_val((int)(_array[i].g * other[1] + 0.5f)); + _array[i].b = clamp_val((int)(_array[i].b * other[2] + 0.5f)); + _alpha[i] = clamp_val((int)(_alpha[i] * other[3] + 0.5f)); + } + + } else { + for (size_t i = 0; i < array_size; ++i) { + _array[i].r = clamp_val((int)(_array[i].r * other[0] + 0.5f)); + _array[i].g = clamp_val((int)(_array[i].g * other[1] + 0.5f)); + _array[i].b = clamp_val((int)(_array[i].b * other[2] + 0.5f)); + } } - } else if (_array != NULL) { - for (size_t i = 0; i < array_size; ++i) { - _array[i].r = clamp_val(_array[i].r * other[0]); - _array[i].g = clamp_val(_array[i].g * other[1]); - _array[i].b = clamp_val(_array[i].b * other[2]); - } - } else if (_alpha != NULL) { - for (size_t i = 0; i < array_size; ++i) { - _alpha[i] = clamp_val(_alpha[i] * other[3]); + } else { + // Not a linear color space: convert back and forth. + int x, y; + for (x = 0; x < _x_size; ++x) { + for (y = 0; y < _y_size; ++y) { + LColorf color = get_xel_a(x, y); + color.componentwise_mult(other); + set_xel_a(x, y, color); + } } } } - diff --git a/panda/src/pnmimage/pnmImage.h b/panda/src/pnmimage/pnmImage.h index 59188050d1..ed5ab05a1c 100644 --- a/panda/src/pnmimage/pnmImage.h +++ b/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 ©); INLINE void operator = (const PNMImage ©); 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 ©); void copy_channel(const PNMImage ©, 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]. + // [0..1], and into the linear color space. - 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 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 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 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 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 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, 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 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 double get_channel(int x, int y, int channel) const; - INLINE void set_channel(int x, int y, int channel, double value); + 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 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); + 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 ©, 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 ©, 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 ©, 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 ©, 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 ©, 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 <, 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 ©, 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 ©); - void box_filter_from(double radius, const PNMImage ©); - void gaussian_filter_from(double radius, const PNMImage ©); + void box_filter_from(float radius, const PNMImage ©); + void gaussian_filter_from(float radius, const PNMImage ©); void quick_filter_from(const PNMImage ©, 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" diff --git a/panda/src/pnmimage/pnmImageHeader.I b/panda/src/pnmimage/pnmImageHeader.I index 357ff0e198..0e22848fdd 100644 --- a/panda/src/pnmimage/pnmImageHeader.I +++ b/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 ©) : _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 ©) { _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 diff --git a/panda/src/pnmimage/pnmImageHeader.h b/panda/src/pnmimage/pnmImageHeader.h index d5e6db3b9c..a70bce197c 100644 --- a/panda/src/pnmimage/pnmImageHeader.h +++ b/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; }; diff --git a/panda/src/pnmimage/pnmPainter.I b/panda/src/pnmimage/pnmPainter.I index ec8dfb7cdd..f0353129d0 100644 --- a/panda/src/pnmimage/pnmPainter.I +++ b/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) { diff --git a/panda/src/pnmimage/pnmPainter.cxx b/panda/src/pnmimage/pnmPainter.cxx index 60bd988498..8583b0e3f5 100644 --- a/panda/src/pnmimage/pnmPainter.cxx +++ b/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; } diff --git a/panda/src/pnmimage/pnmPainter.h b/panda/src/pnmimage/pnmPainter.h index edc50cc9ae..29d8be1883 100644 --- a/panda/src/pnmimage/pnmPainter.h +++ b/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; diff --git a/panda/src/pnmimage/pnmimage_base.h b/panda/src/pnmimage/pnmimage_base.h index 8d649e8358..4cf1e5a83c 100644 --- a/panda/src/pnmimage/pnmimage_base.h +++ b/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 diff --git a/panda/src/pnmimagetypes/pnmFileTypePNG.cxx b/panda/src/pnmimagetypes/pnmFileTypePNG.cxx index 153220a0b5..e1e92d46a6 100755 --- a/panda/src/pnmimagetypes/pnmFileTypePNG.cxx +++ b/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); diff --git a/panda/src/pnmtext/config_pnmtext.cxx b/panda/src/pnmtext/config_pnmtext.cxx index 418fc1113a..aa0cab7c93 100644 --- a/panda/src/pnmtext/config_pnmtext.cxx +++ b/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); diff --git a/panda/src/pnmtext/pnmTextGlyph.cxx b/panda/src/pnmtext/pnmTextGlyph.cxx index 58ae6848a6..91cbcddfc6 100644 --- a/panda/src/pnmtext/pnmTextGlyph.cxx +++ b/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); } } } diff --git a/panda/src/putil/colorSpace.cxx b/panda/src/putil/colorSpace.cxx new file mode 100644 index 0000000000..90906fe97e --- /dev/null +++ b/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 + +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; +} diff --git a/panda/src/putil/colorSpace.h b/panda/src/putil/colorSpace.h new file mode 100644 index 0000000000..86049c0863 --- /dev/null +++ b/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 diff --git a/panda/src/putil/p3putil_composite1.cxx b/panda/src/putil/p3putil_composite1.cxx index 8c68adf9c1..3fd7673773 100644 --- a/panda/src/putil/p3putil_composite1.cxx +++ b/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" diff --git a/pandatool/src/imageprogs/imageTrans.cxx b/pandatool/src/imageprogs/imageTrans.cxx index 94b8bcf4d3..b9889f0bbb 100644 --- a/pandatool/src/imageprogs/imageTrans.cxx +++ b/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]); diff --git a/pandatool/src/palettizer/paletteImage.cxx b/pandatool/src/palettizer/paletteImage.cxx index 247948c947..8e0edb34d8 100644 --- a/pandatool/src/palettizer/paletteImage.cxx +++ b/pandatool/src/palettizer/paletteImage.cxx @@ -93,8 +93,8 @@ operator = (const PaletteImage::ClearedRegion ©) { //////////////////////////////////////////////////////////////////// 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++) {