void SubPalette::initialize(const RGBAPixel& c) { RGBAPixel center = rgba( (OctreePalette2::BIN_FOR_COLOR(rgba_red(c)) * 256 + 128) / OctreePalette2::BINS, (OctreePalette2::BIN_FOR_COLOR(rgba_green(c)) * 256 + 128) / OctreePalette2::BINS, (OctreePalette2::BIN_FOR_COLOR(rgba_blue(c)) * 256 + 128) / OctreePalette2::BINS, (OctreePalette2::BIN_FOR_COLOR(rgba_alpha(c)) * 256 + 128) / OctreePalette2::BINS ); int nearest = 256 * 256 * 4; for (size_t i = 0; i < palette_colors.size(); i++) { int distance = rgba_distance2(palette_colors[i], center); if (distance < nearest) nearest = distance; if (nearest == 0) break; } double tmp = sqrt(nearest) + 2 * sqrt(2) * (128 / OctreePalette2::BINS); int nearest_dist = (tmp * tmp) + 1; for (size_t i = 0; i < palette_colors.size(); i++) { int distance = rgba_distance2(palette_colors[i], center); if (distance <= nearest_dist) colors.push_back(i); } initialized = true; }
int Octree::findNearestColor(const Octree* octree, RGBAPixel color) { assert(octree != nullptr); uint8_t red = rgba_red(color); uint8_t green = rgba_green(color); uint8_t blue = rgba_blue(color); uint8_t alpha = rgba_alpha(color); const Octree* node = octree; for (int i = 7; i >= 8 - OCTREE_COLOR_BITS; i--) { if (node->hasColor()) break; int index = (nth_bit(red, i) << 3) | (nth_bit(green, i) << 2) | nth_bit(blue, i) << 1 | nth_bit(alpha, i); if (node->hasChildren(index)) node = node->getChildren(index); else break; } if (node->hasColor()) return node->getColorID(); auto& colors = node->subtree_colors; int min_distance = -1; int best_color = -1; for (auto it = colors.begin(); it != colors.end(); ++it) { int distance = rgba_distance2(color, it->second); if (best_color == -1 || distance < min_distance) { min_distance = distance; best_color = it->first; } } // this shouldn't happen if all the colors are cached assert(best_color != -1); return best_color; }
void Octree::setColor(RGBAPixel color) { reference++; red += rgba_red(color); green += rgba_green(color); blue += rgba_blue(color); alpha += rgba_alpha(color); }
int OctreePalette2::getNearestColor(const RGBAPixel& color) { // find the belonging sub palette for this color and ask it for the nearest color int bins = OctreePalette2::BINS; size_t index = OctreePalette2::BIN_FOR_COLOR(rgba_red(color)); index += bins * OctreePalette2::BIN_FOR_COLOR(rgba_green(color)); index += bins * bins * OctreePalette2::BIN_FOR_COLOR(rgba_blue(color)); index += bins * bins * bins * OctreePalette2::BIN_FOR_COLOR(rgba_alpha(color)); assert(index < sub_palettes.size()); if (sub_palettes[index] == nullptr) sub_palettes[index] = new SubPalette(colors); return sub_palettes[index]->getNearestColor(color); }
Octree* Octree::findOrCreateNode(Octree* octree, RGBAPixel color) { assert(octree != nullptr); uint8_t red = rgba_red(color); uint8_t green = rgba_green(color); uint8_t blue = rgba_blue(color); uint8_t alpha = rgba_alpha(color); Octree* node = octree; for (int i = 7; i >= 8 - OCTREE_COLOR_BITS; i--) { int index = (nth_bit(red, i) << 3) | (nth_bit(green, i) << 2) | nth_bit(blue, i) << 1 | nth_bit(alpha, i); node = node->getChildren(index); assert(node != nullptr); } return node; }
bool TextureImage::load(const std::string& path, int size, int blur, double water_opacity) { // at first try to load the texture file if (!original.readPNG(path + "/" + name + ".png")) { // make sure the texture image does not have zero dimension // even if the texture does not exist / is broken this->setSize(size, size); original = original_resized = *this; return false; } // check if this is an animated texture, calculate how many frames it has // also make sure there are exactly n frames, so height mod width = 0 if ((original.getHeight() % original.getWidth()) != 0) { LOG(WARNING) << "Texture '" << name << "' has odd size: " << original.getWidth() << "x" << original.getHeight(); } frame_count = original.getHeight() / original.getWidth(); // now resize the texture image // resize some textures with the nearest neighbor interpolation: // - transparent leaves // - redstone // because smooth interpolation causes here half-transparent pixel which are not // good for performance and makes redstone looking not very good and identifiable, // instead of that the nearest neighbor interpolation preserves the pixelated // style of the textures and prevents fuzziness when resizing if ((util::startswith(name, "leaves") && !util::endswith(name, "opaque")) || util::startswith(name, "redstone_dust")) original.resize(original_resized, size, size * frame_count, InterpolationType::NEAREST); else original.resize(original_resized, size, size * frame_count); int width = original_resized.getWidth(); int height = original_resized.getHeight(); // apply a blur to the texture if wanted // this is useful if you use small texture sizes (< 6 maybe) to prevent grainy textures if (blur != 0) { for (int i = 0; i < frame_count; i++) { RGBAImage frame; // process every frame individually original_resized.clip(0, width * i, width, width).blur(frame, blur); original_resized.simpleBlit(frame, 0, width * i); } } // apply opacity factor to water textures if (util::startswith(name, "water_") && water_opacity != 1.0) { for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { RGBAPixel& pixel = original_resized.pixel(x, y); uint8_t alpha = std::min(255.0, rgba_alpha(pixel) * water_opacity); pixel = rgba(rgba_red(pixel), rgba_green(pixel), rgba_blue(pixel), alpha); } } } // assign actual texture to parent RGBAImage object // uses first frame if this is an animated texture this->setSize(size, size); this->simpleAlphaBlit(getFrame(0), 0, 0); return true; }