1 #include <stdexcept>
2
3 #include "Types.h"
4 #include "Debug.h"
5 #include "FileMan.h"
6 #include "HImage.h"
7 #include "ImpTGA.h"
8 #include "PCX.h"
9 #include "STCI.h"
10 #include "WCheck.h"
11 #include "VObject.h"
12 #include "MemMan.h"
13
14 #include "Logger.h"
15
16 // This is the color substituted to keep a 24bpp -> 16bpp color
17 // from going transparent (0x0000) -- DB
18
19 #define BLACK_SUBSTITUTE 0x0001
20
21
22 UINT16 gusRedMask = 0;
23 UINT16 gusGreenMask = 0;
24 UINT16 gusBlueMask = 0;
25 INT16 gusRedShift = 0;
26 INT16 gusBlueShift = 0;
27 INT16 gusGreenShift = 0;
28
29
CreateImage(ST::string filename,const UINT16 fContents)30 SGPImage* CreateImage(ST::string filename, const UINT16 fContents)
31 {
32 // depending on extension of filename, use different image readers
33 ST::string ext = filename.after_last(".");
34 if (ext == filename)
35 {
36 throw std::logic_error("Tried to load image with no extension");
37 }
38
39 return
40 ext.compare_i("STI") == 0 ? LoadSTCIFileToImage(filename, fContents) :
41 ext.compare_i("PCX") == 0 ? LoadPCXFileToImage( filename, fContents) :
42 ext.compare_i("TGA") == 0 ? LoadTGAFileToImage( filename, fContents) :
43 throw std::logic_error("Tried to load image with unknown extension");
44 }
45
46
Copy8BPPImageTo8BPPBuffer(SGPImage const * const img,BYTE * const pDestBuf,UINT16 const usDestWidth,UINT16 const usDestHeight,UINT16 const usX,UINT16 const usY,SGPBox const * const src_box)47 static BOOLEAN Copy8BPPImageTo8BPPBuffer(SGPImage const* const img, BYTE* const pDestBuf, UINT16 const usDestWidth, UINT16 const usDestHeight, UINT16 const usX, UINT16 const usY, SGPBox const* const src_box)
48 {
49 CHECKF(usX < usDestWidth);
50 CHECKF(usY < usDestHeight);
51 CHECKF(src_box->w > 0);
52 CHECKF(src_box->h > 0);
53
54 // Determine memcopy coordinates
55 UINT32 const uiSrcStart = src_box->y * img->usWidth + src_box->x;
56 UINT32 const uiDestStart = usY * usDestWidth + usX;
57 UINT32 const uiLineSize = src_box->w;
58 UINT32 const uiNumLines = src_box->h;
59
60 Assert(usDestWidth >= uiLineSize);
61 Assert(usDestHeight >= uiNumLines);
62
63 // Copy line by line
64 UINT8* dst = static_cast<UINT8*>(pDestBuf) + uiDestStart;
65 UINT8 const* src = static_cast<UINT8 const*>(img->pImageData) + uiSrcStart;
66 for (UINT32 n = uiNumLines; n != 0; --n)
67 {
68 memcpy(dst, src, uiLineSize);
69 dst += usDestWidth;
70 src += img->usWidth;
71 }
72
73 return TRUE;
74 }
75
76
Copy16BPPImageTo16BPPBuffer(SGPImage const * const img,BYTE * const pDestBuf,UINT16 const usDestWidth,UINT16 const usDestHeight,UINT16 const usX,UINT16 const usY,SGPBox const * const src_box)77 static BOOLEAN Copy16BPPImageTo16BPPBuffer(SGPImage const* const img, BYTE* const pDestBuf, UINT16 const usDestWidth, UINT16 const usDestHeight, UINT16 const usX, UINT16 const usY, SGPBox const* const src_box)
78 {
79 CHECKF(usX < img->usWidth);
80 CHECKF(usY < img->usHeight);
81 CHECKF(src_box->w > 0);
82 CHECKF(src_box->h > 0);
83
84 // Determine memcopy coordinates
85 UINT32 const uiSrcStart = src_box->y * img->usWidth + src_box->x;
86 UINT32 const uiDestStart = usY * usDestWidth + usX;
87 UINT32 const uiLineSize = src_box->w;
88 UINT32 const uiNumLines = src_box->h;
89
90 CHECKF(usDestWidth >= uiLineSize);
91 CHECKF(usDestHeight >= uiNumLines);
92
93 // Copy line by line
94 UINT16* dst = static_cast<UINT16*>(static_cast<void*>(pDestBuf)) + uiDestStart;
95 UINT16 const* src = static_cast<UINT16 const*>(static_cast<void const*>(img->pImageData)) + uiSrcStart;
96 for (UINT32 n = uiNumLines; n != 0; --n)
97 {
98 memcpy(dst, src, uiLineSize * 2);
99 dst += usDestWidth;
100 src += img->usWidth;
101 }
102
103 return TRUE;
104 }
105
106
Copy8BPPImageTo16BPPBuffer(SGPImage const * const img,BYTE * const pDestBuf,UINT16 const usDestWidth,UINT16 const usDestHeight,UINT16 const usX,UINT16 const usY,SGPBox const * const src_box)107 static BOOLEAN Copy8BPPImageTo16BPPBuffer(SGPImage const* const img, BYTE* const pDestBuf, UINT16 const usDestWidth, UINT16 const usDestHeight, UINT16 const usX, UINT16 const usY, SGPBox const* const src_box)
108 {
109 CHECKF(img->pImageData);
110 CHECKF(usX < usDestWidth);
111 CHECKF(usY < usDestHeight);
112 CHECKF(src_box->w > 0);
113 CHECKF(src_box->h > 0);
114
115 // Determine memcopy coordinates
116 UINT32 const uiSrcStart = src_box->y * img->usWidth + src_box->x;
117 UINT32 const uiDestStart = usY * usDestWidth + usX;
118 UINT32 const uiLineSize = src_box->w;
119 UINT32 const uiNumLines = src_box->h;
120
121 CHECKF(usDestWidth >= uiLineSize);
122 CHECKF(usDestHeight >= uiNumLines);
123
124 // Convert to Pixel specification
125 UINT16* dst = static_cast<UINT16*>(static_cast<void*>(pDestBuf)) + uiDestStart;
126 UINT8 const* src = static_cast<UINT8 const*>(img->pImageData) + uiSrcStart;
127 UINT16 const* const pal = img->pui16BPPPalette;
128 for (UINT32 rows = uiNumLines; rows != 0; --rows)
129 {
130 UINT16* dst_tmp = dst;
131 UINT8 const* src_tmp = src;
132 for (UINT32 cols = uiLineSize; cols != 0; --cols)
133 {
134 *dst_tmp++ = pal[*src_tmp++];
135 }
136 dst += usDestWidth;
137 src += img->usWidth;
138 }
139
140 return TRUE;
141 }
142
143
CopyImageToBuffer(SGPImage const * const img,UINT32 const fBufferType,BYTE * const pDestBuf,UINT16 const usDestWidth,UINT16 const usDestHeight,UINT16 const usX,UINT16 const usY,SGPBox const * const src_box)144 BOOLEAN CopyImageToBuffer(SGPImage const* const img, UINT32 const fBufferType, BYTE* const pDestBuf, UINT16 const usDestWidth, UINT16 const usDestHeight, UINT16 const usX, UINT16 const usY, SGPBox const* const src_box)
145 {
146 // Use blitter based on type of image
147 if (img->ubBitDepth == 8 && fBufferType == BUFFER_8BPP)
148 {
149 // Default do here
150 SLOGD("Copying 8 BPP Imagery.");
151 return Copy8BPPImageTo8BPPBuffer(img, pDestBuf, usDestWidth, usDestHeight, usX, usY, src_box);
152 }
153 else if (img->ubBitDepth == 8 && fBufferType == BUFFER_16BPP)
154 {
155 SLOGD("Copying 8 BPP Imagery to 16BPP Buffer.");
156 return Copy8BPPImageTo16BPPBuffer(img, pDestBuf, usDestWidth, usDestHeight, usX, usY, src_box);
157 }
158 else if (img->ubBitDepth == 16 && fBufferType == BUFFER_16BPP)
159 {
160 SLOGD("Automatically Copying 16 BPP Imagery.");
161 return Copy16BPPImageTo16BPPBuffer(img, pDestBuf, usDestWidth, usDestHeight, usX, usY, src_box);
162 }
163
164 return FALSE;
165 }
166
167
Create16BPPPalette(const SGPPaletteEntry * pPalette)168 UINT16* Create16BPPPalette(const SGPPaletteEntry* pPalette)
169 {
170 Assert(pPalette != NULL);
171
172 UINT16* const p16BPPPalette = new UINT16[256]{};
173
174 for (UINT32 cnt = 0; cnt < 256; cnt++)
175 {
176 UINT8 const r = pPalette[cnt].r;
177 UINT8 const g = pPalette[cnt].g;
178 UINT8 const b = pPalette[cnt].b;
179 p16BPPPalette[cnt] = Get16BPPColor(FROMRGB(r, g, b));
180 }
181
182 return p16BPPPalette;
183 }
184
185
186 /**********************************************************************************************
187 Create16BPPPaletteShaded
188
189 Creates an 8 bit to 16 bit palette table, and modifies the colors as it builds.
190
191 Parameters:
192 rscale, gscale, bscale:
193 Color mode: Percentages (255=100%) of color to translate into destination palette.
194 Mono mode: Color for monochrome palette.
195 mono:
196 TRUE or FALSE to create a monochrome palette. In mono mode, Luminance values for
197 colors are calculated, and the RGB color is shaded according to each pixel's brightness.
198
199 This can be used in several ways:
200
201 1) To "brighten" a palette, pass down RGB values that are higher than 100% ( > 255) for all
202 three. mono=FALSE.
203 2) To "darken" a palette, do the same with less than 100% ( < 255) values. mono=FALSE.
204
205 3) To create a "glow" palette, select mono=TRUE, and pass the color in the RGB parameters.
206
207 4) For gamma correction, pass in weighted values for each color.
208
209 **********************************************************************************************/
Create16BPPPaletteShaded(const SGPPaletteEntry * pPalette,UINT32 rscale,UINT32 gscale,UINT32 bscale,BOOLEAN mono)210 UINT16* Create16BPPPaletteShaded(const SGPPaletteEntry* pPalette, UINT32 rscale, UINT32 gscale, UINT32 bscale, BOOLEAN mono)
211 {
212 Assert(pPalette != NULL);
213
214 UINT16* const p16BPPPalette = new UINT16[256]{};
215
216 for (UINT32 cnt = 0; cnt < 256; cnt++)
217 {
218 UINT32 rmod;
219 UINT32 gmod;
220 UINT32 bmod;
221 if (mono)
222 {
223 UINT32 lumin = (pPalette[cnt].r * 299 + pPalette[cnt].g * 587 + pPalette[cnt].b * 114) / 1000;
224 rmod = rscale * lumin / 256;
225 gmod = gscale * lumin / 256;
226 bmod = bscale * lumin / 256;
227 }
228 else
229 {
230 rmod = rscale * pPalette[cnt].r / 256;
231 gmod = gscale * pPalette[cnt].g / 256;
232 bmod = bscale * pPalette[cnt].b / 256;
233 }
234
235 UINT8 r = __min(rmod, 255);
236 UINT8 g = __min(gmod, 255);
237 UINT8 b = __min(bmod, 255);
238 p16BPPPalette[cnt] = Get16BPPColor(FROMRGB(r, g, b));
239 }
240 return p16BPPPalette;
241 }
242
243
244 // Convert from RGB to 16 bit value
Get16BPPColor(UINT32 RGBValue)245 UINT16 Get16BPPColor( UINT32 RGBValue )
246 {
247 UINT8 r = SGPGetRValue(RGBValue);
248 UINT8 g = SGPGetGValue(RGBValue);
249 UINT8 b = SGPGetBValue(RGBValue);
250
251 UINT16 r16 = (gusRedShift < 0 ? r >> -gusRedShift : r << gusRedShift);
252 UINT16 g16 = (gusGreenShift < 0 ? g >> -gusGreenShift : g << gusGreenShift);
253 UINT16 b16 = (gusBlueShift < 0 ? b >> -gusBlueShift : b << gusBlueShift);
254
255 UINT16 usColor = (r16 & gusRedMask) | (g16 & gusGreenMask) | (b16 & gusBlueMask);
256
257 // if our color worked out to absolute black, and the original wasn't
258 // absolute black, convert it to a VERY dark grey to avoid transparency
259 // problems
260 if (usColor == 0 && RGBValue != 0) usColor = BLACK_SUBSTITUTE;
261
262 return usColor;
263 }
264
265
266 // Convert from 16 BPP to RGBvalue
GetRGBColor(UINT16 Value16BPP)267 UINT32 GetRGBColor(UINT16 Value16BPP)
268 {
269 UINT32 r16 = Value16BPP & gusRedMask;
270 UINT32 g16 = Value16BPP & gusGreenMask;
271 UINT32 b16 = Value16BPP & gusBlueMask;
272
273 UINT32 r = (gusRedShift < 0 ? r16 << -gusRedShift : r16 >> gusRedShift);
274 UINT32 g = (gusGreenShift < 0 ? g16 << -gusGreenShift : g16 >> gusGreenShift);
275 UINT32 b = (gusBlueShift < 0 ? b16 << -gusBlueShift : b16 >> gusBlueShift);
276
277 r &= 0x000000ff;
278 g &= 0x000000ff;
279 b &= 0x000000ff;
280
281 UINT32 val = FROMRGB(r, g, b);
282 return val;
283 }
284
285
GetETRLEImageData(SGPImage const * const img,ETRLEData * const buf)286 void GetETRLEImageData(SGPImage const* const img, ETRLEData* const buf)
287 {
288 Assert(img);
289 Assert(buf);
290
291 SGP::Buffer<ETRLEObject> etrle_objs(img->usNumberOfObjects);
292 memcpy(etrle_objs, img->pETRLEObject, sizeof(*etrle_objs) * img->usNumberOfObjects);
293
294 SGP::Buffer<UINT8> pix_data(img->uiSizePixData);
295 memcpy(pix_data, img->pImageData, sizeof(*pix_data) * img->uiSizePixData);
296
297 buf->pPixData = pix_data.Release();
298 buf->uiSizePixData = img->uiSizePixData;
299 buf->pETRLEObject = etrle_objs.Release();
300 buf->usNumberOfObjects = img->usNumberOfObjects;
301 }
302
303
ConvertRGBDistribution565To555(UINT16 * p16BPPData,UINT32 uiNumberOfPixels)304 void ConvertRGBDistribution565To555( UINT16 * p16BPPData, UINT32 uiNumberOfPixels )
305 {
306 for (UINT16* Px = p16BPPData; Px != p16BPPData + uiNumberOfPixels; ++Px)
307 {
308 *Px = ((*Px >> 1) & ~0x001F) | (*Px & 0x001F);
309 }
310 }
311
ConvertRGBDistribution565To655(UINT16 * p16BPPData,UINT32 uiNumberOfPixels)312 void ConvertRGBDistribution565To655( UINT16 * p16BPPData, UINT32 uiNumberOfPixels )
313 {
314 for (UINT16* Px = p16BPPData; Px != p16BPPData + uiNumberOfPixels; ++Px)
315 {
316 *Px = ((*Px >> 1) & 0x03E0) | (*Px & ~0x07E0);
317 }
318 }
319
ConvertRGBDistribution565To556(UINT16 * p16BPPData,UINT32 uiNumberOfPixels)320 void ConvertRGBDistribution565To556( UINT16 * p16BPPData, UINT32 uiNumberOfPixels )
321 {
322 for (UINT16* Px = p16BPPData; Px != p16BPPData + uiNumberOfPixels; ++Px)
323 {
324 *Px = (*Px & ~0x003F) | ((*Px << 1) & 0x003F);
325 }
326 }
327
328
ConvertRGBDistribution565ToAny(UINT16 * const p16BPPData,UINT32 const uiNumberOfPixels)329 void ConvertRGBDistribution565ToAny(UINT16* const p16BPPData, UINT32 const uiNumberOfPixels)
330 {
331 UINT16* px = p16BPPData;
332 for (size_t n = uiNumberOfPixels; n != 0; --n)
333 {
334 // put the 565 RGB 16-bit value into a 32-bit RGB value
335 UINT32 const r = (*px ) >> 11;
336 UINT32 const g = (*px & 0x07E0) >> 5;
337 UINT32 const b = (*px & 0x001F);
338 UINT32 const rgb = FROMRGB(r, g, b);
339 // then convert the 32-bit RGB value to whatever 16 bit format is used
340 *px++ = Get16BPPColor(rgb);
341 }
342 }
343
344
345 #ifdef WITH_UNITTESTS
346 #undef FAIL
347 #include "gtest/gtest.h"
348
TEST(HImage,asserts)349 TEST(HImage, asserts)
350 {
351 EXPECT_EQ(sizeof(AuxObjectData), 16u);
352 EXPECT_EQ(sizeof(RelTileLoc), 2u);
353 EXPECT_EQ(sizeof(ETRLEObject), 16u);
354 EXPECT_EQ(sizeof(SGPPaletteEntry), 4u);
355 }
356
357 #endif
358