Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
TextureUtils.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2022 Google LLC
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
9
18#include "src/core/SkDevice.h"
21#include "src/core/SkMipmap.h"
25
34#include "src/gpu/BlurUtils.h"
52
53#include <array>
54
55
59
60namespace skgpu::graphite {
61
62namespace {
63
64sk_sp<Surface> make_renderable_scratch_surface(
65 Recorder* recorder,
66 const SkImageInfo& info,
67 const SkSurfaceProps* surfaceProps = nullptr) {
68 SkColorType ct = recorder->priv().caps()->getRenderableColorType(info.colorType());
69 if (ct == kUnknown_SkColorType) {
70 return nullptr;
71 }
72
73 // TODO(b/323886870): Historically the scratch surfaces used here were exact-fit but they should
74 // be able to be approx-fit and uninstantiated.
75 return Surface::MakeScratch(recorder,
76 info.makeColorType(ct),
78 Mipmapped::kNo,
80}
81
82bool valid_client_provided_image(const SkImage* clientProvided,
83 const SkImage* original,
84 SkImage::RequiredProperties requiredProps) {
85 if (!clientProvided ||
86 !as_IB(clientProvided)->isGraphiteBacked() ||
87 original->dimensions() != clientProvided->dimensions() ||
88 original->alphaType() != clientProvided->alphaType()) {
89 return false;
90 }
91
92 uint32_t origChannels = SkColorTypeChannelFlags(original->colorType());
93 uint32_t clientChannels = SkColorTypeChannelFlags(clientProvided->colorType());
94 if ((origChannels & clientChannels) != origChannels) {
95 return false;
96 }
97
98 // We require provided images to have a TopLeft origin
99 auto graphiteImage = static_cast<const Image*>(clientProvided);
100 if (graphiteImage->textureProxyView().origin() != Origin::kTopLeft) {
101 SKGPU_LOG_E("Client provided image must have a TopLeft origin.");
102 return false;
103 }
104
105 return true;
106}
107
108sk_sp<SkSpecialImage> eval_blur(Recorder* recorder,
109 sk_sp<SkShader> blurEffect,
110 const SkIRect& dstRect,
113 const SkSurfaceProps& outProps) {
114 SkImageInfo outII = SkImageInfo::Make({dstRect.width(), dstRect.height()},
115 colorType, kPremul_SkAlphaType, std::move(outCS));
116 // Protected-ness is pulled off of the recorder
117 auto device = Device::Make(recorder,
118 outII,
120 Mipmapped::kNo,
121#if defined(GRAPHITE_USE_APPROX_FIT_FOR_FILTERS)
123#else
125#endif
126 outProps,
128 if (!device) {
129 return nullptr;
130 }
131
132 // TODO(b/294102201): This is very much like AutoSurface in SkImageFilterTypes.cpp
133 SkIRect subset = SkIRect::MakeSize(dstRect.size());
134 device->clipRect(SkRect::Make(subset), SkClipOp::kIntersect, /*aa=*/false);
135 device->setLocalToDevice(SkM44::Translate(-dstRect.left(), -dstRect.top()));
137 paint.setBlendMode(SkBlendMode::kSrc);
138 paint.setShader(std::move(blurEffect));
139 device->drawPaint(paint);
140 return device->snapSpecial(subset);
141}
142
143sk_sp<SkSpecialImage> blur_2d(Recorder* recorder,
144 SkSize sigma,
145 SkISize radii,
147 const SkIRect& srcRect,
148 SkTileMode tileMode,
149 const SkIRect& dstRect,
151 const SkSurfaceProps& outProps) {
152 std::array<SkV4, kMaxBlurSamples/4> kernel;
153 std::array<SkV4, kMaxBlurSamples/2> offsets;
154 Compute2DBlurKernel(sigma, radii, kernel);
155 Compute2DBlurOffsets(radii, offsets);
156
158 builder.uniform("kernel") = kernel;
159 builder.uniform("offsets") = offsets;
160 // TODO(b/294102201): This is very much like FilterResult::asShader()...
161 builder.child("child") =
162 input->makeSubset(srcRect)->asShader(tileMode,
164 SkMatrix::Translate(srcRect.left(),srcRect.top()));
165
166 return eval_blur(recorder, builder.makeShader(), dstRect,
167 input->colorType(), std::move(outCS), outProps);
168}
169
170sk_sp<SkSpecialImage> blur_1d(Recorder* recorder,
171 float sigma,
172 int radius,
173 SkV2 dir,
175 SkIRect srcRect,
176 SkTileMode tileMode,
177 SkIRect dstRect,
179 const SkSurfaceProps& outProps) {
180 std::array<SkV4, kMaxBlurSamples/2> offsetsAndKernel;
181 Compute1DBlurLinearKernel(sigma, radius, offsetsAndKernel);
182
184 builder.uniform("offsetsAndKernel") = offsetsAndKernel;
185 builder.uniform("dir") = dir;
186 // TODO(b/294102201): This is very much like FilterResult::asShader()...
187 builder.child("child") =
188 input->makeSubset(srcRect)->asShader(tileMode,
190 SkMatrix::Translate(srcRect.left(),srcRect.top()));
191
192 return eval_blur(recorder, builder.makeShader(), dstRect,
193 input->colorType(), std::move(outCS), outProps);
194}
195
196sk_sp<SkSpecialImage> blur_impl(Recorder* recorder,
197 SkSize sigma,
199 SkIRect srcRect,
200 SkTileMode tileMode,
201 SkIRect dstRect,
203 const SkSurfaceProps& outProps) {
204 // See if we can do a blur on the original resolution image
205 if (sigma.width() <= kMaxLinearBlurSigma &&
206 sigma.height() <= kMaxLinearBlurSigma) {
207 int radiusX = BlurSigmaRadius(sigma.width());
208 int radiusY = BlurSigmaRadius(sigma.height());
209 const int kernelArea = BlurKernelWidth(radiusX) * BlurKernelWidth(radiusY);
210 if (kernelArea <= kMaxBlurSamples && radiusX > 0 && radiusY > 0) {
211 // Use a single-pass 2D kernel if it fits and isn't just 1D already
212 return blur_2d(recorder, sigma, {radiusX, radiusY}, std::move(input), srcRect, tileMode,
213 dstRect, std::move(outCS), outProps);
214 } else {
215 // Use two passes of a 1D kernel (one per axis).
216 if (radiusX > 0) {
217 SkIRect intermediateDstRect = dstRect;
218 if (radiusY > 0) {
219 // Outset the output size of dstRect by the radius required for the next Y pass
220 intermediateDstRect.outset(0, radiusY);
221 if (!intermediateDstRect.intersect(srcRect.makeOutset(radiusX, radiusY))) {
222 return nullptr;
223 }
224 }
225
226 input = blur_1d(recorder, sigma.width(), radiusX, {1.f, 0.f},
227 std::move(input), srcRect, tileMode, intermediateDstRect,
228 outCS, outProps);
229 if (!input) {
230 return nullptr;
231 }
232 srcRect = SkIRect::MakeWH(input->width(), input->height());
233 dstRect.offset(-intermediateDstRect.left(), -intermediateDstRect.top());
234 }
235
236 if (radiusY > 0) {
237 input = blur_1d(recorder, sigma.height(), radiusY, {0.f, 1.f},
238 std::move(input), srcRect, tileMode, dstRect, outCS, outProps);
239 }
240
241 return input;
242 }
243 } else {
244 // Rescale the source image, blur that with a reduced sigma, and then upscale back to the
245 // dstRect dimensions.
246 // TODO(b/294102201): Share rescaling logic with GrBlurUtils::GaussianBlur.
247 float sx = sigma.width() > kMaxLinearBlurSigma
248 ? (kMaxLinearBlurSigma / sigma.width()) : 1.f;
249 float sy = sigma.height() > kMaxLinearBlurSigma
250 ? (kMaxLinearBlurSigma / sigma.height()) : 1.f;
251
252 int targetSrcWidth = sk_float_ceil2int(srcRect.width() * sx);
253 int targetSrcHeight = sk_float_ceil2int(srcRect.height() * sy);
254
255 auto inputImage = input->asImage();
256 // TODO(b/288902559): Support approx fit backings for the target of a rescale
257 // TODO(b/294102201): Be smarter about downscaling when there are actual tilemodes to apply
258 // to the image.
259 auto scaledInput = RescaleImage(
260 recorder,
261 inputImage.get(),
262 srcRect.makeOffset(input->subset().topLeft()),
263 inputImage->imageInfo().makeWH(targetSrcWidth, targetSrcHeight),
264 SkImage::RescaleGamma::kLinear,
265 SkImage::RescaleMode::kRepeatedLinear);
266 if (!scaledInput) {
267 return nullptr;
268 }
269
270 // Calculate a scaled dstRect to match (0,0,targetSrcWidth,targetSrcHeight) as srcRect.
271 SkIRect targetDstRect = SkRect::MakeXYWH((dstRect.left() - srcRect.left()) * sx,
272 (dstRect.top() - srcRect.top()) * sy,
273 dstRect.width()*sx,
274 dstRect.height()*sy).roundOut();
275 SkIRect targetSrcRect = SkIRect::MakeWH(targetSrcWidth, targetSrcHeight);
276 // Blur with pinned sigmas. If the sigma was less than the max, that axis of the image was
277 // not scaled so we can use the original. If it was greater than the max, the scale factor
278 // should have taken it the max supported sigma (ignoring the effect of rounding out the
279 // source bounds).
280 auto scaledOutput = blur_impl(
281 recorder,
282 {std::min(sigma.width(), kMaxLinearBlurSigma),
283 std::min(sigma.height(), kMaxLinearBlurSigma)},
284 SkSpecialImages::MakeGraphite(recorder,
285 targetSrcRect,
286 std::move(scaledInput),
287 outProps),
288 targetSrcRect,
289 tileMode,
290 targetDstRect,
291 outCS,
292 outProps);
293 if (!scaledOutput) {
294 return nullptr;
295 }
296
297 // TODO: Pass out the upscaling transform for skif::FilterResult to hold on to.
298 auto scaledOutputImage = scaledOutput->asImage();
299 auto outputImage = RescaleImage(
300 recorder,
301 scaledOutputImage.get(),
302 scaledOutput->subset(),
303 scaledOutputImage->imageInfo().makeWH(dstRect.width(), dstRect.height()),
304 SkImage::RescaleGamma::kLinear,
305 SkImage::RescaleMode::kLinear);
306 if (!outputImage) {
307 return nullptr;
308 }
309
310 SkIRect outputDstRect = outputImage->bounds();
311 return SkSpecialImages::MakeGraphite(recorder,
312 outputDstRect,
313 std::move(outputImage),
314 outProps);
315 }
316}
317
318// This class is the lazy instantiation callback for promise images. It manages calling the
319// client's Fulfill, ImageRelease, and TextureRelease procs.
320class PromiseLazyInstantiateCallback {
321public:
322 PromiseLazyInstantiateCallback(sk_sp<RefCntedCallback> releaseHelper,
323 GraphitePromiseTextureFulfillProc fulfillProc,
324 GraphitePromiseTextureFulfillContext fulfillContext,
325 GraphitePromiseTextureReleaseProc textureReleaseProc)
326 : fReleaseHelper(std::move(releaseHelper))
327 , fFulfillProc(fulfillProc)
328 , fFulfillContext(fulfillContext)
329 , fTextureReleaseProc(textureReleaseProc) {
330 }
331 PromiseLazyInstantiateCallback(PromiseLazyInstantiateCallback&&) = default;
332 PromiseLazyInstantiateCallback(const PromiseLazyInstantiateCallback&) {
333 // Because we get wrapped in std::function we must be copyable. But we should never
334 // be copied.
335 SkASSERT(false);
336 }
337 PromiseLazyInstantiateCallback& operator=(PromiseLazyInstantiateCallback&&) = default;
338 PromiseLazyInstantiateCallback& operator=(const PromiseLazyInstantiateCallback&) {
339 SkASSERT(false);
340 return *this;
341 }
342
343 sk_sp<Texture> operator()(ResourceProvider* resourceProvider) {
344 // Invoke the fulfill proc to get the promised backend texture.
345 auto [ backendTexture, textureReleaseCtx ] = fFulfillProc(fFulfillContext);
346 if (!backendTexture.isValid()) {
347 SKGPU_LOG_W("FulfillProc returned an invalid backend texture");
348 return nullptr;
349 }
350
351 sk_sp<RefCntedCallback> textureReleaseCB = RefCntedCallback::Make(fTextureReleaseProc,
352 textureReleaseCtx);
353
354 sk_sp<Texture> texture = resourceProvider->createWrappedTexture(backendTexture);
355 if (!texture) {
356 SKGPU_LOG_W("Failed to wrap BackendTexture returned by fulfill proc");
357 return nullptr;
358 }
359
360 texture->setReleaseCallback(std::move(textureReleaseCB));
361 return texture;
362 }
363
364private:
365 sk_sp<RefCntedCallback> fReleaseHelper;
368 GraphitePromiseTextureReleaseProc fTextureReleaseProc;
369};
370
371} // anonymous namespace
372
373std::tuple<TextureProxyView, SkColorType> MakeBitmapProxyView(Recorder* recorder,
374 const SkBitmap& bitmap,
375 sk_sp<SkMipmap> mipmapsIn,
376 Mipmapped mipmapped,
377 Budgeted budgeted) {
378 // Adjust params based on input and Caps
379 const Caps* caps = recorder->priv().caps();
380 SkColorType ct = bitmap.info().colorType();
381
382 if (bitmap.dimensions().area() <= 1) {
383 mipmapped = Mipmapped::kNo;
384 }
385
386 Protected isProtected = recorder->priv().isProtected();
387 auto textureInfo = caps->getDefaultSampledTextureInfo(ct, mipmapped, isProtected,
388 Renderable::kNo);
389 if (!textureInfo.isValid()) {
391 textureInfo = caps->getDefaultSampledTextureInfo(ct, mipmapped, isProtected,
392 Renderable::kNo);
393 }
394 SkASSERT(textureInfo.isValid());
395
396 // Convert bitmap to texture colortype if necessary
397 SkBitmap bmpToUpload;
398 if (ct != bitmap.info().colorType()) {
399 if (!bmpToUpload.tryAllocPixels(bitmap.info().makeColorType(ct)) ||
400 !bitmap.readPixels(bmpToUpload.pixmap())) {
401 return {};
402 }
403 bmpToUpload.setImmutable();
404 } else {
405 bmpToUpload = bitmap;
406 }
407
408 if (!SkImageInfoIsValid(bmpToUpload.info())) {
409 return {};
410 }
411
412 int mipLevelCount = (mipmapped == Mipmapped::kYes) ?
413 SkMipmap::ComputeLevelCount(bitmap.width(), bitmap.height()) + 1 : 1;
414
415
416 // setup MipLevels
417 sk_sp<SkMipmap> mipmaps;
418 std::vector<MipLevel> texels;
419 if (mipLevelCount == 1) {
420 texels.resize(mipLevelCount);
421 texels[0].fPixels = bmpToUpload.getPixels();
422 texels[0].fRowBytes = bmpToUpload.rowBytes();
423 } else {
424 mipmaps = SkToBool(mipmapsIn)
425 ? mipmapsIn
426 : sk_sp<SkMipmap>(SkMipmap::Build(bmpToUpload.pixmap(), nullptr));
427 if (!mipmaps) {
428 return {};
429 }
430
431 SkASSERT(mipLevelCount == mipmaps->countLevels() + 1);
432 texels.resize(mipLevelCount);
433
434 texels[0].fPixels = bmpToUpload.getPixels();
435 texels[0].fRowBytes = bmpToUpload.rowBytes();
436
437 for (int i = 1; i < mipLevelCount; ++i) {
438 SkMipmap::Level generatedMipLevel;
439 mipmaps->getLevel(i - 1, &generatedMipLevel);
440 texels[i].fPixels = generatedMipLevel.fPixmap.addr();
441 texels[i].fRowBytes = generatedMipLevel.fPixmap.rowBytes();
442 SkASSERT(texels[i].fPixels);
443 SkASSERT(generatedMipLevel.fPixmap.colorType() == bmpToUpload.colorType());
444 }
445 }
446
447 // Create proxy
449 bmpToUpload.dimensions(), textureInfo, budgeted);
450 if (!proxy) {
451 return {};
452 }
453 SkASSERT(caps->areColorTypeAndTextureInfoCompatible(ct, proxy->textureInfo()));
454 SkASSERT(mipmapped == Mipmapped::kNo || proxy->mipmapped() == Mipmapped::kYes);
455
456 // Src and dst colorInfo are the same
457 const SkColorInfo& colorInfo = bmpToUpload.info().colorInfo();
458 // Add UploadTask to Recorder
460 recorder, proxy, colorInfo, colorInfo, texels,
461 SkIRect::MakeSize(bmpToUpload.dimensions()), std::make_unique<ImageUploadContext>());
462 if (!upload.isValid()) {
463 SKGPU_LOG_E("MakeBitmapProxyView: Could not create UploadInstance");
464 return {};
465 }
466 recorder->priv().add(UploadTask::Make(std::move(upload)));
467
468 Swizzle swizzle = caps->getReadSwizzle(ct, textureInfo);
469 // If the color type is alpha-only, propagate the alpha value to the other channels.
470 if (SkColorTypeIsAlphaOnly(colorInfo.colorType())) {
471 swizzle = Swizzle::Concat(swizzle, Swizzle("aaaa"));
472 }
473 return {{std::move(proxy), swizzle}, ct};
474}
475
477 const Caps* caps,
478 SkISize dimensions,
479 TextureInfo textureInfo,
480 Volatile isVolatile,
481 sk_sp<RefCntedCallback> releaseHelper,
482 GraphitePromiseTextureFulfillProc fulfillProc,
483 GraphitePromiseTextureFulfillContext fulfillContext,
484 GraphitePromiseTextureReleaseProc textureReleaseProc) {
485 SkASSERT(!dimensions.isEmpty());
486 SkASSERT(releaseHelper);
487
488 if (!fulfillProc) {
489 return nullptr;
490 }
491
492 PromiseLazyInstantiateCallback callback{std::move(releaseHelper), fulfillProc,
493 fulfillContext, textureReleaseProc};
494 // Proxies for promise images are assumed to always be destined for a client's SkImage so
495 // are never considered budgeted.
496 return TextureProxy::MakeLazy(caps, dimensions, textureInfo,
497 Budgeted::kNo, isVolatile, std::move(callback));
498}
499
501 const SkColorInfo& colorInfo,
502 const SkBitmap& bitmap,
503 sk_sp<SkMipmap> mipmaps,
504 Budgeted budgeted,
505 SkImage::RequiredProperties requiredProps) {
506 auto mm = requiredProps.fMipmapped ? Mipmapped::kYes : Mipmapped::kNo;
507 auto [view, ct] = MakeBitmapProxyView(recorder, bitmap, std::move(mipmaps), mm, budgeted);
508 if (!view) {
509 return nullptr;
510 }
511
512 SkASSERT(!requiredProps.fMipmapped || view.proxy()->mipmapped() == Mipmapped::kYes);
513 return sk_make_sp<skgpu::graphite::Image>(std::move(view), colorInfo.makeColorType(ct));
514}
515
516size_t ComputeSize(SkISize dimensions,
517 const TextureInfo& info) {
518
519 SkTextureCompressionType compression = info.compressionType();
520
521 size_t colorSize = 0;
522
523 if (compression != SkTextureCompressionType::kNone) {
524 colorSize = SkCompressedFormatDataSize(compression,
525 dimensions,
526 info.mipmapped() == Mipmapped::kYes);
527 } else {
528 // TODO: Should we make sure the backends return zero here if the TextureInfo is for a
529 // memoryless texture?
530 size_t bytesPerPixel = info.bytesPerPixel();
531
532 colorSize = (size_t)dimensions.width() * dimensions.height() * bytesPerPixel;
533 }
534
535 size_t finalSize = colorSize * info.numSamples();
536
537 if (info.mipmapped() == Mipmapped::kYes) {
538 finalSize += colorSize/3;
539 }
540 return finalSize;
541}
542
544 const SkImage* srcImage,
545 SkIRect srcIRect,
546 const SkImageInfo& dstInfo,
547 SkImage::RescaleGamma rescaleGamma,
548 SkImage::RescaleMode rescaleMode) {
549 TRACE_EVENT0("skia.gpu", TRACE_FUNC);
550 TRACE_EVENT_INSTANT2("skia.gpu", "RescaleImage Src", TRACE_EVENT_SCOPE_THREAD,
551 "width", srcIRect.width(), "height", srcIRect.height());
552 TRACE_EVENT_INSTANT2("skia.gpu", "RescaleImage Dst", TRACE_EVENT_SCOPE_THREAD,
553 "width", dstInfo.width(), "height", dstInfo.height());
554
555 // make a Surface matching dstInfo to rescale into
556 SkSurfaceProps surfaceProps = {};
557 sk_sp<SkSurface> dst = make_renderable_scratch_surface(recorder, dstInfo, &surfaceProps);
558 if (!dst) {
559 return nullptr;
560 }
561
562 SkRect srcRect = SkRect::Make(srcIRect);
563 SkRect dstRect = SkRect::Make(dstInfo.dimensions());
564
565 // Get backing texture information for source Image.
566 // For now this needs to be texturable because we can't depend on copies to scale.
567 auto srcGraphiteImage = reinterpret_cast<const graphite::Image*>(srcImage);
568
569 const TextureProxyView& imageView = srcGraphiteImage->textureProxyView();
570 if (!imageView.proxy()) {
571 // With the current definition of SkImage, this shouldn't happen.
572 // If we allow non-texturable formats for compute, we'll need to
573 // copy to a texturable format.
574 SkASSERT(false);
575 return nullptr;
576 }
577
578 SkISize finalSize = SkISize::Make(dstRect.width(), dstRect.height());
579 if (finalSize == srcIRect.size()) {
580 rescaleGamma = Image::RescaleGamma::kSrc;
581 rescaleMode = Image::RescaleMode::kNearest;
582 }
583
584 // Within a rescaling pass tempInput is read from and tempOutput is written to.
585 // At the end of the pass tempOutput's texture is wrapped and assigned to tempInput.
586 const SkImageInfo& srcImageInfo = srcImage->imageInfo();
587 sk_sp<SkImage> tempInput(new Image(imageView, srcImageInfo.colorInfo()));
588 sk_sp<SkSurface> tempOutput;
589
590 // Assume we should ignore the rescale linear request if the surface has no color space since
591 // it's unclear how we'd linearize from an unknown color space.
592 if (rescaleGamma == Image::RescaleGamma::kLinear &&
593 srcImageInfo.colorSpace() &&
594 !srcImageInfo.colorSpace()->gammaIsLinear()) {
595 // Draw the src image into a new surface with linear gamma, and make that the new tempInput
596 sk_sp<SkColorSpace> linearGamma = srcImageInfo.colorSpace()->makeLinearGamma();
597 SkImageInfo gammaDstInfo = SkImageInfo::Make(srcIRect.size(),
598 tempInput->imageInfo().colorType(),
600 std::move(linearGamma));
601 tempOutput = make_renderable_scratch_surface(recorder, gammaDstInfo, &surfaceProps);
602 if (!tempOutput) {
603 return nullptr;
604 }
605 SkCanvas* gammaDst = tempOutput->getCanvas();
606 SkRect gammaDstRect = SkRect::Make(srcIRect.size());
607
609 paint.setBlendMode(SkBlendMode::kSrc);
610 gammaDst->drawImageRect(tempInput, srcRect, gammaDstRect,
613 tempInput = SkSurfaces::AsImage(std::move(tempOutput));
614 srcRect = gammaDstRect;
615 }
616
617 SkImageInfo outImageInfo = tempInput->imageInfo().makeAlphaType(kPremul_SkAlphaType);
618 do {
619 SkISize nextDims = finalSize;
620 if (rescaleMode != Image::RescaleMode::kNearest &&
621 rescaleMode != Image::RescaleMode::kLinear) {
622 if (srcRect.width() > finalSize.width()) {
623 nextDims.fWidth = std::max((srcRect.width() + 1)/2, (float)finalSize.width());
624 } else if (srcRect.width() < finalSize.width()) {
625 nextDims.fWidth = std::min(srcRect.width()*2, (float)finalSize.width());
626 }
627 if (srcRect.height() > finalSize.height()) {
628 nextDims.fHeight = std::max((srcRect.height() + 1)/2, (float)finalSize.height());
629 } else if (srcRect.height() < finalSize.height()) {
630 nextDims.fHeight = std::min(srcRect.height()*2, (float)finalSize.height());
631 }
632 }
633
634 SkRect stepDstRect;
635 if (nextDims == finalSize) {
636 tempOutput = dst;
637 stepDstRect = dstRect;
638 } else {
639 SkImageInfo nextInfo = outImageInfo.makeDimensions(nextDims);
640 tempOutput = make_renderable_scratch_surface(recorder, nextInfo, &surfaceProps);
641 if (!tempOutput) {
642 return nullptr;
643 }
644 stepDstRect = SkRect::Make(tempOutput->imageInfo().dimensions());
645 }
646
647 SkSamplingOptions samplingOptions;
648 if (rescaleMode == Image::RescaleMode::kRepeatedCubic) {
650 } else {
651 samplingOptions = (rescaleMode == Image::RescaleMode::kNearest) ?
654 }
656 paint.setBlendMode(SkBlendMode::kSrc);
657 tempOutput->getCanvas()->drawImageRect(tempInput, srcRect, stepDstRect, samplingOptions,
659
660 tempInput = SkSurfaces::AsImage(std::move(tempOutput));
661 srcRect = SkRect::Make(nextDims);
662 } while (srcRect.width() != finalSize.width() || srcRect.height() != finalSize.height());
663
664 return SkSurfaces::AsImage(std::move(dst));
665}
666
669 const SkColorInfo& colorInfo) {
670 constexpr SkSamplingOptions kSamplingOptions = SkSamplingOptions(SkFilterMode::kLinear);
671
672 SkASSERT(texture->mipmapped() == Mipmapped::kYes);
673
674 // Within a rescaling pass scratchImg is read from and a scratch surface is written to.
675 // At the end of the pass the scratch surface's texture is wrapped and assigned to scratchImg.
676
677 // The scratch surface we create below will use a write swizzle derived from SkColorType and
678 // pixel format. We have to be consistent and swizzle on the read.
679 auto imgSwizzle = recorder->priv().caps()->getReadSwizzle(colorInfo.colorType(),
680 texture->textureInfo());
681 sk_sp<SkImage> scratchImg(new Image(TextureProxyView(texture, imgSwizzle), colorInfo));
682
683 SkISize srcSize = texture->dimensions();
684 const SkColorInfo outColorInfo = colorInfo.makeAlphaType(kPremul_SkAlphaType);
685
686 // Alternate between two scratch surfaces to avoid reading from and writing to a texture in the
687 // same pass. The dimensions of the first usages of the two scratch textures will be 1/2 and 1/4
688 // those of the original texture, respectively.
689 sk_sp<Surface> scratchSurfaces[2];
690 for (int i = 0; i < 2; ++i) {
691 scratchSurfaces[i] = make_renderable_scratch_surface(
692 recorder,
693 SkImageInfo::Make(SkISize::Make(std::max(1, srcSize.width() >> (i + 1)),
694 std::max(1, srcSize.height() >> (i + 1))),
695 outColorInfo));
696 if (!scratchSurfaces[i]) {
697 return false;
698 }
699 }
700
701 for (int mipLevel = 1; srcSize.width() > 1 || srcSize.height() > 1; ++mipLevel) {
702 const SkISize dstSize = SkISize::Make(std::max(srcSize.width() >> 1, 1),
703 std::max(srcSize.height() >> 1, 1));
704
705 Surface* scratchSurface = scratchSurfaces[(mipLevel - 1) & 1].get();
706
708 paint.setBlendMode(SkBlendMode::kSrc);
709 scratchSurface->getCanvas()->drawImageRect(scratchImg,
710 SkRect::Make(srcSize),
711 SkRect::Make(dstSize),
712 kSamplingOptions,
713 &paint,
715
716 // Make sure the rescaling draw finishes before copying the results.
717 Flush(scratchSurface);
718
720 static_cast<const Surface*>(scratchSurface)->readSurfaceView().refProxy(),
721 SkIRect::MakeSize(dstSize),
722 texture,
723 {0, 0},
724 mipLevel);
725 if (!copyTask) {
726 return false;
727 }
728 recorder->priv().add(std::move(copyTask));
729
730 scratchImg = scratchSurface->asImage();
731 srcSize = dstSize;
732 }
733
734 return true;
735}
736
737std::pair<sk_sp<SkImage>, SkSamplingOptions> GetGraphiteBacked(Recorder* recorder,
738 const SkImage* imageIn,
739 SkSamplingOptions sampling) {
740 Mipmapped mipmapped = (sampling.mipmap != SkMipmapMode::kNone)
741 ? Mipmapped::kYes : Mipmapped::kNo;
742
743 if (imageIn->dimensions().area() <= 1 && mipmapped == Mipmapped::kYes) {
744 mipmapped = Mipmapped::kNo;
746 }
747
749 if (as_IB(imageIn)->isGraphiteBacked()) {
750 result = sk_ref_sp(imageIn);
751
752 // If the preexisting Graphite-backed image doesn't have the required mipmaps we will drop
753 // down the sampling
754 if (mipmapped == Mipmapped::kYes && !result->hasMipmaps()) {
755 mipmapped = Mipmapped::kNo;
757 }
758 } else {
759 auto clientImageProvider = recorder->clientImageProvider();
760 result = clientImageProvider->findOrCreate(
761 recorder, imageIn, {mipmapped == Mipmapped::kYes});
762
763 if (!valid_client_provided_image(
764 result.get(), imageIn, {mipmapped == Mipmapped::kYes})) {
765 // The client did not fulfill the ImageProvider contract so drop the image.
766 result = nullptr;
767 }
768 }
769
770 if (sampling.isAniso() && result) {
771 sampling = SkSamplingPriv::AnisoFallback(result->hasMipmaps());
772 }
773
774 return { result, sampling };
775}
776
778 if (!image) {
779 return {};
780 }
781 if (!as_IB(image)->isGraphiteBacked()) {
782 return {};
783 }
784 // A YUVA image (even if backed by graphite textures) is not a single texture
785 if (as_IB(image)->isYUVA()) {
786 return {};
787 }
788
789 auto gi = reinterpret_cast<const Image*>(image);
790 return gi->textureProxyView();
791}
792
794 // GPU compute coverage mask renderers need to bind the mask texture as a storage binding, which
795 // support a limited set of color formats. In general, we use RGBA8 if Alpha8 can't be
796 // supported.
799 }
801}
802
803} // namespace skgpu::graphite
804
805namespace skif {
806
807namespace {
808
809// TODO(michaelludwig): The skgpu::BlurUtils effects will be migrated to src/core to implement a
810// shader BlurEngine that can be shared by rastr, Ganesh, and Graphite. This is blocked by having
811// skif::FilterResult handle the resizing to the max supported sigma.
812class GraphiteBackend : public Backend, private SkBlurEngine, private SkBlurEngine::Algorithm {
813public:
814
815 GraphiteBackend(skgpu::graphite::Recorder* recorder,
816 const SkSurfaceProps& surfaceProps,
818 : Backend(SkImageFilterCache::Create(SkImageFilterCache::kDefaultTransientSize),
819 surfaceProps, colorType)
820 , fRecorder(recorder) {}
821
822 // Backend
823 sk_sp<SkDevice> makeDevice(SkISize size,
824 sk_sp<SkColorSpace> colorSpace,
825 const SkSurfaceProps* props) const override {
826 SkImageInfo imageInfo = SkImageInfo::Make(size,
827 this->colorType(),
828 kPremul_SkAlphaType,
829 std::move(colorSpace));
830 return skgpu::graphite::Device::Make(fRecorder,
831 imageInfo,
833 skgpu::Mipmapped::kNo,
834#if defined(GRAPHITE_USE_APPROX_FIT_FOR_FILTERS)
836#else
838#endif
839 props ? *props : this->surfaceProps(),
840 skgpu::graphite::LoadOp::kDiscard);
841 }
842
843 sk_sp<SkSpecialImage> makeImage(const SkIRect& subset, sk_sp<SkImage> image) const override {
844 return SkSpecialImages::MakeGraphite(fRecorder, subset, image, this->surfaceProps());
845 }
846
847 sk_sp<SkImage> getCachedBitmap(const SkBitmap& data) const override {
848 auto proxy = skgpu::graphite::RecorderPriv::CreateCachedProxy(fRecorder, data);
849 if (!proxy) {
850 return nullptr;
851 }
852
853 const SkColorInfo& colorInfo = data.info().colorInfo();
854 skgpu::Swizzle swizzle = fRecorder->priv().caps()->getReadSwizzle(colorInfo.colorType(),
855 proxy->textureInfo());
856 return sk_make_sp<skgpu::graphite::Image>(
857 skgpu::graphite::TextureProxyView(std::move(proxy), swizzle),
858 colorInfo);
859 }
860
861 const SkBlurEngine* getBlurEngine() const override { return this; }
862
863 // SkBlurEngine
864 const SkBlurEngine::Algorithm* findAlgorithm(SkSize sigma,
865 SkColorType colorType) const override {
866 // The runtime effect blurs handle all tilemodes and color types
867 return this;
868 }
869
870 // SkBlurEngine::Algorithm
871 float maxSigma() const override {
872 // TODO: When FilterResult handles rescaling externally, change this to
873 // skgpu::kMaxLinearBlurSigma.
874 return SK_ScalarInfinity;
875 }
876
877 bool supportsOnlyDecalTiling() const override { return false; }
878
879 sk_sp<SkSpecialImage> blur(SkSize sigma,
881 const SkIRect& srcRect,
882 SkTileMode tileMode,
883 const SkIRect& dstRect) const override {
884 TRACE_EVENT_INSTANT2("skia.gpu", "GaussianBlur", TRACE_EVENT_SCOPE_THREAD,
885 "sigmaX", sigma.width(), "sigmaY", sigma.height());
886
887 SkColorSpace* cs = src->getColorSpace();
888 return skgpu::graphite::blur_impl(fRecorder, sigma, std::move(src), srcRect, tileMode,
889 dstRect, sk_ref_sp(cs), this->surfaceProps());
890 }
891
892private:
893 skgpu::graphite::Recorder* fRecorder;
894};
895
896} // anonymous namespace
897
898sk_sp<Backend> MakeGraphiteBackend(skgpu::graphite::Recorder* recorder,
899 const SkSurfaceProps& surfaceProps,
901 SkASSERT(recorder);
902 return sk_make_sp<GraphiteBackend>(recorder, surfaceProps, colorType);
903}
904
905} // namespace skif
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition DM.cpp:213
#define SKGPU_LOG_E(fmt,...)
Definition Log.h:38
#define SKGPU_LOG_W(fmt,...)
Definition Log.h:40
static sk_sp< Effect > Create()
@ kPremul_SkAlphaType
pixel components are premultiplied by alpha
Definition SkAlphaType.h:29
#define SkASSERT(cond)
Definition SkAssert.h:116
SkColorType
Definition SkColorType.h:19
@ kAlpha_8_SkColorType
pixel with alpha in 8-bit byte
Definition SkColorType.h:21
@ kRGBA_8888_SkColorType
pixel with 8 bits for red, green, blue, alpha; in 32-bit word
Definition SkColorType.h:24
@ kUnknown_SkColorType
uninitialized
Definition SkColorType.h:20
size_t SkCompressedFormatDataSize(SkTextureCompressionType compressionType, SkISize dimensions, bool mipmapped)
#define sk_float_ceil2int(x)
static SkColorType colorType(AImageDecoder *decoder, const AImageDecoderHeaderInfo *headerInfo)
static uint32_t SkColorTypeChannelFlags(SkColorType ct)
static bool SkColorTypeIsAlphaOnly(SkColorType ct)
static bool SkImageInfoIsValid(const SkImageInfo &info)
static SkImage_Base * as_IB(SkImage *image)
sk_sp< T > sk_ref_sp(T *obj)
Definition SkRefCnt.h:381
#define SK_ScalarInfinity
Definition SkScalar.h:26
SkTileMode
Definition SkTileMode.h:13
static constexpr bool SkToBool(const T &x)
Definition SkTo.h:35
#define TRACE_EVENT_SCOPE_THREAD
#define TRACE_FUNC
void setImmutable()
Definition SkBitmap.cpp:400
SkISize dimensions() const
Definition SkBitmap.h:388
const SkPixmap & pixmap() const
Definition SkBitmap.h:133
size_t rowBytes() const
Definition SkBitmap.h:238
SkColorType colorType() const
Definition SkBitmap.h:160
void * getPixels() const
Definition SkBitmap.h:283
const SkImageInfo & info() const
Definition SkBitmap.h:139
bool tryAllocPixels(const SkImageInfo &info, size_t rowBytes)
Definition SkBitmap.cpp:271
@ kStrict_SrcRectConstraint
sample only inside bounds; slower
Definition SkCanvas.h:1542
void drawImageRect(const SkImage *, const SkRect &src, const SkRect &dst, const SkSamplingOptions &, const SkPaint *, SrcRectConstraint)
SkColorInfo makeAlphaType(SkAlphaType newAlphaType) const
SkColorInfo makeColorType(SkColorType newColorType) const
SkColorType colorType() const
bool gammaIsLinear() const
sk_sp< SkColorSpace > makeLinearGamma() const
const SkImageInfo & imageInfo() const
Definition SkImage.h:279
SkISize dimensions() const
Definition SkImage.h:297
SkAlphaType alphaType() const
Definition SkImage.cpp:154
RescaleMode
Definition SkImage.h:587
RescaleGamma
Definition SkImage.h:585
SkColorType colorType() const
Definition SkImage.cpp:152
static SkM44 Translate(SkScalar x, SkScalar y, SkScalar z=0)
Definition SkM44.h:225
static SkMatrix Translate(SkScalar dx, SkScalar dy)
Definition SkMatrix.h:91
static SkMipmap * Build(const SkPixmap &src, SkDiscardableFactoryProc, bool computeContents=true)
Definition SkMipmap.cpp:45
static int ComputeLevelCount(int baseWidth, int baseHeight)
Definition SkMipmap.cpp:134
size_t rowBytes() const
Definition SkPixmap.h:145
SkColorType colorType() const
Definition SkPixmap.h:173
const void * addr() const
Definition SkPixmap.h:153
static SkSamplingOptions AnisoFallback(bool imageIsMipped)
SkCanvas * getCanvas()
Definition SkSurface.cpp:82
static sk_sp< RefCntedCallback > Make(Callback proc, Context ctx)
static constexpr Swizzle Concat(const Swizzle &a, const Swizzle &b)
Definition Swizzle.h:156
skgpu::Swizzle getReadSwizzle(SkColorType, const TextureInfo &) const
Definition Caps.cpp:128
virtual TextureInfo getDefaultStorageTextureInfo(SkColorType) const =0
virtual bool isStorage(const TextureInfo &) const =0
SkColorType getRenderableColorType(SkColorType) const
Definition Caps.cpp:113
virtual TextureInfo getDefaultSampledTextureInfo(SkColorType, Mipmapped mipmapped, Protected, Renderable) const =0
bool areColorTypeAndTextureInfoCompatible(SkColorType, const TextureInfo &) const
Definition Caps.cpp:84
static sk_sp< CopyTextureToTextureTask > Make(sk_sp< TextureProxy > srcProxy, SkIRect srcRect, sk_sp< TextureProxy > dstProxy, SkIPoint dstPoint, int dstLevel=0)
Definition CopyTask.cpp:119
static sk_sp< Device > Make(Recorder *recorder, sk_sp< TextureProxy >, SkISize deviceSize, const SkColorInfo &, const SkSurfaceProps &, LoadOp initialLoadOp, bool registerWithRecorder=true)
Definition Device.cpp:268
virtual sk_sp< SkImage > findOrCreate(Recorder *recorder, const SkImage *image, SkImage::RequiredProperties)=0
const TextureProxyView & textureProxyView() const
static sk_sp< TextureProxy > CreateCachedProxy(Recorder *, const SkBitmap &, Mipmapped=skgpu::Mipmapped::kNo)
Definition Recorder.cpp:536
Protected isProtected() const
const Caps * caps() const
ResourceProvider * resourceProvider()
void add(sk_sp< Task >)
Definition Recorder.cpp:477
ImageProvider * clientImageProvider()
Definition Recorder.h:87
sk_sp< Image > asImage() const
static sk_sp< Surface > MakeScratch(Recorder *recorder, const SkImageInfo &info, Budgeted budgeted=Budgeted::kYes, Mipmapped mipmapped=Mipmapped::kNo, SkBackingFit backingFit=SkBackingFit::kApprox)
static sk_sp< TextureProxy > Make(const Caps *, ResourceProvider *, SkISize dimensions, const TextureInfo &, skgpu::Budgeted)
static sk_sp< TextureProxy > MakeLazy(const Caps *, SkISize dimensions, const TextureInfo &, skgpu::Budgeted, Volatile, LazyInstantiateCallback &&)
static UploadInstance Make(Recorder *, sk_sp< TextureProxy > targetProxy, const SkColorInfo &srcColorInfo, const SkColorInfo &dstColorInfo, SkSpan< const MipLevel > levels, const SkIRect &dstRect, std::unique_ptr< ConditionalUploadContext >)
static sk_sp< UploadTask > Make(UploadList *)
const Paint & paint
VkDevice device
Definition main.cc:53
sk_sp< SkImage > image
Definition examples.cpp:29
FlKeyEvent uint64_t FlKeyResponderAsyncCallback callback
GAsyncResult * result
FlTexture * texture
void * GraphitePromiseTextureFulfillContext
Definition Image.h:39
std::tuple< skgpu::graphite::BackendTexture, GraphitePromiseTextureReleaseContext >(*)(GraphitePromiseTextureFulfillContext) GraphitePromiseTextureFulfillProc
Definition Image.h:45
void(*)(GraphitePromiseTextureReleaseContext) GraphitePromiseTextureReleaseProc
Definition Image.h:47
SK_API sk_sp< SkImage > AsImage(sk_sp< const SkSurface >)
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot data
Definition switches.h:41
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace Enable an endless trace buffer The default is a ring buffer This is useful when very old events need to viewed For during application launch Memory usage will continue to grow indefinitely however Start app with an specific route defined on the framework flutter assets dir
Definition switches.h:145
CanvasImage Image
Definition dart_ui.cc:55
bool GenerateMipmaps(Recorder *recorder, sk_sp< TextureProxy > texture, const SkColorInfo &colorInfo)
SkColorType ComputeShaderCoverageMaskTargetFormat(const Caps *caps)
sk_sp< SkImage > RescaleImage(Recorder *recorder, const SkImage *srcImage, SkIRect srcIRect, const SkImageInfo &dstInfo, SkImage::RescaleGamma rescaleGamma, SkImage::RescaleMode rescaleMode)
std::tuple< TextureProxyView, SkColorType > MakeBitmapProxyView(Recorder *recorder, const SkBitmap &bitmap, sk_sp< SkMipmap > mipmapsIn, Mipmapped mipmapped, Budgeted budgeted)
void Flush(sk_sp< SkSurface > surface)
sk_sp< SkImage > MakeFromBitmap(Recorder *recorder, const SkColorInfo &colorInfo, const SkBitmap &bitmap, sk_sp< SkMipmap > mipmaps, Budgeted budgeted, SkImage::RequiredProperties requiredProps)
sk_sp< TextureProxy > MakePromiseImageLazyProxy(const Caps *caps, SkISize dimensions, TextureInfo textureInfo, Volatile isVolatile, sk_sp< RefCntedCallback > releaseHelper, GraphitePromiseTextureFulfillProc fulfillProc, GraphitePromiseTextureFulfillContext fulfillContext, GraphitePromiseTextureReleaseProc textureReleaseProc)
std::pair< sk_sp< SkImage >, SkSamplingOptions > GetGraphiteBacked(Recorder *recorder, const SkImage *imageIn, SkSamplingOptions sampling)
size_t ComputeSize(SkISize dimensions, const TextureInfo &info)
TextureProxyView AsView(const SkImage *image)
static constexpr int kMaxBlurSamples
Definition BlurUtils.h:52
static constexpr float kMaxLinearBlurSigma
Definition BlurUtils.h:58
Budgeted
Definition GpuTypes.h:35
void Compute2DBlurOffsets(SkISize radius, std::array< SkV4, kMaxBlurSamples/2 > &offsets)
Definition BlurUtils.cpp:93
Mipmapped
Definition GpuTypes.h:53
const SkRuntimeEffect * GetLinearBlur1DEffect(int radius)
int BlurSigmaRadius(float sigma)
Definition BlurUtils.h:41
constexpr int BlurKernelWidth(int radius)
Definition BlurUtils.h:28
void Compute2DBlurKernel(SkSize sigma, SkISize radius, SkSpan< float > kernel)
Definition BlurUtils.cpp:35
const SkRuntimeEffect * GetBlur2DEffect(const SkISize &radii)
void Compute1DBlurLinearKernel(float sigma, int radius, std::array< SkV4, kMaxBlurSamples/2 > &offsetsAndKernel)
Protected
Definition GpuTypes.h:61
Definition ref_ptr.h:256
static constexpr SkCubicResampler CatmullRom()
SkIRect makeOutset(int32_t dx, int32_t dy) const
Definition SkRect.h:350
bool intersect(const SkIRect &r)
Definition SkRect.h:513
constexpr int32_t top() const
Definition SkRect.h:120
constexpr SkISize size() const
Definition SkRect.h:172
constexpr int32_t height() const
Definition SkRect.h:165
static constexpr SkIRect MakeSize(const SkISize &size)
Definition SkRect.h:66
constexpr int32_t width() const
Definition SkRect.h:158
void offset(int32_t dx, int32_t dy)
Definition SkRect.h:367
constexpr SkIRect makeOffset(int32_t dx, int32_t dy) const
Definition SkRect.h:300
static constexpr SkIRect MakeWH(int32_t w, int32_t h)
Definition SkRect.h:56
void outset(int32_t dx, int32_t dy)
Definition SkRect.h:428
constexpr int32_t left() const
Definition SkRect.h:113
bool isEmpty() const
Definition SkSize.h:31
static constexpr SkISize Make(int32_t w, int32_t h)
Definition SkSize.h:20
int32_t fHeight
Definition SkSize.h:18
int32_t fWidth
Definition SkSize.h:17
constexpr int32_t width() const
Definition SkSize.h:36
constexpr int32_t height() const
Definition SkSize.h:37
constexpr int64_t area() const
Definition SkSize.h:39
const SkColorInfo & colorInfo() const
SkImageInfo makeDimensions(SkISize newSize) const
SkColorSpace * colorSpace() const
SkISize dimensions() const
int width() const
static SkImageInfo Make(int width, int height, SkColorType ct, SkAlphaType at)
int height() const
SkPixmap fPixmap
Definition SkMipmap.h:71
static SkRect Make(const SkISize &size)
Definition SkRect.h:669
static constexpr SkRect MakeXYWH(float x, float y, float w, float h)
Definition SkRect.h:659
void roundOut(SkIRect *dst) const
Definition SkRect.h:1241
constexpr float height() const
Definition SkRect.h:769
constexpr float width() const
Definition SkRect.h:762
const SkMipmapMode mipmap
SkScalar width() const
Definition SkSize.h:76
SkScalar height() const
Definition SkSize.h:77
Definition SkM44.h:19
Definition SkM44.h:98
#define TRACE_EVENT0(category_group, name)
#define TRACE_EVENT_INSTANT2(category_group, name, arg1_name, arg1_val, arg2_name, arg2_val)