Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
SkImageShader.cpp
Go to the documentation of this file.
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
9
21#include "modules/skcms/skcms.h"
37
38#ifdef SK_ENABLE_LEGACY_SHADERCONTEXT
40#endif
41
42#include <optional>
43#include <tuple>
44#include <utility>
45
46class SkColorSpace;
47
49#if 0
50 constexpr SkM44 kMitchell = SkM44( 1.f/18.f, -9.f/18.f, 15.f/18.f, -7.f/18.f,
51 16.f/18.f, 0.f/18.f, -36.f/18.f, 21.f/18.f,
52 1.f/18.f, 9.f/18.f, 27.f/18.f, -21.f/18.f,
53 0.f/18.f, 0.f/18.f, -6.f/18.f, 7.f/18.f);
54
55 constexpr SkM44 kCatmull = SkM44(0.0f, -0.5f, 1.0f, -0.5f,
56 1.0f, 0.0f, -2.5f, 1.5f,
57 0.0f, 0.5f, 2.0f, -1.5f,
58 0.0f, 0.0f, -0.5f, 0.5f);
59
60 if (B == 1.0f/3 && C == 1.0f/3) {
61 return kMitchell;
62 }
63 if (B == 0 && C == 0.5f) {
64 return kCatmull;
65 }
66#endif
67 return SkM44( (1.f/6)*B, -(3.f/6)*B - C, (3.f/6)*B + 2*C, - (1.f/6)*B - C,
68 1 - (2.f/6)*B, 0, -3 + (12.f/6)*B + C, 2 - (9.f/6)*B - C,
69 (1.f/6)*B, (3.f/6)*B + C, 3 - (15.f/6)*B - 2*C, -2 + (9.f/6)*B + C,
70 0, 0, -C, (1.f/6)*B + C);
71}
72
73/**
74 * We are faster in clamp, so always use that tiling when we can.
75 */
76static SkTileMode optimize(SkTileMode tm, int dimension) {
77 SkASSERT(dimension > 0);
78#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
79 // need to update frameworks/base/libs/hwui/tests/unit/SkiaBehaviorTests.cpp:55 to allow
80 // for transforming to clamp.
81 return tm;
82#else
83 // mirror and repeat on a 1px axis are the same as clamping, but decal will still transition to
84 // transparent black.
85 return (tm != SkTileMode::kDecal && dimension == 1) ? SkTileMode::kClamp : tm;
86#endif
87}
88
89#if defined(SK_DEBUG)
90static bool needs_subset(SkImage* img, const SkRect& subset) {
91 return subset != SkRect::Make(img->dimensions());
92}
93#endif
94
96 const SkRect& subset,
98 const SkSamplingOptions& sampling,
99 bool raw,
100 bool clampAsIfUnpremul)
101 : fImage(std::move(img))
102 , fSampling(sampling)
103 , fTileModeX(optimize(tmx, fImage->width()))
104 , fTileModeY(optimize(tmy, fImage->height()))
105 , fSubset(subset)
106 , fRaw(raw)
107 , fClampAsIfUnpremul(clampAsIfUnpremul) {
108 // These options should never appear together:
109 SkASSERT(!fRaw || !fClampAsIfUnpremul);
110
111 // Bicubic filtering of raw image shaders would add a surprising clamp - so we don't support it
112 SkASSERT(!fRaw || !fSampling.useCubic);
113}
114
115// just used for legacy-unflattening
117 kNone,
118 kLow,
119 kMedium,
120 kHigh,
121 // this is the special value for backward compatibility
123 // this signals we should use the new SkFilterOptions
125 // use cubic and ignore FilterOptions
127
129};
130
131// fClampAsIfUnpremul is always false when constructed through public APIs,
132// so there's no need to read or write it here.
133
134sk_sp<SkFlattenable> SkImageShader::CreateProc(SkReadBuffer& buffer) {
137
139 bool readSampling = true;
141 !buffer.readBool() /* legacy has_sampling */)
142 {
143 readSampling = false;
144 // we just default to Nearest in sampling
145 }
146 if (readSampling) {
147 sampling = buffer.readSampling();
148 }
149
150 SkMatrix localMatrix;
152 buffer.readMatrix(&localMatrix);
153 }
154 sk_sp<SkImage> img = buffer.readImage();
155 if (!img) {
156 return nullptr;
157 }
158
160 : buffer.readBool();
161
162 // TODO(skbug.com/12784): Subset is not serialized yet; it's only used by special images so it
163 // will never be written to an SKP.
164
165 return raw ? SkImageShader::MakeRaw(std::move(img), tmx, tmy, sampling, &localMatrix)
166 : SkImageShader::Make(std::move(img), tmx, tmy, sampling, &localMatrix);
167}
168
170 buffer.writeUInt((unsigned)fTileModeX);
171 buffer.writeUInt((unsigned)fTileModeY);
172
173 buffer.writeSampling(fSampling);
174
175 buffer.writeImage(fImage.get());
176 SkASSERT(fClampAsIfUnpremul == false);
177
178 // TODO(skbug.com/12784): Subset is not serialized yet; it's only used by special images so it
179 // will never be written to an SKP.
180 SkASSERT(!needs_subset(fImage.get(), fSubset));
181
182 buffer.writeBool(fRaw);
183}
184
186 return fImage->isOpaque() &&
187 fTileModeX != SkTileMode::kDecal && fTileModeY != SkTileMode::kDecal;
188}
189
190#ifdef SK_ENABLE_LEGACY_SHADERCONTEXT
191
192static bool legacy_shader_can_handle(const SkMatrix& inv) {
193 SkASSERT(!inv.hasPerspective());
194
195#if defined(SK_IGNORE_FAST_ROTATED_BLITTER)
196 // We only have methods for scale+translate
197 if (!inv.isScaleTranslate()) {
198 return false;
199 }
200#else
201 // Scale+translate methods are always present, but affine might not be.
202 if (!SkOpts::S32_alpha_D32_filter_DXDY && !inv.isScaleTranslate()) {
203 return false;
204 }
205#endif
206
207 // legacy code uses SkFixed 32.32, so ensure the inverse doesn't map device coordinates
208 // out of range.
209 const SkScalar max_dev_coord = 32767.0f;
210 const SkRect src = inv.mapRect(SkRect::MakeWH(max_dev_coord, max_dev_coord));
211
212 // take 1/4 of max signed 32bits so we have room to subtract local values
213 const SkScalar max_fixed32dot32 = float(SK_MaxS32) * 0.25f;
214 if (!SkRect::MakeLTRB(-max_fixed32dot32, -max_fixed32dot32,
215 +max_fixed32dot32, +max_fixed32dot32).contains(src)) {
216 return false;
217 }
218
219 // legacy shader impl should be able to handle these matrices
220 return true;
221}
222
223SkShaderBase::Context* SkImageShader::onMakeContext(const ContextRec& rec,
224 SkArenaAlloc* alloc) const {
225 SkASSERT(!needs_subset(fImage.get(), fSubset)); // TODO(skbug.com/12784)
226 if (fImage->alphaType() == kUnpremul_SkAlphaType) {
227 return nullptr;
228 }
229 if (fImage->colorType() != kN32_SkColorType) {
230 return nullptr;
231 }
232 if (fTileModeX != fTileModeY) {
233 return nullptr;
234 }
235 if (fTileModeX == SkTileMode::kDecal || fTileModeY == SkTileMode::kDecal) {
236 return nullptr;
237 }
238
239 SkSamplingOptions sampling = fSampling;
240 if (sampling.isAniso()) {
242 }
243
244 auto supported = [](const SkSamplingOptions& sampling) {
245 const std::tuple<SkFilterMode,SkMipmapMode> supported[] = {
249 };
250 for (auto [f, m] : supported) {
251 if (sampling.filter == f && sampling.mipmap == m) {
252 return true;
253 }
254 }
255 return false;
256 };
257 if (sampling.useCubic || !supported(sampling)) {
258 return nullptr;
259 }
260
261 // SkBitmapProcShader stores bitmap coordinates in a 16bit buffer,
262 // so it can't handle bitmaps larger than 65535.
263 //
264 // We back off another bit to 32767 to make small amounts of
265 // intermediate math safe, e.g. in
266 //
267 // SkFixed fx = ...;
268 // fx = tile(fx + SK_Fixed1);
269 //
270 // we want to make sure (fx + SK_Fixed1) never overflows.
271 if (fImage-> width() > 32767 ||
272 fImage->height() > 32767) {
273 return nullptr;
274 }
275
277 if (!rec.fMatrixRec.totalInverse(&inv) || !legacy_shader_can_handle(inv)) {
278 return nullptr;
279 }
280
281 if (!rec.isLegacyCompatible(fImage->colorSpace())) {
282 return nullptr;
283 }
284
285 return SkBitmapProcLegacyShader::MakeContext(*this, fTileModeX, fTileModeY, sampling,
286 as_IB(fImage.get()), rec, alloc);
287}
288#endif
289
291 if (texM) {
292 *texM = SkMatrix::I();
293 }
294 if (xy) {
295 xy[0] = fTileModeX;
296 xy[1] = fTileModeY;
297 }
298 return const_cast<SkImage*>(fImage.get());
299}
300
304 const SkMatrix* localMatrix,
305 bool clampAsIfUnpremul) {
307 return MakeSubset(std::move(image), subset, tmx, tmy, options, localMatrix, clampAsIfUnpremul);
308}
309
313 const SkMatrix* localMatrix) {
314 if (options.useCubic) {
315 return nullptr;
316 }
317 if (!image) {
318 return SkShaders::Empty();
319 }
321
322 sk_sp<SkShader> s = sk_make_sp<SkImageShader>(image,
323 subset,
324 tmx, tmy,
325 options,
326 /*raw=*/true,
327 /*clampAsIfUnpremul=*/false);
328 return s->makeWithLocalMatrix(localMatrix ? *localMatrix : SkMatrix::I());
329}
330
332 const SkRect& subset,
335 const SkMatrix* localMatrix,
336 bool clampAsIfUnpremul) {
337 auto is_unit = [](float x) {
338 return x >= 0 && x <= 1;
339 };
340 if (options.useCubic) {
341 if (!is_unit(options.cubic.B) || !is_unit(options.cubic.C)) {
342 return nullptr;
343 }
344 }
345 if (!image || subset.isEmpty()) {
346 return SkShaders::Empty();
347 }
348
349 // Validate subset and check if we can drop it
351 return nullptr;
352 }
353
354 sk_sp<SkShader> s = sk_make_sp<SkImageShader>(std::move(image),
355 subset,
356 tmx, tmy,
357 options,
358 /*raw=*/false,
359 clampAsIfUnpremul);
360 return s->makeWithLocalMatrix(localMatrix ? *localMatrix : SkMatrix::I());
361}
362
363///////////////////////////////////////////////////////////////////////////////////////////////////
364
367 const SkSamplingOptions& sampling,
368 const SkMatrix* localMatrix, SkCopyPixelsMode mode) {
370 tmx, tmy, sampling, localMatrix);
371 if (!s) {
372 return nullptr;
373 }
374 if (SkColorTypeIsAlphaOnly(src.colorType()) && paint.getShader()) {
375 // Compose the image shader with the paint's shader. Alpha images+shaders should output the
376 // texture's alpha multiplied by the shader's color. DstIn (d*sa) will achieve this with
377 // the source image and dst shader (MakeBlend takes dst first, src second).
378 s = SkShaders::Blend(SkBlendMode::kDstIn, paint.refShader(), std::move(s));
379 }
380 return s;
381}
382
384 const SkSamplingOptions& sampling,
385 SkRect src,
386 SkRect dst,
387 bool strictSrcSubset,
388 SkPaint* paint) {
389 // The paint should have already been cleaned for a regular drawImageRect, e.g. no path
390 // effect and is a fill.
392 SkASSERT(paint->getStyle() == SkPaint::kFill_Style && !paint->getPathEffect());
393
395 SkRect imgBounds = SkRect::Make(image->bounds());
396
397 SkASSERT(src.isFinite() && dst.isFinite() && dst.isSorted());
398 SkMatrix localMatrix = SkMatrix::RectToRect(src, dst);
399 if (!imgBounds.contains(src)) {
400 if (!src.intersect(imgBounds)) {
401 return SkRect::MakeEmpty(); // Nothing to draw for this entry
402 }
403 // Update dst to match smaller src
404 dst = localMatrix.mapRect(src);
405 }
406
407 bool imageIsAlphaOnly = SkColorTypeIsAlphaOnly(image->colorType());
408
409 sk_sp<SkShader> imgShader;
410 if (strictSrcSubset) {
411 imgShader = SkImageShader::MakeSubset(sk_ref_sp(image), src,
413 sampling, &localMatrix);
414 } else {
416 sampling, &localMatrix);
417 }
418 if (!imgShader) {
419 return SkRect::MakeEmpty();
420 }
421 if (imageIsAlphaOnly && paint->getShader()) {
422 // Compose the image shader with the paint's shader. Alpha images+shaders should output the
423 // texture's alpha multiplied by the shader's color. DstIn (d*sa) will achieve this with
424 // the source image and dst shader (MakeBlend takes dst first, src second).
425 imgShader = SkShaders::Blend(SkBlendMode::kDstIn, paint->refShader(), std::move(imgShader));
426 }
427
428 paint->setShader(std::move(imgShader));
429 return dst;
430}
431
433
434namespace {
435
436struct MipLevelHelper {
437 SkPixmap pm;
442 SkRasterPipeline_DecalTileCtx* decalCtx = nullptr;
443
444 void allocAndInit(SkArenaAlloc* alloc,
445 const SkSamplingOptions& sampling,
446 SkTileMode tileModeX,
447 SkTileMode tileModeY) {
448 gather = alloc->make<SkRasterPipeline_GatherCtx>();
449 gather->pixels = pm.addr();
450 gather->stride = pm.rowBytesAsPixels();
451 gather->width = pm.width();
452 gather->height = pm.height();
453
454 if (sampling.useCubic) {
456 .getColMajor(gather->weights);
457 }
458
459 limitX = alloc->make<SkRasterPipeline_TileCtx>();
460 limitY = alloc->make<SkRasterPipeline_TileCtx>();
461 limitX->scale = pm.width();
462 limitX->invScale = 1.0f / pm.width();
463 limitY->scale = pm.height();
464 limitY->invScale = 1.0f / pm.height();
465
466 // We would like an image that is mapped 1:1 with device pixels but at a half pixel offset
467 // to select every pixel from the src image once. Our rasterizer biases upward. That is a
468 // rect from 0.5...1.5 fills pixel 1 and not pixel 0. So we make exact integer pixel sample
469 // values select the pixel to the left/above the integer value.
470 //
471 // Note that a mirror mapping between canvas and image space will not have this property -
472 // on one side of the image a row/column will be skipped and one repeated on the other side.
473 //
474 // The GM nearest_half_pixel_image tests both of the above scenarios.
475 //
476 // The implementation of SkTileMode::kMirror also modifies integer pixel snapping to create
477 // consistency when the sample coords are running backwards and must account for gather
478 // modification we perform here. The GM mirror_tile tests this.
480 gather->roundDownAtInteger = true;
481 limitX->mirrorBiasDir = limitY->mirrorBiasDir = 1;
482 }
483
484 if (tileModeX == SkTileMode::kDecal || tileModeY == SkTileMode::kDecal) {
485 decalCtx = alloc->make<SkRasterPipeline_DecalTileCtx>();
486 decalCtx->limit_x = limitX->scale;
487 decalCtx->limit_y = limitY->scale;
488
489 // When integer sample coords snap left/up then we want the right/bottom edge of the
490 // image bounds to be inside the image rather than the left/top edge, that is (0, w]
491 // rather than [0, w).
492 if (gather->roundDownAtInteger) {
493 decalCtx->inclusiveEdge_x = decalCtx->limit_x;
494 decalCtx->inclusiveEdge_y = decalCtx->limit_y;
495 }
496 }
497 }
498};
499
500} // namespace
501
503 SkFilterMode filter = sampling.filter;
504
505 // When the matrix is just an integer translate, bilerp == nearest neighbor.
506 if (filter == SkFilterMode::kLinear &&
507 matrix.getType() <= SkMatrix::kTranslate_Mask &&
508 matrix.getTranslateX() == (int)matrix.getTranslateX() &&
509 matrix.getTranslateY() == (int)matrix.getTranslateY()) {
510 filter = SkFilterMode::kNearest;
511 }
512
513 return SkSamplingOptions(filter, sampling.mipmap);
514}
515
517 SkASSERT(!needs_subset(fImage.get(), fSubset)); // TODO(skbug.com/12784)
518
519 // We only support certain sampling options in stages so far
520 auto sampling = fSampling;
521 if (sampling.isAniso()) {
523 }
524
526 SkArenaAlloc* alloc = rec.fAlloc;
527
528 SkMatrix baseInv;
529 // If the total matrix isn't valid then we will always access the base MIP level.
530 if (mRec.totalMatrixIsValid()) {
531 if (!mRec.totalInverse(&baseInv)) {
532 return false;
533 }
534 baseInv.normalizePerspective();
535 }
536
538 auto* access = SkMipmapAccessor::Make(alloc, fImage.get(), baseInv, sampling.mipmap);
539 if (!access) {
540 return false;
541 }
542
543 MipLevelHelper upper;
544 std::tie(upper.pm, upper.inv) = access->level();
545
546 if (!sampling.useCubic) {
547 // TODO: can tweak_sampling sometimes for cubic too when B=0
548 if (mRec.totalMatrixIsValid()) {
549 sampling = tweak_sampling(sampling, SkMatrix::Concat(upper.inv, baseInv));
550 }
551 }
552
553 if (!mRec.apply(rec, upper.inv)) {
554 return false;
555 }
556
557 upper.allocAndInit(alloc, sampling, fTileModeX, fTileModeY);
558
559 MipLevelHelper lower;
560 SkRasterPipeline_MipmapCtx* mipmapCtx = nullptr;
561 float lowerWeight = access->lowerWeight();
562 if (lowerWeight > 0) {
563 std::tie(lower.pm, lower.inv) = access->lowerLevel();
564 mipmapCtx = alloc->make<SkRasterPipeline_MipmapCtx>();
565 mipmapCtx->lowerWeight = lowerWeight;
566 mipmapCtx->scaleX = static_cast<float>(lower.pm.width()) / upper.pm.width();
567 mipmapCtx->scaleY = static_cast<float>(lower.pm.height()) / upper.pm.height();
568
569 lower.allocAndInit(alloc, sampling, fTileModeX, fTileModeY);
570
571 p->append(SkRasterPipelineOp::mipmap_linear_init, mipmapCtx);
572 }
573
574 const bool decalBothAxes = fTileModeX == SkTileMode::kDecal && fTileModeY == SkTileMode::kDecal;
575
576 auto append_tiling_and_gather = [&](const MipLevelHelper* level) {
577 if (decalBothAxes) {
578 p->append(SkRasterPipelineOp::decal_x_and_y, level->decalCtx);
579 } else {
580 switch (fTileModeX) {
581 case SkTileMode::kClamp: /* The gather_xxx stage will clamp for us. */
582 break;
584 p->append(SkRasterPipelineOp::mirror_x, level->limitX);
585 break;
587 p->append(SkRasterPipelineOp::repeat_x, level->limitX);
588 break;
590 p->append(SkRasterPipelineOp::decal_x, level->decalCtx);
591 break;
592 }
593 switch (fTileModeY) {
594 case SkTileMode::kClamp: /* The gather_xxx stage will clamp for us. */
595 break;
597 p->append(SkRasterPipelineOp::mirror_y, level->limitY);
598 break;
600 p->append(SkRasterPipelineOp::repeat_y, level->limitY);
601 break;
603 p->append(SkRasterPipelineOp::decal_y, level->decalCtx);
604 break;
605 }
606 }
607
608 void* ctx = level->gather;
609 switch (level->pm.colorType()) {
610 case kAlpha_8_SkColorType: p->append(SkRasterPipelineOp::gather_a8, ctx); break;
611 case kA16_unorm_SkColorType: p->append(SkRasterPipelineOp::gather_a16, ctx); break;
612 case kA16_float_SkColorType: p->append(SkRasterPipelineOp::gather_af16, ctx); break;
613 case kRGB_565_SkColorType: p->append(SkRasterPipelineOp::gather_565, ctx); break;
614 case kARGB_4444_SkColorType: p->append(SkRasterPipelineOp::gather_4444, ctx); break;
615 case kR8G8_unorm_SkColorType: p->append(SkRasterPipelineOp::gather_rg88, ctx); break;
616 case kR16G16_unorm_SkColorType: p->append(SkRasterPipelineOp::gather_rg1616,ctx); break;
617 case kR16G16_float_SkColorType: p->append(SkRasterPipelineOp::gather_rgf16, ctx); break;
618 case kRGBA_8888_SkColorType: p->append(SkRasterPipelineOp::gather_8888, ctx); break;
619
621 p->append(SkRasterPipelineOp::gather_1010102, ctx);
622 break;
623
625 p->append(SkRasterPipelineOp::gather_16161616, ctx);
626 break;
627
629 case kRGBA_F16_SkColorType: p->append(SkRasterPipelineOp::gather_f16, ctx); break;
630 case kRGBA_F32_SkColorType: p->append(SkRasterPipelineOp::gather_f32, ctx); break;
632 p->append(SkRasterPipelineOp::gather_10101010_xr, ctx);
633 p->append(SkRasterPipelineOp::swap_rb);
634 break;
635 case kRGBA_10x6_SkColorType: p->append(SkRasterPipelineOp::gather_10x6, ctx); break;
636
637 case kGray_8_SkColorType: p->append(SkRasterPipelineOp::gather_a8, ctx);
638 p->append(SkRasterPipelineOp::alpha_to_gray ); break;
639
640 case kR8_unorm_SkColorType: p->append(SkRasterPipelineOp::gather_a8, ctx);
641 p->append(SkRasterPipelineOp::alpha_to_red ); break;
642
643 case kRGB_888x_SkColorType: p->append(SkRasterPipelineOp::gather_8888, ctx);
644 p->append(SkRasterPipelineOp::force_opaque ); break;
645
647 p->append(SkRasterPipelineOp::gather_1010102, ctx);
648 p->append(SkRasterPipelineOp::swap_rb);
649 break;
650
652 p->append(SkRasterPipelineOp::gather_1010102, ctx);
653 p->append(SkRasterPipelineOp::force_opaque);
654 break;
655
657 p->append(SkRasterPipelineOp::gather_1010102_xr, ctx);
658 p->append(SkRasterPipelineOp::force_opaque);
659 p->append(SkRasterPipelineOp::swap_rb);
660 break;
661
663 p->append(SkRasterPipelineOp::gather_1010102, ctx);
664 p->append(SkRasterPipelineOp::force_opaque);
665 p->append(SkRasterPipelineOp::swap_rb);
666 break;
667
669 p->append(SkRasterPipelineOp::gather_8888, ctx);
670 p->append(SkRasterPipelineOp::swap_rb);
671 break;
672
674 p->append(SkRasterPipelineOp::gather_8888, ctx);
675 p->appendTransferFunction(*skcms_sRGB_TransferFunction());
676 break;
677
678 case kUnknown_SkColorType: SkASSERT(false);
679 }
680 if (level->decalCtx) {
681 p->append(SkRasterPipelineOp::check_decal_mask, level->decalCtx);
682 }
683 };
684
685 auto append_misc = [&] {
686 SkColorSpace* cs = upper.pm.colorSpace();
687 SkAlphaType at = upper.pm.alphaType();
688
689 // Color for alpha-only images comes from the paint (already converted to dst color space).
690 // If we were sampled by a runtime effect, the paint color was replaced with transparent
691 // black, so this tinting is effectively suppressed. See also: RuntimeEffectRPCallbacks
692 if (SkColorTypeIsAlphaOnly(upper.pm.colorType()) && !fRaw) {
693 p->appendSetRGB(alloc, rec.fPaintColor);
694
695 cs = rec.fDstCS;
697 }
698
699 // Bicubic filtering naturally produces out of range values on both sides of [0,1].
700 if (sampling.useCubic) {
701 p->append(at == kUnpremul_SkAlphaType || fClampAsIfUnpremul
702 ? SkRasterPipelineOp::clamp_01
703 : SkRasterPipelineOp::clamp_gamut);
704 }
705
706 // Transform color space and alpha type to match shader convention (dst CS, premul alpha).
707 if (!fRaw) {
709 }
710
711 return true;
712 };
713
714 // Check for fast-path stages.
715 // TODO: Could we use the fast-path stages for each level when doing linear mipmap filtering?
716 SkColorType ct = upper.pm.colorType();
717 if (true
721 && fTileModeX == SkTileMode::kClamp && fTileModeY == SkTileMode::kClamp) {
722
723 p->append(SkRasterPipelineOp::bilerp_clamp_8888, upper.gather);
724 if (ct == kBGRA_8888_SkColorType) {
725 p->append(SkRasterPipelineOp::swap_rb);
726 }
727 return append_misc();
728 }
729 if (true
732 && fTileModeX == SkTileMode::kClamp && fTileModeY == SkTileMode::kClamp) {
733
734 p->append(SkRasterPipelineOp::bicubic_clamp_8888, upper.gather);
735 if (ct == kBGRA_8888_SkColorType) {
736 p->append(SkRasterPipelineOp::swap_rb);
737 }
738 return append_misc();
739 }
740
741 // This context can be shared by both levels when doing linear mipmap filtering
743
744 auto sample = [&](SkRasterPipelineOp setup_x,
745 SkRasterPipelineOp setup_y,
746 const MipLevelHelper* level) {
747 p->append(setup_x, sampler);
748 p->append(setup_y, sampler);
749 append_tiling_and_gather(level);
750 p->append(SkRasterPipelineOp::accumulate, sampler);
751 };
752
753 auto sample_level = [&](const MipLevelHelper* level) {
754 if (sampling.useCubic) {
756
757 p->append(SkRasterPipelineOp::bicubic_setup, sampler);
758
759 sample(SkRasterPipelineOp::bicubic_n3x, SkRasterPipelineOp::bicubic_n3y, level);
760 sample(SkRasterPipelineOp::bicubic_n1x, SkRasterPipelineOp::bicubic_n3y, level);
761 sample(SkRasterPipelineOp::bicubic_p1x, SkRasterPipelineOp::bicubic_n3y, level);
762 sample(SkRasterPipelineOp::bicubic_p3x, SkRasterPipelineOp::bicubic_n3y, level);
763
764 sample(SkRasterPipelineOp::bicubic_n3x, SkRasterPipelineOp::bicubic_n1y, level);
765 sample(SkRasterPipelineOp::bicubic_n1x, SkRasterPipelineOp::bicubic_n1y, level);
766 sample(SkRasterPipelineOp::bicubic_p1x, SkRasterPipelineOp::bicubic_n1y, level);
767 sample(SkRasterPipelineOp::bicubic_p3x, SkRasterPipelineOp::bicubic_n1y, level);
768
769 sample(SkRasterPipelineOp::bicubic_n3x, SkRasterPipelineOp::bicubic_p1y, level);
770 sample(SkRasterPipelineOp::bicubic_n1x, SkRasterPipelineOp::bicubic_p1y, level);
771 sample(SkRasterPipelineOp::bicubic_p1x, SkRasterPipelineOp::bicubic_p1y, level);
772 sample(SkRasterPipelineOp::bicubic_p3x, SkRasterPipelineOp::bicubic_p1y, level);
773
774 sample(SkRasterPipelineOp::bicubic_n3x, SkRasterPipelineOp::bicubic_p3y, level);
775 sample(SkRasterPipelineOp::bicubic_n1x, SkRasterPipelineOp::bicubic_p3y, level);
776 sample(SkRasterPipelineOp::bicubic_p1x, SkRasterPipelineOp::bicubic_p3y, level);
777 sample(SkRasterPipelineOp::bicubic_p3x, SkRasterPipelineOp::bicubic_p3y, level);
778
779 p->append(SkRasterPipelineOp::move_dst_src);
780 } else if (sampling.filter == SkFilterMode::kLinear) {
781 p->append(SkRasterPipelineOp::bilinear_setup, sampler);
782
783 sample(SkRasterPipelineOp::bilinear_nx, SkRasterPipelineOp::bilinear_ny, level);
784 sample(SkRasterPipelineOp::bilinear_px, SkRasterPipelineOp::bilinear_ny, level);
785 sample(SkRasterPipelineOp::bilinear_nx, SkRasterPipelineOp::bilinear_py, level);
786 sample(SkRasterPipelineOp::bilinear_px, SkRasterPipelineOp::bilinear_py, level);
787
788 p->append(SkRasterPipelineOp::move_dst_src);
789 } else {
790 append_tiling_and_gather(level);
791 }
792 };
793
794 sample_level(&upper);
795
796 if (mipmapCtx) {
797 p->append(SkRasterPipelineOp::mipmap_linear_update, mipmapCtx);
798 sample_level(&lower);
799 p->append(SkRasterPipelineOp::mipmap_linear_finish, mipmapCtx);
800 }
801
802 return append_misc();
803}
804
805namespace SkShaders {
806
810 const SkMatrix* localMatrix) {
811 return SkImageShader::Make(std::move(image), tmx, tmy, options, localMatrix);
812}
813
817 const SkMatrix* localMatrix) {
818 return SkImageShader::MakeRaw(std::move(image), tmx, tmy, options, localMatrix);
819}
820
821} // namespace SkShaders
static SkM44 inv(const SkM44 &m)
Definition 3d.cpp:26
const char * options
kUnpremul_SkAlphaType
SkAlphaType
Definition SkAlphaType.h:26
@ kPremul_SkAlphaType
pixel components are premultiplied by alpha
Definition SkAlphaType.h:29
#define SkASSERT(cond)
Definition SkAssert.h:116
@ kDstIn
r = d * sa
SkColorType
Definition SkColorType.h:19
@ kR16G16B16A16_unorm_SkColorType
pixel with a little endian uint16_t for red, green, blue
Definition SkColorType.h:50
@ kRGBA_10x6_SkColorType
pixel with 10 used bits (most significant) followed by 6 unused
Definition SkColorType.h:33
@ kR8_unorm_SkColorType
Definition SkColorType.h:54
@ kBGR_101010x_SkColorType
pixel with 10 bits each for blue, green, red; in 32-bit word
Definition SkColorType.h:30
@ kARGB_4444_SkColorType
pixel with 4 bits for alpha, red, green, blue; in 16-bit word
Definition SkColorType.h:23
@ kR8G8_unorm_SkColorType
pixel with a uint8_t for red and green
Definition SkColorType.h:43
@ kBGRA_8888_SkColorType
pixel with 8 bits for blue, green, red, alpha; in 32-bit word
Definition SkColorType.h:26
@ kA16_unorm_SkColorType
pixel with a little endian uint16_t for alpha
Definition SkColorType.h:48
@ kRGBA_F16_SkColorType
pixel with half floats for red, green, blue, alpha;
Definition SkColorType.h:38
@ kAlpha_8_SkColorType
pixel with alpha in 8-bit byte
Definition SkColorType.h:21
@ kRGB_101010x_SkColorType
pixel with 10 bits each for red, green, blue; in 32-bit word
Definition SkColorType.h:29
@ kSRGBA_8888_SkColorType
Definition SkColorType.h:53
@ kGray_8_SkColorType
pixel with grayscale level in 8-bit byte
Definition SkColorType.h:35
@ kRGB_565_SkColorType
pixel with 5 bits red, 6 bits green, 5 bits blue, in 16-bit word
Definition SkColorType.h:22
@ kBGRA_10101010_XR_SkColorType
pixel with 10 bits each for blue, green, red, alpha; in 64-bit word, extended range
Definition SkColorType.h:32
@ kRGBA_8888_SkColorType
pixel with 8 bits for red, green, blue, alpha; in 32-bit word
Definition SkColorType.h:24
@ kRGB_888x_SkColorType
pixel with 8 bits each for red, green, blue; in 32-bit word
Definition SkColorType.h:25
@ kBGRA_1010102_SkColorType
10 bits for blue, green, red; 2 bits for alpha; in 32-bit word
Definition SkColorType.h:28
@ kA16_float_SkColorType
pixel with a half float for alpha
Definition SkColorType.h:45
@ kRGBA_F32_SkColorType
pixel using C float for red, green, blue, alpha; in 128-bit word
Definition SkColorType.h:40
@ kRGBA_1010102_SkColorType
10 bits for red, green, blue; 2 bits for alpha; in 32-bit word
Definition SkColorType.h:27
@ kBGR_101010x_XR_SkColorType
pixel with 10 bits each for blue, green, red; in 32-bit word, extended range
Definition SkColorType.h:31
@ kR16G16_unorm_SkColorType
pixel with a little endian uint16_t for red and green
Definition SkColorType.h:49
@ kRGBA_F16Norm_SkColorType
pixel with half floats in [0,1] for red, green, blue, alpha;
Definition SkColorType.h:36
@ kUnknown_SkColorType
uninitialized
Definition SkColorType.h:20
@ kR16G16_float_SkColorType
pixel with a half float for red and green
Definition SkColorType.h:46
#define SK_REGISTER_FLATTENABLE(type)
static bool SkColorTypeIsAlphaOnly(SkColorType ct)
SK_SPI sk_sp< SkImage > SkMakeImageFromRasterBitmap(const SkBitmap &, SkCopyPixelsMode)
SkCopyPixelsMode
Definition SkImagePriv.h:17
LegacyFilterEnum
static SkSamplingOptions tweak_sampling(SkSamplingOptions sampling, const SkMatrix &matrix)
static SkTileMode optimize(SkTileMode tm, int dimension)
SkRect SkModifyPaintAndDstForDrawImageRect(const SkImage *image, const SkSamplingOptions &sampling, SkRect src, SkRect dst, bool strictSrcSubset, SkPaint *paint)
sk_sp< SkShader > SkMakeBitmapShaderForPaint(const SkPaint &paint, const SkBitmap &src, SkTileMode tmx, SkTileMode tmy, const SkSamplingOptions &sampling, const SkMatrix *localMatrix, SkCopyPixelsMode mode)
static SkImage_Base * as_IB(SkImage *image)
static constexpr int32_t SK_MaxS32
Definition SkMath.h:21
static bool contains(const SkRect &r, SkPoint p)
static bool apply(Pass *pass, SkRecord *record)
sk_sp< T > sk_ref_sp(T *obj)
Definition SkRefCnt.h:381
SkFilterMode
SkTileMode
Definition SkTileMode.h:13
auto make(Ctor &&ctor) -> decltype(ctor(nullptr))
SkSamplingOptions sampling() const
bool appendStages(const SkStageRec &, const SkShaders::MatrixRec &) const override
static sk_sp< SkShader > Make(sk_sp< SkImage >, SkTileMode tmx, SkTileMode tmy, const SkSamplingOptions &, const SkMatrix *localMatrix, bool clampAsIfUnpremul=false)
static SkM44 CubicResamplerMatrix(float B, float C)
bool isOpaque() const override
static sk_sp< SkShader > MakeSubset(sk_sp< SkImage >, const SkRect &subset, SkTileMode tmx, SkTileMode tmy, const SkSamplingOptions &, const SkMatrix *localMatrix, bool clampAsIfUnpremul=false)
sk_sp< SkImage > image() const
SkImageShader(sk_sp< SkImage >, const SkRect &subset, SkTileMode tmx, SkTileMode tmy, const SkSamplingOptions &, bool raw, bool clampAsIfUnpremul)
static sk_sp< SkShader > MakeRaw(sk_sp< SkImage >, SkTileMode tmx, SkTileMode tmy, const SkSamplingOptions &, const SkMatrix *localMatrix)
SkImage * onIsAImage(SkMatrix *, SkTileMode *) const override
void flatten(SkWriteBuffer &) const override
SkRect subset() const
SkColorSpace * colorSpace() const
Definition SkImage.cpp:156
SkISize dimensions() const
Definition SkImage.h:297
SkAlphaType alphaType() const
Definition SkImage.cpp:154
sk_sp< SkShader > makeShader(SkTileMode tmx, SkTileMode tmy, const SkSamplingOptions &, const SkMatrix *localMatrix=nullptr) const
Definition SkImage.cpp:179
SkColorType colorType() const
Definition SkImage.cpp:152
bool isOpaque() const
Definition SkImage.h:375
int height() const
Definition SkImage.h:291
SkIRect bounds() const
Definition SkImage.h:303
bool hasMipmaps() const
Definition SkImage.cpp:292
Definition SkM44.h:150
void getColMajor(SkScalar v[]) const
Definition SkM44.h:256
static SkMatrix RectToRect(const SkRect &src, const SkRect &dst, ScaleToFit mode=kFill_ScaleToFit)
Definition SkMatrix.h:157
static SkMatrix Concat(const SkMatrix &a, const SkMatrix &b)
Definition SkMatrix.h:1775
static const SkMatrix & I()
void normalizePerspective()
Definition SkMatrix.h:1270
bool mapRect(SkRect *dst, const SkRect &src, SkApplyPerspectiveClip pc=SkApplyPerspectiveClip::kYes) const
@ kTranslate_Mask
translation SkMatrix
Definition SkMatrix.h:193
static SkMipmapAccessor * Make(SkArenaAlloc *, const SkImage *, const SkMatrix &inv, SkMipmapMode)
@ kFill_Style
set to fill geometry
Definition SkPaint.h:193
@ kNoFilterQualityShaders_Version
int width() const
Definition SkPixmap.h:160
int rowBytesAsPixels() const
Definition SkPixmap.h:214
const void * addr() const
Definition SkPixmap.h:153
int height() const
Definition SkPixmap.h:166
static SkSamplingOptions AnisoFallback(bool imageIsMipped)
static void RegisterFlattenables()
std::optional< MatrixRec > apply(const SkStageRec &rec, const SkMatrix &postInv={}) const
bool totalInverse(SkMatrix *out) const
bool totalMatrixIsValid() const
T * get() const
Definition SkRefCnt.h:303
const Paint & paint
sk_sp< SkImage > image
Definition examples.cpp:29
float SkScalar
Definition extension.cpp:12
struct MyStruct s
static const uint8_t buffer[]
double x
SI T gather(const T *p, U32 ix)
SkSamplingOptions sampling
Definition SkRecords.h:337
Definition ref_ptr.h:256
SkTileMode tmy
SkTileMode tmx
int32_t height
int32_t width
const skcms_TransferFunction * skcms_sRGB_TransferFunction()
Definition skcms.cc:1587
static SkRect Make(const SkISize &size)
Definition SkRect.h:669
static constexpr SkRect MakeEmpty()
Definition SkRect.h:595
bool contains(SkScalar x, SkScalar y) const
Definition extension.cpp:19
bool isEmpty() const
Definition SkRect.h:693
static constexpr SkRect MakeWH(float w, float h)
Definition SkRect.h:609
static constexpr SkRect MakeLTRB(float l, float t, float r, float b)
Definition SkRect.h:646
const SkCubicResampler cubic
const SkFilterMode filter
const SkMipmapMode mipmap
SkColor4f fPaintColor
SkRasterPipeline * fPipeline
SkColorSpace * fDstCS
SkArenaAlloc * fAlloc