static void ARGB_8888_To_RGB(const uint8_t* in, uint8_t* rgb, int width, const SkPMColor*) { const uint32_t* SK_RESTRICT src = (const uint32_t*)in; for (int i = 0; i < width; ++i) { const uint32_t c = *src++; rgb[0] = SkGetPackedR32(c); rgb[1] = SkGetPackedG32(c); rgb[2] = SkGetPackedB32(c); rgb += 3; } }
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; } } }
static void ToColor_SI8_Opaque(SkColor dst[], const void* src, int width, SkColorTable* ctable) { SkASSERT(width > 0); const uint8_t* s = (const uint8_t*)src; const SkPMColor* colors = ctable->lockColors(); do { SkPMColor c = colors[*s++]; *dst++ = SkColorSetRGB(SkGetPackedR32(c), SkGetPackedG32(c), SkGetPackedB32(c)); } while (--width != 0); ctable->unlockColors(false); }
// returns 0..255 static unsigned color_dist32(SkPMColor c, U8CPU r, U8CPU g, U8CPU b) { SkASSERT(r <= 0xFF); SkASSERT(g <= 0xFF); SkASSERT(b <= 0xFF); unsigned dr = SkAbs32(SkGetPackedR32(c) - r); unsigned dg = SkAbs32(SkGetPackedG32(c) - g); unsigned db = SkAbs32(SkGetPackedB32(c) - b); return SkMax32(dr, SkMax32(dg, db)); }
void SkPowerMode::xfer16(uint16_t dst[], const SkPMColor src[], int count, const SkAlpha aa[]) { for (int i = 0; i < count; i++) { SkPMColor c = src[i]; int r = SkGetPackedR32(c); int g = SkGetPackedG32(c); int b = SkGetPackedB32(c); r = fTable[r]; g = fTable[g]; b = fTable[b]; dst[i] = SkPack888ToRGB16(r, g, b); } }
static void rgb2yuv_32(uint8_t dst[], SkPMColor c) { int r = SkGetPackedR32(c); int g = SkGetPackedG32(c); int b = SkGetPackedB32(c); int y = ( CYR*r + CYG*g + CYB*b ) >> CSHIFT; int u = ( CUR*r + CUG*g + CUB*b ) >> CSHIFT; int v = ( CVR*r + CVG*g + CVB*b ) >> CSHIFT; dst[0] = SkToU8(y); dst[1] = SkToU8(u + 128); dst[2] = SkToU8(v + 128); }
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 SkPMColor unpremul_pm(SkPMColor c) { const U8CPU a = SkGetPackedA32(c); if (0 == a) { return 0; } else if (0xFF == a) { return c; } const unsigned scale = SkUnPreMultiply::GetScale(a); return SkPackARGB32NoCheck(a, SkUnPreMultiply::ApplyScale(scale, SkGetPackedR32(c)), SkUnPreMultiply::ApplyScale(scale, SkGetPackedG32(c)), SkUnPreMultiply::ApplyScale(scale, SkGetPackedB32(c))); }
static void apply_gamma(const SkBitmap& bm) { return; // below is our experiment for sRGB correction bm.lockPixels(); for (int y = 0; y < bm.height(); ++y) { for (int x = 0; x < bm.width(); ++x) { SkPMColor c = *bm.getAddr32(x, y); unsigned r = gamma(SkGetPackedR32(c)); unsigned g = gamma(SkGetPackedG32(c)); unsigned b = gamma(SkGetPackedB32(c)); *bm.getAddr32(x, y) = SkPackARGB32(0xFF, r, g, b); } } }
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)); } }
static inline bool S32A_D565_Blend_1(SkPMColor sc, uint16_t dc, U8CPU alpha) { unsigned dst_scale = 255 - SkMulDiv255Round(SkGetPackedA32(sc), alpha); unsigned dr = (SkMulS16(SkGetPackedR32(sc), alpha) >> 3) + SkMulS16(SkGetPackedR16(dc), dst_scale); unsigned dg = (SkMulS16(SkGetPackedG32(sc), alpha) >> 2) + SkMulS16(SkGetPackedG16(dc), dst_scale); unsigned rr = SkDiv255Round(dr); unsigned rg = SkDiv255Round(dg); if (rr <= 31 && rg <= 63) { return true; } return false; }
static int similarBits(const SkBitmap& gr, const SkBitmap& sk) { const int kRowCount = 3; const int kThreshold = 3; int width = SkTMin(gr.width(), sk.width()); if (width < kRowCount) { return true; } int height = SkTMin(gr.height(), sk.height()); if (height < kRowCount) { return true; } int errorTotal = 0; SkTArray<char, true> errorRows; errorRows.push_back_n(width * kRowCount); SkAutoLockPixels autoGr(gr); SkAutoLockPixels autoSk(sk); char* base = &errorRows[0]; for (int y = 0; y < height; ++y) { SkPMColor* grRow = gr.getAddr32(0, y); SkPMColor* skRow = sk.getAddr32(0, y); char* cOut = &errorRows[(y % kRowCount) * width]; for (int x = 0; x < width; ++x) { SkPMColor grColor = grRow[x]; SkPMColor skColor = skRow[x]; int dr = SkGetPackedR32(grColor) - SkGetPackedR32(skColor); int dg = SkGetPackedG32(grColor) - SkGetPackedG32(skColor); int db = SkGetPackedB32(grColor) - SkGetPackedB32(skColor); int error = SkTMax(SkAbs32(dr), SkTMax(SkAbs32(dg), SkAbs32(db))); if ((cOut[x] = error >= kThreshold) && x >= 2 && base[x - 2] && base[width + x - 2] && base[width * 2 + x - 2] && base[x - 1] && base[width + x - 1] && base[width * 2 + x - 1] && base[x - 0] && base[width + x - 0] && base[width * 2 + x - 0]) { errorTotal += error; } } } return errorTotal; }
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; }
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)); } }
void SkNormalMapSourceImpl::Provider::fillScanLine(int x, int y, SkPoint3 output[], int count) const { SkPMColor tmpNormalColors[BUFFER_MAX]; do { int n = SkTMin(count, BUFFER_MAX); fMapContext->shadeSpan(x, y, tmpNormalColors, n); for (int i = 0; i < n; i++) { SkPoint3 tempNorm; tempNorm.set(SkIntToScalar(SkGetPackedR32(tmpNormalColors[i])) - 127.0f, SkIntToScalar(SkGetPackedG32(tmpNormalColors[i])) - 127.0f, SkIntToScalar(SkGetPackedB32(tmpNormalColors[i])) - 127.0f); tempNorm.normalize(); if (!SkScalarNearlyEqual(SkScalarAbs(tempNorm.fZ), 1.0f)) { SkVector transformed = fSource.fInvCTM.mapVector(tempNorm.fX, tempNorm.fY); // Normalizing the transformed X and Y, while keeping constant both Z and the // vector's angle in the XY plane. This maintains the "slope" for the surface while // appropriately rotating the normal for any anisotropic scaling that occurs. // Here, we call scaling factor the number that must divide the transformed X and Y // so that the normal's length remains equal to 1. SkScalar scalingFactorSquared = (SkScalarSquare(transformed.fX) + SkScalarSquare(transformed.fY)) / (1.0f - SkScalarSquare(tempNorm.fZ)); SkScalar invScalingFactor = SkScalarInvert(SkScalarSqrt(scalingFactorSquared)); output[i].fX = transformed.fX * invScalingFactor; output[i].fY = transformed.fY * invScalingFactor; output[i].fZ = tempNorm.fZ; } else { output[i] = {0.0f, 0.0f, tempNorm.fZ}; output[i].normalize(); } SkASSERT(SkScalarNearlyEqual(output[i].length(), 1.0f)); } output += n; x += n; count -= n; } while (count > 0); }
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; }
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; } }
void SkLumaColorFilter::filterSpan(const SkPMColor src[], int count, SkPMColor dst[]) const { for (int i = 0; i < count; ++i) { SkPMColor c = src[i]; /* * While LuminanceToAlpha is defined to operate on un-premultiplied * inputs, due to the final alpha scaling it can be computed based on * premultipled components: * * LumA = (k1 * r / a + k2 * g / a + k3 * b / a) * a * LumA = (k1 * r + k2 * g + k3 * b) */ unsigned luma = SkComputeLuminance(SkGetPackedR32(c), SkGetPackedG32(c), SkGetPackedB32(c)); dst[i] = SkPackARGB32(luma, 0, 0, 0); } }
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 SkMatrixConvolutionImageFilter::filterPixels(const SkBitmap& src, SkBitmap* result, const SkIRect& r, const SkIRect& bounds) const { SkIRect rect(r); if (!rect.intersect(bounds)) { return; } for (int y = rect.fTop; y < rect.fBottom; ++y) { SkPMColor* dptr = result->getAddr32(rect.fLeft - bounds.fLeft, y - bounds.fTop); for (int x = rect.fLeft; x < rect.fRight; ++x) { SkScalar sumA = 0, sumR = 0, sumG = 0, sumB = 0; for (int cy = 0; cy < fKernelSize.fHeight; cy++) { for (int cx = 0; cx < fKernelSize.fWidth; cx++) { SkPMColor s = PixelFetcher::fetch(src, x + cx - fKernelOffset.fX, y + cy - fKernelOffset.fY, bounds); SkScalar k = fKernel[cy * fKernelSize.fWidth + cx]; if (convolveAlpha) { sumA += SkScalarMul(SkIntToScalar(SkGetPackedA32(s)), k); } sumR += SkScalarMul(SkIntToScalar(SkGetPackedR32(s)), k); sumG += SkScalarMul(SkIntToScalar(SkGetPackedG32(s)), k); sumB += SkScalarMul(SkIntToScalar(SkGetPackedB32(s)), k); } } int a = convolveAlpha ? SkClampMax(SkScalarFloorToInt(SkScalarMul(sumA, fGain) + fBias), 255) : 255; int r = SkClampMax(SkScalarFloorToInt(SkScalarMul(sumR, fGain) + fBias), a); int g = SkClampMax(SkScalarFloorToInt(SkScalarMul(sumG, fGain) + fBias), a); int b = SkClampMax(SkScalarFloorToInt(SkScalarMul(sumB, fGain) + fBias), a); if (!convolveAlpha) { a = SkGetPackedA32(PixelFetcher::fetch(src, x, y, bounds)); *dptr++ = SkPreMultiplyARGB(a, r, g, b); } else { *dptr++ = SkPackARGB32(a, r, g, b); } } } }
static SkPMColor blur_pixel(const SkBitmap& bm, int x, int y, float* kernel, int wh) { SkASSERT(wh & 0x1); int halfFilterSize = (wh-1)/2; float r = 0.0f, g = 0.0f, b = 0.0f; for (int yOff = 0; yOff < wh; ++yOff) { int ySamp = y + yOff - halfFilterSize; if (ySamp < 0) { ySamp = 0; } else if (ySamp > bm.height()-1) { ySamp = bm.height()-1; } for (int xOff = 0; xOff < wh; ++xOff) { int xSamp = x + xOff - halfFilterSize; if (xSamp < 0) { xSamp = 0; } else if (xSamp > bm.width()-1) { xSamp = bm.width()-1; } float filter = kernel[yOff*wh + xOff]; SkPMColor c = *bm.getAddr32(xSamp, ySamp); r += SkGetPackedR32(c) * filter; g += SkGetPackedG32(c) * filter; b += SkGetPackedB32(c) * filter; } } U8CPU r8, g8, b8; r8 = (U8CPU) (r+0.5f); g8 = (U8CPU) (g+0.5f); b8 = (U8CPU) (b+0.5f); return SkPackARGB32(255, r8, g8, b8); }
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 SkPDFArray* make_indexed_color_space(SkColorTable* table) { SkPDFArray* result = new SkPDFArray(); result->reserve(4); result->appendName("Indexed"); result->appendName("DeviceRGB"); result->appendInt(table->count() - 1); // Potentially, this could be represented in fewer bytes with a stream. // Max size as a string is 1.5k. SkString index; for (int i = 0; i < table->count(); i++) { char buf[3]; SkColor color = SkUnPreMultiply::PMColorToColor((*table)[i]); buf[0] = SkGetPackedR32(color); buf[1] = SkGetPackedG32(color); buf[2] = SkGetPackedB32(color); index.append(buf, 3); } result->append(new SkPDFString(index))->unref(); return result; }
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 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 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); } }
/** * Test decoding an image in premultiplied mode and unpremultiplied mode and compare * them. */ static void compare_unpremul(skiatest::Reporter* reporter, const SkString& filename) { // Decode a resource: SkBitmap bm8888; SkBitmap bm8888Unpremul; SkFILEStream stream(filename.c_str()); SkImageDecoder::Format format = SkImageDecoder::GetStreamFormat(&stream); if (skip_image_format(format)) { return; } SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(&stream)); if (NULL == decoder.get()) { SkDebugf("couldn't decode %s\n", filename.c_str()); return; } bool success = decoder->decode(&stream, &bm8888, SkBitmap::kARGB_8888_Config, SkImageDecoder::kDecodePixels_Mode); if (!success) { return; } success = stream.rewind(); REPORTER_ASSERT(reporter, success); if (!success) { return; } decoder->setRequireUnpremultipliedColors(true); success = decoder->decode(&stream, &bm8888Unpremul, SkBitmap::kARGB_8888_Config, SkImageDecoder::kDecodePixels_Mode); if (!success) { return; } bool dimensionsMatch = bm8888.width() == bm8888Unpremul.width() && bm8888.height() == bm8888Unpremul.height(); REPORTER_ASSERT(reporter, dimensionsMatch); if (!dimensionsMatch) { return; } // Only do the comparison if the two bitmaps are both 8888. if (bm8888.config() != SkBitmap::kARGB_8888_Config || bm8888Unpremul.config() != SkBitmap::kARGB_8888_Config) { return; } // Now compare the two bitmaps. for (int i = 0; i < bm8888.width(); ++i) { for (int j = 0; j < bm8888.height(); ++j) { // "c0" is the color of the premultiplied bitmap at (i, j). const SkPMColor c0 = *bm8888.getAddr32(i, j); // "c1" is the result of premultiplying the color of the unpremultiplied // bitmap at (i, j). const SkPMColor c1 = premultiply_unpmcolor(*bm8888Unpremul.getAddr32(i, j)); // Compute the difference for each component. int da = SkAbs32(SkGetPackedA32(c0) - SkGetPackedA32(c1)); int dr = SkAbs32(SkGetPackedR32(c0) - SkGetPackedR32(c1)); int dg = SkAbs32(SkGetPackedG32(c0) - SkGetPackedG32(c1)); int db = SkAbs32(SkGetPackedB32(c0) - SkGetPackedB32(c1)); // Alpha component must be exactly the same. REPORTER_ASSERT(reporter, 0 == da); // Color components may not match exactly due to rounding error. REPORTER_ASSERT(reporter, dr <= 1); REPORTER_ASSERT(reporter, dg <= 1); REPORTER_ASSERT(reporter, db <= 1); } } }
// static SkBitmap ImageOperations::ResizeSubpixel(const SkBitmap& source, int dest_width, int dest_height, const SkIRect& dest_subset) { // Currently only works on Linux/BSD because these are the only platforms // where SkFontHost::GetSubpixelOrder is defined. #if defined(XP_UNIX) // Understand the display. const SkFontHost::LCDOrder order = SkFontHost::GetSubpixelOrder(); const SkFontHost::LCDOrientation orientation = SkFontHost::GetSubpixelOrientation(); // Decide on which dimension, if any, to deploy subpixel rendering. int w = 1; int h = 1; switch (orientation) { case SkFontHost::kHorizontal_LCDOrientation: w = dest_width < source.width() ? 3 : 1; break; case SkFontHost::kVertical_LCDOrientation: h = dest_height < source.height() ? 3 : 1; break; } // Resize the image. const int width = dest_width * w; const int height = dest_height * h; SkIRect subset = { dest_subset.fLeft, dest_subset.fTop, dest_subset.fLeft + dest_subset.width() * w, dest_subset.fTop + dest_subset.height() * h }; SkBitmap img = ResizeBasic(source, ImageOperations::RESIZE_LANCZOS3, width, height, subset); const int row_words = img.rowBytes() / 4; if (w == 1 && h == 1) return img; // Render into subpixels. SkBitmap result; SkImageInfo info = SkImageInfo::Make(dest_subset.width(), dest_subset.height(), kBGRA_8888_SkColorType, kPremul_SkAlphaType); result.allocPixels(info); if (!result.readyToDraw()) return img; SkAutoLockPixels locker(img); if (!img.readyToDraw()) return img; uint32_t* src_row = img.getAddr32(0, 0); uint32_t* dst_row = result.getAddr32(0, 0); for (int y = 0; y < dest_subset.height(); y++) { uint32_t* src = src_row; uint32_t* dst = dst_row; for (int x = 0; x < dest_subset.width(); x++, src += w, dst++) { uint8_t r = 0, g = 0, b = 0, a = 0; switch (order) { case SkFontHost::kRGB_LCDOrder: switch (orientation) { case SkFontHost::kHorizontal_LCDOrientation: r = SkGetPackedR32(src[0]); g = SkGetPackedG32(src[1]); b = SkGetPackedB32(src[2]); a = SkGetPackedA32(src[1]); break; case SkFontHost::kVertical_LCDOrientation: r = SkGetPackedR32(src[0 * row_words]); g = SkGetPackedG32(src[1 * row_words]); b = SkGetPackedB32(src[2 * row_words]); a = SkGetPackedA32(src[1 * row_words]); break; } break; case SkFontHost::kBGR_LCDOrder: switch (orientation) { case SkFontHost::kHorizontal_LCDOrientation: b = SkGetPackedB32(src[0]); g = SkGetPackedG32(src[1]); r = SkGetPackedR32(src[2]); a = SkGetPackedA32(src[1]); break; case SkFontHost::kVertical_LCDOrientation: b = SkGetPackedB32(src[0 * row_words]); g = SkGetPackedG32(src[1 * row_words]); r = SkGetPackedR32(src[2 * row_words]); a = SkGetPackedA32(src[1 * row_words]); break; } break; case SkFontHost::kNONE_LCDOrder: break; } // Premultiplied alpha is very fragile. a = a > r ? a : r; a = a > g ? a : g; a = a > b ? a : b; *dst = SkPackARGB32(a, r, g, b); } src_row += h * row_words; dst_row += result.rowBytes() / 4; } result.setAlphaType(img.alphaType()); return result; #else return SkBitmap(); #endif // OS_POSIX && !OS_MACOSX && !defined(OS_ANDROID) }