SkColorFilterImageFilter.cpp revision c12b74dc413ef024b13e0ed478491c4b1bafe6b1
144888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org/*
244888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org * Copyright 2012 The Android Open Source Project
344888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org *
444888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org * Use of this source code is governed by a BSD-style license that can be
544888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org * found in the LICENSE file.
644888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org */
744888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org
844888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org#include "SkColorFilterImageFilter.h"
944888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org#include "SkBitmap.h"
1044888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org#include "SkCanvas.h"
118d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org#include "SkColorMatrixFilter.h"
1244888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org#include "SkDevice.h"
1344888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org#include "SkColorFilter.h"
148b0e8ac5f582de80356019406e2975079bf0829dcommit-bot@chromium.org#include "SkReadBuffer.h"
15c12b74dc413ef024b13e0ed478491c4b1bafe6b1cwallez#include "SkTableColorFilter.h"
168b0e8ac5f582de80356019406e2975079bf0829dcommit-bot@chromium.org#include "SkWriteBuffer.h"
1744888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org
188d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.orgnamespace {
198d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org
208d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.orgvoid mult_color_matrix(SkScalar a[20], SkScalar b[20], SkScalar out[20]) {
218d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org    for (int j = 0; j < 4; ++j) {
228d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org        for (int i = 0; i < 5; ++i) {
238d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org            out[i+j*5] = 4 == i ? a[4+j*5] : 0;
248d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org            for (int k = 0; k < 4; ++k)
25c12b74dc413ef024b13e0ed478491c4b1bafe6b1cwallez                out[i+j*5] += a[k+j*5] * b[i+k*5];
26c12b74dc413ef024b13e0ed478491c4b1bafe6b1cwallez        }
27c12b74dc413ef024b13e0ed478491c4b1bafe6b1cwallez    }
28c12b74dc413ef024b13e0ed478491c4b1bafe6b1cwallez}
29c12b74dc413ef024b13e0ed478491c4b1bafe6b1cwallez
30c12b74dc413ef024b13e0ed478491c4b1bafe6b1cwallez// Combines the two lookup tables so that making a lookup using OUT has
31c12b74dc413ef024b13e0ed478491c4b1bafe6b1cwallez// the same effect as making a lookup through B then A.
32c12b74dc413ef024b13e0ed478491c4b1bafe6b1cwallezvoid combine_color_tables(const uint8_t a[4 * 256],
33c12b74dc413ef024b13e0ed478491c4b1bafe6b1cwallez                          const uint8_t b[4 * 256],
34c12b74dc413ef024b13e0ed478491c4b1bafe6b1cwallez                          uint8_t out[4 * 256]) {
35c12b74dc413ef024b13e0ed478491c4b1bafe6b1cwallez    for (int i = 0; i < 4; i++) {
36c12b74dc413ef024b13e0ed478491c4b1bafe6b1cwallez        for (int j = 0; j < 256; j++) {
37c12b74dc413ef024b13e0ed478491c4b1bafe6b1cwallez            out[i * 256 + j] = a[i * 256 + b[i * 256 + j]];
388d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org        }
398d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org    }
408d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org}
418d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org
428d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org// To detect if we need to apply clamping after applying a matrix, we check if
438d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org// any output component might go outside of [0, 255] for any combination of
448d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org// input components in [0..255].
458d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org// Each output component is an affine transformation of the input component, so
468d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org// the minimum and maximum values are for any combination of minimum or maximum
478d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org// values of input components (i.e. 0 or 255).
488d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org// E.g. if R' = x*R + y*G + z*B + w*A + t
498d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org// Then the maximum value will be for R=255 if x>0 or R=0 if x<0, and the
508d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org// minimum value will be for R=0 if x>0 or R=255 if x<0.
518d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org// Same goes for all components.
528d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.orgbool component_needs_clamping(SkScalar row[5]) {
538d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org    SkScalar maxValue = row[4] / 255;
548d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org    SkScalar minValue = row[4] / 255;
558d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org    for (int i = 0; i < 4; ++i) {
568d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org        if (row[i] > 0)
578d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org            maxValue += row[i];
588d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org        else
598d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org            minValue += row[i];
608d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org    }
618d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org    return (maxValue > 1) || (minValue < 0);
628d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org}
638d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org
648d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.orgbool matrix_needs_clamping(SkScalar matrix[20]) {
658d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org    return component_needs_clamping(matrix)
668d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org        || component_needs_clamping(matrix+5)
678d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org        || component_needs_clamping(matrix+10)
688d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org        || component_needs_clamping(matrix+15);
698d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org}
708d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org
718d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org};
728d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org
73cd9f55989e680b7f52fa21766dde0ac67ac9911fsenorblanco@chromium.orgSkColorFilterImageFilter* SkColorFilterImageFilter::Create(SkColorFilter* cf,
745e5f948b6b363dbfc8c076d8ff0c6b8e9ea99958senorblanco        SkImageFilter* input, const CropRect* cropRect, uint32_t uniqueID) {
759bde31e95ddd82a4a923275bb01265740407dab1sugoi    if (NULL == cf) {
769bde31e95ddd82a4a923275bb01265740407dab1sugoi        return NULL;
779bde31e95ddd82a4a923275bb01265740407dab1sugoi    }
78c12b74dc413ef024b13e0ed478491c4b1bafe6b1cwallez
79cd9f55989e680b7f52fa21766dde0ac67ac9911fsenorblanco@chromium.org    SkColorFilter* inputColorFilter;
80c12b74dc413ef024b13e0ed478491c4b1bafe6b1cwallez    if (input && input->asColorFilter(&inputColorFilter) && inputColorFilter) {
81a1c511b8704c6c266b90860a4c68f30ca7514f9bsugoi@google.com        SkAutoUnref autoUnref(inputColorFilter);
82c12b74dc413ef024b13e0ed478491c4b1bafe6b1cwallez
83c12b74dc413ef024b13e0ed478491c4b1bafe6b1cwallez        // Try to collapse two consecutive matrix filters
84c12b74dc413ef024b13e0ed478491c4b1bafe6b1cwallez        SkScalar colorMatrix[20], inputMatrix[20];
85c12b74dc413ef024b13e0ed478491c4b1bafe6b1cwallez        if (cf->asColorMatrix(colorMatrix) && inputColorFilter->asColorMatrix(inputMatrix)
86c12b74dc413ef024b13e0ed478491c4b1bafe6b1cwallez                                           && !matrix_needs_clamping(inputMatrix)) {
87a1c511b8704c6c266b90860a4c68f30ca7514f9bsugoi@google.com            SkScalar combinedMatrix[20];
883df05015efce95c306fb79c21efc77c79f1ac1basenorblanco            mult_color_matrix(colorMatrix, inputMatrix, combinedMatrix);
89727a352f7412753d2a3e4d30eab6500a1a4de135commit-bot@chromium.org            SkAutoTUnref<SkColorFilter> newCF(SkColorMatrixFilter::Create(combinedMatrix));
905e5f948b6b363dbfc8c076d8ff0c6b8e9ea99958senorblanco            return SkNEW_ARGS(SkColorFilterImageFilter, (newCF, input->getInput(0), cropRect, 0));
91a1c511b8704c6c266b90860a4c68f30ca7514f9bsugoi@google.com        }
92c12b74dc413ef024b13e0ed478491c4b1bafe6b1cwallez
93c12b74dc413ef024b13e0ed478491c4b1bafe6b1cwallez        // Try to collapse two consecutive table filters
94c12b74dc413ef024b13e0ed478491c4b1bafe6b1cwallez        SkBitmap colorTable, inputTable;
95c12b74dc413ef024b13e0ed478491c4b1bafe6b1cwallez        if (cf->asComponentTable(&colorTable) && inputColorFilter->asComponentTable(&inputTable)) {
96c12b74dc413ef024b13e0ed478491c4b1bafe6b1cwallez            uint8_t combinedTable[4 * 256];
97c12b74dc413ef024b13e0ed478491c4b1bafe6b1cwallez            SkAutoLockPixels colorLock(colorTable);
98c12b74dc413ef024b13e0ed478491c4b1bafe6b1cwallez            SkAutoLockPixels inputLock(inputTable);
99c12b74dc413ef024b13e0ed478491c4b1bafe6b1cwallez
100c12b74dc413ef024b13e0ed478491c4b1bafe6b1cwallez            combine_color_tables(colorTable.getAddr8(0, 0), inputTable.getAddr8(0, 0),
101c12b74dc413ef024b13e0ed478491c4b1bafe6b1cwallez                                 combinedTable);
102c12b74dc413ef024b13e0ed478491c4b1bafe6b1cwallez            SkAutoTUnref<SkColorFilter> newCF(SkTableColorFilter::CreateARGB(
103c12b74dc413ef024b13e0ed478491c4b1bafe6b1cwallez                        &combinedTable[256 * 0],
104c12b74dc413ef024b13e0ed478491c4b1bafe6b1cwallez                        &combinedTable[256 * 1],
105c12b74dc413ef024b13e0ed478491c4b1bafe6b1cwallez                        &combinedTable[256 * 2],
106c12b74dc413ef024b13e0ed478491c4b1bafe6b1cwallez                        &combinedTable[256 * 3])
107c12b74dc413ef024b13e0ed478491c4b1bafe6b1cwallez            );
108c12b74dc413ef024b13e0ed478491c4b1bafe6b1cwallez
109c12b74dc413ef024b13e0ed478491c4b1bafe6b1cwallez            return SkNEW_ARGS(SkColorFilterImageFilter, (newCF, input->getInput(0), cropRect, 0));
110c12b74dc413ef024b13e0ed478491c4b1bafe6b1cwallez        }
111cd9f55989e680b7f52fa21766dde0ac67ac9911fsenorblanco@chromium.org    }
112c12b74dc413ef024b13e0ed478491c4b1bafe6b1cwallez
1135e5f948b6b363dbfc8c076d8ff0c6b8e9ea99958senorblanco    return SkNEW_ARGS(SkColorFilterImageFilter, (cf, input, cropRect, uniqueID));
114cd9f55989e680b7f52fa21766dde0ac67ac9911fsenorblanco@chromium.org}
115cd9f55989e680b7f52fa21766dde0ac67ac9911fsenorblanco@chromium.org
116194d775edcf5fa6e82098a97ad53018d70db1155senorblanco@chromium.orgSkColorFilterImageFilter::SkColorFilterImageFilter(SkColorFilter* cf,
1175e5f948b6b363dbfc8c076d8ff0c6b8e9ea99958senorblanco        SkImageFilter* input, const CropRect* cropRect, uint32_t uniqueID)
1189bde31e95ddd82a4a923275bb01265740407dab1sugoi    : INHERITED(1, &input, cropRect, uniqueID), fColorFilter(SkRef(cf)) {
11944888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org}
12044888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org
1219fa60daad4d5f54c0dbe3dbcc7608a8f6d721187reedSkFlattenable* SkColorFilterImageFilter::CreateProc(SkReadBuffer& buffer) {
1229fa60daad4d5f54c0dbe3dbcc7608a8f6d721187reed    SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 1);
123f0f6c405fe9646be224128fc1dd24c7fc15246efreed    SkAutoTUnref<SkColorFilter> cf(buffer.readColorFilter());
1245e5f948b6b363dbfc8c076d8ff0c6b8e9ea99958senorblanco    return Create(cf, common.getInput(0), &common.cropRect(), common.uniqueID());
1259fa60daad4d5f54c0dbe3dbcc7608a8f6d721187reed}
12644888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org
1278b0e8ac5f582de80356019406e2975079bf0829dcommit-bot@chromium.orgvoid SkColorFilterImageFilter::flatten(SkWriteBuffer& buffer) const {
12844888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org    this->INHERITED::flatten(buffer);
12944888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org    buffer.writeFlattenable(fColorFilter);
13044888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org}
13144888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org
13244888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.orgSkColorFilterImageFilter::~SkColorFilterImageFilter() {
1339bde31e95ddd82a4a923275bb01265740407dab1sugoi    fColorFilter->unref();
13444888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org}
13544888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org
13644888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.orgbool SkColorFilterImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& source,
1374cb543d6057b692e1099e9f115155f0bf323a0c8senorblanco@chromium.org                                             const Context& ctx,
13844888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org                                             SkBitmap* result,
139ae761f7545d8ebf181d220169afac2056b057b8ccommit-bot@chromium.org                                             SkIPoint* offset) const {
14068400767be5f72e4b9750ccc8bcf0078d42869a7senorblanco@chromium.org    SkBitmap src = source;
1416776b82d466fa93ccffd251fdf556fe058395444senorblanco@chromium.org    SkIPoint srcOffset = SkIPoint::Make(0, 0);
1424cb543d6057b692e1099e9f115155f0bf323a0c8senorblanco@chromium.org    if (getInput(0) && !getInput(0)->filterImage(proxy, source, ctx, &src, &srcOffset)) {
14368400767be5f72e4b9750ccc8bcf0078d42869a7senorblanco@chromium.org        return false;
14468400767be5f72e4b9750ccc8bcf0078d42869a7senorblanco@chromium.org    }
14568400767be5f72e4b9750ccc8bcf0078d42869a7senorblanco@chromium.org
146194d775edcf5fa6e82098a97ad53018d70db1155senorblanco@chromium.org    SkIRect bounds;
147118252962f89a80db661a0544f1bd61cbaab6321senorblanco@chromium.org    if (!this->applyCropRect(ctx, src, srcOffset, &bounds)) {
148194d775edcf5fa6e82098a97ad53018d70db1155senorblanco@chromium.org        return false;
149194d775edcf5fa6e82098a97ad53018d70db1155senorblanco@chromium.org    }
150194d775edcf5fa6e82098a97ad53018d70db1155senorblanco@chromium.org
1511f2f338e23789f3eef168dcbd8171a28820ba6c1robertphillips@google.com    SkAutoTUnref<SkBaseDevice> device(proxy->createDevice(bounds.width(), bounds.height()));
152cd3b15ca6364a04b0eeeb4f89c7daa8aefe854c8commit-bot@chromium.org    if (NULL == device.get()) {
153cd3b15ca6364a04b0eeeb4f89c7daa8aefe854c8commit-bot@chromium.org        return false;
154cd3b15ca6364a04b0eeeb4f89c7daa8aefe854c8commit-bot@chromium.org    }
15544888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org    SkCanvas canvas(device.get());
15644888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org    SkPaint paint;
15744888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org
15844888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org    paint.setXfermodeMode(SkXfermode::kSrc_Mode);
159cd9f55989e680b7f52fa21766dde0ac67ac9911fsenorblanco@chromium.org    paint.setColorFilter(fColorFilter);
1606776b82d466fa93ccffd251fdf556fe058395444senorblanco@chromium.org    canvas.drawSprite(src, srcOffset.fX - bounds.fLeft, srcOffset.fY - bounds.fTop, &paint);
16144888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org
16244888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org    *result = device.get()->accessBitmap(false);
1636776b82d466fa93ccffd251fdf556fe058395444senorblanco@chromium.org    offset->fX = bounds.fLeft;
1646776b82d466fa93ccffd251fdf556fe058395444senorblanco@chromium.org    offset->fY = bounds.fTop;
16544888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org    return true;
16644888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org}
1678d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org
168a1c511b8704c6c266b90860a4c68f30ca7514f9bsugoi@google.combool SkColorFilterImageFilter::asColorFilter(SkColorFilter** filter) const {
169b295fb6ff3222453912dfcb7a1ea5184d40014b5senorblanco@chromium.org    if (!cropRectIsSet()) {
170194d775edcf5fa6e82098a97ad53018d70db1155senorblanco@chromium.org        if (filter) {
171194d775edcf5fa6e82098a97ad53018d70db1155senorblanco@chromium.org            *filter = fColorFilter;
172194d775edcf5fa6e82098a97ad53018d70db1155senorblanco@chromium.org            fColorFilter->ref();
173194d775edcf5fa6e82098a97ad53018d70db1155senorblanco@chromium.org        }
174194d775edcf5fa6e82098a97ad53018d70db1155senorblanco@chromium.org        return true;
175a1c511b8704c6c266b90860a4c68f30ca7514f9bsugoi@google.com    }
176194d775edcf5fa6e82098a97ad53018d70db1155senorblanco@chromium.org    return false;
1778d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org}
178f3f5bad7ded35265c0b5d042cc4174386b197a33robertphillips
179f3f5bad7ded35265c0b5d042cc4174386b197a33robertphillips#ifndef SK_IGNORE_TO_STRING
180f3f5bad7ded35265c0b5d042cc4174386b197a33robertphillipsvoid SkColorFilterImageFilter::toString(SkString* str) const {
181f3f5bad7ded35265c0b5d042cc4174386b197a33robertphillips    str->appendf("SkColorFilterImageFilter: (");
182f3f5bad7ded35265c0b5d042cc4174386b197a33robertphillips
18333cca886b757ed575136a628b30e845096ef94a3robertphillips    str->appendf("input: (");
18433cca886b757ed575136a628b30e845096ef94a3robertphillips
18533cca886b757ed575136a628b30e845096ef94a3robertphillips    if (this->getInput(0)) {
18633cca886b757ed575136a628b30e845096ef94a3robertphillips        this->getInput(0)->toString(str);
18733cca886b757ed575136a628b30e845096ef94a3robertphillips    }
18833cca886b757ed575136a628b30e845096ef94a3robertphillips
18933cca886b757ed575136a628b30e845096ef94a3robertphillips    str->appendf(") color filter: ");
190f3f5bad7ded35265c0b5d042cc4174386b197a33robertphillips    fColorFilter->toString(str);
191f3f5bad7ded35265c0b5d042cc4174386b197a33robertphillips
192f3f5bad7ded35265c0b5d042cc4174386b197a33robertphillips    str->append(")");
193f3f5bad7ded35265c0b5d042cc4174386b197a33robertphillips}
194f3f5bad7ded35265c0b5d042cc4174386b197a33robertphillips#endif
195