| 1208 | // Falls back to per-pixel gather/scatter through XYMap otherwise. |
| 1209 | template <int hRadius, int vRadius, typename RGB_T, typename AlphaT> |
| 1210 | FL_OPTIMIZE_FUNCTION |
| 1211 | void blurGaussianMappedImpl(CanvasMapped<RGB_T> &canvas, AlphaT alpha) { |
| 1212 | const int w = canvas.width; |
| 1213 | const int h = canvas.height; |
| 1214 | if (w <= 0 || h <= 0) |
| 1215 | return; |
| 1216 | |
| 1217 | // Fast path: rectangular XYMap → delegate to optimized Canvas blur. |
| 1218 | if (canvas.xymap->isRectangularGrid()) { |
| 1219 | Canvas<RGB_T> rect(canvas.pixels, w, h); |
| 1220 | blurGaussianImpl<hRadius, vRadius>(rect, alpha); |
| 1221 | return; |
| 1222 | } |
| 1223 | |
| 1224 | // Slow path: non-rectangular XYMap → per-pixel gather/scatter. |
| 1225 | using P = blur_detail::pixel_ops<RGB_T>; |
| 1226 | using acc_t = fl::conditional_t<sizeof(typename RGB_T::fp) == 1, u16, u32>; |
| 1227 | |
| 1228 | const bool applyAlpha = !(alpha == blur_detail::alpha_identity<AlphaT>()); |
| 1229 | |
| 1230 | // Handle no-blur case. |
| 1231 | if (hRadius == 0 && vRadius == 0) { |
| 1232 | if (applyAlpha) { |
| 1233 | for (int y = 0; y < h; ++y) { |
| 1234 | for (int x = 0; x < w; ++x) { |
| 1235 | RGB_T &p = canvas.at(x, y); |
| 1236 | P ops; |
| 1237 | p = ops.make(ops.ch(p.r), ops.ch(p.g), ops.ch(p.b), alpha); |
| 1238 | } |
| 1239 | } |
| 1240 | } |
| 1241 | return; |
| 1242 | } |
| 1243 | |
| 1244 | fl::span<RGB_T> padbuf = blur_detail::get_padbuf<RGB_T>( |
| 1245 | blur_detail::compute_pad_size<hRadius, vRadius, RGB_T>(w, h)); |
| 1246 | RGB_T *pad = padbuf.data(); |
| 1247 | |
| 1248 | // ── Horizontal pass: gather row via XYMap, convolve, scatter back ── |
| 1249 | if (hRadius > 0) { |
| 1250 | FL_BUILTIN_MEMSET(pad, 0, hRadius * sizeof(RGB_T)); |
| 1251 | FL_BUILTIN_MEMSET(pad + hRadius + w, 0, hRadius * sizeof(RGB_T)); |
| 1252 | |
| 1253 | for (int y = 0; y < h; ++y) { |
| 1254 | for (int x = 0; x < w; ++x) |
| 1255 | pad[hRadius + x] = canvas.at(x, y); |
| 1256 | |
| 1257 | constexpr int shift = 2 * hRadius; |
| 1258 | for (int x = 0; x < w; ++x) { |
| 1259 | acc_t r, g, b; |
| 1260 | blur_detail::interior_row<hRadius, RGB_T, acc_t>::apply(pad, hRadius + x, r, g, b); |
| 1261 | RGB_T result; |
| 1262 | if (vRadius == 0 && applyAlpha) |
| 1263 | result = P().make(static_cast<acc_t>(r >> shift), |
| 1264 | static_cast<acc_t>(g >> shift), |
| 1265 | static_cast<acc_t>(b >> shift), alpha); |
| 1266 | else |
| 1267 | result = P().make(static_cast<acc_t>(r >> shift), |