aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorandroid-autoroll <android-autoroll@skia-corp.google.com.iam.gserviceaccount.com>2023-12-12 03:44:47 +0000
committerandroid-autoroll <android-autoroll@skia-corp.google.com.iam.gserviceaccount.com>2023-12-12 03:44:47 +0000
commit5abbf804c60478acd00ed3113d5cf8dfe37f4dba (patch)
tree28378bbd7abb99637deb9b7a75d1e9fab5bf2f62
parent10156345f9db4658c0930c95b1ad23d67ea3a0e5 (diff)
parentf436b5ea0e62751abd63b77cc12881eb08e0f8b7 (diff)
downloadskia-5abbf804c60478acd00ed3113d5cf8dfe37f4dba.tar.gz
Roll Skia from 6e3dca50f506 to f436b5ea0e62 (5 revisions)
https://skia.googlesource.com/skia.git/+log/6e3dca50f506..f436b5ea0e62 2023-12-12 skia-autoroll@skia-public.iam.gserviceaccount.com Roll skottie-base from c8009b321a35 to f4ecbb1337cf 2023-12-12 bungeman@google.com [pdf] Emit color space of images 2023-12-12 kjlubick@google.com [pdf] Test yuv and cmyk images with ICC profiles 2023-12-12 skia-autoroll@skia-public.iam.gserviceaccount.com Roll shaders-base from 5a31b090d8be to ace4d55f4430 2023-12-12 skia-autoroll@skia-public.iam.gserviceaccount.com Roll jsfiddle-base from 8f0c81eee758 to 29a7285dc594 If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://skia-autoroll.corp.goog/r/android-master-autoroll Please CC djsollen@google.com,rmistry@google.com,scroggo@google.com on the revert to ensure that a human is aware of the problem. To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md Test: Presubmit checks will test this change. Exempt-From-Owner-Approval: The autoroll bot does not require owner approval. Bug: b/186777432 Change-Id: I668a858d41b0d0d56a3c1934fbae2e2bd1e275f9
-rw-r--r--Android.bp1
-rw-r--r--BUILD.gn22
-rw-r--r--METADATA2
-rw-r--r--WORKSPACE.bazel6
-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--resources/images/crbug1465627.jpegbin0 -> 1866351 bytes
-rw-r--r--resources/images/crbug999986.jpegbin0 -> 549989 bytes
-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.cpp66
23 files changed, 212 insertions, 294 deletions
diff --git a/Android.bp b/Android.bp
index 873ab2f997..65bfaa1bc9 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1343,7 +1343,6 @@ cc_library_static {
"src/pathops/SkReduceOrder.cpp",
"src/pdf/SkClusterator.cpp",
"src/pdf/SkDeflate.cpp",
- "src/pdf/SkJpegInfo_libjpegturbo.cpp",
"src/pdf/SkKeyedImage.cpp",
"src/pdf/SkPDFBitmap.cpp",
"src/pdf/SkPDFDevice.cpp",
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/METADATA b/METADATA
index 388725ff61..699c0fe7c9 100644
--- a/METADATA
+++ b/METADATA
@@ -9,7 +9,7 @@ third_party {
type: GIT
value: "https://skia.googlesource.com/skia"
}
- version: "6e3dca50f5064ec003b8b2bcac78d7127e9c1249"
+ version: "f436b5ea0e62751abd63b77cc12881eb08e0f8b7"
license_type: RECIPROCAL
last_upgrade_date {
year: 2023
diff --git a/WORKSPACE.bazel b/WORKSPACE.bazel
index 8cb80aac8c..4852d231e1 100644
--- a/WORKSPACE.bazel
+++ b/WORKSPACE.bazel
@@ -581,7 +581,7 @@ container_pull(
# Pulls the gcr.io/skia-public/jsfiddle-base container.
container_pull(
name = "jsfiddle-base",
- digest = "sha256:8f0c81eee758c8960673df4d178196e783d0b9a2b394d8491e48173e616f0b52",
+ digest = "sha256:29a7285dc594f968a7a312b7856587721f22f55399a5b2aeba04c92447f8d71f",
registry = "gcr.io",
repository = "skia-public/jsfiddle-base",
)
@@ -589,7 +589,7 @@ container_pull(
# Pulls the gcr.io/skia-public/shaders-base container.
container_pull(
name = "shaders-base",
- digest = "sha256:5a31b090d8bed9d282e6f54fd267260debd7bd28ef32b2288b1647a38da9c884",
+ digest = "sha256:ace4d55f44303a22bf43ad404b676e97ce540cf2b09425cdba684835a6c7b903",
registry = "gcr.io",
repository = "skia-public/shaders-base",
)
@@ -597,7 +597,7 @@ container_pull(
# Pulls the gcr.io/skia-public/skottie-base container.
container_pull(
name = "skottie-base",
- digest = "sha256:c8009b321a3571529fdafd5c26b00a7b4da85edb08b5637bf5393279adebc101",
+ digest = "sha256:f4ecbb1337cf377d5ddd8f5cb4d1e95969921aeccf24e387f017d3ad2b655ad2",
registry = "gcr.io",
repository = "skia-public/skottie-base",
)
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/resources/images/crbug1465627.jpeg b/resources/images/crbug1465627.jpeg
new file mode 100644
index 0000000000..9bba15f98e
--- /dev/null
+++ b/resources/images/crbug1465627.jpeg
Binary files differ
diff --git a/resources/images/crbug999986.jpeg b/resources/images/crbug999986.jpeg
new file mode 100644
index 0000000000..4f17d8a10c
--- /dev/null
+++ b/resources/images/crbug999986.jpeg
Binary files differ
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 45663825e5..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"
@@ -61,20 +62,34 @@ DEF_TEST(SkPDF_JpegEmbedTest, r) {
const char test[] = "SkPDF_JpegEmbedTest";
sk_sp<SkData> mandrillData(load_resource(r, test, "images/mandrill_512_q075.jpg"));
sk_sp<SkData> cmykData(load_resource(r, test, "images/CMYK.jpg"));
- if (!mandrillData || !cmykData) {
+ sk_sp<SkData> yuvICCData(load_resource(r, test, "images/crbug999986.jpeg"));
+ sk_sp<SkData> cmykICCData(load_resource(r, test, "images/crbug1465627.jpeg"));
+
+ if (!mandrillData || !cmykData || !yuvICCData || !cmykICCData) {
+ REPORTER_ASSERT(r, false, "Could not load all images");
return;
}
////////////////////////////////////////////////////////////////////////////
SkDynamicMemoryWStream pdf;
auto document = SkPDF::MakeDocument(&pdf);
- SkCanvas* canvas = document->beginPage(642, 1028);
+ SkCanvas* canvas = document->beginPage(642, 2048);
canvas->clear(SK_ColorLTGRAY);
sk_sp<SkImage> im1(SkImages::DeferredFromEncodedData(mandrillData));
- canvas->drawImage(im1.get(), 65.0, 0.0);
+ canvas->drawImage(im1, 65.0, 0.0);
sk_sp<SkImage> im2(SkImages::DeferredFromEncodedData(cmykData));
- canvas->drawImage(im2.get(), 0.0, 512.0);
+ canvas->drawImage(im2, 0.0, 512.0);
+
+ // This should be the same blue as the dark spot seen in the image.
+ SkPaint bluePaint;
+ bluePaint.setColor(SkColorSetARGB(255, 0, 59, 103));
+ canvas->drawRect(SkRect::MakeXYWH(0, 1000, 642, 24), bluePaint);
+ sk_sp<SkImage> im3(SkImages::DeferredFromEncodedData(yuvICCData));
+ canvas->drawImage(im3, 0.0, 1024.0);
+
+ sk_sp<SkImage> im4(SkImages::DeferredFromEncodedData(cmykICCData));
+ canvas->drawImage(im4, 0.0, 1536.0);
document->endPage();
document->close();
@@ -88,6 +103,13 @@ DEF_TEST(SkPDF_JpegEmbedTest, r) {
// This JPEG uses a nonstandard colorspace - it can not be
// embedded into the PDF directly.
REPORTER_ASSERT(r, !is_subset_of(cmykData.get(), pdfData.get()));
+
+ if ((false)) {
+ SkFILEWStream s("/tmp/jpegembed.pdf");
+ REPORTER_ASSERT(r, s.write(pdfData->data(), pdfData->size()));
+ s.flush();
+ s.fsync();
+ }
}
#ifdef SK_SUPPORT_PDF
@@ -100,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;
}