• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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