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-2019 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 "FBSurfaceSDL2.hxx" |
19 | |
20 | #include "ThreadDebugging.hxx" |
21 | |
22 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
23 | FBSurfaceSDL2::FBSurfaceSDL2(FrameBufferSDL2& buffer, |
24 | uInt32 width, uInt32 height, const uInt32* data) |
25 | : myFB(buffer), |
26 | mySurface(nullptr), |
27 | myTexture(nullptr), |
28 | mySecondaryTexture(nullptr), |
29 | mySurfaceIsDirty(true), |
30 | myIsVisible(true), |
31 | myTexAccess(SDL_TEXTUREACCESS_STREAMING), |
32 | myInterpolate(false), |
33 | myBlendEnabled(false), |
34 | myBlendAlpha(255) |
35 | { |
36 | createSurface(width, height, data); |
37 | } |
38 | |
39 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
40 | FBSurfaceSDL2::~FBSurfaceSDL2() |
41 | { |
42 | ASSERT_MAIN_THREAD; |
43 | |
44 | if(mySurface) |
45 | { |
46 | SDL_FreeSurface(mySurface); |
47 | mySurface = nullptr; |
48 | } |
49 | |
50 | free(); |
51 | } |
52 | |
53 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
54 | void FBSurfaceSDL2::fillRect(uInt32 x, uInt32 y, uInt32 w, uInt32 h, ColorId color) |
55 | { |
56 | ASSERT_MAIN_THREAD; |
57 | |
58 | // Fill the rectangle |
59 | SDL_Rect tmp; |
60 | tmp.x = x; |
61 | tmp.y = y; |
62 | tmp.w = w; |
63 | tmp.h = h; |
64 | SDL_FillRect(mySurface, &tmp, myPalette[color]); |
65 | } |
66 | |
67 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
68 | uInt32 FBSurfaceSDL2::width() const |
69 | { |
70 | return mySurface->w; |
71 | } |
72 | |
73 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
74 | uInt32 FBSurfaceSDL2::height() const |
75 | { |
76 | return mySurface->h; |
77 | } |
78 | |
79 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
80 | const Common::Rect& FBSurfaceSDL2::srcRect() const |
81 | { |
82 | return mySrcGUIR; |
83 | } |
84 | |
85 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
86 | const Common::Rect& FBSurfaceSDL2::dstRect() const |
87 | { |
88 | return myDstGUIR; |
89 | } |
90 | |
91 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
92 | void FBSurfaceSDL2::setSrcPos(uInt32 x, uInt32 y) |
93 | { |
94 | mySrcR.x = x; mySrcR.y = y; |
95 | mySrcGUIR.moveTo(x, y); |
96 | } |
97 | |
98 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
99 | void FBSurfaceSDL2::setSrcSize(uInt32 w, uInt32 h) |
100 | { |
101 | mySrcR.w = w; mySrcR.h = h; |
102 | mySrcGUIR.setWidth(w); mySrcGUIR.setHeight(h); |
103 | } |
104 | |
105 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
106 | void FBSurfaceSDL2::setDstPos(uInt32 x, uInt32 y) |
107 | { |
108 | myDstR.x = x; myDstR.y = y; |
109 | myDstGUIR.moveTo(x, y); |
110 | } |
111 | |
112 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
113 | void FBSurfaceSDL2::setDstSize(uInt32 w, uInt32 h) |
114 | { |
115 | myDstR.w = w; myDstR.h = h; |
116 | myDstGUIR.setWidth(w); myDstGUIR.setHeight(h); |
117 | } |
118 | |
119 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
120 | void FBSurfaceSDL2::setVisible(bool visible) |
121 | { |
122 | myIsVisible = visible; |
123 | } |
124 | |
125 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
126 | void FBSurfaceSDL2::translateCoords(Int32& x, Int32& y) const |
127 | { |
128 | x -= myDstR.x; x /= myDstR.w / mySrcR.w; |
129 | y -= myDstR.y; y /= myDstR.h / mySrcR.h; |
130 | } |
131 | |
132 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
133 | bool FBSurfaceSDL2::render() |
134 | { |
135 | ASSERT_MAIN_THREAD; |
136 | |
137 | if(myIsVisible) |
138 | { |
139 | SDL_Texture* texture = myTexture; |
140 | |
141 | if(myTexAccess == SDL_TEXTUREACCESS_STREAMING) { |
142 | SDL_UpdateTexture(myTexture, &mySrcR, mySurface->pixels, mySurface->pitch); |
143 | myTexture = mySecondaryTexture; |
144 | mySecondaryTexture = texture; |
145 | } |
146 | |
147 | SDL_RenderCopy(myFB.myRenderer, texture, &mySrcR, &myDstR); |
148 | |
149 | return true; |
150 | } |
151 | return false; |
152 | } |
153 | |
154 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
155 | void FBSurfaceSDL2::invalidate() |
156 | { |
157 | ASSERT_MAIN_THREAD; |
158 | |
159 | SDL_FillRect(mySurface, nullptr, 0); |
160 | } |
161 | |
162 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
163 | void FBSurfaceSDL2::free() |
164 | { |
165 | ASSERT_MAIN_THREAD; |
166 | |
167 | SDL_Texture* textures[] = {myTexture, mySecondaryTexture}; |
168 | for (SDL_Texture* texture: textures) { |
169 | if (!texture) continue; |
170 | |
171 | SDL_DestroyTexture(myTexture); |
172 | myTexture = nullptr; |
173 | } |
174 | } |
175 | |
176 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
177 | void FBSurfaceSDL2::reload() |
178 | { |
179 | ASSERT_MAIN_THREAD; |
180 | |
181 | // Re-create texture; the underlying SDL_Surface is fine as-is |
182 | SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, myInterpolate ? "1" : "0" ); |
183 | myTexture = SDL_CreateTexture(myFB.myRenderer, myFB.myPixelFormat->format, |
184 | myTexAccess, mySurface->w, mySurface->h); |
185 | |
186 | if (myTexAccess == SDL_TEXTUREACCESS_STREAMING) |
187 | mySecondaryTexture = SDL_CreateTexture(myFB.myRenderer, myFB.myPixelFormat->format, |
188 | myTexAccess, mySurface->w, mySurface->h); |
189 | |
190 | // If the data is static, we only upload it once |
191 | if(myTexAccess == SDL_TEXTUREACCESS_STATIC) |
192 | SDL_UpdateTexture(myTexture, nullptr, myStaticData.get(), myStaticPitch); |
193 | |
194 | SDL_Texture* textures[] = {myTexture, mySecondaryTexture}; |
195 | for (SDL_Texture* texture: textures) { |
196 | if (!texture) continue; |
197 | |
198 | // Blending enabled? |
199 | if(myBlendEnabled) |
200 | { |
201 | SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND); |
202 | SDL_SetTextureAlphaMod(texture, myBlendAlpha); |
203 | } |
204 | } |
205 | } |
206 | |
207 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
208 | void FBSurfaceSDL2::resize(uInt32 width, uInt32 height) |
209 | { |
210 | ASSERT_MAIN_THREAD; |
211 | |
212 | // We will only resize when necessary, and not using static textures |
213 | if((myTexAccess == SDL_TEXTUREACCESS_STATIC) || (mySurface && |
214 | int(width) <= mySurface->w && int(height) <= mySurface->h)) |
215 | return; // don't need to resize at all |
216 | |
217 | if(mySurface) |
218 | SDL_FreeSurface(mySurface); |
219 | free(); |
220 | |
221 | createSurface(width, height, nullptr); |
222 | } |
223 | |
224 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
225 | void FBSurfaceSDL2::createSurface(uInt32 width, uInt32 height, |
226 | const uInt32* data) |
227 | { |
228 | ASSERT_MAIN_THREAD; |
229 | |
230 | // Create a surface in the same format as the parent GL class |
231 | const SDL_PixelFormat* pf = myFB.myPixelFormat; |
232 | |
233 | mySurface = SDL_CreateRGBSurface(0, width, height, |
234 | pf->BitsPerPixel, pf->Rmask, pf->Gmask, pf->Bmask, pf->Amask); |
235 | |
236 | // We start out with the src and dst rectangles containing the same |
237 | // dimensions, indicating no scaling or re-positioning |
238 | setSrcPos(0, 0); |
239 | setDstPos(0, 0); |
240 | setSrcSize(width, height); |
241 | setDstSize(width, height); |
242 | |
243 | //////////////////////////////////////////////////// |
244 | // These *must* be set for the parent class |
245 | myPixels = reinterpret_cast<uInt32*>(mySurface->pixels); |
246 | myPitch = mySurface->pitch / pf->BytesPerPixel; |
247 | //////////////////////////////////////////////////// |
248 | |
249 | if(data) |
250 | { |
251 | myTexAccess = SDL_TEXTUREACCESS_STATIC; |
252 | myStaticPitch = mySurface->w * 4; // we need pitch in 'bytes' |
253 | myStaticData = make_unique<uInt32[]>(mySurface->w * mySurface->h); |
254 | SDL_memcpy(myStaticData.get(), data, mySurface->w * mySurface->h * 4); |
255 | } |
256 | |
257 | applyAttributes(false); |
258 | |
259 | // To generate texture |
260 | reload(); |
261 | } |
262 | |
263 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
264 | void FBSurfaceSDL2::applyAttributes(bool immediate) |
265 | { |
266 | myInterpolate = myAttributes.smoothing; |
267 | myBlendEnabled = myAttributes.blending; |
268 | myBlendAlpha = uInt8(myAttributes.blendalpha * 2.55); |
269 | |
270 | if(immediate) |
271 | { |
272 | // Re-create the texture with the new settings |
273 | free(); |
274 | reload(); |
275 | } |
276 | } |
277 | |