From db481e72321977f982d47f48be402565acc3443c Mon Sep 17 00:00:00 2001 From: TriBlade9 Date: Mon, 19 Jan 2015 14:15:20 +0800 Subject: [PATCH] Added ratio argument to colorize, removed the weird alpha-based ratio. --- doc/lua_api.txt | 5 +- src/tile.cpp | 230 ++++++++++++++++++++++++++++-------------------- 2 files changed, 137 insertions(+), 98 deletions(-) diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 6359ef6de..586c520fc 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -337,9 +337,11 @@ Apply a mask to the base image. The mask is applied using binary AND. -#### `[colorize:` +#### `[colorize::` Colorize the textures with the given color. `` is specified as a `ColorString`. +`` is an int ranging from 0 to 255, and specifies how much of the +color to apply. If ommitted, the alpha will be used. Sounds ------ @@ -3259,4 +3261,3 @@ Definition tables playername = "singleplayer" -- ^ Playername is optional, if specified spawns particle only on the player's client } - diff --git a/src/tile.cpp b/src/tile.cpp index 560dcddb8..81b362d61 100644 --- a/src/tile.cpp +++ b/src/tile.cpp @@ -54,23 +54,23 @@ MutexedMap g_texturename_to_path_cache; */ static bool replace_ext(std::string &path, const char *ext) { - if(ext == NULL) + if (ext == NULL) return false; // Find place of last dot, fail if \ or / found. s32 last_dot_i = -1; - for(s32 i=path.size()-1; i>=0; i--) + for (s32 i=path.size()-1; i>=0; i--) { - if(path[i] == '.') + if (path[i] == '.') { last_dot_i = i; break; } - if(path[i] == '\\' || path[i] == '/') + if (path[i] == '\\' || path[i] == '/') break; } // If not found, return an empty string - if(last_dot_i == -1) + if (last_dot_i == -1) return false; // Else make the new path path = path.substr(0, last_dot_i+1) + ext; @@ -92,15 +92,15 @@ std::string getImagePath(std::string path) NULL }; // If there is no extension, add one - if(removeStringEnd(path, extensions) == "") + if (removeStringEnd(path, extensions) == "") path = path + ".png"; // Check paths until something is found to exist const char **ext = extensions; do{ bool r = replace_ext(path, *ext); - if(r == false) + if (r == false) return ""; - if(fs::PathExists(path)) + if (fs::PathExists(path)) return path; } while((++ext) != NULL); @@ -125,14 +125,14 @@ std::string getTexturePath(const std::string &filename) Check from cache */ bool incache = g_texturename_to_path_cache.get(filename, &fullpath); - if(incache) + if (incache) return fullpath; /* Check from texture_path */ std::string texture_path = g_settings->get("texture_path"); - if(texture_path != "") + if (texture_path != "") { std::string testpath = texture_path + DIR_DELIM + filename; // Check all filename extensions. Returns "" if not found. @@ -142,7 +142,7 @@ std::string getTexturePath(const std::string &filename) /* Check from default data directory */ - if(fullpath == "") + if (fullpath == "") { std::string base_path = porting::path_share + DIR_DELIM + "textures" + DIR_DELIM + "base" + DIR_DELIM + "pack"; @@ -190,7 +190,7 @@ class SourceImageCache { public: ~SourceImageCache() { - for(std::map::iterator iter = m_images.begin(); + for (std::map::iterator iter = m_images.begin(); iter != m_images.end(); iter++) { iter->second->drop(); } @@ -203,8 +203,8 @@ public: // Remove old image std::map::iterator n; n = m_images.find(name); - if(n != m_images.end()){ - if(n->second) + if (n != m_images.end()){ + if (n->second) n->second->drop(); } @@ -212,11 +212,11 @@ public: bool need_to_grab = true; // Try to use local texture instead if asked to - if(prefer_local){ + if (prefer_local){ std::string path = getTexturePath(name); - if(path != ""){ + if (path != ""){ video::IImage *img2 = driver->createImageFromFile(path.c_str()); - if(img2){ + if (img2){ toadd = img2; need_to_grab = false; } @@ -231,7 +231,7 @@ public: { std::map::iterator n; n = m_images.find(name); - if(n != m_images.end()) + if (n != m_images.end()) return n->second; return NULL; } @@ -240,13 +240,13 @@ public: { std::map::iterator n; n = m_images.find(name); - if(n != m_images.end()){ + if (n != m_images.end()){ n->second->grab(); // Grab for caller return n->second; } video::IVideoDriver* driver = device->getVideoDriver(); std::string path = getTexturePath(name); - if(path == ""){ + if (path == ""){ infostream<<"SourceImageCache::getOrLoad(): No path found for \"" <createImageFromFile(path.c_str()); - if(img){ + if (img){ m_images[name] = img; img->grab(); // Grab for caller } @@ -341,7 +341,7 @@ public: { bool is_known = false; bool cache_found = m_source_image_existence.get(name, &is_known); - if(cache_found) + if (cache_found) return is_known; // Not found in cache; find out if a local file exists is_known = (getTexturePath(name) != ""); @@ -480,7 +480,7 @@ u32 TextureSource::getTextureId(const std::string &name) JMutexAutoLock lock(m_textureinfo_cache_mutex); std::map::iterator n; n = m_name_to_id.find(name); - if(n != m_name_to_id.end()) + if (n != m_name_to_id.end()) { return n->second; } @@ -489,7 +489,7 @@ u32 TextureSource::getTextureId(const std::string &name) /* Get texture */ - if(get_current_thread_id() == m_main_thread) + if (get_current_thread_id() == m_main_thread) { return generateTexture(name); } @@ -540,6 +540,11 @@ static void blit_with_alpha(video::IImage *src, video::IImage *dst, static void blit_with_alpha_overlay(video::IImage *src, video::IImage *dst, v2s32 src_pos, v2s32 dst_pos, v2u32 size); +// Like blit_with_alpha overlay, but uses an int to calculate the ratio +// and modifies any destination pixels that are not fully transparent +static void blit_with_interpolate_overlay(video::IImage *src, video::IImage *dst, + v2s32 src_pos, v2s32 dst_pos, v2u32 size, int ratio); + // Apply a mask to an image static void apply_mask(video::IImage *mask, video::IImage *dst, v2s32 mask_pos, v2s32 dst_pos, v2u32 size); @@ -626,7 +631,7 @@ std::string TextureSource::getTextureName(u32 id) { JMutexAutoLock lock(m_textureinfo_cache_mutex); - if(id >= m_textureinfo_cache.size()) + if (id >= m_textureinfo_cache.size()) { errorstream<<"TextureSource::getTextureName(): id="<= m_textureinfo_cache.size()=" @@ -641,7 +646,7 @@ video::ITexture* TextureSource::getTexture(u32 id) { JMutexAutoLock lock(m_textureinfo_cache_mutex); - if(id >= m_textureinfo_cache.size()) + if (id >= m_textureinfo_cache.size()) return NULL; return m_textureinfo_cache[id].texture; @@ -650,7 +655,7 @@ video::ITexture* TextureSource::getTexture(u32 id) video::ITexture* TextureSource::getTexture(const std::string &name, u32 *id) { u32 actual_id = getTextureId(name); - if(id){ + if (id){ *id = actual_id; } return getTexture(actual_id); @@ -662,7 +667,7 @@ void TextureSource::processQueue() Fetch textures */ //NOTE this is only thread safe for ONE consumer thread! - if(!m_get_texture_queue.empty()) + if (!m_get_texture_queue.empty()) { GetRequest request = m_get_texture_queue.pop(); @@ -694,7 +699,7 @@ void TextureSource::rebuildImagesAndTextures() assert(driver != 0); // Recreate textures - for(u32 i=0; iname); #ifdef __ANDROID__ @@ -836,17 +841,17 @@ video::ITexture* TextureSource::generateTextureFromMesh( driver->makeColorKeyTexture(rtt, v2s32(0,0)); - if(params.delete_texture_on_shutdown) + if (params.delete_texture_on_shutdown) m_texture_trash.push_back(rtt); return rtt; } #endif - if(driver->queryFeature(video::EVDF_RENDER_TO_TARGET) == false) + if (driver->queryFeature(video::EVDF_RENDER_TO_TARGET) == false) { static bool warned = false; - if(!warned) + if (!warned) { errorstream<<"TextureSource::generateTextureFromMesh(): " <<"EVDF_RENDER_TO_TARGET not supported."<addRenderTargetTexture( params.dim, params.rtt_texture_name.c_str(), video::ECF_A8R8G8B8); - if(rtt == NULL) + if (rtt == NULL) { errorstream<<"TextureSource::generateTextureFromMesh(): " <<"addRenderTargetTexture returned NULL."<setRenderTarget(0, false, true, 0); - if(params.delete_texture_on_shutdown) + if (params.delete_texture_on_shutdown) m_texture_trash.push_back(rtt); return rtt; @@ -930,7 +935,7 @@ video::IImage* TextureSource::generateImage(const std::string &name) // Find last separator in the name s32 last_separator_pos = -1; u8 paren_bal = 0; - for(s32 i = name.size() - 1; i >= 0; i--) { + for (s32 i = name.size() - 1; i >= 0; i--) { switch(name[i]) { case separator: if (paren_bal == 0) { @@ -1030,7 +1035,7 @@ video::IImage* TextureSource::generateImage(const std::string &name) video::IImage * Align2Npot2(video::IImage * image, video::IVideoDriver* driver) { - if(image == NULL) { + if (image == NULL) { return image; } @@ -1077,7 +1082,7 @@ bool TextureSource::generateImagePart(std::string part_of_name, assert(driver); // Stuff starting with [ are special commands - if(part_of_name.size() == 0 || part_of_name[0] != '[') + if (part_of_name.size() == 0 || part_of_name[0] != '[') { video::IImage *image = m_sourcecache.getOrLoad(part_of_name, m_device); #ifdef __ANDROID__ @@ -1118,7 +1123,7 @@ bool TextureSource::generateImagePart(std::string part_of_name, } // If base image is NULL, load as base. - if(baseimg == NULL) + if (baseimg == NULL) { //infostream<<"Setting "<= 0) + if (img_crack && progression >= 0) { draw_crack(img_crack, baseimg, use_overlay, frame_count, @@ -1202,7 +1207,7 @@ bool TextureSource::generateImagePart(std::string part_of_name, [combine:WxH:X,Y=filename:X,Y=filename2 Creates a bigger texture from an amount of smaller ones */ - else if(part_of_name.substr(0,8) == "[combine") + else if (part_of_name.substr(0,8) == "[combine") { Strfnd sf(part_of_name); sf.next(":"); @@ -1246,7 +1251,7 @@ bool TextureSource::generateImagePart(std::string part_of_name, /* "[brighten" */ - else if(part_of_name.substr(0,9) == "[brighten") + else if (part_of_name.substr(0,9) == "[brighten") { if (baseimg == NULL) { errorstream<<"generateImagePart(): baseimg==NULL " @@ -1264,7 +1269,7 @@ bool TextureSource::generateImagePart(std::string part_of_name, that the transparent parts don't look completely black when simple alpha channel is used for rendering. */ - else if(part_of_name.substr(0,8) == "[noalpha") + else if (part_of_name.substr(0,8) == "[noalpha") { if (baseimg == NULL){ errorstream<<"generateImagePart(): baseimg==NULL " @@ -1276,8 +1281,8 @@ bool TextureSource::generateImagePart(std::string part_of_name, core::dimension2d dim = baseimg->getDimension(); // Set alpha to full - for(u32 y=0; ygetPixel(x,y); c.setAlpha(255); @@ -1288,7 +1293,7 @@ bool TextureSource::generateImagePart(std::string part_of_name, "[makealpha:R,G,B" Convert one color to transparent. */ - else if(part_of_name.substr(0,11) == "[makealpha:") + else if (part_of_name.substr(0,11) == "[makealpha:") { if (baseimg == NULL) { errorstream<<"generateImagePart(): baseimg == NULL " @@ -1311,14 +1316,14 @@ bool TextureSource::generateImagePart(std::string part_of_name, oldbaseimg->drop();*/ // Set alpha to full - for(u32 y=0; ygetPixel(x,y); u32 r = c.getRed(); u32 g = c.getGreen(); u32 b = c.getBlue(); - if(!(r == r1 && g == g1 && b == b1)) + if (!(r == r1 && g == g1 && b == b1)) continue; c.setAlpha(0); baseimg->setPixel(x,y,c); @@ -1344,7 +1349,7 @@ bool TextureSource::generateImagePart(std::string part_of_name, The resulting transform will be equivalent to one of the eight existing ones, though (see: dihedral group). */ - else if(part_of_name.substr(0,10) == "[transform") + else if (part_of_name.substr(0,10) == "[transform") { if (baseimg == NULL) { errorstream<<"generateImagePart(): baseimg == NULL " @@ -1371,7 +1376,7 @@ bool TextureSource::generateImagePart(std::string part_of_name, Example (a grass block (not actually used in game): "[inventorycube{grass.png{mud.png&grass_side.png{mud.png&grass_side.png" */ - else if(part_of_name.substr(0,14) == "[inventorycube") + else if (part_of_name.substr(0,14) == "[inventorycube") { if (baseimg != NULL){ errorstream<<"generateImagePart(): baseimg != NULL " @@ -1488,7 +1493,7 @@ bool TextureSource::generateImagePart(std::string part_of_name, [lowpart:percent:filename Adds the lower part of a texture */ - else if(part_of_name.substr(0,9) == "[lowpart:") + else if (part_of_name.substr(0,9) == "[lowpart:") { Strfnd sf(part_of_name); sf.next(":"); @@ -1496,10 +1501,10 @@ bool TextureSource::generateImagePart(std::string part_of_name, std::string filename = sf.next(":"); //infostream<<"power part "<createImage(video::ECF_A8R8G8B8, v2u32(16,16)); video::IImage *img = m_sourcecache.getOrLoad(filename, m_device); - if(img) + if (img) { core::dimension2d dim = img->getDimension(); core::position2d pos_base(0, 0); @@ -1524,14 +1529,14 @@ bool TextureSource::generateImagePart(std::string part_of_name, Crops a frame of a vertical animation. N = frame count, I = frame index */ - else if(part_of_name.substr(0,15) == "[verticalframe:") + else if (part_of_name.substr(0,15) == "[verticalframe:") { Strfnd sf(part_of_name); sf.next(":"); u32 frame_count = stoi(sf.next(":")); u32 frame_index = stoi(sf.next(":")); - if(baseimg == NULL){ + if (baseimg == NULL){ errorstream<<"generateImagePart(): baseimg != NULL " <<"for part_of_name=\""<createImage(video::ECF_A8R8G8B8, frame_size); - if(!img){ + if (!img){ errorstream<<"generateImagePart(): Could not create image " <<"for part_of_name=\""< dim = baseimg->getDimension(); video::IImage *img = driver->createImage(video::ECF_A8R8G8B8, dim); @@ -1622,7 +1633,7 @@ bool TextureSource::generateImagePart(std::string part_of_name, img->fill(video::SColor(color)); // Overlay the colored image - blit_with_alpha_overlay(img, baseimg, v2s32(0,0), v2s32(0,0), dim); + blit_with_interpolate_overlay(img, baseimg, v2s32(0,0), v2s32(0,0), dim, ratio); img->drop(); } else @@ -1645,8 +1656,8 @@ bool TextureSource::generateImagePart(std::string part_of_name, static void blit_with_alpha(video::IImage *src, video::IImage *dst, v2s32 src_pos, v2s32 dst_pos, v2u32 size) { - for(u32 y0=0; y0getPixel(src_x, src_y); video::SColor dst_c = dst->getPixel(dst_x, dst_y); - if(dst_c.getAlpha() == 255 && src_c.getAlpha() != 0) + if (dst_c.getAlpha() == 255 && src_c.getAlpha() != 0) { dst_c = src_c.getInterpolated(dst_c, (float)src_c.getAlpha()/255.0f); dst->setPixel(dst_x, dst_y, dst_c); @@ -1683,14 +1694,41 @@ static void blit_with_alpha_overlay(video::IImage *src, video::IImage *dst, } } +/* + Draw an image on top of an another one, using the specified ratio + modify all partially-opaque pixels in the destination. +*/ +static void blit_with_interpolate_overlay(video::IImage *src, video::IImage *dst, + v2s32 src_pos, v2s32 dst_pos, v2u32 size, int ratio) +{ + for (u32 y0 = 0; y0 < size.Y; y0++) + for (u32 x0 = 0; x0 < size.X; x0++) + { + s32 src_x = src_pos.X + x0; + s32 src_y = src_pos.Y + y0; + s32 dst_x = dst_pos.X + x0; + s32 dst_y = dst_pos.Y + y0; + video::SColor src_c = src->getPixel(src_x, src_y); + video::SColor dst_c = dst->getPixel(dst_x, dst_y); + if (dst_c.getAlpha() > 0 && src_c.getAlpha() != 0) + { + if (ratio == -1) + dst_c = src_c.getInterpolated(dst_c, (float)src_c.getAlpha()/255.0f); + else + dst_c = src_c.getInterpolated(dst_c, (float)ratio/255.0f); + dst->setPixel(dst_x, dst_y, dst_c); + } + } +} + /* Apply mask to destination */ static void apply_mask(video::IImage *mask, video::IImage *dst, v2s32 mask_pos, v2s32 dst_pos, v2u32 size) { - for(u32 y0 = 0; y0 < size.Y; y0++) { - for(u32 x0 = 0; x0 < size.X; x0++) { + for (u32 y0 = 0; y0 < size.Y; y0++) { + for (u32 x0 = 0; x0 < size.X; x0++) { s32 mask_x = x0 + mask_pos.X; s32 mask_y = y0 + mask_pos.Y; s32 dst_x = x0 + dst_pos.X; @@ -1714,12 +1752,12 @@ static void draw_crack(video::IImage *crack, video::IImage *dst, // Count of crack stages s32 crack_count = dim_crack.Height / dim_crack.Width; // Limit frame_count - if(frame_count > (s32) dim_dst.Height) + if (frame_count > (s32) dim_dst.Height) frame_count = dim_dst.Height; - if(frame_count < 1) + if (frame_count < 1) frame_count = 1; // Limit progression - if(progression > crack_count-1) + if (progression > crack_count-1) progression = crack_count-1; // Dimension of a single crack stage core::dimension2d dim_crack_cropped( @@ -1738,7 +1776,7 @@ static void draw_crack(video::IImage *crack, video::IImage *dst, video::IImage *crack_scaled = driver->createImage( video::ECF_A8R8G8B8, dim_crack_scaled); - if(crack_cropped && crack_scaled) + if (crack_cropped && crack_scaled) { // Crop crack image v2s32 pos_crack(0, progression*dim_crack.Width); @@ -1748,10 +1786,10 @@ static void draw_crack(video::IImage *crack, video::IImage *dst, // Scale crack image by copying crack_cropped->copyToScaling(crack_scaled); // Copy or overlay crack image onto each frame - for(s32 i = 0; i < frame_count; ++i) + for (s32 i = 0; i < frame_count; ++i) { v2s32 dst_pos(0, dim_crack_scaled.Height * i); - if(use_overlay) + if (use_overlay) { blit_with_alpha_overlay(crack_scaled, dst, v2s32(0,0), dst_pos, @@ -1766,22 +1804,22 @@ static void draw_crack(video::IImage *crack, video::IImage *dst, } } - if(crack_scaled) + if (crack_scaled) crack_scaled->drop(); - if(crack_cropped) + if (crack_cropped) crack_cropped->drop(); } void brighten(video::IImage *image) { - if(image == NULL) + if (image == NULL) return; core::dimension2d dim = image->getDimension(); - for(u32 y=0; ygetPixel(x,y); c.setRed(0.5 * 255 + 0.5 * (float)c.getRed()); @@ -1807,17 +1845,17 @@ u32 parseImageTransform(const std::string& s) while(pos < s.size()) { int transform = -1; - for(int i = 0; i <= 7; ++i) + for (int i = 0; i <= 7; ++i) { const std::string &name_i = transform_names[i]; - if(s[pos] == ('0' + i)) + if (s[pos] == ('0' + i)) { transform = i; pos++; break; } - else if(!(name_i.empty()) && + else if (!(name_i.empty()) && lowercase(s.substr(pos, name_i.size())) == name_i) { transform = i; @@ -1825,16 +1863,16 @@ u32 parseImageTransform(const std::string& s) break; } } - if(transform < 0) + if (transform < 0) break; // Multiply total_transform and transform in the group D4 int new_total = 0; - if(transform < 4) + if (transform < 4) new_total = (transform + total_transform) % 4; else new_total = (transform - total_transform + 8) % 4; - if((transform >= 4) ^ (total_transform >= 4)) + if ((transform >= 4) ^ (total_transform >= 4)) new_total += 4; total_transform = new_total; @@ -1844,7 +1882,7 @@ u32 parseImageTransform(const std::string& s) core::dimension2d imageTransformDimension(u32 transform, core::dimension2d dim) { - if(transform % 2 == 0) + if (transform % 2 == 0) return dim; else return core::dimension2d(dim.Height, dim.Width); @@ -1852,7 +1890,7 @@ core::dimension2d imageTransformDimension(u32 transform, core::dimension2d< void imageTransform(u32 transform, video::IImage *src, video::IImage *dst) { - if(src == NULL || dst == NULL) + if (src == NULL || dst == NULL) return; core::dimension2d srcdim = src->getDimension(); @@ -1867,25 +1905,25 @@ void imageTransform(u32 transform, video::IImage *src, video::IImage *dst) */ int sxn = 0; int syn = 2; - if(transform == 0) // identity + if (transform == 0) // identity sxn = 0, syn = 2; // sx = dx, sy = dy - else if(transform == 1) // rotate by 90 degrees ccw + else if (transform == 1) // rotate by 90 degrees ccw sxn = 3, syn = 0; // sx = (H-1) - dy, sy = dx - else if(transform == 2) // rotate by 180 degrees + else if (transform == 2) // rotate by 180 degrees sxn = 1, syn = 3; // sx = (W-1) - dx, sy = (H-1) - dy - else if(transform == 3) // rotate by 270 degrees ccw + else if (transform == 3) // rotate by 270 degrees ccw sxn = 2, syn = 1; // sx = dy, sy = (W-1) - dx - else if(transform == 4) // flip x + else if (transform == 4) // flip x sxn = 1, syn = 2; // sx = (W-1) - dx, sy = dy - else if(transform == 5) // flip x then rotate by 90 degrees ccw + else if (transform == 5) // flip x then rotate by 90 degrees ccw sxn = 2, syn = 0; // sx = dy, sy = dx - else if(transform == 6) // flip y + else if (transform == 6) // flip y sxn = 0, syn = 3; // sx = dx, sy = (H-1) - dy - else if(transform == 7) // flip y then rotate by 90 degrees ccw + else if (transform == 7) // flip y then rotate by 90 degrees ccw sxn = 3, syn = 1; // sx = (H-1) - dy, sy = (W-1) - dx - for(u32 dy=0; dy