Compare commits
43
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d129c900c9 | ||
|
|
de2c8419c6 | ||
|
|
f6137449b6 | ||
|
|
020248c6e3 | ||
|
|
e0a09e1aaf | ||
|
|
4af3ad5dfe | ||
|
|
ff66da9af4 | ||
|
|
c2e358c520 | ||
|
|
d185071acf | ||
|
|
e517e70eb1 | ||
|
|
0a6db54d86 | ||
|
|
c5d5a28f76 | ||
|
|
a0e20005ef | ||
|
|
a3c1f180ce | ||
|
|
cd4f7a1798 | ||
|
|
914294e570 | ||
|
|
831b9d8d9e | ||
|
|
6864a02d7b | ||
|
|
bffec09e66 | ||
|
|
51e5770962 | ||
|
|
e1bfe33031 | ||
|
|
8240fb9c73 | ||
|
|
f9e04b54c2 | ||
|
|
775e800ca0 | ||
|
|
882aaf5b1f | ||
|
|
8ee235ca5d | ||
|
|
cc54bbe952 | ||
|
|
73b202b1e5 | ||
|
|
21042963de | ||
|
|
d443656df5 | ||
|
|
29a8db7280 | ||
|
|
daa52eef9a | ||
|
|
95ce1f0d63 | ||
|
|
4dadef2740 | ||
|
|
b5d8e6d28f | ||
|
|
fc0477a98e | ||
|
|
36d06b9e75 | ||
|
|
51bdd3efbc | ||
|
|
bc2584493a | ||
|
|
0f38772573 | ||
|
|
ef81277925 | ||
|
|
8b20931e12 | ||
|
|
b247982d37 |
No files matched your search
@@ -229,10 +229,10 @@ pref("keyword.enabled", true);
|
||||
pref("general.useragent.locale", "@AB_CD@");
|
||||
pref("general.skins.selectedSkin", "classic/1.0");
|
||||
|
||||
// Native UA mode by default
|
||||
// Native UA mode by default for unbranded
|
||||
pref("general.useragent.compatMode", 0);
|
||||
pref("general.useragent.compatmode.gecko", false);
|
||||
pref("general.useragent.compatmode.firefox", false);
|
||||
pref("general.useragent.compatMode.gecko", false);
|
||||
pref("general.useragent.compatMode.firefox", false);
|
||||
|
||||
pref("general.smoothScroll", true);
|
||||
#ifdef UNIX_BUT_NOT_MAC
|
||||
|
||||
@@ -257,18 +257,13 @@ Sanitizer.prototype = {
|
||||
.getService(Components.interfaces.nsIWindowMediator);
|
||||
var windows = windowManager.getEnumerator("navigator:browser");
|
||||
while (windows.hasMoreElements()) {
|
||||
let currentWindow = windows.getNext();
|
||||
let currentDocument = currentWindow.document;
|
||||
let currentDocument = windows.getNext().document;
|
||||
let searchBar = currentDocument.getElementById("searchbar");
|
||||
if (searchBar)
|
||||
searchBar.textbox.reset();
|
||||
let tabBrowser = currentWindow.gBrowser;
|
||||
for (let tab of tabBrowser.tabs) {
|
||||
if (tabBrowser.isFindBarInitialized(tab))
|
||||
tabBrowser.getFindBar(tab).clear();
|
||||
}
|
||||
// Clear any saved find value
|
||||
tabBrowser._lastFindValue = "";
|
||||
let findBar = currentDocument.getElementById("FindToolbar");
|
||||
if (findBar)
|
||||
findBar.clear();
|
||||
}
|
||||
|
||||
let change = { op: "remove" };
|
||||
@@ -284,8 +279,7 @@ Sanitizer.prototype = {
|
||||
.getService(Components.interfaces.nsIWindowMediator);
|
||||
var windows = windowManager.getEnumerator("navigator:browser");
|
||||
while (windows.hasMoreElements()) {
|
||||
let currentWindow = windows.getNext();
|
||||
let currentDocument = currentWindow.document;
|
||||
let currentDocument = windows.getNext().document;
|
||||
let searchBar = currentDocument.getElementById("searchbar");
|
||||
if (searchBar) {
|
||||
let transactionMgr = searchBar.textbox.editor.transactionManager;
|
||||
@@ -296,12 +290,8 @@ Sanitizer.prototype = {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
let tabBrowser = currentWindow.gBrowser;
|
||||
let findBarCanClear = Array.some(tabBrowser.tabs, function (aTab) {
|
||||
return tabBrowser.isFindBarInitialized(aTab) &&
|
||||
tabBrowser.getFindBar(aTab).canClear;
|
||||
});
|
||||
if (findBarCanClear) {
|
||||
let findBar = currentDocument.getElementById("FindToolbar");
|
||||
if (findBar && findBar.canClear) {
|
||||
aCallback("formdata", true, aArg);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -698,12 +698,7 @@ FeedWriter.prototype = {
|
||||
* @returns moz-icon url of the given file as a string
|
||||
*/
|
||||
_getFileIconURL: function FW__getFileIconURL(file) {
|
||||
var ios = Cc["@mozilla.org/network/io-service;1"].
|
||||
getService(Ci.nsIIOService);
|
||||
var fph = ios.getProtocolHandler("file")
|
||||
.QueryInterface(Ci.nsIFileProtocolHandler);
|
||||
var urlSpec = fph.getURLSpecFromFile(file);
|
||||
return "moz-icon://" + urlSpec + "?size=16";
|
||||
return "moz-icon://dummy.exe?size=16";
|
||||
},
|
||||
|
||||
/**
|
||||
|
||||
@@ -146,14 +146,21 @@ this.PlacesUIUtils = {
|
||||
* annotations are synced from the old one.
|
||||
* @see this._copyableAnnotations for the list of copyable annotations.
|
||||
*/
|
||||
_getFolderCopyTransaction:
|
||||
function PUIU__getFolderCopyTransaction(aData, aContainer, aIndex)
|
||||
{
|
||||
function getChildItemsTransactions(aChildren)
|
||||
{
|
||||
_getFolderCopyTransaction(aData, aContainer, aIndex) {
|
||||
function getChildItemsTransactions(aRoot) {
|
||||
let transactions = [];
|
||||
let index = aIndex;
|
||||
aChildren.forEach(function (node, i) {
|
||||
for (let i = 0; i < aRoot.childCount; ++i) {
|
||||
let child = aRoot.getChild(i);
|
||||
// Temporary hacks until we switch to PlacesTransactions.jsm.
|
||||
let isLivemark =
|
||||
PlacesUtils.annotations.itemHasAnnotation(child.itemId,
|
||||
PlacesUtils.LMANNO_FEEDURI);
|
||||
let [node] = PlacesUtils.unwrapNodes(
|
||||
PlacesUtils.wrapNode(child, PlacesUtils.TYPE_X_MOZ_PLACE, isLivemark),
|
||||
PlacesUtils.TYPE_X_MOZ_PLACE
|
||||
);
|
||||
|
||||
// Make sure that items are given the correct index, this will be
|
||||
// passed by the transaction manager to the backend for the insertion.
|
||||
// Insertion behaves differently for DEFAULT_INDEX (append).
|
||||
@@ -184,19 +191,21 @@ this.PlacesUIUtils = {
|
||||
else {
|
||||
throw new Error("Unexpected item under a bookmarks folder");
|
||||
}
|
||||
});
|
||||
}
|
||||
return transactions;
|
||||
}
|
||||
|
||||
if (aContainer == PlacesUtils.tagsFolderId) { // Copying a tag folder.
|
||||
if (aContainer == PlacesUtils.tagsFolderId) { // Copying into a tag folder.
|
||||
let transactions = [];
|
||||
if (aData.children) {
|
||||
aData.children.forEach(function(aChild) {
|
||||
if (!aData.livemark && aData.type == PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER) {
|
||||
let {root} = PlacesUtils.getFolderContents(aData.id, false, false);
|
||||
let urls = PlacesUtils.getURLsForContainerNode(root);
|
||||
root.containerOpen = false;
|
||||
for (let { uri } of urls) {
|
||||
transactions.push(
|
||||
new PlacesTagURITransaction(PlacesUtils._uri(aChild.uri),
|
||||
[aData.title])
|
||||
new PlacesTagURITransaction(NetUtil.newURI(uri), [aData.title])
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
return new PlacesAggregatedTransaction("addTags", transactions);
|
||||
}
|
||||
@@ -205,7 +214,10 @@ this.PlacesUIUtils = {
|
||||
return this._getLivemarkCopyTransaction(aData, aContainer, aIndex);
|
||||
}
|
||||
|
||||
let transactions = getChildItemsTransactions(aData.children);
|
||||
let {root} = PlacesUtils.getFolderContents(aData.id, false, false);
|
||||
let transactions = getChildItemsTransactions(root);
|
||||
root.containerOpen = false;
|
||||
|
||||
if (aData.dateAdded) {
|
||||
transactions.push(
|
||||
new PlacesEditItemDateAddedTransaction(null, aData.dateAdded)
|
||||
|
||||
@@ -382,6 +382,11 @@ var BookmarkPropertiesPanel = {
|
||||
.addEventListener("input", this, false);
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure the Name Picker textbox is focused on load
|
||||
var namePickerElem = document.getElementById('editBMPanel_namePicker');
|
||||
namePickerElem.focus();
|
||||
namePickerElem.select();
|
||||
}),
|
||||
|
||||
// nsIDOMEventListener
|
||||
|
||||
@@ -1287,15 +1287,15 @@ PlacesController.prototype = {
|
||||
if (!didSuppressNotifications)
|
||||
result.suppressNotifications = true;
|
||||
|
||||
function addData(type, index, overrideURI) {
|
||||
let wrapNode = PlacesUtils.wrapNode(node, type, overrideURI, doCopy);
|
||||
function addData(type, index, feedURI) {
|
||||
let wrapNode = PlacesUtils.wrapNode(node, type, feedURI);
|
||||
dt.mozSetDataAt(type, wrapNode, index);
|
||||
}
|
||||
|
||||
function addURIData(index, overrideURI) {
|
||||
addData(PlacesUtils.TYPE_X_MOZ_URL, index, overrideURI);
|
||||
addData(PlacesUtils.TYPE_UNICODE, index, overrideURI);
|
||||
addData(PlacesUtils.TYPE_HTML, index, overrideURI);
|
||||
function addURIData(index, feedURI) {
|
||||
addData(PlacesUtils.TYPE_X_MOZ_URL, index, feedURI);
|
||||
addData(PlacesUtils.TYPE_UNICODE, index, feedURI);
|
||||
addData(PlacesUtils.TYPE_HTML, index, feedURI);
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -1387,12 +1387,11 @@ PlacesController.prototype = {
|
||||
copiedFolders.push(node);
|
||||
|
||||
let livemarkInfo = this.getCachedLivemarkInfo(node);
|
||||
let overrideURI = livemarkInfo ? livemarkInfo.feedURI.spec : null;
|
||||
let resolveShortcuts = !PlacesControllerDragHelper.canMoveNode(node);
|
||||
let feedURI = livemarkInfo && livemarkInfo.feedURI.spec;
|
||||
|
||||
contents.forEach(function (content) {
|
||||
content.entries.push(
|
||||
PlacesUtils.wrapNode(node, content.type, overrideURI, resolveShortcuts)
|
||||
PlacesUtils.wrapNode(node, content.type, feedURI)
|
||||
);
|
||||
});
|
||||
}, this);
|
||||
|
||||
@@ -222,11 +222,6 @@ var gEditItemOverlay = {
|
||||
}
|
||||
|
||||
let focusElement = () => {
|
||||
let elt = document.querySelector("textbox:not([collapsed=true])");
|
||||
if (elt) {
|
||||
elt.focus();
|
||||
elt.select();
|
||||
}
|
||||
this._initialized = true;
|
||||
};
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
28.2.0a1
|
||||
28.2.2
|
||||
@@ -0,0 +1,47 @@
|
||||
/* -*- 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/. */
|
||||
|
||||
#ifndef mozilla_image_AnimationParams_h
|
||||
#define mozilla_image_AnimationParams_h
|
||||
|
||||
#include <stdint.h>
|
||||
#include "mozilla/gfx/Rect.h"
|
||||
#include "FrameTimeout.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace image {
|
||||
|
||||
enum class BlendMethod : int8_t {
|
||||
// All color components of the frame, including alpha, overwrite the current
|
||||
// contents of the frame's output buffer region.
|
||||
SOURCE,
|
||||
|
||||
// The frame should be composited onto the output buffer based on its alpha,
|
||||
// using a simple OVER operation.
|
||||
OVER
|
||||
};
|
||||
|
||||
enum class DisposalMethod : int8_t {
|
||||
CLEAR_ALL = -1, // Clear the whole image, revealing what's underneath.
|
||||
NOT_SPECIFIED, // Leave the frame and let the new frame draw on top.
|
||||
KEEP, // Leave the frame and let the new frame draw on top.
|
||||
CLEAR, // Clear the frame's area, revealing what's underneath.
|
||||
RESTORE_PREVIOUS // Restore the previous (composited) frame.
|
||||
};
|
||||
|
||||
struct AnimationParams
|
||||
{
|
||||
gfx::IntRect mBlendRect;
|
||||
FrameTimeout mTimeout;
|
||||
uint32_t mFrameNum;
|
||||
BlendMethod mBlendMethod;
|
||||
DisposalMethod mDisposalMethod;
|
||||
};
|
||||
|
||||
} // namespace image
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_image_AnimationParams_h
|
||||
+22
-27
@@ -278,14 +278,14 @@ Decoder::Telemetry() const
|
||||
}
|
||||
|
||||
nsresult
|
||||
Decoder::AllocateFrame(uint32_t aFrameNum,
|
||||
const gfx::IntSize& aOutputSize,
|
||||
Decoder::AllocateFrame(const gfx::IntSize& aOutputSize,
|
||||
const gfx::IntRect& aFrameRect,
|
||||
gfx::SurfaceFormat aFormat,
|
||||
uint8_t aPaletteDepth)
|
||||
uint8_t aPaletteDepth,
|
||||
const Maybe<AnimationParams>& aAnimParams)
|
||||
{
|
||||
mCurrentFrame = AllocateFrameInternal(aFrameNum, aOutputSize, aFrameRect,
|
||||
aFormat, aPaletteDepth,
|
||||
mCurrentFrame = AllocateFrameInternal(aOutputSize, aFrameRect, aFormat,
|
||||
aPaletteDepth, aAnimParams,
|
||||
mCurrentFrame.get());
|
||||
|
||||
if (mCurrentFrame) {
|
||||
@@ -295,7 +295,7 @@ Decoder::AllocateFrame(uint32_t aFrameNum,
|
||||
|
||||
// We should now be on |aFrameNum|. (Note that we're comparing the frame
|
||||
// number, which is zero-based, with the frame count, which is one-based.)
|
||||
MOZ_ASSERT(aFrameNum + 1 == mFrameCount);
|
||||
MOZ_ASSERT_IF(aAnimParams, aAnimParams->mFrameNum + 1 == mFrameCount);
|
||||
|
||||
// If we're past the first frame, PostIsAnimated() should've been called.
|
||||
MOZ_ASSERT_IF(mFrameCount > 1, HasAnimation());
|
||||
@@ -309,18 +309,19 @@ Decoder::AllocateFrame(uint32_t aFrameNum,
|
||||
}
|
||||
|
||||
RawAccessFrameRef
|
||||
Decoder::AllocateFrameInternal(uint32_t aFrameNum,
|
||||
const gfx::IntSize& aOutputSize,
|
||||
Decoder::AllocateFrameInternal(const gfx::IntSize& aOutputSize,
|
||||
const gfx::IntRect& aFrameRect,
|
||||
SurfaceFormat aFormat,
|
||||
uint8_t aPaletteDepth,
|
||||
const Maybe<AnimationParams>& aAnimParams,
|
||||
imgFrame* aPreviousFrame)
|
||||
{
|
||||
if (HasError()) {
|
||||
return RawAccessFrameRef();
|
||||
}
|
||||
|
||||
if (aFrameNum != mFrameCount) {
|
||||
uint32_t frameNum = aAnimParams ? aAnimParams->mFrameNum : 0;
|
||||
if (frameNum != mFrameCount) {
|
||||
MOZ_ASSERT_UNREACHABLE("Allocating frames out of order");
|
||||
return RawAccessFrameRef();
|
||||
}
|
||||
@@ -334,7 +335,8 @@ Decoder::AllocateFrameInternal(uint32_t aFrameNum,
|
||||
NotNull<RefPtr<imgFrame>> frame = WrapNotNull(new imgFrame());
|
||||
bool nonPremult = bool(mSurfaceFlags & SurfaceFlags::NO_PREMULTIPLY_ALPHA);
|
||||
if (NS_FAILED(frame->InitForDecoder(aOutputSize, aFrameRect, aFormat,
|
||||
aPaletteDepth, nonPremult))) {
|
||||
aPaletteDepth, nonPremult,
|
||||
aAnimParams))) {
|
||||
NS_WARNING("imgFrame::Init should succeed");
|
||||
return RawAccessFrameRef();
|
||||
}
|
||||
@@ -345,22 +347,22 @@ Decoder::AllocateFrameInternal(uint32_t aFrameNum,
|
||||
return RawAccessFrameRef();
|
||||
}
|
||||
|
||||
if (aFrameNum == 1) {
|
||||
if (frameNum == 1) {
|
||||
MOZ_ASSERT(aPreviousFrame, "Must provide a previous frame when animated");
|
||||
aPreviousFrame->SetRawAccessOnly();
|
||||
|
||||
// If we dispose of the first frame by clearing it, then the first frame's
|
||||
// refresh area is all of itself.
|
||||
// RESTORE_PREVIOUS is invalid (assumed to be DISPOSE_CLEAR).
|
||||
AnimationData previousFrameData = aPreviousFrame->GetAnimationData();
|
||||
if (previousFrameData.mDisposalMethod == DisposalMethod::CLEAR ||
|
||||
previousFrameData.mDisposalMethod == DisposalMethod::CLEAR_ALL ||
|
||||
previousFrameData.mDisposalMethod == DisposalMethod::RESTORE_PREVIOUS) {
|
||||
mFirstFrameRefreshArea = previousFrameData.mRect;
|
||||
DisposalMethod prevDisposal = aPreviousFrame->GetDisposalMethod();
|
||||
if (prevDisposal == DisposalMethod::CLEAR ||
|
||||
prevDisposal == DisposalMethod::CLEAR_ALL ||
|
||||
prevDisposal == DisposalMethod::RESTORE_PREVIOUS) {
|
||||
mFirstFrameRefreshArea = aPreviousFrame->GetRect();
|
||||
}
|
||||
}
|
||||
|
||||
if (aFrameNum > 0) {
|
||||
if (frameNum > 0) {
|
||||
ref->SetRawAccessOnly();
|
||||
|
||||
// Some GIFs are huge but only have a small area that they animate. We only
|
||||
@@ -432,13 +434,7 @@ Decoder::PostIsAnimated(FrameTimeout aFirstFrameTimeout)
|
||||
}
|
||||
|
||||
void
|
||||
Decoder::PostFrameStop(Opacity aFrameOpacity
|
||||
/* = Opacity::SOME_TRANSPARENCY */,
|
||||
DisposalMethod aDisposalMethod
|
||||
/* = DisposalMethod::KEEP */,
|
||||
FrameTimeout aTimeout /* = FrameTimeout::Forever() */,
|
||||
BlendMethod aBlendMethod /* = BlendMethod::OVER */,
|
||||
const Maybe<nsIntRect>& aBlendRect /* = Nothing() */)
|
||||
Decoder::PostFrameStop(Opacity aFrameOpacity)
|
||||
{
|
||||
// We should be mid-frame
|
||||
MOZ_ASSERT(!IsMetadataDecode(), "Stopping frame during metadata decode");
|
||||
@@ -449,12 +445,11 @@ Decoder::PostFrameStop(Opacity aFrameOpacity
|
||||
mInFrame = false;
|
||||
mFinishedNewFrame = true;
|
||||
|
||||
mCurrentFrame->Finish(aFrameOpacity, aDisposalMethod, aTimeout,
|
||||
aBlendMethod, aBlendRect);
|
||||
mCurrentFrame->Finish(aFrameOpacity);
|
||||
|
||||
mProgress |= FLAG_FRAME_COMPLETE;
|
||||
|
||||
mLoopLength += aTimeout;
|
||||
mLoopLength += mCurrentFrame->GetTimeout();
|
||||
|
||||
// If we're not sending partial invalidations, then we send an invalidation
|
||||
// here when the first frame is complete.
|
||||
|
||||
+9
-13
@@ -11,6 +11,7 @@
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/NotNull.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "AnimationParams.h"
|
||||
#include "DecodePool.h"
|
||||
#include "DecoderFlags.h"
|
||||
#include "Downscaler.h"
|
||||
@@ -28,6 +29,8 @@ namespace Telemetry {
|
||||
|
||||
namespace image {
|
||||
|
||||
class imgFrame;
|
||||
|
||||
struct DecoderFinalStatus final
|
||||
{
|
||||
DecoderFinalStatus(bool aWasMetadataDecode,
|
||||
@@ -443,11 +446,7 @@ protected:
|
||||
// Specify whether this frame is opaque as an optimization.
|
||||
// For animated images, specify the disposal, blend method and timeout for
|
||||
// this frame.
|
||||
void PostFrameStop(Opacity aFrameOpacity = Opacity::SOME_TRANSPARENCY,
|
||||
DisposalMethod aDisposalMethod = DisposalMethod::KEEP,
|
||||
FrameTimeout aTimeout = FrameTimeout::Forever(),
|
||||
BlendMethod aBlendMethod = BlendMethod::OVER,
|
||||
const Maybe<nsIntRect>& aBlendRect = Nothing());
|
||||
void PostFrameStop(Opacity aFrameOpacity = Opacity::SOME_TRANSPARENCY);
|
||||
|
||||
/**
|
||||
* Called by the decoders when they have a region to invalidate. We may not
|
||||
@@ -476,16 +475,13 @@ protected:
|
||||
/**
|
||||
* Allocates a new frame, making it our current frame if successful.
|
||||
*
|
||||
* The @aFrameNum parameter only exists as a sanity check; it's illegal to
|
||||
* create a new frame anywhere but immediately after the existing frames.
|
||||
*
|
||||
* If a non-paletted frame is desired, pass 0 for aPaletteDepth.
|
||||
*/
|
||||
nsresult AllocateFrame(uint32_t aFrameNum,
|
||||
const gfx::IntSize& aOutputSize,
|
||||
nsresult AllocateFrame(const gfx::IntSize& aOutputSize,
|
||||
const gfx::IntRect& aFrameRect,
|
||||
gfx::SurfaceFormat aFormat,
|
||||
uint8_t aPaletteDepth = 0);
|
||||
uint8_t aPaletteDepth = 0,
|
||||
const Maybe<AnimationParams>& aAnimParams = Nothing());
|
||||
|
||||
private:
|
||||
/// Report that an error was encountered while decoding.
|
||||
@@ -509,11 +505,11 @@ private:
|
||||
return mInFrame ? mFrameCount - 1 : mFrameCount;
|
||||
}
|
||||
|
||||
RawAccessFrameRef AllocateFrameInternal(uint32_t aFrameNum,
|
||||
const gfx::IntSize& aOutputSize,
|
||||
RawAccessFrameRef AllocateFrameInternal(const gfx::IntSize& aOutputSize,
|
||||
const gfx::IntRect& aFrameRect,
|
||||
gfx::SurfaceFormat aFormat,
|
||||
uint8_t aPaletteDepth,
|
||||
const Maybe<AnimationParams>& aAnimParams,
|
||||
imgFrame* aPreviousFrame);
|
||||
|
||||
protected:
|
||||
|
||||
@@ -74,7 +74,7 @@ public:
|
||||
Maybe<SurfaceInvalidRect> TakeInvalidRect() override { return Nothing(); }
|
||||
|
||||
template <typename... Rest>
|
||||
nsresult Configure(const DownscalingConfig& aConfig, Rest... aRest)
|
||||
nsresult Configure(const DownscalingConfig& aConfig, const Rest&... aRest)
|
||||
{
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
@@ -115,7 +115,7 @@ public:
|
||||
}
|
||||
|
||||
template <typename... Rest>
|
||||
nsresult Configure(const DownscalingConfig& aConfig, Rest... aRest)
|
||||
nsresult Configure(const DownscalingConfig& aConfig, const Rest&... aRest)
|
||||
{
|
||||
nsresult rv = mNext.Configure(aRest...);
|
||||
if (NS_FAILED(rv)) {
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
/* -*- 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/. */
|
||||
|
||||
#ifndef mozilla_image_FrameTimeout_h
|
||||
#define mozilla_image_FrameTimeout_h
|
||||
|
||||
#include <stdint.h>
|
||||
#include "mozilla/Assertions.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace image {
|
||||
|
||||
/**
|
||||
* FrameTimeout wraps a frame timeout value (measured in milliseconds) after
|
||||
* first normalizing it. This normalization is necessary because some tools
|
||||
* generate incorrect frame timeout values which we nevertheless have to
|
||||
* support. For this reason, code that deals with frame timeouts should always
|
||||
* use a FrameTimeout value rather than the raw value from the image header.
|
||||
*/
|
||||
struct FrameTimeout
|
||||
{
|
||||
/**
|
||||
* @return a FrameTimeout of zero. This should be used only for math
|
||||
* involving FrameTimeout values. You can't obtain a zero FrameTimeout from
|
||||
* FromRawMilliseconds().
|
||||
*/
|
||||
static FrameTimeout Zero() { return FrameTimeout(0); }
|
||||
|
||||
/// @return an infinite FrameTimeout.
|
||||
static FrameTimeout Forever() { return FrameTimeout(-1); }
|
||||
|
||||
/// @return a FrameTimeout obtained by normalizing a raw timeout value.
|
||||
static FrameTimeout FromRawMilliseconds(int32_t aRawMilliseconds)
|
||||
{
|
||||
// Normalize all infinite timeouts to the same value.
|
||||
if (aRawMilliseconds < 0) {
|
||||
return FrameTimeout::Forever();
|
||||
}
|
||||
|
||||
// Very small timeout values are problematic for two reasons: we don't want
|
||||
// to burn energy redrawing animated images extremely fast, and broken tools
|
||||
// generate these values when they actually want a "default" value, so such
|
||||
// images won't play back right without normalization. For some context,
|
||||
// see bug 890743, bug 125137, bug 139677, and bug 207059. The historical
|
||||
// behavior of IE and Opera was:
|
||||
// IE 6/Win:
|
||||
// 10 - 50ms is normalized to 100ms.
|
||||
// >50ms is used unnormalized.
|
||||
// Opera 7 final/Win:
|
||||
// 10ms is normalized to 100ms.
|
||||
// >10ms is used unnormalized.
|
||||
if (aRawMilliseconds >= 0 && aRawMilliseconds <= 10 ) {
|
||||
return FrameTimeout(100);
|
||||
}
|
||||
|
||||
// The provided timeout value is OK as-is.
|
||||
return FrameTimeout(aRawMilliseconds);
|
||||
}
|
||||
|
||||
bool operator==(const FrameTimeout& aOther) const
|
||||
{
|
||||
return mTimeout == aOther.mTimeout;
|
||||
}
|
||||
|
||||
bool operator!=(const FrameTimeout& aOther) const { return !(*this == aOther); }
|
||||
|
||||
FrameTimeout operator+(const FrameTimeout& aOther)
|
||||
{
|
||||
if (*this == Forever() || aOther == Forever()) {
|
||||
return Forever();
|
||||
}
|
||||
|
||||
return FrameTimeout(mTimeout + aOther.mTimeout);
|
||||
}
|
||||
|
||||
FrameTimeout& operator+=(const FrameTimeout& aOther)
|
||||
{
|
||||
*this = *this + aOther;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return this FrameTimeout's value in milliseconds. Illegal to call on a
|
||||
* an infinite FrameTimeout value.
|
||||
*/
|
||||
uint32_t AsMilliseconds() const
|
||||
{
|
||||
if (*this == Forever()) {
|
||||
MOZ_ASSERT_UNREACHABLE("Calling AsMilliseconds() on an infinite FrameTimeout");
|
||||
return 100; // Fail to something sane.
|
||||
}
|
||||
|
||||
return uint32_t(mTimeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return this FrameTimeout value encoded so that non-negative values
|
||||
* represent a timeout in milliseconds, and -1 represents an infinite
|
||||
* timeout.
|
||||
*
|
||||
* XXX(seth): This is a backwards compatibility hack that should be removed.
|
||||
*/
|
||||
int32_t AsEncodedValueDeprecated() const { return mTimeout; }
|
||||
|
||||
private:
|
||||
explicit FrameTimeout(int32_t aTimeout)
|
||||
: mTimeout(aTimeout)
|
||||
{ }
|
||||
|
||||
int32_t mTimeout;
|
||||
};
|
||||
|
||||
} // namespace image
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_image_FrameTimeout_h
|
||||
@@ -70,7 +70,7 @@ public:
|
||||
{ }
|
||||
|
||||
template <typename... Rest>
|
||||
nsresult Configure(const DeinterlacingConfig<PixelType>& aConfig, Rest... aRest)
|
||||
nsresult Configure(const DeinterlacingConfig<PixelType>& aConfig, const Rest&... aRest)
|
||||
{
|
||||
nsresult rv = mNext.Configure(aRest...);
|
||||
if (NS_FAILED(rv)) {
|
||||
@@ -360,7 +360,7 @@ public:
|
||||
{ }
|
||||
|
||||
template <typename... Rest>
|
||||
nsresult Configure(const RemoveFrameRectConfig& aConfig, Rest... aRest)
|
||||
nsresult Configure(const RemoveFrameRectConfig& aConfig, const Rest&... aRest)
|
||||
{
|
||||
nsresult rv = mNext.Configure(aRest...);
|
||||
if (NS_FAILED(rv)) {
|
||||
@@ -590,7 +590,7 @@ public:
|
||||
{ }
|
||||
|
||||
template <typename... Rest>
|
||||
nsresult Configure(const ADAM7InterpolatingConfig& aConfig, Rest... aRest)
|
||||
nsresult Configure(const ADAM7InterpolatingConfig& aConfig, const Rest&... aRest)
|
||||
{
|
||||
nsresult rv = mNext.Configure(aRest...);
|
||||
if (NS_FAILED(rv)) {
|
||||
|
||||
@@ -104,10 +104,11 @@ SurfaceSink::Configure(const SurfaceConfig& aConfig)
|
||||
// XXX(seth): Once every Decoder subclass uses SurfacePipe, we probably want
|
||||
// to allocate the frame directly here and get rid of Decoder::AllocateFrame
|
||||
// altogether.
|
||||
nsresult rv = aConfig.mDecoder->AllocateFrame(aConfig.mFrameNum,
|
||||
surfaceSize,
|
||||
nsresult rv = aConfig.mDecoder->AllocateFrame(surfaceSize,
|
||||
frameRect,
|
||||
aConfig.mFormat);
|
||||
aConfig.mFormat,
|
||||
/* aPaletteDepth */ 0,
|
||||
aConfig.mAnimParams);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
@@ -154,11 +155,11 @@ PalettedSurfaceSink::Configure(const PalettedSurfaceConfig& aConfig)
|
||||
// XXX(seth): Once every Decoder subclass uses SurfacePipe, we probably want
|
||||
// to allocate the frame directly here and get rid of Decoder::AllocateFrame
|
||||
// altogether.
|
||||
nsresult rv = aConfig.mDecoder->AllocateFrame(aConfig.mFrameNum,
|
||||
aConfig.mOutputSize,
|
||||
nsresult rv = aConfig.mDecoder->AllocateFrame(aConfig.mOutputSize,
|
||||
aConfig.mFrameRect,
|
||||
aConfig.mFormat,
|
||||
aConfig.mPaletteDepth);
|
||||
aConfig.mPaletteDepth,
|
||||
aConfig.mAnimParams);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
+4
-2
@@ -34,6 +34,8 @@
|
||||
#include "mozilla/Variant.h"
|
||||
#include "mozilla/gfx/2D.h"
|
||||
|
||||
#include "AnimationParams.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace image {
|
||||
|
||||
@@ -722,10 +724,10 @@ struct SurfaceConfig
|
||||
{
|
||||
using Filter = SurfaceSink;
|
||||
Decoder* mDecoder; /// Which Decoder to use to allocate the surface.
|
||||
uint32_t mFrameNum; /// Which frame of animation this surface is for.
|
||||
gfx::IntSize mOutputSize; /// The size of the surface.
|
||||
gfx::SurfaceFormat mFormat; /// The surface format (BGRA or BGRX).
|
||||
bool mFlipVertically; /// If true, write the rows from bottom to top.
|
||||
Maybe<AnimationParams> mAnimParams; /// Given for animated images.
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -750,12 +752,12 @@ struct PalettedSurfaceConfig
|
||||
{
|
||||
using Filter = PalettedSurfaceSink;
|
||||
Decoder* mDecoder; /// Which Decoder to use to allocate the surface.
|
||||
uint32_t mFrameNum; /// Which frame of animation this surface is for.
|
||||
gfx::IntSize mOutputSize; /// The logical size of the surface.
|
||||
gfx::IntRect mFrameRect; /// The surface subrect which contains data.
|
||||
gfx::SurfaceFormat mFormat; /// The surface format (BGRA or BGRX).
|
||||
uint8_t mPaletteDepth; /// The palette depth of this surface.
|
||||
bool mFlipVertically; /// If true, write the rows from bottom to top.
|
||||
Maybe<AnimationParams> mAnimParams; /// Given for animated images.
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
+10
-12
@@ -70,8 +70,6 @@ public:
|
||||
*
|
||||
* @param aDecoder The decoder whose current frame the SurfacePipe will write
|
||||
* to.
|
||||
* @param aFrameNum Which frame the SurfacePipe will write to. This will be 0
|
||||
* for non-animated images.
|
||||
* @param aInputSize The original size of the image.
|
||||
* @param aOutputSize The size the SurfacePipe should output. Must be the same
|
||||
* as @aInputSize or smaller. If smaller, the image will be
|
||||
@@ -79,6 +77,7 @@ public:
|
||||
* @param aFrameRect The portion of the image that actually contains data.
|
||||
* @param aFormat The surface format of the image; generally B8G8R8A8 or
|
||||
* B8G8R8X8.
|
||||
* @param aAnimParams Extra parameters used by animated images.
|
||||
* @param aFlags Flags enabling or disabling various functionality for the
|
||||
* SurfacePipe; see the SurfacePipeFlags documentation for more
|
||||
* information.
|
||||
@@ -89,11 +88,11 @@ public:
|
||||
*/
|
||||
static Maybe<SurfacePipe>
|
||||
CreateSurfacePipe(Decoder* aDecoder,
|
||||
uint32_t aFrameNum,
|
||||
const nsIntSize& aInputSize,
|
||||
const nsIntSize& aOutputSize,
|
||||
const nsIntRect& aFrameRect,
|
||||
gfx::SurfaceFormat aFormat,
|
||||
const Maybe<AnimationParams>& aAnimParams,
|
||||
SurfacePipeFlags aFlags)
|
||||
{
|
||||
const bool deinterlace = bool(aFlags & SurfacePipeFlags::DEINTERLACE);
|
||||
@@ -125,8 +124,8 @@ public:
|
||||
ADAM7InterpolatingConfig interpolatingConfig;
|
||||
RemoveFrameRectConfig removeFrameRectConfig { aFrameRect };
|
||||
DownscalingConfig downscalingConfig { aInputSize, aFormat };
|
||||
SurfaceConfig surfaceConfig { aDecoder, aFrameNum, aOutputSize,
|
||||
aFormat, flipVertically };
|
||||
SurfaceConfig surfaceConfig { aDecoder, aOutputSize, aFormat,
|
||||
flipVertically, aAnimParams };
|
||||
|
||||
Maybe<SurfacePipe> pipe;
|
||||
|
||||
@@ -181,13 +180,12 @@ public:
|
||||
*
|
||||
* @param aDecoder The decoder whose current frame the SurfacePipe will write
|
||||
* to.
|
||||
* @param aFrameNum Which frame the SurfacePipe will write to. This will be 0
|
||||
* for non-animated images.
|
||||
* @param aInputSize The original size of the image.
|
||||
* @param aFrameRect The portion of the image that actually contains data.
|
||||
* @param aFormat The surface format of the image; generally B8G8R8A8 or
|
||||
* B8G8R8X8.
|
||||
* @param aPaletteDepth The palette depth of the image.
|
||||
* @param aAnimParams Extra parameters used by animated images.
|
||||
* @param aFlags Flags enabling or disabling various functionality for the
|
||||
* SurfacePipe; see the SurfacePipeFlags documentation for more
|
||||
* information.
|
||||
@@ -198,11 +196,11 @@ public:
|
||||
*/
|
||||
static Maybe<SurfacePipe>
|
||||
CreatePalettedSurfacePipe(Decoder* aDecoder,
|
||||
uint32_t aFrameNum,
|
||||
const nsIntSize& aInputSize,
|
||||
const nsIntRect& aFrameRect,
|
||||
gfx::SurfaceFormat aFormat,
|
||||
uint8_t aPaletteDepth,
|
||||
const Maybe<AnimationParams>& aAnimParams,
|
||||
SurfacePipeFlags aFlags)
|
||||
{
|
||||
const bool deinterlace = bool(aFlags & SurfacePipeFlags::DEINTERLACE);
|
||||
@@ -211,9 +209,9 @@ public:
|
||||
|
||||
// Construct configurations for the SurfaceFilters.
|
||||
DeinterlacingConfig<uint8_t> deinterlacingConfig { progressiveDisplay };
|
||||
PalettedSurfaceConfig palettedSurfaceConfig { aDecoder, aFrameNum, aInputSize,
|
||||
aFrameRect, aFormat, aPaletteDepth,
|
||||
flipVertically };
|
||||
PalettedSurfaceConfig palettedSurfaceConfig { aDecoder, aInputSize, aFrameRect,
|
||||
aFormat, aPaletteDepth,
|
||||
flipVertically, aAnimParams };
|
||||
|
||||
Maybe<SurfacePipe> pipe;
|
||||
|
||||
@@ -229,7 +227,7 @@ public:
|
||||
private:
|
||||
template <typename... Configs>
|
||||
static Maybe<SurfacePipe>
|
||||
MakePipe(Configs... aConfigs)
|
||||
MakePipe(const Configs&... aConfigs)
|
||||
{
|
||||
auto pipe = MakeUnique<typename detail::FilterPipeline<Configs...>::Type>();
|
||||
nsresult rv = pipe->Configure(aConfigs...);
|
||||
|
||||
@@ -674,8 +674,7 @@ nsBMPDecoder::ReadBitfields(const char* aData, size_t aLength)
|
||||
}
|
||||
|
||||
MOZ_ASSERT(!mImageData, "Already have a buffer allocated?");
|
||||
nsresult rv = AllocateFrame(/* aFrameNum = */ 0, OutputSize(),
|
||||
FullOutputFrame(),
|
||||
nsresult rv = AllocateFrame(OutputSize(), FullOutputFrame(),
|
||||
mMayHaveTransparency ? SurfaceFormat::B8G8R8A8
|
||||
: SurfaceFormat::B8G8R8X8);
|
||||
if (NS_FAILED(rv)) {
|
||||
|
||||
@@ -187,6 +187,14 @@ nsGIFDecoder2::BeginImageFrame(const IntRect& aFrameRect,
|
||||
// Make sure there's no animation if we're downscaling.
|
||||
MOZ_ASSERT_IF(Size() != OutputSize(), !GetImageMetadata().HasAnimation());
|
||||
|
||||
AnimationParams animParams {
|
||||
aFrameRect,
|
||||
FrameTimeout::FromRawMilliseconds(mGIFStruct.delay_time),
|
||||
uint32_t(mGIFStruct.images_decoded),
|
||||
BlendMethod::OVER,
|
||||
DisposalMethod(mGIFStruct.disposal_method)
|
||||
};
|
||||
|
||||
SurfacePipeFlags pipeFlags = aIsInterlaced
|
||||
? SurfacePipeFlags::DEINTERLACE
|
||||
: SurfacePipeFlags();
|
||||
@@ -198,17 +206,24 @@ nsGIFDecoder2::BeginImageFrame(const IntRect& aFrameRect,
|
||||
|
||||
// The first frame is always decoded into an RGB surface.
|
||||
pipe =
|
||||
SurfacePipeFactory::CreateSurfacePipe(this, mGIFStruct.images_decoded,
|
||||
Size(), OutputSize(),
|
||||
aFrameRect, format, pipeFlags);
|
||||
SurfacePipeFactory::CreateSurfacePipe(this, Size(), OutputSize(),
|
||||
aFrameRect, format,
|
||||
Some(animParams), pipeFlags);
|
||||
} else {
|
||||
// This is an animation frame (and not the first). To minimize the memory
|
||||
// usage of animations, the image data is stored in paletted form.
|
||||
//
|
||||
// We should never use paletted surfaces with a draw target directly, so
|
||||
// the only practical difference between B8G8R8A8 and B8G8R8X8 is the
|
||||
// cleared pixel value if we get truncated. We want 0 in that case to
|
||||
// ensure it is an acceptable value for the color map as was the case
|
||||
// historically.
|
||||
MOZ_ASSERT(Size() == OutputSize());
|
||||
pipe =
|
||||
SurfacePipeFactory::CreatePalettedSurfacePipe(this, mGIFStruct.images_decoded,
|
||||
Size(), aFrameRect, format,
|
||||
aDepth, pipeFlags);
|
||||
SurfacePipeFactory::CreatePalettedSurfacePipe(this, Size(), aFrameRect,
|
||||
SurfaceFormat::B8G8R8A8,
|
||||
aDepth, Some(animParams),
|
||||
pipeFlags);
|
||||
}
|
||||
|
||||
mCurrentFrameIndex = mGIFStruct.images_decoded;
|
||||
@@ -249,9 +264,7 @@ nsGIFDecoder2::EndImageFrame()
|
||||
mGIFStruct.images_decoded++;
|
||||
|
||||
// Tell the superclass we finished a frame
|
||||
PostFrameStop(opacity,
|
||||
DisposalMethod(mGIFStruct.disposal_method),
|
||||
FrameTimeout::FromRawMilliseconds(mGIFStruct.delay_time));
|
||||
PostFrameStop(opacity);
|
||||
|
||||
// Reset the transparent pixel
|
||||
if (mOldColor) {
|
||||
|
||||
@@ -70,8 +70,9 @@ nsIconDecoder::ReadHeader(const char* aData)
|
||||
|
||||
MOZ_ASSERT(!mImageData, "Already have a buffer allocated?");
|
||||
Maybe<SurfacePipe> pipe =
|
||||
SurfacePipeFactory::CreateSurfacePipe(this, 0, Size(), OutputSize(),
|
||||
SurfacePipeFactory::CreateSurfacePipe(this, Size(), OutputSize(),
|
||||
FullFrame(), SurfaceFormat::B8G8R8A8,
|
||||
/* aAnimParams */ Nothing(),
|
||||
SurfacePipeFlags());
|
||||
if (!pipe) {
|
||||
return Transition::TerminateFailure();
|
||||
|
||||
@@ -388,8 +388,8 @@ nsJPEGDecoder::ReadJPEGData(const char* aData, size_t aLength)
|
||||
jpeg_has_multiple_scans(&mInfo);
|
||||
|
||||
MOZ_ASSERT(!mImageData, "Already have a buffer allocated?");
|
||||
nsresult rv = AllocateFrame(/* aFrameNum = */ 0, OutputSize(),
|
||||
FullOutputFrame(), SurfaceFormat::B8G8R8X8);
|
||||
nsresult rv = AllocateFrame(OutputSize(), FullOutputFrame(),
|
||||
SurfaceFormat::B8G8R8X8);
|
||||
if (NS_FAILED(rv)) {
|
||||
mState = JPEG_ERROR;
|
||||
MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug,
|
||||
|
||||
@@ -208,6 +208,25 @@ nsPNGDecoder::CreateFrame(const FrameInfo& aFrameInfo)
|
||||
MOZ_ASSERT_IF(Size() != OutputSize(),
|
||||
transparency != TransparencyType::eFrameRect);
|
||||
|
||||
Maybe<AnimationParams> animParams;
|
||||
#ifdef PNG_APNG_SUPPORTED
|
||||
if (png_get_valid(mPNG, mInfo, PNG_INFO_acTL)) {
|
||||
mAnimInfo = AnimFrameInfo(mPNG, mInfo);
|
||||
|
||||
if (mAnimInfo.mDispose == DisposalMethod::CLEAR) {
|
||||
// We may have to display the background under this image during
|
||||
// animation playback, so we regard it as transparent.
|
||||
PostHasTransparency();
|
||||
}
|
||||
|
||||
animParams.emplace(AnimationParams {
|
||||
aFrameInfo.mFrameRect,
|
||||
FrameTimeout::FromRawMilliseconds(mAnimInfo.mTimeout),
|
||||
mNumFrames, mAnimInfo.mBlend, mAnimInfo.mDispose
|
||||
});
|
||||
}
|
||||
#endif
|
||||
|
||||
// If this image is interlaced, we can display better quality intermediate
|
||||
// results to the user by post processing them with ADAM7InterpolatingFilter.
|
||||
SurfacePipeFlags pipeFlags = aFrameInfo.mIsInterlaced
|
||||
@@ -220,9 +239,9 @@ nsPNGDecoder::CreateFrame(const FrameInfo& aFrameInfo)
|
||||
}
|
||||
|
||||
Maybe<SurfacePipe> pipe =
|
||||
SurfacePipeFactory::CreateSurfacePipe(this, mNumFrames, Size(),
|
||||
OutputSize(), aFrameInfo.mFrameRect,
|
||||
format, pipeFlags);
|
||||
SurfacePipeFactory::CreateSurfacePipe(this, Size(), OutputSize(),
|
||||
aFrameInfo.mFrameRect, format,
|
||||
animParams, pipeFlags);
|
||||
|
||||
if (!pipe) {
|
||||
mPipe = SurfacePipe();
|
||||
@@ -239,18 +258,6 @@ nsPNGDecoder::CreateFrame(const FrameInfo& aFrameInfo)
|
||||
"image frame with %dx%d pixels for decoder %p",
|
||||
mFrameRect.width, mFrameRect.height, this));
|
||||
|
||||
#ifdef PNG_APNG_SUPPORTED
|
||||
if (png_get_valid(mPNG, mInfo, PNG_INFO_acTL)) {
|
||||
mAnimInfo = AnimFrameInfo(mPNG, mInfo);
|
||||
|
||||
if (mAnimInfo.mDispose == DisposalMethod::CLEAR) {
|
||||
// We may have to display the background under this image during
|
||||
// animation playback, so we regard it as transparent.
|
||||
PostHasTransparency();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@@ -269,9 +276,7 @@ nsPNGDecoder::EndImageFrame()
|
||||
opacity = Opacity::FULLY_OPAQUE;
|
||||
}
|
||||
|
||||
PostFrameStop(opacity, mAnimInfo.mDispose,
|
||||
FrameTimeout::FromRawMilliseconds(mAnimInfo.mTimeout),
|
||||
mAnimInfo.mBlend, Some(mFrameRect));
|
||||
PostFrameStop(opacity);
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
||||
+378
-257
@@ -19,10 +19,6 @@ static LazyLogModule sWebPLog("WebPDecoder");
|
||||
|
||||
nsWebPDecoder::nsWebPDecoder(RasterImage* aImage)
|
||||
: Decoder(aImage)
|
||||
, mLexer(Transition::ToUnbuffered(State::FINISHED_WEBP_DATA,
|
||||
State::WEBP_DATA,
|
||||
SIZE_MAX),
|
||||
Transition::TerminateSuccess())
|
||||
, mDecoder(nullptr)
|
||||
, mBlend(BlendMethod::OVER)
|
||||
, mDisposal(DisposalMethod::KEEP)
|
||||
@@ -30,6 +26,13 @@ nsWebPDecoder::nsWebPDecoder(RasterImage* aImage)
|
||||
, mFormat(SurfaceFormat::B8G8R8X8)
|
||||
, mLastRow(0)
|
||||
, mCurrentFrame(0)
|
||||
, mData(nullptr)
|
||||
, mLength(0)
|
||||
, mIteratorComplete(false)
|
||||
, mNeedDemuxer(true)
|
||||
, mGotColorProfile(false)
|
||||
, mInProfile(nullptr)
|
||||
, mTransform(nullptr)
|
||||
{
|
||||
MOZ_LOG(sWebPLog, LogLevel::Debug,
|
||||
("[this=%p] nsWebPDecoder::nsWebPDecoder", this));
|
||||
@@ -39,27 +42,157 @@ nsWebPDecoder::~nsWebPDecoder()
|
||||
{
|
||||
MOZ_LOG(sWebPLog, LogLevel::Debug,
|
||||
("[this=%p] nsWebPDecoder::~nsWebPDecoder", this));
|
||||
WebPIDelete(mDecoder);
|
||||
if (mDecoder) {
|
||||
WebPIDelete(mDecoder);
|
||||
WebPFreeDecBuffer(&mBuffer);
|
||||
}
|
||||
if (mInProfile) {
|
||||
// mTransform belongs to us only if mInProfile is non-null
|
||||
if (mTransform) {
|
||||
qcms_transform_release(mTransform);
|
||||
}
|
||||
qcms_profile_release(mInProfile);
|
||||
}
|
||||
}
|
||||
|
||||
LexerResult
|
||||
nsWebPDecoder::ReadData()
|
||||
{
|
||||
MOZ_ASSERT(mData);
|
||||
MOZ_ASSERT(mLength > 0);
|
||||
|
||||
WebPDemuxer* demuxer = nullptr;
|
||||
bool complete = mIteratorComplete;
|
||||
|
||||
if (mNeedDemuxer) {
|
||||
WebPDemuxState state;
|
||||
WebPData fragment;
|
||||
fragment.bytes = mData;
|
||||
fragment.size = mLength;
|
||||
|
||||
demuxer = WebPDemuxPartial(&fragment, &state);
|
||||
if (state == WEBP_DEMUX_PARSE_ERROR) {
|
||||
MOZ_LOG(sWebPLog, LogLevel::Error,
|
||||
("[this=%p] nsWebPDecoder::ReadData -- demux parse error\n", this));
|
||||
WebPDemuxDelete(demuxer);
|
||||
return LexerResult(TerminalState::FAILURE);
|
||||
}
|
||||
|
||||
if (state == WEBP_DEMUX_PARSING_HEADER) {
|
||||
WebPDemuxDelete(demuxer);
|
||||
return LexerResult(Yield::NEED_MORE_DATA);
|
||||
}
|
||||
|
||||
if (!demuxer) {
|
||||
MOZ_LOG(sWebPLog, LogLevel::Error,
|
||||
("[this=%p] nsWebPDecoder::ReadData -- no demuxer\n", this));
|
||||
return LexerResult(TerminalState::FAILURE);
|
||||
}
|
||||
|
||||
complete = complete || state == WEBP_DEMUX_DONE;
|
||||
}
|
||||
|
||||
LexerResult rv(TerminalState::FAILURE);
|
||||
if (!HasSize()) {
|
||||
rv = ReadHeader(demuxer, complete);
|
||||
} else {
|
||||
rv = ReadPayload(demuxer, complete);
|
||||
}
|
||||
|
||||
WebPDemuxDelete(demuxer);
|
||||
return rv;
|
||||
}
|
||||
|
||||
LexerResult
|
||||
nsWebPDecoder::DoDecode(SourceBufferIterator& aIterator, IResumable* aOnResume)
|
||||
{
|
||||
while (true) {
|
||||
SourceBufferIterator::State state = SourceBufferIterator::COMPLETE;
|
||||
if (!mIteratorComplete) {
|
||||
state = aIterator.AdvanceOrScheduleResume(SIZE_MAX, aOnResume);
|
||||
|
||||
// We need to remember since we can't advance a complete iterator.
|
||||
mIteratorComplete = state == SourceBufferIterator::COMPLETE;
|
||||
}
|
||||
|
||||
if (state == SourceBufferIterator::WAITING) {
|
||||
return LexerResult(Yield::NEED_MORE_DATA);
|
||||
}
|
||||
|
||||
LexerResult rv = UpdateBuffer(aIterator, state);
|
||||
if (rv.is<Yield>() && rv.as<Yield>() == Yield::NEED_MORE_DATA) {
|
||||
// We need to check the iterator to see if more is available before
|
||||
// giving up unless we are already complete.
|
||||
if (mIteratorComplete) {
|
||||
MOZ_LOG(sWebPLog, LogLevel::Error,
|
||||
("[this=%p] nsWebPDecoder::DoDecode -- read all data, "
|
||||
"but needs more\n", this));
|
||||
return LexerResult(TerminalState::FAILURE);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
LexerResult
|
||||
nsWebPDecoder::UpdateBuffer(SourceBufferIterator& aIterator,
|
||||
SourceBufferIterator::State aState)
|
||||
{
|
||||
MOZ_ASSERT(!HasError(), "Shouldn't call DoDecode after error!");
|
||||
|
||||
return mLexer.Lex(aIterator, aOnResume,
|
||||
[=](State aState, const char* aData, size_t aLength) {
|
||||
switch (aState) {
|
||||
case State::WEBP_DATA:
|
||||
if (!HasSize()) {
|
||||
return ReadHeader(aData, aLength);
|
||||
}
|
||||
return ReadPayload(aData, aLength);
|
||||
case State::FINISHED_WEBP_DATA:
|
||||
return FinishedData();
|
||||
switch (aState) {
|
||||
case SourceBufferIterator::READY:
|
||||
if (!mData) {
|
||||
// For as long as we hold onto an iterator, we know the data pointers
|
||||
// to the chunks cannot change underneath us, so save the pointer to
|
||||
// the first block.
|
||||
MOZ_ASSERT(mLength == 0);
|
||||
mData = reinterpret_cast<const uint8_t*>(aIterator.Data());
|
||||
}
|
||||
mLength += aIterator.Length();
|
||||
return ReadData();
|
||||
case SourceBufferIterator::COMPLETE:
|
||||
return ReadData();
|
||||
default:
|
||||
MOZ_LOG(sWebPLog, LogLevel::Error,
|
||||
("[this=%p] nsWebPDecoder::DoDecode -- bad state\n", this));
|
||||
return LexerResult(TerminalState::FAILURE);
|
||||
}
|
||||
|
||||
// We need to buffer. If we have no data buffered, we need to get everything
|
||||
// from the first chunk of the source buffer before appending the new data.
|
||||
if (mBufferedData.empty()) {
|
||||
MOZ_ASSERT(mData);
|
||||
MOZ_ASSERT(mLength > 0);
|
||||
|
||||
if (!mBufferedData.append(mData, mLength)) {
|
||||
MOZ_LOG(sWebPLog, LogLevel::Error,
|
||||
("[this=%p] nsWebPDecoder::DoDecode -- oom, initialize %zu\n",
|
||||
this, mLength));
|
||||
return LexerResult(TerminalState::FAILURE);
|
||||
}
|
||||
MOZ_CRASH("Unknown State");
|
||||
});
|
||||
|
||||
MOZ_LOG(sWebPLog, LogLevel::Debug,
|
||||
("[this=%p] nsWebPDecoder::DoDecode -- buffered %zu bytes\n",
|
||||
this, mLength));
|
||||
}
|
||||
|
||||
// Append the incremental data from the iterator.
|
||||
if (!mBufferedData.append(aIterator.Data(), aIterator.Length())) {
|
||||
MOZ_LOG(sWebPLog, LogLevel::Error,
|
||||
("[this=%p] nsWebPDecoder::DoDecode -- oom, append %zu on %zu\n",
|
||||
this, aIterator.Length(), mBufferedData.length()));
|
||||
return LexerResult(TerminalState::FAILURE);
|
||||
}
|
||||
|
||||
MOZ_LOG(sWebPLog, LogLevel::Debug,
|
||||
("[this=%p] nsWebPDecoder::DoDecode -- buffered %zu -> %zu bytes\n",
|
||||
this, aIterator.Length(), mBufferedData.length()));
|
||||
mData = mBufferedData.begin();
|
||||
mLength = mBufferedData.length();
|
||||
return ReadData();
|
||||
}
|
||||
|
||||
nsresult
|
||||
@@ -69,13 +202,22 @@ nsWebPDecoder::CreateFrame(const nsIntRect& aFrameRect)
|
||||
MOZ_ASSERT(!mDecoder);
|
||||
|
||||
MOZ_LOG(sWebPLog, LogLevel::Debug,
|
||||
("[this=%p] nsWebPDecoder::CreateFrame -- frame %u, %d x %d\n",
|
||||
this, mCurrentFrame, aFrameRect.width, aFrameRect.height));
|
||||
("[this=%p] nsWebPDecoder::CreateFrame -- frame %u, (%d, %d) %d x %d\n",
|
||||
this, mCurrentFrame, aFrameRect.x, aFrameRect.y,
|
||||
aFrameRect.width, aFrameRect.height));
|
||||
|
||||
if (aFrameRect.width <= 0 || aFrameRect.height <= 0) {
|
||||
MOZ_LOG(sWebPLog, LogLevel::Error,
|
||||
("[this=%p] nsWebPDecoder::CreateFrame -- bad frame rect\n",
|
||||
this));
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// If this is our first frame in an animation and it doesn't cover the
|
||||
// full frame, then we are transparent even if there is no alpha
|
||||
if (mCurrentFrame == 0 && !aFrameRect.IsEqualEdges(FullFrame())) {
|
||||
MOZ_ASSERT(HasAnimation());
|
||||
mFormat = SurfaceFormat::B8G8R8A8;
|
||||
PostHasTransparency();
|
||||
}
|
||||
|
||||
@@ -90,16 +232,22 @@ nsWebPDecoder::CreateFrame(const nsIntRect& aFrameRect)
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
SurfacePipeFlags pipeFlags = SurfacePipeFlags();
|
||||
|
||||
AnimationParams animParams {
|
||||
aFrameRect, mTimeout, mCurrentFrame, mBlend, mDisposal
|
||||
};
|
||||
|
||||
Maybe<SurfacePipe> pipe = SurfacePipeFactory::CreateSurfacePipe(this,
|
||||
mCurrentFrame, Size(), OutputSize(), aFrameRect,
|
||||
mFormat, SurfacePipeFlags());
|
||||
Size(), OutputSize(), aFrameRect, mFormat, Some(animParams), pipeFlags);
|
||||
if (!pipe) {
|
||||
MOZ_LOG(sWebPLog, LogLevel::Error,
|
||||
("[this=%p] nsWebPDecoder::CreateFrame -- no pipe\n", this));
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
mPipe = Move(*pipe);
|
||||
mFrameRect = aFrameRect;
|
||||
mPipe = std::move(*pipe);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@@ -118,154 +266,149 @@ nsWebPDecoder::EndFrame()
|
||||
this, mCurrentFrame, (int)opacity, (int)mDisposal,
|
||||
mTimeout.AsEncodedValueDeprecated(), (int)mBlend));
|
||||
|
||||
PostFrameStop(opacity, mDisposal, mTimeout, mBlend);
|
||||
WebPFreeDecBuffer(&mBuffer);
|
||||
PostFrameStop(opacity);
|
||||
WebPIDelete(mDecoder);
|
||||
WebPFreeDecBuffer(&mBuffer);
|
||||
mDecoder = nullptr;
|
||||
mLastRow = 0;
|
||||
++mCurrentFrame;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsWebPDecoder::GetDataBuffer(const uint8_t*& aData, size_t& aLength)
|
||||
void
|
||||
nsWebPDecoder::ApplyColorProfile(const char* aProfile, size_t aLength)
|
||||
{
|
||||
if (!mData.empty() && mData.begin() != aData) {
|
||||
if (!mData.append(aData, aLength)) {
|
||||
MOZ_LOG(sWebPLog, LogLevel::Error,
|
||||
("[this=%p] nsWebPDecoder::GetDataBuffer -- oom, append %zu on %zu\n",
|
||||
this, aLength, mData.length()));
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
aData = mData.begin();
|
||||
aLength = mData.length();
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
MOZ_ASSERT(!mGotColorProfile);
|
||||
mGotColorProfile = true;
|
||||
|
||||
nsresult
|
||||
nsWebPDecoder::SaveDataBuffer(const uint8_t* aData, size_t aLength)
|
||||
{
|
||||
if (mData.empty() && !mData.append(aData, aLength)) {
|
||||
if (GetSurfaceFlags() & SurfaceFlags::NO_COLORSPACE_CONVERSION) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto mode = gfxPlatform::GetCMSMode();
|
||||
if (mode == eCMSMode_Off || (mode == eCMSMode_TaggedOnly && !aProfile)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!aProfile || !gfxPlatform::GetCMSOutputProfile()) {
|
||||
MOZ_LOG(sWebPLog, LogLevel::Debug,
|
||||
("[this=%p] nsWebPDecoder::ApplyColorProfile -- not tagged or no output "
|
||||
"profile , use sRGB transform\n", this));
|
||||
mTransform = gfxPlatform::GetCMSRGBATransform();
|
||||
return;
|
||||
}
|
||||
|
||||
mInProfile = qcms_profile_from_memory(aProfile, aLength);
|
||||
if (!mInProfile) {
|
||||
MOZ_LOG(sWebPLog, LogLevel::Error,
|
||||
("[this=%p] nsWebPDecoder::SaveDataBuffer -- oom, append %zu on %zu\n",
|
||||
this, aLength, mData.length()));
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
("[this=%p] nsWebPDecoder::ApplyColorProfile -- bad color profile\n",
|
||||
this));
|
||||
return;
|
||||
}
|
||||
return NS_OK;
|
||||
|
||||
// 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,
|
||||
QCMS_DATA_RGBA_8,
|
||||
gfxPlatform::GetCMSOutputProfile(),
|
||||
QCMS_DATA_RGBA_8,
|
||||
(qcms_intent)intent);
|
||||
MOZ_LOG(sWebPLog, LogLevel::Debug,
|
||||
("[this=%p] nsWebPDecoder::ApplyColorProfile -- use tagged "
|
||||
"transform\n", this));
|
||||
}
|
||||
|
||||
LexerTransition<nsWebPDecoder::State>
|
||||
nsWebPDecoder::ReadHeader(const char* aData, size_t aLength)
|
||||
LexerResult
|
||||
nsWebPDecoder::ReadHeader(WebPDemuxer* aDemuxer,
|
||||
bool aIsComplete)
|
||||
{
|
||||
MOZ_ASSERT(aDemuxer);
|
||||
|
||||
MOZ_LOG(sWebPLog, LogLevel::Debug,
|
||||
("[this=%p] nsWebPDecoder::ReadHeader -- %zu bytes\n", this, aLength));
|
||||
("[this=%p] nsWebPDecoder::ReadHeader -- %zu bytes\n", this, mLength));
|
||||
|
||||
// XXX(aosmond): In an ideal world, we could request the lexer to do this
|
||||
// buffering for us (and in turn the underlying SourceBuffer). That way we
|
||||
// could avoid extra copies during the decode and just do
|
||||
// SourceBuffer::Compact on each iteration. For a typical WebP image we
|
||||
// can hope that we will get the full header in the first packet, but
|
||||
// for animated images we will end up buffering the whole stream if it
|
||||
// not already fully received and contiguous.
|
||||
auto data = (const uint8_t*)aData;
|
||||
size_t length = aLength;
|
||||
if (NS_FAILED(GetDataBuffer(data, length))) {
|
||||
return Transition::TerminateFailure();
|
||||
}
|
||||
uint32_t flags = WebPDemuxGetI(aDemuxer, WEBP_FF_FORMAT_FLAGS);
|
||||
|
||||
WebPBitstreamFeatures features;
|
||||
VP8StatusCode status = WebPGetFeatures(data, length, &features);
|
||||
switch (status) {
|
||||
case VP8_STATUS_OK:
|
||||
break;
|
||||
case VP8_STATUS_NOT_ENOUGH_DATA:
|
||||
if (NS_FAILED(SaveDataBuffer(data, length))) {
|
||||
return Transition::TerminateFailure();
|
||||
if (!IsMetadataDecode() && !mGotColorProfile) {
|
||||
if (flags & WebPFeatureFlags::ICCP_FLAG) {
|
||||
WebPChunkIterator iter;
|
||||
if (!WebPDemuxGetChunk(aDemuxer, "ICCP", 1, &iter)) {
|
||||
return aIsComplete ? LexerResult(TerminalState::FAILURE)
|
||||
: LexerResult(Yield::NEED_MORE_DATA);
|
||||
}
|
||||
return Transition::ContinueUnbuffered(State::WEBP_DATA);
|
||||
default:
|
||||
MOZ_LOG(sWebPLog, LogLevel::Error,
|
||||
("[this=%p] nsWebPDecoder::ReadHeader -- parse error %d\n",
|
||||
this, status));
|
||||
return Transition::TerminateFailure();
|
||||
|
||||
ApplyColorProfile(reinterpret_cast<const char*>(iter.chunk.bytes),
|
||||
iter.chunk.size);
|
||||
WebPDemuxReleaseChunkIterator(&iter);
|
||||
} else {
|
||||
ApplyColorProfile(nullptr, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (features.has_animation) {
|
||||
if (flags & WebPFeatureFlags::ANIMATION_FLAG) {
|
||||
// A metadata decode expects to get the correct first frame timeout which
|
||||
// sadly is not provided by the normal WebP header parsing.
|
||||
WebPDemuxState state;
|
||||
WebPData fragment;
|
||||
fragment.bytes = data;
|
||||
fragment.size = length;
|
||||
WebPDemuxer* demuxer = WebPDemuxPartial(&fragment, &state);
|
||||
if (!demuxer || state == WEBP_DEMUX_PARSE_ERROR) {
|
||||
MOZ_LOG(sWebPLog, LogLevel::Error,
|
||||
("[this=%p] nsWebPDecoder::ReadHeader -- demux parse error\n", this));
|
||||
WebPDemuxDelete(demuxer);
|
||||
return Transition::TerminateFailure();
|
||||
}
|
||||
|
||||
WebPIterator iter;
|
||||
if (!WebPDemuxGetFrame(demuxer, 1, &iter)) {
|
||||
WebPDemuxDelete(demuxer);
|
||||
if (state == WEBP_DEMUX_DONE) {
|
||||
MOZ_LOG(sWebPLog, LogLevel::Error,
|
||||
("[this=%p] nsWebPDecoder::ReadHeader -- demux parse error\n",
|
||||
this));
|
||||
return Transition::TerminateFailure();
|
||||
}
|
||||
if (NS_FAILED(SaveDataBuffer(data, length))) {
|
||||
return Transition::TerminateFailure();
|
||||
}
|
||||
return Transition::ContinueUnbuffered(State::WEBP_DATA);
|
||||
if (!WebPDemuxGetFrame(aDemuxer, 1, &iter)) {
|
||||
return aIsComplete ? LexerResult(TerminalState::FAILURE)
|
||||
: LexerResult(Yield::NEED_MORE_DATA);
|
||||
}
|
||||
|
||||
PostIsAnimated(FrameTimeout::FromRawMilliseconds(iter.duration));
|
||||
WebPDemuxReleaseIterator(&iter);
|
||||
WebPDemuxDelete(demuxer);
|
||||
} else {
|
||||
// Single frames don't need a demuxer to be created.
|
||||
mNeedDemuxer = false;
|
||||
}
|
||||
|
||||
MOZ_LOG(sWebPLog, LogLevel::Debug,
|
||||
("[this=%p] nsWebPDecoder::ReadHeader -- %d x %d, alpha %d, "
|
||||
"animation %d, format %d, metadata decode %d, first frame decode %d\n",
|
||||
this, features.width, features.height, features.has_alpha,
|
||||
features.has_animation, features.format, IsMetadataDecode(),
|
||||
IsFirstFrameDecode()));
|
||||
uint32_t width = WebPDemuxGetI(aDemuxer, WEBP_FF_CANVAS_WIDTH);
|
||||
uint32_t height = WebPDemuxGetI(aDemuxer, WEBP_FF_CANVAS_HEIGHT);
|
||||
if (width > INT32_MAX || height > INT32_MAX) {
|
||||
return LexerResult(TerminalState::FAILURE);
|
||||
}
|
||||
|
||||
PostSize(features.width, features.height);
|
||||
if (features.has_alpha) {
|
||||
PostSize(width, height);
|
||||
|
||||
bool alpha = flags & WebPFeatureFlags::ALPHA_FLAG;
|
||||
if (alpha) {
|
||||
mFormat = SurfaceFormat::B8G8R8A8;
|
||||
PostHasTransparency();
|
||||
}
|
||||
|
||||
MOZ_LOG(sWebPLog, LogLevel::Debug,
|
||||
("[this=%p] nsWebPDecoder::ReadHeader -- %u x %u, alpha %d, "
|
||||
"animation %d, metadata decode %d, first frame decode %d\n",
|
||||
this, width, height, alpha, HasAnimation(),
|
||||
IsMetadataDecode(), IsFirstFrameDecode()));
|
||||
|
||||
if (IsMetadataDecode()) {
|
||||
return Transition::TerminateSuccess();
|
||||
return LexerResult(TerminalState::SUCCESS);
|
||||
}
|
||||
|
||||
auto transition = ReadPayload((const char*)data, length);
|
||||
if (!features.has_animation) {
|
||||
mData.clearAndFree();
|
||||
}
|
||||
return transition;
|
||||
return ReadPayload(aDemuxer, aIsComplete);
|
||||
}
|
||||
|
||||
LexerTransition<nsWebPDecoder::State>
|
||||
nsWebPDecoder::ReadPayload(const char* aData, size_t aLength)
|
||||
LexerResult
|
||||
nsWebPDecoder::ReadPayload(WebPDemuxer* aDemuxer,
|
||||
bool aIsComplete)
|
||||
{
|
||||
auto data = (const uint8_t*)aData;
|
||||
if (!HasAnimation()) {
|
||||
auto rv = ReadSingle(data, aLength, true, FullFrame());
|
||||
if (rv.NextStateIsTerminal() &&
|
||||
rv.NextStateAsTerminal() == TerminalState::SUCCESS) {
|
||||
auto rv = ReadSingle(mData, mLength, FullFrame());
|
||||
if (rv.is<TerminalState>() &&
|
||||
rv.as<TerminalState>() == TerminalState::SUCCESS) {
|
||||
PostDecodeDone();
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
return ReadMultiple(data, aLength);
|
||||
return ReadMultiple(aDemuxer, aIsComplete);
|
||||
}
|
||||
|
||||
LexerTransition<nsWebPDecoder::State>
|
||||
nsWebPDecoder::ReadSingle(const uint8_t* aData, size_t aLength, bool aAppend, const IntRect& aFrameRect)
|
||||
LexerResult
|
||||
nsWebPDecoder::ReadSingle(const uint8_t* aData, size_t aLength, const IntRect& aFrameRect)
|
||||
{
|
||||
MOZ_ASSERT(!IsMetadataDecode());
|
||||
MOZ_ASSERT(aData);
|
||||
@@ -275,125 +418,120 @@ nsWebPDecoder::ReadSingle(const uint8_t* aData, size_t aLength, bool aAppend, co
|
||||
("[this=%p] nsWebPDecoder::ReadSingle -- %zu bytes\n", this, aLength));
|
||||
|
||||
if (!mDecoder && NS_FAILED(CreateFrame(aFrameRect))) {
|
||||
return Transition::TerminateFailure();
|
||||
return LexerResult(TerminalState::FAILURE);
|
||||
}
|
||||
|
||||
// XXX(aosmond): The demux API can be used for single images according to the
|
||||
// documentation. If WebPIAppend is not any more efficient in its buffering
|
||||
// than what we do for animated images, we should just combine the use cases.
|
||||
bool complete;
|
||||
VP8StatusCode status;
|
||||
if (aAppend) {
|
||||
status = WebPIAppend(mDecoder, aData, aLength);
|
||||
} else {
|
||||
status = WebPIUpdate(mDecoder, aData, aLength);
|
||||
}
|
||||
switch (status) {
|
||||
case VP8_STATUS_OK:
|
||||
complete = true;
|
||||
break;
|
||||
case VP8_STATUS_SUSPENDED:
|
||||
complete = false;
|
||||
break;
|
||||
default:
|
||||
MOZ_LOG(sWebPLog, LogLevel::Error,
|
||||
("[this=%p] nsWebPDecoder::ReadSingle -- append error %d\n",
|
||||
this, status));
|
||||
return Transition::TerminateFailure();
|
||||
}
|
||||
|
||||
int lastRow = -1;
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
int stride = 0;
|
||||
const uint8_t* rowStart = WebPIDecGetRGB(mDecoder, &lastRow, &width, &height, &stride);
|
||||
if (!rowStart || lastRow == -1) {
|
||||
return Transition::ContinueUnbuffered(State::WEBP_DATA);
|
||||
}
|
||||
|
||||
if (width <= 0 || height <= 0 || stride <= 0) {
|
||||
MOZ_LOG(sWebPLog, LogLevel::Error,
|
||||
("[this=%p] nsWebPDecoder::ReadSingle -- bad (w,h,s) = (%d, %d, %d)\n",
|
||||
this, width, height, stride));
|
||||
return Transition::TerminateFailure();
|
||||
}
|
||||
|
||||
for (int row = mLastRow; row < lastRow; row++) {
|
||||
const uint8_t* src = rowStart + row * stride;
|
||||
auto result = mPipe.WritePixelsToRow<uint32_t>([&]() -> NextPixel<uint32_t> {
|
||||
MOZ_ASSERT(mFormat == SurfaceFormat::B8G8R8A8 || src[3] == 0xFF);
|
||||
const uint32_t pixel = gfxPackedPixel(src[3], src[0], src[1], src[2]);
|
||||
src += 4;
|
||||
return AsVariant(pixel);
|
||||
});
|
||||
MOZ_ASSERT(result != WriteState::FAILURE);
|
||||
MOZ_ASSERT_IF(result == WriteState::FINISHED, complete && row == lastRow - 1);
|
||||
|
||||
if (result == WriteState::FAILURE) {
|
||||
MOZ_LOG(sWebPLog, LogLevel::Error,
|
||||
("[this=%p] nsWebPDecoder::ReadSingle -- write pixels error\n",
|
||||
this));
|
||||
return Transition::TerminateFailure();
|
||||
do {
|
||||
VP8StatusCode status = WebPIUpdate(mDecoder, aData, aLength);
|
||||
switch (status) {
|
||||
case VP8_STATUS_OK:
|
||||
complete = true;
|
||||
break;
|
||||
case VP8_STATUS_SUSPENDED:
|
||||
complete = false;
|
||||
break;
|
||||
default:
|
||||
MOZ_LOG(sWebPLog, LogLevel::Error,
|
||||
("[this=%p] nsWebPDecoder::ReadSingle -- append error %d\n",
|
||||
this, status));
|
||||
return LexerResult(TerminalState::FAILURE);
|
||||
}
|
||||
|
||||
int lastRow = -1;
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
int stride = 0;
|
||||
uint8_t* rowStart = WebPIDecGetRGB(mDecoder, &lastRow, &width, &height, &stride);
|
||||
|
||||
MOZ_LOG(sWebPLog, LogLevel::Debug,
|
||||
("[this=%p] nsWebPDecoder::ReadSingle -- complete %d, read %d rows, "
|
||||
"has %d rows available\n", this, complete, mLastRow, lastRow));
|
||||
|
||||
if (!rowStart || lastRow == -1 || lastRow == mLastRow) {
|
||||
return LexerResult(Yield::NEED_MORE_DATA);
|
||||
}
|
||||
|
||||
if (width != mFrameRect.width || height != mFrameRect.height ||
|
||||
stride < mFrameRect.width * 4 ||
|
||||
lastRow > mFrameRect.height) {
|
||||
MOZ_LOG(sWebPLog, LogLevel::Error,
|
||||
("[this=%p] nsWebPDecoder::ReadSingle -- bad (w,h,s) = (%d, %d, %d)\n",
|
||||
this, width, height, stride));
|
||||
return LexerResult(TerminalState::FAILURE);
|
||||
}
|
||||
|
||||
const bool noPremultiply =
|
||||
bool(GetSurfaceFlags() & SurfaceFlags::NO_PREMULTIPLY_ALPHA);
|
||||
|
||||
for (int row = mLastRow; row < lastRow; row++) {
|
||||
uint8_t* src = rowStart + row * stride;
|
||||
if (mTransform) {
|
||||
qcms_transform_data(mTransform, src, src, width);
|
||||
}
|
||||
|
||||
WriteState result;
|
||||
if (noPremultiply) {
|
||||
result = mPipe.WritePixelsToRow<uint32_t>([&]() -> NextPixel<uint32_t> {
|
||||
MOZ_ASSERT(mFormat == SurfaceFormat::B8G8R8A8 || src[3] == 0xFF);
|
||||
const uint32_t pixel =
|
||||
gfxPackedPixelNoPreMultiply(src[3], src[0], src[1], src[2]);
|
||||
src += 4;
|
||||
return AsVariant(pixel);
|
||||
});
|
||||
} else {
|
||||
result = mPipe.WritePixelsToRow<uint32_t>([&]() -> NextPixel<uint32_t> {
|
||||
MOZ_ASSERT(mFormat == SurfaceFormat::B8G8R8A8 || src[3] == 0xFF);
|
||||
const uint32_t pixel = gfxPackedPixel(src[3], src[0], src[1], src[2]);
|
||||
src += 4;
|
||||
return AsVariant(pixel);
|
||||
});
|
||||
}
|
||||
|
||||
Maybe<SurfaceInvalidRect> invalidRect = mPipe.TakeInvalidRect();
|
||||
if (invalidRect) {
|
||||
PostInvalidation(invalidRect->mInputSpaceRect,
|
||||
Some(invalidRect->mOutputSpaceRect));
|
||||
}
|
||||
|
||||
if (result == WriteState::FAILURE) {
|
||||
MOZ_LOG(sWebPLog, LogLevel::Error,
|
||||
("[this=%p] nsWebPDecoder::ReadSingle -- write pixels error\n",
|
||||
this));
|
||||
return LexerResult(TerminalState::FAILURE);
|
||||
}
|
||||
|
||||
if (result == WriteState::FINISHED) {
|
||||
MOZ_ASSERT(row == lastRow - 1, "There was more data to read?");
|
||||
complete = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mLastRow != lastRow) {
|
||||
mLastRow = lastRow;
|
||||
|
||||
Maybe<SurfaceInvalidRect> invalidRect = mPipe.TakeInvalidRect();
|
||||
if (invalidRect) {
|
||||
PostInvalidation(invalidRect->mInputSpaceRect,
|
||||
Some(invalidRect->mOutputSpaceRect));
|
||||
}
|
||||
}
|
||||
} while (!complete);
|
||||
|
||||
if (!complete) {
|
||||
return Transition::ContinueUnbuffered(State::WEBP_DATA);
|
||||
return LexerResult(Yield::NEED_MORE_DATA);
|
||||
}
|
||||
|
||||
EndFrame();
|
||||
return Transition::TerminateSuccess();
|
||||
return LexerResult(TerminalState::SUCCESS);
|
||||
}
|
||||
|
||||
LexerTransition<nsWebPDecoder::State>
|
||||
nsWebPDecoder::ReadMultiple(const uint8_t* aData, size_t aLength)
|
||||
LexerResult
|
||||
nsWebPDecoder::ReadMultiple(WebPDemuxer* aDemuxer, bool aIsComplete)
|
||||
{
|
||||
MOZ_ASSERT(!IsMetadataDecode());
|
||||
MOZ_ASSERT(aData);
|
||||
MOZ_ASSERT(aDemuxer);
|
||||
|
||||
MOZ_LOG(sWebPLog, LogLevel::Debug,
|
||||
("[this=%p] nsWebPDecoder::ReadMultiple -- %zu bytes\n", this, aLength));
|
||||
("[this=%p] nsWebPDecoder::ReadMultiple\n", this));
|
||||
|
||||
auto data = aData;
|
||||
size_t length = aLength;
|
||||
if (NS_FAILED(GetDataBuffer(data, length))) {
|
||||
return Transition::TerminateFailure();
|
||||
}
|
||||
|
||||
WebPDemuxState state;
|
||||
WebPData fragment;
|
||||
fragment.bytes = data;
|
||||
fragment.size = length;
|
||||
WebPDemuxer* demuxer = WebPDemuxPartial(&fragment, &state);
|
||||
if (!demuxer) {
|
||||
MOZ_LOG(sWebPLog, LogLevel::Error,
|
||||
("[this=%p] nsWebPDecoder::ReadMultiple -- create demuxer error\n",
|
||||
this));
|
||||
return Transition::TerminateFailure();
|
||||
}
|
||||
|
||||
if (state == WEBP_DEMUX_PARSE_ERROR) {
|
||||
MOZ_LOG(sWebPLog, LogLevel::Error,
|
||||
("[this=%p] nsWebPDecoder::ReadMultiple -- demuxer parse error\n",
|
||||
this));
|
||||
WebPDemuxDelete(demuxer);
|
||||
return Transition::TerminateFailure();
|
||||
}
|
||||
|
||||
bool complete = false;
|
||||
bool complete = aIsComplete;
|
||||
WebPIterator iter;
|
||||
auto rv = Transition::ContinueUnbuffered(State::WEBP_DATA);
|
||||
if (WebPDemuxGetFrame(demuxer, mCurrentFrame + 1, &iter)) {
|
||||
auto rv = LexerResult(Yield::NEED_MORE_DATA);
|
||||
if (WebPDemuxGetFrame(aDemuxer, mCurrentFrame + 1, &iter)) {
|
||||
switch (iter.blend_method) {
|
||||
case WEBP_MUX_BLEND:
|
||||
mBlend = BlendMethod::OVER;
|
||||
@@ -418,51 +556,34 @@ nsWebPDecoder::ReadMultiple(const uint8_t* aData, size_t aLength)
|
||||
break;
|
||||
}
|
||||
|
||||
mFormat = iter.has_alpha ? SurfaceFormat::B8G8R8A8 : SurfaceFormat::B8G8R8X8;
|
||||
mFormat = iter.has_alpha || mCurrentFrame > 0 ? SurfaceFormat::B8G8R8A8
|
||||
: SurfaceFormat::B8G8R8X8;
|
||||
mTimeout = FrameTimeout::FromRawMilliseconds(iter.duration);
|
||||
nsIntRect frameRect(iter.x_offset, iter.y_offset, iter.width, iter.height);
|
||||
|
||||
rv = ReadSingle(iter.fragment.bytes, iter.fragment.size, false, frameRect);
|
||||
complete = state == WEBP_DEMUX_DONE && !WebPDemuxNextFrame(&iter);
|
||||
rv = ReadSingle(iter.fragment.bytes, iter.fragment.size, frameRect);
|
||||
complete = complete && !WebPDemuxNextFrame(&iter);
|
||||
WebPDemuxReleaseIterator(&iter);
|
||||
}
|
||||
|
||||
if (rv.NextStateIsTerminal()) {
|
||||
if (rv.NextStateAsTerminal() == TerminalState::SUCCESS) {
|
||||
// If we extracted one frame, and it is not the last, we need to yield to
|
||||
// the lexer to allow the upper layers to acknowledge the frame.
|
||||
if (!complete && !IsFirstFrameDecode()) {
|
||||
// The resume point is determined by whether or not we had to buffer.
|
||||
// If we have yet to buffer, we want to resume at the same point,
|
||||
// otherwise our internal buffer has everything we need and we want
|
||||
// to resume having consumed all of the current fragment.
|
||||
rv = Transition::ContinueUnbufferedAfterYield(State::WEBP_DATA,
|
||||
mData.empty() ? 0 : aLength);
|
||||
} else {
|
||||
uint32_t loopCount = WebPDemuxGetI(demuxer, WEBP_FF_LOOP_COUNT);
|
||||
if (rv.is<TerminalState>() &&
|
||||
rv.as<TerminalState>() == TerminalState::SUCCESS) {
|
||||
// If we extracted one frame, and it is not the last, we need to yield to
|
||||
// the lexer to allow the upper layers to acknowledge the frame.
|
||||
if (!complete && !IsFirstFrameDecode()) {
|
||||
rv = LexerResult(Yield::OUTPUT_AVAILABLE);
|
||||
} else {
|
||||
uint32_t loopCount = WebPDemuxGetI(aDemuxer, WEBP_FF_LOOP_COUNT);
|
||||
|
||||
MOZ_LOG(sWebPLog, LogLevel::Debug,
|
||||
("[this=%p] nsWebPDecoder::ReadMultiple -- loop count %u\n",
|
||||
this, loopCount));
|
||||
PostDecodeDone(loopCount - 1);
|
||||
}
|
||||
MOZ_LOG(sWebPLog, LogLevel::Debug,
|
||||
("[this=%p] nsWebPDecoder::ReadMultiple -- loop count %u\n",
|
||||
this, loopCount));
|
||||
PostDecodeDone(loopCount - 1);
|
||||
}
|
||||
} else if (NS_FAILED(SaveDataBuffer(data, length))) {
|
||||
rv = Transition::TerminateFailure();
|
||||
}
|
||||
|
||||
WebPDemuxDelete(demuxer);
|
||||
return rv;
|
||||
}
|
||||
|
||||
LexerTransition<nsWebPDecoder::State>
|
||||
nsWebPDecoder::FinishedData()
|
||||
{
|
||||
// Since we set up an unbuffered read for SIZE_MAX bytes, if we actually read
|
||||
// all that data something is really wrong.
|
||||
MOZ_ASSERT_UNREACHABLE("Read the entire address space?");
|
||||
return Transition::TerminateFailure();
|
||||
}
|
||||
|
||||
} // namespace image
|
||||
} // namespace mozilla
|
||||
@@ -16,7 +16,7 @@ namespace mozilla {
|
||||
namespace image {
|
||||
class RasterImage;
|
||||
|
||||
class nsWebPDecoder : public Decoder
|
||||
class nsWebPDecoder final : public Decoder
|
||||
{
|
||||
public:
|
||||
virtual ~nsWebPDecoder();
|
||||
@@ -31,34 +31,28 @@ private:
|
||||
// Decoders should only be instantiated via DecoderFactory.
|
||||
explicit nsWebPDecoder(RasterImage* aImage);
|
||||
|
||||
enum class State
|
||||
{
|
||||
WEBP_DATA,
|
||||
FINISHED_WEBP_DATA
|
||||
};
|
||||
void ApplyColorProfile(const char* aProfile, size_t aLength);
|
||||
|
||||
LexerTransition<State> ReadHeader(const char* aData, size_t aLength);
|
||||
LexerTransition<State> ReadPayload(const char* aData, size_t aLength);
|
||||
LexerTransition<State> FinishedData();
|
||||
LexerResult UpdateBuffer(SourceBufferIterator& aIterator,
|
||||
SourceBufferIterator::State aState);
|
||||
LexerResult ReadData();
|
||||
LexerResult ReadHeader(WebPDemuxer* aDemuxer, bool aIsComplete);
|
||||
LexerResult ReadPayload(WebPDemuxer* aDemuxer, bool aIsComplete);
|
||||
|
||||
nsresult CreateFrame(const nsIntRect& aFrameRect);
|
||||
void EndFrame();
|
||||
|
||||
nsresult GetDataBuffer(const uint8_t*& aData, size_t& aLength);
|
||||
nsresult SaveDataBuffer(const uint8_t* aData, size_t aLength);
|
||||
LexerResult ReadSingle(const uint8_t* aData, size_t aLength,
|
||||
const IntRect& aFrameRect);
|
||||
|
||||
LexerTransition<State> ReadSingle(const uint8_t* aData, size_t aLength,
|
||||
bool aAppend, const IntRect& aFrameRect);
|
||||
|
||||
LexerTransition<State> ReadMultiple(const uint8_t* aData, size_t aLength);
|
||||
|
||||
StreamingLexer<State> mLexer;
|
||||
LexerResult ReadMultiple(WebPDemuxer* aDemuxer, bool aIsComplete);
|
||||
|
||||
/// The SurfacePipe used to write to the output surface.
|
||||
SurfacePipe mPipe;
|
||||
|
||||
/// The buffer used to accumulate data until the complete WebP header is received.
|
||||
Vector<uint8_t> mData;
|
||||
/// The buffer used to accumulate data until the complete WebP header is
|
||||
/// received, if and only if the iterator is discontiguous.
|
||||
Vector<uint8_t> mBufferedData;
|
||||
|
||||
/// The libwebp output buffer descriptor pointing to the decoded data.
|
||||
WebPDecBuffer mBuffer;
|
||||
@@ -78,11 +72,35 @@ private:
|
||||
/// Surface format for the current frame.
|
||||
gfx::SurfaceFormat mFormat;
|
||||
|
||||
/// Frame rect for the current frame.
|
||||
IntRect mFrameRect;
|
||||
|
||||
/// The last row of decoded pixels written to mPipe.
|
||||
int mLastRow;
|
||||
|
||||
/// Number of decoded frames.
|
||||
uint32_t mCurrentFrame;
|
||||
|
||||
/// Pointer to the start of the contiguous encoded image data.
|
||||
const uint8_t* mData;
|
||||
|
||||
/// Length of data pointed to by mData.
|
||||
size_t mLength;
|
||||
|
||||
/// True if the iterator has reached its end.
|
||||
bool mIteratorComplete;
|
||||
|
||||
/// True if this decoding pass requires a WebPDemuxer.
|
||||
bool mNeedDemuxer;
|
||||
|
||||
/// True if we have setup the color profile for the image.
|
||||
bool mGotColorProfile;
|
||||
|
||||
/// Color management profile from the ICCP chunk in the image.
|
||||
qcms_profile* mInProfile;
|
||||
|
||||
/// Color management transform to apply to image data.
|
||||
qcms_transform* mTransform;
|
||||
};
|
||||
|
||||
} // namespace image
|
||||
|
||||
+16
-15
@@ -161,13 +161,13 @@ imgFrame::imgFrame()
|
||||
: mMonitor("imgFrame")
|
||||
, mDecoded(0, 0, 0, 0)
|
||||
, mLockCount(0)
|
||||
, mAborted(false)
|
||||
, mFinished(false)
|
||||
, mOptimizable(false)
|
||||
, mTimeout(FrameTimeout::FromRawMilliseconds(100))
|
||||
, mDisposalMethod(DisposalMethod::NOT_SPECIFIED)
|
||||
, mBlendMethod(BlendMethod::OVER)
|
||||
, mHasNoAlpha(false)
|
||||
, mAborted(false)
|
||||
, mFinished(false)
|
||||
, mOptimizable(false)
|
||||
, mPalettedImageData(nullptr)
|
||||
, mPaletteDepth(0)
|
||||
, mNonPremult(false)
|
||||
@@ -192,7 +192,8 @@ imgFrame::InitForDecoder(const nsIntSize& aImageSize,
|
||||
const nsIntRect& aRect,
|
||||
SurfaceFormat aFormat,
|
||||
uint8_t aPaletteDepth /* = 0 */,
|
||||
bool aNonPremult /* = false */)
|
||||
bool aNonPremult /* = false */,
|
||||
const Maybe<AnimationParams>& aAnimParams /* = Nothing() */)
|
||||
{
|
||||
// Assert for properties that should be verified by decoders,
|
||||
// warn for properties related to bad content.
|
||||
@@ -205,6 +206,15 @@ imgFrame::InitForDecoder(const nsIntSize& aImageSize,
|
||||
mImageSize = aImageSize;
|
||||
mFrameRect = aRect;
|
||||
|
||||
if (aAnimParams) {
|
||||
mBlendRect = aAnimParams->mBlendRect;
|
||||
mTimeout = aAnimParams->mTimeout;
|
||||
mBlendMethod = aAnimParams->mBlendMethod;
|
||||
mDisposalMethod = aAnimParams->mDisposalMethod;
|
||||
} else {
|
||||
mBlendRect = aRect;
|
||||
}
|
||||
|
||||
// We only allow a non-trivial frame rect (i.e., a frame rect that doesn't
|
||||
// cover the entire image) for paletted animation frames. We never draw those
|
||||
// frames directly; we just use FrameAnimator to composite them and produce a
|
||||
@@ -607,12 +617,7 @@ imgFrame::ImageUpdatedInternal(const nsIntRect& aUpdateRect)
|
||||
}
|
||||
|
||||
void
|
||||
imgFrame::Finish(Opacity aFrameOpacity /* = Opacity::SOME_TRANSPARENCY */,
|
||||
DisposalMethod aDisposalMethod /* = DisposalMethod::KEEP */,
|
||||
FrameTimeout aTimeout
|
||||
/* = FrameTimeout::FromRawMilliseconds(0) */,
|
||||
BlendMethod aBlendMethod /* = BlendMethod::OVER */,
|
||||
const Maybe<IntRect>& aBlendRect /* = Nothing() */)
|
||||
imgFrame::Finish(Opacity aFrameOpacity /* = Opacity::SOME_TRANSPARENCY */)
|
||||
{
|
||||
MonitorAutoLock lock(mMonitor);
|
||||
MOZ_ASSERT(mLockCount > 0, "Image data should be locked");
|
||||
@@ -621,10 +626,6 @@ imgFrame::Finish(Opacity aFrameOpacity /* = Opacity::SOME_TRANSPARENCY */,
|
||||
mHasNoAlpha = true;
|
||||
}
|
||||
|
||||
mDisposalMethod = aDisposalMethod;
|
||||
mTimeout = aTimeout;
|
||||
mBlendMethod = aBlendMethod;
|
||||
mBlendRect = aBlendRect;
|
||||
ImageUpdatedInternal(GetRect());
|
||||
mFinished = true;
|
||||
|
||||
@@ -844,7 +845,7 @@ imgFrame::GetAnimationData() const
|
||||
bool hasAlpha = mFormat == SurfaceFormat::B8G8R8A8;
|
||||
|
||||
return AnimationData(data, PaletteDataLength(), mTimeout, GetRect(),
|
||||
mBlendMethod, mBlendRect, mDisposalMethod, hasAlpha);
|
||||
mBlendMethod, Some(mBlendRect), mDisposalMethod, hasAlpha);
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
+26
-146
@@ -12,6 +12,7 @@
|
||||
#include "mozilla/Monitor.h"
|
||||
#include "mozilla/Move.h"
|
||||
#include "mozilla/VolatileBuffer.h"
|
||||
#include "AnimationParams.h"
|
||||
#include "gfxDrawable.h"
|
||||
#include "imgIContainer.h"
|
||||
#include "MainThreadUtils.h"
|
||||
@@ -23,129 +24,11 @@ class ImageRegion;
|
||||
class DrawableFrameRef;
|
||||
class RawAccessFrameRef;
|
||||
|
||||
enum class BlendMethod : int8_t {
|
||||
// All color components of the frame, including alpha, overwrite the current
|
||||
// contents of the frame's output buffer region.
|
||||
SOURCE,
|
||||
|
||||
// The frame should be composited onto the output buffer based on its alpha,
|
||||
// using a simple OVER operation.
|
||||
OVER
|
||||
};
|
||||
|
||||
enum class DisposalMethod : int8_t {
|
||||
CLEAR_ALL = -1, // Clear the whole image, revealing what's underneath.
|
||||
NOT_SPECIFIED, // Leave the frame and let the new frame draw on top.
|
||||
KEEP, // Leave the frame and let the new frame draw on top.
|
||||
CLEAR, // Clear the frame's area, revealing what's underneath.
|
||||
RESTORE_PREVIOUS // Restore the previous (composited) frame.
|
||||
};
|
||||
|
||||
enum class Opacity : uint8_t {
|
||||
FULLY_OPAQUE,
|
||||
SOME_TRANSPARENCY
|
||||
};
|
||||
|
||||
/**
|
||||
* FrameTimeout wraps a frame timeout value (measured in milliseconds) after
|
||||
* first normalizing it. This normalization is necessary because some tools
|
||||
* generate incorrect frame timeout values which we nevertheless have to
|
||||
* support. For this reason, code that deals with frame timeouts should always
|
||||
* use a FrameTimeout value rather than the raw value from the image header.
|
||||
*/
|
||||
struct FrameTimeout
|
||||
{
|
||||
/**
|
||||
* @return a FrameTimeout of zero. This should be used only for math
|
||||
* involving FrameTimeout values. You can't obtain a zero FrameTimeout from
|
||||
* FromRawMilliseconds().
|
||||
*/
|
||||
static FrameTimeout Zero() { return FrameTimeout(0); }
|
||||
|
||||
/// @return an infinite FrameTimeout.
|
||||
static FrameTimeout Forever() { return FrameTimeout(-1); }
|
||||
|
||||
/// @return a FrameTimeout obtained by normalizing a raw timeout value.
|
||||
static FrameTimeout FromRawMilliseconds(int32_t aRawMilliseconds)
|
||||
{
|
||||
// Normalize all infinite timeouts to the same value.
|
||||
if (aRawMilliseconds < 0) {
|
||||
return FrameTimeout::Forever();
|
||||
}
|
||||
|
||||
// Very small timeout values are problematic for two reasons: we don't want
|
||||
// to burn energy redrawing animated images extremely fast, and broken tools
|
||||
// generate these values when they actually want a "default" value, so such
|
||||
// images won't play back right without normalization. For some context,
|
||||
// see bug 890743, bug 125137, bug 139677, and bug 207059. The historical
|
||||
// behavior of IE and Opera was:
|
||||
// IE 6/Win:
|
||||
// 10 - 50ms is normalized to 100ms.
|
||||
// >50ms is used unnormalized.
|
||||
// Opera 7 final/Win:
|
||||
// 10ms is normalized to 100ms.
|
||||
// >10ms is used unnormalized.
|
||||
if (aRawMilliseconds >= 0 && aRawMilliseconds <= 10 ) {
|
||||
return FrameTimeout(100);
|
||||
}
|
||||
|
||||
// The provided timeout value is OK as-is.
|
||||
return FrameTimeout(aRawMilliseconds);
|
||||
}
|
||||
|
||||
bool operator==(const FrameTimeout& aOther) const
|
||||
{
|
||||
return mTimeout == aOther.mTimeout;
|
||||
}
|
||||
|
||||
bool operator!=(const FrameTimeout& aOther) const { return !(*this == aOther); }
|
||||
|
||||
FrameTimeout operator+(const FrameTimeout& aOther)
|
||||
{
|
||||
if (*this == Forever() || aOther == Forever()) {
|
||||
return Forever();
|
||||
}
|
||||
|
||||
return FrameTimeout(mTimeout + aOther.mTimeout);
|
||||
}
|
||||
|
||||
FrameTimeout& operator+=(const FrameTimeout& aOther)
|
||||
{
|
||||
*this = *this + aOther;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return this FrameTimeout's value in milliseconds. Illegal to call on a
|
||||
* an infinite FrameTimeout value.
|
||||
*/
|
||||
uint32_t AsMilliseconds() const
|
||||
{
|
||||
if (*this == Forever()) {
|
||||
MOZ_ASSERT_UNREACHABLE("Calling AsMilliseconds() on an infinite FrameTimeout");
|
||||
return 100; // Fail to something sane.
|
||||
}
|
||||
|
||||
return uint32_t(mTimeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return this FrameTimeout value encoded so that non-negative values
|
||||
* represent a timeout in milliseconds, and -1 represents an infinite
|
||||
* timeout.
|
||||
*
|
||||
* XXX(seth): This is a backwards compatibility hack that should be removed.
|
||||
*/
|
||||
int32_t AsEncodedValueDeprecated() const { return mTimeout; }
|
||||
|
||||
private:
|
||||
explicit FrameTimeout(int32_t aTimeout)
|
||||
: mTimeout(aTimeout)
|
||||
{ }
|
||||
|
||||
int32_t mTimeout;
|
||||
};
|
||||
|
||||
/**
|
||||
* AnimationData contains all of the information necessary for using an imgFrame
|
||||
* as part of an animation.
|
||||
@@ -210,14 +93,19 @@ public:
|
||||
const nsIntRect& aRect,
|
||||
SurfaceFormat aFormat,
|
||||
uint8_t aPaletteDepth = 0,
|
||||
bool aNonPremult = false);
|
||||
bool aNonPremult = false,
|
||||
const Maybe<AnimationParams>& aAnimParams = Nothing());
|
||||
|
||||
nsresult InitForDecoder(const nsIntSize& aSize,
|
||||
SurfaceFormat aFormat,
|
||||
uint8_t aPaletteDepth = 0)
|
||||
{
|
||||
return InitForDecoder(aSize, nsIntRect(0, 0, aSize.width, aSize.height),
|
||||
aFormat, aPaletteDepth);
|
||||
nsIntRect frameRect(0, 0, aSize.width, aSize.height);
|
||||
AnimationParams animParams { frameRect, FrameTimeout::Forever(),
|
||||
/* aFrameNum */ 1, BlendMethod::OVER,
|
||||
DisposalMethod::NOT_SPECIFIED };
|
||||
return InitForDecoder(aSize, frameRect,
|
||||
aFormat, aPaletteDepth, false, Some(animParams));
|
||||
}
|
||||
|
||||
|
||||
@@ -268,22 +156,8 @@ public:
|
||||
* RawAccessFrameRef pointing to an imgFrame.
|
||||
*
|
||||
* @param aFrameOpacity Whether this imgFrame is opaque.
|
||||
* @param aDisposalMethod For animation frames, how this imgFrame is cleared
|
||||
* from the compositing frame before the next frame is
|
||||
* displayed.
|
||||
* @param aTimeout For animation frames, the timeout before the next
|
||||
* frame is displayed.
|
||||
* @param aBlendMethod For animation frames, a blending method to be used
|
||||
* when compositing this frame.
|
||||
* @param aBlendRect For animation frames, if present, the subrect in
|
||||
* which @aBlendMethod applies. Outside of this
|
||||
* subrect, BlendMethod::OVER is always used.
|
||||
*/
|
||||
void Finish(Opacity aFrameOpacity = Opacity::SOME_TRANSPARENCY,
|
||||
DisposalMethod aDisposalMethod = DisposalMethod::KEEP,
|
||||
FrameTimeout aTimeout = FrameTimeout::FromRawMilliseconds(0),
|
||||
BlendMethod aBlendMethod = BlendMethod::OVER,
|
||||
const Maybe<IntRect>& aBlendRect = Nothing());
|
||||
void Finish(Opacity aFrameOpacity = Opacity::SOME_TRANSPARENCY);
|
||||
|
||||
/**
|
||||
* Mark this imgFrame as aborted. This informs the imgFrame that if it isn't
|
||||
@@ -322,9 +196,15 @@ public:
|
||||
*/
|
||||
uint32_t GetBytesPerPixel() const { return GetIsPaletted() ? 1 : 4; }
|
||||
|
||||
IntSize GetImageSize() const { return mImageSize; }
|
||||
IntRect GetRect() const { return mFrameRect; }
|
||||
const IntSize& GetImageSize() const { return mImageSize; }
|
||||
const IntRect& GetRect() const { return mFrameRect; }
|
||||
IntSize GetSize() const { return mFrameRect.Size(); }
|
||||
const IntRect& GetBlendRect() const { return mBlendRect; }
|
||||
IntRect GetBoundedBlendRect() const { return mBlendRect.Intersect(mFrameRect); }
|
||||
FrameTimeout GetTimeout() const { return mTimeout; }
|
||||
BlendMethod GetBlendMethod() const { return mBlendMethod; }
|
||||
DisposalMethod GetDisposalMethod() const { return mDisposalMethod; }
|
||||
bool FormatHasAlpha() const { return mFormat == SurfaceFormat::B8G8R8A8; }
|
||||
void GetImageData(uint8_t** aData, uint32_t* length) const;
|
||||
uint8_t* GetImageData() const;
|
||||
|
||||
@@ -406,14 +286,6 @@ private: // data
|
||||
//! Number of RawAccessFrameRefs currently alive for this imgFrame.
|
||||
int32_t mLockCount;
|
||||
|
||||
//! The timeout for this frame.
|
||||
FrameTimeout mTimeout;
|
||||
|
||||
DisposalMethod mDisposalMethod;
|
||||
BlendMethod mBlendMethod;
|
||||
Maybe<IntRect> mBlendRect;
|
||||
SurfaceFormat mFormat;
|
||||
|
||||
bool mHasNoAlpha;
|
||||
bool mAborted;
|
||||
bool mFinished;
|
||||
@@ -426,6 +298,14 @@ private: // data
|
||||
|
||||
IntSize mImageSize;
|
||||
IntRect mFrameRect;
|
||||
IntRect mBlendRect;
|
||||
|
||||
//! The timeout for this frame.
|
||||
FrameTimeout mTimeout;
|
||||
|
||||
DisposalMethod mDisposalMethod;
|
||||
BlendMethod mBlendMethod;
|
||||
SurfaceFormat mFormat;
|
||||
|
||||
// The palette and image data for images that are paletted, since Cairo
|
||||
// doesn't support these images.
|
||||
|
||||
@@ -245,7 +245,7 @@ already_AddRefed<Decoder> CreateTrivialDecoder();
|
||||
* @param aConfigs The configuration for the pipeline.
|
||||
*/
|
||||
template <typename Func, typename... Configs>
|
||||
void WithFilterPipeline(Decoder* aDecoder, Func aFunc, Configs... aConfigs)
|
||||
void WithFilterPipeline(Decoder* aDecoder, Func aFunc, const Configs&... aConfigs)
|
||||
{
|
||||
auto pipe = MakeUnique<typename detail::FilterPipeline<Configs...>::Type>();
|
||||
nsresult rv = pipe->Configure(aConfigs...);
|
||||
@@ -268,7 +268,7 @@ void WithFilterPipeline(Decoder* aDecoder, Func aFunc, Configs... aConfigs)
|
||||
* @param aConfigs The configuration for the pipeline.
|
||||
*/
|
||||
template <typename... Configs>
|
||||
void AssertConfiguringPipelineFails(Decoder* aDecoder, Configs... aConfigs)
|
||||
void AssertConfiguringPipelineFails(Decoder* aDecoder, const Configs&... aConfigs)
|
||||
{
|
||||
auto pipe = MakeUnique<typename detail::FilterPipeline<Configs...>::Type>();
|
||||
nsresult rv = pipe->Configure(aConfigs...);
|
||||
|
||||
@@ -33,7 +33,7 @@ WithADAM7InterpolatingFilter(const IntSize& aSize, Func aFunc)
|
||||
|
||||
WithFilterPipeline(decoder, Forward<Func>(aFunc),
|
||||
ADAM7InterpolatingConfig { },
|
||||
SurfaceConfig { decoder, 0, aSize,
|
||||
SurfaceConfig { decoder, aSize,
|
||||
SurfaceFormat::B8G8R8A8, false });
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ AssertConfiguringADAM7InterpolatingFilterFails(const IntSize& aSize)
|
||||
|
||||
AssertConfiguringPipelineFails(decoder,
|
||||
ADAM7InterpolatingConfig { },
|
||||
SurfaceConfig { decoder, 0, aSize,
|
||||
SurfaceConfig { decoder, aSize,
|
||||
SurfaceFormat::B8G8R8A8, false });
|
||||
}
|
||||
|
||||
@@ -664,7 +664,7 @@ TEST(ImageADAM7InterpolatingFilter, ConfiguringPalettedADAM7InterpolatingFilterF
|
||||
// should fail.
|
||||
AssertConfiguringPipelineFails(decoder,
|
||||
ADAM7InterpolatingConfig { },
|
||||
PalettedSurfaceConfig { decoder, 0, IntSize(100, 100),
|
||||
PalettedSurfaceConfig { decoder, IntSize(100, 100),
|
||||
IntRect(0, 0, 50, 50),
|
||||
SurfaceFormat::B8G8R8A8, 8,
|
||||
false });
|
||||
|
||||
@@ -28,7 +28,7 @@ WithDeinterlacingFilter(const IntSize& aSize,
|
||||
|
||||
WithFilterPipeline(decoder, Forward<Func>(aFunc),
|
||||
DeinterlacingConfig<uint32_t> { aProgressiveDisplay },
|
||||
SurfaceConfig { decoder, 0, aSize,
|
||||
SurfaceConfig { decoder, aSize,
|
||||
SurfaceFormat::B8G8R8A8, false });
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ WithPalettedDeinterlacingFilter(const IntSize& aSize,
|
||||
|
||||
WithFilterPipeline(decoder, Forward<Func>(aFunc),
|
||||
DeinterlacingConfig<uint8_t> { /* mProgressiveDisplay = */ true },
|
||||
PalettedSurfaceConfig { decoder, 0, aSize,
|
||||
PalettedSurfaceConfig { decoder, aSize,
|
||||
IntRect(0, 0, 100, 100),
|
||||
SurfaceFormat::B8G8R8A8, 8,
|
||||
false });
|
||||
@@ -55,7 +55,7 @@ AssertConfiguringDeinterlacingFilterFails(const IntSize& aSize)
|
||||
|
||||
AssertConfiguringPipelineFails(decoder,
|
||||
DeinterlacingConfig<uint32_t> { /* mProgressiveDisplay = */ true},
|
||||
SurfaceConfig { decoder, 0, aSize,
|
||||
SurfaceConfig { decoder, aSize,
|
||||
SurfaceFormat::B8G8R8A8, false });
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ WithDownscalingFilter(const IntSize& aInputSize,
|
||||
WithFilterPipeline(decoder, Forward<Func>(aFunc),
|
||||
DownscalingConfig { aInputSize,
|
||||
SurfaceFormat::B8G8R8A8 },
|
||||
SurfaceConfig { decoder, 0, aOutputSize,
|
||||
SurfaceConfig { decoder, aOutputSize,
|
||||
SurfaceFormat::B8G8R8A8, false });
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ AssertConfiguringDownscalingFilterFails(const IntSize& aInputSize,
|
||||
AssertConfiguringPipelineFails(decoder,
|
||||
DownscalingConfig { aInputSize,
|
||||
SurfaceFormat::B8G8R8A8 },
|
||||
SurfaceConfig { decoder, 0, aOutputSize,
|
||||
SurfaceConfig { decoder, aOutputSize,
|
||||
SurfaceFormat::B8G8R8A8, false });
|
||||
}
|
||||
|
||||
@@ -224,7 +224,7 @@ TEST(ImageDownscalingFilter, ConfiguringPalettedDownscaleFails)
|
||||
AssertConfiguringPipelineFails(decoder,
|
||||
DownscalingConfig { IntSize(100, 100),
|
||||
SurfaceFormat::B8G8R8A8 },
|
||||
PalettedSurfaceConfig { decoder, 0, IntSize(20, 20),
|
||||
PalettedSurfaceConfig { decoder, IntSize(20, 20),
|
||||
IntRect(0, 0, 20, 20),
|
||||
SurfaceFormat::B8G8R8A8, 8,
|
||||
false });
|
||||
|
||||
@@ -52,6 +52,6 @@ TEST(ImageDownscalingFilter, NoSkia)
|
||||
AssertConfiguringPipelineFails(decoder,
|
||||
DownscalingConfig { IntSize(100, 100),
|
||||
SurfaceFormat::B8G8R8A8 },
|
||||
SurfaceConfig { decoder, 0, IntSize(50, 50),
|
||||
SurfaceConfig { decoder, IntSize(50, 50),
|
||||
SurfaceFormat::B8G8R8A8, false });
|
||||
}
|
||||
@@ -28,7 +28,7 @@ WithRemoveFrameRectFilter(const IntSize& aSize,
|
||||
|
||||
WithFilterPipeline(decoder, Forward<Func>(aFunc),
|
||||
RemoveFrameRectConfig { aFrameRect },
|
||||
SurfaceConfig { decoder, 0, aSize,
|
||||
SurfaceConfig { decoder, aSize,
|
||||
SurfaceFormat::B8G8R8A8, false });
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ AssertConfiguringRemoveFrameRectFilterFails(const IntSize& aSize,
|
||||
|
||||
AssertConfiguringPipelineFails(decoder,
|
||||
RemoveFrameRectConfig { aFrameRect },
|
||||
SurfaceConfig { decoder, 0, aSize,
|
||||
SurfaceConfig { decoder, aSize,
|
||||
SurfaceFormat::B8G8R8A8, false });
|
||||
}
|
||||
|
||||
@@ -320,7 +320,7 @@ TEST(ImageRemoveFrameRectFilter, ConfiguringPalettedRemoveFrameRectFails)
|
||||
// should fail.
|
||||
AssertConfiguringPipelineFails(decoder,
|
||||
RemoveFrameRectConfig { IntRect(0, 0, 50, 50) },
|
||||
PalettedSurfaceConfig { decoder, 0, IntSize(100, 100),
|
||||
PalettedSurfaceConfig { decoder, IntSize(100, 100),
|
||||
IntRect(0, 0, 50, 50),
|
||||
SurfaceFormat::B8G8R8A8, 8,
|
||||
false });
|
||||
|
||||
@@ -149,7 +149,7 @@ TEST_F(ImageSurfacePipeIntegration, SurfacePipe)
|
||||
|
||||
auto sink = MakeUnique<SurfaceSink>();
|
||||
nsresult rv =
|
||||
sink->Configure(SurfaceConfig { decoder, 0, IntSize(100, 100),
|
||||
sink->Configure(SurfaceConfig { decoder, IntSize(100, 100),
|
||||
SurfaceFormat::B8G8R8A8, false });
|
||||
ASSERT_TRUE(NS_SUCCEEDED(rv));
|
||||
|
||||
@@ -227,7 +227,7 @@ TEST_F(ImageSurfacePipeIntegration, PalettedSurfacePipe)
|
||||
|
||||
auto sink = MakeUnique<PalettedSurfaceSink>();
|
||||
nsresult rv =
|
||||
sink->Configure(PalettedSurfaceConfig { decoder, 0, IntSize(100, 100),
|
||||
sink->Configure(PalettedSurfaceConfig { decoder, IntSize(100, 100),
|
||||
IntRect(0, 0, 100, 100),
|
||||
SurfaceFormat::B8G8R8A8,
|
||||
8, false });
|
||||
@@ -313,7 +313,7 @@ TEST_F(ImageSurfacePipeIntegration, DeinterlaceDownscaleWritePixels)
|
||||
DeinterlacingConfig<uint32_t> { /* mProgressiveDisplay = */ true },
|
||||
DownscalingConfig { IntSize(100, 100),
|
||||
SurfaceFormat::B8G8R8A8 },
|
||||
SurfaceConfig { decoder, 0, IntSize(25, 25),
|
||||
SurfaceConfig { decoder, IntSize(25, 25),
|
||||
SurfaceFormat::B8G8R8A8, false });
|
||||
}
|
||||
|
||||
@@ -369,7 +369,7 @@ TEST_F(ImageSurfacePipeIntegration, RemoveFrameRectBottomRightDownscaleWritePixe
|
||||
RemoveFrameRectConfig { IntRect(50, 50, 100, 100) },
|
||||
DownscalingConfig { IntSize(100, 100),
|
||||
SurfaceFormat::B8G8R8A8 },
|
||||
SurfaceConfig { decoder, 0, IntSize(20, 20),
|
||||
SurfaceConfig { decoder, IntSize(20, 20),
|
||||
SurfaceFormat::B8G8R8A8, false });
|
||||
}
|
||||
|
||||
@@ -403,7 +403,7 @@ TEST_F(ImageSurfacePipeIntegration, RemoveFrameRectTopLeftDownscaleWritePixels)
|
||||
RemoveFrameRectConfig { IntRect(-50, -50, 100, 100) },
|
||||
DownscalingConfig { IntSize(100, 100),
|
||||
SurfaceFormat::B8G8R8A8 },
|
||||
SurfaceConfig { decoder, 0, IntSize(20, 20),
|
||||
SurfaceConfig { decoder, IntSize(20, 20),
|
||||
SurfaceFormat::B8G8R8A8, false });
|
||||
}
|
||||
|
||||
@@ -427,7 +427,7 @@ TEST_F(ImageSurfacePipeIntegration, DeinterlaceRemoveFrameRectWritePixels)
|
||||
WithFilterPipeline(decoder, test,
|
||||
DeinterlacingConfig<uint32_t> { /* mProgressiveDisplay = */ true },
|
||||
RemoveFrameRectConfig { IntRect(50, 50, 100, 100) },
|
||||
SurfaceConfig { decoder, 0, IntSize(100, 100),
|
||||
SurfaceConfig { decoder, IntSize(100, 100),
|
||||
SurfaceFormat::B8G8R8A8, false });
|
||||
}
|
||||
|
||||
@@ -450,7 +450,7 @@ TEST_F(ImageSurfacePipeIntegration, DeinterlaceRemoveFrameRectDownscaleWritePixe
|
||||
RemoveFrameRectConfig { IntRect(50, 50, 100, 100) },
|
||||
DownscalingConfig { IntSize(100, 100),
|
||||
SurfaceFormat::B8G8R8A8 },
|
||||
SurfaceConfig { decoder, 0, IntSize(20, 20),
|
||||
SurfaceConfig { decoder, IntSize(20, 20),
|
||||
SurfaceFormat::B8G8R8A8, false });
|
||||
}
|
||||
|
||||
@@ -465,7 +465,7 @@ TEST_F(ImageSurfacePipeIntegration, ConfiguringPalettedRemoveFrameRectDownscaleF
|
||||
RemoveFrameRectConfig { IntRect(0, 0, 50, 50) },
|
||||
DownscalingConfig { IntSize(100, 100),
|
||||
SurfaceFormat::B8G8R8A8 },
|
||||
PalettedSurfaceConfig { decoder, 0, IntSize(100, 100),
|
||||
PalettedSurfaceConfig { decoder, IntSize(100, 100),
|
||||
IntRect(0, 0, 50, 50),
|
||||
SurfaceFormat::B8G8R8A8, 8,
|
||||
false });
|
||||
@@ -482,7 +482,7 @@ TEST_F(ImageSurfacePipeIntegration, ConfiguringPalettedDeinterlaceDownscaleFails
|
||||
DeinterlacingConfig<uint8_t> { /* mProgressiveDisplay = */ true},
|
||||
DownscalingConfig { IntSize(100, 100),
|
||||
SurfaceFormat::B8G8R8A8 },
|
||||
PalettedSurfaceConfig { decoder, 0, IntSize(100, 100),
|
||||
PalettedSurfaceConfig { decoder, IntSize(100, 100),
|
||||
IntRect(0, 0, 20, 20),
|
||||
SurfaceFormat::B8G8R8A8, 8,
|
||||
false });
|
||||
@@ -503,6 +503,6 @@ TEST_F(ImageSurfacePipeIntegration, ConfiguringHugeDeinterlacingBufferFails)
|
||||
DeinterlacingConfig<uint32_t> { /* mProgressiveDisplay = */ true},
|
||||
DownscalingConfig { IntSize(60000, 60000),
|
||||
SurfaceFormat::B8G8R8A8 },
|
||||
SurfaceConfig { decoder, 0, IntSize(600, 600),
|
||||
SurfaceConfig { decoder, IntSize(600, 600),
|
||||
SurfaceFormat::B8G8R8A8, false });
|
||||
}
|
||||
@@ -32,7 +32,7 @@ WithSurfaceSink(Func aFunc)
|
||||
const bool flipVertically = Orientation == Orient::FLIP_VERTICALLY;
|
||||
|
||||
WithFilterPipeline(decoder, Forward<Func>(aFunc),
|
||||
SurfaceConfig { decoder, 0, IntSize(100, 100),
|
||||
SurfaceConfig { decoder, IntSize(100, 100),
|
||||
SurfaceFormat::B8G8R8A8, flipVertically });
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ WithPalettedSurfaceSink(const IntRect& aFrameRect, Func aFunc)
|
||||
ASSERT_TRUE(decoder != nullptr);
|
||||
|
||||
WithFilterPipeline(decoder, Forward<Func>(aFunc),
|
||||
PalettedSurfaceConfig { decoder, 0, IntSize(100, 100),
|
||||
PalettedSurfaceConfig { decoder, IntSize(100, 100),
|
||||
aFrameRect, SurfaceFormat::B8G8R8A8,
|
||||
8, false });
|
||||
}
|
||||
|
||||
@@ -291,6 +291,11 @@ nsHttpPipeline::PushBack(const char *data, uint32_t length)
|
||||
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
|
||||
MOZ_ASSERT(mPushBackLen == 0, "push back buffer already has data!");
|
||||
|
||||
// Some bad behaving proxies may yank the connection out from under us.
|
||||
// Check if we still have a connection to work with.
|
||||
if (!mConnection)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
// If we have no chance for a pipeline (e.g. due to an Upgrade)
|
||||
// then push this data down to original connection
|
||||
if (!mConnection->IsPersistent())
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
+2475
-1559
File diff suppressed because it is too large.
Load diff
@@ -186,7 +186,7 @@
|
||||
placeholder="&search.placeholder;"/>
|
||||
</hbox>
|
||||
|
||||
<hbox flex="1">
|
||||
<hbox id="main" flex="1">
|
||||
|
||||
<!-- category list -->
|
||||
<richlistbox id="categories">
|
||||
|
||||
Reference in new issue
Block a user