60 changed files with 474 additions and 458 deletions
@ -0,0 +1,53 @@
|
||||
#include "engine/surface.hpp" |
||||
|
||||
#include <cstring> |
||||
|
||||
namespace devilution { |
||||
|
||||
namespace { |
||||
|
||||
template <bool SkipColorIndexZero> |
||||
void SurfaceBlit(const Surface &src, SDL_Rect srcRect, const Surface &dst, Point dstPosition) |
||||
{ |
||||
// We do not use `SDL_BlitSurface` here because the palettes may be different objects
|
||||
// and SDL would attempt to map them.
|
||||
|
||||
dst.Clip(&srcRect, &dstPosition); |
||||
if (srcRect.w <= 0 || srcRect.h <= 0) |
||||
return; |
||||
|
||||
const std::uint8_t *srcBuf = src.at(srcRect.x, srcRect.y); |
||||
const auto srcPitch = src.pitch(); |
||||
std::uint8_t *dstBuf = &dst[dstPosition]; |
||||
const auto dstPitch = dst.pitch(); |
||||
|
||||
for (unsigned h = srcRect.h; h != 0; --h) { |
||||
if (SkipColorIndexZero) { |
||||
for (unsigned w = srcRect.w; w != 0; --w) { |
||||
if (*srcBuf != 0) |
||||
*dstBuf = *srcBuf; |
||||
++srcBuf, ++dstBuf; |
||||
} |
||||
srcBuf += srcPitch - srcRect.w; |
||||
dstBuf += dstPitch - srcRect.w; |
||||
} else { |
||||
std::memcpy(dstBuf, srcBuf, srcRect.w); |
||||
srcBuf += srcPitch; |
||||
dstBuf += dstPitch; |
||||
} |
||||
} |
||||
} |
||||
|
||||
} // namespace
|
||||
|
||||
void Surface::BlitFrom(const Surface &src, SDL_Rect srcRect, Point targetPosition) const |
||||
{ |
||||
SurfaceBlit</*SkipColorIndexZero=*/false>(src, srcRect, *this, targetPosition); |
||||
} |
||||
|
||||
void Surface::BlitFromSkipColorIndexZero(const Surface &src, SDL_Rect srcRect, Point targetPosition) const |
||||
{ |
||||
SurfaceBlit</*SkipColorIndexZero=*/true>(src, srcRect, *this, targetPosition); |
||||
} |
||||
|
||||
} // namespace devilution
|
||||
@ -0,0 +1,175 @@
|
||||
#pragma once |
||||
|
||||
#include <cstdint> |
||||
#include <cstddef> |
||||
|
||||
#include <SDL_version.h> |
||||
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0) |
||||
#include <SDL_rect.h> |
||||
#include <SDL_surface.h> |
||||
#else |
||||
#include <SDL_video.h> |
||||
#include "utils/sdl2_to_1_2_backports.h" |
||||
#endif |
||||
|
||||
#include "engine/point.hpp" |
||||
#include "utils/sdl_geometry.h" |
||||
|
||||
namespace devilution { |
||||
|
||||
/**
|
||||
* @brief 8-bit surface. |
||||
*/ |
||||
struct Surface { |
||||
SDL_Surface *surface; |
||||
SDL_Rect region; |
||||
|
||||
Surface() |
||||
: surface(nullptr) |
||||
, region(SDL_Rect { 0, 0, 0, 0 }) |
||||
{ |
||||
} |
||||
|
||||
explicit Surface(SDL_Surface *surface) |
||||
: surface(surface) |
||||
, region(MakeSdlRect(0, 0, surface->w, surface->h)) |
||||
{ |
||||
} |
||||
|
||||
Surface(SDL_Surface *surface, SDL_Rect region) |
||||
: surface(surface) |
||||
, region(region) |
||||
{ |
||||
} |
||||
|
||||
Surface(const Surface &other) = default; |
||||
Surface& operator=(const Surface &other) = default; |
||||
|
||||
/**
|
||||
* @brief Allocate a buffer that owns its underlying data. |
||||
*/ |
||||
static Surface Alloc(std::size_t width, std::size_t height) |
||||
{ |
||||
return Surface(SDL_CreateRGBSurfaceWithFormat(0, width, height, 8, SDL_PIXELFORMAT_INDEX8)); |
||||
} |
||||
|
||||
/**
|
||||
* @brief Free the underlying data. |
||||
* |
||||
* Only use this if the buffer owns its data. |
||||
*/ |
||||
void Free() |
||||
{ |
||||
SDL_FreeSurface(this->surface); |
||||
this->surface = nullptr; |
||||
} |
||||
|
||||
int w() const |
||||
{ |
||||
return region.w; |
||||
} |
||||
int h() const |
||||
{ |
||||
return region.h; |
||||
} |
||||
|
||||
std::uint8_t &operator[](Point p) const |
||||
{ |
||||
return *at(p.x, p.y); |
||||
} |
||||
|
||||
std::uint8_t *at(int x, int y) const |
||||
{ |
||||
return static_cast<uint8_t *>(surface->pixels) + region.x + x + surface->pitch * (region.y + y); |
||||
} |
||||
|
||||
std::uint8_t *begin() const |
||||
{ |
||||
return at(0, 0); |
||||
} |
||||
std::uint8_t *end() const |
||||
{ |
||||
return at(0, region.h); |
||||
} |
||||
|
||||
/**
|
||||
* @brief Set the value of a single pixel if it is in bounds. |
||||
* @param point Target buffer coordinate |
||||
* @param col Color index from current palette |
||||
*/ |
||||
void SetPixel(Point position, std::uint8_t col) const |
||||
{ |
||||
if (InBounds(position)) |
||||
(*this)[position] = col; |
||||
} |
||||
|
||||
/**
|
||||
* @brief Line width of the raw underlying byte buffer. |
||||
* May be wider than its logical width (for power-of-2 alignment). |
||||
*/ |
||||
int pitch() const |
||||
{ |
||||
return surface->pitch; |
||||
} |
||||
|
||||
bool InBounds(Point position) const |
||||
{ |
||||
return position.x >= 0 && position.y >= 0 && position.x < region.w && position.y < region.h; |
||||
} |
||||
|
||||
/**
|
||||
* @brief Returns a subregion of the given buffer. |
||||
*/ |
||||
Surface subregion(int x, int y, int w, int h) const |
||||
{ |
||||
return Surface(surface, MakeSdlRect(region.x + x, region.y + y, w, h)); |
||||
} |
||||
|
||||
/**
|
||||
* @brief Returns a buffer that starts at `y` of height `h`. |
||||
*/ |
||||
Surface subregionY(int y, int h) const |
||||
{ |
||||
SDL_Rect subregion = region; |
||||
subregion.y += static_cast<decltype(SDL_Rect{}.y)>(y); |
||||
subregion.h = static_cast<decltype(SDL_Rect{}.h)>(h); |
||||
return Surface(surface, subregion); |
||||
} |
||||
|
||||
/**
|
||||
* @brief Clips srcRect and targetPosition to this output buffer. |
||||
*/ |
||||
void Clip(SDL_Rect *srcRect, Point *targetPosition) const |
||||
{ |
||||
if (targetPosition->x < 0) { |
||||
srcRect->x -= targetPosition->x; |
||||
srcRect->w += targetPosition->x; |
||||
targetPosition->x = 0; |
||||
} |
||||
if (targetPosition->y < 0) { |
||||
srcRect->y -= targetPosition->y; |
||||
srcRect->h += targetPosition->y; |
||||
targetPosition->y = 0; |
||||
} |
||||
if (targetPosition->x + srcRect->w > region.w) { |
||||
srcRect->w = region.w - targetPosition->x; |
||||
} |
||||
if (targetPosition->y + srcRect->h > region.h) { |
||||
srcRect->h = region.h - targetPosition->y; |
||||
} |
||||
} |
||||
|
||||
/**
|
||||
* @brief Copies the `srcRect` portion of the given buffer to this buffer at `targetPosition`. |
||||
*/ |
||||
void BlitFrom(const Surface &src, SDL_Rect srcRect, Point targetPosition) const; |
||||
|
||||
/**
|
||||
* @brief Copies the `srcRect` portion of the given buffer to this buffer at `targetPosition`. |
||||
* Source pixels with index 0 are not copied. |
||||
*/ |
||||
void BlitFromSkipColorIndexZero(const Surface &src, SDL_Rect srcRect, Point targetPosition) const; |
||||
}; |
||||
|
||||
} // namespace devilution
|
||||
Loading…
Reference in new issue