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 10#include "SkCanvas.h" 11#include "SkColorFilter.h" 12#include "SkReadBuffer.h" 13#include "SkSpecialImage.h" 14#include "SkSpecialSurface.h" 15#include "SkWriteBuffer.h" 16 17sk_sp<SkImageFilter> SkColorFilterImageFilter::Make(sk_sp<SkColorFilter> cf, 18 sk_sp<SkImageFilter> input, 19 const CropRect* cropRect) { 20 if (!cf) { 21 return nullptr; 22 } 23 24 SkColorFilter* inputCF; 25 if (input && input->isColorFilterNode(&inputCF)) { 26 // This is an optimization, as it collapses the hierarchy by just combining the two 27 // colorfilters into a single one, which the new imagefilter will wrap. 28 sk_sp<SkColorFilter> newCF(SkColorFilter::MakeComposeFilter(cf,// can't move bc of fallthru 29 sk_sp<SkColorFilter>(inputCF))); 30 if (newCF) { 31 return sk_sp<SkImageFilter>(new SkColorFilterImageFilter(std::move(newCF), 32 sk_ref_sp(input->getInput(0)), 33 cropRect)); 34 } 35 } 36 37 return sk_sp<SkImageFilter>(new SkColorFilterImageFilter(std::move(cf), 38 std::move(input), 39 cropRect)); 40} 41 42SkColorFilterImageFilter::SkColorFilterImageFilter(sk_sp<SkColorFilter> cf, 43 sk_sp<SkImageFilter> input, 44 const CropRect* cropRect) 45 : INHERITED(&input, 1, cropRect) 46 , fColorFilter(std::move(cf)) { 47} 48 49sk_sp<SkFlattenable> SkColorFilterImageFilter::CreateProc(SkReadBuffer& buffer) { 50 SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 1); 51 sk_sp<SkColorFilter> cf(buffer.readColorFilter()); 52 return Make(std::move(cf), common.getInput(0), &common.cropRect()); 53} 54 55void SkColorFilterImageFilter::flatten(SkWriteBuffer& buffer) const { 56 this->INHERITED::flatten(buffer); 57 buffer.writeFlattenable(fColorFilter.get()); 58} 59 60sk_sp<SkSpecialImage> SkColorFilterImageFilter::onFilterImage(SkSpecialImage* source, 61 const Context& ctx, 62 SkIPoint* offset) const { 63 SkIPoint inputOffset = SkIPoint::Make(0, 0); 64 sk_sp<SkSpecialImage> input(this->filterInput(0, source, ctx, &inputOffset)); 65 66 SkIRect inputBounds; 67 if (fColorFilter->affectsTransparentBlack()) { 68 // If the color filter affects transparent black, the bounds are the entire clip. 69 inputBounds = ctx.clipBounds(); 70 } else if (!input) { 71 return nullptr; 72 } else { 73 inputBounds = SkIRect::MakeXYWH(inputOffset.x(), inputOffset.y(), 74 input->width(), input->height()); 75 } 76 77 SkIRect bounds; 78 if (!this->applyCropRect(ctx, inputBounds, &bounds)) { 79 return nullptr; 80 } 81 82 sk_sp<SkSpecialSurface> surf(source->makeSurface(ctx.outputProperties(), bounds.size())); 83 if (!surf) { 84 return nullptr; 85 } 86 87 SkCanvas* canvas = surf->getCanvas(); 88 SkASSERT(canvas); 89 90 SkPaint paint; 91 92 paint.setBlendMode(SkBlendMode::kSrc); 93 paint.setColorFilter(fColorFilter); 94 95 // TODO: it may not be necessary to clear or drawPaint inside the input bounds 96 // (see skbug.com/5075) 97 if (fColorFilter->affectsTransparentBlack()) { 98 // The subsequent input->draw() call may not fill the entire canvas. For filters which 99 // affect transparent black, ensure that the filter is applied everywhere. 100 paint.setColor(SK_ColorTRANSPARENT); 101 canvas->drawPaint(paint); 102 paint.setColor(SK_ColorBLACK); 103 } else { 104 canvas->clear(0x0); 105 } 106 107 if (input) { 108 input->draw(canvas, 109 SkIntToScalar(inputOffset.fX - bounds.fLeft), 110 SkIntToScalar(inputOffset.fY - bounds.fTop), 111 &paint); 112 } 113 114 offset->fX = bounds.fLeft; 115 offset->fY = bounds.fTop; 116 return surf->makeImageSnapshot(); 117} 118 119bool SkColorFilterImageFilter::onIsColorFilterNode(SkColorFilter** filter) const { 120 SkASSERT(1 == this->countInputs()); 121 if (!this->cropRectIsSet()) { 122 if (filter) { 123 *filter = SkRef(fColorFilter.get()); 124 } 125 return true; 126 } 127 return false; 128} 129 130bool SkColorFilterImageFilter::affectsTransparentBlack() const { 131 return fColorFilter->affectsTransparentBlack(); 132} 133 134#ifndef SK_IGNORE_TO_STRING 135void SkColorFilterImageFilter::toString(SkString* str) const { 136 str->appendf("SkColorFilterImageFilter: ("); 137 138 str->appendf("input: ("); 139 140 if (this->getInput(0)) { 141 this->getInput(0)->toString(str); 142 } 143 144 str->appendf(") color filter: "); 145 fColorFilter->toString(str); 146 147 str->append(")"); 148} 149#endif 150