blob: 37af47600b504d8a49efcaa3e66f093b9bb18b0c [file] [log] [blame]
msarett4ab9d5f2015-08-06 15:34:42 -07001/*
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 "SkBmpRLECodec.h"
9#include "SkCodecPriv.h"
10#include "SkColorPriv.h"
msarett5406d6f2015-08-31 06:55:13 -070011#include "SkScaledCodec.h"
msarett4ab9d5f2015-08-06 15:34:42 -070012#include "SkStream.h"
13
14/*
msarett4ab9d5f2015-08-06 15:34:42 -070015 * Creates an instance of the decoder
16 * Called only by NewFromStream
17 */
msarett5406d6f2015-08-31 06:55:13 -070018SkBmpRLECodec::SkBmpRLECodec(const SkImageInfo& info, SkStream* stream,
19 uint16_t bitsPerPixel, uint32_t numColors,
20 uint32_t bytesPerColor, uint32_t offset,
scroggo46c57472015-09-30 08:57:13 -070021 SkCodec::SkScanlineOrder rowOrder,
halcanary385fe4d2015-08-26 13:07:48 -070022 size_t RLEBytes)
msarett4ab9d5f2015-08-06 15:34:42 -070023 : INHERITED(info, stream, bitsPerPixel, rowOrder)
halcanary96fcdcc2015-08-27 07:41:13 -070024 , fColorTable(nullptr)
msarett4ab9d5f2015-08-06 15:34:42 -070025 , fNumColors(this->computeNumColors(numColors))
26 , fBytesPerColor(bytesPerColor)
27 , fOffset(offset)
halcanary385fe4d2015-08-26 13:07:48 -070028 , fStreamBuffer(new uint8_t[RLEBytes])
msarett4ab9d5f2015-08-06 15:34:42 -070029 , fRLEBytes(RLEBytes)
msarett5406d6f2015-08-31 06:55:13 -070030 , fCurrRLEByte(0)
31 , fSampleX(1)
32{}
msarett4ab9d5f2015-08-06 15:34:42 -070033
34/*
35 * Initiates the bitmap decode
36 */
37SkCodec::Result SkBmpRLECodec::onGetPixels(const SkImageInfo& dstInfo,
msarett5406d6f2015-08-31 06:55:13 -070038 void* dst, size_t dstRowBytes,
39 const Options& opts,
40 SkPMColor* inputColorPtr,
msarette6dd0042015-10-09 11:07:34 -070041 int* inputColorCount,
42 int* rowsDecoded) {
msarett4ab9d5f2015-08-06 15:34:42 -070043 if (opts.fSubset) {
44 // Subsets are not supported.
45 return kUnimplemented;
46 }
msarett4ab9d5f2015-08-06 15:34:42 -070047 if (!conversion_possible(dstInfo, this->getInfo())) {
48 SkCodecPrintf("Error: cannot convert input type to output type.\n");
49 return kInvalidConversion;
50 }
51
msarett5406d6f2015-08-31 06:55:13 -070052 Result result = this->prepareToDecode(dstInfo, opts, inputColorPtr, inputColorCount);
53 if (kSuccess != result) {
54 return result;
msarett4ab9d5f2015-08-06 15:34:42 -070055 }
56
57 // Perform the decode
msarette6dd0042015-10-09 11:07:34 -070058 uint32_t rows = this->decodeRows(dstInfo, dst, dstRowBytes, opts);
59 if (rows != dstInfo.height()) {
60 // We set rowsDecoded equal to the height because the background has already
61 // been filled. RLE encodings sometimes skip pixels, so we always start by
62 // filling the background.
63 *rowsDecoded = dstInfo.height();
64 return kIncompleteInput;
65 }
66
67 return kSuccess;
msarett4ab9d5f2015-08-06 15:34:42 -070068}
69
70/*
71 * Process the color table for the bmp input
72 */
73 bool SkBmpRLECodec::createColorTable(int* numColors) {
74 // Allocate memory for color table
75 uint32_t colorBytes = 0;
76 SkPMColor colorTable[256];
77 if (this->bitsPerPixel() <= 8) {
78 // Inform the caller of the number of colors
79 uint32_t maxColors = 1 << this->bitsPerPixel();
halcanary96fcdcc2015-08-27 07:41:13 -070080 if (nullptr != numColors) {
msarett4ab9d5f2015-08-06 15:34:42 -070081 // We set the number of colors to maxColors in order to ensure
82 // safe memory accesses. Otherwise, an invalid pixel could
83 // access memory outside of our color table array.
84 *numColors = maxColors;
85 }
86
87 // Read the color table from the stream
88 colorBytes = fNumColors * fBytesPerColor;
halcanary385fe4d2015-08-26 13:07:48 -070089 SkAutoTDeleteArray<uint8_t> cBuffer(new uint8_t[colorBytes]);
msarett4ab9d5f2015-08-06 15:34:42 -070090 if (stream()->read(cBuffer.get(), colorBytes) != colorBytes) {
91 SkCodecPrintf("Error: unable to read color table.\n");
92 return false;
93 }
94
95 // Fill in the color table
96 uint32_t i = 0;
97 for (; i < fNumColors; 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 colorTable[i] = SkPackARGB32NoCheck(0xFF, red, green, blue);
102 }
103
104 // To avoid segmentation faults on bad pixel data, fill the end of the
105 // color table with black. This is the same the behavior as the
106 // chromium decoder.
107 for (; i < maxColors; i++) {
108 colorTable[i] = SkPackARGB32NoCheck(0xFF, 0, 0, 0);
109 }
110
111 // Set the color table
halcanary385fe4d2015-08-26 13:07:48 -0700112 fColorTable.reset(new SkColorTable(colorTable, maxColors));
msarett4ab9d5f2015-08-06 15:34:42 -0700113 }
114
115 // Check that we have not read past the pixel array offset
116 if(fOffset < colorBytes) {
117 // This may occur on OS 2.1 and other old versions where the color
118 // table defaults to max size, and the bmp tries to use a smaller
119 // color table. This is invalid, and our decision is to indicate
120 // an error, rather than try to guess the intended size of the
121 // color table.
122 SkCodecPrintf("Error: pixel data offset less than color table size.\n");
123 return false;
124 }
125
126 // After reading the color table, skip to the start of the pixel array
127 if (stream()->skip(fOffset - colorBytes) != fOffset - colorBytes) {
128 SkCodecPrintf("Error: unable to skip to image data.\n");
129 return false;
130 }
131
132 // Return true on success
133 return true;
134}
135
136bool SkBmpRLECodec::initializeStreamBuffer() {
137 // Setup a buffer to contain the full input stream
msarett5406d6f2015-08-31 06:55:13 -0700138 // TODO (msarett): I'm not sure it is smart or optimal to trust fRLEBytes (read from header)
139 // as the size of our buffer. First of all, the decode fails if fRLEBytes is
140 // corrupt (negative, zero, or small) when we might be able to decode
141 // successfully with a fixed size buffer. Additionally, we would save memory
142 // using a fixed size buffer if the RLE encoding is large. On the other hand,
143 // we may also waste memory with a fixed size buffer. And determining a
144 // minimum size for our buffer would depend on the image width (so it's not
145 // really "fixed" size), and we may end up allocating a buffer that is
146 // generally larger than the average encoded size anyway.
msarett4ab9d5f2015-08-06 15:34:42 -0700147 size_t totalBytes = this->stream()->read(fStreamBuffer.get(), fRLEBytes);
148 if (totalBytes < fRLEBytes) {
149 fRLEBytes = totalBytes;
150 SkCodecPrintf("Warning: incomplete RLE file.\n");
151 }
152 if (fRLEBytes == 0) {
153 SkCodecPrintf("Error: could not read RLE image data.\n");
154 return false;
155 }
msarett5406d6f2015-08-31 06:55:13 -0700156 fCurrRLEByte = 0;
msarett4ab9d5f2015-08-06 15:34:42 -0700157 return true;
158}
159
160/*
msarettd0375bc2015-08-12 08:08:56 -0700161 * Before signalling kIncompleteInput, we should attempt to load the
162 * stream buffer with additional data.
163 *
164 * @return the number of bytes remaining in the stream buffer after
165 * attempting to read more bytes from the stream
166 */
167size_t SkBmpRLECodec::checkForMoreData() {
168 const size_t remainingBytes = fRLEBytes - fCurrRLEByte;
169 uint8_t* buffer = fStreamBuffer.get();
170
171 // We will be reusing the same buffer, starting over from the beginning.
172 // Move any remaining bytes to the start of the buffer.
173 // We use memmove() instead of memcpy() because there is risk that the dst
174 // and src memory will overlap in corrupt images.
175 memmove(buffer, SkTAddOffset<uint8_t>(buffer, fCurrRLEByte), remainingBytes);
176
177 // Adjust the buffer ptr to the start of the unfilled data.
178 buffer += remainingBytes;
179
180 // Try to read additional bytes from the stream. There are fCurrRLEByte
181 // bytes of additional space remaining in the buffer, assuming that we
182 // have already copied remainingBytes to the start of the buffer.
183 size_t additionalBytes = this->stream()->read(buffer, fCurrRLEByte);
184
185 // Update counters and return the number of bytes we currently have
186 // available. We are at the start of the buffer again.
187 fCurrRLEByte = 0;
188 // If we were unable to fill the buffer, fRLEBytes is no longer equal to
189 // the size of the buffer. There will be unused space at the end. This
190 // should be fine, given that there are no more bytes in the stream.
191 fRLEBytes = remainingBytes + additionalBytes;
192 return fRLEBytes;
193}
194
195/*
msarett4ab9d5f2015-08-06 15:34:42 -0700196 * Set an RLE pixel using the color table
197 */
198void SkBmpRLECodec::setPixel(void* dst, size_t dstRowBytes,
199 const SkImageInfo& dstInfo, uint32_t x, uint32_t y,
200 uint8_t index) {
msarett5406d6f2015-08-31 06:55:13 -0700201 if (is_coord_necessary(x, fSampleX, dstInfo.width())) {
202 // Set the row
203 uint32_t row = this->getDstRow(y, dstInfo.height());
msarett4ab9d5f2015-08-06 15:34:42 -0700204
msarett5406d6f2015-08-31 06:55:13 -0700205 // Set the pixel based on destination color type
206 const int dstX = get_dst_coord(x, fSampleX);
207 switch (dstInfo.colorType()) {
208 case kN32_SkColorType: {
209 SkPMColor* dstRow = SkTAddOffset<SkPMColor>(dst, row * (int) dstRowBytes);
210 dstRow[dstX] = fColorTable->operator[](index);
211 break;
212 }
213 case kRGB_565_SkColorType: {
214 uint16_t* dstRow = SkTAddOffset<uint16_t>(dst, row * (int) dstRowBytes);
215 dstRow[dstX] = SkPixel32ToPixel16(fColorTable->operator[](index));
216 break;
217 }
218 default:
219 // This case should not be reached. We should catch an invalid
220 // color type when we check that the conversion is possible.
221 SkASSERT(false);
222 break;
msarett4ab9d5f2015-08-06 15:34:42 -0700223 }
msarett4ab9d5f2015-08-06 15:34:42 -0700224 }
225}
226
227/*
228 * Set an RLE pixel from R, G, B values
229 */
230void SkBmpRLECodec::setRGBPixel(void* dst, size_t dstRowBytes,
231 const SkImageInfo& dstInfo, uint32_t x,
232 uint32_t y, uint8_t red, uint8_t green,
233 uint8_t blue) {
msarett5406d6f2015-08-31 06:55:13 -0700234 if (is_coord_necessary(x, fSampleX, dstInfo.width())) {
235 // Set the row
236 uint32_t row = this->getDstRow(y, dstInfo.height());
237
238 // Set the pixel based on destination color type
239 const int dstX = get_dst_coord(x, fSampleX);
240 switch (dstInfo.colorType()) {
241 case kN32_SkColorType: {
242 SkPMColor* dstRow = SkTAddOffset<SkPMColor>(dst, row * (int) dstRowBytes);
243 dstRow[dstX] = SkPackARGB32NoCheck(0xFF, red, green, blue);
244 break;
245 }
246 case kRGB_565_SkColorType: {
247 uint16_t* dstRow = SkTAddOffset<uint16_t>(dst, row * (int) dstRowBytes);
248 dstRow[dstX] = SkPack888ToRGB16(red, green, blue);
249 break;
250 }
251 default:
252 // This case should not be reached. We should catch an invalid
253 // color type when we check that the conversion is possible.
254 SkASSERT(false);
255 break;
256 }
257 }
258}
259
260SkCodec::Result SkBmpRLECodec::prepareToDecode(const SkImageInfo& dstInfo,
261 const SkCodec::Options& options, SkPMColor inputColorPtr[], int* inputColorCount) {
scroggoe7fc14b2015-10-02 13:14:46 -0700262 // Reset fSampleX. If it needs to be a value other than 1, it will get modified by
263 // the sampler.
264 fSampleX = 1;
msarett5406d6f2015-08-31 06:55:13 -0700265 // Create the color table if necessary and prepare the stream for decode
266 // Note that if it is non-NULL, inputColorCount will be modified
267 if (!this->createColorTable(inputColorCount)) {
268 SkCodecPrintf("Error: could not create color table.\n");
269 return SkCodec::kInvalidInput;
msarett4ab9d5f2015-08-06 15:34:42 -0700270 }
271
msarett5406d6f2015-08-31 06:55:13 -0700272 // Copy the color table to the client if necessary
273 copy_color_table(dstInfo, this->fColorTable, inputColorPtr, inputColorCount);
274
275 // Initialize a buffer for encoded RLE data
276 if (!this->initializeStreamBuffer()) {
277 SkCodecPrintf("Error: cannot initialize stream buffer.\n");
278 return SkCodec::kInvalidConversion;
msarett4ab9d5f2015-08-06 15:34:42 -0700279 }
msarett5406d6f2015-08-31 06:55:13 -0700280
msarett5406d6f2015-08-31 06:55:13 -0700281 return SkCodec::kSuccess;
msarett4ab9d5f2015-08-06 15:34:42 -0700282}
283
284/*
285 * Performs the bitmap decoding for RLE input format
286 * RLE decoding is performed all at once, rather than a one row at a time
287 */
msarette6dd0042015-10-09 11:07:34 -0700288int SkBmpRLECodec::decodeRows(const SkImageInfo& info, void* dst, size_t dstRowBytes,
289 const Options& opts) {
msarett4ab9d5f2015-08-06 15:34:42 -0700290 // Set RLE flags
291 static const uint8_t RLE_ESCAPE = 0;
292 static const uint8_t RLE_EOL = 0;
293 static const uint8_t RLE_EOF = 1;
294 static const uint8_t RLE_DELTA = 2;
295
296 // Set constant values
msarett5406d6f2015-08-31 06:55:13 -0700297 const int width = this->getInfo().width();
scroggoe7fc14b2015-10-02 13:14:46 -0700298 const int height = info.height();
299
300 // Account for sampling.
301 SkImageInfo dstInfo = info.makeWH(get_scaled_dimension(width, fSampleX), height);
msarett4ab9d5f2015-08-06 15:34:42 -0700302
303 // Destination parameters
304 int x = 0;
305 int y = 0;
306
307 // Set the background as transparent. Then, if the RLE code skips pixels,
308 // the skipped pixels will be transparent.
309 // Because of the need for transparent pixels, kN32 is the only color
310 // type that makes sense for the destination format.
311 SkASSERT(kN32_SkColorType == dstInfo.colorType());
msarette6dd0042015-10-09 11:07:34 -0700312 SkSampler::Fill(dstInfo, dst, dstRowBytes, SK_ColorTRANSPARENT, opts.fZeroInitialized);
msarett4ab9d5f2015-08-06 15:34:42 -0700313
314 while (true) {
315 // If we have reached a row that is beyond the requested height, we have
316 // succeeded.
317 if (y >= height) {
msarette6dd0042015-10-09 11:07:34 -0700318 // It would be better to check for the EOF marker before indicating
msarett4ab9d5f2015-08-06 15:34:42 -0700319 // success, but we may be performing a scanline decode, which
msarette6dd0042015-10-09 11:07:34 -0700320 // would require us to stop before decoding the full height.
321 return height;
msarett4ab9d5f2015-08-06 15:34:42 -0700322 }
323
324 // Every entry takes at least two bytes
325 if ((int) fRLEBytes - fCurrRLEByte < 2) {
msarettd0375bc2015-08-12 08:08:56 -0700326 SkCodecPrintf("Warning: might be incomplete RLE input.\n");
327 if (this->checkForMoreData() < 2) {
msarette6dd0042015-10-09 11:07:34 -0700328 return y;
msarettd0375bc2015-08-12 08:08:56 -0700329 }
msarett4ab9d5f2015-08-06 15:34:42 -0700330 }
331
332 // Read the next two bytes. These bytes have different meanings
333 // depending on their values. In the first interpretation, the first
334 // byte is an escape flag and the second byte indicates what special
335 // task to perform.
336 const uint8_t flag = fStreamBuffer.get()[fCurrRLEByte++];
337 const uint8_t task = fStreamBuffer.get()[fCurrRLEByte++];
338
339 // Perform decoding
340 if (RLE_ESCAPE == flag) {
341 switch (task) {
342 case RLE_EOL:
343 x = 0;
344 y++;
345 break;
346 case RLE_EOF:
347 return kSuccess;
348 case RLE_DELTA: {
349 // Two bytes are needed to specify delta
350 if ((int) fRLEBytes - fCurrRLEByte < 2) {
msarettd0375bc2015-08-12 08:08:56 -0700351 SkCodecPrintf("Warning: might be incomplete RLE input.\n");
352 if (this->checkForMoreData() < 2) {
msarette6dd0042015-10-09 11:07:34 -0700353 return y;
msarettd0375bc2015-08-12 08:08:56 -0700354 }
msarett4ab9d5f2015-08-06 15:34:42 -0700355 }
356 // Modify x and y
357 const uint8_t dx = fStreamBuffer.get()[fCurrRLEByte++];
358 const uint8_t dy = fStreamBuffer.get()[fCurrRLEByte++];
359 x += dx;
360 y += dy;
361 if (x > width || y > height) {
msarettd0375bc2015-08-12 08:08:56 -0700362 SkCodecPrintf("Warning: invalid RLE input.\n");
msarette6dd0042015-10-09 11:07:34 -0700363 return y - dy;
msarett4ab9d5f2015-08-06 15:34:42 -0700364 }
365 break;
366 }
367 default: {
368 // If task does not match any of the above signals, it
369 // indicates that we have a sequence of non-RLE pixels.
370 // Furthermore, the value of task is equal to the number
371 // of pixels to interpret.
372 uint8_t numPixels = task;
373 const size_t rowBytes = compute_row_bytes(numPixels,
374 this->bitsPerPixel());
375 // Abort if setting numPixels moves us off the edge of the
msarettd0375bc2015-08-12 08:08:56 -0700376 // image.
377 if (x + numPixels > width) {
378 SkCodecPrintf("Warning: invalid RLE input.\n");
msarette6dd0042015-10-09 11:07:34 -0700379 return y;
msarettd0375bc2015-08-12 08:08:56 -0700380 }
381 // Also abort if there are not enough bytes
msarett4ab9d5f2015-08-06 15:34:42 -0700382 // remaining in the stream to set numPixels.
msarettd0375bc2015-08-12 08:08:56 -0700383 if ((int) fRLEBytes - fCurrRLEByte < SkAlign2(rowBytes)) {
384 SkCodecPrintf("Warning: might be incomplete RLE input.\n");
385 if (this->checkForMoreData() < SkAlign2(rowBytes)) {
msarette6dd0042015-10-09 11:07:34 -0700386 return y;
msarettd0375bc2015-08-12 08:08:56 -0700387 }
msarett4ab9d5f2015-08-06 15:34:42 -0700388 }
389 // Set numPixels number of pixels
390 while (numPixels > 0) {
391 switch(this->bitsPerPixel()) {
392 case 4: {
393 SkASSERT(fCurrRLEByte < fRLEBytes);
394 uint8_t val = fStreamBuffer.get()[fCurrRLEByte++];
395 setPixel(dst, dstRowBytes, dstInfo, x++,
396 y, val >> 4);
397 numPixels--;
398 if (numPixels != 0) {
399 setPixel(dst, dstRowBytes, dstInfo,
400 x++, y, val & 0xF);
401 numPixels--;
402 }
403 break;
404 }
405 case 8:
406 SkASSERT(fCurrRLEByte < fRLEBytes);
407 setPixel(dst, dstRowBytes, dstInfo, x++,
408 y, fStreamBuffer.get()[fCurrRLEByte++]);
409 numPixels--;
410 break;
411 case 24: {
412 SkASSERT(fCurrRLEByte + 2 < fRLEBytes);
413 uint8_t blue = fStreamBuffer.get()[fCurrRLEByte++];
414 uint8_t green = fStreamBuffer.get()[fCurrRLEByte++];
415 uint8_t red = fStreamBuffer.get()[fCurrRLEByte++];
416 setRGBPixel(dst, dstRowBytes, dstInfo,
417 x++, y, red, green, blue);
418 numPixels--;
419 }
420 default:
421 SkASSERT(false);
msarette6dd0042015-10-09 11:07:34 -0700422 return y;
msarett4ab9d5f2015-08-06 15:34:42 -0700423 }
424 }
425 // Skip a byte if necessary to maintain alignment
426 if (!SkIsAlign2(rowBytes)) {
427 fCurrRLEByte++;
428 }
429 break;
430 }
431 }
432 } else {
433 // If the first byte read is not a flag, it indicates the number of
434 // pixels to set in RLE mode.
435 const uint8_t numPixels = flag;
436 const int endX = SkTMin<int>(x + numPixels, width);
437
438 if (24 == this->bitsPerPixel()) {
439 // In RLE24, the second byte read is part of the pixel color.
440 // There are two more required bytes to finish encoding the
441 // color.
442 if ((int) fRLEBytes - fCurrRLEByte < 2) {
msarettd0375bc2015-08-12 08:08:56 -0700443 SkCodecPrintf("Warning: might be incomplete RLE input.\n");
444 if (this->checkForMoreData() < 2) {
msarette6dd0042015-10-09 11:07:34 -0700445 return y;
msarettd0375bc2015-08-12 08:08:56 -0700446 }
msarett4ab9d5f2015-08-06 15:34:42 -0700447 }
448
449 // Fill the pixels up to endX with the specified color
450 uint8_t blue = task;
451 uint8_t green = fStreamBuffer.get()[fCurrRLEByte++];
452 uint8_t red = fStreamBuffer.get()[fCurrRLEByte++];
453 while (x < endX) {
bungeman0153dea2015-08-27 16:43:42 -0700454 setRGBPixel(dst, dstRowBytes, dstInfo, x++, y, red, green, blue);
msarett4ab9d5f2015-08-06 15:34:42 -0700455 }
456 } else {
457 // In RLE8 or RLE4, the second byte read gives the index in the
458 // color table to look up the pixel color.
459 // RLE8 has one color index that gets repeated
460 // RLE4 has two color indexes in the upper and lower 4 bits of
461 // the bytes, which are alternated
462 uint8_t indices[2] = { task, task };
463 if (4 == this->bitsPerPixel()) {
464 indices[0] >>= 4;
465 indices[1] &= 0xf;
466 }
467
468 // Set the indicated number of pixels
469 for (int which = 0; x < endX; x++) {
bungeman0153dea2015-08-27 16:43:42 -0700470 setPixel(dst, dstRowBytes, dstInfo, x, y, indices[which]);
msarett4ab9d5f2015-08-06 15:34:42 -0700471 which = !which;
472 }
473 }
474 }
475 }
476}
scroggoe7fc14b2015-10-02 13:14:46 -0700477
msarette6dd0042015-10-09 11:07:34 -0700478// FIXME: Make SkBmpRLECodec have no knowledge of sampling.
479// Or it should do all sampling natively.
480// It currently is a hybrid that needs to know what SkScaledCodec is doing.
scroggoe7fc14b2015-10-02 13:14:46 -0700481class SkBmpRLESampler : public SkSampler {
482public:
483 SkBmpRLESampler(SkBmpRLECodec* codec)
484 : fCodec(codec)
485 {
486 SkASSERT(fCodec);
487 }
488
489private:
msarette6dd0042015-10-09 11:07:34 -0700490 int onSetSampleX(int sampleX) override {
scroggoe7fc14b2015-10-02 13:14:46 -0700491 return fCodec->setSampleX(sampleX);
492 }
493
494 // Unowned pointer. fCodec will delete this class in its destructor.
495 SkBmpRLECodec* fCodec;
496};
497
msarette6dd0042015-10-09 11:07:34 -0700498SkSampler* SkBmpRLECodec::getSampler(bool createIfNecessary) {
499 if (!fSampler && createIfNecessary) {
scroggoe7fc14b2015-10-02 13:14:46 -0700500 fSampler.reset(new SkBmpRLESampler(this));
501 }
502
503 return fSampler;
504}
505
msarette6dd0042015-10-09 11:07:34 -0700506int SkBmpRLECodec::setSampleX(int sampleX){
scroggoe7fc14b2015-10-02 13:14:46 -0700507 fSampleX = sampleX;
508 return get_scaled_dimension(this->getInfo().width(), sampleX);
509}