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