diff options
author | Hal Canary <halcanary@google.com> | 2017-07-18 10:28:31 -0400 |
---|---|---|
committer | Skia Commit-Bot <skia-commit-bot@chromium.org> | 2017-07-18 19:33:34 +0000 |
commit | 4f29c20f20ce62150998786be8b9f8a81a901d72 (patch) | |
tree | 82e9ce0a4a2b83261f610ddbfe233728285ed26a /src/pdf | |
parent | e46828675c617fd636e8a64f3be60f7f396c35f1 (diff) |
SkPDF: Re-use Jpeg Image Shaders
Also, de-dup shader better.
- SkBitmapKeyFromImage function factors out image de-dup code.
- SkPDFUtils::ToBitmap funtion factors out to-bitmap code
- make_image_shader function now takes Image rather than Bitmap.
All tests render the same. Some are significantly smaller PDFs.
Change-Id: Id8dd36b31c8e573f44a5e9f6058d5a1d622b6385
Reviewed-on: https://skia-review.googlesource.com/24081
Reviewed-by: Florin Malita <fmalita@chromium.org>
Commit-Queue: Hal Canary <halcanary@google.com>
Diffstat (limited to 'src/pdf')
-rw-r--r-- | src/pdf/SkKeyedImage.cpp | 20 | ||||
-rw-r--r-- | src/pdf/SkKeyedImage.h | 6 | ||||
-rw-r--r-- | src/pdf/SkPDFBitmap.cpp | 28 | ||||
-rw-r--r-- | src/pdf/SkPDFShader.cpp | 110 | ||||
-rw-r--r-- | src/pdf/SkPDFUtils.cpp | 18 | ||||
-rw-r--r-- | src/pdf/SkPDFUtils.h | 5 |
6 files changed, 109 insertions, 78 deletions
diff --git a/src/pdf/SkKeyedImage.cpp b/src/pdf/SkKeyedImage.cpp index bf196323af..bcf7d70519 100644 --- a/src/pdf/SkKeyedImage.cpp +++ b/src/pdf/SkKeyedImage.cpp @@ -9,15 +9,19 @@ #include "SkImage_Base.h" -SkKeyedImage::SkKeyedImage(sk_sp<SkImage> i) : fImage(std::move(i)) { - if (fImage) { - if (const SkBitmap* bm = as_IB(fImage.get())->onPeekBitmap()) { - SkIPoint o = bm->pixelRefOrigin(); - fKey = {fImage->bounds().makeOffset(o.fX, o.fY), bm->getGenerationID()}; - } else { - fKey = {fImage->bounds(), fImage->uniqueID()}; - } +SkBitmapKey SkBitmapKeyFromImage(const SkImage* image) { + if (!image) { + return {{0, 0, 0, 0}, 0}; + } + if (const SkBitmap* bm = as_IB(image)->onPeekBitmap()) { + SkIPoint o = bm->pixelRefOrigin(); + return {image->bounds().makeOffset(o.x(), o.y()), bm->getGenerationID()}; } + return {image->bounds(), image->uniqueID()}; +} + +SkKeyedImage::SkKeyedImage(sk_sp<SkImage> i) : fImage(std::move(i)) { + fKey = SkBitmapKeyFromImage(fImage.get()); } SkKeyedImage::SkKeyedImage(const SkBitmap& bm) : fImage(SkImage::MakeFromBitmap(bm)) { diff --git a/src/pdf/SkKeyedImage.h b/src/pdf/SkKeyedImage.h index f74ec5c170..f489e576cd 100644 --- a/src/pdf/SkKeyedImage.h +++ b/src/pdf/SkKeyedImage.h @@ -37,4 +37,10 @@ private: sk_sp<SkImage> fImage; SkBitmapKey fKey = {{0, 0, 0, 0}, 0}; }; + +/** + * Given an Image, return the Bitmap Key that corresponds to it. If the Image + * wraps a Bitmap, use that Bitmap's key. + */ +SkBitmapKey SkBitmapKeyFromImage(const SkImage*); #endif // SkKeyedImage_DEFINED diff --git a/src/pdf/SkPDFBitmap.cpp b/src/pdf/SkPDFBitmap.cpp index d0dcce6b3c..e28800bf99 100644 --- a/src/pdf/SkPDFBitmap.cpp +++ b/src/pdf/SkPDFBitmap.cpp @@ -5,35 +5,27 @@ * found in the LICENSE file. */ +#include "SkPDFBitmap.h" + #include "SkColorPriv.h" #include "SkData.h" #include "SkDeflate.h" -#include "SkImage_Base.h" +#include "SkImage.h" #include "SkJpegInfo.h" -#include "SkPDFBitmap.h" #include "SkPDFCanon.h" #include "SkPDFTypes.h" +#include "SkPDFUtils.h" #include "SkStream.h" #include "SkUnPreMultiply.h" -void image_get_ro_pixels(const SkImage* image, SkBitmap* dst) { - SkColorSpace* legacyColorSpace = nullptr; - if(as_IB(image)->getROPixels(dst, legacyColorSpace) - && dst->dimensions() == image->dimensions()) { - return; - } - // no pixels or wrong size: fill with zeros. - dst->setInfo(SkImageInfo::MakeN32(image->width(), image->height(), image->alphaType())); -} - bool image_compute_is_opaque(const SkImage* image) { if (image->isOpaque()) { return true; } // keep output PDF small at cost of possible resource use. SkBitmap bm; - image_get_ro_pixels(image, &bm); - return SkBitmap::ComputeIsOpaque(bm); + // if image can not be read, treat as transparent. + return SkPDFUtils::ToBitmap(image, &bm) && SkBitmap::ComputeIsOpaque(bm); } //////////////////////////////////////////////////////////////////////////////// @@ -286,7 +278,10 @@ static void emit_image_xobject(SkWStream* stream, const sk_sp<SkPDFObject>& smask, const SkPDFObjNumMap& objNumMap) { SkBitmap bitmap; - image_get_ro_pixels(image, &bitmap); // TODO(halcanary): test + if (!SkPDFUtils::ToBitmap(image, &bitmap)) { + // no pixels or wrong size: fill with zeros. + bitmap.setInfo(SkImageInfo::MakeN32(image->width(), image->height(), image->alphaType())); + } // Write to a temporary buffer to get the compressed length. SkDynamicMemoryWStream buffer; @@ -433,8 +428,7 @@ sk_sp<SkPDFObject> SkPDFCreateBitmapObject(sk_sp<SkImage> image, if (pixelSerializer) { SkBitmap bm; SkPixmap pmap; - SkColorSpace* legacyColorSpace = nullptr; - if (as_IB(image.get())->getROPixels(&bm, legacyColorSpace) && bm.peekPixels(&pmap)) { + if (SkPDFUtils::ToBitmap(image.get(), &bm) && bm.peekPixels(&pmap)) { data = pixelSerializer->encodeToData(pmap); if (data && SkIsJFIF(data.get(), &info)) { bool yuv = info.fType == SkJFIFInfo::kYCbCr; diff --git a/src/pdf/SkPDFShader.cpp b/src/pdf/SkPDFShader.cpp index 56d42f68e9..e52fad97de 100644 --- a/src/pdf/SkPDFShader.cpp +++ b/src/pdf/SkPDFShader.cpp @@ -22,6 +22,12 @@ #include "SkTemplates.h" +static void draw_image_matrix(SkCanvas* canvas, const SkImage* img, const SkMatrix& matrix) { + SkAutoCanvasRestore acr(canvas, true); + canvas->concat(matrix); + canvas->drawImage(img, 0, 0); +} + static void draw_bitmap_matrix(SkCanvas* canvas, const SkBitmap& bm, const SkMatrix& matrix) { SkAutoCanvasRestore acr(canvas, true); canvas->concat(matrix); @@ -30,9 +36,8 @@ static void draw_bitmap_matrix(SkCanvas* canvas, const SkBitmap& bm, const SkMat static sk_sp<SkPDFStream> make_image_shader(SkPDFDocument* doc, const SkPDFImageShaderKey& key, - SkBitmap image) { - SkASSERT(key.fBitmapKey == - (SkBitmapKey{image.getSubset(), image.getGenerationID()})); + SkImage* image) { + SkASSERT(image); // The image shader pattern cell will be drawn into a separate device // in pattern cell space (no scaling on the bitmap, though there may be @@ -42,14 +47,12 @@ static sk_sp<SkPDFStream> make_image_shader(SkPDFDocument* doc, // to handle fake clamping. SkMatrix finalMatrix = key.fCanvasTransform; finalMatrix.preConcat(key.fShaderTransform); - SkRect deviceBounds; - deviceBounds.set(key.fBBox); + SkRect deviceBounds = SkRect::Make(key.fBBox); if (!SkPDFUtils::InverseTransformBBox(finalMatrix, &deviceBounds)) { return nullptr; } - SkRect bitmapBounds; - image.getBounds(&bitmapBounds); + SkRect bitmapBounds = SkRect::Make(image->bounds()); // For tiling modes, the bounds should be extended to include the bitmap, // otherwise the bitmap gets clipped out and the shader is empty and awful. @@ -63,13 +66,12 @@ static sk_sp<SkPDFStream> make_image_shader(SkPDFDocument* doc, deviceBounds.join(bitmapBounds); } - SkISize size = SkISize::Make(SkScalarRoundToInt(deviceBounds.width()), - SkScalarRoundToInt(deviceBounds.height())); - auto patternDevice = sk_make_sp<SkPDFDevice>(size, doc); + SkISize patternDeviceSize = {SkScalarCeilToInt(deviceBounds.width()), + SkScalarCeilToInt(deviceBounds.height())}; + auto patternDevice = sk_make_sp<SkPDFDevice>(patternDeviceSize, doc); SkCanvas canvas(patternDevice.get()); - SkRect patternBBox; - image.getBounds(&patternBBox); + SkRect patternBBox = SkRect::Make(image->bounds()); // Translate the canvas so that the bitmap origin is at (0, 0). canvas.translate(-deviceBounds.left(), -deviceBounds.top()); @@ -80,24 +82,24 @@ static sk_sp<SkPDFStream> make_image_shader(SkPDFDocument* doc, // If the bitmap is out of bounds (i.e. clamp mode where we only see the // stretched sides), canvas will clip this out and the extraneous data // won't be saved to the PDF. - canvas.drawBitmap(image, 0, 0); + canvas.drawImage(image, 0, 0); - SkScalar width = SkIntToScalar(image.width()); - SkScalar height = SkIntToScalar(image.height()); + SkScalar width = SkIntToScalar(image->width()); + SkScalar height = SkIntToScalar(image->height()); // Tiling is implied. First we handle mirroring. if (tileModes[0] == SkShader::kMirror_TileMode) { SkMatrix xMirror; xMirror.setScale(-1, 1); xMirror.postTranslate(2 * width, 0); - draw_bitmap_matrix(&canvas, image, xMirror); + draw_image_matrix(&canvas, image, xMirror); patternBBox.fRight += width; } if (tileModes[1] == SkShader::kMirror_TileMode) { SkMatrix yMirror; yMirror.setScale(SK_Scalar1, -SK_Scalar1); yMirror.postTranslate(0, 2 * height); - draw_bitmap_matrix(&canvas, image, yMirror); + draw_image_matrix(&canvas, image, yMirror); patternBBox.fBottom += height; } if (tileModes[0] == SkShader::kMirror_TileMode && @@ -105,53 +107,66 @@ static sk_sp<SkPDFStream> make_image_shader(SkPDFDocument* doc, SkMatrix mirror; mirror.setScale(-1, -1); mirror.postTranslate(2 * width, 2 * height); - draw_bitmap_matrix(&canvas, image, mirror); + draw_image_matrix(&canvas, image, mirror); } // Then handle Clamping, which requires expanding the pattern canvas to // cover the entire surfaceBBox. + SkBitmap bitmap; + if (tileModes[0] == SkShader::kClamp_TileMode || + tileModes[1] == SkShader::kClamp_TileMode) { + // For now, the easiest way to access the colors in the corners and sides is + // to just make a bitmap from the image. + if (!SkPDFUtils::ToBitmap(image, &bitmap)) { + bitmap.allocN32Pixels(image->width(), image->height()); + bitmap.eraseColor(0x00000000); + } + } + // If both x and y are in clamp mode, we start by filling in the corners. // (Which are just a rectangles of the corner colors.) if (tileModes[0] == SkShader::kClamp_TileMode && tileModes[1] == SkShader::kClamp_TileMode) { + SkASSERT(!bitmap.drawsNothing()); SkPaint paint; SkRect rect; rect = SkRect::MakeLTRB(deviceBounds.left(), deviceBounds.top(), 0, 0); if (!rect.isEmpty()) { - paint.setColor(image.getColor(0, 0)); + paint.setColor(bitmap.getColor(0, 0)); canvas.drawRect(rect, paint); } rect = SkRect::MakeLTRB(width, deviceBounds.top(), deviceBounds.right(), 0); if (!rect.isEmpty()) { - paint.setColor(image.getColor(image.width() - 1, 0)); + paint.setColor(bitmap.getColor(bitmap.width() - 1, 0)); canvas.drawRect(rect, paint); } rect = SkRect::MakeLTRB(width, height, deviceBounds.right(), deviceBounds.bottom()); if (!rect.isEmpty()) { - paint.setColor(image.getColor(image.width() - 1, - image.height() - 1)); + paint.setColor(bitmap.getColor(bitmap.width() - 1, + bitmap.height() - 1)); canvas.drawRect(rect, paint); } rect = SkRect::MakeLTRB(deviceBounds.left(), height, 0, deviceBounds.bottom()); if (!rect.isEmpty()) { - paint.setColor(image.getColor(0, image.height() - 1)); + paint.setColor(bitmap.getColor(0, bitmap.height() - 1)); canvas.drawRect(rect, paint); } } // Then expand the left, right, top, then bottom. if (tileModes[0] == SkShader::kClamp_TileMode) { - SkIRect subset = SkIRect::MakeXYWH(0, 0, 1, image.height()); + SkASSERT(!bitmap.drawsNothing()); + SkIRect subset = SkIRect::MakeXYWH(0, 0, 1, bitmap.height()); if (deviceBounds.left() < 0) { SkBitmap left; - SkAssertResult(image.extractSubset(&left, subset)); + SkAssertResult(bitmap.extractSubset(&left, subset)); SkMatrix leftMatrix; leftMatrix.setScale(-deviceBounds.left(), 1); @@ -168,8 +183,8 @@ static sk_sp<SkPDFStream> make_image_shader(SkPDFDocument* doc, if (deviceBounds.right() > width) { SkBitmap right; - subset.offset(image.width() - 1, 0); - SkAssertResult(image.extractSubset(&right, subset)); + subset.offset(bitmap.width() - 1, 0); + SkAssertResult(bitmap.extractSubset(&right, subset)); SkMatrix rightMatrix; rightMatrix.setScale(deviceBounds.right() - width, 1); @@ -186,10 +201,11 @@ static sk_sp<SkPDFStream> make_image_shader(SkPDFDocument* doc, } if (tileModes[1] == SkShader::kClamp_TileMode) { - SkIRect subset = SkIRect::MakeXYWH(0, 0, image.width(), 1); + SkASSERT(!bitmap.drawsNothing()); + SkIRect subset = SkIRect::MakeXYWH(0, 0, bitmap.width(), 1); if (deviceBounds.top() < 0) { SkBitmap top; - SkAssertResult(image.extractSubset(&top, subset)); + SkAssertResult(bitmap.extractSubset(&top, subset)); SkMatrix topMatrix; topMatrix.setScale(SK_Scalar1, -deviceBounds.top()); @@ -206,8 +222,8 @@ static sk_sp<SkPDFStream> make_image_shader(SkPDFDocument* doc, if (deviceBounds.bottom() > height) { SkBitmap bottom; - subset.offset(0, image.height() - 1); - SkAssertResult(image.extractSubset(&bottom, subset)); + subset.offset(0, bitmap.height() - 1); + SkAssertResult(bitmap.extractSubset(&bottom, subset)); SkMatrix bottomMatrix; bottomMatrix.setScale(SK_Scalar1, deviceBounds.bottom() - height); @@ -225,7 +241,7 @@ static sk_sp<SkPDFStream> make_image_shader(SkPDFDocument* doc, auto imageShader = sk_make_sp<SkPDFStream>(patternDevice->content()); SkPDFUtils::PopulateTilingPatternDict(imageShader->dict(), patternBBox, - patternDevice->makeResourceDict(), finalMatrix); + patternDevice->makeResourceDict(), finalMatrix); return imageShader; } @@ -245,7 +261,7 @@ static sk_sp<SkPDFObject> make_fallback_shader(SkPDFDocument* doc, canvasTransform, SkMatrix::I(), surfaceBBox, - {{0, 0, 0, 0}, 0}, + {{0, 0, 0, 0}, 0}, // don't need the key; won't de-dup. {SkShader::kClamp_TileMode, SkShader::kClamp_TileMode}}; key.fShaderTransform = shader->getLocalMatrix(); @@ -271,23 +287,25 @@ static sk_sp<SkPDFObject> make_fallback_shader(SkPDFDocument* doc, SkSize scale = {SkIntToScalar(size.width()) / shaderRect.width(), SkIntToScalar(size.height()) / shaderRect.height()}; - SkBitmap image; - image.allocN32Pixels(size.width(), size.height()); - image.eraseColor(SK_ColorTRANSPARENT); + SkBitmap bitmap; + bitmap.allocN32Pixels(size.width(), size.height()); + bitmap.eraseColor(SK_ColorTRANSPARENT); SkPaint p; p.setShader(sk_ref_sp(shader)); - SkCanvas canvas(image); + SkCanvas canvas(bitmap); canvas.scale(scale.width(), scale.height()); canvas.translate(-shaderRect.x(), -shaderRect.y()); canvas.drawPaint(p); key.fShaderTransform.setTranslate(shaderRect.x(), shaderRect.y()); key.fShaderTransform.preScale(1 / scale.width(), 1 / scale.height()); - key.fBitmapKey = SkBitmapKey{image.getSubset(), image.getGenerationID()}; - SkASSERT (!image.isNull()); - return make_image_shader(doc, key, std::move(image)); + + SkASSERT (!bitmap.isNull()); + bitmap.setImmutable(); + sk_sp<SkImage> image = SkImage::MakeFromBitmap(bitmap); + return make_image_shader(doc, key, image.get()); } sk_sp<SkPDFObject> SkPDFMakeShader(SkPDFDocument* doc, @@ -311,20 +329,14 @@ sk_sp<SkPDFObject> SkPDFMakeShader(SkPDFDocument* doc, {SkShader::kClamp_TileMode, SkShader::kClamp_TileMode}}; SkASSERT(shader->asAGradient(nullptr) == SkShader::kNone_GradientType) ; - SkImage* skimg; - if ((skimg = shader->isAImage(&key.fShaderTransform, key.fImageTileModes)) - && skimg->asLegacyBitmap(&image, SkImage::kRO_LegacyBitmapMode)) { - // TODO(halcanary): delay converting to bitmap. - key.fBitmapKey = SkBitmapKey{image.getSubset(), image.getGenerationID()}; - if (image.isNull()) { - return nullptr; - } + if (SkImage* skimg = shader->isAImage(&key.fShaderTransform, key.fImageTileModes)) { + key.fBitmapKey = SkBitmapKeyFromImage(skimg); SkPDFCanon* canon = doc->canon(); sk_sp<SkPDFObject>* shaderPtr = canon->fImageShaderMap.find(key); if (shaderPtr) { return *shaderPtr; } - sk_sp<SkPDFObject> pdfShader = make_image_shader(doc, key, std::move(image)); + sk_sp<SkPDFObject> pdfShader = make_image_shader(doc, key, skimg); canon->fImageShaderMap.set(std::move(key), pdfShader); return pdfShader; } diff --git a/src/pdf/SkPDFUtils.cpp b/src/pdf/SkPDFUtils.cpp index 510be6c27a..af18432eb2 100644 --- a/src/pdf/SkPDFUtils.cpp +++ b/src/pdf/SkPDFUtils.cpp @@ -5,15 +5,16 @@ * found in the LICENSE file. */ +#include "SkPDFUtils.h" #include "SkData.h" #include "SkFixed.h" #include "SkGeometry.h" +#include "SkImage_Base.h" #include "SkPDFResourceDict.h" -#include "SkPDFUtils.h" +#include "SkPDFTypes.h" #include "SkStream.h" #include "SkString.h" -#include "SkPDFTypes.h" #include <cmath> @@ -516,3 +517,16 @@ void SkPDFUtils::PopulateTilingPatternDict(SkPDFDict* pattern, pattern->insertObject("Matrix", SkPDFUtils::MatrixToArray(matrix)); } } + +bool SkPDFUtils::ToBitmap(const SkImage* img, SkBitmap* dst) { + SkASSERT(img); + SkASSERT(dst); + SkBitmap bitmap; + if(as_IB(img)->getROPixels(&bitmap, nullptr)) { + SkASSERT(bitmap.dimensions() == img->dimensions()); + SkASSERT(!bitmap.drawsNothing()); + *dst = std::move(bitmap); + return true; + } + return false; +} diff --git a/src/pdf/SkPDFUtils.h b/src/pdf/SkPDFUtils.h index 93509fedd8..27d0a3adbe 100644 --- a/src/pdf/SkPDFUtils.h +++ b/src/pdf/SkPDFUtils.h @@ -4,11 +4,10 @@ * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ - - #ifndef SkPDFUtils_DEFINED #define SkPDFUtils_DEFINED +#include "SkPDFTypes.h" #include "SkPaint.h" #include "SkPath.h" #include "SkShader.h" @@ -124,6 +123,8 @@ void PopulateTilingPatternDict(SkPDFDict* pattern, SkRect& bbox, sk_sp<SkPDFDict> resources, const SkMatrix& matrix); + +bool ToBitmap(const SkImage* img, SkBitmap* dst); } // namespace SkPDFUtils #endif |