• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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