1
0
mirror of https://github.com/blupi-games/planetblupi synced 2024-12-30 10:15:36 +01:00
planetblupi/src/pixmap.cxx
2017-02-26 17:57:40 +01:00

746 lines
18 KiB
C++

#include <stdlib.h>
#include <stdio.h>
#include <string>
#ifdef _WIN32
#include <io.h>
#define access _access
#else /* _WIN32 */
#include <unistd.h>
#endif /* !_WIN32 */
#include <SDL2/SDL_image.h>
#include "def.h"
#include "pixmap.h"
#include "misc.h"
#include "blupi.h"
/////////////////////////////////////////////////////////////////////////////
// Constructeur.
CPixmap::CPixmap()
{
Sint32 i;
m_mouseType = MOUSETYPEGRA;
m_bDebug = true;
m_bPalette = true;
m_mouseSprite = SPRITE_WAIT;
m_bBackDisplayed = false;
m_lpSDLBlupi = nullptr;
memset (m_filename, 0, sizeof (m_filename));
for (i = 0; i < MAXCURSORS; i++)
m_lpSDLCursors[i] = nullptr;
for (i = 0 ; i < MAXIMAGE ; i++)
m_lpSDLTexture[i] = nullptr;
m_lpCurrentCursor = nullptr;
}
// 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 (i = 0 ; i < countof (m_lpSDLTexture) ; i++)
{
if (m_lpSDLTexture[i] != nullptr)
{
SDL_DestroyTexture (m_lpSDLTexture[i]);
m_lpSDLTexture[i] = nullptr;
}
}
}
// Cr�e l'objet DirectDraw principal.
// Retourne false en cas d'erreur.
bool CPixmap::Create (POINT dim, Sint32 mouseType)
{
m_mouseType = mouseType;
m_dim = dim;
m_clipRect.left = 0;
m_clipRect.top = 0;
m_clipRect.right = dim.x;
m_clipRect.bottom = dim.y;
return true;
}
// Rempli une zone rectangulaire avec une couleur uniforme.
void CPixmap::Fill (RECT rect, COLORREF color)
{
// � faire si n�cessaire ...
}
// Effectue un appel BltFast.
// Les modes sont 0=transparent, 1=opaque.
Sint32 CPixmap::BltFast (Sint32 chDst, Sint32 channel, POINT dst, RECT rcRect)
{
Sint32 res, 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;
if (chDst < 0)
{
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;
res = SDL_RenderCopy (g_renderer, m_lpSDLTexture[channel], &srcRect, &dstRect);
}
else
{
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_SetRenderTarget (g_renderer, m_lpSDLTexture[chDst]);
res = SDL_RenderCopy (g_renderer, m_lpSDLTexture[channel], &srcRect, &dstRect);
SDL_SetRenderTarget (g_renderer, nullptr);
}
return res;
}
// Effectue un appel BltFast.
// Les modes sont 0=transparent, 1=opaque.
Sint32 CPixmap::BltFast (SDL_Texture *lpSDL, Sint32 channel, POINT dst,
RECT rcRect)
{
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_SetRenderTarget (g_renderer, lpSDL);
res = SDL_RenderCopy (g_renderer, m_lpSDLTexture[channel], &srcRect, &dstRect);
SDL_SetRenderTarget (g_renderer, nullptr);
return res;
}
// Cache une image contenant des ic�nes.
bool CPixmap::Cache (Sint32 channel, const char *pFilename, POINT totalDim,
POINT iconDim)
{
if (channel < 0 || channel >= MAXIMAGE)
return false;
std::string file = GetBaseDir() + pFilename;
SDL_Surface *surface = IMG_Load (file.c_str());
if (channel == CHBLUPI)
m_lpSDLBlupi = surface;
SDL_Texture *texture = SDL_CreateTextureFromSurface (g_renderer, surface);
Uint32 format;
Sint32 access, w, h;
SDL_QueryTexture (texture, &format, &access, &w, &h);
if (!m_lpSDLTexture[channel])
{
m_lpSDLTexture[channel] = SDL_CreateTexture (g_renderer, SDL_PIXELFORMAT_RGBA32,
SDL_TEXTUREACCESS_TARGET, w, h);
SDL_SetTextureBlendMode (m_lpSDLTexture[channel], SDL_BLENDMODE_BLEND);
}
else
{
SDL_SetRenderTarget (g_renderer, m_lpSDLTexture[channel]);
SDL_SetRenderDrawColor (g_renderer, 0, 0, 0, 0);
SDL_RenderClear (g_renderer);
SDL_SetRenderTarget (g_renderer, nullptr);
}
SDL_SetRenderTarget (g_renderer, m_lpSDLTexture[channel]);
SDL_RenderCopy (g_renderer, texture, nullptr, nullptr);
SDL_SetRenderTarget (g_renderer, nullptr);
SDL_DestroyTexture (texture);
//m_lpSDLTexture[channel] = SDL_CreateTextureFromSurface (g_renderer, surface);
if (!m_lpSDLTexture[channel])
{
SDL_LogError (SDL_LOG_CATEGORY_APPLICATION,
"Couldn't create texture from surface: %s", SDL_GetError());
return false;
}
if (channel != CHBLUPI)
SDL_FreeSurface (surface);
strcpy (m_filename[channel], pFilename);
m_totalDim[channel] = totalDim;
m_iconDim[channel] = iconDim;
return true;
}
// Cache une image globale.
bool CPixmap::Cache (Sint32 channel, const char *pFilename, POINT totalDim)
{
POINT iconDim;
if (channel < 0 || channel >= MAXIMAGE)
return false;
iconDim.x = 0;
iconDim.y = 0;
return Cache (channel, pFilename, totalDim, iconDim);
}
// Cache une image provenant d'un bitmap.
bool CPixmap::Cache (Sint32 channel, SDL_Surface *surface, POINT totalDim)
{
if (channel < 0 || channel >= MAXIMAGE)
return false;
// Create the offscreen surface, by loading our bitmap.
if (m_lpSDLTexture[channel])
SDL_DestroyTexture (m_lpSDLTexture[channel]);
m_lpSDLTexture[channel] = SDL_CreateTextureFromSurface (g_renderer, surface);
if (!m_lpSDLTexture[channel])
return false;
m_totalDim[channel] = totalDim;
m_iconDim[channel] = 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 (Sint32 channel, Sint32 rank, POINT pos)
{
Sint32 nbx, nby;
if (channel < 0 || channel >= MAXIMAGE)
return false;
if (m_lpSDLTexture[channel] == nullptr)
return false;
if (m_iconDim[channel].x == 0 ||
m_iconDim[channel].y == 0)
return false;
nbx = m_totalDim[channel].x / m_iconDim[channel].x;
nby = m_totalDim[channel].y / m_iconDim[channel].y;
if (rank < 0 || rank >= nbx * nby)
return false;
pos.x += (rank % nbx) * m_iconDim[channel].x;
pos.y += (rank / nbx) * m_iconDim[channel].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_lpSDLTexture[channel]);
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, Sint32 channel, Sint32 rank, POINT pos,
bool bMask)
{
Sint32 nbx, nby;
RECT rect;
if (channel < 0 || channel >= MAXIMAGE)
return false;
if (channel != CHMAP && m_lpSDLTexture[channel] == nullptr)
return false;
if (m_iconDim[channel].x == 0 ||
m_iconDim[channel].y == 0)
return false;
nbx = m_totalDim[channel].x / m_iconDim[channel].x;
nby = m_totalDim[channel].y / m_iconDim[channel].y;
if (rank < 0 || rank >= nbx * nby)
return false;
rect.left = (rank % nbx) * m_iconDim[channel].x;
rect.top = (rank / nbx) * m_iconDim[channel].y;
rect.right = rect.left + m_iconDim[channel].x;
rect.bottom = rect.top + m_iconDim[channel].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, Sint32 channel, Sint32 rank,
POINT pos, bool bMask)
{
Sint32 nbx, nby;
RECT rect;
if (channel < 0 || channel >= MAXIMAGE)
return false;
if (m_lpSDLTexture[channel] == nullptr)
return false;
if (m_iconDim[channel].x == 0 ||
m_iconDim[channel].y == 0)
return false;
nbx = m_totalDim[channel].x / m_iconDim[channel].x;
nby = m_totalDim[channel].y / (m_iconDim[channel].y / 2);
rank = (rank / 32) * 32 + ((rank % 32) / 2) + ((rank % 2) * 16);
if (rank < 0 || rank >= nbx * nby)
return false;
rect.left = (rank % nbx) * m_iconDim[channel].x;
rect.top = (rank / nbx) * (m_iconDim[channel].y / 2);
rect.right = rect.left + m_iconDim[channel].x;
rect.bottom = rect.top + (m_iconDim[channel].y / 2);
return !BltFast (chDst, channel, pos, rect);
}
// Dessine une partie d'image rectangulaire.
bool CPixmap::DrawIconPart (Sint32 chDst, Sint32 channel, Sint32 rank,
POINT pos,
Sint32 startY, Sint32 endY, bool bMask)
{
Sint32 nbx, nby;
RECT rect;
if (channel < 0 || channel >= MAXIMAGE)
return false;
if (m_lpSDLTexture[channel] == nullptr)
return false;
if (m_iconDim[channel].x == 0 ||
m_iconDim[channel].y == 0)
return false;
nbx = m_totalDim[channel].x / m_iconDim[channel].x;
nby = m_totalDim[channel].y / m_iconDim[channel].y;
if (rank < 0 || rank >= nbx * nby)
return false;
rect.left = (rank % nbx) * m_iconDim[channel].x;
rect.top = (rank / nbx) * m_iconDim[channel].y;
rect.right = rect.left + m_iconDim[channel].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, Sint32 channel, POINT dest, RECT rect,
bool bMask)
{
if (channel < 0 || channel >= MAXIMAGE)
return false;
if (m_lpSDLTexture[channel] == nullptr)
return false;
return !BltFast (chDst, channel, dest, rect);
}
// Dessine une partie d'image rectangulaire.
bool CPixmap::DrawImage (Sint32 chDst, Sint32 channel, RECT rect)
{
POINT dst;
Sint32 res;
if (channel < 0 || channel >= MAXIMAGE)
return false;
if (m_lpSDLTexture[channel] == nullptr)
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 (Sint32 channelMask, Sint32 rankMask,
Sint32 channel, Sint32 rankSrc, Sint32 rankDst)
{
Sint32 nbx, nby;
POINT posDst;
RECT rect;
Sint32 res;
if (channel < 0 || channel >= MAXIMAGE)
return false;
if (m_lpSDLTexture[channel] == nullptr)
return false;
if (m_iconDim[channel].x == 0 ||
m_iconDim[channel].y == 0)
return false;
nbx = m_totalDim[channel].x / m_iconDim[channel].x;
nby = m_totalDim[channel].y / m_iconDim[channel].y;
if (rankSrc < 0 || rankSrc >= nbx * nby)
return false;
if (rankDst < 0 || rankDst >= nbx * nby)
return false;
rect.left = (rankSrc % nbx) * m_iconDim[channel].x;
rect.top = (rankSrc / nbx) * m_iconDim[channel].y;
rect.right = rect.left + m_iconDim[channel].x;
rect.bottom = rect.top + m_iconDim[channel].y;
posDst.x = (rankDst % nbx) * m_iconDim[channel].x;
posDst.y = (rankDst / nbx) * m_iconDim[channel].y;
res = BltFast (m_lpSDLTexture[channel], channel, posDst, rect);
if (res)
return false;
if (m_iconDim[channelMask].x == 0 ||
m_iconDim[channelMask].y == 0)
return false;
nbx = m_totalDim[channelMask].x / m_iconDim[channelMask].x;
nby = m_totalDim[channelMask].y / m_iconDim[channelMask].y;
if (rankMask < 0 || rankMask >= nbx * nby)
return false;
rect.left = (rankMask % nbx) * m_iconDim[channelMask].x;
rect.top = (rankMask / nbx) * m_iconDim[channelMask].y;
rect.right = rect.left + m_iconDim[channelMask].x;
rect.bottom = rect.top + m_iconDim[channelMask].y;
res = BltFast (m_lpSDLTexture[channel], channelMask, posDst, rect);
return !res;
}
// Affiche le pixmap � l'�cran.
// Retourne false en cas d'erreur.
bool CPixmap::Display()
{
m_bBackDisplayed = true;
SDL_RenderPresent (g_renderer);
return true;
}
// Change le lutin de la souris.
void CPixmap::SetMouseSprite (Sint32 sprite, bool bDemoPlay)
{
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;
nbx = m_totalDim[CHBLUPI].x / m_iconDim[CHBLUPI].x;
rcRect.left = (rank % nbx) * m_iconDim[CHBLUPI].x;
rcRect.top = (rank / nbx) * m_iconDim[CHBLUPI].y;
rcRect.right = rcRect.left + m_iconDim[CHBLUPI].x;
rcRect.bottom = rcRect.top + m_iconDim[CHBLUPI].y;
return rcRect;
}
SDL_Point CPixmap::GetCursorHotSpot (Sint32 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 (Sint32 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;
}
Sint32 nbx = m_totalDim[CHBLUPI].x / m_iconDim[CHBLUPI].x;
rcRect.x = (rank % nbx) * m_iconDim[CHBLUPI].x;
rcRect.y = (rank / nbx) * m_iconDim[CHBLUPI].y;
rcRect.w = m_iconDim[CHBLUPI].x;
rcRect.h = m_iconDim[CHBLUPI].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
rmask = 0x000000ff;
gmask = 0x0000ff00;
bmask = 0x00ff0000;
amask = 0xff000000;
#endif
for (Sint32 sprite = SPRITE_BEGIN; sprite <= SPRITE_END; ++sprite)
{
SDL_Point hotspot = this->GetCursorHotSpot (sprite);
SDL_Rect rect = this->GetCursorRect (sprite);
SDL_Surface *surface = SDL_CreateRGBSurface (0, rect.w, rect.h, 32, rmask,
gmask, bmask, amask);
SDL_BlitSurface (m_lpSDLBlupi, &rect, surface, nullptr);
// FIXME: change cursor first value to 0
m_lpSDLCursors[sprite - 1] = SDL_CreateColorCursor (surface, hotspot.x,
hotspot.y);
}
}
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];
}