1 /*
2 * Copyright (C) 2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "jpeg_encoder.h"
17 #ifdef IMAGE_COLORSPACE_FLAG
18 #include "color_space.h"
19 #endif
20 #include "image_log.h"
21 #include "image_trace.h"
22 #include "include/core/SkColorSpace.h"
23 #include "include/core/SkImageInfo.h"
24 #include "jerror.h"
25 #include "media_errors.h"
26 #include "pixel_convert.h"
27 #include "src/images/SkImageEncoderFns.h"
28
29 #undef LOG_DOMAIN
30 #define LOG_DOMAIN LOG_TAG_DOMAIN_ID_PLUGIN
31
32 #undef LOG_TAG
33 #define LOG_TAG "JpegEncoder"
34
35 namespace OHOS {
36 namespace ImagePlugin {
37 using namespace MultimediaPlugin;
38 using namespace Media;
39
40 constexpr uint32_t COMPONENT_NUM_ARGB = 4;
41 constexpr uint32_t COMPONENT_NUM_RGBA = 4;
42 constexpr uint32_t COMPONENT_NUM_BGRA = 4;
43 constexpr uint32_t COMPONENT_NUM_RGB = 3;
44 constexpr uint32_t COMPONENT_NUM_GRAY = 1;
45 constexpr uint32_t PIXEL_SIZE_RGBA_F16 = 8;
46 constexpr uint32_t PIXEL_SIZE_RGB565 = 2;
47 // yuv format
48 constexpr uint8_t COMPONENT_NUM_YUV420SP = 3;
49 constexpr uint8_t Y_SAMPLE_ROW = 16;
50 constexpr uint8_t UV_SAMPLE_ROW = 8;
51 constexpr uint8_t SAMPLE_FACTOR_ONE = 1;
52 constexpr uint8_t SAMPLE_FACTOR_TWO = 2;
53 constexpr uint8_t INDEX_ZERO = 0;
54 constexpr uint8_t INDEX_ONE = 1;
55 constexpr uint8_t INDEX_TWO = 2;
56 constexpr uint8_t SHIFT_MASK = 1;
57
JpegDstMgr(OutputDataStream * stream)58 JpegDstMgr::JpegDstMgr(OutputDataStream *stream) : outputStream(stream)
59 {
60 init_destination = InitDstStream;
61 empty_output_buffer = EmptyOutputBuffer;
62 term_destination = TermDstStream;
63 }
64
JpegEncoder()65 JpegEncoder::JpegEncoder() : dstMgr_(nullptr)
66 {
67 // create decompress struct
68 jpeg_create_compress(&encodeInfo_);
69
70 // set error output
71 encodeInfo_.err = jpeg_std_error(&jerr_);
72 jerr_.error_exit = ErrorExit;
73 if (encodeInfo_.err == nullptr) {
74 IMAGE_LOGE("create jpeg encoder failed.");
75 return;
76 }
77 encodeInfo_.err->output_message = &OutputErrorMessage;
78 }
79
StartEncode(OutputDataStream & outputStream,PlEncodeOptions & option)80 uint32_t JpegEncoder::StartEncode(OutputDataStream &outputStream, PlEncodeOptions &option)
81 {
82 ImageTrace imageTrace("JpegEncoder::StartEncode");
83 pixelMaps_.clear();
84 dstMgr_.outputStream = &outputStream;
85 encodeInfo_.dest = &dstMgr_;
86 encodeOpts_ = option;
87 return SUCCESS;
88 }
89
GetEncodeFormat(PixelFormat format,int32_t & componentsNum)90 J_COLOR_SPACE JpegEncoder::GetEncodeFormat(PixelFormat format, int32_t &componentsNum)
91 {
92 J_COLOR_SPACE colorSpace = JCS_UNKNOWN;
93 int32_t components = 0;
94 switch (format) {
95 case PixelFormat::RGBA_F16:
96 case PixelFormat::RGBA_8888: {
97 colorSpace = JCS_EXT_RGBA;
98 components = COMPONENT_NUM_RGBA;
99 break;
100 }
101 case PixelFormat::BGRA_8888: {
102 colorSpace = JCS_EXT_BGRA;
103 components = COMPONENT_NUM_BGRA;
104 break;
105 }
106 case PixelFormat::ARGB_8888: {
107 colorSpace = JCS_EXT_ARGB;
108 components = COMPONENT_NUM_ARGB;
109 break;
110 }
111 case PixelFormat::ALPHA_8: {
112 colorSpace = JCS_GRAYSCALE;
113 components = COMPONENT_NUM_GRAY;
114 break;
115 }
116 case PixelFormat::RGB_565:
117 case PixelFormat::RGB_888: {
118 colorSpace = JCS_RGB;
119 components = COMPONENT_NUM_RGB;
120 break;
121 }
122 case PixelFormat::NV12:
123 case PixelFormat::NV21: {
124 colorSpace = JCS_YCbCr;
125 components = COMPONENT_NUM_YUV420SP;
126 break;
127 }
128 case PixelFormat::CMYK: {
129 colorSpace = JCS_CMYK;
130 components = COMPONENT_NUM_RGBA;
131 break;
132 }
133 default: {
134 IMAGE_LOGE("encode format:[%{public}d] is unsupported!", format);
135 break;
136 }
137 }
138 componentsNum = components;
139 return colorSpace;
140 }
141
AddImage(Media::PixelMap & pixelMap)142 uint32_t JpegEncoder::AddImage(Media::PixelMap &pixelMap)
143 {
144 ImageTrace imageTrace("JpegEncoder::AddImage");
145 if (pixelMaps_.size() >= JPEG_IMAGE_NUM) {
146 IMAGE_LOGE("add pixel map out of range:[%{public}u].", JPEG_IMAGE_NUM);
147 return ERR_IMAGE_ADD_PIXEL_MAP_FAILED;
148 }
149 pixelMaps_.push_back(&pixelMap);
150 return SUCCESS;
151 }
152
FinalizeEncode()153 uint32_t JpegEncoder::FinalizeEncode()
154 {
155 ImageTrace imageTrace("JpegEncoder::FinalizeEncode");
156 uint32_t errorCode = SetCommonConfig();
157 if (errorCode != SUCCESS) {
158 IMAGE_LOGE("set jpeg compress struct failed:%{public}u.", errorCode);
159 return errorCode;
160 }
161 const uint8_t *data = pixelMaps_[0]->GetPixels();
162 if (data == nullptr) {
163 IMAGE_LOGE("encode image buffer is null.");
164 return ERR_IMAGE_INVALID_PARAMETER;
165 }
166 PixelFormat pixelFormat = pixelMaps_[0]->GetPixelFormat();
167 if (pixelFormat == PixelFormat::NV21 || pixelFormat == PixelFormat::NV12) {
168 errorCode = Yuv420spEncoder(data);
169 } else if (pixelFormat == PixelFormat::RGBA_F16) {
170 errorCode = RGBAF16Encoder(data);
171 } else if (pixelFormat == PixelFormat::RGB_565) {
172 errorCode = RGB565Encoder(data);
173 } else {
174 errorCode = SequenceEncoder(data);
175 }
176 if (errorCode != SUCCESS) {
177 IMAGE_LOGE("encode jpeg failed:%{public}u.", errorCode);
178 }
179 return errorCode;
180 }
181
SetCommonConfig()182 uint32_t JpegEncoder::SetCommonConfig()
183 {
184 if (pixelMaps_.empty()) {
185 IMAGE_LOGE("encode image failed, no pixel map input.");
186 return ERR_IMAGE_INVALID_PARAMETER;
187 }
188 if (setjmp(jerr_.setjmp_buffer)) {
189 IMAGE_LOGE("encode image error, set config failed.");
190 return ERR_IMAGE_ENCODE_FAILED;
191 }
192 encodeInfo_.image_width = pixelMaps_[0]->GetWidth();
193 encodeInfo_.image_height = pixelMaps_[0]->GetHeight();
194 PixelFormat pixelFormat = pixelMaps_[0]->GetPixelFormat();
195 encodeInfo_.in_color_space = GetEncodeFormat(pixelFormat, encodeInfo_.input_components);
196 if (encodeInfo_.in_color_space == JCS_UNKNOWN) {
197 IMAGE_LOGE("set input jpeg color space invalid.");
198 return ERR_IMAGE_UNKNOWN_FORMAT;
199 }
200 IMAGE_LOGD("width=%{public}u, height=%{public}u, colorspace=%{public}d, components=%{public}d.",
201 encodeInfo_.image_width, encodeInfo_.image_height, encodeInfo_.in_color_space, encodeInfo_.input_components);
202 jpeg_set_defaults(&encodeInfo_);
203 int32_t quality = encodeOpts_.quality;
204 jpeg_set_quality(&encodeInfo_, quality, TRUE);
205 return SUCCESS;
206 }
207
SequenceEncoder(const uint8_t * data)208 uint32_t JpegEncoder::SequenceEncoder(const uint8_t *data)
209 {
210 if (setjmp(jerr_.setjmp_buffer)) {
211 IMAGE_LOGE("encode image error.");
212 return ERR_IMAGE_ENCODE_FAILED;
213 }
214 jpeg_start_compress(&encodeInfo_, TRUE);
215
216 #ifdef IMAGE_COLORSPACE_FLAG
217 // packing icc profile.
218 SkImageInfo skImageInfo;
219 OHOS::ColorManager::ColorSpace grColorSpace = pixelMaps_[0]->InnerGetGrColorSpace();
220 sk_sp<SkColorSpace> skColorSpace = grColorSpace.ToSkColorSpace();
221
222 // when there is colorspace data, package it.
223 if (skColorSpace != nullptr) {
224 int width = 0;
225 int height = 0;
226 SkColorType ct = SkColorType::kUnknown_SkColorType;
227 SkAlphaType at = SkAlphaType::kUnknown_SkAlphaType;
228 skImageInfo = SkImageInfo::Make(width, height, ct, at, skColorSpace);
229 uint32_t iccPackedresult = iccProfileInfo_.PackingICCProfile(&encodeInfo_, skImageInfo);
230 if (iccPackedresult == OHOS::Media::ERR_IMAGE_ENCODE_ICC_FAILED) {
231 IMAGE_LOGE("encode image icc error.");
232 return iccPackedresult;
233 }
234 }
235 #endif
236
237 uint8_t *base = const_cast<uint8_t *>(data);
238 uint32_t rowStride = encodeInfo_.image_width * encodeInfo_.input_components;
239 uint8_t *buffer = nullptr;
240 while (encodeInfo_.next_scanline < encodeInfo_.image_height) {
241 buffer = base + encodeInfo_.next_scanline * rowStride;
242 jpeg_write_scanlines(&encodeInfo_, &buffer, RW_LINE_NUM);
243 }
244 jpeg_finish_compress(&encodeInfo_);
245 return SUCCESS;
246 }
247
SetYuv420spExtraConfig()248 void JpegEncoder::SetYuv420spExtraConfig()
249 {
250 encodeInfo_.raw_data_in = TRUE;
251 encodeInfo_.dct_method = JDCT_IFAST;
252 encodeInfo_.comp_info[INDEX_ZERO].h_samp_factor = SAMPLE_FACTOR_TWO;
253 encodeInfo_.comp_info[INDEX_ZERO].v_samp_factor = SAMPLE_FACTOR_TWO;
254 encodeInfo_.comp_info[INDEX_ONE].h_samp_factor = SAMPLE_FACTOR_ONE;
255 encodeInfo_.comp_info[INDEX_ONE].v_samp_factor = SAMPLE_FACTOR_ONE;
256 encodeInfo_.comp_info[INDEX_TWO].h_samp_factor = SAMPLE_FACTOR_ONE;
257 encodeInfo_.comp_info[INDEX_TWO].v_samp_factor = SAMPLE_FACTOR_ONE;
258 }
259
Yuv420spEncoder(const uint8_t * data)260 uint32_t JpegEncoder::Yuv420spEncoder(const uint8_t *data)
261 {
262 SetYuv420spExtraConfig();
263 jpeg_start_compress(&encodeInfo_, TRUE);
264 JSAMPROW y[Y_SAMPLE_ROW];
265 JSAMPROW u[UV_SAMPLE_ROW];
266 JSAMPROW v[UV_SAMPLE_ROW];
267 JSAMPARRAY planes[COMPONENT_NUM_YUV420SP]{ y, u, v };
268 uint32_t width = encodeInfo_.image_width;
269 uint32_t height = encodeInfo_.image_height;
270 uint32_t yPlaneSize = width * height;
271 uint8_t *yPlane = const_cast<uint8_t *>(data);
272 uint8_t *uvPlane = const_cast<uint8_t *>(data + yPlaneSize);
273 auto uPlane = std::make_unique<uint8_t[]>((width >> SHIFT_MASK) * UV_SAMPLE_ROW);
274 if (uPlane == nullptr) {
275 IMAGE_LOGE("allocate uPlane memory failed.");
276 return ERR_IMAGE_MALLOC_ABNORMAL;
277 }
278 auto vPlane = std::make_unique<uint8_t[]>((width >> SHIFT_MASK) * UV_SAMPLE_ROW);
279 if (vPlane == nullptr) {
280 IMAGE_LOGE("allocate vPlane memory failed.");
281 return ERR_IMAGE_MALLOC_ABNORMAL;
282 }
283 while (encodeInfo_.next_scanline < height) {
284 Deinterweave(uvPlane, uPlane.get(), vPlane.get(), encodeInfo_.next_scanline, width, height);
285 for (uint32_t i = 0; i < Y_SAMPLE_ROW; i++) {
286 y[i] = yPlane + (encodeInfo_.next_scanline + i) * width;
287 if ((i & SHIFT_MASK) == 0) {
288 uint32_t offset = (i >> SHIFT_MASK) * (width >> SHIFT_MASK);
289 u[i >> SHIFT_MASK] = uPlane.get() + offset;
290 v[i >> SHIFT_MASK] = vPlane.get() + offset;
291 }
292 }
293 jpeg_write_raw_data(&encodeInfo_, planes, Y_SAMPLE_ROW);
294 }
295 jpeg_finish_compress(&encodeInfo_);
296 return SUCCESS;
297 }
298
Deinterweave(uint8_t * uvPlane,uint8_t * uPlane,uint8_t * vPlane,uint32_t curRow,uint32_t width,uint32_t height)299 void JpegEncoder::Deinterweave(uint8_t *uvPlane, uint8_t *uPlane, uint8_t *vPlane, uint32_t curRow, uint32_t width,
300 uint32_t height)
301 {
302 PixelFormat pixelFormat = pixelMaps_[0]->GetPixelFormat();
303 uint32_t rowNum = (height - curRow) >> SHIFT_MASK;
304 if (rowNum > UV_SAMPLE_ROW) {
305 rowNum = UV_SAMPLE_ROW;
306 }
307 uint8_t indexZero = INDEX_ZERO;
308 uint8_t indexOne = INDEX_ONE;
309 if (pixelFormat != PixelFormat::NV12) {
310 std::swap(indexZero, indexOne);
311 }
312
313 for (uint32_t row = 0; row < rowNum; row++) {
314 uint32_t offset = ((curRow >> SHIFT_MASK) + row) * width;
315 uint8_t *uv = uvPlane + offset;
316 uint32_t col = width >> SHIFT_MASK;
317 for (uint32_t i = 0; i < col; i++) {
318 uint32_t index = row * col + i;
319 uPlane[index] = uv[indexZero];
320 vPlane[index] = uv[indexOne];
321 uv += INDEX_TWO;
322 }
323 }
324 }
325
RGBAF16Encoder(const uint8_t * data)326 uint32_t JpegEncoder::RGBAF16Encoder(const uint8_t *data)
327 {
328 if (setjmp(jerr_.setjmp_buffer)) {
329 IMAGE_LOGE("encode image error.");
330 return ERR_IMAGE_ENCODE_FAILED;
331 }
332 jpeg_start_compress(&encodeInfo_, TRUE);
333 uint8_t *base = const_cast<uint8_t *>(data);
334 uint32_t rowStride = encodeInfo_.image_width * encodeInfo_.input_components;
335 uint32_t orgRowStride = encodeInfo_.image_width * PIXEL_SIZE_RGBA_F16;
336 uint8_t *buffer = nullptr;
337 auto rowBuffer = std::make_unique<uint8_t[]>(rowStride);
338 while (encodeInfo_.next_scanline < encodeInfo_.image_height) {
339 buffer = base + encodeInfo_.next_scanline * orgRowStride;
340 for (uint32_t i = 0; i < rowStride;i++) {
341 float orgPlane = HalfToFloat(U8ToU16(buffer[i*2], buffer[i*2+1]));
342 rowBuffer[i] = static_cast<uint8_t>(orgPlane/MAX_HALF*ALPHA_OPAQUE);
343 }
344 uint8_t *rowBufferPtr = rowBuffer.get();
345 jpeg_write_scanlines(&encodeInfo_, &rowBufferPtr, RW_LINE_NUM);
346 }
347 jpeg_finish_compress(&encodeInfo_);
348 return SUCCESS;
349 }
350
RGB565Encoder(const uint8_t * data)351 uint32_t JpegEncoder::RGB565Encoder(const uint8_t *data)
352 {
353 IMAGE_LOGD("RGB565Encoder IN.");
354 if (setjmp(jerr_.setjmp_buffer)) {
355 IMAGE_LOGE("encode image error.");
356 return ERR_IMAGE_ENCODE_FAILED;
357 }
358
359 jpeg_start_compress(&encodeInfo_, TRUE);
360
361 uint8_t *base = const_cast<uint8_t *>(data);
362
363 uint32_t orgRowStride = encodeInfo_.image_width * PIXEL_SIZE_RGB565;
364 uint8_t *orgRowBuffer = nullptr;
365
366 uint32_t outRowStride = encodeInfo_.image_width * encodeInfo_.input_components;
367 auto outRowBuffer = std::make_unique<uint8_t[]>(outRowStride);
368
369 while (encodeInfo_.next_scanline < encodeInfo_.image_height) {
370 orgRowBuffer = base + encodeInfo_.next_scanline * orgRowStride;
371 skcms(reinterpret_cast<char*>(&outRowBuffer[0]),
372 reinterpret_cast<const char*>(orgRowBuffer),
373 encodeInfo_.image_width,
374 skcms_PixelFormat_RGB_565,
375 skcms_AlphaFormat_Unpremul,
376 skcms_PixelFormat_RGB_888,
377 skcms_AlphaFormat_Unpremul);
378
379 uint8_t *rowBufferPtr = outRowBuffer.get();
380 jpeg_write_scanlines(&encodeInfo_, &rowBufferPtr, RW_LINE_NUM);
381 }
382
383 jpeg_finish_compress(&encodeInfo_);
384 IMAGE_LOGD("RGB565Encoder OUT.");
385 return SUCCESS;
386 }
387
~JpegEncoder()388 JpegEncoder::~JpegEncoder()
389 {
390 jpeg_destroy_compress(&encodeInfo_);
391 pixelMaps_.clear();
392 }
393 } // namespace ImagePlugin
394 } // namespace OHOS
395