Skip to content

Commit

Permalink
support reading CMYK, YCCK JPEGs
Browse files Browse the repository at this point in the history
  • Loading branch information
zbjornson committed Sep 9, 2018
1 parent 7ca05b0 commit bbbc373
Show file tree
Hide file tree
Showing 6 changed files with 66 additions and 23 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ canvas.createJPEGStream() // new
* Provide better, Node.js core-style coded errors for failed sys calls. (For
example, provide an error with code 'ENOENT' if setting `img.src` to a path
that does not exist.)
* Support reading CMYK, YCCK JPEGs.

### Added
* Prebuilds (#992) with different libc versions to the prebuilt binary (#1140)
Expand Down
63 changes: 41 additions & 22 deletions src/Image.cc
Original file line number Diff line number Diff line change
Expand Up @@ -777,16 +777,30 @@ static void jpeg_mem_src (j_decompress_ptr cinfo, void* buffer, long nbytes) {

#endif

void Image::jpegToARGB(jpeg_decompress_struct* args, uint8_t* data, uint8_t* src, JPEGDecodeL decode) {
int stride = naturalWidth * 4;
for (int y = 0; y < naturalHeight; ++y) {
jpeg_read_scanlines(args, &src, 1);
uint32_t *row = (uint32_t*)(data + stride * y);
for (int x = 0; x < naturalWidth; ++x) {
uint8_t r = 0, g = 0, b = 0;
int bx = args->output_components * x;
decode(src + bx, r, g, b);
uint32_t *pixel = row + x;
*pixel = 255 << 24 | r << 16 | g << 8 | b;
}
}
}

/*
* Takes an initialised jpeg_decompress_struct and decodes the
* data into _surface.
*/

#include <iostream>
cairo_status_t
Image::decodeJPEGIntoSurface(jpeg_decompress_struct *args) {
int stride = naturalWidth * 4;
cairo_status_t status;

uint8_t *data = (uint8_t *) malloc(naturalWidth * naturalHeight * 4);
if (!data) {
jpeg_abort_decompress(args);
Expand All @@ -804,25 +818,30 @@ Image::decodeJPEGIntoSurface(jpeg_decompress_struct *args) {
return CAIRO_STATUS_NO_MEMORY;
}

for (int y = 0; y < naturalHeight; ++y) {
jpeg_read_scanlines(args, &src, 1);
uint32_t *row = (uint32_t *)(data + stride * y);
for (int x = 0; x < naturalWidth; ++x) {
if (args->jpeg_color_space == 1) {
uint32_t *pixel = row + x;
*pixel = 255 << 24
| src[x] << 16
| src[x] << 8
| src[x];
} else {
int bx = 3 * x;
uint32_t *pixel = row + x;
*pixel = 255 << 24
| src[bx + 0] << 16
| src[bx + 1] << 8
| src[bx + 2];
}
}
// These are the three main cases to handle. libjpeg converts YCCK to CMYK
// and YCbCr to RGB by default.
switch (args->out_color_space) {
case JCS_CMYK:
jpegToARGB(args, data, src, [](uint8_t const* src, uint8_t& r, uint8_t& g, uint8_t& b) {
uint16_t k = static_cast<uint16_t>(src[3]);
r = k * src[0] / 255;
g = k * src[1] / 255;
b = k * src[2] / 255;
});
break;
case JCS_RGB:
jpegToARGB(args, data, src, [](uint8_t const* src, uint8_t& r, uint8_t& g, uint8_t& b) {
r = src[0], g = src[1], b = src[2];
});
break;
case JCS_GRAYSCALE:
jpegToARGB(args, data, src, [](uint8_t const* src, uint8_t& r, uint8_t& g, uint8_t& b) {
r = g = b = src[0];
});
break;
default:
// TODO
break;
}

_surface = cairo_image_surface_create_for_data(
Expand Down
4 changes: 3 additions & 1 deletion src/Image.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

#include "Canvas.h"
#include "CanvasError.h"
#include <functional>

#ifdef HAVE_JPEG
#include <jpeglib.h>
Expand All @@ -34,7 +35,7 @@
#endif
#endif


using JPEGDecodeL = std::function<void(uint8_t* const src, uint8_t& r, uint8_t& g, uint8_t& b)>;

class Image: public Nan::ObjectWrap {
public:
Expand Down Expand Up @@ -81,6 +82,7 @@ class Image: public Nan::ObjectWrap {
#ifdef HAVE_JPEG
cairo_status_t loadJPEGFromBuffer(uint8_t *buf, unsigned len);
cairo_status_t loadJPEG(FILE *stream);
void jpegToARGB(jpeg_decompress_struct* args, uint8_t* data, uint8_t* src, JPEGDecodeL decode);
cairo_status_t decodeJPEGIntoSurface(jpeg_decompress_struct *info);
#if CAIRO_VERSION_MINOR >= 10
cairo_status_t decodeJPEGBufferIntoMimeSurface(uint8_t *buf, unsigned len);
Expand Down
Binary file added test/fixtures/grayscale.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added test/fixtures/ycck.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
21 changes: 21 additions & 0 deletions test/public/tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -1667,6 +1667,27 @@ tests['drawImage(img) jpeg'] = function (ctx, done) {
img.src = imageSrc('face.jpeg')
}

tests['drawImage(img) YCCK JPEG (#425)'] = function (ctx, done) {
// This also provides coverage for CMYK JPEGs
var img = new Image()
img.onload = function () {
ctx.drawImage(img, 0, 0, 100, 100)
done(null)
}
img.onerror = done
img.src = imageSrc('ycck.jpg')
}

tests['drawImage(img) grayscale JPEG (#425)'] = function (ctx, done) {
var img = new Image()
img.onload = function () {
ctx.drawImage(img, 0, 0, 100, 100)
done(null)
}
img.onerror = done
img.src = imageSrc('grayscale.jpg')
}

tests['drawImage(img) svg'] = function (ctx, done) {
var img = new Image()
img.onload = function () {
Expand Down

0 comments on commit bbbc373

Please sign in to comment.