1/*
2 * Copyright (C) 2020-2022 The opuntiaOS Project Authors.
3 * + Contributed by Nikita Melekhin <nimelehin@gmail.com>
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
9#include <algorithm>
10#include <libfoundation/Math.h>
11#include <libfoundation/Memory.h>
12#include <libg/Context.h>
13
14namespace LG {
15
16Context::Context(PixelBitmap& bitmap)
17 : m_bitmap(bitmap)
18 , m_origin_clip(0, 0, bitmap.width(), bitmap.height())
19 , m_clip(0, 0, bitmap.width(), bitmap.height())
20 , m_color()
21{
22}
23
24void Context::add_clip(const Rect& rect)
25{
26 auto r = rect;
27 r.offset_by(m_draw_offset);
28 m_clip.intersect(r);
29}
30
31void Context::reset_clip()
32{
33 m_clip = m_origin_clip;
34}
35
36void Context::set(const Point<int>& start, const PixelBitmap& bitmap)
37{
38 Rect draw_bounds(start.x() + m_draw_offset.x(), start.y() + m_draw_offset.y(), bitmap.width(), bitmap.height());
39 draw_bounds.intersect(m_clip);
40 if (draw_bounds.empty()) {
41 return;
42 }
43
44 int min_x = draw_bounds.min_x();
45 int min_y = draw_bounds.min_y();
46 int max_x = draw_bounds.max_x();
47 int max_y = draw_bounds.max_y();
48 int offset_x = -start.x() - m_draw_offset.x() + m_bitmap_offset.x();
49 int offset_y = -start.y() - m_draw_offset.y() + m_bitmap_offset.y();
50 int bitmap_x = min_x + offset_x;
51 int bitmap_y = min_y + offset_y;
52 int len_x = max_x - min_x + 1;
53 for (int y = min_y; y <= max_y; y++, bitmap_y++) {
54 LFoundation::fast_copy((uint32_t*)&m_bitmap[y][min_x], (uint32_t*)&bitmap[bitmap_y][bitmap_x], len_x);
55 }
56}
57
58void Context::set_with_bounds(const Rect& rect, const PixelBitmap& bitmap)
59{
60 auto draw_bounds = rect;
61 draw_bounds.offset_by(m_draw_offset);
62 draw_bounds.intersect(m_clip);
63 if (draw_bounds.empty()) {
64 return;
65 }
66
67 int min_x = draw_bounds.min_x();
68 int min_y = draw_bounds.min_y();
69 int max_x = draw_bounds.max_x();
70 int max_y = draw_bounds.max_y();
71 int offset_x = -rect.min_x() - m_draw_offset.x();
72 int offset_y = -rect.min_y() - m_draw_offset.y();
73 int bitmap_x = min_x + offset_x + m_bitmap_offset.x();
74 int bitmap_y = min_y + offset_y + m_bitmap_offset.y();
75 int len_x = max_x - min_x + 1;
76 for (int y = min_y; y <= max_y; y++, bitmap_y++) {
77 LFoundation::fast_copy((uint32_t*)&m_bitmap[y][min_x], (uint32_t*)&bitmap[bitmap_y][bitmap_x], len_x);
78 }
79}
80
81void Context::draw(const Point<int>& start, const PixelBitmap& bitmap)
82{
83 if (!bitmap.has_alpha_channel()) {
84 set(start, bitmap);
85 return;
86 }
87
88 Rect draw_bounds(start.x() + m_draw_offset.x(), start.y() + m_draw_offset.y(), bitmap.width(), bitmap.height());
89 draw_bounds.intersect(m_clip);
90 if (draw_bounds.empty()) {
91 return;
92 }
93
94 int min_x = draw_bounds.min_x();
95 int min_y = draw_bounds.min_y();
96 int max_x = draw_bounds.max_x();
97 int max_y = draw_bounds.max_y();
98 int offset_x = -start.x() - m_draw_offset.x() + m_bitmap_offset.x();
99 int offset_y = -start.y() - m_draw_offset.y() + m_bitmap_offset.y();
100 int bitmap_y = min_y + offset_y;
101 for (int y = min_y; y <= max_y; y++, bitmap_y++) {
102 int bitmap_x = min_x + offset_x;
103 for (int x = min_x; x <= max_x; x++, bitmap_x++) {
104 m_bitmap[y][x].mix_with(bitmap[bitmap_y][bitmap_x]);
105 }
106 }
107}
108
109void Context::draw_with_bounds(const Rect& rect, const PixelBitmap& bitmap)
110{
111 if (!bitmap.has_alpha_channel()) {
112 set_with_bounds(rect, bitmap);
113 return;
114 }
115
116 auto draw_bounds = rect;
117 draw_bounds.offset_by(m_draw_offset);
118 draw_bounds.intersect(m_clip);
119 if (draw_bounds.empty()) {
120 return;
121 }
122
123 int min_x = draw_bounds.min_x();
124 int min_y = draw_bounds.min_y();
125 int max_x = draw_bounds.max_x();
126 int max_y = draw_bounds.max_y();
127 int offset_x = -rect.min_x() - m_draw_offset.x() + m_bitmap_offset.x();
128 int offset_y = -rect.min_y() - m_draw_offset.y() + m_bitmap_offset.y();
129 int bitmap_y = min_y + offset_y;
130 for (int y = min_y; y <= max_y; y++, bitmap_y++) {
131 int bitmap_x = min_x + offset_x;
132 for (int x = min_x; x <= max_x; x++, bitmap_x++) {
133 m_bitmap[y][x].mix_with(bitmap[bitmap_y][bitmap_x]);
134 }
135 }
136}
137
138void Context::draw(const Point<int>& start, const GlyphBitmap& bitmap)
139{
140 Rect draw_bounds(start.x() + m_draw_offset.x(), start.y() + m_draw_offset.y(), bitmap.width(), bitmap.height());
141 draw_bounds.intersect(m_clip);
142 if (draw_bounds.empty()) {
143 return;
144 }
145
146 auto& color = fill_color();
147 int min_x = draw_bounds.min_x();
148 int min_y = draw_bounds.min_y();
149 int max_x = draw_bounds.max_x();
150 int max_y = draw_bounds.max_y();
151 int offset_x = -start.x() - m_draw_offset.x();
152 int offset_y = -start.y() - m_draw_offset.y();
153 int bitmap_y = min_y + offset_y;
154 for (int y = min_y; y <= max_y; y++, bitmap_y++) {
155 int bitmap_x = min_x + offset_x;
156 for (int x = min_x; x <= max_x; x++, bitmap_x++) {
157 if (bitmap.bit_at(bitmap_x, bitmap_y)) {
158 m_bitmap[y][x] = color;
159 }
160 }
161 }
162}
163
164[[gnu::flatten]] void Context::draw_rounded(const Point<int>& start, const PixelBitmap& bitmap, const CornerMask& mask)
165{
166 Rect rect(start.x(), start.y(), bitmap.width(), bitmap.height());
167 auto draw_bounds = rect;
168 draw_bounds.offset_by(m_draw_offset);
169 draw_bounds.intersect(m_clip);
170 if (draw_bounds.empty()) {
171 return;
172 }
173
174 size_t radius = std::min(mask.radius(), rect.height() / 2);
175 radius = std::min(radius, rect.width() / 2);
176
177 size_t top_radius = radius;
178 size_t bottom_radius = radius;
179 if (!mask.top_rounded()) {
180 top_radius = 0;
181 }
182 if (!mask.bottom_rounded()) {
183 bottom_radius = 0;
184 }
185
186 int width = rect.width();
187 int top_rwidth = rect.width() - 2 * top_radius;
188 int bottom_rwidth = rect.width() - 2 * bottom_radius;
189 int rheight = rect.height() - top_radius - bottom_radius;
190
191 int min_x = rect.min_x();
192 int min_y = rect.min_y();
193 int top_min_rx = rect.min_x() + top_radius;
194 int top_max_rx = rect.max_x() - top_radius;
195 int bottom_min_rx = rect.min_x() + bottom_radius;
196 int bottom_max_rx = rect.max_x() - bottom_radius;
197 int min_ry = rect.min_y() + top_radius;
198 int max_ry = rect.max_y() - bottom_radius;
199 int bitmap_bottom_x_offset = rect.width() - bottom_radius;
200 int bitmap_bottom_y_offset = rect.height() - bottom_radius;
201
202 m_bitmap_offset = { (int)top_radius, 0 };
203 draw_with_bounds(LG::Rect(top_min_rx, min_y, top_rwidth, top_radius), bitmap);
204 m_bitmap_offset = { 0, (int)top_radius };
205 draw_with_bounds(LG::Rect(min_x, min_ry, width, rheight), bitmap);
206 m_bitmap_offset = { (int)bottom_radius, bitmap_bottom_y_offset };
207 draw_with_bounds(LG::Rect(bottom_min_rx, max_ry + 1, bottom_rwidth, bottom_radius), bitmap);
208
209 auto add_instant_clip = [&](int x, int y, size_t width, size_t height) {
210 add_clip(LG::Rect(x, y, width, height));
211 };
212
213 auto orig_clip = m_clip;
214 auto reset_instant_clip = [&]() {
215 m_clip = orig_clip;
216 };
217
218 int bitmap_bottom_x_circle_offset = rect.width() - bottom_radius - bottom_radius - 1;
219 int bitmap_bottom_y_circle_offset = rect.height() - bottom_radius - bottom_radius - 1;
220 add_instant_clip(min_x, min_y, top_radius, top_radius);
221 m_bitmap_offset = { 0, 0 };
222 draw_rounded_helper({ top_min_rx, min_ry }, top_radius, bitmap);
223 reset_instant_clip();
224
225 add_instant_clip(top_max_rx + 1, min_y, top_radius, top_radius);
226 m_bitmap_offset = { bitmap_bottom_x_circle_offset, 0 };
227 draw_rounded_helper({ top_max_rx, min_ry }, top_radius, bitmap);
228 reset_instant_clip();
229
230 add_instant_clip(min_x, max_ry + 1, bottom_radius, bottom_radius);
231 m_bitmap_offset = { 0, bitmap_bottom_y_circle_offset };
232 draw_rounded_helper({ bottom_min_rx, max_ry }, bottom_radius, bitmap);
233 reset_instant_clip();
234
235 add_instant_clip(bottom_max_rx + 1, max_ry + 1, bottom_radius, bottom_radius);
236 m_bitmap_offset = { bitmap_bottom_x_circle_offset, bitmap_bottom_y_circle_offset };
237 draw_rounded_helper({ bottom_max_rx, max_ry }, bottom_radius, bitmap);
238 reset_instant_clip();
239 m_bitmap_offset = { 0, 0 };
240}
241
242void Context::mix(const Rect& rect)
243{
244 auto draw_bounds = rect;
245 draw_bounds.offset_by(m_draw_offset);
246 draw_bounds.intersect(m_clip);
247
248 if (draw_bounds.empty()) {
249 return;
250 }
251
252 int min_x = draw_bounds.min_x();
253 int min_y = draw_bounds.min_y();
254 int max_x = draw_bounds.max_x();
255 int max_y = draw_bounds.max_y();
256 const auto& color = fill_color();
257 for (int y = min_y; y <= max_y; y++) {
258 for (int x = min_x; x <= max_x; x++) {
259 m_bitmap[y][x].mix_with(color);
260 }
261 }
262}
263
264void Context::fill(const Rect& rect)
265{
266 if (fill_color().is_opaque()) {
267 return;
268 }
269
270 if (fill_color().alpha() != 255) {
271 mix(rect);
272 return;
273 }
274
275 auto draw_bounds = rect;
276 draw_bounds.offset_by(m_draw_offset);
277 draw_bounds.intersect(m_clip);
278
279 if (draw_bounds.empty()) {
280 return;
281 }
282
283 auto color = fill_color().u32();
284 int min_x = draw_bounds.min_x();
285 int min_y = draw_bounds.min_y();
286 int max_x = draw_bounds.max_x();
287 int max_y = draw_bounds.max_y();
288 int len_x = max_x - min_x + 1;
289 for (int y = min_y; y <= max_y; y++) {
290 LFoundation::fast_set((uint32_t*)&m_bitmap[y][min_x], color, len_x);
291 }
292}
293
294void Context::draw_rounded_helper(const Point<int>& start, size_t radius, const PixelBitmap& bitmap)
295{
296 if (!radius) {
297 return;
298 }
299
300 auto center = start;
301 center.offset_by(m_draw_offset);
302 auto draw_bounds = Rect(center.x() - radius, center.y() - radius, radius * 2 + 1, radius * 2 + 1);
303 draw_bounds.intersect(m_clip);
304
305 if (draw_bounds.empty()) {
306 return;
307 }
308
309 int radius2 = radius * radius;
310 int min_x = draw_bounds.min_x();
311 int min_y = draw_bounds.min_y();
312 int max_x = draw_bounds.max_x();
313 int max_y = draw_bounds.max_y();
314 int offset_x = -(start.x() - radius) - m_draw_offset.x() + m_bitmap_offset.x();
315 int offset_y = -(start.y() - radius) - m_draw_offset.y() + m_bitmap_offset.y();
316 int bitmap_y = min_y + offset_y;
317
318 for (int y = min_y; y <= max_y; y++) {
319 int bitmap_x = min_x + offset_x;
320 for (int x = min_x; x <= max_x; x++) {
321 int x2 = (x - center.x()) * (x - center.x());
322 int y2 = (y - center.y()) * (y - center.y());
323 int dist = x2 + y2;
324 if (dist <= radius2) {
325 m_bitmap[y][x].mix_with(bitmap[bitmap_y][bitmap_x]);
326 } else {
327 auto color = bitmap[bitmap_y][bitmap_x];
328 float fdist = 0.5 - (LFoundation::fast_sqrt((float)(dist)) - radius);
329 fdist = std::max(std::min(fdist, 1.0f), 0.0f);
330 int alpha = int(color.alpha() * fdist);
331 color.set_alpha(alpha);
332 m_bitmap[y][x].mix_with(color);
333 }
334 }
335 }
336}
337
338void Context::fill_rounded_helper(const Point<int>& start, size_t radius)
339{
340 if (!radius) {
341 return;
342 }
343
344 auto center = start;
345 center.offset_by(m_draw_offset);
346 auto draw_bounds = Rect(center.x() - radius, center.y() - radius, radius * 2 + 1, radius * 2 + 1);
347 draw_bounds.intersect(m_clip);
348
349 if (draw_bounds.empty()) {
350 return;
351 }
352
353 auto color = fill_color();
354 int min_x = draw_bounds.min_x();
355 int min_y = draw_bounds.min_y();
356 int max_x = draw_bounds.max_x();
357 int max_y = draw_bounds.max_y();
358 int radius2 = radius * radius;
359 for (int y = min_y; y <= max_y; y++) {
360 for (int x = min_x; x <= max_x; x++) {
361 int x2 = (x - center.x()) * (x - center.x());
362 int y2 = (y - center.y()) * (y - center.y());
363 int dist = x2 + y2;
364 if (dist <= radius2) {
365 m_bitmap[y][x].mix_with(fill_color());
366 } else {
367 float fdist = 0.5 - (LFoundation::fast_sqrt((float)(dist)) - radius);
368 fdist = std::max(std::min(fdist, 1.0f), 0.0f);
369 int alpha = int(fill_color().alpha() * fdist);
370 color.set_alpha(alpha);
371 m_bitmap[y][x].mix_with(color);
372 }
373 }
374 }
375}
376
377void Context::shadow_rounded_helper(const Point<int>& start, size_t radius, const Shading& shading)
378{
379 if (!radius) {
380 return;
381 }
382
383 auto center = start;
384 center.offset_by(m_draw_offset);
385 uint32_t hw = shading.spread() * 2 + radius * 2 + 1;
386 auto draw_bounds = Rect(center.x() - radius - shading.spread(), center.y() - radius - shading.spread(), hw, hw);
387 draw_bounds.intersect(m_clip);
388
389 if (draw_bounds.empty()) {
390 return;
391 }
392
393 auto color = fill_color();
394 float shading_spread = shading.spread();
395 float std_alpha = color.alpha();
396 std_alpha /= (shading_spread - 1);
397 int min_x = draw_bounds.min_x();
398 int min_y = draw_bounds.min_y();
399 int max_x = draw_bounds.max_x();
400 int max_y = draw_bounds.max_y();
401 int radius2 = radius * radius;
402 for (int y = min_y; y <= max_y; y++) {
403 for (int x = min_x; x <= max_x; x++) {
404 int x2 = (x - center.x()) * (x - center.x());
405 int y2 = (y - center.y()) * (y - center.y());
406 int dist = x2 + y2;
407 if (dist > radius2) {
408 float fdist = LFoundation::fast_sqrt((float)(dist)) - radius;
409 if (fdist > 0.1) {
410 fdist = shading_spread - fdist;
411 fdist = std::max(fdist, 0.0f);
412 int alpha = std_alpha * fdist;
413 color.set_alpha(alpha);
414 m_bitmap[y][x].mix_with(color);
415 }
416 }
417 }
418 }
419}
420
421[[gnu::flatten]] void Context::fill_rounded(const Rect& rect, const CornerMask& mask)
422{
423 if (mask.radius() == 0) {
424 return fill(rect);
425 }
426
427 auto draw_bounds = rect;
428 draw_bounds.offset_by(m_draw_offset);
429 draw_bounds.intersect(m_clip);
430 if (draw_bounds.empty()) {
431 return;
432 }
433
434 size_t radius = std::min(mask.radius(), rect.height() / 2);
435 radius = std::min(radius, rect.width() / 2);
436
437 size_t top_radius = radius;
438 size_t bottom_radius = radius;
439 if (!mask.top_rounded()) {
440 top_radius = 0;
441 }
442 if (!mask.bottom_rounded()) {
443 bottom_radius = 0;
444 }
445
446 int width = rect.width();
447 int top_rwidth = rect.width() - 2 * top_radius;
448 int bottom_rwidth = rect.width() - 2 * bottom_radius;
449 int rheight = rect.height() - top_radius - bottom_radius;
450
451 int min_x = rect.min_x();
452 int min_y = rect.min_y();
453 int top_min_rx = rect.min_x() + top_radius;
454 int top_max_rx = rect.max_x() - top_radius;
455 int bottom_min_rx = rect.min_x() + bottom_radius;
456 int bottom_max_rx = rect.max_x() - bottom_radius;
457 int min_ry = rect.min_y() + top_radius;
458 int max_ry = rect.max_y() - bottom_radius;
459
460 fill(LG::Rect(top_min_rx, min_y, top_rwidth, top_radius));
461 fill(LG::Rect(min_x, min_ry, width, rheight));
462 fill(LG::Rect(bottom_min_rx, max_ry + 1, bottom_rwidth, bottom_radius));
463
464 auto add_instant_clip = [&](int x, int y, size_t width, size_t height) {
465 add_clip(LG::Rect(x, y, width, height));
466 };
467
468 auto orig_clip = m_clip;
469 auto reset_instant_clip = [&]() {
470 m_clip = orig_clip;
471 };
472
473 add_instant_clip(min_x, min_y, top_radius, top_radius);
474 fill_rounded_helper({ top_min_rx, min_ry }, top_radius);
475 reset_instant_clip();
476
477 add_instant_clip(top_max_rx + 1, min_y, top_radius, top_radius);
478 fill_rounded_helper({ top_max_rx, min_ry }, top_radius);
479 reset_instant_clip();
480
481 add_instant_clip(min_x, max_ry + 1, bottom_radius, bottom_radius);
482 fill_rounded_helper({ bottom_min_rx, max_ry }, bottom_radius);
483 reset_instant_clip();
484
485 add_instant_clip(bottom_max_rx + 1, max_ry + 1, bottom_radius, bottom_radius);
486 fill_rounded_helper({ bottom_max_rx, max_ry }, bottom_radius);
487 reset_instant_clip();
488}
489
490void Context::draw_shading(const Rect& rect, const Shading& shading)
491{
492 auto draw_bounds = rect;
493 draw_bounds.offset_by(m_draw_offset);
494 auto orig_bounds = draw_bounds;
495 draw_bounds.intersect(m_clip);
496
497 if (draw_bounds.empty()) {
498 return;
499 }
500
501 int min_x = draw_bounds.min_x();
502 int min_y = draw_bounds.min_y();
503 int max_x = draw_bounds.max_x();
504 int max_y = draw_bounds.max_y();
505 auto color = fill_color();
506
507 int alpha_diff = color.alpha() - shading.final_alpha();
508 int step, skipped_steps, end_x;
509
510 switch (shading.type()) {
511 case Shading::Type::TopToBottom:
512 step = alpha_diff / (orig_bounds.height());
513 skipped_steps = min_y - orig_bounds.min_y();
514 color.set_alpha(color.alpha() - skipped_steps * step);
515
516 for (int y = min_y; y <= max_y; y++) {
517 for (int x = min_x; x <= max_x; x++) {
518 m_bitmap[y][x].mix_with(color);
519 }
520 color.set_alpha(color.alpha() - step);
521 }
522 return;
523
524 case Shading::Type::BottomToTop:
525 step = alpha_diff / (orig_bounds.height());
526 skipped_steps = orig_bounds.max_y() - max_y;
527 color.set_alpha(color.alpha() - skipped_steps * step);
528
529 for (int y = max_y; y >= min_y; y--) {
530 for (int x = min_x; x <= max_x; x++) {
531 m_bitmap[y][x].mix_with(color);
532 }
533 color.set_alpha(color.alpha() - step);
534 }
535 return;
536
537 case Shading::Type::LeftToRight:
538 step = alpha_diff / orig_bounds.width();
539 skipped_steps = min_x - orig_bounds.min_x();
540 color.set_alpha(color.alpha() - skipped_steps * step);
541
542 for (int x = min_x; x <= max_x; x++) {
543 for (int y = min_y; y <= max_y; y++) {
544 m_bitmap[y][x].mix_with(color);
545 }
546 color.set_alpha(color.alpha() - step);
547 }
548 return;
549
550 case Shading::Type::RightToLeft:
551 step = alpha_diff / orig_bounds.width();
552 skipped_steps = orig_bounds.max_x() - max_x;
553 color.set_alpha(color.alpha() - skipped_steps * step);
554
555 for (int x = max_x; x >= min_x; x--) {
556 for (int y = min_y; y <= max_y; y++) {
557 m_bitmap[y][x].mix_with(color);
558 }
559 color.set_alpha(color.alpha() - step);
560 }
561 return;
562
563 case Shading::Type::Deg45:
564 step = alpha_diff / (orig_bounds.height() - 1);
565 skipped_steps = orig_bounds.max_y() - max_y + min_x - orig_bounds.min_x();
566 if (skipped_steps >= orig_bounds.height()) {
567 return;
568 }
569
570 color.set_alpha(color.alpha() - skipped_steps * step);
571 end_x = std::min(min_x + (int)orig_bounds.height() - skipped_steps, max_x);
572
573 for (int y = max_y; y >= min_y; y--) {
574 auto cur_color = color;
575 for (int x = min_x; x <= end_x; x++) {
576 m_bitmap[y][x].mix_with(cur_color);
577 cur_color.set_alpha(cur_color.alpha() - step);
578 }
579 end_x--;
580 if (!end_x) {
581 return;
582 }
583
584 color.set_alpha(color.alpha() - step);
585 }
586 return;
587
588 case Shading::Type::Deg315:
589 step = alpha_diff / (orig_bounds.height() - 1);
590 skipped_steps = min_y - orig_bounds.min_y() + min_x - orig_bounds.min_x();
591 if (skipped_steps >= orig_bounds.height()) {
592 return;
593 }
594
595 color.set_alpha(color.alpha() - skipped_steps * step);
596 end_x = std::min(min_x + (int)orig_bounds.height() - skipped_steps, max_x);
597
598 for (int y = min_y; y <= max_y; y++) {
599 auto cur_color = color;
600 for (int x = min_x; x <= end_x; x++) {
601 m_bitmap[y][x].mix_with(cur_color);
602 cur_color.set_alpha(cur_color.alpha() - step);
603 }
604 end_x--;
605 if (!end_x) {
606 return;
607 }
608
609 color.set_alpha(color.alpha() - step);
610 }
611 return;
612
613 case Shading::Type::Deg135:
614 step = alpha_diff / (orig_bounds.height() - 1);
615 skipped_steps = orig_bounds.max_y() - max_y + orig_bounds.max_x() - max_x;
616 if (skipped_steps >= orig_bounds.height()) {
617 return;
618 }
619
620 color.set_alpha(color.alpha() - skipped_steps * step);
621 end_x = std::max(max_x - ((int)orig_bounds.height() - skipped_steps), min_x);
622
623 for (int y = max_y; y >= min_y; y--) {
624 auto cur_color = color;
625 for (int x = max_x; x >= end_x; x--) {
626 m_bitmap[y][x].mix_with(cur_color);
627 cur_color.set_alpha(cur_color.alpha() - step);
628 }
629 end_x++;
630 if (end_x == max_x) {
631 return;
632 }
633
634 color.set_alpha(color.alpha() - step);
635 }
636 return;
637
638 case Shading::Type::Deg225:
639 step = alpha_diff / (orig_bounds.height() - 1);
640 skipped_steps = min_y - orig_bounds.min_y() + orig_bounds.max_x() - max_x;
641 if (skipped_steps >= orig_bounds.height()) {
642 return;
643 }
644
645 color.set_alpha(color.alpha() - skipped_steps * step);
646 end_x = std::max(max_x - ((int)orig_bounds.height() - skipped_steps), min_x);
647
648 for (int y = min_y; y <= max_y; y++) {
649 auto cur_color = color;
650 for (int x = max_x; x >= end_x; x--) {
651 m_bitmap[y][x].mix_with(cur_color);
652 cur_color.set_alpha(cur_color.alpha() - step);
653 }
654 end_x++;
655 if (end_x == max_x) {
656 return;
657 }
658
659 color.set_alpha(color.alpha() - step);
660 }
661 return;
662
663 default:
664 break;
665 }
666}
667
668void Context::draw_box_shading(const Rect& rect, const Shading& shading, const CornerMask& mask)
669{
670 size_t top_radius = mask.radius();
671 size_t bottom_radius = mask.radius();
672 if (!mask.top_rounded()) {
673 top_radius = 0;
674 }
675 if (!mask.bottom_rounded()) {
676 bottom_radius = 0;
677 }
678
679 int shading_spread = shading.spread();
680
681 int rwidth = rect.width() - 2 * top_radius;
682 int rheight = rect.height() - top_radius - bottom_radius;
683
684 int top_min_rx = rect.min_x() + top_radius;
685 int top_min_ry = rect.min_y() + top_radius;
686 int bottom_min_rx = rect.min_x() + bottom_radius;
687 int bottom_max_ry = rect.max_y() - bottom_radius;
688 int top_max_rx = rect.max_x() - top_radius;
689 int bottom_max_rx = rect.max_x() - bottom_radius;
690 int min_shading_x = rect.min_x() - shading_spread;
691 int max_shading_x = rect.max_x() + 1;
692 int min_shading_y = rect.min_y() - shading_spread;
693 int max_shading_y = rect.max_y() + 1;
694
695 draw_shading(LG::Rect(top_min_rx, min_shading_y, rwidth - 1, shading_spread), LG::Shading(LG::Shading::Type::BottomToTop, shading.final_alpha()));
696 draw_shading(LG::Rect(top_min_rx, max_shading_y, rwidth - 1, shading_spread), LG::Shading(LG::Shading::Type::TopToBottom, shading.final_alpha()));
697 draw_shading(LG::Rect(min_shading_x, top_min_ry, shading_spread, rheight - 1), LG::Shading(LG::Shading::Type::RightToLeft, shading.final_alpha()));
698 draw_shading(LG::Rect(max_shading_x, top_min_ry, shading_spread, rheight - 1), LG::Shading(LG::Shading::Type::LeftToRight, shading.final_alpha()));
699
700 auto add_instant_clip = [&](int x, int y, size_t width, size_t height) {
701 add_clip(LG::Rect(x, y, width, height));
702 };
703
704 auto orig_clip = m_clip;
705 auto reset_instant_clip = [&]() {
706 m_clip = orig_clip;
707 };
708
709 int top_shading_dims = top_radius + shading_spread;
710 int bottom_shading_dims = bottom_radius + shading_spread;
711
712 if (mask.top_rounded()) {
713 add_instant_clip(min_shading_x, min_shading_y, top_shading_dims, top_shading_dims);
714 shadow_rounded_helper({ top_min_rx, top_min_ry }, top_radius, shading);
715 reset_instant_clip();
716
717 add_instant_clip(top_max_rx, min_shading_y, top_shading_dims, top_shading_dims);
718 shadow_rounded_helper({ top_max_rx, top_min_ry }, top_radius, shading);
719 reset_instant_clip();
720 } else {
721 draw_shading(LG::Rect(min_shading_x, min_shading_y, shading_spread, shading_spread), LG::Shading(LG::Shading::Type::Deg135, 0));
722 draw_shading(LG::Rect(top_max_rx, min_shading_y, shading_spread, shading_spread), LG::Shading(LG::Shading::Type::Deg45, 0));
723 }
724
725 if (mask.bottom_rounded()) {
726 add_instant_clip(min_shading_x, bottom_max_ry, bottom_shading_dims, bottom_shading_dims);
727 shadow_rounded_helper({ bottom_min_rx, bottom_max_ry }, bottom_radius, shading);
728 reset_instant_clip();
729
730 add_instant_clip(bottom_max_rx, bottom_max_ry, bottom_shading_dims, bottom_shading_dims);
731 shadow_rounded_helper({ bottom_max_rx, bottom_max_ry }, bottom_radius, shading);
732 reset_instant_clip();
733 } else {
734 draw_shading(LG::Rect(min_shading_x, bottom_max_ry, shading_spread, shading_spread), LG::Shading(LG::Shading::Type::Deg225, 0));
735 draw_shading(LG::Rect(bottom_max_rx, bottom_max_ry, shading_spread, shading_spread), LG::Shading(LG::Shading::Type::Deg315, 0));
736 }
737}
738
739void Context::add_ellipse(const Rect& rect)
740{
741 int rx = rect.width() / 2;
742 int ry = rect.height() / 2;
743 int xc = rect.mid_x();
744 int yc = rect.mid_y();
745
746 double dx, dy, d1, d2, x, y;
747 double tmp_d1, tmp_d2;
748 x = 0;
749 y = ry;
750
751 d1 = (ry * ry) - (rx * rx * ry) + (0.25 * rx * rx);
752 dx = 2 * ry * ry * x;
753 dy = 2 * rx * rx * y;
754
755 while (dx < dy) {
756 m_bitmap[y + yc][(int)x + xc] = fill_color();
757 m_bitmap[y + yc][(int)-x + xc] = fill_color();
758 m_bitmap[-y + yc][(int)x + xc] = fill_color();
759 m_bitmap[-y + yc][(int)-x + xc] = fill_color();
760
761 x++;
762 dx += 2 * ry * ry;
763 tmp_d1 = d1;
764 d1 += ry * ry + dx;
765 if (tmp_d1 >= 0) {
766 y--;
767 dy -= 2 * rx * rx;
768 d1 -= dy;
769 }
770 }
771
772 d2 = ((ry * ry) * ((x + 0.5) * (x + 0.5))) + ((rx * rx) * ((y - 1) * (y - 1))) - (rx * rx * ry * ry);
773
774 while (y >= 0) {
775 m_bitmap[y + yc][(int)x + xc] = fill_color();
776 m_bitmap[y + yc][(int)-x + xc] = fill_color();
777 m_bitmap[-y + yc][(int)x + xc] = fill_color();
778 m_bitmap[-y + yc][(int)-x + xc] = fill_color();
779
780 y--;
781 dy -= 2 * rx * rx;
782 tmp_d2 = d2;
783 d2 += rx * rx - dy;
784 if (tmp_d2 <= 0) {
785 x++;
786 dx += 2 * ry * ry;
787 d2 += dx;
788 }
789 }
790}
791
792} // namespace LG