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"
158b0e8ac5f582de80356019406e2975079bf0829dcommit-bot@chromium.org#include "SkWriteBuffer.h"
1644888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org
178d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.orgnamespace {
188d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org
198d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.orgvoid mult_color_matrix(SkScalar a[20], SkScalar b[20], SkScalar out[20]) {
208d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org    for (int j = 0; j < 4; ++j) {
218d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org        for (int i = 0; i < 5; ++i) {
228d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org            out[i+j*5] = 4 == i ? a[4+j*5] : 0;
238d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org            for (int k = 0; k < 4; ++k)
248d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org                out[i+j*5] += SkScalarMul(a[k+j*5], b[i+k*5]);
258d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org        }
268d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org    }
278d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org}
288d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org
298d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org// To detect if we need to apply clamping after applying a matrix, we check if
308d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org// any output component might go outside of [0, 255] for any combination of
318d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org// input components in [0..255].
328d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org// Each output component is an affine transformation of the input component, so
338d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org// the minimum and maximum values are for any combination of minimum or maximum
348d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org// values of input components (i.e. 0 or 255).
358d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org// E.g. if R' = x*R + y*G + z*B + w*A + t
368d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org// Then the maximum value will be for R=255 if x>0 or R=0 if x<0, and the
378d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org// minimum value will be for R=0 if x>0 or R=255 if x<0.
388d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org// Same goes for all components.
398d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.orgbool component_needs_clamping(SkScalar row[5]) {
408d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org    SkScalar maxValue = row[4] / 255;
418d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org    SkScalar minValue = row[4] / 255;
428d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org    for (int i = 0; i < 4; ++i) {
438d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org        if (row[i] > 0)
448d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org            maxValue += row[i];
458d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org        else
468d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org            minValue += row[i];
478d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org    }
488d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org    return (maxValue > 1) || (minValue < 0);
498d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org}
508d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org
518d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.orgbool matrix_needs_clamping(SkScalar matrix[20]) {
528d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org    return component_needs_clamping(matrix)
538d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org        || component_needs_clamping(matrix+5)
548d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org        || component_needs_clamping(matrix+10)
558d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org        || component_needs_clamping(matrix+15);
568d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org}
578d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org
588d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org};
598d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org
60cd9f55989e680b7f52fa21766dde0ac67ac9911fsenorblanco@chromium.orgSkColorFilterImageFilter* SkColorFilterImageFilter::Create(SkColorFilter* cf,
61b295fb6ff3222453912dfcb7a1ea5184d40014b5senorblanco@chromium.org        SkImageFilter* input, const CropRect* cropRect) {
62cd9f55989e680b7f52fa21766dde0ac67ac9911fsenorblanco@chromium.org    SkASSERT(cf);
63cd9f55989e680b7f52fa21766dde0ac67ac9911fsenorblanco@chromium.org    SkScalar colorMatrix[20], inputMatrix[20];
64cd9f55989e680b7f52fa21766dde0ac67ac9911fsenorblanco@chromium.org    SkColorFilter* inputColorFilter;
65cd9f55989e680b7f52fa21766dde0ac67ac9911fsenorblanco@chromium.org    if (input && cf->asColorMatrix(colorMatrix)
66a1c511b8704c6c266b90860a4c68f30ca7514f9bsugoi@google.com              && input->asColorFilter(&inputColorFilter)
67a1c511b8704c6c266b90860a4c68f30ca7514f9bsugoi@google.com              && (NULL != inputColorFilter)) {
68a1c511b8704c6c266b90860a4c68f30ca7514f9bsugoi@google.com        SkAutoUnref autoUnref(inputColorFilter);
69a1c511b8704c6c266b90860a4c68f30ca7514f9bsugoi@google.com        if (inputColorFilter->asColorMatrix(inputMatrix) && !matrix_needs_clamping(inputMatrix)) {
70a1c511b8704c6c266b90860a4c68f30ca7514f9bsugoi@google.com            SkScalar combinedMatrix[20];
719ed58377b45739aa8f9dc1b02049ab29c137a838Derek Sollenberger            mult_color_matrix(colorMatrix, inputMatrix, combinedMatrix);
72727a352f7412753d2a3e4d30eab6500a1a4de135commit-bot@chromium.org            SkAutoTUnref<SkColorFilter> newCF(SkColorMatrixFilter::Create(combinedMatrix));
73194d775edcf5fa6e82098a97ad53018d70db1155senorblanco@chromium.org            return SkNEW_ARGS(SkColorFilterImageFilter, (newCF, input->getInput(0), cropRect));
74a1c511b8704c6c266b90860a4c68f30ca7514f9bsugoi@google.com        }
75cd9f55989e680b7f52fa21766dde0ac67ac9911fsenorblanco@chromium.org    }
76194d775edcf5fa6e82098a97ad53018d70db1155senorblanco@chromium.org    return SkNEW_ARGS(SkColorFilterImageFilter, (cf, input, cropRect));
77cd9f55989e680b7f52fa21766dde0ac67ac9911fsenorblanco@chromium.org}
78cd9f55989e680b7f52fa21766dde0ac67ac9911fsenorblanco@chromium.org
79194d775edcf5fa6e82098a97ad53018d70db1155senorblanco@chromium.orgSkColorFilterImageFilter::SkColorFilterImageFilter(SkColorFilter* cf,
80b295fb6ff3222453912dfcb7a1ea5184d40014b5senorblanco@chromium.org        SkImageFilter* input, const CropRect* cropRect)
81194d775edcf5fa6e82098a97ad53018d70db1155senorblanco@chromium.org    : INHERITED(input, cropRect), fColorFilter(cf) {
828d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org    SkASSERT(cf);
8344888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org    SkSafeRef(cf);
8444888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org}
8544888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org
868b0e8ac5f582de80356019406e2975079bf0829dcommit-bot@chromium.orgSkColorFilterImageFilter::SkColorFilterImageFilter(SkReadBuffer& buffer)
87ce33d60187718e7bb01944ee130c9f5d9fb335eccommit-bot@chromium.org  : INHERITED(1, buffer) {
88353482251e61971a8cf3a60bbb6910f482be634freed@google.com    fColorFilter = buffer.readColorFilter();
8944888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org}
9044888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org
918b0e8ac5f582de80356019406e2975079bf0829dcommit-bot@chromium.orgvoid SkColorFilterImageFilter::flatten(SkWriteBuffer& buffer) const {
9244888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org    this->INHERITED::flatten(buffer);
93fbfcd5602128ec010c82cb733c9cdc0a3254f9f3rmistry@google.com
9444888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org    buffer.writeFlattenable(fColorFilter);
9544888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org}
9644888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org
9744888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.orgSkColorFilterImageFilter::~SkColorFilterImageFilter() {
9844888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org    SkSafeUnref(fColorFilter);
9944888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org}
10044888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org
10144888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.orgbool SkColorFilterImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& source,
1024cb543d6057b692e1099e9f115155f0bf323a0c8senorblanco@chromium.org                                             const Context& ctx,
10344888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org                                             SkBitmap* result,
104ae761f7545d8ebf181d220169afac2056b057b8ccommit-bot@chromium.org                                             SkIPoint* offset) const {
10568400767be5f72e4b9750ccc8bcf0078d42869a7senorblanco@chromium.org    SkBitmap src = source;
1066776b82d466fa93ccffd251fdf556fe058395444senorblanco@chromium.org    SkIPoint srcOffset = SkIPoint::Make(0, 0);
1074cb543d6057b692e1099e9f115155f0bf323a0c8senorblanco@chromium.org    if (getInput(0) && !getInput(0)->filterImage(proxy, source, ctx, &src, &srcOffset)) {
10868400767be5f72e4b9750ccc8bcf0078d42869a7senorblanco@chromium.org        return false;
10968400767be5f72e4b9750ccc8bcf0078d42869a7senorblanco@chromium.org    }
11068400767be5f72e4b9750ccc8bcf0078d42869a7senorblanco@chromium.org
111194d775edcf5fa6e82098a97ad53018d70db1155senorblanco@chromium.org    SkIRect bounds;
112118252962f89a80db661a0544f1bd61cbaab6321senorblanco@chromium.org    if (!this->applyCropRect(ctx, src, srcOffset, &bounds)) {
113194d775edcf5fa6e82098a97ad53018d70db1155senorblanco@chromium.org        return false;
114194d775edcf5fa6e82098a97ad53018d70db1155senorblanco@chromium.org    }
115194d775edcf5fa6e82098a97ad53018d70db1155senorblanco@chromium.org
1161f2f338e23789f3eef168dcbd8171a28820ba6c1robertphillips@google.com    SkAutoTUnref<SkBaseDevice> device(proxy->createDevice(bounds.width(), bounds.height()));
117cd3b15ca6364a04b0eeeb4f89c7daa8aefe854c8commit-bot@chromium.org    if (NULL == device.get()) {
118cd3b15ca6364a04b0eeeb4f89c7daa8aefe854c8commit-bot@chromium.org        return false;
119cd3b15ca6364a04b0eeeb4f89c7daa8aefe854c8commit-bot@chromium.org    }
12044888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org    SkCanvas canvas(device.get());
12144888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org    SkPaint paint;
12244888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org
12344888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org    paint.setXfermodeMode(SkXfermode::kSrc_Mode);
124cd9f55989e680b7f52fa21766dde0ac67ac9911fsenorblanco@chromium.org    paint.setColorFilter(fColorFilter);
1256776b82d466fa93ccffd251fdf556fe058395444senorblanco@chromium.org    canvas.drawSprite(src, srcOffset.fX - bounds.fLeft, srcOffset.fY - bounds.fTop, &paint);
12644888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org
12744888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org    *result = device.get()->accessBitmap(false);
1286776b82d466fa93ccffd251fdf556fe058395444senorblanco@chromium.org    offset->fX = bounds.fLeft;
1296776b82d466fa93ccffd251fdf556fe058395444senorblanco@chromium.org    offset->fY = bounds.fTop;
13044888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org    return true;
13144888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org}
1328d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org
133a1c511b8704c6c266b90860a4c68f30ca7514f9bsugoi@google.combool SkColorFilterImageFilter::asColorFilter(SkColorFilter** filter) const {
134b295fb6ff3222453912dfcb7a1ea5184d40014b5senorblanco@chromium.org    if (!cropRectIsSet()) {
135194d775edcf5fa6e82098a97ad53018d70db1155senorblanco@chromium.org        if (filter) {
136194d775edcf5fa6e82098a97ad53018d70db1155senorblanco@chromium.org            *filter = fColorFilter;
137194d775edcf5fa6e82098a97ad53018d70db1155senorblanco@chromium.org            fColorFilter->ref();
138194d775edcf5fa6e82098a97ad53018d70db1155senorblanco@chromium.org        }
139194d775edcf5fa6e82098a97ad53018d70db1155senorblanco@chromium.org        return true;
140a1c511b8704c6c266b90860a4c68f30ca7514f9bsugoi@google.com    }
141194d775edcf5fa6e82098a97ad53018d70db1155senorblanco@chromium.org    return false;
1428d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org}
143