Mana
Loading...
Searching...
No Matches
sdlgraphics.cpp
Go to the documentation of this file.
1/*
2 * The Mana Client
3 * Copyright (C) 2004-2009 The Mana World Development Team
4 * Copyright (C) 2009-2012 The Mana Developers
5 *
6 * This file is part of The Mana Client.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22#include "sdlgraphics.h"
23
24#include "log.h"
25#include "resources/image.h"
26#include "utils/stringutils.h"
27#include "video.h"
28
29#include <guichan/exception.hpp>
30
31#include <cmath>
32
33std::unique_ptr<Graphics> SDLGraphics::create(SDL_Window *window, const VideoSettings &settings)
34{
35 int rendererFlags = 0;
36 if (settings.vsync)
37 rendererFlags |= SDL_RENDERER_PRESENTVSYNC;
38
39 SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, rendererFlags);
40 if (!renderer)
41 {
42 Log::critical(strprintf("Failed to create renderer: %s",
43 SDL_GetError()));
44 return {};
45 }
46
47 return std::make_unique<SDLGraphics>(window, renderer);
48}
49
50SDLGraphics::SDLGraphics(SDL_Window *window, SDL_Renderer *renderer)
51 : mWindow(window)
52 , mRenderer(renderer)
53{
55
56 SDL_GetRendererOutputSize(mRenderer, &mWidth, &mHeight);
57
58 SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
59
60 if (const char *driver = SDL_GetCurrentVideoDriver())
61 Log::info("Using video driver: %s", driver);
62 else
63 Log::info("Using video driver: not initialized");
64
65 SDL_RendererInfo info;
66
67 if (SDL_GetRendererInfo(renderer, &info) == 0) {
68 Log::info("Using renderer: %s", info.name);
69
70 Log::info("The renderer is a software fallback: %s",
71 (info.flags & SDL_RENDERER_SOFTWARE) ? "yes" : "no");
72 Log::info("The renderer is hardware accelerated: %s",
73 (info.flags & SDL_RENDERER_ACCELERATED) ? "yes" : "no");
74 Log::info("Vsync: %s",
75 (info.flags & SDL_RENDERER_PRESENTVSYNC) ? "on" : "off");
76 Log::info("Renderer supports rendering to texture: %s",
77 (info.flags & SDL_RENDERER_TARGETTEXTURE) ? "yes" : "no");
78 Log::info("Max texture size: %dx%d",
79 info.max_texture_width, info.max_texture_height);
80 }
81}
82
84{
85 SDL_DestroyRenderer(mRenderer);
86}
87
88void SDLGraphics::setVSync(bool sync)
89{
90#if SDL_VERSION_ATLEAST(2, 0, 18)
91 SDL_RenderSetVSync(mRenderer, sync ? SDL_TRUE : SDL_FALSE);
92#endif
93}
94
95void SDLGraphics::updateSize(int windowWidth, int windowHeight, float scale)
96{
97 SDL_GetRendererOutputSize(mRenderer, &mWidth, &mHeight);
98
99 float displayScaleX = windowWidth > 0 ? static_cast<float>(mWidth) / windowWidth : 1.0f;
100 float displayScaleY = windowHeight > 0 ? static_cast<float>(mHeight) / windowHeight : 1.0f;
101
102 float scaleX = scale * displayScaleX;
103 float scaleY = scale * displayScaleY;
104
105 mWidth = std::ceil(mWidth / scaleX);
106 mHeight = std::ceil(mHeight / scaleY);
107 mScale = scaleX;
108
109 SDL_RenderSetScale(mRenderer, scaleX, scaleY);
110}
111
113 int srcX, int srcY,
114 int dstX, int dstY,
115 int width, int height,
116 int desiredWidth, int desiredHeight,
117 bool useColor)
118{
119 // Check that preconditions for blitting are met.
120 if (!image || !image->mTexture)
121 return false;
122
123 dstX += mClipStack.top().xOffset;
124 dstY += mClipStack.top().yOffset;
125
126 srcX += image->mBounds.x;
127 srcY += image->mBounds.y;
128
129 SDL_Rect dstRect;
130 SDL_Rect srcRect;
131 dstRect.x = dstX; dstRect.y = dstY;
132 srcRect.x = srcX; srcRect.y = srcY;
133 srcRect.w = width;
134 srcRect.h = height;
135 dstRect.w = desiredWidth;
136 dstRect.h = desiredHeight;
137
138 setColorAlphaMod(image, useColor);
139 return SDL_RenderCopy(mRenderer, image->mTexture, &srcRect, &dstRect) != 0;
140}
141
142#if SDL_VERSION_ATLEAST(2, 0, 10)
143bool SDLGraphics::drawRescaledImageF(const Image *image,
144 int srcX, int srcY,
145 float dstX, float dstY,
146 int width, int height,
147 float desiredWidth, float desiredHeight,
148 bool useColor)
149{
150 // Check that preconditions for blitting are met.
151 if (!image || !image->mTexture)
152 return false;
153
154 dstX += mClipStack.top().xOffset;
155 dstY += mClipStack.top().yOffset;
156
157 srcX += image->mBounds.x;
158 srcY += image->mBounds.y;
159
160 SDL_FRect dstRect;
161 SDL_Rect srcRect;
162 dstRect.x = dstX; dstRect.y = dstY;
163 srcRect.x = srcX; srcRect.y = srcY;
164 srcRect.w = width;
165 srcRect.h = height;
166 dstRect.w = desiredWidth;
167 dstRect.h = desiredHeight;
168
169 setColorAlphaMod(image, useColor);
170 return SDL_RenderCopyF(mRenderer, image->mTexture, &srcRect, &dstRect) == 0;
171}
172#endif
173
175 int srcX, int srcY,
176 int srcW, int srcH,
177 int dstX, int dstY,
178 int dstW, int dstH,
179 int scaledWidth,
180 int scaledHeight)
181{
182 // Check that preconditions for blitting are met.
183 if (!image || !image->mTexture)
184 return;
185
186 if (scaledHeight <= 0 || scaledWidth <= 0)
187 return;
188
189 setColorAlphaMod(image, false);
190
191 SDL_Rect srcRect;
192 srcRect.x = image->mBounds.x + srcX;
193 srcRect.y = image->mBounds.y + srcY;
194
195 for (int py = 0; py < dstH; py += scaledHeight) // Y position on pattern plane
196 {
197 SDL_Rect dstRect;
198 dstRect.h = (py + scaledHeight >= dstH) ? dstH - py : scaledHeight;
199 dstRect.y = dstY + py + mClipStack.top().yOffset;
200
201 for (int px = 0; px < dstW; px += scaledWidth) // X position on pattern plane
202 {
203 dstRect.x = dstX + px + mClipStack.top().xOffset;
204 dstRect.w = (px + scaledWidth >= dstW) ? dstW - px : scaledWidth;
205
206 srcRect.w = srcW * dstRect.w / scaledWidth;
207 srcRect.h = srcH * dstRect.h / scaledHeight;
208
209 if (SDL_RenderCopy(mRenderer, image->mTexture, &srcRect, &dstRect))
210 return;
211 }
212 }
213}
214
216{
217 SDL_RenderPresent(mRenderer);
218}
219
220void SDLGraphics::windowToLogical(int windowX, int windowY,
221 float &logicalX, float &logicalY) const
222{
223#if SDL_VERSION_ATLEAST(2, 0, 18)
224 SDL_RenderWindowToLogical(mRenderer, windowX, windowY, &logicalX, &logicalY);
225#else
226 float scaleX;
227 float scaleY;
228 SDL_RenderGetScale(mRenderer, &scaleX, &scaleY);
229 logicalX = windowX / scaleX;
230 logicalY = windowY / scaleY;
231#endif
232}
233
235{
236#if SDL_BYTEORDER == SDL_BIG_ENDIAN
237 int rmask = 0xff000000;
238 int gmask = 0x00ff0000;
239 int bmask = 0x0000ff00;
240#else
241 int rmask = 0x000000ff;
242 int gmask = 0x0000ff00;
243 int bmask = 0x00ff0000;
244#endif
245 int amask = 0x00000000;
246
247 int width, height;
248 if (SDL_GetRendererOutputSize(mRenderer, &width, &height) != 0)
249 return nullptr;
250
251 SDL_Surface *screenshot = SDL_CreateRGBSurface(0, width, height, 24,
252 rmask, gmask, bmask, amask);
253
254 if (SDL_RenderReadPixels(mRenderer, nullptr,
255 screenshot->format->format,
256 screenshot->pixels,
257 screenshot->pitch) != 0)
258 {
259 SDL_FreeSurface(screenshot);
260 screenshot = nullptr;
261 }
262
263 return screenshot;
264}
265
267{
268 if (mClipRects.empty())
269 {
270 SDL_RenderSetClipRect(mRenderer, nullptr);
271 return;
272 }
273
274 const gcn::Rectangle &clipRect = mClipRects.top();
275 const SDL_Rect rect = {
276 clipRect.x,
277 clipRect.y,
278 clipRect.width,
279 clipRect.height
280 };
281
282 SDL_RenderSetClipRect(mRenderer, &rect);
283}
284
285void SDLGraphics::drawPoint(int x, int y)
286{
287 if (mClipStack.empty())
288 {
289 throw GCN_EXCEPTION("Clip stack is empty, perhaps you called a draw funtion outside of _beginDraw() and _endDraw()?");
290 }
291
292 const gcn::ClipRectangle &top = mClipStack.top();
293
294 x += top.xOffset;
295 y += top.yOffset;
296
297 if (!top.isPointInRect(x, y))
298 return;
299
300 SDL_SetRenderDrawColor(mRenderer,
301 (Uint8)(mColor.r),
302 (Uint8)(mColor.g),
303 (Uint8)(mColor.b),
304 (Uint8)(mColor.a));
305 SDL_RenderDrawPoint(mRenderer, x, y);
306}
307
308void SDLGraphics::drawLine(int x1, int y1, int x2, int y2)
309{
310 if (mClipStack.empty())
311 {
312 throw GCN_EXCEPTION("Clip stack is empty, perhaps you called a draw funtion outside of _beginDraw() and _endDraw()?");
313 }
314
315 const gcn::ClipRectangle &top = mClipStack.top();
316
317 x1 += top.xOffset;
318 y1 += top.yOffset;
319 x2 += top.xOffset;
320 y2 += top.yOffset;
321
322 SDL_SetRenderDrawColor(mRenderer,
323 (Uint8)(mColor.r),
324 (Uint8)(mColor.g),
325 (Uint8)(mColor.b),
326 (Uint8)(mColor.a));
327 SDL_RenderDrawLine(mRenderer, x1, y1, x2, y2);
328}
329
330void SDLGraphics::drawRectangle(const gcn::Rectangle &rectangle)
331{
332 if (mClipStack.empty())
333 {
334 throw GCN_EXCEPTION("Clip stack is empty, perhaps you called a draw funtion outside of _beginDraw() and _endDraw()?");
335 }
336
337 const gcn::ClipRectangle &top = mClipStack.top();
338
339 SDL_Rect rect;
340 rect.x = rectangle.x + top.xOffset;
341 rect.y = rectangle.y + top.yOffset;
342 rect.w = rectangle.width;
343 rect.h = rectangle.height;
344
345 SDL_SetRenderDrawColor(mRenderer,
346 (Uint8)(mColor.r),
347 (Uint8)(mColor.g),
348 (Uint8)(mColor.b),
349 (Uint8)(mColor.a));
350 SDL_RenderDrawRect(mRenderer, &rect);
351}
352
353void SDLGraphics::fillRectangle(const gcn::Rectangle &rectangle)
354{
355 if (mClipStack.empty())
356 {
357 throw GCN_EXCEPTION("Clip stack is empty, perhaps you called a draw funtion outside of _beginDraw() and _endDraw()?");
358 }
359
360 const gcn::ClipRectangle &top = mClipStack.top();
361
362 gcn::Rectangle area = rectangle;
363 area.x += top.xOffset;
364 area.y += top.yOffset;
365
366 if(!area.isIntersecting(top))
367 {
368 return;
369 }
370
371 SDL_Rect rect;
372 rect.x = area.x;
373 rect.y = area.y;
374 rect.w = area.width;
375 rect.h = area.height;
376
377 SDL_SetRenderDrawColor(mRenderer,
378 (Uint8)(mColor.r),
379 (Uint8)(mColor.g),
380 (Uint8)(mColor.b),
381 (Uint8)(mColor.a));
382 SDL_RenderFillRect(mRenderer, &rect);
383}
384
385void SDLGraphics::setColorAlphaMod(const Image *image, bool useColor) const
386{
387 SDL_Color color = { 255, 255, 255, 255 };
388 if (useColor)
389 {
390 color.r = static_cast<uint8_t>(mColor.r);
391 color.g = static_cast<uint8_t>(mColor.g);
392 color.b = static_cast<uint8_t>(mColor.b);
393 color.a = static_cast<uint8_t>(mColor.a);
394 }
395 color.a *= image->getAlpha();
396
397 SDL_Texture *texture = image->mTexture;
398 SDL_SetTextureColorMod(texture, color.r, color.g, color.b);
399 SDL_SetTextureAlphaMod(texture, color.a);
400}
int mWidth
Definition graphics.h:271
std::stack< gcn::Rectangle > mClipRects
Definition graphics.h:277
gcn::Color mColor
Definition graphics.h:274
int mHeight
Definition graphics.h:272
float mScale
Definition graphics.h:273
virtual bool drawRescaledImageF(const Image *image, int srcX, int srcY, float dstX, float dstY, int width, int height, float desiredWidth, float desiredHeight, bool useColor=false)
Draws a rescaled version of the image.
Definition graphics.cpp:60
Defines a class for loading and storing images.
Definition image.h:45
SDL_Texture * mTexture
Definition image.h:166
SDL_Rect mBounds
Definition image.h:153
float getAlpha() const
Returns the alpha value of this image.
Definition image.h:106
static void setRenderer(SDL_Renderer *renderer)
Definition image.cpp:193
static std::unique_ptr< Graphics > create(SDL_Window *window, const VideoSettings &settings)
SDL_Surface * getScreenshot() override
Takes a screenshot and returns it as SDL surface.
SDLGraphics(SDL_Window *window, SDL_Renderer *renderer)
void updateClipRect() override
void drawRescaledImagePattern(const Image *image, int srcX, int srcY, int srcW, int srcH, int dstX, int dstY, int dstW, int dstH, int scaledWidth, int scaledHeight) override
Draw a pattern based on a rescaled version of the given image.
void drawPoint(int x, int y) override
void updateScreen() override
Updates the screen.
void setVSync(bool sync) override
Sets whether vertical refresh syncing is enabled.
SDL_Renderer * mRenderer
Definition sdlgraphics.h:89
~SDLGraphics() override
void updateSize(int windowWidth, int windowHeight, float scale) override
Called when the window size or scale has changed.
bool drawRescaledImage(const Image *image, int srcX, int srcY, int dstX, int dstY, int width, int height, int desiredWidth, int desiredHeight, bool useColor) override
Draws a rescaled version of the image.
void setColorAlphaMod(const Image *image, bool useColor) const
void windowToLogical(int windowX, int windowY, float &logicalX, float &logicalY) const override
Converts a window coordinate to a logical coordinate.
void drawRectangle(const gcn::Rectangle &rectangle) override
void drawLine(int x1, int y1, int x2, int y2) override
void fillRectangle(const gcn::Rectangle &rectangle) override
void info(const char *log_text,...) LOG_PRINTF_ATTR
unsigned char uint8_t
Definition sha256.cpp:81
std::string strprintf(char const *format,...)
A safe version of sprintf that returns a std::string of the result.
bool vsync
Definition video.h:52