| 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..b4305a4cc3f08fad32e8a8a3c73554fe60f47d47
|
| --- /dev/null
|
| +++ b/third_party/WebKit/Source/platform/graphics/ColorSpaceFilter.cpp
|
| @@ -0,0 +1,433 @@
|
| +// 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/gpu/GrPaint.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())) {
|
| + // fprintf(stderr, " color transform profiles are the same: no need for color correction\n");
|
| + return nullptr;
|
| + }
|
| +
|
| + // 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());
|
| +
|
| + RELEASE_ASSERT(filter.get());
|
| + return filter;
|
| +}
|
| +
|
| +PassRefPtr<SkColorFilter> colorSpaceFlattenableFilter(SkColorFilter* filter)
|
| +{
|
| + if (ColorSpaceTransformFilter* transform = static_cast<ColorSpaceTransformFilter*>(filter))
|
| + return transform->asColorFilter();
|
| +
|
| + return nullptr;
|
| +}
|
| +
|
| +String colorSpaceFilterKey(SkColorFilter* filter)
|
| +{
|
| + 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)
|
|
|