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

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: Clean up before public review 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
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
16 /*
17 *
18 * Checks the start of the stream to see if the image is an Ico or Cur
19 *
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 *
32 * Assumes IsIco was called and returned true
33 * Creates an Ico decoder
34 * Reads enough of the stream to determine the image format
35 *
36 */
37 SkCodec* SkIcoCodec::NewFromStream(SkStream* stream) {
38 // Header size constants
39 static const uint32_t kIcoDirectoryBytes = 6;
40 static const uint32_t kIcoDirEntryBytes = 16;
41
42 // Read the directory header
43 SkAutoTDeleteArray<uint8_t> dirBuffer(
44 SkNEW_ARRAY(uint8_t, kIcoDirectoryBytes));
45 if (stream->read(dirBuffer.get(), kIcoDirectoryBytes) !=
46 kIcoDirectoryBytes) {
47 SkDebugf("Error: unable to read ico directory header.\n");
48 return NULL;
49 }
50
51 // Process the directory header
52 const uint16_t numImages = get_short(dirBuffer.get(), 4);
53 if (0 == numImages) {
54 SkDebugf("Error: No images embedded in ico.\n");
55 return NULL;
56 }
57
58 // Ensure that we can read all of indicated directory entries
59 SkAutoTDeleteArray<uint8_t> entryBuffer(
60 SkNEW_ARRAY(uint8_t, numImages*kIcoDirEntryBytes));
61 if (stream->read(entryBuffer.get(), numImages*kIcoDirEntryBytes) !=
62 numImages*kIcoDirEntryBytes) {
63 SkDebugf("Error: unable to read ico directory entries.\n");
64 return NULL;
65 }
66
67 // Ico files may contain multiple embedded images (Bmp or Png). We will
68 // decode the "best" of these embedded images. The largest image in size
scroggo 2015/03/18 21:39:19 In getPixels, the client will specify a desired Sk
msarett 2015/03/20 18:35:56 Done.
69 // is considered the "best". If two images have equal size, the image with
70 // more bits per pixel is the "best". Note that bitsPerPixel is often left
scroggo 2015/03/18 21:39:18 If the client wanted 565, maybe an 8 bit image is
msarett 2015/03/20 18:35:56 Acknowledged.
71 // blank, and we try to infer it from the color palette. This inferred
72 // value is not what we use for the decode, it is just to guess the "best"
73 // image.
74 uint32_t bytesRead = kIcoDirectoryBytes + numImages*kIcoDirEntryBytes;
75 int bestWidth = 0;
76 int bestHeight = 0;
77 int bestSize = 0;
78 uint16_t bestBitsPerPixel = 0;
79 uint32_t offset = 0;
80 for (uint32_t i = 0; i < numImages; i++) {
81 // Read the width and height
82 // Width and height are both stored in a single byte, 0 is used as 256
83 int width = get_byte(entryBuffer.get(), 0 + i*kIcoDirEntryBytes);
84 if (width == 0) {
85 width = 256;
86 }
87 int height = get_byte(entryBuffer.get(), 1 + i*kIcoDirEntryBytes);
88 if (height == 0) {
89 height = 256;
90 }
91 int size = width * height;
92
93 // Read or infer the bit depth
94 uint32_t bitsPerPixel = get_short(entryBuffer.get(),
95 6 + i*kIcoDirEntryBytes);
96 if (0 == bitsPerPixel) {
97 // Number of colors in the color table
98 uint32_t numColors = get_byte(entryBuffer.get(),
99 2 + i*kIcoDirEntryBytes);
100 // This is not documented in the spec but used by many real images
101 if (0 == numColors) {
102 numColors = 256;
103 }
104 // Guess the number of bits per pixel
105 for (uint32_t i = numColors - 1; i != 0; i >>= 1) {
106 bitsPerPixel++;
107 }
108 }
109
110 // Determine if the new entry is "best"
111 bool best = (bestSize == size) ?
112 (bitsPerPixel > bestBitsPerPixel) : (size > bestSize);
113
114 if (best) {
115 bestWidth = width;
116 bestHeight = height;
117 bestSize = size;
118 bestBitsPerPixel = bitsPerPixel;
119
120 // Offset of the image data from the start of the stream
121 offset = get_int(entryBuffer.get(),
122 12 + i*kIcoDirEntryBytes);
123 if (offset < bytesRead) {
124 SkDebugf("Error: invalid image offset.\n");
125 return NULL;
126 }
127 }
128 }
129
130 // Create a codec for the "best" embedded image
131 if (stream->skip(offset - bytesRead) != offset - bytesRead) {
132 SkDebugf("Error: could not skip to image data.\n");
133 return NULL;
134 }
135 // We will pass the current stream to the embedded decoder
136 const bool isPng = SkPngCodec::IsPng(stream);
137 if (!stream->move(-PNG_BYTES_TO_CHECK)) {
scroggo 2015/03/18 21:39:18 Unfortunately, we cannot depend on this call on An
msarett 2015/03/20 18:35:56 I have added a FIXME.
138 SkDebugf("Error: could not rewind embedded ico stream.\n");
139 return NULL;
140 }
141 SkCodec* codec;
142 if (isPng) {
143 codec = SkPngCodec::NewFromStream(stream);
144 } else {
145 // Assume the embedded image is a Bmp. Call a special constructor
146 // because Bmp in Ico images are stored without the first header.
147 codec = SkBmpCodec::NewFromIco(stream);
148 }
149 // Check for a valid result
150 if (NULL == codec) {
151 SkDebugf("Error: could not create embedded codec.\n");
152 return NULL;
153 }
154 SkAutoTDelete<SkCodec> embeddedCodec(codec);
155 if (embeddedCodec->getOriginalInfo().width() != bestWidth ||
156 embeddedCodec->getOriginalInfo().height() != bestHeight) {
157 SkDebugf("Warning: embedded dimensions do not match.\n");
158 // We will defer to the embedded dimensions
159 bestWidth = embeddedCodec->getOriginalInfo().width();
160 bestHeight = embeddedCodec->getOriginalInfo().height();
161 }
162
163 const SkImageInfo& imageInfo = SkImageInfo::Make(bestWidth, bestHeight,
scroggo 2015/03/18 21:39:18 This should not be a reference.
msarett 2015/03/20 18:35:56 Done.
164 embeddedCodec->getOriginalInfo().colorType(),
165 embeddedCodec->getOriginalInfo().alphaType());
166 // Note that stream is owned by the embedded codec, the ico does not need
167 // direct access to the stream.
168 return SkNEW_ARGS(SkIcoCodec, (imageInfo, NULL, embeddedCodec.detach()));
169 }
170
171 /*
172 *
173 * Creates an instance of the decoder
174 * Called only by NewFromStream
175 *
176 */
177 SkIcoCodec::SkIcoCodec(const SkImageInfo& info, SkStream* stream,
178 SkCodec* embeddedCodec)
179 : INHERITED(info, stream)
scroggo 2015/03/18 21:39:19 If stream is always NULL, you can remove it from t
msarett 2015/03/20 18:35:56 Done.
180 , fEmbeddedCodec(embeddedCodec)
181 {}
182
183 /*
184 *
185 * Initiates the Ico decode
186 *
187 */
188 SkCodec::Result SkIcoCodec::onGetPixels(const SkImageInfo& dstInfo,
189 void* dst, size_t dstRowBytes,
190 const Options& opts, SkPMColor* ct,
191 int* ptr) {
192 if (!this->rewindIfNeeded()) {
scroggo 2015/03/18 21:39:18 How does this work? rewindIfNeeded will attempt to
msarett 2015/03/20 18:35:56 Giving the caller an option of which image to pick
193 return kCouldNotRewind;
194 }
195 if (dstInfo.dimensions() != this->getOriginalInfo().dimensions()) {
196 SkDebugf("Error: scaling not supported.\n");
197 return kInvalidScale;
198 }
199 // We will not check if the conversion is possible because the embedded bmp
200 // or png codec will make this check for us.
201
202 // Decode the embedded image
203 return fEmbeddedCodec->getPixels(dstInfo, dst, dstRowBytes);
msarett 2015/03/18 19:59:25 Forgot to mention: I'm not sure about the implicat
scroggo 2015/03/18 21:39:18 There are two versions of getPixels. You should ca
msarett 2015/03/20 18:35:56 Done.
204 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698