| 1128 | // Dim (alpha) is applied once at the final output. |
| 1129 | template <int hRadius, int vRadius, typename RGB_T, typename AlphaT> |
| 1130 | FL_OPTIMIZE_FUNCTION |
| 1131 | void blurGaussianImpl(Canvas<RGB_T> &canvas, AlphaT alpha) { |
| 1132 | const int w = canvas.width; |
| 1133 | const int h = canvas.height; |
| 1134 | if (w <= 0 || h <= 0) |
| 1135 | return; |
| 1136 | |
| 1137 | using P = blur_detail::pixel_ops<RGB_T>; |
| 1138 | |
| 1139 | // Accumulator: u16 for 8-bit channels (max per-pass sum: 255*256=65280), |
| 1140 | // u32 for wider channels. |
| 1141 | using acc_t = fl::conditional_t<sizeof(typename RGB_T::fp) == 1, u16, u32>; |
| 1142 | |
| 1143 | const bool applyAlpha = !(alpha == blur_detail::alpha_identity<AlphaT>()); |
| 1144 | |
| 1145 | // Handle no-blur case (radius 0 in both dimensions). |
| 1146 | if (hRadius == 0 && vRadius == 0) { |
| 1147 | if (applyAlpha) { |
| 1148 | RGB_T *pixels = canvas.pixels; |
| 1149 | for (int i = 0; i < w * h; ++i) { |
| 1150 | RGB_T &p = pixels[i]; |
| 1151 | p = P::make(P::ch(p.r), P::ch(p.g), P::ch(p.b), alpha); |
| 1152 | } |
| 1153 | } |
| 1154 | return; |
| 1155 | } |
| 1156 | |
| 1157 | fl::span<RGB_T> padbuf = blur_detail::get_padbuf<RGB_T>( |
| 1158 | blur_detail::compute_pad_size<hRadius, vRadius, RGB_T>(w, h)); |
| 1159 | RGB_T *pad = padbuf.data(); |
| 1160 | RGB_T *pixels = canvas.pixels; |
| 1161 | |
| 1162 | // ── Horizontal pass ────────────────────────────────────────────── |
| 1163 | if (hRadius > 0) { |
| 1164 | // Zero the fixed padding regions once (reused for every row). |
| 1165 | FL_BUILTIN_MEMSET(pad, 0, hRadius * sizeof(RGB_T)); |
| 1166 | FL_BUILTIN_MEMSET(pad + hRadius + w, 0, hRadius * sizeof(RGB_T)); |
| 1167 | |
| 1168 | for (int y = 0; y < h; ++y) { |
| 1169 | RGB_T *row = pixels + y * w; |
| 1170 | FL_BUILTIN_MEMCPY(pad + hRadius, row, w * sizeof(RGB_T)); |
| 1171 | |
| 1172 | if (vRadius == 0 && applyAlpha) |
| 1173 | blur_detail::hpass_row<hRadius, RGB_T, acc_t, true>( |
| 1174 | pad, row, w, alpha); |
| 1175 | else |
| 1176 | blur_detail::hpass_row<hRadius, RGB_T, acc_t, false>( |
| 1177 | pad, row, w, alpha); |
| 1178 | } |
| 1179 | } |
| 1180 | |
| 1181 | // ── Vertical pass ────────────────────────────────────────────────── |
| 1182 | if (vRadius > 0) { |
| 1183 | if (applyAlpha) |
| 1184 | blur_detail::vpass_full<vRadius, RGB_T, acc_t, true>( |
| 1185 | pixels, w, h, pad, alpha); |
| 1186 | else |
| 1187 | blur_detail::vpass_full<vRadius, RGB_T, acc_t, false>( |