diff --git a/src/graphics/irr_driver.cpp b/src/graphics/irr_driver.cpp index 5b3a0ffff..8c7d90b7d 100644 --- a/src/graphics/irr_driver.cpp +++ b/src/graphics/irr_driver.cpp @@ -763,69 +763,77 @@ void IrrDriver::removeCameraSceneNode(scene::ICameraSceneNode *camera) // ---------------------------------------------------------------------------- /** Loads a texture from a file and returns the texture object. * \param filename File name of the texture to load. + * \param is_premul If the alpha values needd to be multiplied for + * all pixels. + * \param is_prediv If the alpha value needs to be divided into + * each pixel. */ -video::ITexture *IrrDriver::getTexture(const std::string &filename) +video::ITexture *IrrDriver::getTexture(const std::string &filename, + bool is_premul, + bool is_prediv) { -#undef PREMULPNGS -#ifndef PREMULPNGS - video::ITexture* out = m_scene_manager->getVideoDriver()->getTexture(filename.c_str()); -#else - video::IImage* img = m_scene_manager->getVideoDriver()->createImageFromFile(filename.c_str()); - // PNGs are non premul, but some are used for premul tasks, so convert - // http://home.comcast.net/~tom_forsyth/blog.wiki.html#[[Premultiplied%20alpha]] - if(StringUtils::hasSuffix(filename.c_str(), ".png")) // FIXME check param, not name + video::ITexture* out; + if(!is_premul && !is_prediv) + out = m_scene_manager->getVideoDriver()->getTexture(filename.c_str()); + else { - if ((img->getColorFormat() == irr::video::ECF_A8R8G8B8) && img->lock()) + // FIXME: can't we just do this externally, and just use the + // modified textures?? + video::IImage* img = + m_scene_manager->getVideoDriver()->createImageFromFile(filename.c_str()); + // PNGs are non premul, but some are used for premul tasks, so convert + // http://home.comcast.net/~tom_forsyth/blog.wiki.html#[[Premultiplied%20alpha]] + // FIXME check param, not name + if(is_premul && + StringUtils::hasSuffix(filename.c_str(), ".png") && + (img->getColorFormat() == irr::video::ECF_A8R8G8B8) && + img->lock()) { core::dimension2d dim = img->getDimension(); - for(uint x = 0; x < dim.Width; x++) + for(unsigned int x = 0; x < dim.Width; x++) { - for(uint y = 0; y < dim.Height; y++) + for(unsigned int y = 0; y < dim.Height; y++) { video::SColor col = img->getPixel(x, y); - uint alpha = col.getAlpha(); - uint red = alpha * col.getRed() / 255; - uint blue = alpha * col.getBlue() / 255; - uint green = alpha * col.getGreen() / 255; + unsigned int alpha = col.getAlpha(); + unsigned int red = alpha * col.getRed() / 255; + unsigned int blue = alpha * col.getBlue() / 255; + unsigned int green = alpha * col.getGreen() / 255; col.set(alpha, red, green, blue); img->setPixel(x, y, col, false); - } - } + } // for y + } // for x img->unlock(); - } - } - /* - // Other formats can be premul, but the tasks can be non premul - // So divide to get the separate RGBA (only possible if alpha!=0) - else if() // FIXME, use param "DE-PREMUL requested" to match above - { - if ((img->getColorFormat() == irr::video::ECF_A8R8G8B8) && img->lock()) + } // if png and ColorFOrmat and lock + // Other formats can be premul, but the tasks can be non premul + // So divide to get the separate RGBA (only possible if alpha!=0) + else if(is_prediv && + (img->getColorFormat() == irr::video::ECF_A8R8G8B8) && + img->lock()) { core::dimension2d dim = img->getDimension(); - for(uint x = 0; x < dim.Width; x++) + for(unsigned int x = 0; x < dim.Width; x++) { - for(uint y = 0; y < dim.Height; y++) + for(unsigned int y = 0; y < dim.Height; y++) { video::SColor col = img->getPixel(x, y); - uint alpha = col.getAlpha(); + unsigned int alpha = col.getAlpha(); // Avoid divide by zero if (alpha) { - uint red = 255 * col.getRed() / alpha ; - uint blue = 255 * col.getBlue() / alpha; - uint green = 255 * col.getGreen() / alpha; + unsigned int red = 255 * col.getRed() / alpha ; + unsigned int blue = 255 * col.getBlue() / alpha; + unsigned int green = 255 * col.getGreen() / alpha; col.set(alpha, red, green, blue); img->setPixel(x, y, col, false); } - } - } + } // for y + } // for x img->unlock(); - } - } - */ - video::ITexture* out = m_scene_manager->getVideoDriver()->addTexture(filename.c_str(), - img, NULL); -#endif - + } // if premul && color format && lock + out = m_scene_manager->getVideoDriver()->addTexture(filename.c_str(), + img, NULL); + } // if is_premul or is_prediv + #ifndef NDEBUG if (out == NULL) { diff --git a/src/graphics/irr_driver.hpp b/src/graphics/irr_driver.hpp index de4da8866..6a7ab6a46 100644 --- a/src/graphics/irr_driver.hpp +++ b/src/graphics/irr_driver.hpp @@ -112,7 +112,9 @@ public: bool OnEvent(const irr::SEvent &event); void setAmbientLight(const video::SColor &light); - video::ITexture *getTexture(const std::string &filename); + video::ITexture *getTexture(const std::string &filename, + bool is_premul=false, + bool is_prediv=false); scene::IMesh *createQuadMesh(const video::SMaterial *material=NULL, bool create_one_quad=false); scene::IMesh *createTexturedQuadMesh(const video::SMaterial *material, const double w, const double h); diff --git a/src/graphics/material.cpp b/src/graphics/material.cpp index 5fa100d7f..1b8933fb9 100644 --- a/src/graphics/material.cpp +++ b/src/graphics/material.cpp @@ -56,6 +56,17 @@ Material::Material(const XMLNode *node, int index) node->get("clampV", &b); if (b) m_clamp_tex |= VCLAMP; node->get("transparency", &m_alpha_testing ); node->get("lightmap", &m_lightmap ); + std::string s; + node->get("adjust-image", &s ); + if(s=="premultiply") + m_adjust_image = ADJ_PREMUL; + else if (s=="divide") + m_adjust_image = ADJ_DIV; + else if (s=="" || s=="none") + m_adjust_image = ADJ_NONE; + else + printf("Incorrect adjust-image specification: '%s' - ignored.\n", + s.c_str()); node->get("alpha", &m_alpha_blending ); node->get("light", &m_lighting ); node->get("sphere", &m_sphere_map ); @@ -77,7 +88,8 @@ Material::Material(const XMLNode *node, int index) node->get("anisotropic", &m_anisotropic ); node->get("backface-culling", &m_backface_culling ); node->get("disable-z-write", &m_disable_z_write ); - std::string s(""); + + s=""; node->get("graphical-effect", &s ); if(s=="water") m_graphical_effect = GE_WATER; @@ -162,6 +174,7 @@ void Material::init(unsigned int index) m_clamp_tex = 0; m_alpha_testing = false; m_lightmap = false; + m_adjust_image = ADJ_NONE; m_alpha_blending = false; m_lighting = true; m_anisotropic = false; @@ -201,7 +214,8 @@ void Material::install(bool is_full_path) const std::string &full_path = is_full_path ? m_texname : file_manager->getTextureFile(m_texname); - m_texture = irr_driver->getTexture(full_path); + m_texture = irr_driver->getTexture(full_path, + isPreMul(), isPreDiv()); // now set the name to the basename, so that all tests work as expected m_texname = StringUtils::getBasename(m_texname); diff --git a/src/graphics/material.hpp b/src/graphics/material.hpp index 0d7138d7a..18fdaa539 100644 --- a/src/graphics/material.hpp +++ b/src/graphics/material.hpp @@ -96,7 +96,11 @@ private: /** Set to true to disable writing to the Z buffer. Usually to be used with alpha blending */ bool m_disable_z_write; - + + /** Some textures need to be pre-multiplied, some divided to give + * the intended effect. */ + enum {ADJ_NONE, ADJ_PREMUL, ADJ_DIV} + m_adjust_image; /** True if lightmapping is enabled for this material. */ bool m_lightmap; float m_friction; @@ -158,6 +162,12 @@ public: bool isTransparent () const { return m_alpha_testing || m_alpha_blending || m_add; } + // ------------------------------------------------------------------------ + /** Returns true if this materials need pre-multiply of alpha. */ + bool isPreMul() const {return m_adjust_image==ADJ_PREMUL; } + // ------------------------------------------------------------------------ + /** Returns true if this materials need pre-division of alpha. */ + bool isPreDiv() const {return m_adjust_image==ADJ_DIV; } // ------------------------------------------------------------------------ /** Returns the fraction of maximum speed on this material. */ float getMaxSpeedFraction() const { return m_max_speed_fraction; }