diff options
author | vandebo@chromium.org <vandebo@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81> | 2013-10-23 20:17:29 +0000 |
---|---|---|
committer | vandebo@chromium.org <vandebo@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81> | 2013-10-23 20:17:29 +0000 |
commit | e66d37606753f856492355a3bd6afe9a455e81ab (patch) | |
tree | 1eaefe93de13bdfd912030a271cc200dc05cc38a /src/pdf | |
parent | 0794dcd2c04be9d4e04dfb3238c761c47ac0af3f (diff) |
[PDF] Add unpremultiply support and a GM (try4)
This is a resubmit of https://codereview.chromium.org/22831039 which was
reverted because it causes issues on Windows (really, release builds).
BUG=chromium:175548
R=edisonn@google.com
Review URL: https://codereview.chromium.org/33493003
git-svn-id: http://skia.googlecode.com/svn/trunk@11928 2bbb7eff-a529-9590-31e7-b0007b416f81
Diffstat (limited to 'src/pdf')
-rw-r--r-- | src/pdf/SkPDFImage.cpp | 164 |
1 files changed, 162 insertions, 2 deletions
diff --git a/src/pdf/SkPDFImage.cpp b/src/pdf/SkPDFImage.cpp index 19ad79e4a5..ed204550e7 100644 --- a/src/pdf/SkPDFImage.cpp +++ b/src/pdf/SkPDFImage.cpp @@ -336,6 +336,157 @@ static SkPDFArray* make_indexed_color_space(SkColorTable* table) { return result; } +/** + * Removes the alpha component of an ARGB color (including unpremultiply) while + * keeping the output in the same format as the input. + */ +static uint32_t remove_alpha_argb8888(uint32_t pmColor) { + SkColor color = SkUnPreMultiply::PMColorToColor(pmColor); + return SkPackARGB32NoCheck(SK_AlphaOPAQUE, + SkColorGetR(color), + SkColorGetG(color), + SkColorGetB(color)); +} + +static uint16_t remove_alpha_argb4444(uint16_t pmColor) { + return SkPixel32ToPixel4444( + remove_alpha_argb8888(SkPixel4444ToPixel32(pmColor))); +} + +static uint32_t get_argb8888_neighbor_avg_color(const SkBitmap& bitmap, + int xOrig, int yOrig) { + uint8_t count = 0; + uint16_t r = 0; + uint16_t g = 0; + uint16_t b = 0; + + for (int y = yOrig - 1; y <= yOrig + 1; y++) { + if (y < 0 || y >= bitmap.height()) { + continue; + } + uint32_t* src = bitmap.getAddr32(0, y); + for (int x = xOrig - 1; x <= xOrig + 1; x++) { + if (x < 0 || x >= bitmap.width()) { + continue; + } + if (SkGetPackedA32(src[x]) != SK_AlphaTRANSPARENT) { + uint32_t color = remove_alpha_argb8888(src[x]); + r += SkGetPackedR32(color); + g += SkGetPackedG32(color); + b += SkGetPackedB32(color); + count++; + } + } + } + + if (count == 0) { + return SkPackARGB32NoCheck(SK_AlphaOPAQUE, 0, 0, 0); + } else { + return SkPackARGB32NoCheck(SK_AlphaOPAQUE, + r / count, g / count, b / count); + } +} + +static uint16_t get_argb4444_neighbor_avg_color(const SkBitmap& bitmap, + int xOrig, int yOrig) { + uint8_t count = 0; + uint8_t r = 0; + uint8_t g = 0; + uint8_t b = 0; + + for (int y = yOrig - 1; y <= yOrig + 1; y++) { + if (y < 0 || y >= bitmap.height()) { + continue; + } + uint16_t* src = bitmap.getAddr16(0, y); + for (int x = xOrig - 1; x <= xOrig + 1; x++) { + if (x < 0 || x >= bitmap.width()) { + continue; + } + if ((SkGetPackedA4444(src[x]) & 0x0F) != SK_AlphaTRANSPARENT) { + uint16_t color = remove_alpha_argb4444(src[x]); + r += SkGetPackedR4444(color); + g += SkGetPackedG4444(color); + b += SkGetPackedB4444(color); + count++; + } + } + } + + if (count == 0) { + return SkPackARGB4444(SK_AlphaOPAQUE & 0x0F, 0, 0, 0); + } else { + return SkPackARGB4444(SK_AlphaOPAQUE & 0x0F, + r / count, g / count, b / count); + } +} + +static SkBitmap unpremultiply_bitmap(const SkBitmap& bitmap, + const SkIRect& srcRect) { + SkBitmap outBitmap; + outBitmap.setConfig(bitmap.config(), srcRect.width(), srcRect.height()); + outBitmap.allocPixels(); + size_t dstRow = 0; + + outBitmap.lockPixels(); + bitmap.lockPixels(); + switch (bitmap.config()) { + case SkBitmap::kARGB_4444_Config: { + for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { + uint16_t* dst = outBitmap.getAddr16(0, dstRow); + uint16_t* src = bitmap.getAddr16(0, y); + for (int x = srcRect.fLeft; x < srcRect.fRight; x++) { + uint8_t a = SkGetPackedA4444(src[x]); + // 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. + if (a == (SK_AlphaTRANSPARENT & 0x0F)) { + *dst = get_argb4444_neighbor_avg_color(bitmap, x, y); + } else { + *dst = remove_alpha_argb4444(src[x]); + } + dst++; + } + dstRow++; + } + break; + } + case SkBitmap::kARGB_8888_Config: { + for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { + uint32_t* dst = outBitmap.getAddr32(0, dstRow); + uint32_t* src = bitmap.getAddr32(0, y); + for (int x = srcRect.fLeft; x < srcRect.fRight; x++) { + uint8_t a = SkGetPackedA32(src[x]); + if (a == SK_AlphaTRANSPARENT) { + *dst = get_argb8888_neighbor_avg_color(bitmap, x, y); + } else { + *dst = remove_alpha_argb8888(src[x]); + } + dst++; + } + dstRow++; + } + break; + } + default: + SkASSERT(false); + } + bitmap.unlockPixels(); + outBitmap.unlockPixels(); + + outBitmap.setImmutable(); + + return outBitmap; +} + // static SkPDFImage* SkPDFImage::CreateImage(const SkBitmap& bitmap, const SkIRect& srcRect, @@ -358,8 +509,17 @@ SkPDFImage* SkPDFImage::CreateImage(const SkBitmap& bitmap, return NULL; } - SkPDFImage* image = SkNEW_ARGS(SkPDFImage, (NULL, bitmap, - false, srcRect, encoder)); + SkPDFImage* image; + SkBitmap::Config config = bitmap.config(); + if (alphaData.get() != NULL && (config == SkBitmap::kARGB_8888_Config || + config == SkBitmap::kARGB_4444_Config)) { + SkBitmap unpremulBitmap = unpremultiply_bitmap(bitmap, srcRect); + image = SkNEW_ARGS(SkPDFImage, (NULL, unpremulBitmap, false, + SkIRect::MakeWH(srcRect.width(), srcRect.height()), + encoder)); + } else { + image = SkNEW_ARGS(SkPDFImage, (NULL, bitmap, false, srcRect, encoder)); + } if (alphaData.get() != NULL) { SkAutoTUnref<SkPDFImage> mask( SkNEW_ARGS(SkPDFImage, (alphaData.get(), bitmap, |