aboutsummaryrefslogtreecommitdiff
path: root/tools/compile_assets/aseprite/utils/blend.odin
diff options
context:
space:
mode:
authorXander Swan <[hidden email]>2026-01-07 23:12:22 -0500
committerXander Swan <[hidden email]>2026-01-07 23:12:22 -0500
commit0988ab832bfc7a1b1c851125b6172cf68c6d9cb9 (patch)
tree460bc2d9f0bce463af273d6b2b2c20faa880ac29 /tools/compile_assets/aseprite/utils/blend.odin
parentade0dc4d257d053b7064184f193f8168c496e308 (diff)
doesn't compile but i'm commiting anywya
Diffstat (limited to 'tools/compile_assets/aseprite/utils/blend.odin')
-rw-r--r--tools/compile_assets/aseprite/utils/blend.odin727
1 files changed, 727 insertions, 0 deletions
diff --git a/tools/compile_assets/aseprite/utils/blend.odin b/tools/compile_assets/aseprite/utils/blend.odin
new file mode 100644
index 0000000..9bf0f7e
--- /dev/null
+++ b/tools/compile_assets/aseprite/utils/blend.odin
@@ -0,0 +1,727 @@
+package aseprite_file_handler_utility
+
+import "core:math"
+
+@(require) import "core:fmt"
+@(require) import "core:log"
+
+
+ASE_USE_BUGGED_SAT :: #config(ASE_USE_BUGGED_SAT, false)
+
+
+// https://printtechnologies.org/standards/files/pdf-reference-1.6-addendum-blend-modes.pdf
+
+@(private)
+slow_alpha :: proc(a: int, b: ..int) -> (res: int) {
+ // α = α1 * α2 *..αn / 255^(n-1)
+ if len(b) == 0 { return a }
+ if len(b) == 1 { return a * b[0] / 255}
+ res = a
+ d := 255
+ for i in b {
+ res *= i
+ d *= 255
+ }
+ return res / d
+}
+
+
+// Modifies current Image (`cur`)
+blend_images :: proc(last, cur: Image, opacity: int, mode: Blend_Mode) -> (err: Blend_Error) {
+ if len(last.data) != len(cur.data) {
+ return Blend_Error.Unequal_Image_Sizes
+ }
+ return blend_bytes(last.data, cur.data, opacity, mode)
+}
+
+// Modifies current Image (`cur`)
+blend_bytes :: proc(last, cur: []byte, opacity: int, mode: Blend_Mode) -> (err: Blend_Error) {
+ if len(last) != len(cur) {
+ return Blend_Error.Unequal_Image_Sizes
+ }
+
+ for pix in 0..<len(cur)/4 {
+ pos := pix * 4
+ l_pix, c_pix: [4]u8
+
+ copy(l_pix[:], last[pos:pos+4])
+ copy(c_pix[:], cur[pos:pos+4])
+
+ r_pix := blend(l_pix, c_pix, i32(opacity), mode) or_return
+
+ copy(cur[pos:pos+4], r_pix[:])
+ }
+ return
+}
+
+
+alpha :: mul
+mul :: proc{mul_u8, mul_i32, mul_int, mul_vec4, mul_vec3}
+
+mul_vec4 :: proc(a, b: [4]i32) -> [4]i32 {
+ t := a * b + 128
+ return {
+ ((t.r >> 8) + t.r) >> 8,
+ ((t.g >> 8) + t.g) >> 8,
+ ((t.b >> 8) + t.b) >> 8,
+ ((t.a >> 8) + t.a) >> 8,
+ }
+}
+
+mul_vec3 :: proc(a, b: [3]i32) -> [3]i32 {
+ t := a * b + 128
+ return {
+ ((t.r >> 8) + t.r) >> 8,
+ ((t.g >> 8) + t.g) >> 8,
+ ((t.b >> 8) + t.b) >> 8,
+ }
+}
+
+
+mul_i32 :: proc(a, b: i32) -> i32 {
+ // License `.\3rd party licenses\libpixman license`
+ // https://github.com/libpixman/pixman/blob/master/pixman/pixman-combine32.h#L67
+
+ t := a * b + 128
+ return ((t >> 8 ) + t ) >> 8
+}
+
+mul_int :: proc(a, b: int) -> i32 {
+ // License `.\3rd party licenses\libpixman license`
+ // https://github.com/libpixman/pixman/blob/master/pixman/pixman-combine32.h#L67
+
+ t := a * b + 128
+ return i32(((t >> 8 ) + t ) >> 8)
+}
+
+mul_u8 :: proc(a, b: byte) -> i32 {
+ // License `.\3rd party licenses\libpixman license`
+ // https://github.com/libpixman/pixman/blob/master/pixman/pixman-combine32.h#L67
+
+ t := a * b + 128
+ return i32(((t >> 8 ) + t ) >> 8)
+}
+
+
+div :: proc( #any_int a, b: u16) -> u16 {
+ // License `.\3rd party licenses\libpixman license`
+ // https://github.com/libpixman/pixman/blob/master/pixman/pixman-combine32.h#L70
+ return (a * 255 + (b / 2)) / b
+}
+
+blend :: proc(last, cur: Pixel, opacity: i32, mode: Blend_Mode) -> (res: Pixel, err: Blend_Error) {
+ // https://github.com/aseprite/aseprite/blob/main/src/doc/blend_funcs.cpp
+ if last.a == 0 {
+ res.rgb = cur.rgb
+ res.a = byte(mul(i32(cur.a), opacity))
+ return
+ }
+
+ back: [4]i32 = {i32(last.r), i32(last.g), i32(last.b), i32(last.a)}
+ pix: [4]i32 = {i32(cur.r), i32(cur.g), i32(cur.b), i32(cur.a)}
+
+ blen: [4]i32
+ switch mode {
+ case .Src: blen = blend_src(back, pix, opacity)
+ case .Merge: blen = blend_merge(back, pix, opacity)
+ case .Neg_BW: blen = blend_neg_bw(back, pix, opacity)
+ case .Red_Tint: blen = blend_red_tint(back, pix, opacity)
+ case .Blue_Tint: blen = blend_blue_tint(back, pix, opacity)
+ case .Dst_Over: blen = blend_normal_dst_over(back, pix, opacity)
+ case .Addition: blen = blend_addition(back, pix, opacity)
+ case .Color: blen = blend_color(back, pix, opacity)
+ case .Color_Burn: blen = blend_color_burn(back, pix, opacity)
+ case .Color_Dodge: blen = blend_color_dodge(back, pix, opacity)
+ case .Darken: blen = blend_darken(back, pix, opacity)
+ case .Difference: blen = blend_difference(back, pix, opacity)
+ case .Divide: blen = blend_divide(back, pix, opacity)
+ case .Exclusion: blen = blend_exclusion(back, pix, opacity)
+ case .Hard_Light: blen = blend_hard_light(back, pix, opacity)
+ case .Hue: blen = blend_hue(back, pix, opacity)
+ case .Lighten: blen = blend_lighten(back, pix, opacity)
+ case .Luminosity: blen = blend_luminosity(back, pix, opacity)
+ case .Multiply: blen = blend_multiply(back, pix, opacity)
+ case .Normal: blen = blend_normal(back, pix, opacity)
+ case .Overlay: blen = blend_overlay(back, pix, opacity)
+ case .Saturation: blen = blend_saturation(back, pix, opacity)
+ case .Screen: blen = blend_screen(back, pix, opacity)
+ case .Soft_Light: blen = blend_soft_light(back, pix, opacity)
+ case .Subtract: blen = blend_subtract(back, pix, opacity)
+ case .Unspecified: fallthrough
+ case:
+ log.error("Invalid Bland Mode provided:", mode)
+ return last, .Invalid_Mode
+ }
+
+ normal := blend_normal(back, pix, opacity)
+ norm_merge := blend_merge(normal, blen, back.a)
+ blen = blend_merge(norm_merge, blen, alpha(back.a, alpha(pix.a, opacity)))
+
+ return {u8(blen.r), u8(blen.g), u8(blen.b), u8(blen.a)}, nil
+}
+
+
+/* ------------------------------------------------------------------- */
+// RGB Blenders
+blend_normal :: proc(last, cur: B_Pixel, opacity: i32) -> (res: B_Pixel) {
+ if last.a == 0 {
+ res = cur
+ res.a = alpha(cur.a, opacity)
+ } else if cur.a == 0 {
+ return last
+ }
+
+ cur := cur
+ cur.a = alpha(cur.a, opacity)
+
+ res.a = cur.a + last.a - alpha(last.a, cur.a)
+ res.rgb = last.rgb + (cur.rgb - last.rgb) * cur.a / res.a
+
+ return res
+}
+
+blend_src :: proc(last, cur: B_Pixel, opacity: i32) -> (res: B_Pixel) {
+ return last
+}
+
+blend_merge :: proc(last, cur: B_Pixel, opacity: i32) -> (res: B_Pixel) {
+
+ if last.a == 0 {
+ res.rgb = cur.rgb
+
+ } else if cur.a == 0 {
+ res.rgb = last.rgb
+
+ } else {
+ op: [4]i32 = opacity
+ res = last + mul((cur - last), op)
+ }
+
+ res.a = last.a + mul((cur.a - last.a), opacity)
+ if res.a == 0 {
+ res.rgb = 0
+ }
+
+ return
+}
+
+blend_neg_bw :: proc(last, cur: B_Pixel, opacity: i32) -> (res: B_Pixel) {
+ if rgba_luma(last) < 128{
+ return { 255, 255, 255, 255 }
+ }
+ return { 0, 0, 0, 255 }
+}
+
+blend_red_tint :: proc(last, cur: B_Pixel, opacity: i32) -> (res: B_Pixel) {
+ cur := cur
+ luma := rgba_luma(cur)
+ cur = { (255 + luma) >> 1, luma >> 1, luma >> 1, last.a }
+ return blend_normal(last, cur, opacity)
+}
+
+blend_blue_tint :: proc(last, cur: B_Pixel, opacity: i32) -> (res: B_Pixel) {
+ luma := rgba_luma(cur)
+ res = { luma >> 1, luma >> 1, (255 + luma) >> 1, last.a }
+ return blend_normal(last, res, opacity)
+}
+
+blend_normal_dst_over :: proc(last, cur: B_Pixel, opacity: i32) -> (res: B_Pixel) {
+ res.a = alpha(cur.a, opacity)
+ return blend_normal(last, res, opacity)
+}
+
+
+blend_multiply :: proc(last, cur: B_Pixel, opacity: i32) -> (res: B_Pixel) {
+ res = mul(last, cur)
+ res.a = cur.a
+ return blend_normal(last, res, opacity)
+}
+
+blend_screen :: proc(last, cur: B_Pixel, opacity: i32) -> (res: B_Pixel) {
+ res = last + cur - mul(last, cur)
+ res.a = cur.a
+ return blend_normal(last, res, opacity)
+}
+
+blend_overlay :: proc(last, cur: B_Pixel, opacity: i32) -> (res: B_Pixel) {
+ hl :: proc(b, s: i32) -> i32 {
+ if s < 128 {
+ return mul(b, s<<1)
+ }
+ return b + ((s<<1)-255) - mul(b, (s<<1)-255)
+ }
+
+ res.r = hl(cur.r, last.r)
+ res.b = hl(cur.b, last.b)
+ res.g = hl(cur.g, last.g)
+ res.a = cur.a
+ return blend_normal(last, res, opacity)
+}
+
+blend_darken :: proc(last, cur: B_Pixel, opacity: i32) -> (res: B_Pixel) {
+ res.r = min(last.r, cur.r)
+ res.b = min(last.b, cur.b)
+ res.g = min(last.g, cur.g)
+ res.a = cur.a
+ return blend_normal(last, res, opacity)
+}
+
+blend_lighten :: proc(last, cur: B_Pixel, opacity: i32) -> (res: B_Pixel) {
+ res.r = max(last.r, cur.r)
+ res.b = max(last.b, cur.b)
+ res.g = max(last.g, cur.g)
+ res.a = cur.a
+ return blend_normal(last, res, opacity)
+}
+
+blend_color_dodge :: proc(last, cur: B_Pixel, opacity: i32) -> (res: B_Pixel) {
+ cd :: proc( #any_int b, s: u32) -> i32 {
+ if b == 0 {
+ return 0
+ }
+ s1 := 255 - s
+ if b >= s1 {
+ return 255
+ }
+ return i32(div(b, s1))
+ }
+
+ res.r = cd(last.r, cur.r)
+ res.b = cd(last.b, cur.b)
+ res.g = cd(last.g, cur.g)
+ res.a = cur.a
+ return blend_normal(last, res, opacity)
+}
+
+blend_color_burn :: proc(last, cur: B_Pixel, opacity: i32) -> (res: B_Pixel) {
+ cb :: proc( #any_int b, s: u32) -> i32 {
+ if b == 255 {
+ return 255
+ }
+ b1 := 255 - b
+ if b1 >= s {
+ return 0
+ }
+ return i32(255 - div(b1, s))
+ }
+
+ res.r = cb(last.r, cur.r)
+ res.b = cb(last.b, cur.b)
+ res.g = cb(last.g, cur.g)
+ res.a = cur.a
+ return blend_normal(last, res, opacity)
+}
+
+blend_hard_light :: proc(last, cur: B_Pixel, opacity: i32) -> (res: B_Pixel) {
+ hl :: proc(b, s: i32) -> i32 {
+ if s < 128 {
+ return mul(b, s<<1)
+ }
+ return b + ((s<<1)-255) - mul(b, (s<<1)-255)
+ }
+
+ res.r = hl(last.r, cur.r)
+ res.b = hl(last.b, cur.b)
+ res.g = hl(last.g, cur.g)
+ res.a = cur.a
+ return blend_normal(last, res, opacity)
+}
+
+blend_soft_light :: proc(last, cur: B_Pixel, opacity: i32) -> (res: B_Pixel) {
+ sl :: proc(b, s: i32) -> i32 {
+ b := f64(b) / 255
+ s := f64(s) / 255
+ r, d: f64
+
+ if b <= 0.25 {
+ d = ((16 * b - 12) * b + 4) * b
+ } else {
+ d = math.sqrt(b)
+ }
+
+ if s <= 0.5 {
+ r = b - (1 - 2 * s) * b * (1 - b)
+ } else {
+ r = b + (2 * s - 1) * (d - b)
+ }
+ return i32(r * 255 + 0.5)
+ }
+
+ res.r = sl(last.r, cur.r)
+ res.b = sl(last.b, cur.b)
+ res.g = sl(last.g, cur.g)
+ res.a = cur.a
+ return blend_normal(last, res, opacity)
+}
+
+blend_difference :: proc(last, cur: B_Pixel, opacity: i32) -> (res: B_Pixel) {
+ res.r = abs(last.r - cur.r)
+ res.b = abs(last.b - cur.b)
+ res.g = abs(last.g - cur.g)
+ res.a = cur.a
+ return blend_normal(last, res, opacity)
+}
+
+blend_exclusion :: proc(last, cur: B_Pixel, opacity: i32) -> (res: B_Pixel) {
+ ex :: proc(b, s: i32) -> i32 {
+ return b + s - (mul(b, s) << 1)
+ }
+
+ res.r = ex(last.r, cur.r)
+ res.b = ex(last.b, cur.b)
+ res.g = ex(last.g, cur.g)
+ res.a = cur.a
+ return blend_normal(last, res, opacity)
+}
+
+
+
+/* ------------------------------------------------------------------- */
+// HSV Blenders
+blend_hue :: proc(last, cur: B_Pixel, opacity: i32) -> (res: B_Pixel) {
+ /*
+ https://github.com/alpine-alpaca/asefile/blob/main/src/blend.rs#L392
+ https://drafts.fxtf.org/compositing-1/#blendinghue
+ https://printtechnologies.org/standards/files/pdf-reference-1.6-addendum-blend-modes.pdf
+ https://github.com/aseprite/aseprite/blob/main/src/doc/blend_funcs.cpp#L425
+ https://gitlab.freedesktop.org/pixman/pixman/-/blob/master/pixman/pixman-combine-float.c?ref_type=heads#L908
+ */
+
+ lpix := [3]f64{f64(last.r), f64(last.g), f64(last.b)} / 255
+ sat := hsv_sat(lpix)
+ lum := hsv_luma(lpix)
+
+ cpix := [3]f64{f64(cur.r), f64(cur.g), f64(cur.b)} / 255
+ cpix = set_sat(cpix, sat)
+ cpix = set_luma(cpix, lum)
+ cpix *= 255
+
+ return blend_normal(last, {i32(cpix.r), i32(cpix.g), i32(cpix.b), cur.a}, opacity)
+}
+
+
+blend_saturation :: proc(last, cur: B_Pixel, opacity: i32) -> (res: B_Pixel) {
+ pix := [3]f64{ f64(cur.r), f64(cur.g), f64(cur.b) }
+ pix /= 255
+ s := hsv_sat(pix)
+
+ pix = { f64(last.r), f64(last.g), f64(last.b) }
+ pix /= 255
+ l := hsv_luma(pix)
+
+ pix = set_luma(set_sat(pix, s), l)
+
+ pix *= 255
+ res = { i32(pix.r), i32(pix.g), i32(pix.b), cur.a }
+
+ return blend_normal(last, res, opacity)
+}
+
+
+blend_color :: proc(last, cur: B_Pixel, opacity: i32) -> (res: B_Pixel) {
+ pix := [3]f64{ f64(last.r), f64(last.g), f64(last.b) }
+ pix /= 255
+ l := hsv_luma(pix)
+
+ pix = { f64(cur.r), f64(cur.g), f64(cur.b)}
+ pix /= 255
+ pix = set_luma(pix, l)
+
+ pix *= 255
+ res = { i32(pix.r), i32(pix.g), i32(pix.b), cur.a }
+
+ return blend_normal(last, res, opacity)
+}
+
+
+blend_luminosity :: proc(last, cur: B_Pixel, opacity: i32) -> (res: B_Pixel) {
+ pix := [3]f64{ f64(cur.r), f64(cur.g), f64(cur.b) }
+ pix /= 255
+ l := hsv_luma(pix)
+
+ pix = { f64(last.r), f64(last.g), f64(last.b) }
+ pix /= 255
+ pix = set_luma(pix, l)
+
+ pix *= 255
+ res = { i32(pix.r), i32(pix.g), i32(pix.b), cur.a }
+
+ return blend_normal(last, res, opacity)
+}
+
+
+blend_addition :: proc(last, cur: B_Pixel, opacity: i32) -> (res: B_Pixel) {
+ res.r = min(last.r + cur.r, 255)
+ res.g = min(last.g + cur.g, 255)
+ res.b = min(last.b + cur.b, 255)
+ res.a = cur.a
+ return blend_normal(last, res, opacity)
+}
+
+blend_subtract :: proc(last, cur: B_Pixel, opacity: i32) -> (res: B_Pixel) {
+ res.r = max(last.r - cur.r, 0)
+ res.g = max(last.g - cur.g, 0)
+ res.b = max(last.b - cur.b, 0)
+ res.a = cur.a
+ return blend_normal(last, res, opacity)
+}
+
+blend_divide :: proc(last, cur: B_Pixel, opacity: i32) -> (res: B_Pixel) {
+ bd :: proc( #any_int b, s: u32) -> i32 {
+ if b == 0 {
+ return 0
+ } else if b >= s {
+ return 255
+ }
+ return i32(div(b, s))
+ }
+
+ res.r = bd( last.r, cur.r )
+ res.g = bd( last.g, cur.g )
+ res.b = bd( last.b, cur.b )
+ res.a = cur.a
+ return blend_normal(last, res, opacity)
+}
+
+
+
+/* ------------------------------------------------------------------- */
+// RGB Helpers
+
+rgba_luma :: proc(pix: B_Pixel) -> i32 {
+ return rgb_luma(pix.r, pix.b, pix.g)
+}
+
+rgb_luma :: proc(#any_int r, b, g: int) -> i32 {
+ return i32(( r*2126 + g*7152 + b*722 ) / 10000 )
+}
+
+/* ------------------------------------------------------------------- */
+// HSV Helpers
+
+hsv_luma :: proc(p: [3]f64) -> f64 {
+ return 0.3*p.r + 0.59*p.g + 0.11*p.b
+}
+
+hsv_sat :: proc(p: [3]f64) -> f64 {
+ return max(p.r, p.g, p.b) - min(p.r, p.g, p.b)
+}
+
+clip_color :: proc(pix: [3]f64) -> [3]f64 {
+ p := pix
+ lum := hsv_luma(p)
+ n := min(p.r, p.g, p.b)
+ x := max(p.r, p.g, p.b)
+
+ if n < 0 {
+ p = lum + (((p - lum) * lum) / (lum - n))
+ }
+
+ if x > 1 {
+ p = lum + (((p - lum) * (1 - lum)) / (x - lum))
+ }
+
+ return p
+}
+
+set_luma :: proc(p: [3]f64, l: f64) -> [3]f64 {
+ d := l - hsv_luma(p)
+ return clip_color(p + d)
+}
+
+
+set_sat :: proc(p: [3]f64, s: f64) -> (res: [3]f64) {
+ // https://github.com/aseprite/aseprite/blob/main/src/doc/blend_funcs.cpp#L400
+ // The chosen solution is a mix a asefile & pixman
+ // https://github.com/alpine-alpaca/asefile/blob/main/src/blend.rs#L522
+ // https://gitlab.freedesktop.org/pixman/pixman/-/blob/master/pixman/pixman-combine-float.c#L831
+
+
+ when ASE_USE_BUGGED_SAT == true {
+ res = p
+ MIN :: proc(x, y: ^f64) -> ^f64 {
+ return (x^ < y^) ? x : y
+ }
+ MAX :: proc(x, y: ^f64) -> ^f64 {
+ return (x^ > y^) ? x : y
+ }
+
+ r, g, b := &res.r, &res.g, &res.b
+ min := MIN(r, MIN(g, b))
+ max := MAX(r, MAX(g, b))
+ mid := r > g ? (g > b ? g : (r > b ? b : r)) : (g > b ? (b > r ? b : r): g)
+
+ if max > min {
+ mid^ = ((mid^ - min^) * s) / (max^ - min^)
+ max^ = s
+ } else {
+ mid^ = 0
+ max^ = 0
+ }
+ min^ = 0
+
+ } else {
+ val: [3]struct{v: f64, p: int} = {
+ {p.r, 0}, {p.g, 1}, {p.b, 2},
+ }
+ val.rg = val.r.v < val.g.v ? val.rg : val.gr
+ val.rb = val.r.v < val.b.v ? val.rb : val.br
+ val.gb = val.g.v < val.b.v ? val.gb : val.bg
+ min, mid, max := val.r.p, val.g.p, val.b.p
+
+ if (p[max] - p[min]) != 0 {
+ res[mid] = ( (p[mid] - p[min]) * s ) / ( p[max] - p[min] )
+ res[max] = s
+ }
+ }
+
+ return
+}
+
+
+/*
+ The following is for mine (blob) record keeping:
+
+ The way aseprite does set_set is very much bugged.
+ First the sorting doesn't work when if r == g and g < b
+
+ MIN :: proc(x, y: ^f64) -> ^f64 {
+ return (x^ < y^) ? x : y
+ }
+ MAX :: proc(x, y: ^f64) -> ^f64 {
+ return (y^ < x^) ? x : y
+ }
+
+ r, g, b := &pix.r, &pix.g, &pix.b
+ min := MIN(r, MIN(g, b))
+ max := MAX(r, MAX(g, b))
+ mid := r > g ? (g > b ? g : (r > b ? b : r)) : (g > b ? (b > r ? b : r): g)
+
+ Second the way it's value calculations is also wrong & only leaves the blue channel
+ if max > min {
+ mid^ = ((mid^ - min^) * s) / (max^ - min^)
+ max^ = s
+ } else {
+ mid^ = 0
+ max^ = 0
+ }
+
+ The sullotion griven by asefile
+ // r --*--*----- min
+ // | |
+ // g --*--|--*-- mid
+ // | |
+ // b -----*--*-- max
+
+ res = p
+ val := [3][2]f64{{p.r, 0}, {p.g, 1}, {p.b, 2}}
+
+ val.rg = val.r[0] < val.g[0] ? val.rg : val.gr
+ val.rb = val.r[0] < val.b[0] ? val.rb : val.br
+ val.gb = val.g[0] < val.b[0] ? val.gb : val.bg
+ min, mid, max := int(val.r[1]), int(val.g[1]), int(val.b[1])
+
+
+ if max > min {
+ res[mid] = ((res[mid] - res[min]) * s) / (res[max] - res[min])
+ res[max] = s
+ } else {
+ res[mid], res[max] = 0, 0
+ }
+ res[min] = 0
+
+ Pointer based version og working sullotion from pixman
+ res = p
+ min, mid, max: ^f64
+ if p.r > p.g {
+ if p.r > p.b {
+ max = &res.r
+ if p.g > p.b {
+ mid = &res.g
+ min = &res.b
+
+ } else {
+ mid = &res.b
+ min = &res.g
+ }
+
+ } else {
+ max = &res.b
+ mid = &res.r
+ min = &res.g
+ }
+
+ } else {
+ if p.r > p.b {
+ max = &res.g
+ mid = &res.r
+ min = &res.b
+
+ } else {
+ min = &res.r
+ if p.g > p.b {
+ max = &res.g
+ mid = &res.b
+
+ } else {
+ max = &res.b
+ mid = &res.g
+ }
+ }
+ }
+
+
+ if (max^ - min^) == 0 {
+ mid^ = 0
+ max^ = 0
+ } else {
+ mid^ = ((mid^ - min^) * s) / (max^ - min^)
+ max^ = s
+ }
+ min^ = 0
+
+
+ Pointerless pixman
+ min, mid, max: int
+ if p.r > p.g {
+ if p.r > p.b {
+ max = 0
+ if p.g > p.b {
+ mid = 1
+ min = 2
+
+ } else {
+ mid = 2
+ min = 1
+ }
+
+ } else {
+ max = 2
+ mid = 0
+ min = 1
+ }
+
+ } else {
+ if p.r > p.b {
+ max = 1
+ mid = 0
+ min = 2
+
+ } else {
+ min = 0
+ if p.g > p.b {
+ max = 1
+ mid = 2
+
+ } else {
+ max = 2
+ mid = 1
+ }
+ }
+ }
+
+ if (p[max] - p[min]) != 0 {
+ res[mid] = ((p[mid] - p[min]) * s) / (p[max] - p[min])
+ res[max] = s
+ }
+*/