| /* |
| * Copyright 2018 Google Inc. |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "src/core/SkCanvasPriv.h" |
| |
| #include "include/core/SkBlendMode.h" |
| #include "include/core/SkColor.h" |
| #include "include/core/SkColorFilter.h" |
| #include "include/core/SkImageFilter.h" |
| #include "include/core/SkMaskFilter.h" |
| #include "include/core/SkMatrix.h" |
| #include "include/core/SkRect.h" |
| #include "include/core/SkRefCnt.h" |
| #include "include/core/SkShader.h" |
| #include "include/private/base/SkAlign.h" |
| #include "include/private/base/SkAssert.h" |
| #include "include/private/base/SkTo.h" |
| #include "src/base/SkAutoMalloc.h" |
| #include "src/core/SkMaskFilterBase.h" |
| #include "src/core/SkReadBuffer.h" |
| #include "src/core/SkWriteBuffer.h" |
| #include "src/core/SkWriter32.h" |
| |
| #include <utility> |
| #include <cstdint> |
| |
| SkAutoCanvasMatrixPaint::SkAutoCanvasMatrixPaint(SkCanvas* canvas, const SkMatrix* matrix, |
| const SkPaint* paint, const SkRect& bounds) |
| : fCanvas(canvas) |
| , fSaveCount(canvas->getSaveCount()) { |
| if (paint) { |
| SkRect newBounds = bounds; |
| if (matrix) { |
| matrix->mapRect(&newBounds); |
| } |
| canvas->saveLayer(&newBounds, paint); |
| } else if (matrix) { |
| canvas->save(); |
| } |
| |
| if (matrix) { |
| canvas->concat(*matrix); |
| } |
| } |
| |
| SkAutoCanvasMatrixPaint::~SkAutoCanvasMatrixPaint() { |
| fCanvas->restoreToCount(fSaveCount); |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////////////////////////// |
| |
| bool SkCanvasPriv::ReadLattice(SkReadBuffer& buffer, SkCanvas::Lattice* lattice) { |
| lattice->fXCount = buffer.readInt(); |
| lattice->fXDivs = buffer.skipT<int32_t>(lattice->fXCount); |
| lattice->fYCount = buffer.readInt(); |
| lattice->fYDivs = buffer.skipT<int32_t>(lattice->fYCount); |
| int flagCount = buffer.readInt(); |
| lattice->fRectTypes = nullptr; |
| lattice->fColors = nullptr; |
| if (flagCount) { |
| lattice->fRectTypes = buffer.skipT<SkCanvas::Lattice::RectType>(flagCount); |
| lattice->fColors = buffer.skipT<SkColor>(flagCount); |
| } |
| lattice->fBounds = buffer.skipT<SkIRect>(); |
| return buffer.isValid(); |
| } |
| |
| size_t SkCanvasPriv::WriteLattice(void* buffer, const SkCanvas::Lattice& lattice) { |
| int flagCount = lattice.fRectTypes ? (lattice.fXCount + 1) * (lattice.fYCount + 1) : 0; |
| |
| const size_t size = (1 + lattice.fXCount + 1 + lattice.fYCount + 1) * sizeof(int32_t) + |
| SkAlign4(flagCount * sizeof(SkCanvas::Lattice::RectType)) + |
| SkAlign4(flagCount * sizeof(SkColor)) + |
| sizeof(SkIRect); |
| |
| if (buffer) { |
| SkWriter32 writer(buffer, size); |
| writer.write32(lattice.fXCount); |
| writer.write(lattice.fXDivs, lattice.fXCount * sizeof(uint32_t)); |
| writer.write32(lattice.fYCount); |
| writer.write(lattice.fYDivs, lattice.fYCount * sizeof(uint32_t)); |
| writer.write32(flagCount); |
| writer.writePad(lattice.fRectTypes, flagCount * sizeof(uint8_t)); |
| writer.write(lattice.fColors, flagCount * sizeof(SkColor)); |
| SkASSERT(lattice.fBounds); |
| writer.write(lattice.fBounds, sizeof(SkIRect)); |
| SkASSERT(writer.bytesWritten() == size); |
| } |
| return size; |
| } |
| |
| void SkCanvasPriv::WriteLattice(SkWriteBuffer& buffer, const SkCanvas::Lattice& lattice) { |
| const size_t size = WriteLattice(nullptr, lattice); |
| SkAutoSMalloc<1024> storage(size); |
| WriteLattice(storage.get(), lattice); |
| buffer.writePad32(storage.get(), size); |
| } |
| |
| void SkCanvasPriv::GetDstClipAndMatrixCounts(const SkCanvas::ImageSetEntry set[], int count, |
| int* totalDstClipCount, int* totalMatrixCount) { |
| int dstClipCount = 0; |
| int maxMatrixIndex = -1; |
| for (int i = 0; i < count; ++i) { |
| dstClipCount += 4 * set[i].fHasClip; |
| if (set[i].fMatrixIndex > maxMatrixIndex) { |
| maxMatrixIndex = set[i].fMatrixIndex; |
| } |
| } |
| |
| *totalDstClipCount = dstClipCount; |
| *totalMatrixCount = maxMatrixIndex + 1; |
| } |
| |
| // Attempts to convert an image filter to its equivalent color filter, which if possible, modifies |
| // the paint to compose the image filter's color filter into the paint's color filter slot. Returns |
| // true if the paint has been modified. Requires the paint to have an image filter. |
| bool SkCanvasPriv::ImageToColorFilter(SkPaint* paint) { |
| SkASSERT(SkToBool(paint) && paint->getImageFilter()); |
| |
| // An image filter logically runs after any mask filter and the src-over blending against the |
| // layer's transparent black initial content. Moving the image filter (as a color filter) into |
| // the color filter slot causes it to run before the mask filter or blending. |
| // |
| // Src-over blending against transparent black is a no-op, so skipping the layer and drawing the |
| // output of the color filter-image filter with the original blender is valid. |
| // |
| // If there's also a mask filter on the paint, it will operate on an alpha-only layer that's |
| // then shaded with the paint's effects. Moving the CF-IF into the paint's color filter slot |
| // will mean that the CF-IF operates on the output of the original CF *before* it's combined |
| // with the coverage value. Under normal circumstances the CF-IF evaluates the color after |
| // coverage has been multiplied into the alpha channel. |
| // |
| // Some color filters may behave the same, e.g. cf(color)*coverage == cf(color*coverage), but |
| // that's hard to detect so we disable the optimization when both image filters and mask filters |
| // are present. |
| if (paint->getMaskFilter()) { |
| return false; |
| } |
| |
| SkColorFilter* imgCFPtr; |
| if (!paint->getImageFilter()->asAColorFilter(&imgCFPtr)) { |
| return false; |
| } |
| sk_sp<SkColorFilter> imgCF(imgCFPtr); |
| |
| SkColorFilter* paintCF = paint->getColorFilter(); |
| if (paintCF) { |
| // The paint has both a colorfilter(paintCF) and an imagefilter-that-is-a-colorfilter(imgCF) |
| // and we need to combine them into a single colorfilter. |
| imgCF = imgCF->makeComposed(sk_ref_sp(paintCF)); |
| } |
| |
| paint->setColorFilter(std::move(imgCF)); |
| paint->setImageFilter(nullptr); |
| return true; |
| } |
| |
| AutoLayerForImageFilter::AutoLayerForImageFilter(SkCanvas* canvas, |
| const SkPaint& paint, |
| const SkRect* rawBounds, |
| bool skipMaskFilterLayer) |
| : fPaint(paint) |
| , fCanvas(canvas) |
| , fTempLayersForFilters(0) { |
| SkDEBUGCODE(fSaveCount = canvas->getSaveCount();) |
| |
| // Depending on the original paint, this will add 0, 1, or 2 layers that apply the |
| // filter effects to a temporary layer that rasterized the remaining effects. Image filters |
| // are applied to the result of any mask filter, so its layer is added first in the stack. |
| // |
| // If present on the original paint, the image filter layer's restore paint steals the blender |
| // and the image filter so that the draw's paint will never have an image filter. |
| if (fPaint.getImageFilter() && !SkCanvasPriv::ImageToColorFilter(&fPaint)) { |
| this->addImageFilterLayer(rawBounds); |
| } |
| |
| // If present on the original paint, the mask filter layer's restore paint steals all shading |
| // effects and the draw's paint shading is updated to draw a solid opaque color (thus encoding |
| // coverage into the alpha channel). The draw's paint preserves all geometric effects that have |
| // to be applied before the mask filter. The layer's restore paint adds an image filter |
| // representing the mask filter. |
| if (fPaint.getMaskFilter() && !skipMaskFilterLayer) { |
| this->addMaskFilterLayer(rawBounds); |
| } |
| |
| // When the original paint has both an image filter and a mask filter, this will create two |
| // internal layers and perform two restores when finished. This actually creates one fewer |
| // offscreen passes compared to directly composing the mask filter's output with an |
| // SkImageFilters::Shader node and passing that into the rest of the image filter. |
| } |
| |
| AutoLayerForImageFilter::~AutoLayerForImageFilter() { |
| for (int i = 0; i < fTempLayersForFilters; ++i) { |
| fCanvas->fSaveCount -= 1; |
| fCanvas->internalRestore(); |
| } |
| // Negative save count occurs when this layer was moved. |
| SkASSERT(fSaveCount < 0 || fCanvas->getSaveCount() == fSaveCount); |
| } |
| |
| AutoLayerForImageFilter::AutoLayerForImageFilter(AutoLayerForImageFilter&& other) { |
| *this = std::move(other); |
| } |
| |
| AutoLayerForImageFilter& AutoLayerForImageFilter::operator=(AutoLayerForImageFilter&& other) { |
| fPaint = std::move(other.fPaint); |
| fCanvas = other.fCanvas; |
| fTempLayersForFilters = other.fTempLayersForFilters; |
| SkDEBUGCODE(fSaveCount = other.fSaveCount;) |
| |
| other.fTempLayersForFilters = 0; |
| SkDEBUGCODE(other.fSaveCount = -1;) |
| |
| return *this; |
| } |
| |
| void AutoLayerForImageFilter::addImageFilterLayer(const SkRect* drawBounds) { |
| // Shouldn't be adding a layer if there was no image filter to begin with. |
| SkASSERT(fPaint.getImageFilter()); |
| |
| // The restore paint for an image filter layer simply takes the image filter and blending off |
| // the original paint. The blending is applied post image filter because otherwise it'd be |
| // applied with the new layer's transparent dst and not be very interesting. |
| SkPaint restorePaint; |
| restorePaint.setImageFilter(fPaint.refImageFilter()); |
| restorePaint.setBlender(fPaint.refBlender()); |
| |
| // Remove the restorePaint fields from our "working" paint, leaving all other shading and |
| // geometry effects to be rendered into the layer. If there happens to be a mask filter, this |
| // paint will still trigger a second layer for that filter. |
| fPaint.setImageFilter(nullptr); |
| fPaint.setBlendMode(SkBlendMode::kSrcOver); |
| |
| this->addLayer(restorePaint, drawBounds, /*coverageOnly=*/false); |
| } |
| |
| void AutoLayerForImageFilter::addMaskFilterLayer(const SkRect* drawBounds) { |
| // Shouldn't be adding a layer if there was no mask filter to begin with. |
| SkASSERT(fPaint.getMaskFilter()); |
| |
| // Image filters are evaluated after mask filters so any filter should have been converted to |
| // a layer and removed from fPaint already. |
| SkASSERT(!fPaint.getImageFilter()); |
| |
| // TODO: Eventually all SkMaskFilters will implement this method so this can switch to an assert |
| sk_sp<SkImageFilter> maskFilterAsImageFilter = |
| as_MFB(fPaint.getMaskFilter())->asImageFilter(fCanvas->getTotalMatrix()); |
| if (!maskFilterAsImageFilter) { |
| // This is a legacy mask filter that can be handled by raster and Ganesh directly, but will |
| // be ignored by Graphite. Return now, leaving the paint with the mask filter so that the |
| // underlying SkDevice can handle it if it will. |
| return; |
| } |
| |
| // The restore paint for the coverage layer takes over all shading effects that had been on the |
| // original paint, which will be applied to the alpha-only output image from the mask filter |
| // converted to an image filter. |
| SkPaint restorePaint; |
| restorePaint.setColor4f(fPaint.getColor4f()); |
| restorePaint.setShader(fPaint.refShader()); |
| restorePaint.setColorFilter(fPaint.refColorFilter()); |
| restorePaint.setBlender(fPaint.refBlender()); |
| restorePaint.setDither(fPaint.isDither()); |
| restorePaint.setImageFilter(maskFilterAsImageFilter); |
| |
| // Remove all shading effects from the "working" paint so that the layer's alpha channel |
| // will correspond to the coverage. This leaves the original style and AA settings that |
| // contribute to coverage (including any path effect). |
| fPaint.setColor4f(SkColors::kWhite); |
| fPaint.setShader(nullptr); |
| fPaint.setColorFilter(nullptr); |
| fPaint.setMaskFilter(nullptr); |
| fPaint.setDither(false); |
| fPaint.setBlendMode(SkBlendMode::kSrcOver); |
| |
| this->addLayer(restorePaint, drawBounds, /*coverageOnly=*/true); |
| } |
| |
| void AutoLayerForImageFilter::addLayer(const SkPaint& restorePaint, |
| const SkRect* drawBounds, |
| bool coverageOnly) { |
| SkRect storage; |
| const SkRect* contentBounds = nullptr; |
| if (drawBounds && fPaint.canComputeFastBounds()) { |
| // The content bounds will include all paint outsets except for those that have been |
| // extracted into 'restorePaint' or a previously added layer. |
| contentBounds = &fPaint.computeFastBounds(*drawBounds, &storage); |
| } |
| |
| fCanvas->fSaveCount += 1; |
| fCanvas->internalSaveLayer(SkCanvas::SaveLayerRec(contentBounds, &restorePaint), |
| SkCanvas::kFullLayer_SaveLayerStrategy, |
| coverageOnly); |
| fTempLayersForFilters += 1; |
| } |