/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 | } |