bool SkPixmap::erase(const SkColor4f& origColor, const SkIRect* subset) const { SkPixmap pm; if (subset) { if (!this->extractSubset(&pm, *subset)) { return false; } } else { pm = *this; } const SkColor4f color = origColor.pin(); if (kRGBA_F16_SkColorType != pm.colorType()) { Sk4f c4 = Sk4f::Load(color.vec()); SkColor c; (c4 * Sk4f(255) + Sk4f(0.5f)).store(&c); return pm.erase(c); } const uint64_t half4 = color.premul().toF16(); for (int y = 0; y < pm.height(); ++y) { sk_memset64(pm.writable_addr64(0, y), half4, pm.width()); } return true; }
static Sk4f set_argb(float a, float r, float g, float b) { if (0 == SkPM4f::R) { return Sk4f(r, g, b, a); } else { return Sk4f(b, g, r, a); } }
template <DstType D> void srcover_1(const SkXfermode::PM4fState& state, uint32_t dst[], const SkPM4f& src, int count, const SkAlpha aa[]) { Sk4f s4 = Sk4f::Load(src.fVec); Sk4f scale = Sk4f(1 - get_alpha(s4)); if (aa) { for (int i = 0; i < count; ++i) { unsigned a = aa[i]; if (0 == a) { continue; } Sk4f d4 = load_dst<D>(dst[i]); Sk4f r4; if (a != 0xFF) { s4 = scale_by_coverage(s4, a); r4 = s4 + d4 * Sk4f(1 - get_alpha(s4)); } else { r4 = s4 + d4 * scale; } dst[i] = store_dst<D>(r4); } } else { for (int i = 0; i < count; ++i) { Sk4f d4 = load_dst<D>(dst[i]); Sk4f r4 = s4 + d4 * scale; dst[i] = store_dst<D>(r4); } } }
void compute_interval_props(SkScalar t) { fDc = dst_swizzle<DstType>(fInterval->fDc); fCc = dst_swizzle<DstType>(fInterval->fC0); fCc = fCc + fDc * Sk4f(t); fCc = scale_for_dest<DstType, profile>(fCc); fDcDx = scale_for_dest<DstType, profile>(fDc * Sk4f(fDx)); fZeroRamp = fIsVertical || fInterval->isZeroRamp(); }
sk_sp<SkData> encode_bitmap_for_png(SkBitmap bitmap) { const int w = bitmap.width(), h = bitmap.height(); // PNG wants unpremultiplied 8-bit RGBA pixels (16-bit could work fine too). // We leave the gamma of these bytes unspecified, to continue the status quo, // which we think generally is to interpret them as sRGB. SkAutoTMalloc<uint32_t> rgba(w*h); auto srgbColorSpace = SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named); if (bitmap. colorType() == kN32_SkColorType && bitmap.colorSpace() == srgbColorSpace.get()) { // These are premul sRGB 8-bit pixels in SkPMColor order. // We want unpremul sRGB 8-bit pixels in RGBA order. We'll get there via floats. bitmap.lockPixels(); auto px = (const uint32_t*)bitmap.getPixels(); if (!px) { return nullptr; } for (int i = 0; i < w*h; i++) { Sk4f fs = Sk4f_fromS32(px[i]); // Convert up to linear floats. #if defined(SK_PMCOLOR_IS_BGRA) fs = SkNx_shuffle<2,1,0,3>(fs); // Shuffle to RGBA, if not there already. #endif float invA = 1.0f / fs[3]; fs = fs * Sk4f(invA, invA, invA, 1); // Unpremultiply. rgba[i] = Sk4f_toS32(fs); // Pack down to sRGB bytes. } } else if (bitmap.colorType() == kRGBA_F16_SkColorType) { // These are premul linear half-float pixels in RGBA order. // We want unpremul sRGB 8-bit pixels in RGBA order. We'll get there via floats. bitmap.lockPixels(); auto px = (const uint64_t*)bitmap.getPixels(); if (!px) { return nullptr; } for (int i = 0; i < w*h; i++) { // Convert up to linear floats. Sk4f fs(SkHalfToFloat(static_cast<SkHalf>(px[i] >> (0 * 16))), SkHalfToFloat(static_cast<SkHalf>(px[i] >> (1 * 16))), SkHalfToFloat(static_cast<SkHalf>(px[i] >> (2 * 16))), SkHalfToFloat(static_cast<SkHalf>(px[i] >> (3 * 16)))); fs = Sk4f::Max(0.0f, Sk4f::Min(fs, 1.0f)); // Clamp float invA = 1.0f / fs[3]; fs = fs * Sk4f(invA, invA, invA, 1); // Unpremultiply. rgba[i] = Sk4f_toS32(fs); // Pack down to sRGB bytes. } } else {
DEF_TEST(Sk4x_Swizzle, r) { ASSERT_EQ(Sk4f(3,4,1,2), Sk4f(1,2,3,4).zwxy()); ASSERT_EQ(Sk4f(1,2,5,6), Sk4f::XYAB(Sk4f(1,2,3,4), Sk4f(5,6,7,8))); ASSERT_EQ(Sk4f(3,4,7,8), Sk4f::ZWCD(Sk4f(1,2,3,4), Sk4f(5,6,7,8))); ASSERT_EQ(Sk4i(3,4,1,2), Sk4i(1,2,3,4).zwxy()); ASSERT_EQ(Sk4i(1,2,5,6), Sk4i::XYAB(Sk4i(1,2,3,4), Sk4i(5,6,7,8))); ASSERT_EQ(Sk4i(3,4,7,8), Sk4i::ZWCD(Sk4i(1,2,3,4), Sk4i(5,6,7,8))); }
void GrCCFillGeometry::conicTo(const SkPoint P[3], float w) { SkASSERT(fBuildingContour); SkASSERT(P[0] == fPoints.back()); Sk2f p0 = Sk2f::Load(P); Sk2f p1 = Sk2f::Load(P+1); Sk2f p2 = Sk2f::Load(P+2); Sk2f tan0 = p1 - p0; Sk2f tan1 = p2 - p1; if (!is_convex_curve_monotonic(p0, tan0, p2, tan1)) { // The derivative of a conic has a cumbersome order-4 denominator. However, this isn't // necessary if we are only interested in a vector in the same *direction* as a given // tangent line. Since the denominator scales dx and dy uniformly, we can throw it out // completely after evaluating the derivative with the standard quotient rule. This leaves // us with a simpler quadratic function that we use to find the midtangent. float midT = find_midtangent(tan0, tan1, (w - 1) * (p2 - p0), (p2 - p0) - 2*w*(p1 - p0), w*(p1 - p0)); // Use positive logic since NaN fails comparisons. (However midT should not be NaN since we // cull near-linear conics above. And while w=0 is flat, it's not a line and has valid // midtangents.) if (!(midT > 0 && midT < 1)) { // The conic is flat. Otherwise there would be a real midtangent inside T=0..1. this->appendLine(p0, p2); return; } // Chop the conic at midtangent to produce two monotonic segments. Sk4f p3d0 = Sk4f(p0[0], p0[1], 1, 0); Sk4f p3d1 = Sk4f(p1[0], p1[1], 1, 0) * w; Sk4f p3d2 = Sk4f(p2[0], p2[1], 1, 0); Sk4f midT4 = midT; Sk4f p3d01 = lerp(p3d0, p3d1, midT4); Sk4f p3d12 = lerp(p3d1, p3d2, midT4); Sk4f p3d012 = lerp(p3d01, p3d12, midT4); Sk2f midpoint = Sk2f(p3d012[0], p3d012[1]) / p3d012[2]; Sk2f ww = Sk2f(p3d01[2], p3d12[2]) * Sk2f(p3d012[2]).rsqrt(); this->appendMonotonicConic(p0, Sk2f(p3d01[0], p3d01[1]) / p3d01[2], midpoint, ww[0]); this->appendMonotonicConic(midpoint, Sk2f(p3d12[0], p3d12[1]) / p3d12[2], p2, ww[1]); return; } this->appendMonotonicConic(p0, p1, p2, w); }
SkPM4f SkColor4f::premul() const { auto src = Sk4f::Load(this->pin().vec()); float srcAlpha = src[3]; // need the pinned version of our alpha src = src * Sk4f(srcAlpha, srcAlpha, srcAlpha, 1); return SkPM4f::From4f(src); }
void filter_span(const float array[], const T src[], int count, T dst[]) { // c0-c3 are already in [0,1]. const Sk4f c0 = Sk4f::Load(array + 0); const Sk4f c1 = Sk4f::Load(array + 4); const Sk4f c2 = Sk4f::Load(array + 8); const Sk4f c3 = Sk4f::Load(array + 12); // c4 (the translate vector) is in [0, 255]. Bring it back to [0,1]. const Sk4f c4 = Sk4f::Load(array + 16)*Sk4f(1.0f/255); // todo: we could cache this in the constructor... T matrix_translate_pmcolor = Adaptor::From4f(premul(clamp_0_1(c4))); for (int i = 0; i < count; i++) { Sk4f srcf = Adaptor::To4f(src[i]); float srcA = srcf.kth<SkPM4f::A>(); if (0 == srcA) { dst[i] = matrix_translate_pmcolor; continue; } if (1 != srcA) { srcf = unpremul(srcf); } Sk4f r4 = SkNx_dup<SK_R32_SHIFT/8>(srcf); Sk4f g4 = SkNx_dup<SK_G32_SHIFT/8>(srcf); Sk4f b4 = SkNx_dup<SK_B32_SHIFT/8>(srcf); Sk4f a4 = SkNx_dup<SK_A32_SHIFT/8>(srcf); // apply matrix Sk4f dst4 = c0 * r4 + c1 * g4 + c2 * b4 + c3 * a4 + c4; dst[i] = Adaptor::From4f(premul(clamp_0_1(dst4))); } }
void compute_interval_props(SkScalar t) { const Sk4f dC = DstTraits<dstType>::load(fInterval->fDc); fCc = DstTraits<dstType>::load(fInterval->fC0); fCc = fCc + dC * Sk4f(t); fDcDx = dC * fDx; fZeroRamp = fIsVertical || fInterval->isZeroRamp(); }
void SkColorFilter::filterSpan4f(const SkPM4f[], int count, SkPM4f span[]) const { const int N = 128; SkPMColor tmp[N]; while (count > 0) { int n = SkTMin(count, N); for (int i = 0; i < n; ++i) { SkNx_cast<uint8_t>(Sk4f::Load(span[i].fVec) * Sk4f(255) + Sk4f(0.5f)).store(&tmp[i]); } this->filterSpan(tmp, n, tmp); for (int i = 0; i < n; ++i) { span[i] = SkPM4f::FromPMColor(tmp[i]); } span += n; count -= n; } }
void src_1_lcd(uint32_t dst[], const SkPM4f* src, int count, const uint16_t lcd[]) { const Sk4f s4 = Sk4f::Load(src->fVec); if (D == kLinear_Dst) { // operate in bias-255 space for src and dst const Sk4f s4bias = s4 * Sk4f(255); for (int i = 0; i < count; ++i) { uint16_t rgb = lcd[i]; if (0 == rgb) { continue; } Sk4f d4bias = to_4f(dst[i]); dst[i] = to_4b(lerp(s4bias, d4bias, lcd16_to_unit_4f(rgb))) | (SK_A32_MASK << SK_A32_SHIFT); } } else { // kSRGB for (int i = 0; i < count; ++i) { uint16_t rgb = lcd[i]; if (0 == rgb) { continue; } Sk4f d4 = load_dst<D>(dst[i]); dst[i] = store_dst<D>(lerp(s4, d4, lcd16_to_unit_4f(rgb))) | (SK_A32_MASK << SK_A32_SHIFT); } } }
static Sk4f colorburn_4f(const Sk4f& s, const Sk4f& d) { Sk4f sa = alpha(s); Sk4f da = alpha(d); Sk4f isa = Sk4f(1) - sa; Sk4f ida = Sk4f(1) - da; Sk4f srcover = s + d * isa; Sk4f dstover = d + s * ida; Sk4f otherwise = sa * (da - Sk4f::Min(da, (da - d) * sa / s)) + s * ida + d * isa; // Order matters here, preferring d==da over s==0. auto colors = (d == da).thenElse(dstover, (s == Sk4f(0)).thenElse(srcover, otherwise)); return color_alpha(colors, srcover); }
static Sk4f lcd16_to_unit_4f(uint16_t rgb) { #ifdef SK_PMCOLOR_IS_RGBA Sk4i rgbi = Sk4i(SkGetPackedR16(rgb), SkGetPackedG16(rgb), SkGetPackedB16(rgb), 0); #else Sk4i rgbi = Sk4i(SkGetPackedB16(rgb), SkGetPackedG16(rgb), SkGetPackedR16(rgb), 0); #endif return SkNx_cast<float>(rgbi) * Sk4f(1.0f/31, 1.0f/63, 1.0f/31, 0); }
static void srcover_srgb_dst_1(const SkXfermode*, uint32_t dst[], const SkPM4f* src, int count, const SkAlpha aa[]) { Sk4f s4 = src->to4f_pmorder(); Sk4f dst_scale = Sk4f(1 - get_alpha(s4)); if (aa) { for (int i = 0; i < count; ++i) { unsigned a = aa[i]; if (0 == a) { continue; } Sk4f d4 = srgb_4b_to_linear_unit(dst[i]); Sk4f r4; if (a != 0xFF) { const Sk4f s4_aa = scale_by_coverage(s4, a); r4 = s4_aa + d4 * Sk4f(1 - get_alpha(s4_aa)); } else { r4 = s4 + d4 * dst_scale; } dst[i] = to_4b(linear_unit_to_srgb_255f(r4)); } } else { while (count >= 4) { auto d = load_4_srgb(dst); auto s = Sk4x4f{{ src->r() }, { src->g() }, { src->b() }, { src->a() }}; #if defined(SK_PMCOLOR_IS_BGRA) SkTSwap(s.r, s.b); #endif auto invSA = 1.0f - s.a; auto r = s.r + d.r * invSA, g = s.g + d.g * invSA, b = s.b + d.b * invSA, a = s.a + d.a * invSA; store_4_srgb(dst, Sk4x4f{r,g,b,a}); count -= 4; dst += 4; } for (int i = 0; i < count; ++i) { Sk4f d4 = srgb_4b_to_linear_unit(dst[i]); dst[i] = to_4b(linear_unit_to_srgb_255f(s4 + d4 * dst_scale)); } } }
void compute_interval_props(SkScalar t) { SkASSERT(in_range(t, fInterval->fT0, fInterval->fT1)); const Sk4f dc = DstTraits<dstType, premul>::load(fInterval->fCg); fCc = DstTraits<dstType, premul>::load(fInterval->fCb) + dc * Sk4f(t); fDcDx = dc * fDx; fZeroRamp = fIsVertical || (dc == 0).allTrue(); }
static Sk4f overlay_4f(const Sk4f& s, const Sk4f& d) { Sk4f sa = alpha(s); Sk4f da = alpha(d); Sk4f two = Sk4f(2); Sk4f rc = (two * d <= da).thenElse(two * s * d, sa * da - two * (da - d) * (sa - s)); return pin_1(s + d - s * da + color_alpha(rc - d * sa, 0)); }
template <DstType D> void srcover_n(const SkXfermode*, uint32_t dst[], const SkPM4f src[], int count, const SkAlpha aa[]) { if (aa) { for (int i = 0; i < count; ++i) { unsigned a = aa[i]; if (0 == a) { continue; } Sk4f s4 = src[i].to4f_pmorder(); Sk4f d4 = load_dst<D>(dst[i]); if (a != 0xFF) { s4 = scale_by_coverage(s4, a); } Sk4f r4 = s4 + d4 * Sk4f(1 - get_alpha(s4)); dst[i] = store_dst<D>(r4); } } else { while (count >= 4 && D == kSRGB_Dst) { auto d = load_4_srgb(dst); auto s = Sk4x4f::Transpose(src->fVec); #if defined(SK_PMCOLOR_IS_BGRA) SkTSwap(s.r, s.b); #endif auto invSA = 1.0f - s.a; auto r = s.r + d.r * invSA, g = s.g + d.g * invSA, b = s.b + d.b * invSA, a = s.a + d.a * invSA; store_4_srgb(dst, Sk4x4f{r,g,b,a}); count -= 4; dst += 4; src += 4; } for (int i = 0; i < count; ++i) { Sk4f s4 = src[i].to4f_pmorder(); Sk4f d4 = load_dst<D>(dst[i]); Sk4f r4 = s4 + d4 * Sk4f(1 - get_alpha(s4)); dst[i] = store_dst<D>(r4); } } }
void SkLinearGradient:: LinearGradient4fContext::mapTs(int x, int y, SkScalar ts[], int count) const { SkASSERT(count > 0); SkASSERT(fDstToPosClass != kLinear_MatrixClass); SkScalar sx = x + SK_ScalarHalf; const SkScalar sy = y + SK_ScalarHalf; SkPoint pt; if (fDstToPosClass != kPerspective_MatrixClass) { // kLinear_MatrixClass, kFixedStepInX_MatrixClass => fixed dt per scanline const SkScalar dtdx = fDstToPos.fixedStepInX(sy).x(); fDstToPosProc(fDstToPos, sx, sy, &pt); const Sk4f dtdx4 = Sk4f(4 * dtdx); Sk4f t4 = Sk4f(pt.x() + 0 * dtdx, pt.x() + 1 * dtdx, pt.x() + 2 * dtdx, pt.x() + 3 * dtdx); while (count >= 4) { t4.store(ts); t4 = t4 + dtdx4; ts += 4; count -= 4; } if (count & 2) { *ts++ = t4[0]; *ts++ = t4[1]; t4 = SkNx_shuffle<2, 0, 1, 3>(t4); } if (count & 1) { *ts++ = t4[0]; } } else { for (int i = 0; i < count; ++i) { fDstToPosProc(fDstToPos, sx, sy, &pt); ts[i] = pt.x(); sx += SK_Scalar1; } } }
static void srcover_linear_dst_1(const SkXfermode*, uint32_t dst[], const SkPM4f* src, int count, const SkAlpha aa[]) { const Sk4f s4 = src->to4f_pmorder(); const Sk4f dst_scale = Sk4f(1 - get_alpha(s4)); if (aa) { for (int i = 0; i < count; ++i) { unsigned a = aa[i]; if (0 == a) { continue; } Sk4f d4 = Sk4f_fromL32(dst[i]); Sk4f r4; if (a != 0xFF) { Sk4f s4_aa = scale_by_coverage(s4, a); r4 = s4_aa + d4 * Sk4f(1 - get_alpha(s4_aa)); } else { r4 = s4 + d4 * dst_scale; } dst[i] = Sk4f_toL32(r4); } } else { const Sk4f s4_255 = s4 * Sk4f(255) + Sk4f(0.5f); // +0.5 to pre-bias for rounding while (count >= 4) { Sk4f d0 = to_4f(dst[0]); Sk4f d1 = to_4f(dst[1]); Sk4f d2 = to_4f(dst[2]); Sk4f d3 = to_4f(dst[3]); Sk4f_ToBytes((uint8_t*)dst, s4_255 + d0 * dst_scale, s4_255 + d1 * dst_scale, s4_255 + d2 * dst_scale, s4_255 + d3 * dst_scale); dst += 4; count -= 4; } for (int i = 0; i < count; ++i) { Sk4f d4 = to_4f(dst[i]); dst[i] = to_4b(s4_255 + d4 * dst_scale); } } }
void GrCCPathParser::parsePath(const SkMatrix& m, const SkPath& path, SkRect* devBounds, SkRect* devBounds45) { const SkPoint* pts = SkPathPriv::PointData(path); int numPts = path.countPoints(); SkASSERT(numPts + 1 <= fLocalDevPtsBuffer.count()); if (!numPts) { devBounds->setEmpty(); devBounds45->setEmpty(); this->parsePath(path, nullptr); return; } // m45 transforms path points into "45 degree" device space. A bounding box in this space gives // the circumscribing octagon's diagonals. We could use SK_ScalarRoot2Over2, but an orthonormal // transform is not necessary as long as the shader uses the correct inverse. SkMatrix m45; m45.setSinCos(1, 1); m45.preConcat(m); // X,Y,T are two parallel view matrices that accumulate two bounding boxes as they map points: // device-space bounds and "45 degree" device-space bounds (| 1 -1 | * devCoords). // | 1 1 | Sk4f X = Sk4f(m.getScaleX(), m.getSkewY(), m45.getScaleX(), m45.getSkewY()); Sk4f Y = Sk4f(m.getSkewX(), m.getScaleY(), m45.getSkewX(), m45.getScaleY()); Sk4f T = Sk4f(m.getTranslateX(), m.getTranslateY(), m45.getTranslateX(), m45.getTranslateY()); // Map the path's points to device space and accumulate bounding boxes. Sk4f devPt = SkNx_fma(Y, Sk4f(pts[0].y()), T); devPt = SkNx_fma(X, Sk4f(pts[0].x()), devPt); Sk4f topLeft = devPt; Sk4f bottomRight = devPt; // Store all 4 values [dev.x, dev.y, dev45.x, dev45.y]. We are only interested in the first two, // and will overwrite [dev45.x, dev45.y] with the next point. This is why the dst buffer must // be at least one larger than the number of points. devPt.store(&fLocalDevPtsBuffer[0]); for (int i = 1; i < numPts; ++i) { devPt = SkNx_fma(Y, Sk4f(pts[i].y()), T); devPt = SkNx_fma(X, Sk4f(pts[i].x()), devPt); topLeft = Sk4f::Min(topLeft, devPt); bottomRight = Sk4f::Max(bottomRight, devPt); devPt.store(&fLocalDevPtsBuffer[i]); } SkPoint topLeftPts[2], bottomRightPts[2]; topLeft.store(topLeftPts); bottomRight.store(bottomRightPts); devBounds->setLTRB(topLeftPts[0].x(), topLeftPts[0].y(), bottomRightPts[0].x(), bottomRightPts[0].y()); devBounds45->setLTRB(topLeftPts[1].x(), topLeftPts[1].y(), bottomRightPts[1].x(), bottomRightPts[1].y()); this->parsePath(path, fLocalDevPtsBuffer.get()); }
static void clear(const SkXfermode*, uint64_t dst[], const SkPM4f*, int count, const SkAlpha aa[]) { if (aa) { for (int i = 0; i < count; ++i) { if (aa[i]) { const Sk4f d4 = SkHalfToFloat_finite_ftz(dst[i]); SkFloatToHalf_finite_ftz(d4 * Sk4f((255 - aa[i]) * 1.0f/255)).store(&dst[i]); } } } else { sk_memset64(dst, 0, count); } }
void advance(SkScalar advX) { SkASSERT(advX > 0); SkASSERT(fAdvX >= 0); if (advX >= fAdvX) { advX = this->advance_interval(advX); } SkASSERT(advX < fAdvX); fCc = fCc + fDcDx * Sk4f(advX); fAdvX -= advX; }
static void clear_srgb(const SkXfermode*, uint32_t dst[], const SkPM4f[], int count, const SkAlpha aa[]) { if (aa) { for (int i = 0; i < count; ++i) { if (aa[i]) { Sk4f d = Sk4f_fromS32(dst[i]) * Sk4f((255 - aa[i]) * (1/255.0f)); dst[i] = Sk4f_toS32(d); } } } else { sk_memset32(dst, 0, count); } }
static void clear_srgb_n(const SkXfermode::PM4fState& state, uint32_t dst[], const SkPM4f[], int count, const SkAlpha aa[]) { if (aa) { for (int i = 0; i < count; ++i) { unsigned a = aa[i]; if (a) { Sk4f d = Sk4f_fromS32(dst[i]) * Sk4f((255 - a) * (1/255.0f)); dst[i] = Sk4f_toS32(d); } } } else { sk_bzero(dst, count * sizeof(SkPMColor)); } }
static void srcover_1(const SkXfermode*, uint64_t dst[], const SkPM4f* src, int count, const SkAlpha aa[]) { const Sk4f s4 = Sk4f::Load(src->fVec); const Sk4f dst_scale = Sk4f(1 - get_alpha(s4)); for (int i = 0; i < count; ++i) { const Sk4f d4 = SkHalfToFloat_finite_ftz(dst[i]); const Sk4f r4 = s4 + d4 * dst_scale; if (aa) { SkFloatToHalf_finite_ftz(lerp_by_coverage(r4, d4, aa[i])).store(&dst[i]); } else { SkFloatToHalf_finite_ftz(r4).store(&dst[i]); } } }
void srcover_n_lcd(uint32_t dst[], const SkPM4f src[], int count, const uint16_t lcd[]) { for (int i = 0; i < count; ++i) { uint16_t rgb = lcd[i]; if (0 == rgb) { continue; } Sk4f s4 = Sk4f::Load(src[i].fVec); Sk4f dst_scale = Sk4f(1 - get_alpha(s4)); Sk4f d4 = load_dst<D>(dst[i]); Sk4f r4 = s4 + d4 * dst_scale; r4 = lerp(r4, d4, lcd16_to_unit_4f(rgb)); dst[i] = store_dst<D>(r4) | (SK_A32_MASK << SK_A32_SHIFT); } }
template <DstType D> void srcover_n(const SkXfermode::U64State& state, uint64_t dst[], const SkPM4f src[], int count, const SkAlpha aa[]) { for (int i = 0; i < count; ++i) { const Sk4f s4 = pm_to_rgba_order(Sk4f::Load(src[i].fVec)); const Sk4f dst_scale = Sk4f(1 - get_alpha(s4)); const Sk4f s4bias = unit_to_bias<D>(s4); const Sk4f d4bias = load_from_dst<D>(dst[i]); const Sk4f r4bias = s4bias + d4bias * dst_scale; if (aa) { dst[i] = store_to_dst<D>(lerp_by_coverage(r4bias, d4bias, aa[i])); } else { dst[i] = store_to_dst<D>(r4bias); } } }
static Sk4f softlight_4f(const Sk4f& s, const Sk4f& d) { Sk4f sa = alpha(s); Sk4f da = alpha(d); Sk4f isa = Sk4f(1) - sa; Sk4f ida = Sk4f(1) - da; // Some common terms. Sk4f m = (da > Sk4f(0)).thenElse(d / da, Sk4f(0)); Sk4f s2 = Sk4f(2) * s; Sk4f m4 = Sk4f(4) * m; // The logic forks three ways: // 1. dark src? // 2. light src, dark dst? // 3. light src, light dst? Sk4f darkSrc = d * (sa + (s2 - sa) * (Sk4f(1) - m)); // Used in case 1. Sk4f darkDst = (m4 * m4 + m4) * (m - Sk4f(1)) + Sk4f(7) * m; // Used in case 2. Sk4f liteDst = m.sqrt() - m; // Used in case 3. Sk4f liteSrc = d * sa + da * (s2 - sa) * (Sk4f(4) * d <= da).thenElse(darkDst, liteDst); // Case 2 or 3? return color_alpha(s * ida + d * isa + (s2 <= sa).thenElse(darkSrc, liteSrc), // Case 1 or 2/3? s + d * isa); }
SkPM4f SkColor4f::premul() const { auto src = Sk4f::Load(this->pin().vec()); float srcAlpha = src.kth<0>(); // need the pinned version of our alpha src = src * Sk4f(1, srcAlpha, srcAlpha, srcAlpha); #ifdef SK_PMCOLOR_IS_BGRA // ARGB -> BGRA Sk4f dst = SkNx_shuffle<3,2,1,0>(src); #else // ARGB -> RGBA Sk4f dst = SkNx_shuffle<1,2,3,0>(src); #endif SkPM4f pm4; dst.store(&pm4); return pm4; }