Chromium Code Reviews| 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 |