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