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