MCPcopy Create free account
hub / github.com/ImageOptim/libimagequant / dither_row

Function dither_row

src/remap.rs:235–319  ·  view source on GitHub ↗
(row_pixels: &[f_pixel], output_pixels_row: &mut [MaybeUninit<PalIndexRemap>], width: u32, dither_map: &[u8], base_dithering_level: f32, max_dither_error: f32, n: &Nearest, palette: &[f_pixel], transp

Source from the content-addressed store, hash-verified

233
234#[inline(never)]
235fn dither_row(row_pixels: &[f_pixel], output_pixels_row: &mut [MaybeUninit<PalIndexRemap>], width: u32, dither_map: &[u8], base_dithering_level: f32, max_dither_error: f32, n: &Nearest, palette: &[f_pixel], transparent_index: PalIndexRemap, bg_pixels: &[f_pixel], guess_from_remapped_pixels: bool, diffusion: &mut [f_pixel], even_row: bool) {
236 let width = width as usize;
237 assert_eq!(row_pixels.len(), width);
238 assert_eq!(output_pixels_row.len(), width);
239
240 let (thiserr, nexterr) = {
241 // +2 saves from checking out of bounds access
242 let (d1, d2) = diffusion.split_at_mut(width + 2);
243 if even_row { (d1, d2) } else { (d2, d1) }
244 };
245
246 nexterr.fill_with(f_pixel::default);
247
248 let mut undithered_bg_used = 0u8;
249 let mut last_match = 0;
250 for x in 0..width {
251 let col = if even_row { x } else { width - 1 - x };
252 let thiserr = &mut thiserr[col .. col + 3];
253 let nexterr = &mut nexterr[col .. col + 3];
254 let input_px = row_pixels[col];
255
256 let mut dither_level = base_dithering_level;
257 if let Some(&l) = dither_map.get(col) {
258 dither_level *= f32::from(l);
259 }
260
261 let spx = get_dithered_pixel(dither_level, max_dither_error, thiserr[1], input_px);
262 let guessed_match = if guess_from_remapped_pixels {
263 unsafe { output_pixels_row[col].assume_init() }
264 } else {
265 last_match
266 };
267 let (matched, dither_diff) = n.search(&spx, guessed_match as _);
268 let mut matched = matched as PalIndexRemap;
269 last_match = matched as PalIndexRemap;
270 let mut output_px = palette[last_match as usize];
271 if let Some(bg_pixel) = bg_pixels.get(col) {
272 // if the background makes better match *with* dithering, it's a definitive win
273 let bg_for_dither_diff = spx.diff(bg_pixel);
274 if bg_for_dither_diff <= dither_diff {
275 output_px = *bg_pixel;
276 matched = transparent_index;
277 } else if undithered_bg_used > 1 {
278 // the undithered fallback can cause artifacts when too many undithered pixels accumulate a big dithering error
279 // so periodically ignore undithered fallback to prevent that
280 undithered_bg_used = 0;
281 } else {
282 // if dithering is not applied, there's a high risk of creating artifacts (flat areas, error accumulating badly),
283 // OTOH poor dithering disturbs static backgrounds and creates oscilalting frames that break backgrounds
284 // back and forth in two differently bad ways
285 let max_diff = input_px.diff(bg_pixel);
286 let dithered_diff = input_px.diff(&output_px);
287 // if dithering is worse than natural difference between frames
288 // (this rule dithers moving areas, but does not dither static areas)
289 if dithered_diff > max_diff {
290 // then see if an undithered color is closer to the ideal
291 let guessed_px = palette[guessed_match as usize];
292 let undithered_diff = input_px.diff(&guessed_px); // If dithering error is crazy high, don't propagate it that much

Callers 1

remap_to_palette_floydFunction · 0.85

Calls 5

get_dithered_pixelFunction · 0.85
assume_initMethod · 0.80
searchMethod · 0.80
diffMethod · 0.80
writeMethod · 0.80

Tested by

no test coverage detected