blob: b5afd831d6e913f47c4dfc6486fd0c33398ee30a [file] [log] [blame]
 /* * Copyright 2019 Google LLC * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ // Convert RGBA -> HSLA (including unpremul). // // Based on work by Sam Hocevar, Emil Persson, and Ian Taylor . High-level ideas: // // - minimize the number of branches by sorting and computing the hue phase in parallel (vec4s) // // - trade the third sorting branch for a potentially faster std::min and leaving 2nd/3rd // channels unsorted (based on the observation that swapping both the channels and the bias sign // has no effect under abs) // // - use epsilon offsets for denominators, to avoid explicit zero-checks // // An additional trick we employ is deferring premul->unpremul conversion until the very end: the // alpha factor gets naturally simplified for H and S, and only L requires a dedicated unpremul // division (so we trade three divs for one). // //  http://lolengine.net/blog/2013/01/13/fast-rgb-to-hsv //  http://lolengine.net/blog/2013/07/27/rgb-to-hsv-in-glsl //  http://www.chilliant.com/rgb2hsv.html in fragmentProcessor inputFP; half4 main() { half4 c = sample(inputFP); half4 p = (c.g < c.b) ? half4(c.bg, -1, 2/3.0) : half4(c.gb, 0, -1/3.0); half4 q = (c.r < p.x) ? half4(p.x, c.r, p.yw) : half4(c.r, p.x, p.yz); // q.x -> max channel value // q.yz -> 2nd/3rd channel values (unsorted) // q.w -> bias value dependent on max channel selection half eps = 0.0001; half pmV = q.x; half pmC = pmV - min(q.y, q.z); half pmL = pmV - pmC * 0.5; half H = abs(q.w + (q.y - q.z) / (pmC * 6 + eps)); half S = pmC / (c.a + eps - abs(pmL * 2 - c.a)); half L = pmL / (c.a + eps); return half4(H, S, L, c.a); } @optimizationFlags { (inputFP ? ProcessorOptimizationFlags(inputFP.get()) : kAll_OptimizationFlags) & (kConstantOutputForConstantInput_OptimizationFlag | kPreservesOpaqueInput_OptimizationFlag) } @class { #include "include/private/SkColorData.h" SkPMColor4f constantOutputForConstantInput(const SkPMColor4f& inColor) const override { SkPMColor4f c = ConstantOutputForConstantInput(this->childProcessor(0), inColor); const auto p = (c.fG < c.fB) ? SkPMColor4f{ c.fB, c.fG, -1, 2/3.f } : SkPMColor4f{ c.fG, c.fB, 0, -1/3.f }, q = (c.fR < p) ? SkPMColor4f{ p, c.fR, p, p } : SkPMColor4f{ c.fR, p, p, p }; const auto eps = 0.0001f, // matching SkSL/ColorMatrix half4 epsilon pmV = q, pmC = pmV - std::min(q, q), pmL = pmV - pmC * 0.5f, H = std::abs(q + (q - q) / (pmC * 6 + eps)), S = pmC / (c.fA + eps - std::abs(pmL * 2 - c.fA)), L = pmL / (c.fA + eps); return { H, S, L, c.fA }; } }