Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 /* | |
| 2 * Copyright 2016 Google Inc. | |
| 3 * | |
| 4 * Use of this source code is governed by a BSD-style license that can be | |
| 5 * found in the LICENSE file. | |
| 6 */ | |
| 7 | |
| 8 #include "SkColorSpaceXform_A2B.h" | |
| 9 | |
| 10 #include "SkColorPriv.h" | |
| 11 #include "SkColorSpace_A2B.h" | |
| 12 #include "SkColorSpace_XYZ.h" | |
| 13 #include "SkColorSpacePriv.h" | |
| 14 #include "SkColorSpaceXformPriv.h" | |
| 15 #include "SkMakeUnique.h" | |
| 16 #include "SkNx.h" | |
| 17 #include "SkSRGB.h" | |
| 18 #include "SkTypes.h" | |
| 19 | |
| 20 class SkColorSpaceXform_A2B::ProcessingElement { | |
| 21 public: | |
| 22 virtual ~ProcessingElement() {} | |
| 23 virtual void apply(Sk4f* rSrc, Sk4f* gSrc, Sk4f* bSrc, int len) = 0; | |
| 24 virtual bool merge(ProcessingElement* other) = 0; | |
| 25 | |
| 26 enum class Type { | |
| 27 kGamma, | |
| 28 kGammaTable, | |
| 29 kMatrix, | |
| 30 kCLUT, | |
| 31 kLabToXYZ | |
| 32 }; | |
| 33 virtual Type type() const = 0; | |
| 34 }; | |
| 35 | |
| 36 class SkColorSpaceXform_A2B::ApplyCLUT : public SkColorSpaceXform_A2B::Processin gElement { | |
| 37 public: | |
| 38 ApplyCLUT(const SkColorLookUpTable* clut) | |
| 39 : fCLUT(sk_ref_sp(clut)) | |
| 40 {} | |
| 41 | |
| 42 void apply(Sk4f* rSrc, Sk4f* gSrc, Sk4f* bSrc, int len) override { | |
|
msarett
2016/10/26 20:43:29
This feels like a really good place to use SkRaste
raftias
2016/10/27 18:39:39
Acknowledged.
| |
| 43 while (len > 0) { | |
| 44 // unpack into float[3] = {r, g, b} arrays like interp_3d_clut() nee ds | |
|
msarett
2016/10/26 20:43:29
If it's convenient, you are welcome to change the
raftias
2016/10/27 18:39:38
It involved look-up tables so without constructing
| |
| 45 float rgb[4][3] = { | |
| 46 {(*rSrc)[0], (*gSrc)[0], (*bSrc)[0]}, | |
| 47 {(*rSrc)[1], (*gSrc)[1], (*bSrc)[1]}, | |
| 48 {(*rSrc)[2], (*gSrc)[2], (*bSrc)[2]}, | |
| 49 {(*rSrc)[3], (*gSrc)[3], (*bSrc)[3]} | |
| 50 }; | |
| 51 for (int i = 0; i < 4; ++i) { | |
| 52 interp_3d_clut(rgb[i], rgb[i], fCLUT.get()); | |
| 53 } | |
| 54 *rSrc = Sk4f(rgb[0][0], rgb[1][0], rgb[2][0], rgb[3][0]); | |
| 55 *gSrc = Sk4f(rgb[0][1], rgb[1][1], rgb[2][1], rgb[3][1]); | |
| 56 *bSrc = Sk4f(rgb[0][2], rgb[1][2], rgb[2][2], rgb[3][2]); | |
| 57 ++rSrc; | |
| 58 ++gSrc; | |
| 59 ++bSrc; | |
| 60 --len; | |
| 61 } | |
| 62 } | |
| 63 | |
| 64 bool merge(ProcessingElement*) override { return false; } | |
| 65 | |
| 66 Type type() const override { return Type::kCLUT; } | |
| 67 | |
| 68 private: | |
| 69 sk_sp<const SkColorLookUpTable> fCLUT; | |
| 70 }; | |
| 71 | |
| 72 class SkColorSpaceXform_A2B::LabToXYZ : public SkColorSpaceXform_A2B::Processing Element { | |
| 73 public: | |
| 74 void apply(Sk4f* rSrc, Sk4f* gSrc, Sk4f* bSrc, int len) override { | |
| 75 while (len > 0) { | |
| 76 // convert from Lab to XYZ | |
| 77 Sk4f Y = *rSrc*(100.f/116.f) + (16.f/116.f); | |
| 78 Sk4f X = *gSrc * (255.f/500.f) - (128.f/500.f) + Y; | |
| 79 Sk4f Z = Y - *bSrc * (255.f/200.f) - (128.f/200.f); | |
| 80 | |
| 81 /*const Sk4f l = *rSrc * 100.f; | |
| 82 const Sk4f a = *gSrc * 255.f - 128.f; | |
| 83 const Sk4f b = *bSrc * 255.f - 128.f; | |
| 84 Sk4f Y = (l + 16.f) * (1.f/116.f); | |
| 85 Sk4f X = a * (1.f/500.f) + Y; | |
| 86 Sk4f Z = Y - (b * (1.f/200.f));*/ | |
| 87 | |
| 88 | |
| 89 Sk4f cubed; | |
| 90 cubed = X*X*X; | |
| 91 X = (cubed > 0.008856f).thenElse(cubed, (X - (16.f/116.f)) * (1.f/7. 787f)); | |
| 92 cubed = Y*Y*Y; | |
| 93 Y = (cubed > 0.008856f).thenElse(cubed, (Y - (16.f/116.f)) * (1.f/7. 787f)); | |
| 94 cubed = Z*Z*Z; | |
| 95 Z = (cubed > 0.008856f).thenElse(cubed, (Z - (16.f/116.f)) * (1.f/7. 787f)); | |
| 96 | |
| 97 // adjust to D50 illuminant | |
| 98 X *= 0.96422f; | |
| 99 Y *= 1.00000f; | |
| 100 Z *= 0.82521f; | |
| 101 | |
| 102 *rSrc = X; | |
| 103 *gSrc = Y; | |
| 104 *bSrc = Z; | |
| 105 ++rSrc; | |
| 106 ++gSrc; | |
| 107 ++bSrc; | |
| 108 --len; | |
| 109 } | |
| 110 } | |
| 111 | |
| 112 bool merge(ProcessingElement*) override { return false; } | |
| 113 | |
| 114 Type type() const override { return Type::kLabToXYZ; } | |
| 115 }; | |
| 116 | |
| 117 class SkColorSpaceXform_A2B::ApplyMatrix : public SkColorSpaceXform_A2B::Process ingElement { | |
| 118 public: | |
| 119 ApplyMatrix(const SkMatrix44& matrix) | |
| 120 : fMatrix(matrix) | |
| 121 {} | |
| 122 | |
| 123 void apply(Sk4f* rSrc, Sk4f* gSrc, Sk4f* bSrc, int len) override { | |
| 124 Sk4f rXgXbX, rYgYbY, rZgZbZ, rTgTbT; | |
| 125 float colMajor[16]; | |
| 126 fMatrix.asColMajorf(colMajor); | |
| 127 load_matrix(colMajor, rXgXbX, rYgYbY, rZgZbZ, rTgTbT); | |
| 128 while (len > 0) { | |
| 129 Sk4f dr, dg, db, a; // a is ignored | |
| 130 transform_gamut(*rSrc, *gSrc, *bSrc, a, rXgXbX, rYgYbY, rZgZbZ, dr, dg, db, a); | |
| 131 translate_gamut(rTgTbT, dr, dg, db); | |
| 132 *rSrc = dr; | |
| 133 *gSrc = dg; | |
| 134 *bSrc = db; | |
| 135 ++rSrc; | |
| 136 ++gSrc; | |
| 137 ++bSrc; | |
| 138 --len; | |
| 139 } | |
| 140 } | |
| 141 bool merge(ProcessingElement* other) override { | |
| 142 if (other->type() == Type::kMatrix) { | |
| 143 fMatrix.preConcat(static_cast<ApplyMatrix*>(other)->fMatrix); | |
| 144 return true; | |
| 145 } | |
| 146 return false; | |
| 147 } | |
| 148 Type type() const override { | |
| 149 return Type::kMatrix; | |
| 150 } | |
| 151 private: | |
| 152 SkMatrix44 fMatrix; | |
| 153 }; | |
| 154 | |
| 155 | |
| 156 | |
| 157 // adapted from the integer-input version in SkSRGB.h | |
| 158 template <int N> | |
| 159 static inline SkNx<N,float> sk_linear_from_float_srgb_math(const SkNx<N,float>& x) { | |
|
msarett
2016/10/26 20:43:29
Let's move this to SkSRGB.h. I think the int->flo
raftias
2016/10/27 18:39:38
Removed function entirely. Will consider this if/w
| |
| 160 // Non-linear segment of sRGB curve approximated by | |
| 161 // l = 0.0025 + 0.6975x^2 + 0.3x^3 | |
| 162 const SkNx<N,float> k0 = 0.0025f, | |
| 163 k2 = 0.6975f, | |
| 164 k3 = 0.3000f; | |
| 165 auto hi = x*x*(x*k3 + k2) + k0; | |
| 166 | |
| 167 // Linear segment of sRGB curve: the normal slope, extended a little further than normal. | |
| 168 auto lo = x * (1.f/12.92f); | |
| 169 | |
| 170 return (x < (14.025f / 255.f)).thenElse(lo, hi); | |
| 171 } | |
| 172 | |
| 173 | |
| 174 class SkColorSpaceXform_A2B::ApplyGammaSRGB : public SkColorSpaceXform_A2B::Proc essingElement { | |
| 175 public: | |
| 176 void apply(Sk4f* rSrc, Sk4f* gSrc, Sk4f* bSrc, int len) override { | |
| 177 while (len > 0) { | |
| 178 *rSrc = sk_linear_from_float_srgb_math(*rSrc); | |
| 179 *gSrc = sk_linear_from_float_srgb_math(*gSrc); | |
| 180 *bSrc = sk_linear_from_float_srgb_math(*bSrc); | |
| 181 ++rSrc; | |
| 182 ++gSrc; | |
| 183 ++bSrc; | |
| 184 --len; | |
| 185 } | |
| 186 } | |
| 187 bool merge(ProcessingElement* other) override { return false; } | |
| 188 Type type() const override { return Type::kGamma; } | |
| 189 }; | |
| 190 | |
| 191 static inline Sk4f linear_from_2dot2(const Sk4f& x) { | |
| 192 | |
| 193 // x^(141/64) = x^(2.20312) is a great approximation of the true value, x^(2 .2). | |
| 194 auto x16 = x.rsqrt().rsqrt().rsqrt().rsqrt(); // x^(1/16) = x^(4/64); | |
| 195 auto x64 = x16.rsqrt().rsqrt(); // x^(1/64) | |
| 196 | |
| 197 // x^(35/16) = x^(2.1875) is an okay one as well and would be quicker: | |
|
msarett
2016/10/26 20:43:29
Please remove this comment. It's not a bad point,
raftias
2016/10/27 18:39:39
Removed function entirely. Will consider this if/w
| |
| 198 // (both pass ColorSpaceXformTests's +/-1 difference vs x^2.2) | |
| 199 // return x*x * x16*x16*x16; | |
| 200 | |
| 201 // x^(141/64) = x^(128/64) * x^(12/64) * x^(1/64) | |
| 202 return x*x * x16*x16*x16 * x64; | |
| 203 } | |
| 204 | |
| 205 class SkColorSpaceXform_A2B::ApplyGamma2Dot2 : public SkColorSpaceXform_A2B::Pro cessingElement { | |
| 206 public: | |
| 207 void apply(Sk4f* rSrc, Sk4f* gSrc, Sk4f* bSrc, int len) override { | |
| 208 while (len > 0) { | |
| 209 *rSrc = linear_from_2dot2(*rSrc); | |
| 210 *gSrc = linear_from_2dot2(*gSrc); | |
| 211 *bSrc = linear_from_2dot2(*bSrc); | |
| 212 ++rSrc; | |
| 213 ++gSrc; | |
| 214 ++bSrc; | |
| 215 --len; | |
| 216 } | |
| 217 } | |
| 218 bool merge(ProcessingElement* other) override { return false; } | |
| 219 Type type() const override { return Type::kGamma; } | |
| 220 }; | |
| 221 | |
| 222 // TODO(raftias): See if there's some actually-vectorized implementation somewhe re? | |
|
msarett
2016/10/26 20:43:29
As far I know, there's not. I don't even think th
| |
| 223 // or remove once all powf() calling transforms (value, param) are converted int o tables | |
| 224 static inline Sk4f SkNx_powf(const Sk4f& x, float exp) { | |
| 225 return Sk4f{::powf(x[0], exp), ::powf(x[1], exp), ::powf(x[2], exp), ::powf( x[3], exp)}; | |
| 226 } | |
| 227 | |
| 228 // TODO(raftias): remove and store in tables | |
| 229 class SkColorSpaceXform_A2B::ApplyGammaValue : public SkColorSpaceXform_A2B::Pro cessingElement { | |
| 230 public: | |
| 231 ApplyGammaValue(float rExp, float gExp, float bExp) | |
| 232 : fRExp(rExp) | |
| 233 , fGExp(gExp) | |
| 234 , fBExp(bExp) | |
| 235 {} | |
| 236 void apply(Sk4f* rSrc, Sk4f* gSrc, Sk4f* bSrc, int len) override { | |
| 237 while (len > 0) { | |
| 238 *rSrc = SkNx_powf(*rSrc, fRExp); | |
| 239 *gSrc = SkNx_powf(*gSrc, fGExp); | |
| 240 *bSrc = SkNx_powf(*bSrc, fBExp); | |
| 241 ++rSrc; | |
| 242 ++gSrc; | |
| 243 ++bSrc; | |
| 244 --len; | |
| 245 } | |
| 246 } | |
| 247 bool merge(ProcessingElement* other) override { return false; } | |
| 248 Type type() const override { return Type::kGamma; } | |
| 249 private: | |
| 250 float fRExp; | |
| 251 float fGExp; | |
| 252 float fBExp; | |
| 253 }; | |
| 254 | |
| 255 // TODO(raftias): remove and store in tables? | |
| 256 class SkColorSpaceXform_A2B::ApplyGammaParams : public SkColorSpaceXform_A2B::Pr ocessingElement { | |
| 257 public: | |
| 258 ApplyGammaParams(const SkColorSpaceTransferFn& rParams, const SkColorSpaceTr ansferFn& gParams, | |
| 259 const SkColorSpaceTransferFn& bParams) | |
| 260 : fRP(rParams) | |
| 261 , fGP(gParams) | |
| 262 , fBP(bParams) | |
| 263 {} | |
| 264 void apply(Sk4f* rSrc, Sk4f* gSrc, Sk4f* bSrc, int len) override { | |
| 265 // TODO(raftias): profile against a non-vectorized implementation, since if the (short) | |
| 266 // linear segment is hit a lot then there's a lot of unnecessary powf() calls. | |
| 267 // Or just remove these and cache into tables. | |
| 268 while (len > 0) { | |
| 269 *rSrc = (*rSrc <= fRP.fD).thenElse(fRP.fE * (*rSrc) + fRP.fF, | |
| 270 SkNx_powf(*rSrc*fRP.fA + fRP.fB, fRP.fG) + fRP.fC); | |
| 271 *gSrc = (*gSrc <= fGP.fD).thenElse(fGP.fE * (*gSrc) + fGP.fF, | |
| 272 SkNx_powf(*gSrc*fGP.fA + fGP.fB, fGP.fG) + fGP.fC); | |
| 273 *bSrc = (*bSrc <= fBP.fD).thenElse(fBP.fE * (*bSrc) + fBP.fF, | |
| 274 SkNx_powf(*bSrc*fBP.fA + fBP.fB, fBP.fG) + fBP.fC); | |
| 275 ++rSrc; | |
| 276 ++gSrc; | |
| 277 ++bSrc; | |
| 278 --len; | |
| 279 } | |
| 280 } | |
| 281 bool merge(ProcessingElement* other) override { return false; } | |
| 282 Type type() const override { return Type::kGamma; } | |
| 283 private: | |
| 284 const SkColorSpaceTransferFn fRP; | |
| 285 const SkColorSpaceTransferFn fGP; | |
| 286 const SkColorSpaceTransferFn fBP; | |
| 287 }; | |
| 288 | |
| 289 class SkColorSpaceXform_A2B::ApplyGammaTable : public SkColorSpaceXform_A2B::Pro cessingElement { | |
| 290 public: | |
| 291 ApplyGammaTable(const SkGammas* gammas) { | |
| 292 // TODO(raftias): use 1 table if all channels point to the same table | |
| 293 const int numTables = 3; | |
| 294 const size_t tableBytes = numTables * kDstGammaTableSize * sizeof(float) ; | |
| 295 const float tableStep = 1.f / (float)(kDstGammaTableSize - 1); | |
| 296 fStorage.reset(tableBytes); | |
| 297 float* outTable = fStorage.get(); | |
| 298 for (int i = 0; i < numTables; ++i) { | |
| 299 SkASSERT(gammas->isTable(i)); | |
| 300 const float* inTable = gammas->table(i); | |
| 301 const int inTableSize = gammas->data(i).fTable.fSize; | |
| 302 fGammaTables[i] = outTable; | |
| 303 if (kDstGammaTableSize == inTableSize) { | |
| 304 memcpy(outTable, inTable, sizeof(float) * kDstGammaTableSize); | |
| 305 return; | |
| 306 } | |
| 307 | |
| 308 for (float x = 0.0f; x <= 1.0f; x += tableStep) { | |
| 309 *outTable++ = interp_lut(x, inTable, inTableSize); | |
| 310 } | |
| 311 } | |
| 312 } | |
| 313 | |
| 314 void apply(Sk4f* rSrc, Sk4f* gSrc, Sk4f* bSrc, int len) override { | |
| 315 const float maxIndex = kDstGammaTableSize - 1; | |
| 316 while (len > 0) { | |
| 317 *rSrc = Sk4f::Min(Sk4f::Max(maxIndex * (*rSrc), 0.f), maxIndex); | |
| 318 *gSrc = Sk4f::Min(Sk4f::Max(maxIndex * (*gSrc), 0.f), maxIndex); | |
| 319 *bSrc = Sk4f::Min(Sk4f::Max(maxIndex * (*bSrc), 0.f), maxIndex); | |
| 320 const Sk4i ir = Sk4f_round(*rSrc); | |
| 321 const Sk4i ig = Sk4f_round(*gSrc); | |
| 322 const Sk4i ib = Sk4f_round(*bSrc); | |
| 323 *rSrc = Sk4f((float) fGammaTables[0][ir[0]], | |
| 324 (float) fGammaTables[0][ir[1]], | |
| 325 (float) fGammaTables[0][ir[2]], | |
| 326 (float) fGammaTables[0][ir[3]]); | |
| 327 *gSrc = Sk4f((float) fGammaTables[1][ig[0]], | |
| 328 (float) fGammaTables[1][ig[1]], | |
| 329 (float) fGammaTables[1][ig[2]], | |
| 330 (float) fGammaTables[1][ig[3]]); | |
| 331 *bSrc = Sk4f((float) fGammaTables[2][ib[0]], | |
| 332 (float) fGammaTables[2][ib[1]], | |
| 333 (float) fGammaTables[2][ib[2]], | |
| 334 (float) fGammaTables[2][ib[3]]); | |
| 335 ++rSrc; | |
| 336 ++gSrc; | |
| 337 ++bSrc; | |
| 338 --len; | |
| 339 } | |
| 340 } | |
| 341 | |
| 342 bool merge(ProcessingElement* other) override { | |
| 343 if (Type::kGammaTable == other->type()) { | |
| 344 auto otherGamma = static_cast<const SkColorSpaceXform_A2B::ApplyGamm aTable*>(other); | |
| 345 const int numTables = 3; // set to 1 if matching | |
| 346 for (int table = 0; table < numTables; ++table) { | |
| 347 for (int i = 0; i < kDstGammaTableSize; ++i) { | |
| 348 const int indexIntoOther = (int)(fGammaTables[table][i] + 0. 5f); | |
| 349 fGammaTables[table][i] = otherGamma->fGammaTables[table][ind exIntoOther]; | |
| 350 } | |
| 351 } | |
| 352 return true; | |
| 353 } | |
| 354 return false; | |
| 355 } | |
| 356 | |
| 357 Type type() const override { return Type::kGammaTable; } | |
| 358 | |
| 359 private: | |
| 360 SkAutoTMalloc<float> fStorage; | |
| 361 float* fGammaTables[3]; | |
| 362 }; | |
| 363 | |
| 364 //////////////////////////////////////////////////////////////////////////////// /////////////////// | |
| 365 | |
| 366 | |
| 367 bool SkColorSpaceXform_A2B::onApply(ColorFormat dstFormat, void* dst, ColorForma t srcFormat, | |
| 368 const void* src, int count, SkAlphaType alph aType) const { | |
| 369 LoadFn load; | |
| 370 const bool loadAlpha = (kPremul_SkAlphaType == alphaType) || | |
| 371 (kRGBA_F16_ColorFormat == dstFormat) || | |
| 372 (kRGBA_F32_ColorFormat == dstFormat); | |
| 373 | |
| 374 switch (srcFormat) { | |
| 375 case kRGBA_8888_ColorFormat: | |
| 376 load = loadAlpha ? load_rgba_linear<kRGBA_Order> | |
| 377 : load_rgb_linear<kRGBA_Order>; | |
| 378 break; | |
| 379 case kBGRA_8888_ColorFormat: | |
| 380 load = loadAlpha ? load_rgba_linear<kBGRA_Order> | |
| 381 : load_rgb_linear<kBGRA_Order>; | |
| 382 break; | |
| 383 default: | |
| 384 SkCSXformPrintf("F16/F32 source color format not supported\n"); | |
| 385 return false; | |
| 386 } | |
| 387 | |
| 388 StoreFn store = store_linear<kRGBA_Order>; | |
| 389 size_t sizeOfDstPixel = 0; | |
| 390 switch (dstFormat) { | |
| 391 case kRGBA_8888_ColorFormat: | |
| 392 switch (fDstGamma) { | |
| 393 case kLinear_SkGammaNamed: | |
| 394 store = store_linear<kRGBA_Order>; | |
| 395 break; | |
| 396 case kSRGB_SkGammaNamed: | |
| 397 store = store_srgb<kRGBA_Order>; | |
| 398 break; | |
| 399 case k2Dot2Curve_SkGammaNamed: | |
| 400 store = store_2dot2<kRGBA_Order>; | |
| 401 break; | |
| 402 default: | |
| 403 store = store_generic<kRGBA_Order>; | |
| 404 break; | |
| 405 } | |
| 406 sizeOfDstPixel = 4; | |
| 407 break; | |
| 408 case kBGRA_8888_ColorFormat: | |
| 409 switch (fDstGamma) { | |
| 410 case kLinear_SkGammaNamed: | |
| 411 store = store_linear<kBGRA_Order>; | |
| 412 break; | |
| 413 case kSRGB_SkGammaNamed: | |
| 414 store = store_srgb<kBGRA_Order>; | |
| 415 break; | |
| 416 case k2Dot2Curve_SkGammaNamed: | |
| 417 store = store_2dot2<kBGRA_Order>; | |
| 418 break; | |
| 419 default: | |
| 420 store = store_generic<kBGRA_Order>; | |
| 421 break; | |
| 422 } | |
| 423 sizeOfDstPixel = 4; | |
| 424 break; | |
| 425 case kRGBA_F16_ColorFormat: | |
| 426 if (fDstGamma != kLinear_SkGammaNamed) { | |
| 427 return false; | |
| 428 } | |
| 429 store = (kOpaque_SkAlphaType == alphaType) ? store_f16_opaque<kRGBA_ Order> | |
| 430 : store_f16<kRGBA_Order>; | |
| 431 sizeOfDstPixel = 8; | |
| 432 break; | |
| 433 case kRGBA_F32_ColorFormat: | |
| 434 if (fDstGamma != kLinear_SkGammaNamed) { | |
| 435 return false; | |
| 436 } | |
| 437 store = store_f32<kRGBA_Order>; | |
| 438 sizeOfDstPixel = 16; | |
| 439 break; | |
| 440 } | |
| 441 SkASSERT(sizeOfDstPixel > 0); | |
| 442 | |
| 443 const int allocLen = (count + 3) / 4; | |
| 444 SkAutoTMalloc<Sk4f> rSrc(allocLen); | |
| 445 SkAutoTMalloc<Sk4f> gSrc(allocLen); | |
| 446 SkAutoTMalloc<Sk4f> bSrc(allocLen); | |
| 447 Sk4f ignore; | |
| 448 const uint32_t* loadSrc = (const uint32_t*)src; | |
| 449 int loadLen = count; | |
| 450 Sk4f* loadRSrc = rSrc.get(); | |
| 451 Sk4f* loadGSrc = gSrc.get(); | |
| 452 Sk4f* loadBSrc = bSrc.get(); | |
| 453 while (loadLen >= 4) { | |
| 454 load(loadSrc, *loadRSrc, *loadGSrc, *loadBSrc, ignore, nullptr); | |
| 455 loadSrc += 4; | |
| 456 loadLen -= 4; | |
| 457 ++loadRSrc; | |
| 458 ++loadGSrc; | |
| 459 ++loadBSrc; | |
| 460 } | |
| 461 if (loadLen > 0) { | |
| 462 // read the last 1-3 RGBA values into a buffer and load all 4 RGBA | |
| 463 // values out of the buffer, letting the last 1-3 be arbitrary values | |
| 464 // (all byte combos are valid RGBA values) | |
| 465 uint32_t readOverrun[16]; | |
| 466 memcpy(readOverrun, loadSrc, loadLen * sizeof(uint32_t)); | |
| 467 load(readOverrun, *loadRSrc, *loadGSrc, *loadBSrc, ignore, nullptr); | |
| 468 } | |
| 469 | |
| 470 for (const std::unique_ptr<ProcessingElement>& element : fProcessingElements ) { | |
| 471 element->apply(rSrc.get(), gSrc.get(), bSrc.get(), allocLen); | |
| 472 } | |
| 473 | |
| 474 Sk4f* storeRSrc = rSrc.get(); | |
| 475 Sk4f* storeGSrc = gSrc.get(); | |
| 476 Sk4f* storeBSrc = bSrc.get(); | |
| 477 loadSrc = (const uint32_t*)src; | |
| 478 Sk4f alpha; | |
| 479 while (count >= 4) { | |
| 480 // load the alpha from the source image since our transformations only a ffected R/G/B | |
| 481 load(loadSrc, ignore, ignore, ignore, alpha, nullptr); | |
| 482 | |
| 483 if (kPremul_SkAlphaType == alphaType) { | |
| 484 premultiply(*storeRSrc, *storeGSrc, *storeBSrc, alpha); | |
| 485 } | |
| 486 | |
| 487 store(dst, (const uint32_t*)src, *storeRSrc, *storeGSrc, *storeBSrc, alp ha, | |
| 488 fDstGammaTables); | |
| 489 dst = SkTAddOffset<void>(dst, 4 * sizeOfDstPixel); | |
| 490 count -= 4; | |
| 491 loadSrc += 4; // to load alpha | |
| 492 ++storeRSrc; | |
| 493 ++storeGSrc; | |
| 494 ++storeBSrc; | |
| 495 } | |
| 496 if (count > 0) { | |
| 497 const int maxSizeOfDstPixel = 16; | |
| 498 SkASSERT(sizeOfDstPixel <= maxSizeOfDstPixel); | |
| 499 uint32_t writeOverrun[4*maxSizeOfDstPixel]; | |
| 500 | |
| 501 load(loadSrc, ignore, ignore, ignore, alpha, nullptr); | |
| 502 | |
| 503 if (kPremul_SkAlphaType == alphaType) { | |
| 504 premultiply(*storeRSrc, *storeGSrc, *storeBSrc, alpha); | |
| 505 } | |
| 506 | |
| 507 store(writeOverrun, (const uint32_t*)src, *storeRSrc, *storeGSrc, *store BSrc, alpha, | |
| 508 fDstGammaTables); | |
| 509 memcpy(dst, writeOverrun, count * sizeOfDstPixel); | |
| 510 } | |
| 511 return true; | |
| 512 } | |
| 513 | |
| 514 SkColorSpaceXform_A2B::SkColorSpaceXform_A2B(SkColorSpace_A2B* srcSpace, | |
| 515 SkColorSpace_XYZ* dstSpace) { | |
| 516 #if (SkCSXformPrintfDefined) | |
| 517 static const char* debugGammaNamed[4] = { | |
| 518 "Linear", "SRGB", "2.2", "NonStandard" | |
| 519 }; | |
| 520 static const char* debugGammas[5] = { | |
| 521 "None", "Named", "Value", "Table", "Param" | |
| 522 }; | |
| 523 #endif | |
| 524 // add in all device -> PCS xforms | |
| 525 for (size_t i = 0; i < srcSpace->count(); ++i) { | |
| 526 const SkColorSpace_A2B::Element& e = srcSpace->element(i); | |
| 527 switch (e.type()) { | |
| 528 case SkColorSpace_A2B::Element::Type::kGammaNamed: | |
| 529 SkCSXformPrintf("Gamma element added: %s\n", | |
| 530 debugGammaNamed[(int)e.gammaNamed()]); | |
| 531 switch (e.gammaNamed()) { | |
| 532 case kLinear_SkGammaNamed: | |
| 533 // there is no point in adding a transform here | |
| 534 break; | |
| 535 case kSRGB_SkGammaNamed: | |
| 536 fProcessingElements.push_back(skstd::make_unique<ApplyGa mmaSRGB>()); | |
| 537 break; | |
| 538 case k2Dot2Curve_SkGammaNamed: | |
| 539 fProcessingElements.push_back(skstd::make_unique<ApplyGa mma2Dot2>()); | |
| 540 break; | |
| 541 case kNonStandard_SkGammaNamed: | |
| 542 SkASSERT(false); | |
| 543 break; | |
| 544 } | |
| 545 break; | |
| 546 case SkColorSpace_A2B::Element::Type::kGammas: { | |
| 547 const SkGammas* gammas = &e.gammas(); | |
| 548 SkCSXformPrintf("Adding gamma element:"); | |
| 549 for (int channel = 0; channel < 3; ++channel) { | |
| 550 SkCSXformPrintf(" %s", debugGammas[(int)gammas->type(ch annel)]); | |
| 551 } | |
| 552 SkCSXformPrintf("\n"); | |
| 553 if (gammas->type(0) != gammas->type(1) || gammas->type(0) != gammas->type(2)) { | |
| 554 SkCSXformPrintf("Mixed gamma types are not supported rig ht now."); | |
| 555 SkCSXformPrintf(" Using only 1st gamma channel.\n"); | |
| 556 } | |
| 557 switch (gammas->type(0)) | |
| 558 { | |
| 559 case SkGammas::Type::kNone_Type: | |
| 560 SkCSXformPrintf("Invalid gamma type\n"); | |
| 561 break; | |
| 562 case SkGammas::Type::kNamed_Type: | |
| 563 switch (gammas->data(0).fNamed) { | |
| 564 case kLinear_SkGammaNamed: | |
| 565 // there is no point in adding a transform h ere | |
| 566 break; | |
| 567 case kSRGB_SkGammaNamed: | |
| 568 fProcessingElements.push_back( | |
| 569 skstd::make_unique<ApplyGammaSRGB>() ); | |
| 570 break; | |
| 571 case k2Dot2Curve_SkGammaNamed: | |
| 572 fProcessingElements.push_back( | |
| 573 skstd::make_unique<ApplyGamma2Dot2>( )); | |
| 574 break; | |
| 575 case kNonStandard_SkGammaNamed: | |
| 576 SkASSERT(false); | |
| 577 break; | |
| 578 } | |
| 579 break; | |
| 580 case SkGammas::Type::kValue_Type: | |
| 581 // TODO(raftias): pre-compute and cache these into a table | |
| 582 fProcessingElements.push_back(skstd::make_unique<App lyGammaValue>( | |
| 583 gammas->data(0).fValue, gammas->data(1).fVal ue, | |
| 584 gammas->data(2).fValue)); | |
| 585 break; | |
| 586 case SkGammas::Type::kTable_Type: | |
| 587 fProcessingElements.push_back( | |
| 588 skstd::make_unique<ApplyGammaTable>(gammas)); | |
| 589 break; | |
| 590 case SkGammas::Type::kParam_Type: { | |
| 591 // TODO(raftias): pre-compute and cache these into a table? | |
| 592 const SkColorSpaceTransferFn& pr = gammas->params(0) ; | |
| 593 const SkColorSpaceTransferFn& pg = gammas->params(1) ; | |
| 594 const SkColorSpaceTransferFn& pb = gammas->params(2) ; | |
| 595 fProcessingElements.push_back(skstd::make_unique<App lyGammaParams>( | |
| 596 pr, pg, pb)); | |
| 597 } | |
| 598 break; | |
| 599 } | |
| 600 } | |
| 601 break; | |
| 602 case SkColorSpace_A2B::Element::Type::kCLUT: | |
| 603 fProcessingElements.push_back(skstd::make_unique<ApplyCLUT>(&e.c olorLUT())); | |
| 604 break; | |
| 605 case SkColorSpace_A2B::Element::Type::kMatrix: | |
| 606 if (!e.matrix().isIdentity()) { | |
| 607 fProcessingElements.push_back(skstd::make_unique<ApplyMatrix >(e.matrix())); | |
| 608 } | |
| 609 break; | |
| 610 } | |
| 611 } | |
| 612 | |
| 613 // Lab PCS -> XYZ PCS | |
| 614 if (SkColorSpace_A2B::PCS::kLAB == srcSpace->pcs()) { | |
| 615 SkCSXformPrintf("Lab -> XYZ element added\n"); | |
| 616 fProcessingElements.push_back(skstd::make_unique<LabToXYZ>()); | |
| 617 } | |
| 618 | |
| 619 // and XYZ PCS -> device xforms | |
| 620 if (!dstSpace->fromXYZD50()->isIdentity()) { | |
| 621 fProcessingElements.push_back(skstd::make_unique<ApplyMatrix>(*dstSpace- >fromXYZD50())); | |
| 622 } | |
| 623 | |
| 624 //concatTransforms(); | |
|
msarett
2016/10/26 20:43:29
Let's drop this for now if it's not being used yet
raftias
2016/10/27 18:39:39
Done.
| |
| 625 | |
| 626 const int numDstTables = num_tables(dstSpace); | |
| 627 dstSpace->toDstGammaTables(fDstGammaTables, &fDstStorage, numDstTables); | |
| 628 fDstGamma = dstSpace->gammaNamed(); | |
| 629 } | |
| 630 | |
| 631 void SkColorSpaceXform_A2B::concatTransforms() { | |
| 632 uint32_t i = 0; | |
| 633 while (i + 1 < fProcessingElements.size()) { | |
| 634 if (fProcessingElements[i]->merge(fProcessingElements[i + 1].get())) { | |
| 635 // We could just reset the (i+1)th element then do 1 pass at the end | |
| 636 // to remove null elements to get O(n) instead of O(n^2), but n shou ld | |
| 637 // be very small so this should not matter | |
| 638 fProcessingElements.erase(fProcessingElements.begin() + i + 1); | |
| 639 } else { | |
| 640 ++i; | |
| 641 } | |
| 642 } | |
| 643 } | |
| OLD | NEW |