opuntiaOS - an operating system targeting x86 and ARMv7
at master 27 kB view raw
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