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