xoreos  0.0.5
pltfile.cpp
Go to the documentation of this file.
1 /* xoreos - A reimplementation of BioWare's Aurora engine
2  *
3  * xoreos is the legal property of its developers, whose names
4  * can be found in the AUTHORS file distributed with this source
5  * distribution.
6  *
7  * xoreos is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 3
10  * of the License, or (at your option) any later version.
11  *
12  * xoreos is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with xoreos. If not, see <http://www.gnu.org/licenses/>.
19  */
20 
25 /* A PLT file consist of up to 10 layers that can be tinted independently,
26  * that are all flattened into one texture for display. The tinting is
27  * dynamic, though, meaning the colors can be changed on-the-fly from the
28  * engine code, even for PLTs that are currently displayed.
29  *
30  * For each pixel, the PLT specifies two values: to which layer it belongs
31  * to, and its coloring intensity. As such, each pixel only ever belongs
32  * to a single layer.
33  *
34  * For the actual colors involved, each layer in turn requires a palette
35  * image, usually a TGA in BGRA. The coloring value given by the engine
36  * code specifies the Y coordinate from where to pick the color from
37  * the palette image, and the PLT-supplied intensity specifies the X
38  * coordinate.
39  *
40  * So, this is the algorithm to build the final texture:
41  *
42  * for each pixel p in image {
43  * int layerIndex = read layer index value from the PLT file
44  * int intensity = read intensity value from the PLT file
45  * int colorIndex = get color index value from the engine code
46  *
47  * p = layerImages[layerIndex].getPixel(intensity, colorIndex)
48  * }
49  */
50 
51 #include <cassert>
52 #include <cstring>
53 
54 #include "src/common/error.h"
55 #include "src/common/readstream.h"
56 #include "src/common/strutil.h"
57 
60 
62 
63 static const uint32 kPLTID = MKTAG('P', 'L', 'T', ' ');
64 static const uint32 kVersion1 = MKTAG('V', '1', ' ', ' ');
65 
66 namespace Graphics {
67 
68 namespace Aurora {
69 
71  _name(name), _surface(0) {
72 
73  for (size_t i = 0; i < kLayerMAX; i++)
74  _colors[i] = 0;
75 
76  load(plt);
77 }
78 
80 }
81 
82 bool PLTFile::isDynamic() const {
83  return true;
84 }
85 
87  // We can't reload PLT files
88  return false;
89 }
90 
91 void PLTFile::setLayerColor(Layer layer, uint8 color) {
92  assert((layer >= 0) && (layer < kLayerMAX));
93 
94  _colors[layer] = color;
95 }
96 
98  build();
99  refresh();
100 }
101 
103  // --- PLT header ---
104  AuroraFile::readHeader(plt);
105 
106  if (_id != kPLTID)
107  throw Common::Exception("Not a PLT file (%s)", Common::debugTag(_id).c_str());
108  if (_version != kVersion1)
109  throw Common::Exception("Unsupported PLT file version %s", Common::debugTag(_version).c_str());
110 
111  const uint32 layers = plt.readUint32LE();
112  if (layers > kLayerMAX)
113  throw Common::Exception("Too many layers (%d)", layers);
114 
115  plt.skip(4); // Unknown
116 
117  const size_t width = plt.readUint32LE();
118  const size_t height = plt.readUint32LE();
119 
120  if ((plt.size() - plt.pos()) < (2 * width * height))
121  throw Common::Exception("Not enough data");
122 
123  // --- PLT layer data ---
124 
125  size_t size = width * height;
126 
127  _dataImage.reset(new uint8[size]);
128  _dataLayers.reset(new uint8[size]);
129 
130  uint8 *image = _dataImage.get();
131  uint8 *layer = _dataLayers.get();
132  while (size-- > 0) {
133  *image++ = plt.readByte();
134  *layer++ = MIN<uint8>(plt.readByte(), kLayerMAX - 1);
135  }
136 
137  // --- Create the actual texture surface ---
138 
139  // Initialize it to pink, for high debug visibility
140  _surface = new Surface(width, height);
141  _surface->fill(0xFF, 0x00, 0xFF, 0xFF);
142 
144  addToQueues();
145 }
146 
148  /* For all layers, copy one whole row of pixels into the row buffer.
149  * The row picked for each layer corresponds to the color index we want.
150  * We don't care about the other rows, as they belong to other color indices. */
151  byte rows[4 * 256 * kLayerMAX];
152  getColorRows(rows, _colors);
153 
154  const size_t pixels = _width * _height;
155  const uint8 *image = _dataImage.get();
156  const uint8 *layer = _dataLayers.get();
157  byte *dst = _surface->getData();
158 
159  /* Now iterate over all pixels, each time copying the correct BGRA values
160  * for the pixel's intensity into the final image. */
161  for (size_t i = 0; i < pixels; i++, image++, layer++, dst += 4)
162  memcpy(dst, rows + (*layer * 4 * 256) + (*image * 4), 4);
163 }
164 
166 static const char * const kPalettes[PLTFile::kLayerMAX] = {
167  "pal_skin01",
168  "pal_hair01",
169  "pal_armor01",
170  "pal_armor02",
171  "pal_cloth01",
172  "pal_cloth01",
173  "pal_leath01",
174  "pal_leath01",
175  "pal_tattoo01",
176  "pal_tattoo01"
177 };
178 
181  assert(layer < kLayerMAX);
182 
183  // TODO: We may want to cache these somehow...
185 
186  if (palette->getFormat() != kPixelFormatBGRA)
187  throw Common::Exception("Invalid format (%d)", palette->getFormat());
188 
189  if (palette->getMipMapCount() < 1)
190  throw Common::Exception("No mip maps");
191 
192  const ImageDecoder::MipMap &mipMap = palette->getMipMap(0);
193 
194  if (mipMap.width != 256)
195  throw Common::Exception("Invalid width (%d)", mipMap.width);
196 
197  if (row >= mipMap.height)
198  throw Common::Exception("Invalid height (%d >= %d)", row, mipMap.height);
199 
200  return palette.release();
201 }
202 
203 void PLTFile::getColorRows(byte rows[4 * 256 * kLayerMAX], const uint8 colors[kLayerMAX]) {
204  for (size_t i = 0; i < kLayerMAX; i++, rows += 4 * 256) {
205  try {
206  Common::ScopedPtr<ImageDecoder> palette(getLayerPalette(i, colors[i]));
207 
208  // The images have their origin at the bottom left, so we flip the color row
209  const uint8 row = palette->getMipMap(0).height - 1 - colors[i];
210 
211  // Copy the whole row into the buffer
212  memcpy(rows, palette->getMipMap(0).data.get() + (row * 4 * 256), 4 * 256);
213 
214  } catch (...) {
215  // On error set to pink (while honoring intensity), for high debug visibility
216  for (size_t p = 0; p < 256; p++) {
217  rows[p * 4 + 0] = p;
218  rows[p * 4 + 1] = 0x00;
219  rows[p * 4 + 2] = p;
220  rows[p * 4 + 3] = 0xFF;
221  }
222 
223  Common::exceptionDispatcherWarning("Failed to load palette \"%s\"", kPalettes[i]);
224  }
225  }
226 }
227 
228 } // End of namespace Aurora
229 
230 } // End of namespace Graphics
#define MKTAG(a0, a1, a2, a3)
A wrapper macro used around four character constants, like &#39;DATA&#39;, to ensure portability.
Definition: endianness.h:140
Generic image decoder interface.
byte * getData()
Definition: surface.cpp:61
uint32 readUint32LE()
Read an unsigned 32-bit word stored in little endian (LSB first) order from the stream and return it...
Definition: readstream.h:133
Packed layer texture.
Definition: types.h:63
static const uint32 kVersion1
Definition: pltfile.cpp:64
A class holding an UTF-8 string.
Definition: ustring.h:48
Common::ScopedArray< uint8 > _dataLayers
Definition: pltfile.h:73
void reset(PointerType o=0)
Resets the pointer with the new value.
Definition: scopedptr.h:87
PointerType release()
Returns the plain pointer value and releases ScopedPtr.
Definition: scopedptr.h:103
Common::ScopedArray< uint8 > _dataImage
Definition: pltfile.h:72
uint8_t uint8
Definition: types.h:200
void fill(byte r, byte g, byte b, byte a)
Definition: surface.cpp:69
uint8 _colors[kLayerMAX]
Definition: pltfile.h:75
static void getColorRows(byte rows[4 *256 *kLayerMAX], const uint8 colors[kLayerMAX])
Definition: pltfile.cpp:203
Utility templates and functions for working with strings and streams.
int height
The mip map&#39;s height.
Definition: decoder.h:53
Exception that provides a stack of explanations.
Definition: error.h:36
static const uint32 kPLTID
Definition: pltfile.cpp:63
void exceptionDispatcherWarning(const char *s,...)
Exception dispatcher that prints the exception as a warning, and adds another reason on top...
Definition: error.cpp:158
Basic exceptions to throw.
static ImageDecoder * loadImage(const Common::UString &name, bool deswizzle=false)
Load an image in any of the common texture formats.
Definition: texture.cpp:401
virtual size_t skip(ptrdiff_t offset)
Skip the specified number of bytes, adding that offset to the current position in the stream...
Definition: readstream.h:317
static ImageDecoder * getLayerPalette(uint32 layer, uint8 row)
Load a specific layer palette image and perform some sanity checks.
Definition: pltfile.cpp:180
void setLayerColor(Layer layer, uint8 color)
Set the color of one layer within this layer texture.
Definition: pltfile.cpp:91
Common::UString _name
Definition: pltfile.h:68
uint32 _id
The file&#39;s ID.
Definition: aurorafile.h:77
StackException Exception
Definition: error.h:59
A scoped plain pointer, allowing pointer-y access and normal deletion.
Definition: scopedptr.h:120
uint32 _version
The file&#39;s version.
Definition: aurorafile.h:78
virtual size_t size() const =0
Obtains the total size of the stream, measured in bytes.
Basic reading stream interfaces.
virtual size_t pos() const =0
Obtains the current value of the stream position indicator of the stream.
PointerType get() const
Returns the plain pointer value.
Definition: scopedptr.h:96
int width
The mip map&#39;s width.
Definition: decoder.h:52
An image surface, in BGRA format.
bool reload()
Try to reload the texture.
Definition: pltfile.cpp:86
static const char *const kPalettes[PLTFile::kLayerMAX]
The palette image resource names for all layers.
Definition: pltfile.cpp:166
uint32_t uint32
Definition: types.h:204
UString debugTag(uint32 tag, bool trim)
Create an elaborate string from an integer tag, for debugging purposes.
Definition: strutil.cpp:117
A generic interface for image decoders.
Definition: decoder.h:48
bool isDynamic() const
Is this a dynamic texture, or a shared static one?
Definition: pltfile.cpp:82
void load(Common::SeekableReadStream &plt)
Definition: pltfile.cpp:102
Interface for a seekable & readable data stream.
Definition: readstream.h:265
byte readByte()
Read an unsigned byte from the stream and return it.
Definition: readstream.h:92
void rebuild()
Rebuild the combined texture image.
Definition: pltfile.cpp:97
BioWare&#39;s Packed Layered Texture.
uint8 byte
Definition: types.h:209
PLTFile(const Common::UString &name, Common::SeekableReadStream &plt)
Definition: pltfile.cpp:70