/*
* The ManaPlus Client
* Copyright (C) 2004-2009 The Mana World Development Team
* Copyright (C) 2009-2010 The Mana Developers
* Copyright (C) 2011-2013 The ManaPlus Developers
*
* This file is part of The ManaPlus Client.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "render/graphics.h"
#include "main.h"
#include "configuration.h"
#include "graphicsmanager.h"
#include "logger.h"
#include "resources/image.h"
#include "resources/imagehelper.h"
#include "resources/openglimagehelper.h"
#ifdef USE_OPENGL
#ifdef __APPLE__
#include <OpenGL/OpenGL.h>
#endif
#endif
#include "debug.h"
#ifdef USE_OPENGL
#ifndef GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX
#define GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX 0x9049
#endif
#endif
Graphics *mainGraphics = nullptr;
Graphics::Graphics() :
gcn::Graphics(),
mWidth(0),
mHeight(0),
mWindow(nullptr),
#ifdef USE_SDL2
mRenderer(nullptr),
#ifdef USE_OPENGL
mGLContext(nullptr),
#endif
#endif
mBpp(0),
mAlpha(false),
mFullscreen(false),
mHWAccel(false),
mRedraw(false),
mDoubleBuffer(false),
mRect(),
mSecure(false),
mOpenGL(RENDER_SOFTWARE),
mEnableResize(false),
mNoFrame(false),
mName("Unknown"),
mStartFreeMem(0),
mSync(false),
mColor(),
mColor2()
{
mRect.x = 0;
mRect.y = 0;
mRect.w = 0;
mRect.h = 0;
}
Graphics::~Graphics()
{
_endDraw();
#ifdef USE_SDL2
if (mRenderer)
{
SDL_DestroyRenderer(mRenderer);
mRenderer = nullptr;
}
#ifdef USE_OPENGL
if (mGLContext)
{
SDL_GL_DeleteContext(mGLContext);
mGLContext = nullptr;
}
#endif
#endif
}
void Graphics::setSync(const bool sync)
{
mSync = sync;
}
void Graphics::setMainFlags(const int w, const int h, const int bpp,
const bool fs, const bool hwaccel,
const bool resize, const bool noFrame)
{
logger->log("graphics backend: %s", getName().c_str());
logger->log("Setting video mode %dx%d %s",
w, h, fs ? "fullscreen" : "windowed");
mWidth = w;
mHeight = h;
mBpp = bpp;
mFullscreen = fs;
mHWAccel = hwaccel;
mEnableResize = resize;
mNoFrame = noFrame;
}
int Graphics::getOpenGLFlags() const
{
#ifdef USE_OPENGL
#ifdef USE_SDL2
int displayFlags = SDL_WINDOW_OPENGL;
if (mFullscreen)
displayFlags |= SDL_WINDOW_FULLSCREEN;
#else
int displayFlags = SDL_ANYFORMAT | SDL_OPENGL;
#endif // USE_SDL2
if (mFullscreen)
{
displayFlags |= SDL_FULLSCREEN;
}
else
{
// Resizing currently not supported on Windows, where it would require
// reuploading all textures.
#if !defined(_WIN32)
if (mEnableResize)
displayFlags |= SDL_RESIZABLE;
#endif
}
if (mNoFrame)
displayFlags |= SDL_NOFRAME;
return displayFlags;
#else
return 0;
#endif // USE_OPENGL
}
bool Graphics::setOpenGLMode()
{
#ifdef USE_OPENGL
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
if (!(mWindow = graphicsManager.createWindow(mWidth, mHeight,
mBpp, getOpenGLFlags())))
{
mRect.w = 0;
mRect.h = 0;
return false;
}
#ifdef USE_SDL2
int w1 = 0;
int h1 = 0;
SDL_GetWindowSize(mWindow, &w1, &h1);
mRect.w = w1;
mRect.h = h1;
mGLContext = SDL_GL_CreateContext(mWindow);
#else // USE_SDL2
mRect.w = static_cast<uint16_t>(mWindow->w);
mRect.h = static_cast<uint16_t>(mWindow->h);
#endif // USE_SDL2
#ifdef __APPLE__
if (mSync)
{
const GLint VBL = 1;
CGLSetParameter(CGLGetCurrentContext(), kCGLCPSwapInterval, &VBL);
}
#endif
graphicsManager.setGLVersion();
graphicsManager.logVersion();
// Setup OpenGL
glViewport(0, 0, mWidth, mHeight);
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);
int gotDoubleBuffer = 0;
SDL_GL_GetAttribute(SDL_GL_DOUBLEBUFFER, &gotDoubleBuffer);
logger->log("Using OpenGL %s double buffering.",
(gotDoubleBuffer ? "with" : "without"));
graphicsManager.initOpenGL();
initArrays();
graphicsManager.updateTextureFormat();
updateMemoryInfo();
GLint texSize;
bool rectTex = graphicsManager.supportExtension(
"GL_ARB_texture_rectangle");
if (rectTex && OpenGLImageHelper::getInternalTextureType() == 4
&& getOpenGL() != 3 && config.getBoolValue("rectangulartextures")
&& !graphicsManager.isUseTextureSampler())
{
logger->log1("using GL_ARB_texture_rectangle");
OpenGLImageHelper::mTextureType = GL_TEXTURE_RECTANGLE_ARB;
glEnable(GL_TEXTURE_RECTANGLE_ARB);
glGetIntegerv(GL_MAX_RECTANGLE_TEXTURE_SIZE_ARB, &texSize);
OpenGLImageHelper::mTextureSize = texSize;
logger->log("OpenGL texture size: %d pixels (rectangle textures)",
OpenGLImageHelper::mTextureSize);
}
else
{
OpenGLImageHelper::mTextureType = GL_TEXTURE_2D;
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &texSize);
OpenGLImageHelper::mTextureSize = texSize;
logger->log("OpenGL texture size: %d pixels",
OpenGLImageHelper::mTextureSize);
}
return videoInfo();
#else // USE_OPENGL
return false;
#endif // USE_OPENGL
}
int Graphics::getSoftwareFlags() const
{
#ifdef USE_SDL2
int displayFlags = SDL_WINDOW_SHOWN;
#else
int displayFlags = SDL_ANYFORMAT;
if (mHWAccel)
displayFlags |= SDL_HWSURFACE | SDL_DOUBLEBUF;
else
displayFlags |= SDL_SWSURFACE;
#endif
if (mFullscreen)
displayFlags |= SDL_FULLSCREEN;
else if (mEnableResize)
displayFlags |= SDL_RESIZABLE;
if (mNoFrame)
displayFlags |= SDL_NOFRAME;
return displayFlags;
}
void Graphics::updateMemoryInfo()
{
#ifdef USE_OPENGL
if (mStartFreeMem)
return;
if (graphicsManager.supportExtension("GL_NVX_gpu_memory_info"))
{
glGetIntegerv(GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX,
&mStartFreeMem);
logger->log("free video memory: %d", mStartFreeMem);
}
#endif
}
int Graphics::getMemoryUsage() const
{
#ifdef USE_OPENGL
if (!mStartFreeMem)
return 0;
if (graphicsManager.supportExtension("GL_NVX_gpu_memory_info"))
{
GLint val;
glGetIntegerv(GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX,
&val);
return mStartFreeMem - val;
}
#endif
return 0;
}
#ifdef USE_SDL2
void Graphics::dumpRendererInfo(const char *const str,
const SDL_RendererInfo &info)
{
logger->log(str, info.name);
logger->log("flags:");
if (info.flags & SDL_RENDERER_SOFTWARE)
logger->log(" software");
if (info.flags & SDL_RENDERER_ACCELERATED)
logger->log(" accelerated");
if (info.flags & SDL_RENDERER_PRESENTVSYNC)
logger->log(" vsync");
if (info.flags & SDL_RENDERER_TARGETTEXTURE)
logger->log(" texture target");
}
#endif
bool Graphics::videoInfo()
{
logger->log("SDL video info");
#ifdef USE_SDL2
logger->log("Using video driver: %s", SDL_GetCurrentVideoDriver());
if (mRenderer)
{
SDL_RendererInfo info;
SDL_GetRendererInfo(mRenderer, &info);
dumpRendererInfo("Current SDL renderer name: %s", info);
const int num = SDL_GetNumRenderDrivers();
logger->log("Known renderers");
for (int f = 0; f < num; f ++)
{
if (!SDL_GetRenderDriverInfo(f, &info))
dumpRendererInfo("renderer name: %s", info);
}
}
#else
char videoDriverName[65];
if (SDL_VideoDriverName(videoDriverName, 64))
logger->log("Using video driver: %s", videoDriverName);
else
logger->log1("Using video driver: unknown");
mDoubleBuffer = ((mWindow->flags & SDL_DOUBLEBUF) == SDL_DOUBLEBUF);
logger->log("Double buffer mode: %s", mDoubleBuffer ? "yes" : "no");
imageHelper->dumpSurfaceFormat(mWindow);
const SDL_VideoInfo *const vi = SDL_GetVideoInfo();
if (!vi)
return false;
logger->log("Possible to create hardware surfaces: %s",
((vi->hw_available) ? "yes" : "no"));
logger->log("Window manager available: %s",
((vi->wm_available) ? "yes" : "no"));
logger->log("Accelerated hardware to hardware blits: %s",
((vi->blit_hw) ? "yes" : "no"));
logger->log("Accelerated hardware to hardware colorkey blits: %s",
((vi->blit_hw_CC) ? "yes" : "no"));
logger->log("Accelerated hardware to hardware alpha blits: %s",
((vi->blit_hw_A) ? "yes" : "no"));
logger->log("Accelerated software to hardware blits: %s",
((vi->blit_sw) ? "yes" : "no"));
logger->log("Accelerated software to hardware colorkey blits: %s",
((vi->blit_sw_CC) ? "yes" : "no"));
logger->log("Accelerated software to hardware alpha blits: %s",
((vi->blit_sw_A) ? "yes" : "no"));
logger->log("Accelerated color fills: %s",
((vi->blit_fill) ? "yes" : "no"));
#endif
return true;
}
bool Graphics::setFullscreen(const bool fs)
{
if (mFullscreen == fs)
return true;
return setVideoMode(mWidth, mHeight, mBpp, fs, mHWAccel,
mEnableResize, mNoFrame);
}
bool Graphics::resizeScreen(const int width, const int height)
{
if (mWidth == width && mHeight == height)
return true;
#ifdef USE_SDL2
_endDraw();
mRect.w = width;
mRect.h = height;
mWidth = width;
mHeight = height;
#ifdef USE_OPENGL
// +++ probably this way will not work in windows/mac
// Setup OpenGL
glViewport(0, 0, mWidth, mHeight);
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);
#else
// +++ need impliment resize in soft mode
#endif // USE_OPENGL
_beginDraw();
return true;
#else
const int prevWidth = mWidth;
const int prevHeight = mHeight;
_endDraw();
const bool success = setVideoMode(width, height, mBpp,
mFullscreen, mHWAccel, mEnableResize, mNoFrame);
// If it didn't work, try to restore the previous size. If that didn't
// work either, bail out (but then we're in deep trouble).
if (!success)
{
if (!setVideoMode(prevWidth, prevHeight, mBpp,
mFullscreen, mHWAccel, mEnableResize, mNoFrame))
{
return false;
}
}
_beginDraw();
return success;
#endif // USE_SDL2
}
int Graphics::getWidth() const
{
return mWidth;
}
int Graphics::getHeight() const
{
return mHeight;
}
bool Graphics::drawImage(const Image *image, int x, int y)
{
if (image)
{
return drawImage2(image, 0, 0, x, y,
image->mBounds.w, image->mBounds.h, false);
}
else
{
return false;
}
}
void Graphics::drawImageRect(const int x, const int y,
const int w, const int h,
const Image *const topLeft,
const Image *const topRight,
const Image *const bottomLeft,
const Image *const bottomRight,
const Image *const top,
const Image *const right,
const Image *const bottom,
const Image *const left,
const Image *const center)
{
BLOCK_START("Graphics::drawImageRect")
const bool drawMain = center && topLeft && topRight
&& bottomLeft && bottomRight;
// Draw the center area
if (center && drawMain)
{
const int tlw = topLeft->getWidth();
const int tlh = topLeft->getHeight();
drawImagePattern(center, tlw + x, tlh + y,
w - tlw - topRight->getWidth(),
h - tlh - bottomLeft->getHeight());
}
// Draw the sides
if (top && left && bottom && right)
{
const int lw = left->getWidth();
const int rw = right->getWidth();
const int th = top->getHeight();
const int bh = bottom->getHeight();
drawImagePattern(top, x + lw, y, w - lw - rw, th);
drawImagePattern(bottom, x + lw, h - bh + y, w - lw - rw, bh);
drawImagePattern(left, x, y + th, lw, h - th - bh);
if (w > rw)
drawImagePattern(right, x + w - rw, th + y, rw, h - th - bh);
}
// Draw the corners
if (drawMain)
{
DRAW_IMAGE(this, topLeft, x, y);
const int trw = topRight->getWidth();
if (w > trw)
{
DRAW_IMAGE(this, topRight, x + w - trw, y);
}
DRAW_IMAGE(this, bottomLeft, x, h - bottomLeft->getHeight() + y);
const int brw = bottomRight->getWidth();
if (w > brw)
{
DRAW_IMAGE(this, bottomRight,
x + w - brw,
y + h - bottomRight->getHeight());
}
}
BLOCK_END("Graphics::drawImageRect")
}
void Graphics::drawImageRect(int x, int y, int w, int h,
const ImageRect &imgRect)
{
drawImageRect(x, y, w, h,
imgRect.grid[0], imgRect.grid[2], imgRect.grid[6], imgRect.grid[8],
imgRect.grid[1], imgRect.grid[5], imgRect.grid[7], imgRect.grid[3],
imgRect.grid[4]);
}
bool Graphics::drawNet(const int x1, const int y1, const int x2, const int y2,
const int width, const int height)
{
for (int y = y1; y < y2; y += height)
drawLine(x1, y, x2, y);
for (int x = x1; x < x2; x += width)
drawLine(x, y1, x, y2);
return true;
}
bool Graphics::calcImageRect(ImageVertexes *const vert,
const int x, const int y,
const int w, const int h,
const Image *const topLeft,
const Image *const topRight,
const Image *const bottomLeft,
const Image *const bottomRight,
const Image *const top,
const Image *const right,
const Image *const bottom,
const Image *const left,
const Image *const center)
{
if (!vert)
return false;
BLOCK_START("Graphics::calcImageRect")
const bool drawMain = center && topLeft && topRight
&& bottomLeft && bottomRight;
// pushClipArea(gcn::Rectangle(x, y, w, h));
// Draw the center area
if (center && drawMain)
{
const int tlw = topLeft->getWidth();
const int tlh = topLeft->getHeight();
calcImagePattern(vert, center, tlw + x, tlh + y,
w - tlw - topRight->getWidth(),
h - tlh - bottomLeft->getHeight());
}
// Draw the sides
if (top && left && bottom && right)
{
const int lw = left->getWidth();
const int rw = right->getWidth();
const int th = top->getHeight();
const int bh = bottom->getHeight();
calcImagePattern(vert, top, x + lw, y, w - lw - rw, th);
calcImagePattern(vert, bottom, x + lw, y + h - bh, w - lw - rw, bh);
calcImagePattern(vert, left, x, y + th, lw, h - th - bh);
if (w > rw)
calcImagePattern(vert, right, x + w - rw, y + th, rw, h - th - bh);
}
calcTileVertexes(vert, topLeft, x, y);
if (topRight)
{
const int trw = topRight->getWidth();
if (w > trw)
calcTileVertexes(vert, topRight, x + w - trw, y);
}
if (bottomLeft)
calcTileVertexes(vert, bottomLeft, x, y + h - bottomLeft->getHeight());
if (bottomRight)
{
const int brw = bottomRight->getWidth();
if (w > brw)
{
calcTileVertexes(vert, bottomRight, x + w - brw,
y + h - bottomRight->getHeight());
}
}
// popClipArea();
BLOCK_END("Graphics::calcImageRect")
return 0;
}
void Graphics::setWindowSize(const int width A_UNUSED,
const int height A_UNUSED)
{
#ifdef USE_SDL2
SDL_SetWindowSize(mWindow, width, height);
#endif
}