bool SkPaintToGrPaintWithTexture(GrContext* context, const SkPaint& paint, const SkMatrix& viewM, const GrFragmentProcessor* fp, bool textureIsAlphaOnly, GrPaint* grPaint) { SkAutoTUnref<const GrFragmentProcessor> shaderFP; if (textureIsAlphaOnly) { if (const SkShader* shader = paint.getShader()) { shaderFP.reset(shader->asFragmentProcessor(context, viewM, nullptr, paint.getFilterQuality())); if (!shaderFP) { return false; } const GrFragmentProcessor* fpSeries[] = { shaderFP.get(), fp }; shaderFP.reset(GrFragmentProcessor::RunInSeries(fpSeries, 2)); } else { shaderFP.reset(GrFragmentProcessor::MulOutputByInputUnpremulColor(fp)); } } else { shaderFP.reset(GrFragmentProcessor::MulOutputByInputAlpha(fp)); } return SkPaintToGrPaintReplaceShader(context, paint, shaderFP.get(), grPaint); }
bool SkPaintToGrPaintWithTexture(GrContext* context, const GrColorSpaceInfo& colorSpaceInfo, const SkPaint& paint, const SkMatrix& viewM, std::unique_ptr<GrFragmentProcessor> fp, bool textureIsAlphaOnly, GrPaint* grPaint) { std::unique_ptr<GrFragmentProcessor> shaderFP; if (textureIsAlphaOnly) { if (const auto* shader = as_SB(paint.getShader())) { shaderFP = shader->asFragmentProcessor(GrFPArgs( context, &viewM, nullptr, paint.getFilterQuality(), &colorSpaceInfo)); if (!shaderFP) { return false; } std::unique_ptr<GrFragmentProcessor> fpSeries[] = { std::move(shaderFP), std::move(fp) }; shaderFP = GrFragmentProcessor::RunInSeries(fpSeries, 2); } else { shaderFP = GrFragmentProcessor::MakeInputPremulAndMulByOutput(std::move(fp)); } } else { shaderFP = GrFragmentProcessor::MulChildByInputAlpha(std::move(fp)); } return SkPaintToGrPaintReplaceShader(context, colorSpaceInfo, paint, std::move(shaderFP), grPaint); }
void SkGpuDevice::drawTextureProducer(GrTextureProducer* producer, const SkRect* srcRect, const SkRect* dstRect, SkCanvas::SrcRectConstraint constraint, const SkMatrix& viewMatrix, const GrClip& clip, const SkPaint& paint) { // This is the funnel for all non-tiled bitmap/image draw calls. Log a histogram entry. SK_HISTOGRAM_BOOLEAN("DrawTiled", false); // Figure out the actual dst and src rect by clipping the src rect to the bounds of the // adjuster. If the src rect is clipped then the dst rect must be recomputed. Also determine // the matrix that maps the src rect to the dst rect. SkRect clippedSrcRect; SkRect clippedDstRect; const SkRect srcBounds = SkRect::MakeIWH(producer->width(), producer->height()); SkMatrix srcToDstMatrix; if (srcRect) { if (!dstRect) { dstRect = &srcBounds; } if (!srcBounds.contains(*srcRect)) { clippedSrcRect = *srcRect; if (!clippedSrcRect.intersect(srcBounds)) { return; } if (!srcToDstMatrix.setRectToRect(*srcRect, *dstRect, SkMatrix::kFill_ScaleToFit)) { return; } srcToDstMatrix.mapRect(&clippedDstRect, clippedSrcRect); } else { clippedSrcRect = *srcRect; clippedDstRect = *dstRect; if (!srcToDstMatrix.setRectToRect(*srcRect, *dstRect, SkMatrix::kFill_ScaleToFit)) { return; } } } else { clippedSrcRect = srcBounds; if (dstRect) { clippedDstRect = *dstRect; if (!srcToDstMatrix.setRectToRect(srcBounds, *dstRect, SkMatrix::kFill_ScaleToFit)) { return; } } else { clippedDstRect = srcBounds; srcToDstMatrix.reset(); } } // Now that we have both the view and srcToDst matrices, log our scale factor. LogDrawScaleFactor(SkMatrix::Concat(viewMatrix, srcToDstMatrix), paint.getFilterQuality()); this->drawTextureProducerImpl(producer, clippedSrcRect, clippedDstRect, constraint, viewMatrix, srcToDstMatrix, clip, paint); }
bool SkBitmapProcShader::asFragmentProcessor(GrContext* context, const SkPaint& paint, const SkMatrix& viewM, const SkMatrix* localMatrix, GrColor* paintColor, GrProcessorDataManager* procDataManager, GrFragmentProcessor** fp) const { SkMatrix matrix; matrix.setIDiv(fRawBitmap.width(), fRawBitmap.height()); SkMatrix lmInverse; if (!this->getLocalMatrix().invert(&lmInverse)) { return false; } if (localMatrix) { SkMatrix inv; if (!localMatrix->invert(&inv)) { return false; } lmInverse.postConcat(inv); } matrix.preConcat(lmInverse); SkShader::TileMode tm[] = { (TileMode)fTileModeX, (TileMode)fTileModeY, }; // Must set wrap and filter on the sampler before requesting a texture. In two places below // we check the matrix scale factors to determine how to interpret the filter quality setting. // This completely ignores the complexity of the drawVertices case where explicit local coords // are provided by the caller. bool doBicubic; GrTextureParams::FilterMode textureFilterMode = GrSkFilterQualityToGrFilterMode(paint.getFilterQuality(), viewM, this->getLocalMatrix(), &doBicubic); GrTextureParams params(tm, textureFilterMode); SkAutoTUnref<GrTexture> texture(GrRefCachedBitmapTexture(context, fRawBitmap, ¶ms)); if (!texture) { SkErrorInternals::SetError( kInternalError_SkError, "Couldn't convert bitmap to texture."); return false; } *paintColor = (kAlpha_8_SkColorType == fRawBitmap.colorType()) ? SkColor2GrColor(paint.getColor()) : SkColor2GrColorJustAlpha(paint.getColor()); if (doBicubic) { *fp = GrBicubicEffect::Create(procDataManager, texture, matrix, tm); } else { *fp = GrSimpleTextureEffect::Create(procDataManager, texture, matrix, params); } return true; }
PassRefPtr<JSONObject> LoggingCanvas::objectForSkPaint(const SkPaint& paint) { RefPtr<JSONObject> paintItem = JSONObject::create(); paintItem->setNumber("textSize", paint.getTextSize()); paintItem->setNumber("textScaleX", paint.getTextScaleX()); paintItem->setNumber("textSkewX", paint.getTextSkewX()); if (SkShader* shader = paint.getShader()) paintItem->setObject("shader", objectForSkShader(*shader)); paintItem->setString("color", stringForSkColor(paint.getColor())); paintItem->setNumber("strokeWidth", paint.getStrokeWidth()); paintItem->setNumber("strokeMiter", paint.getStrokeMiter()); paintItem->setString("flags", stringForSkPaintFlags(paint)); paintItem->setString("filterLevel", filterQualityName(paint.getFilterQuality())); paintItem->setString("textAlign", textAlignName(paint.getTextAlign())); paintItem->setString("strokeCap", strokeCapName(paint.getStrokeCap())); paintItem->setString("strokeJoin", strokeJoinName(paint.getStrokeJoin())); paintItem->setString("styleName", styleName(paint.getStyle())); paintItem->setString("textEncoding", textEncodingName(paint.getTextEncoding())); paintItem->setString("hinting", hintingName(paint.getHinting())); return paintItem.release(); }
bool SkPaint2GrPaint(GrContext* context, GrRenderTarget* rt, const SkPaint& skPaint, const SkMatrix& viewM, bool constantColor, GrPaint* grPaint) { SkShader* shader = skPaint.getShader(); if (nullptr == shader) { return SkPaint2GrPaintNoShader(context, rt, skPaint, SkColor2GrColor(skPaint.getColor()), constantColor, grPaint); } GrColor paintColor = SkColor2GrColor(skPaint.getColor()); const GrFragmentProcessor* fp = shader->asFragmentProcessor(context, viewM, NULL, skPaint.getFilterQuality(), grPaint->getProcessorDataManager()); if (!fp) { return false; } grPaint->addColorFragmentProcessor(fp)->unref(); constantColor = false; // The grcolor is automatically set when calling asFragmentProcessor. // If the shader can be seen as an effect it returns true and adds its effect to the grpaint. return SkPaint2GrPaintNoShader(context, rt, skPaint, paintColor, constantColor, grPaint); }
/* * 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); }
static inline bool skpaint_to_grpaint_impl(GrContext* context, const SkPaint& skPaint, const SkMatrix& viewM, const GrFragmentProcessor** shaderProcessor, SkXfermode::Mode* primColorMode, bool primitiveIsSrc, GrPaint* grPaint) { grPaint->setAntiAlias(skPaint.isAntiAlias()); // Setup the initial color considering the shader, the SkPaint color, and the presence or not // of per-vertex colors. SkAutoTUnref<const GrFragmentProcessor> aufp; const GrFragmentProcessor* shaderFP = nullptr; if (!primColorMode || blend_requires_shader(*primColorMode, primitiveIsSrc)) { if (shaderProcessor) { shaderFP = *shaderProcessor; } else if (const SkShader* shader = skPaint.getShader()) { aufp.reset(shader->asFragmentProcessor(context, viewM, nullptr, skPaint.getFilterQuality())); shaderFP = aufp; 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. GrColor shaderInput = SkColorToOpaqueGrColor(skPaint.getColor()); shaderFP = GrFragmentProcessor::OverrideInput(shaderFP, shaderInput); aufp.reset(shaderFP); if (primitiveIsSrc) { shaderFP = GrXfermodeFragmentProcessor::CreateFromDstProcessor(shaderFP, *primColorMode); } else { shaderFP = GrXfermodeFragmentProcessor::CreateFromSrcProcessor(shaderFP, *primColorMode); } aufp.reset(shaderFP); // The above may return null if compose results in a pass through of the prim color. if (shaderFP) { grPaint->addColorFragmentProcessor(shaderFP); } GrColor paintAlpha = SkColorAlphaToGrColor(skPaint.getColor()); if (GrColor_WHITE != paintAlpha) { grPaint->addColorFragmentProcessor(GrConstColorProcessor::Create( paintAlpha, GrConstColorProcessor::kModulateRGBA_InputMode))->unref(); } } else { // The shader's FP sees the paint unpremul color grPaint->setColor(SkColorToUnpremulGrColor(skPaint.getColor())); grPaint->addColorFragmentProcessor(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. SkAutoTUnref<const GrFragmentProcessor> processor( GrConstColorProcessor::Create(SkColorToOpaqueGrColor(skPaint.getColor()), GrConstColorProcessor::kIgnore_InputMode)); if (primitiveIsSrc) { processor.reset(GrXfermodeFragmentProcessor::CreateFromDstProcessor(processor, *primColorMode)); } else { processor.reset(GrXfermodeFragmentProcessor::CreateFromSrcProcessor(processor, *primColorMode)); } if (processor) { grPaint->addColorFragmentProcessor(processor); } grPaint->setColor(SkColorToOpaqueGrColor(skPaint.getColor())); GrColor paintAlpha = SkColorAlphaToGrColor(skPaint.getColor()); if (GrColor_WHITE != paintAlpha) { grPaint->addColorFragmentProcessor(GrConstColorProcessor::Create( paintAlpha, GrConstColorProcessor::kModulateRGBA_InputMode))->unref(); } } else { // No shader, no primitive color. grPaint->setColor(SkColorToPremulGrColor(skPaint.getColor())); applyColorFilterToPaintColor = true; } } SkColorFilter* colorFilter = skPaint.getColorFilter(); if (colorFilter) { if (applyColorFilterToPaintColor) { grPaint->setColor(SkColorToPremulGrColor(colorFilter->filterColor(skPaint.getColor()))); } else { SkAutoTUnref<const GrFragmentProcessor> cfFP( colorFilter->asFragmentProcessor(context)); if (cfFP) { grPaint->addColorFragmentProcessor(cfFP); } else { return false; } } } SkXfermode* mode = skPaint.getXfermode(); GrXPFactory* xpFactory = nullptr; if (!SkXfermode::AsXPFactory(mode, &xpFactory)) { // Fall back to src-over // return false here? xpFactory = GrPorterDuffXPFactory::Create(SkXfermode::kSrcOver_Mode); } SkASSERT(xpFactory); grPaint->setXPFactory(xpFactory)->unref(); #ifndef SK_IGNORE_GPU_DITHER if (skPaint.isDither() && grPaint->numColorFragmentProcessors() > 0) { grPaint->addColorFragmentProcessor(GrDitherEffect::Create())->unref(); } #endif return true; }
bool SkBitmapProcInfo::init(const SkMatrix& inv, const SkPaint& paint) { SkASSERT(inv.isScaleTranslate()); fPixmap.reset(); fInvMatrix = inv; fFilterQuality = paint.getFilterQuality(); fBMState = SkBitmapController::RequestBitmap(fProvider, inv, paint.getFilterQuality(), &fAlloc); // Note : we allow the controller to return an empty (zero-dimension) result. Should we? if (nullptr == fBMState || fBMState->pixmap().info().isEmpty()) { return false; } fPixmap = fBMState->pixmap(); fInvMatrix = fBMState->invMatrix(); fRealInvMatrix = fBMState->invMatrix(); fPaintColor = paint.getColor(); fFilterQuality = fBMState->quality(); SkASSERT(fFilterQuality <= kLow_SkFilterQuality); SkASSERT(fPixmap.addr()); bool integral_translate_only = just_trans_integral(fInvMatrix); if (!integral_translate_only) { // Most of the scanline procs deal with "unit" texture coordinates, as this // makes it easy to perform tiling modes (repeat = (x & 0xFFFF)). To generate // those, we divide the matrix by its dimensions here. // // We don't do this if we're either trivial (can ignore the matrix) or clamping // in both X and Y since clamping to width,height is just as easy as to 0xFFFF. if (fTileModeX != SkShader::kClamp_TileMode || fTileModeY != SkShader::kClamp_TileMode) { fInvMatrix.postIDiv(fPixmap.width(), fPixmap.height()); } // Now that all possible changes to the matrix have taken place, check // to see if we're really close to a no-scale matrix. If so, explicitly // set it to be so. Subsequent code may inspect this matrix to choose // a faster path in this case. // This code will only execute if the matrix has some scale component; // if it's already pure translate then we won't do this inversion. if (matrix_only_scale_translate(fInvMatrix)) { SkMatrix forward; if (fInvMatrix.invert(&forward) && just_trans_general(forward)) { fInvMatrix.setTranslate(-forward.getTranslateX(), -forward.getTranslateY()); } } // Recompute the flag after matrix adjustments. integral_translate_only = just_trans_integral(fInvMatrix); } fInvType = fInvMatrix.getType(); if (kLow_SkFilterQuality == fFilterQuality && (!valid_for_filtering(fPixmap.width() | fPixmap.height()) || integral_translate_only)) { fFilterQuality = kNone_SkFilterQuality; } return true; }
bool SkBitmapProcShader::asFragmentProcessor(GrContext* context, const SkPaint& paint, const SkMatrix& viewM, const SkMatrix* localMatrix, GrColor* paintColor, GrProcessorDataManager* procDataManager, GrFragmentProcessor** fp) const { SkMatrix matrix; matrix.setIDiv(fRawBitmap.width(), fRawBitmap.height()); SkMatrix lmInverse; if (!this->getLocalMatrix().invert(&lmInverse)) { return false; } if (localMatrix) { SkMatrix inv; if (!localMatrix->invert(&inv)) { return false; } lmInverse.postConcat(inv); } matrix.preConcat(lmInverse); SkShader::TileMode tm[] = { (TileMode)fTileModeX, (TileMode)fTileModeY, }; // Must set wrap and filter on the sampler before requesting a texture. In two places below // we check the matrix scale factors to determine how to interpret the filter quality setting. // This completely ignores the complexity of the drawVertices case where explicit local coords // are provided by the caller. bool useBicubic = false; GrTextureParams::FilterMode textureFilterMode; switch(paint.getFilterQuality()) { case kNone_SkFilterQuality: textureFilterMode = GrTextureParams::kNone_FilterMode; break; case kLow_SkFilterQuality: textureFilterMode = GrTextureParams::kBilerp_FilterMode; break; case kMedium_SkFilterQuality: { SkMatrix matrix; matrix.setConcat(viewM, this->getLocalMatrix()); if (matrix.getMinScale() < SK_Scalar1) { textureFilterMode = GrTextureParams::kMipMap_FilterMode; } else { // Don't trigger MIP level generation unnecessarily. textureFilterMode = GrTextureParams::kBilerp_FilterMode; } break; } case kHigh_SkFilterQuality: { SkMatrix matrix; matrix.setConcat(viewM, this->getLocalMatrix()); useBicubic = GrBicubicEffect::ShouldUseBicubic(matrix, &textureFilterMode); break; } default: SkErrorInternals::SetError( kInvalidPaint_SkError, "Sorry, I don't understand the filtering " "mode you asked for. Falling back to " "MIPMaps."); textureFilterMode = GrTextureParams::kMipMap_FilterMode; break; } GrTextureParams params(tm, textureFilterMode); SkAutoTUnref<GrTexture> texture(GrRefCachedBitmapTexture(context, fRawBitmap, ¶ms)); if (!texture) { SkErrorInternals::SetError( kInternalError_SkError, "Couldn't convert bitmap to texture."); return false; } *paintColor = (kAlpha_8_SkColorType == fRawBitmap.colorType()) ? SkColor2GrColor(paint.getColor()) : SkColor2GrColorJustAlpha(paint.getColor()); if (useBicubic) { *fp = GrBicubicEffect::Create(procDataManager, texture, matrix, tm); } else { *fp = GrSimpleTextureEffect::Create(procDataManager, texture, matrix, params); } return true; }
/* * Analyze filter-quality and matrix, and decide how to implement that. * * In general, we cascade down the request level [ High ... None ] * - for a given level, if we can fulfill it, fine, else * - else we downgrade to the next lower level and try again. * We can always fulfill requests for Low and None * - sometimes we will "ignore" Low and give None, but this is likely a legacy perf hack * and may be removed. */ bool SkBitmapProcState::chooseProcs(const SkMatrix& inv, const SkPaint& paint) { fPixmap.reset(); fInvMatrix = inv; fFilterLevel = paint.getFilterQuality(); const int origW = fProvider.info().width(); const int origH = fProvider.info().height(); bool allow_ignore_fractional_translate = true; // historical default if (kMedium_SkFilterQuality == fFilterLevel) { allow_ignore_fractional_translate = false; } SkDefaultBitmapController controller; fBMState = controller.requestBitmap(fProvider, inv, paint.getFilterQuality(), fBMStateStorage.get(), fBMStateStorage.size()); // Note : we allow the controller to return an empty (zero-dimension) result. Should we? if (nullptr == fBMState || fBMState->pixmap().info().isEmpty()) { return false; } fPixmap = fBMState->pixmap(); fInvMatrix = fBMState->invMatrix(); fFilterLevel = fBMState->quality(); SkASSERT(fPixmap.addr()); bool trivialMatrix = (fInvMatrix.getType() & ~SkMatrix::kTranslate_Mask) == 0; bool clampClamp = SkShader::kClamp_TileMode == fTileModeX && SkShader::kClamp_TileMode == fTileModeY; // Most of the scanline procs deal with "unit" texture coordinates, as this // makes it easy to perform tiling modes (repeat = (x & 0xFFFF)). To generate // those, we divide the matrix by its dimensions here. // // We don't do this if we're either trivial (can ignore the matrix) or clamping // in both X and Y since clamping to width,height is just as easy as to 0xFFFF. if (!(clampClamp || trivialMatrix)) { fInvMatrix.postIDiv(fPixmap.width(), fPixmap.height()); } // Now that all possible changes to the matrix have taken place, check // to see if we're really close to a no-scale matrix. If so, explicitly // set it to be so. Subsequent code may inspect this matrix to choose // a faster path in this case. // This code will only execute if the matrix has some scale component; // if it's already pure translate then we won't do this inversion. if (matrix_only_scale_translate(fInvMatrix)) { SkMatrix forward; if (fInvMatrix.invert(&forward)) { if ((clampClamp && allow_ignore_fractional_translate) ? just_trans_clamp(forward, fPixmap) : just_trans_general(forward)) { fInvMatrix.setTranslate(-forward.getTranslateX(), -forward.getTranslateY()); } } } fInvProc = fInvMatrix.getMapXYProc(); fInvType = fInvMatrix.getType(); fInvSx = SkScalarToFixed(fInvMatrix.getScaleX()); fInvSxFractionalInt = SkScalarToFractionalInt(fInvMatrix.getScaleX()); fInvKy = SkScalarToFixed(fInvMatrix.getSkewY()); fInvKyFractionalInt = SkScalarToFractionalInt(fInvMatrix.getSkewY()); fAlphaScale = SkAlpha255To256(paint.getAlpha()); fShaderProc32 = nullptr; fShaderProc16 = nullptr; fSampleProc32 = nullptr; // recompute the triviality of the matrix here because we may have // changed it! trivialMatrix = (fInvMatrix.getType() & ~SkMatrix::kTranslate_Mask) == 0; // If our target pixmap is the same as the original, then we revert back to legacy behavior // and allow the code to ignore fractional translate. // // The width/height check allows allow_ignore_fractional_translate to stay false if we // previously set it that way (e.g. we started in kMedium). // if (fPixmap.width() == origW && fPixmap.height() == origH) { allow_ignore_fractional_translate = true; } if (kLow_SkFilterQuality == fFilterLevel && allow_ignore_fractional_translate) { // Only try bilerp if the matrix is "interesting" and // the image has a suitable size. if (fInvType <= SkMatrix::kTranslate_Mask || !valid_for_filtering(fPixmap.width() | fPixmap.height())) { fFilterLevel = kNone_SkFilterQuality; } } return this->chooseScanlineProcs(trivialMatrix, clampClamp, paint); }
void SkBitmapDevice::drawBitmapRect(const SkDraw& draw, const SkBitmap& bitmap, const SkRect* src, const SkRect& dst, const SkPaint& paint, SkCanvas::SrcRectConstraint constraint) { SkMatrix matrix; SkRect bitmapBounds, tmpSrc, tmpDst; SkBitmap tmpBitmap; bitmapBounds.isetWH(bitmap.width(), bitmap.height()); // Compute matrix from the two rectangles if (src) { tmpSrc = *src; } else { tmpSrc = bitmapBounds; } matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit); LogDrawScaleFactor(SkMatrix::Concat(*draw.fMatrix, matrix), paint.getFilterQuality()); const SkRect* dstPtr = &dst; const SkBitmap* bitmapPtr = &bitmap; // clip the tmpSrc to the bounds of the bitmap, and recompute dstRect if // needed (if the src was clipped). No check needed if src==null. if (src) { if (!bitmapBounds.contains(*src)) { if (!tmpSrc.intersect(bitmapBounds)) { return; // nothing to draw } // recompute dst, based on the smaller tmpSrc matrix.mapRect(&tmpDst, tmpSrc); dstPtr = &tmpDst; } } if (src && !src->contains(bitmapBounds) && SkCanvas::kFast_SrcRectConstraint == constraint && paint.getFilterQuality() != kNone_SkFilterQuality) { // src is smaller than the bounds of the bitmap, and we are filtering, so we don't know // how much more of the bitmap we need, so we can't use extractSubset or drawBitmap, // but we must use a shader w/ dst bounds (which can access all of the bitmap needed). goto USE_SHADER; } if (src) { // since we may need to clamp to the borders of the src rect within // the bitmap, we extract a subset. const SkIRect srcIR = tmpSrc.roundOut(); if (!bitmap.extractSubset(&tmpBitmap, srcIR)) { return; } bitmapPtr = &tmpBitmap; // Since we did an extract, we need to adjust the matrix accordingly SkScalar dx = 0, dy = 0; if (srcIR.fLeft > 0) { dx = SkIntToScalar(srcIR.fLeft); } if (srcIR.fTop > 0) { dy = SkIntToScalar(srcIR.fTop); } if (dx || dy) { matrix.preTranslate(dx, dy); } SkRect extractedBitmapBounds; extractedBitmapBounds.isetWH(bitmapPtr->width(), bitmapPtr->height()); if (extractedBitmapBounds == tmpSrc) { // no fractional part in src, we can just call drawBitmap goto USE_DRAWBITMAP; } } else { USE_DRAWBITMAP: // We can go faster by just calling drawBitmap, which will concat the // matrix with the CTM, and try to call drawSprite if it can. If not, // it will make a shader and call drawRect, as we do below. if (CanApplyDstMatrixAsCTM(matrix, paint)) { draw.drawBitmap(*bitmapPtr, matrix, dstPtr, paint); return; } } USE_SHADER: // Since the shader need only live for our stack-frame, pass in a custom allocator. This // can save malloc calls, and signals to SkMakeBitmapShader to not try to copy the bitmap // if its mutable, since that precaution is not needed (give the short lifetime of the shader). SkTBlitterAllocator allocator; // construct a shader, so we can call drawRect with the dst auto s = SkMakeBitmapShader(*bitmapPtr, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode, &matrix, kNever_SkCopyPixelsMode, &allocator); if (!s) { return; } // we deliberately add a ref, since the allocator wants to be the last owner s.get()->ref(); SkPaint paintWithShader(paint); paintWithShader.setStyle(SkPaint::kFill_Style); paintWithShader.setShader(s); // Call ourself, in case the subclass wanted to share this setup code // but handle the drawRect code themselves. this->drawRect(draw, *dstPtr, paintWithShader); }
void SkBitmapDevice::drawBitmap(const SkDraw& draw, const SkBitmap& bitmap, const SkMatrix& matrix, const SkPaint& paint) { LogDrawScaleFactor(SkMatrix::Concat(*draw.fMatrix, matrix), paint.getFilterQuality()); draw.drawBitmap(bitmap, matrix, nullptr, paint); }
bool LightingShader::asFragmentProcessor(GrContext* context, const SkPaint& paint, const SkMatrix& viewM, const SkMatrix* localMatrix, GrColor* color, GrProcessorDataManager*, GrFragmentProcessor** fp) const { // we assume diffuse and normal maps have same width and height // TODO: support different sizes SkASSERT(fDiffuseMap.width() == fNormalMap.width() && fDiffuseMap.height() == fNormalMap.height()); SkMatrix matrix; matrix.setIDiv(fDiffuseMap.width(), fDiffuseMap.height()); SkMatrix lmInverse; if (!this->getLocalMatrix().invert(&lmInverse)) { return false; } if (localMatrix) { SkMatrix inv; if (!localMatrix->invert(&inv)) { return false; } lmInverse.postConcat(inv); } matrix.preConcat(lmInverse); // Must set wrap and filter on the sampler before requesting a texture. In two places below // we check the matrix scale factors to determine how to interpret the filter quality setting. // This completely ignores the complexity of the drawVertices case where explicit local coords // are provided by the caller. GrTextureParams::FilterMode textureFilterMode = GrTextureParams::kBilerp_FilterMode; switch (paint.getFilterQuality()) { case kNone_SkFilterQuality: textureFilterMode = GrTextureParams::kNone_FilterMode; break; case kLow_SkFilterQuality: textureFilterMode = GrTextureParams::kBilerp_FilterMode; break; case kMedium_SkFilterQuality:{ SkMatrix matrix; matrix.setConcat(viewM, this->getLocalMatrix()); if (matrix.getMinScale() < SK_Scalar1) { textureFilterMode = GrTextureParams::kMipMap_FilterMode; } else { // Don't trigger MIP level generation unnecessarily. textureFilterMode = GrTextureParams::kBilerp_FilterMode; } break; } case kHigh_SkFilterQuality: default: SkErrorInternals::SetError(kInvalidPaint_SkError, "Sorry, I don't understand the filtering " "mode you asked for. Falling back to " "MIPMaps."); textureFilterMode = GrTextureParams::kMipMap_FilterMode; break; } // TODO: support other tile modes GrTextureParams params(kClamp_TileMode, textureFilterMode); SkAutoTUnref<GrTexture> diffuseTexture(GrRefCachedBitmapTexture(context, fDiffuseMap, ¶ms)); if (!diffuseTexture) { SkErrorInternals::SetError(kInternalError_SkError, "Couldn't convert bitmap to texture."); return false; } SkAutoTUnref<GrTexture> normalTexture(GrRefCachedBitmapTexture(context, fNormalMap, ¶ms)); if (!normalTexture) { SkErrorInternals::SetError(kInternalError_SkError, "Couldn't convert bitmap to texture."); return false; } GrColor lightColor = GrColorPackRGBA(SkColorGetR(fLight.fColor), SkColorGetG(fLight.fColor), SkColorGetB(fLight.fColor), SkColorGetA(fLight.fColor)); GrColor ambientColor = GrColorPackRGBA(SkColorGetR(fAmbientColor), SkColorGetG(fAmbientColor), SkColorGetB(fAmbientColor), SkColorGetA(fAmbientColor)); *fp = SkNEW_ARGS(LightingFP, (diffuseTexture, normalTexture, matrix, fLight.fDirection, lightColor, ambientColor)); *color = GrColorPackA4(paint.getAlpha()); return true; }
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; }
void SkGpuDevice::drawTextureProducerImpl(GrTextureProducer* producer, const SkRect& clippedSrcRect, const SkRect& clippedDstRect, SkCanvas::SrcRectConstraint constraint, const SkMatrix& viewMatrix, const SkMatrix& srcToDstMatrix, const GrClip& clip, const SkPaint& paint) { // Specifying the texture coords as local coordinates is an attempt to enable more GrDrawOp // combining by not baking anything about the srcRect, dstRect, or viewMatrix, into the texture // FP. In the future this should be an opaque optimization enabled by the combination of // GrDrawOp/GP and FP. const SkMaskFilter* mf = paint.getMaskFilter(); // The shader expects proper local coords, so we can't replace local coords with texture coords // if the shader will be used. If we have a mask filter we will change the underlying geometry // that is rendered. bool canUseTextureCoordsAsLocalCoords = !use_shader(producer->isAlphaOnly(), paint) && !mf; bool doBicubic; GrSamplerParams::FilterMode fm = GrSkFilterQualityToGrFilterMode(paint.getFilterQuality(), viewMatrix, srcToDstMatrix, &doBicubic); const GrSamplerParams::FilterMode* filterMode = doBicubic ? nullptr : &fm; GrTextureProducer::FilterConstraint constraintMode; if (SkCanvas::kFast_SrcRectConstraint == constraint) { constraintMode = GrTextureAdjuster::kNo_FilterConstraint; } else { constraintMode = GrTextureAdjuster::kYes_FilterConstraint; } // If we have to outset for AA then we will generate texture coords outside the src rect. The // same happens for any mask filter that extends the bounds rendered in the dst. // This is conservative as a mask filter does not have to expand the bounds rendered. bool coordsAllInsideSrcRect = !paint.isAntiAlias() && !mf; // Check for optimization to drop the src rect constraint when on bilerp. if (filterMode && GrSamplerParams::kBilerp_FilterMode == *filterMode && GrTextureAdjuster::kYes_FilterConstraint == constraintMode && coordsAllInsideSrcRect) { SkMatrix combinedMatrix; combinedMatrix.setConcat(viewMatrix, srcToDstMatrix); if (can_ignore_bilerp_constraint(*producer, clippedSrcRect, combinedMatrix, fRenderTargetContext->isUnifiedMultisampled())) { constraintMode = GrTextureAdjuster::kNo_FilterConstraint; } } const SkMatrix* textureMatrix; SkMatrix tempMatrix; if (canUseTextureCoordsAsLocalCoords) { textureMatrix = &SkMatrix::I(); } else { if (!srcToDstMatrix.invert(&tempMatrix)) { return; } textureMatrix = &tempMatrix; } sk_sp<GrFragmentProcessor> fp(producer->createFragmentProcessor( *textureMatrix, clippedSrcRect, constraintMode, coordsAllInsideSrcRect, filterMode, fRenderTargetContext->getColorSpace())); if (!fp) { return; } GrPaint grPaint; if (!SkPaintToGrPaintWithTexture(fContext.get(), fRenderTargetContext.get(), paint, viewMatrix, fp, producer->isAlphaOnly(), &grPaint)) { return; } GrAA aa = GrBoolToAA(paint.isAntiAlias()); if (canUseTextureCoordsAsLocalCoords) { fRenderTargetContext->fillRectToRect(clip, std::move(grPaint), aa, viewMatrix, clippedDstRect, clippedSrcRect); return; } if (!mf) { fRenderTargetContext->drawRect(clip, std::move(grPaint), aa, viewMatrix, clippedDstRect); return; } // First see if we can do the draw + mask filter direct to the dst. if (viewMatrix.isScaleTranslate()) { SkRect devClippedDstRect; viewMatrix.mapRectScaleTranslate(&devClippedDstRect, clippedDstRect); SkStrokeRec rec(SkStrokeRec::kFill_InitStyle); if (mf->directFilterRRectMaskGPU(fContext.get(), fRenderTargetContext.get(), std::move(grPaint), clip, viewMatrix, rec, SkRRect::MakeRect(clippedDstRect), SkRRect::MakeRect(devClippedDstRect))) { return; } } SkPath rectPath; rectPath.addRect(clippedDstRect); rectPath.setIsVolatile(true); GrBlurUtils::drawPathWithMaskFilter(this->context(), fRenderTargetContext.get(), this->clip(), rectPath, std::move(grPaint), aa, viewMatrix, mf, GrStyle::SimpleFill(), true); }