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 "box/item_data_box.h"
17
18 namespace {
19 const uint8_t CONSTRUCTION_METHOD_FILE_OFFSET = 0;
20 const uint8_t CONSTRUCTION_METHOD_IDAT_OFFSET = 1;
21 const uint32_t MAX_HEIF_IMAGE_GRID_SIZE = 128 * 1024 * 1024;
22 const uint32_t MAX_HEIF_ITEM_COUNT = 2000;
23 }
24
25 namespace OHOS {
26 namespace ImagePlugin {
ParseExtents(Item & item,HeifStreamReader & reader,int indexSize,int offsetSize,int lengthSize)27 void HeifIlocBox::ParseExtents(Item& item, HeifStreamReader &reader, int indexSize, int offsetSize, int lengthSize)
28 {
29 uint16_t extentNum = reader.Read16();
30 item.extents.resize(extentNum);
31 for (int extentIndex = 0; extentIndex < extentNum; extentIndex++) {
32 // indexSize is taken from the set {0, 4, 8} and indicates the length in bytes of 'index'
33 if (GetVersion() >= HEIF_BOX_VERSION_ONE) {
34 item.extents[extentIndex].index = 0;
35 if (indexSize == UINT32_BYTES_NUM) {
36 item.extents[extentIndex].index = reader.Read32();
37 } else if (indexSize == UINT64_BYTES_NUM) {
38 item.extents[extentIndex].index = reader.Read64();
39 }
40 }
41
42 // offsetSize is taken from the set {0, 4, 8} and indicates the length in bytes of 'offset'
43 item.extents[extentIndex].offset = 0;
44 if (offsetSize == UINT32_BYTES_NUM) {
45 item.extents[extentIndex].offset = reader.Read32();
46 } else if (offsetSize == UINT64_BYTES_NUM) {
47 item.extents[extentIndex].offset = reader.Read64();
48 }
49
50 // lengthSize is taken from the set {0, 4, 8} and indicates the length in bytes of 'length'
51 item.extents[extentIndex].length = 0;
52 if (lengthSize == UINT32_BYTES_NUM) {
53 item.extents[extentIndex].length = reader.Read32();
54 } else if (lengthSize == UINT64_BYTES_NUM) {
55 item.extents[extentIndex].length = reader.Read64();
56 }
57 }
58 }
59
ParseContent(HeifStreamReader & reader)60 heif_error HeifIlocBox::ParseContent(HeifStreamReader &reader)
61 {
62 ParseFullHeader(reader);
63
64 const uint8_t offsetSizeShift = 12;
65 const uint8_t baseOffsetSizeShift = 4;
66 uint16_t values4 = reader.Read16();
67 int offsetSize = (values4 >> offsetSizeShift) & 0xF;
68 int lengthSize = (values4 >> ONE_BYTE_SHIFT) & 0xF;
69 int baseOffsetSize = (values4 >> baseOffsetSizeShift) & 0xF;
70 int indexSize = (GetVersion() >= HEIF_BOX_VERSION_ONE) ? (values4 & 0xF) : 0;
71 uint32_t itemCount = GetVersion() < HEIF_BOX_VERSION_TWO ? reader.Read16() : reader.Read32();
72 if (itemCount > MAX_HEIF_ITEM_COUNT) {
73 return heif_error_too_many_item;
74 }
75 for (uint32_t itemIndex = 0; itemIndex < itemCount; ++itemIndex) {
76 Item item;
77 item.itemId = GetVersion() < HEIF_BOX_VERSION_TWO ? reader.Read16() : reader.Read32();
78
79 if (GetVersion() >= HEIF_BOX_VERSION_ONE) {
80 values4 = reader.Read16();
81 item.constructionMethod = (values4 & 0xF);
82 }
83 item.dataReferenceIndex = reader.Read16();
84
85 // baseOffsetSize is taken from the set {0, 4, 8} and indicates the length in bytes of 'baseOffset'
86 item.baseOffset = 0;
87 if (baseOffsetSize == UINT32_BYTES_NUM) {
88 item.baseOffset = reader.Read32();
89 } else if (baseOffsetSize == UINT64_BYTES_NUM) {
90 item.baseOffset = reader.Read64();
91 }
92 ParseExtents(item, reader, indexSize, offsetSize, lengthSize);
93 if (!reader.HasError()) {
94 items_.push_back(item);
95 }
96 }
97 return reader.GetError();
98 }
99
GetIlocDataLength(const Item & item,size_t & length)100 heif_error HeifIlocBox::GetIlocDataLength(const Item &item, size_t &length)
101 {
102 for (const auto &extent: item.extents) {
103 if (extent.length > MAX_HEIF_IMAGE_GRID_SIZE) {
104 return heif_error_grid_too_large;
105 }
106 length = extent.length;
107 }
108 return heif_error_ok;
109 }
110
ReadData(const Item & item,const std::shared_ptr<HeifInputStream> & stream,const std::shared_ptr<HeifIdatBox> & idat,std::vector<uint8_t> * dest) const111 heif_error HeifIlocBox::ReadData(const Item &item, const std::shared_ptr<HeifInputStream> &stream,
112 const std::shared_ptr<HeifIdatBox> &idat, std::vector<uint8_t> *dest) const
113 {
114 for (const auto &extent: item.extents) {
115 if (item.constructionMethod == CONSTRUCTION_METHOD_FILE_OFFSET) {
116 stream->Seek(extent.offset + item.baseOffset);
117
118 size_t oldSize = dest->size();
119 if (extent.length > MAX_HEIF_IMAGE_GRID_SIZE) {
120 return heif_error_grid_too_large;
121 }
122 dest->resize(static_cast<size_t>(oldSize + extent.length));
123 stream->Read(reinterpret_cast<char*>(dest->data()) + oldSize, static_cast<size_t>(extent.length));
124 } else if (item.constructionMethod == CONSTRUCTION_METHOD_IDAT_OFFSET) {
125 if (!idat) {
126 return heif_error_no_idat;
127 }
128 uint64_t start = extent.offset + item.baseOffset;
129 idat->ReadData(stream, start, extent.length, *dest);
130 } else {
131 return heif_error_no_idat;
132 }
133 }
134
135 return heif_error_ok;
136 }
137
AppendData(heif_item_id itemId,const std::vector<uint8_t> & data,uint8_t constructionMethod)138 heif_error HeifIlocBox::AppendData(heif_item_id itemId, const std::vector<uint8_t> &data, uint8_t constructionMethod)
139 {
140 size_t idx;
141 for (idx = 0; idx < items_.size(); idx++) {
142 if (items_[idx].itemId == itemId) {
143 break;
144 }
145 }
146 if (idx == items_.size()) {
147 Item item;
148 item.itemId = itemId;
149 item.constructionMethod = constructionMethod;
150
151 items_.push_back(item);
152 }
153 Extent extent;
154 extent.data = data;
155 if (constructionMethod == CONSTRUCTION_METHOD_IDAT_OFFSET) {
156 extent.offset = idatOffset_;
157 extent.length = data.size();
158
159 idatOffset_ += data.size();
160 }
161 items_[idx].extents.push_back(std::move(extent));
162 return heif_error_ok;
163 }
164
UpdateData(heif_item_id itemID,const std::vector<uint8_t> & data,uint8_t constructionMethod)165 heif_error HeifIlocBox::UpdateData(heif_item_id itemID, const std::vector<uint8_t> &data, uint8_t constructionMethod)
166 {
167 if (0 != constructionMethod) {
168 return heif_invalid_exif_data;
169 }
170
171 // check whether this item ID already exists
172 size_t idx;
173 for (idx = 0; idx < items_.size(); idx++) {
174 if (items_[idx].itemId == itemID) {
175 break;
176 }
177 }
178
179 // No item existing return
180 if (idx == items_.size()) {
181 return heif_error_item_not_found;
182 }
183
184 Extent extent;
185 extent.data = data;
186 extent.length = data.size();
187
188 // clean any old extends
189 items_[idx].extents.clear();
190
191 // push the new extend in
192 items_[idx].extents.push_back(std::move(extent));
193 return heif_error_ok;
194 }
195
InferFullBoxVersion()196 void HeifIlocBox::InferFullBoxVersion()
197 {
198 int minVersion = items_.size() < 0xFFFF ? HEIF_BOX_VERSION_ONE : HEIF_BOX_VERSION_TWO;
199 offsetSize_ = 0;
200 lengthSize_ = 0;
201 baseOffsetSize_ = 0;
202 indexSize_ = 0;
203
204 for (const auto &item: items_) {
205 minVersion = (item.itemId < 0xFFFF) ? minVersion : std::max(minVersion, (int)HEIF_BOX_VERSION_TWO);
206 minVersion = (item.constructionMethod == CONSTRUCTION_METHOD_FILE_OFFSET) ?
207 minVersion : std::max(minVersion, (int)HEIF_BOX_VERSION_ONE);
208 }
209
210 offsetSize_ = UINT32_BYTES_NUM;
211 lengthSize_ = UINT32_BYTES_NUM;
212 baseOffsetSize_ = 0;
213 indexSize_ = 0;
214
215 SetVersion((uint8_t)minVersion);
216 }
217
Write(HeifStreamWriter & writer) const218 heif_error HeifIlocBox::Write(HeifStreamWriter &writer) const
219 {
220 size_t idatTotalSize = 0;
221 for (const auto &item: items_) {
222 idatTotalSize += (item.constructionMethod == CONSTRUCTION_METHOD_IDAT_OFFSET) ? item.GetExtentsTotalSize() : 0;
223 }
224 if (idatTotalSize > 0) {
225 // need add header bytes size
226 writer.Write32((uint32_t) (idatTotalSize + UINT64_BYTES_NUM));
227 writer.Write32(BOX_TYPE_IDAT);
228
229 for (auto &item: items_) {
230 if (item.constructionMethod != CONSTRUCTION_METHOD_IDAT_OFFSET) {
231 continue;
232 }
233 for (auto &extent: item.extents) {
234 writer.Write(extent.data);
235 }
236 }
237 }
238
239 size_t boxStart = ReserveHeader(writer);
240 startPos_ = writer.GetPos();
241 int nSkip = 0;
242 nSkip += UINT16_BYTES_NUM;
243 nSkip += (GetVersion() < HEIF_BOX_VERSION_TWO) ? UINT16_BYTES_NUM : UINT32_BYTES_NUM;
244 for (const auto &item: items_) {
245 nSkip += (GetVersion() < HEIF_BOX_VERSION_TWO) ? UINT16_BYTES_NUM : UINT32_BYTES_NUM;
246 nSkip += (GetVersion() >= HEIF_BOX_VERSION_ONE) ? UINT16_BYTES_NUM : 0;
247 nSkip += UINT32_BYTES_NUM + baseOffsetSize_;
248 for (const auto &extent: item.extents) {
249 (void) extent;
250 if (GetVersion() >= HEIF_BOX_VERSION_ONE) {
251 nSkip += indexSize_;
252 }
253 nSkip += offsetSize_ + lengthSize_;
254 }
255 }
256 writer.Skip(nSkip);
257 WriteCalculatedHeader(writer, boxStart);
258 return heif_error_ok;
259 }
260
WriteMdatBox(HeifStreamWriter & writer)261 heif_error HeifIlocBox::WriteMdatBox(HeifStreamWriter &writer)
262 {
263 // all mdat data size
264 size_t mdatTotalSize = 0;
265
266 for (const auto &item: items_) {
267 mdatTotalSize += (item.constructionMethod != CONSTRUCTION_METHOD_FILE_OFFSET) ?
268 0 : item.GetExtentsTotalSize();
269 }
270
271 // need add header bytes
272 writer.Write32((uint32_t) (mdatTotalSize + UINT64_BYTES_NUM));
273 writer.Write32(BOX_TYPE_MDAT);
274
275 for (auto &item: items_) {
276 if (item.constructionMethod != CONSTRUCTION_METHOD_FILE_OFFSET) {
277 continue;
278 }
279 for (auto &extent: item.extents) {
280 extent.offset = writer.GetPos();
281 extent.length = extent.data.size();
282
283 writer.Write(extent.data);
284 }
285 }
286
287 PackIlocHeader(writer);
288 return heif_error_ok;
289 }
290
ReadToExtentData(Item & item,const std::shared_ptr<HeifInputStream> & stream,const std::shared_ptr<HeifIdatBox> & idatBox)291 heif_error HeifIlocBox::ReadToExtentData(Item &item, const std::shared_ptr<HeifInputStream> &stream,
292 const std::shared_ptr<HeifIdatBox> &idatBox)
293 {
294 for (auto &extent: item.extents) {
295 if (!extent.data.empty()) {
296 continue;
297 }
298 if (item.constructionMethod == CONSTRUCTION_METHOD_FILE_OFFSET) {
299 bool ret = stream->Seek(extent.offset + item.baseOffset);
300 if (!ret) {
301 return heif_error_eof;
302 }
303 ret = stream->CheckSize(extent.length, -1);
304 if (!ret) {
305 return heif_error_eof;
306 }
307 extent.data.resize(extent.length);
308 ret = stream->Read(extent.data.data(), static_cast<size_t>(extent.length));
309 if (!ret) {
310 return heif_error_eof;
311 }
312 } else if (item.constructionMethod == CONSTRUCTION_METHOD_IDAT_OFFSET) {
313 if (!idatBox) {
314 return heif_error_no_idat;
315 }
316 idatBox->ReadData(stream, extent.offset + item.baseOffset, extent.length, extent.data);
317 }
318 }
319
320 return heif_error_ok;
321 }
322
PackIlocHeader(HeifStreamWriter & writer) const323 void HeifIlocBox::PackIlocHeader(HeifStreamWriter &writer) const
324 {
325 size_t oldPos = writer.GetPos();
326 writer.SetPos(startPos_);
327 const uint8_t offsetSizeShift = 4;
328 writer.Write8((uint8_t) ((offsetSize_ << offsetSizeShift) | (lengthSize_)));
329 writer.Write8((uint8_t) ((baseOffsetSize_ << offsetSizeShift) | (indexSize_)));
330
331 if (GetVersion() < HEIF_BOX_VERSION_TWO) {
332 writer.Write16((uint16_t) items_.size());
333 } else {
334 writer.Write32((uint32_t) items_.size());
335 }
336
337 for (const auto &item: items_) {
338 if (GetVersion() < HEIF_BOX_VERSION_TWO) {
339 writer.Write16((uint16_t) item.itemId);
340 } else {
341 writer.Write32((uint32_t) item.itemId);
342 }
343
344 if (GetVersion() >= HEIF_BOX_VERSION_ONE) {
345 writer.Write16(item.constructionMethod);
346 }
347
348 writer.Write16(item.dataReferenceIndex);
349 writer.Write(baseOffsetSize_, item.baseOffset);
350 writer.Write16((uint16_t) item.extents.size());
351
352 for (const auto &extent: item.extents) {
353 if (GetVersion() >= HEIF_BOX_VERSION_ONE && indexSize_ > 0) {
354 writer.Write(indexSize_, extent.index);
355 }
356
357 writer.Write(offsetSize_, extent.offset);
358 writer.Write(lengthSize_, extent.length);
359 }
360 }
361
362 writer.SetPos(oldPos);
363 }
364
ParseContent(HeifStreamReader & reader)365 heif_error HeifIdatBox::ParseContent(HeifStreamReader &reader)
366 {
367 startPos_ = reader.GetStream()->Tell();
368
369 return reader.GetError();
370 }
371
Write(HeifStreamWriter & writer) const372 heif_error HeifIdatBox::Write(HeifStreamWriter &writer) const
373 {
374 if (dataForWriting_.empty()) {
375 return heif_error_ok;
376 }
377
378 size_t boxStart = ReserveHeader(writer);
379
380 writer.Write(dataForWriting_);
381
382 WriteCalculatedHeader(writer, boxStart);
383
384 return heif_error_ok;
385 }
386
ReadData(const std::shared_ptr<HeifInputStream> & stream,uint64_t start,uint64_t length,std::vector<uint8_t> & outData) const387 heif_error HeifIdatBox::ReadData(const std::shared_ptr<HeifInputStream> &stream,
388 uint64_t start, uint64_t length, std::vector<uint8_t> &outData) const
389 {
390 auto currSize = outData.size();
391 if (start > (uint64_t) startPos_ + GetBoxSize()) {
392 return heif_error_eof;
393 } else if (length > GetBoxSize() || start + length > GetBoxSize()) {
394 return heif_error_eof;
395 }
396
397 stream->Seek(startPos_ + (std::streampos) start);
398
399 if (length > 0) {
400 outData.resize(static_cast<size_t>(currSize + length));
401 uint8_t *data = &outData[currSize];
402
403 stream->Read(reinterpret_cast<char*>(data), static_cast<size_t>(length));
404 }
405
406 return heif_error_ok;
407 }
408 } // namespace ImagePlugin
409 } // namespace OHOS
410