1 //============================================================================
2 //
3 //   SSSS    tt          lll  lll
4 //  SS  SS   tt           ll   ll
5 //  SS     tttttt  eeee   ll   ll   aaaa
6 //   SSSS    tt   ee  ee  ll   ll      aa
7 //      SS   tt   eeeeee  ll   ll   aaaaa  --  "An Atari 2600 VCS Emulator"
8 //  SS  SS   tt   ee      ll   ll  aa  aa
9 //   SSSS     ttt  eeeee llll llll  aaaaa
10 //
11 // Copyright (c) 1995-2021 by Bradford W. Mott, Stephen Anthony
12 // and the Stella Team
13 //
14 // See the file "License.txt" for information on usage and redistribution of
15 // this file, and for a DISCLAIMER OF ALL WARRANTIES.
16 //============================================================================
17 
18 #include "FBBackendSDL2.hxx"
19 #include "ThreadDebugging.hxx"
20 #include "QisBlitter.hxx"
21 
22 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
QisBlitter(FBBackendSDL2 & fb)23 QisBlitter::QisBlitter(FBBackendSDL2& fb)
24   : myFB{fb}
25 {
26 }
27 
28 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
~QisBlitter()29 QisBlitter::~QisBlitter()
30 {
31   free();
32 }
33 
34 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
isSupported(FBBackendSDL2 & fb)35 bool QisBlitter::isSupported(FBBackendSDL2& fb)
36 {
37   if (!fb.isInitialized()) throw runtime_error("framebuffer not initialized");
38 
39   return fb.hasRenderTargetSupport();
40 }
41 
42 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
reinitialize(SDL_Rect srcRect,SDL_Rect destRect,FBSurface::Attributes attributes,SDL_Surface * staticData)43 void QisBlitter::reinitialize(
44   SDL_Rect srcRect,
45   SDL_Rect destRect,
46   FBSurface::Attributes attributes,
47   SDL_Surface* staticData
48 )
49 {
50   myRecreateTextures = myRecreateTextures || !(
51     mySrcRect.w == srcRect.w &&
52     mySrcRect.h == srcRect.h &&
53     myDstRect.w == myFB.scaleX(destRect.w) &&
54     myDstRect.h == myFB.scaleY(destRect.h) &&
55     attributes == myAttributes &&
56     myStaticData == staticData
57    );
58 
59    myStaticData = staticData;
60    mySrcRect = srcRect;
61    myAttributes = attributes;
62 
63    myDstRect.x = myFB.scaleX(destRect.x);
64    myDstRect.y = myFB.scaleY(destRect.y);
65    myDstRect.w = myFB.scaleX(destRect.w);
66    myDstRect.h = myFB.scaleY(destRect.h);
67 }
68 
69 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
free()70 void QisBlitter::free()
71 {
72   if (!myTexturesAreAllocated) {
73     return;
74   }
75 
76   ASSERT_MAIN_THREAD;
77 
78   std::array<SDL_Texture*, 3> textures = {
79     mySrcTexture, myIntermediateTexture, mySecondaryIntermedateTexture
80   };
81   for (SDL_Texture* texture: textures) {
82     if (!texture) continue;
83 
84     SDL_DestroyTexture(texture);
85   }
86 
87   myTexturesAreAllocated = false;
88 }
89 
90 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
blit(SDL_Surface & surface)91 void QisBlitter::blit(SDL_Surface& surface)
92 {
93   ASSERT_MAIN_THREAD;
94 
95   recreateTexturesIfNecessary();
96 
97   SDL_Texture* intermediateTexture = myIntermediateTexture;
98 
99   if(myStaticData == nullptr) {
100     SDL_UpdateTexture(mySrcTexture, &mySrcRect, surface.pixels, surface.pitch);
101 
102     blitToIntermediate();
103 
104     myIntermediateTexture = mySecondaryIntermedateTexture;
105     mySecondaryIntermedateTexture = intermediateTexture;
106 
107     SDL_Texture* temporary = mySrcTexture;
108     mySrcTexture = mySecondarySrcTexture;
109     mySecondarySrcTexture = temporary;
110   }
111 
112   SDL_RenderCopy(myFB.renderer(), intermediateTexture, &myIntermediateRect, &myDstRect);
113 }
114 
115 
116 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
blitToIntermediate()117 void QisBlitter::blitToIntermediate()
118 {
119   ASSERT_MAIN_THREAD;
120 
121   SDL_Rect r = mySrcRect;
122   r.x = r.y = 0;
123 
124   SDL_SetRenderTarget(myFB.renderer(), myIntermediateTexture);
125 
126   SDL_SetRenderDrawColor(myFB.renderer(), 0, 0, 0, 255);
127   SDL_RenderClear(myFB.renderer());
128 
129   SDL_RenderCopy(myFB.renderer(), mySrcTexture, &r, &myIntermediateRect);
130 
131   SDL_SetRenderTarget(myFB.renderer(), nullptr);
132 }
133 
134 
135 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
recreateTexturesIfNecessary()136 void QisBlitter::recreateTexturesIfNecessary()
137 {
138   if (myTexturesAreAllocated && !myRecreateTextures) {
139     return;
140   }
141 
142   ASSERT_MAIN_THREAD;
143 
144   if (myTexturesAreAllocated) {
145     free();
146   }
147 
148   SDL_TextureAccess texAccess = myStaticData == nullptr ? SDL_TEXTUREACCESS_STREAMING : SDL_TEXTUREACCESS_STATIC;
149 
150   myIntermediateRect.w = (myDstRect.w / mySrcRect.w) * mySrcRect.w;
151   myIntermediateRect.h = (myDstRect.h / mySrcRect.h) * mySrcRect.h;
152   myIntermediateRect.x = 0;
153   myIntermediateRect.y = 0;
154 
155   SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0");
156 
157   mySrcTexture = SDL_CreateTexture(myFB.renderer(), myFB.pixelFormat().format,
158     texAccess, mySrcRect.w, mySrcRect.h);
159 
160   if (myStaticData == nullptr) {
161     mySecondarySrcTexture = SDL_CreateTexture(myFB.renderer(), myFB.pixelFormat().format,
162       texAccess, mySrcRect.w, mySrcRect.h);
163   } else {
164     mySecondarySrcTexture = nullptr;
165   }
166 
167   SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1");
168 
169   myIntermediateTexture = SDL_CreateTexture(myFB.renderer(), myFB.pixelFormat().format,
170       SDL_TEXTUREACCESS_TARGET, myIntermediateRect.w, myIntermediateRect.h);
171 
172   if (myStaticData == nullptr) {
173     mySecondaryIntermedateTexture = SDL_CreateTexture(myFB.renderer(), myFB.pixelFormat().format,
174         SDL_TEXTUREACCESS_TARGET, myIntermediateRect.w, myIntermediateRect.h);
175   } else {
176     mySecondaryIntermedateTexture = nullptr;
177     SDL_UpdateTexture(mySrcTexture, nullptr, myStaticData->pixels, myStaticData->pitch);
178 
179     blitToIntermediate();
180   }
181 
182   if (myAttributes.blending) {
183     uInt8 blendAlpha = uInt8(myAttributes.blendalpha * 2.55);
184 
185     std::array<SDL_Texture*, 3> textures = {
186       mySrcTexture, myIntermediateTexture, mySecondaryIntermedateTexture
187     };
188     for (SDL_Texture* texture: textures) {
189       if (!texture) continue;
190 
191       SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
192       SDL_SetTextureAlphaMod(texture, blendAlpha);
193     }
194   }
195 
196   myRecreateTextures = false;
197   myTexturesAreAllocated = true;
198 }
199