Chromium Code Reviews| Index: src/pdf/SkPDFDevice.cpp |
| diff --git a/src/pdf/SkPDFDevice.cpp b/src/pdf/SkPDFDevice.cpp |
| index 461ae7f9e274fb41c2f91ce4f2d4d9543c042125..eb1409712de71bd3a101181fd587f4b754c2b0ea 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" |
| @@ -1040,36 +1041,125 @@ 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; |
| + } |
| + |
| + 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); |
| +} |
| - bitmapBounds.isetWH(bitmap.width(), bitmap.height()); |
| +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; |
| + // FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME |
|
tomhudson
2015/09/30 16:10:30
Not ready to land?
hal.canary
2015/10/01 01:24:55
Done.
|
| // 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 |
| @@ -1081,11 +1171,12 @@ void SkPDFDevice::drawBitmapRect(const SkDraw& draw, const SkBitmap& bitmap, |
| // 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 +1189,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 +1494,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 +2131,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 +2194,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 +2214,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 +2233,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 +2256,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 +2266,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 +2274,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); |
| } |