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; } }
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; } }
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 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) }
/** * 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); } } }
void SkPixelXorXfermode::flatten(SkWriteBuffer& wb) const { wb.writeColor(SkColorSetRGB(SkGetPackedR32(fOpColor), SkGetPackedG32(fOpColor), SkGetPackedB32(fOpColor))); }
PassRefPtr<ByteArray> getImageData(const IntRect& rect, SkDevice& srcDevice, const IntSize& size) { RefPtr<ByteArray> result = ByteArray::create(rect.width() * rect.height() * 4); SkBitmap::Config srcConfig = srcDevice.accessBitmap(false).config(); if (srcConfig == SkBitmap::kNo_Config) { // This is an empty SkBitmap that could not be configured. ASSERT(!size.width() || !size.height()); return result.release(); } unsigned char* data = result->data(); if (rect.x() < 0 || rect.y() < 0 || rect.maxX() > size.width() || rect.maxY() > size.height()) memset(data, 0, result->length()); int originX = rect.x(); int destX = 0; if (originX < 0) { destX = -originX; originX = 0; } int endX = rect.maxX(); if (endX > size.width()) endX = size.width(); int numColumns = endX - originX; if (numColumns <= 0) return result.release(); int originY = rect.y(); int destY = 0; if (originY < 0) { destY = -originY; originY = 0; } int endY = rect.maxY(); if (endY > size.height()) endY = size.height(); int numRows = endY - originY; if (numRows <= 0) return result.release(); ASSERT(srcConfig == SkBitmap::kARGB_8888_Config); unsigned destBytesPerRow = 4 * rect.width(); SkBitmap srcBitmap; srcDevice.readPixels(SkIRect::MakeXYWH(originX, originY, numColumns, numRows), &srcBitmap); unsigned char* destRow = data + destY * destBytesPerRow + destX * 4; // Do conversion of byte order and alpha divide (if necessary) for (int y = 0; y < numRows; ++y) { SkPMColor* srcBitmapRow = srcBitmap.getAddr32(0, y); for (int x = 0; x < numColumns; ++x) { SkPMColor srcPMColor = srcBitmapRow[x]; unsigned char* destPixel = &destRow[x * 4]; if (multiplied == Unmultiplied) { unsigned char a = SkGetPackedA32(srcPMColor); destPixel[0] = a ? SkGetPackedR32(srcPMColor) * 255 / a : 0; destPixel[1] = a ? SkGetPackedG32(srcPMColor) * 255 / a : 0; destPixel[2] = a ? SkGetPackedB32(srcPMColor) * 255 / a : 0; destPixel[3] = a; } else { // Input and output are both pre-multiplied, we just need to re-arrange the // bytes from the bitmap format to RGBA. destPixel[0] = SkGetPackedR32(srcPMColor); destPixel[1] = SkGetPackedG32(srcPMColor); destPixel[2] = SkGetPackedB32(srcPMColor); destPixel[3] = SkGetPackedA32(srcPMColor); } } destRow += destBytesPerRow; } return result.release(); }
PassRefPtr<ImageData> getImageData(const IntRect& rect, const SkBitmap& bitmap, const IntSize& size) { RefPtr<ImageData> result = ImageData::create(rect.width(), rect.height()); if (bitmap.config() == SkBitmap::kNo_Config) { // This is an empty SkBitmap that could not be configured. ASSERT(!size.width() || !size.height()); return result; } unsigned char* data = result->data()->data()->data(); if (rect.x() < 0 || rect.y() < 0 || (rect.x() + rect.width()) > size.width() || (rect.y() + rect.height()) > size.height()) memset(data, 0, result->data()->length()); int originX = rect.x(); int destX = 0; if (originX < 0) { destX = -originX; originX = 0; } int endX = rect.x() + rect.width(); if (endX > size.width()) endX = size.width(); int numColumns = endX - originX; int originY = rect.y(); int destY = 0; if (originY < 0) { destY = -originY; originY = 0; } int endY = rect.y() + rect.height(); if (endY > size.height()) endY = size.height(); int numRows = endY - originY; ASSERT(bitmap.config() == SkBitmap::kARGB_8888_Config); SkAutoLockPixels bitmapLock(bitmap); unsigned destBytesPerRow = 4 * rect.width(); unsigned char* destRow = data + destY * destBytesPerRow + destX * 4; for (int y = 0; y < numRows; ++y) { uint32_t* srcRow = bitmap.getAddr32(originX, originY + y); for (int x = 0; x < numColumns; ++x) { unsigned char* destPixel = &destRow[x * 4]; if (multiplied == Unmultiplied) { SkColor color = srcRow[x]; unsigned a = SkColorGetA(color); destPixel[0] = a ? SkColorGetR(color) * 255 / a : 0; destPixel[1] = a ? SkColorGetG(color) * 255 / a : 0; destPixel[2] = a ? SkColorGetB(color) * 255 / a : 0; destPixel[3] = a; } else { // Input and output are both pre-multiplied, we just need to re-arrange the // bytes from the bitmap format to RGBA. destPixel[0] = SkGetPackedR32(srcRow[x]); destPixel[1] = SkGetPackedG32(srcRow[x]); destPixel[2] = SkGetPackedB32(srcRow[x]); destPixel[3] = SkGetPackedA32(srcRow[x]); } } destRow += destBytesPerRow; } return result; }
// unpremultiply and extract R, G, B components. static void pmcolor_to_rgb24(SkPMColor pmColor, uint8_t* rgb) { uint32_t s = SkUnPreMultiply::GetScale(SkGetPackedA32(pmColor)); rgb[0] = SkUnPreMultiply::ApplyScale(s, SkGetPackedR32(pmColor)); rgb[1] = SkUnPreMultiply::ApplyScale(s, SkGetPackedG32(pmColor)); rgb[2] = SkUnPreMultiply::ApplyScale(s, SkGetPackedB32(pmColor)); }
static inline SkPMColor luma_proc(const SkPMColor a, const SkPMColor b) { unsigned luma = SkComputeLuminance(SkGetPackedR32(b), SkGetPackedG32(b), SkGetPackedB32(b)); return SkAlphaMulQ(a, SkAlpha255To256(luma)); }