OLD | NEW |
---|---|
(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 } | |
OLD | NEW |