SkColorFilterImageFilter.cpp revision 8d21f6c7a9d0cf4f87d77c235c6da7203620c7e5
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"
1444888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org#include "SkFlattenableBuffers.h"
1544888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org
168d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.orgnamespace {
178d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org
188d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.orgvoid mult_color_matrix(SkScalar a[20], SkScalar b[20], SkScalar out[20]) {
198d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org    for (int j = 0; j < 4; ++j) {
208d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org        for (int i = 0; i < 5; ++i) {
218d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org            out[i+j*5] = 4 == i ? a[4+j*5] : 0;
228d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org            for (int k = 0; k < 4; ++k)
238d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org                out[i+j*5] += SkScalarMul(a[k+j*5], b[i+k*5]);
248d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org        }
258d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org    }
268d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org}
278d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org
288d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org// To detect if we need to apply clamping after applying a matrix, we check if
298d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org// any output component might go outside of [0, 255] for any combination of
308d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org// input components in [0..255].
318d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org// Each output component is an affine transformation of the input component, so
328d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org// the minimum and maximum values are for any combination of minimum or maximum
338d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org// values of input components (i.e. 0 or 255).
348d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org// E.g. if R' = x*R + y*G + z*B + w*A + t
358d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org// Then the maximum value will be for R=255 if x>0 or R=0 if x<0, and the
368d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org// minimum value will be for R=0 if x>0 or R=255 if x<0.
378d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org// Same goes for all components.
388d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.orgbool component_needs_clamping(SkScalar row[5]) {
398d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org    SkScalar maxValue = row[4] / 255;
408d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org    SkScalar minValue = row[4] / 255;
418d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org    for (int i = 0; i < 4; ++i) {
428d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org        if (row[i] > 0)
438d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org            maxValue += row[i];
448d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org        else
458d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org            minValue += row[i];
468d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org    }
478d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org    return (maxValue > 1) || (minValue < 0);
488d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org}
498d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org
508d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.orgbool matrix_needs_clamping(SkScalar matrix[20]) {
518d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org    return component_needs_clamping(matrix)
528d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org        || component_needs_clamping(matrix+5)
538d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org        || component_needs_clamping(matrix+10)
548d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org        || component_needs_clamping(matrix+15);
558d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org}
568d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org
578d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org};
588d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org
5944888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.orgSkColorFilterImageFilter::SkColorFilterImageFilter(SkColorFilter* cf, SkImageFilter* input) : INHERITED(input), fColorFilter(cf) {
608d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org    SkASSERT(cf);
6144888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org    SkSafeRef(cf);
6244888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org}
6344888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org
6444888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.orgSkColorFilterImageFilter::SkColorFilterImageFilter(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {
6544888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org    fColorFilter = buffer.readFlattenableT<SkColorFilter>();
6644888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org}
6744888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org
6844888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.orgvoid SkColorFilterImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const {
6944888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org    this->INHERITED::flatten(buffer);
70fbfcd5602128ec010c82cb733c9cdc0a3254f9f3rmistry@google.com
7144888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org    buffer.writeFlattenable(fColorFilter);
7244888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org}
7344888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org
7444888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.orgSkColorFilterImageFilter::~SkColorFilterImageFilter() {
7544888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org    SkSafeUnref(fColorFilter);
7644888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org}
7744888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org
7844888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.orgbool SkColorFilterImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& source,
7944888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org                                             const SkMatrix& matrix,
8044888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org                                             SkBitmap* result,
8144888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org                                             SkIPoint* loc) {
828d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org    SkImageFilter* parent = getInput(0);
838d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org    SkScalar colorMatrix[20];
848d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org    SkBitmap src;
858d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org    SkColorFilter* cf;
868d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org    if (parent && fColorFilter->asColorMatrix(colorMatrix)) {
878d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org        SkColorFilter* parentColorFilter;
888d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org        SkScalar parentMatrix[20];
898d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org        while (parent && (parentColorFilter = parent->asColorFilter())
908d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org                      && parentColorFilter->asColorMatrix(parentMatrix)
918d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org                      && !matrix_needs_clamping(parentMatrix)) {
928d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org            SkScalar combinedMatrix[20];
938d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org            mult_color_matrix(parentMatrix, colorMatrix, combinedMatrix);
948d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org            memcpy(colorMatrix, combinedMatrix, 20 * sizeof(SkScalar));
958d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org            parent = parent->getInput(0);
968d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org        }
978d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org        if (!parent || !parent->filterImage(proxy, source, matrix, &src, loc)) {
988d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org            src = source;
998d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org        }
1008d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org        cf = SkNEW_ARGS(SkColorMatrixFilter, (colorMatrix));
1018d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org    } else {
1028d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org        src = this->getInputResult(proxy, source, matrix, loc);
1038d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org        cf = fColorFilter;
1048d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org        cf->ref();
10544888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org    }
10644888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org
10744888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org    SkAutoTUnref<SkDevice> device(proxy->createDevice(src.width(), src.height()));
10844888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org    SkCanvas canvas(device.get());
10944888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org    SkPaint paint;
11044888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org
11144888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org    paint.setXfermodeMode(SkXfermode::kSrc_Mode);
1128d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org    paint.setColorFilter(cf)->unref();
11344888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org    canvas.drawSprite(src, 0, 0, &paint);
11444888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org
11544888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org    *result = device.get()->accessBitmap(false);
11644888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org    return true;
11744888c66d4bf03da58eb9fbd3db92eb477141aabsenorblanco@chromium.org}
1188d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org
1198d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.orgSkColorFilter* SkColorFilterImageFilter::asColorFilter() const {
1208d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org    return fColorFilter;
1218d21f6c7a9d0cf4f87d77c235c6da7203620c7e5senorblanco@chromium.org}
122