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