aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/images
diff options
context:
space:
mode:
authorGravatar Matt Sarett <msarett@google.com>2017-01-27 18:16:40 -0500
committerGravatar Skia Commit-Bot <skia-commit-bot@chromium.org>2017-01-28 01:59:09 +0000
commite95941f2c61d8bcb5aaf60968e4391a1c7a0d593 (patch)
tree7e473b36bfec6094cf0edf9f3d0c75ebf8605094 /src/images
parent0bd8f36309293720b44af92f113f6ebe96d54779 (diff)
Update SkJPEGImageEncoder
(1) Use libjpeg-turbo color space extensions when possible. (2) Use transform_scanline_procs when pre-libjpeg-turbo transformation are required. (3) Add support for Gray and F16. BUG=skia: Change-Id: I77b977cb8c9e0afc17d907dd73a1cf3f16539bcc Reviewed-on: https://skia-review.googlesource.com/7642 Reviewed-by: Leon Scroggins <scroggo@google.com> Commit-Queue: Matt Sarett <msarett@google.com>
Diffstat (limited to 'src/images')
-rw-r--r--src/images/SkImageEncoderPriv.h1
-rw-r--r--src/images/SkJPEGImageEncoder.cpp174
2 files changed, 91 insertions, 84 deletions
diff --git a/src/images/SkImageEncoderPriv.h b/src/images/SkImageEncoderPriv.h
index e2f72caa8d..5f7560d2de 100644
--- a/src/images/SkImageEncoderPriv.h
+++ b/src/images/SkImageEncoderPriv.h
@@ -23,6 +23,7 @@ struct SkEncodeOptions {
};
#ifdef SK_HAS_JPEG_LIBRARY
+ bool SkEncodeImageAsJPEG(SkWStream*, const SkPixmap&, const SkEncodeOptions&);
bool SkEncodeImageAsJPEG(SkWStream*, const SkPixmap&, int quality);
#else
#define SkEncodeImageAsJPEG(...) false
diff --git a/src/images/SkJPEGImageEncoder.cpp b/src/images/SkJPEGImageEncoder.cpp
index 345f869d3c..c37aa4687e 100644
--- a/src/images/SkJPEGImageEncoder.cpp
+++ b/src/images/SkJPEGImageEncoder.cpp
@@ -13,6 +13,7 @@
#include "SkJPEGWriteUtility.h"
#include "SkStream.h"
#include "SkTemplates.h"
+#include "transform_scanline.h"
#include <stdio.h>
@@ -21,75 +22,82 @@ extern "C" {
#include "jerror.h"
}
-typedef void (*WriteScanline)(uint8_t* SK_RESTRICT dst,
- const void* SK_RESTRICT src, int width,
- const SkPMColor* SK_RESTRICT ctable);
-
-static void Write_32_RGB(uint8_t* SK_RESTRICT dst,
- const void* SK_RESTRICT srcRow, int width,
- const SkPMColor*) {
- const uint32_t* SK_RESTRICT src = (const uint32_t*)srcRow;
- while (--width >= 0) {
- uint32_t c = *src++;
- dst[0] = SkGetPackedR32(c);
- dst[1] = SkGetPackedG32(c);
- dst[2] = SkGetPackedB32(c);
- dst += 3;
+/**
+ * Returns true if |info| is supported by the jpeg encoder and false otherwise.
+ * |jpegColorType| will be set to the proper libjpeg-turbo type for input to the library.
+ * |numComponents| will be set to the number of components in the |jpegColorType|.
+ * |proc| will be set if we need to pre-convert the input before passing to
+ * libjpeg-turbo. Otherwise will be set to nullptr.
+ */
+// TODO (skbug.com/1501):
+// Should we fail on non-opaque encodes?
+// Or should we change alpha behavior (ex: unpremultiply when the input is premul)?
+// Or is ignoring the alpha type and alpha channel ok here?
+static bool set_encode_config(J_COLOR_SPACE* jpegColorType, int* numComponents,
+ transform_scanline_proc* proc, const SkImageInfo& info) {
+ *proc = nullptr;
+ switch (info.colorType()) {
+ case kRGBA_8888_SkColorType:
+ *jpegColorType = JCS_EXT_RGBA;
+ *numComponents = 4;
+ return true;
+ case kBGRA_8888_SkColorType:
+ *jpegColorType = JCS_EXT_BGRA;
+ *numComponents = 4;
+ return true;
+ case kRGB_565_SkColorType:
+ *proc = transform_scanline_565;
+ *jpegColorType = JCS_RGB;
+ *numComponents = 3;
+ return true;
+ case kARGB_4444_SkColorType:
+ *proc = transform_scanline_444;
+ *jpegColorType = JCS_RGB;
+ *numComponents = 3;
+ return true;
+ case kIndex_8_SkColorType:
+ *proc = transform_scanline_index8_opaque;
+ *jpegColorType = JCS_RGB;
+ *numComponents = 3;
+ return true;
+ case kGray_8_SkColorType:
+ SkASSERT(info.isOpaque());
+ *jpegColorType = JCS_GRAYSCALE;
+ *numComponents = 1;
+ return true;
+ case kRGBA_F16_SkColorType:
+ if (!info.colorSpace() || !info.colorSpace()->gammaIsLinear()) {
+ return false;
+ }
+
+ *proc = transform_scanline_F16_to_8888;
+ *jpegColorType = JCS_EXT_RGBA;
+ *numComponents = 4;
+ return true;
+ default:
+ return false;
}
-}
-static void Write_4444_RGB(uint8_t* SK_RESTRICT dst,
- const void* SK_RESTRICT srcRow, int width,
- const SkPMColor*) {
- const SkPMColor16* SK_RESTRICT src = (const SkPMColor16*)srcRow;
- while (--width >= 0) {
- SkPMColor16 c = *src++;
- dst[0] = SkPacked4444ToR32(c);
- dst[1] = SkPacked4444ToG32(c);
- dst[2] = SkPacked4444ToB32(c);
- dst += 3;
- }
-}
-static void Write_16_RGB(uint8_t* SK_RESTRICT dst,
- const void* SK_RESTRICT srcRow, int width,
- const SkPMColor*) {
- const uint16_t* SK_RESTRICT src = (const uint16_t*)srcRow;
- while (--width >= 0) {
- uint16_t c = *src++;
- dst[0] = SkPacked16ToR32(c);
- dst[1] = SkPacked16ToG32(c);
- dst[2] = SkPacked16ToB32(c);
- dst += 3;
- }
}
-static void Write_Index_RGB(uint8_t* SK_RESTRICT dst,
- const void* SK_RESTRICT srcRow, int width,
- const SkPMColor* SK_RESTRICT ctable) {
- const uint8_t* SK_RESTRICT src = (const uint8_t*)srcRow;
- while (--width >= 0) {
- uint32_t c = ctable[*src++];
- dst[0] = SkGetPackedR32(c);
- dst[1] = SkGetPackedG32(c);
- dst[2] = SkGetPackedB32(c);
- dst += 3;
+bool SkEncodeImageAsJPEG(SkWStream* stream, const SkPixmap& pixmap, const SkEncodeOptions& opts) {
+ SkASSERT(!pixmap.colorSpace() || pixmap.colorSpace()->gammaCloseToSRGB() ||
+ pixmap.colorSpace()->gammaIsLinear());
+
+ SkPixmap src = pixmap;
+ if (SkEncodeOptions::PremulBehavior::kLegacy == opts.fPremulBehavior) {
+ src.setColorSpace(nullptr);
+ } else {
+ // kGammaCorrect behavior requires a color space. It's not actually critical in the
+ // jpeg case (since jpegs are opaque), but Skia color correct behavior generally
+ // requires pixels to be tagged with color spaces.
+ if (!src.colorSpace()) {
+ return false;
+ }
}
-}
-static WriteScanline ChooseWriter(SkColorType ct) {
- switch (ct) {
- case kN32_SkColorType:
- return Write_32_RGB;
- case kRGB_565_SkColorType:
- return Write_16_RGB;
- case kARGB_4444_SkColorType:
- return Write_4444_RGB;
- case kIndex_8_SkColorType:
- return Write_Index_RGB;
- default:
- return nullptr;
- }
+ return SkEncodeImageAsJPEG(stream, src, 100);
}
bool SkEncodeImageAsJPEG(SkWStream* stream, const SkPixmap& pixmap, int quality) {
@@ -100,8 +108,8 @@ bool SkEncodeImageAsJPEG(SkWStream* stream, const SkPixmap& pixmap, int quality)
skjpeg_error_mgr sk_err;
skjpeg_destination_mgr sk_wstream(stream);
- // allocate these before set call setjmp
- SkAutoTMalloc<uint8_t> oneRow;
+ // Declare before calling setjmp.
+ SkAutoTMalloc<uint8_t> storage;
cinfo.err = jpeg_std_error(&sk_err);
sk_err.error_exit = skjpeg_error_exit;
@@ -109,9 +117,10 @@ bool SkEncodeImageAsJPEG(SkWStream* stream, const SkPixmap& pixmap, int quality)
return false;
}
- // Keep after setjmp or mark volatile.
- const WriteScanline writer = ChooseWriter(pixmap.colorType());
- if (!writer) {
+ J_COLOR_SPACE jpegColorSpace;
+ int numComponents;
+ transform_scanline_proc proc;
+ if (!set_encode_config(&jpegColorSpace, &numComponents, &proc, pixmap.info())) {
return false;
}
@@ -119,13 +128,8 @@ bool SkEncodeImageAsJPEG(SkWStream* stream, const SkPixmap& pixmap, int quality)
cinfo.dest = &sk_wstream;
cinfo.image_width = pixmap.width();
cinfo.image_height = pixmap.height();
- cinfo.input_components = 3;
-
- // FIXME: Can we take advantage of other in_color_spaces in libjpeg-turbo?
- cinfo.in_color_space = JCS_RGB;
-
- // The gamma value is ignored by libjpeg-turbo.
- cinfo.input_gamma = 1;
+ cinfo.input_components = numComponents;
+ cinfo.in_color_space = jpegColorSpace;
jpeg_set_defaults(&cinfo);
@@ -137,19 +141,21 @@ bool SkEncodeImageAsJPEG(SkWStream* stream, const SkPixmap& pixmap, int quality)
jpeg_start_compress(&cinfo, TRUE);
- const int width = pixmap.width();
- uint8_t* oneRowP = oneRow.reset(width * 3);
+ if (proc) {
+ storage.reset(numComponents * pixmap.width());
+ }
+ const void* srcRow = pixmap.addr();
const SkPMColor* colors = pixmap.ctable() ? pixmap.ctable()->readColors() : nullptr;
- const void* srcRow = pixmap.addr();
-
while (cinfo.next_scanline < cinfo.image_height) {
- JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */
-
- writer(oneRowP, srcRow, width, colors);
- row_pointer[0] = oneRowP;
- (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
- srcRow = (const void*)((const char*)srcRow + pixmap.rowBytes());
+ JSAMPLE* jpegSrcRow = (JSAMPLE*) srcRow;
+ if (proc) {
+ proc((char*)storage.get(), (const char*)srcRow, pixmap.width(), numComponents, colors);
+ jpegSrcRow = storage.get();
+ }
+
+ (void) jpeg_write_scanlines(&cinfo, &jpegSrcRow, 1);
+ srcRow = SkTAddOffset<const void>(srcRow, pixmap.rowBytes());
}
jpeg_finish_compress(&cinfo);