mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 14:30:27 +00:00
c8732098b3
- Bug 1246851 (Part 1) - Add a new SurfacePipe API for writing to image surfaces in a safe and composable manner. r=njn (2e3353a6a7) - Bug 1246851 (Part 2) - Add SurfaceFilter implementations for basic surface output operations. r=njn (fa30948a3f) - Bug 1246851 (Part 3) - Add a factory for constructing SurfacePipes. r=njn (7a746d3ec8) - Bug 1191347 - Explicitly release surfaces on the main thread in TestDecodeToSurface. r=me (fa1a68dc9a) - Bug 1246851 (Part 4) - Add a test suite for SurfacePipes and SurfaceFilters. r=njn (380dc94c08) - Bug 1247152 (Part 1) - Use SurfacePipe in the GIF decoder. r=njn (8b3d76f017) - Bug 1247152 (Part 2) - Remove even more code from the GIF decoder. r=edwin (80c8cc5e1c) - side effects Bug 1247152 (Part 1) (5b6cbf0ad5) - Bug 1237232 - Properly check the result of Vector append() calls in security/. r=keeler (b06dce061f) - Bug 1235308 - Fix -Wimplicit-fallthrough warnings in security/. r=keeler (ac5c3bf540) - Bug 1235188 - Fix -Wformat warnings in security/certverifier/. r=keeler (25ee7ee77c) - Bug 1004149 - Remove some dead code. r=keeler (59fedcb6a6) - Bug 1004149 - Return mozilla::pkix::Result values in nsNSSHttpInterface functions. r=keeler (76933c7d94) - Bug 1187173 - Disable warning C4623 on security/certverifier. r=briansmith (c1353b0577) - Bug 1256484 - Disable C4456 and C4458 to unblock compilation on VS2015; r=keeler (b51ac368ba) - Bug 1251009 - Remove unused nsICertificateDialogs.notifyCACertExists() method. r=keeler, r=mfinkle (9ca9aee3d0) - Bug 1249224 - window.open() should open a new window in the same container, r=bz (c57c76ec2f) - Bug 1245451 - add default nullptr value to mChrome. r=bsmedberg (3236ea34df) - Bug 1255849. Add some documentation for AutoJSAPI instances that seem to be used just for cxpushing. r=bholley (60a81652de) - Bug 1189554 - Make Saved Passwords dialog resizable on Windows again. r=smaug (a367796251) - Bug 1252974 - Convert screen's available dimensions to CSS-pixel units in nsWindowWatcher. r=emk (5524055ab0) - Bug 1233328 - Part 1: Ignore SHA-1 pins in PublicKeyPinningService.cpp. r=keeler (e5fe732a4b) - Bug 1233328 - Part 2: Use SHA-256 StaticFingerprints directly instead창 of StaticPinset since the SHA-1 StaticFingerprints entry will always be null. r=keeler (50f88c76da) - Bug 1256089 - Fix Mutex support for tier-3 platforms; r=froydnj (76ab483843) - Bug 1257019 - Add move construction to js::Mutex<T>. r=terrence (383ad474ba) - No bug, Automated HPKP preload list update from host bld-linux64-spot-309 - a=hpkp-update (0a6d3b9c40) - No bug, Automated HPKP preload list update from host bld-linux64-spot-223 - a=hpkp-update (7b3dbac6f8) - Bug 1255655 - Const-ify kPinset_* arrays. r=cykesiopka. (ddbaad40a0) - No bug, Automated HPKP preload list update from host bld-linux64-spot-543 - a=hpkp-update (f5c37e366a) - bug 1233853 - make nsSyncJPAKE aware of NSS shutdown r=jcj (9f74b5a46f) - Bug 1215734 - Expand GeckoMediaPlugin sandbox policy for Clang 3.7 ASan. r=kang (a96e0429e5) - part Bug 1154738 - Fixed WMFUtils.cpp compilation on mingw (9ba8bd1481) - Bug 1256498. Explicitly convert to float. r=bas (ce97ef100e) - Bug 1223736 - Part 1: Set correct effective transform on mask layers. r=thinker (a87514ac4f) - Bug 1249813 - part 1 - revise nsShmImage to allow draw targets anywhere inside its bounds. r=jrmuizel (cba4cdf8c9) - Bug 1249813 - part 2 - make Cairo mark a surface as clear if clip covers entire surface. r=jrmuizel (45ccfcfc15) - Bug 1249813 - part 3 - tell the compositor if the root layer has opaque content so it can skip clears. r=mattwoodrow (2abaa0cf8b) - Bug 1255303 - Use SurfaceFormat::B8G8R8X8 as back buffer if possible r=jrmuizel (5f81a83123) - Bug 1253860 - Don't update the scrollbar unless we're actually painting. r=mstange (d3a2482408) - Bug 1253860 - Add a flag on scroll frames indicating if they have an APZ counterpart. r=mstange (2aef746ee1) - Bug 1253860 - Skip paints for main thread scrolls if we can ask APZ to handle the scrolling for us. r=mstange (4483da1f16) - Bug 1244116 - Telemetry for mixed content requests by plugins. r=smaug, p=ally (fd5f87f666) - Bug 1252829 - CSP Telemetry. r=ckerschb, p=bsmedberg (8d340fa824) - Bug 1246464: Add 'const' to some stylesheet args in nsDocument methods. r=heycam (4e744d81d2) - Bug 1255705 - Add some useful logging that can be enabled at compile time. r=botond (80f0202160) - Bug 1255856 - Don't allow paint-skipping if there are windowed plugins on the page. r=mstange,jimm (25e6d8ed22) - Bug 1256727 - Don't allow paint-skipping on pages with scroll-linked effects. r=mstange (a8bace52ff) - Bug 1247098 - Take document resolution into account when computing root composition bounds for displayport base. r=tnikkel (1b7b61c82e) - Bug 1250550 - Ensure a scroll event posted during a refresh driver tick fires during that same tick. r=mats (d5bfc24524) - Bug 1253739 - Fix incorrect namespace on forward declaration. r=botond (dfc7aac51e) - Bug 1253489 - Update SendFenceHandleIfPresent() r=nical (798c209dff) - Bug 1253860 - Stop APZC from reprocessing stale metrics on unrelated layer tree updates. r=botond (1013df0068) - Bug 1253860 - Add machinery to update APZ's scroll offset without a main-thread paint. r=botond (cb95baf9c6) - Bug 1239564 - Post translate maskSurface to renderTarget. r=roc (66c56d227d) - Bug 1223736 - Part 2: Draw masks in the correct coordinate space when doing 3d transforms in BasicCompositor. r=lsalzman (2d62616534) - Bug 1223736 - Part 3: Remove the distinction between 2d and 3d masks since it only adds complexity. r=Bas (61c2306875) - Bug 1137561 - Follow up VS2015 build error. r=masayuki (f12716a1ab) - Bug 1217275 Fix missing \n in IMMHandler::HandleDocumentFeed(), it was replaced to empty string accidentally r=m_kato (3315b1c270) - Bug 1243268 - Adjust ATOK workaround. r=masayuki (13af7184d1) - Bug 1242690 - If a drag block is interrupted by something else, have it create a new drag block when it resumes. r=rbarker (fe3dc8deac) - Bug 1242690 - Add untransforming of mouse events not in a drag block. r=rbarker (a39e715efe) - Bug 1241991 - Switch mTreeLock from a Monitor to a Mutex. r=kats (0eca284591) - Bug 1242690 - Further refine the mouse event untransformation code to only apply to events directed at a scrollbar. r=rbarker (5c92ca2807) - Bug 1251608 - Add a root-content annotation to the APZ test data structure. r=botond (a1ee8e496a) - Bug 1249748 - Ensure the mHandledByAPZ flag is set on WidgetTouchEvents that are handled by APZ. r=botond (11bdfae896) - Bug 1242690 - Don't apply the main-thread callback transform for events in a drag block. r=rbarker (3db1405b68) - Bug 1243589 - Use SingleTiledContentClient even for scrollable layers if the layer is smaller than a single tile. r=mattwoodrow (56bb664de1) - Bug 1250517 - Differentiate between no critical display port and empty critical display port in ClientTiledPaintedLayer; r=kats (1b809fac8e) - Bug 1255448 - Call ClientMultiTiledLayerBuffer::PaintThebes even when region to paint is empty. r=mattwoodrow (31ac878dc0) - Bug 1255907 - Fix unification build issues in APZ & Layers. r=kats (9829525402) - Bug 1256344 - If a long-press is interrupted by a non-touch block, don't dispatch the long-press event. r=botond (b3bdd0e58e) - Bug 1256341 - Guard against scenarios where GenerateSingleTap is called without an active touch block. r=botond (d4ec208407) - Bug 1230552 - Minor follow-up to add an assertion. rs=kats (6f9eec7bd4) - Bug 1247964 - Allow InputBlockState::SetScrolledApzc to accept new APZC when it is an ancestor of the current APZC r=kats # Please enter the commit message for your changes. Lines starting (08ce9cfa98) - Bug 1257264 - When apz.allow_immediate_handoff=false, APZ handoff should not occur when panning changes direction r=botond (6ec9ec22e6) - Bug 1250213 - Ensure the scroll offset does not go outside the page bounds when going full screen r=kats (ace8e8a80c) - Bug 1241332 - part 1, Request content repaint at end of APZ animation r=kats (74f7e249f2) - Bug 1255705 - Generalize the NotifyLayersUpdated short-circuit codepath to trigger on empty transactions as well. r=botond (0344d44844) - Bug 1250614 - Repeated zooming in bug on mobile Wikipedia site r=botond (459f02b8d3) - Bug 1251001 - Input fields at the bottom of a page do not pan into view when gaining focus. r=botond (770102bf9f) - Bug 1190093 (Comment Tweak) - Add better comments for nsIDocument::mIsShowing and mVisible. r=me DONTBUILD (e3822ef086) - Bug 1217226: P1. Use VideoInfo display size data rather than attempt to detect value in stream. r=cpearce (a4a7e9b19a) - Bug 1217226: P2. Implement WMFMediaDataDecoder::ConfigurationChanged. r=cpearce (52177525c5) - fix misspatch (61a694fe3d) - Bug 1249706: Added telemetry for the proportion of frames dropped keyed by several details. r=jya (cb459d971c) - Bug 1202296 - Recreate the MFTDecoder when we want to disable DXVA. r=cpearce (d82999e94f) - Bug 1176071 - Handle WMF MFTDecoder returning success but no output, with telemetry. r=mattwoodrow,r=vladan (da8017c92b) - align, cleanup (9a86d51dfd) - no bug - fix case of nsIDocShell.h in WindowsUIUtils.cpp (0ccabaa445) - Bug 1187724 Don't dispatch KeyboardEvents when the target of WM_APPCOMMAND is a windowed plug-in for preventing deadlock r=jimm (7b6d83559c) - Bug 1187178 - Use MOZ_WINSDK_MAXVER instead of #ifndef. r=jimm (06c949ddb9) - bits of Bug 1165515 - Part 13-2: Replace usage of PRLogModuleLevel (7beaa2dcc4) - Bug 1257791: Return correct DPI and printing scale from nsDeviceContextSpecWin when printing to PDF. r=jimm (87360301ea) - Bug 1242295 - Fix compile error in nsDeviceContextSpecWin. r=jimm (d1d4680319) - Bug 1229881 - fix off-by-one error in nsPrinterEnumeratorWin::GetPrinterNameList; r=dbaron; a=KWierso (439061ba50) - Bug 1239683 - Replace NS_UNCONSTRAINEDSIZE with NS_MAXSIZE in windows/nsWindow.cpp. r=mats (4135189bec) - Bug 1033488 - Send RTL information to child process by WM_INPUTLANGCHANGE. r=masayuki (eaf8f70d34) - Bug 1238137 - Telemetry pings for main thread touch scrolling (Windows only). r=kats (f2194a2965) - Bug 1156182 - Ensure nsWindow::Destroy() is called before destroying mPresentLock to avoid a race with the compositor thread. r=Bas (0452ffc641) - Bug 1173617. Don't cache titlebar caption sizes unless the widget has a titlebar. r=jimm (b29e190504) - Bug 580165 - Clean up dead code related to missing screen managers in widget. r=jimm (bf83f21ddc) - Bug 1256501: Fix warning C4312 with 64-bit VS2015 in widget/windows/nsWindow.cpp; r=jimm (635151964e) - Bug 1242690 - Squash together DispatchAPZAwareEvent and DispatchInputEvent. r=dvander (d7b4fea361) - Bug 1242616: Add break in nsWindow.cpp WM_GETOBJECT handling. r=tbsaunde (eb516d4119) - Bug 1239353 - Don't try to change DPI on the fly for popup windows, they remain connected to their parent's presShell and therefore need to share its resolution. r=emk (1491b84fd1) - Bug 1246389 - Resize window appropriately on WM_DPICHANGED messages for dynamic resolution changes. r=emk (f3dbfcdbb1) - Bug 1254019 - Don't attempt to resize a maximized window on DPI change; and when handling a DPI change, constrain the resized window to the screen bounds. r=emk (2047c2dcb5) - Bug 1252191 - use UniquePtr instead of ScopedFreePtr in PoisonIOInterposerMac.cpp; r=aklotz (7c26e55b76) - Bug 1233208: Disable IOInterposer on Beta and Release; r=froydnj (04a6f8b07c) - crashreporter (377572bbb1) - Bug 1055322 - The realloc for libnestegg should free with size 0. r=froydnj (ea624646ca) - Bug 1236789. Avoid creating an unnecessary thread pool thread for tail-dispatch in TaskQueue. r=bholley (e7fb9e4373) - Bug 1255655 - Const-ify named CIDs. r=froydnj. (03b414d92b) - Bug 1250396 (part 1) - Document a subtle contraint on nsIAtom. r=froydnj. (ff279149ac) - Bug 1250396 (part 2) - Remove nsStaticAtomStringType. r=froydnj. (5520507680) - Bug 1255343 - Stop returning nsresult from NS_RegisterStaticAtoms; r=ehsan (de1d387937) - Bug 1251495 - remove unnecessary Logging.h include from nsStaticAtom.h; r=erahm (5226a840fa)
977 lines
32 KiB
C++
977 lines
32 KiB
C++
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "ImageLogging.h"
|
|
#include "imgFrame.h"
|
|
#include "nsJPEGDecoder.h"
|
|
#include "Orientation.h"
|
|
#include "EXIF.h"
|
|
|
|
#include "nsIInputStream.h"
|
|
|
|
#include "nspr.h"
|
|
#include "nsCRT.h"
|
|
#include "gfxColor.h"
|
|
|
|
#include "jerror.h"
|
|
|
|
#include "gfxPlatform.h"
|
|
#include "mozilla/Endian.h"
|
|
#include "mozilla/Telemetry.h"
|
|
|
|
extern "C" {
|
|
#include "iccjpeg.h"
|
|
}
|
|
|
|
#if MOZ_BIG_ENDIAN
|
|
#define MOZ_JCS_EXT_NATIVE_ENDIAN_XRGB JCS_EXT_XRGB
|
|
#else
|
|
#define MOZ_JCS_EXT_NATIVE_ENDIAN_XRGB JCS_EXT_BGRX
|
|
#endif
|
|
|
|
static void cmyk_convert_rgb(JSAMPROW row, JDIMENSION width);
|
|
|
|
namespace mozilla {
|
|
namespace image {
|
|
|
|
static mozilla::LazyLogModule sJPEGLog("JPEGDecoder");
|
|
|
|
static mozilla::LazyLogModule sJPEGDecoderAccountingLog("JPEGDecoderAccounting");
|
|
|
|
static qcms_profile*
|
|
GetICCProfile(struct jpeg_decompress_struct& info)
|
|
{
|
|
JOCTET* profilebuf;
|
|
uint32_t profileLength;
|
|
qcms_profile* profile = nullptr;
|
|
|
|
if (read_icc_profile(&info, &profilebuf, &profileLength)) {
|
|
profile = qcms_profile_from_memory(profilebuf, profileLength);
|
|
free(profilebuf);
|
|
}
|
|
|
|
return profile;
|
|
}
|
|
|
|
METHODDEF(void) init_source (j_decompress_ptr jd);
|
|
METHODDEF(boolean) fill_input_buffer (j_decompress_ptr jd);
|
|
METHODDEF(void) skip_input_data (j_decompress_ptr jd, long num_bytes);
|
|
METHODDEF(void) term_source (j_decompress_ptr jd);
|
|
METHODDEF(void) my_error_exit (j_common_ptr cinfo);
|
|
|
|
// Normal JFIF markers can't have more bytes than this.
|
|
#define MAX_JPEG_MARKER_LENGTH (((uint32_t)1 << 16) - 1)
|
|
|
|
nsJPEGDecoder::nsJPEGDecoder(RasterImage* aImage,
|
|
Decoder::DecodeStyle aDecodeStyle)
|
|
: Decoder(aImage)
|
|
, mDecodeStyle(aDecodeStyle)
|
|
, mSampleSize(0)
|
|
{
|
|
mState = JPEG_HEADER;
|
|
mReading = true;
|
|
mImageData = nullptr;
|
|
|
|
mBytesToSkip = 0;
|
|
memset(&mInfo, 0, sizeof(jpeg_decompress_struct));
|
|
memset(&mSourceMgr, 0, sizeof(mSourceMgr));
|
|
mInfo.client_data = (void*)this;
|
|
|
|
mSegment = nullptr;
|
|
mSegmentLen = 0;
|
|
|
|
mBackBuffer = nullptr;
|
|
mBackBufferLen = mBackBufferSize = mBackBufferUnreadLen = 0;
|
|
|
|
mInProfile = nullptr;
|
|
mTransform = nullptr;
|
|
|
|
mCMSMode = 0;
|
|
|
|
MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug,
|
|
("nsJPEGDecoder::nsJPEGDecoder: Creating JPEG decoder %p",
|
|
this));
|
|
}
|
|
|
|
nsJPEGDecoder::~nsJPEGDecoder()
|
|
{
|
|
// Step 8: Release JPEG decompression object
|
|
mInfo.src = nullptr;
|
|
jpeg_destroy_decompress(&mInfo);
|
|
|
|
PR_FREEIF(mBackBuffer);
|
|
if (mTransform) {
|
|
qcms_transform_release(mTransform);
|
|
}
|
|
if (mInProfile) {
|
|
qcms_profile_release(mInProfile);
|
|
}
|
|
|
|
MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug,
|
|
("nsJPEGDecoder::~nsJPEGDecoder: Destroying JPEG decoder %p",
|
|
this));
|
|
}
|
|
|
|
Telemetry::ID
|
|
nsJPEGDecoder::SpeedHistogram()
|
|
{
|
|
return Telemetry::IMAGE_DECODE_SPEED_JPEG;
|
|
}
|
|
|
|
void
|
|
nsJPEGDecoder::InitInternal()
|
|
{
|
|
mCMSMode = gfxPlatform::GetCMSMode();
|
|
if (GetSurfaceFlags() & SurfaceFlags::NO_COLORSPACE_CONVERSION) {
|
|
mCMSMode = eCMSMode_Off;
|
|
}
|
|
|
|
// We set up the normal JPEG error routines, then override error_exit.
|
|
mInfo.err = jpeg_std_error(&mErr.pub);
|
|
// mInfo.err = jpeg_std_error(&mErr.pub);
|
|
mErr.pub.error_exit = my_error_exit;
|
|
// Establish the setjmp return context for my_error_exit to use.
|
|
if (setjmp(mErr.setjmp_buffer)) {
|
|
// If we get here, the JPEG code has signaled an error.
|
|
// We need to clean up the JPEG object, close the input file, and return.
|
|
PostDecoderError(NS_ERROR_FAILURE);
|
|
return;
|
|
}
|
|
|
|
// Step 1: allocate and initialize JPEG decompression object
|
|
jpeg_create_decompress(&mInfo);
|
|
// Set the source manager
|
|
mInfo.src = &mSourceMgr;
|
|
|
|
// Step 2: specify data source (eg, a file)
|
|
|
|
// Setup callback functions.
|
|
mSourceMgr.init_source = init_source;
|
|
mSourceMgr.fill_input_buffer = fill_input_buffer;
|
|
mSourceMgr.skip_input_data = skip_input_data;
|
|
mSourceMgr.resync_to_restart = jpeg_resync_to_restart;
|
|
mSourceMgr.term_source = term_source;
|
|
|
|
// Record app markers for ICC data
|
|
for (uint32_t m = 0; m < 16; m++) {
|
|
jpeg_save_markers(&mInfo, JPEG_APP0 + m, 0xFFFF);
|
|
}
|
|
}
|
|
|
|
void
|
|
nsJPEGDecoder::FinishInternal()
|
|
{
|
|
// If we're not in any sort of error case, force our state to JPEG_DONE.
|
|
if ((mState != JPEG_DONE && mState != JPEG_SINK_NON_JPEG_TRAILER) &&
|
|
(mState != JPEG_ERROR) &&
|
|
!IsMetadataDecode()) {
|
|
mState = JPEG_DONE;
|
|
}
|
|
}
|
|
|
|
void
|
|
nsJPEGDecoder::WriteInternal(const char* aBuffer, uint32_t aCount)
|
|
{
|
|
mSegment = (const JOCTET*)aBuffer;
|
|
mSegmentLen = aCount;
|
|
|
|
MOZ_ASSERT(!HasError(), "Shouldn't call WriteInternal after error!");
|
|
|
|
// Return here if there is a fatal error within libjpeg.
|
|
nsresult error_code;
|
|
// This cast to nsresult makes sense because setjmp() returns whatever we
|
|
// passed to longjmp(), which was actually an nsresult.
|
|
if ((error_code = (nsresult)setjmp(mErr.setjmp_buffer)) != NS_OK) {
|
|
if (error_code == NS_ERROR_FAILURE) {
|
|
PostDataError();
|
|
// Error due to corrupt stream - return NS_OK and consume silently
|
|
// so that ImageLib doesn't throw away a partial image load
|
|
mState = JPEG_SINK_NON_JPEG_TRAILER;
|
|
MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug,
|
|
("} (setjmp returned NS_ERROR_FAILURE)"));
|
|
return;
|
|
} else {
|
|
// Error due to reasons external to the stream (probably out of
|
|
// memory) - let ImageLib attempt to clean up, even though
|
|
// mozilla is seconds away from falling flat on its face.
|
|
PostDecoderError(error_code);
|
|
mState = JPEG_ERROR;
|
|
MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug,
|
|
("} (setjmp returned an error)"));
|
|
return;
|
|
}
|
|
}
|
|
|
|
MOZ_LOG(sJPEGLog, LogLevel::Debug,
|
|
("[this=%p] nsJPEGDecoder::Write -- processing JPEG data\n", this));
|
|
|
|
switch (mState) {
|
|
case JPEG_HEADER: {
|
|
LOG_SCOPE((mozilla::LogModule*)sJPEGLog, "nsJPEGDecoder::Write -- entering JPEG_HEADER"
|
|
" case");
|
|
|
|
// Step 3: read file parameters with jpeg_read_header()
|
|
if (jpeg_read_header(&mInfo, TRUE) == JPEG_SUSPENDED) {
|
|
MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug,
|
|
("} (JPEG_SUSPENDED)"));
|
|
return; // I/O suspension
|
|
}
|
|
|
|
// If we have a sample size specified for -moz-sample-size, use it.
|
|
if (mSampleSize > 0) {
|
|
mInfo.scale_num = 1;
|
|
mInfo.scale_denom = mSampleSize;
|
|
}
|
|
|
|
// Used to set up image size so arrays can be allocated
|
|
jpeg_calc_output_dimensions(&mInfo);
|
|
|
|
// Post our size to the superclass
|
|
PostSize(mInfo.output_width, mInfo.output_height,
|
|
ReadOrientationFromEXIF());
|
|
if (HasError()) {
|
|
// Setting the size led to an error.
|
|
mState = JPEG_ERROR;
|
|
return;
|
|
}
|
|
|
|
// If we're doing a metadata decode, we're done.
|
|
if (IsMetadataDecode()) {
|
|
return;
|
|
}
|
|
|
|
// We're doing a full decode.
|
|
if (mCMSMode != eCMSMode_Off &&
|
|
(mInProfile = GetICCProfile(mInfo)) != nullptr) {
|
|
uint32_t profileSpace = qcms_profile_get_color_space(mInProfile);
|
|
bool mismatch = false;
|
|
|
|
#ifdef DEBUG_tor
|
|
fprintf(stderr, "JPEG profileSpace: 0x%08X\n", profileSpace);
|
|
#endif
|
|
switch (mInfo.jpeg_color_space) {
|
|
case JCS_GRAYSCALE:
|
|
if (profileSpace == icSigRgbData) {
|
|
mInfo.out_color_space = JCS_RGB;
|
|
} else if (profileSpace != icSigGrayData) {
|
|
mismatch = true;
|
|
}
|
|
break;
|
|
case JCS_RGB:
|
|
if (profileSpace != icSigRgbData) {
|
|
mismatch = true;
|
|
}
|
|
break;
|
|
case JCS_YCbCr:
|
|
if (profileSpace == icSigRgbData) {
|
|
mInfo.out_color_space = JCS_RGB;
|
|
} else {
|
|
// qcms doesn't support ycbcr
|
|
mismatch = true;
|
|
}
|
|
break;
|
|
case JCS_CMYK:
|
|
case JCS_YCCK:
|
|
// qcms doesn't support cmyk
|
|
mismatch = true;
|
|
break;
|
|
default:
|
|
mState = JPEG_ERROR;
|
|
PostDataError();
|
|
MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug,
|
|
("} (unknown colorpsace (1))"));
|
|
return;
|
|
}
|
|
|
|
if (!mismatch) {
|
|
qcms_data_type type;
|
|
switch (mInfo.out_color_space) {
|
|
case JCS_GRAYSCALE:
|
|
type = QCMS_DATA_GRAY_8;
|
|
break;
|
|
case JCS_RGB:
|
|
type = QCMS_DATA_RGB_8;
|
|
break;
|
|
default:
|
|
mState = JPEG_ERROR;
|
|
PostDataError();
|
|
MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug,
|
|
("} (unknown colorpsace (2))"));
|
|
return;
|
|
}
|
|
#if 0
|
|
// We don't currently support CMYK profiles. The following
|
|
// code dealt with lcms types. Add something like this
|
|
// back when we gain support for CMYK.
|
|
|
|
// Adobe Photoshop writes YCCK/CMYK files with inverted data
|
|
if (mInfo.out_color_space == JCS_CMYK) {
|
|
type |= FLAVOR_SH(mInfo.saw_Adobe_marker ? 1 : 0);
|
|
}
|
|
#endif
|
|
|
|
if (gfxPlatform::GetCMSOutputProfile()) {
|
|
|
|
// Calculate rendering intent.
|
|
int intent = gfxPlatform::GetRenderingIntent();
|
|
if (intent == -1) {
|
|
intent = qcms_profile_get_rendering_intent(mInProfile);
|
|
}
|
|
|
|
// Create the color management transform.
|
|
mTransform = qcms_transform_create(mInProfile,
|
|
type,
|
|
gfxPlatform::GetCMSOutputProfile(),
|
|
QCMS_DATA_RGB_8,
|
|
(qcms_intent)intent);
|
|
}
|
|
} else {
|
|
#ifdef DEBUG_tor
|
|
fprintf(stderr, "ICM profile colorspace mismatch\n");
|
|
#endif
|
|
}
|
|
}
|
|
|
|
if (!mTransform) {
|
|
switch (mInfo.jpeg_color_space) {
|
|
case JCS_GRAYSCALE:
|
|
case JCS_RGB:
|
|
case JCS_YCbCr:
|
|
// if we're not color managing we can decode directly to
|
|
// MOZ_JCS_EXT_NATIVE_ENDIAN_XRGB
|
|
if (mCMSMode != eCMSMode_All) {
|
|
mInfo.out_color_space = MOZ_JCS_EXT_NATIVE_ENDIAN_XRGB;
|
|
mInfo.out_color_components = 4;
|
|
} else {
|
|
mInfo.out_color_space = JCS_RGB;
|
|
}
|
|
break;
|
|
case JCS_CMYK:
|
|
case JCS_YCCK:
|
|
// libjpeg can convert from YCCK to CMYK, but not to RGB
|
|
mInfo.out_color_space = JCS_CMYK;
|
|
break;
|
|
default:
|
|
mState = JPEG_ERROR;
|
|
PostDataError();
|
|
MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug,
|
|
("} (unknown colorpsace (3))"));
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Don't allocate a giant and superfluous memory buffer
|
|
// when not doing a progressive decode.
|
|
mInfo.buffered_image = mDecodeStyle == PROGRESSIVE &&
|
|
jpeg_has_multiple_scans(&mInfo);
|
|
|
|
MOZ_ASSERT(!mImageData, "Already have a buffer allocated?");
|
|
nsIntSize targetSize = mDownscaler ? mDownscaler->TargetSize() : GetSize();
|
|
nsresult rv = AllocateFrame(0, targetSize,
|
|
nsIntRect(nsIntPoint(), targetSize),
|
|
gfx::SurfaceFormat::B8G8R8A8);
|
|
if (NS_FAILED(rv)) {
|
|
mState = JPEG_ERROR;
|
|
MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug,
|
|
("} (could not initialize image frame)"));
|
|
return;
|
|
}
|
|
|
|
MOZ_ASSERT(mImageData, "Should have a buffer now");
|
|
|
|
if (mDownscaler) {
|
|
nsresult rv = mDownscaler->BeginFrame(GetSize(), Nothing(),
|
|
mImageData,
|
|
/* aHasAlpha = */ false);
|
|
if (NS_FAILED(rv)) {
|
|
mState = JPEG_ERROR;
|
|
return;
|
|
}
|
|
}
|
|
|
|
MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug,
|
|
(" JPEGDecoderAccounting: nsJPEGDecoder::"
|
|
"Write -- created image frame with %ux%u pixels",
|
|
mInfo.output_width, mInfo.output_height));
|
|
|
|
mState = JPEG_START_DECOMPRESS;
|
|
MOZ_FALLTHROUGH; // to start decompressing.
|
|
}
|
|
|
|
case JPEG_START_DECOMPRESS: {
|
|
LOG_SCOPE((mozilla::LogModule*)sJPEGLog, "nsJPEGDecoder::Write -- entering"
|
|
" JPEG_START_DECOMPRESS case");
|
|
// Step 4: set parameters for decompression
|
|
|
|
// FIXME -- Should reset dct_method and dither mode
|
|
// for final pass of progressive JPEG
|
|
|
|
mInfo.dct_method = JDCT_ISLOW;
|
|
mInfo.dither_mode = JDITHER_FS;
|
|
mInfo.do_fancy_upsampling = TRUE;
|
|
mInfo.enable_2pass_quant = FALSE;
|
|
mInfo.do_block_smoothing = TRUE;
|
|
|
|
// Step 5: Start decompressor
|
|
if (jpeg_start_decompress(&mInfo) == FALSE) {
|
|
MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug,
|
|
("} (I/O suspension after jpeg_start_decompress())"));
|
|
return; // I/O suspension
|
|
}
|
|
|
|
// If this is a progressive JPEG ...
|
|
mState = mInfo.buffered_image ?
|
|
JPEG_DECOMPRESS_PROGRESSIVE : JPEG_DECOMPRESS_SEQUENTIAL;
|
|
MOZ_FALLTHROUGH; // to decompress sequential JPEG.
|
|
}
|
|
|
|
case JPEG_DECOMPRESS_SEQUENTIAL: {
|
|
if (mState == JPEG_DECOMPRESS_SEQUENTIAL) {
|
|
LOG_SCOPE((mozilla::LogModule*)sJPEGLog, "nsJPEGDecoder::Write -- "
|
|
"JPEG_DECOMPRESS_SEQUENTIAL case");
|
|
|
|
bool suspend;
|
|
OutputScanlines(&suspend);
|
|
|
|
if (suspend) {
|
|
MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug,
|
|
("} (I/O suspension after OutputScanlines() - SEQUENTIAL)"));
|
|
return; // I/O suspension
|
|
}
|
|
|
|
// If we've completed image output ...
|
|
NS_ASSERTION(mInfo.output_scanline == mInfo.output_height,
|
|
"We didn't process all of the data!");
|
|
mState = JPEG_DONE;
|
|
}
|
|
MOZ_FALLTHROUGH; // to decompress progressive JPEG.
|
|
}
|
|
|
|
case JPEG_DECOMPRESS_PROGRESSIVE: {
|
|
if (mState == JPEG_DECOMPRESS_PROGRESSIVE) {
|
|
LOG_SCOPE((mozilla::LogModule*)sJPEGLog,
|
|
"nsJPEGDecoder::Write -- JPEG_DECOMPRESS_PROGRESSIVE case");
|
|
|
|
int status;
|
|
do {
|
|
status = jpeg_consume_input(&mInfo);
|
|
} while ((status != JPEG_SUSPENDED) &&
|
|
(status != JPEG_REACHED_EOI));
|
|
|
|
for (;;) {
|
|
if (mInfo.output_scanline == 0) {
|
|
int scan = mInfo.input_scan_number;
|
|
|
|
// if we haven't displayed anything yet (output_scan_number==0)
|
|
// and we have enough data for a complete scan, force output
|
|
// of the last full scan
|
|
if ((mInfo.output_scan_number == 0) &&
|
|
(scan > 1) &&
|
|
(status != JPEG_REACHED_EOI))
|
|
scan--;
|
|
|
|
if (!jpeg_start_output(&mInfo, scan)) {
|
|
MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug,
|
|
("} (I/O suspension after jpeg_start_output() -"
|
|
" PROGRESSIVE)"));
|
|
return; // I/O suspension
|
|
}
|
|
}
|
|
|
|
if (mInfo.output_scanline == 0xffffff) {
|
|
mInfo.output_scanline = 0;
|
|
}
|
|
|
|
bool suspend;
|
|
OutputScanlines(&suspend);
|
|
|
|
if (suspend) {
|
|
if (mInfo.output_scanline == 0) {
|
|
// didn't manage to read any lines - flag so we don't call
|
|
// jpeg_start_output() multiple times for the same scan
|
|
mInfo.output_scanline = 0xffffff;
|
|
}
|
|
MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug,
|
|
("} (I/O suspension after OutputScanlines() - PROGRESSIVE)"));
|
|
return; // I/O suspension
|
|
}
|
|
|
|
if (mInfo.output_scanline == mInfo.output_height) {
|
|
if (!jpeg_finish_output(&mInfo)) {
|
|
MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug,
|
|
("} (I/O suspension after jpeg_finish_output() -"
|
|
" PROGRESSIVE)"));
|
|
return; // I/O suspension
|
|
}
|
|
|
|
if (jpeg_input_complete(&mInfo) &&
|
|
(mInfo.input_scan_number == mInfo.output_scan_number))
|
|
break;
|
|
|
|
mInfo.output_scanline = 0;
|
|
if (mDownscaler) {
|
|
mDownscaler->ResetForNextProgressivePass();
|
|
}
|
|
}
|
|
}
|
|
|
|
mState = JPEG_DONE;
|
|
}
|
|
MOZ_FALLTHROUGH; // to finish decompressing.
|
|
}
|
|
|
|
case JPEG_DONE: {
|
|
LOG_SCOPE((mozilla::LogModule*)sJPEGLog, "nsJPEGDecoder::ProcessData -- entering"
|
|
" JPEG_DONE case");
|
|
|
|
// Step 7: Finish decompression
|
|
|
|
if (jpeg_finish_decompress(&mInfo) == FALSE) {
|
|
MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug,
|
|
("} (I/O suspension after jpeg_finish_decompress() - DONE)"));
|
|
return; // I/O suspension
|
|
}
|
|
|
|
mState = JPEG_SINK_NON_JPEG_TRAILER;
|
|
|
|
// we're done dude
|
|
break;
|
|
}
|
|
case JPEG_SINK_NON_JPEG_TRAILER:
|
|
MOZ_LOG(sJPEGLog, LogLevel::Debug,
|
|
("[this=%p] nsJPEGDecoder::ProcessData -- entering"
|
|
" JPEG_SINK_NON_JPEG_TRAILER case\n", this));
|
|
|
|
break;
|
|
|
|
case JPEG_ERROR:
|
|
MOZ_ASSERT(false,
|
|
"Should always return immediately after error and not re-enter "
|
|
"decoder");
|
|
}
|
|
|
|
MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug,
|
|
("} (end of function)"));
|
|
return;
|
|
}
|
|
|
|
Orientation
|
|
nsJPEGDecoder::ReadOrientationFromEXIF()
|
|
{
|
|
jpeg_saved_marker_ptr marker;
|
|
|
|
// Locate the APP1 marker, where EXIF data is stored, in the marker list.
|
|
for (marker = mInfo.marker_list ; marker != nullptr ; marker = marker->next) {
|
|
if (marker->marker == JPEG_APP0 + 1) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If we're at the end of the list, there's no EXIF data.
|
|
if (!marker) {
|
|
return Orientation();
|
|
}
|
|
|
|
// Extract the orientation information.
|
|
EXIFData exif = EXIFParser::Parse(marker->data,
|
|
static_cast<uint32_t>(marker->data_length));
|
|
return exif.orientation;
|
|
}
|
|
|
|
void
|
|
nsJPEGDecoder::NotifyDone()
|
|
{
|
|
PostFrameStop(Opacity::FULLY_OPAQUE);
|
|
PostDecodeDone();
|
|
}
|
|
|
|
void
|
|
nsJPEGDecoder::OutputScanlines(bool* suspend)
|
|
{
|
|
*suspend = false;
|
|
|
|
const uint32_t top = mInfo.output_scanline;
|
|
|
|
while ((mInfo.output_scanline < mInfo.output_height)) {
|
|
uint32_t* imageRow = nullptr;
|
|
if (mDownscaler) {
|
|
imageRow = reinterpret_cast<uint32_t*>(mDownscaler->RowBuffer());
|
|
} else {
|
|
imageRow = reinterpret_cast<uint32_t*>(mImageData) +
|
|
(mInfo.output_scanline * mInfo.output_width);
|
|
}
|
|
|
|
MOZ_ASSERT(imageRow, "Should have a row buffer here");
|
|
|
|
if (mInfo.out_color_space == MOZ_JCS_EXT_NATIVE_ENDIAN_XRGB) {
|
|
// Special case: scanline will be directly converted into packed ARGB
|
|
if (jpeg_read_scanlines(&mInfo, (JSAMPARRAY)&imageRow, 1) != 1) {
|
|
*suspend = true; // suspend
|
|
break;
|
|
}
|
|
if (mDownscaler) {
|
|
mDownscaler->CommitRow();
|
|
}
|
|
continue; // all done for this row!
|
|
}
|
|
|
|
JSAMPROW sampleRow = (JSAMPROW)imageRow;
|
|
if (mInfo.output_components == 3) {
|
|
// Put the pixels at end of row to enable in-place expansion
|
|
sampleRow += mInfo.output_width;
|
|
}
|
|
|
|
// Request one scanline. Returns 0 or 1 scanlines.
|
|
if (jpeg_read_scanlines(&mInfo, &sampleRow, 1) != 1) {
|
|
*suspend = true; // suspend
|
|
break;
|
|
}
|
|
|
|
if (mTransform) {
|
|
JSAMPROW source = sampleRow;
|
|
if (mInfo.out_color_space == JCS_GRAYSCALE) {
|
|
// Convert from the 1byte grey pixels at begin of row
|
|
// to the 3byte RGB byte pixels at 'end' of row
|
|
sampleRow += mInfo.output_width;
|
|
}
|
|
qcms_transform_data(mTransform, source, sampleRow, mInfo.output_width);
|
|
// Move 3byte RGB data to end of row
|
|
if (mInfo.out_color_space == JCS_CMYK) {
|
|
memmove(sampleRow + mInfo.output_width,
|
|
sampleRow,
|
|
3 * mInfo.output_width);
|
|
sampleRow += mInfo.output_width;
|
|
}
|
|
} else {
|
|
if (mInfo.out_color_space == JCS_CMYK) {
|
|
// Convert from CMYK to RGB
|
|
// We cannot convert directly to Cairo, as the CMSRGBTransform
|
|
// may wants to do a RGB transform...
|
|
// Would be better to have platform CMSenabled transformation
|
|
// from CMYK to (A)RGB...
|
|
cmyk_convert_rgb((JSAMPROW)imageRow, mInfo.output_width);
|
|
sampleRow += mInfo.output_width;
|
|
}
|
|
if (mCMSMode == eCMSMode_All) {
|
|
// No embedded ICC profile - treat as sRGB
|
|
qcms_transform* transform = gfxPlatform::GetCMSRGBTransform();
|
|
if (transform) {
|
|
qcms_transform_data(transform, sampleRow, sampleRow,
|
|
mInfo.output_width);
|
|
}
|
|
}
|
|
}
|
|
|
|
// counter for while() loops below
|
|
uint32_t idx = mInfo.output_width;
|
|
|
|
// copy as bytes until source pointer is 32-bit-aligned
|
|
for (; (NS_PTR_TO_UINT32(sampleRow) & 0x3) && idx; --idx) {
|
|
*imageRow++ = gfxPackedPixel(0xFF, sampleRow[0], sampleRow[1],
|
|
sampleRow[2]);
|
|
sampleRow += 3;
|
|
}
|
|
|
|
// copy pixels in blocks of 4
|
|
while (idx >= 4) {
|
|
GFX_BLOCK_RGB_TO_FRGB(sampleRow, imageRow);
|
|
idx -= 4;
|
|
sampleRow += 12;
|
|
imageRow += 4;
|
|
}
|
|
|
|
// copy remaining pixel(s)
|
|
while (idx--) {
|
|
// 32-bit read of final pixel will exceed buffer, so read bytes
|
|
*imageRow++ = gfxPackedPixel(0xFF, sampleRow[0], sampleRow[1],
|
|
sampleRow[2]);
|
|
sampleRow += 3;
|
|
}
|
|
|
|
if (mDownscaler) {
|
|
mDownscaler->CommitRow();
|
|
}
|
|
}
|
|
|
|
if (mDownscaler && mDownscaler->HasInvalidation()) {
|
|
DownscalerInvalidRect invalidRect = mDownscaler->TakeInvalidRect();
|
|
PostInvalidation(invalidRect.mOriginalSizeRect,
|
|
Some(invalidRect.mTargetSizeRect));
|
|
MOZ_ASSERT(!mDownscaler->HasInvalidation());
|
|
} else if (!mDownscaler && top != mInfo.output_scanline) {
|
|
PostInvalidation(nsIntRect(0, top,
|
|
mInfo.output_width,
|
|
mInfo.output_scanline - top));
|
|
}
|
|
}
|
|
|
|
// Override the standard error method in the IJG JPEG decoder code.
|
|
METHODDEF(void)
|
|
my_error_exit (j_common_ptr cinfo)
|
|
{
|
|
decoder_error_mgr* err = (decoder_error_mgr*) cinfo->err;
|
|
|
|
// Convert error to a browser error code
|
|
nsresult error_code = err->pub.msg_code == JERR_OUT_OF_MEMORY
|
|
? NS_ERROR_OUT_OF_MEMORY
|
|
: NS_ERROR_FAILURE;
|
|
|
|
#ifdef DEBUG
|
|
char buffer[JMSG_LENGTH_MAX];
|
|
|
|
// Create the message
|
|
(*err->pub.format_message) (cinfo, buffer);
|
|
|
|
fprintf(stderr, "JPEG decoding error:\n%s\n", buffer);
|
|
#endif
|
|
|
|
// 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));
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* This is the callback routine from the IJG JPEG library used to supply new
|
|
* data to the decompressor when its input buffer is exhausted. It juggles
|
|
* multiple buffers in an attempt to avoid unnecessary copying of input data.
|
|
*
|
|
* (A simpler scheme is possible: It's much easier to use only a single
|
|
* buffer; when fill_input_buffer() is called, move any unconsumed data
|
|
* (beyond the current pointer/count) down to the beginning of this buffer and
|
|
* then load new data into the remaining buffer space. This approach requires
|
|
* a little more data copying but is far easier to get right.)
|
|
*
|
|
* At any one time, the JPEG decompressor is either reading from the necko
|
|
* input buffer, which is volatile across top-level calls to the IJG library,
|
|
* or the "backtrack" buffer. The backtrack buffer contains the remaining
|
|
* unconsumed data from the necko buffer after parsing was suspended due
|
|
* to insufficient data in some previous call to the IJG library.
|
|
*
|
|
* When suspending, the decompressor will back up to a convenient restart
|
|
* point (typically the start of the current MCU). The variables
|
|
* next_input_byte & bytes_in_buffer indicate where the restart point will be
|
|
* if the current call returns FALSE. Data beyond this point must be
|
|
* rescanned after resumption, so it must be preserved in case the decompressor
|
|
* decides to backtrack.
|
|
*
|
|
* Returns:
|
|
* TRUE if additional data is available, FALSE if no data present and
|
|
* the JPEG library should therefore suspend processing of input stream
|
|
******************************************************************************/
|
|
|
|
/******************************************************************************/
|
|
/* data source manager method */
|
|
/******************************************************************************/
|
|
|
|
/******************************************************************************/
|
|
/* data source manager method
|
|
Initialize source. This is called by jpeg_read_header() before any
|
|
data is actually read. May leave
|
|
bytes_in_buffer set to 0 (in which case a fill_input_buffer() call
|
|
will occur immediately).
|
|
*/
|
|
METHODDEF(void)
|
|
init_source (j_decompress_ptr jd)
|
|
{
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/* data source manager method
|
|
Skip num_bytes worth of data. The buffer pointer and count should
|
|
be advanced over num_bytes input bytes, refilling the buffer as
|
|
needed. This is used to skip over a potentially large amount of
|
|
uninteresting data (such as an APPn marker). In some applications
|
|
it may be possible to optimize away the reading of the skipped data,
|
|
but it's not clear that being smart is worth much trouble; large
|
|
skips are uncommon. bytes_in_buffer may be zero on return.
|
|
A zero or negative skip count should be treated as a no-op.
|
|
*/
|
|
METHODDEF(void)
|
|
skip_input_data (j_decompress_ptr jd, long num_bytes)
|
|
{
|
|
struct jpeg_source_mgr* src = jd->src;
|
|
nsJPEGDecoder* decoder = (nsJPEGDecoder*)(jd->client_data);
|
|
|
|
if (num_bytes > (long)src->bytes_in_buffer) {
|
|
// Can't skip it all right now until we get more data from
|
|
// network stream. Set things up so that fill_input_buffer
|
|
// will skip remaining amount.
|
|
decoder->mBytesToSkip = (size_t)num_bytes - src->bytes_in_buffer;
|
|
src->next_input_byte += src->bytes_in_buffer;
|
|
src->bytes_in_buffer = 0;
|
|
|
|
} else {
|
|
// Simple case. Just advance buffer pointer
|
|
|
|
src->bytes_in_buffer -= (size_t)num_bytes;
|
|
src->next_input_byte += num_bytes;
|
|
}
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/* data source manager method
|
|
This is called whenever bytes_in_buffer has reached zero and more
|
|
data is wanted. In typical applications, it should read fresh data
|
|
into the buffer (ignoring the current state of next_input_byte and
|
|
bytes_in_buffer), reset the pointer & count to the start of the
|
|
buffer, and return TRUE indicating that the buffer has been reloaded.
|
|
It is not necessary to fill the buffer entirely, only to obtain at
|
|
least one more byte. bytes_in_buffer MUST be set to a positive value
|
|
if TRUE is returned. A FALSE return should only be used when I/O
|
|
suspension is desired.
|
|
*/
|
|
METHODDEF(boolean)
|
|
fill_input_buffer (j_decompress_ptr jd)
|
|
{
|
|
struct jpeg_source_mgr* src = jd->src;
|
|
nsJPEGDecoder* decoder = (nsJPEGDecoder*)(jd->client_data);
|
|
|
|
if (decoder->mReading) {
|
|
const JOCTET* new_buffer = decoder->mSegment;
|
|
uint32_t new_buflen = decoder->mSegmentLen;
|
|
|
|
if (!new_buffer || new_buflen == 0) {
|
|
return false; // suspend
|
|
}
|
|
|
|
decoder->mSegmentLen = 0;
|
|
|
|
if (decoder->mBytesToSkip) {
|
|
if (decoder->mBytesToSkip < new_buflen) {
|
|
// All done skipping bytes; Return what's left.
|
|
new_buffer += decoder->mBytesToSkip;
|
|
new_buflen -= decoder->mBytesToSkip;
|
|
decoder->mBytesToSkip = 0;
|
|
} else {
|
|
// Still need to skip some more data in the future
|
|
decoder->mBytesToSkip -= (size_t)new_buflen;
|
|
return false; // suspend
|
|
}
|
|
}
|
|
|
|
decoder->mBackBufferUnreadLen = src->bytes_in_buffer;
|
|
|
|
src->next_input_byte = new_buffer;
|
|
src->bytes_in_buffer = (size_t)new_buflen;
|
|
decoder->mReading = false;
|
|
|
|
return true;
|
|
}
|
|
|
|
if (src->next_input_byte != decoder->mSegment) {
|
|
// Backtrack data has been permanently consumed.
|
|
decoder->mBackBufferUnreadLen = 0;
|
|
decoder->mBackBufferLen = 0;
|
|
}
|
|
|
|
// Save remainder of netlib buffer in backtrack buffer
|
|
const uint32_t new_backtrack_buflen = src->bytes_in_buffer +
|
|
decoder->mBackBufferLen;
|
|
|
|
// Make sure backtrack buffer is big enough to hold new data.
|
|
if (decoder->mBackBufferSize < new_backtrack_buflen) {
|
|
// Check for malformed MARKER segment lengths, before allocating space
|
|
// for it
|
|
if (new_backtrack_buflen > MAX_JPEG_MARKER_LENGTH) {
|
|
my_error_exit((j_common_ptr)(&decoder->mInfo));
|
|
}
|
|
|
|
// Round up to multiple of 256 bytes.
|
|
const size_t roundup_buflen = ((new_backtrack_buflen + 255) >> 8) << 8;
|
|
JOCTET* buf = (JOCTET*)PR_REALLOC(decoder->mBackBuffer, roundup_buflen);
|
|
// Check for OOM
|
|
if (!buf) {
|
|
decoder->mInfo.err->msg_code = JERR_OUT_OF_MEMORY;
|
|
my_error_exit((j_common_ptr)(&decoder->mInfo));
|
|
}
|
|
decoder->mBackBuffer = buf;
|
|
decoder->mBackBufferSize = roundup_buflen;
|
|
}
|
|
|
|
// Copy remainder of netlib segment into backtrack buffer.
|
|
memmove(decoder->mBackBuffer + decoder->mBackBufferLen,
|
|
src->next_input_byte,
|
|
src->bytes_in_buffer);
|
|
|
|
// Point to start of data to be rescanned.
|
|
src->next_input_byte = decoder->mBackBuffer + decoder->mBackBufferLen -
|
|
decoder->mBackBufferUnreadLen;
|
|
src->bytes_in_buffer += decoder->mBackBufferUnreadLen;
|
|
decoder->mBackBufferLen = (size_t)new_backtrack_buflen;
|
|
decoder->mReading = true;
|
|
|
|
return false;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/* data source manager method */
|
|
/*
|
|
* Terminate source --- called by jpeg_finish_decompress() after all
|
|
* data has been read to clean up JPEG source manager. NOT called by
|
|
* jpeg_abort() or jpeg_destroy().
|
|
*/
|
|
METHODDEF(void)
|
|
term_source (j_decompress_ptr jd)
|
|
{
|
|
nsJPEGDecoder* decoder = (nsJPEGDecoder*)(jd->client_data);
|
|
|
|
// This function shouldn't be called if we ran into an error we didn't
|
|
// recover from.
|
|
MOZ_ASSERT(decoder->mState != JPEG_ERROR,
|
|
"Calling term_source on a JPEG with mState == JPEG_ERROR!");
|
|
|
|
// Notify using a helper method to get around protectedness issues.
|
|
decoder->NotifyDone();
|
|
}
|
|
|
|
} // namespace image
|
|
} // namespace mozilla
|
|
|
|
///*************** Inverted CMYK -> RGB conversion *************************
|
|
/// Input is (Inverted) CMYK stored as 4 bytes per pixel.
|
|
/// Output is RGB stored as 3 bytes per pixel.
|
|
/// @param row Points to row buffer containing the CMYK bytes for each pixel
|
|
/// in the row.
|
|
/// @param width Number of pixels in the row.
|
|
static void cmyk_convert_rgb(JSAMPROW row, JDIMENSION width)
|
|
{
|
|
// Work from end to front to shrink from 4 bytes per pixel to 3
|
|
JSAMPROW in = row + width*4;
|
|
JSAMPROW out = in;
|
|
|
|
for (uint32_t i = width; i > 0; i--) {
|
|
in -= 4;
|
|
out -= 3;
|
|
|
|
// Source is 'Inverted CMYK', output is RGB.
|
|
// See: http://www.easyrgb.com/math.php?MATH=M12#text12
|
|
// Or: http://www.ilkeratalay.com/colorspacesfaq.php#rgb
|
|
|
|
// From CMYK to CMY
|
|
// C = ( C * ( 1 - K ) + K )
|
|
// M = ( M * ( 1 - K ) + K )
|
|
// Y = ( Y * ( 1 - K ) + K )
|
|
|
|
// From Inverted CMYK to CMY is thus:
|
|
// C = ( (1-iC) * (1 - (1-iK)) + (1-iK) ) => 1 - iC*iK
|
|
// Same for M and Y
|
|
|
|
// Convert from CMY (0..1) to RGB (0..1)
|
|
// R = 1 - C => 1 - (1 - iC*iK) => iC*iK
|
|
// G = 1 - M => 1 - (1 - iM*iK) => iM*iK
|
|
// B = 1 - Y => 1 - (1 - iY*iK) => iY*iK
|
|
|
|
// Convert from Inverted CMYK (0..255) to RGB (0..255)
|
|
const uint32_t iC = in[0];
|
|
const uint32_t iM = in[1];
|
|
const uint32_t iY = in[2];
|
|
const uint32_t iK = in[3];
|
|
out[0] = iC*iK/255; // Red
|
|
out[1] = iM*iK/255; // Green
|
|
out[2] = iY*iK/255; // Blue
|
|
}
|
|
}
|