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

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: onGetScaled dimensions, improved choice of proper 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
« src/codec/SkCodec_libbmp.h ('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 "SkTDArray.h"
16 #include "SkTSort.h"
17
18 /*
19 * Checks the start of the stream to see if the image is an Ico or Cur
20 */
21 bool SkIcoCodec::IsIco(SkStream* stream) {
22 const char icoSig[] = { '\x00', '\x00', '\x01', '\x00' };
23 const char curSig[] = { '\x00', '\x00', '\x02', '\x00' };
24 char buffer[sizeof(icoSig)];
25 return stream->read(buffer, sizeof(icoSig)) == sizeof(icoSig) &&
26 (!memcmp(buffer, icoSig, sizeof(icoSig)) ||
27 !memcmp(buffer, curSig, sizeof(curSig)));
28 }
29
30 /*
31 * Assumes IsIco was called and returned true
32 * Creates an Ico decoder
33 * Reads enough of the stream to determine the image format
34 */
35 SkCodec* SkIcoCodec::NewFromStream(SkStream* stream) {
36 // Header size constants
37 static const uint32_t kIcoDirectoryBytes = 6;
38 static const uint32_t kIcoDirEntryBytes = 16;
39
40 // Read the directory header
41 SkAutoTDeleteArray<uint8_t> dirBuffer(
42 SkNEW_ARRAY(uint8_t, kIcoDirectoryBytes));
43 if (stream->read(dirBuffer.get(), kIcoDirectoryBytes) !=
44 kIcoDirectoryBytes) {
45 SkDebugf("Error: unable to read ico directory header.\n");
46 return NULL;
47 }
48
49 // Process the directory header
50 const uint16_t numImages = get_short(dirBuffer.get(), 4);
51 if (0 == numImages) {
52 SkDebugf("Error: No images embedded in ico.\n");
53 return NULL;
54 }
55
56 // Ensure that we can read all of indicated directory entries
57 SkAutoTDeleteArray<uint8_t> entryBuffer(
58 SkNEW_ARRAY(uint8_t, numImages*kIcoDirEntryBytes));
59 if (stream->read(entryBuffer.get(), numImages*kIcoDirEntryBytes) !=
60 numImages*kIcoDirEntryBytes) {
61 SkDebugf("Error: unable to read ico directory entries.\n");
62 return NULL;
63 }
64
65 // This structure is used to represent the vital information about entries
66 // in the directory header. We will obtain this information for each
67 // directory entry.
68 struct Entry {
69 uint32_t offset;
70 uint32_t size;
71 };
72 SkAutoTDeleteArray<Entry> directoryEntries(SkNEW_ARRAY(Entry, numImages));
73
74 // Iterate over directory entries
75 for (uint32_t i = 0; i < numImages; i++) {
76 // The directory entry contains information such as width, height,
77 // bits per pixel, and number of colors in the color palette. We will
78 // ignore these fields since they are repeated in the header of the
79 // embedded image. In the event of an inconsistency, we would always
80 // defer to the value in the embedded header anyway.
81
82 // Specifies the size of the embedded image, including the header
83 uint32_t size = get_int(entryBuffer.get(), 8 + i*kIcoDirEntryBytes);
84
85 // Specifies the offset of the embedded image from the start of file.
86 // It does not indicate the start of the pixel data, but rather the
87 // start of the embedded image header.
88 uint32_t offset = get_int(entryBuffer.get(), 12 + i*kIcoDirEntryBytes);
89
90 // Save the vital fields
91 directoryEntries.get()[i].offset = offset;
92 directoryEntries.get()[i].size = size;
93 }
94
95 // It is "customary" that the embedded images will be stored in order of
96 // increasing offset. However, the specification does not indicate that
97 // they must be stored in this order, so we will not trust that this is the
98 // case. Here we sort the embedded images by increasing offset.
99 struct EntryLessThan {
100 bool operator() (Entry a, Entry b) const {
101 return a.offset < b.offset;
102 }
103 };
104 EntryLessThan lessThan;
105 SkTQSort(directoryEntries.get(), directoryEntries.get() + numImages - 1,
106 lessThan);
107
108 // Now will construct a candidate codec for each of the embedded images
109 uint32_t bytesRead = kIcoDirectoryBytes + numImages*kIcoDirEntryBytes;
110 SkTDArray<SkCodec*>* codecs = SkNEW(SkTDArray<SkCodec*>);
scroggo 2015/03/23 13:41:24 This can leak. SkTDArray has a copy constructor.
msarett 2015/03/23 19:40:02 Went with the SkAutoTDelete<SkTArray<SkAutoTDelete
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 SkAutoTUnref<SkData> data(SkData::NewFromStream(stream, size));
131 if (NULL == data.get()) {
132 SkDebugf("Warning: could not create embedded stream.\n");
133 break;
134 }
135 SkAutoTDelete<SkMemoryStream>
136 embeddedStream(SkNEW_ARGS(SkMemoryStream, (data.get())));
137 bytesRead += size;
138
139 // Check if the embedded codec is bmp or png and create the codec
140 SkAutoTDelete<SkCodec*> codec(SkNEW(SkCodec*));
scroggo 2015/03/23 13:41:25 I think this is rarely something you want to do.
msarett 2015/03/23 19:40:02 Acknowledged.
141 const bool isPng = SkPngCodec::IsPng(embeddedStream);
142 if (!embeddedStream->rewind()) {
scroggo 2015/03/23 13:41:24 SkMemoryStream::rewind will always return true. (M
msarett 2015/03/23 19:40:02 Done.
143 return NULL;
144 }
145 if (isPng) {
146 *codec = SkPngCodec::NewFromStream(embeddedStream.detach());
147 } else {
148 *codec = SkBmpCodec::NewFromIco(embeddedStream.detach());
149 }
150
151 // Save a valid codec
152 if (NULL != *codec) {
153 codecs->append(1, codec);
154 }
155 }
156
157 // Recognize if there are no valid codecs
158 uint32_t numValidImages = codecs->count();
scroggo 2015/03/23 13:41:24 I think this can be const.
msarett 2015/03/23 19:40:02 You are correct. We are no longer using this vari
159 if (0 == numValidImages) {
160 SkDebugf("Error: could not find any valid embedded ico codecs.\n");
161 return NULL;
162 }
163
164 // Use the largest codec as a "suggestion" for image info
165 uint32_t maxSize = 0;
166 uint32_t maxIndex = 0;
167 SkImageInfo info = SkImageInfo::MakeUnknown();
168 for (uint32_t i = 0; i < numValidImages; i++) {
169 codecs->operator[](i)->getInfo(&info);
scroggo 2015/03/23 13:41:25 I think there's a version of getInfo which just re
msarett 2015/03/23 19:40:02 Done.
170 uint32_t size = info.width() * info.height();
171 if (size > maxSize) {
172 maxSize = size;
173 maxIndex = i;
174 }
175 }
176 codecs->operator[](maxIndex)->getInfo(&info);
177
178 // Note that stream is owned by the embedded codec, the ico does not need
179 // direct access to the stream.
180 return SkNEW_ARGS(SkIcoCodec, (info, codecs->detach(), numValidImages));
scroggo 2015/03/23 13:41:25 If you stored codecs as an SkTDArray or SkTArray i
msarett 2015/03/23 19:40:02 Acknowledged.
181 }
182
183 /*
184 * Creates an instance of the decoder
185 * Called only by NewFromStream
186 */
187 SkIcoCodec::SkIcoCodec(const SkImageInfo& info, SkCodec** embeddedCodecs,
188 uint32_t numImages)
189 : INHERITED(info, NULL)
190 , fEmbeddedCodecs(embeddedCodecs)
191 , fNumImages(numImages)
192 {}
193
194 /*
195 * Chooses the best dimensions given the desired scale
196 */
197 SkISize SkIcoCodec::onGetScaledDimensions(float desiredScale) const {
198 // We set the dimensions to the largest candidate image by default.
199 // Regardless of the scale request, this is the largest image that we
200 // will decode.
201 if (desiredScale >= 1.0) {
202 return this->getOriginalInfo().dimensions();
scroggo 2015/03/23 13:41:25 getInfo()
msarett 2015/03/23 19:40:02 Done.
203 }
204
205 int origWidth = this->getOriginalInfo().width();
206 int origHeight = this->getOriginalInfo().height();
207 float desiredSize = desiredScale * origWidth * origHeight;
208 // At least one image will have smaller error than this initial value
209 float minError = origWidth * origHeight - desiredSize + 1.0;
210 int32_t minIndex = -1;
211 for (uint32_t i = 0; i < fNumImages; i++) {
212 int width = fEmbeddedCodecs.get()[i]->getOriginalInfo().width();;
scroggo 2015/03/23 13:41:25 no need for double ;
msarett 2015/03/23 19:40:02 Done.
213 int height = fEmbeddedCodecs.get()[i]->getOriginalInfo().width();;
scroggo 2015/03/23 13:41:25 height()*
msarett 2015/03/23 19:40:02 Done.
214 float error = SkTAbs(((float) (width * height)) - desiredSize);
215 if (error < minError) {
216 minError = error;
217 minIndex = (int32_t) i;
218 }
219 }
220 SkASSERT(minIndex >= 0);
221
222 return fEmbeddedCodecs.get()[minIndex]->getOriginalInfo().dimensions();
223 }
224
225 /*
226 * Initiates the Ico decode
227 */
228 SkCodec::Result SkIcoCodec::onGetPixels(const SkImageInfo& dstInfo,
229 void* dst, size_t dstRowBytes,
230 const Options& opts, SkPMColor* ct,
231 int* ptr) {
232 for (uint32_t i = 0; i < fNumImages; i++) {
233 // If the dimensions match, try to decode
234 if (dstInfo.dimensions() ==
235 fEmbeddedCodecs.get()[i]->getOriginalInfo().dimensions()) {
236
237 // Perform the decode
238 Result result = fEmbeddedCodecs.get()[i]->getPixels(dstInfo, dst,
239 dstRowBytes, &opts, ct, ptr);
240
241 // On a fatal error, keep trying to find an image to decode
242 if (kInvalidConversion == result || kInvalidInput == result) {
243 SkDebugf("Warning: Attempt to decode candidate ico failed.\n");
244 continue;
245 }
246
247 // On success or partial success, return the result
248 return result;
249 }
250 }
251
252 SkDebugf("Error: No ico candidate image with matching dimensions.\n");
253 return kInvalidScale;
254 }
OLDNEW
« src/codec/SkCodec_libbmp.h ('K') | « src/codec/SkCodec_libico.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698