#pragma once #include #include #include #if SDL_VERSION_ATLEAST(2, 0, 0) #include #include #else #include "utils/sdl2_to_1_2_backports.h" #include #endif #include "engine/point.hpp" #include "utils/sdl_geometry.h" #include "utils/sdl_wrap.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; 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(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(y); subregion.h = static_cast(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; }; class OwnedSurface : public Surface { SDLSurfaceUniquePtr pinnedSurface; public: explicit OwnedSurface(SDLSurfaceUniquePtr surface) : Surface(surface.get()) , pinnedSurface(std::move(surface)) { } OwnedSurface(int width, int height) : OwnedSurface(SDLWrap::CreateRGBSurfaceWithFormat(0, width, height, 8, SDL_PIXELFORMAT_INDEX8)) { } explicit OwnedSurface(Size size) : OwnedSurface(size.width, size.height) { } }; } // namespace devilution