blob: 9c2c54eb56a72a4a9fcea24e461e6d4f95103e63 [file] [log] [blame]
senorblanco@chromium.org44888c62012-08-20 19:23:24 +00001/*
2 * Copyright 2012 The Android Open Source Project
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "SkColorFilterImageFilter.h"
9#include "SkBitmap.h"
10#include "SkCanvas.h"
senorblanco@chromium.org8d21f6c2012-10-12 19:14:06 +000011#include "SkColorMatrixFilter.h"
senorblanco@chromium.org44888c62012-08-20 19:23:24 +000012#include "SkDevice.h"
13#include "SkColorFilter.h"
14#include "SkFlattenableBuffers.h"
15
senorblanco@chromium.org8d21f6c2012-10-12 19:14:06 +000016namespace {
17
18void mult_color_matrix(SkScalar a[20], SkScalar b[20], SkScalar out[20]) {
19 for (int j = 0; j < 4; ++j) {
20 for (int i = 0; i < 5; ++i) {
21 out[i+j*5] = 4 == i ? a[4+j*5] : 0;
22 for (int k = 0; k < 4; ++k)
23 out[i+j*5] += SkScalarMul(a[k+j*5], b[i+k*5]);
24 }
25 }
26}
27
28// To detect if we need to apply clamping after applying a matrix, we check if
29// any output component might go outside of [0, 255] for any combination of
30// input components in [0..255].
31// Each output component is an affine transformation of the input component, so
32// the minimum and maximum values are for any combination of minimum or maximum
33// values of input components (i.e. 0 or 255).
34// E.g. if R' = x*R + y*G + z*B + w*A + t
35// Then the maximum value will be for R=255 if x>0 or R=0 if x<0, and the
36// minimum value will be for R=0 if x>0 or R=255 if x<0.
37// Same goes for all components.
38bool component_needs_clamping(SkScalar row[5]) {
39 SkScalar maxValue = row[4] / 255;
40 SkScalar minValue = row[4] / 255;
41 for (int i = 0; i < 4; ++i) {
42 if (row[i] > 0)
43 maxValue += row[i];
44 else
45 minValue += row[i];
46 }
47 return (maxValue > 1) || (minValue < 0);
48}
49
50bool matrix_needs_clamping(SkScalar matrix[20]) {
51 return component_needs_clamping(matrix)
52 || component_needs_clamping(matrix+5)
53 || component_needs_clamping(matrix+10)
54 || component_needs_clamping(matrix+15);
55}
56
57};
58
senorblanco@chromium.orgcd9f5592012-10-26 19:37:00 +000059SkColorFilterImageFilter* SkColorFilterImageFilter::Create(SkColorFilter* cf,
senorblanco@chromium.org194d7752013-07-24 22:19:24 +000060 SkImageFilter* input, const SkIRect* cropRect) {
senorblanco@chromium.orgcd9f5592012-10-26 19:37:00 +000061 SkASSERT(cf);
62 SkScalar colorMatrix[20], inputMatrix[20];
63 SkColorFilter* inputColorFilter;
64 if (input && cf->asColorMatrix(colorMatrix)
sugoi@google.coma1c511b2013-02-21 15:02:28 +000065 && input->asColorFilter(&inputColorFilter)
66 && (NULL != inputColorFilter)) {
67 SkAutoUnref autoUnref(inputColorFilter);
68 if (inputColorFilter->asColorMatrix(inputMatrix) && !matrix_needs_clamping(inputMatrix)) {
69 SkScalar combinedMatrix[20];
70 mult_color_matrix(inputMatrix, colorMatrix, combinedMatrix);
71 SkAutoTUnref<SkColorFilter> newCF(SkNEW_ARGS(SkColorMatrixFilter, (combinedMatrix)));
senorblanco@chromium.org194d7752013-07-24 22:19:24 +000072 return SkNEW_ARGS(SkColorFilterImageFilter, (newCF, input->getInput(0), cropRect));
sugoi@google.coma1c511b2013-02-21 15:02:28 +000073 }
senorblanco@chromium.orgcd9f5592012-10-26 19:37:00 +000074 }
senorblanco@chromium.org194d7752013-07-24 22:19:24 +000075 return SkNEW_ARGS(SkColorFilterImageFilter, (cf, input, cropRect));
senorblanco@chromium.orgcd9f5592012-10-26 19:37:00 +000076}
77
senorblanco@chromium.org194d7752013-07-24 22:19:24 +000078SkColorFilterImageFilter::SkColorFilterImageFilter(SkColorFilter* cf,
79 SkImageFilter* input, const SkIRect* cropRect)
80 : INHERITED(input, cropRect), fColorFilter(cf) {
senorblanco@chromium.org8d21f6c2012-10-12 19:14:06 +000081 SkASSERT(cf);
senorblanco@chromium.org44888c62012-08-20 19:23:24 +000082 SkSafeRef(cf);
83}
84
85SkColorFilterImageFilter::SkColorFilterImageFilter(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {
86 fColorFilter = buffer.readFlattenableT<SkColorFilter>();
87}
88
89void SkColorFilterImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const {
90 this->INHERITED::flatten(buffer);
rmistry@google.comfbfcd562012-08-23 18:09:54 +000091
senorblanco@chromium.org44888c62012-08-20 19:23:24 +000092 buffer.writeFlattenable(fColorFilter);
93}
94
95SkColorFilterImageFilter::~SkColorFilterImageFilter() {
96 SkSafeUnref(fColorFilter);
97}
98
99bool SkColorFilterImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& source,
100 const SkMatrix& matrix,
101 SkBitmap* result,
102 SkIPoint* loc) {
senorblanco@chromium.org68400762013-05-24 15:04:07 +0000103 SkBitmap src = source;
104 if (getInput(0) && !getInput(0)->filterImage(proxy, source, matrix, &src, loc)) {
105 return false;
106 }
107
senorblanco@chromium.org194d7752013-07-24 22:19:24 +0000108 SkIRect bounds;
109 src.getBounds(&bounds);
110 if (!this->applyCropRect(&bounds)) {
111 return false;
112 }
113
robertphillips@google.com9b051a32013-08-20 20:06:40 +0000114 SkAutoTUnref<SkDevice> device(proxy->createDevice(bounds.width(), bounds.height()));
senorblanco@chromium.org44888c62012-08-20 19:23:24 +0000115 SkCanvas canvas(device.get());
116 SkPaint paint;
117
118 paint.setXfermodeMode(SkXfermode::kSrc_Mode);
senorblanco@chromium.orgcd9f5592012-10-26 19:37:00 +0000119 paint.setColorFilter(fColorFilter);
senorblanco@chromium.org194d7752013-07-24 22:19:24 +0000120 canvas.drawSprite(src, -bounds.fLeft, -bounds.fTop, &paint);
senorblanco@chromium.org44888c62012-08-20 19:23:24 +0000121
122 *result = device.get()->accessBitmap(false);
senorblanco@chromium.org194d7752013-07-24 22:19:24 +0000123 loc->fX += bounds.fLeft;
124 loc->fY += bounds.fTop;
senorblanco@chromium.org44888c62012-08-20 19:23:24 +0000125 return true;
126}
senorblanco@chromium.org8d21f6c2012-10-12 19:14:06 +0000127
sugoi@google.coma1c511b2013-02-21 15:02:28 +0000128bool SkColorFilterImageFilter::asColorFilter(SkColorFilter** filter) const {
senorblanco@chromium.org194d7752013-07-24 22:19:24 +0000129 if (cropRect().isLargest()) {
130 if (filter) {
131 *filter = fColorFilter;
132 fColorFilter->ref();
133 }
134 return true;
sugoi@google.coma1c511b2013-02-21 15:02:28 +0000135 }
senorblanco@chromium.org194d7752013-07-24 22:19:24 +0000136 return false;
senorblanco@chromium.org8d21f6c2012-10-12 19:14:06 +0000137}