| 1 | // Aseprite |
| 2 | // Copyright (C) 2019-2021 Igara Studio S.A. |
| 3 | // Copyright (C) 2001-2018 David Capello |
| 4 | // |
| 5 | // This program is distributed under the terms of |
| 6 | // the End-User License Agreement for Aseprite. |
| 7 | |
| 8 | #ifdef HAVE_CONFIG_H |
| 9 | #include "config.h" |
| 10 | #endif |
| 11 | |
| 12 | #include "app/util/new_image_from_mask.h" |
| 13 | |
| 14 | #include "app/doc.h" |
| 15 | #include "app/site.h" |
| 16 | #include "doc/image_impl.h" |
| 17 | #include "doc/layer.h" |
| 18 | #include "doc/mask.h" |
| 19 | #include "doc/primitives.h" |
| 20 | #include "render/render.h" |
| 21 | |
| 22 | namespace app { |
| 23 | |
| 24 | using namespace doc; |
| 25 | |
| 26 | Image* new_image_from_mask(const Site& site, const bool newBlend) |
| 27 | { |
| 28 | const Mask* srcMask = site.document()->mask(); |
| 29 | return new_image_from_mask(site, srcMask, newBlend); |
| 30 | } |
| 31 | |
| 32 | doc::Image* new_image_from_mask(const Site& site, |
| 33 | const doc::Mask* srcMask, |
| 34 | const bool newBlend, |
| 35 | bool merged) |
| 36 | { |
| 37 | const Sprite* srcSprite = site.sprite(); |
| 38 | ASSERT(srcSprite); |
| 39 | ASSERT(srcMask); |
| 40 | |
| 41 | const Image* srcMaskBitmap = srcMask->bitmap(); |
| 42 | const gfx::Rect& srcBounds = srcMask->bounds(); |
| 43 | |
| 44 | ASSERT(srcMaskBitmap); |
| 45 | ASSERT(!srcBounds.isEmpty()); |
| 46 | |
| 47 | std::unique_ptr<Image> dst(Image::create(srcSprite->pixelFormat(), srcBounds.w, srcBounds.h)); |
| 48 | if (!dst) |
| 49 | return nullptr; |
| 50 | |
| 51 | // Clear the new image |
| 52 | dst->setMaskColor(srcSprite->transparentColor()); |
| 53 | clear_image(dst.get(), dst->maskColor()); |
| 54 | |
| 55 | const Image* src = nullptr; |
| 56 | int x = 0, y = 0; |
| 57 | if (merged || site.layer()->isTilemap()) { |
| 58 | render::Render render; |
| 59 | render.setNewBlend(newBlend); |
| 60 | if (merged) |
| 61 | render.renderSprite(dst.get(), srcSprite, site.frame(), |
| 62 | gfx::Clip(0, 0, srcBounds)); |
| 63 | else { |
| 64 | ASSERT(site.layer()->isTilemap()); |
| 65 | if (auto cel = site.cel()) { |
| 66 | render.renderCel( |
| 67 | dst.get(), cel, srcSprite, |
| 68 | cel->image(), cel->layer(), |
| 69 | srcSprite->palette(cel->frame()), |
| 70 | cel->bounds(), |
| 71 | gfx::Clip(0, 0, srcBounds), |
| 72 | 255, BlendMode::NORMAL); |
| 73 | } |
| 74 | } |
| 75 | |
| 76 | src = dst.get(); |
| 77 | } |
| 78 | else { |
| 79 | src = site.image(&x, &y); |
| 80 | } |
| 81 | |
| 82 | // Copy the masked zones |
| 83 | if (src) { |
| 84 | if (srcMaskBitmap) { |
| 85 | // Copy active layer with mask |
| 86 | const LockImageBits<BitmapTraits> maskBits(srcMaskBitmap, gfx::Rect(0, 0, srcBounds.w, srcBounds.h)); |
| 87 | LockImageBits<BitmapTraits>::const_iterator mask_it = maskBits.begin(); |
| 88 | |
| 89 | for (int v=0; v<srcBounds.h; ++v) { |
| 90 | for (int u=0; u<srcBounds.w; ++u, ++mask_it) { |
| 91 | ASSERT(mask_it != maskBits.end()); |
| 92 | |
| 93 | if (src != dst.get()) { |
| 94 | if (*mask_it) { |
| 95 | int getx = u+srcBounds.x-x; |
| 96 | int gety = v+srcBounds.y-y; |
| 97 | |
| 98 | if ((getx >= 0) && (getx < src->width()) && |
| 99 | (gety >= 0) && (gety < src->height())) |
| 100 | dst->putPixel(u, v, src->getPixel(getx, gety)); |
| 101 | } |
| 102 | } |
| 103 | else { |
| 104 | if (!*mask_it) { |
| 105 | dst->putPixel(u, v, dst->maskColor()); |
| 106 | } |
| 107 | } |
| 108 | } |
| 109 | } |
| 110 | } |
| 111 | else if (src != dst.get()) { |
| 112 | copy_image(dst.get(), src, -srcBounds.x, -srcBounds.y); |
| 113 | } |
| 114 | } |
| 115 | |
| 116 | return dst.release(); |
| 117 | } |
| 118 | |
| 119 | doc::Image* new_tilemap_from_mask(const Site& site, |
| 120 | const doc::Mask* srcMask) |
| 121 | { |
| 122 | const Sprite* srcSprite = site.sprite(); |
| 123 | ASSERT(srcSprite); |
| 124 | ASSERT(srcMask); |
| 125 | |
| 126 | const Cel* srcCel = site.cel(); |
| 127 | if (!srcCel) |
| 128 | return nullptr; |
| 129 | |
| 130 | const doc::Grid grid = site.grid(); |
| 131 | const Image* srcMaskBitmap = srcMask->bitmap(); |
| 132 | const gfx::Rect& srcBounds = srcMask->bounds(); |
| 133 | const gfx::Rect srcTilesBounds = grid.canvasToTile(srcBounds); |
| 134 | |
| 135 | ASSERT(srcMaskBitmap); |
| 136 | ASSERT(!srcTilesBounds.isEmpty()); |
| 137 | ASSERT(site.layer()->isTilemap()); |
| 138 | |
| 139 | std::unique_ptr<Image> dst(Image::create(IMAGE_TILEMAP, |
| 140 | srcTilesBounds.w, |
| 141 | srcTilesBounds.h)); |
| 142 | if (!dst) |
| 143 | return nullptr; |
| 144 | |
| 145 | // Clear the new tilemap |
| 146 | clear_image(dst.get(), dst->maskColor()); |
| 147 | |
| 148 | // Copy the masked zones |
| 149 | if (srcMaskBitmap) { |
| 150 | // Copy active layer with mask |
| 151 | const LockImageBits<BitmapTraits> maskBits(srcMaskBitmap, gfx::Rect(0, 0, srcBounds.w, srcBounds.h)); |
| 152 | auto mask_it = maskBits.begin(); |
| 153 | const gfx::Point originPt = grid.canvasToTile(srcBounds.origin()); |
| 154 | |
| 155 | for (int v=0; v<srcBounds.h; ++v) { |
| 156 | for (int u=0; u<srcBounds.w; ++u, ++mask_it) { |
| 157 | ASSERT(mask_it != maskBits.end()); |
| 158 | if (*mask_it) { |
| 159 | gfx::Point srcPt = grid.canvasToTile(gfx::Point(srcBounds.x+u, srcBounds.y+v)); |
| 160 | gfx::Point dstPt = srcPt - originPt; |
| 161 | |
| 162 | if (dst->bounds().contains(dstPt) && |
| 163 | srcCel->image()->bounds().contains(srcPt)) { |
| 164 | dst->putPixel(dstPt.x, dstPt.y, srcCel->image()->getPixel(srcPt.x, srcPt.y)); |
| 165 | } |
| 166 | } |
| 167 | } |
| 168 | } |
| 169 | } |
| 170 | else { |
| 171 | // Just copy tilemap data |
| 172 | dst->copy(srcCel->image(), gfx::Clip(0, 0, srcTilesBounds)); |
| 173 | } |
| 174 | |
| 175 | return dst.release(); |
| 176 | } |
| 177 | |
| 178 | } // namespace app |
| 179 | |