sw-composite-0.7.2/Cargo.toml.orig010064400007650000024000000004631356105321700152650ustar0000000000000000[package] name = "sw-composite" version = "0.7.2" authors = ["Jeff Muizelaar "] license = "BSD-3-Clause" description = "a collection of software compositing routines" documentation = "https://docs.rs/sw-composite" repository = "https://github.com/jrmuizel/sw-composite/" [dependencies] sw-composite-0.7.2/Cargo.toml0000644000000015000000000000000115240ustar00# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies # # If you believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're # editing this file be aware that the upstream Cargo.toml # will likely look very different (and much more reasonable) [package] name = "sw-composite" version = "0.7.2" authors = ["Jeff Muizelaar "] description = "a collection of software compositing routines" documentation = "https://docs.rs/sw-composite" license = "BSD-3-Clause" repository = "https://github.com/jrmuizel/sw-composite/" [dependencies] sw-composite-0.7.2/src/blend.rs010064400007650000024000000517501356105315400146240ustar0000000000000000/* * Copyright 2006 The Android Open Source Project * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #![allow(non_snake_case)] use crate::*; pub trait Blend { fn blend(src: u32, dst: u32) -> u32; } pub struct Dst; impl Blend for Dst { #[inline] fn blend(_src: u32, dst: u32) -> u32 { dst } } pub struct Src; impl Blend for Src { #[inline] fn blend(src: u32, _dst: u32) -> u32 { src } } pub struct Clear; impl Blend for Clear { #[inline] fn blend(_src: u32, _dst: u32) -> u32 { 0 } } pub struct SrcOver; impl Blend for SrcOver { #[inline] fn blend(src: u32, dst: u32) -> u32 { over(src, dst) } } pub struct DstOver; impl Blend for DstOver { #[inline] fn blend(src: u32, dst: u32) -> u32 { over(dst, src) } } pub struct SrcIn; impl Blend for SrcIn { #[inline] fn blend(src: u32, dst: u32) -> u32 { alpha_mul(src, alpha_to_alpha256(packed_alpha(dst))) } } pub struct DstIn; impl Blend for DstIn { #[inline] fn blend(src: u32, dst: u32) -> u32 { alpha_mul(dst, alpha_to_alpha256(packed_alpha(src))) } } pub struct SrcOut; impl Blend for SrcOut { #[inline] fn blend(src: u32, dst: u32) -> u32 { alpha_mul(src, alpha_to_alpha256(255 - packed_alpha(dst))) } } pub struct DstOut; impl Blend for DstOut { #[inline] fn blend(src: u32, dst: u32) -> u32 { alpha_mul(dst, alpha_to_alpha256(255 - packed_alpha(src))) } } pub struct SrcAtop; impl Blend for SrcAtop { #[inline] fn blend(src: u32, dst: u32) -> u32 { let sa = packed_alpha(src); let da = packed_alpha(dst); let isa = 255 - sa; return pack_argb32(da, muldiv255(da, get_packed_r32(src)) + muldiv255(isa, get_packed_r32(dst)), muldiv255(da, get_packed_g32(src)) + muldiv255(isa, get_packed_g32(dst)), muldiv255(da, get_packed_b32(src)) + muldiv255(isa, get_packed_b32(dst))); } } pub struct DstAtop; impl Blend for DstAtop { #[inline] fn blend(src: u32, dst: u32) -> u32 { let sa = packed_alpha(src); let da = packed_alpha(dst); let ida = 255 - da; return pack_argb32(sa, muldiv255(ida, get_packed_r32(src)) + muldiv255(sa, get_packed_r32(dst)), muldiv255(ida, get_packed_g32(src)) + muldiv255(sa, get_packed_g32(dst)), muldiv255(ida, get_packed_b32(src)) + muldiv255(sa, get_packed_b32(dst))); } } pub struct Xor; impl Blend for Xor { #[inline] fn blend(src: u32, dst: u32) -> u32 { let sa = packed_alpha(src); let da = packed_alpha(dst); let isa = 255 - sa; let ida = 255 - da; return pack_argb32(sa + da - (muldiv255(sa, da) * 2), muldiv255(ida, get_packed_r32(src)) + muldiv255(isa, get_packed_r32(dst)), muldiv255(ida, get_packed_g32(src)) + muldiv255(isa, get_packed_g32(dst)), muldiv255(ida, get_packed_b32(src)) + muldiv255(isa, get_packed_b32(dst))); } } fn saturated_add(a: u32, b: u32) -> u32 { debug_assert!(a <= 255); debug_assert!(b <= 255); let sum = a + b; if sum > 255 { 255 } else { sum } } pub struct Add; impl Blend for Add { #[inline] fn blend(src: u32, dst: u32) -> u32 { pack_argb32(saturated_add(get_packed_a32(src), get_packed_a32(dst)), saturated_add(get_packed_r32(src), get_packed_r32(dst)), saturated_add(get_packed_g32(src), get_packed_g32(dst)), saturated_add(get_packed_b32(src), get_packed_b32(dst))) } } // kMultiply_Mode // B(Cb, Cs) = Cb x Cs // multiply uses its own version of blendfunc_byte because sa and da are not needed fn blendfunc_multiply_byte(sc: i32, dc: i32, sa: i32, da: i32) -> u32 { clamp_div255round(sc * (255 - da) + dc * (255 - sa) + sc * dc) } pub struct Multiply; impl Blend for Multiply { fn blend(src: u32, dst: u32) -> u32 { let sa = get_packed_a32(src) as i32; let da = get_packed_a32(dst) as i32; pack_argb32(srcover_byte(get_packed_a32(src), get_packed_a32(dst)), blendfunc_multiply_byte(get_packed_r32(src) as i32, get_packed_r32(dst) as i32, sa, da), blendfunc_multiply_byte(get_packed_g32(src) as i32, get_packed_g32(dst) as i32, sa, da), blendfunc_multiply_byte(get_packed_b32(src) as i32, get_packed_b32(dst) as i32, sa, da)) } } fn srcover_byte(a: u32, b: u32) -> u32 { a + b - muldiv255(a, b) } pub struct Screen; impl Blend for Screen { fn blend(src: u32, dst: u32) -> u32 { pack_argb32(srcover_byte(get_packed_a32(src), get_packed_a32(dst)), srcover_byte(get_packed_r32(src), get_packed_r32(dst)), srcover_byte(get_packed_g32(src), get_packed_g32(dst)), srcover_byte(get_packed_b32(src), get_packed_b32(dst))) } } fn clamp_div255round(prod: i32) -> u32 { if prod <= 0 { return 0; } else if prod >= 255 * 255 { return 255; } else { return div255(prod as u32); } } fn overlay_byte(sc: u32, dc: u32, sa: u32, da: u32) -> u32 { let tmp = sc * (255 - da) + dc * (255 - sa); let rc; if 2 * dc <= da { rc = 2 * sc * dc; } else { rc = sa * da - 2 * (da - dc) * (sa - sc); } clamp_div255round((rc + tmp) as i32) } pub struct Overlay; impl Blend for Overlay { fn blend(src: u32, dst: u32) -> u32 { let sa = get_packed_a32(src); let da = get_packed_a32(dst); pack_argb32(srcover_byte(sa, da), overlay_byte(get_packed_r32(src), get_packed_r32(dst), sa, da), overlay_byte(get_packed_g32(src), get_packed_g32(dst), sa, da), overlay_byte(get_packed_b32(src), get_packed_b32(dst), sa, da)) } } fn darken_byte(sc: u32, dc: u32, sa: u32, da: u32) -> u32 { let sd = sc * da; let ds = dc * sa; if sd < ds { // srcover return sc + dc - div255(ds); } else { // dstover return dc + sc - div255(sd); } } pub struct Darken; impl Blend for Darken { fn blend(src: u32, dst: u32) -> u32 { let sa = get_packed_a32(src); let da = get_packed_a32(dst); pack_argb32(srcover_byte(sa, da), darken_byte(get_packed_r32(src), get_packed_r32(dst), sa, da), darken_byte(get_packed_g32(src), get_packed_g32(dst), sa, da), darken_byte(get_packed_b32(src), get_packed_b32(dst), sa, da)) } } fn lighten_byte(sc: u32, dc: u32, sa: u32, da: u32) -> u32 { let sd = sc * da; let ds = dc * sa; if sd > ds { // srcover return sc + dc - div255(ds); } else { // dstover return dc + sc - div255(sd); } } pub struct Lighten; impl Blend for Lighten { fn blend(src: u32, dst: u32) -> u32 { let sa = get_packed_a32(src); let da = get_packed_a32(dst); pack_argb32(srcover_byte(sa, da), lighten_byte(get_packed_r32(src), get_packed_r32(dst), sa, da), lighten_byte(get_packed_g32(src), get_packed_g32(dst), sa, da), lighten_byte(get_packed_b32(src), get_packed_b32(dst), sa, da)) } } fn colordodge_byte(sc: i32, dc: i32, sa: i32, da: i32) -> u32 { let mut diff = sa - sc; let rc; if 0 == dc { return muldiv255(sc as u32, (255 - da) as u32); } else if 0 == diff { rc = sa * da + sc * (255 - da) + dc * (255 - sa); } else { diff = (dc * sa) / diff; rc = sa * (if da < diff { da } else { diff }) + sc * (255 - da) + dc * (255 - sa); } return clamp_div255round(rc); } pub struct ColorDodge; impl Blend for ColorDodge { fn blend(src: u32, dst: u32) -> u32 { let sa = get_packed_a32(src) as i32; let da = get_packed_a32(dst) as i32; pack_argb32(srcover_byte(sa as u32, da as u32), colordodge_byte(get_packed_r32(src) as i32, get_packed_r32(dst) as i32, sa, da), colordodge_byte(get_packed_g32(src) as i32, get_packed_g32(dst) as i32, sa, da), colordodge_byte(get_packed_b32(src) as i32, get_packed_b32(dst) as i32, sa, da)) } } fn colorburn_byte(sc: i32, dc: i32, sa: i32, da: i32) -> u32 { let rc; if dc == da { rc = sa * da + sc * (255 - da) + dc * (255 - sa); } else if 0 == sc { return muldiv255(dc as u32, (255 - sa) as u32); } else { let tmp = (da - dc) * sa / sc; rc = sa * (da - (if da < tmp { da } else { tmp })) + sc * (255 - da) + dc * (255 - sa); } return clamp_div255round(rc); } pub struct ColorBurn; impl Blend for ColorBurn { fn blend(src: u32, dst: u32) -> u32 { let sa = get_packed_a32(src) as i32; let da = get_packed_a32(dst) as i32; pack_argb32(srcover_byte(sa as u32, da as u32), colorburn_byte(get_packed_r32(src) as i32, get_packed_r32(dst) as i32, sa, da), colorburn_byte(get_packed_g32(src) as i32, get_packed_g32(dst) as i32, sa, da), colorburn_byte(get_packed_b32(src) as i32, get_packed_b32(dst) as i32, sa, da)) } } fn hardlight_byte(sc: i32, dc: i32, sa: i32, da: i32) -> u32 { let rc; if 2 * sc <= sa { rc = 2 * sc * dc; } else { rc = sa * da - 2 * (da - dc) * (sa - sc); } return clamp_div255round(rc + sc * (255 - da) + dc * (255 - sa)); } pub struct HardLight; impl Blend for HardLight { fn blend(src: u32, dst: u32) -> u32 { let sa = get_packed_a32(src) as i32; let da = get_packed_a32(dst) as i32; pack_argb32(srcover_byte(sa as u32, da as u32), hardlight_byte(get_packed_r32(src) as i32, get_packed_r32(dst) as i32, sa, da), hardlight_byte(get_packed_g32(src) as i32, get_packed_g32(dst) as i32, sa, da), hardlight_byte(get_packed_b32(src) as i32, get_packed_b32(dst) as i32, sa, da)) } } /* www.worldserver.com/turk/computergraphics/FixedSqrt.pdf */ fn sqrt_bits(x: i32, count: i32) -> i32 { debug_assert!(x >= 0 && count > 0 && count <= 30); let mut root = 0; let mut rem_hi = 0; let mut rem_lo = x; loop { root <<= 1; rem_hi = (rem_hi << 2) | (rem_lo >> 30); rem_lo <<= 2; let test_div = (root << 1) + 1; if rem_hi >= test_div { rem_hi -= test_div; root += 1; } if -count < 0 { break; } } return root; } type U8Cpu = u32; // returns 255 * sqrt(n/255) fn sqrt_unit_byte(n: U8Cpu) -> U8Cpu { return sqrt_bits(n as i32, 15 + 4) as u32; } fn softlight_byte(sc: i32, dc: i32, sa: i32, da: i32) -> u32 { let m = if da != 0 { dc * 256 / da } else { 0 }; let rc; if 2 * sc <= sa { rc = dc * (sa + ((2 * sc - sa) * (256 - m) >> 8)); } else if 4 * dc <= da { let tmp = (4 * m * (4 * m + 256) * (m - 256) >> 16) + 7 * m; rc = dc * sa + (da * (2 * sc - sa) * tmp >> 8); } else { let tmp = sqrt_unit_byte(m as u32) as i32 - m; rc = dc * sa + (da * (2 * sc - sa) * tmp >> 8); } return clamp_div255round(rc + sc * (255 - da) + dc * (255 - sa)); } pub struct SoftLight; impl Blend for SoftLight { fn blend(src: u32, dst: u32) -> u32 { let sa = get_packed_a32(src) as i32; let da = get_packed_a32(dst) as i32; pack_argb32(srcover_byte(sa as u32, da as u32), softlight_byte(get_packed_r32(src) as i32, get_packed_r32(dst) as i32, sa, da), softlight_byte(get_packed_g32(src) as i32, get_packed_g32(dst) as i32, sa, da), softlight_byte(get_packed_b32(src) as i32, get_packed_b32(dst) as i32, sa, da)) } } fn clamp_signed_byte(n: i32) -> u32 { if n < 0 { 0 } else if n > 255 { 255 } else { n as u32 } } fn difference_byte(sc: i32, dc: i32, sa: i32, da: i32) -> u32 { let tmp = (sc * da).min(dc * sa); return clamp_signed_byte(sc + dc - 2 * div255(tmp as u32) as i32); } pub struct Difference; impl Blend for Difference { fn blend(src: u32, dst: u32) -> u32 { let sa = get_packed_a32(src) as i32; let da = get_packed_a32(dst) as i32; pack_argb32(srcover_byte(sa as u32, da as u32), difference_byte(get_packed_r32(src) as i32, get_packed_r32(dst) as i32, sa, da), difference_byte(get_packed_g32(src) as i32, get_packed_g32(dst) as i32, sa, da), difference_byte(get_packed_b32(src) as i32, get_packed_b32(dst) as i32, sa, da)) } } fn exclusion_byte(sc: i32, dc: i32, _sa: i32, _da: i32) -> u32 { // this equations is wacky, wait for SVG to confirm it //int r = sc * da + dc * sa - 2 * sc * dc + sc * (255 - da) + dc * (255 - sa); // The above equation can be simplified as follows let r = 255 * (sc + dc) - 2 * sc * dc; return clamp_div255round(r); } pub struct Exclusion; impl Blend for Exclusion { fn blend(src: u32, dst: u32) -> u32 { let sa = get_packed_a32(src) as i32; let da = get_packed_a32(dst) as i32; pack_argb32(srcover_byte(sa as u32, da as u32), exclusion_byte(get_packed_r32(src) as i32, get_packed_r32(dst) as i32, sa, da), exclusion_byte(get_packed_g32(src) as i32, get_packed_g32(dst) as i32, sa, da), exclusion_byte(get_packed_b32(src) as i32, get_packed_b32(dst) as i32, sa, da)) } } // The CSS compositing spec introduces the following formulas: // (See https://dvcs.w3.org/hg/FXTF/rawfile/tip/compositing/index.html#blendingnonseparable) // SkComputeLuminance is similar to this formula but it uses the new definition from Rec. 709 // while PDF and CG uses the one from Rec. Rec. 601 // See http://www.glennchan.info/articles/technical/hd-versus-sd-color-space/hd-versus-sd-color-space.htm fn lum(r: i32, g: i32, b: i32) -> i32 { div255((r * 77 + g * 150 + b * 28) as u32) as i32 } fn mul_div(numer1: i32, numer2: i32, denom: i32) -> i32 { let tmp = (numer1 as i64 * numer2 as i64) / denom as i64; return tmp as i32; } fn minimum(a: i32, b: i32, c: i32) -> i32 { a.min(b).min(c) } fn maximum(a: i32, b: i32, c: i32) -> i32 { a.max(b).max(c) } fn clip_color(r: &mut i32, g: &mut i32, b: &mut i32, a: i32) { let L = lum(*r, *g, *b); let n = minimum(*r, *g, *b); let x = maximum(*r, *g, *b); let denom = L - n; if (n < 0) && (denom != 0) { // Compute denom and make sure it's non zero *r = L + mul_div(*r - L, L, denom); *g = L + mul_div(*g - L, L, denom); *b = L + mul_div(*b - L, L, denom); } let denom = x - L; if (x > a) && (denom != 0) { // Compute denom and make sure it's non zero let numer = a - L; *r = L + mul_div(*r - L, numer, denom); *g = L + mul_div(*g - L, numer, denom); *b = L + mul_div(*b - L, numer, denom); } } fn sat(r: i32, g: i32, b: i32) -> i32 { maximum(r, g, b) - minimum(r, g, b) } fn set_saturation_components(cmin: &mut i32, cmind: &mut i32, cmax: &mut i32, s: i32) { if *cmax > *cmin { *cmind = mul_div(*cmind - *cmin, s, *cmax - *cmin); *cmax = s; } else { *cmax = 0; *cmind = 0; } *cmin = 0; } fn set_sat(r: &mut i32, g: &mut i32, b: &mut i32, s: i32) { if *r <= *g { if *g <= *b { set_saturation_components(r, g, b, s); } else if *r <= *b { set_saturation_components(r, b, g, s); } else { set_saturation_components(b, r, g, s); } } else if *r <= *b { set_saturation_components(g, r, b, s); } else if *g <= *b { set_saturation_components(g, b, r, s); } else { set_saturation_components(b, g, r, s); } } fn set_lum(r: &mut i32, g: &mut i32, b: &mut i32, a: i32, l: i32) { let d = l - lum(*r, *g, *b); *r += d; *g += d; *b += d; clip_color(r, g, b, a); } // non-separable blend modes are done in non-premultiplied alpha fn blendfunc_nonsep_byte(sc: i32, dc: i32, sa: i32, da: i32, blendval: i32) -> u32 { clamp_div255round(sc * (255 - da) + dc * (255 - sa) + blendval) } pub struct Hue; impl Blend for Hue { fn blend(src: u32, dst: u32) -> u32 { let sr = get_packed_r32(src) as i32; let sg = get_packed_g32(src) as i32; let sb = get_packed_b32(src) as i32; let sa = get_packed_a32(src) as i32; let dr = get_packed_r32(dst) as i32; let dg = get_packed_g32(dst) as i32; let db = get_packed_b32(dst) as i32; let da = get_packed_a32(dst) as i32; let mut Sr; let mut Sg; let mut Sb; if sa != 0 && da != 0 { Sr = sr * sa; Sg = sg * sa; Sb = sb * sa; set_sat(&mut Sr, &mut Sg, &mut Sb, sat(dr, dg, db) * sa); set_lum(&mut Sr, &mut Sg, &mut Sb, sa * da, lum(dr, dg, db) * sa); } else { Sr = 0; Sg = 0; Sb = 0; } let a = srcover_byte(sa as u32, da as u32); let r = blendfunc_nonsep_byte(sr, dr, sa, da, Sr); let g = blendfunc_nonsep_byte(sg, dg, sa, da, Sg); let b = blendfunc_nonsep_byte(sb, db, sa, da, Sb); return pack_argb32(a, r, g, b); } } pub struct Saturation; impl Blend for Saturation { fn blend(src: u32, dst: u32) -> u32 { let sr = get_packed_r32(src) as i32; let sg = get_packed_g32(src) as i32; let sb = get_packed_b32(src) as i32; let sa = get_packed_a32(src) as i32; let dr = get_packed_r32(dst) as i32; let dg = get_packed_g32(dst) as i32; let db = get_packed_b32(dst) as i32; let da = get_packed_a32(dst) as i32; let mut Dr; let mut Dg; let mut Db; if sa != 0 && da != 0 { Dr = dr * sa; Dg = dg * sa; Db = db * sa; set_sat(&mut Dr, &mut Dg, &mut Db, sat(sr, sg, sb) * da); set_lum(&mut Dr, &mut Dg, &mut Db, sa * da, lum(dr, dg, db) * sa); } else { Dr = 0; Dg = 0; Db = 0; } let a = srcover_byte(sa as u32, da as u32); let r = blendfunc_nonsep_byte(sr, dr, sa, da, Dr); let g = blendfunc_nonsep_byte(sg, dg, sa, da, Dg); let b = blendfunc_nonsep_byte(sb, db, sa, da, Db); return pack_argb32(a, r, g, b); } } pub struct Color; impl Blend for Color { fn blend(src: u32, dst: u32) -> u32 { let sr = get_packed_r32(src) as i32; let sg = get_packed_g32(src) as i32; let sb = get_packed_b32(src) as i32; let sa = get_packed_a32(src) as i32; let dr = get_packed_r32(dst) as i32; let dg = get_packed_g32(dst) as i32; let db = get_packed_b32(dst) as i32; let da = get_packed_a32(dst) as i32; let mut Sr; let mut Sg; let mut Sb; if sa != 0 && da != 0 { Sr = sr * sa; Sg = sg * sa; Sb = sb * sa; set_lum(&mut Sr, &mut Sg, &mut Sb, sa * da, lum(dr, dg, db) * sa); } else { Sr = 0; Sg = 0; Sb = 0; } let a = srcover_byte(sa as u32, da as u32); let r = blendfunc_nonsep_byte(sr, dr, sa, da, Sr); let g = blendfunc_nonsep_byte(sg, dg, sa, da, Sg); let b = blendfunc_nonsep_byte(sb, db, sa, da, Sb); return pack_argb32(a, r, g, b); } } pub struct Luminosity; impl Blend for Luminosity { // B(Cb, Cs) = SetLum(Cb, Lum(Cs)) // Create a color with the luminosity of the source color and the hue and saturation of the backdrop color. fn blend(src: u32, dst: u32) -> u32 { let sr = get_packed_r32(src) as i32; let sg = get_packed_g32(src) as i32; let sb = get_packed_b32(src) as i32; let sa = get_packed_a32(src) as i32; let dr = get_packed_r32(dst) as i32; let dg = get_packed_g32(dst) as i32; let db = get_packed_b32(dst) as i32; let da = get_packed_a32(dst) as i32; let mut Dr; let mut Dg; let mut Db; if sa != 0 && da != 0 { Dr = dr * sa; Dg = dg * sa; Db = db * sa; set_lum(&mut Dr, &mut Dg, &mut Db, sa * da, lum(sr, sg, sb) * da); } else { Dr = 0; Dg = 0; Db = 0; } let a = srcover_byte(sa as u32, da as u32); let r = blendfunc_nonsep_byte(sr, dr, sa, da, Dr); let g = blendfunc_nonsep_byte(sg, dg, sa, da, Dg); let b = blendfunc_nonsep_byte(sb, db, sa, da, Db); return pack_argb32(a, r, g, b); } } sw-composite-0.7.2/src/lib.rs010064400007650000024000000407561355605561200143170ustar0000000000000000pub mod blend; const BILINEAR_INTERPOLATION_BITS: u32 = 4; const A32_SHIFT: u32 = 24; const R32_SHIFT: u32 = 16; const G32_SHIFT: u32 = 8; const B32_SHIFT: u32 = 0; type Alpha256 = u32; /// A unpremultiplied color #[derive(Clone, Copy, PartialEq, Debug)] pub struct Color { val: u32 } impl Color { pub fn new(a: u8, r: u8, g: u8, b: u8) -> Color { Color { val: ((a as u32) << A32_SHIFT) | ((r as u32) << R32_SHIFT) | ((g as u32) << G32_SHIFT) | ((b as u32) << B32_SHIFT) } } } #[derive(Clone, Copy)] pub struct Image<'a> { pub width: i32, pub height: i32, pub data: &'a [u32], } /// t is 0..256 #[inline] pub fn lerp(a: u32, b: u32, t: u32) -> u32 { // we can reduce this to two multiplies // http://stereopsis.com/doubleblend.html let mask = 0xff00ff; let brb = ((b & 0xff00ff) * t) >> 8; let bag = ((b >> 8) & 0xff00ff) * t; let t = 256 - t; let arb = ((a & 0xff00ff) * t) >> 8; let aag = ((a >> 8) & 0xff00ff) * t; let rb = arb + brb; let ag = aag + bag; return (rb & mask) | (ag & !mask); } /// color is unpremultiplied argb #[derive(Clone, Copy, Debug)] pub struct GradientStop { pub position: f32, pub color: Color, } pub struct GradientSource { matrix: MatrixFixedPoint, lut: [u32; 256], } pub struct TwoCircleRadialGradientSource { matrix: MatrixFixedPoint, c1x: f32, c1y: f32, r1: f32, c2x: f32, c2y: f32, r2: f32, lut: [u32; 256], } #[derive(Clone, Copy)] pub enum Spread { Pad, Reflect, Repeat, } fn apply_spread(mut x: i32, spread: Spread) -> i32 { match spread { Spread::Pad => { if x >= 255 { x = 255; } if x < 0 { x = 0; } } Spread::Repeat => { x &= 255; } Spread::Reflect => { // a trick from skia to reflect the bits. 256 -> 255 let sign = (x << 23) >> 31; x = (x ^ sign) & 255; } } x } impl GradientSource { pub fn radial_gradient_eval(&self, x: u16, y: u16, spread: Spread) -> u32 { let p = self.matrix.transform(x, y); // there's no chance that p will overflow when squared // so it's safe to use sqrt let px = p.x as f32; let py = p.y as f32; let mut distance = (px * px + py * py).sqrt() as i32; distance >>= 8; self.lut[apply_spread(distance, spread) as usize] } pub fn linear_gradient_eval(&self, x: u16, y: u16, spread: Spread) -> u32 { let p = self.matrix.transform(x, y); let lx = p.x >> 8; self.lut[apply_spread(lx, spread) as usize] } } impl TwoCircleRadialGradientSource { pub fn eval(&self, x: u16, y: u16, spread: Spread) -> u32 { let p = self.matrix.transform(x, y); // XXX: this is slow and bad // the derivation is from pixman let px = p.x as f32 / 65536.; let py = p.y as f32 / 65536.; let cdx = self.c2x - self.c1x; let cdy = self.c2y - self.c1y; let pdx = px - self.c1x; let pdy = py - self.c1y; let dr = self.r2 - self.r1; let a = cdx*cdx + cdy*cdy - dr*dr; let b = pdx*cdx + pdy*cdy + self.r1*dr; let c = pdx*pdx + pdy*pdy - self.r1*self.r1; let t1 = (b + (b*b - a*c).sqrt())/a; let t2 = (b - (b*b - a*c).sqrt())/a; let t = if a == 0. { 0. } else { if t1 > t2 { t1 } else { t2 } }; self.lut[apply_spread((t * 255.) as i32, spread) as usize] } } #[derive(Clone, Debug)] pub struct Gradient { pub stops: Vec } impl Gradient { pub fn make_source(&self, matrix: &MatrixFixedPoint, alpha: u32) -> Box { let mut source = Box::new(GradientSource { matrix: (*matrix).clone(), lut: [0; 256] }); self.build_lut(&mut source.lut, alpha_to_alpha256(alpha)); source } pub fn make_two_circle_source(&self, c1x: f32, c1y: f32, r1: f32, c2x: f32, c2y: f32, r2: f32, matrix: &MatrixFixedPoint, alpha: u32) -> Box { let mut source = Box::new(TwoCircleRadialGradientSource { c1x, c1y, r1, c2x, c2y, r2, matrix: (*matrix).clone(), lut: [0; 256] }); self.build_lut(&mut source.lut, alpha_to_alpha256(alpha)); source } fn build_lut(&self, lut: &mut [u32; 256], alpha: Alpha256) { let mut stop_idx = 0; let mut stop = &self.stops[stop_idx]; let mut last_color = alpha_mul(stop.color.val, alpha); let mut last_pos = 0; let mut next_color = last_color; let mut next_pos = (255. * stop.position) as u32; let mut i = 0; const FIXED_SHIFT: u32 = 8; const FIXED_ONE: u32 = 1 << FIXED_SHIFT; const FIXED_HALF: u32 = FIXED_ONE >> 1; while i <= 255 { while next_pos <= i { stop_idx += 1; last_color = next_color; if stop_idx >= self.stops.len() { stop = &self.stops[self.stops.len() - 1]; next_pos = 255; next_color = alpha_mul(stop.color.val, alpha); break; } else { stop = &self.stops[stop_idx]; } next_pos = (255. * stop.position) as u32; next_color = alpha_mul(stop.color.val, alpha); } let inverse = (FIXED_ONE * 256) / (next_pos - last_pos); let mut t = 0; // XXX we could actually avoid doing any multiplications inside // this loop by accumulating (next_color - last_color)*inverse while i <= next_pos { // stops need to be represented in unpremultipled form otherwise we lose information // that we need when lerping between colors lut[i as usize] = premultiply(lerp(last_color, next_color, (t + FIXED_HALF) >> FIXED_SHIFT)); t += inverse; i += 1; } last_pos = next_pos; } } } pub trait PixelFetch { fn get_pixel(bitmap: &Image, x: i32, y: i32) -> u32; } pub struct PadFetch; impl PixelFetch for PadFetch { fn get_pixel(bitmap: &Image, mut x: i32, mut y: i32) -> u32 { if x < 0 { x = 0; } if x >= bitmap.width { x = bitmap.width - 1; } if y < 0 { y = 0; } if y >= bitmap.height { y = bitmap.height - 1; } return bitmap.data[(y * bitmap.width + x) as usize]; } } pub struct RepeatFetch; impl PixelFetch for RepeatFetch { fn get_pixel(bitmap: &Image, mut x: i32, mut y: i32) -> u32 { // XXX: This is a very slow approach to repeating. // We should instead do the wrapping in the iterator x = x % bitmap.width; if x < 0 { x = x + bitmap.width; } y = y % bitmap.height; if y < 0 { y = y + bitmap.height; } return bitmap.data[(y * bitmap.width + x) as usize]; } } /* Inspired by Filter_32_opaque from Skia */ fn bilinear_interpolation( tl: u32, tr: u32, bl: u32, br: u32, mut distx: u32, mut disty: u32, ) -> u32 { let distxy; let distxiy; let distixy; let distixiy; let mut lo; let mut hi; distx <<= 4 - BILINEAR_INTERPOLATION_BITS; disty <<= 4 - BILINEAR_INTERPOLATION_BITS; distxy = distx * disty; distxiy = (distx << 4) - distxy; /* distx * (16 - disty) */ distixy = (disty << 4) - distxy; /* disty * (16 - distx) */ /* (16 - distx) * (16 - disty) */ // The intermediate calculation can underflow so we use // wrapping arithmetic to let the compiler know that it's ok distixiy = (16u32 * 16) .wrapping_sub(disty << 4) .wrapping_sub(distx << 4) .wrapping_add(distxy); lo = (tl & 0xff00ff) * distixiy; hi = ((tl >> 8) & 0xff00ff) * distixiy; lo += (tr & 0xff00ff) * distxiy; hi += ((tr >> 8) & 0xff00ff) * distxiy; lo += (bl & 0xff00ff) * distixy; hi += ((bl >> 8) & 0xff00ff) * distixy; lo += (br & 0xff00ff) * distxy; hi += ((br >> 8) & 0xff00ff) * distxy; ((lo >> 8) & 0xff00ff) | (hi & !0xff00ff) } /* Inspired by Filter_32_alpha from Skia */ fn bilinear_interpolation_alpha( tl: u32, tr: u32, bl: u32, br: u32, mut distx: u32, mut disty: u32, alpha: Alpha256 ) -> u32 { let distxy; let distxiy; let distixy; let distixiy; let mut lo; let mut hi; distx <<= 4 - BILINEAR_INTERPOLATION_BITS; disty <<= 4 - BILINEAR_INTERPOLATION_BITS; distxy = distx * disty; distxiy = (distx << 4) - distxy; /* distx * (16 - disty) */ distixy = (disty << 4) - distxy; /* disty * (16 - distx) */ /* (16 - distx) * (16 - disty) */ // The intermediate calculation can underflow so we use // wrapping arithmetic to let the compiler know that it's ok distixiy = (16u32 * 16) .wrapping_sub(disty << 4) .wrapping_sub(distx << 4) .wrapping_add(distxy); lo = (tl & 0xff00ff) * distixiy; hi = ((tl >> 8) & 0xff00ff) * distixiy; lo += (tr & 0xff00ff) * distxiy; hi += ((tr >> 8) & 0xff00ff) * distxiy; lo += (bl & 0xff00ff) * distixy; hi += ((bl >> 8) & 0xff00ff) * distixy; lo += (br & 0xff00ff) * distxy; hi += ((br >> 8) & 0xff00ff) * distxy; lo = ((lo >> 8) & 0xff00ff) * alpha; hi = ((hi >> 8) & 0xff00ff) * alpha; ((lo >> 8) & 0xff00ff) | (hi & !0xff00ff) } const FIXED_FRACTION_BITS: u32 = 16; pub const FIXED_ONE: i32 = 1 << FIXED_FRACTION_BITS; fn bilinear_weight(x: Fixed) -> u32 { // discard the unneeded bits of precision let reduced = x >> (FIXED_FRACTION_BITS - BILINEAR_INTERPOLATION_BITS); // extract the remaining fraction let fraction = reduced & ((1 << BILINEAR_INTERPOLATION_BITS) - 1); fraction as u32 } type Fixed = i32; fn fixed_to_int(x: Fixed) -> i32 { x >> FIXED_FRACTION_BITS } // there are various tricks the can be used // to make this faster. Let's just do simplest // thing for now pub fn float_to_fixed(x: f32) -> Fixed { ((x * (1 << FIXED_FRACTION_BITS) as f32) + 0.5) as i32 } pub fn fetch_bilinear(image: &Image, x: Fixed, y: Fixed) -> u32 { let dist_x = bilinear_weight(x); let dist_y = bilinear_weight(y); let x1 = fixed_to_int(x); let y1 = fixed_to_int(y); let x2 = x1 + 1; let y2 = y1 + 1; let tl = Fetch::get_pixel(image, x1, y1); let tr = Fetch::get_pixel(image, x2, y1); let bl = Fetch::get_pixel(image, x1, y2); let br = Fetch::get_pixel(image, x2, y2); bilinear_interpolation(tl, tr, bl, br, dist_x, dist_y) } pub fn fetch_bilinear_alpha(image: &Image, x: Fixed, y: Fixed, alpha: Alpha256) -> u32 { let dist_x = bilinear_weight(x); let dist_y = bilinear_weight(y); let x1 = fixed_to_int(x); let y1 = fixed_to_int(y); let x2 = x1 + 1; let y2 = y1 + 1; let tl = Fetch::get_pixel(image, x1, y1); let tr = Fetch::get_pixel(image, x2, y1); let bl = Fetch::get_pixel(image, x1, y2); let br = Fetch::get_pixel(image, x2, y2); bilinear_interpolation_alpha(tl, tr, bl, br, dist_x, dist_y, alpha) } pub fn fetch_nearest(image: &Image, x: Fixed, y: Fixed) -> u32 { Fetch::get_pixel(image, fixed_to_int(x), fixed_to_int(y)) } pub fn fetch_nearest_alpha(image: &Image, x: Fixed, y: Fixed, alpha: Alpha256) -> u32 { alpha_mul(Fetch::get_pixel(image, fixed_to_int(x), fixed_to_int(y)), alpha) } pub struct PointFixedPoint { pub x: Fixed, pub y: Fixed, } #[derive(Clone)] pub struct MatrixFixedPoint { pub xx: Fixed, pub xy: Fixed, pub yx: Fixed, pub yy: Fixed, pub x0: Fixed, pub y0: Fixed, } impl MatrixFixedPoint { pub fn transform(&self, x: u16, y: u16) -> PointFixedPoint { let x = x as i32; let y = y as i32; // when taking integer parameters we can use a regular mulitply instead of a fixed one PointFixedPoint { x: x * self.xx + self.xy * y + self.x0, y: y * self.yy + self.yx * x + self.y0, } } } fn premultiply(c: u32) -> u32 { // This could be optimized by using SWAR let a = get_packed_a32(c); let mut r = get_packed_r32(c); let mut g = get_packed_g32(c); let mut b = get_packed_b32(c); if a < 255 { r = muldiv255(r, a); g = muldiv255(g, a); b = muldiv255(b, a); } pack_argb32(a, r, g, b) } fn pack_argb32(a: u32, r: u32, g: u32, b: u32) -> u32 { debug_assert!(r <= a); debug_assert!(g <= a); debug_assert!(b <= a); return (a << A32_SHIFT) | (r << R32_SHIFT) | (g << G32_SHIFT) | (b << B32_SHIFT); } fn get_packed_a32(packed: u32) -> u32 { ((packed) << (24 - A32_SHIFT)) >> 24 } fn get_packed_r32(packed: u32) -> u32 { ((packed) << (24 - R32_SHIFT)) >> 24 } fn get_packed_g32(packed: u32) -> u32 { ((packed) << (24 - G32_SHIFT)) >> 24 } fn get_packed_b32(packed: u32) -> u32 { ((packed) << (24 - B32_SHIFT)) >> 24 } #[inline] fn packed_alpha(x: u32) -> u32 { x >> A32_SHIFT } // this is an approximation of true 'over' that does a division by 256 instead // of 255. It is the same style of blending that Skia does. pub fn over(src: u32, dst: u32) -> u32 { let a = packed_alpha(src); let a = 256 - a; let mask = 0xff00ff; let rb = ((dst & 0xff00ff) * a) >> 8; let ag = ((dst >> 8) & 0xff00ff) * a; src + (rb & mask) | (ag & !mask) } #[inline] pub fn alpha_to_alpha256(alpha: u32) -> u32 { alpha + 1 } /** Calculates 256 - (value * alpha256) / 255 in range [0,256], * for [0,255] value and [0,256] alpha256. */ #[inline] fn alpha_mul_inv256(value: u32, alpha256: u32) -> u32 { let prod = 0xFFFF - value * alpha256; return (prod + (prod >> 8)) >> 8; } /** Calculates (value * alpha256) / 255 in range [0,256], * for [0,255] value and [0,256] alpha256. */ fn alpha_mul_256(value: u32, alpha256: u32) -> u32 { let prod = value * alpha256; return (prod + (prod >> 8)) >> 8; } pub fn muldiv255(a: u32, b: u32) -> u32 { let tmp = a * b + 128; ((tmp + (tmp >> 8)) >> 8) } pub fn div255(a: u32) -> u32 { let tmp = a + 128; ((tmp + (tmp >> 8)) >> 8) } #[inline] pub fn alpha_mul(x: u32, a: Alpha256) -> u32 { let mask = 0xFF00FF; let src_rb = ((x & mask) * a) >> 8; let src_ag = ((x >> 8) & mask) * a; return (src_rb & mask) | (src_ag & !mask) } // This approximates the division by 255 using a division by 256. // It matches the behaviour of SkBlendARGB32 from Skia in 2017. // The behaviour of this function was changed in 2016 by Lee Salzman // in Skia:40254c2c2dc28a34f96294d5a1ad94a99b0be8a6 to keep more of the // intermediate precision #[inline] pub fn over_in(src: u32, dst: u32, alpha: u32) -> u32 { let src_alpha = alpha_to_alpha256(alpha); let dst_alpha = alpha_mul_inv256(packed_alpha(src), src_alpha); let mask = 0xFF00FF; let src_rb = (src & mask) * src_alpha; let src_ag = ((src >> 8) & mask) * src_alpha; let dst_rb = (dst & mask) * dst_alpha; let dst_ag = ((dst >> 8) & mask) * dst_alpha; // we sum src and dst before reducing to 8 bit to avoid accumulating rounding errors return (((src_rb + dst_rb) >> 8) & mask) | ((src_ag + dst_ag) & !mask); } // Similar to over_in but includes an additional clip alpha value #[inline] pub fn over_in_in(src: u32, dst: u32, mask: u32, clip: u32) -> u32 { let src_alpha = alpha_to_alpha256(mask); let src_alpha = alpha_to_alpha256(alpha_mul_256(clip, src_alpha)); let dst_alpha = alpha_mul_inv256(packed_alpha(src), src_alpha); let mask = 0xFF00FF; let src_rb = (src & mask) * src_alpha; let src_ag = ((src >> 8) & mask) * src_alpha; let dst_rb = (dst & mask) * dst_alpha; let dst_ag = ((dst >> 8) & mask) * dst_alpha; // we sum src and dst before reducing to 8 bit to avoid accumulating rounding errors return (((src_rb + dst_rb) >> 8) & mask) | ((src_ag + dst_ag) & !mask); } #[test] fn test_over_in() { assert_eq!(over_in(0xff00ff00, 0xffff0000, 0xff), 0xff00ff00); assert_eq!(over_in_in(0xff00ff00, 0xffff0000, 0xff, 0xff), 0xff00ff00); } pub fn alpha_lerp(src: u32, dst: u32, mask: u32, clip: u32) -> u32 { let alpha = alpha_mul_256(alpha_to_alpha256(mask), clip); return lerp(src, dst, alpha); } sw-composite-0.7.2/.gitignore010064400007650000024000000000311346442754000143630ustar0000000000000000/target **/*.rs.bk .idea sw-composite-0.7.2/src/sse2.rs010064400007650000024000000003351337132466700144160ustar0000000000000000fn unpack() -> { unpack_lo_epi8(); unpack_hi_epi8(); } pub fn over() { alpha = expand_alpha(src) dst = unpack(dst); dst = multiply(dst, alpha); dst = pack(dst); _mm_add_epi32(src, dst); }