1 | // Aseprite Document Library |
2 | // Copyright (c) 2021 Igara Studio S.A. |
3 | // Copyright (c) 2018 David Capello |
4 | // |
5 | // This file is released under the terms of the MIT license. |
6 | // Read LICENSE.txt for more information. |
7 | |
8 | #ifdef HAVE_CONFIG_H |
9 | #include "config.h" |
10 | #endif |
11 | |
12 | #include "doc/algorithm/modify_selection.h" |
13 | |
14 | #include "doc/image_impl.h" |
15 | #include "doc/mask.h" |
16 | #include "doc/primitives.h" |
17 | |
18 | #include <memory> |
19 | |
20 | namespace doc { |
21 | namespace algorithm { |
22 | |
23 | // TODO create morphological operators/functions in "doc" namespace |
24 | // TODO the impl is not optimal, but is good enough as a first version |
25 | void modify_selection(const SelectionModifier modifier, |
26 | const Mask* srcMask, |
27 | Mask* dstMask, |
28 | const int radius, |
29 | const doc::BrushType brush) |
30 | { |
31 | const doc::Image* srcImage = srcMask->bitmap(); |
32 | doc::Image* dstImage = dstMask->bitmap(); |
33 | const gfx::Point offset = |
34 | srcMask->bounds().origin() - |
35 | dstMask->bounds().origin(); |
36 | |
37 | // Image bounds to clip get/put pixels |
38 | const gfx::Rect srcBounds = srcImage->bounds(); |
39 | |
40 | // Create a kernel |
41 | const int size = 2*radius+1; |
42 | std::unique_ptr<doc::Image> kernel(doc::Image::create(IMAGE_BITMAP, size, size)); |
43 | doc::clear_image(kernel.get(), 0); |
44 | if (brush == doc::kCircleBrushType) |
45 | doc::fill_ellipse(kernel.get(), 0, 0, size-1, size-1, 0, 0, 1); |
46 | else |
47 | doc::fill_rect(kernel.get(), 0, 0, size-1, size-1, 1); |
48 | doc::put_pixel(kernel.get(), radius, radius, 0); |
49 | |
50 | int total = 0; // Number of 1s in the kernel image |
51 | for (int v=0; v<size; ++v) |
52 | for (int u=0; u<size; ++u) |
53 | total += kernel->getPixel(u, v); |
54 | |
55 | for (int y=-radius; y<srcBounds.h+radius; ++y) { |
56 | for (int x=-radius; x<srcBounds.w+radius; ++x) { |
57 | doc::color_t c; |
58 | if (srcBounds.contains(x, y)) |
59 | c = srcImage->getPixel(x, y); |
60 | else |
61 | c = 0; |
62 | |
63 | int accum = 0; |
64 | for (int v=0; v<size; ++v) { |
65 | for (int u=0; u<size; ++u) { |
66 | if (kernel->getPixel(u, v)) { |
67 | if (srcBounds.contains(x+u-radius, y+v-radius)) |
68 | accum += srcImage->getPixel(x-radius+u, y-radius+v); |
69 | } |
70 | } |
71 | } |
72 | |
73 | switch (modifier) { |
74 | case SelectionModifier::Border: { |
75 | c = (c && accum < total) ? 1: 0; |
76 | break; |
77 | } |
78 | case SelectionModifier::Expand: { |
79 | c = (c || accum > 0) ? 1: 0; |
80 | break; |
81 | } |
82 | case SelectionModifier::Contract: { |
83 | c = (c && accum == total) ? 1: 0; |
84 | break; |
85 | } |
86 | } |
87 | |
88 | if (c) |
89 | doc::put_pixel(dstImage, |
90 | offset.x+x, |
91 | offset.y+y, 1); |
92 | } |
93 | } |
94 | } |
95 | |
96 | } // namespace algorithm |
97 | } // namespace app |
98 | |