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 "SkBmpCodec.h"
9 #include "SkBmpMaskCodec.h"
10 #include "SkBmpRLECodec.h"
11 #include "SkBmpStandardCodec.h"
12 #include "SkCodecPriv.h"
13 #include "SkColorPriv.h"
14 #include "SkStream.h"
15 
16 /*
17  * Defines the version and type of the second bitmap header
18  */
19 enum BmpHeaderType {
20     kInfoV1_BmpHeaderType,
21     kInfoV2_BmpHeaderType,
22     kInfoV3_BmpHeaderType,
23     kInfoV4_BmpHeaderType,
24     kInfoV5_BmpHeaderType,
25     kOS2V1_BmpHeaderType,
26     kOS2VX_BmpHeaderType,
27     kUnknown_BmpHeaderType
28 };
29 
30 /*
31  * Possible bitmap compression types
32  */
33 enum BmpCompressionMethod {
34     kNone_BmpCompressionMethod =          0,
35     k8BitRLE_BmpCompressionMethod =       1,
36     k4BitRLE_BmpCompressionMethod =       2,
37     kBitMasks_BmpCompressionMethod =      3,
38     kJpeg_BmpCompressionMethod =          4,
39     kPng_BmpCompressionMethod =           5,
40     kAlphaBitMasks_BmpCompressionMethod = 6,
41     kCMYK_BmpCompressionMethod =          11,
42     kCMYK8BitRLE_BmpCompressionMethod =   12,
43     kCMYK4BitRLE_BmpCompressionMethod =   13
44 };
45 
46 /*
47  * Used to define the input format of the bmp
48  */
49 enum BmpInputFormat {
50     kStandard_BmpInputFormat,
51     kRLE_BmpInputFormat,
52     kBitMask_BmpInputFormat,
53     kUnknown_BmpInputFormat
54 };
55 
56 /*
57  * Checks the start of the stream to see if the image is a bitmap
58  */
IsBmp(const void * buffer,size_t bytesRead)59 bool SkBmpCodec::IsBmp(const void* buffer, size_t bytesRead) {
60     // TODO: Support "IC", "PT", "CI", "CP", "BA"
61     const char bmpSig[] = { 'B', 'M' };
62     return bytesRead >= sizeof(bmpSig) && !memcmp(buffer, bmpSig, sizeof(bmpSig));
63 }
64 
65 /*
66  * Assumes IsBmp was called and returned true
67  * Creates a bmp decoder
68  * Reads enough of the stream to determine the image format
69  */
NewFromStream(SkStream * stream)70 SkCodec* SkBmpCodec::NewFromStream(SkStream* stream) {
71     return SkBmpCodec::NewFromStream(stream, false);
72 }
73 
74 /*
75  * Creates a bmp decoder for a bmp embedded in ico
76  * Reads enough of the stream to determine the image format
77  */
NewFromIco(SkStream * stream)78 SkCodec* SkBmpCodec::NewFromIco(SkStream* stream) {
79     return SkBmpCodec::NewFromStream(stream, true);
80 }
81 
82 /*
83  * Read enough of the stream to initialize the SkBmpCodec. Returns a bool
84  * representing success or failure. If it returned true, and codecOut was
85  * not nullptr, it will be set to a new SkBmpCodec.
86  * Does *not* take ownership of the passed in SkStream.
87  */
ReadHeader(SkStream * stream,bool inIco,SkCodec ** codecOut)88 bool SkBmpCodec::ReadHeader(SkStream* stream, bool inIco, SkCodec** codecOut) {
89     // Header size constants
90     static const uint32_t kBmpHeaderBytes = 14;
91     static const uint32_t kBmpHeaderBytesPlusFour = kBmpHeaderBytes + 4;
92     static const uint32_t kBmpOS2V1Bytes = 12;
93     static const uint32_t kBmpOS2V2Bytes = 64;
94     static const uint32_t kBmpInfoBaseBytes = 16;
95     static const uint32_t kBmpInfoV1Bytes = 40;
96     static const uint32_t kBmpInfoV2Bytes = 52;
97     static const uint32_t kBmpInfoV3Bytes = 56;
98     static const uint32_t kBmpInfoV4Bytes = 108;
99     static const uint32_t kBmpInfoV5Bytes = 124;
100     static const uint32_t kBmpMaskBytes = 12;
101 
102     // The total bytes in the bmp file
103     // We only need to use this value for RLE decoding, so we will only
104     // check that it is valid in the RLE case.
105     uint32_t totalBytes;
106     // The offset from the start of the file where the pixel data begins
107     uint32_t offset;
108     // The size of the second (info) header in bytes
109     uint32_t infoBytes;
110 
111     // Bmps embedded in Icos skip the first Bmp header
112     if (!inIco) {
113         // Read the first header and the size of the second header
114         SkAutoTDeleteArray<uint8_t> hBuffer(new uint8_t[kBmpHeaderBytesPlusFour]);
115         if (stream->read(hBuffer.get(), kBmpHeaderBytesPlusFour) !=
116                 kBmpHeaderBytesPlusFour) {
117             SkCodecPrintf("Error: unable to read first bitmap header.\n");
118             return false;
119         }
120 
121         totalBytes = get_int(hBuffer.get(), 2);
122         offset = get_int(hBuffer.get(), 10);
123         if (offset < kBmpHeaderBytes + kBmpOS2V1Bytes) {
124             SkCodecPrintf("Error: invalid starting location for pixel data\n");
125             return false;
126         }
127 
128         // The size of the second (info) header in bytes
129         // The size is the first field of the second header, so we have already
130         // read the first four infoBytes.
131         infoBytes = get_int(hBuffer.get(), 14);
132         if (infoBytes < kBmpOS2V1Bytes) {
133             SkCodecPrintf("Error: invalid second header size.\n");
134             return false;
135         }
136     } else {
137         // This value is only used by RLE compression.  Bmp in Ico files do not
138         // use RLE.  If the compression field is incorrectly signaled as RLE,
139         // we will catch this and signal an error below.
140         totalBytes = 0;
141 
142         // Bmps in Ico cannot specify an offset.  We will always assume that
143         // pixel data begins immediately after the color table.  This value
144         // will be corrected below.
145         offset = 0;
146 
147         // Read the size of the second header
148         SkAutoTDeleteArray<uint8_t> hBuffer(new uint8_t[4]);
149         if (stream->read(hBuffer.get(), 4) != 4) {
150             SkCodecPrintf("Error: unable to read size of second bitmap header.\n");
151             return false;
152         }
153         infoBytes = get_int(hBuffer.get(), 0);
154         if (infoBytes < kBmpOS2V1Bytes) {
155             SkCodecPrintf("Error: invalid second header size.\n");
156             return false;
157         }
158     }
159 
160     // We already read the first four bytes of the info header to get the size
161     const uint32_t infoBytesRemaining = infoBytes - 4;
162 
163     // Read the second header
164     SkAutoTDeleteArray<uint8_t> iBuffer(new uint8_t[infoBytesRemaining]);
165     if (stream->read(iBuffer.get(), infoBytesRemaining) != infoBytesRemaining) {
166         SkCodecPrintf("Error: unable to read second bitmap header.\n");
167         return false;
168     }
169 
170     // The number of bits used per pixel in the pixel data
171     uint16_t bitsPerPixel;
172 
173     // The compression method for the pixel data
174     uint32_t compression = kNone_BmpCompressionMethod;
175 
176     // Number of colors in the color table, defaults to 0 or max (see below)
177     uint32_t numColors = 0;
178 
179     // Bytes per color in the color table, early versions use 3, most use 4
180     uint32_t bytesPerColor;
181 
182     // The image width and height
183     int width, height;
184 
185     // Determine image information depending on second header format
186     BmpHeaderType headerType;
187     if (infoBytes >= kBmpInfoBaseBytes) {
188         // Check the version of the header
189         switch (infoBytes) {
190             case kBmpInfoV1Bytes:
191                 headerType = kInfoV1_BmpHeaderType;
192                 break;
193             case kBmpInfoV2Bytes:
194                 headerType = kInfoV2_BmpHeaderType;
195                 break;
196             case kBmpInfoV3Bytes:
197                 headerType = kInfoV3_BmpHeaderType;
198                 break;
199             case kBmpInfoV4Bytes:
200                 headerType = kInfoV4_BmpHeaderType;
201                 break;
202             case kBmpInfoV5Bytes:
203                 headerType = kInfoV5_BmpHeaderType;
204                 break;
205             case 16:
206             case 20:
207             case 24:
208             case 28:
209             case 32:
210             case 36:
211             case 42:
212             case 46:
213             case 48:
214             case 60:
215             case kBmpOS2V2Bytes:
216                 headerType = kOS2VX_BmpHeaderType;
217                 break;
218             default:
219                 // We do not signal an error here because there is the
220                 // possibility of new or undocumented bmp header types.  Most
221                 // of the newer versions of bmp headers are similar to and
222                 // build off of the older versions, so we may still be able to
223                 // decode the bmp.
224                 SkCodecPrintf("Warning: unknown bmp header format.\n");
225                 headerType = kUnknown_BmpHeaderType;
226                 break;
227         }
228         // We check the size of the header before entering the if statement.
229         // We should not reach this point unless the size is large enough for
230         // these required fields.
231         SkASSERT(infoBytesRemaining >= 12);
232         width = get_int(iBuffer.get(), 0);
233         height = get_int(iBuffer.get(), 4);
234         bitsPerPixel = get_short(iBuffer.get(), 10);
235 
236         // Some versions do not have these fields, so we check before
237         // overwriting the default value.
238         if (infoBytesRemaining >= 16) {
239             compression = get_int(iBuffer.get(), 12);
240             if (infoBytesRemaining >= 32) {
241                 numColors = get_int(iBuffer.get(), 28);
242             }
243         }
244 
245         // All of the headers that reach this point, store color table entries
246         // using 4 bytes per pixel.
247         bytesPerColor = 4;
248     } else if (infoBytes >= kBmpOS2V1Bytes) {
249         // The OS2V1 is treated separately because it has a unique format
250         headerType = kOS2V1_BmpHeaderType;
251         width = (int) get_short(iBuffer.get(), 0);
252         height = (int) get_short(iBuffer.get(), 2);
253         bitsPerPixel = get_short(iBuffer.get(), 6);
254         bytesPerColor = 3;
255     } else {
256         // There are no valid bmp headers
257         SkCodecPrintf("Error: second bitmap header size is invalid.\n");
258         return false;
259     }
260 
261     // Check for valid dimensions from header
262     SkCodec::SkScanlineOrder rowOrder = SkCodec::kBottomUp_SkScanlineOrder;
263     if (height < 0) {
264         height = -height;
265         rowOrder = SkCodec::kTopDown_SkScanlineOrder;
266     }
267     // The height field for bmp in ico is double the actual height because they
268     // contain an XOR mask followed by an AND mask
269     if (inIco) {
270         height /= 2;
271     }
272     if (width <= 0 || height <= 0) {
273         // TODO: Decide if we want to disable really large bmps as well.
274         // https://code.google.com/p/skia/issues/detail?id=3617
275         SkCodecPrintf("Error: invalid bitmap dimensions.\n");
276         return false;
277     }
278 
279     // Create mask struct
280     SkMasks::InputMasks inputMasks;
281     memset(&inputMasks, 0, sizeof(SkMasks::InputMasks));
282 
283     // Determine the input compression format and set bit masks if necessary
284     uint32_t maskBytes = 0;
285     BmpInputFormat inputFormat = kUnknown_BmpInputFormat;
286     switch (compression) {
287         case kNone_BmpCompressionMethod:
288             inputFormat = kStandard_BmpInputFormat;
289             break;
290         case k8BitRLE_BmpCompressionMethod:
291             if (bitsPerPixel != 8) {
292                 SkCodecPrintf("Warning: correcting invalid bitmap format.\n");
293                 bitsPerPixel = 8;
294             }
295             inputFormat = kRLE_BmpInputFormat;
296             break;
297         case k4BitRLE_BmpCompressionMethod:
298             if (bitsPerPixel != 4) {
299                 SkCodecPrintf("Warning: correcting invalid bitmap format.\n");
300                 bitsPerPixel = 4;
301             }
302             inputFormat = kRLE_BmpInputFormat;
303             break;
304         case kAlphaBitMasks_BmpCompressionMethod:
305         case kBitMasks_BmpCompressionMethod:
306             // Load the masks
307             inputFormat = kBitMask_BmpInputFormat;
308             switch (headerType) {
309                 case kInfoV1_BmpHeaderType: {
310                     // The V1 header stores the bit masks after the header
311                     SkAutoTDeleteArray<uint8_t> mBuffer(new uint8_t[kBmpMaskBytes]);
312                     if (stream->read(mBuffer.get(), kBmpMaskBytes) !=
313                             kBmpMaskBytes) {
314                         SkCodecPrintf("Error: unable to read bit inputMasks.\n");
315                         return false;
316                     }
317                     maskBytes = kBmpMaskBytes;
318                     inputMasks.red = get_int(mBuffer.get(), 0);
319                     inputMasks.green = get_int(mBuffer.get(), 4);
320                     inputMasks.blue = get_int(mBuffer.get(), 8);
321                     break;
322                 }
323                 case kInfoV2_BmpHeaderType:
324                 case kInfoV3_BmpHeaderType:
325                 case kInfoV4_BmpHeaderType:
326                 case kInfoV5_BmpHeaderType:
327                     // Header types are matched based on size.  If the header
328                     // is V2+, we are guaranteed to be able to read at least
329                     // this size.
330                     SkASSERT(infoBytesRemaining >= 48);
331                     inputMasks.red = get_int(iBuffer.get(), 36);
332                     inputMasks.green = get_int(iBuffer.get(), 40);
333                     inputMasks.blue = get_int(iBuffer.get(), 44);
334                     break;
335                 case kOS2VX_BmpHeaderType:
336                     // TODO: Decide if we intend to support this.
337                     //       It is unsupported in the previous version and
338                     //       in chromium.  I have not come across a test case
339                     //       that uses this format.
340                     SkCodecPrintf("Error: huffman format unsupported.\n");
341                     return false;
342                 default:
343                    SkCodecPrintf("Error: invalid bmp bit masks header.\n");
344                    return false;
345             }
346             break;
347         case kJpeg_BmpCompressionMethod:
348             if (24 == bitsPerPixel) {
349                 inputFormat = kRLE_BmpInputFormat;
350                 break;
351             }
352             // Fall through
353         case kPng_BmpCompressionMethod:
354             // TODO: Decide if we intend to support this.
355             //       It is unsupported in the previous version and
356             //       in chromium.  I think it is used mostly for printers.
357             SkCodecPrintf("Error: compression format not supported.\n");
358             return false;
359         case kCMYK_BmpCompressionMethod:
360         case kCMYK8BitRLE_BmpCompressionMethod:
361         case kCMYK4BitRLE_BmpCompressionMethod:
362             // TODO: Same as above.
363             SkCodecPrintf("Error: CMYK not supported for bitmap decoding.\n");
364             return false;
365         default:
366             SkCodecPrintf("Error: invalid format for bitmap decoding.\n");
367             return false;
368     }
369 
370     // Most versions of bmps should be rendered as opaque.  Either they do
371     // not have an alpha channel, or they expect the alpha channel to be
372     // ignored.  V3+ bmp files introduce an alpha mask and allow the creator
373     // of the image to use the alpha channels.  However, many of these images
374     // leave the alpha channel blank and expect to be rendered as opaque.  This
375     // is the case for almost all V3 images, so we render these as opaque.  For
376     // V4+ images in kMask mode, we will use the alpha mask.
377     //
378     // skbug.com/4116: We should perhaps also apply the alpha mask in kStandard
379     //                 mode.  We just haven't seen any images that expect this
380     //                 behavior.
381     //
382     // Additionally, V3 bmp-in-ico may use the alpha mask.
383     SkAlphaType alphaType = kOpaque_SkAlphaType;
384     if ((kInfoV3_BmpHeaderType == headerType && inIco) ||
385             kInfoV4_BmpHeaderType == headerType ||
386             kInfoV5_BmpHeaderType == headerType) {
387         // Header types are matched based on size.  If the header is
388         // V3+, we are guaranteed to be able to read at least this size.
389         SkASSERT(infoBytesRemaining > 52);
390         inputMasks.alpha = get_int(iBuffer.get(), 48);
391         if (inputMasks.alpha != 0) {
392             alphaType = kUnpremul_SkAlphaType;
393         }
394     }
395     iBuffer.free();
396 
397     // Additionally, 32 bit bmp-in-icos use the alpha channel.
398     // FIXME (msarett): Don't all bmp-in-icos use the alpha channel?
399     // And, RLE inputs may skip pixels, leaving them as transparent.  This
400     // is uncommon, but we cannot be certain that an RLE bmp will be opaque.
401     if ((inIco && 32 == bitsPerPixel) || (kRLE_BmpInputFormat == inputFormat)) {
402         alphaType = kUnpremul_SkAlphaType;
403     }
404 
405     // Check for valid bits per pixel.
406     // At the same time, use this information to choose a suggested color type
407     // and to set default masks.
408     SkColorType colorType = kN32_SkColorType;
409     switch (bitsPerPixel) {
410         // In addition to more standard pixel compression formats, bmp supports
411         // the use of bit masks to determine pixel components.  The standard
412         // format for representing 16-bit colors is 555 (XRRRRRGGGGGBBBBB),
413         // which does not map well to any Skia color formats.  For this reason,
414         // we will always enable mask mode with 16 bits per pixel.
415         case 16:
416             if (kBitMask_BmpInputFormat != inputFormat) {
417                 inputMasks.red = 0x7C00;
418                 inputMasks.green = 0x03E0;
419                 inputMasks.blue = 0x001F;
420                 inputFormat = kBitMask_BmpInputFormat;
421             }
422             break;
423         // We want to decode to kIndex_8 for input formats that are already
424         // designed in index format.
425         case 1:
426         case 2:
427         case 4:
428         case 8:
429             // However, we cannot in RLE format since we may need to leave some
430             // pixels as transparent.  Similarly, we also cannot for ICO images
431             // since we may need to apply a transparent mask.
432             if (kRLE_BmpInputFormat != inputFormat && !inIco) {
433                 colorType = kIndex_8_SkColorType;
434             }
435 
436             // Mask bmps must have 16, 24, or 32 bits per pixel.
437             if (kBitMask_BmpInputFormat == inputFormat) {
438                 SkCodecPrintf("Error: invalid input value of bits per pixel for mask bmp.\n");
439                 return false;
440             }
441         case 24:
442         case 32:
443             break;
444         default:
445             SkCodecPrintf("Error: invalid input value for bits per pixel.\n");
446             return false;
447     }
448 
449     // Check that input bit masks are valid and create the masks object
450     SkAutoTDelete<SkMasks>
451             masks(SkMasks::CreateMasks(inputMasks, bitsPerPixel));
452     if (nullptr == masks) {
453         SkCodecPrintf("Error: invalid input masks.\n");
454         return false;
455     }
456 
457     // Check for a valid number of total bytes when in RLE mode
458     if (totalBytes <= offset && kRLE_BmpInputFormat == inputFormat) {
459         SkCodecPrintf("Error: RLE requires valid input size.\n");
460         return false;
461     }
462     const size_t RLEBytes = totalBytes - offset;
463 
464     // Calculate the number of bytes read so far
465     const uint32_t bytesRead = kBmpHeaderBytes + infoBytes + maskBytes;
466     if (!inIco && offset < bytesRead) {
467         // TODO (msarett): Do we really want to fail if the offset in the header is invalid?
468         //                 Seems like we can just assume that the offset is zero and try to decode?
469         //                 Maybe we don't want to try to decode corrupt images?
470         SkCodecPrintf("Error: pixel data offset less than header size.\n");
471         return false;
472     }
473 
474     // Skip to the start of the pixel array.
475     // We can do this here because there is no color table to read
476     // in bit mask mode.
477     if (!inIco && kBitMask_BmpInputFormat == inputFormat) {
478         if (stream->skip(offset - bytesRead) != offset - bytesRead) {
479             SkCodecPrintf("Error: unable to skip to image data.\n");
480             return false;
481         }
482     }
483 
484     if (codecOut) {
485         // BMPs-in-ICOs contain an alpha mask after the image which means we
486         // cannot guarantee that an image is opaque, even if the bmp thinks
487         // it is.
488         bool isOpaque = kOpaque_SkAlphaType == alphaType;
489         if (inIco) {
490             alphaType = kUnpremul_SkAlphaType;
491         }
492 
493         // Set the image info
494         const SkImageInfo& imageInfo = SkImageInfo::Make(width, height,
495                 colorType, alphaType);
496 
497         // Return the codec
498         switch (inputFormat) {
499             case kStandard_BmpInputFormat:
500                 // We require streams to have a memory base for Bmp-in-Ico decodes.
501                 SkASSERT(!inIco || nullptr != stream->getMemoryBase());
502                 *codecOut = new SkBmpStandardCodec(imageInfo, stream, bitsPerPixel, numColors,
503                         bytesPerColor, offset - bytesRead, rowOrder, isOpaque, inIco);
504                 return true;
505             case kBitMask_BmpInputFormat:
506                 // Bmp-in-Ico must be standard mode
507                 if (inIco) {
508                     SkCodecPrintf("Error: Icos may not use bit mask format.\n");
509                     return false;
510                 }
511 
512                 *codecOut = new SkBmpMaskCodec(imageInfo, stream, bitsPerPixel, masks.detach(),
513                         rowOrder);
514                 return true;
515             case kRLE_BmpInputFormat:
516                 // Bmp-in-Ico must be standard mode
517                 // When inIco is true, this line cannot be reached, since we
518                 // require that RLE Bmps have a valid number of totalBytes, and
519                 // Icos skip the header that contains totalBytes.
520                 SkASSERT(!inIco);
521                 *codecOut = new SkBmpRLECodec(imageInfo, stream, bitsPerPixel, numColors,
522                         bytesPerColor, offset - bytesRead, rowOrder, RLEBytes);
523                 return true;
524             default:
525                 SkASSERT(false);
526                 return false;
527         }
528     }
529 
530     return true;
531 }
532 
533 /*
534  * Creates a bmp decoder
535  * Reads enough of the stream to determine the image format
536  */
NewFromStream(SkStream * stream,bool inIco)537 SkCodec* SkBmpCodec::NewFromStream(SkStream* stream, bool inIco) {
538     SkAutoTDelete<SkStream> streamDeleter(stream);
539     SkCodec* codec = nullptr;
540     if (ReadHeader(stream, inIco, &codec)) {
541         // codec has taken ownership of stream, so we do not need to
542         // delete it.
543         SkASSERT(codec);
544         streamDeleter.detach();
545         return codec;
546     }
547     return nullptr;
548 }
549 
SkBmpCodec(const SkImageInfo & info,SkStream * stream,uint16_t bitsPerPixel,SkCodec::SkScanlineOrder rowOrder)550 SkBmpCodec::SkBmpCodec(const SkImageInfo& info, SkStream* stream,
551         uint16_t bitsPerPixel, SkCodec::SkScanlineOrder rowOrder)
552     : INHERITED(info, stream)
553     , fBitsPerPixel(bitsPerPixel)
554     , fRowOrder(rowOrder)
555     , fSrcRowBytes(SkAlign4(compute_row_bytes(info.width(), fBitsPerPixel)))
556 {}
557 
onRewind()558 bool SkBmpCodec::onRewind() {
559     return SkBmpCodec::ReadHeader(this->stream(), this->inIco(), nullptr);
560 }
561 
getDstRow(int32_t y,int32_t height) const562 int32_t SkBmpCodec::getDstRow(int32_t y, int32_t height) const {
563     if (SkCodec::kTopDown_SkScanlineOrder == fRowOrder) {
564         return y;
565     }
566     SkASSERT(SkCodec::kBottomUp_SkScanlineOrder == fRowOrder);
567     return height - y - 1;
568 }
569 
onStartScanlineDecode(const SkImageInfo & dstInfo,const SkCodec::Options & options,SkPMColor inputColorPtr[],int * inputColorCount)570 SkCodec::Result SkBmpCodec::onStartScanlineDecode(const SkImageInfo& dstInfo,
571         const SkCodec::Options& options, SkPMColor inputColorPtr[], int* inputColorCount) {
572     if (!conversion_possible(dstInfo, this->getInfo())) {
573         SkCodecPrintf("Error: cannot convert input type to output type.\n");
574         return kInvalidConversion;
575     }
576 
577     return prepareToDecode(dstInfo, options, inputColorPtr, inputColorCount);
578 }
579 
onGetScanlines(void * dst,int count,size_t rowBytes)580 int SkBmpCodec::onGetScanlines(void* dst, int count, size_t rowBytes) {
581     // Create a new image info representing the portion of the image to decode
582     SkImageInfo rowInfo = this->dstInfo().makeWH(this->dstInfo().width(), count);
583 
584     // Decode the requested rows
585     return this->decodeRows(rowInfo, dst, rowBytes, this->options());
586 }
587 
skipRows(int count)588 bool SkBmpCodec::skipRows(int count) {
589     const size_t bytesToSkip = count * fSrcRowBytes;
590     return this->stream()->skip(bytesToSkip) == bytesToSkip;
591 }
592 
onSkipScanlines(int count)593 bool SkBmpCodec::onSkipScanlines(int count) {
594     return this->skipRows(count);
595 }
596