/** * 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." * * @file pnmImage.I * @author drose * @date 2000-06-15 */ /** * */ INLINE PNMImage:: PNMImage() { _array = nullptr; _alpha = nullptr; clear(); } /** * */ INLINE PNMImage:: PNMImage(int x_size, int y_size, int num_channels, xelval maxval, PNMFileType *type, ColorSpace color_space) { _array = nullptr; _alpha = nullptr; clear(x_size, y_size, num_channels, maxval, type, color_space); } /** * */ INLINE PNMImage:: PNMImage(const PNMImage ©) { // We don't need to invoke PNMImageHeader's copy constructor, because we'll // just call copy_from(). _array = nullptr; _alpha = nullptr; copy_from(copy); } /** * */ INLINE void PNMImage:: operator = (const PNMImage ©) { copy_from(copy); } /** * */ INLINE PNMImage:: ~PNMImage() { clear(); } /** * A handy function to clamp values to [0..get_maxval()]. */ INLINE xelval PNMImage:: clamp_val(int input_value) const { return (xelval)std::min(std::max(0, input_value), (int)get_maxval()); } /** * A handy function to scale non-alpha values from [0..1] to * [0..get_maxval()]. Do not use this for alpha values, see to_alpha_val. */ INLINE xelval PNMImage:: to_val(float input_value) const { switch (_xel_encoding) { case XE_generic: case XE_generic_alpha: return (int)(std::min(1.0f, std::max(0.0f, 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 std::min(std::max(0, (int)((8192 * input_value) + 4096.5f)), 65535); default: return 0; } } /** * 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)); } /** * 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); default: return 0.0f; } } /** * A handy function to scale alpha values from [0..get_maxval()] to [0..1]. */ INLINE float PNMImage:: from_alpha_val(xelval input_value) const { return (float)input_value * _inv_maxval; } /** * Sets the entire image (except the alpha channel) to the given color. */ INLINE void PNMImage:: fill(float red, float green, float blue) { fill_val(to_val(red), to_val(green), to_val(blue)); } /** * Sets the entire image (except the alpha channel) to the given grayscale * level. */ INLINE void PNMImage:: fill(float gray) { fill(gray, gray, gray); } /** * Sets the entire image (except the alpha channel) to the given grayscale * level. */ INLINE void PNMImage:: fill_val(xelval gray) { fill_val(gray, gray, gray); } /** * Sets the entire alpha channel to the given level. */ INLINE void PNMImage:: alpha_fill(float alpha) { alpha_fill_val(to_alpha_val(alpha)); } /** * Specifies the size to we'd like to scale the image upon reading it. This * will affect the next call to read(). This is usually used to reduce the * image size, e.g. for a thumbnail. * * If the file type reader supports it (e.g. JPEG), then this will scale the * image during the read operation, consequently reducing memory and CPU * utilization. If the file type reader does not support it, this will load * the image normally, and them perform a linear scale after it has been * loaded. */ INLINE void PNMImage:: set_read_size(int x_size, int y_size) { _read_x_size = x_size; _read_y_size = y_size; _has_read_size = true; } /** * Undoes the effect of a previous call to set_read_size(). */ INLINE void PNMImage:: clear_read_size() { _has_read_size = false; } /** * Returns true if set_read_size() has been called. */ INLINE bool PNMImage:: has_read_size() const { return _has_read_size; } /** * Returns the requested x_size of the image if set_read_size() has been * called, or the image x_size otherwise (if it is known). */ INLINE int PNMImage:: get_read_x_size() const { return _has_read_size ? _read_x_size : get_x_size(); } /** * Returns the requested y_size of the image if set_read_size() has been * called, or the image y_size otherwise (if it is known). */ INLINE int PNMImage:: get_read_y_size() const { return _has_read_size ? _read_y_size : get_y_size(); } /** * Returns the color space in which the image is encoded. */ INLINE ColorSpace PNMImage:: get_color_space() const { return _color_space; } /** * Returns true if the image has been read in or correctly initialized with a * height and width. If this returns false, virtually all member functions * except clear() and read() are invalid function calls. */ INLINE bool PNMImage:: is_valid() const { return (_array != nullptr); } /** * Changes the number of channels associated with the image. The new number * of channels must be an integer in the range 1 through 4, inclusive. This * will allocate and/or deallocate memory as necessary to accommodate; see * set_color_type(). */ INLINE void PNMImage:: set_num_channels(int num_channels) { nassertv(num_channels >= 1 && num_channels <= 4); set_color_type((ColorType)num_channels); } /** * Adds an alpha channel to the image, if it does not already have one. The * alpha channel is initialized to zeros. */ INLINE void PNMImage:: add_alpha() { set_color_type(is_grayscale() ? CT_two_channel : CT_four_channel); } /** * Removes the image's alpha channel, if it exists. */ INLINE void PNMImage:: remove_alpha() { set_color_type(is_grayscale() ? CT_grayscale : CT_color); } /** * Converts the image from RGB to grayscale. Any alpha channel, if present, * is left undisturbed. */ INLINE void PNMImage:: make_grayscale() { make_grayscale(_default_rc, _default_gc, _default_bc); } /** * Converts the image from grayscale to RGB. Any alpha channel, if present, * is left undisturbed. */ INLINE void PNMImage:: make_rgb() { set_color_type(has_alpha() ? CT_four_channel : CT_color); } /** * Returns the RGB color at the indicated pixel. Each component is in the * range 0..maxval. */ INLINE xel &PNMImage:: get_xel_val(int x, int y) { nassertr(x >= 0 && x < _x_size && y >= 0 && y < _y_size, _array[0]); return row(y)[x]; } /** * Returns the RGB color at the indicated pixel. Each component is in the * range 0..maxval. */ INLINE xel PNMImage:: get_xel_val(int x, int y) const { nassertr(x >= 0 && x < _x_size && y >= 0 && y < _y_size, _array[0]); return row(y)[x]; } /** * Changes the RGB color at the indicated pixel. Each 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) { nassertv(x >= 0 && x < _x_size && y >= 0 && y < _y_size); row(y)[x] = value; } /** * Changes the RGB color at the indicated pixel. Each 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) { nassertv(x >= 0 && x < _x_size && y >= 0 && y < _y_size); PPM_ASSIGN(row(y)[x], r, g, b); } /** * Changes all three color components at the indicated pixel to the same * value. The value is in the range 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) { nassertv(x >= 0 && x < _x_size && y >= 0 && y < _y_size); PPM_ASSIGN(row(y)[x], gray, gray, gray); } /** * Returns the red component color at the indicated 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 { return PPM_GETR(get_xel_val(x, y)); } /** * Returns the green component color at the indicated 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 { return PPM_GETG(get_xel_val(x, y)); } /** * Returns the blue component color at the indicated 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 { return PPM_GETB(get_xel_val(x, y)); } /** * Returns the gray component color at the indicated 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 and encoded in the * configured color space. */ INLINE xelval PNMImage:: get_gray_val(int x, int y) const { return PPM_GETB(get_xel_val(x, y)); } /** * 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 and always linear. */ INLINE xelval PNMImage:: get_alpha_val(int x, int y) const { nassertr(_alpha != nullptr && x >= 0 && x < _x_size && y >= 0 && y < _y_size, 0); return alpha_row(y)[x]; } /** * Sets the red component color only at the indicated pixel. The value given * should be in the range 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) { nassertv(x >= 0 && x < _x_size && y >= 0 && y < _y_size); PPM_PUTR(row(y)[x], r); } /** * Sets the green component color only at the indicated pixel. The value * given should be in the range 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) { nassertv(x >= 0 && x < _x_size && y >= 0 && y < _y_size); PPM_PUTG(row(y)[x], g); } /** * Sets the blue component color only at the indicated pixel. The value given * should be in the range 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) { nassertv(x >= 0 && x < _x_size && y >= 0 && y < _y_size); PPM_PUTB(row(y)[x], b); } /** * Sets the gray component color at the indicated pixel. This is only * meaningful for grayscale images; for other image types, this simply sets * the blue component color. However, also see set_xel_val(), 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, 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) { nassertv(x >= 0 && x < _x_size && y >= 0 && y < _y_size); PPM_PUTB(row(y)[x], gray); } /** * Sets the alpha component color only at the indicated 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) { nassertv(_alpha != nullptr && x >= 0 && x < _x_size && y >= 0 && y < _y_size); alpha_row(y)[x] = a; } /** * Returns the RGB color at the indicated pixel. Each component is a * linearized float in the range 0..1. */ INLINE LRGBColorf PNMImage:: get_xel(int x, int y) const { 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); default: return LRGBColorf(0); } } /** * Changes the RGB color at the indicated pixel. Each component is a * linearized float in the range 0..1. */ INLINE void PNMImage:: 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 = std::min(std::max(0, (int)scaled[0]), 65535); col.g = std::min(std::max(0, (int)scaled[1]), 65535); col.b = std::min(std::max(0, (int)scaled[2]), 65535); } break; } } /** * Changes the RGB color at the indicated pixel. Each component is a * linearized float in the range 0..1. */ INLINE void PNMImage:: set_xel(int x, int y, float r, float g, float b) { set_xel(x, y, LRGBColorf(r, g, b)); } /** * Changes all three color components at the indicated 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, float gray) { xelval val = to_val(gray); set_xel_val(x, y, val); } /** * Returns the RGBA color at the indicated pixel. Each component is a * linearized float in the range 0..1. */ INLINE LColorf PNMImage:: get_xel_a(int x, int y) const { 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; } default: return LColorf(0); } } /** * Changes the RGBA color at the indicated pixel. Each component is a * linearized float in the range 0..1. */ INLINE void PNMImage:: 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 = std::min(std::max(0, (int)scaled[0]), 65535); col.g = std::min(std::max(0, (int)scaled[1]), 65535); col.b = std::min(std::max(0, (int)scaled[2]), 65535); } break; case XE_scRGB_alpha: { LColorf scaled = value * 8192.0f + 4096.5f; col.r = std::min(std::max(0, (int)scaled[0]), 65535); col.g = std::min(std::max(0, (int)scaled[1]), 65535); col.b = std::min(std::max(0, (int)scaled[2]), 65535); alpha_row(y)[x] = std::min(std::max(0, (int)(value[3] * 65535 + 0.5f)), 65535); } break; } } /** * Changes the RGBA color at the indicated pixel. Each component is a * linearized float in the range 0..1. */ INLINE void PNMImage:: 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)); } /** * Returns the red component color at the indicated pixel. The value returned * is a linearized float in the range 0..1. */ INLINE float PNMImage:: get_red(int x, int y) const { return from_val(get_red_val(x, y)); } /** * Returns the green component color at the indicated pixel. The value * returned is a linearized float in the range 0..1. */ INLINE float PNMImage:: get_green(int x, int y) const { return from_val(get_green_val(x, y)); } /** * Returns the blue component color at the indicated pixel. The value * returned is a linearized float in the range 0..1. */ INLINE float PNMImage:: get_blue(int x, int y) const { return from_val(get_blue_val(x, y)); } /** * Returns the gray component color at the indicated 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 linearized float in the range 0..1. */ INLINE float PNMImage:: get_gray(int x, int y) const { return from_val(get_gray_val(x, y)); } /** * 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 float in * the range 0..1. */ INLINE float PNMImage:: get_alpha(int x, int y) const { return from_alpha_val(get_alpha_val(x, y)); } /** * Sets the red component color only at the indicated pixel. The value given * should be a linearized float in the range 0..1. */ INLINE void PNMImage:: set_red(int x, int y, float r) { set_red_val(x, y, to_val(r)); } /** * Sets the green component color only at the indicated pixel. The value * given should be a linearized float in the range 0..1. */ INLINE void PNMImage:: set_green(int x, int y, float g) { set_green_val(x, y, to_val(g)); } /** * Sets the blue component color only at the indicated pixel. The value given * should be a linearized float in the range 0..1. */ INLINE void PNMImage:: set_blue(int x, int y, float b) { set_blue_val(x, y, to_val(b)); } /** * Sets the gray component color at the indicated pixel. This is only * meaningful for grayscale images; for other image types, this simply sets * the blue component color. However, also see set_xel(), 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 a * linearized float in the range 0..1. */ INLINE void PNMImage:: set_gray(int x, int y, float gray) { set_gray_val(x, y, to_val(gray)); } /** * Sets the alpha component color only at the indicated pixel. It is an error * to call this unless has_alpha() is true. The value given should be in the * range 0..1. */ INLINE void PNMImage:: set_alpha(int x, int y, float a) { set_alpha_val(x, y, to_alpha_val(a)); } /** * Returns the linear brightness of the given xel, as a 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 float PNMImage:: get_bright(int x, int y) const { return get_bright(x, y, _default_rc, _default_gc, _default_bc); } /** * This flavor of get_bright() works correctly only for color images. It * returns a single brightness value for the RGB color at the indicated pixel, * based on the supplied weights for each component. */ 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)); } /** * This flavor of get_bright() works correctly only for four-channel images. * It returns a single brightness value for the RGBA color at the indicated * pixel, based on the supplied weights for each component. */ 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)); } /** * Smoothly blends the indicated pixel value in with whatever was already in * the image, based on the given alpha value. An alpha of 1.0 is fully opaque * and completely replaces whatever was there previously; alpha of 0.0 is * fully transparent and does nothing. */ INLINE void PNMImage:: blend(int x, int y, const LRGBColorf &val, float alpha) { blend(x, y, val[0], val[1], val[2], alpha); } /** * This flavor of box_filter() will apply the filter over the entire image * without resizing or copying; the effect is that of a blur operation. */ INLINE void PNMImage:: box_filter(float radius) { box_filter_from(radius, *this); } /** * This flavor of gaussian_filter() will apply the filter over the entire * image without resizing or copying; the effect is that of a blur operation. */ INLINE void PNMImage:: gaussian_filter(float radius) { gaussian_filter_from(radius, *this); } /** * Assuming the image was constructed with a gamma curve of from_gamma in the * RGB channels, converts it to an image with a gamma curve of to_gamma in the * RGB channels. Does not affect the alpha channel. */ INLINE void PNMImage:: gamma_correct(float from_gamma, float to_gamma) { apply_exponent(from_gamma / to_gamma); } /** * Assuming the image was constructed with a gamma curve of from_gamma in the * alpha channel, converts it to an image with a gamma curve of to_gamma in * the alpha channel. Does not affect the RGB channels. */ INLINE void PNMImage:: gamma_correct_alpha(float from_gamma, float to_gamma) { apply_exponent(1.0, from_gamma / to_gamma); } /** * Adjusts each channel of the image by raising the corresponding component * value to the indicated exponent, such that L' = L ^ exponent. */ INLINE void PNMImage:: apply_exponent(float gray_exponent) { apply_exponent(gray_exponent, gray_exponent, gray_exponent, 1.0); } /** * Adjusts each channel of the image by raising the corresponding component * value to the indicated exponent, such that L' = L ^ exponent. */ INLINE void PNMImage:: apply_exponent(float gray_exponent, float alpha_exponent) { apply_exponent(gray_exponent, gray_exponent, gray_exponent, alpha_exponent); } /** * Adjusts each channel of the image by raising the corresponding component * value to the indicated exponent, such that L' = L ^ exponent. For a * grayscale image, the blue_exponent value is used for the grayscale value, * and red_exponent and green_exponent are unused. */ INLINE void PNMImage:: apply_exponent(float red_exponent, float green_exponent, float blue_exponent) { apply_exponent(red_exponent, green_exponent, blue_exponent, 1.0); } /** */ INLINE PNMImage::Row:: Row(PNMImage &image, int y) : _image(image), _y(y) { nassertv(y >= 0 && y < _image._y_size); } /** * Get the number of pixels in the row. */ INLINE size_t PNMImage::Row:: size() const { return _image.get_x_size(); } /** * Fetch the RGB value at the given column in the row. */ INLINE LColorf PNMImage::Row:: operator[](int x) const { return _image.get_xel_a(x, _y); } #ifdef HAVE_PYTHON /** * Set the pixel at the given column in the row. If the image has no alpha * channel, the alpha component is ignored. */ INLINE void PNMImage::Row:: __setitem__(int x, const LColorf &v) { _image.set_xel_a(x, _y, v); } #endif /** * Fetch the pixel at the given column in the row. */ INLINE xel &PNMImage::Row:: get_xel_val(int x) { return _image.get_xel_val(x, _y); } /** * Set the pixel at the given column in the row. */ INLINE void PNMImage::Row:: set_xel_val(int x, const xel &v) { _image.set_xel_val(x, _y, v); } /** * Fetch the alpha value at the given column in the row. */ INLINE xelval PNMImage::Row:: get_alpha_val(int x) const { return _image.get_alpha_val(x, _y); } /** * Set the alpha value at the given column in the row. */ INLINE void PNMImage::Row:: set_alpha_val(int x, xelval v) { _image.set_alpha_val(x, _y, v); } /** */ INLINE PNMImage::CRow:: CRow(const PNMImage &image, int y) : _image(image), _y(y) { nassertv(y >= 0 && y < _image._y_size); } /** * Get the number of pixels in the row. */ INLINE size_t PNMImage::CRow:: size() const { return _image.get_x_size(); } /** * Fetch the RGB value at the given column in the row. */ INLINE LColorf PNMImage::CRow:: operator[](int x) const { return _image.get_xel_a(x, _y); } /** * Fetch the pixel at the given column in the row. */ INLINE xel PNMImage::CRow:: get_xel_val(int x) const { return _image.get_xel_val(x, _y); } /** * Fetch the alpha value at the given column in the row. */ INLINE xelval PNMImage::CRow:: get_alpha_val(int x) const { return _image.get_alpha_val(x, _y); } /** * Allows the PNMImage to appear to be a 2-d array of xels. */ INLINE PNMImage::Row PNMImage:: operator [] (int y) { return Row(*this, y); } /** * Allows the PNMImage to appear to be a 2-d array of xels. */ INLINE PNMImage::CRow PNMImage:: operator [] (int y) const { return CRow(*this, y); } /** * Directly access the underlying PNMImage array. Know what you are doing! */ INLINE xel *PNMImage:: get_array() { return _array; } /** * Directly access the underlying PNMImage array. Know what you are doing! */ INLINE const xel *PNMImage:: get_array() const { return _array; } /** * Directly access the underlying PNMImage array of alpha values. Know what * you are doing! */ INLINE xelval *PNMImage:: get_alpha_array() { return _alpha; } /** * Directly access the underlying PNMImage array of alpha values. Know what * you are doing! */ INLINE const xelval *PNMImage:: get_alpha_array() const { return _alpha; } /** * Returns the underlying PNMImage array and removes it from the PNMImage. * You become the owner of this array and must eventually free it with * PANDA_FREE_ARRAY() (or pass it to another PNMImage with set_array()). Know * what you are doing! */ INLINE xel *PNMImage:: take_array() { xel *array = _array; _array = nullptr; return array; } /** * Returns the underlying PNMImage array and removes it from the PNMImage. * You become the owner of this array and must eventually free it with * PANDA_FREE_ARRAY() (or pass it to another PNMImage with set_alpha_array()). * Know what you are doing! */ INLINE xelval *PNMImage:: take_alpha_array() { xelval *alpha = _alpha; _alpha = nullptr; return alpha; } /** * Allocates the internal memory for the RGB or grayscale pixels in the image * (except alpha). */ INLINE void PNMImage:: allocate_array() { _array = (xel *)PANDA_MALLOC_ARRAY((size_t)_x_size * (size_t)_y_size * sizeof(xel)); } /** * Allocates the internal memory for the alpha pixels in the image. */ INLINE void PNMImage:: allocate_alpha() { _alpha = (xelval *)PANDA_MALLOC_ARRAY((size_t)_x_size * (size_t)_y_size * sizeof(xelval)); } /** * Returns an array of xels corresponding to the nth row of the image. */ INLINE xel *PNMImage:: row(int y) const { nassertr(y >= 0 && y < _y_size, nullptr); return _array + y * _x_size; } /** * Returns an array of xelvals corresponding to the nth row of the alpha * channel. */ INLINE xelval *PNMImage:: alpha_row(int y) const { nassertr(_alpha != nullptr && y >= 0 && y < _y_size, nullptr); return _alpha + y * _x_size; } /** * Computes xmin, ymin, xmax, and ymax, based on the input parameters for * copy_sub_image() and related methods. */ INLINE void PNMImage:: setup_sub_image(const PNMImage ©, int &xto, int &yto, int &xfrom, int &yfrom, int &x_size, int &y_size, int &xmin, int &ymin, int &xmax, int &ymax) { if (x_size < 0) { x_size = copy.get_x_size() - xfrom; } if (y_size < 0) { y_size = copy.get_y_size() - yfrom; } if (xfrom < 0) { xto += -xfrom; x_size -= -xfrom; xfrom = 0; } if (yfrom < 0) { yto += -yfrom; y_size -= -yfrom; yfrom = 0; } if (xto < 0) { xfrom += -xto; x_size -= -xto; xto = 0; } if (yto < 0) { yfrom += -yto; y_size -= -yto; yto = 0; } x_size = std::min(x_size, copy.get_x_size() - xfrom); y_size = std::min(y_size, copy.get_y_size() - yfrom); xmin = xto; ymin = yto; xmax = std::min(xmin + x_size, get_x_size()); ymax = std::min(ymin + y_size, get_y_size()); } /** * Called by render_spot to compute the color of a single pixel, based in (the * square of) its distance from the center. */ INLINE void PNMImage:: 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); float d2 = d * d; float t = (3.0f * d2) - (2.0f * d * d2); c = fg + t * (bg - fg); } else { c = fg; } } /** * Returns a new PNMImage in which each pixel value is the sum of the * corresponding pixel values in the two given images. Only valid when both * images have the same size. */ INLINE PNMImage PNMImage:: operator + (const PNMImage &other) const { PNMImage target (*this); target += other; return target; } /** * Returns a new PNMImage in which the provided color is added to each pixel * in the provided image. */ INLINE PNMImage PNMImage:: operator + (const LColorf &other) const { PNMImage target (*this); target += other; return target; } /** * Returns a new PNMImage in which each pixel value from the right image is * subtracted from each pixel value from the left image. Only valid when both * images have the same size. */ INLINE PNMImage PNMImage:: operator - (const PNMImage &other) const { PNMImage target (*this); target -= other; return target; } /** * Returns a new PNMImage in which the provided color is subtracted from each * pixel in the provided image. */ INLINE PNMImage PNMImage:: operator - (const LColorf &other) const { PNMImage target (*this); target -= other; return target; } /** * Returns a new PNMImage in which each pixel value from the left image is * multiplied by each pixel value from the right image. Note that the * floating-point values in the 0..1 range are multiplied, not in the * 0..maxval range. Only valid when both images have the same size. */ INLINE PNMImage PNMImage:: operator * (const PNMImage &other) const { PNMImage target (*this); target *= other; return target; } /** * Multiplies every pixel value in the image by a constant floating-point * multiplier value. */ INLINE PNMImage PNMImage:: operator * (float multiplier) const { PNMImage target (*this); target *= multiplier; return target; } /** * Returns a new PNMImage in which the provided color is multiplied to each * pixel in the provided image. */ INLINE PNMImage PNMImage:: operator * (const LColorf &other) const { PNMImage target (*this); target *= other; return target; }