aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen Wagner <bungeman@google.com>2023-12-01 16:40:20 -0500
committerSkCQ <skcq-be@skia-corp.google.com.iam.gserviceaccount.com>2023-12-12 03:26:08 +0000
commiteff8181a629035377ad4e74c24aa250d4c095a90 (patch)
treef1d8b841c1dc3713de222a259a59ca22bf6a32f3
parent6e442416432609be9d1ba8a310d90c16ef9fed7f (diff)
downloadskia-eff8181a629035377ad4e74c24aa250d4c095a90.tar.gz
[pdf] Emit color space of images
This also makes Skia PDF directly depend on the Skia JPEG encoders and decoders instead of making them build time optional. All known users of PDF already depend on these so it should not make a difference in practice. If there is a need to allow other (or no) encoders and decoders that could be considered as a follow up. Bug: chromium:999986,chromium:1465627 Change-Id: I4992b0d9267a30154cd176528fcb830a39a12494 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/784553 Reviewed-by: Brian Osman <brianosman@google.com> Commit-Queue: Ben Wagner <bungeman@google.com>
-rw-r--r--BUILD.gn22
-rw-r--r--gn/pdf.gni1
-rw-r--r--include/codec/SkCodec.h1
-rw-r--r--include/private/SkEncodedInfo.h5
-rw-r--r--public.bzl2
-rw-r--r--relnotes/PDF_jpeg.md4
-rw-r--r--src/codec/SkJpegCodec.cpp9
-rw-r--r--src/codec/SkJpegPriv.h4
-rw-r--r--src/pdf/BUILD.bazel14
-rw-r--r--src/pdf/SkJpegInfo.h18
-rw-r--r--src/pdf/SkJpegInfo_libjpegturbo.cpp59
-rw-r--r--src/pdf/SkJpegInfo_none.cpp120
-rw-r--r--src/pdf/SkPDFBitmap.cpp139
-rw-r--r--src/pdf/SkPDFBitmap.h24
-rw-r--r--src/pdf/SkPDFDocumentPriv.h4
-rw-r--r--src/pdf/SkPDFTypes.cpp4
-rw-r--r--src/pdf/SkPDFTypes.h1
-rw-r--r--tests/PDFJpegEmbedTest.cpp37
18 files changed, 183 insertions, 285 deletions
diff --git a/BUILD.gn b/BUILD.gn
index aeec2d9460..e93503ee48 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -1233,7 +1233,8 @@ optional("graphite") {
}
optional("pdf") {
- enabled = skia_use_zlib && skia_enable_pdf
+ enabled = skia_use_zlib && skia_enable_pdf && skia_use_libjpeg_turbo_decode &&
+ skia_use_libjpeg_turbo_encode
public_defines = [ "SK_SUPPORT_PDF" ]
deps = [ "//third_party/zlib" ]
@@ -1248,21 +1249,10 @@ optional("pdf") {
defines = [ "SK_PDF_USE_SFNTLY" ]
}
- if (skia_use_libjpeg_turbo_decode) {
- deps += [
- ":jpeg_decode",
-
- # This is not a public_dep so we need to directly depend on it
- # to use jpeg headers
- "//third_party/libjpeg-turbo:libjpeg",
- ]
- sources += [ "src/pdf/SkJpegInfo_libjpegturbo.cpp" ]
- } else {
- sources += [ "src/pdf/SkJpegInfo_none.cpp" ]
- }
- if (skia_use_libjpeg_turbo_encode) {
- deps += [ ":jpeg_encode" ]
- }
+ deps += [
+ ":jpeg_decode",
+ ":jpeg_encode",
+ ]
}
optional("xps") {
diff --git a/gn/pdf.gni b/gn/pdf.gni
index 4228265c9f..a51ad5f1a3 100644
--- a/gn/pdf.gni
+++ b/gn/pdf.gni
@@ -22,7 +22,6 @@ skia_pdf_sources = [
"$_src/pdf/SkClusterator.h",
"$_src/pdf/SkDeflate.cpp",
"$_src/pdf/SkDeflate.h",
- "$_src/pdf/SkJpegInfo.h",
"$_src/pdf/SkKeyedImage.cpp",
"$_src/pdf/SkKeyedImage.h",
"$_src/pdf/SkPDFBitmap.cpp",
diff --git a/include/codec/SkCodec.h b/include/codec/SkCodec.h
index 70c1ec7c51..2f5f124c9b 100644
--- a/include/codec/SkCodec.h
+++ b/include/codec/SkCodec.h
@@ -1029,6 +1029,7 @@ private:
friend class SkSampledCodec;
friend class SkIcoCodec;
friend class SkAndroidCodec; // for fEncodedInfo
+ friend class SkPDFBitmap; // for fEncodedInfo
};
namespace SkCodecs {
diff --git a/include/private/SkEncodedInfo.h b/include/private/SkEncodedInfo.h
index 74e2ad1480..eb1f3257f2 100644
--- a/include/private/SkEncodedInfo.h
+++ b/include/private/SkEncodedInfo.h
@@ -29,6 +29,7 @@ public:
static std::unique_ptr<ICCProfile> Make(const skcms_ICCProfile&);
const skcms_ICCProfile* profile() const { return &fProfile; }
+ sk_sp<SkData> data() const { return fData; }
private:
ICCProfile(const skcms_ICCProfile&, sk_sp<SkData> = nullptr);
@@ -197,6 +198,10 @@ public:
if (!fProfile) return nullptr;
return fProfile->profile();
}
+ sk_sp<SkData> profileData() const {
+ if (!fProfile) return nullptr;
+ return fProfile->data();
+ }
uint8_t bitsPerComponent() const { return fBitsPerComponent; }
diff --git a/public.bzl b/public.bzl
index ebf0f4f774..8c44cb46e5 100644
--- a/public.bzl
+++ b/public.bzl
@@ -1363,8 +1363,6 @@ BASE_SRCS_ALL = [
"src/pdf/SkClusterator.h",
"src/pdf/SkDeflate.cpp",
"src/pdf/SkDeflate.h",
- "src/pdf/SkJpegInfo.h",
- "src/pdf/SkJpegInfo_none.cpp",
"src/pdf/SkKeyedImage.cpp",
"src/pdf/SkKeyedImage.h",
"src/pdf/SkPDFBitmap.cpp",
diff --git a/relnotes/PDF_jpeg.md b/relnotes/PDF_jpeg.md
new file mode 100644
index 0000000000..15594bb32b
--- /dev/null
+++ b/relnotes/PDF_jpeg.md
@@ -0,0 +1,4 @@
+The PDF code now directly depends on Skia's JPEG decoder and encoder. The build
+time shims to avoid using a JPEG decoder and encoder have been removed. In the
+future these may be made optional again by allowing the user to supply them at
+runtime.
diff --git a/src/codec/SkJpegCodec.cpp b/src/codec/SkJpegCodec.cpp
index b6848fff8d..33d0a58ee1 100644
--- a/src/codec/SkJpegCodec.cpp
+++ b/src/codec/SkJpegCodec.cpp
@@ -1412,12 +1412,3 @@ std::unique_ptr<SkCodec> Decode(sk_sp<SkData> data,
}
} // namespace SkJpegDecoder
-
-namespace SkJpegPriv {
-
-SkEncodedOrigin get_exif_orientation(jpeg_decompress_struct* dinfo) {
- auto metadataDecoder = SkJpegMetadataDecoder::Make(get_sk_marker_list(dinfo));
- return get_exif_orientation(metadataDecoder->getExifMetadata(/*copyData=*/false));
-}
-
-} // namespace SkJpegPriv
diff --git a/src/codec/SkJpegPriv.h b/src/codec/SkJpegPriv.h
index 0e8441d3f2..5728b0237f 100644
--- a/src/codec/SkJpegPriv.h
+++ b/src/codec/SkJpegPriv.h
@@ -56,8 +56,4 @@ struct skjpeg_error_mgr : jpeg_error_mgr {
jmp_buf* fStack[4] = {};
};
-namespace SkJpegPriv {
-SkEncodedOrigin get_exif_orientation(jpeg_decompress_struct* dinfo);
-}
-
#endif
diff --git a/src/pdf/BUILD.bazel b/src/pdf/BUILD.bazel
index b41f5aac18..38f6f081cb 100644
--- a/src/pdf/BUILD.bazel
+++ b/src/pdf/BUILD.bazel
@@ -38,7 +38,6 @@ PDF_FILES = [
"SkPDFMetadata.h",
"SkPDFResourceDict.cpp",
"SkPDFResourceDict.h",
- "SkJpegInfo.h",
"SkPDFShader.cpp",
"SkPDFShader.h",
"SkPDFSubsetFont.cpp",
@@ -60,11 +59,13 @@ split_srcs_and_hdrs(
files = PDF_FILES,
)
+#TODO: remove after Chromium no longer references this.
skia_filegroup(
name = "jpeg_info_libjpeg",
srcs = ["SkJpegInfo_libjpegturbo.cpp"],
)
+#TODO: remove after Chromium no longer references this.
skia_filegroup(
name = "jpeg_info_none",
srcs = ["SkJpegInfo_none.cpp"],
@@ -74,10 +75,7 @@ skia_filegroup(
name = "srcs",
srcs = [
":pdf_srcs",
- ] + select({
- "//src/codec:jpeg_decode_codec": [":jpeg_info_libjpeg"],
- "//conditions:default": [":jpeg_info_none"],
- }),
+ ],
visibility = ["//src:__pkg__"],
)
@@ -113,7 +111,11 @@ selects.config_setting_group(
skia_cc_deps(
name = "deps",
visibility = ["//src:__pkg__"],
- deps = ["@zlib_skia//:zlib"] + select({
+ deps = [
+ "@zlib_skia//:zlib",
+ #"//src/codec:jpeg_decode",
+ #"//src/encode:jpeg_encode",
+ ] + select({
":uses_harfbuzz": ["@harfbuzz"],
"//conditions:default": [],
}),
diff --git a/src/pdf/SkJpegInfo.h b/src/pdf/SkJpegInfo.h
index 736575d42f..17155e4830 100644
--- a/src/pdf/SkJpegInfo.h
+++ b/src/pdf/SkJpegInfo.h
@@ -7,22 +7,4 @@
#ifndef SkJpegInfo_DEFINED
#define SkJpegInfo_DEFINED
-#include "include/codec/SkEncodedOrigin.h"
-#include "include/private/SkEncodedInfo.h"
-
-#include <cstddef>
-
-struct SkISize;
-
-/** Returns true if the data seems to be a valid JPEG image with a known colorType.
-
- @param [out] size Image size in pixels
- @param [out] colorType Encoded color type (kGray_Color, kYUV_Color, several others).
- @param [out] orientation EXIF Orientation of the image.
-*/
-bool SkGetJpegInfo(const void* data, size_t len,
- SkISize* size,
- SkEncodedInfo::Color* colorType,
- SkEncodedOrigin* orientation);
-
#endif // SkJpegInfo_DEFINED
diff --git a/src/pdf/SkJpegInfo_libjpegturbo.cpp b/src/pdf/SkJpegInfo_libjpegturbo.cpp
index 41e73757df..020c041a5a 100644
--- a/src/pdf/SkJpegInfo_libjpegturbo.cpp
+++ b/src/pdf/SkJpegInfo_libjpegturbo.cpp
@@ -7,62 +7,3 @@
* This file can parse information from a JPEG using libjpeg (which must be compiled in).
*/
-#include "src/pdf/SkJpegInfo.h"
-
-#include "include/codec/SkEncodedOrigin.h"
-#include "include/core/SkSize.h"
-#include "include/core/SkStream.h"
-#include "include/core/SkTypes.h"
-#include "include/private/SkEncodedInfo.h"
-#include "include/private/base/SkTo.h"
-#include "src/codec/SkJpegCodec.h"
-#include "src/codec/SkJpegConstants.h"
-#include "src/codec/SkJpegDecoderMgr.h"
-#include "src/codec/SkJpegPriv.h"
-
-#include <cstddef>
-#include <csetjmp>
-
-extern "C" {
- #include "jpeglib.h" // NO_G3_REWRITE
-}
-
-bool SkGetJpegInfo(const void* data, size_t len,
- SkISize* size,
- SkEncodedInfo::Color* colorType,
- SkEncodedOrigin* orientation) {
- if (!SkJpegCodec::IsJpeg(data, len)) {
- return false;
- }
-
- SkMemoryStream stream(data, len);
- JpegDecoderMgr decoderMgr(&stream);
- // libjpeg errors will be caught and reported here
- skjpeg_error_mgr::AutoPushJmpBuf jmp(decoderMgr.errorMgr());
- if (setjmp(jmp)) {
- return false;
- }
- decoderMgr.init();
- jpeg_decompress_struct* dinfo = decoderMgr.dinfo();
- jpeg_save_markers(dinfo, kExifMarker, 0xFFFF);
- jpeg_save_markers(dinfo, kICCMarker, 0xFFFF);
- jpeg_save_markers(dinfo, kMpfMarker, 0xFFFF);
- jpeg_save_markers(dinfo, kGainmapMarker, 0xFFFF);
- if (JPEG_HEADER_OK != jpeg_read_header(dinfo, true)) {
- return false;
- }
- SkEncodedInfo::Color encodedColorType;
- if (!decoderMgr.getEncodedColor(&encodedColorType)) {
- return false; // Unable to interpret the color channels as colors.
- }
- if (colorType) {
- *colorType = encodedColorType;
- }
- if (orientation) {
- *orientation = SkJpegPriv::get_exif_orientation(dinfo);
- }
- if (size) {
- *size = {SkToS32(dinfo->image_width), SkToS32(dinfo->image_height)};
- }
- return true;
-}
diff --git a/src/pdf/SkJpegInfo_none.cpp b/src/pdf/SkJpegInfo_none.cpp
index b0e3cba263..78e44cd483 100644
--- a/src/pdf/SkJpegInfo_none.cpp
+++ b/src/pdf/SkJpegInfo_none.cpp
@@ -8,123 +8,3 @@
* it. This does not need Skia to be built with a JPEG library.
*/
-#include "src/pdf/SkJpegInfo.h"
-
-#include "include/codec/SkEncodedOrigin.h"
-#include "include/core/SkSize.h"
-#include "include/private/base/SkTo.h"
-
-namespace {
-class JpegSegment {
-public:
- JpegSegment(const void* data, size_t size)
- : fData(static_cast<const char*>(data))
- , fSize(size)
- , fOffset(0)
- , fLength(0) {}
- bool read() {
- if (!this->readBigendianUint16(&fMarker)) {
- return false;
- }
- if (JpegSegment::StandAloneMarker(fMarker)) {
- fLength = 0;
- fBuffer = nullptr;
- return true;
- }
- if (!this->readBigendianUint16(&fLength) || fLength < 2) {
- return false;
- }
- fLength -= 2; // Length includes itself for some reason.
- if (fOffset + fLength > fSize) {
- return false; // Segment too long.
- }
- fBuffer = &fData[fOffset];
- fOffset += fLength;
- return true;
- }
-
- bool isSOF() {
- return (fMarker & 0xFFF0) == 0xFFC0 && fMarker != 0xFFC4 &&
- fMarker != 0xFFC8 && fMarker != 0xFFCC;
- }
- uint16_t marker() { return fMarker; }
- uint16_t length() { return fLength; }
- const char* data() { return fBuffer; }
-
- static uint16_t GetBigendianUint16(const char* ptr) {
- // "the most significant byte shall come first"
- return (static_cast<uint8_t>(ptr[0]) << 8) |
- static_cast<uint8_t>(ptr[1]);
- }
-
-private:
- const char* const fData;
- const size_t fSize;
- size_t fOffset;
- const char* fBuffer;
- uint16_t fMarker;
- uint16_t fLength;
-
- bool readBigendianUint16(uint16_t* value) {
- if (fOffset + 2 > fSize) {
- return false;
- }
- *value = JpegSegment::GetBigendianUint16(&fData[fOffset]);
- fOffset += 2;
- return true;
- }
- static bool StandAloneMarker(uint16_t marker) {
- // RST[m] markers or SOI, EOI, TEM
- return (marker & 0xFFF8) == 0xFFD0 || marker == 0xFFD8 ||
- marker == 0xFFD9 || marker == 0xFF01;
- }
-};
-} // namespace
-
-bool SkGetJpegInfo(const void* data, size_t len,
- SkISize* size,
- SkEncodedInfo::Color* colorType,
- SkEncodedOrigin* orientation) {
- static const uint16_t kSOI = 0xFFD8;
- static const uint16_t kAPP0 = 0xFFE0;
- JpegSegment segment(data, len);
- if (!segment.read() || segment.marker() != kSOI) {
- return false; // not a JPEG
- }
- if (!segment.read() || segment.marker() != kAPP0) {
- return false; // not an APP0 segment
- }
- static const char kJfif[] = {'J', 'F', 'I', 'F', '\0'};
- SkASSERT(segment.data());
- if (SkToSizeT(segment.length()) < sizeof(kJfif) ||
- 0 != memcmp(segment.data(), kJfif, sizeof(kJfif))) {
- return false; // Not JFIF JPEG
- }
- do {
- if (!segment.read()) {
- return false; // malformed JPEG
- }
- } while (!segment.isSOF());
- if (segment.length() < 6) {
- return false; // SOF segment is short
- }
- if (8 != segment.data()[0]) {
- return false; // Only support 8-bit precision
- }
- int numberOfComponents = segment.data()[5];
- if (numberOfComponents != 1 && numberOfComponents != 3) {
- return false; // Invalid JFIF
- }
- if (size) {
- *size = {JpegSegment::GetBigendianUint16(&segment.data()[3]),
- JpegSegment::GetBigendianUint16(&segment.data()[1])};
- }
- if (colorType) {
- *colorType = numberOfComponents == 3 ? SkEncodedInfo::kYUV_Color
- : SkEncodedInfo::kGray_Color;
- }
- if (orientation) {
- *orientation = kTopLeft_SkEncodedOrigin;
- }
- return true;
-}
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) {
diff --git a/src/pdf/SkPDFBitmap.h b/src/pdf/SkPDFBitmap.h
index bc2c57bd3b..d310d7b6a6 100644
--- a/src/pdf/SkPDFBitmap.h
+++ b/src/pdf/SkPDFBitmap.h
@@ -7,8 +7,13 @@
#ifndef SkPDFBitmap_DEFINED
#define SkPDFBitmap_DEFINED
+#include "include/core/SkData.h"
+#include "src/core/SkChecksum.h"
+
+class SkCodec;
class SkImage;
class SkPDFDocument;
+struct SkEncodedInfo;
struct SkPDFIndirectReference;
/**
@@ -19,4 +24,23 @@ SkPDFIndirectReference SkPDFSerializeImage(const SkImage* img,
SkPDFDocument* doc,
int encodingQuality = 101);
+class SkPDFBitmap {
+public:
+ static const SkEncodedInfo& GetEncodedInfo(SkCodec&);
+};
+
+struct SkPDFIccProfileKey {
+ sk_sp<SkData> fData;
+ bool operator==(const SkPDFIccProfileKey& that) const {
+ return fData->equals(that.fData.get());
+ }
+ bool operator!=(const SkPDFIccProfileKey& rhs) const { return !(*this == rhs); }
+
+ struct Hash {
+ uint32_t operator()(const SkPDFIccProfileKey& k) const {
+ return SkChecksum::Hash32(k.fData->data(), k.fData->size());
+ }
+ };
+};
+
#endif // SkPDFBitmap_DEFINED
diff --git a/src/pdf/SkPDFDocumentPriv.h b/src/pdf/SkPDFDocumentPriv.h
index dffc239c3e..4d8e5eb892 100644
--- a/src/pdf/SkPDFDocumentPriv.h
+++ b/src/pdf/SkPDFDocumentPriv.h
@@ -12,6 +12,7 @@
#include "include/docs/SkPDFDocument.h"
#include "include/private/base/SkMutex.h"
#include "src/core/SkTHash.h"
+#include "src/pdf/SkPDFBitmap.h"
#include "src/pdf/SkPDFGraphicState.h"
#include "src/pdf/SkPDFMetadata.h"
#include "src/pdf/SkPDFShader.h"
@@ -149,6 +150,9 @@ public:
SkPDFIndirectReference,
SkPDFGradientShader::KeyHash> fGradientPatternMap;
skia_private::THashMap<SkBitmapKey, SkPDFIndirectReference> fPDFBitmapMap;
+ skia_private::THashMap<SkPDFIccProfileKey,
+ SkPDFIndirectReference,
+ SkPDFIccProfileKey::Hash> fICCProfileMap;
skia_private::THashMap<uint32_t, std::unique_ptr<SkAdvancedTypefaceMetrics>> fTypefaceMetrics;
skia_private::THashMap<uint32_t, std::vector<SkString>> fType1GlyphNames;
skia_private::THashMap<uint32_t, std::vector<SkUnichar>> fToUnicodeMap;
diff --git a/src/pdf/SkPDFTypes.cpp b/src/pdf/SkPDFTypes.cpp
index 79c402a9ad..d9b6d854b8 100644
--- a/src/pdf/SkPDFTypes.cpp
+++ b/src/pdf/SkPDFTypes.cpp
@@ -527,6 +527,10 @@ void SkPDFDict::insertTextString(const char key[], SkString value) {
fRecords.emplace_back(SkPDFUnion::Name(key), SkPDFUnion::TextString(std::move(value)));
}
+void SkPDFDict::insertUnion(const char key[], SkPDFUnion&& value) {
+ fRecords.emplace_back(SkPDFUnion::Name(key), std::move(value));
+}
+
////////////////////////////////////////////////////////////////////////////////
diff --git a/src/pdf/SkPDFTypes.h b/src/pdf/SkPDFTypes.h
index 370c94878f..579434ba8e 100644
--- a/src/pdf/SkPDFTypes.h
+++ b/src/pdf/SkPDFTypes.h
@@ -186,6 +186,7 @@ public:
void insertTextString(const char key[], const char value[]);
void insertByteString(const char key[], SkString value);
void insertTextString(const char key[], SkString value);
+ void insertUnion(const char key[], SkPDFUnion&&);
private:
std::vector<std::pair<SkPDFUnion, SkPDFUnion>> fRecords;
diff --git a/tests/PDFJpegEmbedTest.cpp b/tests/PDFJpegEmbedTest.cpp
index 28d09d3b82..d2593f12eb 100644
--- a/tests/PDFJpegEmbedTest.cpp
+++ b/tests/PDFJpegEmbedTest.cpp
@@ -6,6 +6,7 @@
*/
#include "include/codec/SkEncodedOrigin.h"
+#include "include/codec/SkJpegDecoder.h"
#include "include/core/SkCanvas.h"
#include "include/core/SkColor.h"
#include "include/core/SkData.h"
@@ -17,7 +18,7 @@
#include "include/core/SkTypes.h"
#include "include/docs/SkPDFDocument.h"
#include "include/private/SkEncodedInfo.h"
-#include "src/pdf/SkJpegInfo.h"
+#include "src/pdf/SkPDFBitmap.h"
#include "tests/Test.h"
#include "tools/Resources.h"
@@ -121,19 +122,29 @@ struct SkJFIFInfo {
} fType;
};
bool SkIsJFIF(const SkData* data, SkJFIFInfo* info) {
- SkISize jpegSize;
- SkEncodedInfo::Color jpegColorType;
- SkEncodedOrigin exifOrientation;
- if (data && SkGetJpegInfo(data->data(), data->size(), &jpegSize,
- &jpegColorType, &exifOrientation)) {
- bool yuv = jpegColorType == SkEncodedInfo::kYUV_Color;
- bool goodColorType = yuv || jpegColorType == SkEncodedInfo::kGray_Color;
- if (goodColorType && kTopLeft_SkEncodedOrigin == exifOrientation) {
- if (info) {
- *info = {jpegSize, yuv ? SkJFIFInfo::kYCbCr : SkJFIFInfo::kGrayscale};
- }
- return true;
+ static constexpr const SkCodecs::Decoder decoders[] = {
+ SkJpegDecoder::Decoder(),
+ };
+
+ if (!data) {
+ return false;
+ }
+ std::unique_ptr<SkCodec> codec = SkCodec::MakeFromData(sk_ref_sp(data), decoders);
+ if (!codec) {
+ return false;
+ }
+
+ SkISize jpegSize = codec->dimensions();
+ SkEncodedInfo::Color jpegColorType = SkPDFBitmap::GetEncodedInfo(*codec).color();
+ SkEncodedOrigin exifOrientation = codec->getOrigin();
+
+ bool yuv = jpegColorType == SkEncodedInfo::kYUV_Color;
+ bool goodColorType = yuv || jpegColorType == SkEncodedInfo::kGray_Color;
+ if (goodColorType && kTopLeft_SkEncodedOrigin == exifOrientation) {
+ if (info) {
+ *info = {jpegSize, yuv ? SkJFIFInfo::kYCbCr : SkJFIFInfo::kGrayscale};
}
+ return true;
}
return false;
}