Skip to content

Commit

Permalink
Bug 1514393 - Isolate internals of nsJPEGEncoder r=aosmond
Browse files Browse the repository at this point in the history
Both libjpeg and windows.h typedef `boolean` to different types (`int` and
`unsiched char` respectively) and nsJPEGEncoder's public definition includes a
function that returns a `boolean`. Exposing this header results in type
conflicts.

We now isolate the internals of nsJPEGEncoder into a friend class whose
internals are hidden from the publica, allowing the header to exported.

Differential Revision: https://phabricator.services.mozilla.com/D14638
  • Loading branch information
brennie committed Dec 17, 2018
1 parent de2a04e commit 6a27394
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 82 deletions.
156 changes: 86 additions & 70 deletions image/encoders/jpeg/nsJPEGEncoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
#include "nsStreamUtils.h"
#include "gfxColor.h"

extern "C" {
#include "jpeglib.h"
}

#include <setjmp.h>
#include "jerror.h"

Expand All @@ -17,6 +21,45 @@ using namespace mozilla;
NS_IMPL_ISUPPORTS(nsJPEGEncoder, imgIEncoder, nsIInputStream,
nsIAsyncInputStream)

class nsJPEGEncoderInternal {
friend class nsJPEGEncoder;

protected:
/**
* Initialize destination. This is called by jpeg_start_compress() before
* any data is actually written. It must initialize next_output_byte and
* free_in_buffer. free_in_buffer must be initialized to a positive value.
*/
static void initDestination(jpeg_compress_struct* cinfo);

/**
* This is called whenever the buffer has filled (free_in_buffer reaches
* zero). In typical applications, it should write out the *entire* buffer
* (use the saved start address and buffer length; ignore the current state
* of next_output_byte and free_in_buffer). Then reset the pointer & count
* to the start of the buffer, and return TRUE indicating that the buffer
* has been dumped. free_in_buffer must be set to a positive value when
* TRUE is returned. A FALSE return should only be used when I/O suspension
* is desired (this operating mode is discussed in the next section).
*/
static boolean emptyOutputBuffer(jpeg_compress_struct* cinfo);


/**
* Terminate destination --- called by jpeg_finish_compress() after all data
* has been written. In most applications, this must flush any data
* remaining in the buffer. Use either next_output_byte or free_in_buffer
* to determine how much data is in the buffer.
*/
static void termDestination(jpeg_compress_struct* cinfo);

/**
* Override the standard error method in the IJG JPEG decoder code. This
* was mostly copied from nsJPEGDecoder.cpp
*/
static void errorExit(jpeg_common_struct* cinfo);
};

// used to pass error info through the JPEG library
struct encoder_error_mgr {
jpeg_error_mgr pub;
Expand Down Expand Up @@ -111,7 +154,7 @@ nsJPEGEncoder::InitFromData(const uint8_t* aData,
// This must be done before the call to create_compress
encoder_error_mgr errmgr;
cinfo.err = jpeg_std_error(&errmgr.pub);
errmgr.pub.error_exit = errorExit;
errmgr.pub.error_exit = nsJPEGEncoderInternal::errorExit;
// Establish the setjmp return context for my_error_exit to use.
if (setjmp(errmgr.setjmp_buffer)) {
// If we get here, the JPEG code has signaled an error.
Expand All @@ -138,9 +181,9 @@ nsJPEGEncoder::InitFromData(const uint8_t* aData,

// set up the destination manager
jpeg_destination_mgr destmgr;
destmgr.init_destination = initDestination;
destmgr.empty_output_buffer = emptyOutputBuffer;
destmgr.term_destination = termDestination;
destmgr.init_destination = nsJPEGEncoderInternal::initDestination;
destmgr.empty_output_buffer = nsJPEGEncoderInternal::emptyOutputBuffer;
destmgr.term_destination = nsJPEGEncoderInternal::termDestination;
cinfo.dest = &destmgr;
cinfo.client_data = this;

Expand Down Expand Up @@ -347,14 +390,39 @@ void nsJPEGEncoder::ConvertRGBARow(const uint8_t* aSrc, uint8_t* aDest,
}
}

// nsJPEGEncoder::initDestination
//
// Initialize destination. This is called by jpeg_start_compress() before
// any data is actually written. It must initialize next_output_byte and
// free_in_buffer. free_in_buffer must be initialized to a positive value.

void // static
nsJPEGEncoder::initDestination(jpeg_compress_struct* cinfo) {
void nsJPEGEncoder::NotifyListener() {
// We might call this function on multiple threads (any threads that call
// AsyncWait and any that do encoding) so we lock to avoid notifying the
// listener twice about the same data (which generally leads to a truncated
// image).
ReentrantMonitorAutoEnter autoEnter(mReentrantMonitor);

if (mCallback &&
(mImageBufferUsed - mImageBufferReadPoint >= mNotifyThreshold ||
mFinished)) {
nsCOMPtr<nsIInputStreamCallback> callback;
if (mCallbackTarget) {
callback = NS_NewInputStreamReadyEvent("nsJPEGEncoder::NotifyListener",
mCallback, mCallbackTarget);
} else {
callback = mCallback;
}

NS_ASSERTION(callback, "Shouldn't fail to make the callback");
// Null the callback first because OnInputStreamReady could reenter
// AsyncWait
mCallback = nullptr;
mCallbackTarget = nullptr;
mNotifyThreshold = 0;

callback->OnInputStreamReady(this);
}
}


/* static */ void
nsJPEGEncoderInternal::initDestination(jpeg_compress_struct* cinfo) {
nsJPEGEncoder* that = static_cast<nsJPEGEncoder*>(cinfo->client_data);
NS_ASSERTION(!that->mImageBuffer, "Image buffer already initialized");

Expand All @@ -366,19 +434,8 @@ nsJPEGEncoder::initDestination(jpeg_compress_struct* cinfo) {
cinfo->dest->free_in_buffer = that->mImageBufferSize;
}

// nsJPEGEncoder::emptyOutputBuffer
//
// This is called whenever the buffer has filled (free_in_buffer reaches
// zero). In typical applications, it should write out the *entire* buffer
// (use the saved start address and buffer length; ignore the current state
// of next_output_byte and free_in_buffer). Then reset the pointer & count
// to the start of the buffer, and return TRUE indicating that the buffer
// has been dumped. free_in_buffer must be set to a positive value when
// TRUE is returned. A FALSE return should only be used when I/O suspension
// is desired (this operating mode is discussed in the next section).

boolean // static
nsJPEGEncoder::emptyOutputBuffer(jpeg_compress_struct* cinfo) {
/* static */ boolean
nsJPEGEncoderInternal::emptyOutputBuffer(jpeg_compress_struct* cinfo) {
nsJPEGEncoder* that = static_cast<nsJPEGEncoder*>(cinfo->client_data);
NS_ASSERTION(that->mImageBuffer, "No buffer to empty!");

Expand Down Expand Up @@ -413,15 +470,8 @@ nsJPEGEncoder::emptyOutputBuffer(jpeg_compress_struct* cinfo) {
return 1;
}

// nsJPEGEncoder::termDestination
//
// Terminate destination --- called by jpeg_finish_compress() after all data
// has been written. In most applications, this must flush any data
// remaining in the buffer. Use either next_output_byte or free_in_buffer
// to determine how much data is in the buffer.

void // static
nsJPEGEncoder::termDestination(jpeg_compress_struct* cinfo) {
/* static */ void
nsJPEGEncoderInternal::termDestination(jpeg_compress_struct* cinfo) {
nsJPEGEncoder* that = static_cast<nsJPEGEncoder*>(cinfo->client_data);
if (!that->mImageBuffer) {
return;
Expand All @@ -432,13 +482,8 @@ nsJPEGEncoder::termDestination(jpeg_compress_struct* cinfo) {
that->NotifyListener();
}

// nsJPEGEncoder::errorExit
//
// Override the standard error method in the IJG JPEG decoder code. This
// was mostly copied from nsJPEGDecoder.cpp

void // static
nsJPEGEncoder::errorExit(jpeg_common_struct* cinfo) {
/* static */ void
nsJPEGEncoderInternal::errorExit(jpeg_common_struct* cinfo) {
nsresult error_code;
encoder_error_mgr* err = (encoder_error_mgr*)cinfo->err;

Expand All @@ -454,33 +499,4 @@ nsJPEGEncoder::errorExit(jpeg_common_struct* cinfo) {
// Return control to the setjmp point. We pass an nsresult masquerading as
// an int, which works because the setjmp() caller casts it back.
longjmp(err->setjmp_buffer, static_cast<int>(error_code));
}

void nsJPEGEncoder::NotifyListener() {
// We might call this function on multiple threads (any threads that call
// AsyncWait and any that do encoding) so we lock to avoid notifying the
// listener twice about the same data (which generally leads to a truncated
// image).
ReentrantMonitorAutoEnter autoEnter(mReentrantMonitor);

if (mCallback &&
(mImageBufferUsed - mImageBufferReadPoint >= mNotifyThreshold ||
mFinished)) {
nsCOMPtr<nsIInputStreamCallback> callback;
if (mCallbackTarget) {
callback = NS_NewInputStreamReadyEvent("nsJPEGEncoder::NotifyListener",
mCallback, mCallbackTarget);
} else {
callback = mCallback;
}

NS_ASSERTION(callback, "Shouldn't fail to make the callback");
// Null the callback first because OnInputStreamReady could reenter
// AsyncWait
mCallback = nullptr;
mCallbackTarget = nullptr;
mNotifyThreshold = 0;

callback->OnInputStreamReady(this);
}
}
}
16 changes: 4 additions & 12 deletions image/encoders/jpeg/nsJPEGEncoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,8 @@

#include "nsCOMPtr.h"

// needed for JPEG library
#include <stdio.h>

extern "C" {
#include "jpeglib.h"
}
struct jpeg_compress_struct;
struct jpeg_common_struct;

#define NS_JPEGENCODER_CID \
{ \
Expand All @@ -30,8 +26,10 @@ extern "C" {

// Provides JPEG encoding functionality. Use InitFromData() to do the
// encoding. See that function definition for encoding options.
class nsJPEGEncoderInternal;

class nsJPEGEncoder final : public imgIEncoder {
friend class nsJPEGEncoderInternal;
typedef mozilla::ReentrantMonitor ReentrantMonitor;

public:
Expand All @@ -51,12 +49,6 @@ class nsJPEGEncoder final : public imgIEncoder {
void ConvertRGBARow(const uint8_t* aSrc, uint8_t* aDest,
uint32_t aPixelWidth);

static void initDestination(jpeg_compress_struct* cinfo);
static boolean emptyOutputBuffer(jpeg_compress_struct* cinfo);
static void termDestination(jpeg_compress_struct* cinfo);

static void errorExit(jpeg_common_struct* cinfo);

void NotifyListener();

bool mFinished;
Expand Down

0 comments on commit 6a27394

Please sign in to comment.