1 /*
2  * Copyright (C) 2024 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_mpf_parser.h"
17 
18 #include <vector>
19 #include "hilog/log_cpp.h"
20 #include "image_log.h"
21 #include "image_utils.h"
22 #include "media_errors.h"
23 
24 #undef LOG_DOMAIN
25 #define LOG_DOMAIN LOG_TAG_DOMAIN_ID_IMAGE
26 
27 #undef LOG_TAG
28 #define LOG_TAG "JpegMpfParser"
29 
30 namespace OHOS {
31 namespace Media {
32 
33 using namespace std;
34 
35 constexpr uint8_t MP_INDEX_IFD_BYTE_SIZE = 12;
36 constexpr uint8_t MP_ENTRY_BYTE_SIZE = 16;
37 constexpr uint8_t UINT16_BYTE_SIZE = 2;
38 constexpr uint8_t UINT32_BYTE_SIZE = 4;
39 constexpr uint16_t TAG_TYPE_UNDEFINED = 0x07;
40 constexpr uint16_t TAG_TYPE_LONG = 0x04;
41 constexpr uint16_t HDR_MULTI_PICTURE_APP_LENGTH = 90;
42 constexpr uint16_t FRAGMENT_METADATA_LENGTH = 20;
43 constexpr uint16_t AUXILIARY_TAG_NAME_LENGTH = 8;
44 
45 constexpr uint8_t JPEG_MARKER_PREFIX = 0xFF;
46 constexpr uint8_t JPEG_MARKER_APP2 = 0xE2;
47 
48 static constexpr uint8_t MULTI_PICTURE_HEADER_FLAG[] = {
49     'M', 'P', 'F', '\0'
50 };
51 static constexpr uint8_t BIG_ENDIAN_FLAG[] = {
52     0x4D, 0x4D, 0x00, 0x2A
53 };
54 static constexpr uint8_t LITTLE_ENDIAN_FLAG[] = {
55     0x49, 0x49, 0x2A, 0x00
56 };
57 
58 static constexpr uint8_t MPF_VERSION_DEFAULT[] = {
59     '0', '1', '0', '0'
60 };
61 
62 static constexpr uint8_t FRAGMENT_META_FLAG[] = {
63     0xFF, 0xEC, 0x00, 0x12
64 };
65 
66 enum MpfIFDTag : uint16_t {
67     MPF_VERSION_TAG = 45056,
68     NUMBERS_OF_IMAGES_TAG = 45057,
69     MP_ENTRY_TAG = 45058,
70     IMAGE_UID_LIST_TAG = 45059,
71     TOTAL_FRAMES_TAG = 45060,
72 };
73 
74 static const std::map<std::string, AuxiliaryPictureType> AUXILIARY_TAG_TYPE_MAP = {
75     {AUXILIARY_TAG_DEPTH_MAP_BACK, AuxiliaryPictureType::DEPTH_MAP},
76     {AUXILIARY_TAG_DEPTH_MAP_FRONT, AuxiliaryPictureType::DEPTH_MAP},
77     {AUXILIARY_TAG_UNREFOCUS_MAP, AuxiliaryPictureType::UNREFOCUS_MAP},
78     {AUXILIARY_TAG_LINEAR_MAP, AuxiliaryPictureType::LINEAR_MAP},
79     {AUXILIARY_TAG_FRAGMENT_MAP, AuxiliaryPictureType::FRAGMENT_MAP}
80 };
81 
CheckMpfOffset(uint8_t * data,uint32_t size,uint32_t & offset)82 bool JpegMpfParser::CheckMpfOffset(uint8_t* data, uint32_t size, uint32_t& offset)
83 {
84     if (data == nullptr) {
85         return false;
86     }
87     for (offset = 0; offset < size; offset++) {
88         if (data[offset] == JPEG_MARKER_PREFIX && (data[offset + 1] == JPEG_MARKER_APP2)) {
89             offset += UINT32_BYTE_SIZE;
90             return true;
91         }
92     }
93     return false;
94 }
95 
Parsing(uint8_t * data,uint32_t size)96 bool JpegMpfParser::Parsing(uint8_t* data, uint32_t size)
97 {
98     if (data == nullptr || size == 0) {
99         return false;
100     }
101     if (memcmp(data, MULTI_PICTURE_HEADER_FLAG, sizeof(MULTI_PICTURE_HEADER_FLAG)) != 0) {
102         return false;
103     }
104     data += UINT32_BYTE_SIZE;
105     size -= UINT32_BYTE_SIZE;
106     uint32_t dataOffset = 0;
107     bool isBigEndian = false;
108     if (memcmp(data, BIG_ENDIAN_FLAG, sizeof(BIG_ENDIAN_FLAG)) == 0) {
109         isBigEndian = true;
110     } else if (memcmp(data, LITTLE_ENDIAN_FLAG, sizeof(LITTLE_ENDIAN_FLAG)) == 0) {
111         isBigEndian = false;
112     } else {
113         return false;
114     }
115     dataOffset += UINT32_BYTE_SIZE;
116     uint32_t ifdOffset = ImageUtils::BytesToUint32(data, dataOffset, isBigEndian);
117     if (ifdOffset < dataOffset || ifdOffset > size) {
118         IMAGE_LOGD("get ifd offset error");
119         return false;
120     }
121     dataOffset = ifdOffset;
122     return ParsingMpIndexIFD(data, size, dataOffset, isBigEndian);
123 }
124 
ParsingMpIndexIFD(uint8_t * data,uint32_t size,uint32_t dataOffset,bool isBigEndian)125 bool JpegMpfParser::ParsingMpIndexIFD(uint8_t* data, uint32_t size, uint32_t dataOffset, bool isBigEndian)
126 {
127     uint16_t tagCount = ImageUtils::BytesToUint16(data, dataOffset, isBigEndian);
128     if (dataOffset + MP_INDEX_IFD_BYTE_SIZE * tagCount > size) {
129         return false;
130     }
131     uint16_t previousTag = 0;
132     for (int i = 0; i < tagCount; i++) {
133         uint16_t tag = ImageUtils::BytesToUint16(data, dataOffset, isBigEndian);
134         if (tag <= previousTag) {
135             return false;
136         }
137         previousTag = tag;
138         uint16_t type = ImageUtils::BytesToUint16(data, dataOffset, isBigEndian);
139         uint32_t count = ImageUtils::BytesToUint32(data, dataOffset, isBigEndian);
140         uint32_t value = ImageUtils::BytesToUint32(data, dataOffset, isBigEndian);
141         IMAGE_LOGD("mpf tag=%{public}d,type=%{public}d,count=%{public}d,value=%{public}d", tag, type, count, value);
142         switch (tag) {
143             case MpfIFDTag::MPF_VERSION_TAG:
144                 if (memcmp(data + (dataOffset - UINT32_BYTE_SIZE), MPF_VERSION_DEFAULT,
145                     sizeof(MPF_VERSION_DEFAULT)) != 0) {
146                     return false;
147                 }
148                 break;
149             case MpfIFDTag::NUMBERS_OF_IMAGES_TAG:
150                 imageNums_ = value;
151                 break;
152             case MpfIFDTag::MP_ENTRY_TAG:
153                 if (count != MP_ENTRY_BYTE_SIZE * imageNums_ || value < dataOffset || value > size) {
154                     return false;
155                 }
156                 if (!ParsingMpEntry(data + value, size - value, isBigEndian, imageNums_)) {
157                     IMAGE_LOGD("mpf parse entry failed");
158                     return false;
159                 }
160                 break;
161             default:
162                 break;
163         }
164     }
165     uint32_t mpAttrIFDOffset = ImageUtils::BytesToUint32(data, dataOffset, isBigEndian);
166     if (mpAttrIFDOffset > 0 && dataOffset > mpAttrIFDOffset) {
167         return false;
168     }
169     return true;
170 }
171 
ParsingMpEntry(uint8_t * data,uint32_t size,bool isBigEndian,uint32_t imageNums)172 bool JpegMpfParser::ParsingMpEntry(uint8_t* data, uint32_t size, bool isBigEndian, uint32_t imageNums)
173 {
174     uint32_t dataOffset = 0;
175     if (imageNums == 0 || imageNums * MP_ENTRY_BYTE_SIZE > size) {
176         return false;
177     }
178     images_.resize(imageNums);
179     for (uint32_t i = 0; i < imageNums; i++) {
180         uint32_t imageAttr = ImageUtils::BytesToUint32(data, dataOffset, isBigEndian);
181         images_[i].size = ImageUtils::BytesToUint32(data, dataOffset, isBigEndian);
182         images_[i].offset = ImageUtils::BytesToUint32(data, dataOffset, isBigEndian);
183         uint16_t image1EntryNum = ImageUtils::BytesToUint16(data, dataOffset, isBigEndian);
184         uint16_t image2EntryNum = ImageUtils::BytesToUint16(data, dataOffset, isBigEndian);
185         IMAGE_LOGD("index=%{public}d, imageAttr=%{public}d, image1entrynum=%{public}d, image2entryNum=%{puublic}d",
186             i, imageAttr, image1EntryNum, image2EntryNum);
187     }
188     return true;
189 }
190 
FindAuxiliaryTags(const uint8_t * data,uint32_t size,std::string & foundTag)191 static bool FindAuxiliaryTags(const uint8_t* data, uint32_t size, std::string& foundTag)
192 {
193     if (data == nullptr || size < AUXILIARY_TAG_NAME_LENGTH) {
194         return false;
195     }
196     for (const auto &[tagName, _] : AUXILIARY_TAG_TYPE_MAP) {
197         if (memcmp(data, tagName.c_str(), tagName.size()) == 0) {
198             foundTag = tagName;
199             return true;
200         }
201     }
202     return false;
203 }
204 
205 // |<------------------ Auxiliary picture structure ----------------->|
206 // |<- Image data ->|<- Image size(4 Bytes) ->|<- Tag name(8 Bytes) ->|
GetLastAuxiliaryTagOffset(const uint8_t * data,uint32_t size,std::string & foundTag)207 static int32_t GetLastAuxiliaryTagOffset(const uint8_t* data, uint32_t size, std::string& foundTag)
208 {
209     if (data == nullptr || size < AUXILIARY_TAG_NAME_LENGTH) {
210         return ERR_MEDIA_INVALID_VALUE;
211     }
212     uint32_t offset = size - AUXILIARY_TAG_NAME_LENGTH;
213     while (offset > 0) {
214         if (FindAuxiliaryTags(data + offset, size - offset, foundTag)) {
215             return static_cast<int32_t>(offset);
216         }
217         --offset;
218     }
219     return ERR_MEDIA_INVALID_VALUE;
220 }
221 
222 // Parse the following types of auxiliary pictures: DEPTH_MAP, UNREFOCUS_MAP, LINEAR_MAP, FRAGMENT_MAP
ParsingAuxiliaryPictures(uint8_t * data,uint32_t dataSize,bool isBigEndian)223 bool JpegMpfParser::ParsingAuxiliaryPictures(uint8_t* data, uint32_t dataSize, bool isBigEndian)
224 {
225     if (data == nullptr || dataSize == 0) {
226         return false;
227     }
228 
229     uint32_t offset = dataSize;
230     while (offset > 0) {
231         std::string foundTag("");
232         int32_t matchedPos = GetLastAuxiliaryTagOffset(data, offset, foundTag);
233         if (matchedPos == ERR_MEDIA_INVALID_VALUE) {
234             IMAGE_LOGI("%{public}s no more auxiliary pictures", __func__);
235             break;
236         }
237         offset = static_cast<uint32_t>(matchedPos);
238         auto it = AUXILIARY_TAG_TYPE_MAP.find(foundTag);
239         if (it == AUXILIARY_TAG_TYPE_MAP.end()) {
240             IMAGE_LOGW("%{public}s unknown auxiliary tag: %{public}s", __func__, foundTag.c_str());
241             continue;
242         }
243 
244         if (offset < UINT32_BYTE_SIZE) {
245             IMAGE_LOGW("%{public}s invalid offset: %{public}u, auxiliary tag: %{public}s",
246                 __func__, offset, foundTag.c_str());
247             continue;
248         }
249         offset -= UINT32_BYTE_SIZE;
250         uint32_t imageSize = ImageUtils::BytesToUint32(data, offset, isBigEndian);
251         if (offset < imageSize + UINT32_BYTE_SIZE) {
252             IMAGE_LOGW("%{public}s invalid image size: %{public}u, offset: %{public}u, auxiliary tag: %{public}s",
253                 __func__, imageSize, offset, foundTag.c_str());
254             continue;
255         }
256         offset = offset - imageSize - UINT32_BYTE_SIZE;
257         SingleJpegImage auxImage = {
258             .offset = offset,
259             .size = imageSize,
260             .auxType = it->second,
261             .auxTagName = it->first,
262         };
263         images_.push_back(auxImage);
264         IMAGE_LOGD("[%{public}s] auxType=%{public}d, offset=%{public}u, size=%{public}u, tagName=%{public}s",
265             __func__, auxImage.auxType, auxImage.offset, auxImage.size, auxImage.auxTagName.c_str());
266     }
267     return true;
268 }
269 
ParsingFragmentMetadata(uint8_t * data,uint32_t size,Rect & fragmentRect,bool isBigEndian)270 bool JpegMpfParser::ParsingFragmentMetadata(uint8_t* data, uint32_t size, Rect& fragmentRect, bool isBigEndian)
271 {
272     if (data == nullptr || size == 0) {
273         return false;
274     }
275 
276     for (uint32_t offset = 0; offset < size; offset++) {
277         if (memcmp(data + offset, FRAGMENT_META_FLAG, sizeof(FRAGMENT_META_FLAG)) == 0) {
278             if (offset + FRAGMENT_METADATA_LENGTH > size) {
279                 return false;
280             }
281             offset += UINT32_BYTE_SIZE;
282             fragmentRect.left = ImageUtils::BytesToInt32(data, offset, isBigEndian);
283             fragmentRect.top = ImageUtils::BytesToInt32(data, offset, isBigEndian);
284             fragmentRect.width = ImageUtils::BytesToInt32(data, offset, isBigEndian);
285             fragmentRect.height = ImageUtils::BytesToInt32(data, offset, isBigEndian);
286             IMAGE_LOGD("[%{public}s] left=%{public}d, top=%{public}d, width=%{public}d, height=%{public}d",
287                 __func__, fragmentRect.left, fragmentRect.top, fragmentRect.width, fragmentRect.height);
288             return true;
289         }
290     }
291     return false;
292 }
293 
WriteMPEntryToBytes(vector<uint8_t> & bytes,uint32_t & offset,std::vector<SingleJpegImage> images)294 static void WriteMPEntryToBytes(vector<uint8_t>& bytes, uint32_t& offset, std::vector<SingleJpegImage> images)
295 {
296     for (int i = 0; i < images.size(); i++) {
297         uint32_t attributeData = 0;
298         if (i == 0) {
299             // 0x20: representative image flag / 0x03: primary image type code;
300             attributeData = 0x20030000;
301         }
302         ImageUtils::Uint32ToBytes(attributeData, bytes, offset);
303         ImageUtils::Uint32ToBytes(images[i].size, bytes, offset);
304         ImageUtils::Uint32ToBytes(images[i].offset, bytes, offset);
305         const uint16_t dependentImage1EntryNumber = 0;
306         const uint16_t dependentImage2EntryNumber = 0;
307         ImageUtils::Uint16ToBytes(dependentImage1EntryNumber, bytes, offset);
308         ImageUtils::Uint16ToBytes(dependentImage2EntryNumber, bytes, offset);
309     }
310 }
311 
WriteMpIndexIFD(vector<uint8_t> & bytes,uint32_t & offset,uint8_t imageNum)312 static void WriteMpIndexIFD(vector<uint8_t>& bytes, uint32_t& offset, uint8_t imageNum)
313 {
314     // tag count is three(MPF_VERSION_TAG, NUMBERS_OF_IMAGES_TAG, MP_ENTRY_TAG)
315     const uint16_t tagCount = 3;
316     ImageUtils::Uint16ToBytes(tagCount, bytes, offset);
317 
318     // tag MPF_VERSION_TAG
319     const uint16_t versionTagCount = 4;
320     ImageUtils::Uint16ToBytes(MPF_VERSION_TAG, bytes, offset);
321     ImageUtils::Uint16ToBytes(TAG_TYPE_UNDEFINED, bytes, offset);
322     ImageUtils::Uint32ToBytes(versionTagCount, bytes, offset);
323     ImageUtils::ArrayToBytes(MPF_VERSION_DEFAULT, UINT32_BYTE_SIZE, bytes, offset);
324 
325     // tag NUMBERS_OF_IMAGES_TAG
326     const uint16_t imageNumTagCount = 1;
327     ImageUtils::Uint16ToBytes(NUMBERS_OF_IMAGES_TAG, bytes, offset);
328     ImageUtils::Uint16ToBytes(TAG_TYPE_LONG, bytes, offset);
329     ImageUtils::Uint32ToBytes(imageNumTagCount, bytes, offset);
330     ImageUtils::Uint32ToBytes(imageNum, bytes, offset);
331 
332     // tag MP_ENTRY_TAG
333     const uint32_t mpEntryCount = static_cast<uint32_t>(MP_ENTRY_BYTE_SIZE) * static_cast<uint32_t>(imageNum);
334     ImageUtils::Uint16ToBytes(MP_ENTRY_TAG, bytes, offset);
335     ImageUtils::Uint16ToBytes(TAG_TYPE_UNDEFINED, bytes, offset);
336     ImageUtils::Uint32ToBytes(mpEntryCount, bytes, offset);
337 
338     // offset-markerSize(2)-lengthSize(2)-MULTI_PICTURE_FLAG size(4)+mpEntryOffsetSize(4)+attributeIfdOffset(4)
339     uint32_t mpEntryOffset = offset - UINT16_BYTE_SIZE - UINT16_BYTE_SIZE - UINT32_BYTE_SIZE +
340         UINT32_BYTE_SIZE + UINT32_BYTE_SIZE;
341     ImageUtils::Uint32ToBytes(mpEntryOffset, bytes, offset);
342 }
343 
PackHdrJpegMpfMarker(SingleJpegImage base,SingleJpegImage gainmap)344 std::vector<uint8_t> JpegMpfPacker::PackHdrJpegMpfMarker(SingleJpegImage base, SingleJpegImage gainmap)
345 {
346     vector<uint8_t> bytes(HDR_MULTI_PICTURE_APP_LENGTH);
347     uint32_t index = 0;
348     bytes[index++] = 0xFF;
349     bytes[index++] = 0xE2;
350 
351     // length dont combine marker(0xFFE2)
352     ImageUtils::Uint16ToBytes(HDR_MULTI_PICTURE_APP_LENGTH - UINT16_BYTE_SIZE, bytes, index);
353     ImageUtils::ArrayToBytes(MULTI_PICTURE_HEADER_FLAG, UINT32_BYTE_SIZE, bytes, index);
354     ImageUtils::ArrayToBytes(BIG_ENDIAN_FLAG, UINT32_BYTE_SIZE, bytes, index);
355 
356     // BIG_ENDIAN_FLAG size + IFDOffset size
357     const uint32_t IFDOffset = UINT32_BYTE_SIZE + UINT32_BYTE_SIZE;
358     ImageUtils::Uint32ToBytes(IFDOffset, bytes, index);
359     std::vector<SingleJpegImage> images = {base, gainmap};
360     WriteMpIndexIFD(bytes, index, images.size());
361     const uint32_t attributeIfdOffset = 0;
362     ImageUtils::Uint32ToBytes(attributeIfdOffset, bytes, index);
363     WriteMPEntryToBytes(bytes, index, images);
364     return bytes;
365 }
366 
PackFragmentMetadata(Rect & fragmentRect,bool isBigEndian)367 std::vector<uint8_t> JpegMpfPacker::PackFragmentMetadata(Rect& fragmentRect, bool isBigEndian)
368 {
369     std::vector<uint8_t> bytes(FRAGMENT_METADATA_LENGTH);
370     uint32_t offset = 0;
371     ImageUtils::ArrayToBytes(FRAGMENT_META_FLAG, UINT32_BYTE_SIZE, bytes, offset);
372     ImageUtils::Int32ToBytes(fragmentRect.left, bytes, offset, isBigEndian);
373     ImageUtils::Int32ToBytes(fragmentRect.top, bytes, offset, isBigEndian);
374     ImageUtils::Int32ToBytes(fragmentRect.width, bytes, offset, isBigEndian);
375     ImageUtils::Int32ToBytes(fragmentRect.height, bytes, offset, isBigEndian);
376     return bytes;
377 }
378 
PackDataSize(uint32_t size,bool isBigEndian)379 std::vector<uint8_t> JpegMpfPacker::PackDataSize(uint32_t size, bool isBigEndian)
380 {
381     std::vector<uint8_t> bytes(UINT32_BYTE_SIZE);
382     uint32_t offset = 0;
383     ImageUtils::Uint32ToBytes(size, bytes, offset, isBigEndian);
384     return bytes;
385 }
386 
PackAuxiliaryTagName(std::string & tagName)387 std::vector<uint8_t> JpegMpfPacker::PackAuxiliaryTagName(std::string& tagName)
388 {
389     std::vector<uint8_t> bytes(AUXILIARY_TAG_NAME_LENGTH, 0x00);
390     if (tagName.size() <= AUXILIARY_TAG_NAME_LENGTH) {
391         std::copy(tagName.begin(), tagName.end(), bytes.begin());
392     }
393     return bytes;
394 }
395 }
396 }
397