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