• 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 "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