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