Compare commits

...
Author SHA1 Message Date
wolfbeast d129c900c9 Version bump for point release 2018-12-04 20:09:52 +01:00
wolfbeast de2c8419c6 Fix typo for Firefox compatmode prefs.
Fixes regression from ec2e3bc027
2018-12-04 20:05:14 +01:00
JustOff f6137449b6 Revert "[PALEMOON] Sanitize - "Form and search history" sanitize also the Findbar text and history (Ctrl-Z)"
This reverts commit a97887a677.
2018-12-04 20:04:58 +01:00
Matt A. Tobin 020248c6e3 [TychoAM] Give the second <hbox> an ID so it can be targeted in extensions.xul 2018-12-04 20:04:32 +01:00
trav90 e0a09e1aaf Update HSTS preload list
Tag #447
2018-12-04 20:04:16 +01:00
wolfbeast 4af3ad5dfe Fix debug assertion.
See: https://github.com/MoonchildProductions/UXP/commit/622098073e132995994fac4d61e3629d08ee1801#commitcomment-31390051
2018-12-04 20:03:58 +01:00
wolfbeast ff66da9af4 Fix blank pixel color for truncated GIFs 2018-12-04 20:03:25 +01:00
wolfbeast c2e358c520 Split out FrameTimeout into its own header file for re-use. 2018-12-04 20:03:12 +01:00
wolfbeast d185071acf Unrefactor mRawVeggies back to mVBuffMeat ;P
(mRawSurface -> mVBuf)
2018-12-04 20:02:59 +01:00
wolfbeast e517e70eb1 Fix pasta error 2018-12-04 20:02:46 +01:00
wolfbeast 0a6db54d86 Bug 1462355 - Part 1c. Make individual image decoders to use updated Decoder/SurfacePipe methods.
Extend this change to nsWebPDecoder.cpp
2018-12-04 20:02:29 +01:00
wolfbeast c5d5a28f76 Bug 1462355 - Part 1b. Update Decoder and SurfacePipe plumbing to use updated imgFrame methods. 2018-12-04 20:02:14 +01:00
wolfbeast a0e20005ef Bug 1462355 - Part 1a. Make imgFrame animation parameters threadsafe.
We currently choose to set the animation parameters (blend method, blend
rect, disposal method, timeout) in imgFrame::Finish instead of
imgFrame::InitForDecoder. The decoders themselves already have access to
the necessary information at the time InitForDecoder is called, so there
is no reason to do this. Moving the configuration to initialization will
allow us to relax the mutex protection on these parameters.

This part simply reorganizes imgFrame, and subsequent parts will
introduce the necessary changes to SurfacePipe and decoders.
2018-12-04 20:01:58 +01:00
wolfbeast a3c1f180ce Port WebP decoder changes.
This breaks animated WebP for the moment, but adds QCMS color management and lexer changes.
Tag #831
2018-12-04 20:01:37 +01:00
wolfbeast cd4f7a1798 Add a nullcheck on pipelined HTTP connections' pushback. 2018-12-04 20:01:01 +01:00
JustOff 914294e570 [PALEMOON] Align the places component with the updated PlacesUtils API 2018-12-04 20:00:31 +01:00
Matt A. Tobin 831b9d8d9e [PALEMOON] Use generic application icon for external applications in about:feeds 2018-12-04 19:59:14 +01:00
Matt A. Tobin 6864a02d7b Issue #861 - The bookmark properties window should focus on the Name Picker text box on init 2018-11-14 13:42:16 +01:00
wolfbeast bffec09e66 Version bump for point release. 2018-11-14 10:42:57 +01:00
wolfbeast 51e5770962 Revert "[PALEMOON] Focus and select the first non-collapsed text element in the bookmark properties dialog"
This reverts commit e9a58eae57.

Tag #867
2018-11-14 09:59:06 +01:00
wolfbeast e1bfe33031 Version bump for release 28.2.0 2018-11-07 12:54:01 +01:00
wolfbeast 8240fb9c73 Merge branch 'master' into Pale_Moon-release
# Conflicts:
#	application/palemoon/config/version.txt
2018-11-07 12:47:12 +01:00
JustOff f9e04b54c2 [PALEMOON] Restore strings mistakenly removed by [8be0c16be6] 2018-09-21 10:26:41 +02:00
JustOff 775e800ca0 Use SessionStore.promiseInitialized() to avoid race condition in "about:home" (#774)
* Use SessionStore.promiseInitialized() to avoid race condition in "about:home"

* Improve code style of [d6c3adb91c9f29786f8ed8d93baa14c076494017]
2018-09-18 08:25:39 +02:00
wolfbeast 882aaf5b1f Merge branch 'master' into Pale_Moon-release
# Conflicts:
#	application/palemoon/config/version.txt
#	js/src/jit/x86-shared/AssemblerBuffer-x86-shared.h
#	toolkit/components/search/orginal/nsSearchService.js
2018-09-17 08:51:49 +02:00
wolfbeast 8ee235ca5d Version bump for minor bugfix release. 2018-08-30 16:59:46 +02:00
wolfbeast cc54bbe952 Revert "Bug 1388020. r=nical, a=RyanVM"
This reverts commit e7189e33f5.
2018-08-30 12:36:03 +02:00
wolfbeast 73b202b1e5 Revert "Bug 1444668 - Avoid allocating large AssemblerBuffers. r=luke, r=bbouvier, a=RyanVM"
This reverts commit 9472136272.
2018-08-30 12:33:56 +02:00
wolfbeast 21042963de Version bump for win rebuild with less aggressive build config. 2018-08-26 17:44:06 +02:00
wolfbeast d443656df5 Version bump for release builds. 2018-08-07 14:20:47 +02:00
Matt A. Tobin 29a8db7280 Fix include path 2018-08-06 18:02:27 +02:00
Matt A. Tobin daa52eef9a Add a slightly modified version of the gecko/44 search service and use it when building Pale Moon 2018-08-06 18:02:10 +02:00
Matt A. Tobin 95ce1f0d63 Move the "native" search service to a subdirectory 2018-08-06 18:01:49 +02:00
SpockMan02 4dadef2740 Restore some osx icons 2018-08-06 11:12:29 +02:00
SpockMan02 b5d8e6d28f Revert osx editBookmarkOverlay.css to osx version 2018-08-06 11:12:16 +02:00
adeshkp fc0477a98e Fix constructor in nsHtml5AtomTable.cpp
Follow up to 2bc00ef to fix build bustage
2018-08-06 11:10:24 +02:00
adeshkp 36d06b9e75 Improve nsHtml5AtomTable performance (#693)
* cpp change
* h change
2018-08-06 11:10:10 +02:00
wolfbeast 51bdd3efbc Make AC option for devtools (client) an enable_bool to align with the default. 2018-08-04 08:21:29 +02:00
janekptacijarabaci bc2584493a [PALEMOON] Bug 863773 - Changes the way plugin handlers are loaded at the preferences applications pane. Uses enabledPlugin attribute from each navigator.mimeTypes to find the actual plugin used to handle the mime type 2018-08-04 08:07:02 +02:00
janekptacijarabaci 0f38772573 [PALEMOON] Bug 1034043 - Fix remaining use of "awlaysAsk" attribute in applications pref pane 2018-08-04 08:06:49 +02:00
janekptacijarabaci ef81277925 [PALEMOON] Bug 480242 - "Always ask" doesn't work for Plugins 2018-08-04 08:06:36 +02:00
janekptacijarabaci 8b20931e12 [PALEMOON] Bug 757726 - Populate Preferences' Applications list using PluginHost 2018-08-04 08:06:24 +02:00
SpockMan02 b247982d37 Restore Mac bookmark doorhanger styling 2018-08-04 08:05:34 +02:00
38 changed files with 5344 additions and 3452 deletions

No files matched your search

+3 -3
View File
@@ -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
+7 -17
View File
@@ -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
View File
@@ -1 +1 @@
28.2.0a1
28.2.2
+47
View File
@@ -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
View File
@@ -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
View File
@@ -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:
+2 -2
View File
@@ -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)) {
+119
View File
@@ -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
+3 -3
View File
@@ -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)) {
+7 -6
View File
@@ -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
View File
@@ -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
View File
@@ -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...);
+1 -2
View File
@@ -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)) {
+22 -9
View File
@@ -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) {
+2 -1
View File
@@ -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();
+2 -2
View File
@@ -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,
+23 -18
View File
@@ -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
View File
@@ -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
+37 -19
View File
@@ -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
View File
@@ -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
View File
@@ -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.
+2 -2
View File
@@ -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 });
+3 -3
View File
@@ -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 });
}
+3 -3
View File
@@ -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 });
+10 -10
View File
@@ -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 });
}
+2 -2
View File
@@ -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 });
}
+5
View File
@@ -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
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">