OLD | NEW |
1 /* | 1 /* |
2 * Copyright 2016 Google Inc. | 2 * Copyright 2016 Google Inc. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
6 */ | 6 */ |
7 | 7 |
8 #include "SkColorPriv.h" | 8 #include "SkColorPriv.h" |
| 9 #include "SkColorSpace_A2B.h" |
9 #include "SkColorSpace_Base.h" | 10 #include "SkColorSpace_Base.h" |
| 11 #include "SkColorSpace_XYZ.h" |
10 #include "SkColorSpacePriv.h" | 12 #include "SkColorSpacePriv.h" |
11 #include "SkColorSpaceXform_Base.h" | 13 #include "SkColorSpaceXform_Base.h" |
12 #include "SkHalf.h" | 14 #include "SkHalf.h" |
13 #include "SkOpts.h" | 15 #include "SkOpts.h" |
14 #include "SkSRGB.h" | 16 #include "SkSRGB.h" |
15 | 17 |
16 static constexpr float sk_linear_from_2dot2[256] = { | 18 static constexpr float sk_linear_from_2dot2[256] = { |
17 0.000000000000000000f, 0.000005077051900662f, 0.000023328004666099f, 0.0
00056921765712193f, | 19 0.000000000000000000f, 0.000005077051900662f, 0.000023328004666099f, 0.0
00056921765712193f, |
18 0.000107187362341244f, 0.000175123977503027f, 0.000261543754548491f, 0.0
00367136269815943f, | 20 0.000107187362341244f, 0.000175123977503027f, 0.000261543754548491f, 0.0
00367136269815943f, |
19 0.000492503787191433f, 0.000638182842167022f, 0.000804658499513058f, 0.0
00992374304074325f, | 21 0.000492503787191433f, 0.000638182842167022f, 0.000804658499513058f, 0.0
00992374304074325f, |
(...skipping 227 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
247 nullptr, | 249 nullptr, |
248 nullptr, | 250 nullptr, |
249 &build_table_linear_to_gamma, | 251 &build_table_linear_to_gamma, |
250 &build_table_linear_to_gamma, | 252 &build_table_linear_to_gamma, |
251 &build_table_linear_to_gamma, | 253 &build_table_linear_to_gamma, |
252 }; | 254 }; |
253 | 255 |
254 // Build tables to transform src gamma to linear. | 256 // Build tables to transform src gamma to linear. |
255 template <typename T> | 257 template <typename T> |
256 static void build_gamma_tables(const T* outGammaTables[3], T* gammaTableStorage,
int gammaTableSize, | 258 static void build_gamma_tables(const T* outGammaTables[3], T* gammaTableStorage,
int gammaTableSize, |
257 const SkColorSpace* space, const GammaFns<T>& fns
, | 259 const SkColorSpace_XYZ* space, const GammaFns<T>&
fns, |
258 bool gammasAreMatching) | 260 bool gammasAreMatching) |
259 { | 261 { |
260 switch (as_CSB(space)->gammaNamed()) { | 262 switch (space->gammaNamed()) { |
261 case kSRGB_SkGammaNamed: | 263 case kSRGB_SkGammaNamed: |
262 outGammaTables[0] = outGammaTables[1] = outGammaTables[2] = fns.fSRG
BTable; | 264 outGammaTables[0] = outGammaTables[1] = outGammaTables[2] = fns.fSRG
BTable; |
263 break; | 265 break; |
264 case k2Dot2Curve_SkGammaNamed: | 266 case k2Dot2Curve_SkGammaNamed: |
265 outGammaTables[0] = outGammaTables[1] = outGammaTables[2] = fns.f2Do
t2Table; | 267 outGammaTables[0] = outGammaTables[1] = outGammaTables[2] = fns.f2Do
t2Table; |
266 break; | 268 break; |
267 case kLinear_SkGammaNamed: | 269 case kLinear_SkGammaNamed: |
268 outGammaTables[0] = outGammaTables[1] = outGammaTables[2] = nullptr; | 270 outGammaTables[0] = outGammaTables[1] = outGammaTables[2] = nullptr; |
269 break; | 271 break; |
270 default: { | 272 default: { |
271 const SkGammas* gammas = as_CSB(space)->gammas(); | 273 const SkGammas* gammas = space->gammas(); |
272 SkASSERT(gammas); | 274 SkASSERT(gammas); |
273 | 275 |
274 auto build_table = [=](int i) { | 276 auto build_table = [=](int i) { |
275 if (gammas->isNamed(i)) { | 277 if (gammas->isNamed(i)) { |
276 switch (gammas->data(i).fNamed) { | 278 switch (gammas->data(i).fNamed) { |
277 case kSRGB_SkGammaNamed: | 279 case kSRGB_SkGammaNamed: |
278 (*fns.fBuildFromParam)(&gammaTableStorage[i * gammaT
ableSize], 2.4f, | 280 (*fns.fBuildFromParam)(&gammaTableStorage[i * gammaT
ableSize], 2.4f, |
279 (1.0f / 1.055f), (0.055f / 1.
055f), 0.0f, | 281 (1.0f / 1.055f), (0.055f / 1.
055f), 0.0f, |
280 0.04045f, (1.0f / 12.92f), 0.
0f); | 282 0.04045f, (1.0f / 12.92f), 0.
0f); |
281 outGammaTables[i] = &gammaTableStorage[i * gammaTabl
eSize]; | 283 outGammaTables[i] = &gammaTableStorage[i * gammaTabl
eSize]; |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
319 build_table(1); | 321 build_table(1); |
320 build_table(2); | 322 build_table(2); |
321 } | 323 } |
322 | 324 |
323 break; | 325 break; |
324 } | 326 } |
325 } | 327 } |
326 } | 328 } |
327 | 329 |
328 void SkColorSpaceXform_Base::BuildDstGammaTables(const uint8_t* dstGammaTables[3
], | 330 void SkColorSpaceXform_Base::BuildDstGammaTables(const uint8_t* dstGammaTables[3
], |
329 uint8_t* dstStorage, const SkCo
lorSpace* space, | 331 uint8_t* dstStorage, |
| 332 const SkColorSpace_XYZ* space, |
330 bool gammasAreMatching) { | 333 bool gammasAreMatching) { |
331 build_gamma_tables(dstGammaTables, dstStorage, kDstGammaTableSize, space, kF
romLinear, | 334 build_gamma_tables(dstGammaTables, dstStorage, kDstGammaTableSize, space, kF
romLinear, |
332 gammasAreMatching); | 335 gammasAreMatching); |
333 } | 336 } |
334 | 337 |
335 ////////////////////////////////////////////////////////////////////////////////
/////////////////// | 338 ////////////////////////////////////////////////////////////////////////////////
/////////////////// |
336 | 339 |
337 static inline bool is_almost_identity(const SkMatrix44& srcToDst) { | 340 static inline bool is_almost_identity(const SkMatrix44& srcToDst) { |
338 for (int i = 0; i < 4; i++) { | 341 for (int i = 0; i < 4; i++) { |
339 for (int j = 0; j < 4; j++) { | 342 for (int j = 0; j < 4; j++) { |
340 float expected = (i == j) ? 1.0f : 0.0f; | 343 float expected = (i == j) ? 1.0f : 0.0f; |
341 if (!color_space_almost_equal(srcToDst.getFloat(i,j), expected)) { | 344 if (!color_space_almost_equal(srcToDst.getFloat(i,j), expected)) { |
342 return false; | 345 return false; |
343 } | 346 } |
344 } | 347 } |
345 } | 348 } |
346 return true; | 349 return true; |
347 } | 350 } |
348 | 351 |
349 ////////////////////////////////////////////////////////////////////////////////
/////////////////// | 352 ////////////////////////////////////////////////////////////////////////////////
/////////////////// |
350 | 353 |
351 std::unique_ptr<SkColorSpaceXform> SkColorSpaceXform::New(SkColorSpace* srcSpace
, | 354 std::unique_ptr<SkColorSpaceXform> SkColorSpaceXform::New(SkColorSpace* srcSpace
, |
352 SkColorSpace* dstSpace
) { | 355 SkColorSpace* dstSpace
) { |
353 if (!srcSpace || !dstSpace) { | 356 if (!srcSpace || !dstSpace) { |
354 // Invalid input | 357 // Invalid input |
355 return nullptr; | 358 return nullptr; |
356 } | 359 } |
357 | 360 |
| 361 if (SkColorSpace_Base::Type::kA2B == as_CSB(dstSpace)->type()) { |
| 362 SkColorSpacePrintf("A2B destinations not supported\n"); |
| 363 return nullptr; |
| 364 } |
| 365 |
| 366 if (SkColorSpace_Base::Type::kA2B == as_CSB(srcSpace)->type()) { |
| 367 // TODO (raftias): return an A2B-supporting SkColorSpaceXform here once
the xform. |
| 368 // is implemented. SkColorSpaceXform_Base only supports XYZ+TRC based Sk
ColorSpaces |
| 369 //SkColorSpace_A2B* src = static_cast<SkColorSpace_A2B*>(srcSpace); |
| 370 //SkColorSpace_XYZ* dst = static_cast<SkColorSpace_XYZ*>(dstSpace); |
| 371 //return std::unique_ptr<SkColorSpaceXform>(new SkColorSpaceXform_A2B(sr
c, dst)); |
| 372 SkColorSpacePrintf("A2B sources not supported (yet)\n"); |
| 373 return nullptr; |
| 374 } |
| 375 SkColorSpace_XYZ* srcSpaceXYZ = static_cast<SkColorSpace_XYZ*>(srcSpace); |
| 376 SkColorSpace_XYZ* dstSpaceXYZ = static_cast<SkColorSpace_XYZ*>(dstSpace); |
| 377 |
358 ColorSpaceMatch csm = kNone_ColorSpaceMatch; | 378 ColorSpaceMatch csm = kNone_ColorSpaceMatch; |
359 SkMatrix44 srcToDst(SkMatrix44::kUninitialized_Constructor); | 379 SkMatrix44 srcToDst(SkMatrix44::kUninitialized_Constructor); |
360 if (SkColorSpace::Equals(srcSpace, dstSpace)) { | 380 if (SkColorSpace::Equals(srcSpace, dstSpace)) { |
361 srcToDst.setIdentity(); | 381 srcToDst.setIdentity(); |
362 csm = kFull_ColorSpaceMatch; | 382 csm = kFull_ColorSpaceMatch; |
363 } else { | 383 } else { |
364 srcToDst.setConcat(as_CSB(dstSpace)->fromXYZD50(), as_CSB(srcSpace)->toX
YZD50()); | 384 srcToDst.setConcat(*dstSpaceXYZ->fromXYZD50(), *srcSpaceXYZ->toXYZD50())
; |
365 | 385 |
366 if (is_almost_identity(srcToDst)) { | 386 if (is_almost_identity(srcToDst)) { |
367 srcToDst.setIdentity(); | 387 srcToDst.setIdentity(); |
368 csm = kGamut_ColorSpaceMatch; | 388 csm = kGamut_ColorSpaceMatch; |
369 } | 389 } |
370 } | 390 } |
371 | 391 |
372 switch (csm) { | 392 switch (csm) { |
373 case kNone_ColorSpaceMatch: | 393 case kNone_ColorSpaceMatch: |
374 switch (as_CSB(dstSpace)->gammaNamed()) { | 394 switch (dstSpaceXYZ->gammaNamed()) { |
375 case kSRGB_SkGammaNamed: | 395 case kSRGB_SkGammaNamed: |
376 if (srcSpace->gammaIsLinear()) { | 396 if (srcSpaceXYZ->gammaIsLinear()) { |
377 return std::unique_ptr<SkColorSpaceXform>(new SkColorSpa
ceXform_XYZ | 397 return std::unique_ptr<SkColorSpaceXform>(new SkColorSpa
ceXform_XYZ |
378 <kLinear_SrcGamma, kSRGB_DstGamma, kNone_ColorSp
aceMatch> | 398 <kLinear_SrcGamma, kSRGB_DstGamma, kNone_ColorSp
aceMatch> |
379 (srcSpace, srcToDst, dstSpace)); | 399 (srcSpaceXYZ, srcToDst, dstSpaceXYZ)); |
380 } else { | 400 } else { |
381 return std::unique_ptr<SkColorSpaceXform>(new SkColorSpa
ceXform_XYZ | 401 return std::unique_ptr<SkColorSpaceXform>(new SkColorSpa
ceXform_XYZ |
382 <kTable_SrcGamma, kSRGB_DstGamma, kNone_ColorSpa
ceMatch> | 402 <kTable_SrcGamma, kSRGB_DstGamma, kNone_ColorSpa
ceMatch> |
383 (srcSpace, srcToDst, dstSpace)); | 403 (srcSpaceXYZ, srcToDst, dstSpaceXYZ)); |
384 } | 404 } |
385 case k2Dot2Curve_SkGammaNamed: | 405 case k2Dot2Curve_SkGammaNamed: |
386 if (srcSpace->gammaIsLinear()) { | 406 if (srcSpaceXYZ->gammaIsLinear()) { |
387 return std::unique_ptr<SkColorSpaceXform>(new SkColorSpa
ceXform_XYZ | 407 return std::unique_ptr<SkColorSpaceXform>(new SkColorSpa
ceXform_XYZ |
388 <kLinear_SrcGamma, k2Dot2_DstGamma, kNone_ColorS
paceMatch> | 408 <kLinear_SrcGamma, k2Dot2_DstGamma, kNone_ColorS
paceMatch> |
389 (srcSpace, srcToDst, dstSpace)); | 409 (srcSpaceXYZ, srcToDst, dstSpaceXYZ)); |
390 } else { | 410 } else { |
391 return std::unique_ptr<SkColorSpaceXform>(new SkColorSpa
ceXform_XYZ | 411 return std::unique_ptr<SkColorSpaceXform>(new SkColorSpa
ceXform_XYZ |
392 <kTable_SrcGamma, k2Dot2_DstGamma, kNone_ColorSp
aceMatch> | 412 <kTable_SrcGamma, k2Dot2_DstGamma, kNone_ColorSp
aceMatch> |
393 (srcSpace, srcToDst, dstSpace)); | 413 (srcSpaceXYZ, srcToDst, dstSpaceXYZ)); |
394 } | 414 } |
395 case kLinear_SkGammaNamed: | 415 case kLinear_SkGammaNamed: |
396 if (srcSpace->gammaIsLinear()) { | 416 if (srcSpaceXYZ->gammaIsLinear()) { |
397 return std::unique_ptr<SkColorSpaceXform>(new SkColorSpa
ceXform_XYZ | 417 return std::unique_ptr<SkColorSpaceXform>(new SkColorSpa
ceXform_XYZ |
398 <kLinear_SrcGamma, kLinear_DstGamma, kNone_Color
SpaceMatch> | 418 <kLinear_SrcGamma, kLinear_DstGamma, kNone_Color
SpaceMatch> |
399 (srcSpace, srcToDst, dstSpace)); | 419 (srcSpaceXYZ, srcToDst, dstSpaceXYZ)); |
400 } else { | 420 } else { |
401 return std::unique_ptr<SkColorSpaceXform>(new SkColorSpa
ceXform_XYZ | 421 return std::unique_ptr<SkColorSpaceXform>(new SkColorSpa
ceXform_XYZ |
402 <kTable_SrcGamma, kLinear_DstGamma, kNone_ColorS
paceMatch> | 422 <kTable_SrcGamma, kLinear_DstGamma, kNone_ColorS
paceMatch> |
403 (srcSpace, srcToDst, dstSpace)); | 423 (srcSpaceXYZ, srcToDst, dstSpaceXYZ)); |
404 } | 424 } |
405 default: | 425 default: |
406 if (srcSpace->gammaIsLinear()) { | 426 if (srcSpaceXYZ->gammaIsLinear()) { |
407 return std::unique_ptr<SkColorSpaceXform>(new SkColorSpa
ceXform_XYZ | 427 return std::unique_ptr<SkColorSpaceXform>(new SkColorSpa
ceXform_XYZ |
408 <kLinear_SrcGamma, kTable_DstGamma, kNone_ColorS
paceMatch> | 428 <kLinear_SrcGamma, kTable_DstGamma, kNone_ColorS
paceMatch> |
409 (srcSpace, srcToDst, dstSpace)); | 429 (srcSpaceXYZ, srcToDst, dstSpaceXYZ)); |
410 } else { | 430 } else { |
411 return std::unique_ptr<SkColorSpaceXform>(new SkColorSpa
ceXform_XYZ | 431 return std::unique_ptr<SkColorSpaceXform>(new SkColorSpa
ceXform_XYZ |
412 <kTable_SrcGamma, kTable_DstGamma, kNone_ColorSp
aceMatch> | 432 <kTable_SrcGamma, kTable_DstGamma, kNone_ColorSp
aceMatch> |
413 (srcSpace, srcToDst, dstSpace)); | 433 (srcSpaceXYZ, srcToDst, dstSpaceXYZ)); |
414 } | 434 } |
415 } | 435 } |
416 case kGamut_ColorSpaceMatch: | 436 case kGamut_ColorSpaceMatch: |
417 switch (as_CSB(dstSpace)->gammaNamed()) { | 437 switch (dstSpaceXYZ->gammaNamed()) { |
418 case kSRGB_SkGammaNamed: | 438 case kSRGB_SkGammaNamed: |
419 if (srcSpace->gammaIsLinear()) { | 439 if (srcSpaceXYZ->gammaIsLinear()) { |
420 return std::unique_ptr<SkColorSpaceXform>(new SkColorSpa
ceXform_XYZ | 440 return std::unique_ptr<SkColorSpaceXform>(new SkColorSpa
ceXform_XYZ |
421 <kLinear_SrcGamma, kSRGB_DstGamma, kGamut_ColorS
paceMatch> | 441 <kLinear_SrcGamma, kSRGB_DstGamma, kGamut_ColorS
paceMatch> |
422 (srcSpace, srcToDst, dstSpace)); | 442 (srcSpaceXYZ, srcToDst, dstSpaceXYZ)); |
423 } else { | 443 } else { |
424 return std::unique_ptr<SkColorSpaceXform>(new SkColorSpa
ceXform_XYZ | 444 return std::unique_ptr<SkColorSpaceXform>(new SkColorSpa
ceXform_XYZ |
425 <kTable_SrcGamma, kSRGB_DstGamma, kGamut_ColorSp
aceMatch> | 445 <kTable_SrcGamma, kSRGB_DstGamma, kGamut_ColorSp
aceMatch> |
426 (srcSpace, srcToDst, dstSpace)); | 446 (srcSpaceXYZ, srcToDst, dstSpaceXYZ)); |
427 } | 447 } |
428 case k2Dot2Curve_SkGammaNamed: | 448 case k2Dot2Curve_SkGammaNamed: |
429 if (srcSpace->gammaIsLinear()) { | 449 if (srcSpaceXYZ->gammaIsLinear()) { |
430 return std::unique_ptr<SkColorSpaceXform>(new SkColorSpa
ceXform_XYZ | 450 return std::unique_ptr<SkColorSpaceXform>(new SkColorSpa
ceXform_XYZ |
431 <kLinear_SrcGamma, k2Dot2_DstGamma, kGamut_Color
SpaceMatch> | 451 <kLinear_SrcGamma, k2Dot2_DstGamma, kGamut_Color
SpaceMatch> |
432 (srcSpace, srcToDst, dstSpace)); | 452 (srcSpaceXYZ, srcToDst, dstSpaceXYZ)); |
433 } else { | 453 } else { |
434 return std::unique_ptr<SkColorSpaceXform>(new SkColorSpa
ceXform_XYZ | 454 return std::unique_ptr<SkColorSpaceXform>(new SkColorSpa
ceXform_XYZ |
435 <kTable_SrcGamma, k2Dot2_DstGamma, kGamut_ColorS
paceMatch> | 455 <kTable_SrcGamma, k2Dot2_DstGamma, kGamut_ColorS
paceMatch> |
436 (srcSpace, srcToDst, dstSpace)); | 456 (srcSpaceXYZ, srcToDst, dstSpaceXYZ)); |
437 } | 457 } |
438 case kLinear_SkGammaNamed: | 458 case kLinear_SkGammaNamed: |
439 if (srcSpace->gammaIsLinear()) { | 459 if (srcSpaceXYZ->gammaIsLinear()) { |
440 return std::unique_ptr<SkColorSpaceXform>(new SkColorSpa
ceXform_XYZ | 460 return std::unique_ptr<SkColorSpaceXform>(new SkColorSpa
ceXform_XYZ |
441 <kLinear_SrcGamma, kLinear_DstGamma, kGamut_Colo
rSpaceMatch> | 461 <kLinear_SrcGamma, kLinear_DstGamma, kGamut_Colo
rSpaceMatch> |
442 (srcSpace, srcToDst, dstSpace)); | 462 (srcSpaceXYZ, srcToDst, dstSpaceXYZ)); |
443 } else { | 463 } else { |
444 return std::unique_ptr<SkColorSpaceXform>(new SkColorSpa
ceXform_XYZ | 464 return std::unique_ptr<SkColorSpaceXform>(new SkColorSpa
ceXform_XYZ |
445 <kTable_SrcGamma, kLinear_DstGamma, kGamut_Color
SpaceMatch> | 465 <kTable_SrcGamma, kLinear_DstGamma, kGamut_Color
SpaceMatch> |
446 (srcSpace, srcToDst, dstSpace)); | 466 (srcSpaceXYZ, srcToDst, dstSpaceXYZ)); |
447 } | 467 } |
448 default: | 468 default: |
449 if (srcSpace->gammaIsLinear()) { | 469 if (srcSpaceXYZ->gammaIsLinear()) { |
450 return std::unique_ptr<SkColorSpaceXform>(new SkColorSpa
ceXform_XYZ | 470 return std::unique_ptr<SkColorSpaceXform>(new SkColorSpa
ceXform_XYZ |
451 <kLinear_SrcGamma, kTable_DstGamma, kGamut_Color
SpaceMatch> | 471 <kLinear_SrcGamma, kTable_DstGamma, kGamut_Color
SpaceMatch> |
452 (srcSpace, srcToDst, dstSpace)); | 472 (srcSpaceXYZ, srcToDst, dstSpaceXYZ)); |
453 } else { | 473 } else { |
454 return std::unique_ptr<SkColorSpaceXform>(new SkColorSpa
ceXform_XYZ | 474 return std::unique_ptr<SkColorSpaceXform>(new SkColorSpa
ceXform_XYZ |
455 <kTable_SrcGamma, kTable_DstGamma, kGamut_ColorS
paceMatch> | 475 <kTable_SrcGamma, kTable_DstGamma, kGamut_ColorS
paceMatch> |
456 (srcSpace, srcToDst, dstSpace)); | 476 (srcSpaceXYZ, srcToDst, dstSpaceXYZ)); |
457 } | 477 } |
458 } | 478 } |
459 case kFull_ColorSpaceMatch: | 479 case kFull_ColorSpaceMatch: |
460 switch (as_CSB(dstSpace)->gammaNamed()) { | 480 switch (dstSpaceXYZ->gammaNamed()) { |
461 case kSRGB_SkGammaNamed: | 481 case kSRGB_SkGammaNamed: |
462 return std::unique_ptr<SkColorSpaceXform>(new SkColorSpaceXf
orm_XYZ | 482 return std::unique_ptr<SkColorSpaceXform>(new SkColorSpaceXf
orm_XYZ |
463 <kTable_SrcGamma, kSRGB_DstGamma, kFull_ColorSpaceMa
tch> | 483 <kTable_SrcGamma, kSRGB_DstGamma, kFull_ColorSpaceMa
tch> |
464 (srcSpace, srcToDst, dstSpace)); | 484 (srcSpaceXYZ, srcToDst, dstSpaceXYZ)); |
465 case k2Dot2Curve_SkGammaNamed: | 485 case k2Dot2Curve_SkGammaNamed: |
466 return std::unique_ptr<SkColorSpaceXform>(new SkColorSpaceXf
orm_XYZ | 486 return std::unique_ptr<SkColorSpaceXform>(new SkColorSpaceXf
orm_XYZ |
467 <kTable_SrcGamma, k2Dot2_DstGamma, kFull_ColorSpaceM
atch> | 487 <kTable_SrcGamma, k2Dot2_DstGamma, kFull_ColorSpaceM
atch> |
468 (srcSpace, srcToDst, dstSpace)); | 488 (srcSpaceXYZ, srcToDst, dstSpaceXYZ)); |
469 case kLinear_SkGammaNamed: | 489 case kLinear_SkGammaNamed: |
470 return std::unique_ptr<SkColorSpaceXform>(new SkColorSpaceXf
orm_XYZ | 490 return std::unique_ptr<SkColorSpaceXform>(new SkColorSpaceXf
orm_XYZ |
471 <kLinear_SrcGamma, kLinear_DstGamma, kFull_ColorSpac
eMatch> | 491 <kLinear_SrcGamma, kLinear_DstGamma, kFull_ColorSpac
eMatch> |
472 (srcSpace, srcToDst, dstSpace)); | 492 (srcSpaceXYZ, srcToDst, dstSpaceXYZ)); |
473 default: | 493 default: |
474 return std::unique_ptr<SkColorSpaceXform>(new SkColorSpaceXf
orm_XYZ | 494 return std::unique_ptr<SkColorSpaceXform>(new SkColorSpaceXf
orm_XYZ |
475 <kTable_SrcGamma, kTable_DstGamma, kFull_ColorSpaceM
atch> | 495 <kTable_SrcGamma, kTable_DstGamma, kFull_ColorSpaceM
atch> |
476 (srcSpace, srcToDst, dstSpace)); | 496 (srcSpaceXYZ, srcToDst, dstSpaceXYZ)); |
477 } | 497 } |
478 default: | 498 default: |
479 SkASSERT(false); | 499 SkASSERT(false); |
480 return nullptr; | 500 return nullptr; |
481 } | 501 } |
482 } | 502 } |
483 | 503 |
484 ////////////////////////////////////////////////////////////////////////////////
/////////////////// | 504 ////////////////////////////////////////////////////////////////////////////////
/////////////////// |
485 | 505 |
486 static float byte_to_float(uint8_t byte) { | |
487 return ((float) byte) * (1.0f / 255.0f); | |
488 } | |
489 | |
490 // Clamp to the 0-1 range. | |
491 static float clamp_normalized_float(float v) { | |
492 if (v > 1.0f) { | |
493 return 1.0f; | |
494 } else if ((v < 0.0f) || (v != v)) { | |
495 return 0.0f; | |
496 } else { | |
497 return v; | |
498 } | |
499 } | |
500 | |
501 static void interp_3d_clut(float dst[3], float src[3], const SkColorLookUpTable*
colorLUT) { | |
502 // Call the src components x, y, and z. | |
503 uint8_t maxX = colorLUT->fGridPoints[0] - 1; | |
504 uint8_t maxY = colorLUT->fGridPoints[1] - 1; | |
505 uint8_t maxZ = colorLUT->fGridPoints[2] - 1; | |
506 | |
507 // An approximate index into each of the three dimensions of the table. | |
508 float x = src[0] * maxX; | |
509 float y = src[1] * maxY; | |
510 float z = src[2] * maxZ; | |
511 | |
512 // This gives us the low index for our interpolation. | |
513 int ix = sk_float_floor2int(x); | |
514 int iy = sk_float_floor2int(y); | |
515 int iz = sk_float_floor2int(z); | |
516 | |
517 // Make sure the low index is not also the max index. | |
518 ix = (maxX == ix) ? ix - 1 : ix; | |
519 iy = (maxY == iy) ? iy - 1 : iy; | |
520 iz = (maxZ == iz) ? iz - 1 : iz; | |
521 | |
522 // Weighting factors for the interpolation. | |
523 float diffX = x - ix; | |
524 float diffY = y - iy; | |
525 float diffZ = z - iz; | |
526 | |
527 // Constants to help us navigate the 3D table. | |
528 // Ex: Assume x = a, y = b, z = c. | |
529 // table[a * n001 + b * n010 + c * n100] logically equals table[a][b][c]
. | |
530 const int n000 = 0; | |
531 const int n001 = 3 * colorLUT->fGridPoints[1] * colorLUT->fGridPoints[2]; | |
532 const int n010 = 3 * colorLUT->fGridPoints[2]; | |
533 const int n011 = n001 + n010; | |
534 const int n100 = 3; | |
535 const int n101 = n100 + n001; | |
536 const int n110 = n100 + n010; | |
537 const int n111 = n110 + n001; | |
538 | |
539 // Base ptr into the table. | |
540 const float* ptr = &(colorLUT->table()[ix*n001 + iy*n010 + iz*n100]); | |
541 | |
542 // The code below performs a tetrahedral interpolation for each of the three | |
543 // dst components. Once the tetrahedron containing the interpolation point
is | |
544 // identified, the interpolation is a weighted sum of grid values at the | |
545 // vertices of the tetrahedron. The claim is that tetrahedral interpolation | |
546 // provides a more accurate color conversion. | |
547 // blogs.mathworks.com/steve/2006/11/24/tetrahedral-interpolation-for-colors
pace-conversion/ | |
548 // | |
549 // I have one test image, and visually I can't tell the difference between | |
550 // tetrahedral and trilinear interpolation. In terms of computation, the | |
551 // tetrahedral code requires more branches but less computation. The | |
552 // SampleICC library provides an option for the client to choose either | |
553 // tetrahedral or trilinear. | |
554 for (int i = 0; i < 3; i++) { | |
555 if (diffZ < diffY) { | |
556 if (diffZ < diffX) { | |
557 dst[i] = (ptr[n000] + diffZ * (ptr[n110] - ptr[n010]) + | |
558 diffY * (ptr[n010] - ptr[n000]) + | |
559 diffX * (ptr[n111] - ptr[n110])); | |
560 } else if (diffY < diffX) { | |
561 dst[i] = (ptr[n000] + diffZ * (ptr[n111] - ptr[n011]) + | |
562 diffY * (ptr[n011] - ptr[n001]) + | |
563 diffX * (ptr[n001] - ptr[n000])); | |
564 } else { | |
565 dst[i] = (ptr[n000] + diffZ * (ptr[n111] - ptr[n011]) + | |
566 diffY * (ptr[n010] - ptr[n000]) + | |
567 diffX * (ptr[n011] - ptr[n010])); | |
568 } | |
569 } else { | |
570 if (diffZ < diffX) { | |
571 dst[i] = (ptr[n000] + diffZ * (ptr[n101] - ptr[n001]) + | |
572 diffY * (ptr[n111] - ptr[n101]) + | |
573 diffX * (ptr[n001] - ptr[n000])); | |
574 } else if (diffY < diffX) { | |
575 dst[i] = (ptr[n000] + diffZ * (ptr[n100] - ptr[n000]) + | |
576 diffY * (ptr[n111] - ptr[n101]) + | |
577 diffX * (ptr[n101] - ptr[n100])); | |
578 } else { | |
579 dst[i] = (ptr[n000] + diffZ * (ptr[n100] - ptr[n000]) + | |
580 diffY * (ptr[n110] - ptr[n100]) + | |
581 diffX * (ptr[n111] - ptr[n110])); | |
582 } | |
583 } | |
584 | |
585 // Increment the table ptr in order to handle the next component. | |
586 // Note that this is the how table is designed: all of nXXX | |
587 // variables are multiples of 3 because there are 3 output | |
588 // components. | |
589 ptr++; | |
590 } | |
591 } | |
592 | |
593 static void handle_color_lut(uint32_t* dst, const void* vsrc, int len, | |
594 SkColorLookUpTable* colorLUT) { | |
595 const uint32_t* src = (const uint32_t*) vsrc; | |
596 while (len-- > 0) { | |
597 uint8_t r = (*src >> 0) & 0xFF, | |
598 g = (*src >> 8) & 0xFF, | |
599 b = (*src >> 16) & 0xFF; | |
600 | |
601 float in[3]; | |
602 float out[3]; | |
603 in[0] = byte_to_float(r); | |
604 in[1] = byte_to_float(g); | |
605 in[2] = byte_to_float(b); | |
606 interp_3d_clut(out, in, colorLUT); | |
607 | |
608 r = sk_float_round2int(255.0f * clamp_normalized_float(out[0])); | |
609 g = sk_float_round2int(255.0f * clamp_normalized_float(out[1])); | |
610 b = sk_float_round2int(255.0f * clamp_normalized_float(out[2])); | |
611 *dst = SkPackARGB_as_RGBA(0xFF, r, g, b); | |
612 | |
613 src++; | |
614 dst++; | |
615 } | |
616 } | |
617 | |
618 static inline void load_matrix(const float matrix[16], | 506 static inline void load_matrix(const float matrix[16], |
619 Sk4f& rXgXbX, Sk4f& rYgYbY, Sk4f& rZgZbZ, Sk4f& r
TgTbT) { | 507 Sk4f& rXgXbX, Sk4f& rYgYbY, Sk4f& rZgZbZ, Sk4f& r
TgTbT) { |
620 rXgXbX = Sk4f::Load(matrix + 0); | 508 rXgXbX = Sk4f::Load(matrix + 0); |
621 rYgYbY = Sk4f::Load(matrix + 4); | 509 rYgYbY = Sk4f::Load(matrix + 4); |
622 rZgZbZ = Sk4f::Load(matrix + 8); | 510 rZgZbZ = Sk4f::Load(matrix + 8); |
623 rTgTbT = Sk4f::Load(matrix + 12); | 511 rTgTbT = Sk4f::Load(matrix + 12); |
624 } | 512 } |
625 | 513 |
626 enum Order { | 514 enum Order { |
627 kRGBA_Order, | 515 kRGBA_Order, |
(...skipping 591 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1219 store_1(dst, src, rgba, a, dstTables); | 1107 store_1(dst, src, rgba, a, dstTables); |
1220 | 1108 |
1221 src += 1; | 1109 src += 1; |
1222 len -= 1; | 1110 len -= 1; |
1223 dst = SkTAddOffset<void>(dst, sizeOfDstPixel); | 1111 dst = SkTAddOffset<void>(dst, sizeOfDstPixel); |
1224 } | 1112 } |
1225 } | 1113 } |
1226 | 1114 |
1227 ////////////////////////////////////////////////////////////////////////////////
/////////////////// | 1115 ////////////////////////////////////////////////////////////////////////////////
/////////////////// |
1228 | 1116 |
1229 static inline int num_tables(SkColorSpace* space) { | 1117 static inline int num_tables(SkColorSpace_XYZ* space) { |
1230 switch (as_CSB(space)->gammaNamed()) { | 1118 switch (space->gammaNamed()) { |
1231 case kSRGB_SkGammaNamed: | 1119 case kSRGB_SkGammaNamed: |
1232 case k2Dot2Curve_SkGammaNamed: | 1120 case k2Dot2Curve_SkGammaNamed: |
1233 case kLinear_SkGammaNamed: | 1121 case kLinear_SkGammaNamed: |
1234 return 0; | 1122 return 0; |
1235 default: { | 1123 default: { |
1236 const SkGammas* gammas = as_CSB(space)->gammas(); | 1124 const SkGammas* gammas = space->gammas(); |
1237 SkASSERT(gammas); | 1125 SkASSERT(gammas); |
1238 | 1126 |
1239 bool gammasAreMatching = (gammas->type(0) == gammas->type(1)) && | 1127 bool gammasAreMatching = (gammas->type(0) == gammas->type(1)) && |
1240 (gammas->data(0) == gammas->data(1)) && | 1128 (gammas->data(0) == gammas->data(1)) && |
1241 (gammas->type(0) == gammas->type(2)) && | 1129 (gammas->type(0) == gammas->type(2)) && |
1242 (gammas->data(0) == gammas->data(2)); | 1130 (gammas->data(0) == gammas->data(2)); |
1243 | 1131 |
1244 // It's likely that each component will have the same gamma. In thi
s case, | 1132 // It's likely that each component will have the same gamma. In thi
s case, |
1245 // we only need to build one table. | 1133 // we only need to build one table. |
1246 return gammasAreMatching ? 1 : 3; | 1134 return gammasAreMatching ? 1 : 3; |
1247 } | 1135 } |
1248 } | 1136 } |
1249 } | 1137 } |
1250 | 1138 |
1251 template <SrcGamma kSrc, DstGamma kDst, ColorSpaceMatch kCSM> | 1139 template <SrcGamma kSrc, DstGamma kDst, ColorSpaceMatch kCSM> |
1252 SkColorSpaceXform_XYZ<kSrc, kDst, kCSM> | 1140 SkColorSpaceXform_XYZ<kSrc, kDst, kCSM> |
1253 ::SkColorSpaceXform_XYZ(SkColorSpace* srcSpace, const SkMatrix44& srcToDst, SkCo
lorSpace* dstSpace) | 1141 ::SkColorSpaceXform_XYZ(SkColorSpace_XYZ* srcSpace, const SkMatrix44& srcToDst, |
1254 : fColorLUT(sk_ref_sp((SkColorLookUpTable*) as_CSB(srcSpace)->colorLUT())) | 1142 SkColorSpace_XYZ* dstSpace) |
1255 { | 1143 { |
1256 srcToDst.asColMajorf(fSrcToDst); | 1144 srcToDst.asColMajorf(fSrcToDst); |
1257 | 1145 |
1258 const int numSrcTables = num_tables(srcSpace); | 1146 const int numSrcTables = num_tables(srcSpace); |
1259 const size_t srcEntries = numSrcTables * 256; | 1147 const size_t srcEntries = numSrcTables * 256; |
1260 const bool srcGammasAreMatching = (1 >= numSrcTables); | 1148 const bool srcGammasAreMatching = (1 >= numSrcTables); |
1261 fSrcStorage.reset(srcEntries); | 1149 fSrcStorage.reset(srcEntries); |
1262 build_gamma_tables(fSrcGammaTables, fSrcStorage.get(), 256, srcSpace, kToLin
ear, | 1150 build_gamma_tables(fSrcGammaTables, fSrcStorage.get(), 256, srcSpace, kToLin
ear, |
1263 srcGammasAreMatching); | 1151 srcGammasAreMatching); |
1264 | 1152 |
1265 const int numDstTables = num_tables(dstSpace); | 1153 const int numDstTables = num_tables(dstSpace); |
1266 as_CSB(dstSpace)->toDstGammaTables(fDstGammaTables, &fDstStorage, numDstTabl
es); | 1154 dstSpace->toDstGammaTables(fDstGammaTables, &fDstStorage, numDstTables); |
1267 } | 1155 } |
1268 | 1156 |
1269 ////////////////////////////////////////////////////////////////////////////////
/////////////////// | 1157 ////////////////////////////////////////////////////////////////////////////////
/////////////////// |
1270 | 1158 |
1271 template <SrcFormat kSrc, DstFormat kDst, ColorSpaceMatch kCSM> | 1159 template <SrcFormat kSrc, DstFormat kDst, ColorSpaceMatch kCSM> |
1272 static inline bool apply_set_alpha(void* dst, const void* src, int len, SkAlphaT
ype alphaType, | 1160 static inline bool apply_set_alpha(void* dst, const void* src, int len, SkAlphaT
ype alphaType, |
1273 const float* const srcTables[3], const float
matrix[16], | 1161 const float* const srcTables[3], const float
matrix[16], |
1274 const uint8_t* const dstTables[3]) { | 1162 const uint8_t* const dstTables[3]) { |
1275 switch (alphaType) { | 1163 switch (alphaType) { |
1276 case kOpaque_SkAlphaType: | 1164 case kOpaque_SkAlphaType: |
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1341 case kRGBA_F16_ColorFormat: | 1229 case kRGBA_F16_ColorFormat: |
1342 case kRGBA_F32_ColorFormat: | 1230 case kRGBA_F32_ColorFormat: |
1343 // There's still work to do to xform to linear floats. | 1231 // There's still work to do to xform to linear floats. |
1344 break; | 1232 break; |
1345 default: | 1233 default: |
1346 return false; | 1234 return false; |
1347 } | 1235 } |
1348 } | 1236 } |
1349 } | 1237 } |
1350 | 1238 |
1351 #if defined(GOOGLE3) | |
1352 // Stack frame size is limited in GOOGLE3. | |
1353 SkAutoSMalloc<256 * sizeof(uint32_t)> storage; | |
1354 #else | |
1355 SkAutoSMalloc<1024 * sizeof(uint32_t)> storage; | |
1356 #endif | |
1357 if (fColorLUT) { | |
1358 size_t storageBytes = len * sizeof(uint32_t); | |
1359 storage.reset(storageBytes); | |
1360 handle_color_lut((uint32_t*) storage.get(), src, len, fColorLUT.get()); | |
1361 src = (const uint32_t*) storage.get(); | |
1362 } | |
1363 | |
1364 switch (dstColorFormat) { | 1239 switch (dstColorFormat) { |
1365 case kRGBA_8888_ColorFormat: | 1240 case kRGBA_8888_ColorFormat: |
1366 switch (kDst) { | 1241 switch (kDst) { |
1367 case kLinear_DstGamma: | 1242 case kLinear_DstGamma: |
1368 return apply_set_src<kSrc, kRGBA_8888_Linear_DstFormat, kCSM
> | 1243 return apply_set_src<kSrc, kRGBA_8888_Linear_DstFormat, kCSM
> |
1369 (dst, src, len, alphaType, fSrcGammaTables, fSrcToDs
t, nullptr, | 1244 (dst, src, len, alphaType, fSrcGammaTables, fSrcToDs
t, nullptr, |
1370 srcColorFormat); | 1245 srcColorFormat); |
1371 case kSRGB_DstGamma: | 1246 case kSRGB_DstGamma: |
1372 return apply_set_src<kSrc, kRGBA_8888_SRGB_DstFormat, kCSM> | 1247 return apply_set_src<kSrc, kRGBA_8888_SRGB_DstFormat, kCSM> |
1373 (dst, src, len, alphaType, fSrcGammaTables, fSrcToDs
t, nullptr, | 1248 (dst, src, len, alphaType, fSrcGammaTables, fSrcToDs
t, nullptr, |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1424 } | 1299 } |
1425 | 1300 |
1426 bool SkColorSpaceXform::apply(ColorFormat dstColorFormat, void* dst, ColorFormat
srcColorFormat, | 1301 bool SkColorSpaceXform::apply(ColorFormat dstColorFormat, void* dst, ColorFormat
srcColorFormat, |
1427 const void* src, int len, SkAlphaType alphaType) c
onst { | 1302 const void* src, int len, SkAlphaType alphaType) c
onst { |
1428 return ((SkColorSpaceXform_Base*) this)->onApply(dstColorFormat, dst, srcCol
orFormat, src, len, | 1303 return ((SkColorSpaceXform_Base*) this)->onApply(dstColorFormat, dst, srcCol
orFormat, src, len, |
1429 alphaType); | 1304 alphaType); |
1430 } | 1305 } |
1431 | 1306 |
1432 ////////////////////////////////////////////////////////////////////////////////
/////////////////// | 1307 ////////////////////////////////////////////////////////////////////////////////
/////////////////// |
1433 | 1308 |
1434 std::unique_ptr<SkColorSpaceXform> SlowIdentityXform(SkColorSpace* space) { | 1309 std::unique_ptr<SkColorSpaceXform> SlowIdentityXform(SkColorSpace_XYZ* space) { |
1435 return std::unique_ptr<SkColorSpaceXform>(new SkColorSpaceXform_XYZ | 1310 return std::unique_ptr<SkColorSpaceXform>(new SkColorSpaceXform_XYZ |
1436 <kTable_SrcGamma, kTable_DstGamma, kNone_ColorSpaceMatch> | 1311 <kTable_SrcGamma, kTable_DstGamma, kNone_ColorSpaceMatch> |
1437 (space, SkMatrix::I(), space)); | 1312 (space, SkMatrix::I(), space)); |
1438 } | 1313 } |
OLD | NEW |