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

Side by Side Diff: src/codec/SkCodec_libico.cpp

Issue 1011343003: Enabling ico decoding with use of png and bmp decoders (Closed) Base URL: https://skia.googlesource.com/skia.git@swizzle
Patch Set: Provide the user with an option of which ico to decode Created 5 years, 9 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
« dm/DMSrcSink.cpp ('K') | « src/codec/SkCodec_libico.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 /*
2 * Copyright 2015 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "SkCodec_libbmp.h"
9 #include "SkCodec_libico.h"
10 #include "SkCodec_libpng.h"
11 #include "SkCodecPriv.h"
12 #include "SkColorPriv.h"
13 #include "SkData.h"
14 #include "SkStream.h"
15 #include "SkTSort.h"
16
17 /*
18 * Checks the start of the stream to see if the image is an Ico or Cur
19 */
20 bool SkIcoCodec::IsIco(SkStream* stream) {
21 const char icoSig[] = { '\x00', '\x00', '\x01', '\x00' };
22 const char curSig[] = { '\x00', '\x00', '\x02', '\x00' };
23 char buffer[sizeof(icoSig)];
24 return stream->read(buffer, sizeof(icoSig)) == sizeof(icoSig) &&
25 (!memcmp(buffer, icoSig, sizeof(icoSig)) ||
26 !memcmp(buffer, curSig, sizeof(curSig)));
27 }
28
29 /*
30 * Assumes IsIco was called and returned true
31 * Creates an Ico decoder
32 * Reads enough of the stream to determine the image format
33 */
34 SkCodec* SkIcoCodec::NewFromStream(SkStream* stream) {
35 // Header size constants
36 static const uint32_t kIcoDirectoryBytes = 6;
37 static const uint32_t kIcoDirEntryBytes = 16;
38
39 // Read the directory header
40 SkAutoTDeleteArray<uint8_t> dirBuffer(
41 SkNEW_ARRAY(uint8_t, kIcoDirectoryBytes));
42 if (stream->read(dirBuffer.get(), kIcoDirectoryBytes) !=
43 kIcoDirectoryBytes) {
44 SkDebugf("Error: unable to read ico directory header.\n");
45 return NULL;
46 }
47
48 // Process the directory header
49 const uint16_t numImages = get_short(dirBuffer.get(), 4);
50 if (0 == numImages) {
51 SkDebugf("Error: No images embedded in ico.\n");
52 return NULL;
53 }
54
55 // Ensure that we can read all of indicated directory entries
56 SkAutoTDeleteArray<uint8_t> entryBuffer(
57 SkNEW_ARRAY(uint8_t, numImages*kIcoDirEntryBytes));
58 if (stream->read(entryBuffer.get(), numImages*kIcoDirEntryBytes) !=
59 numImages*kIcoDirEntryBytes) {
60 SkDebugf("Error: unable to read ico directory entries.\n");
61 return NULL;
62 }
63
64 // This structure is used to represent the vital information about entries
65 // in the directory header. We will obtain this information for each
66 // directory entry.
67 struct Entry {
68 uint32_t offset;
69 uint32_t size;
70 };
71 SkAutoTDeleteArray<Entry> directoryEntries(SkNEW_ARRAY(Entry, numImages));
72
73 // Iterate over directory entries
74 for (uint32_t i = 0; i < numImages; i++) {
75 // The directory entry contains information such as width, height,
76 // bits per pixel, and number of colors in the color palette. We will
77 // ignore these fields since they are repeated in the header of the
78 // embedded image. In the event of an inconsistency, we would always
79 // defer to the value in the embedded header anyway.
80
81 // Specifies the size of the embedded image, including the header
82 uint32_t size = get_int(entryBuffer.get(), 8 + i*kIcoDirEntryBytes);
83
84 // Specifies the offset of the embedded image from the start of file.
85 // It does not indicate the start of the pixel data, but rather the
86 // start of the embedded image header.
87 uint32_t offset = get_int(entryBuffer.get(), 12 + i*kIcoDirEntryBytes);
88
89 // Save the vital fields
90 directoryEntries.get()[i].offset = offset;
91 directoryEntries.get()[i].size = size;
92 }
93
94 // It is "customary" that the embedded images will be stored in order of
95 // increasing offset. However, the specification does not indicate that
96 // they must be stored in this order, so we will not trust that this is the
97 // case. Here we sort the embedded images by increasing offset.
98 struct EntryLessThan {
99 bool operator() (Entry a, Entry b) const {
100 return a.offset < b.offset;
101 }
102 };
103 EntryLessThan lessThan;
104 SkTQSort(directoryEntries.get(), directoryEntries.get() + numImages - 1,
105 lessThan);
106
107 // Now will construct a candidate codec for each of the embedded images
108 uint32_t bytesRead = kIcoDirectoryBytes + numImages*kIcoDirEntryBytes;
109 SkAutoTDeleteArray<SkCodec*> codecs(SkNEW_ARRAY(SkCodec*, numImages));
110 uint32_t index = 0;
111 for (uint32_t i = 0; i < numImages; i++) {
112 uint32_t offset = directoryEntries.get()[i].offset;
113 uint32_t size = directoryEntries.get()[i].size;
114
115 // Ensure that the offset is valid
116 if (offset < bytesRead) {
117 SkDebugf("Warning: invalid ico offset.\n");
118 continue;
119 }
120
121 // If we cannot skip, assume we have reached the end of the stream and
122 // stop trying to make codecs
123 if (stream->skip(offset - bytesRead) != offset - bytesRead) {
124 SkDebugf("Warning: could not skip to ico offset.\n");
125 break;
126 }
127 bytesRead = offset;
128
129 // Create a new stream for the embedded codec
130
msarett 2015/03/20 18:35:57 Will remove this line.
131 SkData* data = SkData::NewFromStream(stream, size);
scroggo 2015/03/20 19:36:01 SkMemoryStream's constructor will call data->ref()
msarett 2015/03/23 12:20:57 Done.
132 SkAutoTDelete<SkMemoryStream>
133 embeddedStream(SkNEW_ARGS(SkMemoryStream, (data)));
134 if (NULL == embeddedStream.get()) {
scroggo 2015/03/20 19:36:00 You called the constructor for SkMemoryStream, so
msarett 2015/03/23 12:20:57 Done.
135 SkDebugf("Warning: could not create embedded stream.\n");
136 break;
scroggo 2015/03/20 19:36:00 The only way for NewFromStream to fail is if the s
msarett 2015/03/23 12:20:58 Agreed. That is why we break out of the loop here
scroggo 2015/03/23 13:41:24 Oh, of course. nvm
137 }
138 bytesRead += size;
139
140 // Use the new stream to create the embedded decoder
141 // FIXME: Implement peek() for SkMemoryStream and use it to determine
scroggo 2015/03/20 19:36:00 Actually, in this case, since you've created an Sk
msarett 2015/03/23 12:20:57 Gotcha, will do. We won't call isBmp because the
142 // if we have a png or bmp.
143 const char pngSig[] = { '\x89', 'P', 'N', 'G' };
144 char* buffer[sizeof(pngSig)];
145 if (embeddedStream->read(buffer, sizeof(pngSig))
146 != sizeof(pngSig)) {
147 break;
148 }
149 if (!embeddedStream->move(-sizeof(pngSig))) {
150 SkDebugf("Warning: could not rewind embedded ico stream.\n");
151 continue;
152 }
153
154 // Check if the embedded codec is bmp or png and create the codec
155 SkCodec* codec;
156 if (memcmp(buffer, pngSig, sizeof(pngSig))) {
157 codec = SkBmpCodec::NewFromIco(embeddedStream.detach());
158 } else {
159 codec = SkPngCodec::NewFromStream(embeddedStream.detach());
160 }
161
162 // Save a valid codec
163 if (NULL != codec) {
164 codecs.get()[index++] = codec;
scroggo 2015/03/20 19:36:01 I think you might be better served here by using a
msarett 2015/03/23 12:20:57 Yeah of course. I was trying to use SkTArray but
165 }
166 }
167 uint32_t numValidImages = index;
168
169 // Use the largest codec as a "suggestion" for dimensions
170 int maxWidth = 0;
171 int maxHeight = 0;
172 SkColorType maxColorType = kUnknown_SkColorType;
173 SkAlphaType maxAlphaType = kUnknown_SkAlphaType;
174 for (uint32_t i = 0; i < numValidImages; i++) {
175 int width = codecs.get()[i]->getOriginalInfo().width();
scroggo 2015/03/20 19:36:01 If you're up to date, you can call getInfo() inste
msarett 2015/03/23 12:20:57 Done.
176 int height = codecs.get()[i]->getOriginalInfo().height();
177 if (width * height > maxWidth * maxHeight) {
178 maxWidth = width;
179 maxHeight = height;
180 maxColorType = codecs.get()[i]->getOriginalInfo().colorType();
scroggo 2015/03/20 19:36:00 I think it might be worth it to set up a local var
msarett 2015/03/23 12:20:57 Yes this is better.
181 maxAlphaType = codecs.get()[i]->getOriginalInfo().alphaType();
182 }
183 }
184
185 // Recognize if there are no valid codecs
186 if (0 == maxWidth) {
scroggo 2015/03/20 19:36:00 I would argue that if a codec reported its width o
msarett 2015/03/23 12:20:57 Agreed. What I am actually checking is if there a
187 SkDebugf("Error: could not find any valid embedded ico codecs.\n");
188 return NULL;
189 }
190
191 const SkImageInfo info = SkImageInfo::Make(maxWidth, maxHeight,
192 maxColorType, maxAlphaType);
193
194 // Note that stream is owned by the embedded codec, the ico does not need
195 // direct access to the stream.
196 return SkNEW_ARGS(SkIcoCodec, (info, codecs.detach(), numValidImages));
197 }
198
199 /*
200 * Creates an instance of the decoder
201 * Called only by NewFromStream
202 */
203 SkIcoCodec::SkIcoCodec(const SkImageInfo& info, SkCodec** embeddedCodecs,
204 uint32_t numImages)
205 : INHERITED(info, NULL)
206 , fEmbeddedCodecs(embeddedCodecs)
207 , fNumImages(numImages)
208 {}
209
210 /*
211 * Initiates the Ico decode
212 */
213 SkCodec::Result SkIcoCodec::onGetPixels(const SkImageInfo& dstInfo,
214 void* dst, size_t dstRowBytes,
215 const Options& opts, SkPMColor* ct,
216 int* ptr) {
217 // FIXME: How do we handle rewindIfNeeded?
218 // TODO: Should we also try to match color and alpha type?
scroggo 2015/03/20 19:36:01 Good question. If we have multiple images with the
msarett 2015/03/23 12:20:57 Done.
219 // Note that we do not handle conversion possible because the embedded
220 // codec will make these checks.
221 for (uint32_t i = 0; i < fNumImages; i++) {
222 if (dstInfo.dimensions() ==
223 fEmbeddedCodecs.get()[i]->getOriginalInfo().dimensions()) {
scroggo 2015/03/20 19:36:00 Is it possible that two embedded images have the s
msarett 2015/03/23 12:20:57 Yes and it's not that rare. The new version will
224 return fEmbeddedCodecs.get()[i]->getPixels(dstInfo, dst,
225 dstRowBytes, &opts, ct, ptr);
226 }
227 }
228
229 SkDebugf("Error: No ico candidate image with matching dimensions.\n");
scroggo 2015/03/20 19:36:01 Please implement onGetScaledDimensions, so a calle
msarett 2015/03/23 12:20:57 Done.
230 return kInvalidScale;
231 }
OLDNEW
« dm/DMSrcSink.cpp ('K') | « src/codec/SkCodec_libico.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698