Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(99)

Side by Side Diff: third_party/WebKit/Source/platform/image-decoders/ico/ICOImageDecoder.cpp

Issue 2930513004: [WIP] Move ImageDecoders to SkCodec
Patch Set: Adding check for decoder creation Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 /*
2 * Copyright (c) 2008, 2009, Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31 #include "platform/image-decoders/ico/ICOImageDecoder.h"
32
33 #include <algorithm>
34 #include "platform/image-decoders/png/PNGImageDecoder.h"
35 #include "platform/wtf/PtrUtil.h"
36
37 namespace blink {
38
39 // Number of bits in .ICO/.CUR used to store the directory and its entries,
40 // respectively (doesn't match sizeof values for member structs since we omit
41 // some fields).
42 static const size_t kSizeOfDirectory = 6;
43 static const size_t kSizeOfDirEntry = 16;
44
45 ICOImageDecoder::ICOImageDecoder(AlphaOption alpha_option,
46 const ColorBehavior& color_behavior,
47 size_t max_decoded_bytes)
48 : ImageDecoder(alpha_option, color_behavior, max_decoded_bytes),
49 fast_reader_(nullptr),
50 decoded_offset_(0),
51 dir_entries_count_(0),
52 color_behavior_(color_behavior) {}
53
54 ICOImageDecoder::~ICOImageDecoder() {}
55
56 void ICOImageDecoder::OnSetData(SegmentReader* data) {
57 fast_reader_.SetData(data);
58
59 for (BMPReaders::iterator i(bmp_readers_.begin()); i != bmp_readers_.end();
60 ++i) {
61 if (*i)
62 (*i)->SetData(data);
63 }
64 for (size_t i = 0; i < png_decoders_.size(); ++i)
65 SetDataForPNGDecoderAtIndex(i);
66 }
67
68 IntSize ICOImageDecoder::Size() const {
69 return frame_size_.IsEmpty() ? ImageDecoder::Size() : frame_size_;
70 }
71
72 IntSize ICOImageDecoder::FrameSizeAtIndex(size_t index) const {
73 return (index && (index < dir_entries_.size())) ? dir_entries_[index].size_
74 : Size();
75 }
76
77 bool ICOImageDecoder::SetSize(unsigned width, unsigned height) {
78 // The size calculated inside the BMPImageReader had better match the one in
79 // the icon directory.
80 return frame_size_.IsEmpty()
81 ? ImageDecoder::SetSize(width, height)
82 : ((IntSize(width, height) == frame_size_) || SetFailed());
83 }
84
85 bool ICOImageDecoder::FrameIsCompleteAtIndex(size_t index) const {
86 if (index >= dir_entries_.size())
87 return false;
88 const IconDirectoryEntry& dir_entry = dir_entries_[index];
89 return (dir_entry.image_offset_ + dir_entry.byte_size_) <= data_->size();
90 }
91
92 bool ICOImageDecoder::SetFailed() {
93 bmp_readers_.clear();
94 png_decoders_.clear();
95 return ImageDecoder::SetFailed();
96 }
97
98 bool ICOImageDecoder::HotSpot(IntPoint& hot_spot) const {
99 // When unspecified, the default frame is always frame 0. This is consistent
100 // with BitmapImage, where CurrentFrame() starts at 0 and only increases when
101 // animation is requested.
102 return HotSpotAtIndex(0, hot_spot);
103 }
104
105 bool ICOImageDecoder::HotSpotAtIndex(size_t index, IntPoint& hot_spot) const {
106 if (index >= dir_entries_.size() || file_type_ != CURSOR)
107 return false;
108
109 hot_spot = dir_entries_[index].hot_spot_;
110 return true;
111 }
112
113 // static
114 bool ICOImageDecoder::CompareEntries(const IconDirectoryEntry& a,
115 const IconDirectoryEntry& b) {
116 // Larger icons are better. After that, higher bit-depth icons are better.
117 const int a_entry_area = a.size_.Width() * a.size_.Height();
118 const int b_entry_area = b.size_.Width() * b.size_.Height();
119 return (a_entry_area == b_entry_area) ? (a.bit_count_ > b.bit_count_)
120 : (a_entry_area > b_entry_area);
121 }
122
123 size_t ICOImageDecoder::DecodeFrameCount() {
124 DecodeSize();
125
126 // If DecodeSize() fails, return the existing number of frames. This way
127 // if we get halfway through the image before decoding fails, we won't
128 // suddenly start reporting that the image has zero frames.
129 if (Failed())
130 return frame_buffer_cache_.size();
131
132 // If the file is incomplete, return the length of the sequence of completely
133 // received frames. We don't do this when the file is fully received, since
134 // some ICOs have entries whose claimed offset + size extends past the end of
135 // the file, and we still want to display these if they don't trigger decoding
136 // failures elsewhere.
137 if (!IsAllDataReceived()) {
138 for (size_t i = 0; i < dir_entries_.size(); ++i) {
139 const IconDirectoryEntry& dir_entry = dir_entries_[i];
140 if ((dir_entry.image_offset_ + dir_entry.byte_size_) > data_->size())
141 return i;
142 }
143 }
144 return dir_entries_.size();
145 }
146
147 void ICOImageDecoder::SetDataForPNGDecoderAtIndex(size_t index) {
148 if (!png_decoders_[index])
149 return;
150
151 png_decoders_[index]->SetData(data_.Get(), IsAllDataReceived());
152 }
153
154 void ICOImageDecoder::Decode(size_t index, bool only_size) {
155 if (Failed())
156 return;
157
158 // Defensively clear the FastSharedBufferReader's cache, as another caller
159 // may have called SharedBuffer::MergeSegmentsIntoBuffer().
160 fast_reader_.ClearCache();
161
162 // If we couldn't decode the image but we've received all the data, decoding
163 // has failed.
164 if ((!DecodeDirectory() || (!only_size && !DecodeAtIndex(index))) &&
165 IsAllDataReceived()) {
166 SetFailed();
167 // If we're done decoding this frame, we don't need the BMPImageReader or
168 // PNGImageDecoder anymore. (If we failed, these have already been
169 // cleared.)
170 } else if ((frame_buffer_cache_.size() > index) &&
171 (frame_buffer_cache_[index].GetStatus() ==
172 ImageFrame::kFrameComplete)) {
173 bmp_readers_[index].reset();
174 png_decoders_[index].reset();
175 }
176 }
177
178 bool ICOImageDecoder::DecodeDirectory() {
179 // Read and process directory.
180 if ((decoded_offset_ < kSizeOfDirectory) && !ProcessDirectory())
181 return false;
182
183 // Read and process directory entries.
184 return (decoded_offset_ >=
185 (kSizeOfDirectory + (dir_entries_count_ * kSizeOfDirEntry))) ||
186 ProcessDirectoryEntries();
187 }
188
189 bool ICOImageDecoder::DecodeAtIndex(size_t index) {
190 SECURITY_DCHECK(index < dir_entries_.size());
191 const IconDirectoryEntry& dir_entry = dir_entries_[index];
192 const ImageType image_type = ImageTypeAtIndex(index);
193 if (image_type == kUnknown)
194 return false; // Not enough data to determine image type yet.
195
196 if (image_type == BMP) {
197 if (!bmp_readers_[index]) {
198 bmp_readers_[index] = WTF::WrapUnique(
199 new BMPImageReader(this, dir_entry.image_offset_, 0, true));
200 bmp_readers_[index]->SetData(data_.Get());
201 }
202 // Update the pointer to the buffer as it could change after
203 // frame_buffer_cache_.resize().
204 bmp_readers_[index]->SetBuffer(&frame_buffer_cache_[index]);
205 frame_size_ = dir_entry.size_;
206 bool result = bmp_readers_[index]->DecodeBMP(false);
207 frame_size_ = IntSize();
208 return result;
209 }
210
211 if (!png_decoders_[index]) {
212 AlphaOption alpha_option =
213 premultiply_alpha_ ? kAlphaPremultiplied : kAlphaNotPremultiplied;
214 png_decoders_[index] = WTF::WrapUnique(
215 new PNGImageDecoder(alpha_option, color_behavior_, max_decoded_bytes_,
216 dir_entry.image_offset_));
217 SetDataForPNGDecoderAtIndex(index);
218 }
219 auto* png_decoder = png_decoders_[index].get();
220 if (png_decoder->IsSizeAvailable()) {
221 // Fail if the size the PNGImageDecoder calculated does not match the size
222 // in the directory.
223 if (png_decoder->Size() != dir_entry.size_)
224 return SetFailed();
225
226 const auto* frame = png_decoder->FrameBufferAtIndex(0);
227 if (frame)
228 frame_buffer_cache_[index] = *frame;
229 }
230 if (png_decoder->Failed())
231 return SetFailed();
232 return frame_buffer_cache_[index].GetStatus() == ImageFrame::kFrameComplete;
233 }
234
235 bool ICOImageDecoder::ProcessDirectory() {
236 // Read directory.
237 DCHECK(!decoded_offset_);
238 if (data_->size() < kSizeOfDirectory)
239 return false;
240 const uint16_t file_type = ReadUint16(2);
241 dir_entries_count_ = ReadUint16(4);
242 decoded_offset_ = kSizeOfDirectory;
243
244 // See if this is an icon filetype we understand, and make sure we have at
245 // least one entry in the directory.
246 if (((file_type != ICON) && (file_type != CURSOR)) || (!dir_entries_count_))
247 return SetFailed();
248
249 file_type_ = static_cast<FileType>(file_type);
250 return true;
251 }
252
253 bool ICOImageDecoder::ProcessDirectoryEntries() {
254 // Read directory entries.
255 DCHECK_EQ(decoded_offset_, kSizeOfDirectory);
256 if ((decoded_offset_ > data_->size()) ||
257 ((data_->size() - decoded_offset_) <
258 (dir_entries_count_ * kSizeOfDirEntry)))
259 return false;
260
261 // Enlarge member vectors to hold all the entries.
262 dir_entries_.resize(dir_entries_count_);
263 bmp_readers_.resize(dir_entries_count_);
264 png_decoders_.resize(dir_entries_count_);
265
266 for (IconDirectoryEntries::iterator i(dir_entries_.begin());
267 i != dir_entries_.end(); ++i)
268 *i = ReadDirectoryEntry(); // Updates decoded_offset_.
269
270 // Make sure the specified image offsets are past the end of the directory
271 // entries.
272 for (IconDirectoryEntries::iterator i(dir_entries_.begin());
273 i != dir_entries_.end(); ++i) {
274 if (i->image_offset_ < decoded_offset_)
275 return SetFailed();
276 }
277
278 // Arrange frames in decreasing quality order.
279 std::sort(dir_entries_.begin(), dir_entries_.end(), CompareEntries);
280
281 // The image size is the size of the largest entry.
282 const IconDirectoryEntry& dir_entry = dir_entries_.front();
283 // Technically, this next call shouldn't be able to fail, since the width
284 // and height here are each <= 256, and |frame_size_| is empty.
285 return SetSize(dir_entry.size_.Width(), dir_entry.size_.Height());
286 }
287
288 ICOImageDecoder::IconDirectoryEntry ICOImageDecoder::ReadDirectoryEntry() {
289 // Read icon data.
290 // The following calls to ReadUint8() return a uint8_t, which is appropriate
291 // because that's the on-disk type of the width and height values. Storing
292 // them in ints (instead of matching uint8_ts) is so we can record dimensions
293 // of size 256 (which is what a zero byte really means).
294 int width = ReadUint8(0);
295 if (!width)
296 width = 256;
297 int height = ReadUint8(1);
298 if (!height)
299 height = 256;
300 IconDirectoryEntry entry;
301 entry.size_ = IntSize(width, height);
302 if (file_type_ == CURSOR) {
303 entry.bit_count_ = 0;
304 entry.hot_spot_ = IntPoint(ReadUint16(4), ReadUint16(6));
305 } else {
306 entry.bit_count_ = ReadUint16(6);
307 entry.hot_spot_ = IntPoint();
308 }
309 entry.byte_size_ = ReadUint32(8);
310 entry.image_offset_ = ReadUint32(12);
311
312 // Some icons don't have a bit depth, only a color count. Convert the
313 // color count to the minimum necessary bit depth. It doesn't matter if
314 // this isn't quite what the bitmap info header says later, as we only use
315 // this value to determine which icon entry is best.
316 if (!entry.bit_count_) {
317 int color_count = ReadUint8(2);
318 if (!color_count)
319 color_count = 256; // Vague in the spec, needed by real-world icons.
320 for (--color_count; color_count; color_count >>= 1)
321 ++entry.bit_count_;
322 }
323
324 decoded_offset_ += kSizeOfDirEntry;
325 return entry;
326 }
327
328 ICOImageDecoder::ImageType ICOImageDecoder::ImageTypeAtIndex(size_t index) {
329 // Check if this entry is a BMP or a PNG; we need 4 bytes to check the magic
330 // number.
331 SECURITY_DCHECK(index < dir_entries_.size());
332 const uint32_t image_offset = dir_entries_[index].image_offset_;
333 if ((image_offset > data_->size()) || ((data_->size() - image_offset) < 4))
334 return kUnknown;
335 char buffer[4];
336 const char* data = fast_reader_.GetConsecutiveData(image_offset, 4, buffer);
337 return strncmp(data, "\x89PNG", 4) ? BMP : PNG;
338 }
339
340 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698