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