From e71491040fc00601424e5c35c8c11138fd59d70c Mon Sep 17 00:00:00 2001 From: David Rose Date: Thu, 21 Feb 2002 00:24:33 +0000 Subject: [PATCH] change normal texture byte ordering from RGBA to BGRA (big-endian) --- panda/src/glgsg/config_glgsg.cxx | 5 ++ panda/src/glgsg/config_glgsg.h | 3 +- panda/src/glgsg/glGraphicsStateGuardian.cxx | 89 ++++++++++++++++++--- panda/src/gobj/pixelBuffer.cxx | 33 ++++---- panda/src/gobj/pixelBuffer.h | 12 +++ 5 files changed, 117 insertions(+), 25 deletions(-) diff --git a/panda/src/glgsg/config_glgsg.cxx b/panda/src/glgsg/config_glgsg.cxx index a809911c6e..9a8c68d60d 100644 --- a/panda/src/glgsg/config_glgsg.cxx +++ b/panda/src/glgsg/config_glgsg.cxx @@ -65,6 +65,11 @@ bool gl_save_mipmaps = config_glgsg.GetBool("gl-save-mipmaps", false); // variable. bool gl_auto_normalize_lighting = config_glgsg.GetBool("auto-normalize-lighting", false); +// Configure this true to indicate the current version of GL fully +// supports textures with B, G, R ordering; false if it only supports +// R, G, B. +bool gl_supports_bgr = config_glgsg.GetBool("gl-supports-bgr", true); + GLDecalType gl_decal_type = GDT_offset; static GLDecalType diff --git a/panda/src/glgsg/config_glgsg.h b/panda/src/glgsg/config_glgsg.h index 33f8e22e2c..f1a710bfce 100644 --- a/panda/src/glgsg/config_glgsg.h +++ b/panda/src/glgsg/config_glgsg.h @@ -30,8 +30,9 @@ extern bool gl_cull_traversal; extern bool gl_ignore_mipmaps; extern bool gl_force_mipmaps; extern bool gl_show_mipmaps; -extern bool gl_auto_normalize_lighting; extern bool gl_save_mipmaps; +extern bool gl_auto_normalize_lighting; +extern bool gl_supports_bgr; // Ways to implement decals. enum GLDecalType { diff --git a/panda/src/glgsg/glGraphicsStateGuardian.cxx b/panda/src/glgsg/glGraphicsStateGuardian.cxx index 7766269290..6ef6ddb0f2 100644 --- a/panda/src/glgsg/glGraphicsStateGuardian.cxx +++ b/panda/src/glgsg/glGraphicsStateGuardian.cxx @@ -2223,9 +2223,15 @@ copy_pixel_buffer(PixelBuffer *pb, const DisplayRegion *dr) { case GL_DEPTH_COMPONENT: glgsg_cat.debug(false) << "GL_DEPTH_COMPONENT, "; break; + case GL_BGR: + glgsg_cat.debug(false) << "GL_BGR, "; + break; case GL_RGB: glgsg_cat.debug(false) << "GL_RGB, "; break; + case GL_BGRA: + glgsg_cat.debug(false) << "GL_BGRA, "; + break; case GL_RGBA: glgsg_cat.debug(false) << "GL_RGBA, "; break; @@ -2350,9 +2356,15 @@ draw_pixel_buffer(PixelBuffer *pb, const DisplayRegion *dr, case GL_DEPTH_COMPONENT: glgsg_cat.debug(false) << "GL_DEPTH_COMPONENT, "; break; + case GL_BGR: + glgsg_cat.debug(false) << "GL_BGR, "; + break; case GL_RGB: glgsg_cat.debug(false) << "GL_RGB, "; break; + case GL_BGRA: + glgsg_cat.debug(false) << "GL_BGRA, "; + break; case GL_RGBA: glgsg_cat.debug(false) << "GL_RGBA, "; break; @@ -3784,10 +3796,12 @@ compute_gl_image_size(int xsize, int ysize, int external_format, int type) { num_components = 2; break; + case GL_BGR: case GL_RGB: num_components = 3; break; + case GL_BGRA: case GL_RGBA: num_components = 4; break; @@ -3819,6 +3833,40 @@ compute_gl_image_size(int xsize, int ysize, int external_format, int type) { } #endif // NDEBUG +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::uchar_bgr_to_rgb +// Description: Recopies the given array of pixels, converting from +// BGR to RGB arrangement. +//////////////////////////////////////////////////////////////////// +static void +uchar_bgr_to_rgb(unsigned char *dest, const unsigned char *source, + int num_pixels) { + for (int i = 0; i < num_pixels; i++) { + dest[0] = source[2]; + dest[1] = source[1]; + dest[2] = source[0]; + dest += 3; + source += 3; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::uchar_bgra_to_rgba +// Description: Recopies the given array of pixels, converting from +// BGRA to RGBA arrangement. +//////////////////////////////////////////////////////////////////// +static void +uchar_bgra_to_rgba(unsigned char *dest, const unsigned char *source, + int num_pixels) { + for (int i = 0; i < num_pixels; i++) { + dest[0] = source[2]; + dest[1] = source[1]; + dest[2] = source[0]; + dest[3] = source[3]; + dest += 4; + source += 4; + } +} //////////////////////////////////////////////////////////////////// // Function: GLGraphicsStateGuardian::apply_texture_immediate @@ -3837,13 +3885,30 @@ apply_texture_immediate(Texture *tex) { return false; } + int xsize = pb->get_xsize(); + int ysize = pb->get_ysize(); + int num_pixels = xsize * ysize; + GLenum internal_format = get_internal_image_format(pb->get_format()); GLenum external_format = get_external_image_format(pb->get_format()); GLenum type = get_image_type(pb->get_image_type()); + uchar *image = pb->_image; + if (!gl_supports_bgr) { + // If the GL doesn't claim to support BGR, we may have to reverse + // the byte ordering of the image. + if (external_format == GL_RGB && pb->get_image_type() == PixelBuffer::T_unsigned_byte) { + image = (uchar *)alloca(num_pixels * 3); + uchar_bgr_to_rgb(image, pb->_image, num_pixels); + } else if (external_format == GL_RGBA && pb->get_image_type() == PixelBuffer::T_unsigned_byte) { + image = (uchar *)alloca(num_pixels * 4); + uchar_bgra_to_rgba(image, pb->_image, num_pixels); + } + } + #ifndef NDEBUG int wanted_size = - compute_gl_image_size(pb->get_xsize(), pb->get_ysize(), + compute_gl_image_size(xsize, ysize, external_format, type); nassertr(wanted_size == (int)pb->_image.size(), false); #endif // NDEBUG @@ -3854,7 +3919,7 @@ apply_texture_immediate(Texture *tex) { glgsg_cat.debug() << "glTexImage2D(GL_TEXTURE_2D, " << (int)internal_format << ", " - << pb->get_xsize() << ", " << pb->get_ysize() << ", " + << xsize << ", " << ysize << ", " << pb->get_border() << ", " << (int)external_format << ", " << (int)type << ", " << tex->get_name() << ")\n"; #endif @@ -3868,8 +3933,8 @@ apply_texture_immediate(Texture *tex) { } #endif gluBuild2DMipmaps(GL_TEXTURE_2D, internal_format, - pb->get_xsize(), pb->get_ysize(), - external_format, type, pb->_image); + xsize, ysize, + external_format, type, image); #ifndef NDEBUG if (gl_save_mipmaps) { save_mipmap_images(tex); @@ -3882,8 +3947,8 @@ apply_texture_immediate(Texture *tex) { nassertr(!pb->_image.empty(), false); glTexImage2D(GL_TEXTURE_2D, 0, internal_format, - pb->get_xsize(), pb->get_ysize(), pb->get_border(), - external_format, type, pb->_image); + xsize, ysize, pb->get_border(), + external_format, type, image); report_errors(); return true; } @@ -3901,6 +3966,8 @@ get_texture_wrap_mode(Texture::WrapMode wm) { return GL_CLAMP; case Texture::WM_repeat: return GL_REPEAT; + case Texture::WM_invalid: + break; } glgsg_cat.error() << "Invalid Texture::WrapMode value!\n"; return GL_CLAMP; @@ -3924,6 +3991,8 @@ get_texture_filter_type(Texture::FilterType ft) { case Texture::FT_nearest_mipmap_linear: case Texture::FT_linear_mipmap_linear: return GL_LINEAR; + case Texture::FT_invalid: + break; } } else { switch (ft) { @@ -3939,6 +4008,8 @@ get_texture_filter_type(Texture::FilterType ft) { return GL_NEAREST_MIPMAP_LINEAR; case Texture::FT_linear_mipmap_linear: return GL_LINEAR_MIPMAP_LINEAR; + case Texture::FT_invalid: + break; } } glgsg_cat.error() << "Invalid Texture::FilterType value!\n"; @@ -3999,14 +4070,14 @@ get_external_image_format(PixelBuffer::Format format) { case PixelBuffer::F_rgb8: case PixelBuffer::F_rgb12: case PixelBuffer::F_rgb332: - return GL_RGB; + return gl_supports_bgr ? GL_BGR : GL_RGB; case PixelBuffer::F_rgba: case PixelBuffer::F_rgbm: case PixelBuffer::F_rgba4: case PixelBuffer::F_rgba5: case PixelBuffer::F_rgba8: case PixelBuffer::F_rgba12: - return GL_RGBA; + return gl_supports_bgr ? GL_BGRA : GL_RGBA; case PixelBuffer::F_luminance: return GL_LUMINANCE; case PixelBuffer::F_luminance_alphamask: @@ -4016,7 +4087,7 @@ get_external_image_format(PixelBuffer::Format format) { glgsg_cat.error() << "Invalid PixelBuffer::Format value in get_external_image_format(): " << (int)format << "\n"; - return GL_RGB; + return GL_BGR; } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/gobj/pixelBuffer.cxx b/panda/src/gobj/pixelBuffer.cxx index 72a504c999..55854049d6 100644 --- a/panda/src/gobj/pixelBuffer.cxx +++ b/panda/src/gobj/pixelBuffer.cxx @@ -168,9 +168,12 @@ bool PixelBuffer::write( const string& name ) const } //////////////////////////////////////////////////////////////////// -// Function: read -// Access: -// Description: +// Function: load +// Access: Public +// Description: Extracts the image data from the given PNMImage and +// stores it in the _image member, as an unadorned array +// of pixel values. Note that we now store pixel +// components in the order B, G, R, A. //////////////////////////////////////////////////////////////////// bool PixelBuffer::load(const PNMImage& pnmimage) { @@ -228,9 +231,9 @@ bool PixelBuffer::load(const PNMImage& pnmimage) if (is_grayscale) { store_unscaled_byte(idx, pnmimage.get_gray_val(i, j)); } else { - store_unscaled_byte(idx, pnmimage.get_red_val(i, j)); - store_unscaled_byte(idx, pnmimage.get_green_val(i, j)); store_unscaled_byte(idx, pnmimage.get_blue_val(i, j)); + store_unscaled_byte(idx, pnmimage.get_green_val(i, j)); + store_unscaled_byte(idx, pnmimage.get_red_val(i, j)); } if (has_alpha) { store_unscaled_byte(idx, pnmimage.get_alpha_val(i, j)); @@ -250,9 +253,9 @@ bool PixelBuffer::load(const PNMImage& pnmimage) if (is_grayscale) { store_unscaled_short(idx, pnmimage.get_gray_val(i, j)); } else { - store_unscaled_short(idx, pnmimage.get_red_val(i, j)); - store_unscaled_short(idx, pnmimage.get_green_val(i, j)); store_unscaled_short(idx, pnmimage.get_blue_val(i, j)); + store_unscaled_short(idx, pnmimage.get_green_val(i, j)); + store_unscaled_short(idx, pnmimage.get_red_val(i, j)); } if (has_alpha) { store_unscaled_short(idx, pnmimage.get_alpha_val(i, j)); @@ -274,9 +277,9 @@ bool PixelBuffer::load(const PNMImage& pnmimage) if (is_grayscale) { store_scaled_byte(idx, pnmimage.get_gray_val(i, j), scale); } else { - store_scaled_byte(idx, pnmimage.get_red_val(i, j), scale); - store_scaled_byte(idx, pnmimage.get_green_val(i, j), scale); store_scaled_byte(idx, pnmimage.get_blue_val(i, j), scale); + store_scaled_byte(idx, pnmimage.get_green_val(i, j), scale); + store_scaled_byte(idx, pnmimage.get_red_val(i, j), scale); } if (has_alpha) { store_scaled_byte(idx, pnmimage.get_alpha_val(i, j), scale); @@ -298,9 +301,9 @@ bool PixelBuffer::load(const PNMImage& pnmimage) if (is_grayscale) { store_scaled_short(idx, pnmimage.get_gray_val(i, j), scale); } else { - store_scaled_short(idx, pnmimage.get_red_val(i, j), scale); - store_scaled_short(idx, pnmimage.get_green_val(i, j), scale); store_scaled_short(idx, pnmimage.get_blue_val(i, j), scale); + store_scaled_short(idx, pnmimage.get_green_val(i, j), scale); + store_scaled_short(idx, pnmimage.get_red_val(i, j), scale); } if (has_alpha) { store_scaled_short(idx, pnmimage.get_alpha_val(i, j), scale); @@ -332,9 +335,9 @@ store(PNMImage &pnmimage) const { if (is_grayscale) { pnmimage.set_gray(i, j, get_unsigned_byte(idx)); } else { - pnmimage.set_red(i, j, get_unsigned_byte(idx)); - pnmimage.set_green(i, j, get_unsigned_byte(idx)); pnmimage.set_blue(i, j, get_unsigned_byte(idx)); + pnmimage.set_green(i, j, get_unsigned_byte(idx)); + pnmimage.set_red(i, j, get_unsigned_byte(idx)); } if (has_alpha) pnmimage.set_alpha(i, j, get_unsigned_byte(idx)); @@ -353,9 +356,9 @@ store(PNMImage &pnmimage) const { if (is_grayscale) { pnmimage.set_gray(i, j, get_unsigned_short(idx)); } else { - pnmimage.set_red(i, j, get_unsigned_short(idx)); - pnmimage.set_green(i, j, get_unsigned_short(idx)); pnmimage.set_blue(i, j, get_unsigned_short(idx)); + pnmimage.set_green(i, j, get_unsigned_short(idx)); + pnmimage.set_red(i, j, get_unsigned_short(idx)); } if (has_alpha) pnmimage.set_alpha(i, j, get_unsigned_short(idx)); diff --git a/panda/src/gobj/pixelBuffer.h b/panda/src/gobj/pixelBuffer.h index 6721e3698f..34d06d7521 100644 --- a/panda/src/gobj/pixelBuffer.h +++ b/panda/src/gobj/pixelBuffer.h @@ -38,6 +38,18 @@ class RenderBuffer; //////////////////////////////////////////////////////////////////// // Class : PixelBuffer // Description : + +// Maintains an array of pixel data corresponding to an +// image, e.g. copied from the frame buffer, or as part +// of a Texture. + +// Pixel data is stored in a generic, uncompressed +// format. Each row of pixels is laid out horizontally, +// from the top to the bottom, with no padding between +// rows. Each pixel consumes one or more bytes, +// according to get_component_width(). If the Format +// indicates multiple components are present, they are +// stored in the order B, G, R, A. //////////////////////////////////////////////////////////////////// class EXPCL_PANDA PixelBuffer : public ImageBuffer { public: