src/LargeImageData.cpp in gosu-1.3.0 vs src/LargeImageData.cpp in gosu-1.4.0

- old
+ new

@@ -2,207 +2,214 @@ #include <Gosu/Bitmap.hpp> #include <Gosu/Graphics.hpp> #include <Gosu/Math.hpp> #include <cmath> #include <stdexcept> -using namespace std; +const Gosu::ImageData& Gosu::LargeImageData::tile(int x, int y) const +{ + return *m_tiles.at(static_cast<std::size_t>(y * m_tiles_x + x)); +} + Gosu::LargeImageData::LargeImageData(const Bitmap& source, int tile_width, int tile_height, unsigned image_flags) { - w = source.width(); - h = source.height(); - tiles_x = static_cast<int>(ceil(1.0 * w / tile_width)); - tiles_y = static_cast<int>(ceil(1.0 * h / tile_height)); + m_w = source.width(); + m_h = source.height(); + // Manual ceil() for integer division. + m_tiles_x = (m_w + tile_width - 1) / tile_width; + m_tiles_y = (m_h + tile_height - 1) / tile_height; // When there are no tiles, set both fields to 0 to avoid entering any for () loop in this class. - if (tiles_x == 0 || tiles_y == 0) { - tiles_x = tiles_y = 0; + if (m_tiles_x == 0 || m_tiles_y == 0) { + m_tiles_x = m_tiles_y = 0; } - tiles.reserve(tiles_x * tiles_y); + m_tiles.reserve(m_tiles_x * m_tiles_y); - for (int y = 0; y < tiles_y; ++y) { - for (int x = 0; x < tiles_x; ++x) { + for (int y = 0; y < m_tiles_y; ++y) { + for (int x = 0; x < m_tiles_x; ++x) { int src_width = tile_width; - if (x == tiles_x - 1 && w % tile_width != 0) { + if (x == m_tiles_x - 1 && m_w % tile_width != 0) { // The right-most parts don't necessarily have the full width. - src_width = w % tile_width; + src_width = m_w % tile_width; } int src_height = tile_height; - if (y == tiles_y - 1 && h % tile_height != 0) { + if (y == m_tiles_y - 1 && m_h % tile_height != 0) { // Same for the parts on the bottom. - src_height = h % tile_height; + src_height = m_h % tile_height; } unsigned local_flags = IF_TILEABLE | image_flags; - + // Left edge, only tileable if requested in image_flags. if (x == 0) { local_flags &= ~IF_TILEABLE_LEFT; local_flags |= (image_flags & IF_TILEABLE_LEFT); } // Right edge, only tileable if requested in image_flags. - if (x == tiles_x - 1) { + if (x == m_tiles_x - 1) { local_flags &= ~IF_TILEABLE_RIGHT; local_flags |= (image_flags & IF_TILEABLE_RIGHT); } // Top edge, only tileable if requested in image_flags. if (y == 0) { local_flags &= ~IF_TILEABLE_TOP; local_flags |= (image_flags & IF_TILEABLE_TOP); } // Bottom edge, only tileable if requested in image_flags. - if (y == tiles_y - 1) { + if (y == m_tiles_y - 1) { local_flags &= ~IF_TILEABLE_BOTTOM; local_flags |= (image_flags & IF_TILEABLE_BOTTOM); } - - tiles.emplace_back(Graphics::create_image(source, - x * tile_width, y * tile_height, - src_width, src_height, local_flags)); + + m_tiles.emplace_back(Graphics::create_image(source, x * tile_width, y * tile_height, + src_width, src_height, local_flags)); } } } -void Gosu::LargeImageData::draw(double x1, double y1, Color c1, - double x2, double y2, Color c2, - double x3, double y3, Color c3, - double x4, double y4, Color c4, +void Gosu::LargeImageData::draw(double x1, double y1, Color c1, double x2, double y2, Color c2, + double x3, double y3, Color c3, double x4, double y4, Color c4, ZPos z, BlendMode mode) const { normalize_coordinates(x1, y1, x2, y2, x3, y3, c3, x4, y4, c4); - + double y = 0; - for (int ty = 0; ty < tiles_y; ++ty) { + for (int ty = 0; ty < m_tiles_y; ++ty) { double x = 0; - for (int tx = 0; tx < tiles_x; ++tx) { - ImageData& tile = *tiles[ty * tiles_x + tx]; + for (int tx = 0; tx < m_tiles_x; ++tx) { + const ImageData& image = tile(tx, ty); - double rel_x_l = x / w; - double rel_x_r = (x + tile.width()) / w; - double rel_y_t = y / h; - double rel_y_b = (y + tile.height()) / h; - - #define LERP2D(what, x_weight, y_weight) \ - lerp(lerp(what##1, what##3, y_weight), \ - lerp(what##2, what##4, y_weight), \ - x_weight); + double rel_x_l = x / m_w; + double rel_x_r = (x + image.width()) / m_w; + double rel_y_t = y / m_h; + double rel_y_b = (y + image.height()) / m_h; +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define LERP2D(what, x_weight, y_weight) \ + lerp(lerp(what##1, what##3, y_weight), lerp(what##2, what##4, y_weight), x_weight); + double x_t_l = LERP2D(x, rel_x_l, rel_y_t); double x_t_r = LERP2D(x, rel_x_r, rel_y_t); double x_b_l = LERP2D(x, rel_x_l, rel_y_b); double x_b_r = LERP2D(x, rel_x_r, rel_y_b); double y_t_l = LERP2D(y, rel_x_l, rel_y_t); double y_t_r = LERP2D(y, rel_x_r, rel_y_t); double y_b_l = LERP2D(y, rel_x_l, rel_y_b); double y_b_r = LERP2D(y, rel_x_r, rel_y_b); - Color c_t_l = LERP2D(c, rel_x_l, rel_y_t); - Color c_t_r = LERP2D(c, rel_x_r, rel_y_t); - Color c_b_l = LERP2D(c, rel_x_l, rel_y_b); - Color c_b_r = LERP2D(c, rel_x_r, rel_y_b); + Color c_t_l = LERP2D(c, rel_x_l, rel_y_t); + Color c_t_r = LERP2D(c, rel_x_r, rel_y_t); + Color c_b_l = LERP2D(c, rel_x_l, rel_y_b); + Color c_b_r = LERP2D(c, rel_x_r, rel_y_b); - tile.draw(x_t_l, y_t_l, c_t_l, - x_t_r, y_t_r, c_t_r, - x_b_l, y_b_l, c_b_l, - x_b_r, y_b_r, c_b_r, - z, mode); - - x += tile.width(); + image.draw(x_t_l, y_t_l, c_t_l, x_t_r, y_t_r, c_t_r, x_b_l, y_b_l, c_b_l, x_b_r, y_b_r, + c_b_r, z, mode); + + x += image.width(); } - y += tiles[ty * tiles_x]->height(); + y += tile(0, ty).height(); } } -unique_ptr<Gosu::ImageData> - Gosu::LargeImageData::subimage(int left, int top, int width, int height) const +std::unique_ptr<Gosu::ImageData> Gosu::LargeImageData::subimage(int left, int top, int width, + int height) const { - if (left < 0 || top < 0 || left + width > w || top + height > h) { - throw invalid_argument("subimage bounds exceed those of its parent"); + if (left < 0 || top < 0 || left + width > m_w || top + height > m_h) { + throw std::invalid_argument{"subimage bounds exceed those of its parent"}; } if (width <= 0 || height <= 0) { - throw invalid_argument("cannot create empty image"); + throw std::invalid_argument{"cannot create empty subimage"}; } - + int sub_tiles_y = 0; - vector<unique_ptr<ImageData>> sub_tiles; - + std::vector<std::unique_ptr<ImageData>> sub_tiles; + int y = 0; - for (int ty = 0; ty < tiles_y; ++ty) { - int row_height = tiles[ty * tiles_x]->height(); - + for (int ty = 0; ty < m_tiles_y; ++ty) { + int row_height = tile(0, ty).height(); + if (y + row_height <= top) { - y += tiles[ty * tiles_x]->height(); + // Skip rows until we are at the requested Y coordinate. + y += row_height; continue; } - if (y >= top + height) break; - + if (y >= top + height) { + // Also skip all rows after reaching the bottom requested Y coordinate. + break; + } + sub_tiles_y += 1; int x = 0; - for (int tx = 0; tx < tiles_x; ++tx) { - ImageData& tile = *tiles[ty * tiles_x + tx]; - - if (x + tile.width() <= left) { - x += tile.width(); + for (int tx = 0; tx < m_tiles_x; ++tx) { + const ImageData& image = tile(tx, ty); + + if (x + image.width() <= left) { + // Skip columns until we are at the requested X coordinate. + x += image.width(); continue; } - if (x >= left + width) break; - - int sub_left = max(0, left - x); - int sub_top = max(0, top - y); - int sub_right = min(tile.width(), left + width - x); - int sub_bottom = min(tile.height(), top + height - y); - - sub_tiles.emplace_back(tile.subimage(sub_left, sub_top, sub_right - sub_left, sub_bottom - sub_top)); - - x += tile.width(); + if (x >= left + width) { + // Also skip all columns after reaching the right-most requested Y coordinate. + break; + } + + int sub_left = std::max(0, left - x); + int sub_top = std::max(0, top - y); + int sub_right = std::min(image.width(), left + width - x); + int sub_bottom = std::min(image.height(), top + height - y); + + sub_tiles.emplace_back( + image.subimage(sub_left, sub_top, sub_right - sub_left, sub_bottom - sub_top)); + + x += image.width(); } - y += tiles[ty * tiles_x]->height(); + y += tile(0, ty).height(); } - + if (sub_tiles.size() == 1) { return move(sub_tiles[0]); } else { - unique_ptr<LargeImageData> result(new LargeImageData()); - result->w = width; - result->h = height; - result->tiles_x = static_cast<int>(sub_tiles.size()) / sub_tiles_y; - result->tiles_y = sub_tiles_y; - result->tiles.swap(sub_tiles); + std::unique_ptr<LargeImageData> result(new LargeImageData()); + result->m_w = width; + result->m_h = height; + result->m_tiles_x = static_cast<int>(sub_tiles.size()) / sub_tiles_y; + result->m_tiles_y = sub_tiles_y; + result->m_tiles.swap(sub_tiles); return move(result); } } Gosu::Bitmap Gosu::LargeImageData::to_bitmap() const { Bitmap bitmap(width(), height()); int y = 0; - for (int ty = 0; ty < tiles_y; ++ty) { + for (int ty = 0; ty < m_tiles_y; ++ty) { int x = 0; - for (int tx = 0; tx < tiles_x; ++tx) { - ImageData& tile = *tiles[ty * tiles_x + tx]; - bitmap.insert(x, y, tile.to_bitmap()); - x += tile.width(); + for (int tx = 0; tx < m_tiles_x; ++tx) { + const ImageData& image = tile(tx, ty); + bitmap.insert(x, y, image.to_bitmap()); + x += image.width(); } - y += tiles[ty * tiles_x]->height(); + y += tile(0, ty).height(); } return bitmap; } void Gosu::LargeImageData::insert(const Bitmap& bitmap, int at_x, int at_y) { int y = 0; - for (int ty = 0; ty < tiles_y; ++ty) { + for (int ty = 0; ty < m_tiles_y; ++ty) { int x = 0; - for (int tx = 0; tx < tiles_x; ++tx) { - ImageData& tile = *tiles[ty * tiles_x + tx]; - tile.insert(bitmap, at_x - x, at_y - y); - x += tile.width(); + for (int tx = 0; tx < m_tiles_x; ++tx) { + ImageData& image = *m_tiles[static_cast<std::size_t>(ty * m_tiles_x + tx)]; + image.insert(bitmap, at_x - x, at_y - y); + x += image.width(); } - y += tiles[ty * tiles_x]->height(); + y += tile(0, ty).height(); } }