1// Aseprite
2// Copyright (C) 2019-2020 Igara Studio S.A.
3//
4// This program is distributed under the terms of
5// the End-User License Agreement for Aseprite.
6
7#ifdef HAVE_CONFIG_H
8#include "config.h"
9#endif
10
11#include "app/script/engine.h"
12#include "app/script/luacpp.h"
13#include "doc/brush.h"
14#include "doc/image.h"
15#include "doc/mask.h"
16
17#include <algorithm>
18
19namespace app {
20namespace script {
21
22namespace {
23
24using namespace doc;
25
26struct BrushObj {
27 BrushRef brush;
28 BrushObj(const BrushRef& brush)
29 : brush(brush) {
30 }
31};
32
33BrushRef Brush_new(lua_State* L, int index)
34{
35 BrushRef brush;
36 if (auto brush2 = may_get_obj<BrushObj>(L, index)) {
37 ASSERT(brush2->brush);
38 if (brush2->brush)
39 brush.reset(new Brush(*brush2->brush));
40 }
41 else if (auto image = may_get_image_from_arg(L, index)) {
42 if (image) {
43 brush.reset(new Brush(kImageBrushType, 1, 0));
44 brush->setImage(image, nullptr);
45 brush->setPattern(BrushPattern::DEFAULT_FOR_SCRIPTS);
46 }
47 }
48 else if (lua_istable(L, index)) {
49 Image* image = nullptr;
50 if (lua_getfield(L, index, "image") != LUA_TNIL) {
51 image = may_get_image_from_arg(L, -1);
52 }
53 lua_pop(L, 1);
54
55 BrushType type = (image ? BrushType::kImageBrushType:
56 BrushType::kCircleBrushType);
57 if (lua_getfield(L, index, "type") != LUA_TNIL)
58 type = (BrushType)lua_tointeger(L, -1);
59 lua_pop(L, 1);
60
61 int size = 1;
62 if (lua_getfield(L, index, "size") != LUA_TNIL) {
63 size = lua_tointeger(L, -1);
64 size = std::max(1, size);
65 }
66 lua_pop(L, 1);
67
68 int angle = 0;
69 if (lua_getfield(L, index, "angle") != LUA_TNIL)
70 angle = lua_tointeger(L, -1);
71 lua_pop(L, 1);
72
73 brush.reset(new Brush(type, size, angle));
74 if (image) {
75 // TODO add a way to add a bitmap mask
76 doc::Mask mask;
77 mask.replace(image->bounds());
78 brush->setImage(image, mask.bitmap());
79 }
80
81 if (lua_getfield(L, index, "center") != LUA_TNIL) {
82 gfx::Point center = convert_args_into_point(L, -1);
83 brush->setCenter(center);
84 }
85 lua_pop(L, 1);
86
87 if (lua_getfield(L, index, "pattern") != LUA_TNIL) {
88 BrushPattern pattern = (BrushPattern)lua_tointeger(L, -1);
89 brush->setPattern(pattern);
90 }
91 else {
92 brush->setPattern(BrushPattern::DEFAULT_FOR_SCRIPTS);
93 }
94 lua_pop(L, 1);
95
96 if (lua_getfield(L, index, "patternOrigin") != LUA_TNIL) {
97 gfx::Point patternOrigin = convert_args_into_point(L, -1);
98 brush->setPatternOrigin(patternOrigin);
99 }
100 lua_pop(L, 1);
101 }
102 else {
103 int size = lua_tointeger(L, index);
104 size = std::max(1, size);
105 brush.reset(new Brush(BrushType::kCircleBrushType, size, 0));
106 brush->setPattern(BrushPattern::DEFAULT_FOR_SCRIPTS);
107 }
108 return brush;
109}
110
111int Brush_new(lua_State* L)
112{
113 BrushRef brush = Brush_new(L, 1);
114 if (brush)
115 push_new<BrushObj>(L, brush);
116 else
117 lua_pushnil(L);
118 return 1;
119}
120
121int Brush_gc(lua_State* L)
122{
123 get_obj<BrushObj>(L, 1)->~BrushObj();
124 return 0;
125}
126
127int Brush_setFgColor(lua_State* L)
128{
129 auto obj = get_obj<BrushObj>(L, 1);
130 if (obj->brush &&
131 obj->brush->image()) {
132 const doc::color_t color = convert_args_into_pixel_color(
133 L, 2, obj->brush->image()->pixelFormat());
134 obj->brush->setImageColor(Brush::ImageColor::MainColor, color);
135 }
136 return 0;
137}
138
139int Brush_setBgColor(lua_State* L)
140{
141 auto obj = get_obj<BrushObj>(L, 1);
142 if (obj->brush &&
143 obj->brush->image()) {
144 const doc::color_t color = convert_args_into_pixel_color(
145 L, 2, obj->brush->image()->pixelFormat());
146 obj->brush->setImageColor(
147 Brush::ImageColor::BackgroundColor, color);
148 }
149 return 0;
150}
151
152int Brush_get_type(lua_State* L)
153{
154 const auto obj = get_obj<BrushObj>(L, 1);
155 lua_pushinteger(L, (int)obj->brush->type());
156 return 1;
157}
158
159int Brush_get_size(lua_State* L)
160{
161 const auto obj = get_obj<BrushObj>(L, 1);
162 lua_pushinteger(L, obj->brush->size());
163 return 1;
164}
165
166int Brush_get_angle(lua_State* L)
167{
168 const auto obj = get_obj<BrushObj>(L, 1);
169 lua_pushinteger(L, obj->brush->angle());
170 return 1;
171}
172
173int Brush_get_image(lua_State* L)
174{
175 const auto obj = get_obj<BrushObj>(L, 1);
176 if (obj->brush->type() == BrushType::kImageBrushType)
177 push_image(L, Image::createCopy(obj->brush->image()));
178 else
179 lua_pushnil(L);
180 return 1;
181}
182
183int Brush_get_center(lua_State* L)
184{
185 const auto obj = get_obj<BrushObj>(L, 1);
186 push_obj(L, obj->brush->center());
187 return 1;
188}
189
190int Brush_get_pattern(lua_State* L)
191{
192 const auto obj = get_obj<BrushObj>(L, 1);
193 lua_pushinteger(L, (int)obj->brush->pattern());
194 return 1;
195}
196
197int Brush_get_patternOrigin(lua_State* L)
198{
199 const auto obj = get_obj<BrushObj>(L, 1);
200 push_obj(L, obj->brush->patternOrigin());
201 return 1;
202}
203
204const luaL_Reg Brush_methods[] = {
205 { "__gc", Brush_gc },
206 { "setFgColor", Brush_setFgColor },
207 { "setBgColor", Brush_setBgColor },
208 { nullptr, nullptr }
209};
210
211const Property Brush_properties[] = {
212 { "type", Brush_get_type, nullptr },
213 { "size", Brush_get_size, nullptr },
214 { "angle", Brush_get_angle, nullptr },
215 { "image", Brush_get_image, nullptr },
216 { "center", Brush_get_center, nullptr },
217 { "pattern", Brush_get_pattern, nullptr },
218 { "patternOrigin", Brush_get_patternOrigin, nullptr },
219 { nullptr, nullptr, nullptr }
220};
221
222} // anonymous namespace
223
224DEF_MTNAME(BrushObj);
225
226void register_brush_class(lua_State* L)
227{
228 using Brush = BrushObj;
229 REG_CLASS(L, Brush);
230 REG_CLASS_NEW(L, Brush);
231 REG_CLASS_PROPERTIES(L, Brush);
232}
233
234void push_brush(lua_State* L, const BrushRef& brush)
235{
236 push_new<BrushObj>(L, brush);
237}
238
239BrushRef get_brush_from_arg(lua_State* L, int index)
240{
241 if (auto obj = may_get_obj<BrushObj>(L, index))
242 return obj->brush;
243 else
244 return Brush_new(L, index);
245}
246
247} // namespace script
248} // namespace app
249