Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(247)

Unified Diff: src/pdf/SkPDFDevice.cpp

Issue 1372783003: SkPDF: Implement drawImage*() properly (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: 2015-09-30 (Wednesday) 21:22:24 EDT Created 5 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/pdf/SkPDFDevice.h ('k') | src/pdf/SkPDFTypes.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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);
}
« no previous file with comments | « src/pdf/SkPDFDevice.h ('k') | src/pdf/SkPDFTypes.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698