LCOV - code coverage report
Current view: top level - image/encoders/jpeg - nsJPEGEncoder.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 218 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 25 0.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13