From af074fd6bf57bb95baa07d9d196294d568d592ac Mon Sep 17 00:00:00 2001 From: David Capello Date: Fri, 23 Sep 2022 14:01:35 -0300 Subject: [PATCH] Add support to render tilemaps --- src/app/render/shader_renderer.cpp | 134 +++++++++++++++++++++++------ src/app/render/shader_renderer.h | 6 ++ 2 files changed, 115 insertions(+), 25 deletions(-) diff --git a/src/app/render/shader_renderer.cpp b/src/app/render/shader_renderer.cpp index 93790067f..bd545d4f6 100644 --- a/src/app/render/shader_renderer.cpp +++ b/src/app/render/shader_renderer.cpp @@ -214,40 +214,92 @@ void ShaderRenderer::drawLayerGroup(SkCanvas* canvas, celBounds = cel->bounds(); } - auto skData = SkData::MakeWithoutCopy( - (const void*)celImage->getPixelAddress(0, 0), - celImage->getMemSize()); - - // TODO support other color modes - ASSERT(celImage->colorMode() == doc::ColorMode::RGB); - - auto skImg = SkImage::MakeRasterData( - SkImageInfo::Make(celImage->width(), - celImage->height(), - kRGBA_8888_SkColorType, - kUnpremul_SkAlphaType), - skData, - celImage->getRowStrideSize()); - int t; int opacity = cel->opacity(); opacity = MUL_UN8(opacity, imgLayer->opacity(), t); - SkPaint p; - p.setAlpha(opacity); - p.setBlendMode(to_skia(imgLayer->blendMode())); - canvas->drawImage(skImg.get(), - SkIntToScalar(celBounds.x), - SkIntToScalar(celBounds.y), - SkSamplingOptions(), - &p); + drawImage(canvas, + celImage, + celBounds.x, + celBounds.y, + opacity, + imgLayer->blendMode()); } break; } - case doc::ObjectType::LayerTilemap: - // TODO impl + case doc::ObjectType::LayerTilemap: { + auto tilemapLayer = static_cast(layer); + doc::Cel* cel = tilemapLayer->cel(frame); + if (!cel) + break; + + const gfx::RectF celBounds = cel->bounds(); + const doc::Image* celImage = cel->image(); + + doc::Grid grid = tilemapLayer->tileset()->grid(); + grid.origin(grid.origin() + gfx::Point(celBounds.origin())); + + // Is the 'm_previewTileset' set to be used with this layer? + const Tileset* tileset; + if (m_previewTileset && cel && + checkIfWeShouldUsePreview(cel)) { + tileset = m_previewTileset; + } + else { + tileset = tilemapLayer->tileset(); + ASSERT(tileset); + if (!tileset) + return; + } + + const gfx::Clip iarea(area); + gfx::Rect tilesToDraw = grid.canvasToTile( + m_proj.remove(gfx::Rect(iarea.src, iarea.size))); + + int yPixelsPerTile = m_proj.applyY(grid.tileSize().h); + if (yPixelsPerTile > 0 && (iarea.size.h + iarea.src.y) % yPixelsPerTile > 0) + tilesToDraw.h += 1; + int xPixelsPerTile = m_proj.applyX(grid.tileSize().w); + if (xPixelsPerTile > 0 && (iarea.size.w + iarea.src.x) % xPixelsPerTile > 0) + tilesToDraw.w += 1; + + // As area.size is not empty at this point, we have to draw at + // least one tile (and the clipping will be performed for the + // tile pixels later). + if (tilesToDraw.w < 1) tilesToDraw.w = 1; + if (tilesToDraw.h < 1) tilesToDraw.h = 1; + + tilesToDraw &= celImage->bounds(); + + for (int v=tilesToDraw.y; vbounds().contains(u, v)) + continue; + + const tile_t t = celImage->getPixel(u, v); + if (t != doc::notile) { + const tile_index i = tile_geti(t); + const ImageRef tileImage = tileset->get(i); + if (!tileImage) + continue; + + int t; + int opacity = cel->opacity(); + opacity = MUL_UN8(opacity, tilemapLayer->opacity(), t); + + drawImage(canvas, + tileImage.get(), + tileBoundsOnCanvas.x, + tileBoundsOnCanvas.y, + opacity, + tilemapLayer->blendMode()); + } + } + } break; + } case doc::ObjectType::LayerGroup: // TODO draw a group in a sub-surface and then compose that surface @@ -307,6 +359,38 @@ void ShaderRenderer::renderImage(doc::Image* dstImage, // TODO impl } +void ShaderRenderer::drawImage(SkCanvas* canvas, + const doc::Image* srcImage, + const int x, + const int y, + const int opacity, + const doc::BlendMode blendMode) +{ + auto skData = SkData::MakeWithoutCopy( + (const void*)srcImage->getPixelAddress(0, 0), + srcImage->getMemSize()); + + // TODO support other color modes + ASSERT(srcImage->colorMode() == doc::ColorMode::RGB); + + auto skImg = SkImage::MakeRasterData( + SkImageInfo::Make(srcImage->width(), + srcImage->height(), + kRGBA_8888_SkColorType, + kUnpremul_SkAlphaType), + skData, + srcImage->getRowStrideSize()); + + SkPaint p; + p.setAlpha(opacity); + p.setBlendMode(to_skia(blendMode)); + canvas->drawImage(skImg.get(), + SkIntToScalar(x), + SkIntToScalar(y), + SkSamplingOptions(), + &p); +} + // TODO this is equal to Render::checkIfWeShouldUsePreview(const Cel*), // we might think in a way to merge both functions bool ShaderRenderer::checkIfWeShouldUsePreview(const doc::Cel* cel) const diff --git a/src/app/render/shader_renderer.h b/src/app/render/shader_renderer.h index 3563b7225..eec90cb17 100644 --- a/src/app/render/shader_renderer.h +++ b/src/app/render/shader_renderer.h @@ -73,6 +73,12 @@ namespace app { const doc::LayerGroup* group, const doc::frame_t frame, const gfx::ClipF& area); + void drawImage(SkCanvas* canvas, + const doc::Image* srcImage, + const int x, + const int y, + const int opacity, + const doc::BlendMode blendMode); bool checkIfWeShouldUsePreview(const doc::Cel* cel) const;