Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(8)

Side by Side Diff: src/codec/SkBmpCodec.cpp

Issue 1820283002: Refactor SkBmpCodec (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: Created 4 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | src/codec/SkBmpStandardCodec.cpp » ('j') | src/codec/SkBmpStandardCodec.cpp » ('J')
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 /* 1 /*
2 * Copyright 2015 Google Inc. 2 * Copyright 2015 Google Inc.
3 * 3 *
4 * Use of this source code is governed by a BSD-style license that can be 4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file. 5 * found in the LICENSE file.
6 */ 6 */
7 7
8 #include "SkBmpCodec.h" 8 #include "SkBmpCodec.h"
9 #include "SkBmpMaskCodec.h" 9 #include "SkBmpMaskCodec.h"
10 #include "SkBmpRLECodec.h" 10 #include "SkBmpRLECodec.h"
(...skipping 268 matching lines...) Expand 10 before | Expand all | Expand 10 after
279 // Create mask struct 279 // Create mask struct
280 SkMasks::InputMasks inputMasks; 280 SkMasks::InputMasks inputMasks;
281 memset(&inputMasks, 0, sizeof(SkMasks::InputMasks)); 281 memset(&inputMasks, 0, sizeof(SkMasks::InputMasks));
282 282
283 // Determine the input compression format and set bit masks if necessary 283 // Determine the input compression format and set bit masks if necessary
284 uint32_t maskBytes = 0; 284 uint32_t maskBytes = 0;
285 BmpInputFormat inputFormat = kUnknown_BmpInputFormat; 285 BmpInputFormat inputFormat = kUnknown_BmpInputFormat;
286 switch (compression) { 286 switch (compression) {
287 case kNone_BmpCompressionMethod: 287 case kNone_BmpCompressionMethod:
288 inputFormat = kStandard_BmpInputFormat; 288 inputFormat = kStandard_BmpInputFormat;
289
290 // In addition to more standard pixel compression formats, bmp suppo rts
291 // the use of bit masks to determine pixel components. The standard
292 // format for representing 16-bit colors is 555 (XRRRRRGGGGGBBBBB),
293 // which does not map well to any Skia color formats. For this reas on,
294 // we will always enable mask mode with 16 bits per pixel.
295 if (16 == bitsPerPixel) {
296 inputMasks.red = 0x7C00;
297 inputMasks.green = 0x03E0;
298 inputMasks.blue = 0x001F;
299 inputFormat = kBitMask_BmpInputFormat;
300 }
289 break; 301 break;
290 case k8BitRLE_BmpCompressionMethod: 302 case k8BitRLE_BmpCompressionMethod:
291 if (bitsPerPixel != 8) { 303 if (bitsPerPixel != 8) {
292 SkCodecPrintf("Warning: correcting invalid bitmap format.\n"); 304 SkCodecPrintf("Warning: correcting invalid bitmap format.\n");
293 bitsPerPixel = 8; 305 bitsPerPixel = 8;
294 } 306 }
295 inputFormat = kRLE_BmpInputFormat; 307 inputFormat = kRLE_BmpInputFormat;
296 break; 308 break;
297 case k4BitRLE_BmpCompressionMethod: 309 case k4BitRLE_BmpCompressionMethod:
298 if (bitsPerPixel != 4) { 310 if (bitsPerPixel != 4) {
(...skipping 25 matching lines...) Expand all
324 case kInfoV3_BmpHeaderType: 336 case kInfoV3_BmpHeaderType:
325 case kInfoV4_BmpHeaderType: 337 case kInfoV4_BmpHeaderType:
326 case kInfoV5_BmpHeaderType: 338 case kInfoV5_BmpHeaderType:
327 // Header types are matched based on size. If the header 339 // Header types are matched based on size. If the header
328 // is V2+, we are guaranteed to be able to read at least 340 // is V2+, we are guaranteed to be able to read at least
329 // this size. 341 // this size.
330 SkASSERT(infoBytesRemaining >= 48); 342 SkASSERT(infoBytesRemaining >= 48);
331 inputMasks.red = get_int(iBuffer.get(), 36); 343 inputMasks.red = get_int(iBuffer.get(), 36);
332 inputMasks.green = get_int(iBuffer.get(), 40); 344 inputMasks.green = get_int(iBuffer.get(), 40);
333 inputMasks.blue = get_int(iBuffer.get(), 44); 345 inputMasks.blue = get_int(iBuffer.get(), 44);
346
347 if (kInfoV2_BmpHeaderType == headerType ||
348 (kInfoV3_BmpHeaderType == headerType && !inIco)) {
349 break;
350 }
351
352 // V3+ bmp files introduce an alpha mask and allow the creat or of the image
353 // to use the alpha channels. However, many of these images leave the
354 // alpha channel blank and expect to be rendered as opaque. This is the
355 // case for almost all V3 images, so we ignore the alpha mas k. For V4+
356 // images in kMask mode, we will use the alpha mask. Additi onally, V3
357 // bmp-in-ico expect us to use the alpha mask.
358 //
359 // skbug.com/4116: We should perhaps also apply the alpha ma sk in kStandard
360 // mode. We just haven't seen any images th at expect this
361 // behavior.
362 //
363 // Header types are matched based on size. If the header is
364 // V3+, we are guaranteed to be able to read at least this s ize.
365 SkASSERT(infoBytesRemaining > 52);
366 inputMasks.alpha = get_int(iBuffer.get(), 48);
334 break; 367 break;
335 case kOS2VX_BmpHeaderType: 368 case kOS2VX_BmpHeaderType:
336 // TODO: Decide if we intend to support this. 369 // TODO: Decide if we intend to support this.
337 // It is unsupported in the previous version and 370 // It is unsupported in the previous version and
338 // in chromium. I have not come across a test case 371 // in chromium. I have not come across a test case
339 // that uses this format. 372 // that uses this format.
340 SkCodecPrintf("Error: huffman format unsupported.\n"); 373 SkCodecPrintf("Error: huffman format unsupported.\n");
341 return false; 374 return false;
342 default: 375 default:
343 SkCodecPrintf("Error: invalid bmp bit masks header.\n"); 376 SkCodecPrintf("Error: invalid bmp bit masks header.\n");
(...skipping 15 matching lines...) Expand all
359 case kCMYK_BmpCompressionMethod: 392 case kCMYK_BmpCompressionMethod:
360 case kCMYK8BitRLE_BmpCompressionMethod: 393 case kCMYK8BitRLE_BmpCompressionMethod:
361 case kCMYK4BitRLE_BmpCompressionMethod: 394 case kCMYK4BitRLE_BmpCompressionMethod:
362 // TODO: Same as above. 395 // TODO: Same as above.
363 SkCodecPrintf("Error: CMYK not supported for bitmap decoding.\n"); 396 SkCodecPrintf("Error: CMYK not supported for bitmap decoding.\n");
364 return false; 397 return false;
365 default: 398 default:
366 SkCodecPrintf("Error: invalid format for bitmap decoding.\n"); 399 SkCodecPrintf("Error: invalid format for bitmap decoding.\n");
367 return false; 400 return false;
368 } 401 }
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.reset(); 402 iBuffer.reset();
396 403
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 404 // Calculate the number of bytes read so far
465 const uint32_t bytesRead = kBmpHeaderBytes + infoBytes + maskBytes; 405 const uint32_t bytesRead = kBmpHeaderBytes + infoBytes + maskBytes;
466 if (!inIco && offset < bytesRead) { 406 if (!inIco && offset < bytesRead) {
467 // TODO (msarett): Do we really want to fail if the offset in the header is invalid? 407 // 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? 408 // 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? 409 // Maybe we don't want to try to decode corrupt images?
470 SkCodecPrintf("Error: pixel data offset less than header size.\n"); 410 SkCodecPrintf("Error: pixel data offset less than header size.\n");
471 return false; 411 return false;
472 } 412 }
473 413
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 414
484 if (codecOut) { 415
485 // BMPs-in-ICOs contain an alpha mask after the image which means we 416 switch (inputFormat) {
486 // cannot guarantee that an image is opaque, even if the bmp thinks 417 case kStandard_BmpInputFormat: {
487 // it is. 418 // BMPs-in-ICOs often contain an alpha mask after the image, which
488 bool isOpaque = kOpaque_SkAlphaType == alphaType; 419 // means we cannot guarantee that an image is opaque, even if the
489 if (inIco) { 420 // embedded bmp is opaque.
490 alphaType = kUnpremul_SkAlphaType; 421 // We use |isOpaque| to indicate if the BMP itself is opaque, but
422 // still need to recommend kUnpremul when it is contained in an ICO.
423 SkColorType colorType = kN32_SkColorType;
424 SkAlphaType alphaType = inIco ? kUnpremul_SkAlphaType : kOpaque_SkAl phaType;
425 bool isOpaque = true;
426 switch (bitsPerPixel) {
427 // Palette formats
428 case 1:
429 case 2:
430 case 4:
431 case 8:
432 // We cannot recommend a palette color type for ICOs because they
433 // may contain a transparency mask.
434 if (!inIco) {
435 colorType = kIndex_8_SkColorType;
436 }
437 break;
438 case 24:
439 case 32:
440 // 32-bit BMP-in-ICOs actually use the alpha channel in plac e of a
441 // transparency mask.
442 if (inIco) {
443 isOpaque = false;
444 }
445 break;
446 default:
447 SkCodecPrintf("Error: invalid input value for bits per pixel .\n");
448 return false;
449 }
450
451 if (codecOut) {
452 // We require streams to have a memory base for Bmp-in-Ico decod es.
453 SkASSERT(!inIco || nullptr != stream->getMemoryBase());
454
455 // Set the image info and create a codec.
456 const SkImageInfo imageInfo = SkImageInfo::Make(width, height, c olorType,
457 alphaType);
458 *codecOut = new SkBmpStandardCodec(imageInfo, stream, bitsPerPix el, numColors,
459 bytesPerColor, offset - bytesRead, rowOrder, isOpaque, i nIco);
460
461 }
462 return true;
491 } 463 }
492 464
493 // Set the image info 465 case kBitMask_BmpInputFormat: {
494 const SkImageInfo& imageInfo = SkImageInfo::Make(width, height, 466 // Bmp-in-Ico must be standard mode
495 colorType, alphaType); 467 if (inIco) {
468 SkCodecPrintf("Error: Icos may not use bit mask format.\n");
469 return false;
470 }
496 471
497 // Return the codec 472 switch (bitsPerPixel) {
498 switch (inputFormat) { 473 case 16:
499 case kStandard_BmpInputFormat: 474 case 24:
500 // We require streams to have a memory base for Bmp-in-Ico decod es. 475 case 32:
501 SkASSERT(!inIco || nullptr != stream->getMemoryBase()); 476 break;
502 *codecOut = new SkBmpStandardCodec(imageInfo, stream, bitsPerPix el, numColors, 477 default:
503 bytesPerColor, offset - bytesRead, rowOrder, isOpaque, i nIco); 478 SkCodecPrintf("Error: invalid input value for bits per pixel .\n");
504 return true; 479 return false;
505 case kBitMask_BmpInputFormat: 480 }
506 // Bmp-in-Ico must be standard mode 481
507 if (inIco) { 482 // Skip to the start of the pixel array.
508 SkCodecPrintf("Error: Icos may not use bit mask format.\n"); 483 // We can do this here because there is no color table to read
484 // in bit mask mode.
485 if (stream->skip(offset - bytesRead) != offset - bytesRead) {
486 SkCodecPrintf("Error: unable to skip to image data.\n");
487 return false;
488 }
489
490 if (codecOut) {
491 // Check that input bit masks are valid and create the masks obj ect
492 SkAutoTDelete<SkMasks> masks(SkMasks::CreateMasks(inputMasks, bi tsPerPixel));
493 if (nullptr == masks) {
494 SkCodecPrintf("Error: invalid input masks.\n");
509 return false; 495 return false;
510 } 496 }
511 497
498 // Set the image info
499 SkAlphaType alphaType = masks->getAlphaMask() ? kUnpremul_SkAlph aType :
500 kOpaque_SkAlphaType;
501 const SkImageInfo imageInfo = SkImageInfo::Make(width, height, k N32_SkColorType,
502 alphaType);
512 *codecOut = new SkBmpMaskCodec(imageInfo, stream, bitsPerPixel, masks.release(), 503 *codecOut = new SkBmpMaskCodec(imageInfo, stream, bitsPerPixel, masks.release(),
513 rowOrder); 504 rowOrder);
514 return true; 505 }
515 case kRLE_BmpInputFormat: 506 return true;
516 // Bmp-in-Ico must be standard mode 507 }
517 // When inIco is true, this line cannot be reached, since we 508
518 // require that RLE Bmps have a valid number of totalBytes, and 509 case kRLE_BmpInputFormat: {
519 // Icos skip the header that contains totalBytes. 510 // We should not reach this point without a valid value of bitsPerPi xel.
520 SkASSERT(!inIco); 511 SkASSERT(4 == bitsPerPixel || 8 == bitsPerPixel || 24 == bitsPerPixe l);
512
513 // Check for a valid number of total bytes when in RLE mode
514 if (totalBytes <= offset) {
515 SkCodecPrintf("Error: RLE requires valid input size.\n");
516 return false;
517 }
518 const size_t RLEBytes = totalBytes - offset;
519
520 // Bmp-in-Ico must be standard mode
521 // When inIco is true, this line cannot be reached, since we
522 // require that RLE Bmps have a valid number of totalBytes, and
523 // Icos skip the header that contains totalBytes.
524 SkASSERT(!inIco);
525
526 if (codecOut) {
527 // RLE inputs may skip pixels, leaving them as transparent. Thi s
528 // is uncommon, but we cannot be certain that an RLE bmp will be
529 // opaque.
530 const SkImageInfo imageInfo = SkImageInfo::Make(width, height, k N32_SkColorType,
531 kUnpremul_SkAlphaType);
521 *codecOut = new SkBmpRLECodec(imageInfo, stream, bitsPerPixel, n umColors, 532 *codecOut = new SkBmpRLECodec(imageInfo, stream, bitsPerPixel, n umColors,
522 bytesPerColor, offset - bytesRead, rowOrder, RLEBytes); 533 bytesPerColor, offset - bytesRead, rowOrder, RLEBytes);
523 return true; 534 }
524 default: 535 return true;
525 SkASSERT(false);
526 return false;
527 } 536 }
537 default:
538 SkASSERT(false);
539 return false;
528 } 540 }
529
530 return true;
531 } 541 }
532 542
533 /* 543 /*
534 * Creates a bmp decoder 544 * Creates a bmp decoder
535 * Reads enough of the stream to determine the image format 545 * Reads enough of the stream to determine the image format
536 */ 546 */
537 SkCodec* SkBmpCodec::NewFromStream(SkStream* stream, bool inIco) { 547 SkCodec* SkBmpCodec::NewFromStream(SkStream* stream, bool inIco) {
538 SkAutoTDelete<SkStream> streamDeleter(stream); 548 SkAutoTDelete<SkStream> streamDeleter(stream);
539 SkCodec* codec = nullptr; 549 SkCodec* codec = nullptr;
540 if (ReadHeader(stream, inIco, &codec)) { 550 if (ReadHeader(stream, inIco, &codec)) {
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
586 } 596 }
587 597
588 bool SkBmpCodec::skipRows(int count) { 598 bool SkBmpCodec::skipRows(int count) {
589 const size_t bytesToSkip = count * fSrcRowBytes; 599 const size_t bytesToSkip = count * fSrcRowBytes;
590 return this->stream()->skip(bytesToSkip) == bytesToSkip; 600 return this->stream()->skip(bytesToSkip) == bytesToSkip;
591 } 601 }
592 602
593 bool SkBmpCodec::onSkipScanlines(int count) { 603 bool SkBmpCodec::onSkipScanlines(int count) {
594 return this->skipRows(count); 604 return this->skipRows(count);
595 } 605 }
OLDNEW
« no previous file with comments | « no previous file | src/codec/SkBmpStandardCodec.cpp » ('j') | src/codec/SkBmpStandardCodec.cpp » ('J')

Powered by Google App Engine
This is Rietveld 408576698