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

Unified Diff: third_party/WebKit/Source/platform/graphics/ColorSpaceFilter.cpp

Issue 1331533002: [poc] curve-filter Base URL: https://chromium.googlesource.com/chromium/blink.git@master
Patch Set: Fix CanvasRenderingContext2D::createPattern crash for #40 Created 4 years, 11 months 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: third_party/WebKit/Source/platform/graphics/ColorSpaceFilter.cpp
diff --git a/third_party/WebKit/Source/platform/graphics/ColorSpaceFilter.cpp b/third_party/WebKit/Source/platform/graphics/ColorSpaceFilter.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..020adf631182b8a5182bae6cdbd6ed5b2f833960
--- /dev/null
+++ b/third_party/WebKit/Source/platform/graphics/ColorSpaceFilter.cpp
@@ -0,0 +1,438 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "platform/graphics/ColorSpaceFilter.h"
+
+#include "platform/graphics/ColorSpaceProfile.h"
+#include "platform/graphics/ColorSpaceTransform.h"
+#include "platform/graphics/GraphicsScreen.h"
+#include "third_party/skia/include/core/SkColorFilter.h"
+#include "third_party/skia/include/core/SkColorPriv.h"
+#include "third_party/skia/include/core/SkData.h"
+#include "third_party/skia/include/core/SkString.h"
+#include "third_party/skia/include/core/SkUnPreMultiply.h"
+#include "third_party/skia/include/core/SkWriteBuffer.h"
+#include "third_party/skia/include/core/SkWriter32.h"
+#include "third_party/skia/include/effects/SkColorCubeFilter.h"
+#ifdef HAVE_COLOR_CURVE_FILTER
+#include "third_party/skia/include/effects/SkColorCurveFilter.h"
+#endif
+#include "third_party/skia/include/effects/SkColorFilterImageFilter.h"
+#include "third_party/skia/include/gpu/GrPaint.h"
+#include "wtf/MainThread.h"
+#include "wtf/RefPtr.h"
+
+#if USE(QCMSLIB)
+
+class ColorSpaceTransformFilter : public SkColorFilter {
+public:
+ static PassRefPtr<ColorSpaceTransformFilter> create(qcms_transform* transform, blink::ColorSpaceProfile* source, blink::ColorSpaceProfile* target)
+ {
+ return adoptRef(new ColorSpaceTransformFilter(transform, source, target));
+ }
+
+ SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(ColorSpaceTransformFilter)
+
+ // Color space transforms do not change pixel alpha.
+
+ virtual uint32_t getFlags() const
+ {
+ return this->SkColorFilter::getFlags() | SkColorFilter::kAlphaUnchanged_Flag;
+ }
+
+ virtual void filterSpan(const SkPMColor src[], int count, SkPMColor dst[]) const
+ {
+ // 1) Copy |src| pixel values to |dst|, convert from native to RGBA format.
+
+ int offset = count; // Record the offset to the first alpha pixel (if any).
+
+ for (int i = 0; i < count; ++i) {
+ SkPMColor pixel = src[i];
+
+ if (SkGetPackedA32(pixel) < 255) {
+ offset = i;
+ break;
+ }
+
+ dst[i] = SkSwizzle_RGBA_to_PMColor(pixel);
+ }
+
+ // 2) Unpremultiply remaining |src| pixel values into |dst| in RGBA format.
+
+ static const SkUnPreMultiply::Scale* table = SkUnPreMultiply::GetScaleTable();
+
+ unsigned char* output = reinterpret_cast<unsigned char*>(&dst[offset]);
+
+ for (int i = offset; i < count; ++i) {
+ SkPMColor pixel = src[i];
+
+ unsigned b = SkGetPackedB32(pixel);
+ unsigned g = SkGetPackedG32(pixel);
+ unsigned r = SkGetPackedR32(pixel);
+ unsigned a = SkGetPackedA32(pixel);
+
+ if (a < 255) {
+ SkUnPreMultiply::Scale scale = table[a];
+ b = SkUnPreMultiply::ApplyScale(scale, b);
+ g = SkUnPreMultiply::ApplyScale(scale, g);
+ r = SkUnPreMultiply::ApplyScale(scale, r);
+ }
+
+ ASSERT(b <= 255);
+ ASSERT(g <= 255);
+ ASSERT(r <= 255);
+ ASSERT(a <= 255);
+
+ *output++ = r;
+ *output++ = g;
+ *output++ = b;
+ *output++ = a;
+ }
+
+ // 3) Color correct the |dst| RGBA pixels in-situ with native pixel output.
+
+ qcms_transform_data_type(m_transform->transform(), dst, dst, count, SK_B32_SHIFT ? QCMS_OUTPUT_RGBX : QCMS_OUTPUT_BGRX);
+
+ // 4) Premultiply the |dst| native pixels (starting from the alpha offset).
+
+ unsigned char* input = reinterpret_cast<unsigned char*>(&dst[offset]);
+
+ for (int i = offset; i < count; ++i, input += 4) {
+ unsigned b = input[SK_B32_SHIFT ? 2 : 0];
+ unsigned g = input[1];
+ unsigned r = input[SK_B32_SHIFT ? 0 : 2];
+ unsigned a = input[3];
+
+ enum FractionControl { RoundFraction = 257 * 128 };
+
+ if (a < 255) {
+ unsigned alpha = a * 257;
+ b = (b * alpha + RoundFraction) >> 16;
+ g = (g * alpha + RoundFraction) >> 16;
+ r = (r * alpha + RoundFraction) >> 16;
+ }
+
+ dst[i] = SkPackARGB32(a, r, g, b);
+ }
+ }
+
+ void showColorSpaceFilter(const char* prefix = (const char *) " ") const
+ {
+ fprintf(stderr, "%scolor transform %s\n", prefix, colorSpaceTransformFilterKey().ascii().data());
+ fflush(stderr);
+ }
+
+ PassRefPtr<SkColorFilter> asColorFilter() const
+ {
+ fprintf(stderr, "asColorFilter %p requested\n", this);
+
+ if (!m_effect.get()) {
+ const_cast<ColorSpaceTransformFilter*>(this)->createColorFilterEffect();
+
+ if (m_effect.get())
+ showColorSpaceFilter("asColorFilter created filter effect ");
+ }
+
+ if (!m_effect.get()) {
+ showColorSpaceFilter("asColorFilter create filter effect: FAILED ");
+ return nullptr;
+ }
+
+ showColorSpaceFilter("asColorFilter return filter SUCCESS ");
+ return m_effect;
+ }
+
+ String colorSpaceTransformFilterKey() const
+ {
+ return transformFilterKey(m_source->profile(), m_target->profile());
+ }
+
+ ~ColorSpaceTransformFilter()
+ {
+ }
+
+protected:
+ ColorSpaceTransformFilter(qcms_transform* transform, blink::ColorSpaceProfile* source, blink::ColorSpaceProfile* target)
+ : m_transform(blink::ColorSpaceTransform::create(transform))
+ , m_source(source)
+ , m_target(target)
+ {
+ fprintf(stderr, "creating ColorSpaceFilter %p transform-is-matrix: %d\n", this, qcms_transform_is_matrix(transform));
+
+ RELEASE_ASSERT(m_transform && m_source->profile() && m_target->profile());
+ }
+
+
+ virtual const GrFragmentProcessor* asFragmentProcessor(GrContext* context) const
+ {
+ fprintf(stderr, "GPU asFragmentProcessor %p requested\n", this);
+
+ if (!m_effect.get()) {
+ const_cast<ColorSpaceTransformFilter*>(this)->createColorFilterEffect();
+
+ if (m_effect.get())
+ showColorSpaceFilter("GPU asFragmentProcessor created filter effect ");
+ }
+
+ if (!m_effect.get()) {
+ showColorSpaceFilter("GPU asFragmentProcessor create filter effect: FAILED ");
+ return nullptr;
+ }
+
+ const GrFragmentProcessor* fragment = m_effect->asFragmentProcessor(context);
+
+ if (fragment) {
+ showColorSpaceFilter("GPU asFragmentProcessor return fragment SUCCESS ");
+ }
+
+ return fragment;
+ }
+
+ void createColorFilterEffect()
+ {
+ RELEASE_ASSERT(!m_effect.get());
+
+ SkColorFilter* filter;
+
+#ifdef HAVE_COLOR_CURVE_FILTER
+ if (qcms_transform_is_matrix(m_transform->transform()))
+ filter = this->createColorCurveFilter();
+ else
+ filter = this->createColorCubeFilter();
+#else
+ filter = this->createColorCubeFilter();
+#endif
+ m_effect = adoptRef(filter);
+ }
+
+#ifdef HAVE_COLOR_CURVE_FILTER
+
+ SkColorFilter* createColorCurveFilter()
+ {
+ uint32_t inputSize = inputToneCurveSize(m_transform->transform(), m_source->profile());
+ SkAutoDataUnref inputData(
+ SkData::NewUninitialized(inputSize * 4 * sizeof(uint16_t)));
+ if (!inputSize || !inputData) {
+ fprintf(stderr, "filter input curve data creation failed\n");
+ return nullptr;
+ }
+
+ uint16_t* inputCurve = static_cast<uint16_t *>(inputData->writable_data());
+ readInputToneCurve(m_transform->transform(), m_source->profile(), inputCurve);
+ fprintf(stderr, "input curve data created %p size=%d\n", this, (int) inputData->size());
+
+ SkAutoTUnref<SkToneCurve> input(
+ SkToneCurve::Create(inputData, inputSize, SkToneCurve::kHalfType));
+
+ SkMatrix matrix;
+ matrix[0] = qcms_transform_get_matrix(m_transform->transform(), 0, 0);
+ matrix[1] = qcms_transform_get_matrix(m_transform->transform(), 0, 1);
+ matrix[2] = qcms_transform_get_matrix(m_transform->transform(), 0, 2);
+ matrix[3] = qcms_transform_get_matrix(m_transform->transform(), 1, 0);
+ matrix[4] = qcms_transform_get_matrix(m_transform->transform(), 1, 1);
+ matrix[5] = qcms_transform_get_matrix(m_transform->transform(), 1, 2);
+ matrix[6] = qcms_transform_get_matrix(m_transform->transform(), 2, 0);
+ matrix[7] = qcms_transform_get_matrix(m_transform->transform(), 2, 1);
+ matrix[8] = qcms_transform_get_matrix(m_transform->transform(), 2, 2);
+
+ fprintf(stderr, "[ %f, %f, %f ]\n", matrix[0], matrix[1], matrix[2]);
+ fprintf(stderr, "[ %f, %f, %f ]\n", matrix[3], matrix[4], matrix[5]);
+ fprintf(stderr, "[ %f, %f, %f ]\n", matrix[6], matrix[7], matrix[8]);
+
+ uint32_t outputSize = outputToneCurveSize(m_transform->transform(), m_target->profile());
+ SkAutoDataUnref outputData(
+ SkData::NewUninitialized(outputSize * 4 * sizeof(uint16_t)));
+ if (!outputSize || !outputData) {
+ fprintf(stderr, "filter output curve data creation failed\n");
+ return nullptr;
+ }
+
+ uint16_t* outputCurve = static_cast<uint16_t *>(outputData->writable_data());
+ readOutputToneCurve(m_transform->transform(), m_target->profile(), outputCurve);
+ fprintf(stderr, "output curve data created %p size=%d\n", this, (int) outputData->size());
+
+ SkAutoTUnref<SkToneCurve> output(
+ SkToneCurve::Create(outputData, outputSize, SkToneCurve::kHalfType));
+
+ SkColorCurveFilter* filter = SkColorCurveFilter::Create(input, output);
+ if (!filter) {
+ fprintf(stderr, "cube filter creation failed\n");
+ return nullptr;
+ }
+
+ filter->setColorMatrix(matrix);
+ return filter;
+ }
+
+ static uint16_t inputToneCurveSize(qcms_transform* transform, qcms_profile* input)
+ {
+ return qcms_transform_get_input_trc_rgba(transform, input, QCMS_TRC_HALF_FLOAT, 0);
+ }
+
+ static uint16_t readInputToneCurve(qcms_transform* transform, qcms_profile* input, uint16_t* rgba)
+ {
+ return qcms_transform_get_input_trc_rgba(transform, input, QCMS_TRC_HALF_FLOAT, rgba);
+ }
+
+ static uint16_t outputToneCurveSize(qcms_transform* transform, qcms_profile* output)
+ {
+ return qcms_transform_get_output_trc_rgba(transform, output, QCMS_TRC_HALF_FLOAT, 0);
+ }
+
+ static uint16_t readOutputToneCurve(qcms_transform* transform, qcms_profile* output, uint16_t* rgba)
+ {
+ return qcms_transform_get_output_trc_rgba(transform, output, QCMS_TRC_HALF_FLOAT, rgba);
+ }
+
+#endif // HAVE_COLOR_CURVE_FILTER
+
+ SkColorFilter* createColorCubeFilter()
+ {
+ enum ColorCubeSize { RgbaBytes = 4, CubeSize = 32, CubeVolume = CubeSize * CubeSize * CubeSize };
+
+ SkAutoDataUnref data(SkData::NewUninitialized(CubeVolume * RgbaBytes));
+
+ // 0.000084877 (84 micro-secs) creating data block
+
+ unsigned char* cube = static_cast<unsigned char*>(data->writable_data());
+ fprintf(stderr, "filter data created %p size=%d\n", this, (int) data->size());
+ RELEASE_ASSERT(cube);
+
+ qcms_profile* source = m_source->profile();
+ qcms_profile* target = m_target->profile();
+
+ if (!qcms_transform_create_LUT_zyx_bgra(source, target, QCMS_INTENT_PERCEPTUAL, CubeSize, cube)) {
+ fprintf(stderr, "filter color cube creation failed\n");
+ return nullptr;
+ }
+
+ // 0.00851917 (8.5 milliseconds!!) qcms_transform_create_LUT_zyx_bgra: that's too slow.
+
+ SkColorFilter* filter = SkColorCubeFilter::Create(data, CubeSize);
+ if (!filter) {
+ fprintf(stderr, "cube filter creation failed\n");
+ return nullptr;
+ }
+
+ // 0.00000405312 (4 micro-secs) ColorCubeFilter::Create and assign
+
+ return filter;
+ }
+
+ static String transformFilterKey(qcms_profile* source, qcms_profile* target)
+ {
+ return qcms_profile_get_description(source) + transformTargetDescription(target);
+ }
+
+ static String transformTargetDescription(qcms_profile* target)
+ {
+ return String(" -> ") + qcms_profile_get_description(target);
+ }
+
+#ifndef SK_IGNORE_TO_STRING
+
+ void toString(SkString* str) const
+ {
+ str->append("ColorSpaceTransformFilter: ()");
+ }
+
+#endif // SK_IGNORE_TO_STRING
+
+ void flatten(SkWriteBuffer&) const
+ {
+ RELEASE_ASSERT_NOT_REACHED();
+ }
+
+private:
+ RefPtr<blink::ColorSpaceTransform> m_transform;
+ RefPtr<blink::ColorSpaceProfile> m_source;
+ RefPtr<blink::ColorSpaceProfile> m_target;
+ RefPtr<SkColorFilter> m_effect;
+};
+
+SkFlattenable* ColorSpaceTransformFilter::CreateProc(class SkReadBuffer&)
+{
+ RELEASE_ASSERT_NOT_REACHED();
+
+ return nullptr;
+}
+
+namespace blink {
+
+PassRefPtr<SkColorFilter> createColorSpaceFilter(ColorSpaceProfile* source, ColorSpaceProfile* target)
+{
+ if (!source || !target || !source->profile() || !target->profile())
+ return nullptr;
+
+ if (ColorSpaceProfile::equal(source->profile(), target->profile()))
+ return nullptr;
+
+ RELEASE_ASSERT(imageColorProfilesEnabled());
+
+ // 5 micro-secs to cache lookup.
+
+ if (RefPtr<SkColorFilter> filter = findColorTransform(source, target)) {
+ // fprintf(stderr, " found color transform in cache %p\n", filter.get());
+ return filter;
+ }
+
+ // 20 micro-secs to qcms_transform_create.
+
+ qcms_transform* transform = qcms_transform_create(source->profile(), QCMS_DATA_RGBA_8, target->profile(), QCMS_DATA_RGBA_8, QCMS_INTENT_PERCEPTUAL);
+ if (!transform)
+ return nullptr;
+
+ // 5 micro-secs to create and cache ColorSpaceTransformFilter.
+
+ RefPtr<SkColorFilter> filter = ColorSpaceTransformFilter::create(transform, source, target);
+ fprintf(stderr, " created color transform filter %s -> %s\n", qcms_profile_get_description(source->profile()), qcms_profile_get_description(target->profile()));
+ cacheColorTransform(source, target, filter.get());
+
+ return filter;
+}
+
+PassRefPtr<SkColorFilter> colorSpaceFlattenableFilter(SkColorFilter* filter)
+{
+ RELEASE_ASSERT(imageColorProfilesEnabled());
+
+ if (ColorSpaceTransformFilter* transform = static_cast<ColorSpaceTransformFilter*>(filter))
+ return transform->asColorFilter();
+
+ return nullptr;
+}
+
+String colorSpaceFilterKey(SkColorFilter* filter)
+{
+ RELEASE_ASSERT(imageColorProfilesEnabled());
+
+ if (ColorSpaceTransformFilter* transform = static_cast<ColorSpaceTransformFilter*>(filter))
+ return transform->colorSpaceTransformFilterKey();
+
+ return String();
+}
+
+} // namespace blink
+
+#else
+
+namespace blink {
+
+PassRefPtr<SkColorFilter> createColorSpaceFilter(ColorSpaceProfile*, ColorSpaceProfile*)
+{
+ return nullptr;
+}
+
+PassRefPtr<SkColorFilter> colorSpaceFlattenableFilter(SkColorFilter*)
+{
+ return nullptr;
+}
+
+String colorSpaceFilterKey(SkColorFilter*)
+{
+ return String();
+}
+
+} // namespace blink
+
+#endif // USE(QCMSLIB)

Powered by Google App Engine
This is Rietveld 408576698