inline std::unique_ptr<GrAtlasTextOp> GrTextBlob::makeOp( const Run::SubRunInfo& info, int glyphCount, uint16_t run, uint16_t subRun, const SkMatrix& viewMatrix, SkScalar x, SkScalar y, const SkIRect& clipRect, const SkPaint& paint, GrColor filteredColor, const SkSurfaceProps& props, const GrDistanceFieldAdjustTable* distanceAdjustTable, GrTextTarget* target) { GrMaskFormat format = info.maskFormat(); GrPaint grPaint; target->makeGrPaint(info.maskFormat(), paint, viewMatrix, &grPaint); std::unique_ptr<GrAtlasTextOp> op; if (info.drawAsDistanceFields()) { // TODO: Can we be even smarter based on the dest transfer function? op = GrAtlasTextOp::MakeDistanceField( target->getContext(), std::move(grPaint), glyphCount, distanceAdjustTable, target->colorSpaceInfo().isLinearlyBlended(), paint.computeLuminanceColor(), props, info.isAntiAliased(), info.hasUseLCDText()); } else { op = GrAtlasTextOp::MakeBitmap(target->getContext(), std::move(grPaint), format, glyphCount, info.needsTransform()); } GrAtlasTextOp::Geometry& geometry = op->geometry(); geometry.fViewMatrix = viewMatrix; geometry.fClipRect = clipRect; geometry.fBlob = SkRef(this); geometry.fRun = run; geometry.fSubRun = subRun; geometry.fColor = info.maskFormat() == kARGB_GrMaskFormat ? GrColor_WHITE : filteredColor; geometry.fX = x; geometry.fY = y; op->init(); return op; }
bool GrTextBlob::mustRegenerate(const SkPaint& paint, const SkMaskFilterBase::BlurRec& blurRec, const SkMatrix& viewMatrix, SkScalar x, SkScalar y) { // If we have LCD text then our canonical color will be set to transparent, in this case we have // to regenerate the blob on any color change // We use the grPaint to get any color filter effects if (fKey.fCanonicalColor == SK_ColorTRANSPARENT && fLuminanceColor != paint.computeLuminanceColor()) { return true; } if (fInitialViewMatrix.hasPerspective() != viewMatrix.hasPerspective()) { return true; } /** This could be relaxed for blobs with only distance field glyphs. */ if (fInitialViewMatrix.hasPerspective() && !fInitialViewMatrix.cheapEqualTo(viewMatrix)) { return true; } // We only cache one masked version if (fKey.fHasBlur && (fBlurRec.fSigma != blurRec.fSigma || fBlurRec.fStyle != blurRec.fStyle)) { return true; } // Similarly, we only cache one version for each style if (fKey.fStyle != SkPaint::kFill_Style && (fStrokeInfo.fFrameWidth != paint.getStrokeWidth() || fStrokeInfo.fMiterLimit != paint.getStrokeMiter() || fStrokeInfo.fJoin != paint.getStrokeJoin())) { return true; } // Mixed blobs must be regenerated. We could probably figure out a way to do integer scrolls // for mixed blobs if this becomes an issue. if (this->hasBitmap() && this->hasDistanceField()) { // Identical viewmatrices and we can reuse in all cases if (fInitialViewMatrix.cheapEqualTo(viewMatrix) && x == fInitialX && y == fInitialY) { return false; } return true; } if (this->hasBitmap()) { if (fInitialViewMatrix.getScaleX() != viewMatrix.getScaleX() || fInitialViewMatrix.getScaleY() != viewMatrix.getScaleY() || fInitialViewMatrix.getSkewX() != viewMatrix.getSkewX() || fInitialViewMatrix.getSkewY() != viewMatrix.getSkewY()) { return true; } // We can update the positions in the cachedtextblobs without regenerating the whole blob, // but only for integer translations. // This cool bit of math will determine the necessary translation to apply to the already // generated vertex coordinates to move them to the correct position SkScalar transX = viewMatrix.getTranslateX() + viewMatrix.getScaleX() * (x - fInitialX) + viewMatrix.getSkewX() * (y - fInitialY) - fInitialViewMatrix.getTranslateX(); SkScalar transY = viewMatrix.getTranslateY() + viewMatrix.getSkewY() * (x - fInitialX) + viewMatrix.getScaleY() * (y - fInitialY) - fInitialViewMatrix.getTranslateY(); if (!SkScalarIsInt(transX) || !SkScalarIsInt(transY)) { return true; } } else if (this->hasDistanceField()) { // A scale outside of [blob.fMaxMinScale, blob.fMinMaxScale] would result in a different // distance field being generated, so we have to regenerate in those cases SkScalar newMaxScale = viewMatrix.getMaxScale(); SkScalar oldMaxScale = fInitialViewMatrix.getMaxScale(); SkScalar scaleAdjust = newMaxScale / oldMaxScale; if (scaleAdjust < fMaxMinScale || scaleAdjust > fMinMaxScale) { return true; } } // It is possible that a blob has neither distanceField nor bitmaptext. This is in the case // when all of the runs inside the blob are drawn as paths. In this case, we always regenerate // the blob anyways at flush time, so no need to regenerate explicitly return false; }