/src/mozilla-central/image/encoders/jpeg/nsJPEGEncoder.cpp
Line | Count | Source (jump to first uncovered line) |
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 | | 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 | | 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 | | mReentrantMonitor("nsJPEGEncoder.mReentrantMonitor") |
36 | 0 | { |
37 | 0 | } |
38 | | |
39 | | nsJPEGEncoder::~nsJPEGEncoder() |
40 | 0 | { |
41 | 0 | if (mImageBuffer) { |
42 | 0 | free(mImageBuffer); |
43 | 0 | mImageBuffer = nullptr; |
44 | 0 | } |
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 | | 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 | 0 | { |
64 | 0 | NS_ENSURE_ARG(aData); |
65 | 0 |
|
66 | 0 | // validate input format |
67 | 0 | if (aInputFormat != INPUT_FORMAT_RGB && |
68 | 0 | aInputFormat != INPUT_FORMAT_RGBA && |
69 | 0 | aInputFormat != INPUT_FORMAT_HOSTARGB) |
70 | 0 | return NS_ERROR_INVALID_ARG; |
71 | 0 | |
72 | 0 | // Stride is the padded width of each row, so it better be longer (I'm afraid |
73 | 0 | // 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 | 0 | } |
82 | 0 |
|
83 | 0 | // can't initialize more than once |
84 | 0 | if (mImageBuffer != nullptr) { |
85 | 0 | return NS_ERROR_ALREADY_INITIALIZED; |
86 | 0 | } |
87 | 0 | |
88 | 0 | // options: we only have one option so this is easy |
89 | 0 | int quality = 92; |
90 | 0 | if (aOutputOptions.Length() > 0) { |
91 | 0 | // 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 | 0 | // have quality string |
96 | 0 | 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 | 0 | } else { |
104 | 0 | NS_WARNING("Quality value out of range, should be 0-100," |
105 | 0 | " using default"); |
106 | 0 | } |
107 | 0 | } else { |
108 | 0 | NS_WARNING("Quality value invalid, should be integer 0-100," |
109 | 0 | " using default"); |
110 | 0 | } |
111 | 0 | } |
112 | 0 | else { |
113 | 0 | return NS_ERROR_INVALID_ARG; |
114 | 0 | } |
115 | 0 | } |
116 | 0 | |
117 | 0 | jpeg_compress_struct cinfo; |
118 | 0 |
|
119 | 0 | // We set up the normal JPEG error routines, then override error_exit. |
120 | 0 | // This must be done before the call to create_compress |
121 | 0 | encoder_error_mgr errmgr; |
122 | 0 | cinfo.err = jpeg_std_error(&errmgr.pub); |
123 | 0 | errmgr.pub.error_exit = errorExit; |
124 | 0 | // Establish the setjmp return context for my_error_exit to use. |
125 | 0 | if (setjmp(errmgr.setjmp_buffer)) { |
126 | 0 | // If we get here, the JPEG code has signaled an error. |
127 | 0 | // We need to clean up the JPEG object, close the input file, and return. |
128 | 0 | return NS_ERROR_FAILURE; |
129 | 0 | } |
130 | 0 | |
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 | 0 |
|
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 | 0 | 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 | 0 | } |
146 | 0 | } |
147 | 0 |
|
148 | 0 | // set up the destination manager |
149 | 0 | 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 | 0 |
|
156 | 0 | jpeg_start_compress(&cinfo, 1); |
157 | 0 |
|
158 | 0 | // 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 | 0 | } |
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 | 0 | } |
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 | 0 | } |
178 | 0 | } |
179 | 0 |
|
180 | 0 | jpeg_finish_compress(&cinfo); |
181 | 0 | jpeg_destroy_compress(&cinfo); |
182 | 0 |
|
183 | 0 | mFinished = true; |
184 | 0 | NotifyListener(); |
185 | 0 |
|
186 | 0 | // 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 | 0 | } |
190 | 0 | |
191 | 0 | return NS_OK; |
192 | 0 | } |
193 | | |
194 | | |
195 | | NS_IMETHODIMP |
196 | | nsJPEGEncoder::StartImageEncode(uint32_t aWidth, |
197 | | uint32_t aHeight, |
198 | | uint32_t aInputFormat, |
199 | | const nsAString& aOutputOptions) |
200 | 0 | { |
201 | 0 | return NS_ERROR_NOT_IMPLEMENTED; |
202 | 0 | } |
203 | | |
204 | | // Returns the number of bytes in the image buffer used. |
205 | | NS_IMETHODIMP |
206 | | nsJPEGEncoder::GetImageBufferUsed(uint32_t* aOutputSize) |
207 | 0 | { |
208 | 0 | NS_ENSURE_ARG_POINTER(aOutputSize); |
209 | 0 | *aOutputSize = mImageBufferUsed; |
210 | 0 | return NS_OK; |
211 | 0 | } |
212 | | |
213 | | // Returns a pointer to the start of the image buffer |
214 | | NS_IMETHODIMP |
215 | | nsJPEGEncoder::GetImageBuffer(char** aOutputBuffer) |
216 | 0 | { |
217 | 0 | NS_ENSURE_ARG_POINTER(aOutputBuffer); |
218 | 0 | *aOutputBuffer = reinterpret_cast<char*>(mImageBuffer); |
219 | 0 | return NS_OK; |
220 | 0 | } |
221 | | |
222 | | NS_IMETHODIMP |
223 | | 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 | 0 | { |
231 | 0 | return NS_ERROR_NOT_IMPLEMENTED; |
232 | 0 | } |
233 | | |
234 | | NS_IMETHODIMP |
235 | | nsJPEGEncoder::EndImageEncode() |
236 | 0 | { |
237 | 0 | return NS_ERROR_NOT_IMPLEMENTED; |
238 | 0 | } |
239 | | |
240 | | |
241 | | NS_IMETHODIMP |
242 | | nsJPEGEncoder::Close() |
243 | 0 | { |
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 | 0 | } |
251 | 0 | return NS_OK; |
252 | 0 | } |
253 | | |
254 | | NS_IMETHODIMP |
255 | | nsJPEGEncoder::Available(uint64_t* _retval) |
256 | 0 | { |
257 | 0 | if (!mImageBuffer) { |
258 | 0 | return NS_BASE_STREAM_CLOSED; |
259 | 0 | } |
260 | 0 | |
261 | 0 | *_retval = mImageBufferUsed - mImageBufferReadPoint; |
262 | 0 | return NS_OK; |
263 | 0 | } |
264 | | |
265 | | NS_IMETHODIMP |
266 | | nsJPEGEncoder::Read(char* aBuf, uint32_t aCount, uint32_t* _retval) |
267 | 0 | { |
268 | 0 | return ReadSegments(NS_CopySegmentToBuffer, aBuf, aCount, _retval); |
269 | 0 | } |
270 | | |
271 | | NS_IMETHODIMP |
272 | | nsJPEGEncoder::ReadSegments(nsWriteSegmentFun aWriter, |
273 | | void* aClosure, uint32_t aCount, uint32_t* _retval) |
274 | 0 | { |
275 | 0 | // Avoid another thread reallocing the buffer underneath us |
276 | 0 | ReentrantMonitorAutoEnter autoEnter(mReentrantMonitor); |
277 | 0 |
|
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 | 0 | } |
283 | 0 |
|
284 | 0 | if (aCount > maxCount) { |
285 | 0 | aCount = maxCount; |
286 | 0 | } |
287 | 0 | nsresult rv = aWriter(this, aClosure, |
288 | 0 | 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 | 0 | } |
295 | 0 |
|
296 | 0 | // errors returned from the writer end here! |
297 | 0 | return NS_OK; |
298 | 0 | } |
299 | | |
300 | | NS_IMETHODIMP |
301 | | nsJPEGEncoder::IsNonBlocking(bool* _retval) |
302 | 0 | { |
303 | 0 | *_retval = true; |
304 | 0 | return NS_OK; |
305 | 0 | } |
306 | | |
307 | | NS_IMETHODIMP |
308 | | nsJPEGEncoder::AsyncWait(nsIInputStreamCallback* aCallback, |
309 | | uint32_t aFlags, uint32_t aRequestedCount, |
310 | | nsIEventTarget* aTarget) |
311 | 0 | { |
312 | 0 | if (aFlags != 0) { |
313 | 0 | return NS_ERROR_NOT_IMPLEMENTED; |
314 | 0 | } |
315 | 0 | |
316 | 0 | if (mCallback || mCallbackTarget) { |
317 | 0 | return NS_ERROR_UNEXPECTED; |
318 | 0 | } |
319 | 0 | |
320 | 0 | mCallbackTarget = aTarget; |
321 | 0 | // 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 | 0 | // notify incessantly |
326 | 0 | } |
327 | 0 |
|
328 | 0 | // We set the callback absolutely last, because NotifyListener uses it to |
329 | 0 | // determine if someone needs to be notified. If we don't set it last, |
330 | 0 | // NotifyListener might try to fire off a notification to a null target |
331 | 0 | // which will generally cause non-threadsafe objects to be used off the |
332 | 0 | // main thread |
333 | 0 | mCallback = aCallback; |
334 | 0 |
|
335 | 0 | // What we are being asked for may be present already |
336 | 0 | NotifyListener(); |
337 | 0 | return NS_OK; |
338 | 0 | } |
339 | | |
340 | | NS_IMETHODIMP |
341 | | nsJPEGEncoder::CloseWithStatus(nsresult aStatus) |
342 | 0 | { |
343 | 0 | return Close(); |
344 | 0 | } |
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 | | nsJPEGEncoder::ConvertHostARGBRow(const uint8_t* aSrc, uint8_t* aDest, |
356 | | uint32_t aPixelWidth) |
357 | 0 | { |
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 | 0 |
|
362 | 0 | pixelOut[0] = (pixelIn & 0xff0000) >> 16; |
363 | 0 | pixelOut[1] = (pixelIn & 0x00ff00) >> 8; |
364 | 0 | pixelOut[2] = (pixelIn & 0x0000ff) >> 0; |
365 | 0 | } |
366 | 0 | } |
367 | | |
368 | | /** |
369 | | * nsJPEGEncoder::ConvertRGBARow |
370 | | * |
371 | | * Input is RGBA, output is RGB, so we should alpha-premultiply. |
372 | | */ |
373 | | void |
374 | | nsJPEGEncoder::ConvertRGBARow(const uint8_t* aSrc, uint8_t* aDest, |
375 | | uint32_t aPixelWidth) |
376 | 0 | { |
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 | 0 |
|
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 | 0 | } |
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 | | nsJPEGEncoder::initDestination(jpeg_compress_struct* cinfo) |
396 | 0 | { |
397 | 0 | nsJPEGEncoder* that = static_cast<nsJPEGEncoder*>(cinfo->client_data); |
398 | 0 | NS_ASSERTION(!that->mImageBuffer, "Image buffer already initialized"); |
399 | 0 |
|
400 | 0 | that->mImageBufferSize = 8192; |
401 | 0 | that->mImageBuffer = (uint8_t*)malloc(that->mImageBufferSize); |
402 | 0 | that->mImageBufferUsed = 0; |
403 | 0 |
|
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 | | nsJPEGEncoder::emptyOutputBuffer(jpeg_compress_struct* cinfo) |
422 | 0 | { |
423 | 0 | nsJPEGEncoder* that = static_cast<nsJPEGEncoder*>(cinfo->client_data); |
424 | 0 | NS_ASSERTION(that->mImageBuffer, "No buffer to empty!"); |
425 | 0 |
|
426 | 0 | // When we're reallocing the buffer we need to take the lock to ensure |
427 | 0 | // that nobody is trying to read from the buffer we are destroying |
428 | 0 | ReentrantMonitorAutoEnter autoEnter(that->mReentrantMonitor); |
429 | 0 |
|
430 | 0 | that->mImageBufferUsed = that->mImageBufferSize; |
431 | 0 |
|
432 | 0 | // expand buffer, just double size each time |
433 | 0 | that->mImageBufferSize *= 2; |
434 | 0 |
|
435 | 0 | uint8_t* newBuf = (uint8_t*)realloc(that->mImageBuffer, |
436 | 0 | that->mImageBufferSize); |
437 | 0 | if (!newBuf) { |
438 | 0 | // 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 | 0 |
|
444 | 0 | // This seems to be the only way to do errors through the JPEG library. We |
445 | 0 | // pass an nsresult masquerading as an int, which works because the |
446 | 0 | // 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 | 0 | } |
450 | 0 | that->mImageBuffer = newBuf; |
451 | 0 |
|
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 | 0 | } |
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 | | nsJPEGEncoder::termDestination(jpeg_compress_struct* cinfo) |
467 | 0 | { |
468 | 0 | nsJPEGEncoder* that = static_cast<nsJPEGEncoder*>(cinfo->client_data); |
469 | 0 | if (!that->mImageBuffer) { |
470 | 0 | return; |
471 | 0 | } |
472 | 0 | that->mImageBufferUsed = cinfo->dest->next_output_byte - that->mImageBuffer; |
473 | 0 | NS_ASSERTION(that->mImageBufferUsed < that->mImageBufferSize, |
474 | 0 | "JPEG library busted, got a bad image buffer size"); |
475 | 0 | that->NotifyListener(); |
476 | 0 | } |
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 | | nsJPEGEncoder::errorExit(jpeg_common_struct* cinfo) |
486 | 0 | { |
487 | 0 | nsresult error_code; |
488 | 0 | encoder_error_mgr* err = (encoder_error_mgr*) cinfo->err; |
489 | 0 |
|
490 | 0 | // Convert error to a browser error code |
491 | 0 | switch (cinfo->err->msg_code) { |
492 | 0 | case JERR_OUT_OF_MEMORY: |
493 | 0 | error_code = NS_ERROR_OUT_OF_MEMORY; |
494 | 0 | break; |
495 | 0 | default: |
496 | 0 | error_code = NS_ERROR_FAILURE; |
497 | 0 | } |
498 | 0 |
|
499 | 0 | // Return control to the setjmp point. We pass an nsresult masquerading as |
500 | 0 | // an int, which works because the setjmp() caller casts it back. |
501 | 0 | longjmp(err->setjmp_buffer, static_cast<int>(error_code)); |
502 | 0 | } |
503 | | |
504 | | void |
505 | | nsJPEGEncoder::NotifyListener() |
506 | 0 | { |
507 | 0 | // We might call this function on multiple threads (any threads that call |
508 | 0 | // AsyncWait and any that do encoding) so we lock to avoid notifying the |
509 | 0 | // listener twice about the same data (which generally leads to a truncated |
510 | 0 | // image). |
511 | 0 | ReentrantMonitorAutoEnter autoEnter(mReentrantMonitor); |
512 | 0 |
|
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 | 0 | } else { |
521 | 0 | callback = mCallback; |
522 | 0 | } |
523 | 0 |
|
524 | 0 | NS_ASSERTION(callback, "Shouldn't fail to make the callback"); |
525 | 0 | // Null the callback first because OnInputStreamReady could reenter |
526 | 0 | // AsyncWait |
527 | 0 | mCallback = nullptr; |
528 | 0 | mCallbackTarget = nullptr; |
529 | 0 | mNotifyThreshold = 0; |
530 | 0 |
|
531 | 0 | callback->OnInputStreamReady(this); |
532 | 0 | } |
533 | 0 | } |