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// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
23FBSurfaceSDL2::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// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
40FBSurfaceSDL2::~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// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
54void 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// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
68uInt32 FBSurfaceSDL2::width() const
69{
70 return mySurface->w;
71}
72
73// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
74uInt32 FBSurfaceSDL2::height() const
75{
76 return mySurface->h;
77}
78
79// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
80const Common::Rect& FBSurfaceSDL2::srcRect() const
81{
82 return mySrcGUIR;
83}
84
85// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
86const Common::Rect& FBSurfaceSDL2::dstRect() const
87{
88 return myDstGUIR;
89}
90
91// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
92void FBSurfaceSDL2::setSrcPos(uInt32 x, uInt32 y)
93{
94 mySrcR.x = x; mySrcR.y = y;
95 mySrcGUIR.moveTo(x, y);
96}
97
98// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
99void FBSurfaceSDL2::setSrcSize(uInt32 w, uInt32 h)
100{
101 mySrcR.w = w; mySrcR.h = h;
102 mySrcGUIR.setWidth(w); mySrcGUIR.setHeight(h);
103}
104
105// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
106void FBSurfaceSDL2::setDstPos(uInt32 x, uInt32 y)
107{
108 myDstR.x = x; myDstR.y = y;
109 myDstGUIR.moveTo(x, y);
110}
111
112// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
113void FBSurfaceSDL2::setDstSize(uInt32 w, uInt32 h)
114{
115 myDstR.w = w; myDstR.h = h;
116 myDstGUIR.setWidth(w); myDstGUIR.setHeight(h);
117}
118
119// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
120void FBSurfaceSDL2::setVisible(bool visible)
121{
122 myIsVisible = visible;
123}
124
125// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
126void 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// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
133bool 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// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
155void FBSurfaceSDL2::invalidate()
156{
157 ASSERT_MAIN_THREAD;
158
159 SDL_FillRect(mySurface, nullptr, 0);
160}
161
162// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
163void 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// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
177void 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// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
208void 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// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
225void 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// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
264void 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