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