1
0
mirror of https://github.com/blupi-games/planetblupi synced 2024-12-30 10:15:36 +01:00
planetblupi/src/pixmap.cxx
Mathieu Schroeter bc0a7cbc76 Do not prepend SDL headers by SDL2/
It's not always the case.
2018-07-17 18:13:08 +02:00

1012 lines
24 KiB
C++

/*
* This file is part of the planetblupi source code
* Copyright (C) 1997, Daniel Roux & EPSITEC SA
* Copyright (C) 2017, Mathieu Schroeter
* http://epsitec.ch; http://www.blupi.org; http://github.com/blupi-games
*
* 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 3 of the License, or
* (at your option) 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://gnu.org/licenses
*/
#include <stdio.h>
#include <stdlib.h>
#include <string>
#ifdef _WIN32
#include <io.h>
#define access _access
#else /* _WIN32 */
#include <unistd.h>
#endif /* !_WIN32 */
#include <SDL_image.h>
#include "blupi.h"
#include "def.h"
#include "event.h"
#include "misc.h"
#include "pixmap.h"
/////////////////////////////////////////////////////////////////////////////
// Constructeur.
CPixmap::CPixmap (CEvent * event)
{
Sint32 i;
m_bDebug = true;
m_bPalette = true;
m_mouseSprite = SPRITE_WAIT;
m_bBackDisplayed = false;
m_lpSDLBlupi = nullptr;
for (i = 0; i < MAXCURSORS; i++)
m_lpSDLCursors[i] = nullptr;
m_lpCurrentCursor = nullptr;
this->mainTexture = nullptr;
this->event = event;
}
// Destructeur.
CPixmap::~CPixmap ()
{
unsigned int i;
for (i = 0; i < countof (m_lpSDLCursors); i++)
{
if (m_lpSDLCursors[i])
{
SDL_FreeCursor (m_lpSDLCursors[i]);
m_lpSDLCursors[i] = nullptr;
}
}
for (auto tex : m_SDLTextureInfo)
{
if (tex.second.texture)
SDL_DestroyTexture (tex.second.texture);
if (tex.second.texMask)
SDL_DestroyTexture (tex.second.texMask);
}
if (m_lpSDLBlupi)
SDL_FreeSurface (m_lpSDLBlupi);
if (this->mainTexture)
SDL_DestroyTexture (this->mainTexture);
}
// Cr�e l'objet DirectDraw principal.
// Retourne false en cas d'erreur.
bool
CPixmap::Create (Point dim)
{
m_dim = dim;
m_clipRect.left = 0;
m_clipRect.top = 0;
m_clipRect.right = dim.x;
m_clipRect.bottom = dim.y;
return true;
}
Sint32
CPixmap::BltFast (Sint32 dstCh, size_t srcCh, Rect dstR, Rect srcR)
{
Sint32 res;
SDL_Rect srcRect, dstRect;
srcRect.x = srcR.left;
srcRect.y = srcR.top;
srcRect.w = srcR.right - srcR.left;
srcRect.h = srcR.bottom - srcR.top;
dstRect.x = dstR.left;
dstRect.y = dstR.top;
dstRect.w = dstR.right - dstR.left;
dstRect.h = dstR.bottom - dstR.top;
auto target = SDL_GetRenderTarget (g_renderer);
if (dstCh < 0)
{
if (!this->mainTexture && g_bFullScreen && g_zoom == 1)
{
SDL_SetHint (SDL_HINT_RENDER_SCALE_QUALITY, "best");
this->mainTexture = SDL_CreateTexture (
g_renderer, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_TARGET,
LXIMAGE (), LYIMAGE ());
SDL_SetHint (SDL_HINT_RENDER_SCALE_QUALITY, "nearest");
}
else if (this->mainTexture && !(g_bFullScreen && g_zoom == 1))
{
SDL_DestroyTexture (this->mainTexture);
this->mainTexture = nullptr;
}
if (this->mainTexture)
SDL_SetRenderTarget (g_renderer, target ? target : this->mainTexture);
res = SDL_RenderCopy (
g_renderer, m_SDLTextureInfo[srcCh].texture, &srcRect, &dstRect);
if (this->mainTexture)
SDL_SetRenderTarget (g_renderer, target);
}
else
{
SDL_SetRenderTarget (g_renderer, m_SDLTextureInfo[dstCh].texture);
res = SDL_RenderCopy (
g_renderer, m_SDLTextureInfo[srcCh].texture, &srcRect, &dstRect);
SDL_SetRenderTarget (g_renderer, target);
}
return res;
}
// Effectue un appel BltFast.
// Les modes sont 0=transparent, 1=opaque.
Sint32
CPixmap::BltFast (Sint32 chDst, size_t channel, Point dst, Rect rcRect)
{
Sint32 limit;
// Effectue un peu de clipping.
if (dst.x < m_clipRect.left)
{
rcRect.left += m_clipRect.left - dst.x;
dst.x = m_clipRect.left;
}
limit = (m_clipRect.right - dst.x) + rcRect.left;
if (rcRect.right > limit)
rcRect.right = limit;
if (dst.y < m_clipRect.top)
{
rcRect.top += m_clipRect.top - dst.y;
dst.y = m_clipRect.top;
}
limit = (m_clipRect.bottom - dst.y) + rcRect.top;
if (rcRect.bottom > limit)
rcRect.bottom = limit;
if (rcRect.left >= rcRect.right || rcRect.top >= rcRect.bottom)
return 0;
Rect dstRect;
dstRect.left = dst.x;
dstRect.top = dst.y;
dstRect.right = dstRect.left + rcRect.right - rcRect.left;
dstRect.bottom = dstRect.top + rcRect.bottom - rcRect.top;
return this->BltFast (chDst, channel, dstRect, rcRect);
}
// Effectue un appel BltFast.
// Les modes sont 0=transparent, 1=opaque.
Sint32
CPixmap::BltFast (
SDL_Texture * lpSDL, size_t channel, Point dst, Rect rcRect,
SDL_BlendMode mode)
{
Sint32 res;
SDL_Rect srcRect, dstRect;
srcRect.x = rcRect.left;
srcRect.y = rcRect.top;
srcRect.w = rcRect.right - rcRect.left;
srcRect.h = rcRect.bottom - rcRect.top;
dstRect = srcRect;
dstRect.x = dst.x;
dstRect.y = dst.y;
SDL_BlendMode oMode;
SDL_GetTextureBlendMode (m_SDLTextureInfo[channel].texture, &oMode);
if (oMode != mode)
SDL_SetTextureBlendMode (m_SDLTextureInfo[channel].texture, mode);
SDL_SetRenderTarget (g_renderer, lpSDL);
res = SDL_RenderCopy (
g_renderer, m_SDLTextureInfo[channel].texture, &srcRect, &dstRect);
SDL_SetRenderTarget (g_renderer, nullptr);
if (oMode != mode)
SDL_SetTextureBlendMode (m_SDLTextureInfo[channel].texture, oMode);
return res;
}
/**
* \brief Reload textures created with access target flag.
*
* \returns true on success.
*/
bool
CPixmap::ReloadTargetTextures ()
{
if (this->mainTexture)
{
SDL_DestroyTexture (this->mainTexture);
this->mainTexture = nullptr;
}
for (auto & tex : m_SDLTextureInfo)
{
if (!tex.second.target)
continue;
if (tex.second.file.empty ())
{
if (!Cache (tex.first, tex.second.dimTotal))
return false;
}
else if (!Cache (
tex.first, tex.second.file, tex.second.dimTotal,
tex.second.dimIcon))
return false;
}
return true;
}
bool
CPixmap::Cache (size_t channel, Point totalDim)
{
if (m_SDLTextureInfo.find (channel) == m_SDLTextureInfo.end ())
{
m_SDLTextureInfo[channel].texture = SDL_CreateTexture (
g_renderer, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_TARGET, totalDim.x,
totalDim.y);
if (!m_SDLTextureInfo[channel].texture)
{
SDL_LogError (
SDL_LOG_CATEGORY_APPLICATION, "Couldn't create texture: %s",
SDL_GetError ());
return false;
}
SDL_SetTextureBlendMode (
m_SDLTextureInfo[channel].texture, SDL_BLENDMODE_BLEND);
}
else
{
SDL_SetRenderTarget (g_renderer, m_SDLTextureInfo[channel].texture);
SDL_SetRenderDrawColor (g_renderer, 0, 0, 0, 0);
SDL_RenderClear (g_renderer);
SDL_SetRenderTarget (g_renderer, nullptr);
}
m_SDLTextureInfo[channel].texMask = nullptr;
m_SDLTextureInfo[channel].target = true;
m_SDLTextureInfo[channel].dimIcon = Point{0, 0};
m_SDLTextureInfo[channel].dimTotal = totalDim;
m_SDLTextureInfo[channel].file = "";
return true;
}
// Cache une image contenant des ic�nes.
bool
CPixmap::Cache (
size_t channel, const std::string & pFilename, Point totalDim, Point iconDim,
Mode mode, size_t chBackWide)
{
std::string file = GetBaseDir () + pFilename;
SDL_Surface * surface = IMG_Load (file.c_str ());
bool blupiChSet = false;
if (channel == CHBLUPI && !m_lpSDLBlupi)
{
m_lpSDLBlupi = surface;
blupiChSet = true;
}
SDL_Texture * texture = SDL_CreateTextureFromSurface (g_renderer, surface);
Uint32 format;
Sint32 access, ow, w, oh, h;
SDL_QueryTexture (texture, &format, &access, &ow, &oh);
auto m = mode == EXPAND || channel == CHBACK;
w = m ? LXIMAGE () : ow;
h = m ? LYIMAGE () : oh;
if (m_SDLTextureInfo.find (channel) == m_SDLTextureInfo.end ())
{
m_SDLTextureInfo[channel].texture = SDL_CreateTexture (
g_renderer, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_TARGET, w, h);
if (!m_SDLTextureInfo[channel].texture)
{
SDL_LogError (
SDL_LOG_CATEGORY_APPLICATION,
"Couldn't create texture from surface: %s", SDL_GetError ());
return false;
}
if (channel != CHBACK)
SDL_SetTextureBlendMode (
m_SDLTextureInfo[channel].texture, SDL_BLENDMODE_BLEND);
}
SDL_SetRenderTarget (g_renderer, m_SDLTextureInfo[channel].texture);
SDL_SetRenderDrawColor (g_renderer, 0, 0, 0, 0);
SDL_RenderClear (g_renderer);
SDL_SetRenderTarget (g_renderer, nullptr);
m_SDLTextureInfo[channel].texMask = channel == CHMASK2 ? texture : nullptr;
m_SDLTextureInfo[channel].target = true;
m_SDLTextureInfo[channel].dimIcon = iconDim;
m_SDLTextureInfo[channel].dimTotal = totalDim;
m_SDLTextureInfo[channel].file = pFilename;
SDL_SetRenderTarget (g_renderer, m_SDLTextureInfo[channel].texture);
switch (mode)
{
case FIX:
{
if (channel == CHBACK && (ow < LXIMAGE () || oh < LYIMAGE ()))
{
if (chBackWide > 0)
{
Rect srcRect;
srcRect.left = 0;
srcRect.right = LXIMAGE ();
srcRect.top = 0;
srcRect.bottom = LYIMAGE ();
this->DrawImage (-1, chBackWide, srcRect);
}
SDL_Rect dst;
dst.x = (LXIMAGE () - ow) / 2;
dst.y = 0;
dst.w = ow;
dst.h = oh;
SDL_RenderCopy (g_renderer, texture, nullptr, &dst);
}
else
SDL_RenderCopy (g_renderer, texture, nullptr, nullptr);
break;
}
case EXPAND:
{
SDL_Rect src, dst;
src.x = 0;
src.y = 0;
src.w = POSDRAWX - 1;
src.h = LYIMAGE ();
dst = src;
SDL_RenderCopy (g_renderer, texture, &src, &dst);
src.x = ow - 16;
src.w = 16;
dst.x = LXIMAGE () - 16;
dst.w = src.w;
SDL_RenderCopy (g_renderer, texture, &src, &dst);
src.x = POSDRAWX - 1;
src.w = ow - src.x - 16;
dst.x = src.x;
dst.w = DIMDRAWX + 1;
SDL_RenderCopy (g_renderer, texture, &src, &dst);
break;
}
}
SDL_SetRenderTarget (g_renderer, nullptr);
if (!m_SDLTextureInfo[channel].texMask)
SDL_DestroyTexture (texture);
if (!blupiChSet)
SDL_FreeSurface (surface);
return true;
}
// Cache une image globale.
bool
CPixmap::Cache (size_t channel, const std::string & pFilename, Point totalDim)
{
Point iconDim;
iconDim.x = 0;
iconDim.y = 0;
return Cache (channel, pFilename, totalDim, iconDim);
}
// Cache une image provenant d'un bitmap.
bool
CPixmap::Cache (size_t channel, SDL_Surface * surface, Point totalDim)
{
// Create the offscreen surface, by loading our bitmap.
if (
m_SDLTextureInfo.find (channel) != m_SDLTextureInfo.end () &&
m_SDLTextureInfo[channel].texture)
SDL_DestroyTexture (m_SDLTextureInfo[channel].texture);
m_SDLTextureInfo[channel].texture =
SDL_CreateTextureFromSurface (g_renderer, surface);
m_SDLTextureInfo[channel].target = false;
if (!m_SDLTextureInfo[channel].texture)
return false;
m_SDLTextureInfo[channel].dimTotal = totalDim;
m_SDLTextureInfo[channel].dimIcon = totalDim;
return true;
}
// Modifie la r�gion de clipping.
void
CPixmap::SetClipping (Rect clip)
{
m_clipRect = clip;
}
// Retourne la r�gion de clipping.
Rect
CPixmap::GetClipping ()
{
return m_clipRect;
}
// Teste si un point fait partie d'une ic�ne.
bool
CPixmap::IsIconPixel (size_t channel, Sint32 rank, Point pos)
{
Sint32 nbx, nby;
auto texInfo = m_SDLTextureInfo.find (channel);
if (texInfo == m_SDLTextureInfo.end ())
return false;
if (texInfo->second.dimIcon.x == 0 || texInfo->second.dimIcon.y == 0)
return false;
nbx = texInfo->second.dimTotal.x / texInfo->second.dimIcon.x;
nby = texInfo->second.dimTotal.y / texInfo->second.dimIcon.y;
if (rank < 0 || rank >= nbx * nby)
return false;
pos.x += (rank % nbx) * texInfo->second.dimIcon.x;
pos.y += (rank / nbx) * texInfo->second.dimIcon.y;
SDL_Rect rect;
rect.x = pos.x;
rect.y = pos.y;
rect.w = 1;
rect.h = 1;
Uint32 pixel = 0;
SDL_SetRenderTarget (g_renderer, m_SDLTextureInfo[channel].texture);
SDL_RenderReadPixels (g_renderer, &rect, 0, &pixel, 4);
SDL_SetRenderTarget (g_renderer, nullptr);
return !!pixel;
}
// Dessine une partie d'image rectangulaire.
// Les modes sont 0=transparent, 1=opaque.
bool
CPixmap::DrawIcon (Sint32 chDst, size_t channel, Sint32 rank, Point pos)
{
Sint32 nbx, nby;
Rect rect;
auto texInfo = m_SDLTextureInfo.find (channel);
if (channel != CHMAP && texInfo == m_SDLTextureInfo.end ())
return false;
if (texInfo->second.dimIcon.x == 0 || texInfo->second.dimIcon.y == 0)
return false;
nbx = texInfo->second.dimTotal.x / texInfo->second.dimIcon.x;
nby = texInfo->second.dimTotal.y / texInfo->second.dimIcon.y;
if (rank < 0 || rank >= nbx * nby)
return false;
rect.left = (rank % nbx) * texInfo->second.dimIcon.x;
rect.top = (rank / nbx) * texInfo->second.dimIcon.y;
rect.right = rect.left + texInfo->second.dimIcon.x;
rect.bottom = rect.top + texInfo->second.dimIcon.y;
return !BltFast (chDst, channel, pos, rect);
}
// Dessine une partie d'image rectangulaire.
// Les modes sont 0=transparent, 1=opaque.
//
// Correspondances in,out :
// 0,0 2,1 ...
// 1,16 3,17
//
// 32,32 34,33
// 33,48 35,49
bool
CPixmap::DrawIconDemi (Sint32 chDst, size_t channel, Sint32 rank, Point pos)
{
Sint32 nbx, nby;
Rect rect;
auto texInfo = m_SDLTextureInfo.find (channel);
if (texInfo == m_SDLTextureInfo.end ())
return false;
if (texInfo->second.dimIcon.x == 0 || texInfo->second.dimIcon.y == 0)
return false;
nbx = texInfo->second.dimTotal.x / texInfo->second.dimIcon.x;
nby = texInfo->second.dimTotal.y / (texInfo->second.dimIcon.y / 2);
rank = (rank / 32) * 32 + ((rank % 32) / 2) + ((rank % 2) * 16);
if (rank < 0 || rank >= nbx * nby)
return false;
rect.left = (rank % nbx) * texInfo->second.dimIcon.x;
rect.top = (rank / nbx) * (texInfo->second.dimIcon.y / 2);
rect.right = rect.left + texInfo->second.dimIcon.x;
rect.bottom = rect.top + (texInfo->second.dimIcon.y / 2);
return !BltFast (chDst, channel, pos, rect);
}
// Dessine une partie d'image rectangulaire.
bool
CPixmap::DrawIconPart (
Sint32 chDst, size_t channel, Sint32 rank, Point pos, Sint32 startY,
Sint32 endY)
{
Sint32 nbx, nby;
Rect rect;
auto texInfo = m_SDLTextureInfo.find (channel);
if (texInfo == m_SDLTextureInfo.end ())
return false;
if (texInfo->second.dimIcon.x == 0 || texInfo->second.dimIcon.y == 0)
return false;
nbx = texInfo->second.dimTotal.x / texInfo->second.dimIcon.x;
nby = texInfo->second.dimTotal.y / texInfo->second.dimIcon.y;
if (rank < 0 || rank >= nbx * nby)
return false;
rect.left = (rank % nbx) * texInfo->second.dimIcon.x;
rect.top = (rank / nbx) * texInfo->second.dimIcon.y;
rect.right = rect.left + texInfo->second.dimIcon.x;
rect.bottom = rect.top + endY;
pos.y += startY;
rect.top += startY;
return !BltFast (chDst, channel, pos, rect);
}
// Dessine une partie d'image n'importe o�.
bool
CPixmap::DrawPart (Sint32 chDst, size_t channel, Point dest, Rect rect)
{
if (m_SDLTextureInfo.find (channel) == m_SDLTextureInfo.end ())
return false;
return !BltFast (chDst, channel, dest, rect);
}
// Dessine une partie d'image rectangulaire.
bool
CPixmap::DrawImage (Sint32 chDst, size_t channel, Rect rect)
{
Point dst;
Sint32 res;
if (m_SDLTextureInfo.find (channel) == m_SDLTextureInfo.end ())
return false;
dst.x = rect.left;
dst.y = rect.top;
res = BltFast (chDst, channel, dst, rect);
if (res)
return false;
if (channel == CHBACK)
m_bBackDisplayed = false;
return true;
}
// Construit une ic�ne en utilisant un masque.
bool
CPixmap::BuildIconMask (
size_t channelMask, Sint32 rankMask, size_t channel, Sint32 rankSrc,
Sint32 rankDst)
{
Sint32 nbx, nby;
Point posDst, posDstMask;
Rect rect, rectMask;
Sint32 res;
auto texInfo = m_SDLTextureInfo.find (channel);
if (texInfo == m_SDLTextureInfo.end ())
return false;
if (texInfo->second.dimIcon.x == 0 || texInfo->second.dimIcon.y == 0)
return false;
nbx = texInfo->second.dimTotal.x / texInfo->second.dimIcon.x;
nby = texInfo->second.dimTotal.y / texInfo->second.dimIcon.y;
if (rankSrc < 0 || rankSrc >= nbx * nby)
return false;
if (rankDst < 0 || rankDst >= nbx * nby)
return false;
rect.left = (rankSrc % nbx) * texInfo->second.dimIcon.x;
rect.top = (rankSrc / nbx) * texInfo->second.dimIcon.y;
rect.right = rect.left + texInfo->second.dimIcon.x;
rect.bottom = rect.top + texInfo->second.dimIcon.y;
posDst.x = (rankDst % nbx) * texInfo->second.dimIcon.x;
posDst.y = (rankDst / nbx) * texInfo->second.dimIcon.y;
res = BltFast (m_SDLTextureInfo[channel].texture, channel, posDst, rect);
if (res)
return false;
auto texMaskInfo = m_SDLTextureInfo.find (channelMask);
if (texMaskInfo->second.dimIcon.x == 0 || texMaskInfo->second.dimIcon.y == 0)
return false;
nbx = texMaskInfo->second.dimTotal.x / texMaskInfo->second.dimIcon.x;
nby = texMaskInfo->second.dimTotal.y / texMaskInfo->second.dimIcon.y;
/* Support only CHMASK1 (white) because it needs CHMASK2 (black) (hardcoded
* here) */
if (rankMask < 0 || rankMask >= nbx * nby || channelMask != CHMASK1)
return false;
rectMask.left = (rankMask % nbx) * texMaskInfo->second.dimIcon.x;
rectMask.top = (rankMask / nbx) * texMaskInfo->second.dimIcon.y;
rectMask.right = rectMask.left + texMaskInfo->second.dimIcon.x;
rectMask.bottom = rectMask.top + texMaskInfo->second.dimIcon.y;
/* Multiply the white mask with the texture (no alpha) */
res = BltFast (
m_SDLTextureInfo[channel].texture, channelMask, posDst, rectMask,
SDL_BLENDMODE_MOD);
posDstMask.x = (rankMask % nbx) * texMaskInfo->second.dimIcon.x;
posDstMask.y = (rankMask / nbx) * texMaskInfo->second.dimIcon.y;
/* Addition the previous texture with the black mask (alpha retrieved) */
res = BltFast (
m_SDLTextureInfo[CHMASK2].texture, channel, posDstMask, rect,
SDL_BLENDMODE_ADD);
/* Blit the altered mask in the final texture */
res = BltFast (
m_SDLTextureInfo[channel].texture, CHMASK2, posDst, rectMask,
SDL_BLENDMODE_NONE);
/* Restore the black mask for the next iteration. */
SDL_SetRenderTarget (g_renderer, m_SDLTextureInfo[CHMASK2].texture);
SDL_RenderCopy (
g_renderer, m_SDLTextureInfo[CHMASK2].texMask, nullptr, nullptr);
SDL_SetRenderTarget (g_renderer, nullptr);
return !res;
}
// Affiche le pixmap � l'�cran.
// Retourne false en cas d'erreur.
bool
CPixmap::Display ()
{
m_bBackDisplayed = true;
if (this->mainTexture)
SDL_RenderCopy (g_renderer, this->mainTexture, nullptr, nullptr);
SDL_RenderPresent (g_renderer);
return true;
}
// Change le lutin de la souris.
void
CPixmap::SetMouseSprite (MouseSprites sprite)
{
if (m_mouseSprite == sprite)
return;
m_mouseSprite = sprite;
SDL_SetCursor (m_lpSDLCursors[sprite - 1]);
}
// Montre ou cache la souris.
void
CPixmap::MouseShow (bool bShow)
{
SDL_ShowCursor (bShow);
}
// Retourne le rectangle correspondant au sprite
// de la souris dans CHBLUPI.
Rect
CPixmap::MouseRectSprite ()
{
Sint32 rank, nbx;
Rect rcRect;
rank = 348;
if (m_mouseSprite == SPRITE_ARROW)
rank = 348;
if (m_mouseSprite == SPRITE_POINTER)
rank = 349;
if (m_mouseSprite == SPRITE_MAP)
rank = 350;
if (m_mouseSprite == SPRITE_WAIT)
rank = 351;
if (m_mouseSprite == SPRITE_FILL)
rank = 352;
if (m_mouseSprite == SPRITE_ARROWL)
rank = 353;
if (m_mouseSprite == SPRITE_ARROWR)
rank = 354;
if (m_mouseSprite == SPRITE_ARROWU)
rank = 355;
if (m_mouseSprite == SPRITE_ARROWD)
rank = 356;
if (m_mouseSprite == SPRITE_ARROWDL)
rank = 357;
if (m_mouseSprite == SPRITE_ARROWDR)
rank = 358;
if (m_mouseSprite == SPRITE_ARROWUL)
rank = 359;
if (m_mouseSprite == SPRITE_ARROWUR)
rank = 360;
auto texMaskInfo = m_SDLTextureInfo.find (CHBLUPI);
nbx = texMaskInfo->second.dimTotal.x / texMaskInfo->second.dimIcon.x;
rcRect.left = (rank % nbx) * texMaskInfo->second.dimIcon.x;
rcRect.top = (rank / nbx) * texMaskInfo->second.dimIcon.y;
rcRect.right = rcRect.left + texMaskInfo->second.dimIcon.x;
rcRect.bottom = rcRect.top + texMaskInfo->second.dimIcon.y;
return rcRect;
}
SDL_Point
CPixmap::GetCursorHotSpot (MouseSprites sprite)
{
static const Sint32 hotspots[MAXCURSORS * 2] = {
30, 30, // SPRITE_ARROW
20, 15, // SPRITE_POINTER
31, 26, // SPRITE_MAP
25, 14, // SPRITE_ARROWU
24, 35, // SPRITE_ARROWD
15, 24, // SPRITE_ARROWL
35, 24, // SPRITE_ARROWR
18, 16, // SPRITE_ARROWUL
32, 18, // SPRITE_ARROWUR
17, 30, // SPRITE_ARROWDL
32, 32, // SPRITE_ARROWDR
30, 30, // SPRITE_WAIT
30, 30, // SPRITE_EMPTY
21, 51, // SPRITE_FILL
};
SDL_Point hotspot = {0, 0};
if (sprite >= SPRITE_BEGIN && sprite <= SPRITE_END)
{
const Sint32 rank = sprite - SPRITE_BEGIN; // rank <- 0..n
hotspot.x = hotspots[rank * 2 + 0];
hotspot.y = hotspots[rank * 2 + 1];
}
return hotspot;
}
SDL_Rect
CPixmap::GetCursorRect (MouseSprites sprite)
{
Sint32 rank;
SDL_Rect rcRect;
switch (sprite)
{
default:
case SPRITE_ARROW:
rank = 348;
break;
case SPRITE_POINTER:
rank = 349;
break;
case SPRITE_MAP:
rank = 350;
break;
case SPRITE_WAIT:
rank = 351;
break;
case SPRITE_FILL:
rank = 352;
break;
case SPRITE_ARROWL:
rank = 353;
break;
case SPRITE_ARROWR:
rank = 354;
break;
case SPRITE_ARROWU:
rank = 355;
break;
case SPRITE_ARROWD:
rank = 356;
break;
case SPRITE_ARROWDL:
rank = 357;
break;
case SPRITE_ARROWDR:
rank = 358;
break;
case SPRITE_ARROWUL:
rank = 359;
break;
case SPRITE_ARROWUR:
rank = 360;
break;
}
auto texMaskInfo = m_SDLTextureInfo.find (CHBLUPI);
Sint32 nbx = texMaskInfo->second.dimTotal.x / texMaskInfo->second.dimIcon.x;
rcRect.x = (rank % nbx) * texMaskInfo->second.dimIcon.x;
rcRect.y = (rank / nbx) * texMaskInfo->second.dimIcon.y;
rcRect.w = texMaskInfo->second.dimIcon.x;
rcRect.h = texMaskInfo->second.dimIcon.y;
return rcRect;
}
void
CPixmap::LoadCursors ()
{
Uint32 rmask, gmask, bmask, amask;
/* SDL interprets each pixel as a 32-bit number, so our masks must depend
* on the endianness (byte order) of the machine
*/
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
rmask = 0xff000000;
gmask = 0x00ff0000;
bmask = 0x0000ff00;
amask = 0x000000ff;
#else /* SDL_BYTEORDER == SDL_BIG_ENDIAN */
rmask = 0x000000ff;
gmask = 0x0000ff00;
bmask = 0x00ff0000;
amask = 0xff000000;
#endif /* SDL_BYTEORDER != SDL_BIG_ENDIAN */
auto scale = this->GetDisplayScale ();
for (int i = SPRITE_BEGIN; i <= SPRITE_END; ++i)
{
MouseSprites sprite = static_cast<MouseSprites> (i);
if (m_lpSDLCursors[sprite - 1])
SDL_FreeCursor (m_lpSDLCursors[sprite - 1]);
SDL_Point hotspot = this->GetCursorHotSpot (sprite);
SDL_Rect rect = this->GetCursorRect (sprite);
SDL_Surface * surface = SDL_CreateRGBSurface (
0, rect.w * scale, rect.h * scale, 32, rmask, gmask, bmask, amask);
SDL_BlitScaled (m_lpSDLBlupi, &rect, surface, nullptr);
// FIXME: change cursor first value to 0
m_lpSDLCursors[sprite - 1] =
SDL_CreateColorCursor (surface, hotspot.x * scale, hotspot.y * scale);
SDL_FreeSurface (surface);
}
}
void
CPixmap::ChangeSprite (MouseSprites sprite)
{
if (m_lpCurrentCursor == m_lpSDLCursors[sprite - 1])
return;
SDL_SetCursor (m_lpSDLCursors[sprite - 1]);
m_lpCurrentCursor = m_lpSDLCursors[sprite - 1];
}
double
CPixmap::GetDisplayScale ()
{
// SDL_DisplayMode displayMode;
// SDL_GetWindowDisplayMode (g_window, &displayMode);
Sint32 w, h;
SDL_GetWindowSize (g_window, &w, &h);
return static_cast<double> (h / LYIMAGE ());
}
void
CPixmap::FromDisplayToGame (Sint32 & x, Sint32 & y, double prevScale)
{
if (this->event->IsDemoPlaying ())
return;
double factor = 1;
if (!g_bFullScreen)
factor = prevScale;
x /= factor;
y /= factor;
}
void
CPixmap::FromGameToDisplay (Sint32 & x, Sint32 & y)
{
double factor = 1;
if (!g_bFullScreen)
factor = g_zoom;
x *= factor;
y *= factor;
if (!g_bFullScreen)
return;
Sint32 w, h;
SDL_GetWindowSize (g_window, &w, &h);
double _w = w, _h = h;
x = x * _w / LXIMAGE ();
y = y * _h / LYIMAGE ();
}