#include "SkTime.h"
class SkCanvas;
+class SkPixelSerializer;
class SkWStream;
/** SK_ScalarDefaultDPI is 72 DPI.
static SkDocument* CreatePDF(SkWStream*,
SkScalar dpi = SK_ScalarDefaultRasterDPI);
+ /**
+ * @param jpegEncoder For PDF documents, if a jpegEncoder is set,
+ * use it to encode SkImages and SkBitmaps as [JFIF]JPEGs.
+ * This feature is deprecated and is only supplied for
+ * backwards compatability.
+ *
+ * The prefered method to create PDFs with JPEG images is
+ * to use SkImage::NewFromEncoded() and not jpegEncoder.
+ * Chromium uses NewFromEncoded.
+ *
+ * If the encoder is unset, or if jpegEncoder->onEncode()
+ * returns NULL, fall back on encoding images losslessly
+ * with Deflate.
+ */
+ static SkDocument* CreatePDF(SkWStream*,
+ SkScalar dpi,
+ SkPixelSerializer* jpegEncoder);
+
/**
* Create a PDF-backed document, writing the results into a file.
*/
public:
SkDocument_PDF(SkWStream* stream,
void (*doneProc)(SkWStream*, bool),
- SkScalar rasterDpi)
+ SkScalar rasterDpi,
+ SkPixelSerializer* jpegEncoder)
: SkDocument(stream, doneProc)
- , fRasterDpi(rasterDpi) {}
+ , fRasterDpi(rasterDpi) {
+ fCanon.fPixelSerializer.reset(SkSafeRef(jpegEncoder));
+ }
virtual ~SkDocument_PDF() {
// subclasses must call close() in their destructors
///////////////////////////////////////////////////////////////////////////////
SkDocument* SkDocument::CreatePDF(SkWStream* stream, SkScalar dpi) {
- return stream ? new SkDocument_PDF(stream, nullptr, dpi) : nullptr;
+ return stream ? new SkDocument_PDF(stream, nullptr, dpi, nullptr) : nullptr;
+}
+
+SkDocument* SkDocument::CreatePDF(SkWStream* stream,
+ SkScalar dpi,
+ SkPixelSerializer* jpegEncoder) {
+ return stream
+ ? new SkDocument_PDF(stream, nullptr, dpi, jpegEncoder)
+ : nullptr;
}
SkDocument* SkDocument::CreatePDF(const char path[], SkScalar dpi) {
return nullptr;
}
auto delete_wstream = [](SkWStream* stream, bool) { delete stream; };
- return new SkDocument_PDF(stream, delete_wstream, dpi);
+ return new SkDocument_PDF(stream, delete_wstream, dpi, nullptr);
}
////////////////////////////////////////////////////////////////////////////////
-SkPDFObject* SkPDFCreateBitmapObject(const SkImage* image) {
+SkPDFObject* SkPDFCreateBitmapObject(const SkImage* image,
+ SkPixelSerializer* pixelSerializer) {
SkAutoTUnref<SkData> data(image->refEncoded());
SkJFIFInfo info;
if (data && SkIsJFIF(data, &info)) {
return new PDFJpegBitmap(info.fSize, data, yuv);
}
}
+
+ if (pixelSerializer) {
+ SkBitmap bm;
+ SkAutoPixmapUnlock apu;
+ if (as_IB(image)->getROPixels(&bm) && bm.requestLock(&apu)) {
+ data.reset(pixelSerializer->encode(apu.pixmap()));
+ if (data && SkIsJFIF(data, &info)) {
+ bool yuv = info.fType == SkJFIFInfo::kYCbCr;
+ if (info.fSize == image->dimensions()) { // Sanity check.
+ return new PDFJpegBitmap(info.fSize, data, yuv);
+ }
+ }
+ }
+ }
+
SkPDFObject* smask =
image_compute_is_opaque(image) ? nullptr : new PDFAlphaBitmap(image);
#ifdef SK_PDF_IMAGE_STATS
* It is designed to use a minimal amout of memory, aside from refing
* the image, and its emitObject() does not cache any data.
*/
-SkPDFObject* SkPDFCreateBitmapObject(const SkImage*);
+SkPDFObject* SkPDFCreateBitmapObject(const SkImage*, SkPixelSerializer*);
#endif // SkPDFBitmap_DEFINED
#include "SkBitmap.h"
#include "SkPDFGraphicState.h"
#include "SkPDFShader.h"
+#include "SkPixelSerializer.h"
#include "SkTDArray.h"
#include "SkTHash.h"
SkTHashMap<uint32_t, bool> fCanEmbedTypeface;
+ SkAutoTUnref<SkPixelSerializer> fPixelSerializer;
+
private:
struct FontRec {
SkPDFFont* fFont;
}
SkAutoTUnref<SkPDFObject> pdfimage(SkSafeRef(fCanon->findPDFBitmap(image)));
if (!pdfimage) {
- pdfimage.reset(SkPDFCreateBitmapObject(image));
+ pdfimage.reset(SkPDFCreateBitmapObject(
+ image, fCanon->fPixelSerializer));
if (!pdfimage) {
return;
}
*/
#include "Test.h"
+#include "Resources.h"
#include "SkCanvas.h"
#include "SkDocument.h"
#include "SkOSFile.h"
#include "SkStream.h"
+#include "SkPixelSerializer.h"
static void test_empty(skiatest::Reporter* reporter) {
SkDynamicMemoryWStream stream;
test_file(reporter);
test_close(reporter);
}
+
+namespace {
+class JPEGSerializer final : public SkPixelSerializer {
+ bool onUseEncodedData(const void*, size_t) override { return true; }
+ SkData* onEncode(const SkPixmap& pixmap) override {
+ SkBitmap bm;
+ return bm.installPixels(pixmap.info(),
+ pixmap.writable_addr(),
+ pixmap.rowBytes(),
+ pixmap.ctable(),
+ nullptr, nullptr)
+ ? SkImageEncoder::EncodeData(bm, SkImageEncoder::kJPEG_Type, 85)
+ : nullptr;
+ }
+};
+} // namespace
+
+size_t count_bytes(const SkBitmap& bm, bool useDCT) {
+ SkDynamicMemoryWStream stream;
+ SkAutoTUnref<SkDocument> doc;
+ if (useDCT) {
+ SkAutoTUnref<SkPixelSerializer> serializer(new JPEGSerializer);
+ doc.reset(SkDocument::CreatePDF(
+ &stream, SK_ScalarDefaultRasterDPI, serializer));
+ } else {
+ doc.reset(SkDocument::CreatePDF(&stream));
+ }
+ SkCanvas* canvas = doc->beginPage(64, 64);
+ canvas->drawBitmap(bm, 0, 0);
+ doc->endPage();
+ doc->close();
+ return stream.bytesWritten();
+}
+
+DEF_TEST(document_dct_encoder, r) {
+ REQUIRE_PDF_DOCUMENT(document_dct_encoder, r);
+ SkBitmap bm;
+ if (GetResourceAsBitmap("mandrill_64.png", &bm)) {
+ // Lossy encoding works better on photographs.
+ REPORTER_ASSERT(r, count_bytes(bm, true) < count_bytes(bm, false));
+ }
+}