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