Browse Source
The format is almost identical to CL2, except it uses the frame header to store frame width and height instead of 5 32-line offsets. This means we always have access to frame dimensions, so we can use it as an on-disk format for our graphics as well. Additionally, we may be able to optimize the rendering even more in the future now that we have guaranteed knowledge of frame dimensions.pull/5182/head
92 changed files with 1815 additions and 1572 deletions
@ -1,68 +0,0 @@
|
||||
#pragma once |
||||
|
||||
#include <cstdint> |
||||
#include <cstring> |
||||
|
||||
#include <SDL_endian.h> |
||||
|
||||
#include "utils/endian.hpp" |
||||
#include "utils/stdcompat/cstddef.hpp" |
||||
|
||||
namespace devilution { |
||||
|
||||
/*
|
||||
* When a CEL is a multi-direction animation, it begins with 8 offsets to the start of |
||||
* the animation for each direction. |
||||
* |
||||
* Fills the `out` array with a pointer to each direction. |
||||
*/ |
||||
inline void CelGetDirectionFrames(const byte *data, const byte **out) |
||||
{ |
||||
for (size_t i = 0; i < 8; ++i) { |
||||
out[i] = &data[LoadLE32(&data[i * 4])]; |
||||
} |
||||
} |
||||
|
||||
inline void CelGetDirectionFrames(byte *data, byte **out) |
||||
{ |
||||
for (size_t i = 0; i < 8; ++i) { |
||||
out[i] = &data[LoadLE32(&data[i * 4])]; |
||||
} |
||||
} |
||||
|
||||
/**
|
||||
* Returns the pointer to the start of the frame data (often a header) and sets `frameSize` to the size of the data in bytes. |
||||
*/ |
||||
inline byte *CelGetFrame(byte *data, int frame, int *frameSize) |
||||
{ |
||||
const std::uint32_t begin = LoadLE32(&data[(frame + 1) * sizeof(std::uint32_t)]); |
||||
*frameSize = static_cast<int>(LoadLE32(&data[(frame + 2) * sizeof(std::uint32_t)]) - begin); |
||||
return &data[begin]; |
||||
} |
||||
|
||||
/**
|
||||
* Returns the pointer to the start of the frame data (often a header) and sets `frameSize` to the size of the data in bytes. |
||||
*/ |
||||
inline const byte *CelGetFrame(const byte *data, int frame, int *frameSize) |
||||
{ |
||||
const std::uint32_t begin = LoadLE32(&data[(frame + 1) * sizeof(std::uint32_t)]); |
||||
*frameSize = static_cast<int>(LoadLE32(&data[(frame + 2) * sizeof(std::uint32_t)]) - begin); |
||||
return &data[begin]; |
||||
} |
||||
|
||||
/**
|
||||
* Returns the pointer to the start of the frame's pixel data and sets `frameSize` to the size of the data in bytes. |
||||
*/ |
||||
inline const byte *CelGetFrameClipped(const byte *data, int frame, int *frameSize) |
||||
{ |
||||
const byte *frameData = CelGetFrame(data, frame, frameSize); |
||||
|
||||
// The frame begins with a header that consists of 5 little-endian 16-bit integers
|
||||
// pointing to the start of the pixel data for rows 0, 32, 64, 96, and 128.
|
||||
const std::uint16_t begin = LoadLE16(frameData); |
||||
|
||||
*frameSize -= begin; |
||||
return &frameData[begin]; |
||||
} |
||||
|
||||
} // namespace devilution
|
||||
@ -1,344 +0,0 @@
|
||||
#pragma once |
||||
|
||||
#include <memory> |
||||
#include <utility> |
||||
|
||||
#include "appfat.h" |
||||
#include "engine/cel_header.hpp" |
||||
#include "utils/pointer_value_union.hpp" |
||||
#include "utils/stdcompat/cstddef.hpp" |
||||
#include "utils/stdcompat/optional.hpp" |
||||
|
||||
namespace devilution { |
||||
|
||||
class OwnedCelSprite; |
||||
class OptionalCelSprite; |
||||
|
||||
/**
|
||||
* Stores a CEL or CL2 sprite and its width(s). |
||||
* Does not own the data. |
||||
*/ |
||||
class CelSprite { |
||||
public: |
||||
CelSprite(const byte *data, uint16_t width) |
||||
: data_ptr_(data) |
||||
, width_(width) |
||||
{ |
||||
} |
||||
|
||||
CelSprite(const byte *data, const uint16_t *widths) |
||||
: data_ptr_(data) |
||||
, width_(widths) |
||||
{ |
||||
} |
||||
|
||||
CelSprite(const byte *data, PointerOrValue<uint16_t> widths) |
||||
: data_ptr_(data) |
||||
, width_(widths) |
||||
{ |
||||
} |
||||
|
||||
explicit CelSprite(const OwnedCelSprite &owned); |
||||
|
||||
CelSprite(const CelSprite &) = default; |
||||
CelSprite(CelSprite &&) noexcept = default; |
||||
CelSprite &operator=(const CelSprite &) = default; |
||||
CelSprite &operator=(CelSprite &&) noexcept = default; |
||||
|
||||
[[nodiscard]] const byte *Data() const |
||||
{ |
||||
return data_ptr_; |
||||
} |
||||
|
||||
[[nodiscard]] uint16_t Width(std::size_t frame = 0) const |
||||
{ |
||||
return width_.HoldsPointer() ? width_.AsPointer()[frame] : width_.AsValue(); |
||||
} |
||||
|
||||
[[nodiscard]] PointerOrValue<uint16_t> widthOrWidths() const |
||||
{ |
||||
return width_; |
||||
} |
||||
|
||||
[[nodiscard]] bool operator==(CelSprite other) const |
||||
{ |
||||
return data_ptr_ == other.data_ptr_; |
||||
} |
||||
[[nodiscard]] bool operator!=(CelSprite other) const |
||||
{ |
||||
return data_ptr_ != other.data_ptr_; |
||||
} |
||||
|
||||
private: |
||||
// for OptionalCelSprite
|
||||
CelSprite() |
||||
: data_ptr_(nullptr) |
||||
, width_(nullptr) |
||||
{ |
||||
} |
||||
|
||||
const byte *data_ptr_; |
||||
PointerOrValue<uint16_t> width_; |
||||
|
||||
friend class OptionalCelSprite; |
||||
}; |
||||
|
||||
/**
|
||||
* @brief Equivalent to `std::optional<CelSprite>` but smaller. |
||||
*/ |
||||
class OptionalCelSprite { |
||||
public: |
||||
OptionalCelSprite() = default; |
||||
|
||||
OptionalCelSprite(CelSprite sprite) |
||||
: sprite_(sprite) |
||||
{ |
||||
} |
||||
|
||||
explicit OptionalCelSprite(const OwnedCelSprite &owned); |
||||
|
||||
OptionalCelSprite(std::nullopt_t) |
||||
: OptionalCelSprite() |
||||
{ |
||||
} |
||||
|
||||
template <typename... Args> |
||||
CelSprite &emplace(Args &&...args) |
||||
{ |
||||
sprite_ = CelSprite(std::forward<Args>(args)...); |
||||
return sprite_; |
||||
} |
||||
|
||||
OptionalCelSprite &operator=(CelSprite sprite) |
||||
{ |
||||
sprite_ = sprite; |
||||
return *this; |
||||
} |
||||
|
||||
OptionalCelSprite &operator=(std::nullopt_t) |
||||
{ |
||||
sprite_ = {}; |
||||
return *this; |
||||
} |
||||
|
||||
CelSprite operator*() const |
||||
{ |
||||
assert(sprite_.data_ptr_ != nullptr); |
||||
return sprite_; |
||||
} |
||||
|
||||
CelSprite *operator->() |
||||
{ |
||||
assert(sprite_.data_ptr_ != nullptr); |
||||
return &sprite_; |
||||
} |
||||
|
||||
const CelSprite *operator->() const |
||||
{ |
||||
assert(sprite_.data_ptr_ != nullptr); |
||||
return &sprite_; |
||||
} |
||||
|
||||
operator bool() const |
||||
{ |
||||
return sprite_.data_ptr_ != nullptr; |
||||
} |
||||
|
||||
private: |
||||
CelSprite sprite_; |
||||
}; |
||||
|
||||
class OptionalOwnedCelSprite; |
||||
|
||||
/**
|
||||
* Stores a CEL or CL2 sprite and its width(s). |
||||
* Owns the data. |
||||
*/ |
||||
class OwnedCelSprite { |
||||
public: |
||||
OwnedCelSprite(std::unique_ptr<byte[]> data, uint16_t width) |
||||
: data_(std::move(data)) |
||||
, width_(width) |
||||
{ |
||||
} |
||||
|
||||
OwnedCelSprite(std::unique_ptr<byte[]> data, const uint16_t *widths) |
||||
: data_(std::move(data)) |
||||
, width_(widths) |
||||
{ |
||||
} |
||||
|
||||
OwnedCelSprite(std::unique_ptr<byte[]> data, PointerOrValue<uint16_t> widths) |
||||
: data_(std::move(data)) |
||||
, width_(widths) |
||||
{ |
||||
} |
||||
|
||||
OwnedCelSprite(OwnedCelSprite &&) noexcept = default; |
||||
OwnedCelSprite &operator=(OwnedCelSprite &&) noexcept = default; |
||||
|
||||
[[nodiscard]] byte *MutableData() |
||||
{ |
||||
return data_.get(); |
||||
} |
||||
|
||||
std::unique_ptr<byte[]> data() && |
||||
{ |
||||
return std::move(data_); |
||||
} |
||||
|
||||
private: |
||||
// for OptionalOwnedCelSprite.
|
||||
OwnedCelSprite() |
||||
: data_(nullptr) |
||||
, width_(nullptr) |
||||
{ |
||||
} |
||||
|
||||
std::unique_ptr<byte[]> data_; |
||||
PointerOrValue<uint16_t> width_; |
||||
|
||||
friend class CelSprite; |
||||
friend class OptionalOwnedCelSprite; |
||||
}; |
||||
|
||||
/**
|
||||
* @brief Equivalent to `std::optional<OwnedCelSprite>` but smaller. |
||||
*/ |
||||
class OptionalOwnedCelSprite { |
||||
public: |
||||
OptionalOwnedCelSprite() = default; |
||||
|
||||
OptionalOwnedCelSprite(OwnedCelSprite &&sprite) |
||||
: sprite_(std::move(sprite)) |
||||
{ |
||||
} |
||||
|
||||
OptionalOwnedCelSprite(std::nullopt_t) |
||||
: OptionalOwnedCelSprite() |
||||
{ |
||||
} |
||||
|
||||
template <typename... Args> |
||||
OwnedCelSprite &emplace(Args &&...args) |
||||
{ |
||||
sprite_ = OwnedCelSprite(std::forward<Args>(args)...); |
||||
return sprite_; |
||||
} |
||||
|
||||
OptionalOwnedCelSprite &operator=(OwnedCelSprite &&sprite) |
||||
{ |
||||
sprite_ = std::move(sprite); |
||||
return *this; |
||||
} |
||||
|
||||
OptionalOwnedCelSprite &operator=(std::nullopt_t) |
||||
{ |
||||
sprite_ = {}; |
||||
return *this; |
||||
} |
||||
|
||||
OwnedCelSprite &operator*() |
||||
{ |
||||
assert(sprite_.data_ != nullptr); |
||||
return sprite_; |
||||
} |
||||
|
||||
const OwnedCelSprite &operator*() const |
||||
{ |
||||
assert(sprite_.data_ != nullptr); |
||||
return sprite_; |
||||
} |
||||
|
||||
OwnedCelSprite *operator->() |
||||
{ |
||||
assert(sprite_.data_ != nullptr); |
||||
return &sprite_; |
||||
} |
||||
|
||||
const OwnedCelSprite *operator->() const |
||||
{ |
||||
assert(sprite_.data_ != nullptr); |
||||
return &sprite_; |
||||
} |
||||
|
||||
operator bool() const |
||||
{ |
||||
return sprite_.data_ != nullptr; |
||||
} |
||||
|
||||
private: |
||||
OwnedCelSprite sprite_; |
||||
}; |
||||
|
||||
inline CelSprite::CelSprite(const OwnedCelSprite &owned) |
||||
: CelSprite(owned.data_.get(), owned.width_) |
||||
{ |
||||
} |
||||
|
||||
inline OptionalCelSprite::OptionalCelSprite(const OwnedCelSprite &owned) |
||||
{ |
||||
sprite_ = CelSprite { owned }; |
||||
} |
||||
|
||||
struct CelSpriteWithFrameHeight { |
||||
CelSprite sprite; |
||||
unsigned frameHeight; |
||||
}; |
||||
|
||||
struct CelFrameWithHeight { |
||||
CelSprite sprite; |
||||
uint16_t frameHeight; |
||||
uint16_t frame; |
||||
|
||||
[[nodiscard]] uint16_t width() const |
||||
{ |
||||
return sprite.Width(frame); |
||||
} |
||||
[[nodiscard]] uint16_t height() const |
||||
{ |
||||
return frameHeight; |
||||
} |
||||
}; |
||||
|
||||
struct OwnedCelSpriteWithFrameHeight { |
||||
OwnedCelSprite ownedSprite; |
||||
uint16_t frameHeight; |
||||
|
||||
[[nodiscard]] CelFrameWithHeight sprite() const |
||||
{ |
||||
return { CelSprite { ownedSprite }, frameHeight, 0 }; |
||||
} |
||||
}; |
||||
|
||||
struct CelSpriteSheetWithFrameHeight { |
||||
CelSprite sheet; |
||||
uint16_t frameHeight; |
||||
|
||||
[[nodiscard]] unsigned numFrames() const |
||||
{ |
||||
return LoadLE32(sheet.Data()); |
||||
} |
||||
|
||||
[[nodiscard]] CelFrameWithHeight sprite(uint16_t frame) const |
||||
{ |
||||
return { CelSprite { sheet }, frameHeight, frame }; |
||||
} |
||||
}; |
||||
|
||||
struct OwnedCelSpriteSheetWithFrameHeight { |
||||
OwnedCelSprite ownedSprite; |
||||
uint16_t frameHeight; |
||||
|
||||
[[nodiscard]] CelSpriteSheetWithFrameHeight sheet() const |
||||
{ |
||||
return CelSpriteSheetWithFrameHeight { CelSprite { ownedSprite }, frameHeight }; |
||||
} |
||||
|
||||
[[nodiscard]] CelFrameWithHeight sprite(uint16_t frame) const |
||||
{ |
||||
return CelFrameWithHeight { CelSprite { ownedSprite }, frameHeight, frame }; |
||||
} |
||||
}; |
||||
|
||||
} // namespace devilution
|
||||
@ -0,0 +1,599 @@
|
||||
#pragma once |
||||
/**
|
||||
* @file clx_sprite.hpp |
||||
* |
||||
* @brief CLX format sprites. |
||||
* |
||||
* CLX is a format used for DevilutionX graphics at runtime. |
||||
* |
||||
* It is identical to CL2, except we use the frame header to store the frame's width and height. |
||||
* |
||||
* CLX frame header (10 bytes, same as CL2): |
||||
* |
||||
* Bytes | Type | Value |
||||
* :-----:|:--------:|------------------------------------ |
||||
* 0..2 | uint16_t | offset to data start (same as CL2) |
||||
* 2..4 | uint16_t | width |
||||
* 4..6 | uint16_t | height |
||||
* 6..10 | - | unused |
||||
* |
||||
* The CLX format is otherwise identical to CL2. |
||||
* |
||||
* Since the header is identical to CL2, CL2 can be converted to CLX without reallocation. |
||||
* |
||||
* CL2 reference: https://github.com/savagesteel/d1-file-formats/blob/master/PC-Mac/CL2.md#2-file-structure
|
||||
*/ |
||||
|
||||
#include <cstddef> |
||||
#include <cstdint> |
||||
|
||||
#include <iterator> |
||||
#include <memory> |
||||
|
||||
#include "appfat.h" |
||||
#include "utils/endian.hpp" |
||||
#include "utils/intrusive_optional.hpp" |
||||
|
||||
namespace devilution { |
||||
|
||||
class OptionalClxSprite; |
||||
|
||||
/**
|
||||
* @brief A single CLX sprite. |
||||
*/ |
||||
class ClxSprite { |
||||
static constexpr uint32_t HeaderSize = 10; |
||||
|
||||
public: |
||||
explicit constexpr ClxSprite(const uint8_t *data, uint32_t dataSize) |
||||
: data_(data) |
||||
, pixel_data_size_(dataSize - HeaderSize) |
||||
{ |
||||
assert(data != nullptr); |
||||
} |
||||
|
||||
[[nodiscard]] constexpr uint16_t width() const |
||||
{ |
||||
return LoadLE16(&data_[2]); |
||||
} |
||||
|
||||
[[nodiscard]] constexpr uint16_t height() const |
||||
{ |
||||
return LoadLE16(&data_[4]); |
||||
} |
||||
|
||||
/**
|
||||
* @brief The raw pixel data (CL2 frame data). |
||||
* |
||||
* Format: https://github.com/savagesteel/d1-file-formats/blob/master/PC-Mac/CL2.md#42-cl2-frame-data
|
||||
*/ |
||||
[[nodiscard]] constexpr const uint8_t *pixelData() const |
||||
{ |
||||
return &data_[HeaderSize]; |
||||
} |
||||
|
||||
[[nodiscard]] constexpr uint32_t pixelDataSize() const |
||||
{ |
||||
return pixel_data_size_; |
||||
} |
||||
|
||||
constexpr bool operator==(const ClxSprite &other) const |
||||
{ |
||||
return data_ == other.data_; |
||||
} |
||||
|
||||
constexpr bool operator!=(const ClxSprite &other) const |
||||
{ |
||||
return !(*this == other); |
||||
} |
||||
|
||||
private: |
||||
// For OptionalClxSprite.
|
||||
constexpr ClxSprite() |
||||
: data_(nullptr) |
||||
, pixel_data_size_(0) |
||||
{ |
||||
} |
||||
|
||||
const uint8_t *data_; |
||||
uint32_t pixel_data_size_; |
||||
|
||||
friend class OptionalClxSprite; |
||||
}; |
||||
|
||||
class OwnedClxSpriteList; |
||||
class OptionalClxSpriteList; |
||||
class ClxSpriteListIterator; |
||||
|
||||
/**
|
||||
* @brief A list of `ClxSprite`s. |
||||
*/ |
||||
class ClxSpriteList { |
||||
public: |
||||
explicit constexpr ClxSpriteList(const uint8_t *data) |
||||
: data_(data) |
||||
{ |
||||
assert(data != nullptr); |
||||
} |
||||
|
||||
ClxSpriteList(const OwnedClxSpriteList &owned); |
||||
|
||||
[[nodiscard]] constexpr uint32_t numSprites() const |
||||
{ |
||||
return LoadLE32(data_); |
||||
} |
||||
|
||||
[[nodiscard]] constexpr ClxSprite operator[](size_t spriteIndex) const |
||||
{ |
||||
assert(spriteIndex < numSprites()); |
||||
const uint32_t begin = spriteOffset(spriteIndex); |
||||
const uint32_t end = spriteOffset(spriteIndex + 1); |
||||
return ClxSprite { &data_[begin], end - begin }; |
||||
} |
||||
|
||||
[[nodiscard]] constexpr uint32_t spriteOffset(size_t spriteIndex) const |
||||
{ |
||||
return LoadLE32(&data_[4 + spriteIndex * 4]); |
||||
} |
||||
|
||||
/** @brief The offset to the next sprite sheet, or file size if this is the last sprite sheet. */ |
||||
[[nodiscard]] constexpr uint32_t nextSpriteSheetOffsetOrFileSize() const |
||||
{ |
||||
return LoadLE32(&data_[4 + numSprites()]); |
||||
} |
||||
|
||||
[[nodiscard]] constexpr const uint8_t *data() const |
||||
{ |
||||
return data_; |
||||
} |
||||
|
||||
[[nodiscard]] constexpr ClxSpriteListIterator begin() const; |
||||
[[nodiscard]] constexpr ClxSpriteListIterator end() const; |
||||
|
||||
private: |
||||
// For OptionalClxSpriteList.
|
||||
constexpr ClxSpriteList() |
||||
: data_(nullptr) |
||||
{ |
||||
} |
||||
|
||||
const uint8_t *data_; |
||||
|
||||
friend class OptionalClxSpriteList; |
||||
}; |
||||
|
||||
class ClxSpriteListIterator { |
||||
public: |
||||
using iterator_category = std::forward_iterator_tag; |
||||
using difference_type = int; |
||||
using value_type = ClxSprite; |
||||
using pointer = void; |
||||
using reference = value_type &; |
||||
|
||||
constexpr ClxSpriteListIterator(ClxSpriteList list, size_t index) |
||||
: list_(list) |
||||
, index_(index) |
||||
{ |
||||
} |
||||
|
||||
constexpr ClxSprite operator*() |
||||
{ |
||||
return list_[index_]; |
||||
} |
||||
|
||||
constexpr ClxSpriteListIterator &operator++() |
||||
{ |
||||
++index_; |
||||
return *this; |
||||
} |
||||
|
||||
constexpr ClxSpriteListIterator operator++(int) |
||||
{ |
||||
auto copy = *this; |
||||
++(*this); |
||||
return copy; |
||||
} |
||||
|
||||
constexpr bool operator==(const ClxSpriteListIterator &other) const |
||||
{ |
||||
return index_ == other.index_; |
||||
} |
||||
|
||||
constexpr bool operator!=(const ClxSpriteListIterator &other) const |
||||
{ |
||||
return !(*this == other); |
||||
} |
||||
|
||||
private: |
||||
ClxSpriteList list_; |
||||
size_t index_; |
||||
}; |
||||
|
||||
inline constexpr ClxSpriteListIterator ClxSpriteList::begin() const |
||||
{ |
||||
return { *this, 0 }; |
||||
} |
||||
|
||||
inline constexpr ClxSpriteListIterator ClxSpriteList::end() const |
||||
{ |
||||
return { *this, numSprites() }; |
||||
} |
||||
|
||||
class OwnedClxSpriteSheet; |
||||
class OptionalClxSpriteSheet; |
||||
class ClxSpriteSheetIterator; |
||||
|
||||
/**
|
||||
* @brief A sprite sheet is a list of `ClxSpriteList`s. |
||||
*/ |
||||
class ClxSpriteSheet { |
||||
public: |
||||
explicit constexpr ClxSpriteSheet(const uint8_t *data, uint16_t numLists) |
||||
: data_(data) |
||||
, num_lists_(numLists) |
||||
{ |
||||
assert(data != nullptr); |
||||
assert(num_lists_ > 0); |
||||
} |
||||
|
||||
ClxSpriteSheet(const OwnedClxSpriteSheet &owned); |
||||
|
||||
[[nodiscard]] constexpr uint16_t numLists() const |
||||
{ |
||||
return num_lists_; |
||||
} |
||||
|
||||
[[nodiscard]] constexpr ClxSpriteList operator[](size_t sheetIndex) const |
||||
{ |
||||
return ClxSpriteList { &data_[sheetOffset(sheetIndex)] }; |
||||
} |
||||
|
||||
[[nodiscard]] constexpr uint32_t sheetOffset(size_t sheetIndex) const |
||||
{ |
||||
assert(sheetIndex < num_lists_); |
||||
return LoadLE32(&data_[4 * sheetIndex]); |
||||
} |
||||
|
||||
[[nodiscard]] constexpr const uint8_t *data() const |
||||
{ |
||||
return data_; |
||||
} |
||||
|
||||
[[nodiscard]] constexpr ClxSpriteSheetIterator begin() const; |
||||
[[nodiscard]] constexpr ClxSpriteSheetIterator end() const; |
||||
|
||||
private: |
||||
// For OptionalClxSpriteSheet.
|
||||
constexpr ClxSpriteSheet() |
||||
: data_(nullptr) |
||||
, num_lists_(0) |
||||
{ |
||||
} |
||||
|
||||
const uint8_t *data_; |
||||
uint16_t num_lists_; |
||||
|
||||
friend class OptionalClxSpriteSheet; |
||||
}; |
||||
|
||||
class ClxSpriteSheetIterator { |
||||
public: |
||||
using iterator_category = std::forward_iterator_tag; |
||||
using difference_type = int; |
||||
using value_type = ClxSpriteList; |
||||
using pointer = void; |
||||
using reference = value_type &; |
||||
|
||||
constexpr ClxSpriteSheetIterator(ClxSpriteSheet sheet, size_t index) |
||||
: sheet_(sheet) |
||||
, index_(index) |
||||
{ |
||||
} |
||||
|
||||
constexpr ClxSpriteList operator*() |
||||
{ |
||||
return sheet_[index_]; |
||||
} |
||||
|
||||
constexpr ClxSpriteSheetIterator &operator++() |
||||
{ |
||||
++index_; |
||||
return *this; |
||||
} |
||||
|
||||
constexpr ClxSpriteSheetIterator operator++(int) |
||||
{ |
||||
auto copy = *this; |
||||
++(*this); |
||||
return copy; |
||||
} |
||||
|
||||
constexpr bool operator==(const ClxSpriteSheetIterator &other) const |
||||
{ |
||||
return index_ == other.index_; |
||||
} |
||||
|
||||
constexpr bool operator!=(const ClxSpriteSheetIterator &other) const |
||||
{ |
||||
return !(*this == other); |
||||
} |
||||
|
||||
private: |
||||
ClxSpriteSheet sheet_; |
||||
size_t index_; |
||||
}; |
||||
|
||||
inline constexpr ClxSpriteSheetIterator ClxSpriteSheet::begin() const |
||||
{ |
||||
return { *this, 0 }; |
||||
} |
||||
|
||||
inline constexpr ClxSpriteSheetIterator ClxSpriteSheet::end() const |
||||
{ |
||||
return { *this, num_lists_ }; |
||||
} |
||||
|
||||
class OptionalOwnedClxSpriteList; |
||||
class OwnedClxSpriteListOrSheet; |
||||
|
||||
/**
|
||||
* @brief Implicitly convertible to `ClxSpriteList` and owns its data. |
||||
*/ |
||||
class OwnedClxSpriteList { |
||||
public: |
||||
explicit OwnedClxSpriteList(std::unique_ptr<uint8_t[]> &&data) |
||||
: data_(std::move(data)) |
||||
{ |
||||
assert(data_ != nullptr); |
||||
} |
||||
|
||||
OwnedClxSpriteList(OwnedClxSpriteList &&) noexcept = default; |
||||
OwnedClxSpriteList &operator=(OwnedClxSpriteList &&) noexcept = default; |
||||
|
||||
[[nodiscard]] ClxSprite operator[](size_t spriteIndex) const |
||||
{ |
||||
return ClxSpriteList { *this }[spriteIndex]; |
||||
} |
||||
|
||||
private: |
||||
// For OptionalOwnedClxSpriteList.
|
||||
OwnedClxSpriteList() = default; |
||||
|
||||
std::unique_ptr<uint8_t[]> data_; |
||||
|
||||
friend class ClxSpriteList; // for implicit conversion
|
||||
friend class OptionalOwnedClxSpriteList; |
||||
friend class OwnedClxSpriteListOrSheet; |
||||
}; |
||||
|
||||
inline ClxSpriteList::ClxSpriteList(const OwnedClxSpriteList &owned) |
||||
: data_(owned.data_.get()) |
||||
{ |
||||
} |
||||
|
||||
/**
|
||||
* @brief Implicitly convertible to `ClxSpriteSheet` and owns its data. |
||||
*/ |
||||
class OwnedClxSpriteSheet { |
||||
public: |
||||
OwnedClxSpriteSheet(std::unique_ptr<uint8_t[]> &&data, uint16_t numLists) |
||||
: data_(std::move(data)) |
||||
, num_lists_(numLists) |
||||
{ |
||||
assert(data_ != nullptr); |
||||
assert(numLists > 0); |
||||
} |
||||
|
||||
OwnedClxSpriteSheet(OwnedClxSpriteSheet &&) noexcept = default; |
||||
OwnedClxSpriteSheet &operator=(OwnedClxSpriteSheet &&) noexcept = default; |
||||
|
||||
[[nodiscard]] ClxSpriteList operator[](size_t sheetIndex) const |
||||
{ |
||||
return ClxSpriteSheet { *this }[sheetIndex]; |
||||
} |
||||
|
||||
[[nodiscard]] ClxSpriteSheetIterator begin() const |
||||
{ |
||||
return ClxSpriteSheet { *this }.begin(); |
||||
} |
||||
|
||||
[[nodiscard]] ClxSpriteSheetIterator end() const |
||||
{ |
||||
return ClxSpriteSheet { *this }.end(); |
||||
} |
||||
|
||||
private: |
||||
// For OptionalOwnedClxSpriteList.
|
||||
OwnedClxSpriteSheet() |
||||
: data_(nullptr) |
||||
, num_lists_(0) |
||||
{ |
||||
} |
||||
|
||||
std::unique_ptr<uint8_t[]> data_; |
||||
uint16_t num_lists_; |
||||
|
||||
friend class ClxSpriteSheet; // for implicit conversion.
|
||||
friend class OptionalOwnedClxSpriteSheet; |
||||
friend class OwnedClxSpriteListOrSheet; |
||||
}; |
||||
|
||||
inline ClxSpriteSheet::ClxSpriteSheet(const OwnedClxSpriteSheet &owned) |
||||
: data_(owned.data_.get()) |
||||
, num_lists_(owned.num_lists_) |
||||
{ |
||||
} |
||||
|
||||
class OwnedClxSpriteListOrSheet; |
||||
class OptionalClxSpriteListOrSheet; |
||||
|
||||
/**
|
||||
* @brief A CLX sprite list or a sprite sheet (list of lists). |
||||
*/ |
||||
class ClxSpriteListOrSheet { |
||||
public: |
||||
constexpr ClxSpriteListOrSheet(const uint8_t *data, uint16_t numLists) |
||||
: data_(data) |
||||
, num_lists_(numLists) |
||||
{ |
||||
} |
||||
|
||||
ClxSpriteListOrSheet(const OwnedClxSpriteListOrSheet &listOrSheet); |
||||
|
||||
[[nodiscard]] constexpr ClxSpriteList list() const |
||||
{ |
||||
assert(num_lists_ == 0); |
||||
return ClxSpriteList { data_ }; |
||||
} |
||||
|
||||
[[nodiscard]] constexpr ClxSpriteSheet sheet() const |
||||
{ |
||||
assert(num_lists_ != 0); |
||||
return ClxSpriteSheet { data_, num_lists_ }; |
||||
} |
||||
|
||||
[[nodiscard]] constexpr bool isSheet() const |
||||
{ |
||||
return num_lists_ != 0; |
||||
} |
||||
|
||||
private: |
||||
const uint8_t *data_; |
||||
uint16_t num_lists_; |
||||
|
||||
// For OptionalClxSpriteListOrSheet.
|
||||
constexpr ClxSpriteListOrSheet() |
||||
: data_(nullptr) |
||||
, num_lists_(0) |
||||
{ |
||||
} |
||||
|
||||
friend class OptionalClxSpriteListOrSheet; |
||||
}; |
||||
|
||||
class OptionalOwnedClxSpriteListOrSheet; |
||||
|
||||
/**
|
||||
* @brief A CLX sprite list or a sprite sheet (list of lists). |
||||
*/ |
||||
class OwnedClxSpriteListOrSheet { |
||||
public: |
||||
explicit OwnedClxSpriteListOrSheet(std::unique_ptr<uint8_t[]> &&data, uint16_t numLists = 0) |
||||
: data_(std::move(data)) |
||||
, num_lists_(numLists) |
||||
{ |
||||
} |
||||
|
||||
explicit OwnedClxSpriteListOrSheet(OwnedClxSpriteSheet &&sheet) |
||||
: data_(std::move(sheet.data_)) |
||||
, num_lists_(sheet.num_lists_) |
||||
{ |
||||
} |
||||
|
||||
explicit OwnedClxSpriteListOrSheet(OwnedClxSpriteList &&list) |
||||
: data_(std::move(list.data_)) |
||||
, num_lists_(0) |
||||
{ |
||||
} |
||||
|
||||
[[nodiscard]] ClxSpriteList list() const & |
||||
{ |
||||
assert(num_lists_ == 0); |
||||
return ClxSpriteList { data_.get() }; |
||||
} |
||||
|
||||
[[nodiscard]] OwnedClxSpriteList list() && |
||||
{ |
||||
assert(num_lists_ == 0); |
||||
return OwnedClxSpriteList { std::move(data_) }; |
||||
} |
||||
|
||||
[[nodiscard]] ClxSpriteSheet sheet() const & |
||||
{ |
||||
assert(num_lists_ != 0); |
||||
return ClxSpriteSheet { data_.get(), num_lists_ }; |
||||
} |
||||
|
||||
[[nodiscard]] OwnedClxSpriteSheet sheet() && |
||||
{ |
||||
assert(num_lists_ != 0); |
||||
return OwnedClxSpriteSheet { std::move(data_), num_lists_ }; |
||||
} |
||||
|
||||
[[nodiscard]] bool isSheet() const |
||||
{ |
||||
return num_lists_ != 0; |
||||
} |
||||
|
||||
private: |
||||
std::unique_ptr<uint8_t[]> data_; |
||||
uint16_t num_lists_; |
||||
|
||||
// For OptionalOwnedClxSpriteListOrSheet.
|
||||
OwnedClxSpriteListOrSheet() |
||||
: data_(nullptr) |
||||
, num_lists_(0) |
||||
{ |
||||
} |
||||
|
||||
friend class ClxSpriteListOrSheet; |
||||
friend class OptionalOwnedClxSpriteListOrSheet; |
||||
}; |
||||
|
||||
inline ClxSpriteListOrSheet::ClxSpriteListOrSheet(const OwnedClxSpriteListOrSheet &listOrSheet) |
||||
: data_(listOrSheet.data_.get()) |
||||
, num_lists_(listOrSheet.num_lists_) |
||||
{ |
||||
} |
||||
|
||||
/**
|
||||
* @brief Equivalent to `std::optional<ClxSprite>` but smaller. |
||||
*/ |
||||
class OptionalClxSprite { |
||||
DEFINE_CONSTEXPR_INTRUSIVE_OPTIONAL(OptionalClxSprite, ClxSprite, data_, nullptr) |
||||
}; |
||||
|
||||
/**
|
||||
* @brief Equivalent to `std::optional<ClxSpriteList>` but smaller. |
||||
*/ |
||||
class OptionalClxSpriteList { |
||||
DEFINE_CONSTEXPR_INTRUSIVE_OPTIONAL(OptionalClxSpriteList, ClxSpriteList, data_, nullptr) |
||||
}; |
||||
|
||||
/**
|
||||
* @brief Equivalent to `std::optional<ClxSpriteSheet>` but smaller. |
||||
*/ |
||||
class OptionalClxSpriteSheet { |
||||
DEFINE_CONSTEXPR_INTRUSIVE_OPTIONAL(OptionalClxSpriteSheet, ClxSpriteSheet, data_, nullptr) |
||||
}; |
||||
|
||||
/**
|
||||
* @brief Equivalent to `std::optional<ClxSpriteListOrSheet>` but smaller. |
||||
*/ |
||||
class OptionalClxSpriteListOrSheet { |
||||
public: |
||||
DEFINE_INTRUSIVE_OPTIONAL(OptionalClxSpriteListOrSheet, ClxSpriteListOrSheet, data_, nullptr); |
||||
}; |
||||
|
||||
/**
|
||||
* @brief Equivalent to `std::optional<OwnedClxSpriteList>` but smaller. |
||||
*/ |
||||
class OptionalOwnedClxSpriteList { |
||||
public: |
||||
DEFINE_INTRUSIVE_OPTIONAL(OptionalOwnedClxSpriteList, OwnedClxSpriteList, data_, nullptr) |
||||
}; |
||||
|
||||
/**
|
||||
* @brief Equivalent to `std::optional<OwnedClxSpriteSheet>` but smaller. |
||||
*/ |
||||
class OptionalOwnedClxSpriteSheet { |
||||
public: |
||||
DEFINE_INTRUSIVE_OPTIONAL(OptionalOwnedClxSpriteSheet, OwnedClxSpriteSheet, data_, nullptr) |
||||
}; |
||||
|
||||
class OptionalOwnedClxSpriteListOrSheet { |
||||
public: |
||||
DEFINE_INTRUSIVE_OPTIONAL(OptionalOwnedClxSpriteListOrSheet, OwnedClxSpriteListOrSheet, data_, nullptr); |
||||
}; |
||||
|
||||
} // namespace devilution
|
||||
@ -0,0 +1,18 @@
|
||||
#include "engine/load_cl2.hpp" |
||||
|
||||
#include <memory> |
||||
#include <utility> |
||||
|
||||
#include "engine/load_file.hpp" |
||||
#include "utils/cl2_to_clx.hpp" |
||||
|
||||
namespace devilution { |
||||
|
||||
OwnedClxSpriteListOrSheet LoadCl2ListOrSheet(const char *pszName, PointerOrValue<uint16_t> widthOrWidths) |
||||
{ |
||||
size_t size; |
||||
std::unique_ptr<uint8_t[]> data = LoadFileInMem<uint8_t>(pszName, &size); |
||||
return Cl2ToClx(std::move(data), size, widthOrWidths); |
||||
} |
||||
|
||||
} // namespace devilution
|
||||
@ -0,0 +1,63 @@
|
||||
#pragma once |
||||
|
||||
#include <cstdint> |
||||
|
||||
#include <function_ref.hpp> |
||||
|
||||
#include "appfat.h" |
||||
#include "engine/clx_sprite.hpp" |
||||
#include "engine/load_file.hpp" |
||||
#include "utils/cl2_to_clx.hpp" |
||||
#include "utils/endian.hpp" |
||||
#include "utils/pointer_value_union.hpp" |
||||
#include "utils/static_vector.hpp" |
||||
|
||||
namespace devilution { |
||||
|
||||
OwnedClxSpriteListOrSheet LoadCl2ListOrSheet(const char *pszName, PointerOrValue<uint16_t> widthOrWidths); |
||||
|
||||
template <size_t MaxCount> |
||||
OwnedClxSpriteSheet LoadMultipleCl2Sheet(tl::function_ref<const char *(size_t)> filenames, size_t count, uint16_t width) |
||||
{ |
||||
StaticVector<SFile, MaxCount> files; |
||||
StaticVector<size_t, MaxCount> fileSizes; |
||||
const size_t sheetHeaderSize = 4 * count; |
||||
size_t totalSize = sheetHeaderSize; |
||||
for (size_t i = 0; i < count; ++i) { |
||||
const char *filename = filenames(i); |
||||
files.emplace_back(filename); |
||||
const size_t size = files[i].Size(); |
||||
fileSizes.emplace_back(size); |
||||
totalSize += size; |
||||
} |
||||
auto data = std::unique_ptr<uint8_t[]> { new uint8_t[totalSize] }; |
||||
const PointerOrValue<uint16_t> frameWidth { width }; |
||||
size_t accumulatedSize = sheetHeaderSize; |
||||
for (size_t i = 0; i < count; ++i) { |
||||
const size_t size = fileSizes[i]; |
||||
if (!files[i].Read(&data[accumulatedSize], size)) |
||||
app_fatal(StrCat("Read failed: ", SDL_GetError())); |
||||
WriteLE32(&data[i * 4], accumulatedSize); |
||||
[[maybe_unused]] const uint16_t numLists = Cl2ToClx(&data[accumulatedSize], size, frameWidth); |
||||
assert(numLists == 0); |
||||
accumulatedSize += size; |
||||
} |
||||
return OwnedClxSpriteSheet { std::move(data), static_cast<uint16_t>(count) }; |
||||
} |
||||
|
||||
inline OwnedClxSpriteList LoadCl2(const char *pszName, uint16_t width) |
||||
{ |
||||
return LoadCl2ListOrSheet(pszName, PointerOrValue<uint16_t> { width }).list(); |
||||
} |
||||
|
||||
inline OwnedClxSpriteList LoadCl2(const char *pszName, const uint16_t *widths) |
||||
{ |
||||
return LoadCl2ListOrSheet(pszName, PointerOrValue<uint16_t> { widths }).list(); |
||||
} |
||||
|
||||
inline OwnedClxSpriteSheet LoadCl2Sheet(const char *pszName, uint16_t width) |
||||
{ |
||||
return LoadCl2ListOrSheet(pszName, PointerOrValue<uint16_t> { width }).sheet(); |
||||
} |
||||
|
||||
} // namespace devilution
|
||||
@ -1,115 +0,0 @@
|
||||
/**
|
||||
* @file cl2_render.hpp |
||||
* |
||||
* CL2 rendering. |
||||
*/ |
||||
#pragma once |
||||
|
||||
#include <cstdint> |
||||
|
||||
#include <array> |
||||
#include <utility> |
||||
|
||||
#include "engine.h" |
||||
#include "engine/cel_sprite.hpp" |
||||
#include "engine/point.hpp" |
||||
#include "lighting.h" |
||||
|
||||
namespace devilution { |
||||
|
||||
/**
|
||||
* @brief Apply the color swaps to a CL2 sprite |
||||
* @param p CL2 buffer |
||||
* @param ttbl Palette translation table |
||||
* @param numFrames Number of frames in the CL2 file |
||||
*/ |
||||
void Cl2ApplyTrans(byte *p, const std::array<uint8_t, 256> &ttbl, int numFrames); |
||||
|
||||
/**
|
||||
* @brief Blit CL2 sprite, to the back buffer at the given coordianates |
||||
* @param out Output buffer |
||||
* @param position Target buffer coordinate |
||||
* @param cel CL2 buffer |
||||
* @param frame CL2 frame number |
||||
*/ |
||||
void Cl2Draw(const Surface &out, Point position, CelSprite cel, int frame); |
||||
|
||||
/**
|
||||
* @brief Same as Cl2Draw but position.y is the top of the sprite instead of the bottom. |
||||
*/ |
||||
inline void RenderCl2Sprite(const Surface &out, CelFrameWithHeight cel, Point position) |
||||
{ |
||||
Cl2Draw(out, { position.x, position.y + static_cast<int>(cel.frameHeight) - 1 }, cel.sprite, cel.frame); |
||||
} |
||||
|
||||
/**
|
||||
* @brief Blit a solid colder shape one pixel larger than the given sprite shape, to the given buffer at the given coordinates |
||||
* @param col Color index from current palette |
||||
* @param out Output buffer |
||||
* @param position Target buffer coordinate |
||||
* @param cel CL2 buffer |
||||
* @param frame CL2 frame number |
||||
*/ |
||||
void Cl2DrawOutline(const Surface &out, uint8_t col, Point position, CelSprite cel, int frame); |
||||
|
||||
/**
|
||||
* @brief Same as `Cl2DrawOutline` but considers colors with index 0 (usually shadows) to be transparent. |
||||
* |
||||
* @param col Color index from current palette |
||||
* @param out Output buffer |
||||
* @param position Target buffer coordinate |
||||
* @param cel CL2 buffer |
||||
* @param frame CL2 frame number |
||||
*/ |
||||
void Cl2DrawOutlineSkipColorZero(const Surface &out, uint8_t col, Point position, CelSprite cel, int frame); |
||||
|
||||
/**
|
||||
* @brief Blit CL2 sprite, and apply given TRN to the given buffer at the given coordinates |
||||
* @param out Output buffer |
||||
* @param position Target buffer coordinate |
||||
* @param cel CL2 buffer |
||||
* @param frame CL2 frame number |
||||
* @param trn TRN to use |
||||
*/ |
||||
void Cl2DrawTRN(const Surface &out, Point position, CelSprite cel, int frame, uint8_t *trn); |
||||
|
||||
/**
|
||||
* @brief Same as Cl2DrawTRN but position.y is the top of the sprite instead of the bottom. |
||||
*/ |
||||
inline void RenderCl2SpriteWithTRN(const Surface &out, CelFrameWithHeight cel, Point position, uint8_t *trn) |
||||
{ |
||||
Cl2DrawTRN(out, { position.x, position.y + static_cast<int>(cel.frameHeight) - 1 }, cel.sprite, cel.frame, trn); |
||||
} |
||||
|
||||
void Cl2DrawBlendedTRN(const Surface &out, Point position, CelSprite cel, int frame, uint8_t *trn); |
||||
|
||||
// defined in scrollrt.cpp
|
||||
extern int LightTableIndex; |
||||
|
||||
/**
|
||||
* @brief Blit CL2 sprite, and apply lighting, to the given buffer at the given coordinates |
||||
* @param out Output buffer |
||||
* @param position Target buffer coordinate |
||||
* @param cel CL2 buffer |
||||
* @param frame CL2 frame number |
||||
*/ |
||||
inline void Cl2DrawLight(const Surface &out, Point position, CelSprite cel, int frame) |
||||
{ |
||||
if (LightTableIndex != 0) |
||||
Cl2DrawTRN(out, position, cel, frame, &LightTables[LightTableIndex * 256]); |
||||
else |
||||
Cl2Draw(out, position, cel, frame); |
||||
} |
||||
|
||||
inline void Cl2DrawLightBlended(const Surface &out, Point position, CelSprite cel, int frame) |
||||
{ |
||||
Cl2DrawBlendedTRN(out, position, cel, frame, &LightTables[LightTableIndex * 256]); |
||||
} |
||||
|
||||
/**
|
||||
* Returns a pair of X coordinates containing the start (inclusive) and end (exclusive) |
||||
* of fully transparent columns in the sprite. |
||||
*/ |
||||
std::pair<int, int> Cl2MeasureSolidHorizontalBounds(CelSprite cel, int frame = 0); |
||||
|
||||
} // namespace devilution
|
||||
@ -0,0 +1,115 @@
|
||||
/**
|
||||
* @file clx_render.hpp |
||||
* |
||||
* CL2 rendering. |
||||
*/ |
||||
#pragma once |
||||
|
||||
#include <cstdint> |
||||
|
||||
#include <array> |
||||
#include <utility> |
||||
|
||||
#include "engine.h" |
||||
#include "engine/clx_sprite.hpp" |
||||
#include "engine/point.hpp" |
||||
#include "lighting.h" |
||||
|
||||
namespace devilution { |
||||
|
||||
/**
|
||||
* @brief Apply the color swaps to a CLX sprite list; |
||||
*/ |
||||
void ClxApplyTrans(ClxSpriteList list, const uint8_t *trn); |
||||
void ClxApplyTrans(ClxSpriteSheet sheet, const uint8_t *trn); |
||||
|
||||
/**
|
||||
* @brief Blit CL2 sprite, to the back buffer at the given coordianates |
||||
* @param out Output buffer |
||||
* @param position Target buffer coordinate |
||||
* @param clx CLX frame |
||||
* @param frame CL2 frame number |
||||
*/ |
||||
void ClxDraw(const Surface &out, Point position, ClxSprite clx); |
||||
|
||||
/**
|
||||
* @brief Same as ClxDraw but position.y is the top of the sprite instead of the bottom. |
||||
*/ |
||||
inline void RenderClxSprite(const Surface &out, ClxSprite clx, Point position) |
||||
{ |
||||
ClxDraw(out, { position.x, position.y + static_cast<int>(clx.height()) - 1 }, clx); |
||||
} |
||||
|
||||
/**
|
||||
* @brief Blit a solid colder shape one pixel larger than the given sprite shape, to the given buffer at the given coordinates |
||||
* @param col Color index from current palette |
||||
* @param out Output buffer |
||||
* @param position Target buffer coordinate |
||||
* @param clx CLX frame |
||||
*/ |
||||
void ClxDrawOutline(const Surface &out, uint8_t col, Point position, ClxSprite clx); |
||||
|
||||
/**
|
||||
* @brief Same as `ClxDrawOutline` but considers colors with index 0 (usually shadows) to be transparent. |
||||
* |
||||
* @param col Color index from current palette |
||||
* @param out Output buffer |
||||
* @param position Target buffer coordinate |
||||
* @param clx CLX frame |
||||
*/ |
||||
void ClxDrawOutlineSkipColorZero(const Surface &out, uint8_t col, Point position, ClxSprite clx); |
||||
|
||||
/**
|
||||
* @brief Blit CL2 sprite, and apply given TRN to the given buffer at the given coordinates |
||||
* @param out Output buffer |
||||
* @param position Target buffer coordinate |
||||
* @param clx CLX frame |
||||
* @param trn TRN to use |
||||
*/ |
||||
void ClxDrawTRN(const Surface &out, Point position, ClxSprite clx, const uint8_t *trn); |
||||
|
||||
/**
|
||||
* @brief Same as ClxDrawTRN but position.y is the top of the sprite instead of the bottom. |
||||
*/ |
||||
inline void RenderClxSpriteWithTRN(const Surface &out, ClxSprite clx, Point position, const uint8_t *trn) |
||||
{ |
||||
ClxDrawTRN(out, { position.x, position.y + static_cast<int>(clx.height()) - 1 }, clx, trn); |
||||
} |
||||
|
||||
void ClxDrawBlendedTRN(const Surface &out, Point position, ClxSprite clx, const uint8_t *trn); |
||||
|
||||
// defined in scrollrt.cpp
|
||||
extern int LightTableIndex; |
||||
|
||||
/**
|
||||
* @brief Blit CL2 sprite, and apply lighting, to the given buffer at the given coordinates |
||||
* @param out Output buffer |
||||
* @param position Target buffer coordinate |
||||
* @param clx CLX frame |
||||
*/ |
||||
inline void ClxDrawLight(const Surface &out, Point position, ClxSprite clx) |
||||
{ |
||||
if (LightTableIndex != 0) |
||||
ClxDrawTRN(out, position, clx, &LightTables[LightTableIndex * 256]); |
||||
else |
||||
ClxDraw(out, position, clx); |
||||
} |
||||
|
||||
/**
|
||||
* @brief Blit CL2 sprite, and apply lighting and transparency blending, to the given buffer at the given coordinates |
||||
* @param out Output buffer |
||||
* @param position Target buffer coordinate |
||||
* @param clx CLX frame |
||||
*/ |
||||
inline void ClxDrawLightBlended(const Surface &out, Point position, ClxSprite clx) |
||||
{ |
||||
ClxDrawBlendedTRN(out, position, clx, &LightTables[LightTableIndex * 256]); |
||||
} |
||||
|
||||
/**
|
||||
* Returns a pair of X coordinates containing the start (inclusive) and end (exclusive) |
||||
* of fully transparent columns in the sprite. |
||||
*/ |
||||
std::pair<int, int> ClxMeasureSolidHorizontalBounds(ClxSprite clx); |
||||
|
||||
} // namespace devilution
|
||||
@ -1,13 +0,0 @@
|
||||
#pragma once |
||||
|
||||
#include <cstddef> |
||||
#include <cstdint> |
||||
|
||||
#include "engine/cel_sprite.hpp" |
||||
#include "utils/pointer_value_union.hpp" |
||||
|
||||
namespace devilution { |
||||
|
||||
OwnedCelSprite CelToCl2(const uint8_t *data, size_t size, PointerOrValue<uint16_t> widthOrWidths); |
||||
|
||||
} // namespace devilution
|
||||
@ -0,0 +1,13 @@
|
||||
#pragma once |
||||
|
||||
#include <cstddef> |
||||
#include <cstdint> |
||||
|
||||
#include "engine/clx_sprite.hpp" |
||||
#include "utils/pointer_value_union.hpp" |
||||
|
||||
namespace devilution { |
||||
|
||||
OwnedClxSpriteListOrSheet CelToClx(const uint8_t *data, size_t size, PointerOrValue<uint16_t> widthOrWidths); |
||||
|
||||
} // namespace devilution
|
||||
@ -0,0 +1,97 @@
|
||||
#include "utils/cl2_to_clx.hpp" |
||||
|
||||
#include <cstring> |
||||
|
||||
#include "utils/endian.hpp" |
||||
|
||||
namespace devilution { |
||||
|
||||
namespace { |
||||
|
||||
constexpr bool IsCl2Opaque(uint8_t control) |
||||
{ |
||||
constexpr uint8_t Cl2OpaqueMin = 0x80; |
||||
return control >= Cl2OpaqueMin; |
||||
} |
||||
|
||||
constexpr uint8_t GetCl2OpaquePixelsWidth(uint8_t control) |
||||
{ |
||||
return -static_cast<std::int8_t>(control); |
||||
} |
||||
|
||||
constexpr bool IsCl2OpaqueFill(uint8_t control) |
||||
{ |
||||
constexpr uint8_t Cl2FillMax = 0xBE; |
||||
return control <= Cl2FillMax; |
||||
} |
||||
|
||||
constexpr uint8_t GetCl2OpaqueFillWidth(uint8_t control) |
||||
{ |
||||
constexpr uint8_t Cl2FillEnd = 0xBF; |
||||
return static_cast<int_fast16_t>(Cl2FillEnd - control); |
||||
} |
||||
|
||||
size_t CountCl2FramePixels(const uint8_t *src, const uint8_t *srcEnd) |
||||
{ |
||||
size_t numPixels = 0; |
||||
while (src != srcEnd) { |
||||
uint8_t val = *src++; |
||||
if (IsCl2Opaque(val)) { |
||||
if (IsCl2OpaqueFill(val)) { |
||||
numPixels += GetCl2OpaqueFillWidth(val); |
||||
++src; |
||||
} else { |
||||
val = GetCl2OpaquePixelsWidth(val); |
||||
numPixels += val; |
||||
src += val; |
||||
} |
||||
} else { |
||||
numPixels += val; |
||||
} |
||||
} |
||||
return numPixels; |
||||
} |
||||
|
||||
} // namespace
|
||||
|
||||
uint16_t Cl2ToClx(uint8_t *data, size_t size, PointerOrValue<uint16_t> widthOrWidths) |
||||
{ |
||||
uint32_t numGroups = 1; |
||||
const uint32_t maybeNumFrames = LoadLE32(data); |
||||
uint8_t *groupBegin = data; |
||||
|
||||
// If it is a number of frames, then the last frame offset will be equal to the size of the file.
|
||||
if (LoadLE32(&data[maybeNumFrames * 4 + 4]) != size) { |
||||
// maybeNumFrames is the address of the first group, right after
|
||||
// the list of group offsets.
|
||||
numGroups = maybeNumFrames / 4; |
||||
} |
||||
|
||||
for (size_t group = 0; group < numGroups; ++group) { |
||||
uint32_t numFrames; |
||||
if (numGroups == 1) { |
||||
numFrames = maybeNumFrames; |
||||
} else { |
||||
groupBegin = &data[LoadLE32(&data[group * 4])]; |
||||
numFrames = LoadLE32(groupBegin); |
||||
} |
||||
|
||||
uint8_t *frameEnd = &groupBegin[LoadLE32(&groupBegin[4])]; |
||||
for (size_t frame = 1; frame <= numFrames; ++frame) { |
||||
uint8_t *frameBegin = frameEnd; |
||||
frameEnd = &groupBegin[LoadLE32(&groupBegin[4 * (frame + 1)])]; |
||||
|
||||
constexpr size_t Cl2FrameHeaderSize = 10; |
||||
const size_t numPixels = CountCl2FramePixels(frameBegin + Cl2FrameHeaderSize, frameEnd); |
||||
|
||||
const uint16_t frameWidth = widthOrWidths.HoldsPointer() ? widthOrWidths.AsPointer()[frame - 1] : widthOrWidths.AsValue(); |
||||
const uint16_t frameHeight = numPixels / frameWidth; |
||||
WriteLE16(&frameBegin[2], frameWidth); |
||||
WriteLE16(&frameBegin[4], frameHeight); |
||||
memset(&frameBegin[6], 0, 4); |
||||
} |
||||
} |
||||
return numGroups == 1 ? 0 : numGroups; |
||||
} |
||||
|
||||
} // namespace devilution
|
||||
@ -0,0 +1,26 @@
|
||||
#pragma once |
||||
|
||||
#include <cstddef> |
||||
#include <cstdint> |
||||
|
||||
#include <memory> |
||||
|
||||
#include "engine/clx_sprite.hpp" |
||||
#include "utils/pointer_value_union.hpp" |
||||
|
||||
namespace devilution { |
||||
|
||||
/**
|
||||
* @brief Converts CL2 to CLX in-place. |
||||
* |
||||
* @return uint16_t The number of lists in a sheet if it is a sheet, 0 otherwise. |
||||
*/ |
||||
uint16_t Cl2ToClx(uint8_t *data, size_t size, PointerOrValue<uint16_t> widthOrWidths); |
||||
|
||||
inline OwnedClxSpriteListOrSheet Cl2ToClx(std::unique_ptr<uint8_t[]> &&data, size_t size, PointerOrValue<uint16_t> widthOrWidths) |
||||
{ |
||||
const uint16_t numLists = Cl2ToClx(data.get(), size, widthOrWidths); |
||||
return OwnedClxSpriteListOrSheet { std::move(data), numLists }; |
||||
} |
||||
|
||||
} // namespace devilution
|
||||
@ -0,0 +1,81 @@
|
||||
#pragma once |
||||
|
||||
#include <type_traits> |
||||
#include <utility> |
||||
|
||||
#include "appfat.h" |
||||
#include "utils/stdcompat/optional.hpp" |
||||
|
||||
/// An optional that uses a field of the stored class and some value to store nullopt.
|
||||
#define DEFINE_INTRUSIVE_OPTIONAL_IMPL(OPTIONAL_CLASS, VALUE_CLASS, FIELD, NULL_VALUE, CONSTEXPR) \ |
||||
public: \
|
||||
CONSTEXPR OPTIONAL_CLASS() = default; \
|
||||
\
|
||||
template <class U = VALUE_CLASS> \
|
||||
CONSTEXPR OPTIONAL_CLASS(VALUE_CLASS &&value) \
|
||||
: value_(std::forward<U>(value)) \
|
||||
{ \
|
||||
} \
|
||||
\
|
||||
CONSTEXPR OPTIONAL_CLASS(std::nullopt_t) \
|
||||
: OPTIONAL_CLASS() \
|
||||
{ \
|
||||
} \
|
||||
\
|
||||
template <typename... Args> \
|
||||
CONSTEXPR VALUE_CLASS &emplace(Args &&...args) \
|
||||
{ \
|
||||
value_ = VALUE_CLASS(std::forward<Args>(args)...); \
|
||||
return value_; \
|
||||
} \
|
||||
\
|
||||
template <class U = VALUE_CLASS> \
|
||||
CONSTEXPR OPTIONAL_CLASS &operator=(VALUE_CLASS &&value) \
|
||||
{ \
|
||||
value_ = std::forward<U>(value); \
|
||||
return *this; \
|
||||
} \
|
||||
\
|
||||
CONSTEXPR OPTIONAL_CLASS &operator=(std::nullopt_t) \
|
||||
{ \
|
||||
value_ = VALUE_CLASS {}; \
|
||||
return *this; \
|
||||
} \
|
||||
\
|
||||
CONSTEXPR const VALUE_CLASS &operator*() const \
|
||||
{ \
|
||||
assert(value_.FIELD != NULL_VALUE); \
|
||||
return value_; \
|
||||
} \
|
||||
\
|
||||
CONSTEXPR VALUE_CLASS &operator*() \
|
||||
{ \
|
||||
assert(value_.FIELD != NULL_VALUE); \
|
||||
return value_; \
|
||||
} \
|
||||
\
|
||||
CONSTEXPR const VALUE_CLASS *operator->() const \
|
||||
{ \
|
||||
assert(value_.FIELD != NULL_VALUE); \
|
||||
return &value_; \
|
||||
} \
|
||||
\
|
||||
CONSTEXPR VALUE_CLASS *operator->() \
|
||||
{ \
|
||||
assert(value_.FIELD != NULL_VALUE); \
|
||||
return &value_; \
|
||||
} \
|
||||
\
|
||||
CONSTEXPR operator bool() const \
|
||||
{ \
|
||||
return value_.FIELD != NULL_VALUE; \
|
||||
} \
|
||||
\
|
||||
private: \
|
||||
VALUE_CLASS value_; |
||||
|
||||
#define DEFINE_CONSTEXPR_INTRUSIVE_OPTIONAL(OPTIONAL_CLASS, VALUE_CLASS, FIELD, NULL_VALUE) \ |
||||
DEFINE_INTRUSIVE_OPTIONAL_IMPL(OPTIONAL_CLASS, VALUE_CLASS, FIELD, NULL_VALUE, constexpr) |
||||
|
||||
#define DEFINE_INTRUSIVE_OPTIONAL(OPTIONAL_CLASS, VALUE_CLASS, FIELD, NULL_VALUE) \ |
||||
DEFINE_INTRUSIVE_OPTIONAL_IMPL(OPTIONAL_CLASS, VALUE_CLASS, FIELD, NULL_VALUE, ) |
||||
Loading…
Reference in new issue