void draw(SkCanvas* canvas) { SkPaint paint; SkDebugf("kSrcOver %c= getBlendMode\n", SkBlendMode::kSrcOver == paint.getBlendMode() ? '=' : '!'); paint.setBlendMode(SkBlendMode::kSrc); SkDebugf("kSrcOver %c= getBlendMode\n", SkBlendMode::kSrcOver == paint.getBlendMode() ? '=' : '!'); }
TEST(SkiaBehavior, porterDuffCreateIsCached) { SkPaint paint; paint.setBlendMode(SkBlendMode::kOverlay); auto expected = paint.getBlendMode(); paint.setBlendMode(SkBlendMode::kClear); ASSERT_NE(expected, paint.getBlendMode()); paint.setBlendMode(SkBlendMode::kOverlay); ASSERT_EQ(expected, paint.getBlendMode()); }
SkXfermodeInterpretation SkInterpretXfermode(const SkPaint& paint, bool dstIsOpaque) { switch (paint.getBlendMode()) { case SkBlendMode::kSrcOver: return kSrcOver_SkXfermodeInterpretation; case SkBlendMode::kSrc: if (just_solid_color(paint)) { return kSrcOver_SkXfermodeInterpretation; } return kNormal_SkXfermodeInterpretation; case SkBlendMode::kDst: return kSkipDrawing_SkXfermodeInterpretation; case SkBlendMode::kDstOver: if (dstIsOpaque) { return kSkipDrawing_SkXfermodeInterpretation; } return kNormal_SkXfermodeInterpretation; case SkBlendMode::kSrcIn: if (dstIsOpaque && just_solid_color(paint)) { return kSrcOver_SkXfermodeInterpretation; } return kNormal_SkXfermodeInterpretation; case SkBlendMode::kDstIn: if (just_solid_color(paint)) { return kSkipDrawing_SkXfermodeInterpretation; } return kNormal_SkXfermodeInterpretation; default: return kNormal_SkXfermodeInterpretation; } }
void CrossfadeGeneratedImage::drawCrossfade( SkCanvas* canvas, const SkPaint& paint, ImageClampingMode clampMode, const ColorBehavior& colorBehavior) { FloatRect fromImageRect(FloatPoint(), FloatSize(m_fromImage->size())); FloatRect toImageRect(FloatPoint(), FloatSize(m_toImage->size())); FloatRect destRect((FloatPoint()), FloatSize(m_crossfadeSize)); // TODO(junov): The various effects encoded into paint should probably be // applied here instead of inside the layer. This probably faulty behavior // was maintained in order to preserve pre-existing behavior while refactoring // this code. This should be investigated further. crbug.com/472634 SkPaint layerPaint; layerPaint.setBlendMode(paint.getBlendMode()); SkAutoCanvasRestore ar(canvas, false); canvas->saveLayer(nullptr, &layerPaint); SkPaint imagePaint(paint); imagePaint.setBlendMode(SkBlendMode::kSrcOver); int imageAlpha = clampedAlphaForBlending(1 - m_percentage); imagePaint.setAlpha(imageAlpha > 255 ? 255 : imageAlpha); imagePaint.setAntiAlias(paint.isAntiAlias()); // TODO(junov): This code should probably be propagating the // RespectImageOrientationEnum from CrossfadeGeneratedImage::draw(). Code was // written this way during refactoring to avoid modifying existing behavior, // but this warrants further investigation. crbug.com/472634 m_fromImage->draw(canvas, imagePaint, destRect, fromImageRect, DoNotRespectImageOrientation, clampMode, colorBehavior); imagePaint.setBlendMode(SkBlendMode::kPlus); imageAlpha = clampedAlphaForBlending(m_percentage); imagePaint.setAlpha(imageAlpha > 255 ? 255 : imageAlpha); m_toImage->draw(canvas, imagePaint, destRect, toImageRect, DoNotRespectImageOrientation, clampMode, colorBehavior); }
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 setup(const SkPixmap& dst, int left, int top, const SkPaint& paint) override { SkASSERT(Supports(dst, fSource, paint)); this->INHERITED::setup(dst, left, top, paint); SkBlendMode mode = paint.getBlendMode(); SkASSERT(mode == SkBlendMode::kSrcOver || mode == SkBlendMode::kSrc); if (mode == SkBlendMode::kSrcOver && !fSource.isOpaque()) { fUseMemcpy = false; } }
sk_sp<SkPDFDict> SkPDFGraphicState::GetGraphicStateForPaint(SkPDFCanon* canon, const SkPaint& p) { SkASSERT(canon); if (SkPaint::kFill_Style == p.getStyle()) { SkPDFFillGraphicState fillKey = {p.getAlpha(), pdf_blend_mode(p.getBlendMode())}; auto& fillMap = canon->fFillGSMap; if (sk_sp<SkPDFDict>* statePtr = fillMap.find(fillKey)) { return *statePtr; } auto state = sk_make_sp<SkPDFDict>(); state->reserve(2); state->insertScalar("ca", fillKey.fAlpha / 255.0f); state->insertName("BM", as_pdf_blend_mode_name((SkBlendMode)fillKey.fBlendMode)); fillMap.set(fillKey, state); return state; } else { SkPDFStrokeGraphicState strokeKey = { p.getStrokeWidth(), p.getStrokeMiter(), SkToU8(p.getStrokeCap()), SkToU8(p.getStrokeJoin()), p.getAlpha(), pdf_blend_mode(p.getBlendMode())}; auto& sMap = canon->fStrokeGSMap; if (sk_sp<SkPDFDict>* statePtr = sMap.find(strokeKey)) { return *statePtr; } auto state = sk_make_sp<SkPDFDict>(); state->reserve(8); state->insertScalar("CA", strokeKey.fAlpha / 255.0f); state->insertScalar("ca", strokeKey.fAlpha / 255.0f); state->insertInt("LC", to_stroke_cap(strokeKey.fStrokeCap)); state->insertInt("LJ", to_stroke_join(strokeKey.fStrokeJoin)); state->insertScalar("LW", strokeKey.fStrokeWidth); state->insertScalar("ML", strokeKey.fStrokeMiter); state->insertBool("SA", true); // SA = Auto stroke adjustment. state->insertName("BM", as_pdf_blend_mode_name((SkBlendMode)strokeKey.fBlendMode)); sMap.set(strokeKey, state); return state; } }
static void test_backToFront(skiatest::Reporter* reporter) { SkLayerDrawLooper::Builder looperBuilder; SkLayerDrawLooper::LayerInfo layerInfo; // Add the back layer, with the defaults. (void)looperBuilder.addLayerOnTop(layerInfo); // Add the front layer, with some layer info set. layerInfo.fOffset.set(10.0f, 20.0f); layerInfo.fPaintBits |= SkLayerDrawLooper::kXfermode_Bit; SkPaint* layerPaint = looperBuilder.addLayerOnTop(layerInfo); layerPaint->setBlendMode(SkBlendMode::kSrc); FakeDevice device; SkCanvas canvas(&device); SkPaint paint; auto looper(looperBuilder.detach()); SkArenaAlloc alloc{48}; SkDrawLooper::Context* context = looper->makeContext(&canvas, &alloc); // The back layer should come first. REPORTER_ASSERT(reporter, context->next(&canvas, &paint)); REPORTER_ASSERT(reporter, paint.getBlendMode() == SkBlendMode::kSrcOver); canvas.drawRect(SkRect::MakeWH(50.0f, 50.0f), paint); REPORTER_ASSERT(reporter, 0.0f == device.fLastMatrix.getTranslateX()); REPORTER_ASSERT(reporter, 0.0f == device.fLastMatrix.getTranslateY()); paint.reset(); // Then the front layer. REPORTER_ASSERT(reporter, context->next(&canvas, &paint)); REPORTER_ASSERT(reporter, paint.getBlendMode() == SkBlendMode::kSrc); canvas.drawRect(SkRect::MakeWH(50.0f, 50.0f), paint); REPORTER_ASSERT(reporter, 10.0f == device.fLastMatrix.getTranslateX()); REPORTER_ASSERT(reporter, 20.0f == device.fLastMatrix.getTranslateY()); // Only two layers were added, so that should be the end. REPORTER_ASSERT(reporter, !context->next(&canvas, &paint)); }
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()); }
/* * Header: * paint flags : 32 * non_def bits : 16 * xfermode enum : 8 * pad zeros : 8 */ static void write_paint(SkWriteBuffer& writer, const SkPaint& paint, unsigned usage) { uint32_t packedFlags = pack_paint_flags(paint.getFlags(), paint.getHinting(), paint.getTextAlign(), paint.getFilterQuality(), paint.getStyle(), paint.getStrokeCap(), paint.getStrokeJoin(), paint.getTextEncoding()); writer.write32(packedFlags); unsigned nondef = compute_nondef(paint, (PaintUsage)usage); const uint8_t pad = 0; writer.write32((nondef << 16) | ((unsigned)paint.getBlendMode() << 8) | pad); CHECK_WRITE_SCALAR(writer, nondef, paint, TextSize); CHECK_WRITE_SCALAR(writer, nondef, paint, TextScaleX); CHECK_WRITE_SCALAR(writer, nondef, paint, TextSkewX); CHECK_WRITE_SCALAR(writer, nondef, paint, StrokeWidth); CHECK_WRITE_SCALAR(writer, nondef, paint, StrokeMiter); if (nondef & kColor_NonDef) { writer.write32(paint.getColor()); } if (nondef & kTypeface_NonDef) { // TODO: explore idea of writing bits indicating "use the prev (or prev N) face" // e.g. 1-N bits is an index into a ring buffer of typefaces SkTypeface* tf = paint.getTypeface(); SkASSERT(tf); writer.writeTypeface(tf); } CHECK_WRITE_FLATTENABLE(writer, nondef, paint, PathEffect); CHECK_WRITE_FLATTENABLE(writer, nondef, paint, Shader); CHECK_WRITE_FLATTENABLE(writer, nondef, paint, MaskFilter); CHECK_WRITE_FLATTENABLE(writer, nondef, paint, ColorFilter); CHECK_WRITE_FLATTENABLE(writer, nondef, paint, Rasterizer); CHECK_WRITE_FLATTENABLE(writer, nondef, paint, ImageFilter); CHECK_WRITE_FLATTENABLE(writer, nondef, paint, DrawLooper); }
// Even with kEntirePaint_Bits, we always ensure that the master paint's // text-encoding is respected, since that controls how we interpret the // text/length parameters of a draw[Pos]Text call. void SkLayerDrawLooper::LayerDrawLooperContext::ApplyInfo( SkPaint* dst, const SkPaint& src, const LayerInfo& info) { SkColor4f srcColor = src.getColor4f(); #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK // The framework may respect the alpha value on the original paint. // Match this legacy behavior. if (src.getAlpha() == 255) { srcColor.fA = dst->getColor4f().fA; } #endif dst->setColor4f(xferColor(srcColor, dst->getColor4f(), (SkBlendMode)info.fColorMode), sk_srgb_singleton()); BitFlags bits = info.fPaintBits; SkPaint::TextEncoding encoding = dst->getTextEncoding(); if (0 == bits) { return; } if (kEntirePaint_Bits == bits) { // we've already computed these, so save it from the assignment uint32_t f = dst->getFlags(); SkColor4f c = dst->getColor4f(); *dst = src; dst->setFlags(f); dst->setColor4f(c, sk_srgb_singleton()); dst->setTextEncoding(encoding); return; } if (bits & kStyle_Bit) { dst->setStyle(src.getStyle()); dst->setStrokeWidth(src.getStrokeWidth()); dst->setStrokeMiter(src.getStrokeMiter()); dst->setStrokeCap(src.getStrokeCap()); dst->setStrokeJoin(src.getStrokeJoin()); } if (bits & kTextSkewX_Bit) { dst->setTextSkewX(src.getTextSkewX()); } if (bits & kPathEffect_Bit) { dst->setPathEffect(src.refPathEffect()); } if (bits & kMaskFilter_Bit) { dst->setMaskFilter(src.refMaskFilter()); } if (bits & kShader_Bit) { dst->setShader(src.refShader()); } if (bits & kColorFilter_Bit) { dst->setColorFilter(src.refColorFilter()); } if (bits & kXfermode_Bit) { dst->setBlendMode(src.getBlendMode()); } // we don't override these #if 0 dst->setTypeface(src.getTypeface()); dst->setTextSize(src.getTextSize()); dst->setTextScaleX(src.getTextScaleX()); dst->setRasterizer(src.getRasterizer()); dst->setLooper(src.getLooper()); dst->setTextEncoding(src.getTextEncoding()); dst->setHinting(src.getHinting()); #endif }
Sprite_4f(const SkPixmap& src, const SkPaint& paint) : INHERITED(src) { fXfer = SkXfermode::Peek(paint.getBlendMode()); fLoader = SkLoadSpanProc_Choose(src.info()); fFilter = SkFilterSpanProc_Choose(paint); fBuffer.reset(src.width()); }
static inline bool skpaint_to_grpaint_impl(GrContext* context, const GrColorSpaceInfo& colorSpaceInfo, const SkPaint& skPaint, const SkMatrix& viewM, std::unique_ptr<GrFragmentProcessor>* shaderProcessor, SkBlendMode* primColorMode, GrPaint* grPaint) { grPaint->setAllowSRGBInputs(colorSpaceInfo.isGammaCorrect()); // Convert SkPaint color to 4f format, including optional linearizing and gamut conversion. GrColor4f origColor = SkColorToUnpremulGrColor4f(skPaint.getColor(), colorSpaceInfo); const GrFPArgs fpArgs(context, &viewM, skPaint.getFilterQuality(), &colorSpaceInfo); // Setup the initial color considering the shader, the SkPaint color, and the presence or not // of per-vertex colors. std::unique_ptr<GrFragmentProcessor> shaderFP; if (!primColorMode || blend_requires_shader(*primColorMode)) { if (shaderProcessor) { shaderFP = std::move(*shaderProcessor); } else if (const auto* shader = as_SB(skPaint.getShader())) { shaderFP = shader->asFragmentProcessor(fpArgs); if (!shaderFP) { return false; } } } // Set this in below cases if the output of the shader/paint-color/paint-alpha/primXfermode is // a known constant value. In that case we can simply apply a color filter during this // conversion without converting the color filter to a GrFragmentProcessor. bool applyColorFilterToPaintColor = false; if (shaderFP) { if (primColorMode) { // There is a blend between the primitive color and the shader color. The shader sees // the opaque paint color. The shader's output is blended using the provided mode by // the primitive color. The blended color is then modulated by the paint's alpha. // The geometry processor will insert the primitive color to start the color chain, so // the GrPaint color will be ignored. GrColor4f shaderInput = origColor.opaque(); shaderFP = GrFragmentProcessor::OverrideInput(std::move(shaderFP), shaderInput); shaderFP = GrXfermodeFragmentProcessor::MakeFromSrcProcessor(std::move(shaderFP), *primColorMode); // The above may return null if compose results in a pass through of the prim color. if (shaderFP) { grPaint->addColorFragmentProcessor(std::move(shaderFP)); } // We can ignore origColor here - alpha is unchanged by gamma GrColor paintAlpha = SkColorAlphaToGrColor(skPaint.getColor()); if (GrColor_WHITE != paintAlpha) { // No gamut conversion - paintAlpha is a (linear) alpha value, splatted to all // color channels. It's value should be treated as the same in ANY color space. grPaint->addColorFragmentProcessor(GrConstColorProcessor::Make( GrColor4f::FromGrColor(paintAlpha), GrConstColorProcessor::InputMode::kModulateRGBA)); } } else { // The shader's FP sees the paint unpremul color grPaint->setColor4f(origColor); grPaint->addColorFragmentProcessor(std::move(shaderFP)); } } else { if (primColorMode) { // There is a blend between the primitive color and the paint color. The blend considers // the opaque paint color. The paint's alpha is applied to the post-blended color. auto processor = GrConstColorProcessor::Make(origColor.opaque(), GrConstColorProcessor::InputMode::kIgnore); processor = GrXfermodeFragmentProcessor::MakeFromSrcProcessor(std::move(processor), *primColorMode); if (processor) { grPaint->addColorFragmentProcessor(std::move(processor)); } grPaint->setColor4f(origColor.opaque()); // We can ignore origColor here - alpha is unchanged by gamma GrColor paintAlpha = SkColorAlphaToGrColor(skPaint.getColor()); if (GrColor_WHITE != paintAlpha) { // No gamut conversion - paintAlpha is a (linear) alpha value, splatted to all // color channels. It's value should be treated as the same in ANY color space. grPaint->addColorFragmentProcessor(GrConstColorProcessor::Make( GrColor4f::FromGrColor(paintAlpha), GrConstColorProcessor::InputMode::kModulateRGBA)); } } else { // No shader, no primitive color. grPaint->setColor4f(origColor.premul()); applyColorFilterToPaintColor = true; } } SkColorFilter* colorFilter = skPaint.getColorFilter(); if (colorFilter) { if (applyColorFilterToPaintColor) { // If we're in legacy mode, we *must* avoid using the 4f version of the color filter, // because that will combine with the linearized version of the stored color. if (colorSpaceInfo.isGammaCorrect()) { grPaint->setColor4f(GrColor4f::FromSkColor4f( colorFilter->filterColor4f(origColor.toSkColor4f())).premul()); } else { grPaint->setColor4f(SkColorToPremulGrColor4fLegacy( colorFilter->filterColor(skPaint.getColor()))); } } else { auto cfFP = colorFilter->asFragmentProcessor(context, colorSpaceInfo); if (cfFP) { grPaint->addColorFragmentProcessor(std::move(cfFP)); } else { return false; } } } SkMaskFilterBase* maskFilter = as_MFB(skPaint.getMaskFilter()); if (maskFilter) { if (auto mfFP = maskFilter->asFragmentProcessor(fpArgs)) { grPaint->addCoverageFragmentProcessor(std::move(mfFP)); } } // When the xfermode is null on the SkPaint (meaning kSrcOver) we need the XPFactory field on // the GrPaint to also be null (also kSrcOver). SkASSERT(!grPaint->getXPFactory()); if (!skPaint.isSrcOver()) { grPaint->setXPFactory(SkBlendMode_AsXPFactory(skPaint.getBlendMode())); } #ifndef SK_IGNORE_GPU_DITHER // Conservative default, in case GrPixelConfigToColorType() fails. SkColorType ct = SkColorType::kRGB_565_SkColorType; GrPixelConfigToColorType(colorSpaceInfo.config(), &ct); if (SkPaintPriv::ShouldDither(skPaint, ct) && grPaint->numColorFragmentProcessors() > 0 && !colorSpaceInfo.isGammaCorrect()) { auto ditherFP = GrDitherEffect::Make(colorSpaceInfo.config()); if (ditherFP) { grPaint->addColorFragmentProcessor(std::move(ditherFP)); } } #endif return true; }