diff options
author | Bjørn Lindeijer <bjorn@lindeijer.nl> | 2007-11-21 19:43:11 +0000 |
---|---|---|
committer | Bjørn Lindeijer <bjorn@lindeijer.nl> | 2007-11-21 19:43:11 +0000 |
commit | 7c7909350565c3506f8b24645cac10f3197e1fc5 (patch) | |
tree | 748ce81596f924d81cb7b6fdba84fe22a054e305 | |
parent | 434d0bd000576b534c51f611c09eb99b3863e1c5 (diff) | |
download | mana-7c7909350565c3506f8b24645cac10f3197e1fc5.tar.gz mana-7c7909350565c3506f8b24645cac10f3197e1fc5.tar.bz2 mana-7c7909350565c3506f8b24645cac10f3197e1fc5.tar.xz mana-7c7909350565c3506f8b24645cac10f3197e1fc5.zip |
Merged revisions 3705-3711,3714,3718,3721-3722,3729-3731,3735,3742 via svnmerge from
https://themanaworld.svn.sourceforge.net/svnroot/themanaworld/tmw/trunk
(dynamic recoloring of sprites and related changes)
........
r3705 | gmelquio | 2007-11-03 10:58:25 +0100 (Sat, 03 Nov 2007) | 1 line
Fixed double load of hair graphics.
........
r3706 | gmelquio | 2007-11-03 22:04:51 +0100 (Sat, 03 Nov 2007) | 1 line
Added automatic recoloring of images. Inspired by fungos' ideas (PR #41).
........
r3707 | gmelquio | 2007-11-03 22:08:21 +0100 (Sat, 03 Nov 2007) | 1 line
Experimented recoloring on scorpions.
........
r3708 | gmelquio | 2007-11-04 12:52:44 +0100 (Sun, 04 Nov 2007) | 1 line
Tightened palette handling.
........
r3709 | gmelquio | 2007-11-04 12:54:31 +0100 (Sun, 04 Nov 2007) | 1 line
Experimented with scorpions again.
........
r3710 | gmelquio | 2007-11-04 16:40:37 +0100 (Sun, 04 Nov 2007) | 1 line
Applied recoloring to hair styles.
........
r3711 | gmelquio | 2007-11-04 17:50:37 +0100 (Sun, 04 Nov 2007) | 1 line
Converted slimes to recoloring.
........
r3742 | gmelquio | 2007-11-16 14:16:00 +0100 (Fri, 16 Nov 2007) | 1 line
Sped up recoloring of transparent pixels.
........
-rw-r--r-- | ChangeLog | 50 | ||||
-rw-r--r-- | src/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/Makefile.am | 4 | ||||
-rw-r--r-- | src/being.cpp | 19 | ||||
-rw-r--r-- | src/being.h | 24 | ||||
-rw-r--r-- | src/gui/char_select.cpp | 13 | ||||
-rw-r--r-- | src/net/beinghandler.cpp | 15 | ||||
-rw-r--r-- | src/net/charserverhandler.cpp | 5 | ||||
-rw-r--r-- | src/player.cpp | 69 | ||||
-rw-r--r-- | src/player.h | 12 | ||||
-rw-r--r-- | src/resources/dye.cpp | 222 | ||||
-rw-r--r-- | src/resources/dye.h | 96 | ||||
-rw-r--r-- | src/resources/image.cpp | 47 | ||||
-rw-r--r-- | src/resources/image.h | 16 | ||||
-rw-r--r-- | src/resources/resourcemanager.cpp | 43 | ||||
-rw-r--r-- | src/resources/resourcemanager.h | 7 | ||||
-rw-r--r-- | src/resources/spritedef.cpp | 25 | ||||
-rw-r--r-- | src/resources/spritedef.h | 3 | ||||
-rw-r--r-- | src/utils/strprintf.cpp | 53 | ||||
-rw-r--r-- | src/utils/strprintf.h | 37 |
20 files changed, 646 insertions, 116 deletions
@@ -5,6 +5,10 @@ data/graphics/maps/new_15-1.tmx: Updated desert maps with new tiles by Len. +2007-11-16 Guillaume Melquiond <guillaume.melquiond@gmail.com> + + * src/resources/image.cpp: Sped up recoloring of transparent pixels. + 2007-11-16 Eugenio Favalli <elvenprogrammer@gmail.com> * src/log.cpp, src/utils/wingettimeofday.h: Removed useless win32 @@ -31,6 +35,46 @@ data/graphics/sprites/chest-vnecksweater-female: Modifications at the back shading of shirt sprites by Pauan. +2007-11-04 Guillaume Melquiond <guillaume.melquiond@gmail.com> + + * src/resources/dye.cpp, src/resources/resourcemanager.cpp: Moved + palette qualifiers to the right as the code appends things on the left. + * src/resources/spritedef.cpp, src/resources/spritedef.h, + src/resources/resourcemanager.h: Added propagation of palettes through + sprite resource names. + * data/monsters.xml, data/graphics/sprites/monster-scorpion.xml: Used + palettes on sprite names. + * data/graphics/sprites/monster-scorpion-black.xml, + data/graphics/sprites/monster-scorpion-red.xml: Removed obsolete files. + * src/player.cpp: Added hair colors. + * data/graphics/sprites/hairstyle*.xml: Removed sprite variants. Set + replaceable color. + * data/graphics/sprites/hairstyle*.png: Reduced image sizes. + * data/monsters.xml, data/graphics/sprites/monster-slime.xml, + data/graphics/sprites/monster-slime.png: Added generic slime. + * data/graphics/sprites/monster-slime-{green,red,yellow}.png, + data/graphics/sprites/monster-slime-{green,red,yellow}.xml: Removed + obsolete files. + +2007-11-03 Guillaume Melquiond <guillaume.melquiond@gmail.com> + + * src/player.cpp, src/player.h, src/gui/char_select.cpp: Factored code. + * src/net/beinghandler.cpp, src/net/charserverhandler.cpp: Fixed double + load of hair graphics. + * src/Makefile.am, src/resources/dye.cpp, src/resources/dye.h: Added + palette holder and linear interpolator of colors. + * src/resources/image.h, src/resources/image.cpp: Added palette-based + recoloring of images. + * src/resources/resourcemanager.h, src/resources/resourcemanager.cpp: + Added automatic recoloring of images depending on their names. + * data/graphics/sprites/monster-scorpion.png: Flattened colors. + * data/graphics/sprites/monster-scorpion-black.png, + data/graphics/sprites/monster-scorpion-red.png: Removed obsolete files. + * data/graphics/sprites/monster-scorpion.xml, + data/graphics/sprites/monster-scorpion-black.xml, + data/graphics/sprites/monster-scorpion-red.xml: Added automatic + recoloring of scorpions. + 2007-11-01 Philipp Sehmisch <tmw@crushnet.org> * data/graphics/particles/aniblaze.png, @@ -465,6 +509,12 @@ new woodland village. * data/items.xml: Tweaked description and values of the scythe. +2007-08-06 Guillaume Melquiond <guillaume.melquiond@gmail.com> + + * src/Makefile.am, src/utils/strprintf.h, src/utils/strprintf.cpp: + Added a helper function for printf-formatting a (gettext) C string + into a dynamically-sized C++ string. + 2007-08-05 Philipp Sehmisch <tmw@crushnet.org> * data/graphics/tiles/woodland_indoor.png, diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 08a3b2a1..396a1101 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -258,6 +258,8 @@ SET(SRCS utils/dtor.h utils/fastsqrt.h utils/minmax.h + utils/strprintf.cpp + utils/strprintf.h utils/tostring.h utils/trim.h utils/xml.cpp diff --git a/src/Makefile.am b/src/Makefile.am index 1a121367..f9e955c6 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -173,6 +173,8 @@ tmw_SOURCES = gui/widgets/resizegrip.cpp \ resources/ambientoverlay.h \ resources/animation.cpp \ resources/animation.h \ + resources/dye.cpp \ + resources/dye.h \ resources/image.cpp \ resources/image.h \ resources/imageloader.cpp \ @@ -208,6 +210,8 @@ tmw_SOURCES = gui/widgets/resizegrip.cpp \ utils/dtor.h \ utils/fastsqrt.h \ utils/minmax.h \ + utils/strprintf.cpp \ + utils/strprintf.h \ utils/tostring.h \ utils/trim.h \ utils/xml.cpp \ diff --git a/src/being.cpp b/src/being.cpp index 73c9ed62..a9dfa93c 100644 --- a/src/being.cpp +++ b/src/being.cpp @@ -127,23 +127,10 @@ Being::setPath(const Path &path) } void -Being::setHairColor(Uint16 color) +Being::setHairStyle(int style, int color) { - mHairColor = color; - if (mHairColor < 1 || mHairColor > NR_HAIR_COLORS + 1) - { - mHairColor = 1; - } -} - -void -Being::setHairStyle(Uint16 style) -{ - mHairStyle = style; - if (mHairStyle < 1 || mHairStyle > NR_HAIR_STYLES) - { - mHairStyle = 1; - } + mHairStyle = style < 0 ? mHairStyle : style % NR_HAIR_STYLES; + mHairColor = color < 0 ? mHairColor : color % NR_HAIR_COLORS; } void diff --git a/src/being.h b/src/being.h index 6c123377..2770dd6b 100644 --- a/src/being.h +++ b/src/being.h @@ -33,7 +33,7 @@ #include "map.h" #include "animatedsprite.h" -#define NR_HAIR_STYLES 7 +#define NR_HAIR_STYLES 8 #define NR_HAIR_COLORS 10 class AnimatedSprite; @@ -183,28 +183,22 @@ class Being : public Sprite setName(const std::string &name) { mName = name; } /** - * Sets the hair color for this being. - */ - virtual void - setHairColor(Uint16 color); - - /** * Gets the hair color for this being. */ - Uint16 - getHairColor() const { return mHairColor; } + int getHairColor() const + { return mHairColor; } /** - * Sets the hair style for this being. + * Gets the hair style for this being. */ - virtual void - setHairStyle(Uint16 style); + int getHairStyle() const + { return mHairStyle; } /** - * Gets the hair style for this being. + * Sets the hair style and color for this being. */ - Uint16 - getHairStyle() const { return mHairStyle; } + virtual void + setHairStyle(int style, int color); /** * Sets visible equipments for this being. diff --git a/src/gui/char_select.cpp b/src/gui/char_select.cpp index 90dd53be..8ab3b879 100644 --- a/src/gui/char_select.cpp +++ b/src/gui/char_select.cpp @@ -256,8 +256,7 @@ CharCreateDialog::CharCreateDialog(Window *parent, int slot, Network *network, { mPlayer = new Player(0, 0, NULL); mPlayer->setGender(gender); - mPlayer->setHairStyle(rand() % NR_HAIR_STYLES + 1); - mPlayer->setHairColor(rand() % NR_HAIR_COLORS + 1); + mPlayer->setHairStyle(rand() % NR_HAIR_STYLES, rand() % NR_HAIR_COLORS); mNameField = new TextField(""); mNameLabel = new gcn::Label("Name:"); @@ -338,18 +337,16 @@ CharCreateDialog::action(const gcn::ActionEvent &event) scheduleDelete(); } else if (event.getId() == "nextcolor") { - mPlayer->setHairColor(mPlayer->getHairColor() % NR_HAIR_COLORS + 1); + mPlayer->setHairStyle(-1, mPlayer->getHairColor() + 1); } else if (event.getId() == "prevcolor") { - int prevColor = mPlayer->getHairColor() + NR_HAIR_COLORS - 2; - mPlayer->setHairColor(prevColor % NR_HAIR_COLORS + 1); + mPlayer->setHairStyle(-1, mPlayer->getHairColor() + NR_HAIR_COLORS - 1); } else if (event.getId() == "nextstyle") { - mPlayer->setHairStyle(mPlayer->getHairStyle() % NR_HAIR_STYLES + 1); + mPlayer->setHairStyle(mPlayer->getHairStyle() + 1, -1); } else if (event.getId() == "prevstyle") { - int prevStyle = mPlayer->getHairStyle() + NR_HAIR_STYLES - 2; - mPlayer->setHairStyle(prevStyle % NR_HAIR_STYLES + 1); + mPlayer->setHairStyle(mPlayer->getHairStyle() + NR_HAIR_STYLES - 1, -1); } } diff --git a/src/net/beinghandler.cpp b/src/net/beinghandler.cpp index 74d1e2d1..e54a66ed 100644 --- a/src/net/beinghandler.cpp +++ b/src/net/beinghandler.cpp @@ -67,6 +67,7 @@ void BeingHandler::handleMessage(MessageIn *msg) Sint16 param1; Sint8 type; Being *srcBeing, *dstBeing; + int hairStyle, hairColor; switch (msg->getId()) { @@ -106,7 +107,7 @@ void BeingHandler::handleMessage(MessageIn *msg) dstBeing->setWalkSpeed(speed); dstBeing->mJob = job; - dstBeing->setHairStyle(msg->readInt16()); + hairStyle = msg->readInt16(); dstBeing->setVisibleEquipment( Being::WEAPON_SPRITE, msg->readInt16()); dstBeing->setVisibleEquipment( @@ -122,7 +123,7 @@ void BeingHandler::handleMessage(MessageIn *msg) headMid = msg->readInt16(); dstBeing->setVisibleEquipment(Being::HAT_SPRITE, headTop); dstBeing->setVisibleEquipment(Being::TOPCLOTHES_SPRITE, headMid); - dstBeing->setHairColor(msg->readInt16()); + hairColor = msg->readInt16(); msg->readInt16(); // unknown msg->readInt16(); // head dir msg->readInt16(); // guild @@ -132,6 +133,7 @@ void BeingHandler::handleMessage(MessageIn *msg) msg->readInt16(); // karma msg->readInt8(); // unknown dstBeing->setGender(1 - msg->readInt8()); // gender + dstBeing->setHairStyle(hairStyle, hairColor); if (msg->getId() == SMSG_BEING_MOVE) { @@ -258,7 +260,7 @@ void BeingHandler::handleMessage(MessageIn *msg) switch (type) { case 1: - dstBeing->setHairStyle(id); + dstBeing->setHairStyle(id, -1); break; case 2: dstBeing->setVisibleEquipment(Being::WEAPON_SPRITE, id); @@ -276,7 +278,7 @@ void BeingHandler::handleMessage(MessageIn *msg) Being::TOPCLOTHES_SPRITE, id); break; case 6: - dstBeing->setHairColor(id); + dstBeing->setHairStyle(-1, id); break; default: logger->log("SMSG_BEING_CHANGE_LOOKS: unsupported type: " @@ -313,7 +315,7 @@ void BeingHandler::handleMessage(MessageIn *msg) dstBeing->setWalkSpeed(speed); dstBeing->mJob = job; - dstBeing->setHairStyle(msg->readInt16()); + hairStyle = msg->readInt16(); dstBeing->setVisibleEquipment( Being::WEAPON_SPRITE, msg->readInt16()); msg->readInt16(); // item id 2 @@ -326,7 +328,7 @@ void BeingHandler::handleMessage(MessageIn *msg) headTop = msg->readInt16(); headMid = msg->readInt16(); - dstBeing->setHairColor(msg->readInt16()); + hairColor = msg->readInt16(); msg->readInt16(); // unknown msg->readInt16(); // head dir msg->readInt32(); // guild @@ -334,6 +336,7 @@ void BeingHandler::handleMessage(MessageIn *msg) msg->readInt16(); // manner msg->readInt8(); // karma dstBeing->setGender(1 - msg->readInt8()); // gender + dstBeing->setHairStyle(hairStyle, hairColor); dstBeing->setVisibleEquipment( Being::BOTTOMCLOTHES_SPRITE, headBottom); dstBeing->setVisibleEquipment(Being::HAT_SPRITE, headTop); diff --git a/src/net/charserverhandler.cpp b/src/net/charserverhandler.cpp index b25b0453..063be212 100644 --- a/src/net/charserverhandler.cpp +++ b/src/net/charserverhandler.cpp @@ -202,7 +202,7 @@ LocalPlayer* CharServerHandler::readPlayerData(MessageIn &msg, int &slot) tempPlayer->mMaxMp = msg.readInt16(); msg.readInt16(); // speed msg.readInt16(); // class - tempPlayer->setHairStyle(msg.readInt16()); + int hairStyle = msg.readInt16(); Uint16 weapon = msg.readInt16(); tempPlayer->setVisibleEquipment(Being::WEAPON_SPRITE, weapon); tempPlayer->mLevel = msg.readInt16(); @@ -211,7 +211,8 @@ LocalPlayer* CharServerHandler::readPlayerData(MessageIn &msg, int &slot) msg.readInt16(); // shield tempPlayer->setVisibleEquipment(Being::HAT_SPRITE, msg.readInt16()); // head option top tempPlayer->setVisibleEquipment(Being::TOPCLOTHES_SPRITE, msg.readInt16()); // head option mid - tempPlayer->setHairColor(msg.readInt16()); + int hairColor = msg.readInt16(); + tempPlayer->setHairStyle(hairStyle, hairColor); msg.readInt16(); // unknown tempPlayer->setName(msg.readString(24)); for (int i = 0; i < 6; i++) { diff --git a/src/player.cpp b/src/player.cpp index 483bb726..d1999c89 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -31,7 +31,7 @@ #include "resources/itemdb.h" #include "resources/iteminfo.h" -#include "utils/tostring.h" +#include "utils/strprintf.h" #include "gui/gui.h" @@ -129,48 +129,39 @@ void Player::setGender(int gender) Being::setGender(gender); } -void Player::setHairColor(int color) +void Player::setHairStyle(int style, int color) { - if (color != mHairColor && mHairStyle > 0) - { - const std::string hairStyle = toString(getHairStyle()); - const std::string gender = (mGender == 0) ? "-male" : "-female"; - - AnimatedSprite *newHairSprite = AnimatedSprite::load( - "graphics/sprites/hairstyle" + hairStyle + gender + ".xml", - color - 1); - if (newHairSprite) - newHairSprite->setDirection(getSpriteDirection()); + style = style < 0 ? mHairStyle : style % NR_HAIR_STYLES; + color = color < 0 ? mHairColor : color % NR_HAIR_COLORS; + if (style == mHairStyle && color == mHairColor) return; - delete mSprites[HAIR_SPRITE]; - mSprites[HAIR_SPRITE] = newHairSprite; - - setAction(mAction); - } - - Being::setHairColor(color); -} + Being::setHairStyle(style, color); -void Player::setHairStyle(int style) -{ - if (style != mHairStyle && mHairColor > 0) + static char const *const colors[NR_HAIR_COLORS] = { - const std::string hairStyle = toString(style); - const std::string gender = (mGender == 0) ? "-male" : "-female"; - - AnimatedSprite *newHairSprite = AnimatedSprite::load( - "graphics/sprites/hairstyle" + hairStyle + gender + ".xml", - mHairColor - 1); - if (newHairSprite) - newHairSprite->setDirection(getSpriteDirection()); - - delete mSprites[HAIR_SPRITE]; - mSprites[HAIR_SPRITE] = newHairSprite; - - setAction(mAction); - } - - Being::setHairStyle(style); + "#8c4b41,da9041,ffffff", // light brown + "#06372b,489e25,fdedcc", // green + "#5f0b33,91191c,f9ad81", // red + "#602486,934cc3,fdc689", // purple + "#805e74,c6b09b,ffffff", // gray + "#8c6625,dab425,ffffff", // yellow + "#1d2d6d,1594a3,fdedcc", // blue + "#831f2d,be4f2d,f8cc8b", // brown + "#432482,584bbc,dae8e5", // light blue + "#460850,611967,e7b4ae", // dark purple + }; + + AnimatedSprite *newHairSprite = AnimatedSprite::load + (strprintf("graphics/sprites/hairstyle%d%s.xml|%s", + style, + "", //(mGender == 0) ? "-male" : "-female", + colors[color])); + if (newHairSprite) + newHairSprite->setDirection(getSpriteDirection()); + delete mSprites[HAIR_SPRITE]; + mSprites[HAIR_SPRITE] = newHairSprite; + + setAction(mAction); } void Player::setVisibleEquipment(int slot, int id) diff --git a/src/player.h b/src/player.h index 742ca465..1526d2bc 100644 --- a/src/player.h +++ b/src/player.h @@ -51,12 +51,14 @@ class Player : public Being virtual void setGender(int gender); - virtual void - setHairColor(int color); - - virtual void - setHairStyle(int style); + /** + * Sets the hair style and color for this player. + */ + void setHairStyle(int style, int color); + /** + * Sets visible equipments for this player. + */ virtual void setVisibleEquipment(int slot, int id); }; diff --git a/src/resources/dye.cpp b/src/resources/dye.cpp new file mode 100644 index 00000000..2ce4a48d --- /dev/null +++ b/src/resources/dye.cpp @@ -0,0 +1,222 @@ +/* + * The Mana World + * Copyright 2007 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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. + * + * The Mana World 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 The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: dye.cpp 3708 2007-11-04 11:52:44Z gmelquio $ + */ + +#include <algorithm> +#include <sstream> + +#include "dye.h" + +#include "../log.h" + +Palette::Palette(std::string const &description) +{ + int size = description.length(); + if (size == 0) return; + if (description[0] != '#') + { + // TODO: load palette from file. + return; + } + + int pos = 1; + for (;;) + { + if (pos + 6 > size) break; + int v = 0; + for (int i = 0; i < 6; ++i) + { + char c = description[pos + i]; + int n; + if ('0' <= c && c <= '9') n = c - '0'; + else if ('A' <= c && c <= 'F') n = c - 'A' + 10; + else if ('a' <= c && c <= 'f') n = c - 'a' + 10; + else goto error; + v = (v << 4) | n; + } + Color c = { { v >> 16, v >> 8, v } }; + mColors.push_back(c); + pos += 6; + if (pos == size) return; + if (description[pos] != ',') break; + ++pos; + } + + error: + logger->log("Error, invalid embedded palette: %s", description.c_str()); +} + +void Palette::getColor(int intensity, int color[3]) const +{ + if (intensity == 0) + { + color[0] = 0; + color[1] = 0; + color[2] = 0; + return; + } + + int last = mColors.size(); + if (last == 0) return; + + int i = intensity * last / 255; + int t = intensity * last % 255; + + int j = t != 0 ? i : i - 1; + // Get the exact color if any, the next color otherwise. + int r2 = mColors[j].value[0], + g2 = mColors[j].value[1], + b2 = mColors[j].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. + int r1 = 0, g1 = 0, b1 = 0; + if (i > 0) + { + r1 = mColors[i - 1].value[0]; + g1 = mColors[i - 1].value[1]; + b1 = mColors[i - 1].value[2]; + } + + // Perform a linear interpolation. + color[0] = ((255 - t) * r1 + t * r2) / 255; + color[1] = ((255 - t) * g1 + t * g2) / 255; + color[2] = ((255 - t) * b1 + t * b2) / 255; +} + +Dye::Dye(std::string const &description) +{ + for (int i = 0; i < 7; ++i) + mPalettes[i] = 0; + + if (description.empty()) return; + + std::string::size_type next_pos = 0, length = description.length(); + do + { + std::string::size_type 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; + default: + logger->log("Error, invalid dye: %s", description.c_str()); + return; + } + mPalettes[i] = new Palette(description.substr(pos + 2, next_pos - pos - 2)); + ++next_pos; + } + while (next_pos < length); +} + +Dye::~Dye() +{ + for (int i = 0; i < 7; ++i) + delete mPalettes[i]; +} + +void Dye::update(int color[3]) const +{ + int cmax = std::max(color[0], std::max(color[1], color[2])); + if (cmax == 0) return; + + int cmin = std::min(color[0], std::min(color[1], color[2])); + int intensity = color[0] + color[1] + color[2]; + + if (cmin != cmax && + (cmin != 0 || (intensity != cmax && intensity != 2 * cmax))) + { + // not pure + return; + } + + int i = (color[0] != 0) | ((color[1] != 0) << 1) | ((color[2] != 0) << 2); + + if (mPalettes[i - 1]) + mPalettes[i - 1]->getColor(cmax, color); +} + +void Dye::instantiate(std::string &target, std::string const &palettes) +{ + std::string::size_type next_pos = target.find('|'); + if (next_pos == std::string::npos || palettes.empty()) return; + ++next_pos; + + std::ostringstream s; + s << target.substr(0, next_pos); + std::string::size_type last_pos = target.length(), pal_pos = 0; + do + { + std::string::size_type pos = next_pos; + next_pos = target.find(';', pos); + if (next_pos == std::string::npos) next_pos = last_pos; + if (next_pos == pos + 1) + { + std::string::size_type pal_next_pos = palettes.find(';'); + 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; + } + 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(); +} diff --git a/src/resources/dye.h b/src/resources/dye.h new file mode 100644 index 00000000..fe8669bb --- /dev/null +++ b/src/resources/dye.h @@ -0,0 +1,96 @@ +/* + * The Mana World + * Copyright 2007 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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. + * + * The Mana World 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 The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: dye.h 3706 2007-11-03 21:04:51Z gmelquio $ + */ + +#ifndef _TMW_DYE_H +#define _TMW_DYE_H + +#include <vector> + +/** + * Class for performing a linear interpolation between colors. + */ +class Palette +{ + 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 '#'. + */ + Palette(std::string const &); + + /** + * Gets a pixel color depending on its intensity. + */ + void getColor(int intensity, int color[3]) const; + + private: + + struct Color { unsigned char value[3]; }; + + std::vector< Color > mColors; +}; + +/** + * Class for dispatching pixel-recoloring amongst several palettes. + */ +class Dye +{ + 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. + */ + Dye(std::string const &); + + /** + * Destroys the associated palettes. + */ + ~Dye(); + + /** + * Modifies a pixel color. + */ + void update(int color[3]) const; + + /** + * Fills the blank in a dye placeholder with some palette names. + */ + static void instantiate(std::string &target, + std::string const &palettes); + + private: + + /** + * The order of the palettes, as well as their uppercase letter, is: + * + * Red, Green, Yellow, Blue, Magenta, White (or rather gray). + */ + Palette *mPalettes[7]; +}; + +#endif diff --git a/src/resources/image.cpp b/src/resources/image.cpp index 7a394edb..d0dae462 100644 --- a/src/resources/image.cpp +++ b/src/resources/image.cpp @@ -21,9 +21,11 @@ * $Id$ */ +#include <SDL_image.h> + #include "image.h" -#include <SDL_image.h> +#include "dye.h" #include "../log.h" @@ -85,6 +87,49 @@ Resource *Image::load(void *buffer, unsigned bufferSize) return image; } +Resource *Image::load(void *buffer, unsigned bufferSize, Dye const &dye) +{ + SDL_RWops *rw = SDL_RWFromMem(buffer, bufferSize); + SDL_Surface *tmpImage = IMG_Load_RW(rw, 1); + + if (!tmpImage) + { + logger->log("Error, image load failed: %s", IMG_GetError()); + return NULL; + } + + SDL_PixelFormat rgba; + rgba.palette = NULL; + rgba.BitsPerPixel = 32; + rgba.BytesPerPixel = 4; + rgba.Rmask = 0xFF000000; rgba.Rloss = 0; rgba.Rshift = 24; + rgba.Gmask = 0x00FF0000; rgba.Gloss = 0; rgba.Gshift = 16; + rgba.Bmask = 0x0000FF00; rgba.Bloss = 0; rgba.Bshift = 8; + rgba.Amask = 0x000000FF; rgba.Aloss = 0; rgba.Ashift = 0; + rgba.colorkey = 0; + rgba.alpha = 255; + + SDL_Surface *surf = SDL_ConvertSurface(tmpImage, &rgba, SDL_SWSURFACE); + SDL_FreeSurface(tmpImage); + + Uint32 *pixels = static_cast< Uint32 * >(surf->pixels); + for (Uint32 *p_end = pixels + surf->w * surf->h; pixels != p_end; ++pixels) + { + int alpha = *pixels & 255; + if (!alpha) continue; + int v[3]; + v[0] = (*pixels >> 24) & 255; + v[1] = (*pixels >> 16) & 255; + v[2] = (*pixels >> 8 ) & 255; + dye.update(v); + *pixels = (v[0] << 24) | (v[1] << 16) | (v[2] << 8) | alpha; + } + + Image *image = load(surf); + SDL_FreeSurface(surf); + return image; +} + Image *Image::load(SDL_Surface *tmpImage) { #ifdef USE_OPENGL diff --git a/src/resources/image.h b/src/resources/image.h index 485ca227..52f286f8 100644 --- a/src/resources/image.h +++ b/src/resources/image.h @@ -40,6 +40,8 @@ #include "resource.h" +class Dye; + /** * Defines a class for loading and storing images. */ @@ -63,12 +65,24 @@ class Image : public Resource * @param buffer The memory buffer containing the image data. * @param bufferSize The size of the memory buffer in bytes. * - * @return <code>NULL</code> if the an error occurred, a valid pointer + * @return <code>NULL</code> if an error occurred, a valid pointer * otherwise. */ static Resource *load(void *buffer, unsigned bufferSize); /** + * Loads an image from a buffer in memory and recolors it. + * + * @param buffer The memory buffer containing the image data. + * @param bufferSize The size of the memory buffer in bytes. + * @param dye The dye used to recolor the image. + * + * @return <code>NULL</code> if an error occurred, a valid pointer + * otherwise. + */ + static Resource *load(void *buffer, unsigned bufferSize, Dye const &dye); + + /** * Loads an image from an SDL surface. */ static Image *load(SDL_Surface *); diff --git a/src/resources/resourcemanager.cpp b/src/resources/resourcemanager.cpp index e507835a..3368d05b 100644 --- a/src/resources/resourcemanager.cpp +++ b/src/resources/resourcemanager.cpp @@ -28,6 +28,7 @@ #include <physfs.h> #include <SDL_image.h> +#include "dye.h" #include "image.h" #include "music.h" #include "soundeffect.h" @@ -204,12 +205,6 @@ Resource *ResourceManager::load(std::string const &path, loader fun) return get(path, ResourceLoader::load, &l); } -Image* -ResourceManager::getImage(const std::string &idPath) -{ - return static_cast<Image*>(load(idPath, Image::load)); -} - Music* ResourceManager::getMusic(const std::string &idPath) { @@ -222,6 +217,38 @@ ResourceManager::getSoundEffect(const std::string &idPath) return static_cast<SoundEffect*>(load(idPath, SoundEffect::load)); } +struct DyedImageLoader +{ + ResourceManager *manager; + std::string path; + static Resource *load(void *v) + { + DyedImageLoader *l = static_cast< DyedImageLoader * >(v); + std::string path = l->path; + std::string::size_type p = path.find('|'); + Dye *d = NULL; + if (p != std::string::npos) + { + d = new Dye(path.substr(p + 1)); + path = path.substr(0, p); + } + int fileSize; + void *buffer = l->manager->loadFile(path, fileSize); + if (!buffer) return NULL; + Resource *res = d ? Image::load(buffer, fileSize, *d) + : Image::load(buffer, fileSize); + free(buffer); + delete d; + return res; + } +}; + +Image *ResourceManager::getImage(std::string const &idPath) +{ + DyedImageLoader l = { this, idPath }; + return static_cast<Image*>(get(idPath, DyedImageLoader::load, &l)); +} + struct ImageSetLoader { ResourceManager *manager; @@ -258,8 +285,8 @@ struct SpriteDefLoader } }; -SpriteDef* -ResourceManager::getSprite(const std::string &path, int variant) +SpriteDef *ResourceManager::getSprite + (std::string const &path, int variant) { SpriteDefLoader l = { path, variant }; std::stringstream ss; diff --git a/src/resources/resourcemanager.h b/src/resources/resourcemanager.h index 150b773c..c52248b1 100644 --- a/src/resources/resourcemanager.h +++ b/src/resources/resourcemanager.h @@ -24,10 +24,9 @@ #ifndef _TMW_RESOURCE_MANAGER_H #define _TMW_RESOURCE_MANAGER_H -#include <iosfwd> #include <map> +#include <string> #include <vector> -#include <SDL.h> class Resource; class Image; @@ -35,6 +34,7 @@ class Music; class SoundEffect; class ImageSet; class SpriteDef; +struct SDL_Surface; /** * A class for loading and managing resources. @@ -151,8 +151,7 @@ class ResourceManager * Creates a sprite definition based on a given path and the supplied * variant. */ - SpriteDef* - getSprite(const std::string &path, int variant = 0); + SpriteDef *getSprite(std::string const &path, int variant = 0); /** * Releases a resource, removing it from the set of loaded resources. diff --git a/src/resources/spritedef.cpp b/src/resources/spritedef.cpp index 45a52d2e..de6f8d0b 100644 --- a/src/resources/spritedef.cpp +++ b/src/resources/spritedef.cpp @@ -25,14 +25,14 @@ #include "spritedef.h" -#include "../log.h" - -#include "animation.h" #include "action.h" -#include "resourcemanager.h" -#include "imageset.h" +#include "animation.h" +#include "dye.h" #include "image.h" +#include "imageset.h" +#include "resourcemanager.h" +#include "../log.h" #include "../utils/xml.h" Action* @@ -53,7 +53,14 @@ SpriteDef *SpriteDef::load(std::string const &animationFile, int variant) { int size; ResourceManager *resman = ResourceManager::getInstance(); - char *data = (char*) resman->loadFile(animationFile.c_str(), size); + + std::string::size_type pos = animationFile.find('|'); + std::string palettes; + if (pos != std::string::npos) + palettes = animationFile.substr(pos + 1); + + char *data = (char*) resman->loadFile + (animationFile.substr(0, pos).c_str(), size); if (!data) return NULL; @@ -89,7 +96,7 @@ SpriteDef *SpriteDef::load(std::string const &animationFile, int variant) { if (xmlStrEqual(node->name, BAD_CAST "imageset")) { - def->loadImageSet(node); + def->loadImageSet(node, palettes); } else if (xmlStrEqual(node->name, BAD_CAST "action")) { @@ -125,13 +132,13 @@ void SpriteDef::substituteActions() substituteAction(ACTION_DEAD, ACTION_HURT); } -void -SpriteDef::loadImageSet(xmlNodePtr node) +void SpriteDef::loadImageSet(xmlNodePtr node, std::string const &palettes) { int width = XML::getProperty(node, "width", 0); int height = XML::getProperty(node, "height", 0); std::string name = XML::getProperty(node, "name", ""); std::string imageSrc = XML::getProperty(node, "src", ""); + Dye::instantiate(imageSrc, palettes); ResourceManager *resman = ResourceManager::getInstance(); ImageSet *imageSet = resman->getImageSet(imageSrc, width, height); diff --git a/src/resources/spritedef.h b/src/resources/spritedef.h index 121f23cc..4f316875 100644 --- a/src/resources/spritedef.h +++ b/src/resources/spritedef.h @@ -95,8 +95,7 @@ class SpriteDef : public Resource /** * Loads an imageset element. */ - void - loadImageSet(xmlNodePtr node); + void loadImageSet(xmlNodePtr node, std::string const &palettes); /** * Loads an action element. diff --git a/src/utils/strprintf.cpp b/src/utils/strprintf.cpp new file mode 100644 index 00000000..547d9712 --- /dev/null +++ b/src/utils/strprintf.cpp @@ -0,0 +1,53 @@ +/* + * The Mana World + * Copyright 2007 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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. + * + * The Mana World 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 The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: strprintf.cpp 3416 2007-08-06 06:20:14Z gmelquio $ + */ + +#ifndef _TMW_UTILS_TOSTRING_H +#define _TMW_UTISL_TOSTRING_H + +#include <cstdarg> + +#include "strprintf.h" + +std::string strprintf(char const *format, ...) +{ + char buf[256]; + va_list(args); + va_start(args, format); + int nb = vsnprintf(buf, 256, format, args); + va_end(args); + if (nb < 256) + { + return buf; + } + // The static size was not big enough, try again with a dynamic allocation. + ++nb; + char *buf2 = new char[nb]; + va_start(args, format); + vsnprintf(buf2, nb, format, args); + va_end(args); + std::string res(buf2); + delete [] buf2; + return res; +} + +#endif diff --git a/src/utils/strprintf.h b/src/utils/strprintf.h new file mode 100644 index 00000000..66d753fa --- /dev/null +++ b/src/utils/strprintf.h @@ -0,0 +1,37 @@ +/* + * The Mana World + * Copyright 2007 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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. + * + * The Mana World 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 The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: strprintf.h 3417 2007-08-06 11:14:45Z gmelquio $ + */ + +#ifndef _TMW_UTILS_STRPRINTF_H +#define _TMW_UTILS_STRPRINTF_H + +#include <string> + +std::string strprintf(char const *, ...) +#ifdef __GNUC__ + /* This attribute is nice: it even works through gettext invokation. For + example, gcc will complain that strprintf(_("%s"), 42) is ill-formed. */ + __attribute__((format(printf, 1, 2))) +#endif +; + +#endif |