1 /*
2 * Copyright 2007 The Android Open Source Project
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "SkImageEncoderPriv.h"
9
10 #ifdef SK_HAS_JPEG_LIBRARY
11
12 #include "SkColorPriv.h"
13 #include "SkImageEncoderFns.h"
14 #include "SkJPEGWriteUtility.h"
15 #include "SkStream.h"
16 #include "SkTemplates.h"
17
18 #include <stdio.h>
19
20 extern "C" {
21 #include "jpeglib.h"
22 #include "jerror.h"
23 }
24
25 /**
26 * Returns true if |info| is supported by the jpeg encoder and false otherwise.
27 * |jpegColorType| will be set to the proper libjpeg-turbo type for input to the library.
28 * |numComponents| will be set to the number of components in the |jpegColorType|.
29 * |proc| will be set if we need to pre-convert the input before passing to
30 * libjpeg-turbo. Otherwise will be set to nullptr.
31 */
32 // TODO (skbug.com/1501):
33 // Should we fail on non-opaque encodes?
34 // Or should we change alpha behavior (ex: unpremultiply when the input is premul)?
35 // Or is ignoring the alpha type and alpha channel ok here?
set_encode_config(J_COLOR_SPACE * jpegColorType,int * numComponents,transform_scanline_proc * proc,const SkImageInfo & info)36 static bool set_encode_config(J_COLOR_SPACE* jpegColorType, int* numComponents,
37 transform_scanline_proc* proc, const SkImageInfo& info) {
38 *proc = nullptr;
39 switch (info.colorType()) {
40 case kRGBA_8888_SkColorType:
41 *jpegColorType = JCS_EXT_RGBA;
42 *numComponents = 4;
43 return true;
44 case kBGRA_8888_SkColorType:
45 *jpegColorType = JCS_EXT_BGRA;
46 *numComponents = 4;
47 return true;
48 case kRGB_565_SkColorType:
49 *proc = transform_scanline_565;
50 *jpegColorType = JCS_RGB;
51 *numComponents = 3;
52 return true;
53 case kARGB_4444_SkColorType:
54 *proc = transform_scanline_444;
55 *jpegColorType = JCS_RGB;
56 *numComponents = 3;
57 return true;
58 case kIndex_8_SkColorType:
59 *proc = transform_scanline_index8_opaque;
60 *jpegColorType = JCS_RGB;
61 *numComponents = 3;
62 return true;
63 case kGray_8_SkColorType:
64 SkASSERT(info.isOpaque());
65 *jpegColorType = JCS_GRAYSCALE;
66 *numComponents = 1;
67 return true;
68 case kRGBA_F16_SkColorType:
69 if (!info.colorSpace() || !info.colorSpace()->gammaIsLinear()) {
70 return false;
71 }
72
73 *proc = transform_scanline_F16_to_8888;
74 *jpegColorType = JCS_EXT_RGBA;
75 *numComponents = 4;
76 return true;
77 default:
78 return false;
79 }
80
81
82 }
83
SkEncodeImageAsJPEG(SkWStream * stream,const SkPixmap & pixmap,const SkEncodeOptions & opts)84 bool SkEncodeImageAsJPEG(SkWStream* stream, const SkPixmap& pixmap, const SkEncodeOptions& opts) {
85 if (SkTransferFunctionBehavior::kRespect == opts.fUnpremulBehavior) {
86 // Respecting the transfer function requries a color space. It's not actually critical
87 // in the jpeg case (since jpegs are opaque), but Skia color correct behavior generally
88 // requires pixels to be tagged with color spaces.
89 if (!pixmap.colorSpace() || (!pixmap.colorSpace()->gammaCloseToSRGB() &&
90 !pixmap.colorSpace()->gammaIsLinear())) {
91 return false;
92 }
93 }
94
95 return SkEncodeImageAsJPEG(stream, pixmap, 100);
96 }
97
SkEncodeImageAsJPEG(SkWStream * stream,const SkPixmap & pixmap,int quality)98 bool SkEncodeImageAsJPEG(SkWStream* stream, const SkPixmap& pixmap, int quality) {
99 if (!pixmap.addr()) {
100 return false;
101 }
102 jpeg_compress_struct cinfo;
103 skjpeg_error_mgr sk_err;
104 skjpeg_destination_mgr sk_wstream(stream);
105
106 // Declare before calling setjmp.
107 SkAutoTMalloc<uint8_t> storage;
108
109 cinfo.err = jpeg_std_error(&sk_err);
110 sk_err.error_exit = skjpeg_error_exit;
111 if (setjmp(sk_err.fJmpBuf)) {
112 return false;
113 }
114
115 J_COLOR_SPACE jpegColorSpace;
116 int numComponents;
117 transform_scanline_proc proc;
118 if (!set_encode_config(&jpegColorSpace, &numComponents, &proc, pixmap.info())) {
119 return false;
120 }
121
122 jpeg_create_compress(&cinfo);
123 cinfo.dest = &sk_wstream;
124 cinfo.image_width = pixmap.width();
125 cinfo.image_height = pixmap.height();
126 cinfo.input_components = numComponents;
127 cinfo.in_color_space = jpegColorSpace;
128
129 jpeg_set_defaults(&cinfo);
130
131 // Tells libjpeg-turbo to compute optimal Huffman coding tables
132 // for the image. This improves compression at the cost of
133 // slower encode performance.
134 cinfo.optimize_coding = TRUE;
135 jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);
136
137 jpeg_start_compress(&cinfo, TRUE);
138
139 sk_sp<SkData> icc = icc_from_color_space(pixmap.info());
140 if (icc) {
141 // Create a contiguous block of memory with the icc signature followed by the profile.
142 sk_sp<SkData> markerData =
143 SkData::MakeUninitialized(kICCMarkerHeaderSize + icc->size());
144 uint8_t* ptr = (uint8_t*) markerData->writable_data();
145 memcpy(ptr, kICCSig, sizeof(kICCSig));
146 ptr += sizeof(kICCSig);
147 *ptr++ = 1; // This is the first marker.
148 *ptr++ = 1; // Out of one total markers.
149 memcpy(ptr, icc->data(), icc->size());
150
151 jpeg_write_marker(&cinfo, kICCMarker, markerData->bytes(), markerData->size());
152 }
153
154 if (proc) {
155 storage.reset(numComponents * pixmap.width());
156 }
157
158 const void* srcRow = pixmap.addr();
159 const SkPMColor* colors = pixmap.ctable() ? pixmap.ctable()->readColors() : nullptr;
160 while (cinfo.next_scanline < cinfo.image_height) {
161 JSAMPLE* jpegSrcRow = (JSAMPLE*) srcRow;
162 if (proc) {
163 proc((char*)storage.get(), (const char*)srcRow, pixmap.width(), numComponents, colors);
164 jpegSrcRow = storage.get();
165 }
166
167 (void) jpeg_write_scanlines(&cinfo, &jpegSrcRow, 1);
168 srcRow = SkTAddOffset<const void>(srcRow, pixmap.rowBytes());
169 }
170
171 jpeg_finish_compress(&cinfo);
172 jpeg_destroy_compress(&cinfo);
173
174 return true;
175 }
176 #endif
177