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