Skip to content

Commit

Permalink
Support CMYK color space for JPG images and fix the crash issues. (#442)
Browse files Browse the repository at this point in the history
  • Loading branch information
kevingpqi123 authored Jan 23, 2025
1 parent e5103ae commit 0b5d94c
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 8 deletions.
2 changes: 1 addition & 1 deletion DEPS
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
},
{
"url": "${PAG_GROUP}/skcms.git",
"commit": "8ca9333954230146f663b8890db634d2e7a30500",
"commit": "0084176aa5808a668666846c356ae6503a3b2447",
"dir": "third_party/skcms"
},
{
Expand Down
3 changes: 3 additions & 0 deletions resources/apitest/cmyk.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
66 changes: 66 additions & 0 deletions src/core/codecs/jpeg/JpegCodec.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "core/codecs/jpeg/JpegCodec.h"
#include <csetjmp>
#include "core/utils/OrientationHelper.h"
#include "skcms.h"
#include "tgfx/core/Buffer.h"
#include "tgfx/core/Data.h"
#include "tgfx/core/Pixmap.h"
Expand Down Expand Up @@ -108,6 +109,53 @@ std::shared_ptr<ImageCodec> JpegCodec::MakeFromData(const std::string& filePath,
orientation, filePath, std::move(byteData)));
}

bool ExtractICCProfile(jpeg_decompress_struct* cinfo, std::vector<uint8_t>& iccProfileData) {
jpeg_saved_marker_ptr marker = cinfo->marker_list;
while (marker) {
if (marker->marker == JPEG_APP0 + 2 && marker->data_length > 14 &&
std::memcmp(marker->data, "ICC_PROFILE", 12) == 0) {
iccProfileData.insert(iccProfileData.end(), marker->data + 14,
marker->data + marker->data_length);
}
marker = marker->next;
}
return !iccProfileData.empty();
}

bool ParseICCProfile(const std::vector<uint8_t>& iccProfileData, gfx::skcms_ICCProfile* profile) {
return skcms_Parse(iccProfileData.data(), iccProfileData.size(), profile);
}

bool ConvertCMYKPixels(void* dst, const gfx::skcms_ICCProfile cmykProfile,
const ImageInfo& dstInfo) {
gfx::skcms_PixelFormat dstPixelFormat;
switch (dstInfo.colorType()) {
case ColorType::BGRA_8888:
dstPixelFormat = gfx::skcms_PixelFormat_BGRA_8888;
break;
case ColorType::RGBA_8888:
dstPixelFormat = gfx::skcms_PixelFormat_RGBA_8888;
break;
default:
return false;
}
const gfx::skcms_ICCProfile* dstProfile = gfx::skcms_sRGB_profile();
auto width = dstInfo.width();
auto height = dstInfo.height();
auto src = dst;
for (int i = 0; i < height; i++) {
bool status = gfx::skcms_Transform(
src, gfx::skcms_PixelFormat_RGBA_8888, gfx::skcms_AlphaFormat_Unpremul, &cmykProfile, dst,
dstPixelFormat, gfx::skcms_AlphaFormat_Unpremul, dstProfile, static_cast<size_t>(width));
if (!status) {
return false;
}
dst = reinterpret_cast<uint8_t*>(dst) + dstInfo.rowBytes();
src = reinterpret_cast<uint8_t*>(src) + dstInfo.rowBytes();
}
return true;
}

bool JpegCodec::readPixels(const ImageInfo& dstInfo, void* dstPixels) const {
if (dstPixels == nullptr || dstInfo.isEmpty()) {
return false;
Expand Down Expand Up @@ -159,13 +207,18 @@ bool JpegCodec::readPixels(const ImageInfo& dstInfo, void* dstPixels) const {
jpeg_create_decompress(&cinfo);
if (infile) {
jpeg_stdio_src(&cinfo, infile);
jpeg_save_markers(&cinfo, JPEG_APP0 + 1, 0xFFFF);
jpeg_save_markers(&cinfo, JPEG_APP0 + 2, 0xFFFF);
} else {
jpeg_mem_src(&cinfo, fileData->bytes(), fileData->size());
}
if (jpeg_read_header(&cinfo, TRUE) != JPEG_HEADER_OK) {
break;
}
cinfo.out_color_space = out_color_space;
if (cinfo.jpeg_color_space == JCS_CMYK || cinfo.jpeg_color_space == JCS_YCCK) {
cinfo.out_color_space = JCS_CMYK;
}
if (!jpeg_start_decompress(&cinfo)) {
break;
}
Expand All @@ -178,6 +231,19 @@ bool JpegCodec::readPixels(const ImageInfo& dstInfo, void* dstPixels) const {
jpeg_read_scanlines(&cinfo, pRow, 1);
line++;
}
if (cinfo.out_color_space == JCS_CMYK) {
std::vector<uint8_t> iccProfileData;
if (ExtractICCProfile(&cinfo, iccProfileData)) {
gfx::skcms_ICCProfile cmykProfile;
if (ParseICCProfile(iccProfileData, &cmykProfile)) {
if (!ConvertCMYKPixels(outPixels, cmykProfile, dstInfo)) {
result = false;
jpeg_finish_decompress(&cinfo);
break;
}
}
}
}
result = jpeg_finish_decompress(&cinfo);
} while (false);
jpeg_destroy_decompress(&cinfo);
Expand Down
12 changes: 6 additions & 6 deletions test/baseline/version.json
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@
"getLayersUnderPoint": "b062b9a",
"greyColorMatrix": "a1605b2",
"identityMatrix": "a1605b2",
"imageLayer": "99a5cd9",
"imageLayer": "e5103ae6",
"imageMask": "4b176e4",
"innerShadow": "270695a",
"shapeMask": "612c09e",
Expand Down Expand Up @@ -149,7 +149,7 @@
"PixelMap_BGRA_to_rgb_A": "d010fb8",
"PixelMap_RGBA_Original": "d010fb8",
"PixelMap_RGBA_to_BGRA": "d010fb8",
"PixelMap_RGBA_to_Gray8": "d010fb8",
"PixelMap_RGBA_to_Gray8": "b74f86c1",
"PixelMap_RGBA_to_RGB565": "d010fb8",
"PixelMap_RGBA_to_RGBA": "d010fb8",
"PixelMap_RGBA_to_RGBA_-100_-100": "d010fb8",
Expand All @@ -166,11 +166,11 @@
"PixelMap_rgb_A_to_alpha": "d010fb8",
"PixelMap_rgb_A_to_bgr_A": "d010fb8",
"PngCodec_Decode_Alpha8": "afd80b4",
"PngCodec_Decode_Gray8": "afd80b4",
"PngCodec_Decode_Gray8": "b74f86c1",
"PngCodec_Decode_RGB565": "afd80b4",
"PngCodec_Decode_RGBA": "afd80b4",
"PngCodec_Encode_Alpha8": "afd80b4",
"PngCodec_Encode_Gray8": "afd80b4",
"PngCodec_Encode_Gray8": "b74f86c1",
"PngCodec_Encode_RGB565": "afd80b4",
"PngCodec_Encode_RGBA": "afd80b4",
"Surface_BL_rgb_A_to_rgb_A": "d010fb8",
Expand All @@ -186,11 +186,11 @@
"Surface_rgb_A_to_rgb_A_100_-100": "d010fb8",
"Surface_rgb_A_to_rgb_A_100_100": "d010fb8",
"WebpCodec_Decode_Alpha8": "d010fb8",
"WebpCodec_Decode_Gray8": "d010fb8",
"WebpCodec_Decode_Gray8": "b74f86c1",
"WebpCodec_Decode_RGB565": "d010fb8",
"WebpCodec_Decode_RGBA": "d010fb8",
"WebpCodec_Encode_Alpha8": "d010fb8",
"WebpCodec_Encode_Gray8": "d010fb8",
"WebpCodec_Encode_Gray8": "b74f86c1",
"WebpCodec_Encode_RGB565": "d010fb8",
"WebpCodec_Encode_RGBA": "d010fb8"
},
Expand Down
2 changes: 1 addition & 1 deletion test/src/LayerTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ TGFX_TEST(LayerTest, imageLayer) {
ContextScope scope;
auto context = scope.getContext();
ASSERT_TRUE(context != nullptr);
auto image = MakeImage("resources/apitest/image_as_mask.png");
auto image = MakeImage("resources/apitest/cmyk.jpg");
auto surface = Surface::Make(context, image->width() * 5, image->height() * 5);
auto displayList = std::make_unique<DisplayList>();
auto layer = Layer::Make();
Expand Down

0 comments on commit 0b5d94c

Please sign in to comment.