aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--gm/gmmain.cpp38
-rw-r--r--gyp/pdf.gyp2
-rw-r--r--include/pdf/SkPDFDevice.h19
-rw-r--r--src/pdf/SkPDFDevice.cpp5
-rw-r--r--src/pdf/SkPDFImage.cpp18
-rw-r--r--src/pdf/SkPDFImage.h10
-rw-r--r--src/pdf/SkPDFImageStream.cpp79
-rw-r--r--src/pdf/SkPDFImageStream.h53
-rw-r--r--src/pdf/SkPDFStream.h43
-rw-r--r--tests/PDFPrimitivesTest.cpp121
-rw-r--r--tools/PdfRenderer.cpp1
-rw-r--r--tools/PdfRenderer.h9
-rw-r--r--tools/render_pdfs_main.cpp51
13 files changed, 420 insertions, 29 deletions
diff --git a/gm/gmmain.cpp b/gm/gmmain.cpp
index 65058d6198..d513a0e1ab 100644
--- a/gm/gmmain.cpp
+++ b/gm/gmmain.cpp
@@ -174,6 +174,8 @@ static PipeFlagComboData gPipeWritingFlagCombos[] = {
| SkGPipeWriter::kSharedAddressSpace_Flag }
};
+static bool encode_to_dct_stream(SkWStream* stream, const SkBitmap& bitmap, const SkIRect& rect);
+
const static ErrorCombination kDefaultIgnorableErrorTypes = ErrorCombination()
.plus(kMissingExpectations_ErrorType)
.plus(kIntentionallySkipped_ErrorType);
@@ -556,6 +558,7 @@ public:
SkScalarRoundToInt(content.height()));
dev = new SkPDFDevice(pageSize, contentSize, initialTransform);
}
+ dev->setDCTEncoder(encode_to_dct_stream);
SkAutoUnref aur(dev);
SkCanvas c(dev);
@@ -1246,6 +1249,37 @@ DEFINE_bool2(verbose, v, false, "Give more detail (e.g. list all GMs run, more i
"each test).");
DEFINE_string2(writePath, w, "", "Write rendered images into this directory.");
DEFINE_string2(writePicturePath, p, "", "Write .skp files into this directory.");
+DEFINE_int32(pdfJpegQuality, -1, "Encodes images in JPEG at quality level N, "
+ "which can be in range 0-100). N = -1 will disable JPEG compression. "
+ "Default is N = 100, maximum quality.");
+
+static bool encode_to_dct_stream(SkWStream* stream, const SkBitmap& bitmap, const SkIRect& rect) {
+ // Filter output of warnings that JPEG is not available for the image.
+ if (bitmap.width() >= 65500 || bitmap.height() >= 65500) return false;
+ if (FLAGS_pdfJpegQuality == -1) return false;
+
+ SkIRect bitmapBounds;
+ SkBitmap subset;
+ const SkBitmap* bitmapToUse = &bitmap;
+ bitmap.getBounds(&bitmapBounds);
+ if (rect != bitmapBounds) {
+ SkAssertResult(bitmap.extractSubset(&subset, rect));
+ bitmapToUse = ⊂
+ }
+
+#if defined(SK_BUILD_FOR_MAC)
+ // Workaround bug #1043 where bitmaps with referenced pixels cause
+ // CGImageDestinationFinalize to crash
+ SkBitmap copy;
+ bitmapToUse->deepCopyTo(&copy, bitmapToUse->config());
+ bitmapToUse = ©
+#endif
+
+ return SkImageEncoder::EncodeStream(stream,
+ *bitmapToUse,
+ SkImageEncoder::kJPEG_Type,
+ FLAGS_pdfJpegQuality);
+}
static int findConfig(const char config[]) {
for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); i++) {
@@ -1804,6 +1838,10 @@ int tool_main(int argc, char** argv) {
}
}
+ if (FLAGS_pdfJpegQuality < -1 || FLAGS_pdfJpegQuality > 100) {
+ gm_fprintf(stderr, "%s\n", "pdfJpegQuality must be in [-1 .. 100] range.");
+ }
+
Iter iter;
GM* gm;
while ((gm = iter.next()) != NULL) {
diff --git a/gyp/pdf.gyp b/gyp/pdf.gyp
index 081df01afd..0e759142ca 100644
--- a/gyp/pdf.gyp
+++ b/gyp/pdf.gyp
@@ -34,6 +34,8 @@
'../src/pdf/SkPDFGraphicState.h',
'../src/pdf/SkPDFImage.cpp',
'../src/pdf/SkPDFImage.h',
+ '../src/pdf/SkPDFImageStream.cpp',
+ '../src/pdf/SkPDFImageStream.h',
'../src/pdf/SkPDFPage.cpp',
'../src/pdf/SkPDFPage.h',
'../src/pdf/SkPDFShader.cpp',
diff --git a/include/pdf/SkPDFDevice.h b/include/pdf/SkPDFDevice.h
index f8261b5da1..7731d3f4ae 100644
--- a/include/pdf/SkPDFDevice.h
+++ b/include/pdf/SkPDFDevice.h
@@ -37,6 +37,8 @@ struct ContentEntry;
struct GraphicStateEntry;
struct NamedDestination;
+typedef bool (*EncodeToDCTStream)(SkWStream* stream, const SkBitmap& bitmap, const SkIRect& rect);
+
/** \class SkPDFDevice
The drawing context for the PDF backend.
@@ -126,6 +128,21 @@ public:
*/
SK_API void setDrawingArea(DrawingArea drawingArea);
+ /** Sets the DCTEncoder for images.
+ * @param encoder The encoder to encode a bitmap as JPEG (DCT).
+ * Result of encodings are cached, if the encoder changes the
+ * behaivor dynamically and an image is added to a second catalog,
+ * we will likely use the result of the first encoding call.
+ * By returning false from the encoder function, the encoder result
+ * is not used.
+ * Callers might not want to encode small images, as the time spent
+ * encoding and decoding might not be worth the space savings,
+ * if any at all.
+ */
+ void setDCTEncoder(EncodeToDCTStream encoder) {
+ fEncoder = encoder;
+ }
+
// PDF specific methods.
/** Returns the resource dictionary for this device.
@@ -230,6 +247,8 @@ private:
// Glyph ids used for each font on this device.
SkTScopedPtr<SkPDFGlyphSetMap> fFontGlyphUsage;
+ EncodeToDCTStream fEncoder;
+
SkPDFDevice(const SkISize& layerSize, const SkClipStack& existingClipStack,
const SkRegion& existingClipRegion);
diff --git a/src/pdf/SkPDFDevice.cpp b/src/pdf/SkPDFDevice.cpp
index 6298f7b943..15da5a5621 100644
--- a/src/pdf/SkPDFDevice.cpp
+++ b/src/pdf/SkPDFDevice.cpp
@@ -591,7 +591,8 @@ SkPDFDevice::SkPDFDevice(const SkISize& pageSize, const SkISize& contentSize,
fContentSize(contentSize),
fLastContentEntry(NULL),
fLastMarginContentEntry(NULL),
- fClipStack(NULL) {
+ fClipStack(NULL),
+ fEncoder(NULL) {
// Skia generally uses the top left as the origin but PDF natively has the
// origin at the bottom left. This matrix corrects for that. But that only
// needs to be done once, we don't do it when layering.
@@ -1822,7 +1823,7 @@ void SkPDFDevice::internalDrawBitmap(const SkMatrix& matrix,
return;
}
- SkPDFImage* image = SkPDFImage::CreateImage(bitmap, subset);
+ SkPDFImage* image = SkPDFImage::CreateImage(bitmap, subset, fEncoder);
if (!image) {
return;
}
diff --git a/src/pdf/SkPDFImage.cpp b/src/pdf/SkPDFImage.cpp
index f7889f16d7..04307be77e 100644
--- a/src/pdf/SkPDFImage.cpp
+++ b/src/pdf/SkPDFImage.cpp
@@ -249,7 +249,8 @@ SkPDFArray* makeIndexedColorSpace(SkColorTable* table) {
// static
SkPDFImage* SkPDFImage::CreateImage(const SkBitmap& bitmap,
- const SkIRect& srcRect) {
+ const SkIRect& srcRect,
+ EncodeToDCTStream encoder) {
if (bitmap.getConfig() == SkBitmap::kNo_Config) {
return NULL;
}
@@ -265,10 +266,12 @@ SkPDFImage* SkPDFImage::CreateImage(const SkBitmap& bitmap,
}
SkPDFImage* image =
- new SkPDFImage(imageData, bitmap, srcRect, false);
+ SkNEW_ARGS(SkPDFImage, (imageData, bitmap, srcRect, false, encoder));
if (alphaData != NULL) {
- image->addSMask(new SkPDFImage(alphaData, bitmap, srcRect, true))->unref();
+ // Don't try to use DCT compression with alpha because alpha is small
+ // anyway and it could lead to artifacts.
+ image->addSMask(SkNEW_ARGS(SkPDFImage, (alphaData, bitmap, srcRect, true, NULL)))->unref();
}
return image;
}
@@ -289,9 +292,12 @@ void SkPDFImage::getResources(const SkTSet<SkPDFObject*>& knownResourceObjects,
GetResourcesHelper(&fResources, knownResourceObjects, newResourceObjects);
}
-SkPDFImage::SkPDFImage(SkStream* imageData, const SkBitmap& bitmap,
- const SkIRect& srcRect, bool doingAlpha) {
- this->setData(imageData);
+SkPDFImage::SkPDFImage(SkStream* imageData,
+ const SkBitmap& bitmap,
+ const SkIRect& srcRect,
+ bool doingAlpha,
+ EncodeToDCTStream encoder)
+ : SkPDFImageStream(imageData, bitmap, srcRect, encoder) {
SkBitmap::Config config = bitmap.getConfig();
bool alphaOnly = (config == SkBitmap::kA1_Config ||
config == SkBitmap::kA8_Config);
diff --git a/src/pdf/SkPDFImage.h b/src/pdf/SkPDFImage.h
index d41466f4fe..31f894086e 100644
--- a/src/pdf/SkPDFImage.h
+++ b/src/pdf/SkPDFImage.h
@@ -10,7 +10,8 @@
#ifndef SkPDFImage_DEFINED
#define SkPDFImage_DEFINED
-#include "SkPDFStream.h"
+#include "SkPDFDevice.h"
+#include "SkPDFImageStream.h"
#include "SkPDFTypes.h"
#include "SkRefCnt.h"
@@ -26,7 +27,7 @@ struct SkIRect;
// We could play the same trick here as is done in SkPDFGraphicState, storing
// a copy of the Bitmap object (not the pixels), the pixel generation number,
// and settings used from the paint to canonicalize image objects.
-class SkPDFImage : public SkPDFStream {
+class SkPDFImage : public SkPDFImageStream {
public:
/** Create a new Image XObject to represent the passed bitmap.
* @param bitmap The image to encode.
@@ -36,7 +37,8 @@ public:
* the given parameters.
*/
static SkPDFImage* CreateImage(const SkBitmap& bitmap,
- const SkIRect& srcRect);
+ const SkIRect& srcRect,
+ EncodeToDCTStream encoder);
virtual ~SkPDFImage();
@@ -63,7 +65,7 @@ private:
* @param paint Used to calculate alpha, masks, etc.
*/
SkPDFImage(SkStream* imageData, const SkBitmap& bitmap,
- const SkIRect& srcRect, bool alpha);
+ const SkIRect& srcRect, bool alpha, EncodeToDCTStream encoder);
};
#endif
diff --git a/src/pdf/SkPDFImageStream.cpp b/src/pdf/SkPDFImageStream.cpp
new file mode 100644
index 0000000000..7130f2b7ca
--- /dev/null
+++ b/src/pdf/SkPDFImageStream.cpp
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#include "SkData.h"
+#include "SkFlate.h"
+#include "SkPDFCatalog.h"
+#include "SkPDFImageStream.h"
+#include "SkStream.h"
+
+#define kNoColorTransform 0
+
+static bool skip_compression(SkPDFCatalog* catalog) {
+ return SkToBool(catalog->getDocumentFlags() &
+ SkPDFDocument::kFavorSpeedOverSize_Flags);
+}
+
+// TODO(edisonn): Use SkData (after removing deprecated constructor in SkPDFStream).
+SkPDFImageStream::SkPDFImageStream(SkStream* stream,
+ const SkBitmap& bitmap,
+ const SkIRect& srcRect,
+ EncodeToDCTStream encoder)
+ : SkPDFStream(stream),
+ fBitmap(bitmap),
+ fSrcRect(srcRect),
+ fEncoder(encoder) {
+}
+
+SkPDFImageStream::SkPDFImageStream(const SkPDFImageStream& pdfStream)
+ : SkPDFStream(pdfStream),
+ fBitmap(pdfStream.fBitmap),
+ fSrcRect(pdfStream.fSrcRect),
+ fEncoder(pdfStream.fEncoder) {
+}
+
+SkPDFImageStream::~SkPDFImageStream() {}
+
+bool SkPDFImageStream::populate(SkPDFCatalog* catalog) {
+ if (getState() == kUnused_State) {
+ if (!skip_compression(catalog)) {
+ SkDynamicMemoryWStream dctCompressedWStream;
+ if (!fEncoder || !fEncoder(&dctCompressedWStream, fBitmap, fSrcRect)) {
+ return INHERITED::populate(catalog);
+ }
+
+ if (dctCompressedWStream.getOffset() < getData()->getLength()) {
+ SkData* data = dctCompressedWStream.copyToData();
+ SkMemoryStream* stream = SkNEW_ARGS(SkMemoryStream, (data));
+ setData(stream);
+ stream->unref();
+ if (data) {
+ // copyToData and new SkMemoryStream both call ref(), supress one.
+ data->unref();
+ }
+
+ insertName("Filter", "DCTDecode");
+ insertInt("ColorTransform", kNoColorTransform);
+ setState(kCompressed_State);
+ }
+ }
+ setState(kNoCompression_State);
+ insertInt("Length", getData()->getLength());
+ } else if (getState() == kNoCompression_State && !skip_compression(catalog) &&
+ (SkFlate::HaveFlate() || fEncoder)) {
+ // Compression has not been requested when the stream was first created.
+ // But a new Catalog would want it compressed.
+ if (!getSubstitute()) {
+ SkPDFImageStream* substitute = SkNEW_ARGS(SkPDFImageStream, (*this));
+ setSubstitute(substitute);
+ catalog->setSubstitute(this, substitute);
+ }
+ return false;
+ }
+ return true;
+}
diff --git a/src/pdf/SkPDFImageStream.h b/src/pdf/SkPDFImageStream.h
new file mode 100644
index 0000000000..c518081518
--- /dev/null
+++ b/src/pdf/SkPDFImageStream.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkPDFImageStream_DEFINED
+#define SkPDFImageStream_DEFINED
+
+#include "SkBitmap.h"
+#include "SkPDFDevice.h"
+#include "SkPDFStream.h"
+#include "SkPDFTypes.h"
+#include "SkRect.h"
+#include "SkRefCnt.h"
+#include "SkStream.h"
+#include "SkTemplates.h"
+
+class SkPDFCatalog;
+
+/** \class SkPDFImageStream
+
+ An image stream object in a PDF. Note, all streams must be indirect objects
+ (via SkObjRef).
+ This class is similar to SkPDFStream, but it is also able to use image
+ specific compression. Currently we support DCT(jpeg) and flate(zip).
+*/
+class SkPDFImageStream : public SkPDFStream {
+public:
+ /** Create a PDF stream with the same content and dictionary entries
+ * as the passed one.
+ */
+ explicit SkPDFImageStream(const SkPDFImageStream& pdfStream);
+ virtual ~SkPDFImageStream();
+
+protected:
+ SkPDFImageStream(SkStream* stream, const SkBitmap& bitmap,
+ const SkIRect& srcRect, EncodeToDCTStream encoder);
+
+ // Populate the stream dictionary. This method returns false if
+ // fSubstitute should be used.
+ virtual bool populate(SkPDFCatalog* catalog);
+
+private:
+ const SkBitmap fBitmap;
+ const SkIRect fSrcRect;
+ EncodeToDCTStream fEncoder;
+
+ typedef SkPDFStream INHERITED;
+};
+
+#endif
diff --git a/src/pdf/SkPDFStream.h b/src/pdf/SkPDFStream.h
index 6f7a08eb81..4b8153f9ab 100644
--- a/src/pdf/SkPDFStream.h
+++ b/src/pdf/SkPDFStream.h
@@ -44,32 +44,53 @@ public:
virtual size_t getOutputSize(SkPDFCatalog* catalog, bool indirect);
protected:
+ enum State {
+ kUnused_State, //!< The stream hasn't been requested yet.
+ kNoCompression_State, //!< The stream's been requested in an
+ // uncompressed form.
+ kCompressed_State, //!< The stream's already been compressed.
+ };
+
/* Create a PDF stream with no data. The setData method must be called to
* set the data.
*/
SkPDFStream();
+ // Populate the stream dictionary. This method returns false if
+ // fSubstitute should be used.
+ virtual bool populate(SkPDFCatalog* catalog);
+
+ void setSubstitute(SkPDFStream* stream) {
+ fSubstitute.reset(stream);
+ }
+
+ SkPDFStream* getSubstitute() {
+ return fSubstitute.get();
+ }
+
void setData(SkStream* stream);
+ SkStream* getData() {
+ return fData.get();
+ }
+
+ void setState(State state) {
+ fState = state;
+ }
+
+ State getState() {
+ return fState;
+ }
+
private:
- enum State {
- kUnused_State, //!< The stream hasn't been requested yet.
- kNoCompression_State, //!< The stream's been requested in an
- // uncompressed form.
- kCompressed_State, //!< The stream's already been compressed.
- };
// Indicates what form (or if) the stream has been requested.
State fState;
-
+
// TODO(vandebo): Use SkData (after removing deprecated constructor).
SkAutoTUnref<SkStream> fData;
SkAutoTUnref<SkPDFStream> fSubstitute;
typedef SkPDFDict INHERITED;
-
- // Populate the stream dictionary. This method returns false if
- // fSubstitute should be used.
- bool populate(SkPDFCatalog* catalog);
};
#endif
diff --git a/tests/PDFPrimitivesTest.cpp b/tests/PDFPrimitivesTest.cpp
index 3b54d1772d..cf929b4488 100644
--- a/tests/PDFPrimitivesTest.cpp
+++ b/tests/PDFPrimitivesTest.cpp
@@ -6,11 +6,13 @@
* found in the LICENSE file.
*/
-
#include "Test.h"
+#include "SkBitmap.h"
#include "SkCanvas.h"
#include "SkData.h"
#include "SkFlate.h"
+#include "SkImageEncoder.h"
+#include "SkMatrix.h"
#include "SkPDFCatalog.h"
#include "SkPDFDevice.h"
#include "SkPDFStream.h"
@@ -37,6 +39,11 @@ private:
SkTDArray<SkPDFObject*> fResources;
};
+static bool encode_to_dct_stream(SkWStream* stream, const SkBitmap& bitmap, const SkIRect& rect) {
+ stream->writeText("DCT compessed stream.");
+ return true;
+}
+
static bool stream_equals(const SkDynamicMemoryWStream& stream, size_t offset,
const void* buffer, size_t len) {
SkAutoDataUnref data(stream.copyToData());
@@ -46,6 +53,20 @@ static bool stream_equals(const SkDynamicMemoryWStream& stream, size_t offset,
return memcmp(data->bytes() + offset, buffer, len) == 0;
}
+static bool stream_contains(const SkDynamicMemoryWStream& stream,
+ const char* buffer) {
+ SkAutoDataUnref data(stream.copyToData());
+ int len = strlen(buffer); // our buffer does not have EOSs.
+
+ for (int offset = 0 ; offset < (int)data->size() - len; offset++) {
+ if (memcmp(data->bytes() + offset, buffer, len) == 0) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
static void CheckObjectOutput(skiatest::Reporter* reporter, SkPDFObject* obj,
const char* expectedData, size_t expectedSize,
bool indirect, bool compression) {
@@ -219,6 +240,102 @@ static void TestSubstitute(skiatest::Reporter* reporter) {
buffer.getOffset()));
}
+// Create a bitmap that would be easier to be compressed in a JPEG than ZIP.
+static void setup_jpegBitmap(SkBitmap* bitmap, int width, int height) {
+ bitmap->setConfig(SkBitmap::kARGB_8888_Config, width, height);
+ bitmap->allocPixels();
+ for (int y = 0; y < bitmap->height(); y++) {
+ for (int x = 0; x < bitmap->width(); x++) {
+ *bitmap->getAddr32(x, y) =
+ SkColorSetRGB(0 + y % 20 + 128 + 100 * cos(x * 0.01F),
+ 1 + y % 20 + 128 + 100 * cos(x * 0.1F),
+ 2 + y % 20 + 128 + 100 * cos(x * 1.0F));
+ }
+ }
+}
+
+// Create a bitmap that would be very eficiently compressed in a ZIP.
+static void setup_solidBitmap(SkBitmap* bitmap, int width, int height) {
+ bitmap->setConfig(SkBitmap::kARGB_8888_Config, width, height);
+ bitmap->allocPixels();
+ bitmap->eraseColor(SK_ColorWHITE);
+}
+
+static void TestImage(skiatest::Reporter* reporter, const SkBitmap& bitmap,
+ const char* expected, bool useDCTEncoder) {
+ SkISize pageSize = SkISize::Make(bitmap.width(), bitmap.height());
+ SkPDFDevice* dev = new SkPDFDevice(pageSize, pageSize, SkMatrix::I());
+
+ if (useDCTEncoder) {
+ dev->setDCTEncoder(encode_to_dct_stream);
+ }
+
+ SkCanvas c(dev);
+ c.drawBitmap(bitmap, 0, 0, NULL);
+
+ SkPDFDocument doc;
+ doc.appendPage(dev);
+
+ SkDynamicMemoryWStream stream;
+ doc.emitPDF(&stream);
+
+ REPORTER_ASSERT(reporter, stream_contains(stream, expected));
+}
+
+static void TestUncompressed(skiatest::Reporter* reporter) {
+ SkBitmap bitmap;
+ setup_solidBitmap(&bitmap, 1, 1);
+ TestImage(reporter, bitmap,
+ "/Subtype /Image\n"
+ "/Width 1\n"
+ "/Height 1\n"
+ "/ColorSpace /DeviceRGB\n"
+ "/BitsPerComponent 8\n"
+ "/Length 3\n"
+ ">> stream",
+ true);
+}
+
+static void TestFlateDecode(skiatest::Reporter* reporter) {
+ if (!SkFlate::HaveFlate()) {
+ return;
+ }
+ SkBitmap bitmap;
+ setup_solidBitmap(&bitmap, 10, 10);
+ TestImage(reporter, bitmap,
+ "/Subtype /Image\n"
+ "/Width 10\n"
+ "/Height 10\n"
+ "/ColorSpace /DeviceRGB\n"
+ "/BitsPerComponent 8\n"
+ "/Filter /FlateDecode\n"
+ "/Length 13\n"
+ ">> stream",
+ false);
+}
+
+static void TestDCTDecode(skiatest::Reporter* reporter) {
+ SkBitmap bitmap;
+ setup_jpegBitmap(&bitmap, 32, 32);
+ TestImage(reporter, bitmap,
+ "/Subtype /Image\n"
+ "/Width 32\n"
+ "/Height 32\n"
+ "/ColorSpace /DeviceRGB\n"
+ "/BitsPerComponent 8\n"
+ "/Filter /DCTDecode\n"
+ "/ColorTransform 0\n"
+ "/Length 21\n"
+ ">> stream",
+ true);
+}
+
+static void TestImages(skiatest::Reporter* reporter) {
+ TestUncompressed(reporter);
+ TestFlateDecode(reporter);
+ TestDCTDecode(reporter);
+}
+
// This test used to assert without the fix submitted for
// http://code.google.com/p/skia/issues/detail?id=1083.
// SKP files might have invalid glyph ids. This test ensures they are ignored,
@@ -324,6 +441,8 @@ static void TestPDFPrimitives(skiatest::Reporter* reporter) {
TestSubstitute(reporter);
test_issue1083();
+
+ TestImages(reporter);
}
#include "TestClassDef.h"
diff --git a/tools/PdfRenderer.cpp b/tools/PdfRenderer.cpp
index 9a4bd38be6..704cbeae51 100644
--- a/tools/PdfRenderer.cpp
+++ b/tools/PdfRenderer.cpp
@@ -36,6 +36,7 @@ SkCanvas* PdfRenderer::setupCanvas() {
SkCanvas* PdfRenderer::setupCanvas(int width, int height) {
SkISize pageSize = SkISize::Make(width, height);
fPDFDevice = SkNEW_ARGS(SkPDFDevice, (pageSize, pageSize, SkMatrix::I()));
+ fPDFDevice->setDCTEncoder(fEncoder);
return SkNEW_ARGS(SkCanvas, (fPDFDevice));
}
diff --git a/tools/PdfRenderer.h b/tools/PdfRenderer.h
index d2d66378b6..d2d1a5c67c 100644
--- a/tools/PdfRenderer.h
+++ b/tools/PdfRenderer.h
@@ -14,6 +14,7 @@
//
#include "SkMath.h"
+#include "SkPDFDevice.h"
#include "SkPicture.h"
#include "SkTypes.h"
#include "SkTDArray.h"
@@ -22,7 +23,6 @@
class SkBitmap;
class SkCanvas;
-class SkPDFDevice;
namespace sk_tools {
@@ -33,9 +33,10 @@ public:
virtual void render() = 0;
virtual void end();
- PdfRenderer()
+ PdfRenderer(EncodeToDCTStream encoder)
: fPicture(NULL)
, fPDFDevice(NULL)
+ , fEncoder(encoder)
{}
void write(SkWStream* stream) const;
@@ -47,7 +48,7 @@ protected:
SkAutoTUnref<SkCanvas> fCanvas;
SkPicture* fPicture;
SkPDFDevice* fPDFDevice;
-
+ EncodeToDCTStream fEncoder;
private:
typedef SkRefCnt INHERITED;
@@ -55,6 +56,8 @@ private:
class SimplePdfRenderer : public PdfRenderer {
public:
+ SimplePdfRenderer(EncodeToDCTStream encoder)
+ : PdfRenderer(encoder) {}
virtual void render() SK_OVERRIDE;
private:
diff --git a/tools/render_pdfs_main.cpp b/tools/render_pdfs_main.cpp
index 1ac02d3fc3..96666acec9 100644
--- a/tools/render_pdfs_main.cpp
+++ b/tools/render_pdfs_main.cpp
@@ -9,6 +9,7 @@
#include "SkDevice.h"
#include "SkGraphics.h"
#include "SkImageDecoder.h"
+#include "SkImageEncoder.h"
#include "SkOSFile.h"
#include "SkPicture.h"
#include "SkStream.h"
@@ -38,7 +39,7 @@ static void usage(const char* argv0) {
SkDebugf("SKP to PDF rendering tool\n");
SkDebugf("\n"
"Usage: \n"
-" %s <input>... -w <outputDir> \n"
+" %s <input>... [-w <outputDir>] [--jpegQuality N] \n"
, argv0);
SkDebugf("\n\n");
SkDebugf(
@@ -47,6 +48,12 @@ static void usage(const char* argv0) {
SkDebugf(
" outputDir: directory to write the rendered pdfs.\n\n");
SkDebugf("\n");
+ SkDebugf(
+" jpegQuality N: encodes images in JPEG at quality level N, which can\n"
+" be in range 0-100).\n"
+" N = -1 will disable JPEG compression.\n"
+" Default is N = 100, maximum quality.\n\n");
+ SkDebugf("\n");
}
/** Replaces the extension of a file.
@@ -71,6 +78,33 @@ static bool replace_filename_extension(SkString* path,
return false;
}
+int gJpegQuality = 100;
+static bool encode_to_dct_stream(SkWStream* stream, const SkBitmap& bitmap, const SkIRect& rect) {
+ if (gJpegQuality == -1) return false;
+
+ SkIRect bitmapBounds;
+ SkBitmap subset;
+ const SkBitmap* bitmapToUse = &bitmap;
+ bitmap.getBounds(&bitmapBounds);
+ if (rect != bitmapBounds) {
+ SkAssertResult(bitmap.extractSubset(&subset, rect));
+ bitmapToUse = &subset;
+ }
+
+#if defined(SK_BUILD_FOR_MAC)
+ // Workaround bug #1043 where bitmaps with referenced pixels cause
+ // CGImageDestinationFinalize to crash
+ SkBitmap copy;
+ bitmapToUse->deepCopyTo(&copy, bitmapToUse->config());
+ bitmapToUse = &copy;
+#endif
+
+ return SkImageEncoder::EncodeStream(stream,
+ *bitmapToUse,
+ SkImageEncoder::kJPEG_Type,
+ gJpegQuality);
+}
+
/** Builds the output filename. path = dir/name, and it replaces expected
* .skp extension with .pdf extention.
* @param path Output filename.
@@ -200,6 +234,19 @@ static void parse_commandline(int argc, char* const argv[],
exit(-1);
}
*outputDir = SkString(*argv);
+ } else if (0 == strcmp(*argv, "--jpegQuality")) {
+ ++argv;
+ if (argv >= stop) {
+ SkDebugf("Missing argument for --jpegQuality\n");
+ usage(argv0);
+ exit(-1);
+ }
+ gJpegQuality = atoi(*argv);
+ if (gJpegQuality < -1 || gJpegQuality > 100) {
+ SkDebugf("Invalid argument for --jpegQuality\n");
+ usage(argv0);
+ exit(-1);
+ }
} else {
inputs->push_back(SkString(*argv));
}
@@ -217,7 +264,7 @@ int tool_main_core(int argc, char** argv) {
SkTArray<SkString> inputs;
SkAutoTUnref<sk_tools::PdfRenderer>
- renderer(SkNEW(sk_tools::SimplePdfRenderer));
+ renderer(SkNEW_ARGS(sk_tools::SimplePdfRenderer, (encode_to_dct_stream)));
SkASSERT(renderer.get());
SkString outputDir;