PNMImage sRGB support, use floats instead of doubles

This commit is contained in:
rdb 2015-03-15 17:22:28 +01:00
parent b784549939
commit 22120524a2
29 changed files with 1539 additions and 840 deletions

View File

@ -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:

View File

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

View File

@ -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),

View File

@ -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);

View File

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

View File

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

View File

@ -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);
}
}

View File

@ -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);
}
}
}

View File

@ -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 &copy) {
box_filter_from(float width, const PNMImage &copy) {
filter_image(*this, copy, width, &box_filter_impl);
}
@ -549,7 +545,7 @@ box_filter_from(double width, const PNMImage &copy) {
// appropriate filter to perform the stretching.
////////////////////////////////////////////////////////////////////
void PNMImage::
gaussian_filter_from(double width, const PNMImage &copy) {
gaussian_filter_from(float width, const PNMImage &copy) {
filter_image(*this, copy, width, &gaussian_filter_impl);
}
@ -624,7 +620,7 @@ gaussian_filter_from(double width, const PNMImage &copy) {
// another. Both images can be the same with no ill effects.
static void
filter_image(PfmFile &dest, const PfmFile &source,
double width, FilterFunction *make_filter) {
float width, FilterFunction *make_filter) {
int num_channels = min(dest.get_num_channels(), source.get_num_channels());
if (source.has_no_data_value()) {
@ -654,8 +650,6 @@ filter_image(PfmFile &dest, const PfmFile &source,
}
}
////////////////////////////////////////////////////////////////////
// Function: PfmFile::box_filter_from
// Access: Public
@ -666,7 +660,7 @@ filter_image(PfmFile &dest, const PfmFile &source,
// appropriate filter to perform the stretching.
////////////////////////////////////////////////////////////////////
void PfmFile::
box_filter_from(double width, const PfmFile &copy) {
box_filter_from(float width, const PfmFile &copy) {
filter_image(*this, copy, width, &box_filter_impl);
}
@ -680,99 +674,86 @@ box_filter_from(double width, const PfmFile &copy) {
// appropriate filter to perform the stretching.
////////////////////////////////////////////////////////////////////
void PfmFile::
gaussian_filter_from(double width, const PfmFile &copy) {
gaussian_filter_from(float width, const PfmFile &copy) {
filter_image(*this, copy, width, &gaussian_filter_impl);
}
//
// The following functions are support for quick_box_filter().
//
INLINE void
box_filter_xel(const PNMImage &image,
int x, int y, double x_contrib, double y_contrib,
double &red, double &grn, double &blu, double &alpha,
double &pixel_count) {
double contrib = x_contrib * y_contrib;
red += image.get_red_val(x, y) * contrib;
grn += image.get_green_val(x, y) * contrib;
blu += image.get_blue_val(x, y) * contrib;
if (image.has_alpha()) {
alpha += image.get_alpha_val(x, y) * contrib;
}
static INLINE void
box_filter_xel(const PNMImage &img,
int x, int y, float x_contrib, float y_contrib,
LColorf &color, float &pixel_count) {
float contrib = x_contrib * y_contrib;
color += img.get_xel_a(x, y) * contrib;
pixel_count += contrib;
}
INLINE void
static INLINE void
box_filter_line(const PNMImage &image,
double x0, int y, double x1, double y_contrib,
double &red, double &grn, double &blu, double &alpha,
double &pixel_count) {
float x0, int y, float x1, float y_contrib,
LColorf &color, float &pixel_count) {
int x = (int)x0;
// Get the first (partial) xel
box_filter_xel(image, x, y, (double)(x+1)-x0, y_contrib,
red, grn, blu, alpha, pixel_count);
box_filter_xel(image, x, y, (float)(x+1)-x0, y_contrib,
color, pixel_count);
int x_last = (int)x1;
if (x < x_last) {
x++;
while (x < x_last) {
// Get each consecutive (complete) xel
box_filter_xel(image, x, y, 1.0, y_contrib,
red, grn, blu, alpha, pixel_count);
box_filter_xel(image, x, y, 1.0f, y_contrib,
color, pixel_count);
x++;
}
// Get the final (partial) xel
double x_contrib = x1 - (double)x_last;
if (x_contrib > 0.0001) {
float x_contrib = x1 - (float)x_last;
if (x_contrib > 0.0001f) {
box_filter_xel(image, x, y, x_contrib, y_contrib,
red, grn, blu, alpha, pixel_count);
color, pixel_count);
}
}
}
static void
static LColorf
box_filter_region(const PNMImage &image,
double x0, double y0, double x1, double y1,
xel &result, xelval &alpha_result) {
double red = 0.0, grn = 0.0, blu = 0.0, alpha = 0.0;
double pixel_count = 0.0;
float x0, float y0, float x1, float y1) {
LColorf color = LColorf::zero();
float pixel_count = 0.0f;
assert(y0 >=0 && y1 >=0);
assert(y0 >= 0 && y1 >= 0);
int y = (int)y0;
// Get the first (partial) row
box_filter_line(image, x0, y, x1, (double)(y+1)-y0,
red, grn, blu, alpha, pixel_count);
box_filter_line(image, x0, y, x1, (float)(y+1)-y0,
color, pixel_count);
int y_last = (int)y1;
if (y < y_last) {
y++;
while (y < y_last) {
// Get each consecutive (complete) row
box_filter_line(image, x0, y, x1, 1.0,
red, grn, blu, alpha, pixel_count);
box_filter_line(image, x0, y, x1, 1.0f,
color, pixel_count);
y++;
}
// Get the final (partial) row
double y_contrib = y1 - (double)y_last;
if (y_contrib > 0.0001) {
float y_contrib = y1 - (float)y_last;
if (y_contrib > 0.0001f) {
box_filter_line(image, x0, y, x1, y_contrib,
red, grn, blu, alpha, pixel_count);
color, pixel_count);
}
}
PPM_ASSIGN(result,
(xelval)(red / pixel_count + 0.5),
(xelval)(grn / pixel_count + 0.5),
(xelval)(blu / pixel_count + 0.5));
alpha_result = (xelval)(alpha / pixel_count + 0.5);
//cerr << pixel_count << "\n";
color /= pixel_count;
return color;
}
////////////////////////////////////////////////////////////////////
@ -798,11 +779,13 @@ quick_filter_from(const PNMImage &from, int xborder, int yborder) {
int to_xoff = xborder / 2;
int to_yoff = yborder / 2;
double from_x0, from_x1, from_y0, from_y1;
float from_x0, from_x1, from_y0, from_y1;
int to_x, to_y;
double x_scale = (double)from_xs / (double)to_xs;
double y_scale = (double)from_ys / (double)to_ys;
float x_scale = (float)from_xs / (float)to_xs;
float y_scale = (float)from_ys / (float)to_ys;
LColorf color;
from_y0 = max(0, -to_yoff) * y_scale;
for (to_y = max(0, -to_yoff);
@ -818,14 +801,10 @@ quick_filter_from(const PNMImage &from, int xborder, int yborder) {
// Now the box from (from_x0, from_y0) - (from_x1, from_y1)
// but not including (from_x1, from_y1) maps to the pixel (to_x, to_y).
xelval alpha_result;
box_filter_region(from,
from_x0, from_y0, from_x1, from_y1,
(*this)[to_yoff + to_y][to_xoff + to_x],
alpha_result);
if (has_alpha()) {
set_alpha_val(to_xoff+to_x, to_yoff+to_y, alpha_result);
}
color = box_filter_region(from,
from_x0, from_y0, from_x1, from_y1);
set_xel_a(to_xoff + to_x, to_yoff + to_y, color);
from_x0 = from_x1;
}

View File

@ -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;
}

View File

@ -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:

View File

@ -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"

View File

@ -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 &copy, int &xto, int &yto,
// from the center.
////////////////////////////////////////////////////////////////////
INLINE void PNMImage::
compute_spot_pixel(LColord &c, double d2,
double min_radius, double max_radius,
const LColord &fg, const LColord &bg) {
double d = sqrt(d2);
compute_spot_pixel(LColorf &c, float d2,
float min_radius, float max_radius,
const LColorf &fg, const LColorf &bg) {
float d = sqrt(d2);
if (d > max_radius) {
c = bg;
} else if (d > min_radius) {
d = (d - min_radius) / (max_radius - min_radius);
double d2 = d * d;
double t = (3.0 * d2) - (2.0 * d * d2);
float d2 = d * d;
float t = (3.0 * d2) - (2.0 * d * d2);
c = fg + t * (bg - fg);
} else {
c = fg;
@ -1118,7 +1367,7 @@ operator + (const PNMImage &other) const {
// is added to each pixel in the provided image.
////////////////////////////////////////////////////////////////////
INLINE PNMImage PNMImage::
operator + (const LColord &other) const {
operator + (const LColorf &other) const {
PNMImage target (*this);
target += other;
return target;
@ -1146,7 +1395,7 @@ operator - (const PNMImage &other) const {
// is subtracted from each pixel in the provided image.
////////////////////////////////////////////////////////////////////
INLINE PNMImage PNMImage::
operator - (const LColord &other) const {
operator - (const LColorf &other) const {
PNMImage target (*this);
target -= other;
return target;
@ -1176,7 +1425,7 @@ operator * (const PNMImage &other) const {
// a constant floating-point multiplier value.
////////////////////////////////////////////////////////////////////
INLINE PNMImage PNMImage::
operator * (double multiplier) const {
operator * (float multiplier) const {
PNMImage target (*this);
target *= multiplier;
return target;
@ -1189,7 +1438,7 @@ operator * (double multiplier) const {
// is multiplied to each pixel in the provided image.
////////////////////////////////////////////////////////////////////
INLINE PNMImage PNMImage::
operator * (const LColord &other) const {
operator * (const LColorf &other) const {
PNMImage target (*this);
target *= other;
return target;

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

@ -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;
};

View File

@ -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) {

View File

@ -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;
}

View File

@ -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;

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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);
}
}
}

View File

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

View File

@ -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

View File

@ -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"

View File

@ -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]);

View File

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