static inline bool S32A_D565_Blend_01(SkPMColor sc, uint16_t dc, U8CPU alpha) { unsigned dst_scale = 255 - SkMulDiv255Round(SkGetPackedA32(sc), alpha); unsigned dr = SkMulS16(SkGetPackedR32(sc), alpha) + SkMulS16(SkGetPackedR16(dc) << 3, dst_scale); unsigned dg = SkMulS16(SkGetPackedG32(sc), alpha) + SkMulS16(SkGetPackedG16(dc) << 2, dst_scale); unsigned rr = SkDiv255Round(dr) >> 3; unsigned rg = SkDiv255Round(dg) >> 2; if (rr <= 31 && rg <= 63) { return true; } return false; }
SkColor SkPMColorToColor(SkPMColor pm) { if (0 == pm) return 0; unsigned a = SkGetPackedA32(pm); uint32_t scale = (255 << 16) / a; return SkColorSetARGB(a, InvScaleByte(SkGetPackedR32(pm), scale), InvScaleByte(SkGetPackedG32(pm), scale), InvScaleByte(SkGetPackedB32(pm), scale)); }
static SkStream* extract_argb8888_data(const SkBitmap& bitmap, const SkIRect& srcRect, bool extractAlpha, bool* isOpaque, bool* isTransparent) { size_t streamSize = extractAlpha ? srcRect.width() * srcRect.height() : get_uncompressed_size(bitmap, srcRect); SkStream* stream = SkNEW_ARGS(SkMemoryStream, (streamSize)); uint8_t* dst = (uint8_t*)stream->getMemoryBase(); const SkUnPreMultiply::Scale* scaleTable = SkUnPreMultiply::GetScaleTable(); for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { uint32_t* src = bitmap.getAddr32(0, y); for (int x = srcRect.fLeft; x < srcRect.fRight; x++) { SkPMColor c = src[x]; U8CPU alpha = SkGetPackedA32(c); if (extractAlpha) { *isOpaque &= alpha == SK_AlphaOPAQUE; *isTransparent &= alpha == SK_AlphaTRANSPARENT; *dst++ = alpha; } else { if (SK_AlphaTRANSPARENT == alpha) { // It is necessary to average the color component of // transparent pixels with their surrounding neighbors // since the PDF renderer may separately re-sample the // alpha and color channels when the image is not // displayed at its native resolution. Since an alpha of // zero gives no information about the color component, // the pathological case is a white image with sharp // transparency bounds - the color channel goes to black, // and the should-be-transparent pixels are rendered // as grey because of the separate soft mask and color // resizing. c = get_argb8888_neighbor_avg_color(bitmap, x, y); *dst++ = SkGetPackedR32(c); *dst++ = SkGetPackedG32(c); *dst++ = SkGetPackedB32(c); } else { SkUnPreMultiply::Scale s = scaleTable[alpha]; *dst++ = SkUnPreMultiply::ApplyScale(s, SkGetPackedR32(c)); *dst++ = SkUnPreMultiply::ApplyScale(s, SkGetPackedG32(c)); *dst++ = SkUnPreMultiply::ApplyScale(s, SkGetPackedB32(c)); } } } } SkASSERT(dst == streamSize + (uint8_t*)stream->getMemoryBase()); return stream; }
bool Image::ConvertToRGBA(Image *nimg, unsigned char *rgba, bool flipY, bool premultiply) { int length; int k; const unsigned char *pixels; int width; nimg->m_Image->lockPixels(); if ((pixels = static_cast<const unsigned char *>(nimg->m_Image->getPixels())) == NULL) { nimg->m_Image->unlockPixels(); return false; } width = nimg->getWidth(); length = width * nimg->getHeight() * 4; width *= 4; k = flipY ? length - width : 0; for (int i = 0; i < length; i += 4) { const uint32_t pixel = *reinterpret_cast<const uint32_t *>(&pixels[i]); int alpha = SkGetPackedA32(pixel); if (alpha != 0 && alpha != 255 && !premultiply) { SkColor unmultiplied = SkUnPreMultiply::PMColorToColor(pixel); rgba[k + 0] = SkColorGetR(unmultiplied); rgba[k + 1] = SkColorGetG(unmultiplied); rgba[k + 2] = SkColorGetB(unmultiplied); rgba[k + 3] = alpha; } else { rgba[k + 0] = SkGetPackedR32(pixel); rgba[k + 1] = SkGetPackedG32(pixel); rgba[k + 2] = SkGetPackedB32(pixel); rgba[k + 3] = alpha; } if (flipY && (i + 4) % width == 0) { k = length - (i + 4) - width; } else { k += 4; } } return true; }
virtual void xferA8(SK_RESTRICT SkAlpha dst[], const SK_RESTRICT SkPMColor src[], int count, const SK_RESTRICT SkAlpha aa[]) { SkASSERT(dst && src && count >= 0); if (NULL == aa) { for (int i = count - 1; i >= 0; --i) { dst[i] = SkToU8(SkGetPackedA32(src[i])); } } else { for (int i = count - 1; i >= 0; --i) { unsigned a = aa[i]; if (0 != a) { unsigned srcA = SkGetPackedA32(src[i]); if (a == 0xFF) { dst[i] = SkToU8(srcA); } else { dst[i] = SkToU8(SkAlphaBlend(srcA, dst[i], a)); } } } } }
static inline bool S32A_D565_Blend_2(SkPMColor sc, uint16_t dc, U8CPU alpha) { unsigned dst_scale = 255*255 - SkGetPackedA32(sc) * alpha; alpha *= 255; unsigned dr = (SkGetPackedR32(sc) >> 3) * alpha + SkGetPackedR16(dc) * dst_scale; unsigned dg = (SkGetPackedG32(sc) >> 2) * alpha + SkGetPackedG16(dc) * dst_scale; unsigned rr = SkDiv65025Round(dr); unsigned rg = SkDiv65025Round(dg); if (rr <= 31 && rg <= 63) { return true; } return false; }
void alphaBlendNonPremultiplied(blink::ImageFrame& src, blink::ImageFrame& dst, int canvasY, int left, int width) { for (int x = 0; x < width; ++x) { int canvasX = left + x; blink::ImageFrame::PixelData& pixel = *src.getAddr(canvasX, canvasY); if (SkGetPackedA32(pixel) != 0xff) { blink::ImageFrame::PixelData prevPixel = *dst.getAddr(canvasX, canvasY); pixel = blendSrcOverDstNonPremultiplied(pixel, prevPixel); } } }
inline SkPMColor cubicBlend(const SkScalar c[16], SkScalar t, SkPMColor c0, SkPMColor c1, SkPMColor c2, SkPMColor c3) { SkScalar t2 = t * t, t3 = t2 * t; SkScalar cc[4]; // FIXME: For the fractx case, this should be refactored out of this function. cc[0] = c[0] + SkScalarMul(c[1], t) + SkScalarMul(c[2], t2) + SkScalarMul(c[3], t3); cc[1] = c[4] + SkScalarMul(c[5], t) + SkScalarMul(c[6], t2) + SkScalarMul(c[7], t3); cc[2] = c[8] + SkScalarMul(c[9], t) + SkScalarMul(c[10], t2) + SkScalarMul(c[11], t3); cc[3] = c[12] + SkScalarMul(c[13], t) + SkScalarMul(c[14], t2) + SkScalarMul(c[15], t3); SkScalar a = SkScalarClampMax(SkScalarMul(cc[0], SkGetPackedA32(c0)) + SkScalarMul(cc[1], SkGetPackedA32(c1)) + SkScalarMul(cc[2], SkGetPackedA32(c2)) + SkScalarMul(cc[3], SkGetPackedA32(c3)), 255); SkScalar r = SkScalarMul(cc[0], SkGetPackedR32(c0)) + SkScalarMul(cc[1], SkGetPackedR32(c1)) + SkScalarMul(cc[2], SkGetPackedR32(c2)) + SkScalarMul(cc[3], SkGetPackedR32(c3)); SkScalar g = SkScalarMul(cc[0], SkGetPackedG32(c0)) + SkScalarMul(cc[1], SkGetPackedG32(c1)) + SkScalarMul(cc[2], SkGetPackedG32(c2)) + SkScalarMul(cc[3], SkGetPackedG32(c3)); SkScalar b = SkScalarMul(cc[0], SkGetPackedB32(c0)) + SkScalarMul(cc[1], SkGetPackedB32(c1)) + SkScalarMul(cc[2], SkGetPackedB32(c2)) + SkScalarMul(cc[3], SkGetPackedB32(c3)); return SkPackARGB32(SkScalarRoundToInt(a), SkScalarRoundToInt(SkScalarClampMax(r, a)), SkScalarRoundToInt(SkScalarClampMax(g, a)), SkScalarRoundToInt(SkScalarClampMax(b, a))); }
static inline bool S32A_D565_Blend_02(SkPMColor sc, uint16_t dc, U8CPU alpha) { unsigned dst_scale = 255 - SkMulDiv255Round(SkGetPackedA32(sc), alpha); unsigned dr = SkMulS16(SkGetPackedR32(sc), alpha) + SkMulS16(GetPackedR16As32(dc), dst_scale); unsigned dg = SkMulS16(SkGetPackedG32(sc), alpha) + SkMulS16(GetPackedG16As32(dc), dst_scale); unsigned db = SkMulS16(SkGetPackedB32(sc), alpha) + SkMulS16(GetPackedB16As32(dc), dst_scale); int rc = SkPack888ToRGB16(SkDiv255Round(dr), SkDiv255Round(dg), SkDiv255Round(db)); unsigned rr = SkGetPackedR16(rc); unsigned rg = SkGetPackedG16(rc); if (rr <= 31 && rg <= 63) { return true; } return false; }
static inline void D16_S32A_Blend_Pixel_helper(uint16_t* dst, SkPMColor sc, unsigned src_scale) { uint16_t dc = *dst; unsigned sa = SkGetPackedA32(sc); unsigned dr, dg, db; if (255 == sa) { dr = SkAlphaBlend(SkPacked32ToR16(sc), SkGetPackedR16(dc), src_scale); dg = SkAlphaBlend(SkPacked32ToG16(sc), SkGetPackedG16(dc), src_scale); db = SkAlphaBlend(SkPacked32ToB16(sc), SkGetPackedB16(dc), src_scale); } else { unsigned dst_scale = 255 - SkAlphaMul(sa, src_scale); dr = (SkPacked32ToR16(sc) * src_scale + SkGetPackedR16(dc) * dst_scale) >> 8; dg = (SkPacked32ToG16(sc) * src_scale + SkGetPackedG16(dc) * dst_scale) >> 8; db = (SkPacked32ToB16(sc) * src_scale + SkGetPackedB16(dc) * dst_scale) >> 8; } *dst = SkPackRGB16(dr, dg, db); }
virtual void filterSpan(const SkPMColor shader[], int count, SkPMColor result[]) { unsigned scaleR = SkAlpha255To256(SkColorGetR(fMul)); unsigned scaleG = SkAlpha255To256(SkColorGetG(fMul)); unsigned scaleB = SkAlpha255To256(SkColorGetB(fMul)); for (int i = 0; i < count; i++) { SkPMColor c = shader[i]; if (c) { unsigned a = SkGetPackedA32(c); unsigned r = SkAlphaMul(SkGetPackedR32(c), scaleR); unsigned g = SkAlphaMul(SkGetPackedG32(c), scaleG); unsigned b = SkAlphaMul(SkGetPackedB32(c), scaleB); c = SkPackARGB32(a, r, g, b); } result[i] = c; } }
bool check_gamma(uint32_t src, uint32_t dst, bool toSRGB, float error, uint32_t* expected) { bool result = true; uint32_t expectedColor = src & 0xff000000; // Alpha should always be exactly preserved. if ((src & 0xff000000) != (dst & 0xff000000)) { result = false; } // need to unpremul before we can perform srgb magic float invScale = 0; float alpha = SkGetPackedA32(src); if (alpha) { invScale = 255.0f / alpha; } for (int c = 0; c < 3; ++c) { float srcComponent = ((src & (0xff << (c * 8))) >> (c * 8)) * invScale; float lower = SkTMax(0.f, srcComponent - error); float upper = SkTMin(255.f, srcComponent + error); if (toSRGB) { lower = linear_to_srgb(lower / 255.f); upper = linear_to_srgb(upper / 255.f); } else { lower = srgb_to_linear(lower / 255.f); upper = srgb_to_linear(upper / 255.f); } lower *= alpha; upper *= alpha; SkASSERT(lower >= 0.f && lower <= 255.f); SkASSERT(upper >= 0.f && upper <= 255.f); uint8_t dstComponent = (dst & (0xff << (c * 8))) >> (c * 8); if (dstComponent < SkScalarFloorToInt(lower) || dstComponent > SkScalarCeilToInt(upper)) { result = false; } uint8_t expectedComponent = SkScalarRoundToInt((lower + upper) * 0.5f); expectedColor |= expectedComponent << (c * 8); } *expected = expectedColor; return result; }
static void extract_alpha(const SkMask& dst, const SkPMColor* srcRow, size_t srcRB) { int width = dst.fBounds.width(); int height = dst.fBounds.height(); int dstRB = dst.fRowBytes; uint8_t* dstRow = dst.fImage; for (int y = 0; y < height; ++y) { for (int x = 0; x < width; ++x) { dstRow[x] = SkGetPackedA32(srcRow[x]); } // zero any padding on each row for (int x = width; x < dstRB; ++x) { dstRow[x] = 0; } dstRow += dstRB; srcRow = (const SkPMColor*)((const char*)srcRow + srcRB); } }
virtual void xfer32(SK_RESTRICT SkPMColor dst[], const SK_RESTRICT SkPMColor src[], int count, const SK_RESTRICT SkAlpha aa[]) { SkASSERT(dst && src); if (count <= 0) { return; } if (NULL != aa) { return this->INHERITED::xfer32(dst, src, count, aa); } do { unsigned a = SkGetPackedA32(*src); *dst = SkAlphaMulQ(*dst, SkAlpha255To256(255 - a)); dst++; src++; } while (--count != 0); }
void SkARGB32_Blitter::blitV(int x, int y, int height, SkAlpha alpha) { if (alpha == 0 || fSrcA == 0) { return; } uint32_t* device = fDevice.getAddr32(x, y); uint32_t color = fPMColor; if (alpha != 255) { color = SkAlphaMulQ(color, SkAlpha255To256(alpha)); } unsigned dst_scale = 255 - SkGetPackedA32(color); uint32_t rowBytes = fDevice.rowBytes(); while (--height >= 0) { device[0] = color + SkAlphaMulQ(device[0], dst_scale); device = (uint32_t*)((char*)device + rowBytes); } }
virtual void filterSpan(const SkPMColor shader[], int count, SkPMColor result[]) { unsigned addR = SkColorGetR(fAdd); unsigned addG = SkColorGetG(fAdd); unsigned addB = SkColorGetB(fAdd); for (int i = 0; i < count; i++) { SkPMColor c = shader[i]; if (c) { unsigned a = SkGetPackedA32(c); unsigned scaleA = SkAlpha255To256(a); unsigned r = pin(SkGetPackedR32(c) + SkAlphaMul(addR, scaleA), a); unsigned g = pin(SkGetPackedG32(c) + SkAlphaMul(addG, scaleA), a); unsigned b = pin(SkGetPackedB32(c) + SkAlphaMul(addB, scaleA), a); c = SkPackARGB32(a, r, g, b); } result[i] = c; } }
void SkARGB32_Blitter::blitRect(int x, int y, int width, int height) { SkASSERT(x >= 0 && y >= 0 && x + width <= fDevice.width() && y + height <= fDevice.height()); if (fSrcA == 0) { return; } uint32_t* device = fDevice.getAddr32(x, y); uint32_t color = fPMColor; size_t rowBytes = fDevice.rowBytes(); if (255 == SkGetPackedA32(color)) { fColorRect32Proc(device, width, height, rowBytes, color); } else { while (--height >= 0) { fColor32Proc(device, device, width, color); device = (uint32_t*)((char*)device + rowBytes); } } }
static void preMultipliedBGRAtoRGBA(const void* pixels, int pixelCount, unsigned char* output) { static const SkUnPreMultiply::Scale* scale = SkUnPreMultiply::GetScaleTable(); const SkPMColor* input = static_cast<const SkPMColor*>(pixels); for (; pixelCount-- > 0; ++input) { const unsigned alpha = SkGetPackedA32(*input); if ((alpha != 0) && (alpha != 255)) { *output++ = SkUnPreMultiply::ApplyScale(scale[alpha], SkGetPackedR32(*input)); *output++ = SkUnPreMultiply::ApplyScale(scale[alpha], SkGetPackedG32(*input)); *output++ = SkUnPreMultiply::ApplyScale(scale[alpha], SkGetPackedB32(*input)); *output++ = alpha; } else { *output++ = SkGetPackedR32(*input); *output++ = SkGetPackedG32(*input); *output++ = SkGetPackedB32(*input); *output++ = alpha; } } }
SkColor SkPMColorToColor(SkPMColor pm) { if (!pm) return 0; unsigned a = SkGetPackedA32(pm); if (!a) { // A zero alpha value when there are non-zero R, G, or B channels is an // invalid premultiplied color (since all channels should have been // multiplied by 0 if a=0). SkASSERT(false); // In production, return 0 to protect against division by zero. return 0; } uint32_t scale = (255 << 16) / a; return SkColorSetARGB(a, InvScaleByte(SkGetPackedR32(pm), scale), InvScaleByte(SkGetPackedG32(pm), scale), InvScaleByte(SkGetPackedB32(pm), scale)); }
template <bool doSwapRB, AlphaVerb doAlpha> uint32_t convert32(uint32_t c) { if (doSwapRB) { c = SkSwizzle_RB(c); } // Lucky for us, in both RGBA and BGRA, the alpha component is always in the same place, so // we can perform premul or unpremul the same way without knowing the swizzles for RGB. switch (doAlpha) { case kNothing_AlphaVerb: // no change break; case kPremul_AlphaVerb: c = SkPreMultiplyARGB(SkGetPackedA32(c), SkGetPackedR32(c), SkGetPackedG32(c), SkGetPackedB32(c)); break; case kUnpremul_AlphaVerb: c = SkUnPreMultiply::UnPreMultiplyPreservingByteOrder(c); break; } return c; }
static void blit_lcd16_opaque(SkPMColor dst[], const uint16_t src[], SkPMColor color, int width) { int srcR = SkGetPackedR32(color); int srcG = SkGetPackedG32(color); int srcB = SkGetPackedB32(color); for (int i = 0; i < width; i++) { uint16_t mask = src[i]; if (0 == mask) { continue; } SkPMColor d = dst[i]; /* We want all of these in 5bits, hence the shifts in case one of them * (green) is 6bits. */ int maskR = SkGetPackedR16(mask) >> (SK_R16_BITS - 5); int maskG = SkGetPackedG16(mask) >> (SK_G16_BITS - 5); int maskB = SkGetPackedB16(mask) >> (SK_B16_BITS - 5); // Now upscale them to 0..256, so we can use SkAlphaBlend maskR = upscale31To32(maskR); maskG = upscale31To32(maskG); maskB = upscale31To32(maskB); int maskA = SkMax32(SkMax32(maskR, maskG), maskB); int dstA = SkGetPackedA32(d); int dstR = SkGetPackedR32(d); int dstG = SkGetPackedG32(d); int dstB = SkGetPackedB32(d); dst[i] = SkPackARGB32(blend32(0xFF, dstA, maskA), blend32(srcR, dstR, maskR), blend32(srcG, dstG, maskG), blend32(srcB, dstB, maskB)); } }
// Test that a colormatrix that "should" preserve opaquness actually does. DEF_TEST(ColorMatrixFilter, reporter) { const CFProc procs[] = { make_cf0, make_cf1, make_cf2, make_cf3, }; for (size_t i = 0; i < SK_ARRAY_COUNT(procs); ++i) { auto cf(procs[i]()); // generate all possible r,g,b triples for (int r = 0; r < 256; ++r) { for (int g = 0; g < 256; ++g) { SkPMColor storage[256]; for (int b = 0; b < 256; ++b) { storage[b] = SkPackARGB32(0xFF, r, g, b); } cf->filterSpan(storage, 256, storage); for (int b = 0; b < 256; ++b) { REPORTER_ASSERT(reporter, 0xFF == SkGetPackedA32(storage[b])); } } } } }
static void SkARGB32_Blit32(const SkPixmap& device, const SkMask& mask, const SkIRect& clip, SkPMColor srcColor) { U8CPU alpha = SkGetPackedA32(srcColor); unsigned flags = SkBlitRow::kSrcPixelAlpha_Flag32; if (alpha != 255) { flags |= SkBlitRow::kGlobalAlpha_Flag32; } SkBlitRow::Proc32 proc = SkBlitRow::Factory32(flags); int x = clip.fLeft; int y = clip.fTop; int width = clip.width(); int height = clip.height(); SkPMColor* dstRow = device.writable_addr32(x, y); const SkPMColor* srcRow = reinterpret_cast<const SkPMColor*>(mask.getAddr8(x, y)); do { proc(dstRow, srcRow, width, alpha); dstRow = (SkPMColor*)((char*)dstRow + device.rowBytes()); srcRow = (const SkPMColor*)((const char*)srcRow + mask.fRowBytes); } while (--height != 0); }
static void erode(const SkPMColor* src, SkPMColor* dst, int radius, int width, int height, int srcStride, int dstStride) { const int srcStrideX = direction == kX ? 1 : srcStride; const int dstStrideX = direction == kX ? 1 : dstStride; const int srcStrideY = direction == kX ? srcStride : 1; const int dstStrideY = direction == kX ? dstStride : 1; radius = SkMin32(radius, width - 1); const SkPMColor* upperSrc = src + radius * srcStrideX; for (int x = 0; x < width; ++x) { const SkPMColor* lp = src; const SkPMColor* up = upperSrc; SkPMColor* dptr = dst; for (int y = 0; y < height; ++y) { int minB = 255, minG = 255, minR = 255, minA = 255; for (const SkPMColor* p = lp; p <= up; p += srcStrideX) { int b = SkGetPackedB32(*p); int g = SkGetPackedG32(*p); int r = SkGetPackedR32(*p); int a = SkGetPackedA32(*p); if (b < minB) minB = b; if (g < minG) minG = g; if (r < minR) minR = r; if (a < minA) minA = a; } *dptr = SkPackARGB32(minA, minR, minG, minB); dptr += dstStrideY; lp += srcStrideY; up += srcStrideY; } if (x >= radius) src += srcStrideX; if (x + radius < width - 1) upperSrc += srcStrideX; dst += dstStrideX; } }
static void blit_lcd32_opaque(SkPMColor dst[], const uint32_t src[], SkPMColor color, int width) { int srcR = SkGetPackedR32(color); int srcG = SkGetPackedG32(color); int srcB = SkGetPackedB32(color); for (int i = 0; i < width; i++) { uint32_t mask = src[i]; if (0 == mask) { continue; } SkPMColor d = dst[i]; int maskR = SkGetPackedR32(mask); int maskG = SkGetPackedG32(mask); int maskB = SkGetPackedB32(mask); // Now upscale them to 0..256, so we can use SkAlphaBlend maskR = SkAlpha255To256(maskR); maskG = SkAlpha255To256(maskG); maskB = SkAlpha255To256(maskB); int maskA = SkMax32(SkMax32(maskR, maskG), maskB); int dstA = SkGetPackedA32(d); int dstR = SkGetPackedR32(d); int dstG = SkGetPackedG32(d); int dstB = SkGetPackedB32(d); dst[i] = SkPackARGB32(SkAlphaBlend(0xFF, dstA, maskA), SkAlphaBlend(srcR, dstR, maskR), SkAlphaBlend(srcG, dstG, maskG), SkAlphaBlend(srcB, dstB, maskB)); } }
static void ARGB_8888_To_RGBA(const uint8_t* in, uint8_t* rgb, int width, const SkPMColor*) { const uint32_t* SK_RESTRICT src = (const uint32_t*)in; const SkUnPreMultiply::Scale* SK_RESTRICT table = SkUnPreMultiply::GetScaleTable(); for (int i = 0; i < width; ++i) { const uint32_t c = *src++; uint8_t a = SkGetPackedA32(c); uint8_t r = SkGetPackedR32(c); uint8_t g = SkGetPackedG32(c); uint8_t b = SkGetPackedB32(c); if (0 != a && 255 != a) { SkUnPreMultiply::Scale scale = table[a]; r = SkUnPreMultiply::ApplyScale(scale, r); g = SkUnPreMultiply::ApplyScale(scale, g); b = SkUnPreMultiply::ApplyScale(scale, b); } rgb[0] = r; rgb[1] = g; rgb[2] = b; rgb[3] = a; rgb += 4; } }
static void dilate(const SkPMColor* src, SkPMColor* dst, int radius, int width, int height, int srcStride, int dstStride) { const int srcStrideX = direction == kX ? 1 : srcStride; const int dstStrideX = direction == kX ? 1 : dstStride; const int srcStrideY = direction == kX ? srcStride : 1; const int dstStrideY = direction == kX ? dstStride : 1; radius = SkMin32(radius, width - 1); const SkPMColor* upperSrc = src + radius * srcStrideX; for (int x = 0; x < width; ++x) { const SkPMColor* lp = src; const SkPMColor* up = upperSrc; SkPMColor* dptr = dst; for (int y = 0; y < height; ++y) { int maxB = 0, maxG = 0, maxR = 0, maxA = 0; for (const SkPMColor* p = lp; p <= up; p += srcStrideX) { int b = SkGetPackedB32(*p); int g = SkGetPackedG32(*p); int r = SkGetPackedR32(*p); int a = SkGetPackedA32(*p); if (b > maxB) maxB = b; if (g > maxG) maxG = g; if (r > maxR) maxR = r; if (a > maxA) maxA = a; } *dptr = SkPackARGB32(maxA, maxR, maxG, maxB); dptr += dstStrideY; lp += srcStrideY; up += srcStrideY; } if (x >= radius) src += srcStrideX; if (x + radius < width - 1) upperSrc += srcStrideX; dst += dstStrideX; } }
void SkBlitLCD16OpaqueRow_neon(SkPMColor dst[], const uint16_t src[], SkColor color, int width, SkPMColor opaqueDst) { int colR = SkColorGetR(color); int colG = SkColorGetG(color); int colB = SkColorGetB(color); uint8x8_t vcolR, vcolG, vcolB; uint8x8_t vopqDstA, vopqDstR, vopqDstG, vopqDstB; if (width >= 8) { vcolR = vdup_n_u8(colR); vcolG = vdup_n_u8(colG); vcolB = vdup_n_u8(colB); vopqDstA = vdup_n_u8(SkGetPackedA32(opaqueDst)); vopqDstR = vdup_n_u8(SkGetPackedR32(opaqueDst)); vopqDstG = vdup_n_u8(SkGetPackedG32(opaqueDst)); vopqDstB = vdup_n_u8(SkGetPackedB32(opaqueDst)); } while (width >= 8) { uint8x8x4_t vdst; uint16x8_t vmask; uint16x8_t vmaskR, vmaskG, vmaskB; uint8x8_t vsel_trans, vsel_opq; vdst = vld4_u8((uint8_t*)dst); vmask = vld1q_u16(src); // Prepare compare masks vsel_trans = vmovn_u16(vceqq_u16(vmask, vdupq_n_u16(0))); vsel_opq = vmovn_u16(vceqq_u16(vmask, vdupq_n_u16(0xFFFF))); // Get all the color masks on 5 bits vmaskR = vshrq_n_u16(vmask, SK_R16_SHIFT); vmaskG = vshrq_n_u16(vshlq_n_u16(vmask, SK_R16_BITS), SK_B16_BITS + SK_R16_BITS + 1); vmaskB = vmask & vdupq_n_u16(SK_B16_MASK); // Upscale to 0..32 vmaskR = vmaskR + vshrq_n_u16(vmaskR, 4); vmaskG = vmaskG + vshrq_n_u16(vmaskG, 4); vmaskB = vmaskB + vshrq_n_u16(vmaskB, 4); vdst.val[NEON_A] = vbsl_u8(vsel_trans, vdst.val[NEON_A], vdup_n_u8(0xFF)); vdst.val[NEON_A] = vbsl_u8(vsel_opq, vopqDstA, vdst.val[NEON_A]); vdst.val[NEON_R] = SkBlend32_neon8(vcolR, vdst.val[NEON_R], vmaskR); vdst.val[NEON_G] = SkBlend32_neon8(vcolG, vdst.val[NEON_G], vmaskG); vdst.val[NEON_B] = SkBlend32_neon8(vcolB, vdst.val[NEON_B], vmaskB); vdst.val[NEON_R] = vbsl_u8(vsel_opq, vopqDstR, vdst.val[NEON_R]); vdst.val[NEON_G] = vbsl_u8(vsel_opq, vopqDstG, vdst.val[NEON_G]); vdst.val[NEON_B] = vbsl_u8(vsel_opq, vopqDstB, vdst.val[NEON_B]); vst4_u8((uint8_t*)dst, vdst); dst += 8; src += 8; width -= 8; } // Leftovers for (int i = 0; i < width; i++) { dst[i] = SkBlendLCD16Opaque(colR, colG, colB, dst[i], src[i], opaqueDst); } }
static bool require_0(SkPMColor src) { return SkGetPackedA32(src) == 0; }
static bool require_255(SkPMColor src) { return SkGetPackedA32(src) == 0xFF; }