diff options
Diffstat (limited to 'src/resources/dye')
-rw-r--r-- | src/resources/dye/dye.cpp | 385 | ||||
-rw-r--r-- | src/resources/dye/dye.h | 93 | ||||
-rw-r--r-- | src/resources/dye/dye_unittest.cc | 254 | ||||
-rw-r--r-- | src/resources/dye/dyecolor.h | 53 | ||||
-rw-r--r-- | src/resources/dye/dyepalette.cpp | 586 | ||||
-rw-r--r-- | src/resources/dye/dyepalette.h | 88 |
6 files changed, 1459 insertions, 0 deletions
diff --git a/src/resources/dye/dye.cpp b/src/resources/dye/dye.cpp new file mode 100644 index 000000000..3b7904122 --- /dev/null +++ b/src/resources/dye/dye.cpp @@ -0,0 +1,385 @@ +/* + * The ManaPlus Client + * Copyright (C) 2007-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * Copyright (C) 2011-2015 The ManaPlus Developers + * + * This file is part of The ManaPlus Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "resources/dye/dye.h" + +#include "logger.h" + +#include "resources/dye/dyepalette.h" + +#include "utils/delete2.h" + +#include <sstream> + +#include <SDL_endian.h> + +#include "debug.h" + +Dye::Dye(const std::string &description) +{ + for (int i = 0; i < dyePalateSize; ++i) + mDyePalettes[i] = nullptr; + + if (description.empty()) + return; + + size_t next_pos = 0; + const size_t length = description.length(); + do + { + const size_t pos = next_pos; + next_pos = description.find(';', pos); + + if (next_pos == std::string::npos) + next_pos = length; + + if (next_pos <= pos + 3 || description[pos + 1] != ':') + { + logger->log("Error, invalid dye: %s", description.c_str()); + return; + } + + int i = 0; + + switch (description[pos]) + { + case 'R': i = 0; break; + case 'G': i = 1; break; + case 'Y': i = 2; break; + case 'B': i = 3; break; + case 'M': i = 4; break; + case 'C': i = 5; break; + case 'W': i = 6; break; + case 'S': i = 7; break; + case 'A': i = 8; break; + default: + logger->log("Error, invalid dye: %s", description.c_str()); + return; + } + mDyePalettes[i] = new DyePalette(description.substr( + pos + 2, next_pos - pos - 2), i != 8 ? 6 : 8); + ++next_pos; + } + while (next_pos < length); +} + +Dye::~Dye() +{ + for (int i = 0; i < dyePalateSize; ++i) + delete2(mDyePalettes[i]) +} + +void Dye::instantiate(std::string &restrict target, + const std::string &restrict palettes) +{ + size_t next_pos = target.find('|'); + + if (next_pos == std::string::npos || palettes.empty()) + return; + + ++next_pos; + + std::ostringstream s; + s << target.substr(0, next_pos); + size_t last_pos = target.length(), pal_pos = 0; + do + { + const size_t pos = next_pos; + next_pos = target.find(';', pos); + + if (next_pos == std::string::npos) + next_pos = last_pos; + + if (next_pos == pos + 1 && pal_pos != std::string::npos) + { + const size_t pal_next_pos = palettes.find(';', pal_pos); + s << target[pos] << ':'; + if (pal_next_pos == std::string::npos) + { + s << palettes.substr(pal_pos); + s << target.substr(next_pos); + break; + } + s << palettes.substr(pal_pos, pal_next_pos - pal_pos); + pal_pos = pal_next_pos + 1; + } + else if (next_pos > pos + 2) + { + s << target.substr(pos, next_pos - pos); + } + else + { + logger->log("Error, invalid dye placeholder: %s", target.c_str()); + return; + } + s << target[next_pos]; + ++next_pos; + } + while (next_pos < last_pos); + + target = s.str(); +} + +int Dye::getType() const +{ + if (mDyePalettes[sPaleteIndex]) + return 1; + if (mDyePalettes[aPaleteIndex]) + return 2; + return 0; +} + +void Dye::normalDye(uint32_t *restrict pixels, const int bufSize) const +{ + if (!pixels) + return; + +#ifdef ENABLE_CILKPLUS + cilk_for (int ptr = 0; ptr < bufSize; ptr ++) + { + const uint32_t p = pixels[ptr]; +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + const int alpha = p & 0xff000000; +#else + const int alpha = p & 0xff; +#endif + if (alpha) + { + unsigned int color[3]; +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + color[0] = (p) & 255U; + color[1] = (p >> 8U) & 255U; + color[2] = (p >> 16U) & 255U; +#else + color[0] = (p >> 24U) & 255U; + color[1] = (p >> 16U) & 255U; + color[2] = (p >> 8U) & 255U; +#endif + const unsigned int cmax = std::max( + color[0], std::max(color[1], color[2])); + if (cmax == 0) + goto endlabel; + + const unsigned int cmin = std::min( + color[0], std::min(color[1], color[2])); + const unsigned int intensity = color[0] + color[1] + color[2]; + unsigned int i; + + if (cmin != cmax && (cmin != 0 || (intensity != cmax + && intensity != 2 * cmax))) + { + // not pure + goto endlabel; + } + + i = (color[0] != 0) | ((color[1] != 0) << 1) + | ((color[2] != 0) << 2); + + if (mDyePalettes[i - 1]) + mDyePalettes[i - 1]->getColor(cmax, color); + +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + pixels[ptr] = (color[0]) | (color[1] << 8) + | (color[2] << 16) | alpha; +#else + pixels[ptr] = (color[0] << 24) | (color[1] << 16) + | (color[2] << 8) | alpha; +#endif + } +endlabel:{} + } + +#else // ENABLE_CILKPLUS + + for (uint32_t *p_end = pixels + static_cast<size_t>(bufSize); + pixels != p_end; + ++ pixels) + { + const uint32_t p = *pixels; +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + const int alpha = p & 0xff000000; +#else + const int alpha = p & 0xff; +#endif + if (!alpha) + continue; + unsigned int color[3]; +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + color[0] = (p) & 255U; + color[1] = (p >> 8U) & 255U; + color[2] = (p >> 16U) & 255U; +#else + color[0] = (p >> 24U) & 255U; + color[1] = (p >> 16U) & 255U; + color[2] = (p >> 8U) & 255U; +#endif + + const unsigned int cmax = std::max( + color[0], std::max(color[1], color[2])); + if (cmax == 0) + continue; + + const unsigned int cmin = std::min( + color[0], std::min(color[1], color[2])); + const unsigned int intensity = color[0] + color[1] + color[2]; + + if (cmin != cmax && (cmin != 0 || (intensity != cmax + && intensity != 2 * cmax))) + { + // not pure + continue; + } + + const unsigned int i = (color[0] != 0) | ((color[1] != 0) << 1) + | ((color[2] != 0) << 2); + + if (mDyePalettes[i - 1]) + mDyePalettes[i - 1]->getColor(cmax, color); + +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + *pixels = (color[0]) | (color[1] << 8) + | (color[2] << 16) | alpha; +#else + *pixels = (color[0] << 24) | (color[1] << 16) + | (color[2] << 8) | alpha; +#endif + } +#endif // ENABLE_CILKPLUS +} + +void Dye::normalOGLDye(uint32_t *restrict pixels, const int bufSize) const +{ + if (!pixels) + return; + +#ifdef ENABLE_CILKPLUS + cilk_for (int ptr = 0; ptr < bufSize; ptr ++) + { + const uint32_t p = pixels[ptr]; +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + const int alpha = p & 255; +#else + const int alpha = p & 0xff000000; +#endif + if (alpha) + { + unsigned int color[3]; +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + color[0] = (p >> 24U) & 255U; + color[1] = (p >> 16U) & 255U; + color[2] = (p >> 8U) & 255U; +#else + color[0] = (p) & 255U; + color[1] = (p >> 8U) & 255U; + color[2] = (p >> 16U) & 255U; +#endif + + const unsigned int cmax = std::max( + color[0], std::max(color[1], color[2])); + if (cmax == 0) + goto endlabel; + + const unsigned int cmin = std::min( + color[0], std::min(color[1], color[2])); + const unsigned int intensity = color[0] + color[1] + color[2]; + + if (cmin != cmax && (cmin != 0 || (intensity != cmax + && intensity != 2 * cmax))) + { + // not pure + goto endlabel; + } + + const unsigned int i = (color[0] != 0) | ((color[1] != 0) << 1) + | ((color[2] != 0) << 2); + + if (mDyePalettes[i - 1]) + mDyePalettes[i - 1]->getColor(cmax, color); + +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + pixels[ptr] = (color[0] << 24) | (color[1] << 16) + | (color[2] << 8) | alpha; +#else + pixels[ptr] = (color[0]) | (color[1] << 8) + | (color[2] << 16) | alpha; +#endif + } +endlabel:{} + } + +#else // ENABLE_CILKPLUS + + for (uint32_t *p_end = pixels + static_cast<size_t>(bufSize); + pixels != p_end; + ++ pixels) + { + const uint32_t p = *pixels; +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + const int alpha = p & 255; +#else + const int alpha = p & 0xff000000; +#endif + if (!alpha) + continue; + unsigned int color[3]; +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + color[0] = (p >> 24U) & 255U; + color[1] = (p >> 16U) & 255U; + color[2] = (p >> 8U) & 255U; +#else + color[0] = (p) & 255U; + color[1] = (p >> 8U) & 255U; + color[2] = (p >> 16U) & 255U; +#endif + + const unsigned int cmax = std::max( + color[0], std::max(color[1], color[2])); + if (cmax == 0) + continue; + + const unsigned int cmin = std::min( + color[0], std::min(color[1], color[2])); + const unsigned int intensity = color[0] + color[1] + color[2]; + + if (cmin != cmax && (cmin != 0 || (intensity != cmax + && intensity != 2 * cmax))) + { + // not pure + continue; + } + + const unsigned int i = (color[0] != 0) | ((color[1] != 0) << 1) + | ((color[2] != 0) << 2); + + if (mDyePalettes[i - 1]) + mDyePalettes[i - 1]->getColor(cmax, color); + +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + *pixels = (color[0] << 24) | (color[1] << 16) + | (color[2] << 8) | alpha; +#else + *pixels = (color[0]) | (color[1] << 8) + | (color[2] << 16) | alpha; +#endif + } +#endif // ENABLE_CILKPLUS +} diff --git a/src/resources/dye/dye.h b/src/resources/dye/dye.h new file mode 100644 index 000000000..02e1b3f05 --- /dev/null +++ b/src/resources/dye/dye.h @@ -0,0 +1,93 @@ +/* + * The ManaPlus Client + * Copyright (C) 2007-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * Copyright (C) 2011-2015 The ManaPlus Developers + * + * This file is part of The ManaPlus Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef RESOURCES_DYE_DYE_H +#define RESOURCES_DYE_DYE_H + +#include <string> + +#include "localconsts.h" + +class DyePalette; + +const int dyePalateSize = 9; +const int sPaleteIndex = 7; +const int aPaleteIndex = 8; + +/** + * Class for dispatching pixel-recoloring amongst several palettes. + */ +class Dye final +{ + public: + /** + * Creates a set of palettes based on the given string. + * + * The parts of string are separated by semi-colons. Each part starts + * by an uppercase letter, followed by a colon and then a palette name. + */ + explicit Dye(const std::string &dye); + + A_DELETE_COPY(Dye) + + /** + * Destroys the associated palettes. + */ + ~Dye(); + + /** + * Fills the blank in a dye placeholder with some palette names. + */ + static void instantiate(std::string &restrict target, + const std::string &restrict palettes); + + /** + * Return special dye palete (S) + */ + const DyePalette *getSPalete() const A_WARN_UNUSED + { return mDyePalettes[sPaleteIndex]; } + + /** + * Return special dye palete (A) + */ + const DyePalette *getAPalete() const A_WARN_UNUSED + { return mDyePalettes[aPaleteIndex]; } + + /** + * Return dye type for S - 1, for A - 2, 0 for other + */ + int getType() const A_WARN_UNUSED; + + void normalDye(uint32_t *restrict pixels, const int bufSize) const; + + void normalOGLDye(uint32_t *restrict pixels, const int bufSize) const; + + private: + /** + * The order of the palettes, as well as their uppercase letter, is: + * + * Red, Green, Yellow, Blue, Magenta, White (or rather gray), Simple. + */ + DyePalette *mDyePalettes[dyePalateSize]; +}; + +#endif // RESOURCES_DYE_DYE_H diff --git a/src/resources/dye/dye_unittest.cc b/src/resources/dye/dye_unittest.cc new file mode 100644 index 000000000..1ee109ffa --- /dev/null +++ b/src/resources/dye/dye_unittest.cc @@ -0,0 +1,254 @@ +/* + * The ManaPlus Client + * Copyright (C) 2013 The ManaPlus Developers + * + * This file is part of The ManaPlus Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "catch.hpp" + +#include "resources/dye/dye.h" +#include "resources/dye/dyepalette.h" + +#include "debug.h" + +TEST_CASE("Dye replaceSOGLColor 1") +{ + DyePalette palette("#00ff00,000011", 6); + uint8_t data[4]; + data[0] = 0x01; + data[1] = 0x02; + data[2] = 0x03; + data[3] = 0x10; + palette.replaceSOGLColor(reinterpret_cast<uint32_t*>(&data[0]), 1); + REQUIRE(0x01 == data[0]); + REQUIRE(0x02 == data[1]); + REQUIRE(0x03 == data[2]); + REQUIRE(0x10 == data[3]); +} + +TEST_CASE("Dye replaceSOGLColor 2") +{ + DyePalette palette("#01ff02,030411", 6); + uint8_t data[4]; + data[0] = 0x01; + data[1] = 0xff; + data[2] = 0x02; + data[3] = 0x20; + palette.replaceSOGLColor(reinterpret_cast<uint32_t*>(&data[0]), 1); + REQUIRE(0x03 == data[0]); + REQUIRE(0x04 == data[1]); + REQUIRE(0x11 == data[2]); + REQUIRE(0x20 == data[3]); +} + +TEST_CASE("Dye replaceSOGLColor 3") +{ + DyePalette palette("#404040,200000,0100ee,102030", 6); + uint8_t data[4]; + data[0] = 0x01; + data[1] = 0x00; + data[2] = 0xee; + data[3] = 0x40; + palette.replaceSOGLColor(reinterpret_cast<uint32_t*>(&data[0]), 1); + REQUIRE(0x10 == data[0]); + REQUIRE(0x20 == data[1]); + REQUIRE(0x30 == data[2]); + REQUIRE(0x40 == data[3]); +} + + +TEST_CASE("Dye replaceAOGLColor 1") +{ + DyePalette palette("#00ff0010,00001120", 8); + uint8_t data[4]; + data[0] = 0x01; + data[1] = 0x02; + data[2] = 0x03; + data[3] = 0x10; + palette.replaceAOGLColor(reinterpret_cast<uint32_t*>(&data[0]), 1); + REQUIRE(0x01 == data[0]); + REQUIRE(0x02 == data[1]); + REQUIRE(0x03 == data[2]); + REQUIRE(0x10 == data[3]); +} + +TEST_CASE("Dye replaceAOGLColor 2") +{ + DyePalette palette("#00ff0120,020311ff", 8); + uint8_t data[4]; + data[0] = 0x00; + data[1] = 0xff; + data[2] = 0x01; + data[3] = 0x20; + palette.replaceAOGLColor(reinterpret_cast<uint32_t*>(&data[0]), 1); + REQUIRE(0x02 == data[0]); + REQUIRE(0x03 == data[1]); + REQUIRE(0x11 == data[2]); + REQUIRE(0xff == data[3]); +} + +TEST_CASE("Dye replaceAOGLColor 3") +{ + DyePalette palette("#40404040,20000000,0100ee40,102030ff", 8); + uint8_t data[4]; + data[0] = 0x01; + data[1] = 0x00; + data[2] = 0xee; + data[3] = 0x40; + palette.replaceAOGLColor(reinterpret_cast<uint32_t*>(&data[0]), 1); + REQUIRE(0x10 == data[0]); + REQUIRE(0x20 == data[1]); + REQUIRE(0x30 == data[2]); + REQUIRE(0xff == data[3]); +} + + +TEST_CASE("Dye replaceSColor 1") +{ + DyePalette palette("#00ff00,000011", 6); + uint8_t data[4]; + data[0] = 0x01; + data[1] = 0x02; + data[2] = 0x03; + data[3] = 0x10; + palette.replaceSColor(reinterpret_cast<uint32_t*>(&data[0]), 1); + REQUIRE(0x01 == data[0]); + REQUIRE(0x02 == data[1]); + REQUIRE(0x03 == data[2]); + REQUIRE(0x10 == data[3]); +} + +TEST_CASE("Dye replaceSColor 2") +{ + DyePalette palette("#403020,706050", 6); + uint8_t data[4]; + data[0] = 0x10; + data[1] = 0x20; + data[2] = 0x30; + data[3] = 0x40; + palette.replaceSColor(reinterpret_cast<uint32_t*>(&data[0]), 1); + REQUIRE(0x10 == data[0]); + REQUIRE(0x50 == data[1]); + REQUIRE(0x60 == data[2]); + REQUIRE(0x70 == data[3]); +} + +TEST_CASE("Dye replaceSColor 3") +{ + DyePalette palette("#123456,000000,ff3020,706050", 6); + uint8_t data[4]; + data[0] = 0x10; + data[1] = 0x20; + data[2] = 0x30; + data[3] = 0xff; + palette.replaceSColor(reinterpret_cast<uint32_t*>(&data[0]), 1); + REQUIRE(0x10 == data[0]); + REQUIRE(0x50 == data[1]); + REQUIRE(0x60 == data[2]); + REQUIRE(0x70 == data[3]); +} + + +TEST_CASE("Dye replaceAColor 1") +{ + DyePalette palette("#00ff0010,00001120", 8); + uint8_t data[4]; + data[0] = 0x01; + data[1] = 0x02; + data[2] = 0x03; + data[3] = 0x10; + palette.replaceAColor(reinterpret_cast<uint32_t*>(&data[0]), 1); + REQUIRE(0x01 == data[0]); + REQUIRE(0x02 == data[1]); + REQUIRE(0x03 == data[2]); + REQUIRE(0x10 == data[3]); +} + +TEST_CASE("Dye replaceAColor 2") +{ + DyePalette palette("#02ff0120,040311ff", 8); + uint8_t data[4]; + data[0] = 0x20; + data[1] = 0x01; + data[2] = 0xff; + data[3] = 0x02; + palette.replaceAColor(reinterpret_cast<uint32_t*>(&data[0]), 1); + REQUIRE(0xff == data[0]); + REQUIRE(0x11 == data[1]); + REQUIRE(0x03 == data[2]); + REQUIRE(0x04 == data[3]); +} + +TEST_CASE("Dye replaceAColor 3") +{ + DyePalette palette("#40404040,20000000,0100ee40,102030ff", 8); + uint8_t data[4]; + data[0] = 0x40; + data[1] = 0xee; + data[2] = 0x00; + data[3] = 0x01; + palette.replaceAColor(reinterpret_cast<uint32_t*>(&data[0]), 1); + REQUIRE(0xff == data[0]); + REQUIRE(0x30 == data[1]); + REQUIRE(0x20 == data[2]); + REQUIRE(0x10 == data[3]); +} + +TEST_CASE("Dye normalDye 1") +{ + Dye dye("R:#203040,506070"); + uint8_t data[4]; + data[0] = 0x55; + data[1] = 0x00; + data[2] = 0x00; + data[3] = 0x50; + dye.normalDye(reinterpret_cast<uint32_t*>(&data[0]), 1); + REQUIRE(0x55 == data[0]); + REQUIRE(0x28 == data[1]); + REQUIRE(0x1e == data[2]); + REQUIRE(0x14 == data[3]); +} + +TEST_CASE("Dye normalDye 2") +{ + Dye dye("G:#203040,506070"); + uint8_t data[4]; + data[0] = 0x60; + data[1] = 0x00; + data[2] = 0x50; + data[3] = 0x00; + dye.normalDye(reinterpret_cast<uint32_t*>(&data[0]), 1); + REQUIRE(0x60 == data[0]); + REQUIRE(0x28 == data[1]); + REQUIRE(0x1e == data[2]); + REQUIRE(0x14 == data[3]); +} + +TEST_CASE("Dye normalOGLDye 1") +{ + Dye dye("R:#203040,506070"); + uint8_t data[4]; + data[0] = 0x55; + data[1] = 0x00; + data[2] = 0x00; + data[3] = 0x50; + dye.normalOGLDye(reinterpret_cast<uint32_t*>(&data[0]), 1); + REQUIRE(0x15 == data[0]); + REQUIRE(0x20 == data[1]); + REQUIRE(0x2a == data[2]); + REQUIRE(0x50 == data[3]); +} diff --git a/src/resources/dye/dyecolor.h b/src/resources/dye/dyecolor.h new file mode 100644 index 000000000..4e8864c66 --- /dev/null +++ b/src/resources/dye/dyecolor.h @@ -0,0 +1,53 @@ +/* + * The ManaPlus Client + * Copyright (C) 2013-2015 The ManaPlus Developers + * + * This file is part of The ManaPlus Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef RESOURCES_DYE_DYECOLOR_H +#define RESOURCES_DYE_DYECOLOR_H + +#include "localconsts.h" + +struct DyeColor final +{ + DyeColor() + { + value[3] = 255; + } + + DyeColor(const uint8_t r, const uint8_t g, const uint8_t b) + { + value[0] = r; + value[1] = g; + value[2] = b; + value[3] = 255; + } + + DyeColor(const uint8_t r, const uint8_t g, const uint8_t b, + const uint8_t a) + { + value[0] = r; + value[1] = g; + value[2] = b; + value[3] = a; + } + + uint8_t value[4]; +}; + +#endif // RESOURCES_DYE_DYECOLOR_H diff --git a/src/resources/dye/dyepalette.cpp b/src/resources/dye/dyepalette.cpp new file mode 100644 index 000000000..2dd71f2ec --- /dev/null +++ b/src/resources/dye/dyepalette.cpp @@ -0,0 +1,586 @@ +/* + * The ManaPlus Client + * Copyright (C) 2007-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * Copyright (C) 2011-2015 The ManaPlus Developers + * + * This file is part of The ManaPlus Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "resources/dye/dyepalette.h" + +#include "logger.h" + +#ifndef DYECMD +#include "resources/db/palettedb.h" +#endif + +#include <cmath> + +#include <SDL_endian.h> + +#include "debug.h" + +DyePalette::DyePalette(const std::string &description, + const uint8_t blockSize) : + mColors() +{ + const size_t size = static_cast<size_t>(description.length()); + if (size == 0) + return; + + if (description[0] == '#') + { + size_t pos = 1; + for ( ; ; ) + { + if (pos + blockSize > size) + break; + + DyeColor color(0, 0, 0, 0); + + for (size_t i = 0, colorIdx = 0; i < blockSize && colorIdx < 4; + i += 2, colorIdx ++) + { + color.value[colorIdx] = static_cast<unsigned char>(( + hexDecode(description[pos + i]) << 4) + + hexDecode(description[pos + i + 1])); + } + mColors.push_back(color); + pos += blockSize; + + if (pos == size) + return; + if (description[pos] != ',') + break; + + ++pos; + } + } +#ifndef DYECMD + else if (description[0] == '@') + { + size_t pos = 1; + for ( ; pos < size ; ) + { + const size_t idx = description.find(',', pos); + if (idx == std::string::npos) + return; + if (idx == pos) + break; + mColors.push_back(PaletteDB::getColor( + description.substr(pos, idx - pos))); + pos = idx + 1; + } + } +#endif + logger->log("Error, invalid embedded palette: %s", description.c_str()); +} + +unsigned int DyePalette::hexDecode(const signed char c) +{ + if ('0' <= c && c <= '9') + return c - '0'; + else if ('A' <= c && c <= 'F') + return c - 'A' + 10; + else if ('a' <= c && c <= 'f') + return c - 'a' + 10; + else + return 0; +} + +void DyePalette::getColor(const unsigned int intensity, + unsigned int (&color)[3]) const +{ + if (intensity == 0) + { + color[0] = 0; + color[1] = 0; + color[2] = 0; + return; + } + + const int last = static_cast<int>(mColors.size()); + if (last == 0) + return; + + const int intLast = intensity * last; + const int i = intLast / 255; + const int t = intLast % 255; + + int j = t != 0 ? i : i - 1; + + if (j >= last) + j = 0; + + // Get the exact color if any, the next color otherwise. + const DyeColor &colorJ = mColors[j]; + const int r2 = colorJ.value[0]; + const int g2 = colorJ.value[1]; + const int b2 = colorJ.value[2]; + + if (t == 0) + { + // Exact color. + color[0] = r2; + color[1] = g2; + color[2] = b2; + return; + } + + // Get the previous color. First color is implicitly black. + if (i > 0 && i < last + 1) + { + const DyeColor &colorI = mColors[i - 1]; + const int t2 = 255 - t; + // Perform a linear interpolation. + color[0] = (t2 * colorI.value[0] + t * r2) / 255; + color[1] = (t2 * colorI.value[1] + t * g2) / 255; + color[2] = (t2 * colorI.value[2] + t * b2) / 255; + } + else + { + // Perform a linear interpolation. + color[0] = (t * r2) / 255; + color[1] = (t * g2) / 255; + color[2] = (t * b2) / 255; + } +} + +void DyePalette::getColor(double intensity, int (&color)[3]) const +{ + // Nothing to do here + if (mColors.empty()) + return; + + // Force range + if (intensity > 1.0) + intensity = 1.0; + else if (intensity < 0.0) + intensity = 0.0; + + // Scale up + intensity *= static_cast<double>(mColors.size() - 1); + + // Color indices + const int i = static_cast<int>(floor(intensity)); + const int j = static_cast<int>(ceil(intensity)); + const DyeColor &colorI = mColors[i]; + + if (i == j) + { + // Exact color. + color[0] = colorI.value[0]; + color[1] = colorI.value[1]; + color[2] = colorI.value[2]; + return; + } + + intensity -= i; + const double rest = 1 - intensity; + const DyeColor &colorJ = mColors[j]; + + // Perform the interpolation. + color[0] = static_cast<int>(rest * colorI.value[0] + + intensity * colorJ.value[0]); + color[1] = static_cast<int>(rest * colorI.value[1] + + intensity * colorJ.value[1]); + color[2] = static_cast<int>(rest * colorI.value[2] + + intensity * colorJ.value[2]); +} + +void DyePalette::replaceSColor(uint32_t *restrict pixels, + const int bufSize) const +{ + std::vector<DyeColor>::const_iterator it_end = mColors.end(); + const size_t sz = mColors.size(); + if (!sz || !pixels) + return; + if (sz % 2) + -- it_end; + +#ifdef ENABLE_CILKPLUS + cilk_for (int ptr = 0; ptr < bufSize; ptr ++) + { + uint8_t *const p = reinterpret_cast<uint8_t *>(&pixels[ptr]); +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + const int alpha = pixels[ptr] & 0xff000000; + const unsigned int data = pixels[ptr] & 0x00ffffff; +#else + const int alpha = *p & 0xff; + const unsigned int data = pixels[ptr] & 0xffffff00; +#endif +// logger->log("c:%04d %08x", c, *pixels); +// logger->log("data: %08x", data); + if (!alpha) + { +// logger->log("skip: %08x", *pixels); + } + else + { + std::vector<DyeColor>::const_iterator it = mColors.begin(); + while (it != it_end) + { + const DyeColor &col = *it; + ++ it; + const DyeColor &col2 = *it; + +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + const unsigned int coldata = (col.value[2] << 16U) + | (col.value[1] << 8U) | (col.value[0]); +#else + const unsigned int coldata = (col.value[2] << 8U) + | (col.value[1] << 16U) | (col.value[0] << 24U); +#endif +// logger->log("coldata: %08x", coldata); + if (data == coldata) + { +// logger->log("correct"); + p[3] = col2.value[0]; + p[2] = col2.value[1]; + p[1] = col2.value[2]; + break; + } + ++ it; + } + } + } +#else // ENABLE_CILKPLUS + + for (uint32_t *p_end = pixels + static_cast<size_t>(bufSize); + pixels != p_end; + ++ pixels) + { + uint8_t *const p = reinterpret_cast<uint8_t *>(pixels); +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + const int alpha = *pixels & 0xff000000; + const unsigned int data = (*pixels) & 0x00ffffff; +#else + const int alpha = *p & 0xff; + const unsigned int data = (*pixels) & 0xffffff00; +#endif +// logger->log("c:%04d %08x", c, *pixels); +// logger->log("data: %08x", data); + if (!alpha) + { +// logger->log("skip: %08x", *pixels); + continue; + } + + std::vector<DyeColor>::const_iterator it = mColors.begin(); + while (it != it_end) + { + const DyeColor &col = *it; + ++ it; + const DyeColor &col2 = *it; + +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + const unsigned int coldata = (col.value[2] << 16U) + | (col.value[1] << 8U) | (col.value[0]); +#else + const unsigned int coldata = (col.value[2] << 8U) + | (col.value[1] << 16U) | (col.value[0] << 24U); +#endif +// logger->log("coldata: %08x", coldata); + if (data == coldata) + { +// logger->log("correct"); + p[3] = col2.value[0]; + p[2] = col2.value[1]; + p[1] = col2.value[2]; + break; + } + + ++ it; + } + } +#endif // ENABLE_CILKPLUS +} + +void DyePalette::replaceAColor(uint32_t *restrict pixels, + const int bufSize) const +{ + std::vector<DyeColor>::const_iterator it_end = mColors.end(); + const size_t sz = mColors.size(); + if (!sz || !pixels) + return; + if (sz % 2) + -- it_end; + +#ifdef ENABLE_CILKPLUS + cilk_for (int ptr = 0; ptr < bufSize; ptr ++) + { + uint8_t *const p = reinterpret_cast<uint8_t *>(&pixels[ptr]); + const unsigned int data = pixels[ptr]; + + std::vector<DyeColor>::const_iterator it = mColors.begin(); + while (it != it_end) + { + const DyeColor &col = *it; + ++ it; + const DyeColor &col2 = *it; + +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + const unsigned int coldata = (col.value[3] << 24U) + | (col.value[2] << 16U) + | (col.value[1] << 8U) + | (col.value[0]); +#else + const unsigned int coldata = (col.value[3]) + | (col.value[2] << 8U) + | (col.value[1] << 16U) | + (col.value[0] << 24U); +#endif + + if (data == coldata) + { + p[3] = col2.value[0]; + p[2] = col2.value[1]; + p[1] = col2.value[2]; + p[0] = col2.value[3]; + break; + } + + ++ it; + } + } + +#else // ENABLE_CILKPLUS + + for (uint32_t *p_end = pixels + static_cast<size_t>(bufSize); + pixels != p_end; + ++pixels) + { + uint8_t *const p = reinterpret_cast<uint8_t *>(pixels); + const unsigned int data = *pixels; + + std::vector<DyeColor>::const_iterator it = mColors.begin(); + while (it != it_end) + { + const DyeColor &col = *it; + ++ it; + const DyeColor &col2 = *it; + +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + const unsigned int coldata = (col.value[3] << 24U) + | (col.value[2] << 16U) + | (col.value[1] << 8U) + | (col.value[0]); +#else + const unsigned int coldata = (col.value[3]) + | (col.value[2] << 8U) + | (col.value[1] << 16U) | + (col.value[0] << 24U); +#endif + + if (data == coldata) + { + p[3] = col2.value[0]; + p[2] = col2.value[1]; + p[1] = col2.value[2]; + p[0] = col2.value[3]; + break; + } + + ++ it; + } + } +#endif // ENABLE_CILKPLUS +} + +void DyePalette::replaceSOGLColor(uint32_t *restrict pixels, + const int bufSize) const +{ + std::vector<DyeColor>::const_iterator it_end = mColors.end(); + const size_t sz = mColors.size(); + if (!sz || !pixels) + return; + if (sz % 2) + -- it_end; + +#ifdef ENABLE_CILKPLUS + cilk_for (int ptr = 0; ptr < bufSize; ptr ++) + { + uint8_t *const p = reinterpret_cast<uint8_t *>(&pixels[ptr]); +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + const int alpha = *p & 0xff; + const unsigned int data = (pixels[ptr]) & 0xffffff00; +#else + const int alpha = pixels[ptr] & 0xff000000; + const unsigned int data = (pixels[ptr]) & 0x00ffffff; +#endif + if (alpha) + { + std::vector<DyeColor>::const_iterator it = mColors.begin(); + while (it != it_end) + { + const DyeColor &col = *it; + ++ it; + const DyeColor &col2 = *it; + +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + const unsigned int coldata = (col.value[0] << 24) + | (col.value[1] << 16) | (col.value[2] << 8); +#else + const unsigned int coldata = (col.value[0]) + | (col.value[1] << 8) | (col.value[2] << 16); +#endif + if (data == coldata) + { + p[0] = col2.value[0]; + p[1] = col2.value[1]; + p[2] = col2.value[2]; + break; + } + + ++ it; + } + } + } + +#else // ENABLE_CILKPLUS + + for (uint32_t *p_end = pixels + static_cast<size_t>(bufSize); + pixels != p_end; + ++pixels) + { + uint8_t *const p = reinterpret_cast<uint8_t *>(pixels); +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + const int alpha = *p & 0xff; + const unsigned int data = (*pixels) & 0xffffff00; +#else + const int alpha = *pixels & 0xff000000; + const unsigned int data = (*pixels) & 0x00ffffff; +#endif + if (!alpha) + continue; + + std::vector<DyeColor>::const_iterator it = mColors.begin(); + while (it != it_end) + { + const DyeColor &col = *it; + ++ it; + const DyeColor &col2 = *it; + +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + const unsigned int coldata = (col.value[0] << 24) + | (col.value[1] << 16) | (col.value[2] << 8); +#else + const unsigned int coldata = (col.value[0]) + | (col.value[1] << 8) | (col.value[2] << 16); +#endif + if (data == coldata) + { + p[0] = col2.value[0]; + p[1] = col2.value[1]; + p[2] = col2.value[2]; + break; + } + + ++ it; + } + } +#endif // ENABLE_CILKPLUS +} + +void DyePalette::replaceAOGLColor(uint32_t *restrict pixels, + const int bufSize) const +{ + std::vector<DyeColor>::const_iterator it_end = mColors.end(); + const size_t sz = mColors.size(); + if (!sz || !pixels) + return; + if (sz % 2) + -- it_end; + +#ifdef ENABLE_CILKPLUS + cilk_for (int ptr = 0; ptr < bufSize; ptr ++) + { + uint8_t *const p = reinterpret_cast<uint8_t *>(&pixels[ptr]); + const unsigned int data = pixels[ptr]; + + std::vector<DyeColor>::const_iterator it = mColors.begin(); + while (it != it_end) + { + const DyeColor &col = *it; + ++ it; + const DyeColor &col2 = *it; + +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + const unsigned int coldata = (col.value[0] << 24U) + | (col.value[1] << 16U) + | (col.value[2] << 8U) + | col.value[3]; +#else + const unsigned int coldata = (col.value[0]) + | (col.value[1] << 8U) + | (col.value[2] << 16U) + | (col.value[3] << 24U); +#endif + if (data == coldata) + { + p[0] = col2.value[0]; + p[1] = col2.value[1]; + p[2] = col2.value[2]; + p[3] = col2.value[3]; + break; + } + + ++ it; + } + } + +#else // ENABLE_CILKPLUS + + for (uint32_t *p_end = pixels + static_cast<size_t>(bufSize); + pixels != p_end; + ++pixels) + { + uint8_t *const p = reinterpret_cast<uint8_t *>(pixels); + const unsigned int data = *pixels; + + std::vector<DyeColor>::const_iterator it = mColors.begin(); + while (it != it_end) + { + const DyeColor &col = *it; + ++ it; + const DyeColor &col2 = *it; + +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + const unsigned int coldata = (col.value[0] << 24U) + | (col.value[1] << 16U) + | (col.value[2] << 8U) + | col.value[3]; +#else + const unsigned int coldata = (col.value[0]) + | (col.value[1] << 8U) + | (col.value[2] << 16U) + | (col.value[3] << 24U); +#endif + if (data == coldata) + { + p[0] = col2.value[0]; + p[1] = col2.value[1]; + p[2] = col2.value[2]; + p[3] = col2.value[3]; + break; + } + + ++ it; + } + } +#endif // ENABLE_CILKPLUS +} diff --git a/src/resources/dye/dyepalette.h b/src/resources/dye/dyepalette.h new file mode 100644 index 000000000..33d16fa50 --- /dev/null +++ b/src/resources/dye/dyepalette.h @@ -0,0 +1,88 @@ +/* + * The ManaPlus Client + * Copyright (C) 2007-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * Copyright (C) 2011-2015 The ManaPlus Developers + * + * This file is part of The ManaPlus Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef RESOURCES_DYE_DYEPALETTE_H +#define RESOURCES_DYE_DYEPALETTE_H + +#include "resources/dye/dyecolor.h" + +#include <string> +#include <vector> + +#include "localconsts.h" + +/** + * Class for performing a linear interpolation between colors. + */ +class DyePalette final +{ + public: + /** + * Creates a palette based on the given string. + * The string is either a file name or a sequence of hexadecimal RGB + * values separated by ',' and starting with '#'. + */ + DyePalette(const std::string &pallete, const uint8_t blockSize); + + A_DELETE_COPY(DyePalette) + + /** + * Gets a pixel color depending on its intensity. First color is + * implicitly black (0, 0, 0). + */ + void getColor(const unsigned int intensity, + unsigned int (&color)[3]) const; + + /** + * Gets a pixel color depending on its intensity. + */ + void getColor(double intensity, int (&color)[3]) const; + + /** + * replace colors for SDL for S dye. + */ + void replaceSColor(uint32_t *restrict pixels, const int bufSize) const; + + /** + * replace colors for SDL for S dye. + */ + void replaceAColor(uint32_t *restrict pixels, const int bufSize) const; + + /** + * replace colors for OpenGL for S dye. + */ + void replaceSOGLColor(uint32_t *restrict pixels, + const int bufSize) const; + + /** + * replace colors for OpenGL for A dye. + */ + void replaceAOGLColor(uint32_t *restrict pixels, + const int bufSize) const; + + static unsigned int hexDecode(const signed char c) A_WARN_UNUSED; + + private: + std::vector<DyeColor> mColors; +}; + +#endif // RESOURCES_DYE_DYEPALETTE_H |