Index: src/pdf/SkPDFDevice.cpp |
diff --git a/src/pdf/SkPDFDevice.cpp b/src/pdf/SkPDFDevice.cpp |
index 461ae7f9e274fb41c2f91ce4f2d4d9543c042125..8d53b9918c39839513daec606e9a3a46a517b925 100644 |
--- a/src/pdf/SkPDFDevice.cpp |
+++ b/src/pdf/SkPDFDevice.cpp |
@@ -18,6 +18,7 @@ |
#include "SkPath.h" |
#include "SkPathOps.h" |
#include "SkPDFBitmap.h" |
+#include "SkPDFCanon.h" |
#include "SkPDFFont.h" |
#include "SkPDFFormXObject.h" |
#include "SkPDFGraphicState.h" |
@@ -570,7 +571,7 @@ static bool not_supported_for_layers(const SkPaint& layerPaint) { |
// PDF does not support image filters, so render them on CPU. |
// Note that this rendering is done at "screen" resolution (100dpi), not |
// printer resolution. |
- // FIXME: It may be possible to express some filters natively using PDF |
+ // TODO: It may be possible to express some filters natively using PDF |
// to improve quality and file size (http://skbug.com/3043) |
// TODO: should we return true if there is a colorfilter? |
@@ -1040,36 +1041,123 @@ void SkPDFDevice::drawPath(const SkDraw& d, |
&content.entry()->fContent); |
} |
-void SkPDFDevice::drawBitmapRect(const SkDraw& draw, const SkBitmap& bitmap, |
- const SkRect* src, const SkRect& dst, |
- const SkPaint& srcPaint, SkCanvas::SrcRectConstraint constraint) { |
+void SkPDFDevice::drawBitmapRect(const SkDraw& draw, |
+ const SkBitmap& bitmap, |
+ const SkRect* src, |
+ const SkRect& dst, |
+ const SkPaint& srcPaint, |
+ SkCanvas::SrcRectConstraint constraint) { |
+ const SkImage* image = fCanon->bitmapToImage(bitmap); |
+ if (!image) { |
+ return; |
+ } |
+ // ownership of this image is retained by the canon. |
+ this->drawImageRect(draw, image, src, dst, srcPaint, constraint); |
+} |
+ |
+void SkPDFDevice::drawBitmap(const SkDraw& d, |
+ const SkBitmap& bitmap, |
+ const SkMatrix& matrix, |
+ const SkPaint& srcPaint) { |
SkPaint paint = srcPaint; |
if (bitmap.isOpaque()) { |
replace_srcmode_on_opaque_paint(&paint); |
} |
- // TODO: this code path must be updated to respect the flags parameter |
- SkMatrix matrix; |
- SkRect bitmapBounds, tmpSrc, tmpDst; |
- SkBitmap tmpBitmap; |
+ if (d.fClip->isEmpty()) { |
+ return; |
+ } |
- bitmapBounds.isetWH(bitmap.width(), bitmap.height()); |
+ SkMatrix transform = matrix; |
+ transform.postConcat(*d.fMatrix); |
+ const SkImage* image = fCanon->bitmapToImage(bitmap); |
+ if (!image) { |
+ return; |
+ } |
+ this->internalDrawImage(transform, d.fClipStack, *d.fClip, image, nullptr, |
+ paint); |
+} |
+ |
+void SkPDFDevice::drawSprite(const SkDraw& d, |
+ const SkBitmap& bitmap, |
+ int x, |
+ int y, |
+ const SkPaint& srcPaint) { |
+ SkPaint paint = srcPaint; |
+ if (bitmap.isOpaque()) { |
+ replace_srcmode_on_opaque_paint(&paint); |
+ } |
+ |
+ if (d.fClip->isEmpty()) { |
+ return; |
+ } |
+ |
+ SkMatrix matrix; |
+ matrix.setTranslate(SkIntToScalar(x), SkIntToScalar(y)); |
+ const SkImage* image = fCanon->bitmapToImage(bitmap); |
+ if (!image) { |
+ return; |
+ } |
+ this->internalDrawImage(matrix, d.fClipStack, *d.fClip, image, nullptr, |
+ paint); |
+} |
+ |
+void SkPDFDevice::drawImage(const SkDraw& draw, |
+ const SkImage* image, |
+ SkScalar x, |
+ SkScalar y, |
+ const SkPaint& srcPaint) { |
+ SkPaint paint = srcPaint; |
+ if (!image) { |
+ return; |
+ } |
+ if (image->isOpaque()) { |
+ replace_srcmode_on_opaque_paint(&paint); |
+ } |
+ if (draw.fClip->isEmpty()) { |
+ return; |
+ } |
+ SkMatrix transform = SkMatrix::MakeTrans(x, y); |
+ transform.postConcat(*draw.fMatrix); |
+ this->internalDrawImage(transform, draw.fClipStack, *draw.fClip, image, |
+ nullptr, paint); |
+} |
+ |
+void SkPDFDevice::drawImageRect(const SkDraw& draw, |
+ const SkImage* image, |
+ const SkRect* src, |
+ const SkRect& dst, |
+ const SkPaint& srcPaint, |
+ SkCanvas::SrcRectConstraint constraint) { |
+ if (!image) { |
+ return; |
+ } |
+ if (draw.fClip->isEmpty()) { |
+ return; |
+ } |
+ SkPaint paint = srcPaint; |
+ if (image->isOpaque()) { |
+ replace_srcmode_on_opaque_paint(&paint); |
+ } |
+ // TODO: this code path must be updated to respect the flags parameter |
+ SkMatrix matrix; |
+ SkRect tmpSrc, tmpDst; |
+ SkRect imageBounds = SkRect::Make(image->bounds()); |
// Compute matrix from the two rectangles |
if (src) { |
tmpSrc = *src; |
} else { |
- tmpSrc = bitmapBounds; |
+ tmpSrc = imageBounds; |
} |
matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit); |
- const SkBitmap* bitmapPtr = &bitmap; |
- |
// clip the tmpSrc to the bounds of the bitmap, and recompute dstRect if |
// needed (if the src was clipped). No check needed if src==null. |
+ SkAutoTUnref<const SkImage> autoImageUnref; |
if (src) { |
- if (!bitmapBounds.contains(*src)) { |
- if (!tmpSrc.intersect(bitmapBounds)) { |
+ if (!imageBounds.contains(*src)) { |
+ if (!tmpSrc.intersect(imageBounds)) { |
return; // nothing to draw |
} |
// recompute dst, based on the smaller tmpSrc |
@@ -1078,14 +1166,14 @@ void SkPDFDevice::drawBitmapRect(const SkDraw& draw, const SkBitmap& bitmap, |
// since we may need to clamp to the borders of the src rect within |
// the bitmap, we extract a subset. |
- // TODO: make sure this is handled in drawBitmap and remove from here. |
SkIRect srcIR; |
tmpSrc.roundOut(&srcIR); |
- if (!bitmap.extractSubset(&tmpBitmap, srcIR)) { |
+ |
+ autoImageUnref.reset(image->newSubset(srcIR)); |
+ if (!autoImageUnref) { |
return; |
} |
- bitmapPtr = &tmpBitmap; |
- |
+ image = autoImageUnref; |
// Since we did an extract, we need to adjust the matrix accordingly |
SkScalar dx = 0, dy = 0; |
if (srcIR.fLeft > 0) { |
@@ -1098,41 +1186,9 @@ void SkPDFDevice::drawBitmapRect(const SkDraw& draw, const SkBitmap& bitmap, |
matrix.preTranslate(dx, dy); |
} |
} |
- this->drawBitmap(draw, *bitmapPtr, matrix, paint); |
-} |
- |
-void SkPDFDevice::drawBitmap(const SkDraw& d, const SkBitmap& bitmap, |
- const SkMatrix& matrix, const SkPaint& srcPaint) { |
- SkPaint paint = srcPaint; |
- if (bitmap.isOpaque()) { |
- replace_srcmode_on_opaque_paint(&paint); |
- } |
- |
- if (d.fClip->isEmpty()) { |
- return; |
- } |
- |
- SkMatrix transform = matrix; |
- transform.postConcat(*d.fMatrix); |
- this->internalDrawBitmap(transform, d.fClipStack, *d.fClip, bitmap, nullptr, |
- paint); |
-} |
- |
-void SkPDFDevice::drawSprite(const SkDraw& d, const SkBitmap& bitmap, |
- int x, int y, const SkPaint& srcPaint) { |
- SkPaint paint = srcPaint; |
- if (bitmap.isOpaque()) { |
- replace_srcmode_on_opaque_paint(&paint); |
- } |
- |
- if (d.fClip->isEmpty()) { |
- return; |
- } |
- |
- SkMatrix matrix; |
- matrix.setTranslate(SkIntToScalar(x), SkIntToScalar(y)); |
- this->internalDrawBitmap(matrix, d.fClipStack, *d.fClip, bitmap, nullptr, |
- paint); |
+ matrix.postConcat(*draw.fMatrix); |
+ this->internalDrawImage(matrix, draw.fClipStack, *draw.fClip, image, |
+ nullptr, paint); |
} |
// Create a PDF string. Maximum length (in bytes) is 65,535. |
@@ -1435,7 +1491,9 @@ SkPDFArray* SkPDFDevice::copyMediaBox() const { |
SkStreamAsset* SkPDFDevice::content() const { |
SkDynamicMemoryWStream buffer; |
this->writeContent(&buffer); |
- return buffer.detachAsStream(); |
+ return buffer.bytesWritten() > 0 |
+ ? buffer.detachAsStream() |
+ : new SkMemoryStream; |
} |
void SkPDFDevice::copyContentEntriesToData(ContentEntry* entry, |
@@ -2070,42 +2128,59 @@ int SkPDFDevice::getFontResourceIndex(SkTypeface* typeface, uint16_t glyphID) { |
return resourceIndex; |
} |
-void SkPDFDevice::internalDrawBitmap(const SkMatrix& origMatrix, |
- const SkClipStack* clipStack, |
- const SkRegion& origClipRegion, |
- const SkBitmap& origBitmap, |
- const SkIRect* srcRect, |
- const SkPaint& paint) { |
+static SkSize rect_to_size(const SkRect& r) { |
+ return SkSize::Make(r.width(), r.height()); |
+} |
+ |
+static const SkImage* color_filter(const SkImage* image, |
+ SkColorFilter* colorFilter) { |
+ SkAutoTUnref<SkSurface> surface(SkSurface::NewRaster( |
+ SkImageInfo::MakeN32Premul(image->dimensions()))); |
+ if (!surface) { |
+ return image; |
+ } |
+ SkCanvas* canvas = surface->getCanvas(); |
+ canvas->clear(SK_ColorTRANSPARENT); |
+ SkPaint paint; |
+ paint.setColorFilter(colorFilter); |
+ canvas->drawImage(image, 0, 0, &paint); |
+ canvas->flush(); |
+ return surface->newImageSnapshot(); |
+} |
+ |
+//////////////////////////////////////////////////////////////////////////////// |
+void SkPDFDevice::internalDrawImage(const SkMatrix& origMatrix, |
+ const SkClipStack* clipStack, |
+ const SkRegion& origClipRegion, |
+ const SkImage* image, |
+ const SkIRect* srcRect, |
+ const SkPaint& paint) { |
+ SkASSERT(image); |
+ #ifdef SK_PDF_IMAGE_STATS |
+ gDrawImageCalls.fetch_add(1); |
+ #endif |
SkMatrix matrix = origMatrix; |
SkRegion perspectiveBounds; |
const SkRegion* clipRegion = &origClipRegion; |
- SkBitmap perspectiveBitmap; |
- const SkBitmap* bitmap = &origBitmap; |
- SkBitmap tmpSubsetBitmap; |
+ SkAutoTUnref<const SkImage> autoImageUnref; |
+ if (srcRect) { |
+ autoImageUnref.reset(image->newSubset(*srcRect)); |
+ if (!autoImageUnref) { |
+ return; |
+ } |
+ image = autoImageUnref; |
+ } |
// Rasterize the bitmap using perspective in a new bitmap. |
if (origMatrix.hasPerspective()) { |
if (fRasterDpi == 0) { |
return; |
} |
- SkBitmap* subsetBitmap; |
- if (srcRect) { |
- if (!origBitmap.extractSubset(&tmpSubsetBitmap, *srcRect)) { |
- return; |
- } |
- subsetBitmap = &tmpSubsetBitmap; |
- } else { |
- subsetBitmap = &tmpSubsetBitmap; |
- *subsetBitmap = origBitmap; |
- } |
- srcRect = nullptr; |
- |
// Transform the bitmap in the new space, without taking into |
// account the initial transform. |
SkPath perspectiveOutline; |
- perspectiveOutline.addRect( |
- SkRect::MakeWH(SkIntToScalar(subsetBitmap->width()), |
- SkIntToScalar(subsetBitmap->height()))); |
+ SkRect imageBounds = SkRect::Make(image->bounds()); |
+ perspectiveOutline.addRect(imageBounds); |
perspectiveOutline.transform(origMatrix); |
// TODO(edisonn): perf - use current clip too. |
@@ -2116,20 +2191,18 @@ void SkPDFDevice::internalDrawBitmap(const SkMatrix& origMatrix, |
// account the initial transform. |
SkMatrix total = origMatrix; |
total.postConcat(fInitialTransform); |
- total.postScale(SkIntToScalar(fRasterDpi) / |
- SkIntToScalar(DPI_FOR_RASTER_SCALE_ONE), |
- SkIntToScalar(fRasterDpi) / |
- SkIntToScalar(DPI_FOR_RASTER_SCALE_ONE)); |
+ SkScalar dpiScale = SkIntToScalar(fRasterDpi) / |
+ SkIntToScalar(DPI_FOR_RASTER_SCALE_ONE); |
+ total.postScale(dpiScale, dpiScale); |
+ |
SkPath physicalPerspectiveOutline; |
- physicalPerspectiveOutline.addRect( |
- SkRect::MakeWH(SkIntToScalar(subsetBitmap->width()), |
- SkIntToScalar(subsetBitmap->height()))); |
+ physicalPerspectiveOutline.addRect(imageBounds); |
physicalPerspectiveOutline.transform(total); |
- SkScalar scaleX = physicalPerspectiveOutline.getBounds().width() / |
- bounds.width(); |
- SkScalar scaleY = physicalPerspectiveOutline.getBounds().height() / |
- bounds.height(); |
+ SkRect physicalPerspectiveBounds = |
+ physicalPerspectiveOutline.getBounds(); |
+ SkScalar scaleX = physicalPerspectiveBounds.width() / bounds.width(); |
+ SkScalar scaleY = physicalPerspectiveBounds.height() / bounds.height(); |
// TODO(edisonn): A better approach would be to use a bitmap shader |
// (in clamp mode) and draw a rect over the entire bounding box. Then |
@@ -2138,14 +2211,15 @@ void SkPDFDevice::internalDrawBitmap(const SkMatrix& origMatrix, |
// the image. Avoiding alpha will reduce the pdf size and generation |
// CPU time some. |
- const int w = SkScalarCeilToInt(physicalPerspectiveOutline.getBounds().width()); |
- const int h = SkScalarCeilToInt(physicalPerspectiveOutline.getBounds().height()); |
- if (!perspectiveBitmap.tryAllocN32Pixels(w, h)) { |
+ SkISize wh = rect_to_size(physicalPerspectiveBounds).toCeil(); |
+ |
+ SkAutoTUnref<SkSurface> surface( |
+ SkSurface::NewRaster(SkImageInfo::MakeN32Premul(wh))); |
+ if (!surface) { |
return; |
} |
- perspectiveBitmap.eraseColor(SK_ColorTRANSPARENT); |
- |
- SkCanvas canvas(perspectiveBitmap); |
+ SkCanvas* canvas = surface->getCanvas(); |
+ canvas->clear(SK_ColorTRANSPARENT); |
SkScalar deltaX = bounds.left(); |
SkScalar deltaY = bounds.top(); |
@@ -2156,26 +2230,22 @@ void SkPDFDevice::internalDrawBitmap(const SkMatrix& origMatrix, |
// Translate the draw in the new canvas, so we perfectly fit the |
// shape in the bitmap. |
- canvas.setMatrix(offsetMatrix); |
- |
- canvas.drawBitmap(*subsetBitmap, SkIntToScalar(0), SkIntToScalar(0)); |
- |
+ canvas->setMatrix(offsetMatrix); |
+ canvas->drawImage(image, 0, 0, nullptr); |
// Make sure the final bits are in the bitmap. |
- canvas.flush(); |
+ canvas->flush(); |
// In the new space, we use the identity matrix translated |
// and scaled to reflect DPI. |
matrix.setScale(1 / scaleX, 1 / scaleY); |
matrix.postTranslate(deltaX, deltaY); |
- perspectiveBounds.setRect( |
- SkIRect::MakeXYWH(SkScalarFloorToInt(bounds.x()), |
- SkScalarFloorToInt(bounds.y()), |
- SkScalarCeilToInt(bounds.width()), |
- SkScalarCeilToInt(bounds.height()))); |
+ perspectiveBounds.setRect(bounds.roundOut()); |
clipRegion = &perspectiveBounds; |
srcRect = nullptr; |
- bitmap = &perspectiveBitmap; |
+ |
+ autoImageUnref.reset(surface->newImageSnapshot()); |
+ image = autoImageUnref; |
} |
SkMatrix scaled; |
@@ -2183,9 +2253,9 @@ void SkPDFDevice::internalDrawBitmap(const SkMatrix& origMatrix, |
scaled.setScale(SK_Scalar1, -SK_Scalar1); |
scaled.postTranslate(0, SK_Scalar1); |
// Scale the image up from 1x1 to WxH. |
- SkIRect subset = bitmap->bounds(); |
- scaled.postScale(SkIntToScalar(subset.width()), |
- SkIntToScalar(subset.height())); |
+ SkIRect subset = image->bounds(); |
+ scaled.postScale(SkIntToScalar(image->width()), |
+ SkIntToScalar(image->height())); |
scaled.postConcat(matrix); |
ScopedContentEntry content(this, clipStack, *clipRegion, scaled, paint); |
if (!content.entry() || (srcRect && !subset.intersect(*srcRect))) { |
@@ -2193,8 +2263,7 @@ void SkPDFDevice::internalDrawBitmap(const SkMatrix& origMatrix, |
} |
if (content.needShape()) { |
SkPath shape; |
- shape.addRect(SkRect::MakeWH(SkIntToScalar(subset.width()), |
- SkIntToScalar(subset.height()))); |
+ shape.addRect(SkRect::Make(subset)); |
shape.transform(matrix); |
content.setShape(shape); |
} |
@@ -2202,32 +2271,25 @@ void SkPDFDevice::internalDrawBitmap(const SkMatrix& origMatrix, |
return; |
} |
- SkBitmap subsetBitmap; |
- if (!bitmap->extractSubset(&subsetBitmap, subset)) { |
- return; |
- } |
if (SkColorFilter* colorFilter = paint.getColorFilter()) { |
// TODO(http://skbug.com/4378): implement colorfilter on other |
- // draw calls. This code here works for all drawBitmap*() |
- // calls amd ImageFilters (which rasterize a layer on this |
- // backend). Fortuanely, this seems to be how Chromium |
- // impements most color-filters. |
- SkBitmap tmp; |
- if (subsetBitmap.copyTo(&tmp, kN32_SkColorType)) { |
- SkAutoLockPixels autoLockPixelsTmp(tmp); |
- for (int y = 0; y < tmp.height(); ++y) { |
- SkPMColor* pixels = tmp.getAddr32(0, y); |
- colorFilter->filterSpan(pixels, tmp.width(), pixels); |
- } |
- tmp.setImmutable(); |
- subsetBitmap = tmp; |
+ // draw calls. This code here works for all |
+ // drawBitmap*()/drawImage*() calls amd ImageFilters (which |
+ // rasterize a layer on this backend). Fortuanely, this seems |
+ // to be how Chromium impements most color-filters. |
+ autoImageUnref.reset(color_filter(image, colorFilter)); |
+ image = autoImageUnref; |
+ // TODO(halcanary): de-dupe this by caching filtered images. |
+ // (maybe in the resource cache?) |
+ } |
+ SkAutoTUnref<SkPDFObject> pdfimage(fCanon->findPDFBitmap(image)); |
+ if (!pdfimage) { |
+ pdfimage.reset(SkPDFCreateBitmapObject(image)); |
+ if (!pdfimage) { |
+ return; |
} |
+ fCanon->addPDFBitmap(image->uniqueID(), pdfimage); |
} |
- SkAutoTUnref<SkPDFObject> image(SkPDFBitmap::Create(fCanon, subsetBitmap)); |
- if (!image) { |
- return; |
- } |
- |
- SkPDFUtils::DrawFormXObject(this->addXObjectResource(image.get()), |
+ SkPDFUtils::DrawFormXObject(this->addXObjectResource(SkRef(pdfimage.get())), |
&content.entry()->fContent); |
} |