1/*
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"
11#include "SkColorMatrixFilter.h"
12#include "SkDevice.h"
13#include "SkColorFilter.h"
14#include "SkReadBuffer.h"
15#include "SkWriteBuffer.h"
16
17namespace {
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
60SkColorFilterImageFilter* SkColorFilterImageFilter::Create(SkColorFilter* cf,
61        SkImageFilter* input, const CropRect* cropRect, uint32_t uniqueID) {
62    SkASSERT(cf);
63    SkScalar colorMatrix[20], inputMatrix[20];
64    SkColorFilter* inputColorFilter;
65    if (input && cf->asColorMatrix(colorMatrix)
66              && input->asColorFilter(&inputColorFilter)
67              && (inputColorFilter)) {
68        SkAutoUnref autoUnref(inputColorFilter);
69        if (inputColorFilter->asColorMatrix(inputMatrix) && !matrix_needs_clamping(inputMatrix)) {
70            SkScalar combinedMatrix[20];
71            mult_color_matrix(colorMatrix, inputMatrix, combinedMatrix);
72            SkAutoTUnref<SkColorFilter> newCF(SkColorMatrixFilter::Create(combinedMatrix));
73            return SkNEW_ARGS(SkColorFilterImageFilter, (newCF, input->getInput(0), cropRect, 0));
74        }
75    }
76    return SkNEW_ARGS(SkColorFilterImageFilter, (cf, input, cropRect, uniqueID));
77}
78
79SkColorFilterImageFilter::SkColorFilterImageFilter(SkColorFilter* cf,
80        SkImageFilter* input, const CropRect* cropRect, uint32_t uniqueID)
81    : INHERITED(1, &input, cropRect, uniqueID), fColorFilter(cf) {
82    SkASSERT(cf);
83    SkSafeRef(cf);
84}
85
86#ifdef SK_SUPPORT_LEGACY_DEEPFLATTENING
87SkColorFilterImageFilter::SkColorFilterImageFilter(SkReadBuffer& buffer)
88  : INHERITED(1, buffer) {
89    fColorFilter = buffer.readColorFilter();
90}
91#endif
92
93SkFlattenable* SkColorFilterImageFilter::CreateProc(SkReadBuffer& buffer) {
94    SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 1);
95    SkAutoTUnref<SkColorFilter> cf(buffer.readColorFilter());
96    return Create(cf, common.getInput(0), &common.cropRect(), common.uniqueID());
97}
98
99void SkColorFilterImageFilter::flatten(SkWriteBuffer& buffer) const {
100    this->INHERITED::flatten(buffer);
101    buffer.writeFlattenable(fColorFilter);
102}
103
104SkColorFilterImageFilter::~SkColorFilterImageFilter() {
105    SkSafeUnref(fColorFilter);
106}
107
108bool SkColorFilterImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& source,
109                                             const Context& ctx,
110                                             SkBitmap* result,
111                                             SkIPoint* offset) const {
112    SkBitmap src = source;
113    SkIPoint srcOffset = SkIPoint::Make(0, 0);
114    if (getInput(0) && !getInput(0)->filterImage(proxy, source, ctx, &src, &srcOffset)) {
115        return false;
116    }
117
118    SkIRect bounds;
119    if (!this->applyCropRect(ctx, src, srcOffset, &bounds)) {
120        return false;
121    }
122
123    SkAutoTUnref<SkBaseDevice> device(proxy->createDevice(bounds.width(), bounds.height()));
124    if (NULL == device.get()) {
125        return false;
126    }
127    SkCanvas canvas(device.get());
128    SkPaint paint;
129
130    paint.setXfermodeMode(SkXfermode::kSrc_Mode);
131    paint.setColorFilter(fColorFilter);
132    canvas.drawSprite(src, srcOffset.fX - bounds.fLeft, srcOffset.fY - bounds.fTop, &paint);
133
134    *result = device.get()->accessBitmap(false);
135    offset->fX = bounds.fLeft;
136    offset->fY = bounds.fTop;
137    return true;
138}
139
140bool SkColorFilterImageFilter::asColorFilter(SkColorFilter** filter) const {
141    if (!cropRectIsSet()) {
142        if (filter) {
143            *filter = fColorFilter;
144            fColorFilter->ref();
145        }
146        return true;
147    }
148    return false;
149}
150