SkColorSpaceXform.cpp revision c4ce6b592487305de251bbebaf8eeee38371b877
19876ac5b3016e5353c072378ac1545a0a2270757msarett/* 29876ac5b3016e5353c072378ac1545a0a2270757msarett * Copyright 2016 Google Inc. 39876ac5b3016e5353c072378ac1545a0a2270757msarett * 49876ac5b3016e5353c072378ac1545a0a2270757msarett * Use of this source code is governed by a BSD-style license that can be 59876ac5b3016e5353c072378ac1545a0a2270757msarett * found in the LICENSE file. 69876ac5b3016e5353c072378ac1545a0a2270757msarett */ 79876ac5b3016e5353c072378ac1545a0a2270757msarett 89876ac5b3016e5353c072378ac1545a0a2270757msarett#include "SkColorPriv.h" 99876ac5b3016e5353c072378ac1545a0a2270757msarett#include "SkColorSpace_Base.h" 109876ac5b3016e5353c072378ac1545a0a2270757msarett#include "SkColorSpaceXform.h" 11a9e878c836994bce695274b4c28890290139dcdfmsarett#include "SkOpts.h" 129876ac5b3016e5353c072378ac1545a0a2270757msarett 13dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarettstatic inline bool compute_gamut_xform(SkMatrix44* srcToDst, const SkMatrix44& srcToXYZ, 14dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett const SkMatrix44& dstToXYZ) { 159876ac5b3016e5353c072378ac1545a0a2270757msarett if (!dstToXYZ.invert(srcToDst)) { 169876ac5b3016e5353c072378ac1545a0a2270757msarett return false; 179876ac5b3016e5353c072378ac1545a0a2270757msarett } 189876ac5b3016e5353c072378ac1545a0a2270757msarett 199876ac5b3016e5353c072378ac1545a0a2270757msarett srcToDst->postConcat(srcToXYZ); 209876ac5b3016e5353c072378ac1545a0a2270757msarett return true; 219876ac5b3016e5353c072378ac1545a0a2270757msarett} 229876ac5b3016e5353c072378ac1545a0a2270757msarett 239876ac5b3016e5353c072378ac1545a0a2270757msarettstd::unique_ptr<SkColorSpaceXform> SkColorSpaceXform::New(const sk_sp<SkColorSpace>& srcSpace, 249876ac5b3016e5353c072378ac1545a0a2270757msarett const sk_sp<SkColorSpace>& dstSpace) { 259876ac5b3016e5353c072378ac1545a0a2270757msarett if (!srcSpace || !dstSpace) { 26dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett // Invalid input 279876ac5b3016e5353c072378ac1545a0a2270757msarett return nullptr; 289876ac5b3016e5353c072378ac1545a0a2270757msarett } 299876ac5b3016e5353c072378ac1545a0a2270757msarett 30dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett if (as_CSB(srcSpace)->colorLUT() || as_CSB(dstSpace)->colorLUT()) { 31dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett // Unimplemented 32dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett return nullptr; 33dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett } 349876ac5b3016e5353c072378ac1545a0a2270757msarett 35dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett SkMatrix44 srcToDst(SkMatrix44::kUninitialized_Constructor); 36dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett if (!compute_gamut_xform(&srcToDst, srcSpace->xyz(), dstSpace->xyz())) { 37dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett return nullptr; 38dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett } 39dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett 40a9e878c836994bce695274b4c28890290139dcdfmsarett if (SkColorSpace::k2Dot2Curve_GammaNamed == srcSpace->gammaNamed() && 41a9e878c836994bce695274b4c28890290139dcdfmsarett SkColorSpace::k2Dot2Curve_GammaNamed == dstSpace->gammaNamed()) 42a9e878c836994bce695274b4c28890290139dcdfmsarett { 43a9e878c836994bce695274b4c28890290139dcdfmsarett return std::unique_ptr<SkColorSpaceXform>(new Sk2Dot2Xform(srcToDst)); 449876ac5b3016e5353c072378ac1545a0a2270757msarett } 459876ac5b3016e5353c072378ac1545a0a2270757msarett 46dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett return std::unique_ptr<SkColorSpaceXform>( 47dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett new SkDefaultXform(as_CSB(srcSpace)->gammas(), srcToDst, as_CSB(dstSpace)->gammas())); 489876ac5b3016e5353c072378ac1545a0a2270757msarett} 499876ac5b3016e5353c072378ac1545a0a2270757msarett 509876ac5b3016e5353c072378ac1545a0a2270757msarett/////////////////////////////////////////////////////////////////////////////////////////////////// 519876ac5b3016e5353c072378ac1545a0a2270757msarett 52a9e878c836994bce695274b4c28890290139dcdfmsarettSk2Dot2Xform::Sk2Dot2Xform(const SkMatrix44& srcToDst) 53a9e878c836994bce695274b4c28890290139dcdfmsarett{ 54a9e878c836994bce695274b4c28890290139dcdfmsarett // Build row major 4x4 matrix: 55a9e878c836994bce695274b4c28890290139dcdfmsarett // rX gX bX 0 56a9e878c836994bce695274b4c28890290139dcdfmsarett // rY gY bY 0 57a9e878c836994bce695274b4c28890290139dcdfmsarett // rZ gZ bZ 0 58a9e878c836994bce695274b4c28890290139dcdfmsarett // rQ gQ bQ 0 59a9e878c836994bce695274b4c28890290139dcdfmsarett fSrcToDst[0] = srcToDst.getFloat(0, 0); 60a9e878c836994bce695274b4c28890290139dcdfmsarett fSrcToDst[1] = srcToDst.getFloat(0, 1); 61a9e878c836994bce695274b4c28890290139dcdfmsarett fSrcToDst[2] = srcToDst.getFloat(0, 2); 62a9e878c836994bce695274b4c28890290139dcdfmsarett fSrcToDst[3] = 0.0f; 63a9e878c836994bce695274b4c28890290139dcdfmsarett fSrcToDst[4] = srcToDst.getFloat(1, 0); 64a9e878c836994bce695274b4c28890290139dcdfmsarett fSrcToDst[5] = srcToDst.getFloat(1, 1); 65a9e878c836994bce695274b4c28890290139dcdfmsarett fSrcToDst[6] = srcToDst.getFloat(1, 2); 66a9e878c836994bce695274b4c28890290139dcdfmsarett fSrcToDst[7] = 0.0f; 67a9e878c836994bce695274b4c28890290139dcdfmsarett fSrcToDst[8] = srcToDst.getFloat(2, 0); 68a9e878c836994bce695274b4c28890290139dcdfmsarett fSrcToDst[9] = srcToDst.getFloat(2, 1); 69a9e878c836994bce695274b4c28890290139dcdfmsarett fSrcToDst[10] = srcToDst.getFloat(2, 2); 70a9e878c836994bce695274b4c28890290139dcdfmsarett fSrcToDst[11] = 0.0f; 71a9e878c836994bce695274b4c28890290139dcdfmsarett fSrcToDst[12] = srcToDst.getFloat(3, 0); 72a9e878c836994bce695274b4c28890290139dcdfmsarett fSrcToDst[13] = srcToDst.getFloat(3, 1); 73a9e878c836994bce695274b4c28890290139dcdfmsarett fSrcToDst[14] = srcToDst.getFloat(3, 2); 74a9e878c836994bce695274b4c28890290139dcdfmsarett fSrcToDst[15] = 0.0f; 75a9e878c836994bce695274b4c28890290139dcdfmsarett} 76a9e878c836994bce695274b4c28890290139dcdfmsarett 77a9e878c836994bce695274b4c28890290139dcdfmsarettvoid Sk2Dot2Xform::xform_RGBA_8888(uint32_t* dst, const uint32_t* src, uint32_t len) const { 78a9e878c836994bce695274b4c28890290139dcdfmsarett SkOpts::color_xform_2Dot2_RGBA_to_8888(dst, src, len, fSrcToDst); 79a9e878c836994bce695274b4c28890290139dcdfmsarett} 80a9e878c836994bce695274b4c28890290139dcdfmsarett 81a9e878c836994bce695274b4c28890290139dcdfmsarett/////////////////////////////////////////////////////////////////////////////////////////////////// 82a9e878c836994bce695274b4c28890290139dcdfmsarett 83dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarettstatic inline float byte_to_float(uint8_t v) { 84dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett return ((float) v) * (1.0f / 255.0f); 859876ac5b3016e5353c072378ac1545a0a2270757msarett} 869876ac5b3016e5353c072378ac1545a0a2270757msarett 87a9e878c836994bce695274b4c28890290139dcdfmsarett// Expand range from 0-1 to 0-255, then convert. 88a9e878c836994bce695274b4c28890290139dcdfmsarettstatic inline uint8_t clamp_normalized_float_to_byte(float v) { 899876ac5b3016e5353c072378ac1545a0a2270757msarett v = v * 255.0f; 90a9e878c836994bce695274b4c28890290139dcdfmsarett if (v >= 254.5f) { 919876ac5b3016e5353c072378ac1545a0a2270757msarett return 255; 92a9e878c836994bce695274b4c28890290139dcdfmsarett } else if (v < 0.5f) { 939876ac5b3016e5353c072378ac1545a0a2270757msarett return 0; 949876ac5b3016e5353c072378ac1545a0a2270757msarett } else { 959876ac5b3016e5353c072378ac1545a0a2270757msarett return (uint8_t) (v + 0.5f); 969876ac5b3016e5353c072378ac1545a0a2270757msarett } 979876ac5b3016e5353c072378ac1545a0a2270757msarett} 989876ac5b3016e5353c072378ac1545a0a2270757msarett 99dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett// Interpolating lookup in a variably sized table. 100dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarettstatic inline float interp_lut(uint8_t byte, float* table, size_t tableSize) { 101dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett float index = byte_to_float(byte) * (tableSize - 1); 102dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett float diff = index - sk_float_floor2int(index); 103dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett return table[(int) sk_float_floor2int(index)] * (1.0f - diff) + 104dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett table[(int) sk_float_ceil2int(index)] * diff; 105dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett} 106dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett 107dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett// Inverse table lookup. Ex: what index corresponds to the input value? This will 108dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett// have strange results when the table is non-increasing. But any sane gamma 109dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett// function will be increasing. 110dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett// FIXME (msarett): 111dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett// This is a placeholder implementation for inverting table gammas. First, I need to 112dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett// verify if there are actually destination profiles that require this functionality. 113dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett// Next, there are certainly faster and more robust approaches to solving this problem. 114dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett// The LUT based approach in QCMS would be a good place to start. 115dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarettstatic inline float interp_lut_inv(float input, float* table, size_t tableSize) { 116dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett if (input <= table[0]) { 117dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett return table[0]; 118dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett } else if (input >= table[tableSize - 1]) { 119dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett return 1.0f; 120dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett } 121dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett 122dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett for (uint32_t i = 1; i < tableSize; i++) { 123dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett if (table[i] >= input) { 124dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett // We are guaranteed that input is greater than table[i - 1]. 125dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett float diff = input - table[i - 1]; 126dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett float distance = table[i] - table[i - 1]; 127dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett float index = (i - 1) + diff / distance; 128dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett return index / (tableSize - 1); 129dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett } 130dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett } 131dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett 132dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett // Should be unreachable, since we'll return before the loop if input is 133dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett // larger than the last entry. 134dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett SkASSERT(false); 135dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett return 0.0f; 136dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett} 137dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett 138dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarettSkDefaultXform::SkDefaultXform(const sk_sp<SkGammas>& srcGammas, const SkMatrix44& srcToDst, 139dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett const sk_sp<SkGammas>& dstGammas) 140dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett : fSrcGammas(srcGammas) 141dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett , fSrcToDst(srcToDst) 142dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett , fDstGammas(dstGammas) 143dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett{} 144dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett 145dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarettvoid SkDefaultXform::xform_RGBA_8888(uint32_t* dst, const uint32_t* src, uint32_t len) const { 146dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett while (len-- > 0) { 147dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett // Convert to linear. 148dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett // FIXME (msarett): 149dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett // Rather than support three different strategies of transforming gamma, QCMS 150dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett // builds a 256 entry float lookup table from the gamma info. This handles 151dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett // the gamma transform and the conversion from bytes to floats. This may 152dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett // be simpler and faster than our current approach. 153dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett float srcFloats[3]; 154dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett for (int i = 0; i < 3; i++) { 155dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett uint8_t byte = (*src >> (8 * i)) & 0xFF; 156c4ce6b592487305de251bbebaf8eeee38371b877msarett if (fSrcGammas) { 157c4ce6b592487305de251bbebaf8eeee38371b877msarett const SkGammaCurve& gamma = (*fSrcGammas)[i]; 158c4ce6b592487305de251bbebaf8eeee38371b877msarett if (gamma.isValue()) { 159c4ce6b592487305de251bbebaf8eeee38371b877msarett srcFloats[i] = powf(byte_to_float(byte), gamma.fValue); 160c4ce6b592487305de251bbebaf8eeee38371b877msarett } else if (gamma.isTable()) { 161c4ce6b592487305de251bbebaf8eeee38371b877msarett srcFloats[i] = interp_lut(byte, gamma.fTable.get(), gamma.fTableSize); 162dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett } else { 163c4ce6b592487305de251bbebaf8eeee38371b877msarett SkASSERT(gamma.isParametric()); 164c4ce6b592487305de251bbebaf8eeee38371b877msarett float component = byte_to_float(byte); 165c4ce6b592487305de251bbebaf8eeee38371b877msarett if (component < gamma.fD) { 166c4ce6b592487305de251bbebaf8eeee38371b877msarett // Y = E * X + F 167c4ce6b592487305de251bbebaf8eeee38371b877msarett srcFloats[i] = gamma.fE * component + gamma.fF; 168c4ce6b592487305de251bbebaf8eeee38371b877msarett } else { 169c4ce6b592487305de251bbebaf8eeee38371b877msarett // Y = (A * X + B)^G + C 170c4ce6b592487305de251bbebaf8eeee38371b877msarett srcFloats[i] = powf(gamma.fA * component + gamma.fB, gamma.fG) + gamma.fC; 171c4ce6b592487305de251bbebaf8eeee38371b877msarett } 172dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett } 173c4ce6b592487305de251bbebaf8eeee38371b877msarett } else { 174c4ce6b592487305de251bbebaf8eeee38371b877msarett // FIXME: Handle named gammas. 175c4ce6b592487305de251bbebaf8eeee38371b877msarett srcFloats[i] = powf(byte_to_float(byte), 2.2f); 176dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett } 177dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett } 178dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett 179dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett // Convert to dst gamut. 180dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett float dstFloats[3]; 181dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett dstFloats[0] = srcFloats[0] * fSrcToDst.getFloat(0, 0) + 182dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett srcFloats[1] * fSrcToDst.getFloat(1, 0) + 183dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett srcFloats[2] * fSrcToDst.getFloat(2, 0) + fSrcToDst.getFloat(3, 0); 184dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett dstFloats[1] = srcFloats[0] * fSrcToDst.getFloat(0, 1) + 185dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett srcFloats[1] * fSrcToDst.getFloat(1, 1) + 186dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett srcFloats[2] * fSrcToDst.getFloat(2, 1) + fSrcToDst.getFloat(3, 1); 187dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett dstFloats[2] = srcFloats[0] * fSrcToDst.getFloat(0, 2) + 188dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett srcFloats[1] * fSrcToDst.getFloat(1, 2) + 189dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett srcFloats[2] * fSrcToDst.getFloat(2, 2) + fSrcToDst.getFloat(3, 2); 190dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett 191dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett // Convert to dst gamma. 192dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett // FIXME (msarett): 193dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett // Rather than support three different strategies of transforming inverse gamma, 194dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett // QCMS builds a large float lookup table from the gamma info. Is this faster or 195dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett // better than our approach? 196dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett for (int i = 0; i < 3; i++) { 197c4ce6b592487305de251bbebaf8eeee38371b877msarett if (fDstGammas) { 198c4ce6b592487305de251bbebaf8eeee38371b877msarett const SkGammaCurve& gamma = (*fDstGammas)[i]; 199c4ce6b592487305de251bbebaf8eeee38371b877msarett if (gamma.isValue()) { 200c4ce6b592487305de251bbebaf8eeee38371b877msarett dstFloats[i] = powf(dstFloats[i], 1.0f / gamma.fValue); 201c4ce6b592487305de251bbebaf8eeee38371b877msarett } else if (gamma.isTable()) { 202c4ce6b592487305de251bbebaf8eeee38371b877msarett // FIXME (msarett): 203c4ce6b592487305de251bbebaf8eeee38371b877msarett // An inverse table lookup is particularly strange and non-optimal. 204c4ce6b592487305de251bbebaf8eeee38371b877msarett dstFloats[i] = interp_lut_inv(dstFloats[i], gamma.fTable.get(), 205c4ce6b592487305de251bbebaf8eeee38371b877msarett gamma.fTableSize); 206dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett } else { 207c4ce6b592487305de251bbebaf8eeee38371b877msarett SkASSERT(gamma.isParametric()); 208c4ce6b592487305de251bbebaf8eeee38371b877msarett // FIXME (msarett): 209c4ce6b592487305de251bbebaf8eeee38371b877msarett // This is a placeholder implementation for inverting parametric gammas. 210c4ce6b592487305de251bbebaf8eeee38371b877msarett // First, I need to verify if there are actually destination profiles that 211c4ce6b592487305de251bbebaf8eeee38371b877msarett // require this functionality. Next, I need to explore other possibilities 212c4ce6b592487305de251bbebaf8eeee38371b877msarett // for this implementation. The LUT based approach in QCMS would be a good 213c4ce6b592487305de251bbebaf8eeee38371b877msarett // place to start. 214c4ce6b592487305de251bbebaf8eeee38371b877msarett 215c4ce6b592487305de251bbebaf8eeee38371b877msarett // We need to take the inverse of a piecewise function. Assume that 216c4ce6b592487305de251bbebaf8eeee38371b877msarett // the gamma function is continuous, or this won't make much sense 217c4ce6b592487305de251bbebaf8eeee38371b877msarett // anyway. 218c4ce6b592487305de251bbebaf8eeee38371b877msarett // Plug in |fD| to the first equation to calculate the new piecewise 219c4ce6b592487305de251bbebaf8eeee38371b877msarett // interval. Then simply use the inverse of the original functions. 220c4ce6b592487305de251bbebaf8eeee38371b877msarett float interval = gamma.fE * gamma.fD + gamma.fF; 221c4ce6b592487305de251bbebaf8eeee38371b877msarett if (dstFloats[i] < interval) { 222c4ce6b592487305de251bbebaf8eeee38371b877msarett // X = (Y - F) / E 223c4ce6b592487305de251bbebaf8eeee38371b877msarett if (0.0f == gamma.fE) { 224c4ce6b592487305de251bbebaf8eeee38371b877msarett // The gamma curve for this segment is constant, so the inverse 225c4ce6b592487305de251bbebaf8eeee38371b877msarett // is undefined. 226c4ce6b592487305de251bbebaf8eeee38371b877msarett dstFloats[i] = 0.0f; 227c4ce6b592487305de251bbebaf8eeee38371b877msarett } else { 228c4ce6b592487305de251bbebaf8eeee38371b877msarett dstFloats[i] = (dstFloats[i] - gamma.fF) / gamma.fE; 229c4ce6b592487305de251bbebaf8eeee38371b877msarett } 230dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett } else { 231c4ce6b592487305de251bbebaf8eeee38371b877msarett // X = ((Y - C)^(1 / G) - B) / A 232c4ce6b592487305de251bbebaf8eeee38371b877msarett if (0.0f == gamma.fA || 0.0f == gamma.fG) { 233c4ce6b592487305de251bbebaf8eeee38371b877msarett // The gamma curve for this segment is constant, so the inverse 234c4ce6b592487305de251bbebaf8eeee38371b877msarett // is undefined. 235c4ce6b592487305de251bbebaf8eeee38371b877msarett dstFloats[i] = 0.0f; 236c4ce6b592487305de251bbebaf8eeee38371b877msarett } else { 237c4ce6b592487305de251bbebaf8eeee38371b877msarett dstFloats[i] = (powf(dstFloats[i] - gamma.fC, 1.0f / gamma.fG) - 238c4ce6b592487305de251bbebaf8eeee38371b877msarett gamma.fB) / gamma.fA; 239c4ce6b592487305de251bbebaf8eeee38371b877msarett } 240dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett } 241dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett } 242c4ce6b592487305de251bbebaf8eeee38371b877msarett } else { 243c4ce6b592487305de251bbebaf8eeee38371b877msarett // FIXME: Handle named gammas. 244c4ce6b592487305de251bbebaf8eeee38371b877msarett dstFloats[i] = powf(dstFloats[i], 1.0f / 2.2f); 245dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett } 246dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett } 247dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett 248dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett *dst = SkPackARGB32NoCheck(((*src >> 24) & 0xFF), 249a9e878c836994bce695274b4c28890290139dcdfmsarett clamp_normalized_float_to_byte(dstFloats[0]), 250a9e878c836994bce695274b4c28890290139dcdfmsarett clamp_normalized_float_to_byte(dstFloats[1]), 251a9e878c836994bce695274b4c28890290139dcdfmsarett clamp_normalized_float_to_byte(dstFloats[2])); 252dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett 253dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett dst++; 254dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett src++; 255dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett } 256dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett} 257