Coverage Report

Created: 2021-08-22 09:07

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