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 "SkBmpStandardCodec.h" 9 #include "SkCodecPriv.h" 10 #include "SkColorPriv.h" 11 #include "SkStream.h" 12 13 /* 14 * Creates an instance of the decoder 15 * Called only by NewFromStream 16 */ 17 SkBmpStandardCodec::SkBmpStandardCodec(const SkImageInfo& info, SkStream* stream, 18 uint16_t bitsPerPixel, uint32_t numColors, 19 uint32_t bytesPerColor, uint32_t offset, 20 SkCodec::SkScanlineOrder rowOrder, 21 bool isOpaque, bool inIco) 22 : INHERITED(info, stream, bitsPerPixel, rowOrder) 23 , fColorTable(nullptr) 24 , fNumColors(numColors) 25 , fBytesPerColor(bytesPerColor) 26 , fOffset(offset) 27 , fSwizzler(nullptr) 28 , fSrcBuffer(new uint8_t [this->srcRowBytes()]) 29 , fIsOpaque(isOpaque) 30 , fInIco(inIco) 31 , fAndMaskRowBytes(fInIco ? SkAlign4(compute_row_bytes(this->getInfo().width(), 1)) : 0) 32 {} 33 34 /* 35 * Initiates the bitmap decode 36 */ 37 SkCodec::Result SkBmpStandardCodec::onGetPixels(const SkImageInfo& dstInfo, 38 void* dst, size_t dstRowBytes, 39 const Options& opts, 40 SkPMColor* inputColorPtr, 41 int* inputColorCount, 42 int* rowsDecoded) { 43 if (opts.fSubset) { 44 // Subsets are not supported. 45 return kUnimplemented; 46 } 47 if (dstInfo.dimensions() != this->getInfo().dimensions()) { 48 SkCodecPrintf("Error: scaling not supported.\n"); 49 return kInvalidScale; 50 } 51 if (!conversion_possible(dstInfo, this->getInfo())) { 52 SkCodecPrintf("Error: cannot convert input type to output type.\n"); 53 return kInvalidConversion; 54 } 55 56 Result result = this->prepareToDecode(dstInfo, opts, inputColorPtr, inputColorCount); 57 if (kSuccess != result) { 58 return result; 59 } 60 int rows = this->decodeRows(dstInfo, dst, dstRowBytes, opts); 61 if (rows != dstInfo.height()) { 62 *rowsDecoded = rows; 63 return kIncompleteInput; 64 } 65 return kSuccess; 66 } 67 68 /* 69 * Process the color table for the bmp input 70 */ 71 bool SkBmpStandardCodec::createColorTable(SkAlphaType dstAlphaType, int* numColors) { 72 // Allocate memory for color table 73 uint32_t colorBytes = 0; 74 SkPMColor colorTable[256]; 75 if (this->bitsPerPixel() <= 8) { 76 // Inform the caller of the number of colors 77 uint32_t maxColors = 1 << this->bitsPerPixel(); 78 if (nullptr != numColors) { 79 // We set the number of colors to maxColors in order to ensure 80 // safe memory accesses. Otherwise, an invalid pixel could 81 // access memory outside of our color table array. 82 *numColors = maxColors; 83 } 84 // Don't bother reading more than maxColors. 85 const uint32_t numColorsToRead = 86 fNumColors == 0 ? maxColors : SkTMin(fNumColors, maxColors); 87 88 // Read the color table from the stream 89 colorBytes = numColorsToRead * fBytesPerColor; 90 SkAutoTDeleteArray<uint8_t> cBuffer(new uint8_t[colorBytes]); 91 if (stream()->read(cBuffer.get(), colorBytes) != colorBytes) { 92 SkCodecPrintf("Error: unable to read color table.\n"); 93 return false; 94 } 95 96 // Choose the proper packing function 97 SkPMColor (*packARGB) (uint32_t, uint32_t, uint32_t, uint32_t); 98 if (fIsOpaque || kUnpremul_SkAlphaType == dstAlphaType) { 99 packARGB = &SkPackARGB32NoCheck; 100 } else { 101 packARGB = &SkPremultiplyARGBInline; 102 } 103 104 // Fill in the color table 105 uint32_t i = 0; 106 for (; i < numColorsToRead; i++) { 107 uint8_t blue = get_byte(cBuffer.get(), i*fBytesPerColor); 108 uint8_t green = get_byte(cBuffer.get(), i*fBytesPerColor + 1); 109 uint8_t red = get_byte(cBuffer.get(), i*fBytesPerColor + 2); 110 uint8_t alpha; 111 if (fIsOpaque) { 112 alpha = 0xFF; 113 } else { 114 alpha = get_byte(cBuffer.get(), i*fBytesPerColor + 3); 115 } 116 colorTable[i] = packARGB(alpha, red, green, blue); 117 } 118 119 // To avoid segmentation faults on bad pixel data, fill the end of the 120 // color table with black. This is the same the behavior as the 121 // chromium decoder. 122 for (; i < maxColors; i++) { 123 colorTable[i] = SkPackARGB32NoCheck(0xFF, 0, 0, 0); 124 } 125 126 // Set the color table 127 fColorTable.reset(new SkColorTable(colorTable, maxColors)); 128 } 129 130 // Bmp-in-Ico files do not use an offset to indicate where the pixel data 131 // begins. Pixel data always begins immediately after the color table. 132 if (!fInIco) { 133 // Check that we have not read past the pixel array offset 134 if(fOffset < colorBytes) { 135 // This may occur on OS 2.1 and other old versions where the color 136 // table defaults to max size, and the bmp tries to use a smaller 137 // color table. This is invalid, and our decision is to indicate 138 // an error, rather than try to guess the intended size of the 139 // color table. 140 SkCodecPrintf("Error: pixel data offset less than color table size.\n"); 141 return false; 142 } 143 144 // After reading the color table, skip to the start of the pixel array 145 if (stream()->skip(fOffset - colorBytes) != fOffset - colorBytes) { 146 SkCodecPrintf("Error: unable to skip to image data.\n"); 147 return false; 148 } 149 } 150 151 // Return true on success 152 return true; 153 } 154 155 void SkBmpStandardCodec::initializeSwizzler(const SkImageInfo& dstInfo, const Options& opts) { 156 // Get swizzler configuration 157 SkSwizzler::SrcConfig config = SkSwizzler::kUnknown; 158 switch (this->bitsPerPixel()) { 159 case 1: 160 config = SkSwizzler::kIndex1; 161 break; 162 case 2: 163 config = SkSwizzler::kIndex2; 164 break; 165 case 4: 166 config = SkSwizzler::kIndex4; 167 break; 168 case 8: 169 config = SkSwizzler::kIndex; 170 break; 171 case 24: 172 config = SkSwizzler::kBGR; 173 break; 174 case 32: 175 if (fIsOpaque) { 176 config = SkSwizzler::kBGRX; 177 } else { 178 config = SkSwizzler::kBGRA; 179 } 180 break; 181 default: 182 SkASSERT(false); 183 } 184 185 // Get a pointer to the color table if it exists 186 const SkPMColor* colorPtr = get_color_ptr(fColorTable.get()); 187 188 // Create swizzler 189 fSwizzler.reset(SkSwizzler::CreateSwizzler(config, colorPtr, dstInfo, opts)); 190 SkASSERT(fSwizzler); 191 } 192 193 SkCodec::Result SkBmpStandardCodec::prepareToDecode(const SkImageInfo& dstInfo, 194 const SkCodec::Options& options, SkPMColor inputColorPtr[], int* inputColorCount) { 195 // Create the color table if necessary and prepare the stream for decode 196 // Note that if it is non-NULL, inputColorCount will be modified 197 if (!this->createColorTable(dstInfo.alphaType(), inputColorCount)) { 198 SkCodecPrintf("Error: could not create color table.\n"); 199 return SkCodec::kInvalidInput; 200 } 201 202 // Copy the color table to the client if necessary 203 copy_color_table(dstInfo, this->fColorTable, inputColorPtr, inputColorCount); 204 205 // Initialize a swizzler 206 this->initializeSwizzler(dstInfo, options); 207 return SkCodec::kSuccess; 208 } 209 210 /* 211 * Performs the bitmap decoding for standard input format 212 */ 213 int SkBmpStandardCodec::decodeRows(const SkImageInfo& dstInfo, void* dst, size_t dstRowBytes, 214 const Options& opts) { 215 // Iterate over rows of the image 216 const int height = dstInfo.height(); 217 for (int y = 0; y < height; y++) { 218 // Read a row of the input 219 if (this->stream()->read(fSrcBuffer.get(), this->srcRowBytes()) != this->srcRowBytes()) { 220 SkCodecPrintf("Warning: incomplete input stream.\n"); 221 return y; 222 } 223 224 // Decode the row in destination format 225 uint32_t row = this->getDstRow(y, dstInfo.height()); 226 227 void* dstRow = SkTAddOffset<void>(dst, row * dstRowBytes); 228 fSwizzler->swizzle(dstRow, fSrcBuffer.get()); 229 } 230 231 if (fInIco) { 232 const int startScanline = this->currScanline(); 233 if (startScanline < 0) { 234 // We are not performing a scanline decode. 235 // Just decode the entire ICO mask and return. 236 decodeIcoMask(this->stream(), dstInfo, dst, dstRowBytes); 237 return height; 238 } 239 240 // In order to perform a scanline ICO decode, we must be able 241 // to skip ahead in the stream in order to apply the AND mask 242 // to the requested scanlines. 243 // We will do this by taking advantage of the fact that 244 // SkIcoCodec always uses a SkMemoryStream as its underlying 245 // representation of the stream. 246 const void* memoryBase = this->stream()->getMemoryBase(); 247 SkASSERT(nullptr != memoryBase); 248 SkASSERT(this->stream()->hasLength()); 249 SkASSERT(this->stream()->hasPosition()); 250 251 const size_t length = this->stream()->getLength(); 252 const size_t currPosition = this->stream()->getPosition(); 253 254 // Calculate how many bytes we must skip to reach the AND mask. 255 const int remainingScanlines = this->getInfo().height() - startScanline - height; 256 const size_t bytesToSkip = remainingScanlines * this->srcRowBytes() + 257 startScanline * fAndMaskRowBytes; 258 const size_t subStreamStartPosition = currPosition + bytesToSkip; 259 if (subStreamStartPosition >= length) { 260 // FIXME: How can we indicate that this decode was actually incomplete? 261 return height; 262 } 263 264 // Create a subStream to pass to decodeIcoMask(). It is useful to encapsulate 265 // the memory base into a stream in order to safely handle incomplete images 266 // without reading out of bounds memory. 267 const void* subStreamMemoryBase = SkTAddOffset<const void>(memoryBase, 268 subStreamStartPosition); 269 const size_t subStreamLength = length - subStreamStartPosition; 270 // This call does not transfer ownership of the subStreamMemoryBase. 271 SkMemoryStream subStream(subStreamMemoryBase, subStreamLength, false); 272 273 // FIXME: If decodeIcoMask does not succeed, is there a way that we can 274 // indicate the decode was incomplete? 275 decodeIcoMask(&subStream, dstInfo, dst, dstRowBytes); 276 } 277 278 return height; 279 } 280 281 void SkBmpStandardCodec::decodeIcoMask(SkStream* stream, const SkImageInfo& dstInfo, 282 void* dst, size_t dstRowBytes) { 283 // BMP in ICO have transparency, so this cannot be 565, and this mask 284 // prevents us from using kIndex8. The below code depends on the output 285 // being an SkPMColor. 286 SkASSERT(dstInfo.colorType() == kN32_SkColorType); 287 288 // If we are sampling, make sure that we only mask the sampled pixels. 289 // We do not need to worry about sampling in the y-dimension because that 290 // should be handled by SkSampledCodec. 291 const int sampleX = fSwizzler->sampleX(); 292 const int sampledWidth = get_scaled_dimension(this->getInfo().width(), sampleX); 293 const int srcStartX = get_start_coord(sampleX); 294 295 296 SkPMColor* dstPtr = (SkPMColor*) dst; 297 for (int y = 0; y < dstInfo.height(); y++) { 298 // The srcBuffer will at least be large enough 299 if (stream->read(fSrcBuffer.get(), fAndMaskRowBytes) != fAndMaskRowBytes) { 300 SkCodecPrintf("Warning: incomplete AND mask for bmp-in-ico.\n"); 301 return; 302 } 303 304 int row = this->getDstRow(y, dstInfo.height()); 305 306 SkPMColor* dstRow = 307 SkTAddOffset<SkPMColor>(dstPtr, row * dstRowBytes); 308 309 int srcX = srcStartX; 310 for (int dstX = 0; dstX < sampledWidth; dstX++) { 311 int quotient; 312 int modulus; 313 SkTDivMod(srcX, 8, "ient, &modulus); 314 uint32_t shift = 7 - modulus; 315 uint32_t alphaBit = (fSrcBuffer.get()[quotient] >> shift) & 0x1; 316 dstRow[dstX] &= alphaBit - 1; 317 srcX += sampleX; 318 } 319 } 320 } 321 322 uint32_t SkBmpStandardCodec::onGetFillValue(SkColorType colorType) const { 323 const SkPMColor* colorPtr = get_color_ptr(fColorTable.get()); 324 if (colorPtr) { 325 return get_color_table_fill_value(colorType, colorPtr, 0); 326 } 327 return INHERITED::onGetFillValue(colorType); 328 } 329