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