blob: 8cdd546b993b772ca8be6569fc2077d7b45983db [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"
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +000014#include "SkReadBuffer.h"
15#include "SkWriteBuffer.h"
senorblanco@chromium.org44888c62012-08-20 19:23:24 +000016
senorblanco@chromium.org8d21f6c2012-10-12 19:14:06 +000017namespace {
18
19void mult_color_matrix(SkScalar a[20], SkScalar b[20], SkScalar out[20]) {
20 for (int j = 0; j < 4; ++j) {
21 for (int i = 0; i < 5; ++i) {
22 out[i+j*5] = 4 == i ? a[4+j*5] : 0;
23 for (int k = 0; k < 4; ++k)
24 out[i+j*5] += SkScalarMul(a[k+j*5], b[i+k*5]);
25 }
26 }
27}
28
29// To detect if we need to apply clamping after applying a matrix, we check if
30// any output component might go outside of [0, 255] for any combination of
31// input components in [0..255].
32// Each output component is an affine transformation of the input component, so
33// the minimum and maximum values are for any combination of minimum or maximum
34// values of input components (i.e. 0 or 255).
35// E.g. if R' = x*R + y*G + z*B + w*A + t
36// Then the maximum value will be for R=255 if x>0 or R=0 if x<0, and the
37// minimum value will be for R=0 if x>0 or R=255 if x<0.
38// Same goes for all components.
39bool component_needs_clamping(SkScalar row[5]) {
40 SkScalar maxValue = row[4] / 255;
41 SkScalar minValue = row[4] / 255;
42 for (int i = 0; i < 4; ++i) {
43 if (row[i] > 0)
44 maxValue += row[i];
45 else
46 minValue += row[i];
47 }
48 return (maxValue > 1) || (minValue < 0);
49}
50
51bool matrix_needs_clamping(SkScalar matrix[20]) {
52 return component_needs_clamping(matrix)
53 || component_needs_clamping(matrix+5)
54 || component_needs_clamping(matrix+10)
55 || component_needs_clamping(matrix+15);
56}
57
58};
59
senorblanco@chromium.orgcd9f5592012-10-26 19:37:00 +000060SkColorFilterImageFilter* SkColorFilterImageFilter::Create(SkColorFilter* cf,
senorblanco@chromium.orgb295fb62013-10-10 13:51:19 +000061 SkImageFilter* input, const CropRect* cropRect) {
senorblanco@chromium.orgcd9f5592012-10-26 19:37:00 +000062 SkASSERT(cf);
63 SkScalar colorMatrix[20], inputMatrix[20];
64 SkColorFilter* inputColorFilter;
65 if (input && cf->asColorMatrix(colorMatrix)
sugoi@google.coma1c511b2013-02-21 15:02:28 +000066 && input->asColorFilter(&inputColorFilter)
67 && (NULL != inputColorFilter)) {
68 SkAutoUnref autoUnref(inputColorFilter);
69 if (inputColorFilter->asColorMatrix(inputMatrix) && !matrix_needs_clamping(inputMatrix)) {
70 SkScalar combinedMatrix[20];
71 mult_color_matrix(inputMatrix, colorMatrix, combinedMatrix);
commit-bot@chromium.org727a3522014-02-21 18:46:30 +000072 SkAutoTUnref<SkColorFilter> newCF(SkColorMatrixFilter::Create(combinedMatrix));
senorblanco@chromium.org194d7752013-07-24 22:19:24 +000073 return SkNEW_ARGS(SkColorFilterImageFilter, (newCF, input->getInput(0), cropRect));
sugoi@google.coma1c511b2013-02-21 15:02:28 +000074 }
senorblanco@chromium.orgcd9f5592012-10-26 19:37:00 +000075 }
senorblanco@chromium.org194d7752013-07-24 22:19:24 +000076 return SkNEW_ARGS(SkColorFilterImageFilter, (cf, input, cropRect));
senorblanco@chromium.orgcd9f5592012-10-26 19:37:00 +000077}
78
senorblanco@chromium.org194d7752013-07-24 22:19:24 +000079SkColorFilterImageFilter::SkColorFilterImageFilter(SkColorFilter* cf,
senorblanco@chromium.orgb295fb62013-10-10 13:51:19 +000080 SkImageFilter* input, const CropRect* cropRect)
senorblanco@chromium.org194d7752013-07-24 22:19:24 +000081 : INHERITED(input, cropRect), fColorFilter(cf) {
senorblanco@chromium.org8d21f6c2012-10-12 19:14:06 +000082 SkASSERT(cf);
senorblanco@chromium.org44888c62012-08-20 19:23:24 +000083 SkSafeRef(cf);
84}
85
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +000086SkColorFilterImageFilter::SkColorFilterImageFilter(SkReadBuffer& buffer)
commit-bot@chromium.orgce33d602013-11-25 21:46:31 +000087 : INHERITED(1, buffer) {
reed@google.com35348222013-10-16 13:05:06 +000088 fColorFilter = buffer.readColorFilter();
senorblanco@chromium.org44888c62012-08-20 19:23:24 +000089}
90
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +000091void SkColorFilterImageFilter::flatten(SkWriteBuffer& buffer) const {
senorblanco@chromium.org44888c62012-08-20 19:23:24 +000092 this->INHERITED::flatten(buffer);
rmistry@google.comfbfcd562012-08-23 18:09:54 +000093
senorblanco@chromium.org44888c62012-08-20 19:23:24 +000094 buffer.writeFlattenable(fColorFilter);
95}
96
97SkColorFilterImageFilter::~SkColorFilterImageFilter() {
98 SkSafeUnref(fColorFilter);
99}
100
101bool SkColorFilterImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& source,
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +0000102 const Context& ctx,
senorblanco@chromium.org44888c62012-08-20 19:23:24 +0000103 SkBitmap* result,
commit-bot@chromium.orgae761f72014-02-05 22:32:02 +0000104 SkIPoint* offset) const {
senorblanco@chromium.org68400762013-05-24 15:04:07 +0000105 SkBitmap src = source;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +0000106 SkIPoint srcOffset = SkIPoint::Make(0, 0);
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +0000107 if (getInput(0) && !getInput(0)->filterImage(proxy, source, ctx, &src, &srcOffset)) {
senorblanco@chromium.org68400762013-05-24 15:04:07 +0000108 return false;
109 }
110
senorblanco@chromium.org194d7752013-07-24 22:19:24 +0000111 SkIRect bounds;
senorblanco@chromium.org11825292014-03-14 17:44:41 +0000112 if (!this->applyCropRect(ctx, src, srcOffset, &bounds)) {
senorblanco@chromium.org194d7752013-07-24 22:19:24 +0000113 return false;
114 }
115
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000116 SkAutoTUnref<SkBaseDevice> device(proxy->createDevice(bounds.width(), bounds.height()));
commit-bot@chromium.orgcd3b15c2013-12-04 17:06:49 +0000117 if (NULL == device.get()) {
118 return false;
119 }
senorblanco@chromium.org44888c62012-08-20 19:23:24 +0000120 SkCanvas canvas(device.get());
121 SkPaint paint;
122
123 paint.setXfermodeMode(SkXfermode::kSrc_Mode);
senorblanco@chromium.orgcd9f5592012-10-26 19:37:00 +0000124 paint.setColorFilter(fColorFilter);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +0000125 canvas.drawSprite(src, srcOffset.fX - bounds.fLeft, srcOffset.fY - bounds.fTop, &paint);
senorblanco@chromium.org44888c62012-08-20 19:23:24 +0000126
127 *result = device.get()->accessBitmap(false);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +0000128 offset->fX = bounds.fLeft;
129 offset->fY = bounds.fTop;
senorblanco@chromium.org44888c62012-08-20 19:23:24 +0000130 return true;
131}
senorblanco@chromium.org8d21f6c2012-10-12 19:14:06 +0000132
sugoi@google.coma1c511b2013-02-21 15:02:28 +0000133bool SkColorFilterImageFilter::asColorFilter(SkColorFilter** filter) const {
senorblanco@chromium.orgb295fb62013-10-10 13:51:19 +0000134 if (!cropRectIsSet()) {
senorblanco@chromium.org194d7752013-07-24 22:19:24 +0000135 if (filter) {
136 *filter = fColorFilter;
137 fColorFilter->ref();
138 }
139 return true;
sugoi@google.coma1c511b2013-02-21 15:02:28 +0000140 }
senorblanco@chromium.org194d7752013-07-24 22:19:24 +0000141 return false;
senorblanco@chromium.org8d21f6c2012-10-12 19:14:06 +0000142}