blob: 09285579c16a90ed7770836c6fb999a84bbff74f [file] [log] [blame]
bsalomon045802d2015-10-20 07:58:01 -07001/*
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 "GrContext.h"
12#include "GrDrawContext.h"
13#include "GrGpu.h"
bsalomon89fe56b2015-10-29 10:49:28 -070014#include "GrGpuResourcePriv.h"
15#include "GrResourceKey.h"
bsalomon045802d2015-10-20 07:58:01 -070016#include "GrTexture.h"
17#include "GrTextureParams.h"
18#include "GrTextureProvider.h"
19#include "SkCanvas.h"
20#include "SkGr.h"
21#include "SkGrPriv.h"
bsalomonc55271f2015-11-09 11:55:57 -080022#include "effects/GrBicubicEffect.h"
bsalomon89fe56b2015-10-29 10:49:28 -070023#include "effects/GrTextureDomain.h"
bsalomon045802d2015-10-20 07:58:01 -070024
bsalomon89fe56b2015-10-29 10:49:28 -070025typedef GrTextureProducer::CopyParams CopyParams;
bsalomon045802d2015-10-20 07:58:01 -070026
bsalomon89fe56b2015-10-29 10:49:28 -070027//////////////////////////////////////////////////////////////////////////////
28
29static GrTexture* copy_on_gpu(GrTexture* inputTexture, const SkIRect* subset,
30 const CopyParams& copyParams) {
31 SkASSERT(!subset || !subset->isEmpty());
bsalomon045802d2015-10-20 07:58:01 -070032 GrContext* context = inputTexture->getContext();
33 SkASSERT(context);
34 const GrCaps* caps = context->caps();
35
36 // Either it's a cache miss or the original wasn't cached to begin with.
37 GrSurfaceDesc rtDesc = inputTexture->desc();
bsalomon89fe56b2015-10-29 10:49:28 -070038 rtDesc.fFlags = rtDesc.fFlags | kRenderTarget_GrSurfaceFlag;
39 rtDesc.fWidth = copyParams.fWidth;
bsalomon045802d2015-10-20 07:58:01 -070040 rtDesc.fHeight = copyParams.fHeight;
41 rtDesc.fConfig = GrMakePixelConfigUncompressed(rtDesc.fConfig);
42
43 // If the config isn't renderable try converting to either A8 or an 32 bit config. Otherwise,
44 // fail.
45 if (!caps->isConfigRenderable(rtDesc.fConfig, false)) {
46 if (GrPixelConfigIsAlphaOnly(rtDesc.fConfig)) {
47 if (caps->isConfigRenderable(kAlpha_8_GrPixelConfig, false)) {
48 rtDesc.fConfig = kAlpha_8_GrPixelConfig;
49 } else if (caps->isConfigRenderable(kSkia8888_GrPixelConfig, false)) {
50 rtDesc.fConfig = kSkia8888_GrPixelConfig;
51 } else {
52 return nullptr;
53 }
54 } else if (kRGB_GrColorComponentFlags ==
55 (kRGB_GrColorComponentFlags & GrPixelConfigComponentMask(rtDesc.fConfig))) {
56 if (caps->isConfigRenderable(kSkia8888_GrPixelConfig, false)) {
57 rtDesc.fConfig = kSkia8888_GrPixelConfig;
58 } else {
59 return nullptr;
60 }
61 } else {
62 return nullptr;
63 }
64 }
65
66 SkAutoTUnref<GrTexture> copy(context->textureProvider()->createTexture(rtDesc, true));
67 if (!copy) {
68 return nullptr;
69 }
70
bsalomon89fe56b2015-10-29 10:49:28 -070071 // TODO: If no scaling is being performed then use copySurface.
72
bsalomon045802d2015-10-20 07:58:01 -070073 GrPaint paint;
74
bsalomon89fe56b2015-10-29 10:49:28 -070075 SkScalar sx;
76 SkScalar sy;
77 if (subset) {
78 sx = 1.f / inputTexture->width();
79 sy = 1.f / inputTexture->height();
80 }
bsalomon045802d2015-10-20 07:58:01 -070081
bsalomon89fe56b2015-10-29 10:49:28 -070082 if (copyParams.fFilter != GrTextureParams::kNone_FilterMode && subset &&
83 (subset->width() != copyParams.fWidth || subset->height() != copyParams.fHeight)) {
84 SkRect domain;
85 domain.fLeft = (subset->fLeft + 0.5f) * sx;
86 domain.fTop = (subset->fTop + 0.5f)* sy;
87 domain.fRight = (subset->fRight - 0.5f) * sx;
88 domain.fBottom = (subset->fBottom - 0.5f) * sy;
89 // This would cause us to read values from outside the subset. Surely, the caller knows
90 // better!
91 SkASSERT(copyParams.fFilter != GrTextureParams::kMipMap_FilterMode);
92 paint.addColorFragmentProcessor(
93 GrTextureDomainEffect::Create(inputTexture, SkMatrix::I(), domain,
94 GrTextureDomain::kClamp_Mode,
95 copyParams.fFilter))->unref();
96 } else {
97 GrTextureParams params(SkShader::kClamp_TileMode, copyParams.fFilter);
98 paint.addColorTextureProcessor(inputTexture, SkMatrix::I(), params);
99 }
egdaniel813351f2015-11-23 15:12:23 -0800100 paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode);
bsalomon89fe56b2015-10-29 10:49:28 -0700101
102 SkRect localRect;
103 if (subset) {
104 localRect = SkRect::Make(*subset);
105 localRect.fLeft *= sx;
106 localRect.fTop *= sy;
107 localRect.fRight *= sx;
108 localRect.fBottom *= sy;
109 } else {
110 localRect = SkRect::MakeWH(1.f, 1.f);
111 }
bsalomon045802d2015-10-20 07:58:01 -0700112
113 SkAutoTUnref<GrDrawContext> drawContext(context->drawContext(copy->asRenderTarget()));
114 if (!drawContext) {
115 return nullptr;
116 }
117
bsalomon89fe56b2015-10-29 10:49:28 -0700118 SkRect dstRect = SkRect::MakeWH(SkIntToScalar(rtDesc.fWidth), SkIntToScalar(rtDesc.fHeight));
bsalomona2e69fc2015-11-05 10:41:43 -0800119 drawContext->fillRectToRect(GrClip::WideOpen(), paint, SkMatrix::I(), dstRect, localRect);
bsalomon045802d2015-10-20 07:58:01 -0700120 return copy.detach();
121}
122
bsalomonc75be342015-10-29 12:34:31 -0700123GrTextureAdjuster::GrTextureAdjuster(GrTexture* original, const SkIRect& contentArea)
bsalomon3aa5fce2015-11-12 09:59:44 -0800124 : INHERITED(contentArea.width(), contentArea.height())
125 , fOriginal(original) {
126 SkASSERT(SkIRect::MakeWH(original->width(), original->height()).contains(contentArea));
bsalomonc75be342015-10-29 12:34:31 -0700127 if (contentArea.fLeft > 0 || contentArea.fTop > 0 ||
128 contentArea.fRight < original->width() || contentArea.fBottom < original->height()) {
129 fContentArea.set(contentArea);
bsalomon89fe56b2015-10-29 10:49:28 -0700130 }
131}
132
133GrTexture* GrTextureAdjuster::refTextureSafeForParams(const GrTextureParams& params,
134 SkIPoint* outOffset) {
135 GrTexture* texture = this->originalTexture();
136 GrContext* context = texture->getContext();
137 CopyParams copyParams;
bsalomonc55271f2015-11-09 11:55:57 -0800138 const SkIRect* contentArea = this->contentAreaOrNull();
bsalomon89fe56b2015-10-29 10:49:28 -0700139
bsalomonc75be342015-10-29 12:34:31 -0700140 if (contentArea && GrTextureParams::kMipMap_FilterMode == params.filterMode()) {
141 // If we generate a MIP chain for texture it will read pixel values from outside the content
142 // area.
143 copyParams.fWidth = contentArea->width();
144 copyParams.fHeight = contentArea->height();
145 copyParams.fFilter = GrTextureParams::kBilerp_FilterMode;
146 } else if (!context->getGpu()->makeCopyForTextureParams(texture->width(), texture->height(),
147 params, &copyParams)) {
bsalomon89fe56b2015-10-29 10:49:28 -0700148 if (outOffset) {
bsalomonc75be342015-10-29 12:34:31 -0700149 if (contentArea) {
150 outOffset->set(contentArea->fLeft, contentArea->fRight);
bsalomon89fe56b2015-10-29 10:49:28 -0700151 } else {
152 outOffset->set(0, 0);
153 }
154 }
155 return SkRef(texture);
156 }
157 GrUniqueKey key;
158 this->makeCopyKey(copyParams, &key);
159 if (key.isValid()) {
160 GrTexture* result = context->textureProvider()->findAndRefTextureByUniqueKey(key);
161 if (result) {
162 return result;
163 }
164 }
bsalomonc75be342015-10-29 12:34:31 -0700165 GrTexture* result = copy_on_gpu(texture, contentArea, copyParams);
bsalomon89fe56b2015-10-29 10:49:28 -0700166 if (result) {
167 if (key.isValid()) {
168 result->resourcePriv().setUniqueKey(key);
169 this->didCacheCopy(key);
170 }
171 if (outOffset) {
172 outOffset->set(0, 0);
173 }
174 }
175 return result;
176}
177
bsalomonc55271f2015-11-09 11:55:57 -0800178enum DomainMode {
179 kNoDomain_DomainMode,
180 kDomain_DomainMode,
181 kTightCopy_DomainMode
182};
183
184/** Determines whether a texture domain is necessary and if so what domain to use. There are two
185 * rectangles to consider:
186 * - The first is the content area specified by the texture adjuster. We can *never* allow
187 * filtering to cause bleed of pixels outside this rectangle.
188 * - The second rectangle is the constraint rectangle, which is known to be contained by the
189 * content area. The filterConstraint specifies whether we are allowed to bleed across this
190 * rect.
191 *
192 * We want to avoid using a domain if possible. We consider the above rectangles, the filter type,
193 * and whether the coords generated by the draw would all fall within the constraint rect. If the
194 * latter is true we only need to consider whether the filter would extend beyond the rects.
195 */
196static DomainMode determine_domain_mode(
197 const SkRect& constraintRect,
198 GrTextureAdjuster::FilterConstraint filterConstraint,
199 bool coordsLimitedToConstraintRect,
200 int texW, int texH,
201 const SkIRect* textureContentArea,
202 const GrTextureParams::FilterMode* filterModeOrNullForBicubic,
203 SkRect* domainRect) {
204
205 SkASSERT(SkRect::MakeIWH(texW, texH).contains(constraintRect));
206 // We only expect a content area rect if there is some non-content area.
207 SkASSERT(!textureContentArea ||
208 (!textureContentArea->contains(SkIRect::MakeWH(texW, texH)) &&
209 SkRect::Make(*textureContentArea).contains(constraintRect)));
210
211 SkRect textureBounds = SkRect::MakeIWH(texW, texH);
212 // If the src rectangle contains the whole texture then no need for a domain.
213 if (constraintRect.contains(textureBounds)) {
214 return kNoDomain_DomainMode;
215 }
216
bsalomonb1b01992015-11-18 10:56:08 -0800217 bool restrictFilterToRect = (filterConstraint == GrTextureProducer::kYes_FilterConstraint);
bsalomonc55271f2015-11-09 11:55:57 -0800218
219 // If we can filter outside the constraint rect, and there is no non-content area of the
220 // texture, and we aren't going to generate sample coords outside the constraint rect then we
221 // don't need a domain.
222 if (!restrictFilterToRect && !textureContentArea && coordsLimitedToConstraintRect) {
223 return kNoDomain_DomainMode;
224 }
225
226 // Get the domain inset based on sampling mode (or bail if mipped)
227 SkScalar filterHalfWidth = 0.f;
228 if (filterModeOrNullForBicubic) {
229 switch (*filterModeOrNullForBicubic) {
230 case GrTextureParams::kNone_FilterMode:
231 if (coordsLimitedToConstraintRect) {
232 return kNoDomain_DomainMode;
233 } else {
234 filterHalfWidth = 0.f;
235 }
236 break;
237 case GrTextureParams::kBilerp_FilterMode:
238 filterHalfWidth = .5f;
239 break;
240 case GrTextureParams::kMipMap_FilterMode:
bsalomonb1b01992015-11-18 10:56:08 -0800241 if (restrictFilterToRect || textureContentArea) {
242 // No domain can save us here.
243 return kTightCopy_DomainMode;
244 }
245 return kNoDomain_DomainMode;
bsalomonc55271f2015-11-09 11:55:57 -0800246 }
247 } else {
248 // bicubic does nearest filtering internally.
249 filterHalfWidth = 1.5f;
250 }
251
252 // Both bilerp and bicubic use bilinear filtering and so need to be clamped to the center
253 // of the edge texel. Pinning to the texel center has no impact on nearest mode and MIP-maps
254
255 static const SkScalar kDomainInset = 0.5f;
256 // Figure out the limits of pixels we're allowed to sample from.
257 // Unless we know the amount of outset and the texture matrix we have to conservatively enforce
258 // the domain.
259 if (restrictFilterToRect) {
260 domainRect->fLeft = constraintRect.fLeft + kDomainInset;
261 domainRect->fTop = constraintRect.fTop + kDomainInset;
262 domainRect->fRight = constraintRect.fRight - kDomainInset;
263 domainRect->fBottom = constraintRect.fBottom - kDomainInset;
264 } else if (textureContentArea) {
265 // If we got here then: there is a textureContentArea, the coords are limited to the
266 // constraint rect, and we're allowed to filter across the constraint rect boundary. So
267 // we check whether the filter would reach across the edge of the content area.
268 // We will only set the sides that are required.
269
270 domainRect->setLargest();
271 if (coordsLimitedToConstraintRect) {
272 // We may be able to use the fact that the texture coords are limited to the constraint
273 // rect in order to avoid having to add a domain.
274 bool needContentAreaConstraint = false;
275 if (textureContentArea->fLeft > 0 &&
276 textureContentArea->fLeft + filterHalfWidth > constraintRect.fLeft) {
277 domainRect->fLeft = textureContentArea->fLeft + kDomainInset;
278 needContentAreaConstraint = true;
279 }
280 if (textureContentArea->fTop > 0 &&
281 textureContentArea->fTop + filterHalfWidth > constraintRect.fTop) {
282 domainRect->fTop = textureContentArea->fTop + kDomainInset;
283 needContentAreaConstraint = true;
284 }
285 if (textureContentArea->fRight < texW &&
286 textureContentArea->fRight - filterHalfWidth < constraintRect.fRight) {
287 domainRect->fRight = textureContentArea->fRight - kDomainInset;
288 needContentAreaConstraint = true;
289 }
290 if (textureContentArea->fBottom < texH &&
291 textureContentArea->fBottom - filterHalfWidth < constraintRect.fBottom) {
292 domainRect->fBottom = textureContentArea->fBottom - kDomainInset;
293 needContentAreaConstraint = true;
294 }
295 if (!needContentAreaConstraint) {
296 return kNoDomain_DomainMode;
297 }
298 } else {
299 // Our sample coords for the texture are allowed to be outside the constraintRect so we
300 // don't consider it when computing the domain.
301 if (textureContentArea->fLeft != 0) {
302 domainRect->fLeft = textureContentArea->fLeft + kDomainInset;
303 }
304 if (textureContentArea->fTop != 0) {
305 domainRect->fTop = textureContentArea->fTop + kDomainInset;
306 }
307 if (textureContentArea->fRight != texW) {
308 domainRect->fRight = textureContentArea->fRight - kDomainInset;
309 }
310 if (textureContentArea->fBottom != texH) {
311 domainRect->fBottom = textureContentArea->fBottom - kDomainInset;
312 }
313 }
314 } else {
315 return kNoDomain_DomainMode;
316 }
317
318 if (domainRect->fLeft > domainRect->fRight) {
319 domainRect->fLeft = domainRect->fRight = SkScalarAve(domainRect->fLeft, domainRect->fRight);
320 }
321 if (domainRect->fTop > domainRect->fBottom) {
322 domainRect->fTop = domainRect->fBottom = SkScalarAve(domainRect->fTop, domainRect->fBottom);
323 }
324 domainRect->fLeft /= texW;
325 domainRect->fTop /= texH;
326 domainRect->fRight /= texW;
327 domainRect->fBottom /= texH;
328 return kDomain_DomainMode;
329}
330
bsalomonb1b01992015-11-18 10:56:08 -0800331static const GrFragmentProcessor* create_fp_for_domain_and_filter(
332 GrTexture* texture,
333 const SkMatrix& textureMatrix,
334 DomainMode domainMode,
335 const SkRect& domain,
336 const GrTextureParams::FilterMode* filterOrNullForBicubic) {
337 SkASSERT(kTightCopy_DomainMode != domainMode);
338 if (filterOrNullForBicubic) {
339 if (kDomain_DomainMode == domainMode) {
340 return GrTextureDomainEffect::Create(texture, textureMatrix, domain,
341 GrTextureDomain::kClamp_Mode,
342 *filterOrNullForBicubic);
343 } else {
344 GrTextureParams params(SkShader::kClamp_TileMode, *filterOrNullForBicubic);
345 return GrSimpleTextureEffect::Create(texture, textureMatrix, params);
346 }
347 } else {
348 if (kDomain_DomainMode == domainMode) {
349 return GrBicubicEffect::Create(texture, textureMatrix, domain);
350 } else {
351 static const SkShader::TileMode kClampClamp[] =
352 { SkShader::kClamp_TileMode, SkShader::kClamp_TileMode };
353 return GrBicubicEffect::Create(texture, textureMatrix, kClampClamp);
354 }
355 }
356}
357
bsalomonc55271f2015-11-09 11:55:57 -0800358const GrFragmentProcessor* GrTextureAdjuster::createFragmentProcessor(
bsalomon3aa5fce2015-11-12 09:59:44 -0800359 const SkMatrix& origTextureMatrix,
360 const SkRect& origConstraintRect,
bsalomonc55271f2015-11-09 11:55:57 -0800361 FilterConstraint filterConstraint,
362 bool coordsLimitedToConstraintRect,
363 const GrTextureParams::FilterMode* filterOrNullForBicubic) {
364
bsalomon3aa5fce2015-11-12 09:59:44 -0800365 SkMatrix textureMatrix = origTextureMatrix;
bsalomonc55271f2015-11-09 11:55:57 -0800366 const SkIRect* contentArea = this->contentAreaOrNull();
bsalomon3aa5fce2015-11-12 09:59:44 -0800367 // Convert the constraintRect to be relative to the texture rather than the content area so
368 // that both rects are in the same coordinate system.
369 SkTCopyOnFirstWrite<SkRect> constraintRect(origConstraintRect);
370 if (contentArea) {
371 SkScalar l = SkIntToScalar(contentArea->fLeft);
372 SkScalar t = SkIntToScalar(contentArea->fTop);
373 constraintRect.writable()->offset(l, t);
374 textureMatrix.postTranslate(l, t);
375 }
bsalomonc55271f2015-11-09 11:55:57 -0800376
377 SkRect domain;
378 GrTexture* texture = this->originalTexture();
379 DomainMode domainMode =
bsalomon3aa5fce2015-11-12 09:59:44 -0800380 determine_domain_mode(*constraintRect, filterConstraint, coordsLimitedToConstraintRect,
bsalomonc55271f2015-11-09 11:55:57 -0800381 texture->width(), texture->height(),
382 contentArea, filterOrNullForBicubic,
383 &domain);
384 if (kTightCopy_DomainMode == domainMode) {
385 // TODO: Copy the texture and adjust the texture matrix (both parts need to consider
386 // non-int constraint rect)
387 // For now: treat as bilerp and ignore what goes on above level 0.
388
389 // We only expect MIP maps to require a tight copy.
390 SkASSERT(filterOrNullForBicubic &&
391 GrTextureParams::kMipMap_FilterMode == *filterOrNullForBicubic);
392 static const GrTextureParams::FilterMode kBilerp = GrTextureParams::kBilerp_FilterMode;
393 domainMode =
bsalomon3aa5fce2015-11-12 09:59:44 -0800394 determine_domain_mode(*constraintRect, filterConstraint, coordsLimitedToConstraintRect,
bsalomonc55271f2015-11-09 11:55:57 -0800395 texture->width(), texture->height(),
396 contentArea, &kBilerp, &domain);
397 SkASSERT(kTightCopy_DomainMode != domainMode);
bsalomonc55271f2015-11-09 11:55:57 -0800398 }
399 SkASSERT(kNoDomain_DomainMode == domainMode ||
400 (domain.fLeft <= domain.fRight && domain.fTop <= domain.fBottom));
bsalomon3aa5fce2015-11-12 09:59:44 -0800401 textureMatrix.postIDiv(texture->width(), texture->height());
bsalomonb1b01992015-11-18 10:56:08 -0800402 return create_fp_for_domain_and_filter(texture, textureMatrix, domainMode, domain,
403 filterOrNullForBicubic);
bsalomonc55271f2015-11-09 11:55:57 -0800404}
405
bsalomon89fe56b2015-10-29 10:49:28 -0700406//////////////////////////////////////////////////////////////////////////////
407
bsalomonb1b01992015-11-18 10:56:08 -0800408GrTexture* GrTextureMaker::refTextureForParams(const GrTextureParams& params) {
bsalomon045802d2015-10-20 07:58:01 -0700409 CopyParams copyParams;
bsalomonb1b01992015-11-18 10:56:08 -0800410 if (!fContext->getGpu()->makeCopyForTextureParams(this->width(), this->height(), params,
411 &copyParams)) {
412 return this->refOriginalTexture();
bsalomon045802d2015-10-20 07:58:01 -0700413 }
414 GrUniqueKey copyKey;
415 this->makeCopyKey(copyParams, &copyKey);
416 if (copyKey.isValid()) {
bsalomonb1b01992015-11-18 10:56:08 -0800417 GrTexture* result = fContext->textureProvider()->findAndRefTextureByUniqueKey(copyKey);
bsalomon045802d2015-10-20 07:58:01 -0700418 if (result) {
419 return result;
420 }
421 }
422
bsalomonb1b01992015-11-18 10:56:08 -0800423 GrTexture* result = this->generateTextureForParams(copyParams);
bsalomon045802d2015-10-20 07:58:01 -0700424 if (!result) {
425 return nullptr;
426 }
427
428 if (copyKey.isValid()) {
bsalomonb1b01992015-11-18 10:56:08 -0800429 fContext->textureProvider()->assignUniqueKeyToTexture(copyKey, result);
bsalomon045802d2015-10-20 07:58:01 -0700430 this->didCacheCopy(copyKey);
431 }
432 return result;
433}
434
bsalomonb1b01992015-11-18 10:56:08 -0800435const GrFragmentProcessor* GrTextureMaker::createFragmentProcessor(
436 const SkMatrix& textureMatrix,
437 const SkRect& constraintRect,
438 FilterConstraint filterConstraint,
439 bool coordsLimitedToConstraintRect,
440 const GrTextureParams::FilterMode* filterOrNullForBicubic) {
441
442 const GrTextureParams::FilterMode* fmForDetermineDomain = filterOrNullForBicubic;
443 if (filterOrNullForBicubic && GrTextureParams::kMipMap_FilterMode == *filterOrNullForBicubic &&
444 kYes_FilterConstraint == filterConstraint) {
445 // TODo: Here we should force a copy restricted to the constraintRect since MIP maps will
446 // read outside the constraint rect. However, as in the adjuster case, we aren't currently
447 // doing that.
448 // We instead we compute the domain as though were bilerping which is only correct if we
449 // only sample level 0.
450 static const GrTextureParams::FilterMode kBilerp = GrTextureParams::kBilerp_FilterMode;
451 fmForDetermineDomain = &kBilerp;
452 }
453
454 GrTextureParams params;
455 if (filterOrNullForBicubic) {
456 params.reset(SkShader::kClamp_TileMode, *filterOrNullForBicubic);
457 } else {
458 // Bicubic doesn't use filtering for it's texture accesses.
459 params.reset(SkShader::kClamp_TileMode, GrTextureParams::kNone_FilterMode);
460 }
461 SkAutoTUnref<GrTexture> texture(this->refTextureForParams(params));
462 if (!texture) {
463 return nullptr;
464 }
465 SkRect domain;
466 DomainMode domainMode =
467 determine_domain_mode(constraintRect, filterConstraint, coordsLimitedToConstraintRect,
468 texture->width(), texture->height(), nullptr, fmForDetermineDomain,
469 &domain);
470 SkASSERT(kTightCopy_DomainMode != domainMode);
471 SkMatrix normalizedTextureMatrix = textureMatrix;
472 normalizedTextureMatrix.postIDiv(texture->width(), texture->height());
473 return create_fp_for_domain_and_filter(texture, normalizedTextureMatrix, domainMode, domain,
474 filterOrNullForBicubic);
475}
476
477GrTexture* GrTextureMaker::generateTextureForParams(const CopyParams& copyParams) {
478 SkAutoTUnref<GrTexture> original(this->refOriginalTexture());
bsalomon100b8f82015-10-28 08:37:44 -0700479 if (!original) {
480 return nullptr;
bsalomon045802d2015-10-20 07:58:01 -0700481 }
bsalomon89fe56b2015-10-29 10:49:28 -0700482 return copy_on_gpu(original, nullptr, copyParams);
bsalomon045802d2015-10-20 07:58:01 -0700483}