1 /*
2 *
3 * Iter Vehemens ad Necem (IVAN)
4 * Copyright (C) Timo Kiviluoto
5 * Released under the GNU General
6 * Public License
7 *
8 * See LICENSING which should be included
9 * along with this file for more details
10 *
11 */
12
13 #ifdef USE_SDL
14 #include "SDL.h"
15 #endif
16
17 #ifdef __DJGPP__
18 #include <dpmi.h>
19 #include <conio.h>
20 #include <go32.h>
21 #endif
22
23 #include <iostream>
24 #include <sstream>
25
26 #include "graphics.h"
27 #include "bitmap.h"
28 #include "whandler.h"
29 #include "error.h"
30 #include "rawbit.h"
31 #include "felist.h"
32 #include "feio.h"
33
34 void (*graphics::SwitchModeHandler)();
35
36 #ifdef USE_SDL
37 #if SDL_MAJOR_VERSION == 1
38 SDL_Surface* graphics::Screen;
39 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
40 SDL_Surface* graphics::TempSurface;
41 #endif
42 #else
43 SDL_Window* graphics::Window;
44 SDL_Renderer* graphics::Renderer;
45 SDL_Texture* graphics::Texture;
46 #endif
47 #endif
48
49 #ifdef __DJGPP__
50 ulong graphics::BufferSize;
51 ushort graphics::ScreenSelector = 0;
52 graphics::vesainfo graphics::VesaInfo;
53 graphics::modeinfo graphics::ModeInfo;
54 #endif
55
56 //TODO create a utility `class sregion{}` to set it's values outside here w/o using graphics::...
57 struct stretchRegion //TODO all these booleans could be a single uint32? unnececessarily complicated?
58 {
59 int iIndex;
60 const char* strId;
61 bool bEnabled;
62 blitdata B;
63 bitmap* bmpOverride;
64
65 bool bUseXBRZ;
66 bool bUseXBRZDrawBeforeFelistPage;
67 bool bDrawBeforeFelistPage;
68 bool bDrawAfterFelist;
69 bool bDrawAlways;
70 bool bSpecialListItem;
71 bool bDrawRectangleOutline;
72 bool bAllowTransparency; //mask
73
74 v2 v2SquareSize; //to clear
75
76 std::vector<v2> vv2ClearSquaresAt; //these are the relative top left square's pixels at BClearSquares.Bitmap
77 blitdata BClearSquares; //intermediary, it's bitmap will be filled to serve as source
78
79 bitmap* CacheBitmap;
80 };
81 #define DBGMSG_STRETCHREGION
82 #include "dbgmsgproj.h"
83
84 const stretchRegion SRdefault = {
85 -1,"READABLE ID NOT SET!!!",true,DEFAULT_BLITDATA,NULL,
86 false,false,false, false,false,false, false,false,
87 v2(),
88 std::vector<v2>(), DEFAULT_BLITDATA,
89 NULL
90 };
91 bool graphics::bSpecialListItemAltPos=false;
92 bool bPrepareCacheBitmapsBeforeFelist=false;
93 bool bDrawCacheBitmapsBeforeFelist=false;
94
95 /* !!! USE GetSRegion(), never access this vector indexes directly !!! */
96 std::vector<stretchRegion> vStretchRegion;
97
98 truth graphics::bAllowStretchedRegionsBlit=false;
99
100 bitmap* graphics::DoubleBuffer=NULL;
101 bitmap* graphics::StretchedBuffer=NULL; //this is actually a 3rd buffer...
102 v2 graphics::Res;
103 int graphics::Scale;
104 int graphics::ColorDepth;
105 rawbitmap* graphics::DefaultFont = 0;
106
Init()107 void graphics::Init()
108 {
109 static truth AlreadyInstalled = false;
110
111 if(!AlreadyInstalled)
112 {
113 AlreadyInstalled = true;
114
115 #ifdef USE_SDL
116 if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_NOPARACHUTE))
117 ABORT("Can't initialize SDL.");
118 #if SDL_MAJOR_VERSION == 2
119 SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengl");
120 #endif
121 #endif
122
123 #ifdef __DJGPP__
124 VesaInfo.Retrieve();
125 #endif
126
127 atexit(graphics::DeInit);
128 }
129 }
130
DeInit()131 void graphics::DeInit()
132 {
133 delete DefaultFont;
134 DefaultFont = 0;
135
136 #ifdef USE_SDL
137 #if SDL_MAJOR_VERSION == 1
138 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
139 SDL_FreeSurface(TempSurface);
140 #endif
141 #else
142 if(Texture)
143 SDL_DestroyTexture(Texture);
144
145 if(Renderer)
146 SDL_DestroyRenderer(Renderer);
147
148 if(Window)
149 SDL_DestroyWindow(Window);
150 #endif
151 SDL_Quit();
152 #endif
153
154 #ifdef __DJGPP__
155 if(ScreenSelector)
156 {
157 __dpmi_free_ldt_descriptor(ScreenSelector);
158 ScreenSelector = 0;
159 textmode(0x3);
160 }
161 #endif
162 }
163
164 #ifdef USE_SDL
165
166 bool bAllowMouseInFullScreen=false;
SetAllowMouseInFullScreen(bool b)167 void graphics::SetAllowMouseInFullScreen(bool b)
168 {
169 bAllowMouseInFullScreen=b;
170 }
171
SetMode(cchar * Title,cchar * IconName,v2 NewRes,int NewScale,int ScalingQuality,truth FullScreen)172 void graphics::SetMode(cchar* Title, cchar* IconName,
173 v2 NewRes, int NewScale, int ScalingQuality,
174 truth FullScreen)
175 {
176 #if SDL_MAJOR_VERSION == 1
177 if(IconName)
178 {
179 SDL_Surface* Icon = SDL_LoadBMP(IconName);
180 SDL_SetColorKey(Icon, SDL_SRCCOLORKEY,
181 SDL_MapRGB(Icon->format, 255, 255, 255));
182 SDL_WM_SetIcon(Icon, NULL);
183 }
184 #endif
185
186 ulong Flags = SDL_SWSURFACE;
187
188 if(FullScreen)
189 {
190 if(!bAllowMouseInFullScreen)
191 SDL_ShowCursor(SDL_DISABLE);
192 #if SDL_MAJOR_VERSION == 1
193 Flags |= SDL_FULLSCREEN;
194 #else
195 Flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
196 #endif
197 }
198
199 #if SDL_MAJOR_VERSION == 1
200 Screen = SDL_SetVideoMode(NewRes.X, NewRes.Y, 16, Flags);
201 if(!Screen)
202 ABORT("Couldn't set video mode.");
203
204 SDL_WM_SetCaption(Title, 0);
205 #else
206 Flags |= SDL_WINDOW_ALLOW_HIGHDPI|SDL_WINDOW_HIDDEN;
207
208 Window = SDL_CreateWindow(Title,
209 SDL_WINDOWPOS_UNDEFINED,
210 SDL_WINDOWPOS_UNDEFINED,
211 NewRes.X, NewRes.Y, Flags);
212
213 if(!Window)
214 ABORT("Couldn't set video mode.");
215
216 if(IconName)
217 {
218 SDL_Surface* Icon = SDL_LoadBMP(IconName);
219 SDL_SetColorKey(Icon, SDL_TRUE,
220 SDL_MapRGB(Icon->format, 255, 255, 255));
221 SDL_SetWindowIcon(Window, Icon);
222 SDL_FreeSurface(Icon);
223 }
224
225 Renderer = SDL_CreateRenderer(Window, -1, 0);
226 if(!Renderer)
227 ABORT("Couldn't set renderer mode.");
228
229 SDL_RenderSetLogicalSize(Renderer, NewRes.X, NewRes.Y);
230
231 switch(ScalingQuality){
232 case 1: SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear"); break;
233 default: SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "nearest");
234 }
235
236 Texture = SDL_CreateTexture(Renderer,
237 SDL_PIXELFORMAT_RGB565,
238 SDL_TEXTUREACCESS_STREAMING,
239 NewRes.X, NewRes.Y);
240 #endif
241
242 globalwindowhandler::Init();
243 DoubleBuffer = new bitmap(NewRes);
244 StretchedBuffer = new bitmap(NewRes); DBG2("StretchedBuffer",StretchedBuffer);
245 Res = NewRes;
246 SetScale(NewScale);
247 ColorDepth = 16;
248
249 #if SDL_MAJOR_VERSION == 1
250 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
251
252 Uint32 rmask, gmask, bmask;
253 rmask = 0xF800;
254 gmask = 0x7E0;
255 bmask = 0x1F;
256
257 TempSurface = SDL_CreateRGBSurface(SDL_SWSURFACE, Res.X, Res.Y, 16,
258 rmask, gmask, bmask, 0);
259
260 if(!TempSurface)
261 ABORT("CreateRGBSurface failed: %s\n", SDL_GetError());
262
263 #endif
264 #endif
265 }
266
267 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
268
BlitDBToScreen()269 void graphics::BlitDBToScreen()
270 {
271 #if SDL_MAJOR_VERSION == 1
272 SDL_LockSurface(TempSurface);
273 packcol16* SrcPtr = DoubleBuffer->GetImage()[0];
274 packcol16* DestPtr = static_cast<packcol16*>(TempSurface->pixels);
275 ulong ScreenYMove = (TempSurface->pitch >> 1);
276 ulong LineSize = Res.X << 1;
277
278 for(int y = 0; y < Res.Y; ++y, SrcPtr += Res.X, DestPtr += ScreenYMove)
279 memcpy(DestPtr, SrcPtr, LineSize);
280
281 SDL_UnlockSurface(TempSurface);
282 SDL_Surface* S = SDL_DisplayFormat(TempSurface);
283 SDL_BlitSurface(S, NULL, Screen, NULL);
284 SDL_FreeSurface(S);
285 SDL_UpdateRect(Screen, 0, 0, Res.X, Res.Y);
286 #else
287 SDL_UpdateTexture(sdlTexture, NULL, myPixels, 640 * sizeof (Uint32));
288 #endif
289 }
290
291 #else
292
Stretch(bool bXbrzMode,bitmap * pBmpFrom,blitdata & rBto,bool bAllowTransparency)293 void graphics::Stretch(bool bXbrzMode, bitmap* pBmpFrom, blitdata& rBto, bool bAllowTransparency){
294 if(bXbrzMode){
295 pBmpFrom->StretchBlitXbrz(rBto,bAllowTransparency);
296 }else{
297 pBmpFrom->StretchBlit(rBto);
298 }
299 }
300
301 /* if on class, make it private */
GetSRegion(int iIndex)302 stretchRegion& GetSRegion(int iIndex){ // vectors are too permissive, it eveb accepts -1 index and will cause a lot of weirdness...
303 if(iIndex>=vStretchRegion.size() || iIndex<0)ABORT("Invalid SRegion index=%d (tot=%d)",iIndex,(int)vStretchRegion.size());
304 return vStretchRegion[iIndex];
305 }
306
IsSRegionEnabled(int iIndex)307 bool graphics::IsSRegionEnabled(int iIndex){
308 if(iIndex>=vStretchRegion.size())return false; //not ready yet
309 return GetSRegion(iIndex).bEnabled;
310 }
SetSRegionEnabled(int iIndex,bool b)311 void graphics::SetSRegionEnabled(int iIndex, bool b){
312 GetSRegion(iIndex).bEnabled=b; DBG2(iIndex,vStretchRegion.size());DBGEXEC(if(b){stretchRegion& rSR = GetSRegion(iIndex);DBGSR;});
313 }
SetSRegionUseXBRZ(int iIndex,bool b)314 void graphics::SetSRegionUseXBRZ(int iIndex, bool b){
315 GetSRegion(iIndex).bUseXBRZ=b;
316 }
SetSRegionDrawBeforeFelistPage(int iIndex,bool bDrawBeforeFelistPage,bool bUseXBRZDrawBeforeFelistPage)317 void graphics::SetSRegionDrawBeforeFelistPage(int iIndex, bool bDrawBeforeFelistPage, bool bUseXBRZDrawBeforeFelistPage){
318 stretchRegion& rSR = GetSRegion(iIndex);
319 rSR.bDrawBeforeFelistPage=bDrawBeforeFelistPage;
320 rSR.bUseXBRZDrawBeforeFelistPage=bUseXBRZDrawBeforeFelistPage;
321 }
SetSRegionDrawAfterFelist(int iIndex,bool b)322 void graphics::SetSRegionDrawAfterFelist(int iIndex, bool b){
323 GetSRegion(iIndex).bDrawAfterFelist=b;
324 }
SetSRegionDrawAlways(int iIndex,bool b)325 void graphics::SetSRegionDrawAlways(int iIndex, bool b){
326 GetSRegion(iIndex).bDrawAlways=b;
327 }
SetSRegionDrawRectangleOutline(int iIndex,bool b)328 void graphics::SetSRegionDrawRectangleOutline(int iIndex, bool b){
329 GetSRegion(iIndex).bDrawRectangleOutline=b;
330 }
SetSRegionClearSquaresAt(int iIndex,v2 v2Size,std::vector<v2> vv2)331 void graphics::SetSRegionClearSquaresAt(int iIndex, v2 v2Size, std::vector<v2> vv2){
332 stretchRegion& rSR = GetSRegion(iIndex);
333 rSR.vv2ClearSquaresAt=vv2;
334 rSR.v2SquareSize=v2Size;
335 rSR.bAllowTransparency=true;
336 }
337 /**
338 * there can only be one set at a time
339 */
SetSRegionListItem(int iIndex)340 void graphics::SetSRegionListItem(int iIndex){
341 stretchRegion& rSR = GetSRegion(iIndex);
342 if(rSR.bSpecialListItem)return; //permissive on redundant setup
343
344 for(int i=0;i<vStretchRegion.size();i++){
345 stretchRegion SR=vStretchRegion[i];
346 if(SR.bSpecialListItem)ABORT("some other SRegion is already bSpecialListItem");
347 }
348
349 rSR.bSpecialListItem=true;
350 rSR.bDrawAfterFelist=true;
351 rSR.bEnabled=false;
352 }
353 /**
354 * it actually copies the blitdata
355 */
SetSRegionBlitdata(int iIndex,blitdata B)356 int graphics::SetSRegionBlitdata(int iIndex, blitdata B){
357 if(B.Stretch <=1 )ABORT("SRegion stretch value invalid %d", B.Stretch); // some actual scaling is required
358 if(B.Bitmap !=NULL)ABORT("SRegion bitmap should not be set."); // see below
359 if(StretchedBuffer==NULL)ABORT("StretchedBuffer not set yet.");
360
361 B.Bitmap = StretchedBuffer;
362 if(iIndex==-1){ //add
363 stretchRegion SRcp = SRdefault;
364 stretchRegion& rSR = SRcp;
365 rSR.B=B;
366 iIndex = rSR.iIndex = vStretchRegion.size();
367 vStretchRegion.push_back(rSR);DBGSRI("Add");
368 }else{ //update
369 stretchRegion& rSR = GetSRegion(iIndex);
370 if(rSR.bmpOverride!=NULL)ABORT("wrong usage, bitmap override already set: %s %d",rSR.strId,rSR.iIndex);
371 DBG2(rSR.iIndex,iIndex);if(rSR.iIndex!=iIndex)ABORT("wrongly configured SRegion internal index %d, expecting %d",rSR.iIndex,iIndex);
372 rSR.B=B;
373 DBGSRI("Update");
374 }
375
376 return iIndex;
377 }
378
379 /**
380 * If bmp is not NULL, it is an indicator (bool) itself. Therefore setting to null will disable it's functionality.
381 */
SetSRegionSrcBitmapOverride(int iIndex,bitmap * bmp,int iStretch,v2 v2Dest)382 void graphics::SetSRegionSrcBitmapOverride(int iIndex, bitmap* bmp, int iStretch, v2 v2Dest){
383 stretchRegion& rSR = GetSRegion(iIndex);
384 rSR.B.Src={0,0};
385
386 bool bDeletePrevious=false;
387 if(bmp == NULL)bDeletePrevious=true;
388 if(bmp != rSR.bmpOverride)bDeletePrevious=true;
389
390 if(bDeletePrevious){
391 if(rSR.bmpOverride != NULL){
392 delete rSR.bmpOverride;
393 rSR.bmpOverride = NULL;
394 }
395 }
396
397 rSR.bmpOverride=bmp;
398 if(bmp!=NULL)rSR.B.Border=bmp->GetSize();
399
400 rSR.B.Stretch=iStretch;
401
402 rSR.B.Dest=v2Dest;
403 }
404
AddStretchRegion(blitdata B,const char * strId)405 int graphics::AddStretchRegion(blitdata B,const char* strId){
406 int i = SetSRegionBlitdata(-1, B);
407 stretchRegion& rSR = vStretchRegion[i];
408 rSR.strId=strId;DBGSRI("AddOk");
409 return i;
410 }
411
SRegionPrepareClearedSquares(bitmap * DoubleBuffer,stretchRegion & rSR)412 bitmap* SRegionPrepareClearedSquares(bitmap* DoubleBuffer, stretchRegion& rSR){
413 blitdata& rB = rSR.B;
414 blitdata& rBC = rSR.BClearSquares;
415 if(rBC.Bitmap==NULL || rBC.Bitmap->GetSize()!=rB.Border){
416 if(rBC.Bitmap!=NULL)delete rBC.Bitmap;
417 rBC.Bitmap = new bitmap(rB.Border);
418 }
419
420 rBC.Src = rB.Src;DBGLN;
421 rBC.Dest = {0,0};DBGLN;
422 rBC.Border = rB.Border;DBGLN;
423 DBGBLD(rBC);
424 DoubleBuffer->NormalBlit(rBC);DBGLN;
425
426 for(int i=0;i<rSR.vv2ClearSquaresAt.size();i++){
427 rBC.Bitmap->Fill(rSR.vv2ClearSquaresAt[i],rSR.v2SquareSize,TRANSPARENT_COLOR);
428 }
429 rSR.vv2ClearSquaresAt.clear();
430
431 rB.Src=v2(); //as the blitdata for cleared squares will now be the source to be stretched from
432 return rBC.Bitmap;
433 }
434
isStretchedRegionsAllowed()435 bool graphics::isStretchedRegionsAllowed(){
436 return
437 bAllowStretchedRegionsBlit
438 &&
439 !iosystem::IsOnMenu() //main menu
440 &&
441 vStretchRegion.size()>0
442 ;
443 }
444
PrepareBeforeDrawingFelist()445 void graphics::PrepareBeforeDrawingFelist(){
446 if(!isStretchedRegionsAllowed())return;
447
448 for(int i=0;i<vStretchRegion.size();i++){
449 stretchRegion& rSR=vStretchRegion[i];
450 blitdata& rB=rSR.B;
451
452 if(!rSR.bDrawBeforeFelistPage)continue;
453
454 if(rSR.CacheBitmap==NULL || (rSR.CacheBitmap->GetSize() != rB.Border)){
455 if(rSR.CacheBitmap!=NULL){
456 delete rSR.CacheBitmap;
457 }
458
459 rSR.CacheBitmap = new bitmap(rB.Border * rB.Stretch);
460 }
461
462 blitdata B = rSR.B; //copy
463 B.Bitmap = rSR.CacheBitmap;
464 B.Dest=v2(0,0);
465
466 Stretch(rSR.bUseXBRZ && rSR.bUseXBRZDrawBeforeFelistPage, DoubleBuffer, B, false);
467 }
468 }
469
DrawAtDoubleBufferBeforeFelistPage()470 void graphics::DrawAtDoubleBufferBeforeFelistPage(){
471 if(!isStretchedRegionsAllowed())return;
472
473 for(int i=0;i<vStretchRegion.size();i++){
474 stretchRegion& rSR=vStretchRegion[i];
475
476 if(!rSR.bDrawBeforeFelistPage)continue;
477
478 rSR.CacheBitmap->FastBlit(DoubleBuffer, rSR.B.Dest); // is a cache substitute to the region scaling
479 }
480 }
481
debugTinyDungeon(bitmap * DoubleBuffer,bitmap * StretchedBuffer)482 void debugTinyDungeon(bitmap* DoubleBuffer, bitmap* StretchedBuffer){
483 static bool bDbgTinyDungeon = [](){const char* c=std::getenv("IVAN_DebugShowTinyDungeon");return c!=NULL && strcmp(c,"true")==0;}();
484 if(bDbgTinyDungeon){
485 blitdata Bdbg=DEFAULT_BLITDATA;
486 Bdbg.Bitmap=StretchedBuffer;
487 Bdbg.Border={400,300};
488 DoubleBuffer->NormalBlit(Bdbg);
489 }
490 }
491
PrepareBuffer()492 bitmap* graphics::PrepareBuffer(){
493 bitmap* ReturnBuffer = DoubleBuffer;
494
495 // if(felist::isAnyFelistCurrentlyDrawn())graphics::DrawAtDoubleBufferBeforeFelistPage();
496
497 if(isStretchedRegionsAllowed()){ // !!!!!!!!!!!!!!!!!!!!!!!! DO NOT MODIFY DoubleBuffer HERE (chaotic recursive blitting problem) !!!!!!!!!!!!!!!!!!!!!!!!
498 bool bDidStretch=false;
499 bool bOk=true;
500
501 for(int i=0;i<vStretchRegion.size();i++){
502 stretchRegion& rSR=vStretchRegion[i];
503 blitdata& rB=rSR.B;DBGSRI("tryBlit");
504
505 if(rB.Bitmap!=StretchedBuffer)ABORT("SRegion bitmap is not pointing to StretchedBuffer.");
506
507 // try to disable below, is easier to read long lists
508 bOk=true;
509
510 if(bOk && (!rSR.bEnabled))bOk=false;DBGSB(bOk);
511
512 if(bOk && (rB.Stretch<2 ))bOk=false;DBGSB(bOk);
513
514 // if(bOk && (rSR.bDrawBeforeFelistPage))bOk=false;DBGSB(bOk); //bDrawBeforeFelistPage is not meant to work here.
515
516 if(!rSR.bDrawAlways){
517 if(felist::isAnyFelistCurrentlyDrawn()){
518 if(bOk && (!rSR.bDrawAfterFelist))bOk=false;DBGSB(bOk);
519 }else{
520 if(bOk && ( rSR.bDrawAfterFelist))bOk=false;DBGSB(bOk);
521 }
522 }
523
524 if(!(rB.Border.X>=0 && rB.Border.Y>=0))ABORT("invalid SRegion border (negatives are critical) %d,%d",rB.Border.X,rB.Border.Y);
525 if(bOk)if(rB.Border.X==0 || rB.Border.Y==0){DBGSB(bOk);
526 if(rB.Border.Is0()){DBGSB(bOk); //being 0,0 may mean it is not ready yet (wouldnt be accepted to blit anyway).
527 bOk=false;DBGSB(bOk);
528 }else{DBGSB(bOk);
529 if(!(rB.Border.X>0 && rB.Border.Y>0))ABORT("SRegion border: minimum (if not 0,0) is 1,1: %d,%d",rB.Border.X,rB.Border.Y);
530 }
531 }
532
533 if(!(rB.Dest.X>=0 && rB.Dest.Y>=0))ABORT("invalid SRegion Dest (negatives are critical) %d,%d",rB.Dest.X,rB.Dest.Y);DBGSB(bOk);
534
535 if(bOk){
536 if(!bDidStretch){
537 // first time, if there is at least one stretching, prepare "background/base" on the stretched
538 DoubleBuffer->FastBlit(StretchedBuffer); //simple copy (like a 3rd buffer)
539 ReturnBuffer = StretchedBuffer; //and set stretched as the final source
540 }
541
542 if(rSR.bSpecialListItem){ DBGSRI("ListItem");
543 rB.Src = felist::GetCurrentListSelectedItemPos(); //the tiny one
544 }
545
546 bool bDrawSROutline=rSR.bDrawRectangleOutline;
547 if(rSR.bSpecialListItem){
548 if(bSpecialListItemAltPos){
549 bool bAltPosFullBkg=false; //TODO user option ? doesnt look too good anyway..
550 // let felist re-configure the blitdata before the stretching below
551 felist::PrepareListItemAltPosBackground(rB,bAltPosFullBkg);
552 if(bAltPosFullBkg)bDrawSROutline=false;
553 }
554 }
555
556 if(bDrawSROutline){
557 graphics::DrawRectangleOutlineAround(rB.Bitmap, rB.Dest, (rB.Border) * (rB.Stretch), DARK_GRAY, true);
558 }
559
560 bitmap* pbmpFrom = DoubleBuffer;
561 if(rSR.bmpOverride!=NULL){
562 pbmpFrom = rSR.bmpOverride;
563 }else
564 if(rSR.vv2ClearSquaresAt.size()>0){
565 pbmpFrom = SRegionPrepareClearedSquares(DoubleBuffer,rSR);
566 }
567
568 DBGSRI("STRETCHING FROM DoubleBuffer TO StretchedBuffer");
569 Stretch(rSR.bUseXBRZ, pbmpFrom, rB, rSR.bAllowTransparency);
570
571 bDidStretch=true;
572 }
573 }
574
575 debugTinyDungeon(DoubleBuffer,StretchedBuffer);
576 }
577
578 DrawAboveAll(ReturnBuffer);
579
580 return ReturnBuffer;
581 }
582
583 struct drawaboveentry{
584 drawabove da;
585 int iPriority;
586 const char* desc; //to help on development
587 };
588 std::vector<drawaboveentry> vDrawabove;
DrawAboveAll(bitmap * bmpBuffer)589 void graphics::DrawAboveAll(bitmap* bmpBuffer)
590 {
591 for(int i=0;i<vDrawabove.size();i++)
592 vDrawabove[i].da(bmpBuffer);
593 }
594
595 /**
596 * Priority to reorganize (recreate) the vector ordering.
597 * Priority can be any value.
598 * Lowest priority will be drawn below, and higher ones above.
599 *
600 * The suggestion is to skip by 100 every new layer, so if there are too many they can
601 * fit in between w/o having to change everywhere ex.:
602 * 900 : some new layer below Map
603 * 1000 : Map
604 * 1100 : Some other new layer above Map
605 * many days later just create another
606 * 950 : another new layer will be drawn above 900 and below the Map
607 * and you dont have to change everywhere.
608 */
AddDrawAboveAll(drawabove da,int iPriority,const char * desc)609 void graphics::AddDrawAboveAll(drawabove da, int iPriority, const char* desc)
610 {
611 drawaboveentry dae;
612 dae.da=da;
613 dae.iPriority=iPriority;
614 dae.desc=desc;
615
616 std::vector<drawaboveentry> vDrawaboveTmp(vDrawabove);
617 vDrawaboveTmp.push_back(dae);
618
619 vDrawabove.clear();
620
621 while(vDrawaboveTmp.size()>0){
622 drawaboveentry daeLowestPriority;
623 daeLowestPriority.iPriority=1000000000; //TODO should be max int
624 int iIndexLP=-1;
625 for(int i=0;i<vDrawaboveTmp.size();i++){
626 if(vDrawaboveTmp[i].iPriority < daeLowestPriority.iPriority){
627 daeLowestPriority=vDrawaboveTmp[i];
628 iIndexLP=i;
629 }
630 }
631
632 vDrawabove.push_back(daeLowestPriority);
633
634 vDrawaboveTmp.erase(vDrawaboveTmp.begin()+iIndexLP);
635 }
636
637 char* c = std::getenv("IVAN_LISTDRAWABOVE"); //to help on development, so all priorities can be adjusted easily
638 bool bCOut = c!=NULL && strcmp(c,"true")==0;
639 festring fsDrawAbovePriority;
640 for(int i=0;i<vDrawabove.size();i++){
641 fsDrawAbovePriority << "DrawAbovePriority:" << vDrawabove[i].iPriority << ":" << vDrawabove[i].desc;
642 if(bCOut)
643 std::cout << fsDrawAbovePriority.CStr() << std::endl;
644 DBG1(fsDrawAbovePriority.CStr());
645 fsDrawAbovePriority.Empty();
646 }
647
648 }
649
DrawRectangleOutlineAround(bitmap * bmpAt,v2 v2TopLeft,v2 v2Border,col16 color,bool bWide)650 void graphics::DrawRectangleOutlineAround(bitmap* bmpAt, v2 v2TopLeft, v2 v2Border, col16 color, bool bWide){
651 v2 v2BottomRight = v2TopLeft+v2Border;
652 if(bWide){ //is 3 thickness
653 v2TopLeft -= v2(2,2);
654 v2BottomRight += v2(1,1);
655 }else{
656 v2TopLeft -= v2(1,1);
657 }
658
659 // fixer
660 int iMargin=bWide?2:1; //wide is 3, middle is at 2
661 if(v2TopLeft.X<iMargin)v2TopLeft.X=iMargin-1;
662 if(v2TopLeft.Y<iMargin)v2TopLeft.Y=iMargin-1;
663 int iMMax;
664 iMMax=bmpAt->GetSize().X-iMargin;if(v2BottomRight.X>iMMax)v2BottomRight.X=iMMax;
665 iMMax=bmpAt->GetSize().Y-iMargin;if(v2BottomRight.Y>iMMax)v2BottomRight.Y=iMMax;
666
667 bmpAt->DrawRectangle(v2TopLeft, v2BottomRight, color, bWide);
668 }
669
GetTotSRegions()670 int graphics::GetTotSRegions(){
671 return vStretchRegion.size();
672 }
673
BlitDBToScreen()674 void graphics::BlitDBToScreen()
675 {
676 #if SDL_MAJOR_VERSION == 1
677 if(SDL_MUSTLOCK(Screen) && SDL_LockSurface(Screen) < 0)
678 ABORT("Can't lock screen");
679
680 packcol16* SrcPtr = DoubleBuffer->GetImage()[0];
681
682 packcol16* DestPtr = static_cast<packcol16*>(Screen->pixels);
683 ulong ScreenYMove = (Screen->pitch >> 1);
684 ulong LineSize = Res.X << 1;
685
686 for(int y = 0; y < Res.Y; ++y, SrcPtr += Res.X, DestPtr += ScreenYMove)
687 memcpy(DestPtr, SrcPtr, LineSize);
688
689 if(SDL_MUSTLOCK(Screen))
690 SDL_UnlockSurface(Screen);
691
692 SDL_UpdateRect(Screen, 0, 0, Res.X, Res.Y);
693 #else
694 packcol16* SrcPtr = PrepareBuffer()->GetImage()[0];
695 void* DestPtr;
696 int Pitch;
697
698 if (SDL_LockTexture(Texture, NULL, &DestPtr, &Pitch) == 0)
699 {
700 memcpy(DestPtr, SrcPtr, Res.Y * Pitch);
701 SDL_UnlockTexture(Texture);
702 }
703 else
704 {
705 // Try to use the slower SDL_UpdateTexture() as a fallback if SDL_LockTexture() fails.
706 SDL_UpdateTexture(Texture, NULL, SrcPtr, Res.X * sizeof(packcol16));
707 }
708
709 SDL_RenderClear(Renderer);
710 SDL_RenderCopy(Renderer, Texture, NULL, NULL);
711 SDL_RenderPresent(Renderer);
712 #endif
713 }
714
715 #endif
716
SetScale(int NewScale)717 void graphics::SetScale(int NewScale)
718 {
719 Scale = NewScale;
720 #if SDL_MAJOR_VERSION == 1
721 #warning Graphics scaling not implemented for SDL v1
722 #else
723 // Scale the window, maintaining its center position.
724 v2 WindowPos, OldSize, NewSize = Res * NewScale;
725 SDL_GetWindowPosition(Window, &WindowPos.X, &WindowPos.Y);
726 SDL_GetWindowSize(Window, &OldSize.X, &OldSize.Y);
727 WindowPos += (OldSize - NewSize) / 2;
728 SDL_SetWindowPosition(Window, WindowPos.X, WindowPos.Y);
729 SDL_SetWindowSize(Window, Res.X * NewScale, Res.Y * NewScale);
730 SDL_RenderSetScale(Renderer, NewScale, NewScale);
731 #endif
732 }
733
SwitchMode()734 void graphics::SwitchMode()
735 {
736 #if SDL_MAJOR_VERSION == 1
737 ulong Flags;
738
739 if(Screen->flags & SDL_FULLSCREEN)
740 {
741 SDL_ShowCursor(SDL_ENABLE);
742 Flags = SDL_SWSURFACE;
743 }
744 else
745 {
746 SDL_ShowCursor(SDL_DISABLE);
747 Flags = SDL_SWSURFACE|SDL_FULLSCREEN;
748 }
749
750 Screen = SDL_SetVideoMode(Res.X, Res.Y, ColorDepth, Flags);
751
752 if(!Screen)
753 ABORT("Couldn't toggle fullscreen mode.");
754
755 BlitDBToScreen();
756 #else
757 ulong Flags = SDL_GetWindowFlags(Window);
758 if(Flags & SDL_WINDOW_FULLSCREEN_DESKTOP)
759 {
760 SDL_ShowCursor(SDL_ENABLE);
761 SDL_SetWindowFullscreen(Window, 0);
762 SetScale(Scale);
763 }
764 else
765 {
766 if(!bAllowMouseInFullScreen)
767 SDL_ShowCursor(SDL_DISABLE);
768 SDL_SetWindowFullscreen(Window, SDL_WINDOW_FULLSCREEN_DESKTOP);
769 }
770 BlitDBToScreen();
771 #endif
772
773 if(SwitchModeHandler)
774 SwitchModeHandler();
775 }
776
777 #endif
778
LoadDefaultFont(cfestring & FileName)779 void graphics::LoadDefaultFont(cfestring& FileName)
780 {
781 DefaultFont = new rawbitmap(FileName);
782 }
783
784 #ifdef __DJGPP__
785
SetMode(cchar *,cchar *,v2 NewRes,truth)786 void graphics::SetMode(cchar*, cchar*, v2 NewRes, truth)
787 {
788 ulong Mode;
789
790 for(Mode = 0; Mode < 0x10000; ++Mode)
791 {
792 ModeInfo.Retrieve(Mode);
793
794 if(ModeInfo.Attribs1 & 0x01
795 && ModeInfo.Attribs1 & 0xFF
796 && ModeInfo.Width == NewRes.X
797 && ModeInfo.Height == NewRes.Y
798 && ModeInfo.BitsPerPixel == 16)
799 break;
800 }
801
802 if(Mode == 0x10000)
803 ABORT("Resolution %dx%d not supported!", NewRes.X, NewRes.Y);
804
805 __dpmi_regs Regs;
806 Regs.x.ax = 0x4F02;
807 Regs.x.bx = Mode | 0x4000;
808 __dpmi_int(0x10, &Regs);
809 Res.X = ModeInfo.Width;
810 Res.Y = ModeInfo.Height;
811 BufferSize = Res.Y * ModeInfo.BytesPerLine;
812 delete DoubleBuffer;
813 DoubleBuffer = new bitmap(Res);
814 __dpmi_meminfo MemoryInfo;
815 MemoryInfo.size = BufferSize;
816 MemoryInfo.address = ModeInfo.PhysicalLFBAddress;
817 __dpmi_physical_address_mapping(&MemoryInfo);
818 __dpmi_lock_linear_region(&MemoryInfo);
819 ScreenSelector = __dpmi_allocate_ldt_descriptors(1);
820 __dpmi_set_segment_base_address(ScreenSelector, MemoryInfo.address);
821 __dpmi_set_segment_limit(ScreenSelector, BufferSize - 1);
822 }
823
BlitDBToScreen()824 void graphics::BlitDBToScreen()
825 {
826 movedata(_my_ds(), ulong(DoubleBuffer->GetImage()[0]),
827 ScreenSelector, 0, BufferSize);
828 }
829
Retrieve()830 void graphics::vesainfo::Retrieve()
831 {
832 Signature = 0x32454256;
833 dosmemput(this, sizeof(vesainfo), __tb);
834 __dpmi_regs Regs;
835 Regs.x.ax = 0x4F00;
836 Regs.x.di = __tb & 0x000F;
837 Regs.x.es = (__tb >> 4) & 0xFFFF;
838 __dpmi_int(0x10, &Regs);
839 dosmemget(__tb, sizeof(vesainfo), this);
840 }
841
Retrieve(ushort Mode)842 void graphics::modeinfo::Retrieve(ushort Mode)
843 {
844 __dpmi_regs Regs;
845 Regs.x.ax = 0x4F01;
846 Regs.x.cx = Mode;
847 Regs.x.di = __tb & 0x000F;
848 Regs.x.es = (__tb >> 4) & 0xFFFF;
849 dosmemput(this, sizeof(modeinfo), __tb);
850 __dpmi_int(0x10, &Regs);
851 dosmemget(__tb, sizeof(modeinfo), this);
852 }
853
854 #endif
855