//////////////////////////////////////////////////////////////////////////////// // // Licensed to the Apache Software Foundation (ASF) under one or more // contributor license agreements. See the NOTICE file distributed with // this work for additional information regarding copyright ownership. // The ASF licenses this file to You under the Apache License, Version 2.0 // (the "License"); you may not use this file except in compliance with // the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //////////////////////////////////////////////////////////////////////////////// kernel Saturation < namespace : "Flame"; vendor : "Adobe"; version : 1; description : "Saturation blend mode"; > { input image4 dst; input image4 src; output pixel4 result; // PB bytecode can't do functions, use macros to define common functions // gets the max number out of the three #define max3( x, y, z ) ( max((x), max((y), (z))) ) // gets the min number out of the three #define min3( x, y, z ) ( min((x), min((y), (z))) ) // gets the max number out of the three elements in a vector #define max3v(C) ( max3((C.x), (C.y), (C.z)) ) // gets the min number out of the three elements in a vector #define min3v(C) ( min3((C.x), (C.y), (C.z)) ) // Sat - returns float, takes in a pixel3, or pixel4 #define saturation(C) ( (max3((C.r), (C.g), (C.b)) - min3((C.r), (C.g), (C.b))) ) // Luminance - returns float, takes in a pixel3, or pixel4 #define luminance(C) ( (((C.r) * 0.3) + ((C.g) * 0.59) + ((C.b) * 0.11)) ) void evaluatePixel() { pixel4 a = sampleNearest(dst,outCoord()); // cb pixel4 b = sampleNearest(src,outCoord()); // cs // remove premultiplied (srcCP/srcA, dstCP/dstA) pixel3 cb = a.rgb; pixel3 cs = b.rgb; if (a.a > 0.0) { cb.rgb = a.rgb / a.a; } if (b.a > 0.0) { cs.rgb = b.rgb / b.a; } // dstA' = (1-srcA)*dstA + srcA result.a = (1.0-b.a)*a.a + b.a; // record old version of cb //float3 old_cb.rgb = cb.rgb; // SetSat(cb, Sat(cs)) // setSat (setSatColor, sat) -> setSatResult /* -------------------------------------------------------- * void setsaturation(inout float3 color, in float satVal) * makes color have the target saturation. * input and output of float3 color * input of the target saturation * * -------------------------------------------------------- void setsatcomponents(inout float minComp, inout float midComp, inout float maxComp, in float satVal) { midComp -= minComp; maxComp -= minComp; minComp = 0.0; if (maxComp > 0.0) { // max(..., 0.0000001) prevents divide by 0 midComp *= satVal/max(maxComp, 0.0000001); maxComp = satVal; } } */ float3 color = cb.rgb; float satVal = saturation(cs); if (color.x <= color.y) { if (color.y <= color.z) { // x <= y <= z // setsatcomponents(color.x, color.y, color.z, satVal); // min, mid, max, val color.y -= color.x; color.z -= color.x; color.x = 0.0; if (color.z > 0.0) { // max(..., 0.0000001) prevents divide by 0 color.y *= satVal/max(color.z, 0.0000001); color.z = satVal; } } else { if (color.x <= color.z) { // x <= z <= y // setsatcomponents(color.x, color.z, color.y, satVal); // min, mid, max, value color.z -= color.x; color.y -= color.x; color.x = 0.0; if (color.y > 0.0) { // max(..., 0.0000001) prevents divide by 0 color.z *= satVal/max(color.y, 0.0000001); color.y = satVal; } } else { // z <= x <= y // setsatcomponents(color.z, color.x, color.y, satVal); // min, mid, max, val color.x -= color.z; color.y -= color.z; color.z = 0.0; if (color.y > 0.0) { // max(..., 0.0000001) prevents divide by 0 color.x *= satVal/max(color.y, 0.0000001); color.y = satVal; } } } } else { if (color.x <= color.z) { // y <= x <= z // setsatcomponents(color.y, color.x, color.z, satVal); color.x -= color.y; color.z -= color.y; color.y = 0.0; if (color.z > 0.0) { // max(..., 0.0000001) prevents divide by 0 color.x *= satVal/max(color.z, 0.0000001); color.z = satVal; } } else { if (color.y <= color.z) { // y <= z <= x // setsatcomponents(color.y, color.z, color.x, satVal); // min, mid, max, val color.z -= color.y; color.x -= color.y; color.y = 0.0; if (color.x > 0.0) { // max(..., 0.0000001) prevents divide by 0 color.z *= satVal/max(color.x, 0.0000001); color.x = satVal; } } else { // z <= y <= x // setsatcomponents(color.z, color.y, color.x, satVal); // min, mid, max, val color.y -= color.z; color.x -= color.z; color.z = 0.0; if (color.x > 0.0) { // max(..., 0.0000001) prevents divide by 0 color.y *= satVal/max(color.x, 0.0000001); color.x = satVal; } } } } // end setSaturation - result: color ---------------------- // // adjustment //color = color + lum_cb - luminance(color); //dstColorOld - dstColor float3 adjVec = cb.rgb - color.rgb; float adjustment = luminance(adjVec); float3 adjustedColor = color + adjustment; // ClipRGB(adjustedColor) -> color_cl /* -------------------------------------------------------- * void clipcolor(inout float3 color) * clips color. * input and output float3 color_cl * * -------------------------------------------------------- */ float3 color_cl = adjustedColor; float lum_cl = luminance(color_cl); float3 lumVec = float3(lum_cl, lum_cl, lum_cl); float mini = min3v(color_cl); float maxi = max3v(color_cl); if (mini < 0.0) { mini = lum_cl - mini; // max(..., 0.0000001) prevents divide by 0 color_cl = lumVec + (color_cl - lumVec)*lum_cl/max(mini, 0.0000001); } if (maxi > 1.0) { maxi = maxi - lum_cl; // max(..., 0.0000001) prevents divide by 0 color_cl = lumVec + (color_cl - lumVec)*(1.0 - lum_cl)/max(maxi, 0.0000001); } // end clipcolor - result: color_cl ---------------------- // // dstCP' = (1-srcA)*dstCP + (1-dstA)*srcCP + srcA*dstA*Blend(srcCP/srcA, dstCP/dstA) result .rgb = ((1.0-b.a)*a.rgb) + ((1.0-a.a)*b.rgb) + b.a*a.a*color_cl.rgb; } }