Skip to content

Commit

Permalink
Bug 1201763 - Add downscale-during-decode support for the ICON decode…
Browse files Browse the repository at this point in the history
…r. r=tn
  • Loading branch information
sethfowler committed Sep 9, 2015
1 parent a53e4ac commit e660da5
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 27 deletions.
1 change: 1 addition & 0 deletions image/ImageFactory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ ShouldDownscaleDuringDecode(const nsCString& aMimeType)
{
DecoderType type = DecoderFactory::GetDecoderType(aMimeType.get());
return type == DecoderType::JPEG ||
type == DecoderType::ICON ||
type == DecoderType::PNG ||
type == DecoderType::BMP;
}
Expand Down
115 changes: 91 additions & 24 deletions image/decoders/nsIconDecoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,31 +12,46 @@
#include "RasterImage.h"
#include <algorithm>

using namespace mozilla::gfx;

using std::min;

namespace mozilla {
namespace image {

nsIconDecoder::nsIconDecoder(RasterImage* aImage)
: Decoder(aImage),
mWidth(-1),
mHeight(-1),
mPixBytesRead(0),
mState(iconStateStart)
: Decoder(aImage)
, mExpectedDataLength(0)
, mPixBytesRead(0)
, mState(iconStateStart)
, mWidth(-1)
, mHeight(-1)
{
// Nothing to do
}

nsIconDecoder::~nsIconDecoder()
{ }

nsresult
nsIconDecoder::SetTargetSize(const nsIntSize& aSize)
{
// Make sure the size is reasonable.
if (MOZ_UNLIKELY(aSize.width <= 0 || aSize.height <= 0)) {
return NS_ERROR_FAILURE;
}

// Create a downscaler that we'll filter our output through.
mDownscaler.emplace(aSize);

return NS_OK;
}

void
nsIconDecoder::WriteInternal(const char* aBuffer, uint32_t aCount)
{
MOZ_ASSERT(!HasError(), "Shouldn't call WriteInternal after error!");

// We put this here to avoid errors about crossing initialization with case
// jumps on linux.
uint32_t bytesToRead = 0;

// Loop until the input data is gone
while (aCount > 0) {
switch (mState) {
Expand Down Expand Up @@ -73,9 +88,16 @@ nsIconDecoder::WriteInternal(const char* aBuffer, uint32_t aCount)
break;
}

// The input is 32bpp, so we expect 4 bytes of data per pixel.
mExpectedDataLength = mWidth * mHeight * 4;

{
MOZ_ASSERT(!mImageData, "Already have a buffer allocated?");
nsresult rv = AllocateBasicFrame();
IntSize targetSize = mDownscaler ? mDownscaler->TargetSize()
: GetSize();
nsresult rv = AllocateFrame(0, targetSize,
IntRect(IntPoint(), targetSize),
gfx::SurfaceFormat::B8G8R8A8);
if (NS_FAILED(rv)) {
mState = iconStateFinished;
return;
Expand All @@ -84,6 +106,16 @@ nsIconDecoder::WriteInternal(const char* aBuffer, uint32_t aCount)

MOZ_ASSERT(mImageData, "Should have a buffer now");

if (mDownscaler) {
nsresult rv = mDownscaler->BeginFrame(GetSize(),
mImageData,
/* aHasAlpha = */ true);
if (NS_FAILED(rv)) {
mState = iconStateFinished;
return;
}
}

// Book Keeping
aBuffer++;
aCount--;
Expand All @@ -93,25 +125,60 @@ nsIconDecoder::WriteInternal(const char* aBuffer, uint32_t aCount)
case iconStateReadPixels: {

// How many bytes are we reading?
bytesToRead = std::min(aCount, mImageDataLength - mPixBytesRead);

// Copy the bytes
memcpy(mImageData + mPixBytesRead, aBuffer, bytesToRead);
uint32_t bytesToRead = min(aCount, mExpectedDataLength - mPixBytesRead);

if (mDownscaler) {
uint8_t* row = mDownscaler->RowBuffer();
const uint32_t bytesPerRow = mWidth * 4;
const uint32_t rowOffset = mPixBytesRead % bytesPerRow;

// Update global state; we're about to read |bytesToRead| bytes.
aCount -= bytesToRead;
mPixBytesRead += bytesToRead;

if (rowOffset > 0) {
// Finish the current row.
const uint32_t remaining = bytesPerRow - rowOffset;
memcpy(row + rowOffset, aBuffer, remaining);
aBuffer += remaining;
bytesToRead -= remaining;
mDownscaler->CommitRow();
}

// Performance isn't critical here, so our update rectangle is
// always the full icon
nsIntRect r(0, 0, mWidth, mHeight);
// Copy the bytes a row at a time.
while (bytesToRead > bytesPerRow) {
memcpy(row, aBuffer, bytesPerRow);
aBuffer += bytesPerRow;
bytesToRead -= bytesPerRow;
mDownscaler->CommitRow();
}

// Invalidate
PostInvalidation(r);
// Copy any leftover bytes. (Leaving the current row incomplete.)
if (bytesToRead > 0) {
memcpy(row, aBuffer, bytesToRead);
aBuffer += bytesPerRow;
bytesToRead -= bytesPerRow;
}

// Book Keeping
aBuffer += bytesToRead;
aCount -= bytesToRead;
mPixBytesRead += bytesToRead;
if (mDownscaler->HasInvalidation()) {
DownscalerInvalidRect invalidRect = mDownscaler->TakeInvalidRect();
PostInvalidation(invalidRect.mOriginalSizeRect,
Some(invalidRect.mTargetSizeRect));
}
} else {
// Copy all the bytes at once.
memcpy(mImageData + mPixBytesRead, aBuffer, bytesToRead);
aBuffer += bytesToRead;
aCount -= bytesToRead;
mPixBytesRead += bytesToRead;

// Invalidate. Performance isn't critical here, so our update
// rectangle is always the full icon.
PostInvalidation(IntRect(0, 0, mWidth, mHeight));
}

// If we've got all the pixel bytes, we're finished
if (mPixBytesRead == mImageDataLength) {
if (mPixBytesRead == mExpectedDataLength) {
PostFrameStop();
PostDecodeDone();
mState = iconStateFinished;
Expand Down
10 changes: 7 additions & 3 deletions image/decoders/nsIconDecoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ class nsIconDecoder : public Decoder
public:
virtual ~nsIconDecoder();

virtual nsresult SetTargetSize(const nsIntSize& aSize) override;

virtual void WriteInternal(const char* aBuffer, uint32_t aCount) override;

private:
Expand All @@ -47,11 +49,13 @@ class nsIconDecoder : public Decoder
// Decoders should only be instantiated via DecoderFactory.
explicit nsIconDecoder(RasterImage* aImage);

public:
uint8_t mWidth;
uint8_t mHeight;
Maybe<Downscaler> mDownscaler;

uint32_t mExpectedDataLength;
uint32_t mPixBytesRead;
uint32_t mState;
uint8_t mWidth;
uint8_t mHeight;
};

enum {
Expand Down

0 comments on commit e660da5

Please sign in to comment.