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 "jerror.h"
17 #include "jpeg_encoder.h"
18 #include "media_errors.h"
19
20 namespace OHOS {
21 namespace ImagePlugin {
22 using namespace OHOS::HiviewDFX;
23 using namespace MultimediaPlugin;
24 using namespace Media;
25
26 constexpr uint32_t COMPONENT_NUM_ARGB = 4;
27 constexpr uint32_t COMPONENT_NUM_RGBA = 4;
28 constexpr uint32_t COMPONENT_NUM_BGRA = 4;
29 constexpr uint32_t COMPONENT_NUM_RGB = 3;
30 constexpr uint32_t COMPONENT_NUM_GRAY = 1;
31 // yuv format
32 constexpr uint8_t COMPONENT_NUM_YUV420SP = 3;
33 constexpr uint8_t Y_SAMPLE_ROW = 16;
34 constexpr uint8_t UV_SAMPLE_ROW = 8;
35 constexpr uint8_t SAMPLE_FACTOR_ONE = 1;
36 constexpr uint8_t SAMPLE_FACTOR_TWO = 2;
37 constexpr uint8_t INDEX_ZERO = 0;
38 constexpr uint8_t INDEX_ONE = 1;
39 constexpr uint8_t INDEX_TWO = 2;
40 constexpr uint8_t SHIFT_MASK = 1;
41
JpegDstMgr(OutputDataStream * stream)42 JpegDstMgr::JpegDstMgr(OutputDataStream *stream) : outputStream(stream)
43 {
44 init_destination = InitDstStream;
45 empty_output_buffer = EmptyOutputBuffer;
46 term_destination = TermDstStream;
47 }
48
JpegEncoder()49 JpegEncoder::JpegEncoder() : dstMgr_(nullptr)
50 {
51 // create decompress struct
52 jpeg_create_compress(&encodeInfo_);
53
54 // set error output
55 encodeInfo_.err = jpeg_std_error(&jerr_);
56 jerr_.error_exit = ErrorExit;
57 if (encodeInfo_.err == nullptr) {
58 HiLog::Error(LABEL, "create jpeg encoder failed.");
59 return;
60 }
61 encodeInfo_.err->output_message = &OutputErrorMessage;
62 }
63
StartEncode(OutputDataStream & outputStream,PlEncodeOptions & option)64 uint32_t JpegEncoder::StartEncode(OutputDataStream &outputStream, PlEncodeOptions &option)
65 {
66 pixelMaps_.clear();
67 dstMgr_.outputStream = &outputStream;
68 encodeInfo_.dest = &dstMgr_;
69 encodeOpts_ = option;
70 return SUCCESS;
71 }
72
GetEncodeFormat(PixelFormat format,int32_t & componentsNum)73 J_COLOR_SPACE JpegEncoder::GetEncodeFormat(PixelFormat format, int32_t &componentsNum)
74 {
75 J_COLOR_SPACE colorSpace = JCS_UNKNOWN;
76 int32_t components = 0;
77 switch (format) {
78 case PixelFormat::RGBA_8888: {
79 colorSpace = JCS_EXT_RGBA;
80 components = COMPONENT_NUM_RGBA;
81 break;
82 }
83 case PixelFormat::BGRA_8888: {
84 colorSpace = JCS_EXT_BGRA;
85 components = COMPONENT_NUM_BGRA;
86 break;
87 }
88 case PixelFormat::ARGB_8888: {
89 colorSpace = JCS_EXT_ARGB;
90 components = COMPONENT_NUM_ARGB;
91 break;
92 }
93 case PixelFormat::ALPHA_8: {
94 colorSpace = JCS_GRAYSCALE;
95 components = COMPONENT_NUM_GRAY;
96 break;
97 }
98 case PixelFormat::RGB_888: {
99 colorSpace = JCS_RGB;
100 components = COMPONENT_NUM_RGB;
101 break;
102 }
103 case PixelFormat::NV12:
104 case PixelFormat::NV21: {
105 colorSpace = JCS_YCbCr;
106 components = COMPONENT_NUM_YUV420SP;
107 break;
108 }
109 case PixelFormat::CMYK: {
110 colorSpace = JCS_CMYK;
111 components = COMPONENT_NUM_RGBA;
112 break;
113 }
114 default: {
115 HiLog::Error(LABEL, "encode format:[%{public}d] is unsupported!", format);
116 break;
117 }
118 }
119 componentsNum = components;
120 return colorSpace;
121 }
122
AddImage(Media::PixelMap & pixelMap)123 uint32_t JpegEncoder::AddImage(Media::PixelMap &pixelMap)
124 {
125 if (pixelMaps_.size() >= JPEG_IMAGE_NUM) {
126 HiLog::Error(LABEL, "add pixel map out of range:[%{public}u].", JPEG_IMAGE_NUM);
127 return ERR_IMAGE_ADD_PIXEL_MAP_FAILED;
128 }
129 pixelMaps_.push_back(&pixelMap);
130 return SUCCESS;
131 }
132
FinalizeEncode()133 uint32_t JpegEncoder::FinalizeEncode()
134 {
135 uint32_t errorCode = SetCommonConfig();
136 if (errorCode != SUCCESS) {
137 HiLog::Error(LABEL, "set jpeg compress struct failed:%{public}u.", errorCode);
138 return errorCode;
139 }
140 const uint8_t *data = pixelMaps_[0]->GetPixels();
141 if (data == nullptr) {
142 HiLog::Error(LABEL, "encode image buffer is null.");
143 return ERR_IMAGE_INVALID_PARAMETER;
144 }
145 PixelFormat pixelFormat = pixelMaps_[0]->GetPixelFormat();
146 if (pixelFormat == PixelFormat::NV21 || pixelFormat == PixelFormat::NV12) {
147 errorCode = Yuv420spEncoder(data);
148 } else {
149 errorCode = SequenceEncoder(data);
150 }
151 if (errorCode != SUCCESS) {
152 HiLog::Error(LABEL, "encode jpeg failed:%{public}u.", errorCode);
153 }
154 return errorCode;
155 }
156
SetCommonConfig()157 uint32_t JpegEncoder::SetCommonConfig()
158 {
159 if (pixelMaps_.empty()) {
160 HiLog::Error(LABEL, "encode image failed, no pixel map input.");
161 return ERR_IMAGE_INVALID_PARAMETER;
162 }
163 if (setjmp(jerr_.setjmp_buffer)) {
164 HiLog::Error(LABEL, "encode image error, set config failed.");
165 return ERR_IMAGE_ENCODE_FAILED;
166 }
167 encodeInfo_.image_width = pixelMaps_[0]->GetWidth();
168 encodeInfo_.image_height = pixelMaps_[0]->GetHeight();
169 PixelFormat pixelFormat = pixelMaps_[0]->GetPixelFormat();
170 encodeInfo_.in_color_space = GetEncodeFormat(pixelFormat, encodeInfo_.input_components);
171 if (encodeInfo_.in_color_space == JCS_UNKNOWN) {
172 HiLog::Error(LABEL, "set input jpeg color space invalid.");
173 return ERR_IMAGE_UNKNOWN_FORMAT;
174 }
175 HiLog::Debug(LABEL, "width=%{public}u, height=%{public}u, colorspace=%{public}d, components=%{public}d.",
176 encodeInfo_.image_width, encodeInfo_.image_height, encodeInfo_.in_color_space,
177 encodeInfo_.input_components);
178 jpeg_set_defaults(&encodeInfo_);
179 int32_t quality = encodeOpts_.quality;
180 jpeg_set_quality(&encodeInfo_, quality, TRUE);
181 return SUCCESS;
182 }
183
SequenceEncoder(const uint8_t * data)184 uint32_t JpegEncoder::SequenceEncoder(const uint8_t *data)
185 {
186 if (setjmp(jerr_.setjmp_buffer)) {
187 HiLog::Error(LABEL, "encode image error.");
188 return ERR_IMAGE_ENCODE_FAILED;
189 }
190 jpeg_start_compress(&encodeInfo_, TRUE);
191 uint8_t *base = const_cast<uint8_t *>(data);
192 uint32_t rowStride = encodeInfo_.image_width * encodeInfo_.input_components;
193 uint8_t *buffer = nullptr;
194 while (encodeInfo_.next_scanline < encodeInfo_.image_height) {
195 buffer = base + encodeInfo_.next_scanline * rowStride;
196 jpeg_write_scanlines(&encodeInfo_, &buffer, RW_LINE_NUM);
197 }
198 jpeg_finish_compress(&encodeInfo_);
199 return SUCCESS;
200 }
201
SetYuv420spExtraConfig()202 void JpegEncoder::SetYuv420spExtraConfig()
203 {
204 encodeInfo_.raw_data_in = TRUE;
205 encodeInfo_.dct_method = JDCT_IFAST;
206 encodeInfo_.comp_info[INDEX_ZERO].h_samp_factor = SAMPLE_FACTOR_TWO;
207 encodeInfo_.comp_info[INDEX_ZERO].v_samp_factor = SAMPLE_FACTOR_TWO;
208 encodeInfo_.comp_info[INDEX_ONE].h_samp_factor = SAMPLE_FACTOR_ONE;
209 encodeInfo_.comp_info[INDEX_ONE].v_samp_factor = SAMPLE_FACTOR_ONE;
210 encodeInfo_.comp_info[INDEX_TWO].h_samp_factor = SAMPLE_FACTOR_ONE;
211 encodeInfo_.comp_info[INDEX_TWO].v_samp_factor = SAMPLE_FACTOR_ONE;
212 }
213
Yuv420spEncoder(const uint8_t * data)214 uint32_t JpegEncoder::Yuv420spEncoder(const uint8_t *data)
215 {
216 SetYuv420spExtraConfig();
217 jpeg_start_compress(&encodeInfo_, TRUE);
218 JSAMPROW y[Y_SAMPLE_ROW];
219 JSAMPROW u[UV_SAMPLE_ROW];
220 JSAMPROW v[UV_SAMPLE_ROW];
221 JSAMPARRAY planes[COMPONENT_NUM_YUV420SP]{ y, u, v };
222 uint32_t width = encodeInfo_.image_width;
223 uint32_t height = encodeInfo_.image_height;
224 uint32_t yPlaneSize = width * height;
225 uint8_t *yPlane = const_cast<uint8_t *>(data);
226 uint8_t *uvPlane = const_cast<uint8_t *>(data + yPlaneSize);
227 auto uPlane = std::make_unique<uint8_t[]>((width >> SHIFT_MASK) * UV_SAMPLE_ROW);
228 if (uPlane == nullptr) {
229 HiLog::Error(LABEL, "allocate uPlane memory failed.");
230 return ERR_IMAGE_MALLOC_ABNORMAL;
231 }
232 auto vPlane = std::make_unique<uint8_t[]>((width >> SHIFT_MASK) * UV_SAMPLE_ROW);
233 if (vPlane == nullptr) {
234 HiLog::Error(LABEL, "allocate vPlane memory failed.");
235 return ERR_IMAGE_MALLOC_ABNORMAL;
236 }
237 while (encodeInfo_.next_scanline < height) {
238 Deinterweave(uvPlane, uPlane.get(), vPlane.get(), encodeInfo_.next_scanline, width, height);
239 for (uint32_t i = 0; i < Y_SAMPLE_ROW; i++) {
240 y[i] = yPlane + (encodeInfo_.next_scanline + i) * width;
241 if ((i & SHIFT_MASK) == 0) {
242 uint32_t offset = (i >> SHIFT_MASK) * (width >> SHIFT_MASK);
243 u[i >> SHIFT_MASK] = uPlane.get() + offset;
244 v[i >> SHIFT_MASK] = vPlane.get() + offset;
245 }
246 }
247 jpeg_write_raw_data(&encodeInfo_, planes, Y_SAMPLE_ROW);
248 }
249 jpeg_finish_compress(&encodeInfo_);
250 return SUCCESS;
251 }
252
Deinterweave(uint8_t * uvPlane,uint8_t * uPlane,uint8_t * vPlane,uint32_t curRow,uint32_t width,uint32_t height)253 void JpegEncoder::Deinterweave(uint8_t *uvPlane, uint8_t *uPlane, uint8_t *vPlane, uint32_t curRow, uint32_t width,
254 uint32_t height)
255 {
256 PixelFormat pixelFormat = pixelMaps_[0]->GetPixelFormat();
257 uint32_t rowNum = (height - curRow) >> SHIFT_MASK;
258 if (rowNum > UV_SAMPLE_ROW) {
259 rowNum = UV_SAMPLE_ROW;
260 }
261 uint8_t indexZero = INDEX_ZERO;
262 uint8_t indexOne = INDEX_ONE;
263 if (pixelFormat != PixelFormat::NV12) {
264 std::swap(indexZero, indexOne);
265 }
266
267 for (uint32_t row = 0; row < rowNum; row++) {
268 uint32_t offset = ((curRow >> SHIFT_MASK) + row) * width;
269 uint8_t *uv = uvPlane + offset;
270 uint32_t col = width >> SHIFT_MASK;
271 for (uint32_t i = 0; i < col; i++) {
272 uint32_t index = row * col + i;
273 uPlane[index] = uv[indexZero];
274 vPlane[index] = uv[indexOne];
275 uv += INDEX_TWO;
276 }
277 }
278 }
279
~JpegEncoder()280 JpegEncoder::~JpegEncoder()
281 {
282 jpeg_destroy_compress(&encodeInfo_);
283 pixelMaps_.clear();
284 }
285 } // namespace ImagePlugin
286 } // namespace OHOS
287