diff options
Diffstat (limited to 'src/pdf/SkPDFBitmap.cpp')
-rw-r--r-- | src/pdf/SkPDFBitmap.cpp | 139 |
1 files changed, 102 insertions, 37 deletions
diff --git a/src/pdf/SkPDFBitmap.cpp b/src/pdf/SkPDFBitmap.cpp index 8806150ce0..a3f4632e25 100644 --- a/src/pdf/SkPDFBitmap.cpp +++ b/src/pdf/SkPDFBitmap.cpp @@ -7,26 +7,34 @@ #include "src/pdf/SkPDFBitmap.h" +#include "include/codec/SkCodec.h" #include "include/codec/SkEncodedImageFormat.h" +#include "include/codec/SkJpegDecoder.h" #include "include/core/SkBitmap.h" #include "include/core/SkData.h" #include "include/core/SkExecutor.h" #include "include/core/SkImage.h" #include "include/core/SkStream.h" +#include "include/encode/SkICC.h" #include "include/encode/SkJpegEncoder.h" #include "include/private/SkColorData.h" #include "include/private/base/SkTo.h" #include "src/core/SkImageInfoPriv.h" #include "src/pdf/SkDeflate.h" -#include "src/pdf/SkJpegInfo.h" #include "src/pdf/SkPDFDocumentPriv.h" #include "src/pdf/SkPDFTypes.h" +#include "src/pdf/SkPDFUnion.h" #include "src/pdf/SkPDFUtils.h" -//////////////////////////////////////////////////////////////////////////////// + +/*static*/ const SkEncodedInfo& SkPDFBitmap::GetEncodedInfo(SkCodec& codec) { + return codec.getEncodedInfo(); +} + +namespace { // write a single byte to a stream n times. -static void fill_stream(SkWStream* out, char value, size_t n) { +void fill_stream(SkWStream* out, char value, size_t n) { char buffer[4096]; memset(buffer, value, sizeof(buffer)); for (size_t i = 0; i < n / sizeof(buffer); ++i) { @@ -44,7 +52,7 @@ static void fill_stream(SkWStream* out, char value, size_t n) { channel goes to black, and the should-be-transparent pixels are rendered as grey because of the separate soft mask and color resizing. e.g.: gm/bitmappremul.cpp */ -static SkColor get_neighbor_avg_color(const SkPixmap& bm, int xOrig, int yOrig) { +SkColor get_neighbor_avg_color(const SkPixmap& bm, int xOrig, int yOrig) { SkASSERT(kBGRA_8888_SkColorType == bm.colorType()); unsigned r = 0, g = 0, b = 0, n = 0; // Clamp the range to the edge of the bitmap. @@ -68,24 +76,22 @@ static SkColor get_neighbor_avg_color(const SkPixmap& bm, int xOrig, int yOrig) : SK_ColorTRANSPARENT; } -namespace { enum class SkPDFStreamFormat { DCT, Flate, Uncompressed }; -} template <typename T> -static void emit_image_stream(SkPDFDocument* doc, - SkPDFIndirectReference ref, - T writeStream, - SkISize size, - const char* colorSpace, - SkPDFIndirectReference sMask, - int length, - SkPDFStreamFormat format) { +void emit_image_stream(SkPDFDocument* doc, + SkPDFIndirectReference ref, + T writeStream, + SkISize size, + SkPDFUnion&& colorSpace, + SkPDFIndirectReference sMask, + int length, + SkPDFStreamFormat format) { SkPDFDict pdfDict("XObject"); pdfDict.insertName("Subtype", "Image"); pdfDict.insertInt("Width", size.width()); pdfDict.insertInt("Height", size.height()); - pdfDict.insertName("ColorSpace", colorSpace); + pdfDict.insertUnion("ColorSpace", std::move(colorSpace)); if (sMask) { pdfDict.insertRef("SMask", sMask); } @@ -113,7 +119,7 @@ static void emit_image_stream(SkPDFDocument* doc, doc->emitStream(pdfDict, std::move(writeStream), ref); } -static void do_deflated_alpha(const SkPixmap& pm, SkPDFDocument* doc, SkPDFIndirectReference ref) { +void do_deflated_alpha(const SkPixmap& pm, SkPDFDocument* doc, SkPDFIndirectReference ref) { SkPDF::Metadata::CompressionLevel compressionLevel = doc->metadata().fCompressionLevel; SkPDFStreamFormat format = compressionLevel == SkPDF::Metadata::CompressionLevel::None ? SkPDFStreamFormat::Uncompressed @@ -156,14 +162,37 @@ static void do_deflated_alpha(const SkPixmap& pm, SkPDFDocument* doc, SkPDFIndir #endif int length = SkToInt(buffer.bytesWritten()); emit_image_stream(doc, ref, [&buffer](SkWStream* stream) { buffer.writeToAndReset(stream); }, - pm.info().dimensions(), "DeviceGray", SkPDFIndirectReference(), - length, format); + pm.info().dimensions(), SkPDFUnion::Name("DeviceGray"), + SkPDFIndirectReference(), length, format); } -static void do_deflated_image(const SkPixmap& pm, - SkPDFDocument* doc, - bool isOpaque, - SkPDFIndirectReference ref) { +SkPDFUnion write_icc_profile(SkPDFDocument* doc, sk_sp<SkData>&& icc, int channels) { + SkPDFIndirectReference iccStreamRef; + { + static SkMutex iccProfileMapMutex; + SkAutoMutexExclusive lock(iccProfileMapMutex); + + SkPDFIndirectReference* ref = doc->fICCProfileMap.find(SkPDFIccProfileKey{icc}); + if (ref) { + iccStreamRef = *ref; + } else { + std::unique_ptr<SkPDFDict> iccStreamDict = SkPDFMakeDict(); + iccStreamDict->insertInt("N", channels); + iccStreamRef = SkPDFStreamOut(std::move(iccStreamDict), SkMemoryStream::Make(icc), doc); + doc->fICCProfileMap.set(SkPDFIccProfileKey{icc}, iccStreamRef); + } + } + + std::unique_ptr<SkPDFArray> iccPDF = SkPDFMakeArray(); + iccPDF->appendName("ICCBased"); + iccPDF->appendRef(iccStreamRef); + return SkPDFUnion::Object(std::move(iccPDF)); +} + +void do_deflated_image(const SkPixmap& pm, + SkPDFDocument* doc, + bool isOpaque, + SkPDFIndirectReference ref) { SkPDFIndirectReference sMask; if (!isOpaque) { sMask = doc->reserveRef(); @@ -179,18 +208,22 @@ static void do_deflated_image(const SkPixmap& pm, deflateWStream.emplace(&buffer, SkToInt(compressionLevel)); stream = &*deflateWStream; } - const char* colorSpace = "DeviceGray"; + SkPDFUnion colorSpace = SkPDFUnion::Name("DeviceGray"); + int channels; switch (pm.colorType()) { case kAlpha_8_SkColorType: + channels = 1; fill_stream(stream, '\x00', pm.width() * pm.height()); break; case kGray_8_SkColorType: + channels = 1; SkASSERT(sMask.fValue = -1); SkASSERT(pm.rowBytes() == (size_t)pm.width()); stream->write(pm.addr8(), pm.width() * pm.height()); break; default: - colorSpace = "DeviceRGB"; + colorSpace = SkPDFUnion::Name("DeviceRGB"); + channels = 3; SkASSERT(pm.alphaType() == kUnpremul_SkAlphaType); SkASSERT(pm.colorType() == kBGRA_8888_SkColorType); SkASSERT(pm.rowBytes() == (size_t)pm.width() * 4); @@ -219,26 +252,40 @@ static void do_deflated_image(const SkPixmap& pm, if (deflateWStream) { deflateWStream->finalize(); } + + if (pm.colorSpace()) { + skcms_ICCProfile iccProfile; + pm.colorSpace()->toProfile(&iccProfile); + sk_sp<SkData> iccData = SkWriteICCProfile(&iccProfile, ""); + colorSpace = write_icc_profile(doc, std::move(iccData), channels); + } + #ifdef SK_PDF_BASE85_BINARY SkPDFUtils::Base85Encode(buffer.detachAsStream(), &buffer); #endif int length = SkToInt(buffer.bytesWritten()); emit_image_stream(doc, ref, [&buffer](SkWStream* stream) { buffer.writeToAndReset(stream); }, - pm.info().dimensions(), colorSpace, sMask, length, format); + pm.info().dimensions(), std::move(colorSpace), sMask, length, format); if (!isOpaque) { do_deflated_alpha(pm, doc, sMask); } } -static bool do_jpeg(sk_sp<SkData> data, SkPDFDocument* doc, SkISize size, - SkPDFIndirectReference ref) { - SkISize jpegSize; - SkEncodedInfo::Color jpegColorType; - SkEncodedOrigin exifOrientation; - if (!SkGetJpegInfo(data->data(), data->size(), &jpegSize, - &jpegColorType, &exifOrientation)) { +bool do_jpeg(sk_sp<SkData> data, SkColorSpace* imageColorSpace, SkPDFDocument* doc, SkISize size, + SkPDFIndirectReference ref) { + static constexpr const SkCodecs::Decoder decoders[] = { + SkJpegDecoder::Decoder(), + }; + std::unique_ptr<SkCodec> codec = SkCodec::MakeFromData(data, decoders); + if (!codec) { return false; } + + SkISize jpegSize = codec->dimensions(); + const SkEncodedInfo& encodedInfo = SkPDFBitmap::GetEncodedInfo(*codec); + SkEncodedInfo::Color jpegColorType = encodedInfo.color(); + SkEncodedOrigin exifOrientation = codec->getOrigin(); + bool yuv = jpegColorType == SkEncodedInfo::kYUV_Color; bool goodColorType = yuv || jpegColorType == SkEncodedInfo::kGray_Color; if (jpegSize != size // Safety check. @@ -252,14 +299,28 @@ static bool do_jpeg(sk_sp<SkData> data, SkPDFDocument* doc, SkISize size, data = buffer.detachAsData(); #endif + int channels = yuv ? 3 : 1; + SkPDFUnion colorSpace = yuv ? SkPDFUnion::Name("DeviceRGB") : SkPDFUnion::Name("DeviceGray"); + if (sk_sp<SkData> encodedIccProfileData = encodedInfo.profileData()) { + colorSpace = write_icc_profile(doc, std::move(encodedIccProfileData), channels); + } else if (const skcms_ICCProfile* codecIccProfile = codec->getICCProfile()) { + sk_sp<SkData> codecIccData = SkWriteICCProfile(codecIccProfile, ""); + colorSpace = write_icc_profile(doc, std::move(codecIccData), channels); + } else if (imageColorSpace) { + skcms_ICCProfile imageIccProfile; + imageColorSpace->toProfile(&imageIccProfile); + sk_sp<SkData> imageIccData = SkWriteICCProfile(&imageIccProfile, ""); + colorSpace = write_icc_profile(doc, std::move(imageIccData), channels); + } + emit_image_stream(doc, ref, [&data](SkWStream* dst) { dst->write(data->data(), data->size()); }, - jpegSize, yuv ? "DeviceRGB" : "DeviceGray", + jpegSize, std::move(colorSpace), SkPDFIndirectReference(), SkToInt(data->size()), SkPDFStreamFormat::DCT); return true; } -static SkBitmap to_pixels(const SkImage* image) { +SkBitmap to_pixels(const SkImage* image) { SkBitmap bm; int w = image->width(), h = image->height(); @@ -273,7 +334,8 @@ static SkBitmap to_pixels(const SkImage* image) { default: { // TODO: makeColorSpace(sRGB) or actually tag the images SkAlphaType at = bm.isOpaque() ? kOpaque_SkAlphaType : kUnpremul_SkAlphaType; - bm.allocPixels(SkImageInfo::Make(w, h, kBGRA_8888_SkColorType, at)); + bm.allocPixels( + SkImageInfo::Make(w, h, kBGRA_8888_SkColorType, at, image->refColorSpace())); } } // TODO: support GPU images in PDFs @@ -291,8 +353,9 @@ void serialize_image(const SkImage* img, SkASSERT(doc); SkASSERT(encodingQuality >= 0); SkISize dimensions = img->dimensions(); + if (sk_sp<SkData> data = img->refEncodedData()) { - if (do_jpeg(std::move(data), doc, dimensions, ref)) { + if (do_jpeg(std::move(data), img->colorSpace(), doc, dimensions, ref)) { return; } } @@ -304,7 +367,7 @@ void serialize_image(const SkImage* img, jOpts.fQuality = encodingQuality; SkDynamicMemoryWStream stream; if (SkJpegEncoder::Encode(&stream, pm, jOpts)) { - if (do_jpeg(stream.detachAsData(), doc, dimensions, ref)) { + if (do_jpeg(stream.detachAsData(), pm.colorSpace(), doc, dimensions, ref)) { return; } } @@ -312,6 +375,8 @@ void serialize_image(const SkImage* img, do_deflated_image(pm, doc, isOpaque, ref); } +} // namespace + SkPDFIndirectReference SkPDFSerializeImage(const SkImage* img, SkPDFDocument* doc, int encodingQuality) { |