/** * We are faster in clamp, so always use that tiling when we can.
*/ static SkTileMode optimize(SkTileMode tm, int dimension) {
SkASSERT(dimension > 0); #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK // need to update frameworks/base/libs/hwui/tests/unit/SkiaBehaviorTests.cpp:55 to allow // for transforming to clamp. return tm; #else // mirror and repeat on a 1px axis are the same as clamping, but decal will still transition to // transparent black. return (tm != SkTileMode::kDecal && dimension == 1) ? SkTileMode::kClamp : tm; #endif
}
// Bicubic filtering of raw image shaders would add a surprising clamp - so we don't support it
SkASSERT(!fRaw || !fSampling.useCubic);
}
// just used for legacy-unflattening enumclass LegacyFilterEnum {
kNone,
kLow,
kMedium,
kHigh, // this is the special value for backward compatibility
kInheritFromPaint, // this signals we should use the new SkFilterOptions
kUseFilterOptions, // use cubic and ignore FilterOptions
kUseCubicResampler,
kLast = kUseCubicResampler,
};
// fClampAsIfUnpremul is always false when constructed through public APIs, // so there's no need to read or write it here.
sk_sp<SkFlattenable> SkImageShader::CreateProc(SkReadBuffer& buffer) { auto tmx = buffer.read32LE<SkTileMode>(SkTileMode::kLastTileMode); auto tmy = buffer.read32LE<SkTileMode>(SkTileMode::kLastTileMode);
SkSamplingOptions sampling; bool readSampling = true; if (buffer.isVersionLT(SkPicturePriv::kNoFilterQualityShaders_Version) &&
!buffer.readBool() /* legacy has_sampling */)
{
readSampling = false; // we just default to Nearest in sampling
} if (readSampling) {
sampling = buffer.readSampling();
}
SkMatrix localMatrix; if (buffer.isVersionLT(SkPicturePriv::Version::kNoShaderLocalMatrix)) {
buffer.readMatrix(&localMatrix);
}
sk_sp<SkImage> img = buffer.readImage(); if (!img) { return nullptr;
}
bool raw = buffer.isVersionLT(SkPicturePriv::Version::kRawImageShaders) ? false
: buffer.readBool();
// TODO(skbug.com/12784): Subset is not serialized yet; it's only used by special images so it // will never be written to an SKP.
// TODO(skbug.com/12784): Subset is not serialized yet; it's only used by special images so it // will never be written to an SKP.
SkASSERT(!needs_subset(fImage.get(), fSubset));
// Scale+translate methods are always present, but affine might not be. if (!SkOpts::S32_alpha_D32_filter_DXDY && !inv.isScaleTranslate()) { returnfalse;
}
// legacy code uses SkFixed 32.32, so ensure the inverse doesn't map device coordinates // out of range. const SkScalar max_dev_coord = 32767.0f; const SkRect src = inv.mapRect(SkRect::MakeWH(max_dev_coord, max_dev_coord));
// take 1/4 of max signed 32bits so we have room to subtract local values const SkScalar max_fixed32dot32 = float(SK_MaxS32) * 0.25f; if (!SkRect::MakeLTRB(-max_fixed32dot32, -max_fixed32dot32,
+max_fixed32dot32, +max_fixed32dot32).contains(src)) { returnfalse;
}
// legacy shader impl should be able to handle these matrices returntrue;
}
auto supported = [](const SkSamplingOptions& sampling) { const std::tuple<SkFilterMode,SkMipmapMode> supported[] = {
{SkFilterMode::kNearest, SkMipmapMode::kNone}, // legacy None
{SkFilterMode::kLinear, SkMipmapMode::kNone}, // legacy Low
{SkFilterMode::kLinear, SkMipmapMode::kNearest}, // legacy Medium
}; for (auto [f, m] : supported) { if (sampling.filter == f && sampling.mipmap == m) { returntrue;
}
} returnfalse;
}; if (sampling.useCubic || !supported(sampling)) { return nullptr;
}
// SkBitmapProcShader stores bitmap coordinates in a 16bit buffer, // so it can't handle bitmaps larger than 65535. // // We back off another bit to 32767 to make small amounts of // intermediate math safe, e.g. in // // SkFixed fx = ...; // fx = tile(fx + SK_Fixed1); // // we want to make sure (fx + SK_Fixed1) never overflows. if (fImage-> width() > 32767 ||
fImage->height() > 32767) { return nullptr;
}
SkMatrix inv; if (!rec.fMatrixRec.totalInverse(&inv) || !legacy_shader_can_handle(inv)) { return nullptr;
}
if (!rec.isLegacyCompatible(fImage->colorSpace())) { return nullptr;
}
sk_sp<SkShader> SkMakeBitmapShaderForPaint(const SkPaint& paint, const SkBitmap& src,
SkTileMode tmx, SkTileMode tmy, const SkSamplingOptions& sampling, const SkMatrix* localMatrix, SkCopyPixelsMode mode) { auto s = SkImageShader::Make(SkMakeImageFromRasterBitmap(src, mode),
tmx, tmy, sampling, localMatrix); if (!s) { return nullptr;
} if (SkColorTypeIsAlphaOnly(src.colorType()) && paint.getShader()) { // Compose the image shader with the paint's shader. Alpha images+shaders should output the // texture's alpha multiplied by the shader's color. DstIn (d*sa) will achieve this with // the source image and dst shader (MakeBlend takes dst first, src second).
s = SkShaders::Blend(SkBlendMode::kDstIn, paint.refShader(), std::move(s));
} return s;
}
SkRect SkModifyPaintAndDstForDrawImageRect(const SkImage* image, const SkSamplingOptions& sampling,
SkRect src,
SkRect dst, bool strictSrcSubset,
SkPaint* paint) { // The paint should have already been cleaned for a regular drawImageRect, e.g. no path // effect and is a fill.
SkASSERT(paint);
SkASSERT(paint->getStyle() == SkPaint::kFill_Style && !paint->getPathEffect());
// We would like an image that is mapped 1:1 with device pixels but at a half pixel offset // to select every pixel from the src image once. Our rasterizer biases upward. That is a // rect from 0.5...1.5 fills pixel 1 and not pixel 0. So we make exact integer pixel sample // values select the pixel to the left/above the integer value. // // Note that a mirror mapping between canvas and image space will not have this property - // on one side of the image a row/column will be skipped and one repeated on the other side. // // The GM nearest_half_pixel_image tests both of the above scenarios. // // The implementation of SkTileMode::kMirror also modifies integer pixel snapping to create // consistency when the sample coords are running backwards and must account for gather // modification we perform here. The GM mirror_tile tests this. if (!sampling.useCubic && sampling.filter == SkFilterMode::kNearest) {
gather->roundDownAtInteger = true;
limitX->mirrorBiasDir = limitY->mirrorBiasDir = 1;
}
// When integer sample coords snap left/up then we want the right/bottom edge of the // image bounds to be inside the image rather than the left/top edge, that is (0, w] // rather than [0, w). if (gather->roundDownAtInteger) {
decalCtx->inclusiveEdge_x = decalCtx->limit_x;
decalCtx->inclusiveEdge_y = decalCtx->limit_y;
}
}
}
};
// We only support certain sampling options in stages so far auto sampling = fSampling; if (sampling.isAniso()) {
sampling = SkSamplingPriv::AnisoFallback(fImage->hasMipmaps());
}
SkRasterPipeline* p = rec.fPipeline;
SkArenaAlloc* alloc = rec.fAlloc;
SkMatrix baseInv; // If the total matrix isn't valid then we will always access the base MIP level. if (mRec.totalMatrixIsValid()) { if (!mRec.totalInverse(&baseInv)) { returnfalse;
}
baseInv.normalizePerspective();
}
if (!sampling.useCubic) { // TODO: can tweak_sampling sometimes for cubic too when B=0 if (mRec.totalMatrixIsValid()) {
sampling = tweak_sampling(sampling, SkMatrix::Concat(upper.inv, baseInv));
}
}
auto append_tiling_and_gather = [&](const MipLevelHelper* level) { if (decalBothAxes) {
p->append(SkRasterPipelineOp::decal_x_and_y, level->decalCtx);
} else { switch (fTileModeX) { case SkTileMode::kClamp: /* The gather_xxx stage will clamp for us. */ break; case SkTileMode::kMirror:
p->append(SkRasterPipelineOp::mirror_x, level->limitX); break; case SkTileMode::kRepeat:
p->append(SkRasterPipelineOp::repeat_x, level->limitX); break; case SkTileMode::kDecal:
p->append(SkRasterPipelineOp::decal_x, level->decalCtx); break;
} switch (fTileModeY) { case SkTileMode::kClamp: /* The gather_xxx stage will clamp for us. */ break; case SkTileMode::kMirror:
p->append(SkRasterPipelineOp::mirror_y, level->limitY); break; case SkTileMode::kRepeat:
p->append(SkRasterPipelineOp::repeat_y, level->limitY); break; case SkTileMode::kDecal:
p->append(SkRasterPipelineOp::decal_y, level->decalCtx); break;
}
}
void* ctx = level->gather; switch (level->pm.colorType()) { case kAlpha_8_SkColorType: p->append(SkRasterPipelineOp::gather_a8, ctx); break; case kA16_unorm_SkColorType: p->append(SkRasterPipelineOp::gather_a16, ctx); break; case kA16_float_SkColorType: p->append(SkRasterPipelineOp::gather_af16, ctx); break; case kRGB_565_SkColorType: p->append(SkRasterPipelineOp::gather_565, ctx); break; case kARGB_4444_SkColorType: p->append(SkRasterPipelineOp::gather_4444, ctx); break; case kR8G8_unorm_SkColorType: p->append(SkRasterPipelineOp::gather_rg88, ctx); break; case kR16G16_unorm_SkColorType: p->append(SkRasterPipelineOp::gather_rg1616,ctx); break; case kR16G16_float_SkColorType: p->append(SkRasterPipelineOp::gather_rgf16, ctx); break; case kRGBA_8888_SkColorType: p->append(SkRasterPipelineOp::gather_8888, ctx); break;
case kRGBA_1010102_SkColorType:
p->append(SkRasterPipelineOp::gather_1010102, ctx); break;
case kR16G16B16A16_unorm_SkColorType:
p->append(SkRasterPipelineOp::gather_16161616, ctx); break;
case kRGBA_F16Norm_SkColorType: case kRGBA_F16_SkColorType: p->append(SkRasterPipelineOp::gather_f16, ctx); break; case kRGBA_F32_SkColorType: p->append(SkRasterPipelineOp::gather_f32, ctx); break; case kBGRA_10101010_XR_SkColorType:
p->append(SkRasterPipelineOp::gather_10101010_xr, ctx);
p->append(SkRasterPipelineOp::swap_rb); break; case kRGBA_10x6_SkColorType: p->append(SkRasterPipelineOp::gather_10x6, ctx); break;
case kGray_8_SkColorType: p->append(SkRasterPipelineOp::gather_a8, ctx);
p->append(SkRasterPipelineOp::alpha_to_gray ); break;
case kR8_unorm_SkColorType: p->append(SkRasterPipelineOp::gather_a8, ctx);
p->append(SkRasterPipelineOp::alpha_to_red ); break;
case kRGB_888x_SkColorType: p->append(SkRasterPipelineOp::gather_8888, ctx);
p->append(SkRasterPipelineOp::force_opaque ); break; case kRGB_F16F16F16x_SkColorType:
p->append(SkRasterPipelineOp::gather_f16, ctx);
p->append(SkRasterPipelineOp::force_opaque); break; case kBGRA_1010102_SkColorType:
p->append(SkRasterPipelineOp::gather_1010102, ctx);
p->append(SkRasterPipelineOp::swap_rb); break;
case kRGB_101010x_SkColorType:
p->append(SkRasterPipelineOp::gather_1010102, ctx);
p->append(SkRasterPipelineOp::force_opaque); break;
case kBGR_101010x_XR_SkColorType:
p->append(SkRasterPipelineOp::gather_1010102_xr, ctx);
p->append(SkRasterPipelineOp::force_opaque);
p->append(SkRasterPipelineOp::swap_rb); break;
case kBGR_101010x_SkColorType:
p->append(SkRasterPipelineOp::gather_1010102, ctx);
p->append(SkRasterPipelineOp::force_opaque);
p->append(SkRasterPipelineOp::swap_rb); break;
case kBGRA_8888_SkColorType:
p->append(SkRasterPipelineOp::gather_8888, ctx);
p->append(SkRasterPipelineOp::swap_rb); break;
case kSRGBA_8888_SkColorType:
p->append(SkRasterPipelineOp::gather_8888, ctx);
p->appendTransferFunction(*skcms_sRGB_TransferFunction()); break;
case kUnknown_SkColorType: SkASSERT(false);
} if (level->decalCtx) {
p->append(SkRasterPipelineOp::check_decal_mask, level->decalCtx);
}
};
auto append_misc = [&] {
SkColorSpace* cs = upper.pm.colorSpace();
SkAlphaType at = upper.pm.alphaType();
// Color for alpha-only images comes from the paint (already converted to dst color space). // If we were sampled by a runtime effect, the paint color was replaced with transparent // black, so this tinting is effectively suppressed. See also: RuntimeEffectRPCallbacks if (SkColorTypeIsAlphaOnly(upper.pm.colorType()) && !fRaw) {
p->appendSetRGB(alloc, rec.fPaintColor);
cs = rec.fDstCS;
at = kUnpremul_SkAlphaType;
}
// Bicubic filtering naturally produces out of range values on both sides of [0,1]. if (sampling.useCubic) {
p->append(at == kUnpremul_SkAlphaType || fClampAsIfUnpremul
? SkRasterPipelineOp::clamp_01
: SkRasterPipelineOp::clamp_gamut);
}
// Transform color space and alpha type to match shader convention (dst CS, premul alpha). if (!fRaw) {
alloc->make<SkColorSpaceXformSteps>(cs, at, rec.fDstCS, kPremul_SkAlphaType)->apply(p);
}
returntrue;
};
// Check for fast-path stages. // TODO: Could we use the fast-path stages for each level when doing linear mipmap filtering?
SkColorType ct = upper.pm.colorType(); if (true
&& (ct == kRGBA_8888_SkColorType || ct == kBGRA_8888_SkColorType)
&& !sampling.useCubic && sampling.filter == SkFilterMode::kLinear
&& sampling.mipmap != SkMipmapMode::kLinear
&& fTileModeX == SkTileMode::kClamp && fTileModeY == SkTileMode::kClamp) {
// This context can be shared by both levels when doing linear mipmap filtering
SkRasterPipeline_SamplerCtx* sampler = alloc->make<SkRasterPipeline_SamplerCtx>();
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.