static bool Supports(const SkPixmap& dst, const SkPixmap& src, const SkPaint& paint) { if (dst.colorType() != src.colorType()) { return false; } if (dst.info().gammaCloseToSRGB() != src.info().gammaCloseToSRGB()) { return false; } if (paint.getMaskFilter() || paint.getColorFilter() || paint.getImageFilter()) { return false; } if (0xFF != paint.getAlpha()) { return false; } SkBlendMode mode = paint.getBlendMode(); if (SkBlendMode::kSrc == mode) { return true; } if (SkBlendMode::kSrcOver == mode && src.isOpaque()) { return true; } // At this point memcpy can't be used. The following check for using SrcOver. if (dst.colorType() != kN32_SkColorType || !dst.info().gammaCloseToSRGB()) { return false; } return SkBlendMode::kSrcOver == mode; }
void SkBaseDevice::drawSpriteWithFilter(const SkDraw& draw, const SkBitmap& bitmap, int x, int y, const SkPaint& paint) { SkImageFilter* filter = paint.getImageFilter(); SkASSERT(filter); SkIPoint offset = SkIPoint::Make(0, 0); SkMatrix matrix = *draw.fMatrix; matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y)); const SkIRect clipBounds = draw.fRC->getBounds().makeOffset(-x, -y); SkAutoTUnref<SkImageFilterCache> cache(this->getImageFilterCache()); SkImageFilter::Context ctx(matrix, clipBounds, cache.get()); sk_sp<SkSpecialImage> srcImg(SkSpecialImage::internal_fromBM(bitmap, &this->surfaceProps())); if (!srcImg) { return; // something disastrous happened } sk_sp<SkSpecialImage> resultImg(filter->filterImage(srcImg.get(), ctx, &offset)); if (resultImg) { SkPaint tmpUnfiltered(paint); tmpUnfiltered.setImageFilter(nullptr); SkBitmap resultBM; if (resultImg->internal_getBM(&resultBM)) { // TODO: add drawSprite(SkSpecialImage) to SkDevice? (see skbug.com/5073) this->drawSprite(draw, resultBM, x + offset.x(), y + offset.y(), tmpUnfiltered); } } }
void SkBitmapDevice::drawSpecial(const SkDraw& draw, SkSpecialImage* srcImg, int x, int y, const SkPaint& paint) { SkASSERT(!srcImg->isTextureBacked()); SkBitmap resultBM; SkImageFilter* filter = paint.getImageFilter(); if (filter) { SkIPoint offset = SkIPoint::Make(0, 0); SkMatrix matrix = *draw.fMatrix; matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y)); const SkIRect clipBounds = draw.fRC->getBounds().makeOffset(-x, -y); SkAutoTUnref<SkImageFilterCache> cache(this->getImageFilterCache()); SkImageFilter::OutputProperties outputProperties(fBitmap.colorSpace()); SkImageFilter::Context ctx(matrix, clipBounds, cache.get(), outputProperties); sk_sp<SkSpecialImage> resultImg(filter->filterImage(srcImg, ctx, &offset)); if (resultImg) { SkPaint tmpUnfiltered(paint); tmpUnfiltered.setImageFilter(nullptr); if (resultImg->getROPixels(&resultBM)) { this->drawSprite(draw, resultBM, x + offset.x(), y + offset.y(), tmpUnfiltered); } } } else { if (srcImg->getROPixels(&resultBM)) { this->drawSprite(draw, resultBM, x, y, paint); } } }
static bool Supports(const SkPixmap& dst, const SkPixmap& src, const SkPaint& paint) { if (dst.colorType() != src.colorType()) { return false; } if (dst.info().profileType() != src.info().profileType()) { return false; } if (paint.getMaskFilter() || paint.getColorFilter() || paint.getImageFilter()) { return false; } if (0xFF != paint.getAlpha()) { return false; } SkXfermode::Mode mode; if (!SkXfermode::AsMode(paint.getXfermode(), &mode)) { return false; } if (SkXfermode::kSrc_Mode == mode) { return true; } if (SkXfermode::kSrcOver_Mode == mode && src.isOpaque()) { return true; } return false; }
SkPaint SkColorSpaceXformer::apply(const SkPaint& src) { const AutoCachePurge autoPurge(this); SkPaint dst = src; // All SkColorSpaces have the same black point. if (src.getColor() & 0xffffff) { dst.setColor(this->apply(src.getColor())); } if (auto shader = src.getShader()) { dst.setShader(this->apply(shader)); } if (auto cf = src.getColorFilter()) { dst.setColorFilter(this->apply(cf)); } if (auto looper = src.getDrawLooper()) { dst.setDrawLooper(looper->makeColorSpace(this)); } if (auto imageFilter = src.getImageFilter()) { dst.setImageFilter(this->apply(imageFilter)); } return dst; }
static void apply_paint_imagefilter(const SkPaint& paint, Json::Value* target, bool sendBinaries) { SkFlattenable* imageFilter = paint.getImageFilter(); if (imageFilter != nullptr) { Json::Value jsonImageFilter; flatten(imageFilter, &jsonImageFilter, sendBinaries); (*target)[SKJSONCANVAS_ATTRIBUTE_IMAGEFILTER] = jsonImageFilter; } }
static bool fold_opacity_layer_color_to_paint(const SkPaint& layerPaint, bool isSaveLayer, SkPaint* paint) { // We assume layerPaint is always from a saveLayer. If isSaveLayer is // true, we assume paint is too. // The alpha folding can proceed if the filter layer paint does not have properties which cause // the resulting filter layer to be "blended" in complex ways to the parent layer. For example, // looper drawing unmodulated filter layer twice and then modulating the result produces // different image to drawing modulated filter layer twice. // TODO: most likely the looper and only some xfer modes are the hard constraints if (paint->getXfermode() || paint->getLooper()) { return false; } if (!isSaveLayer && paint->getImageFilter()) { // For normal draws, the paint color is used as one input for the color for the draw. Image // filter will operate on the result, and thus we can not change the input. // For layer saves, the image filter is applied to the layer contents. The layer is then // modulated with the paint color, so it's fine to proceed with the fold for saveLayer // paints with image filters. return false; } if (paint->getColorFilter()) { // Filter input depends on the paint color. // Here we could filter the color if we knew the draw is going to be uniform color. This // should be detectable as drawPath/drawRect/.. without a shader being uniform, while // drawBitmap/drawSprite or a shader being non-uniform. However, current matchers don't // give the type out easily, so just do not optimize that at the moment. return false; } const uint32_t layerColor = layerPaint.getColor(); // The layer paint color must have only alpha component. if (SK_ColorTRANSPARENT != SkColorSetA(layerColor, SK_AlphaTRANSPARENT)) { return false; } // The layer paint can not have any effects. if (layerPaint.getPathEffect() || layerPaint.getShader() || layerPaint.getXfermode() || layerPaint.getMaskFilter() || layerPaint.getColorFilter() || layerPaint.getRasterizer() || layerPaint.getLooper() || layerPaint.getImageFilter()) { return false; } paint->setAlpha(SkMulDiv255Round(paint->getAlpha(), SkColorGetA(layerColor))); return true; }
static bool HasAnyEffect(const SkPaint& paint) { return paint.getPathEffect() || paint.getShader() || paint.getXfermode() || paint.getMaskFilter() || paint.getColorFilter() || paint.getRasterizer() || paint.getLooper() || paint.getImageFilter(); }
// Is the supplied paint simply a color? static bool is_simple(const SkPaint& p) { return NULL == p.getPathEffect() && NULL == p.getShader() && NULL == p.getXfermode() && NULL == p.getMaskFilter() && NULL == p.getColorFilter() && NULL == p.getRasterizer() && NULL == p.getLooper() && NULL == p.getImageFilter(); }
bool NeedsDeepCopy(const SkPaint& paint) { /* * The types below are not yet immutable/reentrant-safe, and so we return * true if instances of them are present in the paint. * * Eventually we hope this list will be empty, and we can always return * false. */ return paint.getImageFilter(); }
static uint16_t compute_nondef(const SkPaint& paint, PaintUsage usage) { // kRespectsStroke_PaintUsage is only valid if other bits are also set SkASSERT(0 != (usage & ~kRespectsStroke_PaintUsage)); const SkScalar kTextSize_Default = 12; const SkScalar kTextScaleX_Default = 1; const SkScalar kTextSkewX_Default = 0; const SkScalar kStrokeWidth_Default = 0; const SkScalar kStrokeMiter_Default = 4; const SkColor kColor_Default = SK_ColorBLACK; unsigned bits = (paint.getColor() != kColor_Default) ? kColor_NonDef : 0; if (usage & kText_PaintUsage) { bits |= (paint.getTextSize() != kTextSize_Default ? kTextSize_NonDef : 0); bits |= (paint.getTextScaleX() != kTextScaleX_Default ? kTextScaleX_NonDef : 0); bits |= (paint.getTextSkewX() != kTextSkewX_Default ? kTextSkewX_NonDef : 0); bits |= (paint.getTypeface() ? kTypeface_NonDef : 0); } // TODO: kImage_PaintUsage only needs the shader/maskfilter IF its colortype is kAlpha_8 if (usage & (kVertices_PaintUsage | kDrawPaint_PaintUsage | kImage_PaintUsage | kText_PaintUsage | kGeometry_PaintUsage | kTextBlob_PaintUsage)) { bits |= (paint.getShader() ? kShader_NonDef : 0); } if (usage & (kText_PaintUsage | kGeometry_PaintUsage | kTextBlob_PaintUsage)) { bits |= (paint.getPathEffect() ? kPathEffect_NonDef : 0); bits |= (paint.getRasterizer() ? kRasterizer_NonDef : 0); if (paint.getStyle() != SkPaint::kFill_Style || (usage & kRespectsStroke_PaintUsage)) { bits |= (paint.getStrokeWidth() != kStrokeWidth_Default ? kStrokeWidth_NonDef : 0); bits |= (paint.getStrokeMiter() != kStrokeMiter_Default ? kStrokeMiter_NonDef : 0); } } if (usage & (kText_PaintUsage | kGeometry_PaintUsage | kImage_PaintUsage | kTextBlob_PaintUsage)) { bits |= (paint.getMaskFilter() ? kMaskFilter_NonDef : 0); } bits |= (paint.getColorFilter() ? kColorFilter_NonDef : 0); bits |= (paint.getImageFilter() ? kImageFilter_NonDef : 0); bits |= (paint.getDrawLooper() ? kDrawLooper_NonDef : 0); return SkToU16(bits); }
bool SkPaintPriv::ShouldDither(const SkPaint& p, SkColorType dstCT) { // The paint dither flag can veto. if (!p.isDither()) { return false; } // We always dither 565 or 4444 when requested. if (dstCT == kRGB_565_SkColorType || dstCT == kARGB_4444_SkColorType) { return true; } // Otherwise, dither is only needed for non-const paints. return p.getImageFilter() || p.getMaskFilter() || !p.getShader() || !as_SB(p.getShader())->isConstant(); }
static bool Supports(const SkPixmap& dst, const SkPixmap& src, const SkPaint& paint) { // the caller has already inspected the colorspace on src and dst SkASSERT(!SkColorSpaceXformSteps::Required(src.colorSpace(), dst.colorSpace())); if (dst.colorType() != src.colorType()) { return false; } if (paint.getMaskFilter() || paint.getColorFilter() || paint.getImageFilter()) { return false; } if (0xFF != paint.getAlpha()) { return false; } SkBlendMode mode = paint.getBlendMode(); return SkBlendMode::kSrc == mode || (SkBlendMode::kSrcOver == mode && src.isOpaque()); }
static bool Supports(const SkPixmap& dst, const SkPixmap& src, const SkPaint& paint) { if (dst.colorType() != src.colorType()) { return false; } if (!SkColorSpace::Equals(dst.colorSpace(), src.colorSpace())) { return false; } if (paint.getMaskFilter() || paint.getColorFilter() || paint.getImageFilter()) { return false; } if (0xFF != paint.getAlpha()) { return false; } SkBlendMode mode = paint.getBlendMode(); return SkBlendMode::kSrc == mode || (SkBlendMode::kSrcOver == mode && src.isOpaque()); }
bool NeedsDeepCopy(const SkPaint& paint) { /* * The types below are not yet immutable/reentrant-safe, and so we return * true if instances of them are present in the paint. * * Eventually we hope this list will be empty, and we can always return * false. */ return false #ifdef SK_SUPPORT_LEGACY_SHADER_LOCALMATRIX || paint.getShader() #endif #ifdef SK_SUPPORT_LEGACY_LAYERRASTERIZER_API || paint.getRasterizer() #endif || paint.getImageFilter() ; }
void SkBaseDevice::drawBitmapAsSprite(const SkDraw& draw, const SkBitmap& bitmap, int x, int y, const SkPaint& paint) { SkImageFilter* filter = paint.getImageFilter(); if (filter && !this->canHandleImageFilter(filter)) { SkImageFilter::DeviceProxy proxy(this); SkBitmap dst; SkIPoint offset = SkIPoint::Make(0, 0); SkMatrix matrix = *draw.fMatrix; matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y)); const SkIRect clipBounds = draw.fClip->getBounds().makeOffset(-x, -y); SkAutoTUnref<SkImageFilter::Cache> cache(this->getImageFilterCache()); SkImageFilter::Context ctx(matrix, clipBounds, cache.get()); if (filter->filterImageDeprecated(&proxy, bitmap, ctx, &dst, &offset)) { SkPaint tmpUnfiltered(paint); tmpUnfiltered.setImageFilter(nullptr); this->drawSprite(draw, dst, x + offset.x(), y + offset.y(), tmpUnfiltered); } } else { this->drawSprite(draw, bitmap, x, y, paint); } }
// Returns true if all pixels painted will be opaque. static inline bool paintIsOpaque(const SkPaint& paint, const SkBitmap* bitmap = 0, bool checkFillOnly = false) { if (paint.getAlpha() < 0xFF) return false; if (!checkFillOnly && paint.getStyle() != SkPaint::kFill_Style && paint.isAntiAlias()) return false; SkShader* shader = paint.getShader(); if (shader && !shader->isOpaque()) return false; if (bitmap && !bitmap->isOpaque()) return false; if (paint.getLooper()) return false; if (paint.getImageFilter()) return false; if (paint.getMaskFilter()) return false; SkColorFilter* colorFilter = paint.getColorFilter(); if (colorFilter && !(colorFilter->getFlags() & SkColorFilter::kAlphaUnchanged_Flag)) return false; return true; }
static bool needs_layer(const SkPaint& paint) { return 0xFF != paint.getAlpha() || paint.getColorFilter() || paint.getImageFilter() || SkXfermode::IsMode(paint.getXfermode(), SkXfermode::kSrcOver_Mode); }
void SkBitmapDevice::drawDevice(const SkDraw& draw, SkBaseDevice* device, int x, int y, const SkPaint& paint) { SkASSERT(!paint.getImageFilter()); draw.drawSprite(static_cast<SkBitmapDevice*>(device)->fBitmap, x, y, paint); }