#include "rays/texture.h" #include #include namespace Rays { struct Texture::Data { Bitmap bitmap; int id, width, height; Data () : id(-1), width(0), height(0) { } ~Data () { clear(); } void clear () { if (id >= 0) { GLenum id_ = id; glDeleteTextures(1, &id_); } bitmap = Bitmap(); id = -1; width = height = 0; } };// Texture::Data static bool get_component_type (GLenum* result, ColorSpace cs) { if (!result || !cs) return false; if (cs.is_float()) *result = GL_FLOAT; else { switch (cs.bpc()) { case 8: *result = GL_UNSIGNED_BYTE; break; case 16: *result = GL_UNSIGNED_SHORT; break; case 32: *result = GL_UNSIGNED_INT; break; default: return false; } } return true; } static bool create_texture (Texture::Data* self, GLenum format) { if (!self || !self->bitmap) return false; GLenum type; if (!get_component_type(&type, self->bitmap.color_space())) return false; GLuint id = 0; glGenTextures(1, &id); glBindTexture(GL_TEXTURE_2D, id); if (glIsTexture(id) == GL_FALSE) return false; self->id = id; glTexImage2D( GL_TEXTURE_2D, 0, format, self->bitmap.width(), self->bitmap.height(), 0, format, type, self->bitmap.data()); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);//GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);//GL_LINEAR); return true; } static int min_pow2 (int num) { int n = 1; while (n < num) n *= 2; return n; } static bool colorspace_for_alphabitmap (ColorSpace* result, ColorSpace cs) { if (!result || !cs) return false; *result = COLORSPACE_UNKNOWN; if (cs.is_float()) *result = GRAY_float; else { switch (cs.bpc()) { case 8: *result = GRAY_8; break; case 16: *result = GRAY_16; break; case 24: *result = GRAY_24; break; case 32: *result = GRAY_32; break; default: return false; } } return true; } template static inline void copy_pixel (uchar* dest, const uchar* src) { *dest = *src; copy_pixel(dest + 1, src + 1); } template <> inline void copy_pixel<1> (uchar* dest, const uchar* src) { *dest = *src; } template <> inline void copy_pixel<2> (uchar* dest, const uchar* src) { assert(sizeof(ushort) == 2); *(ushort*) dest = *(ushort*) src; } template <> inline void copy_pixel<4> (uchar* dest, const uchar* src) { assert(sizeof(ulong) == 4); *(ulong*) dest = *(ulong*) src; } template static inline void copy_pixels ( size_t width, uchar* dest, size_t dest_offset, const uchar* src, size_t src_offset) { if (!dest || !src || dest_offset <= 0 || src_offset <= 0) return; while (width--) { copy_pixel(dest, src); dest += dest_offset; src += src_offset; } } static inline void copy_pixels ( size_t Bpp, size_t width, uchar* dest, size_t dest_offset, const uchar* src, size_t src_offset) { switch (Bpp) { case 1: copy_pixels<1>(width, dest, dest_offset, src, src_offset); break; case 2: copy_pixels<2>(width, dest, dest_offset, src, src_offset); break; case 3: copy_pixels<3>(width, dest, dest_offset, src, src_offset); break; case 4: copy_pixels<4>(width, dest, dest_offset, src, src_offset); break; } } static bool copy_bitmap (Bitmap* dest, const Bitmap& src, int src_offset = 0) { if (!dest || !src || src_offset < 0) return false; int src_Bpp = src.color_space().Bpp(); int src_Bpc = src.color_space().Bpc(); int dest_Bpp = dest->color_space().Bpp(); if (src_offset >= (src_Bpp / src_Bpc)) return false; for (int y = 0; y < dest->height(); ++y) { const uchar* s = src. at(0, y) + src_offset * src_Bpc; uchar* d = dest->at(0, y); copy_pixels(src_Bpp, dest->width(), d, dest_Bpp, s, src_Bpp); } return true; } static bool create_alpha_texture (Texture::Data* self) { if (!self || !self->bitmap) return false; ColorSpace cs = self->bitmap.color_space(); int width_pow2 = min_pow2(self->width); int height_pow2 = min_pow2(self->height); if ( width_pow2 != self->width || height_pow2 != self->height || cs.is_rgb() || cs.is_bgr()) { ColorSpace newcs; if (!colorspace_for_alphabitmap(&newcs, cs)) return false; Bitmap bmp(width_pow2, height_pow2, newcs); if (!bmp) return false; if (!copy_bitmap(&bmp, self->bitmap, cs.alpha_pos())) return false; self->bitmap = bmp; cs = self->bitmap.color_space(); } if (!self->bitmap.color_space().is_gray()) return false; return create_texture(self, GL_ALPHA); } static bool create_color_texture (Texture::Data* self) { if (!self || !self->bitmap) return false; int width_pow2 = min_pow2(self->width); int height_pow2 = min_pow2(self->height); if ( width_pow2 != self->width || height_pow2 != self->height) { Bitmap bmp( width_pow2, height_pow2, self->bitmap.color_space()); if (!bmp) return false; if (!copy_bitmap(&bmp, self->bitmap)) return false; self->bitmap = bmp; } ColorSpace cs = self->bitmap.color_space(); bool alpha = cs.has_alpha(); if (cs.is_rgb()) return create_texture(self, alpha ? GL_RGBA : GL_RGB); else if (cs.is_bgr()) return create_texture(self, alpha ? GL_BGRA : GL_BGR); else if (cs.is_gray()) return create_texture(self, GL_LUMINANCE); else return false; } static bool setup_texture ( Texture::Data* self, const Bitmap& bitmap, bool alphaonly = false) { if (!self || !bitmap) return false; self->clear(); self->bitmap = bitmap; if (!self->bitmap) return false; self->width = self->bitmap.width(); self->height = self->bitmap.height(); if (alphaonly) return create_alpha_texture(self); else return create_color_texture(self); } Texture::Texture () { } Texture::Texture (const Bitmap& bitmap, bool alphaonly) { setup_texture(self.get(), bitmap, alphaonly); } Texture::~Texture () { } GLuint Texture::id () const { return self->id; } int Texture::width () const { return self->width; } int Texture::height () const { return self->height; } float Texture::s_max () const { return (float) self->width / (float) self->bitmap.width(); } float Texture::t_max () const { return (float) self->height / (float) self->bitmap.height(); } const Bitmap& Texture::bitmap () const { return self->bitmap; } Texture::operator bool () const { return self->id >= 0 && self->width > 0 && self->height > 0; } bool Texture::operator ! () const { return !operator bool(); } }// Rays