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
20namespace doc {
21namespace 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
25void 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