|
14 | 14 |
|
15 | 15 | #include "twin_private.h"
|
16 | 16 |
|
| 17 | +#define TWIN_TITLE_HEIGHT 20 |
| 18 | + |
17 | 19 | /* op, src, dst */
|
18 | 20 | static const twin_src_op comp2[2][4][3] = {
|
19 | 21 | [TWIN_OVER] =
|
@@ -302,6 +304,207 @@ static const twin_src_msk_op comp3[2][4][4][3] = {
|
302 | 304 | #define operand_index(o) \
|
303 | 305 | ((o)->source_kind == TWIN_SOLID ? 3 : o->u.pixmap->format)
|
304 | 306 |
|
| 307 | +#define _twin_add_ARGB(s, d, i, t) (((t) = (s) + twin_get_8(d, i))) |
| 308 | +#define _twin_add(s, d, t) (((t) = (s) + (d))) |
| 309 | +#define _twin_div(d, den, i, t) \ |
| 310 | + (((t) = (d) / (den)), (t) = twin_get_8((t), 0), \ |
| 311 | + (twin_argb32_t) twin_sat(t) << (i)) |
| 312 | +#define _twin_sub_ARGB(s, d, i, t) (((t) = (s) - twin_get_8(d, i))) |
| 313 | +#define _twin_sub(s, d, t) (((t) = (s) - (d))) |
| 314 | +#define twin_put_8(d, i, t) (((t) = (d) << (i))) |
| 315 | + |
| 316 | +void twin_stack(twin_pixmap_t *trg_px, |
| 317 | + twin_pixmap_t *src_px, |
| 318 | + int radius, |
| 319 | + int first_str, |
| 320 | + int first_end, |
| 321 | + int second_str, |
| 322 | + int second_end, |
| 323 | + bool horiz_scan) |
| 324 | +{ |
| 325 | + int den = (radius + 1) * (radius + 1); |
| 326 | + twin_pointer_t src_ptr, trg_ptr, old_ptr, new_ptr; |
| 327 | + twin_argb32_t sumInR, sumOutR, sumR, sumInG, sumOutG, sumG, sumInB, sumOutB, |
| 328 | + sumB, _cur, _old, _new, _src; |
| 329 | + uint16_t t1, t2, t3; |
| 330 | + for (int first = first_str; first < first_end; first++) { |
| 331 | + sumInR = sumOutR = sumR = sumInG = sumOutG = sumG = sumInB = sumOutB = |
| 332 | + sumB = 0x00000000; |
| 333 | + |
| 334 | + /* Initialize SumOut by padding */ |
| 335 | + if (horiz_scan) |
| 336 | + src_ptr = twin_pixmap_pointer(src_px, second_str, first); |
| 337 | + else |
| 338 | + src_ptr = twin_pixmap_pointer(src_px, first, second_str); |
| 339 | + _src = *src_ptr.argb32; |
| 340 | + |
| 341 | + for (int i = second_str; i < second_str + radius; i++) { |
| 342 | + sumOutR = _twin_add_ARGB(sumOutR, _src, 0, t1); |
| 343 | + sumOutG = _twin_add_ARGB(sumOutG, _src, 8, t2); |
| 344 | + sumOutB = _twin_add_ARGB(sumOutB, _src, 16, t3); |
| 345 | + for (int j = 0; j < (i - second_str) + 1; j++) { |
| 346 | + sumR = _twin_add_ARGB(sumR, _src, 0, t1); |
| 347 | + sumG = _twin_add_ARGB(sumG, _src, 8, t2); |
| 348 | + sumB = _twin_add_ARGB(sumB, _src, 16, t3); |
| 349 | + } |
| 350 | + } |
| 351 | + |
| 352 | + /* Initialize SumIn */ |
| 353 | + for (int i = second_str; i < second_str + radius; i++) { |
| 354 | + if (horiz_scan) |
| 355 | + src_ptr = twin_pixmap_pointer(src_px, i, first); |
| 356 | + else |
| 357 | + src_ptr = twin_pixmap_pointer(src_px, first, i); |
| 358 | + _src = *src_ptr.argb32; |
| 359 | + sumInR = _twin_add_ARGB(sumInR, _src, 0, t1); |
| 360 | + sumInG = _twin_add_ARGB(sumInG, _src, 8, t2); |
| 361 | + sumInB = _twin_add_ARGB(sumInB, _src, 16, t3); |
| 362 | + for (int j = 0; j < radius - (i - second_str); j++) { |
| 363 | + sumR = _twin_add_ARGB(sumR, _src, 0, t1); |
| 364 | + sumG = _twin_add_ARGB(sumG, _src, 8, t2); |
| 365 | + sumB = _twin_add_ARGB(sumB, _src, 16, t3); |
| 366 | + } |
| 367 | + } |
| 368 | + |
| 369 | + for (int cur = second_str; cur < second_end; cur++) { |
| 370 | + if (horiz_scan) { |
| 371 | + src_ptr = twin_pixmap_pointer(src_px, cur, first); |
| 372 | + trg_ptr = twin_pixmap_pointer(trg_px, cur, first); |
| 373 | + old_ptr = twin_pixmap_pointer( |
| 374 | + src_px, max(cur - radius, second_str), first); |
| 375 | + new_ptr = twin_pixmap_pointer( |
| 376 | + src_px, min(cur + radius, second_end - 1), first); |
| 377 | + } else { |
| 378 | + src_ptr = twin_pixmap_pointer(src_px, first, cur); |
| 379 | + trg_ptr = twin_pixmap_pointer(trg_px, first, cur); |
| 380 | + old_ptr = twin_pixmap_pointer(src_px, first, |
| 381 | + max(cur - radius, second_str)); |
| 382 | + new_ptr = twin_pixmap_pointer( |
| 383 | + src_px, first, min(cur + radius, second_end - 1)); |
| 384 | + } |
| 385 | + _cur = *src_ptr.argb32; |
| 386 | + _old = *old_ptr.argb32; |
| 387 | + _new = *new_ptr.argb32; |
| 388 | + /* STEP 1 : sum_out + current */ |
| 389 | + sumOutR = _twin_add_ARGB(sumOutR, _cur, 0, t1); |
| 390 | + sumOutG = _twin_add_ARGB(sumOutG, _cur, 8, t2); |
| 391 | + sumOutB = _twin_add_ARGB(sumOutB, _cur, 16, t3); |
| 392 | + /* STEP 2 : sum_in + new */ |
| 393 | + sumInR = _twin_add_ARGB(sumInR, _new, 0, t1); |
| 394 | + sumInG = _twin_add_ARGB(sumInG, _new, 8, t2); |
| 395 | + sumInB = _twin_add_ARGB(sumInB, _new, 16, t3); |
| 396 | + /* STEP 3 : sum + sum_in */ |
| 397 | + sumR = _twin_add(sumR, sumInR, t1); |
| 398 | + sumG = _twin_add(sumG, sumInG, t2); |
| 399 | + sumB = _twin_add(sumB, sumInB, t3); |
| 400 | + /* STEP 4 : sum / denominator */ |
| 401 | + *trg_ptr.argb32 = |
| 402 | + (_twin_div(sumR, den, 0, t1) | _twin_div(sumG, den, 8, t2) | |
| 403 | + _twin_div(sumB, den, 16, t3) | (*src_ptr.argb32 & 0xff000000)); |
| 404 | + /* STEP 5 : sum - sum_out */ |
| 405 | + sumR = _twin_sub(sumR, sumOutR, t1); |
| 406 | + sumG = _twin_sub(sumG, sumOutG, t2); |
| 407 | + sumB = _twin_sub(sumB, sumOutB, t3); |
| 408 | + /* STEP 6 : sum_out - old */ |
| 409 | + sumOutR = _twin_sub_ARGB(sumOutR, _old, 0, t1); |
| 410 | + sumOutG = _twin_sub_ARGB(sumOutG, _old, 8, t2); |
| 411 | + sumOutB = _twin_sub_ARGB(sumOutB, _old, 16, t3); |
| 412 | + /* STEP 7 : sum_in - current */ |
| 413 | + sumInR = _twin_sub_ARGB(sumInR, _cur, 0, t1); |
| 414 | + sumInG = _twin_sub_ARGB(sumInG, _cur, 8, t2); |
| 415 | + sumInB = _twin_sub_ARGB(sumInB, _cur, 16, t3); |
| 416 | + } |
| 417 | + } |
| 418 | +} |
| 419 | + |
| 420 | +void twin_stack_blur(twin_pixmap_t *px, |
| 421 | + int radius, |
| 422 | + twin_coord_t left, |
| 423 | + twin_coord_t right, |
| 424 | + twin_coord_t top, |
| 425 | + twin_coord_t bottom) |
| 426 | +{ |
| 427 | + if (px->format != TWIN_ARGB32) |
| 428 | + return; |
| 429 | + twin_pixmap_t *tmp_px = |
| 430 | + twin_pixmap_create(px->format, px->width, px->height); |
| 431 | + memcpy(tmp_px->p.v, px->p.v, |
| 432 | + px->width * px->height * twin_bytes_per_pixel(px->format)); |
| 433 | + /* |
| 434 | + * Originally, performing a 2D convolution on each pixel takes O(width * |
| 435 | + * height * k²). However, by first scanning horizontally and then vertically |
| 436 | + * across the pixel map, and applying a 1D convolution to each pixel, the |
| 437 | + * complexity is reduced to O(2 * width * height * k). |
| 438 | + */ |
| 439 | + twin_stack(tmp_px, px, radius, top, bottom, left, right, true); |
| 440 | + twin_stack(px, tmp_px, radius, left, right, top, bottom, false); |
| 441 | + twin_pixmap_destroy(tmp_px); |
| 442 | + return; |
| 443 | +} |
| 444 | + |
| 445 | +void twin_shadow_border(twin_pixmap_t *shadow) |
| 446 | +{ |
| 447 | + twin_coord_t right_edge = |
| 448 | + (*shadow).width - (*shadow).window->shadow_offset_x, |
| 449 | + bottom_edge = |
| 450 | + (*shadow).height - (*shadow).window->shadow_offset_y, |
| 451 | + right_span = right_edge, clip, stride = 2; |
| 452 | + twin_pointer_t dst; |
| 453 | + twin_source_u src; |
| 454 | + twin_argb32_t color_x, color_y = 0xff000000, color_shift = 0x0e000000; |
| 455 | + for (twin_coord_t y = TWIN_TITLE_HEIGHT; y < (*shadow).height; y++) { |
| 456 | + color_x = 0xff000000; |
| 457 | + /* Render the right edge of the shadow border. */ |
| 458 | + if (y < bottom_edge) { |
| 459 | + /* |
| 460 | + * Create a shadow with a diagonal shape, extending from the |
| 461 | + * top-left to the bottom-right. |
| 462 | + */ |
| 463 | + clip = max( |
| 464 | + 0, shadow->window->shadow_offset_x - (y - TWIN_TITLE_HEIGHT)); |
| 465 | + for (twin_coord_t x = right_edge; x < (*shadow).width - clip; x++) { |
| 466 | + dst = twin_pixmap_pointer(shadow, x, y); |
| 467 | + src.c = color_x; |
| 468 | + _twin_c_over_argb32(dst, src, 1); |
| 469 | + if ((x - right_edge) % stride == 0) { |
| 470 | + if (color_x > color_shift) |
| 471 | + color_x -= color_shift; |
| 472 | + color_x &= 0xff000000; |
| 473 | + } |
| 474 | + } |
| 475 | + } else |
| 476 | + /* Calculate the range of the corner. */ |
| 477 | + right_span++; |
| 478 | + |
| 479 | + /* Render the bottom edge of the shadow border. */ |
| 480 | + if (y > bottom_edge) { |
| 481 | + /* |
| 482 | + * Create a shadow with a diagonal shape, extending from the |
| 483 | + * top-left to the bottom-right. |
| 484 | + */ |
| 485 | + clip = max(0, y - bottom_edge); |
| 486 | + dst = twin_pixmap_pointer(shadow, clip, y); |
| 487 | + src.c = color_y; |
| 488 | + _twin_c_over_argb32(dst, src, right_edge - clip); |
| 489 | + |
| 490 | + /* Render the bottom-right corner of the shadow border. */ |
| 491 | + for (twin_coord_t i = 0; i < right_span - right_edge; i++) { |
| 492 | + /* The corner's pixels are symmetrical to the diagonal. */ |
| 493 | + dst = twin_pixmap_pointer(shadow, right_edge + i, y); |
| 494 | + _twin_c_over_argb32(dst, src, 1); |
| 495 | + dst = twin_pixmap_pointer( |
| 496 | + shadow, right_edge + (y - bottom_edge), bottom_edge + i); |
| 497 | + _twin_c_over_argb32(dst, src, 1); |
| 498 | + } |
| 499 | + if ((shadow->y + y - bottom_edge) % stride == 0) { |
| 500 | + if (color_y > color_shift) |
| 501 | + color_y -= color_shift; |
| 502 | + color_y &= 0xff000000; |
| 503 | + } |
| 504 | + } |
| 505 | + } |
| 506 | +} |
| 507 | + |
305 | 508 | /* FIXME: source clipping is busted */
|
306 | 509 | static void _twin_composite_simple(twin_pixmap_t *dst,
|
307 | 510 | twin_coord_t dst_x,
|
@@ -777,3 +980,15 @@ void twin_fill(twin_pixmap_t *dst,
|
777 | 980 | (*op)(twin_pixmap_pointer(dst, left, iy), src, right - left);
|
778 | 981 | twin_pixmap_damage(dst, left, top, right, bottom);
|
779 | 982 | }
|
| 983 | +void twin_cover(twin_pixmap_t *dst, |
| 984 | + twin_argb32_t color, |
| 985 | + twin_coord_t x, |
| 986 | + twin_coord_t y, |
| 987 | + twin_coord_t width) |
| 988 | +{ |
| 989 | + twin_pointer_t pt; |
| 990 | + for (twin_coord_t i = 0; i < width; i++) { |
| 991 | + pt = twin_pixmap_pointer(dst, x + i, y); |
| 992 | + *pt.argb32 = color; |
| 993 | + } |
| 994 | +} |
0 commit comments