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

Unified Diff: src/codec/SkCodec_libbmp.cpp

Issue 1013743003: Adding premul and 565 swizzles for bmp: (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: Clean up before public code review Created 5 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 side-by-side diff with in-line comments
Download patch
Index: src/codec/SkCodec_libbmp.cpp
diff --git a/src/codec/SkCodec_libbmp.cpp b/src/codec/SkCodec_libbmp.cpp
index 5b9691c087c84ae9674b29e3b6a5cb1279b9ac1b..ef97082d61853735904ddd2ae643903a282c92d8 100644
--- a/src/codec/SkCodec_libbmp.cpp
+++ b/src/codec/SkCodec_libbmp.cpp
@@ -18,9 +18,9 @@
*/
static bool conversion_possible(const SkImageInfo& dst,
const SkImageInfo& src) {
- // All of the swizzles convert to kN32
- // TODO: Update this when more swizzles are supported
- if (kN32_SkColorType != dst.colorType()) {
+ // Check for supported color types
+ if (kN32_SkColorType != dst.colorType() &&
+ kRGB_565_SkColorType != dst.colorType()) {
return false;
}
// Support the swizzle if the requested alpha type is the same as our guess
@@ -28,8 +28,9 @@ static bool conversion_possible(const SkImageInfo& dst,
if (src.alphaType() == dst.alphaType()) {
return true;
}
- // TODO: Support more swizzles, especially premul
- return false;
+
+ // Support premul as the destination when the source is unpremul
+ return premul_and_unpremul(dst.alphaType(), src.alphaType());
}
/*
@@ -392,51 +393,23 @@ SkCodec* SkBmpCodec::NewFromStream(SkStream* stream) {
return NULL;
}
- // Process the color table
- uint32_t colorBytes = 0;
- SkPMColor* colorTable = NULL;
- if (bitsPerPixel < 16) {
- // Verify the number of colors for the color table
- const uint32_t maxColors = 1 << bitsPerPixel;
- // Zero is a default for maxColors
- // Also set numColors to maxColors when input is too large
- if (numColors <= 0 || numColors > maxColors) {
- numColors = maxColors;
- }
- colorTable = SkNEW_ARRAY(SkPMColor, maxColors);
+ // Verify the number of colors in the color table
+ const uint32_t maxColors = 1 << bitsPerPixel;
scroggo 2015/03/17 13:27:22 Could you move all of this into SkBmpCodec's onGet
msarett 2015/03/17 16:54:05 Doing these checks in onGetPixels actually causes
scroggo 2015/03/17 20:02:58 I think we can get around this... How about this:
msarett 2015/03/18 13:02:52 Yes great! That's a much better way to do it.
+ // Zero is a default for maxColors
+ // Also set numColors to maxColors when input is too large
+ if (numColors <= 0 || numColors > maxColors) {
+ numColors = maxColors;
+ }
- // Construct the color table
+ // Account for the color table size
+ uint32_t colorBytes;
+ if (bitsPerPixel < 16) {
colorBytes = numColors * bytesPerColor;
- SkAutoTDeleteArray<uint8_t> cBuffer(SkNEW_ARRAY(uint8_t, colorBytes));
- if (stream->read(cBuffer.get(), colorBytes) != colorBytes) {
- SkDebugf("Error: unable to read color table.\n");
- return NULL;
- }
-
- // Fill in the color table (colors are stored unpremultiplied)
- uint32_t i = 0;
- for (; i < numColors; i++) {
- uint8_t blue = get_byte(cBuffer.get(), i*bytesPerColor);
- uint8_t green = get_byte(cBuffer.get(), i*bytesPerColor + 1);
- uint8_t red = get_byte(cBuffer.get(), i*bytesPerColor + 2);
- uint8_t alpha = 0xFF;
- if (kOpaque_SkAlphaType != alphaType) {
- alpha = (inputMasks.alpha >> 24) &
- get_byte(cBuffer.get(), i*bytesPerColor + 3);
- }
- // Store the unpremultiplied color
- colorTable[i] = SkPackARGB32NoCheck(alpha, red, green, blue);
- }
-
- // To avoid segmentation faults on bad pixel data, fill the end of the
- // color table with black. This is the same the behavior as the
- // chromium decoder.
- for (; i < maxColors; i++) {
- colorTable[i] = SkPackARGB32NoCheck(0xFF, 0, 0, 0);
- }
+ } else {
+ colorBytes = 0;
}
- // Ensure that the stream now points to the start of the pixel array
+ // Calculate the number of bytes read so far (including the color table)
uint32_t bytesRead = kBmpHeaderBytes + infoBytes + maskBytes + colorBytes;
// Check that we have not read past the pixel array offset
@@ -444,18 +417,11 @@ SkCodec* SkBmpCodec::NewFromStream(SkStream* stream) {
// This may occur on OS 2.1 and other old versions where the color
// table defaults to max size, and the bmp tries to use a smaller color
// table. This is invalid, and our decision is to indicate an error,
- // rather than try to guess the intended size of the color table and
- // rewind the stream to display the image.
+ // rather than try to guess the intended size of the color table.
SkDebugf("Error: pixel data offset less than header size.\n");
return NULL;
}
- // Skip to the start of the pixel array
- if (stream->skip(offset - bytesRead) != offset - bytesRead) {
- SkDebugf("Error: unable to skip to image data.\n");
- return NULL;
- }
-
// Remaining bytes is only used for RLE
const int remainingBytes = totalBytes - offset;
if (remainingBytes <= 0 && kRLE_BitmapInputFormat == inputFormat) {
@@ -465,13 +431,13 @@ SkCodec* SkBmpCodec::NewFromStream(SkStream* stream) {
// Return the codec
// We will use ImageInfo to store width, height, and alpha type. We will
- // choose kN32_SkColorType as the input color type because that is the
- // expected choice for a destination color type. In reality, the input
- // color type has many possible formats.
+ // set color type to kN32_SkColorType because that should be the default
+ // output.
const SkImageInfo& imageInfo = SkImageInfo::Make(width, height,
kN32_SkColorType, alphaType);
return SkNEW_ARGS(SkBmpCodec, (imageInfo, stream, bitsPerPixel,
- inputFormat, masks.detach(), colorTable,
+ inputFormat, masks.detach(), numColors,
+ bytesPerColor, offset - bytesRead,
rowOrder, remainingBytes));
}
@@ -483,14 +449,17 @@ SkCodec* SkBmpCodec::NewFromStream(SkStream* stream) {
*/
SkBmpCodec::SkBmpCodec(const SkImageInfo& info, SkStream* stream,
uint16_t bitsPerPixel, BitmapInputFormat inputFormat,
- SkMasks* masks, SkPMColor* colorTable,
- RowOrder rowOrder,
- const uint32_t remainingBytes)
+ SkMasks* masks, uint32_t numColors,
+ uint32_t bytesPerColor, uint32_t offset,
+ RowOrder rowOrder, uint32_t remainingBytes)
: INHERITED(info, stream)
, fBitsPerPixel(bitsPerPixel)
, fInputFormat(inputFormat)
, fMasks(masks)
- , fColorTable(colorTable)
+ , fColorTable(NULL)
+ , fNumColors(numColors)
+ , fBytesPerColor(bytesPerColor)
+ , fOffset(offset)
, fRowOrder(rowOrder)
, fRemainingBytes(remainingBytes)
{}
@@ -503,6 +472,7 @@ SkBmpCodec::SkBmpCodec(const SkImageInfo& info, SkStream* stream,
SkCodec::Result SkBmpCodec::onGetPixels(const SkImageInfo& dstInfo,
void* dst, size_t dstRowBytes,
SkPMColor*, int*) {
+ // Check for proper input and output formats
if (!this->rewindIfNeeded()) {
return kCouldNotRewind;
}
@@ -515,6 +485,13 @@ SkCodec::Result SkBmpCodec::onGetPixels(const SkImageInfo& dstInfo,
return kInvalidConversion;
}
+ // Create the color table if necessary and prepare the stream for decode
+ if (!createColorTable(dstInfo.alphaType())) {
+ SkDebugf("Error: could not create color table.\n");
+ return kInvalidInput;
+ }
+
+ // Perform the decode
switch (fInputFormat) {
case kBitMask_BitmapInputFormat:
return decodeMask(dstInfo, dst, dstRowBytes);
@@ -530,6 +507,78 @@ SkCodec::Result SkBmpCodec::onGetPixels(const SkImageInfo& dstInfo,
/*
*
+ * Process the color table for the bmp input
+ *
+ */
+ bool SkBmpCodec::createColorTable(const SkAlphaType alphaType) {
+ SkPMColor* colorTable = NULL;
+ if (fBitsPerPixel < 16) {
+ // Allocate memory for a color table
+ const uint32_t maxColors = 1 << fBitsPerPixel;
+ colorTable = SkNEW_ARRAY(SkPMColor, maxColors);
scroggo 2015/03/17 13:27:22 colorTable will leak if we return NULL below.
msarett 2015/03/17 16:54:06 Done.
+
+ // Read the color table from the stream
+ uint32_t colorBytes = fNumColors * fBytesPerColor;
+ SkAutoTDeleteArray<uint8_t> cBuffer(SkNEW_ARRAY(uint8_t, colorBytes));
+ if (stream()->read(cBuffer.get(), colorBytes) != colorBytes) {
+ SkDebugf("Error: unable to read color table.\n");
+ return NULL;
+ }
+
+ // Fill in the color table
+ uint32_t i = 0;
+ for (; i < fNumColors; i++) {
+ uint8_t blue = get_byte(cBuffer.get(), i*fBytesPerColor);
+ uint8_t green = get_byte(cBuffer.get(), i*fBytesPerColor + 1);
+ uint8_t red = get_byte(cBuffer.get(), i*fBytesPerColor + 2);
+ uint8_t alpha;
+ switch (alphaType) {
+ case kOpaque_SkAlphaType:
+ alpha = 0xFF;
+ colorTable[i] = SkPackARGB32NoCheck(alpha, red, green,
scroggo 2015/03/17 13:27:22 nit: no need to update the variable alpha. You can
msarett 2015/03/17 16:54:05 Done.
+ blue);
+ break;
+ case kUnpremul_SkAlphaType:
+ alpha = (fMasks->getAlphaMask() >> 24) &
+ get_byte(cBuffer.get(), i*fBytesPerColor + 3);
+ colorTable[i] = SkPackARGB32NoCheck(alpha, red, green,
+ blue);
+ break;
+ case kPremul_SkAlphaType:
+ alpha = (fMasks->getAlphaMask() >> 24) &
+ get_byte(cBuffer.get(), i*fBytesPerColor + 3);
+ colorTable[i] = SkPreMultiplyARGB(alpha, red, green, blue);
+ break;
+ default:
+ // This should not be reached because conversion possible
+ // should fail if the alpha type is not one of the above
+ // values.
+ SkASSERT(false);
+ break;
+ }
+ }
+
+ // To avoid segmentation faults on bad pixel data, fill the end of the
+ // color table with black. This is the same the behavior as the
+ // chromium decoder.
+ for (; i < maxColors; i++) {
+ colorTable[i] = SkPackARGB32NoCheck(0xFF, 0, 0, 0);
+ }
+ }
+
+ // After reading the color table, skip to the start of the pixel array
+ if (stream()->skip(fOffset) != fOffset) {
+ SkDebugf("Error: unable to skip to image data.\n");
+ return false;
+ }
+
+ // Set the color table and return true on success
+ fColorTable.reset(colorTable);
+ return true;
+}
+
+/*
+ *
* Performs the bitmap decoding for bit masks input format
*
*/
@@ -540,17 +589,22 @@ SkCodec::Result SkBmpCodec::decodeMask(const SkImageInfo& dstInfo,
const int height = dstInfo.height();
const size_t rowBytes = SkAlign4(compute_row_bytes(width, fBitsPerPixel));
- // Allocate space for a row buffer and a source for the swizzler
- SkAutoTDeleteArray<uint8_t> srcBuffer(SkNEW_ARRAY(uint8_t, rowBytes));
+ // Allocate a buffer large enough to hold the full input stream
+ uint8_t* srcBuffer(SkNEW_ARRAY(uint8_t, height*rowBytes));
msarett 2015/03/16 20:21:53 I know that it is preferred to use an SkAutoTDelet
scroggo 2015/03/17 13:27:22 You should definitely use some sort of auto delete
msarett 2015/03/17 16:54:06 Agreed, not sure what I was thinking.
+ uint8_t* srcRow = srcBuffer;
// Get the destination start row and delta
+ SkPMColor* dstStart;
SkPMColor* dstRow;
int delta;
if (kTopDown_RowOrder == fRowOrder) {
- dstRow = (SkPMColor*) dst;
+ dstStart = (SkPMColor*) dst;
+ dstRow = dstStart;
delta = (int) dstRowBytes;
} else {
- dstRow = (SkPMColor*) SkTAddOffset<void>(dst, (height-1) * dstRowBytes);
+ dstStart = (SkPMColor*) SkTAddOffset<void>(
+ dst, (height - 1) * dstRowBytes);
+ dstRow = dstStart;
delta = -((int) dstRowBytes);
}
@@ -562,31 +616,42 @@ SkCodec::Result SkBmpCodec::decodeMask(const SkImageInfo& dstInfo,
bool transparent = true;
for (int y = 0; y < height; y++) {
// Read a row of the input
- if (stream()->read(srcBuffer.get(), rowBytes) != rowBytes) {
+ if (stream()->read(srcRow, rowBytes) != rowBytes) {
SkDebugf("Warning: incomplete input stream.\n");
+ SkDELETE_ARRAY(srcBuffer);
return kIncompleteInput;
}
// Decode the row in destination format
- SkSwizzler::ResultAlpha r = swizzler->next(dstRow, srcBuffer.get());
+ SkSwizzler::ResultAlpha r = swizzler->next(dstRow, srcRow);
transparent &= SkSwizzler::IsTransparent(r);
// Move to the next row
dstRow = SkTAddOffset<SkPMColor>(dstRow, delta);
+ srcRow = SkTAddOffset<uint8_t>(srcRow, rowBytes);
scroggo 2015/03/17 13:27:22 How did this work before?
msarett 2015/03/17 16:54:05 We used to overwrite the same row buffer for each
scroggo 2015/03/17 20:02:58 Ah, of course!
}
// Some fully transparent bmp images are intended to be opaque. Here, we
// correct for this possibility.
- dstRow = (SkPMColor*) dst;
if (transparent) {
+ const SkImageInfo& newInfo = dstInfo.makeAlphaType(kOpaque_SkAlphaType);
scroggo 2015/03/17 13:27:22 nit: Maybe this should be opaqueInfo? Or maskInfo?
msarett 2015/03/17 16:54:06 Done.
+ SkMaskSwizzler* newSwizzler = SkMaskSwizzler::CreateMaskSwizzler(
scroggo 2015/03/17 13:27:22 This will leak.
scroggo 2015/03/17 13:27:22 nit: Maybe name this maskSwizzler?
msarett 2015/03/17 16:54:05 Yeah it looks like it. I think this is not the on
+ newInfo, fMasks, fBitsPerPixel);
msarett 2015/03/16 20:21:53 Increase indent
+ srcRow = srcBuffer;
+ dstRow = dstStart;
for (int y = 0; y < height; y++) {
- for (int x = 0; x < width; x++) {
- dstRow[x] |= 0xFF000000;
- }
- dstRow = SkTAddOffset<SkPMColor>(dstRow, dstRowBytes);
+ // Decode the row in new format
+ newSwizzler->next(dstRow, srcRow);
+
+ // Move to the next row
+ dstRow = SkTAddOffset<SkPMColor>(dstRow, delta);
+ srcRow = SkTAddOffset<uint8_t>(srcRow, rowBytes);
}
}
+ // Clean up memory
+ SkDELETE_ARRAY(srcBuffer);
+
// Finished decoding the entire image
return kSuccess;
}
@@ -596,13 +661,78 @@ SkCodec::Result SkBmpCodec::decodeMask(const SkImageInfo& dstInfo,
* Set an RLE pixel using the color table
*
*/
-void SkBmpCodec::setRLEPixel(SkPMColor* dst, size_t dstRowBytes, int height,
- uint32_t x, uint32_t y, uint8_t index) {
+void SkBmpCodec::setRLEPixel(SkPMColor* dst, size_t dstRowBytes,
+ const SkImageInfo& dstInfo, uint32_t x, uint32_t y,
+ uint8_t index) {
+ // Set the row
+ int height = dstInfo.height();
+ int row;
+ if (kBottomUp_RowOrder == fRowOrder) {
+ row = height - y - 1;
+ } else {
+ row = y;
+ }
+
+ // Set the pixel based on destination color type
+ switch (dstInfo.colorType()) {
+ case kN32_SkColorType: {
+ SkPMColor* dstRow = SkTAddOffset<SkPMColor>(dst,
+ row * (int) dstRowBytes);
+ dstRow[x] = fColorTable.get()[index];
+ break;
+ }
+ case kRGB_565_SkColorType: {
+ uint16_t* dstRow = SkTAddOffset<uint16_t>(dst,
+ row * (int) dstRowBytes);
+ dstRow[x] = SkPixel32ToPixel16(fColorTable.get()[index]);
+ break;
+ }
+ default:
+ // This case should not be reached. We should catch an invalid
+ // color type when we check that the conversion is possible.
+ SkASSERT(false);
+ break;
+ }
+}
+
+/*
+ *
+ * Set an RLE pixel from R, G, B values
+ *
+ */
+void SkBmpCodec::setRLE24Pixel(SkPMColor* dst, size_t dstRowBytes,
+ const SkImageInfo& dstInfo, uint32_t x,
+ uint32_t y, uint8_t red, uint8_t green,
+ uint8_t blue) {
+ // Set the row
+ int height = dstInfo.height();
+ int row;
if (kBottomUp_RowOrder == fRowOrder) {
- y = height - y - 1;
+ row = height - y - 1;
+ } else {
+ row = y;
+ }
+
+ // Set the pixel based on destination color type
+ switch (dstInfo.colorType()) {
+ case kN32_SkColorType: {
+ SkPMColor* dstRow = SkTAddOffset<SkPMColor>(dst,
+ row * (int) dstRowBytes);
+ dstRow[x] = SkPackARGB32NoCheck(0xFF, red, green, blue);
+ break;
+ }
+ case kRGB_565_SkColorType: {
+ uint16_t* dstRow = SkTAddOffset<uint16_t>(dst,
+ row * (int) dstRowBytes);
+ dstRow[x] = SkPack888ToRGB16(red, green, blue);
+ break;
+ }
+ default:
+ // This case should not be reached. We should catch an invalid
+ // color type when we check that the conversion is possible.
+ SkASSERT(false);
+ break;
}
- SkPMColor* dstRow = SkTAddOffset<SkPMColor>(dst, y * dstRowBytes);
- dstRow[x] = fColorTable.get()[index];
}
/*
@@ -627,7 +757,7 @@ SkCodec::Result SkBmpCodec::decodeRLE(const SkImageInfo& dstInfo,
uint32_t currByte = 0;
SkAutoTDeleteArray<uint8_t> buffer(SkNEW_ARRAY(uint8_t, fRemainingBytes));
size_t totalBytes = stream()->read(buffer.get(), fRemainingBytes);
- if ((uint32_t) totalBytes < fRemainingBytes) {
+ if (totalBytes < fRemainingBytes) {
SkDebugf("Warning: incomplete RLE file.\n");
} else if (totalBytes <= 0) {
SkDebugf("Error: could not read RLE image data.\n");
@@ -706,18 +836,16 @@ SkCodec::Result SkBmpCodec::decodeRLE(const SkImageInfo& dstInfo,
return kIncompleteInput;
}
// Set numPixels number of pixels
- SkPMColor* dstRow = SkTAddOffset<SkPMColor>(
- dstPtr, y * dstRowBytes);
while (numPixels > 0) {
switch(fBitsPerPixel) {
case 4: {
SkASSERT(currByte < totalBytes);
uint8_t val = buffer.get()[currByte++];
- setRLEPixel(dstPtr, dstRowBytes, height, x++, y,
- val >> 4);
+ setRLEPixel(dstPtr, dstRowBytes, dstInfo, x++,
+ y, val >> 4);
numPixels--;
if (numPixels != 0) {
- setRLEPixel(dstPtr, dstRowBytes, height,
+ setRLEPixel(dstPtr, dstRowBytes, dstInfo,
x++, y, val & 0xF);
numPixels--;
}
@@ -725,8 +853,8 @@ SkCodec::Result SkBmpCodec::decodeRLE(const SkImageInfo& dstInfo,
}
case 8:
SkASSERT(currByte < totalBytes);
- setRLEPixel(dstPtr, dstRowBytes, height, x++, y,
- buffer.get()[currByte++]);
+ setRLEPixel(dstPtr, dstRowBytes, dstInfo, x++,
+ y, buffer.get()[currByte++]);
numPixels--;
break;
case 24: {
@@ -734,9 +862,8 @@ SkCodec::Result SkBmpCodec::decodeRLE(const SkImageInfo& dstInfo,
uint8_t blue = buffer.get()[currByte++];
uint8_t green = buffer.get()[currByte++];
uint8_t red = buffer.get()[currByte++];
- SkPMColor color = SkPackARGB32NoCheck(
- 0xFF, red, green, blue);
- dstRow[x++] = color;
+ setRLE24Pixel(dstPtr, dstRowBytes, dstInfo,
+ x++, y, red, green, blue);
numPixels--;
}
default:
@@ -770,11 +897,9 @@ SkCodec::Result SkBmpCodec::decodeRLE(const SkImageInfo& dstInfo,
uint8_t blue = task;
uint8_t green = buffer.get()[currByte++];
uint8_t red = buffer.get()[currByte++];
- SkPMColor color = SkPackARGB32NoCheck(0xFF, red, green, blue);
- SkPMColor* dstRow =
- SkTAddOffset<SkPMColor>(dstPtr, y * dstRowBytes);
while (x < endX) {
- dstRow[x++] = color;
+ setRLE24Pixel(dstPtr, dstRowBytes, dstInfo, x++, y, red,
+ green, blue);
}
} else {
// In RLE8 or RLE4, the second byte read gives the index in the
@@ -790,7 +915,7 @@ SkCodec::Result SkBmpCodec::decodeRLE(const SkImageInfo& dstInfo,
// Set the indicated number of pixels
for (int which = 0; x < endX; x++) {
- setRLEPixel(dstPtr, dstRowBytes, height, x, y,
+ setRLEPixel(dstPtr, dstRowBytes, dstInfo, x, y,
indices[which]);
which = !which;
}

Powered by Google App Engine
This is Rietveld 408576698