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