aboutsummaryrefslogtreecommitdiff
path: root/engines/sword25/gfx/image/vectorimage.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/sword25/gfx/image/vectorimage.cpp')
-rwxr-xr-xengines/sword25/gfx/image/vectorimage.cpp571
1 files changed, 571 insertions, 0 deletions
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;
+}