blob: 32580c3fa5155eecd2ade1d66d96c5ead2fc94ac [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"
msarett4ab9d5f2015-08-06 15:34:42 -070011#include "SkStream.h"
12
13/*
msarett4ab9d5f2015-08-06 15:34:42 -070014 * Creates an instance of the decoder
15 * Called only by NewFromStream
16 */
msarett5406d6f2015-08-31 06:55:13 -070017SkBmpRLECodec::SkBmpRLECodec(const SkImageInfo& info, SkStream* stream,
18 uint16_t bitsPerPixel, uint32_t numColors,
19 uint32_t bytesPerColor, uint32_t offset,
scroggo46c57472015-09-30 08:57:13 -070020 SkCodec::SkScanlineOrder rowOrder,
halcanary385fe4d2015-08-26 13:07:48 -070021 size_t RLEBytes)
msarett4ab9d5f2015-08-06 15:34:42 -070022 : INHERITED(info, stream, bitsPerPixel, rowOrder)
halcanary96fcdcc2015-08-27 07:41:13 -070023 , fColorTable(nullptr)
msarett4ab9d5f2015-08-06 15:34:42 -070024 , fNumColors(this->computeNumColors(numColors))
25 , fBytesPerColor(bytesPerColor)
26 , fOffset(offset)
halcanary385fe4d2015-08-26 13:07:48 -070027 , fStreamBuffer(new uint8_t[RLEBytes])
msarett4ab9d5f2015-08-06 15:34:42 -070028 , fRLEBytes(RLEBytes)
msarett5406d6f2015-08-31 06:55:13 -070029 , fCurrRLEByte(0)
30 , fSampleX(1)
31{}
msarett4ab9d5f2015-08-06 15:34:42 -070032
33/*
34 * Initiates the bitmap decode
35 */
36SkCodec::Result SkBmpRLECodec::onGetPixels(const SkImageInfo& dstInfo,
msarett5406d6f2015-08-31 06:55:13 -070037 void* dst, size_t dstRowBytes,
38 const Options& opts,
39 SkPMColor* inputColorPtr,
msarette6dd0042015-10-09 11:07:34 -070040 int* inputColorCount,
41 int* rowsDecoded) {
msarett4ab9d5f2015-08-06 15:34:42 -070042 if (opts.fSubset) {
43 // Subsets are not supported.
44 return kUnimplemented;
45 }
msarett4ab9d5f2015-08-06 15:34:42 -070046 if (!conversion_possible(dstInfo, this->getInfo())) {
47 SkCodecPrintf("Error: cannot convert input type to output type.\n");
48 return kInvalidConversion;
49 }
50
msarett5406d6f2015-08-31 06:55:13 -070051 Result result = this->prepareToDecode(dstInfo, opts, inputColorPtr, inputColorCount);
52 if (kSuccess != result) {
53 return result;
msarett4ab9d5f2015-08-06 15:34:42 -070054 }
55
56 // Perform the decode
msarettf724b992015-10-15 06:41:06 -070057 int rows = this->decodeRows(dstInfo, dst, dstRowBytes, opts);
msarette6dd0042015-10-09 11:07:34 -070058 if (rows != dstInfo.height()) {
59 // We set rowsDecoded equal to the height because the background has already
60 // been filled. RLE encodings sometimes skip pixels, so we always start by
61 // filling the background.
62 *rowsDecoded = dstInfo.height();
63 return kIncompleteInput;
64 }
65
66 return kSuccess;
msarett4ab9d5f2015-08-06 15:34:42 -070067}
68
69/*
70 * Process the color table for the bmp input
71 */
72 bool SkBmpRLECodec::createColorTable(int* numColors) {
73 // Allocate memory for color table
74 uint32_t colorBytes = 0;
75 SkPMColor colorTable[256];
76 if (this->bitsPerPixel() <= 8) {
77 // Inform the caller of the number of colors
78 uint32_t maxColors = 1 << this->bitsPerPixel();
halcanary96fcdcc2015-08-27 07:41:13 -070079 if (nullptr != numColors) {
msarett4ab9d5f2015-08-06 15:34:42 -070080 // We set the number of colors to maxColors in order to ensure
81 // safe memory accesses. Otherwise, an invalid pixel could
82 // access memory outside of our color table array.
83 *numColors = maxColors;
84 }
85
86 // Read the color table from the stream
87 colorBytes = fNumColors * fBytesPerColor;
halcanary385fe4d2015-08-26 13:07:48 -070088 SkAutoTDeleteArray<uint8_t> cBuffer(new uint8_t[colorBytes]);
msarett4ab9d5f2015-08-06 15:34:42 -070089 if (stream()->read(cBuffer.get(), colorBytes) != colorBytes) {
90 SkCodecPrintf("Error: unable to read color table.\n");
91 return false;
92 }
93
94 // Fill in the color table
95 uint32_t i = 0;
96 for (; i < fNumColors; i++) {
97 uint8_t blue = get_byte(cBuffer.get(), i*fBytesPerColor);
98 uint8_t green = get_byte(cBuffer.get(), i*fBytesPerColor + 1);
99 uint8_t red = get_byte(cBuffer.get(), i*fBytesPerColor + 2);
100 colorTable[i] = SkPackARGB32NoCheck(0xFF, red, green, blue);
101 }
102
103 // To avoid segmentation faults on bad pixel data, fill the end of the
104 // color table with black. This is the same the behavior as the
105 // chromium decoder.
106 for (; i < maxColors; i++) {
107 colorTable[i] = SkPackARGB32NoCheck(0xFF, 0, 0, 0);
108 }
109
110 // Set the color table
halcanary385fe4d2015-08-26 13:07:48 -0700111 fColorTable.reset(new SkColorTable(colorTable, maxColors));
msarett4ab9d5f2015-08-06 15:34:42 -0700112 }
113
114 // Check that we have not read past the pixel array offset
115 if(fOffset < colorBytes) {
116 // This may occur on OS 2.1 and other old versions where the color
117 // table defaults to max size, and the bmp tries to use a smaller
118 // color table. This is invalid, and our decision is to indicate
119 // an error, rather than try to guess the intended size of the
120 // color table.
121 SkCodecPrintf("Error: pixel data offset less than color table size.\n");
122 return false;
123 }
124
125 // After reading the color table, skip to the start of the pixel array
126 if (stream()->skip(fOffset - colorBytes) != fOffset - colorBytes) {
127 SkCodecPrintf("Error: unable to skip to image data.\n");
128 return false;
129 }
130
131 // Return true on success
132 return true;
133}
134
135bool SkBmpRLECodec::initializeStreamBuffer() {
136 // Setup a buffer to contain the full input stream
msarett5406d6f2015-08-31 06:55:13 -0700137 // TODO (msarett): I'm not sure it is smart or optimal to trust fRLEBytes (read from header)
138 // as the size of our buffer. First of all, the decode fails if fRLEBytes is
139 // corrupt (negative, zero, or small) when we might be able to decode
140 // successfully with a fixed size buffer. Additionally, we would save memory
141 // using a fixed size buffer if the RLE encoding is large. On the other hand,
142 // we may also waste memory with a fixed size buffer. And determining a
143 // minimum size for our buffer would depend on the image width (so it's not
144 // really "fixed" size), and we may end up allocating a buffer that is
145 // generally larger than the average encoded size anyway.
msarett4ab9d5f2015-08-06 15:34:42 -0700146 size_t totalBytes = this->stream()->read(fStreamBuffer.get(), fRLEBytes);
147 if (totalBytes < fRLEBytes) {
148 fRLEBytes = totalBytes;
149 SkCodecPrintf("Warning: incomplete RLE file.\n");
150 }
151 if (fRLEBytes == 0) {
152 SkCodecPrintf("Error: could not read RLE image data.\n");
153 return false;
154 }
msarett5406d6f2015-08-31 06:55:13 -0700155 fCurrRLEByte = 0;
msarett4ab9d5f2015-08-06 15:34:42 -0700156 return true;
157}
158
159/*
msarettd0375bc2015-08-12 08:08:56 -0700160 * Before signalling kIncompleteInput, we should attempt to load the
161 * stream buffer with additional data.
162 *
163 * @return the number of bytes remaining in the stream buffer after
164 * attempting to read more bytes from the stream
165 */
166size_t SkBmpRLECodec::checkForMoreData() {
167 const size_t remainingBytes = fRLEBytes - fCurrRLEByte;
168 uint8_t* buffer = fStreamBuffer.get();
169
170 // We will be reusing the same buffer, starting over from the beginning.
171 // Move any remaining bytes to the start of the buffer.
172 // We use memmove() instead of memcpy() because there is risk that the dst
173 // and src memory will overlap in corrupt images.
174 memmove(buffer, SkTAddOffset<uint8_t>(buffer, fCurrRLEByte), remainingBytes);
175
176 // Adjust the buffer ptr to the start of the unfilled data.
177 buffer += remainingBytes;
178
179 // Try to read additional bytes from the stream. There are fCurrRLEByte
180 // bytes of additional space remaining in the buffer, assuming that we
181 // have already copied remainingBytes to the start of the buffer.
182 size_t additionalBytes = this->stream()->read(buffer, fCurrRLEByte);
183
184 // Update counters and return the number of bytes we currently have
185 // available. We are at the start of the buffer again.
186 fCurrRLEByte = 0;
187 // If we were unable to fill the buffer, fRLEBytes is no longer equal to
188 // the size of the buffer. There will be unused space at the end. This
189 // should be fine, given that there are no more bytes in the stream.
190 fRLEBytes = remainingBytes + additionalBytes;
191 return fRLEBytes;
192}
193
194/*
msarett4ab9d5f2015-08-06 15:34:42 -0700195 * Set an RLE pixel using the color table
196 */
197void SkBmpRLECodec::setPixel(void* dst, size_t dstRowBytes,
198 const SkImageInfo& dstInfo, uint32_t x, uint32_t y,
199 uint8_t index) {
msarett5406d6f2015-08-31 06:55:13 -0700200 if (is_coord_necessary(x, fSampleX, dstInfo.width())) {
201 // Set the row
202 uint32_t row = this->getDstRow(y, dstInfo.height());
msarett4ab9d5f2015-08-06 15:34:42 -0700203
msarett5406d6f2015-08-31 06:55:13 -0700204 // Set the pixel based on destination color type
205 const int dstX = get_dst_coord(x, fSampleX);
206 switch (dstInfo.colorType()) {
207 case kN32_SkColorType: {
208 SkPMColor* dstRow = SkTAddOffset<SkPMColor>(dst, row * (int) dstRowBytes);
209 dstRow[dstX] = fColorTable->operator[](index);
210 break;
211 }
212 case kRGB_565_SkColorType: {
213 uint16_t* dstRow = SkTAddOffset<uint16_t>(dst, row * (int) dstRowBytes);
214 dstRow[dstX] = SkPixel32ToPixel16(fColorTable->operator[](index));
215 break;
216 }
217 default:
218 // This case should not be reached. We should catch an invalid
219 // color type when we check that the conversion is possible.
220 SkASSERT(false);
221 break;
msarett4ab9d5f2015-08-06 15:34:42 -0700222 }
msarett4ab9d5f2015-08-06 15:34:42 -0700223 }
224}
225
226/*
227 * Set an RLE pixel from R, G, B values
228 */
229void SkBmpRLECodec::setRGBPixel(void* dst, size_t dstRowBytes,
230 const SkImageInfo& dstInfo, uint32_t x,
231 uint32_t y, uint8_t red, uint8_t green,
232 uint8_t blue) {
msarett5406d6f2015-08-31 06:55:13 -0700233 if (is_coord_necessary(x, fSampleX, dstInfo.width())) {
234 // Set the row
235 uint32_t row = this->getDstRow(y, dstInfo.height());
236
237 // Set the pixel based on destination color type
238 const int dstX = get_dst_coord(x, fSampleX);
239 switch (dstInfo.colorType()) {
240 case kN32_SkColorType: {
241 SkPMColor* dstRow = SkTAddOffset<SkPMColor>(dst, row * (int) dstRowBytes);
242 dstRow[dstX] = SkPackARGB32NoCheck(0xFF, red, green, blue);
243 break;
244 }
245 case kRGB_565_SkColorType: {
246 uint16_t* dstRow = SkTAddOffset<uint16_t>(dst, row * (int) dstRowBytes);
247 dstRow[dstX] = SkPack888ToRGB16(red, green, blue);
248 break;
249 }
250 default:
251 // This case should not be reached. We should catch an invalid
252 // color type when we check that the conversion is possible.
253 SkASSERT(false);
254 break;
255 }
256 }
257}
258
259SkCodec::Result SkBmpRLECodec::prepareToDecode(const SkImageInfo& dstInfo,
260 const SkCodec::Options& options, SkPMColor inputColorPtr[], int* inputColorCount) {
msarettfdb47572015-10-13 12:50:14 -0700261 // FIXME: Support subsets for scanline decodes.
262 if (options.fSubset) {
263 // Subsets are not supported.
264 return kUnimplemented;
265 }
266
scroggoe7fc14b2015-10-02 13:14:46 -0700267 // Reset fSampleX. If it needs to be a value other than 1, it will get modified by
268 // the sampler.
269 fSampleX = 1;
msarett5406d6f2015-08-31 06:55:13 -0700270 // Create the color table if necessary and prepare the stream for decode
271 // Note that if it is non-NULL, inputColorCount will be modified
272 if (!this->createColorTable(inputColorCount)) {
273 SkCodecPrintf("Error: could not create color table.\n");
274 return SkCodec::kInvalidInput;
msarett4ab9d5f2015-08-06 15:34:42 -0700275 }
276
msarett5406d6f2015-08-31 06:55:13 -0700277 // Copy the color table to the client if necessary
278 copy_color_table(dstInfo, this->fColorTable, inputColorPtr, inputColorCount);
279
280 // Initialize a buffer for encoded RLE data
281 if (!this->initializeStreamBuffer()) {
282 SkCodecPrintf("Error: cannot initialize stream buffer.\n");
283 return SkCodec::kInvalidConversion;
msarett4ab9d5f2015-08-06 15:34:42 -0700284 }
msarett5406d6f2015-08-31 06:55:13 -0700285
msarett5406d6f2015-08-31 06:55:13 -0700286 return SkCodec::kSuccess;
msarett4ab9d5f2015-08-06 15:34:42 -0700287}
288
289/*
290 * Performs the bitmap decoding for RLE input format
291 * RLE decoding is performed all at once, rather than a one row at a time
292 */
msarette6dd0042015-10-09 11:07:34 -0700293int SkBmpRLECodec::decodeRows(const SkImageInfo& info, void* dst, size_t dstRowBytes,
294 const Options& opts) {
msarett4ab9d5f2015-08-06 15:34:42 -0700295 // Set RLE flags
296 static const uint8_t RLE_ESCAPE = 0;
297 static const uint8_t RLE_EOL = 0;
298 static const uint8_t RLE_EOF = 1;
299 static const uint8_t RLE_DELTA = 2;
300
301 // Set constant values
msarett5406d6f2015-08-31 06:55:13 -0700302 const int width = this->getInfo().width();
scroggoe7fc14b2015-10-02 13:14:46 -0700303 const int height = info.height();
304
305 // Account for sampling.
306 SkImageInfo dstInfo = info.makeWH(get_scaled_dimension(width, fSampleX), height);
msarett4ab9d5f2015-08-06 15:34:42 -0700307
308 // Destination parameters
309 int x = 0;
310 int y = 0;
311
312 // Set the background as transparent. Then, if the RLE code skips pixels,
313 // the skipped pixels will be transparent.
314 // Because of the need for transparent pixels, kN32 is the only color
315 // type that makes sense for the destination format.
316 SkASSERT(kN32_SkColorType == dstInfo.colorType());
msarette6dd0042015-10-09 11:07:34 -0700317 SkSampler::Fill(dstInfo, dst, dstRowBytes, SK_ColorTRANSPARENT, opts.fZeroInitialized);
msarett4ab9d5f2015-08-06 15:34:42 -0700318
319 while (true) {
320 // If we have reached a row that is beyond the requested height, we have
321 // succeeded.
322 if (y >= height) {
msarette6dd0042015-10-09 11:07:34 -0700323 // It would be better to check for the EOF marker before indicating
msarett4ab9d5f2015-08-06 15:34:42 -0700324 // success, but we may be performing a scanline decode, which
msarette6dd0042015-10-09 11:07:34 -0700325 // would require us to stop before decoding the full height.
326 return height;
msarett4ab9d5f2015-08-06 15:34:42 -0700327 }
328
329 // Every entry takes at least two bytes
330 if ((int) fRLEBytes - fCurrRLEByte < 2) {
msarettd0375bc2015-08-12 08:08:56 -0700331 SkCodecPrintf("Warning: might be incomplete RLE input.\n");
332 if (this->checkForMoreData() < 2) {
msarette6dd0042015-10-09 11:07:34 -0700333 return y;
msarettd0375bc2015-08-12 08:08:56 -0700334 }
msarett4ab9d5f2015-08-06 15:34:42 -0700335 }
336
337 // Read the next two bytes. These bytes have different meanings
338 // depending on their values. In the first interpretation, the first
339 // byte is an escape flag and the second byte indicates what special
340 // task to perform.
341 const uint8_t flag = fStreamBuffer.get()[fCurrRLEByte++];
342 const uint8_t task = fStreamBuffer.get()[fCurrRLEByte++];
343
344 // Perform decoding
345 if (RLE_ESCAPE == flag) {
346 switch (task) {
347 case RLE_EOL:
348 x = 0;
349 y++;
350 break;
351 case RLE_EOF:
352 return kSuccess;
353 case RLE_DELTA: {
354 // Two bytes are needed to specify delta
355 if ((int) fRLEBytes - fCurrRLEByte < 2) {
msarettd0375bc2015-08-12 08:08:56 -0700356 SkCodecPrintf("Warning: might be incomplete RLE input.\n");
357 if (this->checkForMoreData() < 2) {
msarette6dd0042015-10-09 11:07:34 -0700358 return y;
msarettd0375bc2015-08-12 08:08:56 -0700359 }
msarett4ab9d5f2015-08-06 15:34:42 -0700360 }
361 // Modify x and y
362 const uint8_t dx = fStreamBuffer.get()[fCurrRLEByte++];
363 const uint8_t dy = fStreamBuffer.get()[fCurrRLEByte++];
364 x += dx;
365 y += dy;
366 if (x > width || y > height) {
msarettd0375bc2015-08-12 08:08:56 -0700367 SkCodecPrintf("Warning: invalid RLE input.\n");
msarette6dd0042015-10-09 11:07:34 -0700368 return y - dy;
msarett4ab9d5f2015-08-06 15:34:42 -0700369 }
370 break;
371 }
372 default: {
373 // If task does not match any of the above signals, it
374 // indicates that we have a sequence of non-RLE pixels.
375 // Furthermore, the value of task is equal to the number
376 // of pixels to interpret.
377 uint8_t numPixels = task;
378 const size_t rowBytes = compute_row_bytes(numPixels,
379 this->bitsPerPixel());
380 // Abort if setting numPixels moves us off the edge of the
msarettd0375bc2015-08-12 08:08:56 -0700381 // image.
382 if (x + numPixels > width) {
383 SkCodecPrintf("Warning: invalid RLE input.\n");
msarette6dd0042015-10-09 11:07:34 -0700384 return y;
msarettd0375bc2015-08-12 08:08:56 -0700385 }
386 // Also abort if there are not enough bytes
msarett4ab9d5f2015-08-06 15:34:42 -0700387 // remaining in the stream to set numPixels.
msarettd0375bc2015-08-12 08:08:56 -0700388 if ((int) fRLEBytes - fCurrRLEByte < SkAlign2(rowBytes)) {
389 SkCodecPrintf("Warning: might be incomplete RLE input.\n");
390 if (this->checkForMoreData() < SkAlign2(rowBytes)) {
msarette6dd0042015-10-09 11:07:34 -0700391 return y;
msarettd0375bc2015-08-12 08:08:56 -0700392 }
msarett4ab9d5f2015-08-06 15:34:42 -0700393 }
394 // Set numPixels number of pixels
395 while (numPixels > 0) {
396 switch(this->bitsPerPixel()) {
397 case 4: {
398 SkASSERT(fCurrRLEByte < fRLEBytes);
399 uint8_t val = fStreamBuffer.get()[fCurrRLEByte++];
400 setPixel(dst, dstRowBytes, dstInfo, x++,
401 y, val >> 4);
402 numPixels--;
403 if (numPixels != 0) {
404 setPixel(dst, dstRowBytes, dstInfo,
405 x++, y, val & 0xF);
406 numPixels--;
407 }
408 break;
409 }
410 case 8:
411 SkASSERT(fCurrRLEByte < fRLEBytes);
412 setPixel(dst, dstRowBytes, dstInfo, x++,
413 y, fStreamBuffer.get()[fCurrRLEByte++]);
414 numPixels--;
415 break;
416 case 24: {
417 SkASSERT(fCurrRLEByte + 2 < fRLEBytes);
418 uint8_t blue = fStreamBuffer.get()[fCurrRLEByte++];
419 uint8_t green = fStreamBuffer.get()[fCurrRLEByte++];
420 uint8_t red = fStreamBuffer.get()[fCurrRLEByte++];
421 setRGBPixel(dst, dstRowBytes, dstInfo,
422 x++, y, red, green, blue);
423 numPixels--;
424 }
425 default:
426 SkASSERT(false);
msarette6dd0042015-10-09 11:07:34 -0700427 return y;
msarett4ab9d5f2015-08-06 15:34:42 -0700428 }
429 }
430 // Skip a byte if necessary to maintain alignment
431 if (!SkIsAlign2(rowBytes)) {
432 fCurrRLEByte++;
433 }
434 break;
435 }
436 }
437 } else {
438 // If the first byte read is not a flag, it indicates the number of
439 // pixels to set in RLE mode.
440 const uint8_t numPixels = flag;
441 const int endX = SkTMin<int>(x + numPixels, width);
442
443 if (24 == this->bitsPerPixel()) {
444 // In RLE24, the second byte read is part of the pixel color.
445 // There are two more required bytes to finish encoding the
446 // color.
447 if ((int) fRLEBytes - fCurrRLEByte < 2) {
msarettd0375bc2015-08-12 08:08:56 -0700448 SkCodecPrintf("Warning: might be incomplete RLE input.\n");
449 if (this->checkForMoreData() < 2) {
msarette6dd0042015-10-09 11:07:34 -0700450 return y;
msarettd0375bc2015-08-12 08:08:56 -0700451 }
msarett4ab9d5f2015-08-06 15:34:42 -0700452 }
453
454 // Fill the pixels up to endX with the specified color
455 uint8_t blue = task;
456 uint8_t green = fStreamBuffer.get()[fCurrRLEByte++];
457 uint8_t red = fStreamBuffer.get()[fCurrRLEByte++];
458 while (x < endX) {
bungeman0153dea2015-08-27 16:43:42 -0700459 setRGBPixel(dst, dstRowBytes, dstInfo, x++, y, red, green, blue);
msarett4ab9d5f2015-08-06 15:34:42 -0700460 }
461 } else {
462 // In RLE8 or RLE4, the second byte read gives the index in the
463 // color table to look up the pixel color.
464 // RLE8 has one color index that gets repeated
465 // RLE4 has two color indexes in the upper and lower 4 bits of
466 // the bytes, which are alternated
467 uint8_t indices[2] = { task, task };
468 if (4 == this->bitsPerPixel()) {
469 indices[0] >>= 4;
470 indices[1] &= 0xf;
471 }
472
473 // Set the indicated number of pixels
474 for (int which = 0; x < endX; x++) {
bungeman0153dea2015-08-27 16:43:42 -0700475 setPixel(dst, dstRowBytes, dstInfo, x, y, indices[which]);
msarett4ab9d5f2015-08-06 15:34:42 -0700476 which = !which;
477 }
478 }
479 }
480 }
481}
scroggoe7fc14b2015-10-02 13:14:46 -0700482
msarette6dd0042015-10-09 11:07:34 -0700483// FIXME: Make SkBmpRLECodec have no knowledge of sampling.
484// Or it should do all sampling natively.
485// It currently is a hybrid that needs to know what SkScaledCodec is doing.
scroggoe7fc14b2015-10-02 13:14:46 -0700486class SkBmpRLESampler : public SkSampler {
487public:
488 SkBmpRLESampler(SkBmpRLECodec* codec)
489 : fCodec(codec)
490 {
491 SkASSERT(fCodec);
492 }
493
494private:
msarette6dd0042015-10-09 11:07:34 -0700495 int onSetSampleX(int sampleX) override {
scroggoe7fc14b2015-10-02 13:14:46 -0700496 return fCodec->setSampleX(sampleX);
497 }
498
499 // Unowned pointer. fCodec will delete this class in its destructor.
500 SkBmpRLECodec* fCodec;
501};
502
msarette6dd0042015-10-09 11:07:34 -0700503SkSampler* SkBmpRLECodec::getSampler(bool createIfNecessary) {
504 if (!fSampler && createIfNecessary) {
scroggoe7fc14b2015-10-02 13:14:46 -0700505 fSampler.reset(new SkBmpRLESampler(this));
506 }
507
508 return fSampler;
509}
510
msarette6dd0042015-10-09 11:07:34 -0700511int SkBmpRLECodec::setSampleX(int sampleX){
scroggoe7fc14b2015-10-02 13:14:46 -0700512 fSampleX = sampleX;
513 return get_scaled_dimension(this->getInfo().width(), sampleX);
514}