bool SkRasterClip::op(const SkRect& localRect, const SkMatrix& matrix, const SkIRect& bounds, SkRegion::Op op, bool doAA) { AUTO_RASTERCLIP_VALIDATE(*this); SkRect devRect; if (fForceConservativeRects) { SkIRect ir; switch (mutate_conservative_op(&op, false)) { case kDoNothing_MutateResult: return !this->isEmpty(); case kReplaceClippedAgainstGlobalBounds_MutateResult: ir = bounds; break; case kContinue_MutateResult: matrix.mapRect(&devRect, localRect); ir = devRect.roundOut(); break; } return this->op(ir, op); } const bool isScaleTrans = matrix.isScaleTranslate(); if (!isScaleTrans) { SkPath path; path.addRect(localRect); path.setIsVolatile(true); return this->op(path, matrix, bounds, op, doAA); } matrix.mapRect(&devRect, localRect); if (fIsBW && doAA) { // check that the rect really needs aa, or is it close enought to // integer boundaries that we can just treat it as a BW rect? if (nearly_integral(devRect.fLeft) && nearly_integral(devRect.fTop) && nearly_integral(devRect.fRight) && nearly_integral(devRect.fBottom)) { doAA = false; } } if (fIsBW && !doAA) { SkIRect ir; devRect.round(&ir); (void)fBW.op(ir, op); } else { if (fIsBW) { this->convertToAA(); } (void)fAA.op(devRect, op, doAA); } return this->updateCacheAndReturnNonEmpty(); }
void SkLatticeIter::mapDstScaleTranslate(const SkMatrix& matrix) { SkASSERT(matrix.isScaleTranslate()); SkScalar tx = matrix.getTranslateX(); SkScalar sx = matrix.getScaleX(); for (int i = 0; i < fDstX.count(); i++) { fDstX[i] = fDstX[i] * sx + tx; } SkScalar ty = matrix.getTranslateY(); SkScalar sy = matrix.getScaleY(); for (int i = 0; i < fDstY.count(); i++) { fDstY[i] = fDstY[i] * sy + ty; } }
static bool legacy_shader_can_handle(const SkMatrix& inv) { if (!inv.isScaleTranslate()) { return false; } // legacy code uses SkFixed 32.32, so ensure the inverse doesn't map device coordinates // out of range. const SkScalar max_dev_coord = 32767.0f; SkRect src; SkAssertResult(inv.mapRect(&src, SkRect::MakeWH(max_dev_coord, max_dev_coord))); // take 1/4 of max signed 32bits so we have room to subtract local values const SkScalar max_fixed32dot32 = SK_MaxS32 * 0.25f; if (!SkRect::MakeLTRB(-max_fixed32dot32, -max_fixed32dot32, max_fixed32dot32, max_fixed32dot32).contains(src)) { return false; } // legacy shader impl should be able to handle these matrices return true; }
// TODO just use class params // TODO trying to figure out why lcd is so whack sk_sp<GrGeometryProcessor> GrAtlasTextBatch::setupDfProcessor(const SkMatrix& viewMatrix, SkColor filteredColor, GrColor color, GrTexture* texture) const { GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kBilerp_FilterMode); bool isLCD = this->isLCD(); // set up any flags uint32_t flags = viewMatrix.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0; flags |= viewMatrix.isScaleTranslate() ? kScaleOnly_DistanceFieldEffectFlag : 0; flags |= fUseGammaCorrectDistanceTable ? kGammaCorrect_DistanceFieldEffectFlag : 0; // see if we need to create a new effect if (isLCD) { flags |= kUseLCD_DistanceFieldEffectFlag; flags |= fUseBGR ? kBGR_DistanceFieldEffectFlag : 0; GrColor colorNoPreMul = skcolor_to_grcolor_nopremultiply(filteredColor); float redCorrection = fDistanceAdjustTable->getAdjustment( GrColorUnpackR(colorNoPreMul) >> kDistanceAdjustLumShift, fUseGammaCorrectDistanceTable); float greenCorrection = fDistanceAdjustTable->getAdjustment( GrColorUnpackG(colorNoPreMul) >> kDistanceAdjustLumShift, fUseGammaCorrectDistanceTable); float blueCorrection = fDistanceAdjustTable->getAdjustment( GrColorUnpackB(colorNoPreMul) >> kDistanceAdjustLumShift, fUseGammaCorrectDistanceTable); GrDistanceFieldLCDTextGeoProc::DistanceAdjust widthAdjust = GrDistanceFieldLCDTextGeoProc::DistanceAdjust::Make(redCorrection, greenCorrection, blueCorrection); return GrDistanceFieldLCDTextGeoProc::Make(color, viewMatrix, texture, params, widthAdjust, flags, this->usesLocalCoords()); } else {
bool SkRasterClip::op(const SkRect& localRect, const SkMatrix& matrix, const SkIRect& devBounds, SkRegion::Op op, bool doAA) { AUTO_RASTERCLIP_VALIDATE(*this); SkRect devRect; const bool isScaleTrans = matrix.isScaleTranslate(); if (!isScaleTrans) { SkPath path; path.addRect(localRect); path.setIsVolatile(true); return this->op(path, matrix, devBounds, op, doAA); } matrix.mapRect(&devRect, localRect); if (fIsBW && doAA) { // check that the rect really needs aa, or is it close enought to // integer boundaries that we can just treat it as a BW rect? if (nearly_integral(devRect.fLeft) && nearly_integral(devRect.fTop) && nearly_integral(devRect.fRight) && nearly_integral(devRect.fBottom)) { doAA = false; } } if (fIsBW && !doAA) { SkIRect ir; devRect.round(&ir); this->applyClipRestriction(op, &ir); (void)fBW.op(ir, op); } else { if (fIsBW) { this->convertToAA(); } this->applyClipRestriction(op, &devRect); (void)fAA.op(devRect, op, doAA); } return this->updateCacheAndReturnNonEmpty(); }
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 SkScalerContext_CairoFT::computeShapeMatrix(const SkMatrix& m) { // Compute a shape matrix compatible with Cairo's _compute_transform. // Finds major/minor scales and uses them to normalize the transform. double scaleX = m.getScaleX(); double skewX = m.getSkewX(); double skewY = m.getSkewY(); double scaleY = m.getScaleY(); double det = scaleX * scaleY - skewY * skewX; if (!std::isfinite(det)) { fScaleX = fRec.fTextSize * fRec.fPreScaleX; fScaleY = fRec.fTextSize; fHaveShape = false; return false; } double major = det != 0.0 ? hypot(scaleX, skewY) : 0.0; double minor = major != 0.0 ? fabs(det) / major : 0.0; // Limit scales to be above 1pt. major = SkTMax(major, 1.0); minor = SkTMax(minor, 1.0); // If the font is not scalable, then choose the best available size. CairoLockedFTFace faceLock(fScaledFont); FT_Face face = faceLock.getFace(); if (face && !FT_IS_SCALABLE(face)) { double bestDist = DBL_MAX; FT_Int bestSize = -1; for (FT_Int i = 0; i < face->num_fixed_sizes; i++) { // Distance is positive if strike is larger than desired size, // or negative if smaller. If previously a found smaller strike, // then prefer a larger strike. Otherwise, minimize distance. double dist = face->available_sizes[i].y_ppem / 64.0 - minor; if (bestDist < 0 ? dist >= bestDist : fabs(dist) <= bestDist) { bestDist = dist; bestSize = i; } } if (bestSize < 0) { fScaleX = fRec.fTextSize * fRec.fPreScaleX; fScaleY = fRec.fTextSize; fHaveShape = false; return false; } major = face->available_sizes[bestSize].x_ppem / 64.0; minor = face->available_sizes[bestSize].y_ppem / 64.0; fHaveShape = true; } else { fHaveShape = !m.isScaleTranslate(); } fScaleX = SkDoubleToScalar(major); fScaleY = SkDoubleToScalar(minor); if (fHaveShape) { // Normalize the transform and convert to fixed-point. double invScaleX = 65536.0 / major; double invScaleY = 65536.0 / minor; fShapeMatrix.xx = (FT_Fixed)(scaleX * invScaleX); fShapeMatrix.yx = -(FT_Fixed)(skewY * invScaleX); fShapeMatrix.xy = -(FT_Fixed)(skewX * invScaleY); fShapeMatrix.yy = (FT_Fixed)(scaleY * invScaleY); } 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); }