1 #include "Debug.h"
2 #include "HImage.h"
3 #include "MemMan.h"
4 #include "Shading.h"
5 #include "VObject_Blitters.h"
6 #include "VSurface.h"
7 #include "Video.h"
8 #include "SGP.h"
9 #include "Logger.h"
10 
11 #include <string_theory/format>
12 #include <string_theory/string>
13 
14 #include <stdexcept>
15 
16 extern SGPVSurface* gpVSurfaceHead;
17 
18 
SGPVSurface(UINT16 const w,UINT16 const h,UINT8 const bpp)19 SGPVSurface::SGPVSurface(UINT16 const w, UINT16 const h, UINT8 const bpp) :
20 	p16BPPPalette(),
21 #ifdef SGP_VIDEO_DEBUGGING
22 	name_(),
23 	code_(),
24 #endif
25 	next_(gpVSurfaceHead)
26 {
27 	Assert(w > 0);
28 	Assert(h > 0);
29 
30 	SDL_Surface* s;
31 	switch (bpp)
32 	{
33 		case 8:
34 			s = SDL_CreateRGBSurface(0, w, h, bpp, 0, 0, 0, 0);
35 			break;
36 
37 		case 16:
38 		{
39 			SDL_PixelFormat const* f = SDL_AllocFormat(SDL_PIXELFORMAT_RGB565);
40 			s = SDL_CreateRGBSurface(0, w, h, bpp, f->Rmask, f->Gmask, f->Bmask, f->Amask);
41 			break;
42 		}
43 
44 		default:
45 			throw std::logic_error("Tried to create video surface with invalid bpp, must be 8 or 16.");
46 	}
47 	if (!s) throw std::runtime_error("Failed to create SDL surface");
48 	surface_ = s;
49 	gpVSurfaceHead = this;
50 #ifdef SGP_VIDEO_DEBUGGING
51 	++guiVSurfaceSize;
52 #endif
53 }
54 
55 
SGPVSurface(SDL_Surface * const s)56 SGPVSurface::SGPVSurface(SDL_Surface* const s) :
57 	surface_(s),
58 	p16BPPPalette(),
59 #ifdef SGP_VIDEO_DEBUGGING
60 	name_(),
61 	code_(),
62 #endif
63 	next_(gpVSurfaceHead)
64 {
65 	gpVSurfaceHead = this;
66 #ifdef SGP_VIDEO_DEBUGGING
67 	++guiVSurfaceSize;
68 #endif
69 }
70 
71 
~SGPVSurface()72 SGPVSurface::~SGPVSurface()
73 {
74 	for (SGPVSurface** anchor = &gpVSurfaceHead;; anchor = &(*anchor)->next_)
75 	{
76 		if (*anchor != this) continue;
77 		*anchor = next_;
78 #ifdef SGP_VIDEO_DEBUGGING
79 		--guiVSurfaceSize;
80 #endif
81 		break;
82 	}
83 
84 	if (p16BPPPalette) delete[] p16BPPPalette;
85 
86 #ifdef SGP_VIDEO_DEBUGGING
87 	if (name_) delete[] name_;
88 	if (code_) delete[] code_;
89 #endif
90 }
91 
92 
SetPalette(const SGPPaletteEntry * const src_pal)93 void SGPVSurface::SetPalette(const SGPPaletteEntry* const src_pal)
94 {
95 	// Create palette object if not already done so
96 	if (!palette_) palette_.Allocate(256);
97 	SGPPaletteEntry* const p = palette_;
98 	for (UINT32 i = 0; i < 256; i++)
99 	{
100 		p[i] = src_pal[i];
101 	}
102 
103 	if (p16BPPPalette != NULL) delete[] p16BPPPalette;
104 	p16BPPPalette = Create16BPPPalette(src_pal);
105 }
106 
107 
SetTransparency(const COLORVAL colour)108 void SGPVSurface::SetTransparency(const COLORVAL colour)
109 {
110 	Uint32 colour_key;
111 	switch (BPP())
112 	{
113 		case  8: colour_key = colour;                break;
114 		case 16: colour_key = Get16BPPColor(colour); break;
115 
116 		default: abort(); // HACK000E
117 	}
118 	SDL_SetColorKey(surface_, SDL_TRUE, colour_key);
119 }
120 
121 
Fill(const UINT16 colour)122 void SGPVSurface::Fill(const UINT16 colour)
123 {
124 	SDL_FillRect(surface_, NULL, colour);
125 }
126 
SGPVSurfaceAuto(UINT16 w,UINT16 h,UINT8 bpp)127 SGPVSurfaceAuto::SGPVSurfaceAuto(UINT16 w, UINT16 h, UINT8 bpp)
128 	: SGPVSurface(w, h, bpp)
129 {
130 }
131 
SGPVSurfaceAuto(SDL_Surface * surface)132 SGPVSurfaceAuto::SGPVSurfaceAuto(SDL_Surface* surface)
133 	: SGPVSurface(surface)
134 {
135 }
136 
~SGPVSurfaceAuto()137 SGPVSurfaceAuto::~SGPVSurfaceAuto()
138 {
139 	if(surface_)
140 	{
141 		SDL_FreeSurface(surface_);
142 	}
143 }
144 
145 
InternalShadowVideoSurfaceRect(SGPVSurface * const dst,INT32 X1,INT32 Y1,INT32 X2,INT32 Y2,const UINT16 * const filter_table)146 static void InternalShadowVideoSurfaceRect(SGPVSurface* const dst, INT32 X1, INT32 Y1, INT32 X2, INT32 Y2, const UINT16* const filter_table)
147 {
148 	if (X1 < 0) X1 = 0;
149 	if (X2 < 0) return;
150 
151 	if (Y2 < 0) return;
152 	if (Y1 < 0) Y1 = 0;
153 
154 	if (X2 >= dst->Width())  X2 = dst->Width() - 1;
155 	if (Y2 >= dst->Height()) Y2 = dst->Height() - 1;
156 
157 	if (X1 >= dst->Width())  return;
158 	if (Y1 >= dst->Height()) return;
159 
160 	if (X2 - X1 <= 0) return;
161 	if (Y2 - Y1 <= 0) return;
162 
163 	SGPRect area;
164 	area.iTop    = Y1;
165 	area.iBottom = Y2;
166 	area.iLeft   = X1;
167 	area.iRight  = X2;
168 
169 	SGPVSurface::Lock ldst(dst);
170 	Blt16BPPBufferFilterRect(ldst.Buffer<UINT16>(), ldst.Pitch(), filter_table, &area);
171 }
172 
173 
ShadowRect(INT32 const x1,INT32 const y1,INT32 const x2,INT32 const y2)174 void SGPVSurface::ShadowRect(INT32 const x1, INT32 const y1, INT32 const x2, INT32 const y2)
175 {
176 	InternalShadowVideoSurfaceRect(this, x1, y1, x2, y2, ShadeTable);
177 }
178 
179 
ShadowRectUsingLowPercentTable(INT32 const x1,INT32 const y1,INT32 const x2,INT32 const y2)180 void SGPVSurface::ShadowRectUsingLowPercentTable(INT32 const x1, INT32 const y1, INT32 const x2, INT32 const y2)
181 {
182 	InternalShadowVideoSurfaceRect(this, x1, y1, x2, y2, IntensityTable);
183 }
184 
185 SGPVSurface* g_back_buffer;
186 SGPVSurfaceAuto* g_frame_buffer;
187 SGPVSurfaceAuto* g_mouse_buffer;
188 
189 
190 #undef AddVideoSurface
191 #undef AddVideoSurfaceFromFile
192 
193 
AddVideoSurface(UINT16 Width,UINT16 Height,UINT8 BitDepth)194 SGPVSurfaceAuto* AddVideoSurface(UINT16 Width, UINT16 Height, UINT8 BitDepth)
195 {
196 	SGPVSurfaceAuto* const vs = new SGPVSurfaceAuto(Width, Height, BitDepth);
197 	return vs;
198 }
199 
200 
AddVideoSurfaceFromFile(const char * const Filename)201 SGPVSurfaceAuto* AddVideoSurfaceFromFile(const char* const Filename)
202 {
203 	AutoSGPImage img(CreateImage(Filename, IMAGE_ALLIMAGEDATA));
204 
205 	SGPVSurfaceAuto* const vs = new SGPVSurfaceAuto(img->usWidth, img->usHeight, img->ubBitDepth);
206 
207 	UINT8 const dst_bpp = vs->BPP();
208 	UINT32      buffer_bpp;
209 	switch (dst_bpp)
210 	{
211 		case  8: buffer_bpp = BUFFER_8BPP;  break;
212 		case 16: buffer_bpp = BUFFER_16BPP; break;
213 		default: throw std::logic_error("Invalid bpp");
214 	}
215 
216 	{ SGPVSurface::Lock l(vs);
217 		UINT8*  const dst   = l.Buffer<UINT8>();
218 		UINT16  const pitch = l.Pitch() / (dst_bpp / 8); // pitch in pixels
219 		SGPBox  const box   = { 0, 0, img->usWidth, img->usHeight };
220 		BOOLEAN const Ret   = CopyImageToBuffer(img.get(), buffer_bpp, dst, pitch, vs->Height(), 0, 0, &box);
221 		if (!Ret)
222 		{
223 			SLOGE("Error Occured Copying SGPImage to video surface");
224 		}
225 	}
226 
227 	if (img->ubBitDepth == 8) vs->SetPalette(img->pPalette);
228 
229 	return vs;
230 }
231 
232 
233 #ifdef SGP_VIDEO_DEBUGGING
234 
RecordVSurface(SGPVSurface * const vs,char const * const Filename,UINT32 const LineNum,char const * const SourceFile)235 static void RecordVSurface(SGPVSurface* const vs, char const* const Filename, UINT32 const LineNum, char const* const SourceFile)
236 {
237 	//record the filename of the vsurface (some are created via memory though)
238 	vs->name_ = new char[strlen(Filename) + 1]{};
239 	strcpy(vs->name_, Filename);
240 
241 	//record the code location of the calling creating function.
242 	char str[256];
243 	sprintf(str, "%s -- line(%d)", SourceFile, LineNum);
244 	vs->code_ = new char[strlen(str) + 1]{};
245 	strcpy(vs->code_, str);
246 }
247 
248 #	define RECORD(vs, name) RecordVSurface((vs), (name), __LINE__, __FILE__)
249 #else
250 #	define RECORD(cs, name) ((void)0)
251 #endif
252 
BltVideoSurfaceHalf(SGPVSurface * const dst,SGPVSurface * const src,INT32 const DestX,INT32 const DestY,SGPBox const * const src_rect)253 void BltVideoSurfaceHalf(SGPVSurface* const dst, SGPVSurface* const src, INT32 const DestX, INT32 const DestY, SGPBox const* const src_rect)
254 {
255 	SGPVSurface::Lock lsrc(src);
256 	SGPVSurface::Lock ldst(dst);
257 	UINT8*  const SrcBuf         = lsrc.Buffer<UINT8>();
258 	UINT32  const SrcPitchBYTES  = lsrc.Pitch();
259 	UINT16* const DestBuf        = ldst.Buffer<UINT16>();
260 	UINT32  const DestPitchBYTES = ldst.Pitch();
261 	Blt8BPPDataTo16BPPBufferHalf(DestBuf, DestPitchBYTES, src, SrcBuf, SrcPitchBYTES, DestX, DestY, src_rect);
262 }
263 
264 
ColorFillVideoSurfaceArea(SGPVSurface * const dst,INT32 iDestX1,INT32 iDestY1,INT32 iDestX2,INT32 iDestY2,const UINT16 Color16BPP)265 void ColorFillVideoSurfaceArea(SGPVSurface* const dst, INT32 iDestX1, INT32 iDestY1, INT32 iDestX2, INT32 iDestY2, const UINT16 Color16BPP)
266 {
267 	SGPRect Clip;
268 	GetClippingRect(&Clip);
269 
270 	if (iDestX1 < Clip.iLeft) iDestX1 = Clip.iLeft;
271 	if (iDestX1 > Clip.iRight) return;
272 
273 	if (iDestX2 > Clip.iRight) iDestX2 = Clip.iRight;
274 	if (iDestX2 < Clip.iLeft) return;
275 
276 	if (iDestY1 < Clip.iTop) iDestY1 = Clip.iTop;
277 	if (iDestY1 > Clip.iBottom) return;
278 
279 	if (iDestY2 > Clip.iBottom) iDestY2 = Clip.iBottom;
280 	if (iDestY2 < Clip.iTop) return;
281 
282 	if (iDestX2 <= iDestX1 || iDestY2 <= iDestY1) return;
283 
284 	SDL_Rect Rect;
285 	Rect.x = iDestX1;
286 	Rect.y = iDestY1;
287 	Rect.w = iDestX2 - iDestX1;
288 	Rect.h = iDestY2 - iDestY1;
289 	SDL_FillRect(dst->surface_, &Rect, Color16BPP);
290 }
291 
292 
293 // Will drop down into user-defined blitter if 8->16 BPP blitting is being done
BltVideoSurface(SGPVSurface * const dst,SGPVSurface * const src,INT32 const iDestX,INT32 const iDestY,SGPBox const * const src_box)294 void BltVideoSurface(SGPVSurface* const dst, SGPVSurface* const src, INT32 const iDestX, INT32 const iDestY, SGPBox const* const src_box)
295 {
296 	Assert(dst);
297 	Assert(src);
298 
299 	const UINT8 src_bpp = src->BPP();
300 	const UINT8 dst_bpp = dst->BPP();
301 	if (src_bpp == dst_bpp)
302 	{
303 		SDL_Rect* src_rect = 0;
304 		SDL_Rect  r;
305 		if (src_box)
306 		{
307 			r.x = src_box->x;
308 			r.y = src_box->y;
309 			r.w = src_box->w;
310 			r.h = src_box->h;
311 			src_rect = &r;
312 		}
313 
314 		SDL_Rect dstrect;
315 		dstrect.x = iDestX;
316 		dstrect.y = iDestY;
317 		SDL_BlitSurface(src->surface_, src_rect, dst->surface_, &dstrect);
318 	}
319 	else if (src_bpp < dst_bpp)
320 	{
321 		SGPBox const* src_rect = src_box;
322 		SGPBox        r;
323 		if (!src_rect)
324 		{
325 			// Check Sizes, SRC size MUST be <= DEST size
326 			if (dst->Height() < src->Height())
327 			{
328 				SLOGD("Incompatible height size given in Video Surface blit");
329 				return;
330 			}
331 			if (dst->Width() < src->Width())
332 			{
333 				SLOGD("Incompatible height size given in Video Surface blit");
334 				return;
335 			}
336 
337 			r.x = 0;
338 			r.y = 0;
339 			r.w = src->Width();
340 			r.h = src->Height();
341 			src_rect = &r;
342 		}
343 
344 		SGPVSurface::Lock lsrc(src);
345 		SGPVSurface::Lock ldst(dst);
346 		UINT8*  const s_buf  = lsrc.Buffer<UINT8>();
347 		UINT32  const spitch = lsrc.Pitch();
348 		UINT16* const d_buf  = ldst.Buffer<UINT16>();
349 		UINT32  const dpitch = ldst.Pitch();
350 		Blt8BPPDataSubTo16BPPBuffer(d_buf, dpitch, src, s_buf, spitch, iDestX, iDestY, src_rect);
351 	}
352 	else
353 	{
354 		SLOGD("Incompatible BPP values with src and dest Video Surfaces for blitting");
355 	}
356 }
357 
358 
BltStretchVideoSurface(SGPVSurface * const dst,SGPVSurface const * const src,SGPBox const * const src_rect,SGPBox const * const dst_rect)359 void BltStretchVideoSurface(SGPVSurface* const dst, SGPVSurface const* const src, SGPBox const* const src_rect, SGPBox const* const dst_rect)
360 {
361 	if (dst->BPP() != 16 || src->BPP() != 16) return;
362 
363 	SDL_Surface const* const ssurface = src->surface_;
364 	SDL_Surface*       const dsurface = dst->surface_;
365 
366 	const UINT32  s_pitch = ssurface->pitch >> 1;
367 	const UINT32  d_pitch = dsurface->pitch >> 1;
368 	UINT16 const* os      = (const UINT16*)ssurface->pixels + s_pitch * src_rect->y + src_rect->x;
369 	UINT16*       d       =       (UINT16*)dsurface->pixels + d_pitch * dst_rect->y + dst_rect->x;
370 
371 	UINT const width  = dst_rect->w;
372 	UINT const height = dst_rect->h;
373 	UINT const dx     = src_rect->w;
374 	UINT const dy     = src_rect->h;
375 	UINT py = 0;
376 	if (ssurface->flags & SDL_TRUE)
377 	{
378 //		const UINT16 key = ssurface->format->colorkey;
379 		const UINT16 key = 0;
380 		for (UINT iy = 0; iy < height; ++iy)
381 		{
382 			const UINT16* s = os;
383 			UINT px = 0;
384 			for (UINT ix = 0; ix < width; ++ix)
385 			{
386 				if (*s != key) *d = *s;
387 				++d;
388 				px += dx;
389 				for (; px >= width; px -= width) ++s;
390 			}
391 			d += d_pitch - width;
392 			py += dy;
393 			for (; py >= height; py -= height) os += s_pitch;
394 		}
395 	}
396 	else
397 	{
398 		for (UINT iy = 0; iy < height; ++iy)
399 		{
400 			const UINT16* s = os;
401 			UINT px = 0;
402 			for (UINT ix = 0; ix < width; ++ix)
403 			{
404 				*d++ = *s;
405 				px += dx;
406 				for (; px >= width; px -= width) ++s;
407 			}
408 			d += d_pitch - width;
409 			py += dy;
410 			for (; py >= height; py -= height) os += s_pitch;
411 		}
412 	}
413 }
414 
415 
BltVideoSurfaceOnce(SGPVSurface * const dst,const char * const filename,INT32 const x,INT32 const y)416 void BltVideoSurfaceOnce(SGPVSurface* const dst, const char* const filename, INT32 const x, INT32 const y)
417 {
418 	std::unique_ptr<SGPVSurfaceAuto> src(AddVideoSurfaceFromFile(filename));
419 	BltVideoSurface(dst, src.get(), x, y, NULL);
420 }
421 
422 /** Draw image on the video surface stretching the image if necessary. */
BltVideoSurfaceOnceWithStretch(SGPVSurface * const dst,const char * const filename)423 void BltVideoSurfaceOnceWithStretch(SGPVSurface* const dst, const char* const filename)
424 {
425 	std::unique_ptr<SGPVSurfaceAuto> src(AddVideoSurfaceFromFile(filename));
426 	FillVideoSurfaceWithStretch(dst, src.get());
427 }
428 
429 /** Fill video surface with another one with stretch. */
FillVideoSurfaceWithStretch(SGPVSurface * const dst,SGPVSurface * const src)430 void FillVideoSurfaceWithStretch(SGPVSurface* const dst, SGPVSurface* const src)
431 {
432 	SGPBox srcRec;
433 	SGPBox dstRec;
434 	srcRec.set(0, 0, src->Width(), src->Height());
435 	dstRec.set(0, 0, dst->Width(), dst->Height());
436 	BltStretchVideoSurface(dst, src, &srcRec, &dstRec);
437 }
438 
439 #ifdef SGP_VIDEO_DEBUGGING
440 
441 UINT32 guiVSurfaceSize = 0;
442 
443 
444 struct DUMPINFO
445 {
446 	UINT32 Counter;
447 	char Name[256];
448 	char Code[256];
449 };
450 
451 
DumpVSurfaceInfoIntoFile(const char * filename,BOOLEAN fAppend)452 void DumpVSurfaceInfoIntoFile(const char* filename, BOOLEAN fAppend)
453 {
454 	if (!guiVSurfaceSize) return;
455 
456 	RustPointer<File> file(File_open(filename, fAppend ? FILE_OPEN_APPEND : FILE_OPEN_WRITE));
457 	if (!file)
458 	{
459 		RustPointer<char> err(getRustError());
460 		SLOGA("DumpVSurfaceInfoIntoFile: %s", err.get());
461 		return;
462 	}
463 
464 	//Allocate enough strings and counters for each node.
465 	DUMPINFO* const Info = new DUMPINFO[guiVSurfaceSize]{};
466 
467 	//Loop through the list and record every unique filename and count them
468 	UINT32 uiUniqueID = 0;
469 	for (SGPVSurface const* i = gpVSurfaceHead; i; i = i->next_)
470 	{
471 		char const* const Name = i->name_;
472 		char const* const Code = i->code_;
473 		BOOLEAN fFound = FALSE;
474 		for (UINT32 i = 0; i < uiUniqueID; i++)
475 		{
476 			if (strcasecmp(Name, Info[i].Name) == 0 && strcasecmp(Code, Info[i].Code) == 0)
477 			{ //same string
478 				fFound = TRUE;
479 				Info[i].Counter++;
480 				break;
481 			}
482 		}
483 		if (!fFound)
484 		{
485 			strcpy(Info[uiUniqueID].Name, Name);
486 			strcpy(Info[uiUniqueID].Code, Code);
487 			Info[uiUniqueID].Counter++;
488 			uiUniqueID++;
489 		}
490 	}
491 
492 	//Now dump the info.
493 	ST::string buf;
494 	buf += "-----------------------------------------------\n";
495 	buf += ST::format(ST::substitute_invalid, "{} unique vSurface names exist in {} VSurfaces\n", uiUniqueID, guiVSurfaceSize);
496 	buf += "-----------------------------------------------\n\n";
497 	for (UINT32 i = 0; i < uiUniqueID; i++)
498 	{
499 		buf += ST::format(ST::substitute_invalid, "{} occurrences of {}\n{}\n\n", Info[i].Counter, Info[i].Name, Info[i].Code);
500 	}
501 	buf += "\n-----------------------------------------------\n\n";
502 
503 	delete[] Info;
504 	if (!File_writeAll(file.get(), reinterpret_cast<const uint8_t*>(buf.c_str()), buf.size()))
505 	{
506 		RustPointer<char> err(getRustError());
507 		SLOGW("DumpVSurfaceInfoIntoFile: %s", err.get());
508 	}
509 }
510 
511 
AddAndRecordVSurface(const UINT16 Width,const UINT16 Height,const UINT8 BitDepth,const UINT32 LineNum,const char * const SourceFile)512 SGPVSurface* AddAndRecordVSurface(const UINT16 Width, const UINT16 Height, const UINT8 BitDepth, const UINT32 LineNum, const char* const SourceFile)
513 {
514 	SGPVSurface* const vs = AddVideoSurface(Width, Height, BitDepth);
515 	RecordVSurface(vs, "<EMPTY>", LineNum, SourceFile);
516 	return vs;
517 }
518 
519 
AddAndRecordVSurfaceFromFile(const char * const Filename,const UINT32 LineNum,const char * const SourceFile)520 SGPVSurface* AddAndRecordVSurfaceFromFile(const char* const Filename, const UINT32 LineNum, const char* const SourceFile)
521 {
522 	SGPVSurface* const vs = AddVideoSurfaceFromFile(Filename);
523 	RecordVSurface(vs, Filename, LineNum, SourceFile);
524 	return vs;
525 }
526 
527 #endif
528