Skip to content

Commit

Permalink
Fix JPEGImageEncoder memory leak
Browse files Browse the repository at this point in the history
BUG=590648, 593435

Review URL: https://codereview.chromium.org/1791443002

Cr-Commit-Position: refs/heads/master@{#380978}
(cherry picked from commit 1d2c4fd)

Review URL: https://codereview.chromium.org/1807553002 .

Cr-Commit-Position: refs/branch-heads/2661@{crosswalk-project#240}
Cr-Branched-From: ef6f6ae-refs/heads/master@{#378081}
  • Loading branch information
junov committed Mar 15, 2016
1 parent ba26855 commit 4c17e48
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 48 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -144,9 +144,14 @@ void CanvasAsyncBlobCreator::encodeImageOnEncoderThread(double quality)
{
ASSERT(!isMainThread());

bool success;
if (m_mimeType == "image/jpeg") {
JPEGImageEncoder::encodeWithPreInitializedState(m_jpegEncoderState.get(), m_data->data());
} else if (!ImageDataBuffer(m_size, m_data->data()).encodeImage(m_mimeType, quality, m_encodedImage.get())) {
success = JPEGImageEncoder::encodeWithPreInitializedState(m_jpegEncoderState.release(), m_data->data());
} else {
success = ImageDataBuffer(m_size, m_data->data()).encodeImage(m_mimeType, quality, m_encodedImage.get());
}

if (!success) {
scheduleCreateNullptrAndCallOnMainThread();
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,28 +43,27 @@ extern "C" {
namespace blink {

struct JPEGOutputBuffer : public jpeg_destination_mgr {
USING_FAST_MALLOC(JPEGOutputBuffer);
WTF_MAKE_NONCOPYABLE(JPEGOutputBuffer);
public:
JPEGOutputBuffer() : jpeg_destination_mgr() {}
DISALLOW_NEW();
Vector<unsigned char>* output;
Vector<unsigned char> buffer;
};

class JPEGImageEncoderStateImpl : public JPEGImageEncoderState {
class JPEGImageEncoderStateImpl final : public JPEGImageEncoderState {
public:
JPEGImageEncoderStateImpl(jpeg_compress_struct* jpeg, JPEGOutputBuffer* destination, jpeg_error_mgr* error)
: m_jpeg(jpeg)
, m_destination(destination)
, m_error(error) {}
~JPEGImageEncoderStateImpl() override;
jpeg_compress_struct* jpeg() { ASSERT(m_jpeg); return m_jpeg; }
JPEGOutputBuffer* outputBuffer() { return m_destination; }
jpeg_error_mgr* error() { return m_error; }
JPEGImageEncoderStateImpl() {}
~JPEGImageEncoderStateImpl() override
{
jpeg_destroy_compress(&m_cinfo);
m_cinfo.client_data = 0;
}
JPEGOutputBuffer* outputBuffer() { return &m_outputBuffer; }
jpeg_compress_struct* cinfo() { return &m_cinfo; }
jpeg_error_mgr* error() { return &m_error; }

private:
jpeg_compress_struct* m_jpeg;
JPEGOutputBuffer* m_destination;
jpeg_error_mgr* m_error;
JPEGOutputBuffer m_outputBuffer;
jpeg_compress_struct m_cinfo;
jpeg_error_mgr m_error;
};

static void prepareOutput(j_compress_ptr cinfo)
Expand Down Expand Up @@ -132,46 +131,42 @@ PassOwnPtr<JPEGImageEncoderState> JPEGImageEncoderState::create(const IntSize& i
if (imageSize.width() <= 0 || imageSize.height() <= 0)
return nullptr;

int compressionQuality = JPEGImageEncoder::computeCompressionQuality(quality);
JPEGOutputBuffer* destination = new JPEGOutputBuffer;
destination->output = output;

jpeg_compress_struct* cinfo = new jpeg_compress_struct;
jpeg_error_mgr* error = new jpeg_error_mgr;
OwnPtr<JPEGImageEncoderStateImpl> encoderState = adoptPtr(new JPEGImageEncoderStateImpl());

jpeg_compress_struct* cinfo = encoderState->cinfo();
jpeg_error_mgr* error = encoderState->error();
cinfo->err = jpeg_std_error(error);
error->error_exit = handleError;

jmp_buf jumpBuffer;
cinfo->client_data = &jumpBuffer;

if (setjmp(jumpBuffer)) {
jpeg_destroy_compress(cinfo);
return nullptr;
}

JPEGOutputBuffer* destination = encoderState->outputBuffer();
destination->output = output;

jpeg_create_compress(cinfo);
cinfo->dest = destination;
cinfo->dest->init_destination = prepareOutput;
cinfo->dest->empty_output_buffer = writeOutput;
cinfo->dest->term_destination = finishOutput;


cinfo->image_height = imageSize.height();
cinfo->image_width = imageSize.width();
cinfo->in_color_space = JCS_RGB;
cinfo->input_components = 3;

jpeg_set_defaults(cinfo);
int compressionQuality = JPEGImageEncoder::computeCompressionQuality(quality);
jpeg_set_quality(cinfo, compressionQuality, TRUE);
disableSubsamplingForHighQuality(cinfo, compressionQuality);
jpeg_start_compress(cinfo, TRUE);

return adoptPtr(new JPEGImageEncoderStateImpl(cinfo, destination, error));
}

JPEGImageEncoderStateImpl::~JPEGImageEncoderStateImpl()
{
jpeg_destroy_compress(m_jpeg);
cinfo->client_data = 0;
return encoderState.release();
}

int JPEGImageEncoder::computeCompressionQuality(const double& quality)
Expand All @@ -182,39 +177,43 @@ int JPEGImageEncoder::computeCompressionQuality(const double& quality)
return compressionQuality;
}

void JPEGImageEncoder::encodeWithPreInitializedState(JPEGImageEncoderState* encoderState, const unsigned char* inputPixels)
bool JPEGImageEncoder::encodeWithPreInitializedState(PassOwnPtr<JPEGImageEncoderState> encoderState, const unsigned char* inputPixels)
{
JPEGImageEncoderStateImpl* encoderStateImpl = static_cast<JPEGImageEncoderStateImpl *>(encoderState);
JPEGImageEncoderStateImpl* encoderStateImpl = static_cast<JPEGImageEncoderStateImpl*>(encoderState.get());

Vector<JSAMPLE> row;
unsigned char* pixels = const_cast<unsigned char*>(inputPixels);
row.resize(encoderStateImpl->cinfo()->image_width * encoderStateImpl->cinfo()->input_components);

row.resize(encoderStateImpl->jpeg()->image_width * encoderStateImpl->jpeg()->input_components);
jmp_buf jumpBuffer;
encoderStateImpl->cinfo()->client_data = &jumpBuffer;

const size_t pixelRowStride = encoderStateImpl->jpeg()->image_width * 4;
while (encoderStateImpl->jpeg()->next_scanline < encoderStateImpl->jpeg()->image_height) {
if (setjmp(jumpBuffer)) {
return false;
}

unsigned char* pixels = const_cast<unsigned char*>(inputPixels);
const size_t pixelRowStride = encoderStateImpl->cinfo()->image_width * 4;
while (encoderStateImpl->cinfo()->next_scanline < encoderStateImpl->cinfo()->image_height) {
JSAMPLE* rowData = row.data();
RGBAtoRGB(pixels, encoderStateImpl->jpeg()->image_width, rowData);
jpeg_write_scanlines(encoderStateImpl->jpeg(), &rowData, 1);
RGBAtoRGB(pixels, encoderStateImpl->cinfo()->image_width, rowData);
jpeg_write_scanlines(encoderStateImpl->cinfo(), &rowData, 1);
pixels += pixelRowStride;
}

jpeg_finish_compress(encoderStateImpl->jpeg());
jpeg_finish_compress(encoderStateImpl->cinfo());
return true;
}

bool JPEGImageEncoder::encode(const ImageDataBuffer& imageData, const double& quality, Vector<unsigned char>* output)
{
if (!imageData.pixels())
return false;
IntSize imageSize(imageData.width(), imageData.height());
const unsigned char* inputPixels = imageData.pixels();

OwnPtr<JPEGImageEncoderState> encoderState = JPEGImageEncoderState::create(imageSize, quality, output);
if (!encoderState.get())
OwnPtr<JPEGImageEncoderState> encoderState = JPEGImageEncoderState::create(IntSize(imageData.width(), imageData.height()), quality, output);
if (!encoderState)
return false;

JPEGImageEncoder::encodeWithPreInitializedState(encoderState.get(), inputPixels);
return true;
return JPEGImageEncoder::encodeWithPreInitializedState(encoderState.release(), imageData.pixels());
}

} // namespace blink
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ class PLATFORM_EXPORT JPEGImageEncoder {
// be safer.
static bool encode(const ImageDataBuffer&, const double& quality, Vector<unsigned char>*);

static void encodeWithPreInitializedState(JPEGImageEncoderState*, const unsigned char*);
static bool encodeWithPreInitializedState(PassOwnPtr<JPEGImageEncoderState>, const unsigned char*);
static int computeCompressionQuality(const double& quality);

private:
Expand Down

0 comments on commit 4c17e48

Please sign in to comment.