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)
2799 lines
85 KiB
C++
2799 lines
85 KiB
C++
/* vim:set tw=80 expandtab softtabstop=4 ts=4 sw=4: */
|
|
|
|
// Copyright © Microsoft Corp.
|
|
// Contributors:
|
|
// Rhinoduck <private>
|
|
// Moonchild <moonchild@palemoon.org>
|
|
//
|
|
// Redistribution and use in source and binary forms, with or without
|
|
// modification, are permitted provided that the following conditions are met:
|
|
//
|
|
// • Redistributions of source code must retain the above copyright notice,
|
|
// this list of conditions and the following disclaimer.
|
|
// • Redistributions in binary form must reproduce the above copyright notice,
|
|
// this list of conditions and the following disclaimer in the documentation
|
|
// and/or other materials provided with the distribution.
|
|
//
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
// POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
|
|
/* This is a Cross-Platform JPEG-XR Decoder, which should work everywhere, including
|
|
* Big-Endian machines like the PowerPC. */
|
|
|
|
#include <set>
|
|
|
|
#include "ImageLogging.h"
|
|
#include "RasterImage.h"
|
|
|
|
// This is here so that GUIDs coming from JXRGlue.h are also defined in this
|
|
// file and that their values are duplicated in xul.dll as a result, because
|
|
// exporting them as symbols from gkmedias.dll would require changes to the
|
|
// library (i.e. the GUIDs would have to be exported as pointers). [rhinoduck]
|
|
#ifdef _WIN32
|
|
#include <initguid.h>
|
|
#endif
|
|
|
|
// Safeguards for when jxrlib meets another piece of code which leaks awful
|
|
// macro names. [rhinoduck]
|
|
#ifdef Failed
|
|
#error A marco with the name 'Failed' has already been defined, jxrlib would redefine it. One will have to be renamed, or a different solution will have to be found.
|
|
#endif
|
|
|
|
#ifdef Report
|
|
#error A marco with the name 'Report' has already been defined, jxrlib would redefine it. One will have to be renamed, or a different solution will have to be found.
|
|
#endif
|
|
|
|
#ifdef Call
|
|
#error A marco with the name 'Call' has already been defined, jxrlib would redefine it. One will have to be renamed, or a different solution will have to be found.
|
|
#endif
|
|
|
|
#ifdef CallIgnoreError
|
|
#error A marco with the name 'CallIgnoreError' has already been defined, jxrlib would redefine it. One will have to be renamed, or a different solution will have to be found.
|
|
#endif
|
|
|
|
#ifdef Test
|
|
#error A marco with the name 'Test' has already been defined, jxrlib would redefine it. One will have to be renamed, or a different solution will have to be found.
|
|
#endif
|
|
|
|
#ifdef FailIf
|
|
#error A marco with the name 'FailIf' has already been defined, jxrlib would redefine it. One will have to be renamed, or a different solution will have to be found.
|
|
#endif
|
|
|
|
// This pulls in the awfully named macros. [rhinoduck]
|
|
#include "jxrlib/JXRGlue.h"
|
|
|
|
// Get rid of the awfully named macros that jxrlib leaks, because otherwise they
|
|
// conflict with function names coming from other includes. [rhinoduck]
|
|
#undef Failed
|
|
#undef Report
|
|
#undef Call
|
|
#undef CallIgnoreError
|
|
#undef Test
|
|
#undef FailIf
|
|
|
|
#include "nsJXRDecoder.h"
|
|
|
|
#include "Orientation.h"
|
|
|
|
#include "gfxColor.h"
|
|
#include "gfxPlatform.h"
|
|
#include "qcms.h" // icSigRgbData, icSigGrayData
|
|
#include "gfxPrefs.h" // to control workarounds
|
|
|
|
//////////////////// Mozilla JPEG-XR decoder ///////////////////////////
|
|
|
|
EXTERN_C ERR CreateWS_List(struct WMPStream** ppWS);
|
|
EXTERN_C Int StartDecodingSubband(CTXSTRCODEC ctxSC, SUBBAND sb, const CWMImageInfo *pWMII);
|
|
EXTERN_C Int EndDecodingSubband(CTXSTRCODEC ctxSC);
|
|
|
|
namespace mozilla {
|
|
namespace image {
|
|
|
|
//#ifdef PR_LOGGING
|
|
//static PRLogModuleInfo *
|
|
//GetJXRLog()
|
|
//{
|
|
// static PRLogModuleInfo *sJXRLog;
|
|
//
|
|
// if (!sJXRLog)
|
|
// sJXRLog = PR_NewLogModule("JXRDecoder");
|
|
//
|
|
// return sJXRLog;
|
|
//}
|
|
//#endif
|
|
|
|
// These functions may be used if we decide to provide mamroy allocation/deallocation routines to JXRLib
|
|
//static void * MyAlloc(size_t cBytes)
|
|
//{
|
|
// return moz_malloc(cBytes);
|
|
//}
|
|
//
|
|
//static void MyFree(void *ptr)
|
|
//{
|
|
// moz_free(ptr);
|
|
//}
|
|
|
|
// It looks like hasBeenDecoded is never true if the image is not cached, but is SOMETIMES true for a cached image.
|
|
// A cached image can be consumed quickly, so it's better to decode all its tiles and subbbands in one pass at the end
|
|
// (in FinishInternal()).
|
|
// It would be nice to have a way to know for sure that the image is taken from the browser's cache. In this case,
|
|
// one would not have to do progressive decoding (in case of frequency mode) or fail-safe row-by-row decoding (in case of spatial mode)
|
|
// which are both rather expensive (fail-safe decoding does a lot of memory copying to save and restore coding contexts).
|
|
nsJXRDecoder::nsJXRDecoder(RasterImage* aImage, bool hasBeenDecoded) : Decoder(aImage),
|
|
m_pDecoder(nullptr),
|
|
m_pConverter(nullptr),
|
|
m_pStream(nullptr),
|
|
m_mbRowBuf(nullptr),
|
|
m_mbRowBufStride(0),
|
|
m_decodeAtEnd(hasBeenDecoded),
|
|
m_startedDecodingMBRows(false),
|
|
m_startedDecodingMBRows_Alpha(false),
|
|
m_mainPlaneFinished(false),
|
|
m_decodingAlpha(false),
|
|
m_scale(1),
|
|
m_totalReceived(0),
|
|
m_decoderInitialized(false),
|
|
m_currLine(0),
|
|
m_progressiveDecoding(false),
|
|
m_tileRowBandInfos(nullptr),
|
|
m_tileRowInfos(nullptr),
|
|
m_numAvailableBands(0),
|
|
m_currentTileRow(0),
|
|
m_currentSubBand(0),
|
|
m_startedDecodingSubband(false),
|
|
m_skippedTileRows(false),
|
|
m_alphaBitDepth(0),
|
|
m_planarAlphaIsPremultiplied(false),
|
|
m_outPixelFormat(pfNone),
|
|
m_inProfile(nullptr),
|
|
m_transform(nullptr),
|
|
m_xfBuf(nullptr),
|
|
m_xfBufRowStride(0),
|
|
m_xfPixelFormat(pfNone)
|
|
{
|
|
#ifdef USE_CHAIN_BUFFER
|
|
m_chainBuf.SetMemAllocProc(MyAlloc, MyFree);
|
|
#endif
|
|
}
|
|
|
|
nsJXRDecoder::~nsJXRDecoder()
|
|
{
|
|
DestroyJXRStuff();
|
|
moz_free(m_tileRowBandInfos);
|
|
moz_free(m_tileRowInfos);
|
|
|
|
if (m_transform)
|
|
qcms_transform_release(m_transform);
|
|
|
|
if (m_inProfile)
|
|
qcms_profile_release(m_inProfile);
|
|
|
|
moz_free(m_xfBuf);
|
|
}
|
|
|
|
bool nsJXRDecoder::CreateJXRStuff()
|
|
{
|
|
ERR err = WMP_errSuccess;
|
|
|
|
// TODO: [rhinoduck] Logging/error reporting.
|
|
// Create a JPEG-XR file decoder
|
|
err = PKImageDecode_Create_WMP(&m_pDecoder);
|
|
if (err != WMP_errSuccess) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
// TODO: [rhinoduck] Logging/error reporting.
|
|
// Create a pixel format converter
|
|
err = PKCodecFactory_CreateFormatConverter(&m_pConverter);
|
|
if (err != WMP_errSuccess) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Some converters will need a pointer to decoder, although they use a bad design -
|
|
// they (for instance, BlackWhite_Gray8()) assume that main image is being decode, but it could be alpha too.
|
|
// It would be better to have additional properties in the converter itself.
|
|
m_pConverter->pDecoder = m_pDecoder;
|
|
|
|
// TODO: [rhinoduck] Logging/error reporting.
|
|
// Create a stream
|
|
#if 0
|
|
err = CreateWS_ChainBuf(&m_pStream, &m_chainBuf);
|
|
if (err != WMP_errSuccess) {
|
|
goto Cleanup;
|
|
}
|
|
#else
|
|
err = CreateWS_List(&m_pStream);
|
|
if (err != WMP_errSuccess) {
|
|
goto Cleanup;
|
|
}
|
|
#endif
|
|
|
|
Cleanup:
|
|
|
|
if (WMP_errSuccess != err)
|
|
DestroyJXRStuff();
|
|
|
|
return true;
|
|
}
|
|
|
|
void nsJXRDecoder::DestroyJXRStuff()
|
|
{
|
|
if (nullptr != m_pDecoder)
|
|
m_pDecoder->Release(&m_pDecoder);
|
|
|
|
if (nullptr != m_pConverter)
|
|
m_pConverter->Release(&m_pConverter);
|
|
|
|
if (nullptr != m_pStream)
|
|
m_pStream->Close(&m_pStream);
|
|
|
|
m_decoderInitialized = false;
|
|
}
|
|
|
|
void nsJXRDecoder::InitializeJXRDecoder()
|
|
{
|
|
if (DecoderInitialized())
|
|
return;
|
|
|
|
m_decoderInitialized = WMP_errSuccess == m_pDecoder->Initialize(m_pDecoder, m_pStream);
|
|
}
|
|
|
|
bool nsJXRDecoder::HasAlpha() const
|
|
{
|
|
PKPixelInfo PI;
|
|
PI.pGUIDPixFmt = &m_pDecoder->guidPixFormat;
|
|
ERR err = PixelFormatLookup(&PI, LOOKUP_FORWARD);
|
|
|
|
if (WMP_errSuccess != err)
|
|
return false;
|
|
|
|
bool hasAlpha = (PI.grBit & PK_pixfmtHasAlpha) != 0;
|
|
return hasAlpha;
|
|
}
|
|
|
|
bool nsJXRDecoder::HasPlanarAlpha() const
|
|
{
|
|
return m_pDecoder->WMP.bHasAlpha != 0;
|
|
}
|
|
|
|
bool nsJXRDecoder::GetSize(size_t &width, size_t &height)
|
|
{
|
|
if (nullptr == m_pDecoder)
|
|
{
|
|
width = height = 0;
|
|
return false;
|
|
}
|
|
|
|
I32 w, h;
|
|
#if 0
|
|
m_pDecoder->GetSize(m_pDecoder, &w, &h);
|
|
#else
|
|
w = m_pDecoder->uWidth;
|
|
h = m_pDecoder->uHeight;
|
|
#endif
|
|
|
|
width = (size_t)w;
|
|
height = (size_t)h;
|
|
return true;
|
|
}
|
|
|
|
bool nsJXRDecoder::GetThumbnailSize(size_t &width, size_t &height)
|
|
{
|
|
if (nullptr == m_pDecoder)
|
|
{
|
|
width = height = 0;
|
|
return false;
|
|
}
|
|
|
|
size_t w, h;
|
|
GetSize(w, h);
|
|
|
|
CWMImageInfo wmii;
|
|
wmii.cThumbnailScale = GetScale();
|
|
wmii.cWidth = w;
|
|
wmii.cHeight = h;
|
|
|
|
CalcThumbnailSize(&wmii);
|
|
width = wmii.cThumbnailWidth;
|
|
height = wmii.cThumbnailHeight;
|
|
|
|
return true;
|
|
}
|
|
|
|
size_t nsJXRDecoder::GetNumTileRows() const
|
|
{
|
|
return nullptr == m_pDecoder ? 0 : m_pDecoder->WMP.wmiSCP.cNumOfSliceMinus1H + 1;
|
|
}
|
|
|
|
size_t nsJXRDecoder::GetNumTileCols() const
|
|
{
|
|
return nullptr == m_pDecoder ? 0 : m_pDecoder->WMP.wmiSCP.cNumOfSliceMinus1V + 1;
|
|
}
|
|
|
|
bool nsJXRDecoder::Receive(const uint8_t *buf, uint32_t count)
|
|
{
|
|
WMPStream *pWS = m_pStream;
|
|
pWS->SetPos(pWS, GetTotalNumBytesReceived());
|
|
pWS->Write(pWS, buf, count);
|
|
m_totalReceived += count;
|
|
|
|
return true;
|
|
}
|
|
|
|
uint32_t nsJXRDecoder::GetPixFmtBitsPP(PixelFormat pixFmt)
|
|
{
|
|
switch (pixFmt)
|
|
{
|
|
case pfBGR24:
|
|
case pfRGB24:
|
|
return 24;
|
|
|
|
case pfBGR32:
|
|
case pfRGB32:
|
|
case pfBGRA32:
|
|
case pfRGBA32:
|
|
return 32;
|
|
|
|
case pfGray:
|
|
return 8;
|
|
|
|
case pfCMYK32:
|
|
return 32;
|
|
|
|
case pfCMYKA40:
|
|
return 40;
|
|
|
|
case pfCMYK64:
|
|
return 64;
|
|
|
|
case pfCMYKA80:
|
|
return 80;
|
|
default:
|
|
// A NOP to silence a compiler warning until this is rewritten.
|
|
// [rhinoduck]
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Allocate raster buffer for the decoding of macroblock rows
|
|
void nsJXRDecoder::AllocateMBRowBuffer(size_t width, bool decodeAlpha)
|
|
{
|
|
if (nullptr != m_mbRowBuf)
|
|
FreeMBRowBuffers();
|
|
|
|
PKPixelFormatGUID srcFmtGUID;
|
|
ERR err = m_pDecoder->GetPixelFormat(m_pDecoder, &srcFmtGUID);
|
|
|
|
if (WMP_errSuccess != err)
|
|
return;
|
|
|
|
PKPixelFormatGUID outFmt = GUID_PKPixelFormatDontCare; // to avoid a compiler warning
|
|
PixelFormat cmykPF = pfNone;
|
|
|
|
if (Y_ONLY == m_pDecoder->WMP.wmiI.cfColorFormat)
|
|
{
|
|
assert(!HasAlpha());
|
|
|
|
outFmt = GUID_PKPixelFormat8bppGray;
|
|
m_outPixelFormat = pfGray;
|
|
|
|
if (nullptr != m_transform)
|
|
{
|
|
// We need to convert 8-bit gray to 24bpp RGB to feed it to color transformer
|
|
// JPEG-XR does not support grayscale images with alpha, so don't care about that case
|
|
m_xfPixelFormat = pfRGB24;
|
|
}
|
|
}
|
|
// CMYK formats
|
|
else if (IsEqualGUID(GUID_PKPixelFormat32bppCMYK, srcFmtGUID))
|
|
{
|
|
cmykPF = pfCMYK32;
|
|
m_xfPixelFormat = pfRGB32;
|
|
}
|
|
else if (IsEqualGUID(GUID_PKPixelFormat64bppCMYK, srcFmtGUID))
|
|
{
|
|
cmykPF = pfCMYK64;
|
|
m_xfPixelFormat = pfRGB32;
|
|
}
|
|
else if (IsEqualGUID(GUID_PKPixelFormat40bppCMYKAlpha, srcFmtGUID))
|
|
{
|
|
cmykPF = pfCMYKA40;
|
|
m_xfPixelFormat = decodeAlpha ? pfRGBA32 : pfRGB32;
|
|
}
|
|
else if (IsEqualGUID(GUID_PKPixelFormat80bppCMYKAlpha, srcFmtGUID))
|
|
{
|
|
cmykPF = pfCMYKA80;
|
|
m_xfPixelFormat = decodeAlpha ? pfRGBA32 : pfRGB32;
|
|
}
|
|
// All other formats
|
|
else
|
|
{
|
|
assert(CMYK != m_pDecoder->WMP.wmiI.cfColorFormat);
|
|
|
|
// We need to perform color transformation
|
|
if (decodeAlpha)
|
|
{
|
|
m_outPixelFormat = pfRGBA32;
|
|
outFmt = GUID_PKPixelFormat32bppRGBA;
|
|
}
|
|
else
|
|
{
|
|
if (HasAlpha())
|
|
{
|
|
outFmt = GUID_PKPixelFormat32bppRGB;
|
|
m_outPixelFormat = pfRGB32;
|
|
}
|
|
else
|
|
{
|
|
m_outPixelFormat = pfRGB24;
|
|
outFmt = GUID_PKPixelFormat24bppRGB;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pfNone != cmykPF)
|
|
{
|
|
outFmt = srcFmtGUID;
|
|
m_outPixelFormat = cmykPF;
|
|
}
|
|
|
|
if (nullptr != m_transform)
|
|
{
|
|
// use intermediate pixel format conversion
|
|
m_xfPixelFormat = m_outPixelFormat;
|
|
}
|
|
|
|
err = PKFormatConverter_InitializeConvert(m_pConverter, srcFmtGUID, NULL, outFmt);
|
|
|
|
if (WMP_errSuccess != err)
|
|
{
|
|
switch (m_outPixelFormat)
|
|
{
|
|
case pfRGBA32:
|
|
outFmt = GUID_PKPixelFormat32bppBGRA;
|
|
m_outPixelFormat = pfBGRA32;
|
|
break;
|
|
|
|
case pfRGB32:
|
|
outFmt = GUID_PKPixelFormat32bppBGR;
|
|
m_outPixelFormat = pfBGR32;
|
|
break;
|
|
|
|
case pfRGB24:
|
|
outFmt = GUID_PKPixelFormat24bppBGR;
|
|
m_outPixelFormat = pfBGR24;
|
|
break;
|
|
default:
|
|
// A NOP to silence a compiler warning until this is rewritten.
|
|
// [rhinoduck]
|
|
break;
|
|
}
|
|
|
|
err = PKFormatConverter_InitializeConvert(m_pConverter, srcFmtGUID, NULL, outFmt);
|
|
|
|
if (WMP_errSuccess != err)
|
|
return;
|
|
}
|
|
|
|
if (m_pDecoder->WMP.wmiI.cThumbnailScale < 1 || m_pDecoder->WMP.wmiI.cThumbnailScale > 16)
|
|
m_pDecoder->WMP.wmiI.cThumbnailScale = 1; // just in case
|
|
|
|
const size_t MBR_HEIGHT = 16;
|
|
size_t cLinesPerMBRow = MBR_HEIGHT / m_pDecoder->WMP.wmiI.cThumbnailScale;
|
|
|
|
if (pfNone != m_xfPixelFormat)
|
|
{
|
|
// Allocate a buffer for color transformation
|
|
uint32_t xf_bpp = GetPixFmtBitsPP(m_xfPixelFormat);
|
|
m_xfBufRowStride = ((xf_bpp + 7) >> 3) * width;
|
|
m_xfBuf = (uint8_t *)moz_malloc(m_xfBufRowStride * cLinesPerMBRow);
|
|
|
|
if (nullptr == m_xfBuf)
|
|
return;
|
|
}
|
|
|
|
PKPixelInfo pPIFrom;
|
|
pPIFrom.pGUIDPixFmt = &srcFmtGUID;
|
|
PixelFormatLookup(&pPIFrom, LOOKUP_FORWARD);
|
|
|
|
uint32_t cbStrideFrom = (BD_1 == pPIFrom.bdBitDepth ? ((pPIFrom.cbitUnit * width + 7) >> 3) : (((pPIFrom.cbitUnit + 7) >> 3) * width));
|
|
size_t dest_bpp = GetPixFmtBitsPP(m_outPixelFormat);
|
|
uint32_t cbStrideTo = ((dest_bpp + 7) >> 3) * width;
|
|
|
|
if (cbStrideTo > cbStrideFrom)
|
|
cbStrideFrom = cbStrideTo; // we are going to do in-place pixel format conversion
|
|
|
|
#ifdef ENABLE_OPTIMIZATIONS
|
|
cbStrideTo = (cbStrideTo + 127) / 128 * 128;
|
|
#endif
|
|
|
|
U8 *pb = NULL;
|
|
err = PKAllocAligned((void **)&pb, cbStrideFrom * cLinesPerMBRow, 128);
|
|
|
|
if (WMP_errSuccess == err)
|
|
{
|
|
m_mbRowBuf = pb;
|
|
m_mbRowBufStride = cbStrideFrom;
|
|
}
|
|
}
|
|
|
|
void nsJXRDecoder::AllocateMBRowBuffer_Alpha(size_t width)
|
|
{
|
|
if (nullptr != m_mbRowBuf)
|
|
FreeMBRowBuffers();
|
|
|
|
m_pStream->SetPos(m_pStream, m_pDecoder->WMP.wmiDEMisc.uAlphaOffset);
|
|
CWMImageInfo *pWMII = &m_pDecoder->WMP.wmiI_Alpha;
|
|
Int res = ImageStrDecGetInfo(pWMII, &m_pDecoder->WMP.wmiSCP_Alpha);
|
|
|
|
if (ICERR_OK != res)
|
|
return;
|
|
|
|
pWMII->oOrientation = O_NONE; // we handle orientation here, not in JXRLib decoder
|
|
|
|
size_t cbitUnit;
|
|
|
|
switch (pWMII->bdBitDepth)
|
|
{
|
|
case BD_8:
|
|
cbitUnit = 8;
|
|
break;
|
|
|
|
case BD_16:
|
|
case BD_16S:
|
|
case BD_16F:
|
|
cbitUnit = 16;
|
|
break;
|
|
|
|
case BD_32:
|
|
case BD_32S:
|
|
case BD_32F:
|
|
cbitUnit = 32;
|
|
break;
|
|
|
|
default:
|
|
return;
|
|
}
|
|
|
|
pWMII->cBitsPerUnit = cbitUnit;
|
|
|
|
// Set the pixel fromat GUID for alpha buffer
|
|
PKPixelFormatGUID srcFmtGUID;
|
|
ERR err = m_pDecoder->GetPixelFormat(m_pDecoder, &srcFmtGUID);
|
|
|
|
if (WMP_errSuccess != err)
|
|
return;
|
|
|
|
if (IsEqualGUID(GUID_PKPixelFormat32bppRGBA, srcFmtGUID) || IsEqualGUID(GUID_PKPixelFormat32bppBGRA, srcFmtGUID) ||
|
|
IsEqualGUID(GUID_PKPixelFormat32bppPRGBA, srcFmtGUID) || IsEqualGUID(GUID_PKPixelFormat32bppPBGRA, srcFmtGUID))
|
|
srcFmtGUID = GUID_PKPixelFormat8bppGray;
|
|
else if (IsEqualGUID(GUID_PKPixelFormat64bppRGBA, srcFmtGUID) || IsEqualGUID(GUID_PKPixelFormat64bppPRGBA, srcFmtGUID))
|
|
srcFmtGUID = GUID_PKPixelFormat16bppGray;
|
|
else if (IsEqualGUID(GUID_PKPixelFormat64bppRGBAFixedPoint, srcFmtGUID))
|
|
srcFmtGUID = GUID_PKPixelFormat16bppGrayFixedPoint;
|
|
else if (IsEqualGUID(GUID_PKPixelFormat128bppRGBAFixedPoint, srcFmtGUID))
|
|
srcFmtGUID = GUID_PKPixelFormat32bppGrayFixedPoint;
|
|
else if (IsEqualGUID(GUID_PKPixelFormat128bppRGBAFloat, srcFmtGUID) || IsEqualGUID(GUID_PKPixelFormat128bppPRGBAFloat, srcFmtGUID))
|
|
srcFmtGUID = GUID_PKPixelFormat32bppGrayFloat;
|
|
else if (IsEqualGUID(GUID_PKPixelFormat80bppCMYKAlpha, srcFmtGUID))
|
|
srcFmtGUID = GUID_PKPixelFormat16bppGray;
|
|
else if (IsEqualGUID(GUID_PKPixelFormat40bppCMYKAlpha, srcFmtGUID))
|
|
srcFmtGUID = GUID_PKPixelFormat8bppGray;
|
|
else if (IsEqualGUID(GUID_PKPixelFormat64bppRGBAHalf, srcFmtGUID))
|
|
srcFmtGUID = GUID_PKPixelFormat16bppGrayHalf;
|
|
else
|
|
return; // format not suported
|
|
|
|
err = PKFormatConverter_InitializeConvert(m_pConverter, srcFmtGUID, NULL, GUID_PKPixelFormat8bppGray);
|
|
|
|
if (WMP_errSuccess != err)
|
|
return;
|
|
|
|
uint32_t cbStride = ((cbitUnit + 7) >> 3) * width;
|
|
|
|
// Currently, SSE2 optimization is used only with main image plane.
|
|
//#ifdef ENABLE_OPTIMIZATIONS
|
|
// cbStride = (cbStride + 127) / 128 * 128;
|
|
//#endif
|
|
|
|
if (m_pDecoder->WMP.wmiI.cThumbnailScale < 1 || m_pDecoder->WMP.wmiI.cThumbnailScale > 16)
|
|
m_pDecoder->WMP.wmiI.cThumbnailScale = 1; // just in case
|
|
|
|
const size_t MBR_HEIGHT = 16;
|
|
size_t cLinesPerMBRow = MBR_HEIGHT / m_pDecoder->WMP.wmiI.cThumbnailScale;
|
|
|
|
U8 *pb = NULL;
|
|
// TODO: [rhinoduck] Logging/error reporting.
|
|
err = PKAllocAligned((void **)&pb, cbStride * cLinesPerMBRow, 128);
|
|
if (err != WMP_errSuccess) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
m_alphaBitDepth = cbitUnit;
|
|
m_mbRowBuf = pb;
|
|
m_mbRowBufStride = cbStride;
|
|
|
|
Cleanup:
|
|
return;
|
|
}
|
|
|
|
// Main image plane only
|
|
bool nsJXRDecoder::FillTileRowBandInfo()
|
|
{
|
|
if (!m_pDecoder->WMP.wmiSCP.bProgressiveMode)
|
|
return false;
|
|
|
|
if (nullptr != m_tileRowBandInfos)
|
|
return true;
|
|
|
|
size_t cTileRows, cTileCols, cBands, cHeaderSize;
|
|
size_t *indexTable = GetIndexTable(m_pDecoder->WMP.ctxSC, &cTileRows, &cTileCols, &cBands, &cHeaderSize);
|
|
|
|
if (nullptr == indexTable)
|
|
return false;
|
|
|
|
m_tileRowBandInfos = (TileRowBandInfo *)moz_malloc(sizeof(TileRowBandInfo) * cTileRows);
|
|
|
|
if (nullptr == m_tileRowBandInfos)
|
|
{
|
|
m_numAvailableBands = 0;
|
|
return false;
|
|
}
|
|
|
|
//size_t numTableEntries = cTileRows * cTileCols * cBands;
|
|
size_t width, height;
|
|
GetSize(width, height);
|
|
|
|
if (m_pDecoder->WMP.wmiI.cThumbnailScale < 1 || m_pDecoder->WMP.wmiI.cThumbnailScale > 16)
|
|
m_pDecoder->WMP.wmiI.cThumbnailScale = 1; // just in case
|
|
|
|
const size_t MBR_HEIGHT = 16;
|
|
const size_t mbRowHeight = MBR_HEIGHT / m_pDecoder->WMP.wmiI.cThumbnailScale;
|
|
const size_t imageUpperLimit = m_pDecoder->WMP.wmiSCP.uStreamImageOffset + m_pDecoder->WMP.wmiI.uImageByteCount;
|
|
|
|
// Calculate lower subband limits for every tile row
|
|
for (size_t i = 0; i < cTileRows; ++i)
|
|
{
|
|
TileRowBandInfo &info = m_tileRowBandInfos[i];
|
|
info.topMBRow = m_pDecoder->WMP.wmiSCP.uiTileY[i];
|
|
|
|
if (i > 0)
|
|
--info.topMBRow;
|
|
|
|
info.top = info.topMBRow * mbRowHeight;
|
|
info.height = (cTileRows - 1 == i ? height : m_pDecoder->WMP.wmiSCP.uiTileY[i + 1] * mbRowHeight) - info.top;
|
|
|
|
if (0 == i)
|
|
info.height -= mbRowHeight;
|
|
|
|
for (size_t k = 0; k < cBands; ++k)
|
|
{
|
|
size_t index = (i * cTileCols) * cBands + k;
|
|
size_t offset = indexTable[index];
|
|
bool subBandPresent = true; // a subband may be missing for a tile row
|
|
|
|
// Only the DC subband of the very first tile can be 0
|
|
if (0 == offset && !(0 == i && 0 == k))
|
|
{
|
|
subBandPresent = false;
|
|
|
|
// Try to find a subband entry for this tile row that is not 0, i.e. present
|
|
for (size_t n = 1; n < cTileCols; ++n)
|
|
{
|
|
size_t nextIndex = index + cBands * n;
|
|
offset = indexTable[nextIndex];
|
|
|
|
if (0 != offset)
|
|
{
|
|
subBandPresent = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (subBandPresent)
|
|
info.lowerLimits[k] = offset + cHeaderSize;
|
|
else
|
|
info.lowerLimits[k] = info.upperLimits[k] = 0; // this will mean the subband is not present
|
|
}
|
|
}
|
|
|
|
// Calculate upper subband limits for every tile row
|
|
TileRowBandInfo &topInfo = m_tileRowBandInfos[0];
|
|
|
|
for (size_t i = 0 ; i < cTileRows; ++i)
|
|
{
|
|
TileRowBandInfo &info = m_tileRowBandInfos[i];
|
|
size_t *upperLimits = info.upperLimits;
|
|
|
|
for (size_t k = 0; k < cBands; ++k)
|
|
{
|
|
if (!(0 == i && 0 == k) && 0 == info.lowerLimits[k]) // subband is not present for all tiles in this row
|
|
{
|
|
//info.upperLimits[k] = 0; // should already have been assigned
|
|
continue; // continue to the next subband (although this is probably the last one, namely flexbits)
|
|
}
|
|
|
|
size_t limit = 0;
|
|
|
|
if (cTileRows - 1 == i) // if last tile row // next info's subband is not present
|
|
{
|
|
if (cBands - 1 == k) // if last band
|
|
limit = imageUpperLimit;
|
|
else
|
|
limit = topInfo.lowerLimits[k + 1];
|
|
}
|
|
else
|
|
{
|
|
for (size_t n = i + 1; n < cTileRows; ++n)
|
|
{
|
|
TileRowBandInfo &nextGoodInfo = m_tileRowBandInfos[n];
|
|
limit = nextGoodInfo.lowerLimits[k];
|
|
|
|
if (0 != limit)
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (0 != limit)
|
|
upperLimits[k] = limit;
|
|
else
|
|
{
|
|
if (cBands - 1 == k) // if last band
|
|
upperLimits[k] = imageUpperLimit;
|
|
else
|
|
{
|
|
for (size_t n = 0 ; n < cTileRows; ++n)
|
|
{
|
|
TileRowBandInfo &goodTopInfo = m_tileRowBandInfos[n];
|
|
limit = goodTopInfo.lowerLimits[k + 1];
|
|
|
|
if (0 != limit)
|
|
{
|
|
upperLimits[k] = limit;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (0 == limit)
|
|
upperLimits[k] = imageUpperLimit;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Determine the last available subband for every tile row
|
|
for (size_t k = cBands - 1; k != 0; --k) // DC is always present
|
|
{
|
|
//if (!(info.lowerLimits[k] == 0 && info.upperLimits[k] == 0))
|
|
if (info.lowerLimits[k] != 0) // this condition is enough
|
|
{
|
|
info.lastPresentSubband = k + 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Assign subband upper limits
|
|
for (size_t i = 0; i < MAX_SUBBANDS; ++i)
|
|
{
|
|
m_subbandUpperLimits[i] = 0; // subband is not present for the entire image
|
|
|
|
if (i < cBands)
|
|
{
|
|
for (size_t j = cTileRows; j > 0; --j)
|
|
{
|
|
TileRowBandInfo &info = m_tileRowBandInfos[j - 1];
|
|
size_t limit = info.upperLimits[i];
|
|
|
|
if (0 != limit)
|
|
{
|
|
m_subbandUpperLimits[i] = limit;
|
|
m_numAvailableBands = i + 1; // so it is possible to have m_numAvailableBands < cBands
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool nsJXRDecoder::FillTileRowInfo()
|
|
{
|
|
if (m_pDecoder->WMP.wmiSCP.bProgressiveMode)
|
|
return false;
|
|
|
|
if (nullptr != m_tileRowInfos)
|
|
return false;
|
|
|
|
size_t cTileRows, cTileCols, cBands, cHeaderSize;
|
|
size_t *indexTable = GetIndexTable(m_pDecoder->WMP.ctxSC, &cTileRows, &cTileCols, &cBands, &cHeaderSize);
|
|
bool spatial = SPATIAL == m_pDecoder->WMP.wmiSCP.bfBitstreamFormat;
|
|
size_t cEntriesPerTile = spatial ? 1 : cBands;
|
|
|
|
if (nullptr == indexTable)
|
|
return false;
|
|
|
|
m_tileRowInfos = (TileRowInfo *)moz_malloc(sizeof(TileRowInfo) * cTileRows);
|
|
|
|
if (nullptr == m_tileRowInfos)
|
|
{
|
|
m_numAvailableBands = 0;
|
|
return false;
|
|
}
|
|
|
|
size_t width, height;
|
|
GetSize(width, height);
|
|
|
|
if (m_pDecoder->WMP.wmiI.cThumbnailScale < 1 || m_pDecoder->WMP.wmiI.cThumbnailScale > 16)
|
|
m_pDecoder->WMP.wmiI.cThumbnailScale = 1; // just in case
|
|
|
|
const size_t MBR_HEIGHT = 16;
|
|
const size_t mbRowHeight = MBR_HEIGHT / m_pDecoder->WMP.wmiI.cThumbnailScale;
|
|
const size_t imageUpperLimit = m_pDecoder->WMP.wmiSCP.uStreamImageOffset + m_pDecoder->WMP.wmiI.uImageByteCount;
|
|
size_t nextLowerLimit = imageUpperLimit;
|
|
bool lastRow = true;
|
|
|
|
#if 0
|
|
|
|
// This will probably always work for FREQUENCY mode
|
|
// (and in SPATIAL mode - when all the tilea are in scan order and when there are no tiles shared by
|
|
// different tile rows
|
|
// !!!This won't work with SPATIAL mode when there are shared tiles or tiles that do not come in "scan order"!!!
|
|
for (int32_t i = (int32_t)cTileRows - 1; i >= 0; --i)
|
|
{
|
|
TileRowInfo &info = m_tileRowInfos[i];
|
|
info.top = m_pDecoder->WMP.wmiSCP.uiTileY[i] * mbRowHeight;
|
|
|
|
// We are not interested in the last info, so we set numMBRows to 0.
|
|
// Hopefully, JPEG-XR standard v.2.0 will support macroblock row index tables,
|
|
// so we won't need this.
|
|
info.numMBRows = lastRow ? 0 : m_pDecoder->WMP.wmiSCP.uiTileY[i + 1] - m_pDecoder->WMP.wmiSCP.uiTileY[i];
|
|
|
|
if (i > 0)
|
|
info.top -= mbRowHeight;
|
|
|
|
info.height = (lastRow ? height : m_tileRowInfos[i + 1].top) - info.top;
|
|
|
|
size_t index = i * cTileCols * cEntriesPerTile; // index of the first entry in the first column of current tile row
|
|
size_t lowerLimit = indexTable[index] + cHeaderSize;
|
|
info.upperLimit = nextLowerLimit;
|
|
nextLowerLimit = lowerLimit;
|
|
lastRow = false;
|
|
}
|
|
|
|
#else
|
|
// !!!This code is unfinished!!!
|
|
// In SPATIAL mode, tiles may come in an arbitrary order, and can even be shared
|
|
// We need to caculate maximum possible number of bytes that can be discarded for each tile row
|
|
|
|
// Since the tiles are unordered in a general case, we have tosort the entries in order
|
|
// to determine the upper limit of every entry
|
|
size_t numTableEntries = cEntriesPerTile * cTileRows * cTileCols;
|
|
typedef std::set<size_t> TileEntries;
|
|
TileEntries entries;
|
|
|
|
for (size_t i = 0; i < numTableEntries; ++i)
|
|
{
|
|
size_t offset = indexTable[i];
|
|
|
|
// If an index table entry is 0 in FREQUENCY mode (except teh very first one),
|
|
// this means the subband is not present in this tile, so do not insert it in the set
|
|
if (spatial || !(i > 0 && 0 == offset))
|
|
entries.insert(offset + cHeaderSize);
|
|
}
|
|
|
|
entries.insert(imageUpperLimit);
|
|
size_t cEntriesPerTileRow = cTileCols * cEntriesPerTile;
|
|
|
|
for (int32_t i = (int32_t)cTileRows - 1; i >= 0; --i)
|
|
{
|
|
TileRowInfo &info = m_tileRowInfos[i];
|
|
info.nextLowerLimit = nextLowerLimit;
|
|
info.top = m_pDecoder->WMP.wmiSCP.uiTileY[i] * mbRowHeight;
|
|
|
|
// We are not interested in the last info, so we set numMBRows to 0.
|
|
// Hopefully, JPEG-XR standard v.2.0 will support macroblock row index tables,
|
|
// so we won't need this.
|
|
info.numMBRows = lastRow ? 0 : m_pDecoder->WMP.wmiSCP.uiTileY[i + 1] - m_pDecoder->WMP.wmiSCP.uiTileY[i];
|
|
|
|
if (i > 0)
|
|
info.top -= mbRowHeight;
|
|
|
|
info.height = (lastRow ? height : m_tileRowInfos[i + 1].top) - info.top;
|
|
|
|
size_t index = i * cTileCols * cEntriesPerTile; // index of the first entry in the first column of current tile row
|
|
size_t rowLowerLimit = (size_t)-1, rowUpperLimit = 0; // current tile row's limits
|
|
|
|
for (size_t j = 0; j < cEntriesPerTileRow; ++j)
|
|
{
|
|
size_t offset = indexTable[index + j] + cHeaderSize;
|
|
TileEntries::const_iterator result = entries.find(offset);
|
|
|
|
if (offset < rowLowerLimit)
|
|
rowLowerLimit = offset;
|
|
|
|
++result;
|
|
offset = *result;
|
|
|
|
if (offset > rowUpperLimit)
|
|
rowUpperLimit = offset;
|
|
}
|
|
|
|
info.upperLimit = rowUpperLimit;
|
|
|
|
if (rowLowerLimit < nextLowerLimit)
|
|
nextLowerLimit = rowLowerLimit;
|
|
|
|
lastRow = false;
|
|
}
|
|
#endif
|
|
|
|
m_numAvailableBands = cBands;
|
|
|
|
return true;
|
|
}
|
|
|
|
size_t nsJXRDecoder::GetTileRowForMBRow(size_t mbRow)
|
|
{
|
|
if (nullptr == m_tileRowInfos)
|
|
return 0;
|
|
|
|
size_t cTileRows = GetNumTileRows();
|
|
size_t numRows = 0;
|
|
|
|
// Ignore the last row
|
|
for (size_t i = 0; i < cTileRows - 1; ++i)
|
|
{
|
|
numRows += m_tileRowInfos[i].numMBRows;
|
|
|
|
if (mbRow < numRows)
|
|
return i;
|
|
}
|
|
|
|
return cTileRows - 1;
|
|
}
|
|
|
|
// Number of subbands that can be decoded with the number of bytes received (in progressive layout)
|
|
size_t nsJXRDecoder::GetNumberOfCoveredSubBands()
|
|
{
|
|
size_t result = 0;
|
|
|
|
for (size_t i = 0; i < GetNumAvailableBands(); ++i)
|
|
{
|
|
if (0 != m_subbandUpperLimits[i]) // ignore empty subbands
|
|
{
|
|
if (GetTotalNumBytesReceived() < m_subbandUpperLimits[i])
|
|
break;
|
|
|
|
result = i + 1;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
bool nsJXRDecoder::HasEmptyTileRowSubbands(size_t subband)
|
|
{
|
|
for (size_t i = 0; i < GetNumTileRows(); ++i)
|
|
{
|
|
const TileRowBandInfo &info = m_tileRowBandInfos[i];
|
|
|
|
if (0 == info.upperLimits[subband])
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void nsJXRDecoder::StartDecodingNextSubband()
|
|
{
|
|
if (!StartedDecodingMBRows())
|
|
return;
|
|
|
|
SUBBAND sb;
|
|
|
|
switch (GetCurrentSubBand())
|
|
{
|
|
case 0:
|
|
sb = SB_DC_ONLY;
|
|
break;
|
|
case 1:
|
|
sb = SB_NO_HIGHPASS;
|
|
break;
|
|
case 2:
|
|
sb = SB_NO_FLEXBITS;
|
|
break;
|
|
case 3:
|
|
sb = SB_ALL;
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
m_pDecoder->WMP.wmiSCP.sbSubband = sb;
|
|
m_pDecoder->WMP.wmiSCP_Alpha.sbSubband = sb;
|
|
|
|
StartDecodingSubband(m_pDecoder->WMP.ctxSC, sb, &m_pDecoder->WMP.wmiI);
|
|
m_pDecoder->WMP.DecoderCurrMBRow = 0;
|
|
m_currentTileRow = 0;
|
|
m_currLine = 0;
|
|
|
|
m_startedDecodingSubband = true;
|
|
}
|
|
|
|
void nsJXRDecoder::EndDecodingCurrentSubband()
|
|
{
|
|
if (!StartedDecodingMBRows() || !StartedDecodingSubband())
|
|
return;
|
|
|
|
EndDecodingSubband(m_pDecoder->WMP.ctxSC);
|
|
|
|
m_startedDecodingSubband = false;
|
|
}
|
|
|
|
///////////>
|
|
|
|
void nsJXRDecoder::UpdateImage(size_t top, size_t width, size_t height)
|
|
{
|
|
uint32_t *dest = (uint32_t *)mImageData + (uint32_t)width * top;
|
|
const uint8_t *src;
|
|
size_t srcRowStride;
|
|
PixelFormat pixFmt;
|
|
|
|
if (nullptr != m_xfBuf)
|
|
{
|
|
src = m_xfBuf;
|
|
srcRowStride = m_xfBufRowStride;
|
|
pixFmt = m_xfPixelFormat;
|
|
}
|
|
else
|
|
{
|
|
src = m_mbRowBuf;
|
|
srcRowStride = m_mbRowBufStride;
|
|
pixFmt = pfNone != m_xfPixelFormat ? m_xfPixelFormat : m_outPixelFormat;
|
|
}
|
|
|
|
switch (pixFmt)
|
|
{
|
|
case pfGray:
|
|
{
|
|
const uint8_t *sl = src;
|
|
uint32_t *dl = dest;
|
|
|
|
for (size_t i = 0; i < height; ++i)
|
|
{
|
|
const uint8_t *s = sl;
|
|
|
|
for (size_t j = 0; j < width; ++j)
|
|
{
|
|
const uint8_t alpha = 0xFF;
|
|
dl[j] = gfxPackedPixelNoPreMultiply(alpha, *s, *s, *s);
|
|
++s;
|
|
}
|
|
|
|
dl += width;
|
|
sl += srcRowStride;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case pfRGB24:
|
|
case pfRGB32:
|
|
{
|
|
struct RGB
|
|
{
|
|
uint8_t r, g, b;
|
|
};
|
|
|
|
const uint8_t *sl = src;
|
|
uint32_t *dl = dest;
|
|
const uint32_t srcBPP = pfRGB24 == pixFmt ? 3 : 4;
|
|
// Not making everything opaque if we are expecting transparency
|
|
// values to still be updated later prevents some ugliness.
|
|
// [rhinoduck]
|
|
const uint8_t alpha =
|
|
(pixFmt == pfRGB32 && HasPlanarAlpha() ? 0x00 : 0xFF);
|
|
|
|
for (size_t i = 0; i < height; ++i)
|
|
{
|
|
const uint8_t *s = sl;
|
|
|
|
for (size_t j = 0; j < width; ++j)
|
|
{
|
|
const RGB &sp = *(const RGB *)s;
|
|
s += srcBPP;
|
|
dl[j] = gfxPackedPixelNoPreMultiply(alpha, sp.r, sp.g, sp.b);
|
|
}
|
|
|
|
dl += width;
|
|
sl += srcRowStride;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case pfRGBA32:
|
|
{
|
|
struct RGBA
|
|
{
|
|
uint8_t r, g, b, a;
|
|
};
|
|
|
|
const uint8_t *sl = src;
|
|
uint32_t *dl = dest;
|
|
const uint32_t srcBPP = 4;
|
|
|
|
for (size_t i = 0; i < height; ++i)
|
|
{
|
|
const uint8_t *s = sl;
|
|
|
|
for (size_t j = 0; j < width; ++j)
|
|
{
|
|
const RGBA &sp = *(const RGBA *)s;
|
|
s += srcBPP;
|
|
//dl[j] = gfxPackedPixelNoPreMultiply(alpha, sp.r, sp.g, sp.b);
|
|
dl[j] = gfxPackedPixel(sp.a, sp.r, sp.g, sp.b);
|
|
}
|
|
|
|
dl += width;
|
|
sl += srcRowStride;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case pfBGR24:
|
|
case pfBGR32:
|
|
{
|
|
struct BGR
|
|
{
|
|
uint8_t b, g, r;
|
|
};
|
|
|
|
const uint8_t *sl = src;
|
|
uint32_t *dl = dest;
|
|
const uint32_t srcBPP = pfBGR24 == pixFmt ? 3 : 4;
|
|
// Not making everything opaque if we are expecting transparency
|
|
// values to still be updated later prevents some ugliness.
|
|
// [rhinoduck]
|
|
const uint8_t alpha =
|
|
(pixFmt == pfBGR32 && HasPlanarAlpha() ? 0x00 : 0xFF);
|
|
|
|
for (size_t i = 0; i < height; ++i)
|
|
{
|
|
const uint8_t *s = sl;
|
|
|
|
for (size_t j = 0; j < width; ++j)
|
|
{
|
|
const BGR &sp = *(const BGR *)s;
|
|
s += srcBPP;
|
|
dl[j] = gfxPackedPixelNoPreMultiply(alpha, sp.r, sp.g, sp.b);
|
|
}
|
|
|
|
dl += width;
|
|
sl += srcRowStride;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case pfBGRA32:
|
|
{
|
|
struct BGRA
|
|
{
|
|
uint8_t b, g, r, a;
|
|
};
|
|
|
|
const uint8_t *sl = src;
|
|
uint32_t *dl = dest;
|
|
const uint32_t srcBPP = 4;
|
|
|
|
for (size_t i = 0; i < height; ++i)
|
|
{
|
|
const uint8_t *s = sl;
|
|
|
|
for (size_t j = 0; j < width; ++j)
|
|
{
|
|
const BGRA &sp = *(const BGRA *)s;
|
|
s += srcBPP;
|
|
//dl[j] = gfxPackedPixelNoPreMultiply(sp.a, sp.r, sp.g, sp.b);
|
|
dl[j] = gfxPackedPixel(sp.a, sp.r, sp.g, sp.b);
|
|
}
|
|
|
|
dl += width;
|
|
sl += srcRowStride;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
// A NOP to silence a compiler warning until this is rewritten.
|
|
// [rhinoduck]
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
void nsJXRDecoder::UpdateImage_AlphaOnly(size_t top, size_t width, size_t height)
|
|
{
|
|
uint32_t *dest = (uint32_t *)mImageData + (uint32_t)width * top;
|
|
const uint8_t *src = m_mbRowBuf;
|
|
size_t srcRowStride = m_mbRowBufStride;
|
|
const uint8_t *sl = src;
|
|
uint32_t *dl = dest;
|
|
|
|
if (m_planarAlphaIsPremultiplied) {
|
|
for (size_t i = 0; i < height; ++i)
|
|
{
|
|
const uint8_t *s = sl;
|
|
|
|
for (size_t j = 0; j < width; ++j)
|
|
{
|
|
uint8_t a = *s;
|
|
++s;
|
|
uint32_t dp = dl[j];
|
|
dl[j] = gfxPackedPixelNoPreMultiply(a, (dp & 0x00FF0000) >> 16, (dp & 0x0000FF00) >> 8, (dp & 0x000000FF));
|
|
}
|
|
|
|
dl += width;
|
|
sl += srcRowStride;
|
|
}
|
|
} else {
|
|
for (size_t i = 0; i < height; ++i)
|
|
{
|
|
const uint8_t *s = sl;
|
|
|
|
for (size_t j = 0; j < width; ++j)
|
|
{
|
|
uint8_t a = *s;
|
|
++s;
|
|
uint32_t dp = dl[j];
|
|
dl[j] = gfxPackedPixel(a, (dp & 0x00FF0000) >> 16, (dp & 0x0000FF00) >> 8, (dp & 0x000000FF));
|
|
}
|
|
|
|
dl += width;
|
|
sl += srcRowStride;
|
|
}
|
|
}
|
|
}
|
|
|
|
void nsJXRDecoder::DecodeAllMBRows()
|
|
{
|
|
size_t width, height;
|
|
GetSize(width, height);
|
|
|
|
for (;;)
|
|
{
|
|
bool decoded = DecodeNextMBRow(false);
|
|
|
|
if (!decoded || m_currLine >= height)
|
|
break;
|
|
}
|
|
|
|
// Invalidate
|
|
nsIntRect r(0, 0, width, height);
|
|
PostInvalidation(r);
|
|
}
|
|
|
|
// Decode all macroblock rows with planar alpha
|
|
void nsJXRDecoder::DecodeAllMBRowsWithAlpha()
|
|
{
|
|
size_t width = m_pDecoder->WMP.wmiI.cThumbnailWidth;
|
|
size_t height = m_pDecoder->WMP.wmiI.cThumbnailHeight;
|
|
|
|
for (;;)
|
|
{
|
|
bool decoded = DecodeNextMBRowWithAlpha();
|
|
|
|
if (!decoded || m_currLine >= height)
|
|
break;
|
|
}
|
|
|
|
// Invalidate
|
|
nsIntRect r(0, 0, width, height);
|
|
PostInvalidation(r);
|
|
}
|
|
|
|
void nsJXRDecoder::DecodeAllMBRows_Alpha()
|
|
{
|
|
size_t width = m_pDecoder->WMP.wmiI_Alpha.cThumbnailWidth;
|
|
size_t height = m_pDecoder->WMP.wmiI_Alpha.cThumbnailHeight;
|
|
|
|
for (;;)
|
|
{
|
|
bool decoded = DecodeNextMBRow_Alpha(false);
|
|
|
|
if (!decoded || m_currLine >= height)
|
|
break;
|
|
}
|
|
|
|
// Invalidate
|
|
nsIntRect r(0, 0, width, height);
|
|
PostInvalidation(r);
|
|
}
|
|
|
|
//////////////////////
|
|
|
|
void nsJXRDecoder::StartProgressiveDecoding(bool decodeAlpha)
|
|
{
|
|
if (StartedDecodingMBRows())
|
|
return;
|
|
|
|
m_pDecoder->WMP.wmiI.cThumbnailScale = GetScale();
|
|
|
|
size_t width, height;
|
|
GetThumbnailSize(width, height);
|
|
|
|
AllocateMBRowBuffer(width, decodeAlpha);
|
|
|
|
if (nullptr == m_mbRowBuf)
|
|
{
|
|
PostDecoderError(NS_ERROR_FAILURE);
|
|
return;
|
|
}
|
|
|
|
m_currentTileRow = 0;
|
|
|
|
SUBBAND sb = SB_DC_ONLY;
|
|
m_pDecoder->WMP.wmiSCP.sbSubband = sb;
|
|
m_pDecoder->WMP.wmiSCP_Alpha.sbSubband = sb;
|
|
m_pDecoder->WMP.wmiSCP.uAlphaMode = decodeAlpha ? 2 : 0;
|
|
m_decodingAlpha = decodeAlpha;
|
|
|
|
// The assignment below is unnecessary because in this particular decoder
|
|
// the beginnig of the image file is the beginning of the stream.
|
|
// Keep this line as a reminder that offStart should be changed if the stream begins
|
|
// somewhere else (for example, where the image data starts, i.e. at m_pDecoder->WMP.wmiDEMisc.uImageOffset)
|
|
m_pDecoder->offStart = 0; // our stream starts at the beginning of the image file
|
|
|
|
ERR err = JXR_BeginDecodingMBRows(m_pDecoder, NULL, m_mbRowBuf, m_mbRowBufStride, FALSE, FALSE); // decoding into a single MB row buffer, not fail-safe
|
|
|
|
if (WMP_errSuccess != err)
|
|
return;
|
|
|
|
m_startedDecodingMBRows = true;
|
|
|
|
// Currently, we can determine whether the layout is progressive only after initializing
|
|
// the coding context with JXR_BeginDecodingMBRows. So if not progressive, terminate
|
|
// the decoding
|
|
if (!m_pDecoder->WMP.wmiSCP.bProgressiveMode)
|
|
{
|
|
EndDecodingMBRows();
|
|
return;
|
|
}
|
|
|
|
m_progressiveDecoding = true;
|
|
m_startedDecodingSubband = true;
|
|
}
|
|
|
|
void nsJXRDecoder::StartDecodingMBRows(bool failSafe, bool decodeAlpha)
|
|
{
|
|
if (StartedDecodingMBRows())
|
|
return;
|
|
|
|
m_pDecoder->WMP.wmiI.cThumbnailScale = GetScale();
|
|
|
|
size_t width, height;
|
|
GetThumbnailSize(width, height);
|
|
|
|
AllocateMBRowBuffer(width, decodeAlpha);
|
|
|
|
if (nullptr == m_mbRowBuf)
|
|
{
|
|
PostDecoderError(NS_ERROR_FAILURE);
|
|
return;
|
|
}
|
|
|
|
SUBBAND sb = SB_ALL;
|
|
|
|
m_pDecoder->WMP.wmiSCP.sbSubband = sb;
|
|
m_pDecoder->WMP.wmiSCP_Alpha.sbSubband = sb;
|
|
m_pDecoder->WMP.wmiSCP.uAlphaMode = decodeAlpha ? 2 : 0;
|
|
m_decodingAlpha = decodeAlpha;
|
|
|
|
// The assignment below is unnecessary because in this particular decoder
|
|
// the beginnig of the image file is the beginning of the stream.
|
|
// Keep this line as a reminder that offStart should be changed if the stream begins
|
|
// somewhere else (for example, where the image data starts, i.e. at m_pDecoder->WMP.wmiDEMisc.uImageOffset)
|
|
m_pDecoder->offStart = 0; // our stream starts at the beginning of the image file
|
|
|
|
ERR err = JXR_BeginDecodingMBRows(m_pDecoder, NULL, m_mbRowBuf, m_mbRowBufStride, FALSE, failSafe ? TRUE : FALSE); // decoding into a single MB row buffer
|
|
|
|
if (WMP_errSuccess != err)
|
|
return;
|
|
|
|
m_startedDecodingMBRows = true;
|
|
}
|
|
|
|
void nsJXRDecoder::StartDecodingMBRows_Alpha()
|
|
{
|
|
if (StartedDecodingMBRows_Alpha())
|
|
return;
|
|
|
|
m_pDecoder->WMP.wmiSCP_Alpha.pWStream = m_pStream; // !!! Must be set in m_pDecoder->Initialize() !!!
|
|
m_pDecoder->WMP.wmiI_Alpha.cThumbnailScale = GetScale();
|
|
|
|
size_t width, height;
|
|
GetThumbnailSize(width, height);
|
|
|
|
if (DecodeAtEnd())
|
|
m_pDecoder->WMP.wmiI_Alpha.cBitsPerUnit = m_pDecoder->WMP.wmiI.cBitsPerUnit; // use the same bufer as main plane
|
|
else
|
|
AllocateMBRowBuffer_Alpha(width); // use an alpha-only buffer
|
|
|
|
if (nullptr == m_mbRowBuf)
|
|
{
|
|
PostDecoderError(NS_ERROR_FAILURE);
|
|
return;
|
|
}
|
|
|
|
PKPixelFormatGUID srcFmtGUID;
|
|
|
|
if (WMP_errSuccess != m_pDecoder->GetPixelFormat(m_pDecoder, &srcFmtGUID)) {
|
|
PostDecoderError(NS_ERROR_FAILURE);
|
|
return;
|
|
}
|
|
|
|
// Since the jxrlib conversion to RGB32 does not unmultiply alpha, we need
|
|
// to keep track of whether alpha was pre-multiplied or straight so that we
|
|
// can apply the proper pixel construction function in
|
|
// UpdateImage_AlphaOnly(). [rhinoduck]
|
|
if (IsEqualGUID(GUID_PKPixelFormat32bppPRGBA, srcFmtGUID) ||
|
|
IsEqualGUID(GUID_PKPixelFormat32bppPBGRA, srcFmtGUID) ||
|
|
IsEqualGUID(GUID_PKPixelFormat64bppPRGBA, srcFmtGUID) ||
|
|
IsEqualGUID(GUID_PKPixelFormat128bppPRGBAFloat, srcFmtGUID)) {
|
|
m_planarAlphaIsPremultiplied = true;
|
|
}
|
|
|
|
m_pDecoder->WMP.wmiSCP.uAlphaMode = 0; // it does not matter for planar alpha
|
|
|
|
// The assignment below is unnecessary because in this particular decoder
|
|
// the beginnig of the image file is the beginning of the stream.
|
|
// Keep this line as a reminder that offStart should be changed if the stream begins
|
|
// somewhere else (for example, where the image data starts, i.e. at m_pDecoder->WMP.wmiDEMisc.uImageOffset)
|
|
m_pDecoder->offStart = 0; // our stream starts at the beginning of the image file
|
|
|
|
ERR err = JXR_BeginDecodingMBRows_Alpha(m_pDecoder, NULL, m_mbRowBuf, m_mbRowBufStride, FALSE, FALSE); // decoding into a single MB row buffer, not fail-safe
|
|
|
|
m_startedDecodingMBRows_Alpha = WMP_errSuccess == err;
|
|
}
|
|
|
|
EXTERN_C ERR BGR24_RGB24(PKFormatConverter* pFC, const PKRect* pRect, U8* pb, U32 cbStride);
|
|
EXTERN_C ERR BGRA32_RGBA32(PKFormatConverter* pFC, const PKRect* pRect, U8* pb, U32 cbStride);
|
|
|
|
static void CMYK32_RGB32(const void *pCMYK, void *pRGB)
|
|
{
|
|
struct CMYK
|
|
{
|
|
unsigned char c;
|
|
unsigned char m;
|
|
unsigned char y;
|
|
unsigned char k;
|
|
};
|
|
|
|
struct MyRGB32
|
|
{
|
|
unsigned char r, g, b, x;
|
|
};
|
|
|
|
const CMYK &cmyk = *(const CMYK *)pCMYK;
|
|
MyRGB32 &pix = *(MyRGB32 *)pRGB;
|
|
|
|
double c = double(cmyk.c) / 255.0;
|
|
double m = double(cmyk.m) / 255.0;
|
|
double y = double(cmyk.y) / 255.0;
|
|
double k = double(cmyk.k) / 255.0;
|
|
|
|
double nc = (c * (1.0 - k) + k);
|
|
double nm = (m * (1.0 - k) + k);
|
|
double ny = (y * (1.0 - k) + k);
|
|
|
|
double r = (1.0 - nc) * 255.0;
|
|
double g = (1.0 - nm) * 255.0;
|
|
double b = (1.0 - ny) * 255.0;
|
|
|
|
pix.r = (unsigned char)(int)r;
|
|
pix.g = (unsigned char)(int)g;
|
|
pix.b = (unsigned char)(int)b;
|
|
}
|
|
|
|
static void CMYK32_RGB32(size_t width, size_t height, const void *pCMYK, size_t srcRowStride, void *pRGB, size_t destRowStride)
|
|
{
|
|
const unsigned char *sl = (const unsigned char *)pCMYK;
|
|
unsigned char *dl = (unsigned char *)pRGB;
|
|
|
|
for (size_t i = 0; i < height; ++i)
|
|
{
|
|
const unsigned char *s = sl;
|
|
unsigned char *d = dl;
|
|
|
|
for (unsigned int j = 0; j < width; ++j)
|
|
{
|
|
CMYK32_RGB32(s, d);
|
|
s += 4;
|
|
d += 4;
|
|
}
|
|
|
|
sl += srcRowStride;
|
|
dl += destRowStride;
|
|
}
|
|
}
|
|
|
|
static void CMYKA40_RGBA32(const void *pCMYK, void *pRGB)
|
|
{
|
|
struct CMYKA
|
|
{
|
|
unsigned char c;
|
|
unsigned char m;
|
|
unsigned char y;
|
|
unsigned char k;
|
|
unsigned char a;
|
|
};
|
|
|
|
struct MyRGBA32
|
|
{
|
|
unsigned char r, g, b, a;
|
|
};
|
|
|
|
const CMYKA &cmyk = *(const CMYKA *)pCMYK;
|
|
MyRGBA32 &pix = *(MyRGBA32 *)pRGB;
|
|
|
|
double c = double(cmyk.c) / 255.0;
|
|
double m = double(cmyk.m) / 255.0;
|
|
double y = double(cmyk.y) / 255.0;
|
|
double k = double(cmyk.k) / 255.0;
|
|
|
|
double nc = (c * (1.0 - k) + k);
|
|
double nm = (m * (1.0 - k) + k);
|
|
double ny = (y * (1.0 - k) + k);
|
|
|
|
double r = (1.0 - nc) * 255.0;
|
|
double g = (1.0 - nm) * 255.0;
|
|
double b = (1.0 - ny) * 255.0;
|
|
|
|
pix.r = (unsigned char)(int)r;
|
|
pix.g = (unsigned char)(int)g;
|
|
pix.b = (unsigned char)(int)b;
|
|
pix.a = cmyk.a;
|
|
}
|
|
|
|
static void CMYK40Alpha_RGBA32(size_t width, size_t height, const void *pCMYK, size_t srcRowStride, void *pRGB, size_t destRowStride)
|
|
{
|
|
const unsigned char *sl = (const unsigned char *)pCMYK;
|
|
unsigned char *dl = (unsigned char *)pRGB;
|
|
|
|
for (size_t i = 0; i < height; ++i)
|
|
{
|
|
const unsigned char *s = sl;
|
|
unsigned char *d = dl;
|
|
|
|
for (unsigned int j = 0; j < width; ++j)
|
|
{
|
|
CMYKA40_RGBA32(s, d);
|
|
s += 5;
|
|
d += 4;
|
|
}
|
|
|
|
sl += srcRowStride;
|
|
dl += destRowStride;
|
|
}
|
|
}
|
|
|
|
static void CMYKA40_RGB32(const void *pCMYK, void *pRGB)
|
|
{
|
|
struct CMYKA
|
|
{
|
|
unsigned char c;
|
|
unsigned char m;
|
|
unsigned char y;
|
|
unsigned char k;
|
|
unsigned char a;
|
|
};
|
|
|
|
struct MyRGBA32
|
|
{
|
|
unsigned char r, g, b, a;
|
|
};
|
|
|
|
const CMYKA &cmyk = *(const CMYKA *)pCMYK;
|
|
MyRGBA32 &pix = *(MyRGBA32 *)pRGB;
|
|
|
|
double c = double(cmyk.c) / 255.0;
|
|
double m = double(cmyk.m) / 255.0;
|
|
double y = double(cmyk.y) / 255.0;
|
|
double k = double(cmyk.k) / 255.0;
|
|
|
|
double nc = (c * (1.0 - k) + k);
|
|
double nm = (m * (1.0 - k) + k);
|
|
double ny = (y * (1.0 - k) + k);
|
|
|
|
double r = (1.0 - nc) * 255.0;
|
|
double g = (1.0 - nm) * 255.0;
|
|
double b = (1.0 - ny) * 255.0;
|
|
|
|
pix.r = (unsigned char)(int)r;
|
|
pix.g = (unsigned char)(int)g;
|
|
pix.b = (unsigned char)(int)b;
|
|
//pix.a = cmyk.a;
|
|
}
|
|
|
|
static void CMYK40Alpha_RGB32(size_t width, size_t height, const void *pCMYK, size_t srcRowStride, void *pRGB, size_t destRowStride)
|
|
{
|
|
const unsigned char *sl = (const unsigned char *)pCMYK;
|
|
unsigned char *dl = (unsigned char *)pRGB;
|
|
|
|
for (size_t i = 0; i < height; ++i)
|
|
{
|
|
const unsigned char *s = sl;
|
|
unsigned char *d = dl;
|
|
|
|
for (unsigned int j = 0; j < width; ++j)
|
|
{
|
|
CMYKA40_RGB32(s, d);
|
|
s += 5;
|
|
d += 4;
|
|
}
|
|
|
|
sl += srcRowStride;
|
|
dl += destRowStride;
|
|
}
|
|
}
|
|
|
|
static void CMYK64_RGB32(const void *pCMYK, void *pRGB)
|
|
{
|
|
struct CMYK
|
|
{
|
|
unsigned short c;
|
|
unsigned short m;
|
|
unsigned short y;
|
|
unsigned short k;
|
|
};
|
|
|
|
struct MyRGB32
|
|
{
|
|
unsigned char r, g, b, x;
|
|
};
|
|
|
|
const CMYK &cmyk = *(const CMYK *)pCMYK;
|
|
MyRGB32 &pix = *(MyRGB32 *)pRGB;
|
|
|
|
double c = double(cmyk.c) / double(0xFFFF);
|
|
double m = double(cmyk.m) / double(0xFFFF);
|
|
double y = double(cmyk.y) / double(0xFFFF);
|
|
double k = double(cmyk.k) / double(0xFFFF);
|
|
|
|
double nc = (c * (1.0 - k) + k);
|
|
double nm = (m * (1.0 - k) + k);
|
|
double ny = (y * (1.0 - k) + k);
|
|
|
|
double r = (1.0 - nc) * 255.0;
|
|
double g = (1.0 - nm) * 255.0;
|
|
double b = (1.0 - ny) * 255.0;
|
|
|
|
pix.r = (unsigned char)(int)r;
|
|
pix.g = (unsigned char)(int)g;
|
|
pix.b = (unsigned char)(int)b;
|
|
}
|
|
|
|
static void CMYK64_RGB32(size_t width, size_t height, const void *pCMYK, size_t srcRowStride, void *pRGB, size_t destRowStride)
|
|
{
|
|
const unsigned char *sl = (const unsigned char *)pCMYK;
|
|
unsigned char *dl = (unsigned char *)pRGB;
|
|
|
|
for (size_t i = 0; i < height; ++i)
|
|
{
|
|
const unsigned char *s = sl;
|
|
unsigned char *d = dl;
|
|
|
|
for (unsigned int j = 0; j < width; ++j)
|
|
{
|
|
CMYK64_RGB32(s, d);
|
|
s += 8;
|
|
d += 4;
|
|
}
|
|
|
|
sl += srcRowStride;
|
|
dl += destRowStride;
|
|
}
|
|
}
|
|
|
|
static void CMYKA80_RGBA32(const void *pCMYK, void *pRGB)
|
|
{
|
|
struct CMYKA
|
|
{
|
|
unsigned short c;
|
|
unsigned short m;
|
|
unsigned short y;
|
|
unsigned short k;
|
|
unsigned short a;
|
|
};
|
|
|
|
struct MyRGBA32
|
|
{
|
|
unsigned char r, g, b, a;
|
|
};
|
|
|
|
const CMYKA &cmyk = *(const CMYKA *)pCMYK;
|
|
MyRGBA32 &pix = *(MyRGBA32 *)pRGB;
|
|
|
|
double c = double(cmyk.c) / double(0xFFFF);
|
|
double m = double(cmyk.m) / double(0xFFFF);
|
|
double y = double(cmyk.y) / double(0xFFFF);
|
|
double k = double(cmyk.k) / double(0xFFFF);
|
|
|
|
double nc = (c * (1.0 - k) + k);
|
|
double nm = (m * (1.0 - k) + k);
|
|
double ny = (y * (1.0 - k) + k);
|
|
|
|
double r = (1.0 - nc) * 255.0;
|
|
double g = (1.0 - nm) * 255.0;
|
|
double b = (1.0 - ny) * 255.0;
|
|
|
|
pix.r = (unsigned char)(int)r;
|
|
pix.g = (unsigned char)(int)g;
|
|
pix.b = (unsigned char)(int)b;
|
|
pix.a = (unsigned char)(cmyk.a >> 8);
|
|
}
|
|
|
|
static void CMYK80Alpha_RGBA32(size_t width, size_t height, const void *pCMYK, size_t srcRowStride, void *pRGB, size_t destRowStride)
|
|
{
|
|
const unsigned char *sl = (const unsigned char *)pCMYK;
|
|
unsigned char *dl = (unsigned char *)pRGB;
|
|
|
|
for (size_t i = 0; i < height; ++i)
|
|
{
|
|
const unsigned char *s = sl;
|
|
unsigned char *d = dl;
|
|
|
|
for (unsigned int j = 0; j < width; ++j)
|
|
{
|
|
CMYKA80_RGBA32(s, d);
|
|
s += 10;
|
|
d += 4;
|
|
}
|
|
|
|
sl += srcRowStride;
|
|
dl += destRowStride;
|
|
}
|
|
}
|
|
|
|
static void CMYKA80_RGB32(const void *pCMYK, void *pRGB)
|
|
{
|
|
struct CMYKA
|
|
{
|
|
unsigned short c;
|
|
unsigned short m;
|
|
unsigned short y;
|
|
unsigned short k;
|
|
unsigned short a;
|
|
};
|
|
|
|
struct MyRGBA32
|
|
{
|
|
unsigned char r, g, b, a;
|
|
};
|
|
|
|
const CMYKA &cmyk = *(const CMYKA *)pCMYK;
|
|
MyRGBA32 &pix = *(MyRGBA32 *)pRGB;
|
|
|
|
double c = double(cmyk.c) / double(0xFFFF);
|
|
double m = double(cmyk.m) / double(0xFFFF);
|
|
double y = double(cmyk.y) / double(0xFFFF);
|
|
double k = double(cmyk.k) / double(0xFFFF);
|
|
|
|
double nc = (c * (1.0 - k) + k);
|
|
double nm = (m * (1.0 - k) + k);
|
|
double ny = (y * (1.0 - k) + k);
|
|
|
|
double r = (1.0 - nc) * 255.0;
|
|
double g = (1.0 - nm) * 255.0;
|
|
double b = (1.0 - ny) * 255.0;
|
|
|
|
pix.r = (unsigned char)(int)r;
|
|
pix.g = (unsigned char)(int)g;
|
|
pix.b = (unsigned char)(int)b;
|
|
//pix.a = (unsigned char)(cmyk.a >> 8);
|
|
}
|
|
|
|
// Converting when alpha is not yet available (so no need to convert alpha)
|
|
static void CMYK80Alpha_RGB32(size_t width, size_t height, const void *pCMYK, size_t srcRowStride, void *pRGB, size_t destRowStride)
|
|
{
|
|
const unsigned char *sl = (const unsigned char *)pCMYK;
|
|
unsigned char *dl = (unsigned char *)pRGB;
|
|
|
|
for (size_t i = 0; i < height; ++i)
|
|
{
|
|
const unsigned char *s = sl;
|
|
unsigned char *d = dl;
|
|
|
|
for (unsigned int j = 0; j < width; ++j)
|
|
{
|
|
CMYKA80_RGB32(s, d);
|
|
s += 10;
|
|
d += 4;
|
|
}
|
|
|
|
sl += srcRowStride;
|
|
dl += destRowStride;
|
|
}
|
|
}
|
|
|
|
void nsJXRDecoder::ConvertAndTransform(uint8_t *pDecoded, size_t width, size_t numLines)
|
|
{
|
|
PKRect cr;
|
|
cr.X = 0;
|
|
cr.Y = 0;
|
|
cr.Height = numLines;
|
|
cr.Width = width;
|
|
|
|
PixelFormat outPixFmt = m_outPixelFormat;
|
|
uint32_t outRowStride = m_mbRowBufStride;
|
|
bool cmyk = false;
|
|
|
|
switch (m_outPixelFormat)
|
|
{
|
|
case pfCMYK32:
|
|
CMYK32_RGB32(width, numLines, pDecoded, m_mbRowBufStride, m_xfBuf, m_xfBufRowStride);
|
|
cmyk = true;
|
|
break;
|
|
|
|
case pfCMYKA40:
|
|
if (DecodingAlpha())
|
|
CMYK40Alpha_RGBA32(width, numLines, pDecoded, m_mbRowBufStride, m_xfBuf, m_xfBufRowStride);
|
|
else
|
|
CMYK40Alpha_RGB32(width, numLines, pDecoded, m_mbRowBufStride, m_xfBuf, m_xfBufRowStride);
|
|
|
|
cmyk = true;
|
|
break;
|
|
|
|
case pfCMYK64:
|
|
CMYK64_RGB32(width, numLines, pDecoded, m_mbRowBufStride, m_xfBuf, m_xfBufRowStride);
|
|
cmyk = true;
|
|
break;
|
|
|
|
case pfCMYKA80:
|
|
if (DecodingAlpha())
|
|
CMYK80Alpha_RGBA32(width, numLines, pDecoded, m_mbRowBufStride, m_xfBuf, m_xfBufRowStride);
|
|
else
|
|
CMYK80Alpha_RGB32(width, numLines, pDecoded, m_mbRowBufStride, m_xfBuf, m_xfBufRowStride);
|
|
|
|
cmyk = true;
|
|
break;
|
|
default:
|
|
// A NOP to silence a compiler warning until this is rewritten.
|
|
// [rhinoduck]
|
|
break;
|
|
}
|
|
|
|
if (cmyk)
|
|
{
|
|
outPixFmt = m_xfPixelFormat;
|
|
outRowStride = m_xfBufRowStride;
|
|
}
|
|
|
|
m_pConverter->Convert(m_pConverter, &cr, pDecoded, m_mbRowBufStride);
|
|
|
|
// Perform color transformation if necessary
|
|
if (m_transform)
|
|
{
|
|
if (nullptr != m_xfBuf)
|
|
{
|
|
const uint8_t *pSrc = pDecoded;
|
|
uint8_t *pDest = m_xfBuf;
|
|
|
|
for (size_t i = 0; i < numLines; ++i)
|
|
{
|
|
qcms_transform_data(m_transform, (void *)pSrc, pDest, width);
|
|
pSrc += m_mbRowBufStride;
|
|
pDest += m_xfBufRowStride;
|
|
}
|
|
|
|
outPixFmt = m_xfPixelFormat;
|
|
outRowStride = m_xfBufRowStride;
|
|
}
|
|
else
|
|
{
|
|
// Perform in-place pixel format conversion if necessary
|
|
switch (m_xfPixelFormat)
|
|
{
|
|
case pfRGB24:
|
|
BGR24_RGB24(nullptr, &cr, pDecoded, m_mbRowBufStride);
|
|
outPixFmt = m_xfPixelFormat;
|
|
break;
|
|
|
|
case pfRGB32:
|
|
case pfRGBA32:
|
|
BGRA32_RGBA32(nullptr, &cr, pDecoded, m_mbRowBufStride);
|
|
outPixFmt = m_xfPixelFormat;
|
|
break;
|
|
default:
|
|
// A NOP to silence a compiler warning until this is rewritten.
|
|
// [rhinoduck]
|
|
break;
|
|
}
|
|
|
|
uint8_t *pLine = pDecoded;
|
|
|
|
// Color image
|
|
for (size_t i = 0; i < numLines; ++i)
|
|
{
|
|
qcms_transform_data(m_transform, pLine, pLine, width);
|
|
pLine += m_mbRowBufStride;
|
|
}
|
|
}
|
|
}
|
|
|
|
// To silence compiler warnings until this is rewritten. These variables
|
|
// actually really are not used, only assigned to. Sigh. [rhinoduck]
|
|
(void)outPixFmt;
|
|
(void)outRowStride;
|
|
}
|
|
|
|
bool nsJXRDecoder::DecodeNextMBRow(bool invalidate, bool output)
|
|
{
|
|
if (FinishedDecodingMainPlane())
|
|
return false;
|
|
|
|
size_t width = m_pDecoder->WMP.wmiI.cThumbnailWidth;
|
|
size_t height = m_pDecoder->WMP.wmiI.cThumbnailHeight;
|
|
|
|
if (m_currLine >= height)
|
|
return false;
|
|
|
|
size_t numLinesDecoded;
|
|
Bool finished;
|
|
/*ERR err =*/ JXR_DecodeNextMBRow(m_pDecoder, m_mbRowBuf, m_mbRowBufStride, &numLinesDecoded, &finished);
|
|
// ^ Just continue overlooking errors until this is rewritten. But without
|
|
// a compiler warning. [rhinoduck]
|
|
|
|
if (0 == numLinesDecoded)
|
|
return false;
|
|
|
|
ConvertAndTransform(m_mbRowBuf, width, numLinesDecoded);
|
|
|
|
size_t top = m_currLine;
|
|
m_currLine += numLinesDecoded;
|
|
|
|
if (output)
|
|
{
|
|
UpdateImage(top, width, numLinesDecoded);
|
|
|
|
// Invalidate the rectangle
|
|
if (invalidate)
|
|
{
|
|
nsIntRect r(0, top, (uint32_t)width, numLinesDecoded);
|
|
PostInvalidation(r);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool nsJXRDecoder::DecodeNextMBRow_Alpha(bool invalidate)
|
|
{
|
|
size_t width = m_pDecoder->WMP.wmiI_Alpha.cThumbnailWidth;
|
|
size_t height = m_pDecoder->WMP.wmiI_Alpha.cThumbnailHeight;
|
|
|
|
if (m_currLine >= height)
|
|
return false;
|
|
|
|
size_t numLinesDecoded;
|
|
Bool finished;
|
|
/*ERR err =*/ JXR_DecodeNextMBRow_Alpha(m_pDecoder, m_mbRowBuf, m_mbRowBufStride, &numLinesDecoded, &finished);
|
|
// ^ Just continue overlooking errors until this is rewritten. But without
|
|
// a compiler warning. [rhinoduck]
|
|
|
|
if (0 == numLinesDecoded)
|
|
return false;
|
|
|
|
PKRect cr;
|
|
cr.X = 0;
|
|
cr.Y = 0;
|
|
cr.Height = numLinesDecoded;
|
|
cr.Width = width;
|
|
|
|
m_pConverter->Convert(m_pConverter, &cr, m_mbRowBuf, m_mbRowBufStride);
|
|
|
|
UpdateImage_AlphaOnly(m_currLine, width, numLinesDecoded);
|
|
|
|
if (invalidate)
|
|
{
|
|
nsIntRect r(0, m_currLine, width, numLinesDecoded);
|
|
PostInvalidation(r);
|
|
}
|
|
|
|
m_currLine += numLinesDecoded;
|
|
|
|
return true;
|
|
}
|
|
|
|
// Decoding with planar alpha. No need to invalidate macroblock rows.
|
|
bool nsJXRDecoder::DecodeNextMBRowWithAlpha()
|
|
{
|
|
size_t width = m_pDecoder->WMP.wmiI_Alpha.cThumbnailWidth;
|
|
size_t height = m_pDecoder->WMP.wmiI_Alpha.cThumbnailHeight;
|
|
|
|
if (m_currLine >= height)
|
|
return false;
|
|
|
|
size_t numLinesDecoded, numLinesDecoded1;
|
|
Bool finished;
|
|
/*ERR err =*/ JXR_DecodeNextMBRow(m_pDecoder, m_mbRowBuf, m_mbRowBufStride, &numLinesDecoded, &finished);
|
|
// ^ Just continue overlooking errors until this is rewritten. But without
|
|
// a compiler warning. [rhinoduck]
|
|
JXR_DecodeNextMBRow_Alpha(m_pDecoder, m_mbRowBuf, m_mbRowBufStride, &numLinesDecoded1, &finished);
|
|
|
|
if (0 == numLinesDecoded || numLinesDecoded != numLinesDecoded1)
|
|
return false;
|
|
|
|
ConvertAndTransform(m_mbRowBuf, width, numLinesDecoded);
|
|
|
|
UpdateImage(m_currLine, width, numLinesDecoded);
|
|
m_currLine += numLinesDecoded;
|
|
|
|
return true;
|
|
}
|
|
|
|
void nsJXRDecoder::DecodeNextTileRow()
|
|
{
|
|
size_t width, height;
|
|
//GetSize(width, height);
|
|
GetThumbnailSize(width, height);
|
|
size_t rowTop, rowHeight;
|
|
|
|
if (IsProgressiveDecoding())
|
|
{
|
|
const TileRowBandInfo &tileRowBandInfo = m_tileRowBandInfos[GetCurrentTileRow()];
|
|
rowTop = tileRowBandInfo.top;
|
|
rowHeight = tileRowBandInfo.height;
|
|
}
|
|
else
|
|
{
|
|
const TileRowInfo &tileRowInfo = m_tileRowInfos[GetCurrentTileRow()];
|
|
rowTop = tileRowInfo.top;
|
|
rowHeight = tileRowInfo.height;
|
|
}
|
|
|
|
// Since we skipped tile rows, we do not want to output the first row
|
|
// (which is in fact the last row of the preceding tile row)
|
|
// because it will be a wrong one (the last macroblock row of the tile row preceding the first skipped one)
|
|
bool output = !SkippedTileRows();
|
|
|
|
for (;;)
|
|
{
|
|
bool decoded = DecodeNextMBRow(false, output);
|
|
output = true;
|
|
|
|
if (!decoded || m_currLine >= rowTop + rowHeight)
|
|
break;
|
|
}
|
|
|
|
if (SkippedTileRows())
|
|
{
|
|
m_skippedTileRows = false;
|
|
const size_t MBR_HEIGHT = 16;
|
|
rowTop += MBR_HEIGHT / m_pDecoder->WMP.wmiI.cThumbnailScale;
|
|
}
|
|
|
|
// Invalidate
|
|
nsIntRect r(0, rowTop, width, m_currLine - rowTop);
|
|
PostInvalidation(r);
|
|
}
|
|
|
|
void nsJXRDecoder::EndDecodingMBRows()
|
|
{
|
|
if (!StartedDecodingMBRows())
|
|
return;
|
|
|
|
JXR_EndDecodingMBRows(m_pDecoder);
|
|
m_startedDecodingMBRows = false;
|
|
m_currLine = 0;
|
|
FreeMBRowBuffers();
|
|
}
|
|
|
|
void nsJXRDecoder::EndDecodingMBRows_Alpha()
|
|
{
|
|
if (!StartedDecodingMBRows_Alpha())
|
|
return;
|
|
|
|
JXR_EndDecodingMBRows_Alpha(m_pDecoder);
|
|
m_startedDecodingMBRows_Alpha = false;
|
|
m_currLine = 0;
|
|
FreeMBRowBuffers();
|
|
}
|
|
|
|
void nsJXRDecoder::FreeMBRowBuffers()
|
|
{
|
|
if (nullptr != m_mbRowBuf)
|
|
{
|
|
PKFreeAligned((void **)&m_mbRowBuf);
|
|
m_mbRowBuf = nullptr;
|
|
m_mbRowBufStride = 0;
|
|
}
|
|
|
|
if (nullptr != m_xfBuf)
|
|
{
|
|
moz_free(m_xfBuf);
|
|
m_xfBuf = nullptr;
|
|
}
|
|
}
|
|
|
|
void nsJXRDecoder::CreateColorTransform()
|
|
{
|
|
size_t cb = m_pDecoder->WMP.wmiDEMisc.uColorProfileByteCount;
|
|
|
|
if (0 == cb)
|
|
return;
|
|
|
|
void *buf = moz_malloc(cb);
|
|
|
|
if (nullptr != buf)
|
|
{
|
|
U32 cbRead = cb;
|
|
ERR err = m_pDecoder->GetColorContext(m_pDecoder, (U8 *)buf, &cbRead);
|
|
|
|
if (WMP_errSuccess == err)
|
|
m_inProfile = qcms_profile_from_memory(buf, cb);
|
|
|
|
moz_free(buf);
|
|
|
|
if (m_inProfile != nullptr)
|
|
{
|
|
uint32_t profileSpace = qcms_profile_get_color_space(m_inProfile);
|
|
bool mismatch = false;
|
|
|
|
if (gfxPlatform::GetCMSOutputProfile())
|
|
{
|
|
// Calculate rendering intent
|
|
int intent = gfxPlatform::GetRenderingIntent();
|
|
|
|
if (-1 == intent)
|
|
intent = qcms_profile_get_rendering_intent(m_inProfile);
|
|
|
|
// Create the color management transform
|
|
qcms_data_type inType, outType;
|
|
|
|
if (Y_ONLY == m_pDecoder->WMP.wmiI.cfColorFormat)
|
|
{
|
|
if (icSigGrayData != profileSpace)
|
|
mismatch = true;
|
|
else
|
|
{
|
|
// Gray JPEG-XR images with alpha are not supported
|
|
inType = QCMS_DATA_GRAY_8;
|
|
outType = QCMS_DATA_RGB_8;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (icSigRgbData != profileSpace)
|
|
mismatch = true;
|
|
else
|
|
{
|
|
if (HasAlpha())
|
|
{
|
|
inType = QCMS_DATA_RGBA_8;
|
|
outType = QCMS_DATA_RGBA_8;
|
|
}
|
|
else
|
|
{
|
|
inType = QCMS_DATA_RGB_8;
|
|
outType = QCMS_DATA_RGB_8;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (mismatch)
|
|
{
|
|
// Log the mismatch here
|
|
}
|
|
else
|
|
m_transform = qcms_transform_create(m_inProfile, inType,
|
|
gfxPlatform::GetCMSOutputProfile(), outType, (qcms_intent)intent);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void nsJXRDecoder::InitInternal()
|
|
{
|
|
if (!CreateJXRStuff())
|
|
PostDecoderError(NS_ERROR_FAILURE);
|
|
}
|
|
|
|
void nsJXRDecoder::FixWrongImageSizeTag(size_t maxSize)
|
|
{
|
|
U32 byteCount = maxSize - m_pDecoder->WMP.wmiDEMisc.uImageOffset;
|
|
m_pDecoder->WMP.wmiDEMisc.uImageByteCount = byteCount;
|
|
m_pDecoder->WMP.wmiI.uImageByteCount = byteCount;
|
|
|
|
// Fix the tile row info if exists
|
|
if (nullptr != m_tileRowInfos)
|
|
{
|
|
for (size_t i = 0; i < GetNumTileRows(); ++i)
|
|
{
|
|
TileRowInfo &info = m_tileRowInfos[i];
|
|
|
|
// Workaround for the bug with wrong uImageByteCount in the header
|
|
if (info.upperLimit > maxSize)
|
|
info.upperLimit = maxSize;
|
|
|
|
if (info.nextLowerLimit > maxSize)
|
|
info.nextLowerLimit = maxSize;
|
|
}
|
|
}
|
|
// Fix the tile row band info if exists
|
|
else if (nullptr != m_tileRowBandInfos)
|
|
{
|
|
for (size_t i = 0; i < GetNumTileRows(); ++i)
|
|
{
|
|
TileRowBandInfo &info = m_tileRowBandInfos[i];
|
|
|
|
for (size_t j = 0; j < GetNumAvailableBands(); ++j)
|
|
{
|
|
if (info.upperLimits[j] > maxSize)
|
|
info.upperLimits[j] = maxSize;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!FinishedDecodingMainPlane())
|
|
// This will take care of all other cases, but will not harm the above two ones.
|
|
SetCurrentSubbandUpperLimit(m_pDecoder->WMP.ctxSC, maxSize);
|
|
}
|
|
|
|
#define DISCARD_HEAD
|
|
EXTERN_C Bool SetCurrentTileRow(CTXSTRCODEC ctxSC, size_t tileRow);
|
|
|
|
void nsJXRDecoder::DoTheDecoding()
|
|
{
|
|
if (IsProgressiveDecoding())
|
|
{
|
|
NextSubBand:
|
|
if (!StartedDecodingSubband())
|
|
StartDecodingNextSubband();
|
|
|
|
// Decode at least one tile row
|
|
size_t numCoveredSubBands = GetNumberOfCoveredSubBands();
|
|
bool bandDecoded = false;
|
|
|
|
if (numCoveredSubBands > GetCurrentSubBand() && !(GetCurrentSubBand() + 1 == numCoveredSubBands &&
|
|
GetCurrentTileRow() > 0) && !HasEmptyTileRowSubbands(GetCurrentSubBand()))
|
|
{
|
|
if (numCoveredSubBands > GetCurrentSubBand() + 1)
|
|
{
|
|
// Re-initialize the decoder to decode the available frequency bands
|
|
EndDecodingCurrentSubband();
|
|
m_currentSubBand = numCoveredSubBands - 1;
|
|
StartDecodingNextSubband();
|
|
}
|
|
|
|
// We are going to decode all tile rows at once, so set the upper limit of the entire subband
|
|
SetCurrentSubbandUpperLimit(m_pDecoder->WMP.ctxSC, GetCurrentSubbandUpperLimit());
|
|
DecodeAllMBRows();
|
|
m_currentSubBand = numCoveredSubBands;
|
|
bandDecoded = true;
|
|
}
|
|
else if (GetNumTileRows() > 1)
|
|
{
|
|
// Decode as many tile rows as possible
|
|
for (; ;)
|
|
{
|
|
size_t upperLimit = GetCurrentSubbandTileRowUpperLimit();
|
|
|
|
// upperLimit == 0 means the subband is empty for this tile row.
|
|
if (0 == upperLimit || GetTotalNumBytesReceived() >= upperLimit)
|
|
{
|
|
// A little optimisation - see whether we can avoid decoding of the entire tile row
|
|
bool canSkip = false;
|
|
|
|
if (0 == upperLimit)
|
|
{
|
|
if (m_pDecoder->WMP.wmiSCP.bUseHardTileBoundaries || 0 == m_pDecoder->WMP.wmiSCP.olOverlap)
|
|
{
|
|
// Either "hard" tiles or over tile boundaries overlap filtering is not used
|
|
canSkip = true;
|
|
}
|
|
else
|
|
{
|
|
// If not "hard" tiles or over tile boundaries overlap filtering is used,
|
|
// skip only if the next tile row also does not have this subband
|
|
if (GetCurrentTileRow() < GetNumTileRows() - 1)
|
|
{
|
|
TileRowBandInfo &nextInfo = m_tileRowBandInfos[GetCurrentTileRow() + 1];
|
|
canSkip = 0 == nextInfo.upperLimits[GetCurrentSubBand()];
|
|
}
|
|
}
|
|
}
|
|
|
|
if (canSkip)
|
|
{
|
|
// The subband is empty for this tile row, and we can safely skip it.
|
|
// Decoding this tile row again with an empty subband would make no sense because the output would
|
|
// be exactly the same as after decoding the previous subband
|
|
// This won't take place with DC and LP subbands, so we don't care whether we have "soft" or "hard" tiles
|
|
if (GetCurrentTileRow() < GetNumTileRows() - 1)
|
|
{
|
|
TileRowBandInfo &nextInfo = m_tileRowBandInfos[GetCurrentTileRow() + 1];
|
|
m_pDecoder->WMP.DecoderCurrMBRow = nextInfo.topMBRow + 1;
|
|
SetCurrentTileRow(m_pDecoder->WMP.ctxSC, GetCurrentTileRow() + 1);
|
|
m_currLine = nextInfo.top;
|
|
|
|
// !!!We should instruct the JXRLib decoder not to perform output at all.
|
|
// I.e. the decoder should not call Load() method. For now just do not update
|
|
// the corresponding rows in Firefox' image!!!
|
|
m_skippedTileRows = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Ready to decode next subband in one pass without backing up/restoring the decoder state
|
|
SetCurrentSubbandUpperLimit(m_pDecoder->WMP.ctxSC, upperLimit);
|
|
DecodeNextTileRow();
|
|
}
|
|
|
|
++m_currentTileRow;
|
|
|
|
if (GetCurrentTileRow() >= GetNumTileRows())
|
|
{
|
|
bandDecoded = true;
|
|
++m_currentSubBand;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (bandDecoded)
|
|
{
|
|
if (GetCurrentSubBand() >= GetNumAvailableBands())
|
|
{
|
|
EndDecodingMBRows();
|
|
m_mainPlaneFinished = true;
|
|
return; // there are no more bands for progressive and sequential decoding
|
|
}
|
|
|
|
EndDecodingCurrentSubband();
|
|
goto NextSubBand;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (SPATIAL == m_pDecoder->WMP.wmiSCP.bfBitstreamFormat && 1 == GetNumTileCols())
|
|
{
|
|
// fail-safe macroblock row-by-row decoding
|
|
for (; ;)
|
|
{
|
|
size_t currMBRow = m_pDecoder->WMP.DecoderCurrMBRow;
|
|
bool decoded = DecodeNextMBRow(true);
|
|
|
|
if (decoded)
|
|
{
|
|
#ifdef DISCARD_HEAD
|
|
// Now we can discard the head of the stream that has been decoded.
|
|
// We use the fact that in SPATIAL mode there is only one bitIO object per tile.
|
|
// Again, since there is no macrobloc row index table, we have to use a complex rule to
|
|
// determine the how many bytes we can safely discard at the head of the stream.
|
|
size_t pos;
|
|
ERR err = m_pStream->GetPos(m_pStream, &pos);
|
|
|
|
if (WMP_errSuccess == err)
|
|
{
|
|
if (nullptr != m_tileRowInfos)
|
|
{
|
|
size_t currMBRowTileRow = GetTileRowForMBRow(currMBRow);
|
|
size_t nextLowerLimit = m_tileRowInfos[currMBRowTileRow].nextLowerLimit;
|
|
|
|
if (pos > nextLowerLimit)
|
|
pos = nextLowerLimit;
|
|
}
|
|
|
|
size_t discarded;
|
|
m_pStream->DiscardHead(m_pStream, pos, &discarded);
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
else // multiple tiles, either SPATIAL or FREQUENCY layout
|
|
{
|
|
for (; ;)
|
|
{
|
|
// For tile rows consisting of 1 macrobock row, wait until the next tile row arrives
|
|
// (unfortunately, we don't know the size of the next macroblock row because there is no macroblock index table)
|
|
const TileRowInfo &info = m_tileRowInfos[GetCurrentTileRow()];
|
|
size_t tileRowUpperLimit = 1 == info.numMBRows ? m_tileRowInfos[GetCurrentTileRow() + 1].upperLimit : info.upperLimit;
|
|
|
|
if (GetTotalNumBytesReceived() < tileRowUpperLimit)
|
|
break;
|
|
|
|
SetCurrentSubbandUpperLimit(m_pDecoder->WMP.ctxSC, tileRowUpperLimit);
|
|
DecodeNextTileRow();
|
|
++m_currentTileRow;
|
|
|
|
#ifdef DISCARD_HEAD
|
|
// Now we can safely discard the head of the stream and free some heap
|
|
size_t discarded;
|
|
m_pStream->DiscardHead(m_pStream, info.nextLowerLimit, &discarded);
|
|
#endif
|
|
|
|
if (GetCurrentTileRow() >= GetNumTileRows())
|
|
{
|
|
m_mainPlaneFinished = true;
|
|
return; // there are no more tile rows for sequential decoding
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void nsJXRDecoder::WriteInternal(const char *aBuffer, uint32_t aCount)
|
|
{
|
|
MOZ_ASSERT(!HasError(), "Shouldn't call WriteInternal after error!");
|
|
|
|
// aCount == 0 means EOF
|
|
// In reality, it seems that the function is neevr called with aCount == 0. Leave it here just in case.
|
|
if (0 == aCount)
|
|
{
|
|
if (!DecoderInitialized() || FinishedDecodingMainPlane())
|
|
return;
|
|
|
|
// Workaround for wrong image byte count in the header. This can take place in old images created with a
|
|
// buggy version of JXRLib
|
|
if (GetTotalNumBytesReceived() < m_pDecoder->WMP.wmiDEMisc.uImageOffset + m_pDecoder->WMP.wmiDEMisc.uImageByteCount)
|
|
{
|
|
// Let's not do this, or bad things will happen when we receive
|
|
// incomplete data. [rhinoduck]
|
|
//FixWrongImageSizeTag(GetTotalNumBytesReceived());
|
|
|
|
// For now, let's just raise an error instead. [rhinoduck]
|
|
PostDataError();
|
|
return;
|
|
}
|
|
else
|
|
return;
|
|
|
|
}
|
|
else
|
|
Receive((const unsigned char *)aBuffer, aCount);
|
|
|
|
if (FinishedDecodingMainPlane())
|
|
return;
|
|
|
|
if (!DecoderInitialized())
|
|
{
|
|
m_pStream->SetPos(m_pStream, 0);
|
|
InitializeJXRDecoder();
|
|
|
|
if (!DecoderInitialized())
|
|
return;
|
|
|
|
// The browser needs to know whether the image has an alpha channel
|
|
// before it is fed any data so that it can render it properly. This
|
|
// fixes transparent areas appearing as black in some images.
|
|
// [rhinoduck]
|
|
if (HasAlpha()) {
|
|
PostHasTransparency();
|
|
}
|
|
|
|
size_t width, height;
|
|
//GetSize(width, height);
|
|
GetThumbnailSize(width, height);
|
|
|
|
// Let Firefox handle the desired orientation
|
|
m_pDecoder->WMP.wmiI.oOrientation = O_NONE;
|
|
|
|
if (m_pDecoder->WMP.fOrientationFromContainer && O_NONE != m_pDecoder->WMP.oOrientationFromContainer)
|
|
{
|
|
|
|
/* JXRLIB orientation
|
|
// rotation and flip
|
|
typedef enum ORIENTATION {
|
|
// CRW: Clock Wise 90% Rotation; FlipH: Flip Horizontally; FlipV: Flip Vertically
|
|
// Peform rotation FIRST!
|
|
// CRW FlipH FlipV
|
|
O_NONE = 0, // 0 0 0
|
|
O_FLIPV, // 0 0 1
|
|
O_FLIPH, // 0 1 0
|
|
O_FLIPVH, // 0 1 1
|
|
O_RCW, // 1 0 0
|
|
O_RCW_FLIPV, // 1 0 1
|
|
O_RCW_FLIPH, // 1 1 0
|
|
O_RCW_FLIPVH, // 1 1 1
|
|
// add new ORIENTATION here
|
|
O_MAX
|
|
} ORIENTATION;
|
|
*/
|
|
/* Mozilla orientation
|
|
* A struct that describes an image's orientation as a rotation optionally
|
|
* followed by a reflection. This may be used to be indicate an image's inherent
|
|
* orientation or a desired orientation for the image.
|
|
MOZ_BEGIN_ENUM_CLASS(Angle, uint8_t)
|
|
D0,
|
|
D90,
|
|
D180,
|
|
D270
|
|
MOZ_END_ENUM_CLASS(Angle)
|
|
|
|
MOZ_BEGIN_ENUM_CLASS(Flip, uint8_t)
|
|
Unflipped,
|
|
Horizontal
|
|
MOZ_END_ENUM_CLASS(Flip)
|
|
*/
|
|
Angle angle = Angle::D0;
|
|
Flip flip = Flip::Unflipped;
|
|
|
|
// Mozilla angles are CW (clockwise)
|
|
switch (m_pDecoder->WMP.oOrientationFromContainer)
|
|
{
|
|
// CWR FlipH FlipV
|
|
case O_FLIPV: // 0 0 1
|
|
angle = Angle::D180;
|
|
flip = Flip::Horizontal;
|
|
break;
|
|
|
|
case O_FLIPH: // 0 1 0
|
|
//angle = Angle::D0;
|
|
flip = Flip::Horizontal;
|
|
break;
|
|
|
|
case O_FLIPVH: // 0 1 1
|
|
angle = Angle::D180;
|
|
//flip = Flip::Unflipped;
|
|
break;
|
|
|
|
case O_RCW: // 1 0 0
|
|
angle = Angle::D90;
|
|
//flip = Flip::Unflipped;
|
|
break;
|
|
|
|
case O_RCW_FLIPV: // 1 0 1
|
|
angle = Angle::D270;
|
|
flip = Flip::Horizontal;
|
|
break;
|
|
|
|
case O_RCW_FLIPH: // 1 1 0
|
|
angle = Angle::D90;
|
|
flip = Flip::Horizontal;
|
|
break;
|
|
|
|
case O_RCW_FLIPVH: // 1 1 1
|
|
angle = Angle::D270;
|
|
//flip = Flip::Unflipped;
|
|
break;
|
|
default:
|
|
// A NOP to silence a compiler warning until this is rewritten.
|
|
// [rhinoduck]
|
|
break;
|
|
}
|
|
|
|
Orientation mozOrient(angle, flip);
|
|
PostSize(width, height, mozOrient);
|
|
}
|
|
else
|
|
PostSize(width, height);
|
|
|
|
if (HasError())
|
|
{
|
|
// Setting the size led to an error.
|
|
return;
|
|
}
|
|
|
|
// If the image's main plane is fully available at this point, decode it at once at the end
|
|
if (GetTotalNumBytesReceived() >= m_pDecoder->WMP.wmiDEMisc.uImageOffset + m_pDecoder->WMP.wmiDEMisc.uImageByteCount)
|
|
m_decodeAtEnd = true;
|
|
|
|
// We have the size. If we're doing a size decode, we got what
|
|
// we came for.
|
|
if (IsMetadataDecode())
|
|
return;
|
|
|
|
CreateColorTransform();
|
|
}
|
|
|
|
// here we always allocate a Frame
|
|
// other decoders (JPEG, WEBP) check for an existing buffer.
|
|
// FIXME: can it be done here?
|
|
nsresult rv_ = AllocateBasicFrame();
|
|
if (NS_FAILED(rv_)) {
|
|
return;
|
|
}
|
|
|
|
if (DecodeAtEnd())
|
|
{
|
|
// For now just accumulate the image data. The image will be decoded in FinishInternal().
|
|
// Later this can be optimized either to use a one-piece source buffer (rather than a chain one).
|
|
return;
|
|
}
|
|
|
|
if (!StartedDecodingMBRows())
|
|
{
|
|
bool spatial = SPATIAL == m_pDecoder->WMP.wmiSCP.bfBitstreamFormat;
|
|
bool interleavedAlpha = HasAlpha() && !HasPlanarAlpha();
|
|
|
|
// Try to do progressive decoding
|
|
if (!spatial)
|
|
StartProgressiveDecoding(interleavedAlpha);
|
|
|
|
if (!StartedDecodingMBRows())
|
|
{
|
|
// Do macroblock row-by-row decoding only if there is only one tile column. Otherwise, decode one tile row at a time
|
|
// after we accumulated enough data. Fail-safe is needed because we do not know how many bytes a macroblock takes.
|
|
bool failSafe = SPATIAL == m_pDecoder->WMP.wmiSCP.bfBitstreamFormat && 1 == GetNumTileCols();
|
|
StartDecodingMBRows(failSafe, interleavedAlpha);
|
|
}
|
|
|
|
if (!StartedDecodingMBRows())
|
|
return;
|
|
|
|
#ifdef DISCARD_HEAD
|
|
// We can now safely discard EXIF and other metadata at the beginning of the image, if any
|
|
size_t discarded;
|
|
m_pStream->DiscardHead(m_pStream, m_pDecoder->WMP.wmiDEMisc.uImageOffset, &discarded);
|
|
#endif
|
|
|
|
if (IsProgressiveDecoding() || GetNumTileRows() > 1 || GetNumTileCols() > 1)
|
|
{
|
|
bool res = IsProgressiveDecoding() ? FillTileRowBandInfo() : FillTileRowInfo();
|
|
|
|
if (!res)
|
|
{
|
|
EndDecodingMBRows();
|
|
PostDecoderError(NS_ERROR_FAILURE);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
DoTheDecoding();
|
|
}
|
|
|
|
void nsJXRDecoder::FinishInternal()
|
|
{
|
|
// We shouldn't be called in error cases
|
|
MOZ_ASSERT(!HasError(), "Can't call FinishInternal on error!");
|
|
|
|
// We should never make multiple frames
|
|
//MOZ_ASSERT(GetFrameCount() <= 1, "Multiple JPEG-XR frames?");
|
|
|
|
// Send notifications if appropriate
|
|
if (!IsMetadataDecode() && HasSize())
|
|
{
|
|
if (DecodeAtEnd())
|
|
{
|
|
StartDecodingMBRows(false, HasAlpha());
|
|
|
|
if (HasPlanarAlpha())
|
|
{
|
|
// Circumvent a common bug in JPEG-XR encoders that writes a wrong alpha plane byte count.
|
|
if (m_pDecoder->WMP.wmiDEMisc.uAlphaOffset + m_pDecoder->WMP.wmiI_Alpha.uImageByteCount > GetTotalNumBytesReceived())
|
|
{
|
|
if (gfxPrefs::MediaJXRWorkaroundAlphaplaneBug()) {
|
|
// Make sure we're not having incomplete data and avoid Bad Things(tm)
|
|
// The encoding bug has the alpha plane byte count exactly equal to the
|
|
// total file byte count. If that is not the case, then we have a bad
|
|
// file and need to throw. [Moonchild]
|
|
if (GetTotalNumBytesReceived() != m_pDecoder->WMP.wmiI_Alpha.uImageByteCount) {
|
|
PostDataError();
|
|
return;
|
|
}
|
|
|
|
// Adjust the alpha plane byte count if the value is wrong.
|
|
// If received data is still incomplete but cut off in the alpha plane data,
|
|
// the alpha plane size will be wrong but should still be an acceptable value
|
|
// for the decoder. [Moonchild]
|
|
m_pDecoder->WMP.wmiI_Alpha.uImageByteCount = GetTotalNumBytesReceived() - m_pDecoder->WMP.wmiDEMisc.uAlphaOffset;
|
|
} else {
|
|
// Don't hack around this encoder bug, report an encoding error instead.
|
|
PostDataError();
|
|
return;
|
|
}
|
|
}
|
|
|
|
StartDecodingMBRows_Alpha();
|
|
DecodeAllMBRowsWithAlpha();
|
|
EndDecodingMBRows_Alpha();
|
|
}
|
|
else
|
|
DecodeAllMBRows();
|
|
|
|
EndDecodingMBRows();
|
|
}
|
|
else
|
|
{
|
|
if (DecoderInitialized() || !FinishedDecodingMainPlane())
|
|
{
|
|
// This can happen if the image plane size tag is wrong, so we have not decoded the remaining MB rows before
|
|
// WriteInternal() was called the last time (and it looks like it is never called with aByteCount == 0).
|
|
// We have to finish the decoding here.
|
|
|
|
// Workaround for wrong image byte count in the header. This can take place in old images created with a
|
|
// buggy version of JXRLib
|
|
if (GetTotalNumBytesReceived() < m_pDecoder->WMP.wmiDEMisc.uImageOffset + m_pDecoder->WMP.wmiDEMisc.uImageByteCount)
|
|
{
|
|
// Let's not do this, or bad things will happen when we
|
|
// receive incomplete data. [rhinoduck]
|
|
//FixWrongImageSizeTag(GetTotalNumBytesReceived());
|
|
//DoTheDecoding();
|
|
|
|
// For now, let's just raise an error instead. [rhinoduck]
|
|
PostDataError();
|
|
return;
|
|
}
|
|
}
|
|
|
|
EndDecodingMBRows();
|
|
|
|
if (HasPlanarAlpha() && m_pDecoder->WMP.wmiDEMisc.uAlphaOffset < GetTotalNumBytesReceived())
|
|
{
|
|
// We can now safely discard main image plane part of the stream
|
|
size_t discarded;
|
|
m_pStream->DiscardHead(m_pStream, m_pDecoder->WMP.wmiDEMisc.uAlphaOffset, &discarded);
|
|
|
|
// Circumvent a common bug in JPEG-XR encoders that writes a wrong alpha plane byte count.
|
|
if (m_pDecoder->WMP.wmiDEMisc.uAlphaOffset + m_pDecoder->WMP.wmiI_Alpha.uImageByteCount > GetTotalNumBytesReceived())
|
|
{
|
|
if (gfxPrefs::MediaJXRWorkaroundAlphaplaneBug()) {
|
|
// Make sure we're not having incomplete data and avoid Bad Things(tm)
|
|
// The encoding bug has the alpha plane byte count exactly equal to the
|
|
// total file byte count. If that is not the case, then we have a bad
|
|
// file and need to throw. [Moonchild]
|
|
if (GetTotalNumBytesReceived() != m_pDecoder->WMP.wmiI_Alpha.uImageByteCount) {
|
|
PostDataError();
|
|
return;
|
|
}
|
|
|
|
// Adjust the alpha plane byte count if the value is wrong.
|
|
// If received data is still incomplete but cut off in the alpha plane data,
|
|
// the alpha plane size will be wrong but should still be an acceptable value
|
|
// for the decoder. [Moonchild]
|
|
m_pDecoder->WMP.wmiI_Alpha.uImageByteCount = GetTotalNumBytesReceived() - m_pDecoder->WMP.wmiDEMisc.uAlphaOffset;
|
|
} else {
|
|
// Don't hack around this encoder bug, report an encoding error instead.
|
|
PostDataError();
|
|
return;
|
|
}
|
|
}
|
|
|
|
StartDecodingMBRows_Alpha();
|
|
SetCurrentSubbandUpperLimit(m_pDecoder->WMP.ctxSC, 0);
|
|
SetCurrentSubbandUpperLimit(m_pDecoder->WMP.ctxSC_Alpha, 0);
|
|
|
|
if (StartedDecodingMBRows_Alpha())
|
|
{
|
|
DecodeAllMBRows_Alpha();
|
|
EndDecodingMBRows_Alpha();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (HasAlpha())
|
|
{
|
|
PostFrameStop(Opacity::SOME_TRANSPARENCY);
|
|
}
|
|
else
|
|
{
|
|
PostFrameStop(Opacity::FULLY_OPAQUE);
|
|
}
|
|
|
|
PostDecodeDone();
|
|
}
|
|
}
|
|
|
|
} // namespace image
|
|
} // namespace mozilla
|