diff options
-rw-r--r-- | gm/gmmain.cpp | 38 | ||||
-rw-r--r-- | gyp/pdf.gyp | 2 | ||||
-rw-r--r-- | include/pdf/SkPDFDevice.h | 19 | ||||
-rw-r--r-- | src/pdf/SkPDFDevice.cpp | 5 | ||||
-rw-r--r-- | src/pdf/SkPDFImage.cpp | 18 | ||||
-rw-r--r-- | src/pdf/SkPDFImage.h | 10 | ||||
-rw-r--r-- | src/pdf/SkPDFImageStream.cpp | 79 | ||||
-rw-r--r-- | src/pdf/SkPDFImageStream.h | 53 | ||||
-rw-r--r-- | src/pdf/SkPDFStream.h | 43 | ||||
-rw-r--r-- | tests/PDFPrimitivesTest.cpp | 121 | ||||
-rw-r--r-- | tools/PdfRenderer.cpp | 1 | ||||
-rw-r--r-- | tools/PdfRenderer.h | 9 | ||||
-rw-r--r-- | tools/render_pdfs_main.cpp | 51 |
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(©, 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 = ⊂ + } + +#if defined(SK_BUILD_FOR_MAC) + // Workaround bug #1043 where bitmaps with referenced pixels cause + // CGImageDestinationFinalize to crash + SkBitmap copy; + bitmapToUse->deepCopyTo(©, bitmapToUse->config()); + bitmapToUse = © +#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; |