Coverage Report

Created: 2026-08-14 08:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/pic-scale-safe-0.1.11/src/alpha.rs
Line
Count
Source
1
/*
2
 * Copyright (c) Radzivon Bartoshyk, 10/2024. All rights reserved.
3
 *
4
 * Redistribution and use in source and binary forms, with or without modification,
5
 * are permitted provided that the following conditions are met:
6
 *
7
 * 1.  Redistributions of source code must retain the above copyright notice, this
8
 * list of conditions and the following disclaimer.
9
 *
10
 * 2.  Redistributions in binary form must reproduce the above copyright notice,
11
 * this list of conditions and the following disclaimer in the documentation
12
 * and/or other materials provided with the distribution.
13
 *
14
 * 3.  Neither the name of the copyright holder nor the names of its
15
 * contributors may be used to endorse or promote products derived from
16
 * this software without specific prior written permission.
17
 *
18
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
 */
29
30
#[inline]
31
0
fn div_by_255(v: u16) -> u8 {
32
0
    ((((v + 0x80) >> 8) + v + 0x80) >> 8).min(255) as u8
33
0
}
34
35
#[allow(clippy::manual_checked_ops)]
36
0
const fn make_unpremultiplication_table() -> [u8; 65536] {
37
0
    let mut alpha = 0usize;
38
0
    let mut buf = [0u8; 65536];
39
0
    while alpha < 256 {
40
0
        let mut pixel = 0usize;
41
0
        while pixel < 256 {
42
0
            if alpha == 0 {
43
0
                buf[alpha * 255 + pixel] = 0;
44
0
            } else {
45
0
                let value = (pixel * 255 + alpha / 2) / alpha;
46
0
                buf[alpha * 255 + pixel] = if value > 255 { 255 } else { value as u8 };
47
            }
48
0
            pixel += 1;
49
        }
50
0
        alpha += 1;
51
    }
52
0
    buf
53
0
}
54
55
pub(crate) static UNPREMULTIPLICATION_TABLE: [u8; 65536] = make_unpremultiplication_table();
56
57
/// Associate alpha in place
58
///
59
/// Note, for scaling alpha must be *associated*
60
///
61
/// # Arguments
62
///
63
/// * `in_place`: Slice to where premultiply
64
///
65
0
pub fn premultiply_rgba8(in_place: &mut [u8]) {
66
    // Almost all loops are not auto-vectorised without doing anything dirty.
67
    // So everywhere is just added something beautiful.
68
0
    for chunk in in_place.chunks_exact_mut(4) {
69
0
        let a = chunk[3] as u16;
70
0
        chunk[0] = div_by_255(chunk[0] as u16 * a);
71
0
        chunk[1] = div_by_255(chunk[1] as u16 * a);
72
0
        chunk[2] = div_by_255(chunk[2] as u16 * a);
73
0
        chunk[3] = div_by_255(255 * a);
74
0
    }
75
0
}
76
77
/// Associate alpha to new slice
78
///
79
/// Faster if you need to do a copy first.
80
/// Note, for scaling alpha must be *associated*
81
///
82
/// # Arguments
83
///
84
/// * `source`: Source slice with RGBA data
85
///
86
0
pub fn premultiplied_rgba8(source: &[u8]) -> Vec<u8> {
87
0
    let mut target = vec![0u8; source.len()];
88
    // Almost all loops are not auto-vectorised without doing anything dirty.
89
    // So everywhere is just added something beautiful.
90
0
    for (dst, src) in target.chunks_exact_mut(4).zip(source.chunks_exact(4)) {
91
0
        let a = src[3] as u16;
92
0
        dst[0] = div_by_255(src[0] as u16 * a);
93
0
        dst[1] = div_by_255(src[1] as u16 * a);
94
0
        dst[2] = div_by_255(src[2] as u16 * a);
95
0
        dst[3] = div_by_255(255 * a);
96
0
    }
97
0
    target
98
0
}
99
100
/// Un premultiply alpha in place
101
///
102
/// Note, for scaling alpha must be *associated*
103
///
104
/// # Arguments
105
///
106
/// * `in_place`: Slice to work on
107
///
108
///
109
0
pub fn unpremultiply_rgba8(in_place: &mut [u8]) {
110
0
    for chunk in in_place.chunks_exact_mut(4) {
111
0
        let a = chunk[3];
112
0
        let z = a as u16 * 255;
113
0
        chunk[0] = UNPREMULTIPLICATION_TABLE[(z + chunk[0] as u16) as usize];
114
0
        chunk[1] = UNPREMULTIPLICATION_TABLE[(z + chunk[1] as u16) as usize];
115
0
        chunk[2] = UNPREMULTIPLICATION_TABLE[(z + chunk[2] as u16) as usize];
116
0
    }
117
0
}
118
119
/// Associate alpha in place
120
///
121
/// Note, for scaling alpha must be *associated*
122
///
123
/// # Arguments
124
///
125
/// * `in_place`: Slice to where premultiply
126
///
127
0
pub fn premultiply_la8(in_place: &mut [u8]) {
128
    // Almost all loops are not auto-vectorized without doing anything dirty.
129
    // So everywhere is just added something beautiful.
130
0
    for chunk in in_place.chunks_exact_mut(2) {
131
0
        let a = chunk[1] as u16;
132
0
        chunk[0] = div_by_255(chunk[0] as u16 * a);
133
0
        chunk[1] = div_by_255(255 * a);
134
0
    }
135
0
}
136
137
/// Associate alpha to a new destination
138
///
139
/// Faster if you need to do a copy first.
140
/// Note, for scaling alpha must be *associated*
141
///
142
/// # Arguments
143
///
144
/// * `source`: Source slice with LA data
145
///
146
0
pub fn premultiplied_la8(source: &[u8]) -> Vec<u8> {
147
0
    let mut target = vec![0u8; source.len()];
148
    // Almost all loops are not auto-vectorised without doing anything dirty.
149
    // So everywhere is just added something beautiful.
150
0
    for (dst, src) in target.chunks_exact_mut(2).zip(source.chunks_exact(2)) {
151
0
        let a = src[1] as u16;
152
0
        dst[0] = div_by_255(src[0] as u16 * a);
153
0
        dst[1] = div_by_255(255 * a);
154
0
    }
155
0
    target
156
0
}
157
158
/// Un premultiply alpha in place
159
///
160
/// Note, for scaling alpha must be *associated*
161
///
162
/// # Arguments
163
///
164
/// * `in_place`: Slice to work on
165
///
166
0
pub fn unpremultiply_la8(in_place: &mut [u8]) {
167
    // Almost all loops are not auto-vectorised without doing anything dirty.
168
    // So everywhere is just added something beautiful.
169
0
    for chunk in in_place.chunks_exact_mut(2) {
170
0
        let a = chunk[1];
171
0
        let z = a as u16 * 255;
172
0
        chunk[0] = UNPREMULTIPLICATION_TABLE[(z + chunk[0] as u16) as usize];
173
0
    }
174
0
}
175
176
/// Computes `round(v / (2**n - 1))`. The result is expected to fit in u16.
177
#[inline(always)]
178
0
fn div_by_2pn_m1(v: u32, n: u32) -> u16 {
179
0
    debug_assert!(n > 0 && n <= 16);
180
0
    let round = 1 << (n - 1);
181
0
    let v = v + round;
182
0
    (((v >> n) + v) >> n) as u16
183
0
}
184
185
#[inline]
186
0
fn div_by_1023(v: u32) -> u16 {
187
0
    div_by_2pn_m1(v, 10)
188
0
}
189
190
#[inline]
191
0
fn div_by_4095(v: u32) -> u16 {
192
0
    div_by_2pn_m1(v, 12)
193
0
}
194
195
#[inline]
196
0
fn div_by_65535(v: u32) -> u16 {
197
0
    div_by_2pn_m1(v, 16)
198
0
}
199
200
/// Associate alpha in place
201
///
202
/// Note, for scaling alpha must be *associated*
203
///
204
/// # Arguments
205
///
206
/// * `in_place`: Slice to where premultiply
207
/// * `bit_depth`: Bit-depth of the image
208
///
209
0
pub fn premultiply_rgba16(in_place: &mut [u16], bit_depth: u32) {
210
    // Almost all loops are not auto-vectorised without doing anything dirty.
211
    // So everywhere is just added something beautiful.
212
0
    assert!(bit_depth > 0 && bit_depth <= 16);
213
0
    let max_colors = (1 << bit_depth) - 1;
214
0
    if bit_depth == 10 {
215
0
        for chunk in in_place.chunks_exact_mut(4) {
216
0
            let a = chunk[3] as u32;
217
0
            chunk[0] = div_by_1023(chunk[0] as u32 * a);
218
0
            chunk[1] = div_by_1023(chunk[1] as u32 * a);
219
0
            chunk[2] = div_by_1023(chunk[2] as u32 * a);
220
0
            chunk[3] = div_by_1023(1023 * a);
221
0
        }
222
0
    } else if bit_depth == 12 {
223
0
        for chunk in in_place.chunks_exact_mut(4) {
224
0
            let a = chunk[3] as u32;
225
0
            chunk[0] = div_by_4095(chunk[0] as u32 * a);
226
0
            chunk[1] = div_by_4095(chunk[1] as u32 * a);
227
0
            chunk[2] = div_by_4095(chunk[2] as u32 * a);
228
0
            chunk[3] = div_by_4095(4095 * a);
229
0
        }
230
0
    } else if bit_depth == 16 {
231
0
        for chunk in in_place.chunks_exact_mut(4) {
232
0
            let a = chunk[3] as u32;
233
0
            chunk[0] = div_by_65535(chunk[0] as u32 * a);
234
0
            chunk[1] = div_by_65535(chunk[1] as u32 * a);
235
0
            chunk[2] = div_by_65535(chunk[2] as u32 * a);
236
0
            chunk[3] = div_by_65535(65535 * a);
237
0
        }
238
    } else {
239
0
        for chunk in in_place.chunks_exact_mut(4) {
240
0
            let a = chunk[3] as u32;
241
0
            chunk[0] = div_by_2pn_m1(chunk[0] as u32 * a, bit_depth);
242
0
            chunk[1] = div_by_2pn_m1(chunk[1] as u32 * a, bit_depth);
243
0
            chunk[2] = div_by_2pn_m1(chunk[2] as u32 * a, bit_depth);
244
0
            chunk[3] = div_by_2pn_m1(max_colors * a, bit_depth);
245
0
        }
246
    }
247
0
}
248
249
/// Associate alpha to a new destination
250
///
251
/// Faster, if you need to copy data first.
252
/// Note, for scaling alpha must be *associated*
253
///
254
/// # Arguments
255
///
256
/// * `source`: Source slice with RGBA16 data
257
/// * `bit_depth`: Bit-depth of the image
258
///
259
0
pub fn premultiplied_rgba16(source: &[u16], bit_depth: u32) -> Vec<u16> {
260
0
    let mut target = vec![0u16; source.len()];
261
    // Almost all loops are not auto-vectorised without doing anything dirty.
262
    // So everywhere is just added something beautiful.
263
0
    assert!(bit_depth > 0 && bit_depth <= 16);
264
0
    let max_colors = (1 << bit_depth) - 1;
265
0
    if bit_depth == 10 {
266
0
        for (dst, src) in target.chunks_exact_mut(4).zip(source.chunks_exact(4)) {
267
0
            let a = src[3] as u32;
268
0
            dst[0] = div_by_1023(src[0] as u32 * a);
269
0
            dst[1] = div_by_1023(src[1] as u32 * a);
270
0
            dst[2] = div_by_1023(src[2] as u32 * a);
271
0
            dst[3] = div_by_1023(1023 * a);
272
0
        }
273
0
    } else if bit_depth == 12 {
274
0
        for (dst, src) in target.chunks_exact_mut(4).zip(source.chunks_exact(4)) {
275
0
            let a = src[3] as u32;
276
0
            dst[0] = div_by_4095(src[0] as u32 * a);
277
0
            dst[1] = div_by_4095(src[1] as u32 * a);
278
0
            dst[2] = div_by_4095(src[2] as u32 * a);
279
0
            dst[3] = div_by_4095(4095 * a);
280
0
        }
281
0
    } else if bit_depth == 16 {
282
0
        for (dst, src) in target.chunks_exact_mut(4).zip(source.chunks_exact(4)) {
283
0
            let a = src[3] as u32;
284
0
            dst[0] = div_by_65535(src[0] as u32 * a);
285
0
            dst[1] = div_by_65535(src[1] as u32 * a);
286
0
            dst[2] = div_by_65535(src[2] as u32 * a);
287
0
            dst[3] = div_by_65535(65535 * a);
288
0
        }
289
    } else {
290
0
        for (dst, src) in target.chunks_exact_mut(4).zip(source.chunks_exact(4)) {
291
0
            let a = src[3] as u32;
292
0
            dst[0] = div_by_2pn_m1(src[0] as u32 * a, bit_depth);
293
0
            dst[1] = div_by_2pn_m1(src[1] as u32 * a, bit_depth);
294
0
            dst[2] = div_by_2pn_m1(src[2] as u32 * a, bit_depth);
295
0
            dst[3] = div_by_2pn_m1(max_colors * a, bit_depth);
296
0
        }
297
    }
298
0
    target
299
0
}
300
301
/// Associate alpha in place for up to 16 bit-depth image
302
///
303
/// Note, for scaling alpha must be *associated*
304
///
305
/// # Arguments
306
///
307
/// * `in_place`: Slice to where premultiply
308
/// * `bit_depth`: Bit-depth of the image
309
///
310
0
pub fn premultiply_la16(in_place: &mut [u16], bit_depth: u32) {
311
    // Almost all loops are not auto-vectorised without doing anything dirty.
312
    // So everywhere is just added something beautiful.
313
0
    assert!(bit_depth > 0 && bit_depth <= 16);
314
0
    let max_colors = (1 << bit_depth) - 1;
315
0
    if bit_depth == 10 {
316
0
        for chunk in in_place.chunks_exact_mut(2) {
317
0
            let a = chunk[1] as u32;
318
0
            chunk[0] = div_by_1023(chunk[0] as u32 * a);
319
0
            chunk[1] = div_by_1023(1023 * a);
320
0
        }
321
0
    } else if bit_depth == 12 {
322
0
        for chunk in in_place.chunks_exact_mut(2) {
323
0
            let a = chunk[1] as u32;
324
0
            chunk[0] = div_by_4095(chunk[0] as u32 * a);
325
0
            chunk[1] = div_by_4095(4095 * a);
326
0
        }
327
0
    } else if bit_depth == 16 {
328
0
        for chunk in in_place.chunks_exact_mut(2) {
329
0
            let a = chunk[1] as u32;
330
0
            chunk[0] = div_by_65535(chunk[0] as u32 * a);
331
0
            chunk[1] = div_by_65535(65535 * a);
332
0
        }
333
    } else {
334
0
        for chunk in in_place.chunks_exact_mut(2) {
335
0
            let a = chunk[1] as u32;
336
0
            chunk[0] = div_by_2pn_m1(chunk[0] as u32 * a, bit_depth);
337
0
            chunk[1] = div_by_2pn_m1(max_colors * a, bit_depth);
338
0
        }
339
    }
340
0
}
341
342
/// Associate alpha for up to 16 bit-depth image to a new destination
343
///
344
/// Faster, if you need to copy data first.
345
/// Note, for scaling alpha must be *associated*
346
///
347
/// # Arguments
348
///
349
/// * `source`: Slice with source LA16 data
350
/// * `bit_depth`: Bit-depth of the image
351
///
352
0
pub fn premultiplied_la16(source: &[u16], bit_depth: u32) -> Vec<u16> {
353
0
    let mut target = vec![0u16; source.len()];
354
    // Almost all loops are not auto-vectorised without doing anything dirty.
355
    // So everywhere is just added something beautiful.
356
0
    assert!(bit_depth > 0 && bit_depth <= 16);
357
0
    let max_colors = (1 << bit_depth) - 1;
358
0
    if bit_depth == 10 {
359
0
        for (dst, src) in target.chunks_exact_mut(2).zip(source.chunks_exact(2)) {
360
0
            let a = src[1] as u32;
361
0
            dst[0] = div_by_1023(src[0] as u32 * a);
362
0
            dst[1] = div_by_1023(1023 * a);
363
0
        }
364
0
    } else if bit_depth == 12 {
365
0
        for (dst, src) in target.chunks_exact_mut(2).zip(source.chunks_exact(2)) {
366
0
            let a = src[1] as u32;
367
0
            dst[0] = div_by_4095(src[0] as u32 * a);
368
0
            dst[1] = div_by_4095(4095 * a);
369
0
        }
370
0
    } else if bit_depth == 16 {
371
0
        for (dst, src) in target.chunks_exact_mut(2).zip(source.chunks_exact(2)) {
372
0
            let a = src[1] as u32;
373
0
            dst[0] = div_by_65535(src[0] as u32 * a);
374
0
            dst[1] = div_by_65535(65535 * a);
375
0
        }
376
    } else {
377
0
        for (dst, src) in target.chunks_exact_mut(2).zip(source.chunks_exact(2)) {
378
0
            let a = src[1] as u32;
379
0
            dst[0] = div_by_2pn_m1(src[0] as u32 * a, bit_depth);
380
0
            dst[1] = div_by_2pn_m1(max_colors * a, bit_depth);
381
0
        }
382
    }
383
0
    target
384
0
}
385
386
/// Un premultiply alpha in place for up to 16 bit-depth image
387
///
388
/// Note, for scaling alpha must be *associated*
389
///
390
/// # Arguments
391
///
392
/// * `in_place`: Slice to work on
393
/// * `bit_depth`: Bit-depth of the image
394
///
395
///
396
0
pub fn unpremultiply_la16(in_place: &mut [u16], bit_depth: u32) {
397
    // Almost all loops are not auto-vectorised without doing anything dirty.
398
    // So everywhere is just added something beautiful.
399
0
    assert!(bit_depth > 0 && bit_depth <= 16);
400
0
    let max_colors = (1 << bit_depth) - 1;
401
0
    for chunk in in_place.chunks_exact_mut(2) {
402
0
        let a = chunk[1] as u32;
403
0
        if a != 0 {
404
0
            let a_recip = max_colors as f32 / a as f32;
405
0
            chunk[0] = (chunk[0] as f32 * a_recip) as u16;
406
0
        }
407
    }
408
0
}
409
410
/// Un premultiply alpha in place
411
///
412
/// Note, for scaling alpha must be *associated*
413
///
414
/// # Arguments
415
///
416
/// * `in_place`: Slice to work on
417
/// * `bit_depth`: Bit-depth of the image
418
///
419
///
420
0
pub fn unpremultiply_rgba16(in_place: &mut [u16], bit_depth: u32) {
421
    // Almost all loops are not auto-vectorised without doing anything dirty.
422
    // So everywhere is just added something beautiful.
423
0
    assert!(bit_depth > 0 && bit_depth <= 16);
424
0
    let max_colors = (1 << bit_depth) - 1;
425
0
    for chunk in in_place.chunks_exact_mut(4) {
426
0
        let a = chunk[3] as u32;
427
0
        if a != 0 {
428
0
            let a_recip = max_colors as f32 / a as f32;
429
0
            chunk[0] = (chunk[0] as f32 * a_recip) as u16;
430
0
            chunk[1] = (chunk[1] as f32 * a_recip) as u16;
431
0
            chunk[2] = (chunk[2] as f32 * a_recip) as u16;
432
0
        }
433
    }
434
0
}
435
436
/// Associate alpha in place
437
///
438
/// Note, for scaling alpha must be *associated*
439
///
440
/// # Arguments
441
///
442
/// * `in_place`: Slice to where premultiply
443
///
444
0
pub fn premultiply_rgba_f32(in_place: &mut [f32]) {
445
    // Almost all loops are not auto-vectorised without doing anything dirty.
446
    // So everywhere is just added something beautiful.
447
0
    for chunk in in_place.chunks_exact_mut(4) {
448
0
        let a = chunk[3];
449
0
        chunk[0] *= a;
450
0
        chunk[1] *= a;
451
0
        chunk[2] *= a;
452
0
        chunk[3] = a;
453
0
    }
454
0
}
455
456
/// Associate alpha in place
457
///
458
/// Note, for scaling alpha must be *associated*
459
///
460
/// # Arguments
461
///
462
/// * `in_place`: Slice to where premultiply
463
///
464
0
pub fn premultiply_luma_alpha_f32(in_place: &mut [f32]) {
465
    // Almost all loops are not auto-vectorised without doing anything dirty.
466
    // So everywhere is just added something beautiful.
467
0
    for chunk in in_place.chunks_exact_mut(2) {
468
0
        let a = chunk[1];
469
0
        chunk[0] *= a;
470
0
        chunk[2] = a;
471
0
    }
472
0
}
473
474
/// Associate alpha to a new destination
475
///
476
/// Faster, if you need to do a copy first
477
/// Note, for scaling alpha must be *associated*
478
///
479
/// # Arguments
480
///
481
/// * `source`: Source slice with luma alpha
482
///
483
0
pub fn premultiplied_luma_alpha_f32(source: &[f32]) -> Vec<f32> {
484
0
    let mut target = vec![0.; source.len()];
485
    // Almost all loops are not auto-vectorised without doing anything dirty.
486
    // So everywhere is just added something beautiful.
487
0
    for (dst, src) in target.chunks_exact_mut(2).zip(source.chunks_exact(2)) {
488
0
        let a = src[2];
489
0
        dst[0] = src[0] * a;
490
0
        dst[1] = a;
491
0
    }
492
0
    target
493
0
}
494
495
/// Associate alpha to a new destination
496
///
497
/// Faster, if you need to do a copy first
498
/// Note, for scaling alpha must be *associated*
499
///
500
/// # Arguments
501
///
502
/// * `source`: Source rgba slice
503
///
504
0
pub fn premultiplied_rgba_f32(source: &[f32]) -> Vec<f32> {
505
0
    let mut target = vec![0.; source.len()];
506
    // Almost all loops are not auto-vectorised without doing anything dirty.
507
    // So everywhere is just added something beautiful.
508
0
    for (dst, src) in target.chunks_exact_mut(4).zip(source.chunks_exact(4)) {
509
0
        let a = src[3];
510
0
        dst[0] = src[0] * a;
511
0
        dst[1] = src[1] * a;
512
0
        dst[2] = src[2] * a;
513
0
        dst[3] = a;
514
0
    }
515
0
    target
516
0
}
517
518
/// Un-premultiply alpha in place
519
///
520
/// Note, for scaling alpha must be *associated*
521
///
522
/// # Arguments
523
///
524
/// * `in_place`: Slice to work on
525
///
526
0
pub fn unpremultiply_rgba_f32(in_place: &mut [f32]) {
527
0
    for chunk in in_place.chunks_exact_mut(4) {
528
0
        let a = chunk[3];
529
0
        if a != 0. {
530
0
            let a_recip = 1. / a;
531
0
            chunk[0] *= a_recip;
532
0
            chunk[1] *= a_recip;
533
0
            chunk[2] *= a_recip;
534
0
            chunk[3] = a;
535
0
        }
536
    }
537
0
}
538
539
/// Un-premultiply alpha in place
540
///
541
/// Note, for scaling alpha must be *associated*
542
///
543
/// # Arguments
544
///
545
/// * `in_place`: Slice to work on
546
///
547
0
pub fn unpremultiply_luma_alpha_f32(in_place: &mut [f32]) {
548
0
    for chunk in in_place.chunks_exact_mut(2) {
549
0
        let a = chunk[1];
550
0
        if a != 0. {
551
0
            let a_recip = 1. / a;
552
0
            chunk[0] *= a_recip;
553
0
            chunk[1] = a;
554
0
        }
555
    }
556
0
}