diff options
Diffstat (limited to 'engines/sword25/gfx/image')
-rwxr-xr-x | engines/sword25/gfx/image/b25sloader.cpp | 101 | ||||
-rwxr-xr-x | engines/sword25/gfx/image/b25sloader.h | 52 | ||||
-rwxr-xr-x | engines/sword25/gfx/image/image.h | 208 | ||||
-rwxr-xr-x | engines/sword25/gfx/image/imageloader.cpp | 120 | ||||
-rwxr-xr-x | engines/sword25/gfx/image/imageloader.h | 358 | ||||
-rwxr-xr-x | engines/sword25/gfx/image/imageloader_ids.h | 44 | ||||
-rwxr-xr-x | engines/sword25/gfx/image/pngloader.cpp | 389 | ||||
-rwxr-xr-x | engines/sword25/gfx/image/pngloader.h | 64 | ||||
-rwxr-xr-x | engines/sword25/gfx/image/vectorimage.cpp | 571 | ||||
-rwxr-xr-x | engines/sword25/gfx/image/vectorimage.h | 167 | ||||
-rwxr-xr-x | engines/sword25/gfx/image/vectorimagerenderer.cpp | 198 | ||||
-rwxr-xr-x | engines/sword25/gfx/image/vectorimagerenderer.h | 76 |
12 files changed, 2348 insertions, 0 deletions
diff --git a/engines/sword25/gfx/image/b25sloader.cpp b/engines/sword25/gfx/image/b25sloader.cpp new file mode 100755 index 0000000000..55aaf7a2c5 --- /dev/null +++ b/engines/sword25/gfx/image/b25sloader.cpp @@ -0,0 +1,101 @@ +// ----------------------------------------------------------------------------- +// This file is part of Broken Sword 2.5 +// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer +// +// Broken Sword 2.5 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 +// (at your option) any later version. +// +// Broken Sword 2.5 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 Broken Sword 2.5; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +// ----------------------------------------------------------------------------- + +// ----------------------------------------------------------------------------- +// Includes +// ----------------------------------------------------------------------------- + +#include <string> +#include <sstream> +#include <algorithm> +using namespace std; + +#include "b25sloader.h" +#include "pngloader.h" + +#define BS_LOG_PREFIX "B25SLOADER" + +// ----------------------------------------------------------------------------- + +namespace +{ + unsigned int FindEmbeddedPNG(const char * FileDataPtr, unsigned int FileSize) + { + // Einen Stringstream mit dem Anfang der Datei intialisieren. 512 Byte sollten hierfür genügen. + istringstream StringStream(string(FileDataPtr, FileDataPtr + min(static_cast<unsigned int>(512), FileSize))); + + // Headerinformationen der Spielstandes einlesen. + string Marker, VersionID; + unsigned int CompressedGamedataSize, UncompressedGamedataSize; + StringStream >> Marker >> VersionID >> CompressedGamedataSize >> UncompressedGamedataSize; + if (!StringStream.good()) return 0; + + // Testen, ob wir tatsächlich einen Spielstand haben. + if (Marker == "BS25SAVEGAME") + { + // Offset zum PNG innerhalb des Spielstandes berechnen und zurückgeben. + return static_cast<unsigned int>(StringStream.tellg()) + CompressedGamedataSize + 1; + } + + return 0; + } +} + +// ----------------------------------------------------------------------------- + +bool BS_B25SLoader::IsCorrectImageFormat(const char * FileDataPtr, unsigned int FileSize) +{ + // PNG innerhalb des Spielstandes finden und den Methodenaufruf zu BS_PNGLoader weiterreichen. + unsigned int PNGOffset = FindEmbeddedPNG(FileDataPtr, FileSize); + if (PNGOffset > 0) + { + return BS_PNGLoader::DoIsCorrectImageFormat(FileDataPtr + PNGOffset, FileSize - PNGOffset); + } + + return false; +} + +// ----------------------------------------------------------------------------- + +bool BS_B25SLoader::DecodeImage(const char * FileDataPtr, unsigned int FileSize, BS_GraphicEngine::COLOR_FORMATS ColorFormat, char * & UncompressedDataPtr, + int & Width, int & Height, int & Pitch) +{ + // PNG innerhalb des Spielstandes finden und den Methodenaufruf zu BS_PNGLoader weiterreichen. + unsigned int PNGOffset = FindEmbeddedPNG(FileDataPtr, FileSize); + if (PNGOffset > 0) + { + return BS_PNGLoader::DoDecodeImage(FileDataPtr + PNGOffset, FileSize - PNGOffset, ColorFormat, UncompressedDataPtr, Width, Height, Pitch); + } + + return false; +} + +// ----------------------------------------------------------------------------- + +bool BS_B25SLoader::ImageProperties(const char * FileDataPtr, unsigned int FileSize, BS_GraphicEngine::COLOR_FORMATS & ColorFormat, int & Width, int & Height) +{ + // PNG innerhalb des Spielstandes finden und den Methodenaufruf zu BS_PNGLoader weiterreichen. + unsigned int PNGOffset = FindEmbeddedPNG(FileDataPtr, FileSize); + if (PNGOffset > 0) + { + return BS_PNGLoader::DoImageProperties(FileDataPtr + PNGOffset, FileSize - PNGOffset, ColorFormat, Width, Height); + } + + return false; +} diff --git a/engines/sword25/gfx/image/b25sloader.h b/engines/sword25/gfx/image/b25sloader.h new file mode 100755 index 0000000000..bafea87365 --- /dev/null +++ b/engines/sword25/gfx/image/b25sloader.h @@ -0,0 +1,52 @@ +// ----------------------------------------------------------------------------- +// This file is part of Broken Sword 2.5 +// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer +// +// Broken Sword 2.5 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 +// (at your option) any later version. +// +// Broken Sword 2.5 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 Broken Sword 2.5; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +// ----------------------------------------------------------------------------- + +#ifndef BS_B25SLOADER_H +#define BS_B25SLOADER_H + +// ----------------------------------------------------------------------------- +// Includes +// ----------------------------------------------------------------------------- + +#include "kernel/common.h" +#include "imageloader.h" + +// ----------------------------------------------------------------------------- +// Klassendeklaration +// ----------------------------------------------------------------------------- + +class BS_B25SLoader : public BS_ImageLoader +{ +public: + static BS_ImageLoader * CreateInstance() + { + #include "kernel/memlog_off.h" + return static_cast<BS_ImageLoader *>(new BS_B25SLoader()); + #include "kernel/memlog_on.h" + } + +protected: + virtual bool IsCorrectImageFormat(const char * FileDataPtr, unsigned int FileSize); + virtual bool DecodeImage(const char * FileDataPtr, unsigned int FileSize, BS_GraphicEngine::COLOR_FORMATS ColorFormat, char * & UncompressedDataPtr, + int & Width, int & Height, int & Pitch); + virtual bool ImageProperties(const char * FileDataPtr, unsigned int FileSize, BS_GraphicEngine::COLOR_FORMATS & ColorFormat, int & Width, int & Height); + +}; + +#endif diff --git a/engines/sword25/gfx/image/image.h b/engines/sword25/gfx/image/image.h new file mode 100755 index 0000000000..8f6fe9cab9 --- /dev/null +++ b/engines/sword25/gfx/image/image.h @@ -0,0 +1,208 @@ +// ----------------------------------------------------------------------------- +// This file is part of Broken Sword 2.5 +// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer +// +// Broken Sword 2.5 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 +// (at your option) any later version. +// +// Broken Sword 2.5 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 Broken Sword 2.5; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +// ----------------------------------------------------------------------------- + +/* + BS_Image + -------- + + Autor: Malte Thiesen +*/ + +#ifndef BS_IMAGE_H +#define BS_IMAGE_H + +// Includes +#include "kernel/memlog_off.h" +#include <vector> +#include "kernel/memlog_on.h" +#include "kernel/common.h" +#include "math/rect.h" +#include "gfx/graphicengine.h" + +class BS_Image +{ +public: + virtual ~BS_Image() {}; + + // Enums + /** + @brief Die möglichen Flippingparameter für die Blit-Methode. + */ + enum FLIP_FLAGS + { + /// Das Bild wird nicht gespiegelt. + FLIP_NONE = 0, + /// Das Bild wird an der horizontalen Achse gespiegelt. + FLIP_H = 1, + /// Das Bild wird an der vertikalen Achse gespiegelt. + FLIP_V = 2, + /// Das Bild wird an der horizontalen und vertikalen Achse gespiegelt. + FLIP_HV = FLIP_H | FLIP_V, + /// Das Bild wird an der horizontalen und vertikalen Achse gespiegelt. + FLIP_VH = FLIP_H | FLIP_V + }; + + //@{ + /** @name Accessor-Methoden */ + + /** + @brief Gibt die Breite des Bildes in Pixeln zurück + */ + virtual int GetWidth() const = 0; + + /** + @brief Gibt die Höhe des Bildes in Pixeln zurück + */ + virtual int GetHeight() const = 0; + + /** + @brief Gibt das Farbformat des Bildes zurück + */ + virtual BS_GraphicEngine::COLOR_FORMATS GetColorFormat() const = 0; + + //@} + + //@{ + /** @name Render-Methoden */ + + /** + @brief Rendert das Bild in den Framebuffer. + @param pDest ein Pointer auf das Zielbild. In den meisten Fällen ist dies der Framebuffer. + @param PosX die Position auf der X-Achse im Zielbild in Pixeln, an der das Bild gerendert werden soll.<br> + Der Standardwert ist 0. + @param PosY die Position auf der Y-Achse im Zielbild in Pixeln, an der das Bild gerendert werden soll.<br> + Der Standardwert ist 0. + @param Flipping gibt an, wie das Bild gespiegelt werden soll.<br> + Der Standardwert ist BS_Image::FLIP_NONE (keine Spiegelung) + @param pSrcPartRect Pointer auf ein BS_Rect, welches den Ausschnitt des Quellbildes spezifiziert, der gerendert + werden soll oder NULL, falls das gesamte Bild gerendert werden soll.<br> + Dieser Ausschnitt bezieht sich auf das ungespiegelte und unskalierte Bild.<br> + Der Standardwert ist NULL. + @param Color ein ARGB Farbwert, der die Parameter für die Farbmodulation und fürs Alphablending festlegt.<br> + Die Alpha-Komponente der Farbe bestimmt den Alphablending Parameter (0 = keine Deckung, 255 = volle Deckung).<br> + Die Farbkomponenten geben die Farbe für die Farbmodulation an.<br> + Der Standardwert is BS_ARGB(255, 255, 255, 255) (volle Deckung, keine Farbmodulation). + Zum Erzeugen des Farbwertes können die Makros BS_RGB und BS_ARGB benutzt werden. + @param Width gibt die Ausgabebreite des Bildausschnittes an. + Falls diese von der Breite des Bildausschnittes abweicht wird + das Bild entsprechend Skaliert.<br> + Der Wert -1 gibt an, dass das Bild nicht Skaliert werden soll.<br> + Der Standardwert ist -1. + @param Width gibt die Ausgabehöhe des Bildausschnittes an. + Falls diese von der Höhe des Bildauschnittes abweicht, wird + das Bild entsprechend Skaliert.<br> + Der Wert -1 gibt an, dass das Bild nicht Skaliert werden soll.<br> + Der Standardwert ist -1. + @return Gibt false zurück, falls das Rendern fehlgeschlagen ist. + @remark Er werden nicht alle Blitting-Operationen von allen BS_Image-Klassen unterstützt.<br> + Mehr Informationen gibt es in der Klassenbeschreibung von BS_Image und durch folgende Methoden: + - IsBlitTarget() + - IsScalingAllowed() + - IsFillingAllowed() + - IsAlphaAllowed() + - IsColorModulationAllowed() + - IsSetContentAllowed() + */ + virtual bool Blit(int PosX = 0, int PosY = 0, + int Flipping = FLIP_NONE, + BS_Rect* pPartRect = NULL, + unsigned int Color = BS_ARGB(255, 255, 255, 255), + int Width = -1, int Height = -1) = 0; + + /** + @brief Füllt einen Rechteckigen Bereich des Bildes mit einer Farbe. + @param pFillRect Pointer auf ein BS_Rect, welches den Ausschnitt des Bildes spezifiziert, der gefüllt + werden soll oder NULL, falls das gesamte Bild gefüllt werden soll.<br> + Der Standardwert ist NULL. + @param Color der 32 Bit Farbwert mit dem der Bildbereich gefüllt werden soll. + @remark Es ist möglich über die Methode transparente Rechtecke darzustellen, indem man eine Farbe mit einem Alphawert ungleich + 255 angibt. + @remark Unabhängig vom Farbformat des Bildes muss ein 32 Bit Farbwert angegeben werden. Zur Erzeugung, können die Makros + BS_RGB und BS_ARGB benutzt werden. + @remark Falls das Rechteck nicht völlig innerhalb des Bildschirms ist, wird es automatisch zurechtgestutzt. + */ + virtual bool Fill(const BS_Rect* pFillRect = 0, unsigned int Color = BS_RGB(0, 0, 0)) = 0; + + /** + @brief Füllt den Inhalt des Bildes mit Pixeldaten. + @param Pixeldata ein Vector der die Pixeldaten enthält. Sie müssen in dem Farbformat des Bildes vorliegen und es müssen genügend Daten + vorhanden sein, um das ganze Bild zu füllen. + @param Offset der Offset in Byte im Pixeldata-Vector an dem sich der erste zu schreibende Pixel befindet.<br> + Der Standardwert ist 0. + @param Stride der Abstand in Byte zwischen dem Zeilenende und dem Beginn einer neuen Zeile im Pixeldata-Vector.<br> + Der Standardwert ist 0. + @return Gibt false zurück, falls der Aufruf fehlgeschlagen ist. + @remark Ein Aufruf dieser Methode ist nur erlaubt, wenn IsSetContentAllowed() true zurückgibt. + */ + virtual bool SetContent(const std::vector<unsigned char> & Pixeldata, unsigned int Offset, unsigned int Stride) = 0; + + /** + @brief Liest einen Pixel des Bildes. + @param X die X-Koordinate des Pixels. + @param Y die Y-Koordinate des Pixels + @return Gibt den 32-Bit Farbwert des Pixels an der übergebenen Koordinate zurück. + @remark Diese Methode sollte auf keine Fall benutzt werden um größere Teile des Bildes zu lesen, da sie sehr langsam ist. Sie ist + eher dafür gedacht einzelne Pixel des Bildes auszulesen. + */ + virtual unsigned int GetPixel(int X, int Y) = 0; + + //@{ + /** @name Auskunfts-Methoden */ + + /** + @brief Überprüft, ob an dem BS_Image Blit() aufgerufen werden darf. + @return Gibt false zurück, falls ein Blit()-Aufruf an diesem Objekt nicht gestattet ist. + */ + virtual bool IsBlitSource() const = 0; + + /** + @brief Überprüft, ob das BS_Image ein Zielbild für einen Blit-Aufruf sein kann. + @return Gibt false zurück, falls ein Blit-Aufruf mit diesem Objekt als Ziel nicht gestattet ist. + */ + virtual bool IsBlitTarget() const = 0; + + /** + @brief Gibt true zurück, falls das BS_Image bei einem Aufruf von Blit() skaliert dargestellt werden kann. + */ + virtual bool IsScalingAllowed() const = 0; + + /** + @brief Gibt true zurück, wenn das BS_Image mit einem Aufruf von Fill() gefüllt werden kann. + */ + virtual bool IsFillingAllowed() const = 0; + + /** + @brief Gibt true zurück, wenn das BS_Image bei einem Aufruf von Blit() mit einem Alphawert dargestellt werden kann. + */ + virtual bool IsAlphaAllowed() const = 0; + + /** + @brief Gibt true zurück, wenn das BS_Image bei einem Aufruf von Blit() mit Farbmodulation dargestellt werden kann. + */ + virtual bool IsColorModulationAllowed() const = 0; + + /** + @brief Gibt true zurück, wenn der Inhalt des BS_Image durch eine Aufruf von SetContent() ausgetauscht werden kann. + */ + virtual bool IsSetContentAllowed() const = 0; + + //@} +}; + +#endif diff --git a/engines/sword25/gfx/image/imageloader.cpp b/engines/sword25/gfx/image/imageloader.cpp new file mode 100755 index 0000000000..18d94a4e21 --- /dev/null +++ b/engines/sword25/gfx/image/imageloader.cpp @@ -0,0 +1,120 @@ +// ----------------------------------------------------------------------------- +// This file is part of Broken Sword 2.5 +// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer +// +// Broken Sword 2.5 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 +// (at your option) any later version. +// +// Broken Sword 2.5 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 Broken Sword 2.5; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +// ----------------------------------------------------------------------------- + +#include "imageloader.h" +#include "imageloader_ids.h" + +#define BS_LOG_PREFIX "IMAGELOADER" + +// Statische Elemente der Klasse BS_ImageLoader intialisieren. +std::list<BS_ImageLoader*> BS_ImageLoader::_ImageLoaderList; +bool BS_ImageLoader::_ImageLoaderListInitialized = false; + +// Lade Methode +// ------------ + +bool BS_ImageLoader::LoadImage(const char* pFileData, unsigned int FileSize, + BS_GraphicEngine::COLOR_FORMATS ColorFormat, + char*& pUncompressedData, + int& Width, int& Height, + int& Pitch) +{ + // Falls die Liste der BS_ImageLoader noch nicht initialisiert wurde, wird dies getan. + if (!_ImageLoaderListInitialized) + _InitializeLoaderList(); + + // Passenden BS_ImageLoader finden und Bild dekodieren + BS_ImageLoader* pLoader = _FindSuitableImageLoader(pFileData, FileSize); + if (pLoader) + { + return pLoader->DecodeImage(pFileData, FileSize, + ColorFormat, + pUncompressedData, + Width, Height, + Pitch); + } + + return false; +} + +// Info Methode +// ------------ + +bool BS_ImageLoader::ExtractImageProperties(const char* pFileData, unsigned int FileSize, + BS_GraphicEngine::COLOR_FORMATS& ColorFormat, + int& Width, int& Height) +{ + // Falls die Liste der BS_ImageLoader noch nicht initialisiert wurde, wird dies getan. + if (!_ImageLoaderListInitialized) + _InitializeLoaderList(); + + // Passenden BS_ImageLoader finden und Bildeigenschaften auslesen. + BS_ImageLoader* pLoader = _FindSuitableImageLoader(pFileData, FileSize); + if (pLoader) + { + return pLoader->ImageProperties(pFileData, FileSize, + ColorFormat, + Width, Height); + } + + return false; +} + +// Verwaltungs Methoden +// -------------------- + +void BS_ImageLoader::_InitializeLoaderList() +{ + // Von jedem BS_ImageLoader wird eine Instanz erzeugt, diese fügen sich selbständig in die BS_ImageLoader-Liste ein. + for (int i = 0; i < BS_IMAGELOADER_COUNT; i++) + BS_IMAGELOADER_IDS[i](); + + // Die Liste als gefüllt markieren. + _ImageLoaderListInitialized = true; + + // Sicherstellen, dass beim Beenden alle BS_ImageLoader Instanzen zerstört werden. + atexit(BS_ImageLoader::_DeinitializeLoaderList); +} + +void BS_ImageLoader::_DeinitializeLoaderList() +{ + while (!_ImageLoaderList.empty()) + { + delete _ImageLoaderList.back(); + _ImageLoaderList.pop_back(); + } +} + +BS_ImageLoader* BS_ImageLoader::_FindSuitableImageLoader(const char* pFileData, unsigned int FileSize) +{ + // Alle BS_ImageLoader-Objekte durchgehen, bis eins gefunden wurde, dass das Bild laden kann + std::list<BS_ImageLoader*>::iterator Iter = _ImageLoaderList.begin(); + for (; Iter != _ImageLoaderList.end(); ++Iter) + { + // Falls ein geeigneter BS-ImageLoader gefunden wurde, wird er zurückgegeben. + if ((*Iter)->IsCorrectImageFormat(pFileData, FileSize)) + { + return (*Iter); + } + } + + // Es konnte kein passender BS_ImageLoader gefunden werden. + BS_LOG_ERRORLN("Could not find suitable image loader for image data."); + return NULL; +} diff --git a/engines/sword25/gfx/image/imageloader.h b/engines/sword25/gfx/image/imageloader.h new file mode 100755 index 0000000000..952c36b093 --- /dev/null +++ b/engines/sword25/gfx/image/imageloader.h @@ -0,0 +1,358 @@ +// ----------------------------------------------------------------------------- +// This file is part of Broken Sword 2.5 +// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer +// +// Broken Sword 2.5 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 +// (at your option) any later version. +// +// Broken Sword 2.5 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 Broken Sword 2.5; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +// ----------------------------------------------------------------------------- + +/* + BS_ImageLoader + -------------- + + Autor: Malte Thiesen +*/ + +#ifndef BS_IMAGELOADER_H +#define BS_IMAGELOADER_H + +// Includes +#include "kernel/bs_stdint.h" +#include "kernel/common.h" +#include "../graphicengine.h" + +// Die folgenden Header vertragen sich nicht mit der Memoryleak-Detection, daher wird sie kurzzeitig deaktiviert +#include "kernel/memlog_off.h" +#include <list> +#include "kernel/memlog_on.h" + +/** + @brief Über die statischen Methoden dieser Klasse werden alle unterstützten Bildformate geladen. + + Zum Laden von Bildern wird die #LoadImage-Methode benutzt. + + Außerdem stellt diese Klasse das Interface da, das alle Klassen implementieren müssen, die Bildformate einlesen.<br> + Zur Unterstützung eines neuen Bildformates muss folgendermaßen vorgegangen werden: + - Erzeugen einer neuen von #BS_ImageLoader abgeleiteten Klasse, die die Methoden #IsCorrectImageFormat und #DecodeImage impelementiert. + - Die Klasse muss eine statische Methode haben, die eine Instanz von ihr erzeugt und einen Pointer darauf zurückgibt. + - Diese Methode muss in der Liste in der Datei imageloader_ids.h eingetragen werden. + - Die Klasse muss JEDES Eingabebild seines Bildformates in die folgenden Farbformate konvertieren können: + - BS_GraphicEngine::CF_RGB16 + - BS_GraphicEngine::CF_RGB15 + - BS_GraphicEngine::CF_RGB16_INTERLEAVED + - BS_GraphicEngine::CF_RGB15_INTERLEAVED + - BS_GraphicEngine::CF_ARGB32 + - BS_GraphicEngine::CF_BGRA32 + - Zum Konvertieren der Bilddaten können die Hilfsmethoden dieser Klasse benutzt werden, die ARGB Bilddaten in alle benötigten + Farbformate konvertieren. +*/ +class BS_ImageLoader +{ +public: + + //@{ + /** @name Lade Methoden */ + + /** + @brief Lädt eine Bilddatei. + + Diese Methode kann sämtliche unterstütztem Bildformate lesen. Die Methode erkennt selbstständing um welches Dateiformat es sich + bei der vorliegenden Datei handelt.<br> + Bisher wird nur PNG unterstützt. + + @param pFileData ein Pointer auf die Bilddaten. + @param FileSize die Größe der Bilddaten in Byte. + @param ColorFormat gibt das gewünschte Farbformat an, in das die Bilddaten konvertiert werden sollen.<br> + Folgende Farbformate werden unterstützt: + - BS_GraphicEngine::CF_RGB16 + - BS_GraphicEngine::CF_RGB15 + - BS_GraphicEngine::CF_RGB16_INTERLEAVED + - BS_GraphicEngine::CF_RGB15_INTERLEAVED + - BS_GraphicEngine::CF_ARGB32 + @param pUncompressedData nach erfolgreichen Laden zeigt dieser Pointer auf die enpackten und konvertierten Bilddaten. + @param Width gibt nach erfolgreichen Laden die Breite des geladenen Bildes an. + @param Height gibt nach erfolgreichen Laden die Höhe des geladenen Bildes an. + @param Pitch gibt nach erfolgreichen Laden die Länge einer Bildzeile in Byte an. + @return Gibt false zurück, falls das Laden fehlgeschlagen ist. + @remark Die Größe der Ausgabedaten in Bytes kann wie folgt berechnet werden: Pitch * Height. + @remark Es darf nicht vergessen werden, die Ausgabedaten nach erfolgter Benutzung mit delete freizugeben. + */ + static bool LoadImage(const char* pFileData, unsigned int FileSize, + BS_GraphicEngine::COLOR_FORMATS ColorFormat, + char*& pUncompressedData, + int& Width, int& Height, + int& Pitch); + + /** + @brief Liest die Bildeigenschaften eines Bildes aus. + + @param pFileData ein Pointer auf die Bilddaten. + @param FileSize die Größe des Bilddaten in Byte. + @param ColorFormat enthält nach einem erfolgreichem Aufruf das Farbformat des Bildes. + @param Width enthält nach einem erfolgreichem Aufruf die Breite des Bildes in Pixeln. + @param Height enthält nach einem erfolgreichem Aufruf die Höhe des Bildes in Pixeln. + @return Gibt false zurück, wenn die Bildeigenschaften nicht ausgelesen werden konnten. + @remark Es darf nicht vergessen werden, die Ausgabedaten nach erfolgter Benutzung mit delete freizugeben. + */ + static bool ExtractImageProperties(const char* pFileData, unsigned int FileSize, + BS_GraphicEngine::COLOR_FORMATS& ColorFormat, + int& Width, int& Height); + //@} + +protected: + + // Protected Konstruktor, damit Instanzen dieser Klasse nur von BS_ImageLoader-Objekten erstellt werden können + /** + @brief Der Standardkonstruktor. + + Dieser Konstruktor registriert alle Instanzen von #BS_ImageLoader-Klassen in einer Liste.<br> + Diese Liste enthält jeweils eine Instanz jedes #BS_ImageLoader und wird benutzt um beliebige Bilddateien einem Loader zuzuordnen. + @remark Dieser Konstruktor ist protected damit nur #BS_ImageLoader-Objekte diese Klasse instanziieren können. + */ + BS_ImageLoader() + { + // Klasse registrieren + _ImageLoaderList.push_front(this); + } + + //@{ + /** @name Abstrakte Methoden */ + + /** + @brief Gibt an, ob der #BS_ImageLoader ein Bild lesen kann. + @param pFileData ein Pointer auf die kompletten Daten des Bildes. + @param FileSize die Größe der Daten in Byte. + @return Gibt true zurück, wenn der #BS_ImageLoader das Bild lesen kann, ansonsten false. + @remark Diese Methode muss von allen BS_ImageLoader Klassen implementiert werden. + */ + virtual bool IsCorrectImageFormat(const char* pFileData, unsigned int FileSize) = 0; + + /** + @brief Lädt eine Bilddatei. + @param pFileData ein Pointer auf die Bilddaten. + @param FileSize die Größe der Bilddaten in Byte. + @param ColorFormat gibt das gewünschte Farbformat an, in das die Bilddaten konvertiert werden sollen.<br> + Folgende Farbformate werden unterstützt: + - BS_GraphicEngine::CF_RGB16 + - BS_GraphicEngine::CF_RGB15 + - BS_GraphicEngine::CF_RGB16_INTERLEAVED + - BS_GraphicEngine::CF_RGB15_INTERLEAVED + - BS_GraphicEngine::CF_ARGB32 + @param pUncompressedData nach erfolgreichen Laden zeigt dieser Pointer auf die enpackten und konvertierten Bilddaten. + @param Width gibt nach erfolgreichen Laden die Breite des geladenen Bildes an. + @param Height gibt nach erfolgreichen Laden die Höhe des geladenen Bildes an. + @param Pitch gibt nach erfolgreichen Laden die Länge einer Bildzeile in Byte an. + @return Gibt false zurück, falls das Laden fehlgeschlagen ist. + @remark Die Größe der Ausgabedaten in Bytes kann wie folgt berechnet werden: Pitch * Height. + @remark Es darf nicht vergessen werden, die Ausgabedaten nach erfolgter Benutzung mit delete freizugeben. + @remark Diese Methode muss von allen BS_ImageLoader Klassen implementiert werden. + */ + virtual bool DecodeImage(const char* pFileData, unsigned int FileSize, + BS_GraphicEngine::COLOR_FORMATS ColorFormat, + char*& pUncompressedData, + int& Width, int& Height, + int& Pitch) = 0; + + /** + @brief Liest die Bildeigenschaften aus. + @param pFileData ein Pointer auf die Bilddaten. + @param FileSize die Größe des Bilddaten in Byte. + @param ColorFormat enthält nach einem erfolgreichem Aufruf das Farbformat des Bildes. + @param Width enthält nach einem erfolgreichem Aufruf die Breite des Bildes in Pixeln. + @param Height enthält nach einem erfolgreichem Aufruf die Höhe des Bildes in Pixeln. + @return Gibt false zurück, wenn die Bildeigenschaften nicht ausgelesen werden konnten. + @remark Es darf nicht vergessen werden, die Ausgabedaten nach erfolgter Benutzung mit delete freizugeben. + @remark Diese Methode muss von allen BS_ImageLoader Klassen implementiert werden. + */ + virtual bool ImageProperties(const char* pFileData, unsigned int FileSize, + BS_GraphicEngine::COLOR_FORMATS& ColorFormat, + int& Width, int& Height) = 0; + + //@} + + //@{ + /** @name Konvertierungsmethoden */ + + /** + @brief Konvertiert eine Bildzeile mit ARGB Pixeldaten in das BS_GraphicEngine::CF_RGB16 Farbformat. + @param pSrcData ein Pointer auf die Quelldaten. + @param pDestData ein Pointer auf den Zielpuffern. + @param Width die Anzahl der Pixel in der Bildzeile. + @remark Es gilt zu beachten, dass der Zielpuffer ausreichend groß ist.<br> + Es sind mindestens Width * 2 Byte notwendig. + */ + static void RowARGB32ToRGB16(unsigned char* pSrcData, unsigned char* pDestData, unsigned int Width) + { + for (unsigned int i = 0; i < Width; i++) + { + ((uint16_t*)pDestData)[i] = ((pSrcData[2] >> 3) << 11) | ((pSrcData[1] >> 2) << 5) | (pSrcData[0] >> 3); + pSrcData += 4; + } + } + + /** + @brief Konvertiert eine Bildzeile mit ARGB Pixeldaten in das BS_GraphicEngine::CF_RGB15 Farbformat. + @param pSrcData ein Pointer auf die Quelldaten. + @param pDestData ein Pointer auf den Zielpuffern. + @param Width die Anzahl der Pixel in der Bildzeile. + @remark Es gilt zu beachten, dass der Zielpuffer ausreichend groß ist.<br> + Es sind mindestens Width * 2 Byte notwendig. + */ + static void RowARGB32ToRGB15(unsigned char* pSrcData, unsigned char* pDestData, unsigned int Width) + { + for (unsigned int i = 0; i < Width; i++) + { + ((uint16_t*)pDestData)[i] = ((pSrcData[2] >> 3) << 10) | ((pSrcData[1] >> 3) << 5) | (pSrcData[0] >> 3); + pSrcData += 4; + } + } + + /** + @brief Konvertiert eine Bildzeile mit ARGB Pixeldaten in das BS_GraphicEngine::CF_RGB16_INTERLEAVED Farbformat. + @param pSrcData ein Pointer auf die Quelldaten. + @param pDestData ein Pointer auf den Zielpuffern. + @param Width die Anzahl der Pixel in der Bildzeile. + @remark Es gilt zu beachten, dass der Zielpuffer ausreichend groß sein muss.<br> + Es sind mindestens ((Width + 3) / 4) * 12 Byte notwendig. + */ + static void RowARGB32ToRGB16_INTERLEAVED(unsigned char* pSrcData, unsigned char* pDestData, unsigned int Width) + { + // Die Pixelblöcke erstellen, dabei werden immer jeweils 4 Pixel zu einem Block zusammengefasst + unsigned int BlockFillCount = 0; + unsigned int AlphaBlock = 0; + for (unsigned int i = 0; i < Width; i++) + { + // Alphawert in den Alphablock schreiben + AlphaBlock = (AlphaBlock >> 8) | (pSrcData[BlockFillCount * 4 + 3] << 24); + + // Füllstand der Pixelblockes aktualisieren + BlockFillCount++; + + // Sobald 4 Alphawerte gesammelt wurden, oder die Zeile zu Ende ist wird der Pixelblock in den Zielpuffer geschrieben + if (BlockFillCount == 4 || i == (Width - 1)) + { + // Falls der AlphaBlock nicht ganz gefüllt ist muss geshiftet werden um sicherzustellen, dass die Alphawerte + // "left aligned" sind. + AlphaBlock >>= (4 - BlockFillCount) * 8; + + // Alphablock schreiben + *((unsigned int*)pDestData) = AlphaBlock; + pDestData += 4; + + // Pixel konvertieren und schreiben + RowARGB32ToRGB16(pSrcData, pDestData, BlockFillCount); + + // Pointer auf den nächsten Platz im Zielpuffer setzen + pDestData += 8; + + // Pointer auf die nächsten 4 Pixel im Quellpuffer setzen + pSrcData += 16; + + // Neuen Pixelblock beginnen + BlockFillCount = 0; + } + } + } + + /** + @brief Konvertiert eine Bildzeile mit ARGB Pixeldaten in das BS_GraphicEngine::CF_RGB15_INTERLEAVED Farbformat. + @param pSrcData ein Pointer auf die Quelldaten. + @param pDestData ein Pointer auf den Zielpuffern. + @param Width die Anzahl der Pixel in der Bildzeile. + @remark Es gilt zu beachten, dass der Zielpuffer ausreichend groß ist.<br> + Es sind mindestens (Width / 4 + Width % 4) * 3 Byte notwendig. + */ + static void RowARGB32ToRGB15_INTERLEAVED(unsigned char* pSrcData, unsigned char* pDestData, unsigned int Width) + { + // Die Pixelblöcke erstellen, dabei werden immer jeweils 4 Pixel zu einem Block zusammengefasst + unsigned int BlockFillCount = 0; + unsigned int AlphaBlock = 0; + for (unsigned int i = 0; i < Width; i++) + { + // Alphawert in den Alphablock schreiben + AlphaBlock = (AlphaBlock >> 8) | (pSrcData[BlockFillCount * 4 + 3] << 24); + + // Füllstand der Pixelblockes aktualisieren + BlockFillCount++; + + // Sobald 4 Alphawerte gesammelt wurden, oder die Zeile zu Ende ist wird der Pixelblock in den Zielpuffer geschrieben + if (BlockFillCount == 4 || i == (Width - 1)) + { + // Falls der AlphaBlock nicht ganz gefüllt ist muss geshiftet werden um sicherzustellen, dass die Alphawerte + // "left aligned" sind. + AlphaBlock >>= (4 - BlockFillCount) * 8; + + // Alphablock schreiben + *((unsigned int*)pDestData) = AlphaBlock; + pDestData += 4; + + // Pixel konvertieren und schreiben + RowARGB32ToRGB15(pSrcData, pDestData, BlockFillCount); + + // Pointer auf den nächsten Platz im Zielpuffer setzen + pDestData += 8; + + // Pointer auf die nächsten 4 Pixel im Quellpuffer setzen + pSrcData += 16; + + // Neuen Pixelblock beginnen + BlockFillCount = 0; + } + } + } + + /** + @brief Konvertiert eine Bildzeile mit ARGB Pixeldaten in das BS_GraphicEngine::CF_BGRA32 Farbformat. + @param pSrcData ein Pointer auf die Quelldaten. + @param pDestData ein Pointer auf den Zielpuffern. + @param Width die Anzahl der Pixel in der Bildzeile. + */ + static void RowARGB32ToABGR32(unsigned char* pSrcData, unsigned char* pDestData, unsigned int Width) + { + for (unsigned int i = 0; i < Width; ++i) + { + *pDestData++ = pSrcData[2]; + *pDestData++ = pSrcData[1]; + *pDestData++ = pSrcData[0]; + *pDestData++ = pSrcData[3]; + + pSrcData += 4; + } + } + +private: + + /** + @brief Erzeugt je eine Instanz aller BS_ImageLoader Klassen und fügt diese in eine interne Liste ein. Diese werden dann beim + Laden von Bildern benutzt. + @remark Die Klassen müssen in der Datei imageloader_ids.h eingetragen sein, damit sie an dieser Stelle berücksichtigt werden. + */ + static void _InitializeLoaderList(); + + /** + @brief Zerstört alle Instanzen von BS_ImageLoader Klassen, die in dieser Klasse registriert sind. + */ + static void _DeinitializeLoaderList(); + + /** + @brief Sucht zu Bilddaten ein BS_ImageLoader Objekt, dass die Bilddaten dekodieren kann. + @return Gibt einen Pointer auf ein passendes BS_ImageLoader Objekt zurück, oder NULL, wenn kein passendes Objekt gefunden wurde. + */ + static BS_ImageLoader* BS_ImageLoader::_FindSuitableImageLoader(const char* pFileData, unsigned int FileSize); + + static std::list<BS_ImageLoader*> _ImageLoaderList; // Die Liste aller BS_ImageLoader-Objekte + static bool _ImageLoaderListInitialized; // Gibt an, ob die Liste schon intialisiert wurde +}; + +#endif diff --git a/engines/sword25/gfx/image/imageloader_ids.h b/engines/sword25/gfx/image/imageloader_ids.h new file mode 100755 index 0000000000..4564996a0f --- /dev/null +++ b/engines/sword25/gfx/image/imageloader_ids.h @@ -0,0 +1,44 @@ +// ----------------------------------------------------------------------------- +// This file is part of Broken Sword 2.5 +// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer +// +// Broken Sword 2.5 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 +// (at your option) any later version. +// +// Broken Sword 2.5 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 Broken Sword 2.5; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +// ----------------------------------------------------------------------------- + +/* + imageloader_ids.h + ----------------- + In dieser Datei sind alle ImageLoader verzeichnet. + JEDER neuer ImageLoader muss hier eingetragen werden, ansonsten wird er beim Laden eines Bildes nicht berücksichtigt. + + Autor: Malte Thiesen +*/ + +#include "imageloader.h" + +// Die Headerdateien der ImageLoader müssen hier eingebunden werden +#include "pngloader.h" +#include "b25sloader.h" + +// Die Tabelle enthält Pointer auf statische Member-Funktionen innerhalb der Klassen, die eine Instanz der Klasse +// erzeugen +typedef BS_ImageLoader* (*BS_IMAGELOADER_NEW)(); +const BS_IMAGELOADER_NEW BS_IMAGELOADER_IDS[] = +{ + BS_PNGLoader::CreateInstance, + BS_B25SLoader::CreateInstance, +}; +const int BS_IMAGELOADER_COUNT = sizeof(BS_IMAGELOADER_IDS) / sizeof(BS_IMAGELOADER_NEW); + diff --git a/engines/sword25/gfx/image/pngloader.cpp b/engines/sword25/gfx/image/pngloader.cpp new file mode 100755 index 0000000000..3815f4e4ff --- /dev/null +++ b/engines/sword25/gfx/image/pngloader.cpp @@ -0,0 +1,389 @@ +// ----------------------------------------------------------------------------- +// This file is part of Broken Sword 2.5 +// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer +// +// Broken Sword 2.5 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 +// (at your option) any later version. +// +// Broken Sword 2.5 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 Broken Sword 2.5; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +// ----------------------------------------------------------------------------- + +// ----------------------------------------------------------------------------- +// Includes +// ----------------------------------------------------------------------------- + +#include "image.h" +#include "pngloader.h" +#include "util/libpng/png.h" + +#define BS_LOG_PREFIX "PNGLOADER" + +// ----------------------------------------------------------------------------- +// Konstruktor / Destruktor +// ----------------------------------------------------------------------------- + +BS_PNGLoader::BS_PNGLoader() +{ +} + +// ----------------------------------------------------------------------------- +// Laden +// ----------------------------------------------------------------------------- + +static void png_user_read_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + memcpy(data, (char*)png_ptr->io_ptr, length); + png_ptr->io_ptr = (void*)((png_size_t)png_ptr->io_ptr + length); +} + +// ----------------------------------------------------------------------------- + +bool BS_PNGLoader::DoDecodeImage(const char * FileDataPtr, unsigned int FileSize, BS_GraphicEngine::COLOR_FORMATS ColorFormat, char * & UncompressedDataPtr, + int & Width, int & Height, int & Pitch) +{ + png_structp png_ptr = NULL; + png_infop info_ptr = NULL; + png_bytep RawDataBuffer = NULL; + png_bytep* pRowPtr = NULL; + + int BitDepth; + int ColorType; + int InterlaceType; + int i; + + // Zielfarbformat überprüfen + if (ColorFormat != BS_GraphicEngine::CF_RGB16 && + ColorFormat != BS_GraphicEngine::CF_RGB15 && + ColorFormat != BS_GraphicEngine::CF_RGB16_INTERLEAVED && + ColorFormat != BS_GraphicEngine::CF_RGB15_INTERLEAVED && + ColorFormat != BS_GraphicEngine::CF_ARGB32 && + ColorFormat != BS_GraphicEngine::CF_ABGR32) + { + BS_LOG_ERRORLN("Illegal or unsupported color format."); + return false; + } + + try + { + // PNG Signatur überprüfen + if (!png_check_sig(reinterpret_cast<png_bytep>(const_cast<char *>(FileDataPtr)), 8)) + { + throw(0); + } + + // Die beiden PNG Strukturen erstellen + png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!png_ptr) + { + BS_LOG_ERRORLN("Could not create libpng read struct."); + throw(0); + } + + info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) + { + BS_LOG_ERRORLN("Could not create libpng info struct."); + throw(0); + } + + // Rücksprungpunkt setzen. Wird im Falle eines Fehlers angesprungen. + if (setjmp(png_jmpbuf(png_ptr))) throw(0); + + // Alternative Lesefunktion benutzen + png_set_read_fn(png_ptr, (void*)FileDataPtr, png_user_read_data); + + // PNG Header einlesen + png_read_info(png_ptr, info_ptr); + + // PNG Informationen auslesen + png_get_IHDR(png_ptr, info_ptr, (unsigned long*)&Width, (unsigned long*)&Height, &BitDepth, &ColorType, &InterlaceType, NULL, NULL); + + // Pitch des Ausgabebildes berechnen + Pitch = BS_GraphicEngine::CalcPitch(ColorFormat, Width); + + // Speicher für die endgültigen Bilddaten reservieren + // Dieses geschieht vor dem reservieren von Speicher für temporäre Bilddaten um die Fragmentierung des Speichers gering zu halten + UncompressedDataPtr = new char[Pitch * Height]; + if (!UncompressedDataPtr) + { + BS_LOG_ERRORLN("Could not allocate memory for output image."); + throw(0); + } + + // Bilder jeglicher Farbformate werden zunächst in ARGB Bilder umgewandelt + if (BitDepth == 16) + png_set_strip_16(png_ptr); + if (ColorType == PNG_COLOR_TYPE_PALETTE) + png_set_expand(png_ptr); + if (BitDepth < 8) + png_set_expand(png_ptr); + if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) + png_set_expand(png_ptr); + if (ColorType == PNG_COLOR_TYPE_GRAY || + ColorType == PNG_COLOR_TYPE_GRAY_ALPHA) + png_set_gray_to_rgb(png_ptr); + + png_set_bgr(png_ptr); + + if (ColorType != PNG_COLOR_TYPE_RGB_ALPHA) + png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER); + + // Nachdem die Transformationen registriert wurden, werden die Bilddaten erneut eingelesen + png_read_update_info(png_ptr, info_ptr); + png_get_IHDR(png_ptr, info_ptr, (unsigned long*)&Width, (unsigned long*)&Height, &BitDepth, &ColorType, NULL, NULL, NULL); + + // PNGs ohne Interlacing werden Zeilenweise eingelesen + if (InterlaceType == PNG_INTERLACE_NONE) + { + // Speicher für eine Bildzeile reservieren + RawDataBuffer = new png_byte[png_get_rowbytes(png_ptr, info_ptr)]; + if (!RawDataBuffer) + { + BS_LOG_ERRORLN("Could not allocate memory for row buffer."); + throw(0); + } + + // Bilddaten zeilenweise einlesen und in das gewünschte Zielformat konvertieren + for (i = 0; i < Height; i++) + { + // Zeile einlesen + png_read_row(png_ptr, RawDataBuffer, NULL); + + // Zeile konvertieren + switch (ColorFormat) + { + case BS_GraphicEngine::CF_RGB16: + RowARGB32ToRGB16((unsigned char*)RawDataBuffer, + (unsigned char*)&UncompressedDataPtr[i * Pitch], + Width); + break; + + case BS_GraphicEngine::CF_RGB15: + RowARGB32ToRGB15((unsigned char*)RawDataBuffer, + (unsigned char*)&UncompressedDataPtr[i * Pitch], + Width); + break; + + case BS_GraphicEngine::CF_RGB16_INTERLEAVED: + RowARGB32ToRGB16_INTERLEAVED((unsigned char*)RawDataBuffer, + (unsigned char*)&UncompressedDataPtr[i * Pitch], + Width); + break; + + case BS_GraphicEngine::CF_RGB15_INTERLEAVED: + RowARGB32ToRGB15_INTERLEAVED((unsigned char*)RawDataBuffer, + (unsigned char*)&UncompressedDataPtr[i * Pitch], + Width); + break; + + case BS_GraphicEngine::CF_ARGB32: + memcpy(&UncompressedDataPtr[i * Pitch], + RawDataBuffer, + Pitch); + break; + + case BS_GraphicEngine::CF_ABGR32: + RowARGB32ToABGR32((unsigned char*)RawDataBuffer, + (unsigned char*)&UncompressedDataPtr[i * Pitch], + Width); + break; + + default: + BS_ASSERT(false); + } + } + } + // PNGs mit Interlacing werden an einem Stück eingelesen + else + { + // Speicher für das komplette Bild reservieren + RawDataBuffer = new png_byte[png_get_rowbytes(png_ptr, info_ptr) * Height]; + if (!RawDataBuffer) + { + BS_LOG_ERRORLN("Could not allocate memory for raw image buffer."); + throw(0); + } + + // Speicher für die Rowpointer reservieren + pRowPtr = new png_bytep[Height]; + if (!pRowPtr) + { + BS_LOG_ERRORLN("Could not allocate memory for row pointers."); + throw(0); + } + + // Alle Rowpointer mit den richtigen Offsets initialisieren + for (i = 0; i < Height; i++) + pRowPtr[i] = RawDataBuffer + i * png_get_rowbytes(png_ptr, info_ptr); + + // Bild einlesen + png_read_image(png_ptr, pRowPtr); + + // Bilddaten zeilenweise in das gewünschte Ausgabeformat konvertieren + switch (ColorFormat) + { + case BS_GraphicEngine::CF_RGB16: + for (i = 0; i < Height; i++) + RowARGB32ToRGB16((unsigned char*)(&RawDataBuffer[i * png_get_rowbytes(png_ptr, info_ptr)]), + (unsigned char*)&UncompressedDataPtr[i * Pitch], + Width); + break; + + case BS_GraphicEngine::CF_RGB15: + for (i = 0; i < Height; i++) + RowARGB32ToRGB15((unsigned char*)(&RawDataBuffer[i * png_get_rowbytes(png_ptr, info_ptr)]), + (unsigned char*)&UncompressedDataPtr[i * Pitch], + Width); + break; + + case BS_GraphicEngine::CF_RGB16_INTERLEAVED: + for (i = 0; i < Height; i++) + RowARGB32ToRGB16_INTERLEAVED((unsigned char*)(&RawDataBuffer[i * png_get_rowbytes(png_ptr, info_ptr)]), + (unsigned char*)&UncompressedDataPtr[i * Pitch], + Width); + break; + + case BS_GraphicEngine::CF_RGB15_INTERLEAVED: + for (i = 0; i < Height; i++) + RowARGB32ToRGB15_INTERLEAVED((unsigned char*)(&RawDataBuffer[i * png_get_rowbytes(png_ptr, info_ptr)]), + (unsigned char*)&UncompressedDataPtr[i * Pitch], + Width); + break; + + case BS_GraphicEngine::CF_ARGB32: + for (i = 0; i < Height; i++) + memcpy(&UncompressedDataPtr[i * Pitch], + &RawDataBuffer[i * png_get_rowbytes(png_ptr, info_ptr)], + Pitch); + break; + } + } + + // Die zusätzlichen Daten am Ende des Bildes lesen + png_read_end(png_ptr, NULL); + + // Die Strukturen freigeben + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + + // Temporäre Buffer freigeben + delete[] pRowPtr; + delete[] RawDataBuffer; + } + + catch(int) + { + delete[] pRowPtr; + delete[] RawDataBuffer; + if (png_ptr) png_destroy_read_struct(&png_ptr, NULL, NULL); + if (info_ptr) png_destroy_read_struct(NULL, &info_ptr, NULL); + + // Der Funktionsaufruf war nicht erfolgreich + return false; + } + + // Der Funktionsaufruf war erfolgreich + return true; +} + +// ----------------------------------------------------------------------------- + +bool BS_PNGLoader::DecodeImage(const char * FileDataPtr, unsigned int FileSize, BS_GraphicEngine::COLOR_FORMATS ColorFormat, char * & UncompressedDataPtr, + int & Width, int & Height, int & Pitch) +{ + return DoDecodeImage(FileDataPtr, FileSize, ColorFormat, UncompressedDataPtr, Width, Height, Pitch); +} + +// ----------------------------------------------------------------------------- + +bool BS_PNGLoader::DoImageProperties(const char * FileDataPtr, unsigned int FileSize, BS_GraphicEngine::COLOR_FORMATS & ColorFormat, int & Width, int & Height) +{ + // PNG Signatur überprüfen + if (!DoIsCorrectImageFormat(FileDataPtr, FileSize)) return false; + + png_structp png_ptr = NULL; + png_infop info_ptr = NULL; + try + { + // Die beiden PNG Strukturen erstellen + png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!png_ptr) + { + BS_LOG_ERRORLN("Could not create libpng read struct."); + throw(0); + } + + info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) + { + BS_LOG_ERRORLN("Could not create libpng info struct."); + throw(0); + } + + // Alternative Lesefunktion benutzen + png_set_read_fn(png_ptr, (void*)FileDataPtr, png_user_read_data); + + // PNG Header einlesen + png_read_info(png_ptr, info_ptr); + + // PNG Informationen auslesen + int BitDepth; + int ColorType; + png_get_IHDR(png_ptr, info_ptr, (unsigned long*)&Width, (unsigned long*)&Height, &BitDepth, &ColorType, NULL, NULL, NULL); + + // PNG-ColorType in BS ColorFormat konvertieren. + if (ColorType & PNG_COLOR_MASK_ALPHA || png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) + ColorFormat = BS_GraphicEngine::CF_ARGB32; + else + ColorFormat = BS_GraphicEngine::CF_RGB24; + + // Die Strukturen freigeben + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + } + + catch (int) + { + if (png_ptr) png_destroy_read_struct(&png_ptr, NULL, NULL); + if (info_ptr) png_destroy_read_struct(NULL, &info_ptr, NULL); + + // Der Funktionsaufruf war nicht erfolgreich + return false; + } + + return true; + +} + +// ----------------------------------------------------------------------------- + +bool BS_PNGLoader::ImageProperties(const char* FileDataPtr, unsigned int FileSize, BS_GraphicEngine::COLOR_FORMATS & ColorFormat, int & Width, int & Height) +{ + return DoImageProperties(FileDataPtr, FileSize, ColorFormat, Width, Height); +} + +// ----------------------------------------------------------------------------- +// Header überprüfen +// ----------------------------------------------------------------------------- + +bool BS_PNGLoader::DoIsCorrectImageFormat(const char * FileDataPtr, unsigned int FileSize) +{ + if (FileSize > 8) + return png_check_sig((unsigned char*)FileDataPtr, 8) ? true : false; + else + return false; +} + +// ----------------------------------------------------------------------------- + +bool BS_PNGLoader::IsCorrectImageFormat(const char* FileDataPtr, unsigned int FileSize) +{ + return DoIsCorrectImageFormat(FileDataPtr, FileSize); +} diff --git a/engines/sword25/gfx/image/pngloader.h b/engines/sword25/gfx/image/pngloader.h new file mode 100755 index 0000000000..5fdedf49fc --- /dev/null +++ b/engines/sword25/gfx/image/pngloader.h @@ -0,0 +1,64 @@ +// ----------------------------------------------------------------------------- +// This file is part of Broken Sword 2.5 +// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer +// +// Broken Sword 2.5 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 +// (at your option) any later version. +// +// Broken Sword 2.5 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 Broken Sword 2.5; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +// ----------------------------------------------------------------------------- + +/* + BS_PNGLoader + ------------ + BS_ImageLoader-Klasse zum Laden von PNG-Dateien + + Autor: Malte Thiesen +*/ + +#ifndef BS_PNGLOADER2_H +#define BS_PNGLOADER2_H + +// Includes +#include "kernel/common.h" +#include "imageloader.h" + +// Klassendefinition +class BS_PNGLoader : public BS_ImageLoader +{ +public: + static BS_ImageLoader* CreateInstance() + { + #include "kernel/memlog_off.h" + return (BS_ImageLoader*) new BS_PNGLoader(); + #include "kernel/memlog_on.h" + } + + // Alle virtuellen Methoden von BS_ImageLoader sind hier als static-Methode implementiert, damit sie von BS_B25SLoader aufgerufen werden können. + // Die virtuellen Methoden rufen diese Methoden auf. + static bool DoIsCorrectImageFormat(const char * FileDataPtr, unsigned int FileSize); + static bool DoDecodeImage(const char * FileDataPtr, unsigned int FileSize, BS_GraphicEngine::COLOR_FORMATS ColorFormat, char * & UncompressedDataPtr, + int & Width, int & Height, int & Pitch); + static bool DoImageProperties(const char * FileDataPtr, unsigned int FileSize, BS_GraphicEngine::COLOR_FORMATS & ColorFormat, int & Width, int & Height); + +protected: + BS_PNGLoader(); + bool DecodeImage(const char * pFileData, unsigned int FileSize, + BS_GraphicEngine::COLOR_FORMATS ColorFormat, + char * & pUncompressedData, + int & Width, int & Height, + int & Pitch); + bool IsCorrectImageFormat(const char * FileDataPtr, unsigned int FileSize); + bool ImageProperties(const char * FileDatePtr, unsigned int FileSize, BS_GraphicEngine::COLOR_FORMATS & ColorFormat, int & Width, int & Height); +}; + +#endif diff --git a/engines/sword25/gfx/image/vectorimage.cpp b/engines/sword25/gfx/image/vectorimage.cpp new file mode 100755 index 0000000000..035d572149 --- /dev/null +++ b/engines/sword25/gfx/image/vectorimage.cpp @@ -0,0 +1,571 @@ +// ----------------------------------------------------------------------------- +// This file is part of Broken Sword 2.5 +// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer +// +// Broken Sword 2.5 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 +// (at your option) any later version. +// +// Broken Sword 2.5 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 Broken Sword 2.5; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +// ----------------------------------------------------------------------------- + +// ----------------------------------------------------------------------------- +// Includes +// ----------------------------------------------------------------------------- + +#include "kernel/bs_stdint.h" +#include "vectorimage.h" +#include <vector> +#include <stdexcept> + +#include "agg_bounding_rect.h" + +using namespace std; + +#define BS_LOG_PREFIX "VECTORIMAGE" + + +// ----------------------------------------------------------------------------- +// SWF Datentypen +// ----------------------------------------------------------------------------- + +typedef uint8_t u8; +typedef uint32_t u32; +typedef uint16_t u16; +typedef int16_t s32; + + +// ----------------------------------------------------------------------------- +// Bitstream Hilfsklasse +// ----------------------------------------------------------------------------- +// Das Parsen von SWF-Dateien erfordert sowohl bitweises Auslesen als auch an +// Bytegrenzen ausgerichtetes Lesen. +// Diese Klasse ist speziell dafür ausgestattet. +// ----------------------------------------------------------------------------- + +class BS_VectorImage::SWFBitStream +{ +public: + SWFBitStream(const unsigned char * pData, unsigned int DataSize) : + m_Pos(pData), m_End(pData + DataSize), m_WordMask(0) + {} + + inline u32 GetBits(unsigned int BitCount) + { + if (BitCount == 0 || BitCount > 32) + { + throw(runtime_error("SWFBitStream::GetBits() must read at least 1 and at most 32 bits at a time")); + } + + u32 value = 0; + while (BitCount) + { + if (m_WordMask == 0) FlushByte(); + + value <<= 1; + value |= ((m_Word & m_WordMask) != 0) ? 1 : 0; + m_WordMask >>= 1; + + --BitCount; + } + + return value; + } + + inline s32 GetSignedBits(unsigned int BitCount) + { + // Bits einlesen + u32 Temp = GetBits(BitCount); + + // Falls das Sign-Bit gesetzt ist, den Rest des Rückgabewertes mit 1-Bits auffüllen (Sign Extension) + if (Temp & 1 << (BitCount - 1)) + return (0xffffffff << BitCount) | Temp; + else + return Temp; + } + + inline u32 GetU32() + { + u32 Byte1 = GetU8(); + u32 Byte2 = GetU8(); + u32 Byte3 = GetU8(); + u32 Byte4 = GetU8(); + + return Byte1 | (Byte2 << 8) | (Byte3 << 16) | (Byte4 << 24); + } + + inline u16 GetU16() + { + u32 Byte1 = GetU8(); + u32 Byte2 = GetU8(); + + return Byte1 | (Byte2 << 8); + } + + inline u8 GetU8() + { + FlushByte(); + u8 Value = m_Word; + m_WordMask = 0; + FlushByte(); + + return Value; + } + + inline void FlushByte() + { + if (m_WordMask != 128) + { + if (m_Pos >= m_End) + { + throw(runtime_error("Attempted to read past end of file")); + } + else + { + m_Word = *m_Pos++; + m_WordMask = 128; + } + } + } + + inline void SkipBytes(unsigned int SkipLength) + { + FlushByte(); + if (m_Pos + SkipLength >= m_End) + { + throw(runtime_error("Attempted to read past end of file")); + } + else + { + m_Pos += SkipLength; + m_Word = *(m_Pos - 1); + } + } + +private: + const unsigned char * m_Pos; + const unsigned char * m_End; + + u8 m_Word; + unsigned int m_WordMask; +}; + + +// ----------------------------------------------------------------------------- +// Konstanten und Hilfsfunktionen +// ----------------------------------------------------------------------------- + +namespace +{ + // ----------------------------------------------------------------------------- + // Konstanten + // ----------------------------------------------------------------------------- + + const u32 MAX_ACCEPTED_FLASH_VERSION = 3; // Die höchste Flash-Dateiversion, die vom Lader akzeptiert wird + + + // ----------------------------------------------------------------------------- + // Konvertiert SWF-Rechteckdaten in einem Bitstrom in BS_Rect-Objekte + // ----------------------------------------------------------------------------- + + BS_Rect FlashRectToBSRect(BS_VectorImage::SWFBitStream & bs) + { + bs.FlushByte(); + + // Feststellen mit wie vielen Bits die einzelnen Komponenten kodiert sind + u32 BitsPerValue = bs.GetBits(5); + + // Die einzelnen Komponenten einlesen + s32 XMin = bs.GetSignedBits(BitsPerValue); + s32 XMax = bs.GetSignedBits(BitsPerValue); + s32 YMin = bs.GetSignedBits(BitsPerValue); + s32 YMax = bs.GetSignedBits(BitsPerValue); + + return BS_Rect(XMin, YMin, XMax + 1, YMax + 1); + } + + + // ----------------------------------------------------------------------------- + // Konvertiert SWF-Farben in AntiGrain Farben + // ----------------------------------------------------------------------------- + + agg::rgba8 FlashColorToAGGRGBA8(unsigned int FlashColor) + { + agg::rgba8 ResultColor((FlashColor >> 16) & 0xff, (FlashColor >> 8) & 0xff, FlashColor & 0xff, FlashColor >> 24); + ResultColor.premultiply(); + return ResultColor; + } + + + // ----------------------------------------------------------------------------- + // Berechnet die Bounding-Box eines BS_VectorImageElement + // ----------------------------------------------------------------------------- + + struct CBBGetId + { + CBBGetId(const BS_VectorImageElement & VectorImageElement_) : VectorImageElement(VectorImageElement_) {} + unsigned operator [] (unsigned i) const { return VectorImageElement.GetPathInfo(i).GetID(); } + const BS_VectorImageElement & VectorImageElement; + }; + + BS_Rect CalculateBoundingBox(const BS_VectorImageElement & VectorImageElement) + { + agg::path_storage Path = VectorImageElement.GetPaths(); + CBBGetId IdSource(VectorImageElement); + + double x1, x2, y1, y2; + agg::bounding_rect(Path, IdSource, 0, VectorImageElement.GetPathCount(), &x1, &y1, &x2, &y2); + + return BS_Rect(static_cast<int>(x1), static_cast<int>(y1), static_cast<int>(x2) + 1, static_cast<int>(y2) + 1); + } +} + + +// ----------------------------------------------------------------------------- +// Konstruktion +// ----------------------------------------------------------------------------- + +BS_VectorImage::BS_VectorImage(const unsigned char * pFileData, unsigned int FileSize, bool & Success) +{ + Success = false; + + // Bitstream-Objekt erzeugen + // Im Folgenden werden die Dateidaten aus diesem ausgelesen. + SWFBitStream bs(pFileData, FileSize); + + try + { + // SWF-Signatur überprüfen + u32 Signature[3]; + Signature[0] = bs.GetU8(); + Signature[1] = bs.GetU8(); + Signature[2] = bs.GetU8(); + if (Signature[0] != 'F' || + Signature[1] != 'W' || + Signature[2] != 'S') + { + BS_LOG_ERRORLN("File is not a valid SWF-file"); + return; + } + + // Versionsangabe überprüfen + u32 Version = bs.GetU8(); + if (Version > MAX_ACCEPTED_FLASH_VERSION) + { + BS_LOG_ERRORLN("File is of version %d. Highest accepted version is %d.", Version, MAX_ACCEPTED_FLASH_VERSION); + return; + } + + // Dateigröße auslesen und mit der tatsächlichen Größe vergleichen + u32 StoredFileSize = bs.GetU32(); + if (StoredFileSize != FileSize) + { + BS_LOG_ERRORLN("File is not a valid SWF-file"); + return; + } + + // SWF-Maße auslesen + BS_Rect MovieRect = FlashRectToBSRect(bs); + + // Framerate und Frameanzahl auslesen + u32 FrameRate = bs.GetU16(); + u32 FrameCount = bs.GetU16(); + + // Tags parsen + // Da wir uns nur für das erste DefineShape-Tag interessieren + bool KeepParsing = true; + while (KeepParsing) + { + // Tags beginnen immer an Bytegrenzen + bs.FlushByte(); + + // Tagtyp und Länge auslesen + u16 TagTypeAndLength = bs.GetU16(); + u32 TagType = TagTypeAndLength >> 6; + u32 TagLength = TagTypeAndLength & 0x3f; + if (TagLength == 0x3f) TagLength = bs.GetU32(); + + switch (TagType) + { + case 2: + // DefineShape + Success = ParseDefineShape(2, bs); + return; + case 22: + // DefineShape2 + Success = ParseDefineShape(2, bs); + return; + case 32: + Success = ParseDefineShape(3, bs); + return; + default: + // Unbekannte Tags ignorieren + bs.SkipBytes(TagLength); + } + } + } + + catch (runtime_error & e) + { + // Fehler loggen und Funktion verlassen + // Success ist somit "false" und signalisiert dem Programmierer, dass die Konstruktion fehlgeschlagen ist. + BS_LOG_ERRORLN("The following exception occured while parsing a SWF-file: %s", e.what()); + return; + } + + // Die Ausführung darf nicht an dieser Stelle ankommen: Entweder es wird ein Shape gefunden, dann wird die Funktion mit vorher verlassen, oder + // es wird keines gefunden, dann tritt eine Exception auf sobald über das Ende der Datei hinaus gelesen wird. + BS_ASSERT(false); +} + +// ----------------------------------------------------------------------------- + +bool BS_VectorImage::ParseDefineShape(unsigned int ShapeType, SWFBitStream & bs) +{ + u32 ShapeID = bs.GetU16(); + + // Bounding Box auslesen + m_BoundingBox = FlashRectToBSRect(bs); + + // Erstes Image-Element erzeugen + m_Elements.resize(1); + + // Styles einlesen + unsigned int NumFillBits; + unsigned int NumLineBits; + if (!ParseStyles(ShapeType, bs, NumFillBits, NumLineBits)) return false; + + unsigned int LineStyle = 0; + unsigned int FillStyle0 = 0; + unsigned int FillStyle1 = 0; + + // Shaperecord parsen + // ------------------ + + bool EndOfShapeDiscovered = false; + while (!EndOfShapeDiscovered) + { + u32 TypeFlag = bs.GetBits(1); + + // Non-Edge Record + if (TypeFlag == 0) + { + // Feststellen welche Parameter gesetzt werden + u32 StateNewStyles = bs.GetBits(1); + u32 StateLineStyle = bs.GetBits(1); + u32 StateFillStyle1 = bs.GetBits(1); + u32 StateFillStyle0 = bs.GetBits(1); + u32 StateMoveTo = bs.GetBits(1); + + // End der Shape-Definition erreicht? + if (!StateNewStyles && !StateLineStyle && !StateFillStyle0 && !StateFillStyle1 && !StateMoveTo) + EndOfShapeDiscovered = true; + // Parameter dekodieren + else + { + s32 MoveDeltaX = 0; + s32 MoveDeltaY = 0; + if (StateMoveTo) + { + u32 MoveToBits = bs.GetBits(5); + MoveDeltaX = bs.GetSignedBits(MoveToBits); + MoveDeltaY = bs.GetSignedBits(MoveToBits); + } + + if (StateFillStyle0) + { + if (NumFillBits > 0) + FillStyle0 = bs.GetBits(NumFillBits); + else + FillStyle0 = 0; + } + + if (StateFillStyle1) + { + if (NumFillBits > 0) + FillStyle1 = bs.GetBits(NumFillBits); + else + FillStyle1 = 0; + } + + if (StateLineStyle) + { + if (NumLineBits) + LineStyle = bs.GetBits(NumLineBits); + else + NumLineBits = 0; + } + + if (StateNewStyles) + { + // An dieser Stelle werden in Flash die alten Style-Definitionen verworfen und mit den neuen überschrieben. + // Es wird ein neues Element begonnen. + m_Elements.resize(m_Elements.size() + 1); + if (!ParseStyles(ShapeType, bs, NumFillBits, NumLineBits)) return false; + } + + // Ein neuen Pfad erzeugen, es sei denn, es wurden nur neue Styles definiert + if (StateLineStyle || StateFillStyle0 || StateFillStyle1 || StateMoveTo) + { + // Letzte Zeichenposition merken, beim Aufruf von start_new_path() wird die Zeichenpostionen auf 0, 0 zurückgesetzt + double LastX = m_Elements.back().m_Paths.last_x(); + double LastY = m_Elements.back().m_Paths.last_y(); + + // Neue Pfadinformation erzeugen + m_Elements.back().m_PathInfos.push_back(BS_VectorPathInfo(m_Elements.back().m_Paths.start_new_path(), LineStyle, FillStyle0, FillStyle1)); + + // Falls eine Bewegung definiert wurde, wird die Zeichenpositionen an die entsprechende Position gesetzt. + // Ansonsten wird die Zeichenposition auf die letzte Zeichenposition gesetzt. + if (StateMoveTo) + m_Elements.back().m_Paths.move_to(MoveDeltaX, MoveDeltaY); + else + m_Elements.back().m_Paths.move_to(LastX, LastY); + } + } + } + // Edge Record + else + { + u32 EdgeFlag = bs.GetBits(1); + u32 NumBits = bs.GetBits(4) + 2; + + // Curved edge + if (EdgeFlag == 0) + { + s32 ControlDeltaX = bs.GetSignedBits(NumBits); + s32 ControlDeltaY = bs.GetSignedBits(NumBits); + s32 AnchorDeltaX = bs.GetSignedBits(NumBits); + s32 AnchorDeltaY = bs.GetSignedBits(NumBits); + + double ControlX = m_Elements.back().m_Paths.last_x() + ControlDeltaX; + double ControlY = m_Elements.back().m_Paths.last_y() + ControlDeltaY; + double AnchorX = ControlX + AnchorDeltaX; + double AnchorY = ControlY + AnchorDeltaY; + m_Elements.back().m_Paths.curve3(ControlX, ControlY, AnchorX, AnchorY); + } + // Staight edge + else + { + s32 DeltaX = 0; + s32 DeltaY = 0; + + u32 GeneralLineFlag = bs.GetBits(1); + if (GeneralLineFlag) + { + DeltaX = bs.GetSignedBits(NumBits); + DeltaY = bs.GetSignedBits(NumBits); + } + else + { + u32 VertLineFlag = bs.GetBits(1); + if (VertLineFlag) + DeltaY = bs.GetSignedBits(NumBits); + else + DeltaX = bs.GetSignedBits(NumBits); + } + + m_Elements.back().m_Paths.line_rel(DeltaX, DeltaY); + } + } + } + + // Bounding-Boxes der einzelnen Elemente berechnen + vector<BS_VectorImageElement>::iterator it = m_Elements.begin(); + for (; it != m_Elements.end(); ++it) it->m_BoundingBox = CalculateBoundingBox(*it); + + return true; +} + + +// ----------------------------------------------------------------------------- + +bool BS_VectorImage::ParseStyles(unsigned int ShapeType, SWFBitStream & bs, unsigned int & NumFillBits, unsigned int & NumLineBits) +{ + bs.FlushByte(); + + // Fillstyles parsen + // ----------------- + + // Anzahl an Fillstyles bestimmen + unsigned int FillStyleCount = bs.GetU8(); + if (FillStyleCount == 0xff) FillStyleCount = bs.GetU16(); + + // Alle Fillstyles einlesen, falls ein Fillstyle mit Typ != 0 gefunden wird, wird das Parsen abgebrochen. + // Es wird nur "solid fill" (Typ 0) unterstützt. + m_Elements.back().m_FillStyles.reserve(FillStyleCount); + for (unsigned int i = 0; i < FillStyleCount; ++i) + { + u8 Type = bs.GetU8(); + u32 Color; + if (ShapeType == 3) + { + Color = (bs.GetU8() << 16) | (bs.GetU8() << 8) | bs.GetU8() | (bs.GetU8() << 24); + } + else + Color = bs.GetBits(24) | (0xff << 24); + if (Type != 0) return false; + + m_Elements.back().m_FillStyles.push_back(FlashColorToAGGRGBA8(Color)); + } + + // Linestyles parsen + // ----------------- + + // Anzahl an Linestyles bestimmen + unsigned int LineStyleCount = bs.GetU8(); + if (LineStyleCount == 0xff) LineStyleCount = bs.GetU16(); + + // Alle Linestyles einlesen + m_Elements.back().m_LineStyles.reserve(LineStyleCount); + for (unsigned int i = 0; i < LineStyleCount; ++i) + { + double Width = bs.GetU16(); + u32 Color; + if (ShapeType == 3) + Color = (bs.GetU8() << 16) | (bs.GetU8() << 8) | bs.GetU8() | (bs.GetU8() << 24); + else + Color = bs.GetBits(24) | (0xff << 24); + + m_Elements.back().m_LineStyles.push_back(BS_VectorImageElement::LineStyleType(Width, FlashColorToAGGRGBA8(Color))); + } + + // Bitbreite für die folgenden Styleindizes auslesen + NumFillBits = bs.GetBits(4); + NumLineBits = bs.GetBits(4); + + return true; +} + + +// ----------------------------------------------------------------------------- + +bool BS_VectorImage::Fill(const BS_Rect* pFillRect, unsigned int Color) +{ + BS_LOG_ERRORLN("Fill() is not supported."); + return false; +} + + +// ----------------------------------------------------------------------------- + +unsigned int BS_VectorImage::GetPixel(int X, int Y) +{ + BS_LOG_ERRORLN("GetPixel() is not supported. Returning black."); + return 0; +} + +// ----------------------------------------------------------------------------- + +bool BS_VectorImage::SetContent(const std::vector<unsigned char> & Pixeldata, unsigned int Offset, unsigned int Stride) +{ + BS_LOG_ERRORLN("SetContent() is not supported."); + return 0; +} diff --git a/engines/sword25/gfx/image/vectorimage.h b/engines/sword25/gfx/image/vectorimage.h new file mode 100755 index 0000000000..74f6c860d9 --- /dev/null +++ b/engines/sword25/gfx/image/vectorimage.h @@ -0,0 +1,167 @@ +// ----------------------------------------------------------------------------- +// This file is part of Broken Sword 2.5 +// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer +// +// Broken Sword 2.5 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 +// (at your option) any later version. +// +// Broken Sword 2.5 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 Broken Sword 2.5; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +// ----------------------------------------------------------------------------- + +#ifndef BS_VECTORIMAGE_H +#define BS_VECTORIMAGE_H + +// ----------------------------------------------------------------------------- +// Includes +// ----------------------------------------------------------------------------- + +#include "kernel/common.h" +#include "gfx/image/image.h" +#include "math/rect.h" + +#include <vector> +#include "agg_path_storage.h" +#include "agg_color_rgba.h" + + +class BS_VectorImage; + +/** + @brief Pfadinformationen zu BS_VectorImageElement Objekten + + Jedes BS_VectorImageElement besteht aus Kantenzügen, oder auch Pfaden. Jeder dieser Pfad hat Eigenschaften, die in Objekten diesen Typs + gespeichert werden. +*/ + +class BS_VectorPathInfo +{ +public: + BS_VectorPathInfo(unsigned int ID, unsigned int LineStyle, unsigned int FillStyle0, unsigned int FillStyle1) : + m_ID(ID), m_LineStyle(LineStyle), m_FillStyle0(FillStyle0), m_FillStyle1(FillStyle1) {}; + + unsigned int GetID() const { return m_ID; } + unsigned int GetLineStyle() const { return m_LineStyle; } + unsigned int GetFillStyle0() const { return m_FillStyle0; } + unsigned int GetFillStyle1() const { return m_FillStyle1; } + +private: + unsigned int m_ID; + unsigned int m_LineStyle; + unsigned int m_FillStyle0; + unsigned int m_FillStyle1; +}; + + +/** + @brief Ein Element eines Vektorbild. Ein BS_VectorImage besteht aus diesen Elementen, die jeweils einen Teil der Graphik definieren. + Werden alle Elemente eines Vektorbildes übereinandergelegt, ergibt sich das komplette Bild. +*/ +class BS_VectorImageElement +{ +friend BS_VectorImage; +public: + const agg::path_storage & GetPaths() const { return m_Paths; } + unsigned int GetPathCount() const { return m_PathInfos.size(); } + const BS_VectorPathInfo & GetPathInfo(unsigned int PathNr) const { BS_ASSERT(PathNr < GetPathCount()); return m_PathInfos[PathNr]; } + + double GetLineStyleWidth(unsigned int LineStyle) const + { + BS_ASSERT(LineStyle < m_LineStyles.size()); + return m_LineStyles[LineStyle].Width; + } + + unsigned int GetLineStyleCount() const { return m_LineStyles.size(); } + + const agg::rgba8 & GetLineStyleColor(unsigned int LineStyle) const + { + BS_ASSERT(LineStyle < m_LineStyles.size()); + return m_LineStyles[LineStyle].Color; + } + + unsigned int GetFillStyleCount() const { return m_FillStyles.size(); } + + const agg::rgba8 & GetFillStyleColor(unsigned int FillStyle) const + { + BS_ASSERT(FillStyle < m_FillStyles.size()); + return m_FillStyles[FillStyle]; + } + + const BS_Rect & GetBoundingBox() const { return m_BoundingBox; } + +private: + struct LineStyleType + { + LineStyleType(double Width_, const agg::rgba8 & Color_) : Width(Width_), Color(Color_) {}; + double Width; + agg::rgba8 Color; + }; + + agg::path_storage m_Paths; + std::vector<BS_VectorPathInfo> m_PathInfos; + std::vector<LineStyleType> m_LineStyles; + std::vector<agg::rgba8> m_FillStyles; + BS_Rect m_BoundingBox; +}; + + +/** + @brief Eine Vektorgraphik + + Objekte dieser Klasse enthalten die Informationen eines SWF-Shapes. +*/ + +class BS_VectorImage : public BS_Image +{ +public: + BS_VectorImage(const unsigned char * pFileData, unsigned int FileSize, bool & Success); + + unsigned int GetElementCount() const { return m_Elements.size(); } + const BS_VectorImageElement & GetElement(unsigned int ElementNr) const + { + BS_ASSERT(ElementNr < m_Elements.size()); + return m_Elements[ElementNr]; + } + const BS_Rect & GetBoundingBox() const { return m_BoundingBox; } + + // + // Die abstrakten Methoden von BS_Image + // + virtual int GetWidth() const { return m_BoundingBox.GetWidth(); } + virtual int GetHeight() const { return m_BoundingBox.GetHeight(); } + virtual BS_GraphicEngine::COLOR_FORMATS GetColorFormat() const { return BS_GraphicEngine::CF_ARGB32; } + virtual bool Fill(const BS_Rect* pFillRect = 0, unsigned int Color = BS_RGB(0, 0, 0)); + virtual unsigned int GetPixel(int X, int Y); + virtual bool IsBlitSource() const { return true; } + virtual bool IsBlitTarget() const { return false; } + virtual bool IsScalingAllowed() const { return true; } + virtual bool IsFillingAllowed() const { return false; } + virtual bool IsAlphaAllowed() const { return true; } + virtual bool IsColorModulationAllowed() const { return true; } + virtual bool IsSetContentAllowed() const { return false; } + virtual bool SetContent(const std::vector<unsigned char> & Pixeldata, unsigned int Offset, unsigned int Stride); + virtual bool Blit(int PosX = 0, int PosY = 0, + int Flipping = FLIP_NONE, + BS_Rect* pPartRect = NULL, + unsigned int Color = BS_ARGB(255, 255, 255, 255), + int Width = -1, int Height = -1); + + class SWFBitStream; + +private: + bool ParseDefineShape(unsigned int ShapeType, SWFBitStream & bs); + bool ParseStyles(unsigned int ShapeType, SWFBitStream & bs, unsigned int & NumFillBits, unsigned int & NumLineBits); + + std::vector<BS_VectorImageElement> m_Elements; + BS_Rect m_BoundingBox; +}; + +#endif diff --git a/engines/sword25/gfx/image/vectorimagerenderer.cpp b/engines/sword25/gfx/image/vectorimagerenderer.cpp new file mode 100755 index 0000000000..76f2330a65 --- /dev/null +++ b/engines/sword25/gfx/image/vectorimagerenderer.cpp @@ -0,0 +1,198 @@ +// ----------------------------------------------------------------------------- +// This file is part of Broken Sword 2.5 +// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer +// +// Broken Sword 2.5 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 +// (at your option) any later version. +// +// Broken Sword 2.5 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 Broken Sword 2.5; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +// ----------------------------------------------------------------------------- + +// ----------------------------------------------------------------------------- +// Includes +// ----------------------------------------------------------------------------- + +#include "vectorimagerenderer.h" +#include "vectorimage.h" +#include "agg_conv_curve.h" +#include "agg_path_storage.h" +#include "agg_conv_stroke.h" + + +// ----------------------------------------------------------------------------- +// CompoundShape +// ----------------------------------------------------------------------------- + +class CompoundShape +{ +public: + CompoundShape(const BS_VectorImageElement & VectorImageElement) : + m_ImageElement(VectorImageElement), + m_Path(VectorImageElement.GetPaths()), + m_Affine(), + m_Curve(m_Path), + m_Trans(m_Curve, m_Affine) + {} + + unsigned operator [] (unsigned i) const + { + return m_ImageElement.GetPathInfo(i).GetID(); + } + + unsigned paths() const { return m_ImageElement.GetPathCount(); } + + void rewind(unsigned path_id) + { + m_Trans.rewind(path_id); + } + + unsigned vertex(double* x, double* y) + { + return m_Trans.vertex(x, y); + } + +private: + const BS_VectorImageElement & m_ImageElement; + agg::path_storage m_Path; + agg::trans_affine m_Affine; + agg::conv_curve<agg::path_storage> m_Curve; + agg::conv_transform< agg::conv_curve<agg::path_storage> > m_Trans; +}; + + +// ----------------------------------------------------------------------------- +// StyleHandler +// ----------------------------------------------------------------------------- + +class StyleHandler +{ +public: + StyleHandler(const BS_VectorImageElement & VectorImageElement) : m_ImageElement(VectorImageElement) {} + + bool is_solid(unsigned int style) const + { + return true; + } + + const agg::rgba8 & color(unsigned style) const + { + return m_ImageElement.GetFillStyleColor(style); + } + + void generate_span(agg::rgba8 * span, int x, int y, unsigned len, unsigned style) + { + // Wird nicht benutzt + return; + } + +private: + const BS_VectorImageElement & m_ImageElement; +}; + + +// ----------------------------------------------------------------------------- +// Konstruktion +// ----------------------------------------------------------------------------- + +BS_VectorImageRenderer::BS_VectorImageRenderer() : + PixelFormat(rbuf) +{ + +} + + +// ----------------------------------------------------------------------------- + +bool BS_VectorImageRenderer::Render(const BS_VectorImage & VectorImage, + float ScaleFactorX, float ScaleFactorY, + unsigned int & Width, unsigned int & Height, + std::vector<char> & ImageData, + float LineScaleFactor, + bool NoAlphaShapes) +{ + Width = static_cast<unsigned int>(VectorImage.GetWidth() * ScaleFactorX); + Height = static_cast<unsigned int>(VectorImage.GetHeight() * ScaleFactorY); + + ImageData.resize(Width * Height * 4); + memset(&ImageData[0], 0, ImageData.size()); + rbuf.attach(reinterpret_cast<agg::int8u *>(&ImageData[0]), Width, Height, Width * 4); + + BaseRenderer.attach(PixelFormat); + ScanlineRenderer.attach(BaseRenderer); + + // Die SWF-Shapes sind häufig nicht am Ursprung (0, 0) ausgerichtet, daher wird die Shape vor dem Rendern derart verschoben, dass + // sich die linke obere Ecke der Bounding-Box im Ursprung befindet. Danach wird die Skalierung angewandt. + Scale = agg::trans_affine_translation(- VectorImage.GetBoundingBox().left, - VectorImage.GetBoundingBox().top); + Scale *= agg::trans_affine_scaling(ScaleFactorX, ScaleFactorY); + + for (unsigned int element = 0; element < VectorImage.GetElementCount(); ++element) + { + const BS_VectorImageElement & CurImageElement = VectorImage.GetElement(element); + + CompoundShape ImageCompoundShape(CurImageElement); + StyleHandler ImageStyleHandler(CurImageElement); + agg::conv_transform<CompoundShape> Shape(ImageCompoundShape, Scale); + agg::conv_stroke<agg::conv_transform<CompoundShape> > Stroke(Shape); + + // Fill shape + //---------------------- + CompoundRasterizer.clip_box(0, 0, Width, Height); + CompoundRasterizer.reset(); + for(unsigned int i = 0; i < CurImageElement.GetPathCount(); ++i) + { + unsigned int FillStyle0 = CurImageElement.GetPathInfo(i).GetFillStyle0(); + unsigned int FillStyle1 = CurImageElement.GetPathInfo(i).GetFillStyle1(); + + if (NoAlphaShapes) + { + if (FillStyle0 != 0 && CurImageElement.GetFillStyleColor(FillStyle0 - 1).a != 255) FillStyle0 = 0; + if (FillStyle1 != 0 && CurImageElement.GetFillStyleColor(FillStyle1 - 1).a != 255) FillStyle1 = 0; + } + + if(FillStyle0 != 0 || FillStyle1 != 0) + { + CompoundRasterizer.styles(FillStyle0 - 1, FillStyle1 - 1); + CompoundRasterizer.add_path(Shape, CurImageElement.GetPathInfo(i).GetID()); + } + } + agg::render_scanlines_compound_layered(CompoundRasterizer, Scanline, BaseRenderer, Alloc, ImageStyleHandler); + + + // Draw strokes + //---------------------- + Rasterizer.clip_box(0, 0, Width, Height); + Stroke.line_join(agg::round_join); + Stroke.line_cap(agg::round_cap); + for(unsigned int i = 0; i < CurImageElement.GetPathCount(); ++i) + { + Rasterizer.reset(); + + unsigned int CurrentLineStyle = CurImageElement.GetPathInfo(i).GetLineStyle(); + if (CurrentLineStyle != 0) + { + Stroke.width(ScaleFactorX * CurImageElement.GetLineStyleWidth(CurrentLineStyle - 1) * LineScaleFactor); + Rasterizer.add_path(Stroke, CurImageElement.GetPathInfo(i).GetID()); + ScanlineRenderer.color(CurImageElement.GetLineStyleColor(CurrentLineStyle - 1)); + // HACK + // Die SWF-Frames enthalten zum Teil Reste von grünen Linien, die wohl von Bernd als Umriss benutzt wurden. + // Damit diese Reste nicht störend auffallen werden grüne Linien schlichtweg ignoriert. + if (!(CurImageElement.GetLineStyleColor(CurrentLineStyle - 1).a == 255 && + CurImageElement.GetLineStyleColor(CurrentLineStyle - 1).r == 0 && + CurImageElement.GetLineStyleColor(CurrentLineStyle - 1).g == 255 && + CurImageElement.GetLineStyleColor(CurrentLineStyle - 1).b == 0)) + agg::render_scanlines(Rasterizer, Scanline, ScanlineRenderer); + } + } + } + + return true; +} diff --git a/engines/sword25/gfx/image/vectorimagerenderer.h b/engines/sword25/gfx/image/vectorimagerenderer.h new file mode 100755 index 0000000000..2dc29c480e --- /dev/null +++ b/engines/sword25/gfx/image/vectorimagerenderer.h @@ -0,0 +1,76 @@ +// ----------------------------------------------------------------------------- +// This file is part of Broken Sword 2.5 +// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer +// +// Broken Sword 2.5 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 +// (at your option) any later version. +// +// Broken Sword 2.5 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 Broken Sword 2.5; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +// ----------------------------------------------------------------------------- + +#ifndef BS_VECTORIMAGERENDERER_H +#define BS_VECTORIMAGERENDERER_H + +// ----------------------------------------------------------------------------- +// Includes +// ----------------------------------------------------------------------------- + +#include "kernel/common.h" +#include <vector> + +#include "agg_rendering_buffer.h" +#include "agg_pixfmt_rgba.h" +#include "agg_renderer_scanline.h" +#include "agg_rasterizer_scanline_aa.h" +#include "agg_rasterizer_compound_aa.h" +#include "agg_scanline_u.h" +#include "agg_scanline_bin.h" +#include "agg_trans_affine.h" +#include "agg_span_allocator.h" + +class BS_VectorImage; + + +/** + @brief Rendert BS_VectorImage Objekte +*/ + +class BS_VectorImageRenderer +{ +public: + BS_VectorImageRenderer(); + + bool Render(const BS_VectorImage & VectorImage, + float ScaleFactorX, float ScaleFactorY, + unsigned int & Width, unsigned int & Height, + std::vector<char> & ImageData, + float LineScaleFactor = 1.0f, + bool NoAlphaShapes = false); + +private: + typedef agg::pixfmt_rgba32_pre PixelFormatType; + typedef agg::renderer_base<PixelFormatType> BaseRendererType; + typedef agg::renderer_scanline_aa_solid<BaseRendererType> ScanlineRendererType; + + agg::rendering_buffer rbuf; + PixelFormatType PixelFormat; + BaseRendererType BaseRenderer; + ScanlineRendererType ScanlineRenderer; + agg::rasterizer_scanline_aa<agg::rasterizer_sl_clip_dbl> Rasterizer; + agg::rasterizer_compound_aa<agg::rasterizer_sl_clip_dbl> CompoundRasterizer; + agg::scanline_u8 Scanline; + agg::scanline_bin ScanlineBin; + agg::trans_affine Scale; + agg::span_allocator<agg::rgba8> Alloc; +}; + +#endif |