|
7 | 7 | #include <pixman.h>
|
8 | 8 | #include "twin_private.h"
|
9 | 9 |
|
| 10 | +#define TWIN_TITLE_HEIGHT 20 |
| 11 | + |
10 | 12 | static void twin_argb32_to_pixman_color(twin_argb32_t argb,
|
11 | 13 | pixman_color_t *color)
|
12 | 14 | {
|
@@ -61,6 +63,210 @@ static void pixmap_matrix_scale(pixman_image_t *src, twin_matrix_t *matrix)
|
61 | 63 | pixman_image_set_transform(src, &transform);
|
62 | 64 | }
|
63 | 65 |
|
| 66 | +#define operand_index(o) \ |
| 67 | + ((o)->source_kind == TWIN_SOLID ? 3 : o->u.pixmap->format) |
| 68 | + |
| 69 | +#define _twin_add_ARGB(s, d, i, t) (((t) = (s) + twin_get_8(d, i))) |
| 70 | +#define _twin_add(s, d, t) (((t) = (s) + (d))) |
| 71 | +#define _twin_div(d, den, i, t) \ |
| 72 | + (((t) = (d) / (den)), (t) = twin_get_8((t), 0), \ |
| 73 | + (twin_argb32_t) twin_sat(t) << (i)) |
| 74 | +#define _twin_sub_ARGB(s, d, i, t) (((t) = (s) - twin_get_8(d, i))) |
| 75 | +#define _twin_sub(s, d, t) (((t) = (s) - (d))) |
| 76 | +#define twin_put_8(d, i, t) (((t) = (d) << (i))) |
| 77 | + |
| 78 | +void twin_stack(twin_pixmap_t *trg_px, |
| 79 | + twin_pixmap_t *src_px, |
| 80 | + int radius, |
| 81 | + int first_str, |
| 82 | + int first_end, |
| 83 | + int second_str, |
| 84 | + int second_end, |
| 85 | + bool horiz_scan) |
| 86 | +{ |
| 87 | + int den = (radius + 1) * (radius + 1); |
| 88 | + twin_pointer_t src_ptr, trg_ptr, old_ptr, new_ptr; |
| 89 | + twin_argb32_t sumInR, sumOutR, sumR, sumInG, sumOutG, sumG, sumInB, sumOutB, |
| 90 | + sumB, _cur, _old, _new, _src; |
| 91 | + uint16_t t1, t2, t3; |
| 92 | + for (int first = first_str; first < first_end; first++) { |
| 93 | + sumInR = sumOutR = sumR = sumInG = sumOutG = sumG = sumInB = sumOutB = |
| 94 | + sumB = 0x00000000; |
| 95 | + |
| 96 | + /* Initialize SumOut by padding */ |
| 97 | + if (horiz_scan) |
| 98 | + src_ptr = twin_pixmap_pointer(src_px, second_str, first); |
| 99 | + else |
| 100 | + src_ptr = twin_pixmap_pointer(src_px, first, second_str); |
| 101 | + _src = *src_ptr.argb32; |
| 102 | + |
| 103 | + for (int i = second_str; i < second_str + radius; i++) { |
| 104 | + sumOutR = _twin_add_ARGB(sumOutR, _src, 0, t1); |
| 105 | + sumOutG = _twin_add_ARGB(sumOutG, _src, 8, t2); |
| 106 | + sumOutB = _twin_add_ARGB(sumOutB, _src, 16, t3); |
| 107 | + for (int j = 0; j < (i - second_str) + 1; j++) { |
| 108 | + sumR = _twin_add_ARGB(sumR, _src, 0, t1); |
| 109 | + sumG = _twin_add_ARGB(sumG, _src, 8, t2); |
| 110 | + sumB = _twin_add_ARGB(sumB, _src, 16, t3); |
| 111 | + } |
| 112 | + } |
| 113 | + |
| 114 | + /* Initialize SumIn */ |
| 115 | + for (int i = second_str; i < second_str + radius; i++) { |
| 116 | + if (horiz_scan) |
| 117 | + src_ptr = twin_pixmap_pointer(src_px, i, first); |
| 118 | + else |
| 119 | + src_ptr = twin_pixmap_pointer(src_px, first, i); |
| 120 | + _src = *src_ptr.argb32; |
| 121 | + sumInR = _twin_add_ARGB(sumInR, _src, 0, t1); |
| 122 | + sumInG = _twin_add_ARGB(sumInG, _src, 8, t2); |
| 123 | + sumInB = _twin_add_ARGB(sumInB, _src, 16, t3); |
| 124 | + for (int j = 0; j < radius - (i - second_str); j++) { |
| 125 | + sumR = _twin_add_ARGB(sumR, _src, 0, t1); |
| 126 | + sumG = _twin_add_ARGB(sumG, _src, 8, t2); |
| 127 | + sumB = _twin_add_ARGB(sumB, _src, 16, t3); |
| 128 | + } |
| 129 | + } |
| 130 | + |
| 131 | + for (int cur = second_str; cur < second_end; cur++) { |
| 132 | + if (horiz_scan) { |
| 133 | + src_ptr = twin_pixmap_pointer(src_px, cur, first); |
| 134 | + trg_ptr = twin_pixmap_pointer(trg_px, cur, first); |
| 135 | + old_ptr = twin_pixmap_pointer( |
| 136 | + src_px, max(cur - radius, second_str), first); |
| 137 | + new_ptr = twin_pixmap_pointer( |
| 138 | + src_px, min(cur + radius, second_end - 1), first); |
| 139 | + } else { |
| 140 | + src_ptr = twin_pixmap_pointer(src_px, first, cur); |
| 141 | + trg_ptr = twin_pixmap_pointer(trg_px, first, cur); |
| 142 | + old_ptr = twin_pixmap_pointer(src_px, first, |
| 143 | + max(cur - radius, second_str)); |
| 144 | + new_ptr = twin_pixmap_pointer( |
| 145 | + src_px, first, min(cur + radius, second_end - 1)); |
| 146 | + } |
| 147 | + _cur = *src_ptr.argb32; |
| 148 | + _old = *old_ptr.argb32; |
| 149 | + _new = *new_ptr.argb32; |
| 150 | + /* STEP 1 : sum_out + current */ |
| 151 | + sumOutR = _twin_add_ARGB(sumOutR, _cur, 0, t1); |
| 152 | + sumOutG = _twin_add_ARGB(sumOutG, _cur, 8, t2); |
| 153 | + sumOutB = _twin_add_ARGB(sumOutB, _cur, 16, t3); |
| 154 | + /* STEP 2 : sum_in + new */ |
| 155 | + sumInR = _twin_add_ARGB(sumInR, _new, 0, t1); |
| 156 | + sumInG = _twin_add_ARGB(sumInG, _new, 8, t2); |
| 157 | + sumInB = _twin_add_ARGB(sumInB, _new, 16, t3); |
| 158 | + /* STEP 3 : sum + sum_in */ |
| 159 | + sumR = _twin_add(sumR, sumInR, t1); |
| 160 | + sumG = _twin_add(sumG, sumInG, t2); |
| 161 | + sumB = _twin_add(sumB, sumInB, t3); |
| 162 | + /* STEP 4 : sum / denominator */ |
| 163 | + *trg_ptr.argb32 = |
| 164 | + (_twin_div(sumR, den, 0, t1) | _twin_div(sumG, den, 8, t2) | |
| 165 | + _twin_div(sumB, den, 16, t3) | (*src_ptr.argb32 & 0xff000000)); |
| 166 | + /* STEP 5 : sum - sum_out */ |
| 167 | + sumR = _twin_sub(sumR, sumOutR, t1); |
| 168 | + sumG = _twin_sub(sumG, sumOutG, t2); |
| 169 | + sumB = _twin_sub(sumB, sumOutB, t3); |
| 170 | + /* STEP 6 : sum_out - old */ |
| 171 | + sumOutR = _twin_sub_ARGB(sumOutR, _old, 0, t1); |
| 172 | + sumOutG = _twin_sub_ARGB(sumOutG, _old, 8, t2); |
| 173 | + sumOutB = _twin_sub_ARGB(sumOutB, _old, 16, t3); |
| 174 | + /* STEP 7 : sum_in - current */ |
| 175 | + sumInR = _twin_sub_ARGB(sumInR, _cur, 0, t1); |
| 176 | + sumInG = _twin_sub_ARGB(sumInG, _cur, 8, t2); |
| 177 | + sumInB = _twin_sub_ARGB(sumInB, _cur, 16, t3); |
| 178 | + } |
| 179 | + } |
| 180 | +} |
| 181 | + |
| 182 | +void twin_stack_blur(twin_pixmap_t *px, |
| 183 | + int radius, |
| 184 | + twin_coord_t left, |
| 185 | + twin_coord_t right, |
| 186 | + twin_coord_t top, |
| 187 | + twin_coord_t bottom) |
| 188 | +{ |
| 189 | + if (px->format != TWIN_ARGB32) |
| 190 | + return; |
| 191 | + twin_pixmap_t *tmp_px = |
| 192 | + twin_pixmap_create(px->format, px->width, px->height); |
| 193 | + memcpy(tmp_px->p.v, px->p.v, |
| 194 | + px->width * px->height * twin_bytes_per_pixel(px->format)); |
| 195 | + /* |
| 196 | + * Originally, performing a 2D convolution on each pixel takes O(width * |
| 197 | + * height * k²). However, by first scanning horizontally and then vertically |
| 198 | + * across the pixel map, and applying a 1D convolution to each pixel, the |
| 199 | + * complexity is reduced to O(2 * width * height * k). |
| 200 | + */ |
| 201 | + twin_stack(tmp_px, px, radius, top, bottom, left, right, true); |
| 202 | + twin_stack(px, tmp_px, radius, left, right, top, bottom, false); |
| 203 | + twin_pixmap_destroy(tmp_px); |
| 204 | + return; |
| 205 | +} |
| 206 | + |
| 207 | +void twin_shadow_border(twin_pixmap_t *shadow) |
| 208 | +{ |
| 209 | + twin_coord_t right_edge = |
| 210 | + (*shadow).width - (*shadow).window->shadow_offset_x, |
| 211 | + bottom_edge = |
| 212 | + (*shadow).height - (*shadow).window->shadow_offset_y, |
| 213 | + right_span = right_edge, clip, stride = 2; |
| 214 | + twin_pointer_t dst; |
| 215 | + twin_source_u src; |
| 216 | + twin_argb32_t color_x, color_y = 0xff000000, color_shift = 0x0e000000; |
| 217 | + for (twin_coord_t y = TWIN_TITLE_HEIGHT; y < (*shadow).height; y++) { |
| 218 | + color_x = 0xff000000; |
| 219 | + /* Render the right edge of the shadow border. */ |
| 220 | + if (y < bottom_edge) { |
| 221 | + /* |
| 222 | + * Create a shadow with a diagonal shape, extending from the |
| 223 | + * top-left to the bottom-right. |
| 224 | + */ |
| 225 | + clip = max( |
| 226 | + 0, shadow->window->shadow_offset_x - (y - TWIN_TITLE_HEIGHT)); |
| 227 | + for (twin_coord_t x = right_edge; x < (*shadow).width - clip; x++) { |
| 228 | + dst = twin_pixmap_pointer(shadow, x, y); |
| 229 | + src.c = color_x; |
| 230 | + _twin_c_over_argb32(dst, src, 1); |
| 231 | + if ((x - right_edge) % stride == 0) { |
| 232 | + if (color_x > color_shift) |
| 233 | + color_x -= color_shift; |
| 234 | + color_x &= 0xff000000; |
| 235 | + } |
| 236 | + } |
| 237 | + } else |
| 238 | + /* Calculate the range of the corner. */ |
| 239 | + right_span++; |
| 240 | + |
| 241 | + /* Render the bottom edge of the shadow border. */ |
| 242 | + if (y > bottom_edge) { |
| 243 | + /* |
| 244 | + * Create a shadow with a diagonal shape, extending from the |
| 245 | + * top-left to the bottom-right. |
| 246 | + */ |
| 247 | + clip = max(0, y - bottom_edge); |
| 248 | + dst = twin_pixmap_pointer(shadow, clip, y); |
| 249 | + src.c = color_y; |
| 250 | + _twin_c_over_argb32(dst, src, right_edge - clip); |
| 251 | + |
| 252 | + /* Render the bottom-right corner of the shadow border. */ |
| 253 | + for (twin_coord_t i = 0; i < right_span - right_edge; i++) { |
| 254 | + /* The corner's pixels are symmetrical to the diagonal. */ |
| 255 | + dst = twin_pixmap_pointer(shadow, right_edge + i, y); |
| 256 | + _twin_c_over_argb32(dst, src, 1); |
| 257 | + dst = twin_pixmap_pointer( |
| 258 | + shadow, right_edge + (y - bottom_edge), bottom_edge + i); |
| 259 | + _twin_c_over_argb32(dst, src, 1); |
| 260 | + } |
| 261 | + if ((shadow->y + y - bottom_edge) % stride == 0) { |
| 262 | + if (color_y > color_shift) |
| 263 | + color_y -= color_shift; |
| 264 | + color_y &= 0xff000000; |
| 265 | + } |
| 266 | + } |
| 267 | + } |
| 268 | +} |
| 269 | + |
64 | 270 | void twin_composite(twin_pixmap_t *_dst,
|
65 | 271 | twin_coord_t dst_x,
|
66 | 272 | twin_coord_t dst_y,
|
@@ -190,3 +396,16 @@ void twin_premultiply_alpha(twin_pixmap_t *px)
|
190 | 396 | p.argb32[x] = _twin_apply_alpha(p.argb32[x]);
|
191 | 397 | }
|
192 | 398 | }
|
| 399 | + |
| 400 | +void twin_cover(twin_pixmap_t *dst, |
| 401 | + twin_argb32_t color, |
| 402 | + twin_coord_t x, |
| 403 | + twin_coord_t y, |
| 404 | + twin_coord_t width) |
| 405 | +{ |
| 406 | + twin_pointer_t pt; |
| 407 | + for (twin_coord_t i = 0; i < width; i++) { |
| 408 | + pt = twin_pixmap_pointer(dst, x + i, y); |
| 409 | + *pt.argb32 = color; |
| 410 | + } |
| 411 | +} |
0 commit comments