Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(144)

Unified Diff: src/core/SkColorSpaceXform_A2B.cpp

Issue 2449243003: Initial implementation of a SkColorSpace_A2B xform (Closed)
Patch Set: updated implementation to use SkRasterPipeline Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: src/core/SkColorSpaceXform_A2B.cpp
diff --git a/src/core/SkColorSpaceXform_A2B.cpp b/src/core/SkColorSpaceXform_A2B.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..baa318856e08022b50e9c091ae3e7b516f824894
--- /dev/null
+++ b/src/core/SkColorSpaceXform_A2B.cpp
@@ -0,0 +1,286 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkColorSpaceXform_A2B.h"
+
+#include "SkColorPriv.h"
+#include "SkColorSpace_A2B.h"
+#include "SkColorSpace_XYZ.h"
+#include "SkColorSpacePriv.h"
+#include "SkColorSpaceXformPriv.h"
+#include "SkMakeUnique.h"
+#include "SkNx.h"
+#include "SkSRGB.h"
+#include "SkTypes.h"
+
+#include "SkRasterPipeline_opts.h"
+
+#define AI SK_ALWAYS_INLINE
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+bool SkColorSpaceXform_A2B::onApply(ColorFormat dstFormat, void* dst, ColorFormat srcFormat,
+ const void* src, int count, SkAlphaType alphaType) const {
mtklein_C 2016/11/09 11:04:36 Is this the alphaType for src or dst? I'm guessin
msarett1 2016/11/11 14:36:51 It refers to the dst, and the src is always unprem
+ SkRasterPipeline entirePipeline;
msarett1 2016/11/09 00:01:04 I don't love this variable name.
raftias 2016/11/10 21:36:06 Acknowledged.
+ switch (srcFormat) {
+ case kBGRA_8888_ColorFormat:
+ entirePipeline.append(SkRasterPipeline::load_s_linear_bgra, &src);
+ break;
+ case kRGBA_8888_ColorFormat:
+ entirePipeline.append(SkRasterPipeline::load_s_linear_rgba, &src);
+ break;
+ default:
+ SkCSXformPrintf("F16/F32 source color format not supported\n");
mtklein_C 2016/11/09 11:04:36 load_s_f16 will probably work just fine. And we c
+ return false;
+ }
+
+ entirePipeline.extend(fElementsPipeline);
+
+ if (kPremul_SkAlphaType == alphaType) {
+ //entirePipeline.append(SkRasterPipeline::premul);
msarett1 2016/11/09 00:01:04 Why comment this out?
raftias 2016/11/10 21:36:06 Mistake, I forgot to uncomment it after I tested s
+ }
+
+ switch (dstFormat) {
+ case kBGRA_8888_ColorFormat:
+ entirePipeline.append(SkRasterPipeline::store_linear_bgra, &dst);
+ break;
+ case kRGBA_8888_ColorFormat:
+ entirePipeline.append(SkRasterPipeline::store_linear_rgba, &dst);
+ break;
+ case kRGBA_F16_ColorFormat:
+ if (!fLinearDstGamma) {
+ return false;
+ }
+ entirePipeline.append(SkRasterPipeline::store_f16, &dst);
+ break;
+ case kRGBA_F32_ColorFormat:
+ if (!fLinearDstGamma) {
+ return false;
+ }
+ entirePipeline.append(SkRasterPipeline::store_f32, &dst);
+ break;
+ }
+
+ auto p = entirePipeline.compile();
+
+ p(0, count);
+
+ return true;
+}
+
+static inline SkColorSpaceTransferFn value_to_parametric(float exp) {
+ return {exp, 1.f, 0.f, 0.f, 0.f, 0.f, 0.f};
+}
+
+static inline SkColorSpaceTransferFn gammanamed_to_parametric(SkGammaNamed gammaNamed) {
+ switch (gammaNamed) {
+ case kLinear_SkGammaNamed:
+ return value_to_parametric(1.f);
+ case kSRGB_SkGammaNamed:
+ return {2.4f, (1.f / 1.055f), (0.055f / 1.055f), 0.f, 0.04045f, (1.f / 12.92f), 0.f};
+ case k2Dot2Curve_SkGammaNamed:
+ return value_to_parametric(2.2f);
+ default:
+ SkASSERT(false);
+ return {-1.f, -1.f, -1.f, -1.f, -1.f, -1.f, -1.f};
+ }
+}
+
+static inline SkColorSpaceTransferFn gamma_to_parametric(const SkGammas& gammas, int channel) {
+ switch (gammas.type(channel)) {
+ case SkGammas::Type::kNamed_Type:
+ return gammanamed_to_parametric(gammas.data(channel).fNamed);
+ case SkGammas::Type::kValue_Type:
+ return value_to_parametric(gammas.data(channel).fValue);
+ case SkGammas::Type::kParam_Type:
+ return gammas.params(channel);
+ default:
+ SkASSERT(false);
+ return {-1.f, -1.f, -1.f, -1.f, -1.f, -1.f, -1.f};
+ }
+}
+static inline SkColorSpaceTransferFn invert_parametric(const SkColorSpaceTransferFn& fn) {
+ // Original equation is: y = (ax + b)^g + c for x >= d
+ // y = ex + f otherwise
+ //
+ // so 1st inverse is: (y - c)^(1/g) = ax + b
+ // x = ((y - c)^(1/g) - b) / a
+ //
+ // which can be re-written as: x = (1/a)(y - c)^(1/g) - b/a
+ // x = ((1/a)^g)^(1/g) * (y - c)^(1/g) - b/a
+ // x = ([(1/a)^g]y + [-((1/a)^g)c]) ^ [1/g] + [-b/a]
+ //
+ // and 2nd inverse is: x = (y - f) / e
+ // which can be re-written as: x = [1/e]y + [-f/e]
+ //
+ // and now both can be expressed in terms of the same parametric form as the
+ // original - parameters are enclosed in square barckets.
+
+ // find inverse for linear segment (if possible)
+ float e, f;
+ if (0.f == fn.fE) {
+ // otherwise assume it should be 0 as it is the lower segment
+ // as y = f is a constant function
+ e = 0.f;
+ f = 0.f;
+ } else {
+ e = 1.f / fn.fE;
+ f = -fn.fF / fn.fE;
+ }
+ // find inverse for the other segment (if possible)
+ float g, a, b, c;
+ if (0.f == fn.fA || 0.f == fn.fG) {
+ // otherwise assume it should be 1 as it is the top segment
+ // as you can't invert the constant functions y = b^g + c, or y = 1 + c
+ g = 1.f;
+ a = 0.f;
+ b = 0.f;
+ c = 1.f;
+ } else {
+ g = 1.f / fn.fG;
+ a = powf(1.f / fn.fA, fn.fG);
+ b = -a * fn.fC;
+ c = -fn.fB / fn.fA;
+ }
+ const float d = fn.fE * fn.fD + fn.fF;
+ return {g, a, b, c, d, e, f};
+}
+
+#define M(stage) stage,
+static const SkRasterPipeline::StockStage param_gamma_stages[] = {
+ DECLARE_GAMMA(M, SkRasterPipeline::param_gamma)
+};
+static const SkRasterPipeline::StockStage table_gamma_stages[] = {
+ DECLARE_GAMMA(M, SkRasterPipeline::table_gamma)
+};
+#undef M
+
+SkColorSpaceXform_A2B::SkColorSpaceXform_A2B(SkColorSpace_A2B* srcSpace,
+ SkColorSpace_XYZ* dstSpace)
+ : fLinearDstGamma(kLinear_SkGammaNamed == dstSpace->gammaNamed()) {
+#if (SkCSXformPrintfDefined)
msarett1 2016/11/09 00:01:05 How extremely helpful is this for debugging?
msarett1 2016/11/11 14:36:51 The reason I ask is that we like to avoid #if code
+ static const char* debugGammaNamed[4] = {
+ "Linear", "SRGB", "2.2", "NonStandard"
+ };
+ static const char* debugGammas[5] = {
+ "None", "Named", "Value", "Table", "Param"
+ };
+#endif
+ // add in all device -> PCS xforms
+ for (size_t i = 0; i < srcSpace->count(); ++i) {
msarett1 2016/11/09 00:01:05 nit: Count with ints
raftias 2016/11/10 21:36:06 Done. (forgot to upload the file though - I'll do
+ const SkColorSpace_A2B::Element& e = srcSpace->element(i);
+ switch (e.type()) {
+ case SkColorSpace_A2B::Element::Type::kGammaNamed:
+ if (kLinear_SkGammaNamed != e.gammaNamed()) {
+ SkCSXformPrintf("Gamma stage added: %s\n",
+ debugGammaNamed[(int)e.gammaNamed()]);
+ fParametricGammas.push_front(gammanamed_to_parametric(e.gammaNamed()));
+ for (int channel = 0; channel < 3; ++channel) {
+ fElementsPipeline.append(param_gamma_stages[channel], &fParametricGammas.front());
msarett1 2016/11/09 00:01:05 nit: 100 chars
+ }
+ }
+ break;
+ case SkColorSpace_A2B::Element::Type::kGammas: {
msarett1 2016/11/09 00:01:04 I think it might be possible to make the table cas
raftias 2016/11/10 21:36:06 Done.
+ const SkGammas& gammas = e.gammas();
+ SkCSXformPrintf("Adding gamme stage:");
msarett1 2016/11/09 00:01:04 nit: spelling
raftias 2016/11/10 21:36:06 Acknowledged.
+ for (int channel = 0; channel < 3; ++channel) {
+ SkCSXformPrintf(" %s", debugGammas[(int)gammas.type(channel)]);
+ }
+ SkCSXformPrintf("\n");
+ float* firstDstTable = nullptr;
+ for (int channel = 0; channel < 3; ++channel) {
+ if (SkGammas::Type::kTable_Type == gammas.type(channel)) {
+ float* dstTable;
+ if (channel > 0 && SkGammas::Type::kTable_Type == gammas.type(0) &&
+ gammas.table(0) == gammas.table(channel)) {
+ SkASSERT(firstDstTable != nullptr);
+ dstTable = firstDstTable;
+ } else {
+ const float* inTable = gammas.table(channel);
+ const int inTableSize = gammas.data(channel).fTable.fSize;
+ fTableGammas.push_front(std::vector<float>(kDstGammaTableSize));
+ dstTable = fTableGammas.front().data();
+ if (0 == channel) {
+ firstDstTable = dstTable;
+ }
+ if (kDstGammaTableSize == inTableSize) {
+ memcpy(dstTable, inTable, sizeof(float) * kDstGammaTableSize);
+ } else {
+ const float tableStep = 1.f / (float)(kDstGammaTableSize - 1);
+ float* ptr = dstTable;
+ for (float x = 0.0f; x <= 1.0f; x += tableStep) {
+ *(ptr++) = interp_lut(x, inTable, inTableSize);
+ }
+ }
+ }
+ fElementsPipeline.append(table_gamma_stages[channel], dstTable);
+ } else {
+ fParametricGammas.push_front(gamma_to_parametric(gammas, channel));
+ fElementsPipeline.append(param_gamma_stages[channel],
+ &fParametricGammas.front());
+ }
+ }
+ }
+ break;
+ case SkColorSpace_A2B::Element::Type::kCLUT:
+ SkCSXformPrintf("CLUT stage added [%d][%d][%d]\n", e.colorLUT().fGridPoints[0],
+ e.colorLUT().fGridPoints[1], e.colorLUT().fGridPoints[2]);
+ fCLUTs.push_front(sk_ref_sp(&e.colorLUT()));
+ fElementsPipeline.append(SkRasterPipeline::clut, fCLUTs.front().get());
+ break;
+ case SkColorSpace_A2B::Element::Type::kMatrix:
+ if (!e.matrix().isIdentity()) {
+ SkCSXformPrintf("Matrix stage added\n");
+ fMatrices.push_front(e.matrix());
+ fElementsPipeline.append(SkRasterPipeline::matrix_4x4, &fMatrices.front());
+ }
+ break;
+ }
+ }
+
+ // Lab PCS -> XYZ PCS
+ if (SkColorSpace_A2B::PCS::kLAB == srcSpace->pcs()) {
+ SkCSXformPrintf("Lab -> XYZ element added\n");
+ fElementsPipeline.append(SkRasterPipeline::labtoxyz);
+ }
+
+ // and XYZ PCS -> device xforms
+ if (!dstSpace->fromXYZD50()->isIdentity()) {
+ fMatrices.push_front(*dstSpace->fromXYZD50());
+ fElementsPipeline.append(SkRasterPipeline::matrix_4x4, &fMatrices.front());
+ }
+
+ if (!fLinearDstGamma) {
+ if (kNonStandard_SkGammaNamed != dstSpace->gammaNamed()) {
msarett1 2016/11/09 00:01:04 Is there some refactoring you can do to make this
raftias 2016/11/10 21:36:05 I had an addGamma() method that would add the stor
msarett1 2016/11/11 14:36:51 Sorry my comment was unclear - please ignore
+ fParametricGammas.push_front(
+ invert_parametric(gammanamed_to_parametric(dstSpace->gammaNamed())));
msarett1 2016/11/09 00:01:04 I have confidence that this is much, much better t
raftias 2016/11/10 21:36:06 And use tables instead? The code before only evalu
msarett1 2016/11/11 14:36:51 That is what I meant, but I think you're right, be
+ for (int channel = 0; channel < 3; ++channel) {
+ fElementsPipeline.append(param_gamma_stages[channel], &fParametricGammas.front());
+ }
+ } else {
+ for (int channel = 0; channel < 3; ++channel) {
+ const SkGammas& gammas = *dstSpace->gammas();
+ if (SkGammas::Type::kTable_Type == gammas.type(channel)) {
+ fTableGammas.push_front(std::vector<float>(kDstGammaTableSize));
+ float* dstTable = fTableGammas.front().data();
+ for (int i = 0; i < kDstGammaTableSize; ++i) {
+ const float x = ((float) i) * (1.0f / ((float) (kDstGammaTableSize - 1)));
+ const float y = inverse_interp_lut(x, gammas.table(channel),
+ gammas.data(channel).fTable.fSize);
+ dstTable[i] = y;
+ }
+ fElementsPipeline.append(table_gamma_stages[channel], dstTable);
+ } else {
+ fParametricGammas.push_front(
+ invert_parametric(gamma_to_parametric(gammas, channel)));
+ fElementsPipeline.append(param_gamma_stages[channel], &fParametricGammas.front());
msarett1 2016/11/09 00:01:04 nit: 100 chars
+ }
+ }
+ }
+ }
+}
+
+

Powered by Google App Engine
This is Rietveld 408576698