image/encoders/jpeg/nsJPEGEncoder.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
michael@0 2 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 #include "nsJPEGEncoder.h"
michael@0 7 #include "prprf.h"
michael@0 8 #include "nsString.h"
michael@0 9 #include "nsStreamUtils.h"
michael@0 10 #include "gfxColor.h"
michael@0 11
michael@0 12 #include <setjmp.h>
michael@0 13 #include "jerror.h"
michael@0 14
michael@0 15 using namespace mozilla;
michael@0 16
michael@0 17 NS_IMPL_ISUPPORTS(nsJPEGEncoder, imgIEncoder, nsIInputStream, nsIAsyncInputStream)
michael@0 18
michael@0 19 // used to pass error info through the JPEG library
michael@0 20 struct encoder_error_mgr {
michael@0 21 jpeg_error_mgr pub;
michael@0 22 jmp_buf setjmp_buffer;
michael@0 23 };
michael@0 24
michael@0 25 nsJPEGEncoder::nsJPEGEncoder() : mFinished(false),
michael@0 26 mImageBuffer(nullptr), mImageBufferSize(0),
michael@0 27 mImageBufferUsed(0), mImageBufferReadPoint(0),
michael@0 28 mCallback(nullptr),
michael@0 29 mCallbackTarget(nullptr), mNotifyThreshold(0),
michael@0 30 mReentrantMonitor("nsJPEGEncoder.mReentrantMonitor")
michael@0 31 {
michael@0 32 }
michael@0 33
michael@0 34 nsJPEGEncoder::~nsJPEGEncoder()
michael@0 35 {
michael@0 36 if (mImageBuffer) {
michael@0 37 moz_free(mImageBuffer);
michael@0 38 mImageBuffer = nullptr;
michael@0 39 }
michael@0 40 }
michael@0 41
michael@0 42
michael@0 43 // nsJPEGEncoder::InitFromData
michael@0 44 //
michael@0 45 // One output option is supported: "quality=X" where X is an integer in the
michael@0 46 // range 0-100. Higher values for X give better quality.
michael@0 47 //
michael@0 48 // Transparency is always discarded.
michael@0 49
michael@0 50 NS_IMETHODIMP nsJPEGEncoder::InitFromData(const uint8_t* aData,
michael@0 51 uint32_t aLength, // (unused, req'd by JS)
michael@0 52 uint32_t aWidth,
michael@0 53 uint32_t aHeight,
michael@0 54 uint32_t aStride,
michael@0 55 uint32_t aInputFormat,
michael@0 56 const nsAString& aOutputOptions)
michael@0 57 {
michael@0 58 NS_ENSURE_ARG(aData);
michael@0 59
michael@0 60 // validate input format
michael@0 61 if (aInputFormat != INPUT_FORMAT_RGB &&
michael@0 62 aInputFormat != INPUT_FORMAT_RGBA &&
michael@0 63 aInputFormat != INPUT_FORMAT_HOSTARGB)
michael@0 64 return NS_ERROR_INVALID_ARG;
michael@0 65
michael@0 66 // Stride is the padded width of each row, so it better be longer (I'm afraid
michael@0 67 // people will not understand what stride means, so check it well)
michael@0 68 if ((aInputFormat == INPUT_FORMAT_RGB &&
michael@0 69 aStride < aWidth * 3) ||
michael@0 70 ((aInputFormat == INPUT_FORMAT_RGBA || aInputFormat == INPUT_FORMAT_HOSTARGB) &&
michael@0 71 aStride < aWidth * 4)) {
michael@0 72 NS_WARNING("Invalid stride for InitFromData");
michael@0 73 return NS_ERROR_INVALID_ARG;
michael@0 74 }
michael@0 75
michael@0 76 // can't initialize more than once
michael@0 77 if (mImageBuffer != nullptr)
michael@0 78 return NS_ERROR_ALREADY_INITIALIZED;
michael@0 79
michael@0 80 // options: we only have one option so this is easy
michael@0 81 int quality = 92;
michael@0 82 if (aOutputOptions.Length() > 0) {
michael@0 83 // have options string
michael@0 84 const nsString qualityPrefix(NS_LITERAL_STRING("quality="));
michael@0 85 if (aOutputOptions.Length() > qualityPrefix.Length() &&
michael@0 86 StringBeginsWith(aOutputOptions, qualityPrefix)) {
michael@0 87 // have quality string
michael@0 88 nsCString value = NS_ConvertUTF16toUTF8(Substring(aOutputOptions,
michael@0 89 qualityPrefix.Length()));
michael@0 90 int newquality = -1;
michael@0 91 if (PR_sscanf(value.get(), "%d", &newquality) == 1) {
michael@0 92 if (newquality >= 0 && newquality <= 100) {
michael@0 93 quality = newquality;
michael@0 94 } else {
michael@0 95 NS_WARNING("Quality value out of range, should be 0-100, using default");
michael@0 96 }
michael@0 97 } else {
michael@0 98 NS_WARNING("Quality value invalid, should be integer 0-100, using default");
michael@0 99 }
michael@0 100 }
michael@0 101 else {
michael@0 102 return NS_ERROR_INVALID_ARG;
michael@0 103 }
michael@0 104 }
michael@0 105
michael@0 106 jpeg_compress_struct cinfo;
michael@0 107
michael@0 108 // We set up the normal JPEG error routines, then override error_exit.
michael@0 109 // This must be done before the call to create_compress
michael@0 110 encoder_error_mgr errmgr;
michael@0 111 cinfo.err = jpeg_std_error(&errmgr.pub);
michael@0 112 errmgr.pub.error_exit = errorExit;
michael@0 113 // Establish the setjmp return context for my_error_exit to use.
michael@0 114 if (setjmp(errmgr.setjmp_buffer)) {
michael@0 115 // If we get here, the JPEG code has signaled an error.
michael@0 116 // We need to clean up the JPEG object, close the input file, and return.
michael@0 117 return NS_ERROR_FAILURE;
michael@0 118 }
michael@0 119
michael@0 120 jpeg_create_compress(&cinfo);
michael@0 121 cinfo.image_width = aWidth;
michael@0 122 cinfo.image_height = aHeight;
michael@0 123 cinfo.input_components = 3;
michael@0 124 cinfo.in_color_space = JCS_RGB;
michael@0 125 cinfo.data_precision = 8;
michael@0 126
michael@0 127 jpeg_set_defaults(&cinfo);
michael@0 128 jpeg_set_quality(&cinfo, quality, 1); // quality here is 0-100
michael@0 129 if (quality >= 90) {
michael@0 130 int i;
michael@0 131 for (i=0; i < MAX_COMPONENTS; i++) {
michael@0 132 cinfo.comp_info[i].h_samp_factor=1;
michael@0 133 cinfo.comp_info[i].v_samp_factor=1;
michael@0 134 }
michael@0 135 }
michael@0 136
michael@0 137 // set up the destination manager
michael@0 138 jpeg_destination_mgr destmgr;
michael@0 139 destmgr.init_destination = initDestination;
michael@0 140 destmgr.empty_output_buffer = emptyOutputBuffer;
michael@0 141 destmgr.term_destination = termDestination;
michael@0 142 cinfo.dest = &destmgr;
michael@0 143 cinfo.client_data = this;
michael@0 144
michael@0 145 jpeg_start_compress(&cinfo, 1);
michael@0 146
michael@0 147 // feed it the rows
michael@0 148 if (aInputFormat == INPUT_FORMAT_RGB) {
michael@0 149 while (cinfo.next_scanline < cinfo.image_height) {
michael@0 150 const uint8_t* row = &aData[cinfo.next_scanline * aStride];
michael@0 151 jpeg_write_scanlines(&cinfo, const_cast<uint8_t**>(&row), 1);
michael@0 152 }
michael@0 153 } else if (aInputFormat == INPUT_FORMAT_RGBA) {
michael@0 154 uint8_t* row = new uint8_t[aWidth * 3];
michael@0 155 while (cinfo.next_scanline < cinfo.image_height) {
michael@0 156 ConvertRGBARow(&aData[cinfo.next_scanline * aStride], row, aWidth);
michael@0 157 jpeg_write_scanlines(&cinfo, &row, 1);
michael@0 158 }
michael@0 159 delete[] row;
michael@0 160 } else if (aInputFormat == INPUT_FORMAT_HOSTARGB) {
michael@0 161 uint8_t* row = new uint8_t[aWidth * 3];
michael@0 162 while (cinfo.next_scanline < cinfo.image_height) {
michael@0 163 ConvertHostARGBRow(&aData[cinfo.next_scanline * aStride], row, aWidth);
michael@0 164 jpeg_write_scanlines(&cinfo, &row, 1);
michael@0 165 }
michael@0 166 delete[] row;
michael@0 167 }
michael@0 168
michael@0 169 jpeg_finish_compress(&cinfo);
michael@0 170 jpeg_destroy_compress(&cinfo);
michael@0 171
michael@0 172 mFinished = true;
michael@0 173 NotifyListener();
michael@0 174
michael@0 175 // if output callback can't get enough memory, it will free our buffer
michael@0 176 if (!mImageBuffer)
michael@0 177 return NS_ERROR_OUT_OF_MEMORY;
michael@0 178
michael@0 179 return NS_OK;
michael@0 180 }
michael@0 181
michael@0 182
michael@0 183 NS_IMETHODIMP nsJPEGEncoder::StartImageEncode(uint32_t aWidth,
michael@0 184 uint32_t aHeight,
michael@0 185 uint32_t aInputFormat,
michael@0 186 const nsAString& aOutputOptions)
michael@0 187 {
michael@0 188 return NS_ERROR_NOT_IMPLEMENTED;
michael@0 189 }
michael@0 190
michael@0 191 // Returns the number of bytes in the image buffer used.
michael@0 192 NS_IMETHODIMP nsJPEGEncoder::GetImageBufferUsed(uint32_t *aOutputSize)
michael@0 193 {
michael@0 194 NS_ENSURE_ARG_POINTER(aOutputSize);
michael@0 195 *aOutputSize = mImageBufferUsed;
michael@0 196 return NS_OK;
michael@0 197 }
michael@0 198
michael@0 199 // Returns a pointer to the start of the image buffer
michael@0 200 NS_IMETHODIMP nsJPEGEncoder::GetImageBuffer(char **aOutputBuffer)
michael@0 201 {
michael@0 202 NS_ENSURE_ARG_POINTER(aOutputBuffer);
michael@0 203 *aOutputBuffer = reinterpret_cast<char*>(mImageBuffer);
michael@0 204 return NS_OK;
michael@0 205 }
michael@0 206
michael@0 207 NS_IMETHODIMP nsJPEGEncoder::AddImageFrame(const uint8_t* aData,
michael@0 208 uint32_t aLength,
michael@0 209 uint32_t aWidth,
michael@0 210 uint32_t aHeight,
michael@0 211 uint32_t aStride,
michael@0 212 uint32_t aFrameFormat,
michael@0 213 const nsAString& aFrameOptions)
michael@0 214 {
michael@0 215 return NS_ERROR_NOT_IMPLEMENTED;
michael@0 216 }
michael@0 217
michael@0 218 NS_IMETHODIMP nsJPEGEncoder::EndImageEncode()
michael@0 219 {
michael@0 220 return NS_ERROR_NOT_IMPLEMENTED;
michael@0 221 }
michael@0 222
michael@0 223
michael@0 224 /* void close (); */
michael@0 225 NS_IMETHODIMP nsJPEGEncoder::Close()
michael@0 226 {
michael@0 227 if (mImageBuffer != nullptr) {
michael@0 228 moz_free(mImageBuffer);
michael@0 229 mImageBuffer = nullptr;
michael@0 230 mImageBufferSize = 0;
michael@0 231 mImageBufferUsed = 0;
michael@0 232 mImageBufferReadPoint = 0;
michael@0 233 }
michael@0 234 return NS_OK;
michael@0 235 }
michael@0 236
michael@0 237 /* unsigned long available (); */
michael@0 238 NS_IMETHODIMP nsJPEGEncoder::Available(uint64_t *_retval)
michael@0 239 {
michael@0 240 if (!mImageBuffer)
michael@0 241 return NS_BASE_STREAM_CLOSED;
michael@0 242
michael@0 243 *_retval = mImageBufferUsed - mImageBufferReadPoint;
michael@0 244 return NS_OK;
michael@0 245 }
michael@0 246
michael@0 247 /* [noscript] unsigned long read (in charPtr aBuf, in unsigned long aCount); */
michael@0 248 NS_IMETHODIMP nsJPEGEncoder::Read(char * aBuf, uint32_t aCount,
michael@0 249 uint32_t *_retval)
michael@0 250 {
michael@0 251 return ReadSegments(NS_CopySegmentToBuffer, aBuf, aCount, _retval);
michael@0 252 }
michael@0 253
michael@0 254 /* [noscript] unsigned long readSegments (in nsWriteSegmentFun aWriter, in voidPtr aClosure, in unsigned long aCount); */
michael@0 255 NS_IMETHODIMP nsJPEGEncoder::ReadSegments(nsWriteSegmentFun aWriter, void *aClosure, uint32_t aCount, uint32_t *_retval)
michael@0 256 {
michael@0 257 // Avoid another thread reallocing the buffer underneath us
michael@0 258 ReentrantMonitorAutoEnter autoEnter(mReentrantMonitor);
michael@0 259
michael@0 260 uint32_t maxCount = mImageBufferUsed - mImageBufferReadPoint;
michael@0 261 if (maxCount == 0) {
michael@0 262 *_retval = 0;
michael@0 263 return mFinished ? NS_OK : NS_BASE_STREAM_WOULD_BLOCK;
michael@0 264 }
michael@0 265
michael@0 266 if (aCount > maxCount)
michael@0 267 aCount = maxCount;
michael@0 268 nsresult rv = aWriter(this, aClosure,
michael@0 269 reinterpret_cast<const char*>(mImageBuffer+mImageBufferReadPoint),
michael@0 270 0, aCount, _retval);
michael@0 271 if (NS_SUCCEEDED(rv)) {
michael@0 272 NS_ASSERTION(*_retval <= aCount, "bad write count");
michael@0 273 mImageBufferReadPoint += *_retval;
michael@0 274 }
michael@0 275
michael@0 276 // errors returned from the writer end here!
michael@0 277 return NS_OK;
michael@0 278 }
michael@0 279
michael@0 280 /* boolean isNonBlocking (); */
michael@0 281 NS_IMETHODIMP nsJPEGEncoder::IsNonBlocking(bool *_retval)
michael@0 282 {
michael@0 283 *_retval = true;
michael@0 284 return NS_OK;
michael@0 285 }
michael@0 286
michael@0 287 NS_IMETHODIMP nsJPEGEncoder::AsyncWait(nsIInputStreamCallback *aCallback,
michael@0 288 uint32_t aFlags,
michael@0 289 uint32_t aRequestedCount,
michael@0 290 nsIEventTarget *aTarget)
michael@0 291 {
michael@0 292 if (aFlags != 0)
michael@0 293 return NS_ERROR_NOT_IMPLEMENTED;
michael@0 294
michael@0 295 if (mCallback || mCallbackTarget)
michael@0 296 return NS_ERROR_UNEXPECTED;
michael@0 297
michael@0 298 mCallbackTarget = aTarget;
michael@0 299 // 0 means "any number of bytes except 0"
michael@0 300 mNotifyThreshold = aRequestedCount;
michael@0 301 if (!aRequestedCount)
michael@0 302 mNotifyThreshold = 1024; // 1 KB seems good. We don't want to notify incessantly
michael@0 303
michael@0 304 // We set the callback absolutely last, because NotifyListener uses it to
michael@0 305 // determine if someone needs to be notified. If we don't set it last,
michael@0 306 // NotifyListener might try to fire off a notification to a null target
michael@0 307 // which will generally cause non-threadsafe objects to be used off the main thread
michael@0 308 mCallback = aCallback;
michael@0 309
michael@0 310 // What we are being asked for may be present already
michael@0 311 NotifyListener();
michael@0 312 return NS_OK;
michael@0 313 }
michael@0 314
michael@0 315 NS_IMETHODIMP nsJPEGEncoder::CloseWithStatus(nsresult aStatus)
michael@0 316 {
michael@0 317 return Close();
michael@0 318 }
michael@0 319
michael@0 320
michael@0 321
michael@0 322 // nsJPEGEncoder::ConvertHostARGBRow
michael@0 323 //
michael@0 324 // Our colors are stored with premultiplied alphas, but we need
michael@0 325 // an output with no alpha in machine-independent byte order.
michael@0 326 //
michael@0 327 // See gfx/cairo/cairo/src/cairo-png.c
michael@0 328 void
michael@0 329 nsJPEGEncoder::ConvertHostARGBRow(const uint8_t* aSrc, uint8_t* aDest,
michael@0 330 uint32_t aPixelWidth)
michael@0 331 {
michael@0 332 for (uint32_t x = 0; x < aPixelWidth; x++) {
michael@0 333 const uint32_t& pixelIn = ((const uint32_t*)(aSrc))[x];
michael@0 334 uint8_t *pixelOut = &aDest[x * 3];
michael@0 335
michael@0 336 pixelOut[0] = (pixelIn & 0xff0000) >> 16;
michael@0 337 pixelOut[1] = (pixelIn & 0x00ff00) >> 8;
michael@0 338 pixelOut[2] = (pixelIn & 0x0000ff) >> 0;
michael@0 339 }
michael@0 340 }
michael@0 341
michael@0 342 /**
michael@0 343 * nsJPEGEncoder::ConvertRGBARow
michael@0 344 *
michael@0 345 * Input is RGBA, output is RGB, so we should alpha-premultiply.
michael@0 346 */
michael@0 347 void
michael@0 348 nsJPEGEncoder::ConvertRGBARow(const uint8_t* aSrc, uint8_t* aDest,
michael@0 349 uint32_t aPixelWidth)
michael@0 350 {
michael@0 351 for (uint32_t x = 0; x < aPixelWidth; x++) {
michael@0 352 const uint8_t* pixelIn = &aSrc[x * 4];
michael@0 353 uint8_t* pixelOut = &aDest[x * 3];
michael@0 354
michael@0 355 uint8_t alpha = pixelIn[3];
michael@0 356 pixelOut[0] = gfxPreMultiply(pixelIn[0], alpha);
michael@0 357 pixelOut[1] = gfxPreMultiply(pixelIn[1], alpha);
michael@0 358 pixelOut[2] = gfxPreMultiply(pixelIn[2], alpha);
michael@0 359 }
michael@0 360 }
michael@0 361
michael@0 362 // nsJPEGEncoder::initDestination
michael@0 363 //
michael@0 364 // Initialize destination. This is called by jpeg_start_compress() before
michael@0 365 // any data is actually written. It must initialize next_output_byte and
michael@0 366 // free_in_buffer. free_in_buffer must be initialized to a positive value.
michael@0 367
michael@0 368 void // static
michael@0 369 nsJPEGEncoder::initDestination(jpeg_compress_struct* cinfo)
michael@0 370 {
michael@0 371 nsJPEGEncoder* that = static_cast<nsJPEGEncoder*>(cinfo->client_data);
michael@0 372 NS_ASSERTION(! that->mImageBuffer, "Image buffer already initialized");
michael@0 373
michael@0 374 that->mImageBufferSize = 8192;
michael@0 375 that->mImageBuffer = (uint8_t*)moz_malloc(that->mImageBufferSize);
michael@0 376 that->mImageBufferUsed = 0;
michael@0 377
michael@0 378 cinfo->dest->next_output_byte = that->mImageBuffer;
michael@0 379 cinfo->dest->free_in_buffer = that->mImageBufferSize;
michael@0 380 }
michael@0 381
michael@0 382
michael@0 383 // nsJPEGEncoder::emptyOutputBuffer
michael@0 384 //
michael@0 385 // This is called whenever the buffer has filled (free_in_buffer reaches
michael@0 386 // zero). In typical applications, it should write out the *entire* buffer
michael@0 387 // (use the saved start address and buffer length; ignore the current state
michael@0 388 // of next_output_byte and free_in_buffer). Then reset the pointer & count
michael@0 389 // to the start of the buffer, and return TRUE indicating that the buffer
michael@0 390 // has been dumped. free_in_buffer must be set to a positive value when
michael@0 391 // TRUE is returned. A FALSE return should only be used when I/O suspension
michael@0 392 // is desired (this operating mode is discussed in the next section).
michael@0 393
michael@0 394 boolean // static
michael@0 395 nsJPEGEncoder::emptyOutputBuffer(jpeg_compress_struct* cinfo)
michael@0 396 {
michael@0 397 nsJPEGEncoder* that = static_cast<nsJPEGEncoder*>(cinfo->client_data);
michael@0 398 NS_ASSERTION(that->mImageBuffer, "No buffer to empty!");
michael@0 399
michael@0 400 // When we're reallocing the buffer we need to take the lock to ensure
michael@0 401 // that nobody is trying to read from the buffer we are destroying
michael@0 402 ReentrantMonitorAutoEnter autoEnter(that->mReentrantMonitor);
michael@0 403
michael@0 404 that->mImageBufferUsed = that->mImageBufferSize;
michael@0 405
michael@0 406 // expand buffer, just double size each time
michael@0 407 that->mImageBufferSize *= 2;
michael@0 408
michael@0 409 uint8_t* newBuf = (uint8_t*)moz_realloc(that->mImageBuffer,
michael@0 410 that->mImageBufferSize);
michael@0 411 if (! newBuf) {
michael@0 412 // can't resize, just zero (this will keep us from writing more)
michael@0 413 moz_free(that->mImageBuffer);
michael@0 414 that->mImageBuffer = nullptr;
michael@0 415 that->mImageBufferSize = 0;
michael@0 416 that->mImageBufferUsed = 0;
michael@0 417
michael@0 418 // This seems to be the only way to do errors through the JPEG library. We
michael@0 419 // pass an nsresult masquerading as an int, which works because the
michael@0 420 // setjmp() caller casts it back.
michael@0 421 longjmp(((encoder_error_mgr*)(cinfo->err))->setjmp_buffer,
michael@0 422 static_cast<int>(NS_ERROR_OUT_OF_MEMORY));
michael@0 423 }
michael@0 424 that->mImageBuffer = newBuf;
michael@0 425
michael@0 426 cinfo->dest->next_output_byte = &that->mImageBuffer[that->mImageBufferUsed];
michael@0 427 cinfo->dest->free_in_buffer = that->mImageBufferSize - that->mImageBufferUsed;
michael@0 428 return 1;
michael@0 429 }
michael@0 430
michael@0 431
michael@0 432 // nsJPEGEncoder::termDestination
michael@0 433 //
michael@0 434 // Terminate destination --- called by jpeg_finish_compress() after all data
michael@0 435 // has been written. In most applications, this must flush any data
michael@0 436 // remaining in the buffer. Use either next_output_byte or free_in_buffer
michael@0 437 // to determine how much data is in the buffer.
michael@0 438
michael@0 439 void // static
michael@0 440 nsJPEGEncoder::termDestination(jpeg_compress_struct* cinfo)
michael@0 441 {
michael@0 442 nsJPEGEncoder* that = static_cast<nsJPEGEncoder*>(cinfo->client_data);
michael@0 443 if (! that->mImageBuffer)
michael@0 444 return;
michael@0 445 that->mImageBufferUsed = cinfo->dest->next_output_byte - that->mImageBuffer;
michael@0 446 NS_ASSERTION(that->mImageBufferUsed < that->mImageBufferSize,
michael@0 447 "JPEG library busted, got a bad image buffer size");
michael@0 448 that->NotifyListener();
michael@0 449 }
michael@0 450
michael@0 451
michael@0 452 // nsJPEGEncoder::errorExit
michael@0 453 //
michael@0 454 // Override the standard error method in the IJG JPEG decoder code. This
michael@0 455 // was mostly copied from nsJPEGDecoder.cpp
michael@0 456
michael@0 457 void // static
michael@0 458 nsJPEGEncoder::errorExit(jpeg_common_struct* cinfo)
michael@0 459 {
michael@0 460 nsresult error_code;
michael@0 461 encoder_error_mgr *err = (encoder_error_mgr *) cinfo->err;
michael@0 462
michael@0 463 // Convert error to a browser error code
michael@0 464 switch (cinfo->err->msg_code) {
michael@0 465 case JERR_OUT_OF_MEMORY:
michael@0 466 error_code = NS_ERROR_OUT_OF_MEMORY;
michael@0 467 break;
michael@0 468 default:
michael@0 469 error_code = NS_ERROR_FAILURE;
michael@0 470 }
michael@0 471
michael@0 472 // Return control to the setjmp point. We pass an nsresult masquerading as
michael@0 473 // an int, which works because the setjmp() caller casts it back.
michael@0 474 longjmp(err->setjmp_buffer, static_cast<int>(error_code));
michael@0 475 }
michael@0 476
michael@0 477 void
michael@0 478 nsJPEGEncoder::NotifyListener()
michael@0 479 {
michael@0 480 // We might call this function on multiple threads (any threads that call
michael@0 481 // AsyncWait and any that do encoding) so we lock to avoid notifying the
michael@0 482 // listener twice about the same data (which generally leads to a truncated
michael@0 483 // image).
michael@0 484 ReentrantMonitorAutoEnter autoEnter(mReentrantMonitor);
michael@0 485
michael@0 486 if (mCallback &&
michael@0 487 (mImageBufferUsed - mImageBufferReadPoint >= mNotifyThreshold ||
michael@0 488 mFinished)) {
michael@0 489 nsCOMPtr<nsIInputStreamCallback> callback;
michael@0 490 if (mCallbackTarget) {
michael@0 491 callback = NS_NewInputStreamReadyEvent(mCallback, mCallbackTarget);
michael@0 492 } else {
michael@0 493 callback = mCallback;
michael@0 494 }
michael@0 495
michael@0 496 NS_ASSERTION(callback, "Shouldn't fail to make the callback");
michael@0 497 // Null the callback first because OnInputStreamReady could reenter
michael@0 498 // AsyncWait
michael@0 499 mCallback = nullptr;
michael@0 500 mCallbackTarget = nullptr;
michael@0 501 mNotifyThreshold = 0;
michael@0 502
michael@0 503 callback->OnInputStreamReady(this);
michael@0 504 }
michael@0 505 }

mercurial