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 "GrTextureParamsAdjuster.h"
9
10 #include "GrCaps.h"
11 #include "GrColorSpaceXform.h"
12 #include "GrContext.h"
13 #include "GrDrawContext.h"
14 #include "GrGpu.h"
15 #include "GrGpuResourcePriv.h"
16 #include "GrResourceKey.h"
17 #include "GrTexture.h"
18 #include "GrTextureParams.h"
19 #include "GrTextureProvider.h"
20 #include "SkCanvas.h"
21 #include "SkGr.h"
22 #include "SkGrPriv.h"
23 #include "effects/GrBicubicEffect.h"
24 #include "effects/GrSimpleTextureEffect.h"
25 #include "effects/GrTextureDomain.h"
26
27 typedef GrTextureProducer::CopyParams CopyParams;
28
29 //////////////////////////////////////////////////////////////////////////////
30
copy_on_gpu(GrTexture * inputTexture,const SkIRect * subset,const CopyParams & copyParams)31 static GrTexture* copy_on_gpu(GrTexture* inputTexture, const SkIRect* subset,
32 const CopyParams& copyParams) {
33 SkASSERT(!subset || !subset->isEmpty());
34 GrContext* context = inputTexture->getContext();
35 SkASSERT(context);
36
37 GrPixelConfig config = GrMakePixelConfigUncompressed(inputTexture->config());
38
39 sk_sp<GrDrawContext> copyDC = context->makeDrawContextWithFallback(SkBackingFit::kExact,
40 copyParams.fWidth,
41 copyParams.fHeight,
42 config, nullptr);
43 if (!copyDC) {
44 return nullptr;
45 }
46
47 GrPaint paint;
48 paint.setGammaCorrect(true);
49
50 SkScalar sx SK_INIT_TO_AVOID_WARNING;
51 SkScalar sy SK_INIT_TO_AVOID_WARNING;
52 if (subset) {
53 sx = 1.f / inputTexture->width();
54 sy = 1.f / inputTexture->height();
55 }
56
57 if (copyParams.fFilter != GrTextureParams::kNone_FilterMode && subset &&
58 (subset->width() != copyParams.fWidth || subset->height() != copyParams.fHeight)) {
59 SkRect domain;
60 domain.fLeft = (subset->fLeft + 0.5f) * sx;
61 domain.fTop = (subset->fTop + 0.5f)* sy;
62 domain.fRight = (subset->fRight - 0.5f) * sx;
63 domain.fBottom = (subset->fBottom - 0.5f) * sy;
64 // This would cause us to read values from outside the subset. Surely, the caller knows
65 // better!
66 SkASSERT(copyParams.fFilter != GrTextureParams::kMipMap_FilterMode);
67 paint.addColorFragmentProcessor(
68 GrTextureDomainEffect::Make(inputTexture, nullptr, SkMatrix::I(), domain,
69 GrTextureDomain::kClamp_Mode,
70 copyParams.fFilter));
71 } else {
72 GrTextureParams params(SkShader::kClamp_TileMode, copyParams.fFilter);
73 paint.addColorTextureProcessor(inputTexture, nullptr, SkMatrix::I(), params);
74 }
75 paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
76
77 SkRect localRect;
78 if (subset) {
79 localRect = SkRect::Make(*subset);
80 localRect.fLeft *= sx;
81 localRect.fTop *= sy;
82 localRect.fRight *= sx;
83 localRect.fBottom *= sy;
84 } else {
85 localRect = SkRect::MakeWH(1.f, 1.f);
86 }
87
88 SkRect dstRect = SkRect::MakeIWH(copyParams.fWidth, copyParams.fHeight);
89 copyDC->fillRectToRect(GrNoClip(), paint, SkMatrix::I(), dstRect, localRect);
90 return copyDC->asTexture().release();
91 }
92
GrTextureAdjuster(GrTexture * original,SkAlphaType alphaType,const SkIRect & contentArea,uint32_t uniqueID,SkColorSpace * cs)93 GrTextureAdjuster::GrTextureAdjuster(GrTexture* original, SkAlphaType alphaType,
94 const SkIRect& contentArea, uint32_t uniqueID,
95 SkColorSpace* cs)
96 : INHERITED(contentArea.width(), contentArea.height(),
97 GrPixelConfigIsAlphaOnly(original->config()))
98 , fOriginal(original)
99 , fAlphaType(alphaType)
100 , fColorSpace(cs)
101 , fUniqueID(uniqueID)
102 {
103 SkASSERT(SkIRect::MakeWH(original->width(), original->height()).contains(contentArea));
104 if (contentArea.fLeft > 0 || contentArea.fTop > 0 ||
105 contentArea.fRight < original->width() || contentArea.fBottom < original->height()) {
106 fContentArea.set(contentArea);
107 }
108 }
109
makeCopyKey(const CopyParams & params,GrUniqueKey * copyKey)110 void GrTextureAdjuster::makeCopyKey(const CopyParams& params, GrUniqueKey* copyKey) {
111 GrUniqueKey baseKey;
112 GrMakeKeyFromImageID(&baseKey, fUniqueID, SkIRect::MakeWH(this->width(), this->height()));
113 MakeCopyKeyFromOrigKey(baseKey, params, copyKey);
114 }
115
didCacheCopy(const GrUniqueKey & copyKey)116 void GrTextureAdjuster::didCacheCopy(const GrUniqueKey& copyKey) {
117 // We don't currently have a mechanism for notifications on Images!
118 }
119
getColorSpace()120 SkColorSpace* GrTextureAdjuster::getColorSpace() {
121 return fColorSpace;
122 }
123
refCopy(const CopyParams & copyParams)124 GrTexture* GrTextureAdjuster::refCopy(const CopyParams& copyParams) {
125 GrTexture* texture = this->originalTexture();
126 GrContext* context = texture->getContext();
127 const SkIRect* contentArea = this->contentAreaOrNull();
128 GrUniqueKey key;
129 this->makeCopyKey(copyParams, &key);
130 if (key.isValid()) {
131 GrTexture* cachedCopy = context->textureProvider()->findAndRefTextureByUniqueKey(key);
132 if (cachedCopy) {
133 return cachedCopy;
134 }
135 }
136 GrTexture* copy = copy_on_gpu(texture, contentArea, copyParams);
137 if (copy) {
138 if (key.isValid()) {
139 copy->resourcePriv().setUniqueKey(key);
140 this->didCacheCopy(key);
141 }
142 }
143 return copy;
144 }
145
refTextureSafeForParams(const GrTextureParams & params,SkSourceGammaTreatment gammaTreatment,SkIPoint * outOffset)146 GrTexture* GrTextureAdjuster::refTextureSafeForParams(const GrTextureParams& params,
147 SkSourceGammaTreatment gammaTreatment,
148 SkIPoint* outOffset) {
149 GrTexture* texture = this->originalTexture();
150 GrContext* context = texture->getContext();
151 CopyParams copyParams;
152 const SkIRect* contentArea = this->contentAreaOrNull();
153
154 if (!context) {
155 // The texture was abandoned.
156 return nullptr;
157 }
158
159 if (contentArea && GrTextureParams::kMipMap_FilterMode == params.filterMode()) {
160 // If we generate a MIP chain for texture it will read pixel values from outside the content
161 // area.
162 copyParams.fWidth = contentArea->width();
163 copyParams.fHeight = contentArea->height();
164 copyParams.fFilter = GrTextureParams::kBilerp_FilterMode;
165 } else if (!context->getGpu()->makeCopyForTextureParams(texture, params, ©Params)) {
166 if (outOffset) {
167 if (contentArea) {
168 outOffset->set(contentArea->fLeft, contentArea->fRight);
169 } else {
170 outOffset->set(0, 0);
171 }
172 }
173 return SkRef(texture);
174 }
175
176 GrTexture* copy = this->refCopy(copyParams);
177 if (copy && outOffset) {
178 outOffset->set(0, 0);
179 }
180 return copy;
181 }
182
183 enum DomainMode {
184 kNoDomain_DomainMode,
185 kDomain_DomainMode,
186 kTightCopy_DomainMode
187 };
188
189 /** Determines whether a texture domain is necessary and if so what domain to use. There are two
190 * rectangles to consider:
191 * - The first is the content area specified by the texture adjuster. We can *never* allow
192 * filtering to cause bleed of pixels outside this rectangle.
193 * - The second rectangle is the constraint rectangle, which is known to be contained by the
194 * content area. The filterConstraint specifies whether we are allowed to bleed across this
195 * rect.
196 *
197 * We want to avoid using a domain if possible. We consider the above rectangles, the filter type,
198 * and whether the coords generated by the draw would all fall within the constraint rect. If the
199 * latter is true we only need to consider whether the filter would extend beyond the rects.
200 */
determine_domain_mode(const SkRect & constraintRect,GrTextureAdjuster::FilterConstraint filterConstraint,bool coordsLimitedToConstraintRect,int texW,int texH,const SkIRect * textureContentArea,const GrTextureParams::FilterMode * filterModeOrNullForBicubic,SkRect * domainRect)201 static DomainMode determine_domain_mode(
202 const SkRect& constraintRect,
203 GrTextureAdjuster::FilterConstraint filterConstraint,
204 bool coordsLimitedToConstraintRect,
205 int texW, int texH,
206 const SkIRect* textureContentArea,
207 const GrTextureParams::FilterMode* filterModeOrNullForBicubic,
208 SkRect* domainRect) {
209
210 SkASSERT(SkRect::MakeIWH(texW, texH).contains(constraintRect));
211 // We only expect a content area rect if there is some non-content area.
212 SkASSERT(!textureContentArea ||
213 (!textureContentArea->contains(SkIRect::MakeWH(texW, texH)) &&
214 SkRect::Make(*textureContentArea).contains(constraintRect)));
215
216 SkRect textureBounds = SkRect::MakeIWH(texW, texH);
217 // If the src rectangle contains the whole texture then no need for a domain.
218 if (constraintRect.contains(textureBounds)) {
219 return kNoDomain_DomainMode;
220 }
221
222 bool restrictFilterToRect = (filterConstraint == GrTextureProducer::kYes_FilterConstraint);
223
224 // If we can filter outside the constraint rect, and there is no non-content area of the
225 // texture, and we aren't going to generate sample coords outside the constraint rect then we
226 // don't need a domain.
227 if (!restrictFilterToRect && !textureContentArea && coordsLimitedToConstraintRect) {
228 return kNoDomain_DomainMode;
229 }
230
231 // Get the domain inset based on sampling mode (or bail if mipped)
232 SkScalar filterHalfWidth = 0.f;
233 if (filterModeOrNullForBicubic) {
234 switch (*filterModeOrNullForBicubic) {
235 case GrTextureParams::kNone_FilterMode:
236 if (coordsLimitedToConstraintRect) {
237 return kNoDomain_DomainMode;
238 } else {
239 filterHalfWidth = 0.f;
240 }
241 break;
242 case GrTextureParams::kBilerp_FilterMode:
243 filterHalfWidth = .5f;
244 break;
245 case GrTextureParams::kMipMap_FilterMode:
246 if (restrictFilterToRect || textureContentArea) {
247 // No domain can save us here.
248 return kTightCopy_DomainMode;
249 }
250 return kNoDomain_DomainMode;
251 }
252 } else {
253 // bicubic does nearest filtering internally.
254 filterHalfWidth = 1.5f;
255 }
256
257 // Both bilerp and bicubic use bilinear filtering and so need to be clamped to the center
258 // of the edge texel. Pinning to the texel center has no impact on nearest mode and MIP-maps
259
260 static const SkScalar kDomainInset = 0.5f;
261 // Figure out the limits of pixels we're allowed to sample from.
262 // Unless we know the amount of outset and the texture matrix we have to conservatively enforce
263 // the domain.
264 if (restrictFilterToRect) {
265 domainRect->fLeft = constraintRect.fLeft + kDomainInset;
266 domainRect->fTop = constraintRect.fTop + kDomainInset;
267 domainRect->fRight = constraintRect.fRight - kDomainInset;
268 domainRect->fBottom = constraintRect.fBottom - kDomainInset;
269 } else if (textureContentArea) {
270 // If we got here then: there is a textureContentArea, the coords are limited to the
271 // constraint rect, and we're allowed to filter across the constraint rect boundary. So
272 // we check whether the filter would reach across the edge of the content area.
273 // We will only set the sides that are required.
274
275 domainRect->setLargest();
276 if (coordsLimitedToConstraintRect) {
277 // We may be able to use the fact that the texture coords are limited to the constraint
278 // rect in order to avoid having to add a domain.
279 bool needContentAreaConstraint = false;
280 if (textureContentArea->fLeft > 0 &&
281 textureContentArea->fLeft + filterHalfWidth > constraintRect.fLeft) {
282 domainRect->fLeft = textureContentArea->fLeft + kDomainInset;
283 needContentAreaConstraint = true;
284 }
285 if (textureContentArea->fTop > 0 &&
286 textureContentArea->fTop + filterHalfWidth > constraintRect.fTop) {
287 domainRect->fTop = textureContentArea->fTop + kDomainInset;
288 needContentAreaConstraint = true;
289 }
290 if (textureContentArea->fRight < texW &&
291 textureContentArea->fRight - filterHalfWidth < constraintRect.fRight) {
292 domainRect->fRight = textureContentArea->fRight - kDomainInset;
293 needContentAreaConstraint = true;
294 }
295 if (textureContentArea->fBottom < texH &&
296 textureContentArea->fBottom - filterHalfWidth < constraintRect.fBottom) {
297 domainRect->fBottom = textureContentArea->fBottom - kDomainInset;
298 needContentAreaConstraint = true;
299 }
300 if (!needContentAreaConstraint) {
301 return kNoDomain_DomainMode;
302 }
303 } else {
304 // Our sample coords for the texture are allowed to be outside the constraintRect so we
305 // don't consider it when computing the domain.
306 if (textureContentArea->fLeft != 0) {
307 domainRect->fLeft = textureContentArea->fLeft + kDomainInset;
308 }
309 if (textureContentArea->fTop != 0) {
310 domainRect->fTop = textureContentArea->fTop + kDomainInset;
311 }
312 if (textureContentArea->fRight != texW) {
313 domainRect->fRight = textureContentArea->fRight - kDomainInset;
314 }
315 if (textureContentArea->fBottom != texH) {
316 domainRect->fBottom = textureContentArea->fBottom - kDomainInset;
317 }
318 }
319 } else {
320 return kNoDomain_DomainMode;
321 }
322
323 if (domainRect->fLeft > domainRect->fRight) {
324 domainRect->fLeft = domainRect->fRight = SkScalarAve(domainRect->fLeft, domainRect->fRight);
325 }
326 if (domainRect->fTop > domainRect->fBottom) {
327 domainRect->fTop = domainRect->fBottom = SkScalarAve(domainRect->fTop, domainRect->fBottom);
328 }
329 domainRect->fLeft /= texW;
330 domainRect->fTop /= texH;
331 domainRect->fRight /= texW;
332 domainRect->fBottom /= texH;
333 return kDomain_DomainMode;
334 }
335
create_fp_for_domain_and_filter(GrTexture * texture,sk_sp<GrColorSpaceXform> colorSpaceXform,const SkMatrix & textureMatrix,DomainMode domainMode,const SkRect & domain,const GrTextureParams::FilterMode * filterOrNullForBicubic)336 static sk_sp<GrFragmentProcessor> create_fp_for_domain_and_filter(
337 GrTexture* texture,
338 sk_sp<GrColorSpaceXform> colorSpaceXform,
339 const SkMatrix& textureMatrix,
340 DomainMode domainMode,
341 const SkRect& domain,
342 const GrTextureParams::FilterMode* filterOrNullForBicubic) {
343 SkASSERT(kTightCopy_DomainMode != domainMode);
344 if (filterOrNullForBicubic) {
345 if (kDomain_DomainMode == domainMode) {
346 return GrTextureDomainEffect::Make(texture, std::move(colorSpaceXform), textureMatrix,
347 domain, GrTextureDomain::kClamp_Mode,
348 *filterOrNullForBicubic);
349 } else {
350 GrTextureParams params(SkShader::kClamp_TileMode, *filterOrNullForBicubic);
351 return GrSimpleTextureEffect::Make(texture, std::move(colorSpaceXform), textureMatrix,
352 params);
353 }
354 } else {
355 if (kDomain_DomainMode == domainMode) {
356 return GrBicubicEffect::Make(texture, std::move(colorSpaceXform), textureMatrix,
357 domain);
358 } else {
359 static const SkShader::TileMode kClampClamp[] =
360 { SkShader::kClamp_TileMode, SkShader::kClamp_TileMode };
361 return GrBicubicEffect::Make(texture, std::move(colorSpaceXform), textureMatrix,
362 kClampClamp);
363 }
364 }
365 }
366
createFragmentProcessor(const SkMatrix & origTextureMatrix,const SkRect & origConstraintRect,FilterConstraint filterConstraint,bool coordsLimitedToConstraintRect,const GrTextureParams::FilterMode * filterOrNullForBicubic,SkColorSpace * dstColorSpace,SkSourceGammaTreatment gammaTreatment)367 sk_sp<GrFragmentProcessor> GrTextureAdjuster::createFragmentProcessor(
368 const SkMatrix& origTextureMatrix,
369 const SkRect& origConstraintRect,
370 FilterConstraint filterConstraint,
371 bool coordsLimitedToConstraintRect,
372 const GrTextureParams::FilterMode* filterOrNullForBicubic,
373 SkColorSpace* dstColorSpace,
374 SkSourceGammaTreatment gammaTreatment) {
375
376 SkMatrix textureMatrix = origTextureMatrix;
377 const SkIRect* contentArea = this->contentAreaOrNull();
378 // Convert the constraintRect to be relative to the texture rather than the content area so
379 // that both rects are in the same coordinate system.
380 SkTCopyOnFirstWrite<SkRect> constraintRect(origConstraintRect);
381 if (contentArea) {
382 SkScalar l = SkIntToScalar(contentArea->fLeft);
383 SkScalar t = SkIntToScalar(contentArea->fTop);
384 constraintRect.writable()->offset(l, t);
385 textureMatrix.postTranslate(l, t);
386 }
387
388 SkRect domain;
389 GrTextureParams params;
390 if (filterOrNullForBicubic) {
391 params.setFilterMode(*filterOrNullForBicubic);
392 }
393 SkAutoTUnref<GrTexture> texture(this->refTextureSafeForParams(params, gammaTreatment, nullptr));
394 if (!texture) {
395 return nullptr;
396 }
397 // If we made a copy then we only copied the contentArea, in which case the new texture is all
398 // content.
399 if (texture != this->originalTexture()) {
400 contentArea = nullptr;
401 }
402
403 DomainMode domainMode =
404 determine_domain_mode(*constraintRect, filterConstraint, coordsLimitedToConstraintRect,
405 texture->width(), texture->height(),
406 contentArea, filterOrNullForBicubic,
407 &domain);
408 if (kTightCopy_DomainMode == domainMode) {
409 // TODO: Copy the texture and adjust the texture matrix (both parts need to consider
410 // non-int constraint rect)
411 // For now: treat as bilerp and ignore what goes on above level 0.
412
413 // We only expect MIP maps to require a tight copy.
414 SkASSERT(filterOrNullForBicubic &&
415 GrTextureParams::kMipMap_FilterMode == *filterOrNullForBicubic);
416 static const GrTextureParams::FilterMode kBilerp = GrTextureParams::kBilerp_FilterMode;
417 domainMode =
418 determine_domain_mode(*constraintRect, filterConstraint, coordsLimitedToConstraintRect,
419 texture->width(), texture->height(),
420 contentArea, &kBilerp, &domain);
421 SkASSERT(kTightCopy_DomainMode != domainMode);
422 }
423 SkASSERT(kNoDomain_DomainMode == domainMode ||
424 (domain.fLeft <= domain.fRight && domain.fTop <= domain.fBottom));
425 textureMatrix.postIDiv(texture->width(), texture->height());
426 sk_sp<GrColorSpaceXform> colorSpaceXform = GrColorSpaceXform::Make(this->getColorSpace(),
427 dstColorSpace);
428 return create_fp_for_domain_and_filter(texture, std::move(colorSpaceXform), textureMatrix,
429 domainMode, domain, filterOrNullForBicubic);
430 }
431
432 //////////////////////////////////////////////////////////////////////////////
433
refTextureForParams(const GrTextureParams & params,SkSourceGammaTreatment gammaTreatment)434 GrTexture* GrTextureMaker::refTextureForParams(const GrTextureParams& params,
435 SkSourceGammaTreatment gammaTreatment) {
436 CopyParams copyParams;
437 bool willBeMipped = params.filterMode() == GrTextureParams::kMipMap_FilterMode;
438
439 if (!fContext->caps()->mipMapSupport()) {
440 willBeMipped = false;
441 }
442
443 if (!fContext->getGpu()->makeCopyForTextureParams(this->width(), this->height(), params,
444 ©Params)) {
445 return this->refOriginalTexture(willBeMipped, gammaTreatment);
446 }
447 GrUniqueKey copyKey;
448 this->makeCopyKey(copyParams, ©Key);
449 if (copyKey.isValid()) {
450 GrTexture* result = fContext->textureProvider()->findAndRefTextureByUniqueKey(copyKey);
451 if (result) {
452 return result;
453 }
454 }
455
456 GrTexture* result = this->generateTextureForParams(copyParams, willBeMipped, gammaTreatment);
457 if (!result) {
458 return nullptr;
459 }
460
461 if (copyKey.isValid()) {
462 fContext->textureProvider()->assignUniqueKeyToTexture(copyKey, result);
463 this->didCacheCopy(copyKey);
464 }
465 return result;
466 }
467
createFragmentProcessor(const SkMatrix & textureMatrix,const SkRect & constraintRect,FilterConstraint filterConstraint,bool coordsLimitedToConstraintRect,const GrTextureParams::FilterMode * filterOrNullForBicubic,SkColorSpace * dstColorSpace,SkSourceGammaTreatment gammaTreatment)468 sk_sp<GrFragmentProcessor> GrTextureMaker::createFragmentProcessor(
469 const SkMatrix& textureMatrix,
470 const SkRect& constraintRect,
471 FilterConstraint filterConstraint,
472 bool coordsLimitedToConstraintRect,
473 const GrTextureParams::FilterMode* filterOrNullForBicubic,
474 SkColorSpace* dstColorSpace,
475 SkSourceGammaTreatment gammaTreatment) {
476
477 const GrTextureParams::FilterMode* fmForDetermineDomain = filterOrNullForBicubic;
478 if (filterOrNullForBicubic && GrTextureParams::kMipMap_FilterMode == *filterOrNullForBicubic &&
479 kYes_FilterConstraint == filterConstraint) {
480 // TODo: Here we should force a copy restricted to the constraintRect since MIP maps will
481 // read outside the constraint rect. However, as in the adjuster case, we aren't currently
482 // doing that.
483 // We instead we compute the domain as though were bilerping which is only correct if we
484 // only sample level 0.
485 static const GrTextureParams::FilterMode kBilerp = GrTextureParams::kBilerp_FilterMode;
486 fmForDetermineDomain = &kBilerp;
487 }
488
489 GrTextureParams params;
490 if (filterOrNullForBicubic) {
491 params.reset(SkShader::kClamp_TileMode, *filterOrNullForBicubic);
492 } else {
493 // Bicubic doesn't use filtering for it's texture accesses.
494 params.reset(SkShader::kClamp_TileMode, GrTextureParams::kNone_FilterMode);
495 }
496 SkAutoTUnref<GrTexture> texture(this->refTextureForParams(params, gammaTreatment));
497 if (!texture) {
498 return nullptr;
499 }
500 SkRect domain;
501 DomainMode domainMode =
502 determine_domain_mode(constraintRect, filterConstraint, coordsLimitedToConstraintRect,
503 texture->width(), texture->height(), nullptr, fmForDetermineDomain,
504 &domain);
505 SkASSERT(kTightCopy_DomainMode != domainMode);
506 SkMatrix normalizedTextureMatrix = textureMatrix;
507 normalizedTextureMatrix.postIDiv(texture->width(), texture->height());
508 sk_sp<GrColorSpaceXform> colorSpaceXform = GrColorSpaceXform::Make(this->getColorSpace(),
509 dstColorSpace);
510 return create_fp_for_domain_and_filter(texture, std::move(colorSpaceXform),
511 normalizedTextureMatrix, domainMode, domain,
512 filterOrNullForBicubic);
513 }
514
generateTextureForParams(const CopyParams & copyParams,bool willBeMipped,SkSourceGammaTreatment gammaTreatment)515 GrTexture* GrTextureMaker::generateTextureForParams(const CopyParams& copyParams, bool willBeMipped,
516 SkSourceGammaTreatment gammaTreatment) {
517 SkAutoTUnref<GrTexture> original(this->refOriginalTexture(willBeMipped, gammaTreatment));
518 if (!original) {
519 return nullptr;
520 }
521 return copy_on_gpu(original, nullptr, copyParams);
522 }
523