1cb93a386Sopenharmony_ci/* 2cb93a386Sopenharmony_ci * Copyright 2015 Google Inc. 3cb93a386Sopenharmony_ci * 4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be 5cb93a386Sopenharmony_ci * found in the LICENSE file. 6cb93a386Sopenharmony_ci */ 7cb93a386Sopenharmony_ci 8cb93a386Sopenharmony_ci#include "src/gpu/v1/Device_v1.h" 9cb93a386Sopenharmony_ci 10cb93a386Sopenharmony_ci#include "include/gpu/GrDirectContext.h" 11cb93a386Sopenharmony_ci#include "include/gpu/GrRecordingContext.h" 12cb93a386Sopenharmony_ci#include "include/private/SkTPin.h" 13cb93a386Sopenharmony_ci#include "src/core/SkDraw.h" 14cb93a386Sopenharmony_ci#include "src/core/SkImagePriv.h" 15cb93a386Sopenharmony_ci#include "src/core/SkMaskFilterBase.h" 16cb93a386Sopenharmony_ci#include "src/core/SkSpecialImage.h" 17cb93a386Sopenharmony_ci#include "src/gpu/GrBlurUtils.h" 18cb93a386Sopenharmony_ci#include "src/gpu/GrCaps.h" 19cb93a386Sopenharmony_ci#include "src/gpu/GrColorSpaceXform.h" 20cb93a386Sopenharmony_ci#include "src/gpu/GrOpsTypes.h" 21cb93a386Sopenharmony_ci#include "src/gpu/GrRecordingContextPriv.h" 22cb93a386Sopenharmony_ci#include "src/gpu/GrStyle.h" 23cb93a386Sopenharmony_ci#include "src/gpu/SkGr.h" 24cb93a386Sopenharmony_ci#include "src/gpu/effects/GrBicubicEffect.h" 25cb93a386Sopenharmony_ci#include "src/gpu/effects/GrBlendFragmentProcessor.h" 26cb93a386Sopenharmony_ci#include "src/gpu/effects/GrTextureEffect.h" 27cb93a386Sopenharmony_ci#include "src/gpu/geometry/GrRect.h" 28cb93a386Sopenharmony_ci#include "src/gpu/geometry/GrStyledShape.h" 29cb93a386Sopenharmony_ci#include "src/gpu/v1/SurfaceDrawContext_v1.h" 30cb93a386Sopenharmony_ci#include "src/image/SkImage_Base.h" 31cb93a386Sopenharmony_ci#include "src/image/SkImage_Gpu.h" 32cb93a386Sopenharmony_ci 33cb93a386Sopenharmony_cinamespace { 34cb93a386Sopenharmony_ci 35cb93a386Sopenharmony_ciinline bool use_shader(bool textureIsAlphaOnly, const SkPaint& paint) { 36cb93a386Sopenharmony_ci return textureIsAlphaOnly && paint.getShader(); 37cb93a386Sopenharmony_ci} 38cb93a386Sopenharmony_ci 39cb93a386Sopenharmony_ci////////////////////////////////////////////////////////////////////////////// 40cb93a386Sopenharmony_ci// Helper functions for dropping src rect subset with GrSamplerState::Filter::kLinear. 41cb93a386Sopenharmony_ci 42cb93a386Sopenharmony_cistatic const SkScalar kColorBleedTolerance = 0.001f; 43cb93a386Sopenharmony_ci 44cb93a386Sopenharmony_cibool has_aligned_samples(const SkRect& srcRect, const SkRect& transformedRect) { 45cb93a386Sopenharmony_ci // detect pixel disalignment 46cb93a386Sopenharmony_ci if (SkScalarAbs(SkScalarRoundToScalar(transformedRect.left()) - transformedRect.left()) < kColorBleedTolerance && 47cb93a386Sopenharmony_ci SkScalarAbs(SkScalarRoundToScalar(transformedRect.top()) - transformedRect.top()) < kColorBleedTolerance && 48cb93a386Sopenharmony_ci SkScalarAbs(transformedRect.width() - srcRect.width()) < kColorBleedTolerance && 49cb93a386Sopenharmony_ci SkScalarAbs(transformedRect.height() - srcRect.height()) < kColorBleedTolerance) { 50cb93a386Sopenharmony_ci return true; 51cb93a386Sopenharmony_ci } 52cb93a386Sopenharmony_ci return false; 53cb93a386Sopenharmony_ci} 54cb93a386Sopenharmony_ci 55cb93a386Sopenharmony_cibool may_color_bleed(const SkRect& srcRect, 56cb93a386Sopenharmony_ci const SkRect& transformedRect, 57cb93a386Sopenharmony_ci const SkMatrix& m, 58cb93a386Sopenharmony_ci int numSamples) { 59cb93a386Sopenharmony_ci // Only gets called if has_aligned_samples returned false. 60cb93a386Sopenharmony_ci // So we can assume that sampling is axis aligned but not texel aligned. 61cb93a386Sopenharmony_ci SkASSERT(!has_aligned_samples(srcRect, transformedRect)); 62cb93a386Sopenharmony_ci SkRect innerSrcRect(srcRect), innerTransformedRect, outerTransformedRect(transformedRect); 63cb93a386Sopenharmony_ci if (numSamples > 1) { 64cb93a386Sopenharmony_ci innerSrcRect.inset(SK_Scalar1, SK_Scalar1); 65cb93a386Sopenharmony_ci } else { 66cb93a386Sopenharmony_ci innerSrcRect.inset(SK_ScalarHalf, SK_ScalarHalf); 67cb93a386Sopenharmony_ci } 68cb93a386Sopenharmony_ci m.mapRect(&innerTransformedRect, innerSrcRect); 69cb93a386Sopenharmony_ci 70cb93a386Sopenharmony_ci // The gap between outerTransformedRect and innerTransformedRect 71cb93a386Sopenharmony_ci // represents the projection of the source border area, which is 72cb93a386Sopenharmony_ci // problematic for color bleeding. We must check whether any 73cb93a386Sopenharmony_ci // destination pixels sample the border area. 74cb93a386Sopenharmony_ci outerTransformedRect.inset(kColorBleedTolerance, kColorBleedTolerance); 75cb93a386Sopenharmony_ci innerTransformedRect.outset(kColorBleedTolerance, kColorBleedTolerance); 76cb93a386Sopenharmony_ci SkIRect outer, inner; 77cb93a386Sopenharmony_ci outerTransformedRect.round(&outer); 78cb93a386Sopenharmony_ci innerTransformedRect.round(&inner); 79cb93a386Sopenharmony_ci // If the inner and outer rects round to the same result, it means the 80cb93a386Sopenharmony_ci // border does not overlap any pixel centers. Yay! 81cb93a386Sopenharmony_ci return inner != outer; 82cb93a386Sopenharmony_ci} 83cb93a386Sopenharmony_ci 84cb93a386Sopenharmony_cibool can_ignore_linear_filtering_subset(const SkRect& srcSubset, 85cb93a386Sopenharmony_ci const SkMatrix& srcRectToDeviceSpace, 86cb93a386Sopenharmony_ci int numSamples) { 87cb93a386Sopenharmony_ci if (srcRectToDeviceSpace.rectStaysRect()) { 88cb93a386Sopenharmony_ci // sampling is axis-aligned 89cb93a386Sopenharmony_ci SkRect transformedRect; 90cb93a386Sopenharmony_ci srcRectToDeviceSpace.mapRect(&transformedRect, srcSubset); 91cb93a386Sopenharmony_ci 92cb93a386Sopenharmony_ci if (has_aligned_samples(srcSubset, transformedRect) || 93cb93a386Sopenharmony_ci !may_color_bleed(srcSubset, transformedRect, srcRectToDeviceSpace, numSamples)) { 94cb93a386Sopenharmony_ci return true; 95cb93a386Sopenharmony_ci } 96cb93a386Sopenharmony_ci } 97cb93a386Sopenharmony_ci return false; 98cb93a386Sopenharmony_ci} 99cb93a386Sopenharmony_ci 100cb93a386Sopenharmony_ci////////////////////////////////////////////////////////////////////////////// 101cb93a386Sopenharmony_ci// Helper functions for tiling a large SkBitmap 102cb93a386Sopenharmony_ci 103cb93a386Sopenharmony_cistatic const int kBmpSmallTileSize = 1 << 10; 104cb93a386Sopenharmony_ci 105cb93a386Sopenharmony_ciinline int get_tile_count(const SkIRect& srcRect, int tileSize) { 106cb93a386Sopenharmony_ci int tilesX = (srcRect.fRight / tileSize) - (srcRect.fLeft / tileSize) + 1; 107cb93a386Sopenharmony_ci int tilesY = (srcRect.fBottom / tileSize) - (srcRect.fTop / tileSize) + 1; 108cb93a386Sopenharmony_ci return tilesX * tilesY; 109cb93a386Sopenharmony_ci} 110cb93a386Sopenharmony_ci 111cb93a386Sopenharmony_ciint determine_tile_size(const SkIRect& src, int maxTileSize) { 112cb93a386Sopenharmony_ci if (maxTileSize <= kBmpSmallTileSize) { 113cb93a386Sopenharmony_ci return maxTileSize; 114cb93a386Sopenharmony_ci } 115cb93a386Sopenharmony_ci 116cb93a386Sopenharmony_ci size_t maxTileTotalTileSize = get_tile_count(src, maxTileSize); 117cb93a386Sopenharmony_ci size_t smallTotalTileSize = get_tile_count(src, kBmpSmallTileSize); 118cb93a386Sopenharmony_ci 119cb93a386Sopenharmony_ci maxTileTotalTileSize *= maxTileSize * maxTileSize; 120cb93a386Sopenharmony_ci smallTotalTileSize *= kBmpSmallTileSize * kBmpSmallTileSize; 121cb93a386Sopenharmony_ci 122cb93a386Sopenharmony_ci if (maxTileTotalTileSize > 2 * smallTotalTileSize) { 123cb93a386Sopenharmony_ci return kBmpSmallTileSize; 124cb93a386Sopenharmony_ci } else { 125cb93a386Sopenharmony_ci return maxTileSize; 126cb93a386Sopenharmony_ci } 127cb93a386Sopenharmony_ci} 128cb93a386Sopenharmony_ci 129cb93a386Sopenharmony_ci// Given a bitmap, an optional src rect, and a context with a clip and matrix determine what 130cb93a386Sopenharmony_ci// pixels from the bitmap are necessary. 131cb93a386Sopenharmony_ciSkIRect determine_clipped_src_rect(int width, int height, 132cb93a386Sopenharmony_ci const GrClip* clip, 133cb93a386Sopenharmony_ci const SkMatrix& viewMatrix, 134cb93a386Sopenharmony_ci const SkMatrix& srcToDstRect, 135cb93a386Sopenharmony_ci const SkISize& imageDimensions, 136cb93a386Sopenharmony_ci const SkRect* srcRectPtr) { 137cb93a386Sopenharmony_ci SkIRect clippedSrcIRect = clip ? clip->getConservativeBounds() 138cb93a386Sopenharmony_ci : SkIRect::MakeWH(width, height); 139cb93a386Sopenharmony_ci SkMatrix inv = SkMatrix::Concat(viewMatrix, srcToDstRect); 140cb93a386Sopenharmony_ci if (!inv.invert(&inv)) { 141cb93a386Sopenharmony_ci return SkIRect::MakeEmpty(); 142cb93a386Sopenharmony_ci } 143cb93a386Sopenharmony_ci SkRect clippedSrcRect = SkRect::Make(clippedSrcIRect); 144cb93a386Sopenharmony_ci inv.mapRect(&clippedSrcRect); 145cb93a386Sopenharmony_ci if (srcRectPtr) { 146cb93a386Sopenharmony_ci if (!clippedSrcRect.intersect(*srcRectPtr)) { 147cb93a386Sopenharmony_ci return SkIRect::MakeEmpty(); 148cb93a386Sopenharmony_ci } 149cb93a386Sopenharmony_ci } 150cb93a386Sopenharmony_ci clippedSrcRect.roundOut(&clippedSrcIRect); 151cb93a386Sopenharmony_ci SkIRect bmpBounds = SkIRect::MakeSize(imageDimensions); 152cb93a386Sopenharmony_ci if (!clippedSrcIRect.intersect(bmpBounds)) { 153cb93a386Sopenharmony_ci return SkIRect::MakeEmpty(); 154cb93a386Sopenharmony_ci } 155cb93a386Sopenharmony_ci 156cb93a386Sopenharmony_ci return clippedSrcIRect; 157cb93a386Sopenharmony_ci} 158cb93a386Sopenharmony_ci 159cb93a386Sopenharmony_ci// tileSize and clippedSubset are valid if true is returned 160cb93a386Sopenharmony_cibool should_tile_image_id(GrRecordingContext* context, 161cb93a386Sopenharmony_ci SkISize rtSize, 162cb93a386Sopenharmony_ci const GrClip* clip, 163cb93a386Sopenharmony_ci uint32_t imageID, 164cb93a386Sopenharmony_ci const SkISize& imageSize, 165cb93a386Sopenharmony_ci const SkMatrix& ctm, 166cb93a386Sopenharmony_ci const SkMatrix& srcToDst, 167cb93a386Sopenharmony_ci const SkRect* src, 168cb93a386Sopenharmony_ci int maxTileSize, 169cb93a386Sopenharmony_ci int* tileSize, 170cb93a386Sopenharmony_ci SkIRect* clippedSubset) { 171cb93a386Sopenharmony_ci // if it's larger than the max tile size, then we have no choice but tiling. 172cb93a386Sopenharmony_ci if (imageSize.width() > maxTileSize || imageSize.height() > maxTileSize) { 173cb93a386Sopenharmony_ci *clippedSubset = determine_clipped_src_rect(rtSize.width(), rtSize.height(), clip, ctm, 174cb93a386Sopenharmony_ci srcToDst, imageSize, src); 175cb93a386Sopenharmony_ci *tileSize = determine_tile_size(*clippedSubset, maxTileSize); 176cb93a386Sopenharmony_ci return true; 177cb93a386Sopenharmony_ci } 178cb93a386Sopenharmony_ci 179cb93a386Sopenharmony_ci // If the image would only produce 4 tiles of the smaller size, don't bother tiling it. 180cb93a386Sopenharmony_ci const size_t area = imageSize.width() * imageSize.height(); 181cb93a386Sopenharmony_ci if (area < 4 * kBmpSmallTileSize * kBmpSmallTileSize) { 182cb93a386Sopenharmony_ci return false; 183cb93a386Sopenharmony_ci } 184cb93a386Sopenharmony_ci 185cb93a386Sopenharmony_ci // At this point we know we could do the draw by uploading the entire bitmap as a texture. 186cb93a386Sopenharmony_ci // However, if the texture would be large compared to the cache size and we don't require most 187cb93a386Sopenharmony_ci // of it for this draw then tile to reduce the amount of upload and cache spill. 188cb93a386Sopenharmony_ci // NOTE: if the context is not a direct context, it doesn't have access to the resource cache, 189cb93a386Sopenharmony_ci // and theoretically, the resource cache's limits could be being changed on another thread, so 190cb93a386Sopenharmony_ci // even having access to just the limit wouldn't be a reliable test during recording here. 191cb93a386Sopenharmony_ci // Instead, we will just upload the entire image to be on the safe side and not tile. 192cb93a386Sopenharmony_ci auto direct = context->asDirectContext(); 193cb93a386Sopenharmony_ci if (!direct) { 194cb93a386Sopenharmony_ci return false; 195cb93a386Sopenharmony_ci } 196cb93a386Sopenharmony_ci 197cb93a386Sopenharmony_ci // assumption here is that sw bitmap size is a good proxy for its size as 198cb93a386Sopenharmony_ci // a texture 199cb93a386Sopenharmony_ci size_t bmpSize = area * sizeof(SkPMColor); // assume 32bit pixels 200cb93a386Sopenharmony_ci size_t cacheSize = direct->getResourceCacheLimit(); 201cb93a386Sopenharmony_ci if (bmpSize < cacheSize / 2) { 202cb93a386Sopenharmony_ci return false; 203cb93a386Sopenharmony_ci } 204cb93a386Sopenharmony_ci 205cb93a386Sopenharmony_ci // Figure out how much of the src we will need based on the src rect and clipping. Reject if 206cb93a386Sopenharmony_ci // tiling memory savings would be < 50%. 207cb93a386Sopenharmony_ci *clippedSubset = determine_clipped_src_rect(rtSize.width(), rtSize.height(), clip, ctm, 208cb93a386Sopenharmony_ci srcToDst, imageSize, src); 209cb93a386Sopenharmony_ci *tileSize = kBmpSmallTileSize; // already know whole bitmap fits in one max sized tile. 210cb93a386Sopenharmony_ci size_t usedTileBytes = get_tile_count(*clippedSubset, kBmpSmallTileSize) * 211cb93a386Sopenharmony_ci kBmpSmallTileSize * kBmpSmallTileSize * 212cb93a386Sopenharmony_ci sizeof(SkPMColor); // assume 32bit pixels; 213cb93a386Sopenharmony_ci 214cb93a386Sopenharmony_ci return usedTileBytes * 2 < bmpSize; 215cb93a386Sopenharmony_ci} 216cb93a386Sopenharmony_ci 217cb93a386Sopenharmony_ci// This method outsets 'iRect' by 'outset' all around and then clamps its extents to 218cb93a386Sopenharmony_ci// 'clamp'. 'offset' is adjusted to remain positioned over the top-left corner 219cb93a386Sopenharmony_ci// of 'iRect' for all possible outsets/clamps. 220cb93a386Sopenharmony_ciinline void clamped_outset_with_offset(SkIRect* iRect, int outset, SkPoint* offset, 221cb93a386Sopenharmony_ci const SkIRect& clamp) { 222cb93a386Sopenharmony_ci iRect->outset(outset, outset); 223cb93a386Sopenharmony_ci 224cb93a386Sopenharmony_ci int leftClampDelta = clamp.fLeft - iRect->fLeft; 225cb93a386Sopenharmony_ci if (leftClampDelta > 0) { 226cb93a386Sopenharmony_ci offset->fX -= outset - leftClampDelta; 227cb93a386Sopenharmony_ci iRect->fLeft = clamp.fLeft; 228cb93a386Sopenharmony_ci } else { 229cb93a386Sopenharmony_ci offset->fX -= outset; 230cb93a386Sopenharmony_ci } 231cb93a386Sopenharmony_ci 232cb93a386Sopenharmony_ci int topClampDelta = clamp.fTop - iRect->fTop; 233cb93a386Sopenharmony_ci if (topClampDelta > 0) { 234cb93a386Sopenharmony_ci offset->fY -= outset - topClampDelta; 235cb93a386Sopenharmony_ci iRect->fTop = clamp.fTop; 236cb93a386Sopenharmony_ci } else { 237cb93a386Sopenharmony_ci offset->fY -= outset; 238cb93a386Sopenharmony_ci } 239cb93a386Sopenharmony_ci 240cb93a386Sopenharmony_ci if (iRect->fRight > clamp.fRight) { 241cb93a386Sopenharmony_ci iRect->fRight = clamp.fRight; 242cb93a386Sopenharmony_ci } 243cb93a386Sopenharmony_ci if (iRect->fBottom > clamp.fBottom) { 244cb93a386Sopenharmony_ci iRect->fBottom = clamp.fBottom; 245cb93a386Sopenharmony_ci } 246cb93a386Sopenharmony_ci} 247cb93a386Sopenharmony_ci 248cb93a386Sopenharmony_ci////////////////////////////////////////////////////////////////////////////// 249cb93a386Sopenharmony_ci// Helper functions for drawing an image with v1::SurfaceDrawContext 250cb93a386Sopenharmony_ci 251cb93a386Sopenharmony_cienum class ImageDrawMode { 252cb93a386Sopenharmony_ci // Src and dst have been restricted to the image content. May need to clamp, no need to decal. 253cb93a386Sopenharmony_ci kOptimized, 254cb93a386Sopenharmony_ci // Src and dst are their original sizes, requires use of a decal instead of plain clamping. 255cb93a386Sopenharmony_ci // This is used when a dst clip is provided and extends outside of the optimized dst rect. 256cb93a386Sopenharmony_ci kDecal, 257cb93a386Sopenharmony_ci // Src or dst are empty, or do not intersect the image content so don't draw anything. 258cb93a386Sopenharmony_ci kSkip 259cb93a386Sopenharmony_ci}; 260cb93a386Sopenharmony_ci 261cb93a386Sopenharmony_ci/** 262cb93a386Sopenharmony_ci * Optimize the src rect sampling area within an image (sized 'width' x 'height') such that 263cb93a386Sopenharmony_ci * 'outSrcRect' will be completely contained in the image's bounds. The corresponding rect 264cb93a386Sopenharmony_ci * to draw will be output to 'outDstRect'. The mapping between src and dst will be cached in 265cb93a386Sopenharmony_ci * 'srcToDst'. Outputs are not always updated when kSkip is returned. 266cb93a386Sopenharmony_ci * 267cb93a386Sopenharmony_ci * If 'origSrcRect' is null, implicitly use the image bounds. If 'origDstRect' is null, use the 268cb93a386Sopenharmony_ci * original src rect. 'dstClip' should be null when there is no additional clipping. 269cb93a386Sopenharmony_ci */ 270cb93a386Sopenharmony_ciImageDrawMode optimize_sample_area(const SkISize& image, const SkRect* origSrcRect, 271cb93a386Sopenharmony_ci const SkRect* origDstRect, const SkPoint dstClip[4], 272cb93a386Sopenharmony_ci SkRect* outSrcRect, SkRect* outDstRect, 273cb93a386Sopenharmony_ci SkMatrix* srcToDst) { 274cb93a386Sopenharmony_ci SkRect srcBounds = SkRect::MakeIWH(image.fWidth, image.fHeight); 275cb93a386Sopenharmony_ci 276cb93a386Sopenharmony_ci SkRect src = origSrcRect ? *origSrcRect : srcBounds; 277cb93a386Sopenharmony_ci SkRect dst = origDstRect ? *origDstRect : src; 278cb93a386Sopenharmony_ci 279cb93a386Sopenharmony_ci if (src.isEmpty() || dst.isEmpty()) { 280cb93a386Sopenharmony_ci return ImageDrawMode::kSkip; 281cb93a386Sopenharmony_ci } 282cb93a386Sopenharmony_ci 283cb93a386Sopenharmony_ci if (outDstRect) { 284cb93a386Sopenharmony_ci *srcToDst = SkMatrix::RectToRect(src, dst); 285cb93a386Sopenharmony_ci } else { 286cb93a386Sopenharmony_ci srcToDst->setIdentity(); 287cb93a386Sopenharmony_ci } 288cb93a386Sopenharmony_ci 289cb93a386Sopenharmony_ci if (origSrcRect && !srcBounds.contains(src)) { 290cb93a386Sopenharmony_ci if (!src.intersect(srcBounds)) { 291cb93a386Sopenharmony_ci return ImageDrawMode::kSkip; 292cb93a386Sopenharmony_ci } 293cb93a386Sopenharmony_ci srcToDst->mapRect(&dst, src); 294cb93a386Sopenharmony_ci 295cb93a386Sopenharmony_ci // Both src and dst have gotten smaller. If dstClip is provided, confirm it is still 296cb93a386Sopenharmony_ci // contained in dst, otherwise cannot optimize the sample area and must use a decal instead 297cb93a386Sopenharmony_ci if (dstClip) { 298cb93a386Sopenharmony_ci for (int i = 0; i < 4; ++i) { 299cb93a386Sopenharmony_ci if (!dst.contains(dstClip[i].fX, dstClip[i].fY)) { 300cb93a386Sopenharmony_ci // Must resort to using a decal mode restricted to the clipped 'src', and 301cb93a386Sopenharmony_ci // use the original dst rect (filling in src bounds as needed) 302cb93a386Sopenharmony_ci *outSrcRect = src; 303cb93a386Sopenharmony_ci *outDstRect = (origDstRect ? *origDstRect 304cb93a386Sopenharmony_ci : (origSrcRect ? *origSrcRect : srcBounds)); 305cb93a386Sopenharmony_ci return ImageDrawMode::kDecal; 306cb93a386Sopenharmony_ci } 307cb93a386Sopenharmony_ci } 308cb93a386Sopenharmony_ci } 309cb93a386Sopenharmony_ci } 310cb93a386Sopenharmony_ci 311cb93a386Sopenharmony_ci // The original src and dst were fully contained in the image, or there was no dst clip to 312cb93a386Sopenharmony_ci // worry about, or the clip was still contained in the restricted dst rect. 313cb93a386Sopenharmony_ci *outSrcRect = src; 314cb93a386Sopenharmony_ci *outDstRect = dst; 315cb93a386Sopenharmony_ci return ImageDrawMode::kOptimized; 316cb93a386Sopenharmony_ci} 317cb93a386Sopenharmony_ci 318cb93a386Sopenharmony_ci/** 319cb93a386Sopenharmony_ci * Checks whether the paint is compatible with using SurfaceDrawContext::drawTexture. It is more 320cb93a386Sopenharmony_ci * efficient than the SkImage general case. 321cb93a386Sopenharmony_ci */ 322cb93a386Sopenharmony_cibool can_use_draw_texture(const SkPaint& paint, bool useCubicResampler, SkMipmapMode mm) { 323cb93a386Sopenharmony_ci return (!paint.getColorFilter() && !paint.getShader() && !paint.getMaskFilter() && 324cb93a386Sopenharmony_ci !paint.getImageFilter() && !paint.getBlender() && !useCubicResampler && 325cb93a386Sopenharmony_ci mm == SkMipmapMode::kNone); 326cb93a386Sopenharmony_ci} 327cb93a386Sopenharmony_ci 328cb93a386Sopenharmony_ciSkPMColor4f texture_color(SkColor4f paintColor, float entryAlpha, GrColorType srcColorType, 329cb93a386Sopenharmony_ci const GrColorInfo& dstColorInfo) { 330cb93a386Sopenharmony_ci paintColor.fA *= entryAlpha; 331cb93a386Sopenharmony_ci if (GrColorTypeIsAlphaOnly(srcColorType)) { 332cb93a386Sopenharmony_ci return SkColor4fPrepForDst(paintColor, dstColorInfo).premul(); 333cb93a386Sopenharmony_ci } else { 334cb93a386Sopenharmony_ci float paintAlpha = SkTPin(paintColor.fA, 0.f, 1.f); 335cb93a386Sopenharmony_ci return { paintAlpha, paintAlpha, paintAlpha, paintAlpha }; 336cb93a386Sopenharmony_ci } 337cb93a386Sopenharmony_ci} 338cb93a386Sopenharmony_ci 339cb93a386Sopenharmony_ci// Assumes srcRect and dstRect have already been optimized to fit the proxy 340cb93a386Sopenharmony_civoid draw_texture(skgpu::v1::SurfaceDrawContext* sdc, 341cb93a386Sopenharmony_ci const GrClip* clip, 342cb93a386Sopenharmony_ci const SkMatrix& ctm, 343cb93a386Sopenharmony_ci const SkPaint& paint, 344cb93a386Sopenharmony_ci GrSamplerState::Filter filter, 345cb93a386Sopenharmony_ci const SkRect& srcRect, 346cb93a386Sopenharmony_ci const SkRect& dstRect, 347cb93a386Sopenharmony_ci const SkPoint dstClip[4], 348cb93a386Sopenharmony_ci GrAA aa, 349cb93a386Sopenharmony_ci GrQuadAAFlags aaFlags, 350cb93a386Sopenharmony_ci SkCanvas::SrcRectConstraint constraint, 351cb93a386Sopenharmony_ci GrSurfaceProxyView view, 352cb93a386Sopenharmony_ci const GrColorInfo& srcColorInfo) { 353cb93a386Sopenharmony_ci if (GrColorTypeIsAlphaOnly(srcColorInfo.colorType())) { 354cb93a386Sopenharmony_ci view.concatSwizzle(GrSwizzle("aaaa")); 355cb93a386Sopenharmony_ci } 356cb93a386Sopenharmony_ci const GrColorInfo& dstInfo = sdc->colorInfo(); 357cb93a386Sopenharmony_ci auto textureXform = GrColorSpaceXform::Make(srcColorInfo, sdc->colorInfo()); 358cb93a386Sopenharmony_ci GrSurfaceProxy* proxy = view.proxy(); 359cb93a386Sopenharmony_ci // Must specify the strict constraint when the proxy is not functionally exact and the src 360cb93a386Sopenharmony_ci // rect would access pixels outside the proxy's content area without the constraint. 361cb93a386Sopenharmony_ci if (constraint != SkCanvas::kStrict_SrcRectConstraint && !proxy->isFunctionallyExact()) { 362cb93a386Sopenharmony_ci // Conservative estimate of how much a coord could be outset from src rect: 363cb93a386Sopenharmony_ci // 1/2 pixel for AA and 1/2 pixel for linear filtering 364cb93a386Sopenharmony_ci float buffer = 0.5f * (aa == GrAA::kYes) + 365cb93a386Sopenharmony_ci 0.5f * (filter == GrSamplerState::Filter::kLinear); 366cb93a386Sopenharmony_ci SkRect safeBounds = proxy->getBoundsRect(); 367cb93a386Sopenharmony_ci safeBounds.inset(buffer, buffer); 368cb93a386Sopenharmony_ci if (!safeBounds.contains(srcRect)) { 369cb93a386Sopenharmony_ci constraint = SkCanvas::kStrict_SrcRectConstraint; 370cb93a386Sopenharmony_ci } 371cb93a386Sopenharmony_ci } 372cb93a386Sopenharmony_ci 373cb93a386Sopenharmony_ci SkPMColor4f color = texture_color(paint.getColor4f(), 1.f, srcColorInfo.colorType(), dstInfo); 374cb93a386Sopenharmony_ci if (dstClip) { 375cb93a386Sopenharmony_ci // Get source coords corresponding to dstClip 376cb93a386Sopenharmony_ci SkPoint srcQuad[4]; 377cb93a386Sopenharmony_ci GrMapRectPoints(dstRect, srcRect, dstClip, srcQuad, 4); 378cb93a386Sopenharmony_ci 379cb93a386Sopenharmony_ci sdc->drawTextureQuad(clip, 380cb93a386Sopenharmony_ci std::move(view), 381cb93a386Sopenharmony_ci srcColorInfo.colorType(), 382cb93a386Sopenharmony_ci srcColorInfo.alphaType(), 383cb93a386Sopenharmony_ci filter, 384cb93a386Sopenharmony_ci GrSamplerState::MipmapMode::kNone, 385cb93a386Sopenharmony_ci paint.getBlendMode_or(SkBlendMode::kSrcOver), 386cb93a386Sopenharmony_ci color, 387cb93a386Sopenharmony_ci srcQuad, 388cb93a386Sopenharmony_ci dstClip, 389cb93a386Sopenharmony_ci aa, 390cb93a386Sopenharmony_ci aaFlags, 391cb93a386Sopenharmony_ci constraint == SkCanvas::kStrict_SrcRectConstraint ? &srcRect : nullptr, 392cb93a386Sopenharmony_ci ctm, 393cb93a386Sopenharmony_ci std::move(textureXform)); 394cb93a386Sopenharmony_ci } else { 395cb93a386Sopenharmony_ci sdc->drawTexture(clip, 396cb93a386Sopenharmony_ci std::move(view), 397cb93a386Sopenharmony_ci srcColorInfo.alphaType(), 398cb93a386Sopenharmony_ci filter, 399cb93a386Sopenharmony_ci GrSamplerState::MipmapMode::kNone, 400cb93a386Sopenharmony_ci paint.getBlendMode_or(SkBlendMode::kSrcOver), 401cb93a386Sopenharmony_ci color, 402cb93a386Sopenharmony_ci srcRect, 403cb93a386Sopenharmony_ci dstRect, 404cb93a386Sopenharmony_ci aa, 405cb93a386Sopenharmony_ci aaFlags, 406cb93a386Sopenharmony_ci constraint, 407cb93a386Sopenharmony_ci ctm, 408cb93a386Sopenharmony_ci std::move(textureXform)); 409cb93a386Sopenharmony_ci } 410cb93a386Sopenharmony_ci} 411cb93a386Sopenharmony_ci 412cb93a386Sopenharmony_ci// Assumes srcRect and dstRect have already been optimized to fit the proxy. 413cb93a386Sopenharmony_civoid draw_image(GrRecordingContext* rContext, 414cb93a386Sopenharmony_ci skgpu::v1::SurfaceDrawContext* sdc, 415cb93a386Sopenharmony_ci const GrClip* clip, 416cb93a386Sopenharmony_ci const SkMatrixProvider& matrixProvider, 417cb93a386Sopenharmony_ci const SkPaint& paint, 418cb93a386Sopenharmony_ci const SkImage_Base& image, 419cb93a386Sopenharmony_ci const SkRect& src, 420cb93a386Sopenharmony_ci const SkRect& dst, 421cb93a386Sopenharmony_ci const SkPoint dstClip[4], 422cb93a386Sopenharmony_ci const SkMatrix& srcToDst, 423cb93a386Sopenharmony_ci GrAA aa, 424cb93a386Sopenharmony_ci GrQuadAAFlags aaFlags, 425cb93a386Sopenharmony_ci SkCanvas::SrcRectConstraint constraint, 426cb93a386Sopenharmony_ci SkSamplingOptions sampling, 427cb93a386Sopenharmony_ci SkTileMode tm = SkTileMode::kClamp) { 428cb93a386Sopenharmony_ci const SkMatrix& ctm(matrixProvider.localToDevice()); 429cb93a386Sopenharmony_ci if (tm == SkTileMode::kClamp && 430cb93a386Sopenharmony_ci !image.isYUVA() && 431cb93a386Sopenharmony_ci can_use_draw_texture(paint, sampling.useCubic, sampling.mipmap)) { 432cb93a386Sopenharmony_ci // We've done enough checks above to allow us to pass ClampNearest() and not check for 433cb93a386Sopenharmony_ci // scaling adjustments. 434cb93a386Sopenharmony_ci auto [view, ct] = image.asView(rContext, GrMipmapped::kNo); 435cb93a386Sopenharmony_ci if (!view) { 436cb93a386Sopenharmony_ci return; 437cb93a386Sopenharmony_ci } 438cb93a386Sopenharmony_ci GrColorInfo info(image.imageInfo().colorInfo()); 439cb93a386Sopenharmony_ci info = info.makeColorType(ct); 440cb93a386Sopenharmony_ci draw_texture(sdc, 441cb93a386Sopenharmony_ci clip, 442cb93a386Sopenharmony_ci ctm, 443cb93a386Sopenharmony_ci paint, 444cb93a386Sopenharmony_ci sampling.filter, 445cb93a386Sopenharmony_ci src, 446cb93a386Sopenharmony_ci dst, 447cb93a386Sopenharmony_ci dstClip, 448cb93a386Sopenharmony_ci aa, 449cb93a386Sopenharmony_ci aaFlags, 450cb93a386Sopenharmony_ci constraint, 451cb93a386Sopenharmony_ci std::move(view), 452cb93a386Sopenharmony_ci info); 453cb93a386Sopenharmony_ci return; 454cb93a386Sopenharmony_ci } 455cb93a386Sopenharmony_ci 456cb93a386Sopenharmony_ci const SkMaskFilter* mf = paint.getMaskFilter(); 457cb93a386Sopenharmony_ci 458cb93a386Sopenharmony_ci // The shader expects proper local coords, so we can't replace local coords with texture coords 459cb93a386Sopenharmony_ci // if the shader will be used. If we have a mask filter we will change the underlying geometry 460cb93a386Sopenharmony_ci // that is rendered. 461cb93a386Sopenharmony_ci bool canUseTextureCoordsAsLocalCoords = !use_shader(image.isAlphaOnly(), paint) && !mf; 462cb93a386Sopenharmony_ci 463cb93a386Sopenharmony_ci // Specifying the texture coords as local coordinates is an attempt to enable more GrDrawOp 464cb93a386Sopenharmony_ci // combining by not baking anything about the srcRect, dstRect, or ctm, into the texture 465cb93a386Sopenharmony_ci // FP. In the future this should be an opaque optimization enabled by the combination of 466cb93a386Sopenharmony_ci // GrDrawOp/GP and FP. 467cb93a386Sopenharmony_ci if (mf && as_MFB(mf)->hasFragmentProcessor()) { 468cb93a386Sopenharmony_ci mf = nullptr; 469cb93a386Sopenharmony_ci } 470cb93a386Sopenharmony_ci 471cb93a386Sopenharmony_ci bool restrictToSubset = SkCanvas::kStrict_SrcRectConstraint == constraint; 472cb93a386Sopenharmony_ci 473cb93a386Sopenharmony_ci // If we have to outset for AA then we will generate texture coords outside the src rect. The 474cb93a386Sopenharmony_ci // same happens for any mask filter that extends the bounds rendered in the dst. 475cb93a386Sopenharmony_ci // This is conservative as a mask filter does not have to expand the bounds rendered. 476cb93a386Sopenharmony_ci bool coordsAllInsideSrcRect = aaFlags == GrQuadAAFlags::kNone && !mf; 477cb93a386Sopenharmony_ci 478cb93a386Sopenharmony_ci // Check for optimization to drop the src rect constraint when using linear filtering. 479cb93a386Sopenharmony_ci // TODO: Just rely on image to handle this. 480cb93a386Sopenharmony_ci if (!sampling.useCubic && 481cb93a386Sopenharmony_ci sampling.filter == SkFilterMode::kLinear && 482cb93a386Sopenharmony_ci restrictToSubset && 483cb93a386Sopenharmony_ci sampling.mipmap == SkMipmapMode::kNone && 484cb93a386Sopenharmony_ci coordsAllInsideSrcRect && 485cb93a386Sopenharmony_ci !image.isYUVA()) { 486cb93a386Sopenharmony_ci SkMatrix combinedMatrix; 487cb93a386Sopenharmony_ci combinedMatrix.setConcat(ctm, srcToDst); 488cb93a386Sopenharmony_ci if (can_ignore_linear_filtering_subset(src, combinedMatrix, sdc->numSamples())) { 489cb93a386Sopenharmony_ci restrictToSubset = false; 490cb93a386Sopenharmony_ci } 491cb93a386Sopenharmony_ci } 492cb93a386Sopenharmony_ci 493cb93a386Sopenharmony_ci SkMatrix textureMatrix; 494cb93a386Sopenharmony_ci if (canUseTextureCoordsAsLocalCoords) { 495cb93a386Sopenharmony_ci textureMatrix = SkMatrix::I(); 496cb93a386Sopenharmony_ci } else { 497cb93a386Sopenharmony_ci if (!srcToDst.invert(&textureMatrix)) { 498cb93a386Sopenharmony_ci return; 499cb93a386Sopenharmony_ci } 500cb93a386Sopenharmony_ci } 501cb93a386Sopenharmony_ci const SkRect* subset = restrictToSubset ? &src : nullptr; 502cb93a386Sopenharmony_ci const SkRect* domain = coordsAllInsideSrcRect ? &src : nullptr; 503cb93a386Sopenharmony_ci SkTileMode tileModes[] = {tm, tm}; 504cb93a386Sopenharmony_ci std::unique_ptr<GrFragmentProcessor> fp = image.asFragmentProcessor(rContext, 505cb93a386Sopenharmony_ci sampling, 506cb93a386Sopenharmony_ci tileModes, 507cb93a386Sopenharmony_ci textureMatrix, 508cb93a386Sopenharmony_ci subset, 509cb93a386Sopenharmony_ci domain); 510cb93a386Sopenharmony_ci fp = GrColorSpaceXformEffect::Make(std::move(fp), 511cb93a386Sopenharmony_ci image.imageInfo().colorInfo(), 512cb93a386Sopenharmony_ci sdc->colorInfo()); 513cb93a386Sopenharmony_ci if (image.isAlphaOnly()) { 514cb93a386Sopenharmony_ci if (const auto* shader = as_SB(paint.getShader())) { 515cb93a386Sopenharmony_ci auto shaderFP = shader->asFragmentProcessor( 516cb93a386Sopenharmony_ci GrFPArgs(rContext, matrixProvider, &sdc->colorInfo())); 517cb93a386Sopenharmony_ci if (!shaderFP) { 518cb93a386Sopenharmony_ci return; 519cb93a386Sopenharmony_ci } 520cb93a386Sopenharmony_ci fp = GrBlendFragmentProcessor::Make( 521cb93a386Sopenharmony_ci std::move(fp), std::move(shaderFP), SkBlendMode::kDstIn); 522cb93a386Sopenharmony_ci } else { 523cb93a386Sopenharmony_ci // Multiply the input (paint) color by the texture (alpha) 524cb93a386Sopenharmony_ci fp = GrFragmentProcessor::MulInputByChildAlpha(std::move(fp)); 525cb93a386Sopenharmony_ci } 526cb93a386Sopenharmony_ci } 527cb93a386Sopenharmony_ci 528cb93a386Sopenharmony_ci GrPaint grPaint; 529cb93a386Sopenharmony_ci if (!SkPaintToGrPaintReplaceShader( 530cb93a386Sopenharmony_ci rContext, sdc->colorInfo(), paint, matrixProvider, std::move(fp), &grPaint)) { 531cb93a386Sopenharmony_ci return; 532cb93a386Sopenharmony_ci } 533cb93a386Sopenharmony_ci 534cb93a386Sopenharmony_ci if (!mf) { 535cb93a386Sopenharmony_ci // Can draw the image directly (any mask filter on the paint was converted to an FP already) 536cb93a386Sopenharmony_ci if (dstClip) { 537cb93a386Sopenharmony_ci SkPoint srcClipPoints[4]; 538cb93a386Sopenharmony_ci SkPoint* srcClip = nullptr; 539cb93a386Sopenharmony_ci if (canUseTextureCoordsAsLocalCoords) { 540cb93a386Sopenharmony_ci // Calculate texture coordinates that match the dst clip 541cb93a386Sopenharmony_ci GrMapRectPoints(dst, src, dstClip, srcClipPoints, 4); 542cb93a386Sopenharmony_ci srcClip = srcClipPoints; 543cb93a386Sopenharmony_ci } 544cb93a386Sopenharmony_ci sdc->fillQuadWithEdgeAA(clip, std::move(grPaint), aa, aaFlags, ctm, dstClip, srcClip); 545cb93a386Sopenharmony_ci } else { 546cb93a386Sopenharmony_ci // Provide explicit texture coords when possible, otherwise rely on texture matrix 547cb93a386Sopenharmony_ci sdc->fillRectWithEdgeAA(clip, std::move(grPaint), aa, aaFlags, ctm, dst, 548cb93a386Sopenharmony_ci canUseTextureCoordsAsLocalCoords ? &src : nullptr); 549cb93a386Sopenharmony_ci } 550cb93a386Sopenharmony_ci } else { 551cb93a386Sopenharmony_ci // Must draw the mask filter as a GrStyledShape. For now, this loses the per-edge AA 552cb93a386Sopenharmony_ci // information since it always draws with AA, but that should not be noticeable since the 553cb93a386Sopenharmony_ci // mask filter is probably a blur. 554cb93a386Sopenharmony_ci GrStyledShape shape; 555cb93a386Sopenharmony_ci if (dstClip) { 556cb93a386Sopenharmony_ci // Represent it as an SkPath formed from the dstClip 557cb93a386Sopenharmony_ci SkPath path; 558cb93a386Sopenharmony_ci path.addPoly(dstClip, 4, true); 559cb93a386Sopenharmony_ci shape = GrStyledShape(path); 560cb93a386Sopenharmony_ci } else { 561cb93a386Sopenharmony_ci shape = GrStyledShape(dst); 562cb93a386Sopenharmony_ci } 563cb93a386Sopenharmony_ci 564cb93a386Sopenharmony_ci GrBlurUtils::drawShapeWithMaskFilter( 565cb93a386Sopenharmony_ci rContext, sdc, clip, shape, std::move(grPaint), ctm, mf); 566cb93a386Sopenharmony_ci } 567cb93a386Sopenharmony_ci} 568cb93a386Sopenharmony_ci 569cb93a386Sopenharmony_civoid draw_tiled_bitmap(GrRecordingContext* rContext, 570cb93a386Sopenharmony_ci skgpu::v1::SurfaceDrawContext* sdc, 571cb93a386Sopenharmony_ci const GrClip* clip, 572cb93a386Sopenharmony_ci const SkBitmap& bitmap, 573cb93a386Sopenharmony_ci int tileSize, 574cb93a386Sopenharmony_ci const SkMatrixProvider& matrixProvider, 575cb93a386Sopenharmony_ci const SkMatrix& srcToDst, 576cb93a386Sopenharmony_ci const SkRect& srcRect, 577cb93a386Sopenharmony_ci const SkIRect& clippedSrcIRect, 578cb93a386Sopenharmony_ci const SkPaint& paint, 579cb93a386Sopenharmony_ci GrAA aa, 580cb93a386Sopenharmony_ci SkCanvas::SrcRectConstraint constraint, 581cb93a386Sopenharmony_ci SkSamplingOptions sampling, 582cb93a386Sopenharmony_ci SkTileMode tileMode) { 583cb93a386Sopenharmony_ci SkRect clippedSrcRect = SkRect::Make(clippedSrcIRect); 584cb93a386Sopenharmony_ci 585cb93a386Sopenharmony_ci int nx = bitmap.width() / tileSize; 586cb93a386Sopenharmony_ci int ny = bitmap.height() / tileSize; 587cb93a386Sopenharmony_ci 588cb93a386Sopenharmony_ci for (int x = 0; x <= nx; x++) { 589cb93a386Sopenharmony_ci for (int y = 0; y <= ny; y++) { 590cb93a386Sopenharmony_ci SkRect tileR; 591cb93a386Sopenharmony_ci tileR.setLTRB(SkIntToScalar(x * tileSize), SkIntToScalar(y * tileSize), 592cb93a386Sopenharmony_ci SkIntToScalar((x + 1) * tileSize), SkIntToScalar((y + 1) * tileSize)); 593cb93a386Sopenharmony_ci 594cb93a386Sopenharmony_ci if (!SkRect::Intersects(tileR, clippedSrcRect)) { 595cb93a386Sopenharmony_ci continue; 596cb93a386Sopenharmony_ci } 597cb93a386Sopenharmony_ci 598cb93a386Sopenharmony_ci if (!tileR.intersect(srcRect)) { 599cb93a386Sopenharmony_ci continue; 600cb93a386Sopenharmony_ci } 601cb93a386Sopenharmony_ci 602cb93a386Sopenharmony_ci SkIRect iTileR; 603cb93a386Sopenharmony_ci tileR.roundOut(&iTileR); 604cb93a386Sopenharmony_ci SkVector offset = SkPoint::Make(SkIntToScalar(iTileR.fLeft), 605cb93a386Sopenharmony_ci SkIntToScalar(iTileR.fTop)); 606cb93a386Sopenharmony_ci SkRect rectToDraw = tileR; 607cb93a386Sopenharmony_ci srcToDst.mapRect(&rectToDraw); 608cb93a386Sopenharmony_ci if (sampling.filter != SkFilterMode::kNearest || sampling.useCubic) { 609cb93a386Sopenharmony_ci SkIRect iClampRect; 610cb93a386Sopenharmony_ci 611cb93a386Sopenharmony_ci if (SkCanvas::kFast_SrcRectConstraint == constraint) { 612cb93a386Sopenharmony_ci // In bleed mode we want to always expand the tile on all edges 613cb93a386Sopenharmony_ci // but stay within the bitmap bounds 614cb93a386Sopenharmony_ci iClampRect = SkIRect::MakeWH(bitmap.width(), bitmap.height()); 615cb93a386Sopenharmony_ci } else { 616cb93a386Sopenharmony_ci // In texture-domain/clamp mode we only want to expand the 617cb93a386Sopenharmony_ci // tile on edges interior to "srcRect" (i.e., we want to 618cb93a386Sopenharmony_ci // not bleed across the original clamped edges) 619cb93a386Sopenharmony_ci srcRect.roundOut(&iClampRect); 620cb93a386Sopenharmony_ci } 621cb93a386Sopenharmony_ci int outset = sampling.useCubic ? GrBicubicEffect::kFilterTexelPad : 1; 622cb93a386Sopenharmony_ci clamped_outset_with_offset(&iTileR, outset, &offset, iClampRect); 623cb93a386Sopenharmony_ci } 624cb93a386Sopenharmony_ci 625cb93a386Sopenharmony_ci // We must subset as a bitmap and then turn into an SkImage if we want caching to work. 626cb93a386Sopenharmony_ci // Image subsets always make a copy of the pixels and lose the association with the 627cb93a386Sopenharmony_ci // original's SkPixelRef. 628cb93a386Sopenharmony_ci if (SkBitmap subsetBmp; bitmap.extractSubset(&subsetBmp, iTileR)) { 629cb93a386Sopenharmony_ci auto image = SkMakeImageFromRasterBitmap(subsetBmp, kNever_SkCopyPixelsMode); 630cb93a386Sopenharmony_ci // We should have already handled bitmaps larger than the max texture size. 631cb93a386Sopenharmony_ci SkASSERT(image->width() <= rContext->priv().caps()->maxTextureSize() && 632cb93a386Sopenharmony_ci image->height() <= rContext->priv().caps()->maxTextureSize()); 633cb93a386Sopenharmony_ci 634cb93a386Sopenharmony_ci GrQuadAAFlags aaFlags = GrQuadAAFlags::kNone; 635cb93a386Sopenharmony_ci if (aa == GrAA::kYes) { 636cb93a386Sopenharmony_ci // If the entire bitmap was anti-aliased, turn on AA for the outside tile edges. 637cb93a386Sopenharmony_ci if (tileR.fLeft <= srcRect.fLeft) { 638cb93a386Sopenharmony_ci aaFlags |= GrQuadAAFlags::kLeft; 639cb93a386Sopenharmony_ci } 640cb93a386Sopenharmony_ci if (tileR.fRight >= srcRect.fRight) { 641cb93a386Sopenharmony_ci aaFlags |= GrQuadAAFlags::kRight; 642cb93a386Sopenharmony_ci } 643cb93a386Sopenharmony_ci if (tileR.fTop <= srcRect.fTop) { 644cb93a386Sopenharmony_ci aaFlags |= GrQuadAAFlags::kTop; 645cb93a386Sopenharmony_ci } 646cb93a386Sopenharmony_ci if (tileR.fBottom >= srcRect.fBottom) { 647cb93a386Sopenharmony_ci aaFlags |= GrQuadAAFlags::kBottom; 648cb93a386Sopenharmony_ci } 649cb93a386Sopenharmony_ci } 650cb93a386Sopenharmony_ci 651cb93a386Sopenharmony_ci // now offset it to make it "local" to our tmp bitmap 652cb93a386Sopenharmony_ci tileR.offset(-offset.fX, -offset.fY); 653cb93a386Sopenharmony_ci SkMatrix offsetSrcToDst = srcToDst; 654cb93a386Sopenharmony_ci offsetSrcToDst.preTranslate(offset.fX, offset.fY); 655cb93a386Sopenharmony_ci draw_image(rContext, 656cb93a386Sopenharmony_ci sdc, 657cb93a386Sopenharmony_ci clip, 658cb93a386Sopenharmony_ci matrixProvider, 659cb93a386Sopenharmony_ci paint, 660cb93a386Sopenharmony_ci *as_IB(image.get()), 661cb93a386Sopenharmony_ci tileR, 662cb93a386Sopenharmony_ci rectToDraw, 663cb93a386Sopenharmony_ci nullptr, 664cb93a386Sopenharmony_ci offsetSrcToDst, 665cb93a386Sopenharmony_ci aa, 666cb93a386Sopenharmony_ci aaFlags, 667cb93a386Sopenharmony_ci constraint, 668cb93a386Sopenharmony_ci sampling, 669cb93a386Sopenharmony_ci tileMode); 670cb93a386Sopenharmony_ci } 671cb93a386Sopenharmony_ci } 672cb93a386Sopenharmony_ci } 673cb93a386Sopenharmony_ci} 674cb93a386Sopenharmony_ci 675cb93a386Sopenharmony_ciSkFilterMode downgrade_to_filter(const SkSamplingOptions& sampling) { 676cb93a386Sopenharmony_ci SkFilterMode filter = sampling.filter; 677cb93a386Sopenharmony_ci if (sampling.useCubic || sampling.mipmap != SkMipmapMode::kNone) { 678cb93a386Sopenharmony_ci // if we were "fancier" than just bilerp, only do bilerp 679cb93a386Sopenharmony_ci filter = SkFilterMode::kLinear; 680cb93a386Sopenharmony_ci } 681cb93a386Sopenharmony_ci return filter; 682cb93a386Sopenharmony_ci} 683cb93a386Sopenharmony_ci 684cb93a386Sopenharmony_cibool can_disable_mipmap(const SkMatrix& viewM, 685cb93a386Sopenharmony_ci const SkMatrix& localM, 686cb93a386Sopenharmony_ci bool sharpenMipmappedTextures) { 687cb93a386Sopenharmony_ci SkMatrix matrix; 688cb93a386Sopenharmony_ci matrix.setConcat(viewM, localM); 689cb93a386Sopenharmony_ci // With sharp mips, we bias lookups by -0.5. That means our final LOD is >= 0 until 690cb93a386Sopenharmony_ci // the computed LOD is >= 0.5. At what scale factor does a texture get an LOD of 691cb93a386Sopenharmony_ci // 0.5? 692cb93a386Sopenharmony_ci // 693cb93a386Sopenharmony_ci // Want: 0 = log2(1/s) - 0.5 694cb93a386Sopenharmony_ci // 0.5 = log2(1/s) 695cb93a386Sopenharmony_ci // 2^0.5 = 1/s 696cb93a386Sopenharmony_ci // 1/2^0.5 = s 697cb93a386Sopenharmony_ci // 2^0.5/2 = s 698cb93a386Sopenharmony_ci SkScalar mipScale = sharpenMipmappedTextures ? SK_ScalarRoot2Over2 : SK_Scalar1; 699cb93a386Sopenharmony_ci return matrix.getMinScale() >= mipScale; 700cb93a386Sopenharmony_ci} 701cb93a386Sopenharmony_ci 702cb93a386Sopenharmony_ci} // anonymous namespace 703cb93a386Sopenharmony_ci 704cb93a386Sopenharmony_ci////////////////////////////////////////////////////////////////////////////// 705cb93a386Sopenharmony_ci 706cb93a386Sopenharmony_cinamespace skgpu::v1 { 707cb93a386Sopenharmony_ci 708cb93a386Sopenharmony_civoid Device::drawSpecial(SkSpecialImage* special, 709cb93a386Sopenharmony_ci const SkMatrix& localToDevice, 710cb93a386Sopenharmony_ci const SkSamplingOptions& origSampling, 711cb93a386Sopenharmony_ci const SkPaint& paint) { 712cb93a386Sopenharmony_ci SkASSERT(!paint.getMaskFilter() && !paint.getImageFilter()); 713cb93a386Sopenharmony_ci SkASSERT(special->isTextureBacked()); 714cb93a386Sopenharmony_ci 715cb93a386Sopenharmony_ci SkRect src = SkRect::Make(special->subset()); 716cb93a386Sopenharmony_ci SkRect dst = SkRect::MakeWH(special->width(), special->height()); 717cb93a386Sopenharmony_ci SkMatrix srcToDst = SkMatrix::RectToRect(src, dst); 718cb93a386Sopenharmony_ci 719cb93a386Sopenharmony_ci SkSamplingOptions sampling = SkSamplingOptions(downgrade_to_filter(origSampling)); 720cb93a386Sopenharmony_ci GrAA aa = fSurfaceDrawContext->chooseAA(paint); 721cb93a386Sopenharmony_ci GrQuadAAFlags aaFlags = (aa == GrAA::kYes) ? GrQuadAAFlags::kAll : GrQuadAAFlags::kNone; 722cb93a386Sopenharmony_ci 723cb93a386Sopenharmony_ci SkColorInfo colorInfo(special->colorType(), 724cb93a386Sopenharmony_ci special->alphaType(), 725cb93a386Sopenharmony_ci sk_ref_sp(special->getColorSpace())); 726cb93a386Sopenharmony_ci 727cb93a386Sopenharmony_ci GrSurfaceProxyView view = special->view(this->recordingContext()); 728cb93a386Sopenharmony_ci SkImage_Gpu image(sk_ref_sp(special->getContext()), 729cb93a386Sopenharmony_ci special->uniqueID(), 730cb93a386Sopenharmony_ci std::move(view), 731cb93a386Sopenharmony_ci std::move(colorInfo)); 732cb93a386Sopenharmony_ci // In most cases this ought to hit draw_texture since there won't be a color filter, 733cb93a386Sopenharmony_ci // alpha-only texture+shader, or a high filter quality. 734cb93a386Sopenharmony_ci SkOverrideDeviceMatrixProvider matrixProvider(this->asMatrixProvider(), localToDevice); 735cb93a386Sopenharmony_ci draw_image(fContext.get(), 736cb93a386Sopenharmony_ci fSurfaceDrawContext.get(), 737cb93a386Sopenharmony_ci this->clip(), 738cb93a386Sopenharmony_ci matrixProvider, 739cb93a386Sopenharmony_ci paint, 740cb93a386Sopenharmony_ci image, 741cb93a386Sopenharmony_ci src, 742cb93a386Sopenharmony_ci dst, 743cb93a386Sopenharmony_ci nullptr, 744cb93a386Sopenharmony_ci srcToDst, 745cb93a386Sopenharmony_ci aa, 746cb93a386Sopenharmony_ci aaFlags, 747cb93a386Sopenharmony_ci SkCanvas::kStrict_SrcRectConstraint, 748cb93a386Sopenharmony_ci sampling); 749cb93a386Sopenharmony_ci} 750cb93a386Sopenharmony_ci 751cb93a386Sopenharmony_civoid Device::drawImageQuad(const SkImage* image, 752cb93a386Sopenharmony_ci const SkRect* srcRect, 753cb93a386Sopenharmony_ci const SkRect* dstRect, 754cb93a386Sopenharmony_ci const SkPoint dstClip[4], 755cb93a386Sopenharmony_ci GrAA aa, 756cb93a386Sopenharmony_ci GrQuadAAFlags aaFlags, 757cb93a386Sopenharmony_ci const SkMatrix* preViewMatrix, 758cb93a386Sopenharmony_ci const SkSamplingOptions& origSampling, 759cb93a386Sopenharmony_ci const SkPaint& paint, 760cb93a386Sopenharmony_ci SkCanvas::SrcRectConstraint constraint) { 761cb93a386Sopenharmony_ci SkRect src; 762cb93a386Sopenharmony_ci SkRect dst; 763cb93a386Sopenharmony_ci SkMatrix srcToDst; 764cb93a386Sopenharmony_ci ImageDrawMode mode = optimize_sample_area(SkISize::Make(image->width(), image->height()), 765cb93a386Sopenharmony_ci srcRect, dstRect, dstClip, &src, &dst, &srcToDst); 766cb93a386Sopenharmony_ci if (mode == ImageDrawMode::kSkip) { 767cb93a386Sopenharmony_ci return; 768cb93a386Sopenharmony_ci } 769cb93a386Sopenharmony_ci 770cb93a386Sopenharmony_ci // OH ISSUE: restricting the drawing of abnormal processes 771cb93a386Sopenharmony_ci if (fContext->isPidAbnormal()) { 772cb93a386Sopenharmony_ci return; 773cb93a386Sopenharmony_ci } 774cb93a386Sopenharmony_ci 775cb93a386Sopenharmony_ci if (src.contains(image->bounds())) { 776cb93a386Sopenharmony_ci constraint = SkCanvas::kFast_SrcRectConstraint; 777cb93a386Sopenharmony_ci } 778cb93a386Sopenharmony_ci // Depending on the nature of image, it can flow through more or less optimal pipelines 779cb93a386Sopenharmony_ci SkTileMode tileMode = mode == ImageDrawMode::kDecal ? SkTileMode::kDecal : SkTileMode::kClamp; 780cb93a386Sopenharmony_ci 781cb93a386Sopenharmony_ci // Get final CTM matrix 782cb93a386Sopenharmony_ci SkPreConcatMatrixProvider matrixProvider(this->asMatrixProvider(), 783cb93a386Sopenharmony_ci preViewMatrix ? *preViewMatrix : SkMatrix::I()); 784cb93a386Sopenharmony_ci const SkMatrix& ctm(matrixProvider.localToDevice()); 785cb93a386Sopenharmony_ci 786cb93a386Sopenharmony_ci SkSamplingOptions sampling = origSampling; 787cb93a386Sopenharmony_ci bool sharpenMM = fContext->priv().options().fSharpenMipmappedTextures; 788cb93a386Sopenharmony_ci if (sampling.mipmap != SkMipmapMode::kNone && can_disable_mipmap(ctm, srcToDst, sharpenMM)) { 789cb93a386Sopenharmony_ci sampling = SkSamplingOptions(sampling.filter); 790cb93a386Sopenharmony_ci } 791cb93a386Sopenharmony_ci auto clip = this->clip(); 792cb93a386Sopenharmony_ci 793cb93a386Sopenharmony_ci if (!image->isTextureBacked() && !as_IB(image)->isPinnedOnContext(fContext.get())) { 794cb93a386Sopenharmony_ci int tileFilterPad; 795cb93a386Sopenharmony_ci if (sampling.useCubic) { 796cb93a386Sopenharmony_ci tileFilterPad = GrBicubicEffect::kFilterTexelPad; 797cb93a386Sopenharmony_ci } else if (sampling.filter == SkFilterMode::kNearest) { 798cb93a386Sopenharmony_ci tileFilterPad = 0; 799cb93a386Sopenharmony_ci } else { 800cb93a386Sopenharmony_ci tileFilterPad = 1; 801cb93a386Sopenharmony_ci } 802cb93a386Sopenharmony_ci int maxTileSize = fContext->priv().caps()->maxTextureSize() - 2*tileFilterPad; 803cb93a386Sopenharmony_ci int tileSize; 804cb93a386Sopenharmony_ci SkIRect clippedSubset; 805cb93a386Sopenharmony_ci if (should_tile_image_id(fContext.get(), 806cb93a386Sopenharmony_ci fSurfaceDrawContext->dimensions(), 807cb93a386Sopenharmony_ci clip, 808cb93a386Sopenharmony_ci image->unique(), 809cb93a386Sopenharmony_ci image->dimensions(), 810cb93a386Sopenharmony_ci ctm, 811cb93a386Sopenharmony_ci srcToDst, 812cb93a386Sopenharmony_ci &src, 813cb93a386Sopenharmony_ci maxTileSize, 814cb93a386Sopenharmony_ci &tileSize, 815cb93a386Sopenharmony_ci &clippedSubset)) { 816cb93a386Sopenharmony_ci // Extract pixels on the CPU, since we have to split into separate textures before 817cb93a386Sopenharmony_ci // sending to the GPU if tiling. 818cb93a386Sopenharmony_ci if (SkBitmap bm; as_IB(image)->getROPixels(nullptr, &bm)) { 819cb93a386Sopenharmony_ci // This is the funnel for all paths that draw tiled bitmaps/images. 820cb93a386Sopenharmony_ci draw_tiled_bitmap(fContext.get(), 821cb93a386Sopenharmony_ci fSurfaceDrawContext.get(), 822cb93a386Sopenharmony_ci clip, 823cb93a386Sopenharmony_ci bm, 824cb93a386Sopenharmony_ci tileSize, 825cb93a386Sopenharmony_ci matrixProvider, 826cb93a386Sopenharmony_ci srcToDst, 827cb93a386Sopenharmony_ci src, 828cb93a386Sopenharmony_ci clippedSubset, 829cb93a386Sopenharmony_ci paint, 830cb93a386Sopenharmony_ci aa, 831cb93a386Sopenharmony_ci constraint, 832cb93a386Sopenharmony_ci sampling, 833cb93a386Sopenharmony_ci tileMode); 834cb93a386Sopenharmony_ci return; 835cb93a386Sopenharmony_ci } 836cb93a386Sopenharmony_ci } 837cb93a386Sopenharmony_ci } 838cb93a386Sopenharmony_ci 839cb93a386Sopenharmony_ci draw_image(fContext.get(), 840cb93a386Sopenharmony_ci fSurfaceDrawContext.get(), 841cb93a386Sopenharmony_ci clip, 842cb93a386Sopenharmony_ci matrixProvider, 843cb93a386Sopenharmony_ci paint, 844cb93a386Sopenharmony_ci *as_IB(image), 845cb93a386Sopenharmony_ci src, 846cb93a386Sopenharmony_ci dst, 847cb93a386Sopenharmony_ci dstClip, 848cb93a386Sopenharmony_ci srcToDst, 849cb93a386Sopenharmony_ci aa, 850cb93a386Sopenharmony_ci aaFlags, 851cb93a386Sopenharmony_ci constraint, 852cb93a386Sopenharmony_ci sampling); 853cb93a386Sopenharmony_ci return; 854cb93a386Sopenharmony_ci} 855cb93a386Sopenharmony_ci 856cb93a386Sopenharmony_civoid Device::drawEdgeAAImageSet(const SkCanvas::ImageSetEntry set[], int count, 857cb93a386Sopenharmony_ci const SkPoint dstClips[], const SkMatrix preViewMatrices[], 858cb93a386Sopenharmony_ci const SkSamplingOptions& sampling, const SkPaint& paint, 859cb93a386Sopenharmony_ci SkCanvas::SrcRectConstraint constraint) { 860cb93a386Sopenharmony_ci SkASSERT(count > 0); 861cb93a386Sopenharmony_ci if (!can_use_draw_texture(paint, sampling.useCubic, sampling.mipmap)) { 862cb93a386Sopenharmony_ci // Send every entry through drawImageQuad() to handle the more complicated paint 863cb93a386Sopenharmony_ci int dstClipIndex = 0; 864cb93a386Sopenharmony_ci for (int i = 0; i < count; ++i) { 865cb93a386Sopenharmony_ci // Only no clip or quad clip are supported 866cb93a386Sopenharmony_ci SkASSERT(!set[i].fHasClip || dstClips); 867cb93a386Sopenharmony_ci SkASSERT(set[i].fMatrixIndex < 0 || preViewMatrices); 868cb93a386Sopenharmony_ci 869cb93a386Sopenharmony_ci SkTCopyOnFirstWrite<SkPaint> entryPaint(paint); 870cb93a386Sopenharmony_ci if (set[i].fAlpha != 1.f) { 871cb93a386Sopenharmony_ci auto paintAlpha = paint.getAlphaf(); 872cb93a386Sopenharmony_ci entryPaint.writable()->setAlphaf(paintAlpha * set[i].fAlpha); 873cb93a386Sopenharmony_ci } 874cb93a386Sopenharmony_ci // Always send GrAA::kYes to preserve seaming across tiling in MSAA 875cb93a386Sopenharmony_ci this->drawImageQuad( 876cb93a386Sopenharmony_ci set[i].fImage.get(), &set[i].fSrcRect, &set[i].fDstRect, 877cb93a386Sopenharmony_ci set[i].fHasClip ? dstClips + dstClipIndex : nullptr, GrAA::kYes, 878cb93a386Sopenharmony_ci SkToGrQuadAAFlags(set[i].fAAFlags), 879cb93a386Sopenharmony_ci set[i].fMatrixIndex < 0 ? nullptr : preViewMatrices + set[i].fMatrixIndex, 880cb93a386Sopenharmony_ci sampling, *entryPaint, constraint); 881cb93a386Sopenharmony_ci dstClipIndex += 4 * set[i].fHasClip; 882cb93a386Sopenharmony_ci } 883cb93a386Sopenharmony_ci return; 884cb93a386Sopenharmony_ci } 885cb93a386Sopenharmony_ci 886cb93a386Sopenharmony_ci GrSamplerState::Filter filter = sampling.filter == SkFilterMode::kNearest 887cb93a386Sopenharmony_ci ? GrSamplerState::Filter::kNearest 888cb93a386Sopenharmony_ci : GrSamplerState::Filter::kLinear; 889cb93a386Sopenharmony_ci SkBlendMode mode = paint.getBlendMode_or(SkBlendMode::kSrcOver); 890cb93a386Sopenharmony_ci 891cb93a386Sopenharmony_ci SkAutoTArray<GrTextureSetEntry> textures(count); 892cb93a386Sopenharmony_ci // We accumulate compatible proxies until we find an an incompatible one or reach the end and 893cb93a386Sopenharmony_ci // issue the accumulated 'n' draws starting at 'base'. 'p' represents the number of proxy 894cb93a386Sopenharmony_ci // switches that occur within the 'n' entries. 895cb93a386Sopenharmony_ci int base = 0, n = 0, p = 0; 896cb93a386Sopenharmony_ci auto draw = [&](int nextBase) { 897cb93a386Sopenharmony_ci if (n > 0) { 898cb93a386Sopenharmony_ci auto textureXform = GrColorSpaceXform::Make(set[base].fImage->imageInfo().colorInfo(), 899cb93a386Sopenharmony_ci fSurfaceDrawContext->colorInfo()); 900cb93a386Sopenharmony_ci fSurfaceDrawContext->drawTextureSet(this->clip(), 901cb93a386Sopenharmony_ci textures.get() + base, 902cb93a386Sopenharmony_ci n, 903cb93a386Sopenharmony_ci p, 904cb93a386Sopenharmony_ci filter, 905cb93a386Sopenharmony_ci GrSamplerState::MipmapMode::kNone, 906cb93a386Sopenharmony_ci mode, 907cb93a386Sopenharmony_ci GrAA::kYes, 908cb93a386Sopenharmony_ci constraint, 909cb93a386Sopenharmony_ci this->localToDevice(), 910cb93a386Sopenharmony_ci std::move(textureXform)); 911cb93a386Sopenharmony_ci } 912cb93a386Sopenharmony_ci base = nextBase; 913cb93a386Sopenharmony_ci n = 0; 914cb93a386Sopenharmony_ci p = 0; 915cb93a386Sopenharmony_ci }; 916cb93a386Sopenharmony_ci int dstClipIndex = 0; 917cb93a386Sopenharmony_ci for (int i = 0; i < count; ++i) { 918cb93a386Sopenharmony_ci SkASSERT(!set[i].fHasClip || dstClips); 919cb93a386Sopenharmony_ci SkASSERT(set[i].fMatrixIndex < 0 || preViewMatrices); 920cb93a386Sopenharmony_ci 921cb93a386Sopenharmony_ci // Manage the dst clip pointer tracking before any continues are used so we don't lose 922cb93a386Sopenharmony_ci // our place in the dstClips array. 923cb93a386Sopenharmony_ci const SkPoint* clip = set[i].fHasClip ? dstClips + dstClipIndex : nullptr; 924cb93a386Sopenharmony_ci dstClipIndex += 4 * set[i].fHasClip; 925cb93a386Sopenharmony_ci 926cb93a386Sopenharmony_ci // The default SkBaseDevice implementation is based on drawImageRect which does not allow 927cb93a386Sopenharmony_ci // non-sorted src rects. TODO: Decide this is OK or make sure we handle it. 928cb93a386Sopenharmony_ci if (!set[i].fSrcRect.isSorted()) { 929cb93a386Sopenharmony_ci draw(i + 1); 930cb93a386Sopenharmony_ci continue; 931cb93a386Sopenharmony_ci } 932cb93a386Sopenharmony_ci 933cb93a386Sopenharmony_ci GrSurfaceProxyView view; 934cb93a386Sopenharmony_ci const SkImage_Base* image = as_IB(set[i].fImage.get()); 935cb93a386Sopenharmony_ci // Extract view from image, but skip YUV images so they get processed through 936cb93a386Sopenharmony_ci // drawImageQuad and the proper effect to dynamically sample their planes. 937cb93a386Sopenharmony_ci if (!image->isYUVA()) { 938cb93a386Sopenharmony_ci std::tie(view, std::ignore) = image->asView(this->recordingContext(), GrMipmapped::kNo); 939cb93a386Sopenharmony_ci if (image->isAlphaOnly()) { 940cb93a386Sopenharmony_ci GrSwizzle swizzle = GrSwizzle::Concat(view.swizzle(), GrSwizzle("aaaa")); 941cb93a386Sopenharmony_ci view = {view.detachProxy(), view.origin(), swizzle}; 942cb93a386Sopenharmony_ci } 943cb93a386Sopenharmony_ci } 944cb93a386Sopenharmony_ci 945cb93a386Sopenharmony_ci if (!view) { 946cb93a386Sopenharmony_ci // This image can't go through the texture op, send through general image pipeline 947cb93a386Sopenharmony_ci // after flushing current batch. 948cb93a386Sopenharmony_ci draw(i + 1); 949cb93a386Sopenharmony_ci SkTCopyOnFirstWrite<SkPaint> entryPaint(paint); 950cb93a386Sopenharmony_ci if (set[i].fAlpha != 1.f) { 951cb93a386Sopenharmony_ci auto paintAlpha = paint.getAlphaf(); 952cb93a386Sopenharmony_ci entryPaint.writable()->setAlphaf(paintAlpha * set[i].fAlpha); 953cb93a386Sopenharmony_ci } 954cb93a386Sopenharmony_ci this->drawImageQuad( 955cb93a386Sopenharmony_ci image, &set[i].fSrcRect, &set[i].fDstRect, clip, GrAA::kYes, 956cb93a386Sopenharmony_ci SkToGrQuadAAFlags(set[i].fAAFlags), 957cb93a386Sopenharmony_ci set[i].fMatrixIndex < 0 ? nullptr : preViewMatrices + set[i].fMatrixIndex, 958cb93a386Sopenharmony_ci sampling, *entryPaint, constraint); 959cb93a386Sopenharmony_ci continue; 960cb93a386Sopenharmony_ci } 961cb93a386Sopenharmony_ci 962cb93a386Sopenharmony_ci textures[i].fProxyView = std::move(view); 963cb93a386Sopenharmony_ci textures[i].fSrcAlphaType = image->alphaType(); 964cb93a386Sopenharmony_ci textures[i].fSrcRect = set[i].fSrcRect; 965cb93a386Sopenharmony_ci textures[i].fDstRect = set[i].fDstRect; 966cb93a386Sopenharmony_ci textures[i].fDstClipQuad = clip; 967cb93a386Sopenharmony_ci textures[i].fPreViewMatrix = 968cb93a386Sopenharmony_ci set[i].fMatrixIndex < 0 ? nullptr : preViewMatrices + set[i].fMatrixIndex; 969cb93a386Sopenharmony_ci textures[i].fColor = texture_color(paint.getColor4f(), set[i].fAlpha, 970cb93a386Sopenharmony_ci SkColorTypeToGrColorType(image->colorType()), 971cb93a386Sopenharmony_ci fSurfaceDrawContext->colorInfo()); 972cb93a386Sopenharmony_ci textures[i].fAAFlags = SkToGrQuadAAFlags(set[i].fAAFlags); 973cb93a386Sopenharmony_ci 974cb93a386Sopenharmony_ci if (n > 0 && 975cb93a386Sopenharmony_ci (!GrTextureProxy::ProxiesAreCompatibleAsDynamicState( 976cb93a386Sopenharmony_ci textures[i].fProxyView.proxy(), 977cb93a386Sopenharmony_ci textures[base].fProxyView.proxy()) || 978cb93a386Sopenharmony_ci textures[i].fProxyView.swizzle() != textures[base].fProxyView.swizzle() || 979cb93a386Sopenharmony_ci set[i].fImage->alphaType() != set[base].fImage->alphaType() || 980cb93a386Sopenharmony_ci !SkColorSpace::Equals(set[i].fImage->colorSpace(), set[base].fImage->colorSpace()))) { 981cb93a386Sopenharmony_ci draw(i); 982cb93a386Sopenharmony_ci } 983cb93a386Sopenharmony_ci // Whether or not we submitted a draw in the above if(), this ith entry is in the current 984cb93a386Sopenharmony_ci // set being accumulated so increment n, and increment p if proxies are different. 985cb93a386Sopenharmony_ci ++n; 986cb93a386Sopenharmony_ci if (n == 1 || textures[i - 1].fProxyView.proxy() != textures[i].fProxyView.proxy()) { 987cb93a386Sopenharmony_ci // First proxy or a different proxy (that is compatible, otherwise we'd have drawn up 988cb93a386Sopenharmony_ci // to i - 1). 989cb93a386Sopenharmony_ci ++p; 990cb93a386Sopenharmony_ci } 991cb93a386Sopenharmony_ci } 992cb93a386Sopenharmony_ci draw(count); 993cb93a386Sopenharmony_ci} 994cb93a386Sopenharmony_ci 995cb93a386Sopenharmony_ci} // namespace skgpu::v1 996