SkPDF: clean up PDFDevice.

Motivation:  factor out some code for later re-use; clean up.

  - mask_to_greyscale_image()
  - addSMaskGraphicState()
  - clearMaskOnGraphicState()
  - stop using bare pointer to indicate ownership.
  - add ScopedContentEntry::stream()

Change-Id: I7abe7ff9eab89e1002692017000cda2ca7642631
Reviewed-on: https://skia-review.googlesource.com/20978
Reviewed-by: Ben Wagner <bungeman@google.com>
Commit-Queue: Hal Canary <halcanary@google.com>
This commit is contained in:
Hal Canary 2017-06-27 14:28:37 -04:00 committed by Skia Commit-Bot
parent 0bb0411f59
commit 51329c944c
4 changed files with 87 additions and 94 deletions

View File

@ -62,6 +62,33 @@
// Utility functions
// This function destroys the mask and either frees or takes the pixels.
sk_sp<SkImage> mask_to_greyscale_image(SkMask* mask) {
sk_sp<SkImage> img;
SkPixmap pm(SkImageInfo::Make(mask->fBounds.width(), mask->fBounds.height(),
kGray_8_SkColorType, kOpaque_SkAlphaType),
mask->fImage, mask->fRowBytes);
const int imgQuality = SK_PDF_MASK_QUALITY;
if (imgQuality <= 100 && imgQuality >= 0) {
SkDynamicMemoryWStream buffer;
SkJpegEncoder::Options jpegOptions;
jpegOptions.fQuality = imgQuality;
if (SkJpegEncoder::Encode(&buffer, pm, jpegOptions)) {
img = SkImage::MakeFromEncoded(buffer.detachAsData());
SkASSERT(img);
if (img) {
SkMask::FreeImage(mask->fImage);
}
}
}
if (!img) {
img = SkImage::MakeFromRaster(pm, [](const void* p, void*) { SkMask::FreeImage((void*)p); },
nullptr);
}
*mask = SkMask(); // destructive;
return img;
}
static void draw_points(SkCanvas::PointMode mode,
size_t count,
const SkPoint* points,
@ -433,7 +460,7 @@ SkBaseDevice* SkPDFDevice::onCreateDevice(const CreateInfo& cinfo, const SkPaint
return SkBitmapDevice::Create(cinfo.fInfo, SkSurfaceProps(0, kUnknown_SkPixelGeometry));
}
SkISize size = SkISize::Make(cinfo.fInfo.width(), cinfo.fInfo.height());
return SkPDFDevice::Create(size, fRasterDpi, fDocument);
return SkPDFDevice::Make(size, fRasterDpi, fDocument).release();
}
SkPDFCanon* SkPDFDevice::getCanon() const { return fDocument->canon(); }
@ -476,6 +503,7 @@ public:
}
SkPDFDevice::ContentEntry* entry() { return fContentEntry; }
SkDynamicMemoryWStream* stream() { return &fContentEntry->fContent; }
/* Returns true when we explicitly need the shape of the drawing. */
bool needShape() {
@ -675,34 +703,28 @@ void SkPDFDevice::drawPoints(SkCanvas::PointMode mode,
if (!content.entry()) {
return;
}
SkDynamicMemoryWStream* contentStream = content.stream();
switch (mode) {
case SkCanvas::kPolygon_PointMode:
SkPDFUtils::MoveTo(points[0].fX, points[0].fY,
&content.entry()->fContent);
SkPDFUtils::MoveTo(points[0].fX, points[0].fY, contentStream);
for (size_t i = 1; i < count; i++) {
SkPDFUtils::AppendLine(points[i].fX, points[i].fY,
&content.entry()->fContent);
SkPDFUtils::AppendLine(points[i].fX, points[i].fY, contentStream);
}
SkPDFUtils::StrokePath(&content.entry()->fContent);
SkPDFUtils::StrokePath(contentStream);
break;
case SkCanvas::kLines_PointMode:
for (size_t i = 0; i < count/2; i++) {
SkPDFUtils::MoveTo(points[i * 2].fX, points[i * 2].fY,
&content.entry()->fContent);
SkPDFUtils::AppendLine(points[i * 2 + 1].fX,
points[i * 2 + 1].fY,
&content.entry()->fContent);
SkPDFUtils::StrokePath(&content.entry()->fContent);
SkPDFUtils::MoveTo(points[i * 2].fX, points[i * 2].fY, contentStream);
SkPDFUtils::AppendLine(points[i * 2 + 1].fX, points[i * 2 + 1].fY, contentStream);
SkPDFUtils::StrokePath(contentStream);
}
break;
case SkCanvas::kPoints_PointMode:
SkASSERT(paint->getStrokeCap() == SkPaint::kRound_Cap);
for (size_t i = 0; i < count; i++) {
SkPDFUtils::MoveTo(points[i].fX, points[i].fY,
&content.entry()->fContent);
SkPDFUtils::ClosePath(&content.entry()->fContent);
SkPDFUtils::StrokePath(&content.entry()->fContent);
SkPDFUtils::MoveTo(points[i].fX, points[i].fY, contentStream);
SkPDFUtils::ClosePath(contentStream);
SkPDFUtils::StrokePath(contentStream);
}
break;
default:
@ -775,9 +797,8 @@ void SkPDFDevice::drawRect(const SkRect& rect,
if (!content.entry()) {
return;
}
SkPDFUtils::AppendRectangle(r, &content.entry()->fContent);
SkPDFUtils::PaintPath(paint.getStyle(), SkPath::kWinding_FillType,
&content.entry()->fContent);
SkPDFUtils::AppendRectangle(r, content.stream());
SkPDFUtils::PaintPath(paint.getStyle(), SkPath::kWinding_FillType, content.stream());
}
void SkPDFDevice::drawRRect(const SkRRect& rrect,
@ -840,38 +861,15 @@ void SkPDFDevice::internalDrawPathWithFilter(const SkClipStack& clipStack,
if (!paint->getMaskFilter()->filterMask(&dstMask, sourceMask, ctm, &margin)) {
return;
}
SkPixmap pm(SkImageInfo::Make(dstMask.fBounds.width(), dstMask.fBounds.height(),
kGray_8_SkColorType, kOpaque_SkAlphaType),
dstMask.fImage, dstMask.fRowBytes);
sk_sp<SkImage> mask;
const int maskQuality = SK_PDF_MASK_QUALITY;
if (maskQuality <= 100 && maskQuality >= 0) {
SkDynamicMemoryWStream buffer;
SkJpegEncoder::Options jpegOptions;
jpegOptions.fQuality = maskQuality;
if (SkJpegEncoder::Encode(&buffer, pm, jpegOptions)) {
mask = SkImage::MakeFromEncoded(buffer.detachAsData());
SkASSERT(mask);
if (mask) {
SkMask::FreeImage(dstMask.fImage);
}
}
}
if (!mask) {
mask = SkImage::MakeFromRaster(
pm, [](const void* p, void*) { SkMask::FreeImage((void*)p); }, nullptr);
}
SkIRect dstMaskBounds = dstMask.fBounds;
sk_sp<SkImage> mask = mask_to_greyscale_image(&dstMask);
// PDF doesn't seem to allow masking vector graphics with an Image XObject.
// Must mask with a Form XObject.
sk_sp<SkPDFDevice> maskDevice(SkPDFDevice::CreateUnflipped(fPageSize, fRasterDpi, fDocument));
sk_sp<SkPDFDevice> maskDevice = this->makeCongruentDevice();
{
SkPDFCanvas canvas(maskDevice);
canvas.drawImage(mask, dstMask.fBounds.x(), dstMask.fBounds.y());
canvas.drawImage(mask, dstMaskBounds.x(), dstMaskBounds.y());
}
sk_sp<SkPDFDict> sMaskGS = SkPDFGraphicState::GetSMaskGraphicState(
maskDevice->makeFormXObjectFromDevice(), false,
SkPDFGraphicState::kLuminosity_SMaskMode, fDocument->canon());
maskDevice = nullptr;
if (!ctm.isIdentity() && paint->getShader()) {
transform_shader(paint.writable(), ctm); // Since we are using identity matrix.
}
@ -879,18 +877,25 @@ void SkPDFDevice::internalDrawPathWithFilter(const SkClipStack& clipStack,
if (!content.entry()) {
return;
}
SkPDFUtils::ApplyGraphicState(this->addGraphicStateResource(sMaskGS.get()),
&content.entry()->fContent);
this->addSMaskGraphicState(std::move(maskDevice), content.stream());
SkPDFUtils::AppendRectangle(SkRect::Make(dstMaskBounds), content.stream());
SkPDFUtils::PaintPath(SkPaint::kFill_Style, path.getFillType(), content.stream());
this->clearMaskOnGraphicState(content.stream());
}
SkRect dstBounds = SkRect::Make(dstMask.fBounds);
SkPDFUtils::AppendRectangle(dstBounds, &content.entry()->fContent);
SkPDFUtils::PaintPath(SkPaint::kFill_Style, path.getFillType(), &content.entry()->fContent);
void SkPDFDevice::addSMaskGraphicState(sk_sp<SkPDFDevice> maskDevice,
SkDynamicMemoryWStream* contentStream) {
sk_sp<SkPDFDict> sMaskGS = SkPDFGraphicState::GetSMaskGraphicState(
maskDevice->makeFormXObjectFromDevice(), false,
SkPDFGraphicState::kLuminosity_SMaskMode, this->getCanon());
SkPDFUtils::ApplyGraphicState(this->addGraphicStateResource(sMaskGS.get()), contentStream);
}
void SkPDFDevice::clearMaskOnGraphicState(SkDynamicMemoryWStream* contentStream) {
// The no-softmask graphic state is used to "turn off" the mask for later draw calls.
auto noSMaskGS = SkPDFUtils::GetCachedT(&fDocument->canon()->fNoSmaskGraphicState,
auto noSMaskGS = SkPDFUtils::GetCachedT(&this->getCanon()->fNoSmaskGraphicState,
&SkPDFGraphicState::MakeNoSmaskGraphicState);
SkPDFUtils::ApplyGraphicState(this->addGraphicStateResource(noSMaskGS.get()),
&content.entry()->fContent);
SkPDFUtils::ApplyGraphicState(this->addGraphicStateResource(noSMaskGS.get()), contentStream);
}
void SkPDFDevice::internalDrawPath(const SkClipStack& clipStack,
@ -967,12 +972,9 @@ void SkPDFDevice::internalDrawPath(const SkClipStack& clipStack,
paint.getStyle() == SkPaint::kFill_Style ||
(paint.getStrokeCap() != SkPaint::kRound_Cap &&
paint.getStrokeCap() != SkPaint::kSquare_Cap);
SkPDFUtils::EmitPath(*pathPtr, paint.getStyle(),
consumeDegeratePathSegments,
&content.entry()->fContent,
SkPDFUtils::EmitPath(*pathPtr, paint.getStyle(), consumeDegeratePathSegments, content.stream(),
tolerance);
SkPDFUtils::PaintPath(paint.getStyle(), pathPtr->getFillType(),
&content.entry()->fContent);
SkPDFUtils::PaintPath(paint.getStyle(), pathPtr->getFillType(), content.stream());
}
@ -1537,7 +1539,7 @@ void SkPDFDevice::internalDrawText(
if (!content.entry()) {
return;
}
SkDynamicMemoryWStream* out = &content.entry()->fContent;
SkDynamicMemoryWStream* out = content.stream();
const SkTDArray<SkUnichar>& glyphToUnicode = metrics->fGlyphToUnicode;
out->writeText("BT\n");
@ -1732,8 +1734,7 @@ void SkPDFDevice::drawDevice(SkBaseDevice* device, int x, int y, const SkPaint&
}
sk_sp<SkPDFObject> xObject = pdfDevice->makeFormXObjectFromDevice();
SkPDFUtils::DrawFormXObject(this->addXObjectResource(xObject.get()),
&content.entry()->fContent);
SkPDFUtils::DrawFormXObject(this->addXObjectResource(xObject.get()), content.stream());
}
sk_sp<SkSurface> SkPDFDevice::makeSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
@ -1920,14 +1921,12 @@ void SkPDFDevice::drawFormXObjectWithMask(int xObjectIndex,
if (!content.entry()) {
return;
}
SkPDFUtils::ApplyGraphicState(addGraphicStateResource(sMaskGS.get()),
&content.entry()->fContent);
SkPDFUtils::DrawFormXObject(xObjectIndex, &content.entry()->fContent);
SkPDFUtils::ApplyGraphicState(addGraphicStateResource(sMaskGS.get()), content.stream());
SkPDFUtils::DrawFormXObject(xObjectIndex, content.stream());
sMaskGS = SkPDFUtils::GetCachedT(&fDocument->canon()->fNoSmaskGraphicState,
&SkPDFGraphicState::MakeNoSmaskGraphicState);
SkPDFUtils::ApplyGraphicState(addGraphicStateResource(sMaskGS.get()),
&content.entry()->fContent);
SkPDFUtils::ApplyGraphicState(addGraphicStateResource(sMaskGS.get()), content.stream());
}
SkPDFDevice::ContentEntry* SkPDFDevice::setUpContentEntry(const SkClipStack& clipStack,
@ -2033,8 +2032,7 @@ void SkPDFDevice::finishContentEntry(SkBlendMode blendMode,
blendMode == SkBlendMode::kSrcATop) {
ScopedContentEntry content(this, fExistingClipStack, SkMatrix::I(), stockPaint);
// TODO: addXObjectResource take sk_sp
SkPDFUtils::DrawFormXObject(this->addXObjectResource(dst.get()),
&content.entry()->fContent);
SkPDFUtils::DrawFormXObject(this->addXObjectResource(dst.get()), content.stream());
return;
} else {
blendMode = SkBlendMode::kClear;
@ -2078,9 +2076,8 @@ void SkPDFDevice::finishContentEntry(SkBlendMode blendMode,
blendMode == SkBlendMode::kDstATop) {
ScopedContentEntry content(this, fExistingClipStack, SkMatrix::I(), stockPaint);
if (content.entry()) {
SkPDFUtils::DrawFormXObject(
this->addXObjectResource(srcFormXObject.get()),
&content.entry()->fContent);
SkPDFUtils::DrawFormXObject(this->addXObjectResource(srcFormXObject.get()),
content.stream());
}
if (blendMode == SkBlendMode::kSrc) {
return;
@ -2089,8 +2086,7 @@ void SkPDFDevice::finishContentEntry(SkBlendMode blendMode,
ScopedContentEntry content(this, fExistingClipStack,
SkMatrix::I(), stockPaint);
if (content.entry()) {
SkPDFUtils::DrawFormXObject(this->addXObjectResource(dst.get()),
&content.entry()->fContent);
SkPDFUtils::DrawFormXObject(this->addXObjectResource(dst.get()), content.stream());
}
}
@ -2409,8 +2405,7 @@ void SkPDFDevice::internalDrawImage(const SkMatrix& origMatrix,
fDocument->canon()->fPDFBitmapMap.set(key, pdfimage);
}
// TODO(halcanary): addXObjectResource() should take a sk_sp<SkPDFObject>
SkPDFUtils::DrawFormXObject(this->addXObjectResource(pdfimage.get()),
&content.entry()->fContent);
SkPDFUtils::DrawFormXObject(this->addXObjectResource(pdfimage.get()), content.stream());
}
///////////////////////////////////////////////////////////////////////////////////////////////////

View File

@ -59,17 +59,19 @@ public:
* for early serializing of large immutable objects, such
* as images (via SkPDFDocument::serialize()).
*/
static SkPDFDevice* Create(SkISize pageSize,
SkScalar rasterDpi,
SkPDFDocument* doc) {
return new SkPDFDevice(pageSize, rasterDpi, doc, true);
static sk_sp<SkPDFDevice> Make(SkISize pageSize, SkScalar rasterDpi, SkPDFDocument* doc) {
return sk_sp<SkPDFDevice>(new SkPDFDevice(pageSize, rasterDpi, doc, true));
}
/** Create a PDF drawing context without fipping the y-axis. */
static SkPDFDevice* CreateUnflipped(SkISize pageSize,
SkScalar rasterDpi,
SkPDFDocument* doc) {
return new SkPDFDevice(pageSize, rasterDpi, doc, false);
static sk_sp<SkPDFDevice> MakeUnflipped(SkISize pageSize,
SkScalar rasterDpi,
SkPDFDocument* doc) {
return sk_sp<SkPDFDevice>(new SkPDFDevice(pageSize, rasterDpi, doc, false));
}
sk_sp<SkPDFDevice> makeCongruentDevice() {
return sk_sp<SkPDFDevice>(new SkPDFDevice(fPageSize, fRasterDpi, fDocument, false));
}
~SkPDFDevice() override;
@ -283,12 +285,10 @@ private:
const SkPaint& paint, bool pathIsMutable,
const SkMatrix* prePathMatrix = nullptr);
typedef SkClipStackDevice INHERITED;
void addSMaskGraphicState(sk_sp<SkPDFDevice> maskDevice, SkDynamicMemoryWStream*);
void clearMaskOnGraphicState(SkDynamicMemoryWStream*);
// TODO(edisonn): Only SkDocument_PDF and SkPDFImageShader should be able to create
// an SkPDFDevice
//friend class SkDocument_PDF;
//friend class SkPDFImageShader;
typedef SkClipStackDevice INHERITED;
};
#endif

View File

@ -215,8 +215,7 @@ SkCanvas* SkPDFDocument::onBeginPage(SkScalar width, SkScalar height,
}
SkISize pageSize = SkISize::Make(
SkScalarRoundToInt(width), SkScalarRoundToInt(height));
fPageDevice.reset(
SkPDFDevice::Create(pageSize, fRasterDpi, this));
fPageDevice = SkPDFDevice::Make(pageSize, fRasterDpi, this);
fCanvas.reset(new SkPDFCanvas(fPageDevice));
if (SkRect::MakeWH(width, height) != trimBox) {
fCanvas->clipRect(trimBox);

View File

@ -991,8 +991,7 @@ static sk_sp<SkPDFStream> make_image_shader(SkPDFDocument* doc,
SkISize size = SkISize::Make(SkScalarRoundToInt(deviceBounds.width()),
SkScalarRoundToInt(deviceBounds.height()));
sk_sp<SkPDFDevice> patternDevice(
SkPDFDevice::CreateUnflipped(size, dpi, doc));
sk_sp<SkPDFDevice> patternDevice = SkPDFDevice::MakeUnflipped(size, dpi, doc);
SkCanvas canvas(patternDevice.get());
SkRect patternBBox;