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