mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-03 05:47:29 +09:00
import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo
This commit is contained in:
commit
dcd9973243
150858 changed files with 23884658 additions and 0 deletions
1526
gfx/2d/2D.h
Normal file
1526
gfx/2d/2D.h
Normal file
File diff suppressed because it is too large
Load diff
86
gfx/2d/AutoHelpersWin.h
Normal file
86
gfx/2d/AutoHelpersWin.h
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* 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_gfx_AutoHelpersWin_h
|
||||
#define mozilla_gfx_AutoHelpersWin_h
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
// Get the global device context, and auto-release it on destruction.
|
||||
class AutoDC
|
||||
{
|
||||
public:
|
||||
AutoDC() {
|
||||
mDC = ::GetDC(nullptr);
|
||||
}
|
||||
|
||||
~AutoDC() {
|
||||
::ReleaseDC(nullptr, mDC);
|
||||
}
|
||||
|
||||
HDC GetDC() {
|
||||
return mDC;
|
||||
}
|
||||
|
||||
private:
|
||||
HDC mDC;
|
||||
};
|
||||
|
||||
// Select a font into the given DC, and auto-restore.
|
||||
class AutoSelectFont
|
||||
{
|
||||
public:
|
||||
AutoSelectFont(HDC aDC, LOGFONTW *aLogFont)
|
||||
: mOwnsFont(false)
|
||||
{
|
||||
mFont = ::CreateFontIndirectW(aLogFont);
|
||||
if (mFont) {
|
||||
mOwnsFont = true;
|
||||
mDC = aDC;
|
||||
mOldFont = (HFONT)::SelectObject(aDC, mFont);
|
||||
} else {
|
||||
mOldFont = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
AutoSelectFont(HDC aDC, HFONT aFont)
|
||||
: mOwnsFont(false)
|
||||
{
|
||||
mDC = aDC;
|
||||
mFont = aFont;
|
||||
mOldFont = (HFONT)::SelectObject(aDC, aFont);
|
||||
}
|
||||
|
||||
~AutoSelectFont() {
|
||||
if (mOldFont) {
|
||||
::SelectObject(mDC, mOldFont);
|
||||
if (mOwnsFont) {
|
||||
::DeleteObject(mFont);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool IsValid() const {
|
||||
return mFont != nullptr;
|
||||
}
|
||||
|
||||
HFONT GetFont() const {
|
||||
return mFont;
|
||||
}
|
||||
|
||||
private:
|
||||
HDC mDC;
|
||||
HFONT mFont;
|
||||
HFONT mOldFont;
|
||||
bool mOwnsFont;
|
||||
};
|
||||
|
||||
} // gfx
|
||||
} // mozilla
|
||||
|
||||
#endif // mozilla_gfx_AutoHelpersWin_h
|
||||
110
gfx/2d/BaseCoord.h
Normal file
110
gfx/2d/BaseCoord.h
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
/* -*- 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_GFX_BASECOORD_H_
|
||||
#define MOZILLA_GFX_BASECOORD_H_
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
/**
|
||||
* Do not use this class directly. Subclass it, pass that subclass as the
|
||||
* Sub parameter, and only use that subclass. This allows methods to safely
|
||||
* cast 'this' to 'Sub*'.
|
||||
*/
|
||||
template <class T, class Sub>
|
||||
struct BaseCoord {
|
||||
T value;
|
||||
|
||||
// Constructors
|
||||
constexpr BaseCoord() : value(0) {}
|
||||
explicit constexpr BaseCoord(T aValue) : value(aValue) {}
|
||||
|
||||
// Note that '=' isn't defined so we'll get the
|
||||
// compiler generated default assignment operator
|
||||
|
||||
operator T() const { return value; }
|
||||
|
||||
friend bool operator==(Sub aA, Sub aB) {
|
||||
return aA.value == aB.value;
|
||||
}
|
||||
friend bool operator!=(Sub aA, Sub aB) {
|
||||
return aA.value != aB.value;
|
||||
}
|
||||
|
||||
friend Sub operator+(Sub aA, Sub aB) {
|
||||
return Sub(aA.value + aB.value);
|
||||
}
|
||||
friend Sub operator-(Sub aA, Sub aB) {
|
||||
return Sub(aA.value - aB.value);
|
||||
}
|
||||
friend Sub operator*(Sub aCoord, T aScale) {
|
||||
return Sub(aCoord.value * aScale);
|
||||
}
|
||||
friend Sub operator*(T aScale, Sub aCoord) {
|
||||
return Sub(aScale * aCoord.value);
|
||||
}
|
||||
friend Sub operator/(Sub aCoord, T aScale) {
|
||||
return Sub(aCoord.value / aScale);
|
||||
}
|
||||
// 'scale / coord' is intentionally omitted because it doesn't make sense.
|
||||
|
||||
Sub& operator+=(Sub aCoord) {
|
||||
value += aCoord.value;
|
||||
return *static_cast<Sub*>(this);
|
||||
}
|
||||
Sub& operator-=(Sub aCoord) {
|
||||
value -= aCoord.value;
|
||||
return *static_cast<Sub*>(this);
|
||||
}
|
||||
Sub& operator*=(T aScale) {
|
||||
value *= aScale;
|
||||
return *static_cast<Sub*>(this);
|
||||
}
|
||||
Sub& operator/=(T aScale) {
|
||||
value /= aScale;
|
||||
return *static_cast<Sub*>(this);
|
||||
}
|
||||
|
||||
// Since BaseCoord is implicitly convertible to its value type T, we need
|
||||
// mixed-type operator overloads to avoid ambiguities at mixed-type call
|
||||
// sites. As we transition more of our code to strongly-typed classes, we
|
||||
// may be able to remove some or all of these overloads.
|
||||
friend bool operator==(Sub aA, T aB) {
|
||||
return aA.value == aB;
|
||||
}
|
||||
friend bool operator==(T aA, Sub aB) {
|
||||
return aA == aB.value;
|
||||
}
|
||||
friend bool operator!=(Sub aA, T aB) {
|
||||
return aA.value != aB;
|
||||
}
|
||||
friend bool operator!=(T aA, Sub aB) {
|
||||
return aA != aB.value;
|
||||
}
|
||||
friend T operator+(Sub aA, T aB) {
|
||||
return aA.value + aB;
|
||||
}
|
||||
friend T operator+(T aA, Sub aB) {
|
||||
return aA + aB.value;
|
||||
}
|
||||
friend T operator-(Sub aA, T aB) {
|
||||
return aA.value - aB;
|
||||
}
|
||||
friend T operator-(T aA, Sub aB) {
|
||||
return aA - aB.value;
|
||||
}
|
||||
|
||||
Sub operator-() const {
|
||||
return Sub(-value);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* MOZILLA_GFX_BASECOORD_H_ */
|
||||
152
gfx/2d/BaseMargin.h
Normal file
152
gfx/2d/BaseMargin.h
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
/* -*- 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_GFX_BASEMARGIN_H_
|
||||
#define MOZILLA_GFX_BASEMARGIN_H_
|
||||
|
||||
#include <ostream>
|
||||
|
||||
#include "Types.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
/**
|
||||
* Sides represents a set of physical sides.
|
||||
*/
|
||||
struct Sides final {
|
||||
Sides() : mBits(0) {}
|
||||
explicit Sides(SideBits aSideBits)
|
||||
{
|
||||
MOZ_ASSERT((aSideBits & ~eSideBitsAll) == 0, "illegal side bits");
|
||||
mBits = aSideBits;
|
||||
}
|
||||
bool IsEmpty() const { return mBits == 0; }
|
||||
bool Top() const { return (mBits & eSideBitsTop) != 0; }
|
||||
bool Right() const { return (mBits & eSideBitsRight) != 0; }
|
||||
bool Bottom() const { return (mBits & eSideBitsBottom) != 0; }
|
||||
bool Left() const { return (mBits & eSideBitsLeft) != 0; }
|
||||
bool Contains(SideBits aSideBits) const
|
||||
{
|
||||
MOZ_ASSERT((aSideBits & ~eSideBitsAll) == 0, "illegal side bits");
|
||||
return (mBits & aSideBits) == aSideBits;
|
||||
}
|
||||
Sides operator|(Sides aOther) const
|
||||
{
|
||||
return Sides(SideBits(mBits | aOther.mBits));
|
||||
}
|
||||
Sides operator|(SideBits aSideBits) const
|
||||
{
|
||||
return *this | Sides(aSideBits);
|
||||
}
|
||||
Sides& operator|=(Sides aOther)
|
||||
{
|
||||
mBits |= aOther.mBits;
|
||||
return *this;
|
||||
}
|
||||
Sides& operator|=(SideBits aSideBits)
|
||||
{
|
||||
return *this |= Sides(aSideBits);
|
||||
}
|
||||
bool operator==(Sides aOther) const
|
||||
{
|
||||
return mBits == aOther.mBits;
|
||||
}
|
||||
bool operator!=(Sides aOther) const
|
||||
{
|
||||
return !(*this == aOther);
|
||||
}
|
||||
|
||||
private:
|
||||
uint8_t mBits;
|
||||
};
|
||||
|
||||
namespace gfx {
|
||||
|
||||
/**
|
||||
* Do not use this class directly. Subclass it, pass that subclass as the
|
||||
* Sub parameter, and only use that subclass.
|
||||
*/
|
||||
template <class T, class Sub>
|
||||
struct BaseMargin {
|
||||
typedef mozilla::Side SideT; // because we have a method named Side
|
||||
|
||||
// Do not change the layout of these members; the Side() methods below
|
||||
// depend on this order.
|
||||
T top, right, bottom, left;
|
||||
|
||||
// Constructors
|
||||
BaseMargin() : top(0), right(0), bottom(0), left(0) {}
|
||||
BaseMargin(T aTop, T aRight, T aBottom, T aLeft) :
|
||||
top(aTop), right(aRight), bottom(aBottom), left(aLeft) {}
|
||||
|
||||
void SizeTo(T aTop, T aRight, T aBottom, T aLeft)
|
||||
{
|
||||
top = aTop; right = aRight; bottom = aBottom; left = aLeft;
|
||||
}
|
||||
|
||||
T LeftRight() const { return left + right; }
|
||||
T TopBottom() const { return top + bottom; }
|
||||
|
||||
T& Side(SideT aSide) {
|
||||
// This is ugly!
|
||||
return *(&top + int(aSide));
|
||||
}
|
||||
T Side(SideT aSide) const {
|
||||
// This is ugly!
|
||||
return *(&top + int(aSide));
|
||||
}
|
||||
|
||||
void ApplySkipSides(Sides aSkipSides)
|
||||
{
|
||||
if (aSkipSides.Top()) {
|
||||
top = 0;
|
||||
}
|
||||
if (aSkipSides.Right()) {
|
||||
right = 0;
|
||||
}
|
||||
if (aSkipSides.Bottom()) {
|
||||
bottom = 0;
|
||||
}
|
||||
if (aSkipSides.Left()) {
|
||||
left = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Overloaded operators. Note that '=' isn't defined so we'll get the
|
||||
// compiler generated default assignment operator
|
||||
bool operator==(const Sub& aMargin) const {
|
||||
return top == aMargin.top && right == aMargin.right &&
|
||||
bottom == aMargin.bottom && left == aMargin.left;
|
||||
}
|
||||
bool operator!=(const Sub& aMargin) const {
|
||||
return !(*this == aMargin);
|
||||
}
|
||||
Sub operator+(const Sub& aMargin) const {
|
||||
return Sub(top + aMargin.top, right + aMargin.right,
|
||||
bottom + aMargin.bottom, left + aMargin.left);
|
||||
}
|
||||
Sub operator-(const Sub& aMargin) const {
|
||||
return Sub(top - aMargin.top, right - aMargin.right,
|
||||
bottom - aMargin.bottom, left - aMargin.left);
|
||||
}
|
||||
Sub& operator+=(const Sub& aMargin) {
|
||||
top += aMargin.top;
|
||||
right += aMargin.right;
|
||||
bottom += aMargin.bottom;
|
||||
left += aMargin.left;
|
||||
return *static_cast<Sub*>(this);
|
||||
}
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& aStream,
|
||||
const BaseMargin& aMargin) {
|
||||
return aStream << '(' << aMargin.top << ',' << aMargin.right << ','
|
||||
<< aMargin.bottom << ',' << aMargin.left << ')';
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* MOZILLA_GFX_BASEMARGIN_H_ */
|
||||
121
gfx/2d/BasePoint.h
Normal file
121
gfx/2d/BasePoint.h
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
/* -*- 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_GFX_BASEPOINT_H_
|
||||
#define MOZILLA_GFX_BASEPOINT_H_
|
||||
|
||||
#include <cmath>
|
||||
#include <ostream>
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/FloatingPoint.h"
|
||||
#include "mozilla/TypeTraits.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
/**
|
||||
* Do not use this class directly. Subclass it, pass that subclass as the
|
||||
* Sub parameter, and only use that subclass. This allows methods to safely
|
||||
* cast 'this' to 'Sub*'.
|
||||
*/
|
||||
template <class T, class Sub, class Coord = T>
|
||||
struct BasePoint {
|
||||
union {
|
||||
struct {
|
||||
T x, y;
|
||||
};
|
||||
T components[2];
|
||||
};
|
||||
|
||||
// Constructors
|
||||
constexpr BasePoint() : x(0), y(0) {}
|
||||
constexpr BasePoint(Coord aX, Coord aY) : x(aX), y(aY) {}
|
||||
|
||||
void MoveTo(T aX, T aY) { x = aX; y = aY; }
|
||||
void MoveBy(T aDx, T aDy) { x += aDx; y += aDy; }
|
||||
|
||||
// Note that '=' isn't defined so we'll get the
|
||||
// compiler generated default assignment operator
|
||||
|
||||
bool operator==(const Sub& aPoint) const {
|
||||
return x == aPoint.x && y == aPoint.y;
|
||||
}
|
||||
bool operator!=(const Sub& aPoint) const {
|
||||
return x != aPoint.x || y != aPoint.y;
|
||||
}
|
||||
|
||||
Sub operator+(const Sub& aPoint) const {
|
||||
return Sub(x + aPoint.x, y + aPoint.y);
|
||||
}
|
||||
Sub operator-(const Sub& aPoint) const {
|
||||
return Sub(x - aPoint.x, y - aPoint.y);
|
||||
}
|
||||
Sub& operator+=(const Sub& aPoint) {
|
||||
x += aPoint.x;
|
||||
y += aPoint.y;
|
||||
return *static_cast<Sub*>(this);
|
||||
}
|
||||
Sub& operator-=(const Sub& aPoint) {
|
||||
x -= aPoint.x;
|
||||
y -= aPoint.y;
|
||||
return *static_cast<Sub*>(this);
|
||||
}
|
||||
|
||||
Sub operator*(T aScale) const {
|
||||
return Sub(x * aScale, y * aScale);
|
||||
}
|
||||
Sub operator/(T aScale) const {
|
||||
return Sub(x / aScale, y / aScale);
|
||||
}
|
||||
|
||||
Sub operator-() const {
|
||||
return Sub(-x, -y);
|
||||
}
|
||||
|
||||
T DotProduct(const Sub& aPoint) const {
|
||||
return x * aPoint.x + y * aPoint.y;
|
||||
}
|
||||
|
||||
Coord Length() const {
|
||||
return hypot(x, y);
|
||||
}
|
||||
|
||||
T LengthSquare() const {
|
||||
return x * x + y * y;
|
||||
}
|
||||
|
||||
// Round() is *not* rounding to nearest integer if the values are negative.
|
||||
// They are always rounding as floor(n + 0.5).
|
||||
// See https://bugzilla.mozilla.org/show_bug.cgi?id=410748#c14
|
||||
Sub& Round() {
|
||||
x = Coord(floor(T(x) + T(0.5)));
|
||||
y = Coord(floor(T(y) + T(0.5)));
|
||||
return *static_cast<Sub*>(this);
|
||||
}
|
||||
|
||||
// "Finite" means not inf and not NaN
|
||||
bool IsFinite() const
|
||||
{
|
||||
typedef typename mozilla::Conditional<mozilla::IsSame<T, float>::value, float, double>::Type FloatType;
|
||||
return (mozilla::IsFinite(FloatType(x)) && mozilla::IsFinite(FloatType(y)));
|
||||
return true;
|
||||
}
|
||||
|
||||
void Clamp(T aMaxAbsValue)
|
||||
{
|
||||
x = std::max(std::min(x, aMaxAbsValue), -aMaxAbsValue);
|
||||
y = std::max(std::min(y, aMaxAbsValue), -aMaxAbsValue);
|
||||
}
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& stream, const BasePoint<T, Sub, Coord>& aPoint) {
|
||||
return stream << '(' << aPoint.x << ',' << aPoint.y << ')';
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* MOZILLA_GFX_BASEPOINT_H_ */
|
||||
122
gfx/2d/BasePoint3D.h
Normal file
122
gfx/2d/BasePoint3D.h
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
/* -*- 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_BASEPOINT3D_H_
|
||||
#define MOZILLA_BASEPOINT3D_H_
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
/**
|
||||
* Do not use this class directly. Subclass it, pass that subclass as the
|
||||
* Sub parameter, and only use that subclass. This allows methods to safely
|
||||
* cast 'this' to 'Sub*'.
|
||||
*/
|
||||
template <class T, class Sub>
|
||||
struct BasePoint3D {
|
||||
union {
|
||||
struct {
|
||||
T x, y, z;
|
||||
};
|
||||
T components[3];
|
||||
};
|
||||
|
||||
// Constructors
|
||||
BasePoint3D() : x(0), y(0), z(0) {}
|
||||
BasePoint3D(T aX, T aY, T aZ) : x(aX), y(aY), z(aZ) {}
|
||||
|
||||
void MoveTo(T aX, T aY, T aZ) { x = aX; y = aY; z = aZ; }
|
||||
void MoveBy(T aDx, T aDy, T aDz) { x += aDx; y += aDy; z += aDz; }
|
||||
|
||||
// Note that '=' isn't defined so we'll get the
|
||||
// compiler generated default assignment operator
|
||||
|
||||
T& operator[](int aIndex) {
|
||||
MOZ_ASSERT(aIndex >= 0 && aIndex <= 2);
|
||||
return *((&x)+aIndex);
|
||||
}
|
||||
|
||||
const T& operator[](int aIndex) const {
|
||||
MOZ_ASSERT(aIndex >= 0 && aIndex <= 2);
|
||||
return *((&x)+aIndex);
|
||||
}
|
||||
|
||||
bool operator==(const Sub& aPoint) const {
|
||||
return x == aPoint.x && y == aPoint.y && z == aPoint.z;
|
||||
}
|
||||
bool operator!=(const Sub& aPoint) const {
|
||||
return x != aPoint.x || y != aPoint.y || z != aPoint.z;
|
||||
}
|
||||
|
||||
Sub operator+(const Sub& aPoint) const {
|
||||
return Sub(x + aPoint.x, y + aPoint.y, z + aPoint.z);
|
||||
}
|
||||
Sub operator-(const Sub& aPoint) const {
|
||||
return Sub(x - aPoint.x, y - aPoint.y, z - aPoint.z);
|
||||
}
|
||||
Sub& operator+=(const Sub& aPoint) {
|
||||
x += aPoint.x;
|
||||
y += aPoint.y;
|
||||
z += aPoint.z;
|
||||
return *static_cast<Sub*>(this);
|
||||
}
|
||||
Sub& operator-=(const Sub& aPoint) {
|
||||
x -= aPoint.x;
|
||||
y -= aPoint.y;
|
||||
z -= aPoint.z;
|
||||
return *static_cast<Sub*>(this);
|
||||
}
|
||||
|
||||
Sub operator*(T aScale) const {
|
||||
return Sub(x * aScale, y * aScale, z * aScale);
|
||||
}
|
||||
Sub operator/(T aScale) const {
|
||||
return Sub(x / aScale, y / aScale, z / aScale);
|
||||
}
|
||||
|
||||
Sub& operator*=(T aScale) {
|
||||
x *= aScale;
|
||||
y *= aScale;
|
||||
z *= aScale;
|
||||
return *static_cast<Sub*>(this);
|
||||
}
|
||||
|
||||
Sub& operator/=(T aScale) {
|
||||
x /= aScale;
|
||||
y /= aScale;
|
||||
z /= aScale;
|
||||
return *static_cast<Sub*>(this);
|
||||
}
|
||||
|
||||
Sub operator-() const {
|
||||
return Sub(-x, -y, -z);
|
||||
}
|
||||
|
||||
Sub CrossProduct(const Sub& aPoint) const {
|
||||
return Sub(y * aPoint.z - aPoint.y * z,
|
||||
z * aPoint.x - aPoint.z * x,
|
||||
x * aPoint.y - aPoint.x * y);
|
||||
}
|
||||
|
||||
T DotProduct(const Sub& aPoint) const {
|
||||
return x * aPoint.x + y * aPoint.y + z * aPoint.z;
|
||||
}
|
||||
|
||||
T Length() const {
|
||||
return sqrt(x*x + y*y + z*z);
|
||||
}
|
||||
|
||||
// Invalid for points with distance from origin of 0.
|
||||
void Normalize() {
|
||||
*this /= Length();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* MOZILLA_BASEPOINT3D_H_ */
|
||||
131
gfx/2d/BasePoint4D.h
Normal file
131
gfx/2d/BasePoint4D.h
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
/* -*- 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_BASEPOINT4D_H_
|
||||
#define MOZILLA_BASEPOINT4D_H_
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
/**
|
||||
* Do not use this class directly. Subclass it, pass that subclass as the
|
||||
* Sub parameter, and only use that subclass. This allows methods to safely
|
||||
* cast 'this' to 'Sub*'.
|
||||
*/
|
||||
template <class T, class Sub>
|
||||
struct BasePoint4D {
|
||||
union {
|
||||
struct {
|
||||
T x, y, z, w;
|
||||
};
|
||||
T components[4];
|
||||
};
|
||||
|
||||
// Constructors
|
||||
BasePoint4D() : x(0), y(0), z(0), w(0) {}
|
||||
BasePoint4D(T aX, T aY, T aZ, T aW) : x(aX), y(aY), z(aZ), w(aW) {}
|
||||
|
||||
void MoveTo(T aX, T aY, T aZ, T aW) { x = aX; y = aY; z = aZ; w = aW; }
|
||||
void MoveBy(T aDx, T aDy, T aDz, T aDw) { x += aDx; y += aDy; z += aDz; w += aDw; }
|
||||
|
||||
// Note that '=' isn't defined so we'll get the
|
||||
// compiler generated default assignment operator
|
||||
|
||||
bool operator==(const Sub& aPoint) const {
|
||||
return x == aPoint.x && y == aPoint.y &&
|
||||
z == aPoint.z && w == aPoint.w;
|
||||
}
|
||||
bool operator!=(const Sub& aPoint) const {
|
||||
return x != aPoint.x || y != aPoint.y ||
|
||||
z != aPoint.z || w != aPoint.w;
|
||||
}
|
||||
|
||||
Sub operator+(const Sub& aPoint) const {
|
||||
return Sub(x + aPoint.x, y + aPoint.y, z + aPoint.z, w + aPoint.w);
|
||||
}
|
||||
Sub operator-(const Sub& aPoint) const {
|
||||
return Sub(x - aPoint.x, y - aPoint.y, z - aPoint.z, w - aPoint.w);
|
||||
}
|
||||
Sub& operator+=(const Sub& aPoint) {
|
||||
x += aPoint.x;
|
||||
y += aPoint.y;
|
||||
z += aPoint.z;
|
||||
w += aPoint.w;
|
||||
return *static_cast<Sub*>(this);
|
||||
}
|
||||
Sub& operator-=(const Sub& aPoint) {
|
||||
x -= aPoint.x;
|
||||
y -= aPoint.y;
|
||||
z -= aPoint.z;
|
||||
w -= aPoint.w;
|
||||
return *static_cast<Sub*>(this);
|
||||
}
|
||||
|
||||
Sub operator*(T aScale) const {
|
||||
return Sub(x * aScale, y * aScale, z * aScale, w * aScale);
|
||||
}
|
||||
Sub operator/(T aScale) const {
|
||||
return Sub(x / aScale, y / aScale, z / aScale, w / aScale);
|
||||
}
|
||||
|
||||
Sub& operator*=(T aScale) {
|
||||
x *= aScale;
|
||||
y *= aScale;
|
||||
z *= aScale;
|
||||
w *= aScale;
|
||||
return *static_cast<Sub*>(this);
|
||||
}
|
||||
|
||||
Sub& operator/=(T aScale) {
|
||||
x /= aScale;
|
||||
y /= aScale;
|
||||
z /= aScale;
|
||||
w /= aScale;
|
||||
return *static_cast<Sub*>(this);
|
||||
}
|
||||
|
||||
Sub operator-() const {
|
||||
return Sub(-x, -y, -z, -w);
|
||||
}
|
||||
|
||||
T& operator[](int aIndex) {
|
||||
MOZ_ASSERT(aIndex >= 0 && aIndex <= 3, "Invalid array index");
|
||||
return *((&x)+aIndex);
|
||||
}
|
||||
|
||||
const T& operator[](int aIndex) const {
|
||||
MOZ_ASSERT(aIndex >= 0 && aIndex <= 3, "Invalid array index");
|
||||
return *((&x)+aIndex);
|
||||
}
|
||||
|
||||
T DotProduct(const Sub& aPoint) const {
|
||||
return x * aPoint.x + y * aPoint.y + z * aPoint.z + w * aPoint.w;
|
||||
}
|
||||
|
||||
// Ignores the 4th component!
|
||||
Sub CrossProduct(const Sub& aPoint) const {
|
||||
return Sub(y * aPoint.z - aPoint.y * z,
|
||||
z * aPoint.x - aPoint.z * x,
|
||||
x * aPoint.y - aPoint.x * y,
|
||||
0);
|
||||
}
|
||||
|
||||
T Length() const {
|
||||
return sqrt(x*x + y*y + z*z + w*w);
|
||||
}
|
||||
|
||||
void Normalize() {
|
||||
*this /= Length();
|
||||
}
|
||||
|
||||
bool HasPositiveWCoord() { return w > 0; }
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* MOZILLA_BASEPOINT4D_H_ */
|
||||
586
gfx/2d/BaseRect.h
Normal file
586
gfx/2d/BaseRect.h
Normal file
|
|
@ -0,0 +1,586 @@
|
|||
/* -*- 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_GFX_BASERECT_H_
|
||||
#define MOZILLA_GFX_BASERECT_H_
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <ostream>
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/FloatingPoint.h"
|
||||
#include "mozilla/TypeTraits.h"
|
||||
#include "Types.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
/**
|
||||
* Rectangles have two interpretations: a set of (zero-size) points,
|
||||
* and a rectangular area of the plane. Most rectangle operations behave
|
||||
* the same no matter what interpretation is being used, but some operations
|
||||
* differ:
|
||||
* -- Equality tests behave differently. When a rectangle represents an area,
|
||||
* all zero-width and zero-height rectangles are equal to each other since they
|
||||
* represent the empty area. But when a rectangle represents a set of
|
||||
* mathematical points, zero-width and zero-height rectangles can be unequal.
|
||||
* -- The union operation can behave differently. When rectangles represent
|
||||
* areas, taking the union of a zero-width or zero-height rectangle with
|
||||
* another rectangle can just ignore the empty rectangle. But when rectangles
|
||||
* represent sets of mathematical points, we may need to extend the latter
|
||||
* rectangle to include the points of a zero-width or zero-height rectangle.
|
||||
*
|
||||
* To ensure that these interpretations are explicitly disambiguated, we
|
||||
* deny access to the == and != operators and require use of IsEqualEdges and
|
||||
* IsEqualInterior instead. Similarly we provide separate Union and UnionEdges
|
||||
* methods.
|
||||
*
|
||||
* Do not use this class directly. Subclass it, pass that subclass as the
|
||||
* Sub parameter, and only use that subclass.
|
||||
*/
|
||||
template <class T, class Sub, class Point, class SizeT, class MarginT>
|
||||
struct BaseRect {
|
||||
T x, y, width, height;
|
||||
|
||||
// Constructors
|
||||
BaseRect() : x(0), y(0), width(0), height(0) {}
|
||||
BaseRect(const Point& aOrigin, const SizeT &aSize) :
|
||||
x(aOrigin.x), y(aOrigin.y), width(aSize.width), height(aSize.height)
|
||||
{
|
||||
}
|
||||
BaseRect(T aX, T aY, T aWidth, T aHeight) :
|
||||
x(aX), y(aY), width(aWidth), height(aHeight)
|
||||
{
|
||||
}
|
||||
|
||||
// Emptiness. An empty rect is one that has no area, i.e. its height or width
|
||||
// is <= 0
|
||||
bool IsEmpty() const { return height <= 0 || width <= 0; }
|
||||
void SetEmpty() { width = height = 0; }
|
||||
|
||||
// "Finite" means not inf and not NaN
|
||||
bool IsFinite() const
|
||||
{
|
||||
typedef typename mozilla::Conditional<mozilla::IsSame<T, float>::value, float, double>::Type FloatType;
|
||||
return (mozilla::IsFinite(FloatType(x)) &&
|
||||
mozilla::IsFinite(FloatType(y)) &&
|
||||
mozilla::IsFinite(FloatType(width)) &&
|
||||
mozilla::IsFinite(FloatType(height)));
|
||||
}
|
||||
|
||||
// Returns true if this rectangle contains the interior of aRect. Always
|
||||
// returns true if aRect is empty, and always returns false is aRect is
|
||||
// nonempty but this rect is empty.
|
||||
bool Contains(const Sub& aRect) const
|
||||
{
|
||||
return aRect.IsEmpty() ||
|
||||
(x <= aRect.x && aRect.XMost() <= XMost() &&
|
||||
y <= aRect.y && aRect.YMost() <= YMost());
|
||||
}
|
||||
// Returns true if this rectangle contains the point. Points are considered
|
||||
// in the rectangle if they are on the left or top edge, but outside if they
|
||||
// are on the right or bottom edge.
|
||||
bool Contains(T aX, T aY) const
|
||||
{
|
||||
return x <= aX && aX < XMost() &&
|
||||
y <= aY && aY < YMost();
|
||||
}
|
||||
// Returns true if this rectangle contains the point. Points are considered
|
||||
// in the rectangle if they are on the left or top edge, but outside if they
|
||||
// are on the right or bottom edge.
|
||||
bool Contains(const Point& aPoint) const { return Contains(aPoint.x, aPoint.y); }
|
||||
|
||||
// Intersection. Returns TRUE if the receiver's area has non-empty
|
||||
// intersection with aRect's area, and FALSE otherwise.
|
||||
// Always returns false if aRect is empty or 'this' is empty.
|
||||
bool Intersects(const Sub& aRect) const
|
||||
{
|
||||
return !IsEmpty() && !aRect.IsEmpty() &&
|
||||
x < aRect.XMost() && aRect.x < XMost() &&
|
||||
y < aRect.YMost() && aRect.y < YMost();
|
||||
}
|
||||
// Returns the rectangle containing the intersection of the points
|
||||
// (including edges) of *this and aRect. If there are no points in that
|
||||
// intersection, returns an empty rectangle with x/y set to the std::max of the x/y
|
||||
// of *this and aRect.
|
||||
MOZ_MUST_USE Sub Intersect(const Sub& aRect) const
|
||||
{
|
||||
Sub result;
|
||||
result.x = std::max<T>(x, aRect.x);
|
||||
result.y = std::max<T>(y, aRect.y);
|
||||
result.width = std::min<T>(x - result.x + width, aRect.x - result.x + aRect.width);
|
||||
result.height = std::min<T>(y - result.y + height, aRect.y - result.y + aRect.height);
|
||||
if (result.width < 0 || result.height < 0) {
|
||||
result.SizeTo(0, 0);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
// Sets *this to be the rectangle containing the intersection of the points
|
||||
// (including edges) of *this and aRect. If there are no points in that
|
||||
// intersection, sets *this to be an empty rectangle with x/y set to the std::max
|
||||
// of the x/y of *this and aRect.
|
||||
//
|
||||
// 'this' can be the same object as either aRect1 or aRect2
|
||||
bool IntersectRect(const Sub& aRect1, const Sub& aRect2)
|
||||
{
|
||||
*static_cast<Sub*>(this) = aRect1.Intersect(aRect2);
|
||||
return !IsEmpty();
|
||||
}
|
||||
|
||||
// Returns the smallest rectangle that contains both the area of both
|
||||
// this and aRect2.
|
||||
// Thus, empty input rectangles are ignored.
|
||||
// If both rectangles are empty, returns this.
|
||||
// WARNING! This is not safe against overflow, prefer using SafeUnion instead
|
||||
// when dealing with int-based rects.
|
||||
MOZ_MUST_USE Sub Union(const Sub& aRect) const
|
||||
{
|
||||
if (IsEmpty()) {
|
||||
return aRect;
|
||||
} else if (aRect.IsEmpty()) {
|
||||
return *static_cast<const Sub*>(this);
|
||||
} else {
|
||||
return UnionEdges(aRect);
|
||||
}
|
||||
}
|
||||
// Returns the smallest rectangle that contains both the points (including
|
||||
// edges) of both aRect1 and aRect2.
|
||||
// Thus, empty input rectangles are allowed to affect the result.
|
||||
// WARNING! This is not safe against overflow, prefer using SafeUnionEdges
|
||||
// instead when dealing with int-based rects.
|
||||
MOZ_MUST_USE Sub UnionEdges(const Sub& aRect) const
|
||||
{
|
||||
Sub result;
|
||||
result.x = std::min(x, aRect.x);
|
||||
result.y = std::min(y, aRect.y);
|
||||
result.width = std::max(XMost(), aRect.XMost()) - result.x;
|
||||
result.height = std::max(YMost(), aRect.YMost()) - result.y;
|
||||
return result;
|
||||
}
|
||||
// Computes the smallest rectangle that contains both the area of both
|
||||
// aRect1 and aRect2, and fills 'this' with the result.
|
||||
// Thus, empty input rectangles are ignored.
|
||||
// If both rectangles are empty, sets 'this' to aRect2.
|
||||
//
|
||||
// 'this' can be the same object as either aRect1 or aRect2
|
||||
void UnionRect(const Sub& aRect1, const Sub& aRect2)
|
||||
{
|
||||
*static_cast<Sub*>(this) = aRect1.Union(aRect2);
|
||||
}
|
||||
|
||||
// Computes the smallest rectangle that contains both the points (including
|
||||
// edges) of both aRect1 and aRect2.
|
||||
// Thus, empty input rectangles are allowed to affect the result.
|
||||
//
|
||||
// 'this' can be the same object as either aRect1 or aRect2
|
||||
void UnionRectEdges(const Sub& aRect1, const Sub& aRect2)
|
||||
{
|
||||
*static_cast<Sub*>(this) = aRect1.UnionEdges(aRect2);
|
||||
}
|
||||
|
||||
// Expands the rect to include the point
|
||||
void ExpandToEnclose(const Point& aPoint)
|
||||
{
|
||||
if (aPoint.x < x) {
|
||||
width = XMost() - aPoint.x;
|
||||
x = aPoint.x;
|
||||
} else if (aPoint.x > XMost()) {
|
||||
width = aPoint.x - x;
|
||||
}
|
||||
if (aPoint.y < y) {
|
||||
height = YMost() - aPoint.y;
|
||||
y = aPoint.y;
|
||||
} else if (aPoint.y > YMost()) {
|
||||
height = aPoint.y - y;
|
||||
}
|
||||
}
|
||||
|
||||
void SetRect(T aX, T aY, T aWidth, T aHeight)
|
||||
{
|
||||
x = aX; y = aY; width = aWidth; height = aHeight;
|
||||
}
|
||||
void SetRect(const Point& aPt, const SizeT& aSize)
|
||||
{
|
||||
SetRect(aPt.x, aPt.y, aSize.width, aSize.height);
|
||||
}
|
||||
void MoveTo(T aX, T aY) { x = aX; y = aY; }
|
||||
void MoveTo(const Point& aPoint) { x = aPoint.x; y = aPoint.y; }
|
||||
void MoveBy(T aDx, T aDy) { x += aDx; y += aDy; }
|
||||
void MoveBy(const Point& aPoint) { x += aPoint.x; y += aPoint.y; }
|
||||
void SizeTo(T aWidth, T aHeight) { width = aWidth; height = aHeight; }
|
||||
void SizeTo(const SizeT& aSize) { width = aSize.width; height = aSize.height; }
|
||||
|
||||
void Inflate(T aD) { Inflate(aD, aD); }
|
||||
void Inflate(T aDx, T aDy)
|
||||
{
|
||||
x -= aDx;
|
||||
y -= aDy;
|
||||
width += 2 * aDx;
|
||||
height += 2 * aDy;
|
||||
}
|
||||
void Inflate(const MarginT& aMargin)
|
||||
{
|
||||
x -= aMargin.left;
|
||||
y -= aMargin.top;
|
||||
width += aMargin.LeftRight();
|
||||
height += aMargin.TopBottom();
|
||||
}
|
||||
void Inflate(const SizeT& aSize) { Inflate(aSize.width, aSize.height); }
|
||||
|
||||
void Deflate(T aD) { Deflate(aD, aD); }
|
||||
void Deflate(T aDx, T aDy)
|
||||
{
|
||||
x += aDx;
|
||||
y += aDy;
|
||||
width = std::max(T(0), width - 2 * aDx);
|
||||
height = std::max(T(0), height - 2 * aDy);
|
||||
}
|
||||
void Deflate(const MarginT& aMargin)
|
||||
{
|
||||
x += aMargin.left;
|
||||
y += aMargin.top;
|
||||
width = std::max(T(0), width - aMargin.LeftRight());
|
||||
height = std::max(T(0), height - aMargin.TopBottom());
|
||||
}
|
||||
void Deflate(const SizeT& aSize) { Deflate(aSize.width, aSize.height); }
|
||||
|
||||
// Return true if the rectangles contain the same set of points, including
|
||||
// points on the edges.
|
||||
// Use when we care about the exact x/y/width/height values being
|
||||
// equal (i.e. we care about differences in empty rectangles).
|
||||
bool IsEqualEdges(const Sub& aRect) const
|
||||
{
|
||||
return x == aRect.x && y == aRect.y &&
|
||||
width == aRect.width && height == aRect.height;
|
||||
}
|
||||
// Return true if the rectangles contain the same area of the plane.
|
||||
// Use when we do not care about differences in empty rectangles.
|
||||
bool IsEqualInterior(const Sub& aRect) const
|
||||
{
|
||||
return IsEqualEdges(aRect) || (IsEmpty() && aRect.IsEmpty());
|
||||
}
|
||||
|
||||
friend Sub operator+(Sub aSub, const Point& aPoint)
|
||||
{
|
||||
aSub += aPoint;
|
||||
return aSub;
|
||||
}
|
||||
friend Sub operator-(Sub aSub, const Point& aPoint)
|
||||
{
|
||||
aSub -= aPoint;
|
||||
return aSub;
|
||||
}
|
||||
friend Sub operator+(Sub aSub, const SizeT& aSize)
|
||||
{
|
||||
aSub += aSize;
|
||||
return aSub;
|
||||
}
|
||||
friend Sub operator-(Sub aSub, const SizeT& aSize)
|
||||
{
|
||||
aSub -= aSize;
|
||||
return aSub;
|
||||
}
|
||||
Sub& operator+=(const Point& aPoint)
|
||||
{
|
||||
MoveBy(aPoint);
|
||||
return *static_cast<Sub*>(this);
|
||||
}
|
||||
Sub& operator-=(const Point& aPoint)
|
||||
{
|
||||
MoveBy(-aPoint);
|
||||
return *static_cast<Sub*>(this);
|
||||
}
|
||||
Sub& operator+=(const SizeT& aSize)
|
||||
{
|
||||
width += aSize.width;
|
||||
height += aSize.height;
|
||||
return *static_cast<Sub*>(this);
|
||||
}
|
||||
Sub& operator-=(const SizeT& aSize)
|
||||
{
|
||||
width -= aSize.width;
|
||||
height -= aSize.height;
|
||||
return *static_cast<Sub*>(this);
|
||||
}
|
||||
// Find difference as a Margin
|
||||
MarginT operator-(const Sub& aRect) const
|
||||
{
|
||||
return MarginT(aRect.y - y,
|
||||
XMost() - aRect.XMost(),
|
||||
YMost() - aRect.YMost(),
|
||||
aRect.x - x);
|
||||
}
|
||||
|
||||
// Helpers for accessing the vertices
|
||||
Point TopLeft() const { return Point(x, y); }
|
||||
Point TopRight() const { return Point(XMost(), y); }
|
||||
Point BottomLeft() const { return Point(x, YMost()); }
|
||||
Point BottomRight() const { return Point(XMost(), YMost()); }
|
||||
Point AtCorner(int aCorner) const {
|
||||
switch (aCorner) {
|
||||
case RectCorner::TopLeft: return TopLeft();
|
||||
case RectCorner::TopRight: return TopRight();
|
||||
case RectCorner::BottomRight: return BottomRight();
|
||||
case RectCorner::BottomLeft: return BottomLeft();
|
||||
}
|
||||
MOZ_CRASH("GFX: Incomplete switch");
|
||||
}
|
||||
Point CCWCorner(mozilla::Side side) const {
|
||||
switch (side) {
|
||||
case NS_SIDE_TOP: return TopLeft();
|
||||
case NS_SIDE_RIGHT: return TopRight();
|
||||
case NS_SIDE_BOTTOM: return BottomRight();
|
||||
case NS_SIDE_LEFT: return BottomLeft();
|
||||
}
|
||||
MOZ_CRASH("GFX: Incomplete switch");
|
||||
}
|
||||
Point CWCorner(mozilla::Side side) const {
|
||||
switch (side) {
|
||||
case NS_SIDE_TOP: return TopRight();
|
||||
case NS_SIDE_RIGHT: return BottomRight();
|
||||
case NS_SIDE_BOTTOM: return BottomLeft();
|
||||
case NS_SIDE_LEFT: return TopLeft();
|
||||
}
|
||||
MOZ_CRASH("GFX: Incomplete switch");
|
||||
}
|
||||
Point Center() const { return Point(x, y) + Point(width, height)/2; }
|
||||
SizeT Size() const { return SizeT(width, height); }
|
||||
|
||||
T Area() const { return width * height; }
|
||||
|
||||
// Helper methods for computing the extents
|
||||
T X() const { return x; }
|
||||
T Y() const { return y; }
|
||||
T Width() const { return width; }
|
||||
T Height() const { return height; }
|
||||
T XMost() const { return x + width; }
|
||||
T YMost() const { return y + height; }
|
||||
|
||||
// Get the coordinate of the edge on the given side.
|
||||
T Edge(mozilla::Side aSide) const
|
||||
{
|
||||
switch (aSide) {
|
||||
case NS_SIDE_TOP: return Y();
|
||||
case NS_SIDE_RIGHT: return XMost();
|
||||
case NS_SIDE_BOTTOM: return YMost();
|
||||
case NS_SIDE_LEFT: return X();
|
||||
}
|
||||
MOZ_CRASH("GFX: Incomplete switch");
|
||||
}
|
||||
|
||||
// Moves one edge of the rect without moving the opposite edge.
|
||||
void SetLeftEdge(T aX) {
|
||||
MOZ_ASSERT(aX <= XMost());
|
||||
width = XMost() - aX;
|
||||
x = aX;
|
||||
}
|
||||
void SetRightEdge(T aXMost) {
|
||||
MOZ_ASSERT(aXMost >= x);
|
||||
width = aXMost - x;
|
||||
}
|
||||
void SetTopEdge(T aY) {
|
||||
MOZ_ASSERT(aY <= YMost());
|
||||
height = YMost() - aY;
|
||||
y = aY;
|
||||
}
|
||||
void SetBottomEdge(T aYMost) {
|
||||
MOZ_ASSERT(aYMost >= y);
|
||||
height = aYMost - y;
|
||||
}
|
||||
|
||||
// Round the rectangle edges to integer coordinates, such that the rounded
|
||||
// rectangle has the same set of pixel centers as the original rectangle.
|
||||
// Edges at offset 0.5 round up.
|
||||
// Suitable for most places where integral device coordinates
|
||||
// are needed, but note that any translation should be applied first to
|
||||
// avoid pixel rounding errors.
|
||||
// Note that this is *not* rounding to nearest integer if the values are negative.
|
||||
// They are always rounding as floor(n + 0.5).
|
||||
// See https://bugzilla.mozilla.org/show_bug.cgi?id=410748#c14
|
||||
// If you need similar method which is using NS_round(), you should create
|
||||
// new |RoundAwayFromZero()| method.
|
||||
void Round()
|
||||
{
|
||||
T x0 = static_cast<T>(floor(T(X()) + 0.5));
|
||||
T y0 = static_cast<T>(floor(T(Y()) + 0.5));
|
||||
T x1 = static_cast<T>(floor(T(XMost()) + 0.5));
|
||||
T y1 = static_cast<T>(floor(T(YMost()) + 0.5));
|
||||
|
||||
x = x0;
|
||||
y = y0;
|
||||
|
||||
width = x1 - x0;
|
||||
height = y1 - y0;
|
||||
}
|
||||
|
||||
// Snap the rectangle edges to integer coordinates, such that the
|
||||
// original rectangle contains the resulting rectangle.
|
||||
void RoundIn()
|
||||
{
|
||||
T x0 = static_cast<T>(ceil(T(X())));
|
||||
T y0 = static_cast<T>(ceil(T(Y())));
|
||||
T x1 = static_cast<T>(floor(T(XMost())));
|
||||
T y1 = static_cast<T>(floor(T(YMost())));
|
||||
|
||||
x = x0;
|
||||
y = y0;
|
||||
|
||||
width = x1 - x0;
|
||||
height = y1 - y0;
|
||||
}
|
||||
|
||||
// Snap the rectangle edges to integer coordinates, such that the
|
||||
// resulting rectangle contains the original rectangle.
|
||||
void RoundOut()
|
||||
{
|
||||
T x0 = static_cast<T>(floor(T(X())));
|
||||
T y0 = static_cast<T>(floor(T(Y())));
|
||||
T x1 = static_cast<T>(ceil(T(XMost())));
|
||||
T y1 = static_cast<T>(ceil(T(YMost())));
|
||||
|
||||
x = x0;
|
||||
y = y0;
|
||||
|
||||
width = x1 - x0;
|
||||
height = y1 - y0;
|
||||
}
|
||||
|
||||
// Scale 'this' by aScale without doing any rounding.
|
||||
void Scale(T aScale) { Scale(aScale, aScale); }
|
||||
// Scale 'this' by aXScale and aYScale, without doing any rounding.
|
||||
void Scale(T aXScale, T aYScale)
|
||||
{
|
||||
T right = XMost() * aXScale;
|
||||
T bottom = YMost() * aYScale;
|
||||
x = x * aXScale;
|
||||
y = y * aYScale;
|
||||
width = right - x;
|
||||
height = bottom - y;
|
||||
}
|
||||
// Scale 'this' by aScale, converting coordinates to integers so that the result is
|
||||
// the smallest integer-coordinate rectangle containing the unrounded result.
|
||||
// Note: this can turn an empty rectangle into a non-empty rectangle
|
||||
void ScaleRoundOut(double aScale) { ScaleRoundOut(aScale, aScale); }
|
||||
// Scale 'this' by aXScale and aYScale, converting coordinates to integers so
|
||||
// that the result is the smallest integer-coordinate rectangle containing the
|
||||
// unrounded result.
|
||||
// Note: this can turn an empty rectangle into a non-empty rectangle
|
||||
void ScaleRoundOut(double aXScale, double aYScale)
|
||||
{
|
||||
T right = static_cast<T>(ceil(double(XMost()) * aXScale));
|
||||
T bottom = static_cast<T>(ceil(double(YMost()) * aYScale));
|
||||
x = static_cast<T>(floor(double(x) * aXScale));
|
||||
y = static_cast<T>(floor(double(y) * aYScale));
|
||||
width = right - x;
|
||||
height = bottom - y;
|
||||
}
|
||||
// Scale 'this' by aScale, converting coordinates to integers so that the result is
|
||||
// the largest integer-coordinate rectangle contained by the unrounded result.
|
||||
void ScaleRoundIn(double aScale) { ScaleRoundIn(aScale, aScale); }
|
||||
// Scale 'this' by aXScale and aYScale, converting coordinates to integers so
|
||||
// that the result is the largest integer-coordinate rectangle contained by the
|
||||
// unrounded result.
|
||||
void ScaleRoundIn(double aXScale, double aYScale)
|
||||
{
|
||||
T right = static_cast<T>(floor(double(XMost()) * aXScale));
|
||||
T bottom = static_cast<T>(floor(double(YMost()) * aYScale));
|
||||
x = static_cast<T>(ceil(double(x) * aXScale));
|
||||
y = static_cast<T>(ceil(double(y) * aYScale));
|
||||
width = std::max<T>(0, right - x);
|
||||
height = std::max<T>(0, bottom - y);
|
||||
}
|
||||
// Scale 'this' by 1/aScale, converting coordinates to integers so that the result is
|
||||
// the smallest integer-coordinate rectangle containing the unrounded result.
|
||||
// Note: this can turn an empty rectangle into a non-empty rectangle
|
||||
void ScaleInverseRoundOut(double aScale) { ScaleInverseRoundOut(aScale, aScale); }
|
||||
// Scale 'this' by 1/aXScale and 1/aYScale, converting coordinates to integers so
|
||||
// that the result is the smallest integer-coordinate rectangle containing the
|
||||
// unrounded result.
|
||||
// Note: this can turn an empty rectangle into a non-empty rectangle
|
||||
void ScaleInverseRoundOut(double aXScale, double aYScale)
|
||||
{
|
||||
T right = static_cast<T>(ceil(double(XMost()) / aXScale));
|
||||
T bottom = static_cast<T>(ceil(double(YMost()) / aYScale));
|
||||
x = static_cast<T>(floor(double(x) / aXScale));
|
||||
y = static_cast<T>(floor(double(y) / aYScale));
|
||||
width = right - x;
|
||||
height = bottom - y;
|
||||
}
|
||||
// Scale 'this' by 1/aScale, converting coordinates to integers so that the result is
|
||||
// the largest integer-coordinate rectangle contained by the unrounded result.
|
||||
void ScaleInverseRoundIn(double aScale) { ScaleInverseRoundIn(aScale, aScale); }
|
||||
// Scale 'this' by 1/aXScale and 1/aYScale, converting coordinates to integers so
|
||||
// that the result is the largest integer-coordinate rectangle contained by the
|
||||
// unrounded result.
|
||||
void ScaleInverseRoundIn(double aXScale, double aYScale)
|
||||
{
|
||||
T right = static_cast<T>(floor(double(XMost()) / aXScale));
|
||||
T bottom = static_cast<T>(floor(double(YMost()) / aYScale));
|
||||
x = static_cast<T>(ceil(double(x) / aXScale));
|
||||
y = static_cast<T>(ceil(double(y) / aYScale));
|
||||
width = std::max<T>(0, right - x);
|
||||
height = std::max<T>(0, bottom - y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clamp aPoint to this rectangle. It is allowed to end up on any
|
||||
* edge of the rectangle.
|
||||
*/
|
||||
MOZ_MUST_USE Point ClampPoint(const Point& aPoint) const
|
||||
{
|
||||
return Point(std::max(x, std::min(XMost(), aPoint.x)),
|
||||
std::max(y, std::min(YMost(), aPoint.y)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Translate this rectangle to be inside aRect. If it doesn't fit inside
|
||||
* aRect then the dimensions that don't fit will be shrunk so that they
|
||||
* do fit. The resulting rect is returned.
|
||||
*/
|
||||
MOZ_MUST_USE Sub MoveInsideAndClamp(const Sub& aRect) const
|
||||
{
|
||||
Sub rect(std::max(aRect.x, x),
|
||||
std::max(aRect.y, y),
|
||||
std::min(aRect.width, width),
|
||||
std::min(aRect.height, height));
|
||||
rect.x = std::min(rect.XMost(), aRect.XMost()) - rect.width;
|
||||
rect.y = std::min(rect.YMost(), aRect.YMost()) - rect.height;
|
||||
return rect;
|
||||
}
|
||||
|
||||
// Returns the largest rectangle that can be represented with 32-bit
|
||||
// signed integers, centered around a point at 0,0. As BaseRect's represent
|
||||
// the dimensions as a top-left point with a width and height, the width
|
||||
// and height will be the largest positive 32-bit value. The top-left
|
||||
// position coordinate is divided by two to center the rectangle around a
|
||||
// point at 0,0.
|
||||
static Sub MaxIntRect()
|
||||
{
|
||||
return Sub(
|
||||
static_cast<T>(-std::numeric_limits<int32_t>::max() * 0.5),
|
||||
static_cast<T>(-std::numeric_limits<int32_t>::max() * 0.5),
|
||||
static_cast<T>(std::numeric_limits<int32_t>::max()),
|
||||
static_cast<T>(std::numeric_limits<int32_t>::max())
|
||||
);
|
||||
};
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& stream,
|
||||
const BaseRect<T, Sub, Point, SizeT, MarginT>& aRect) {
|
||||
return stream << '(' << aRect.x << ',' << aRect.y << ','
|
||||
<< aRect.width << ',' << aRect.height << ')';
|
||||
}
|
||||
|
||||
private:
|
||||
// Do not use the default operator== or operator!= !
|
||||
// Use IsEqualEdges or IsEqualInterior explicitly.
|
||||
bool operator==(const Sub& aRect) const { return false; }
|
||||
bool operator!=(const Sub& aRect) const { return false; }
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* MOZILLA_GFX_BASERECT_H_ */
|
||||
100
gfx/2d/BaseSize.h
Normal file
100
gfx/2d/BaseSize.h
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
/* -*- 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_GFX_BASESIZE_H_
|
||||
#define MOZILLA_GFX_BASESIZE_H_
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
/**
|
||||
* Do not use this class directly. Subclass it, pass that subclass as the
|
||||
* Sub parameter, and only use that subclass. This allows methods to safely
|
||||
* cast 'this' to 'Sub*'.
|
||||
*/
|
||||
template <class T, class Sub>
|
||||
struct BaseSize {
|
||||
union {
|
||||
struct {
|
||||
T width, height;
|
||||
};
|
||||
T components[2];
|
||||
};
|
||||
|
||||
// Constructors
|
||||
constexpr BaseSize() : width(0), height(0) {}
|
||||
constexpr BaseSize(T aWidth, T aHeight) : width(aWidth), height(aHeight) {}
|
||||
|
||||
void SizeTo(T aWidth, T aHeight) { width = aWidth; height = aHeight; }
|
||||
|
||||
bool IsEmpty() const {
|
||||
return width <= 0 || height <= 0;
|
||||
}
|
||||
|
||||
bool IsSquare() const {
|
||||
return width == height;
|
||||
}
|
||||
|
||||
// Note that '=' isn't defined so we'll get the
|
||||
// compiler generated default assignment operator
|
||||
|
||||
bool operator==(const Sub& aSize) const {
|
||||
return width == aSize.width && height == aSize.height;
|
||||
}
|
||||
bool operator!=(const Sub& aSize) const {
|
||||
return width != aSize.width || height != aSize.height;
|
||||
}
|
||||
bool operator<=(const Sub& aSize) const {
|
||||
return width <= aSize.width && height <= aSize.height;
|
||||
}
|
||||
bool operator<(const Sub& aSize) const {
|
||||
return *this <= aSize && *this != aSize;
|
||||
}
|
||||
|
||||
Sub operator+(const Sub& aSize) const {
|
||||
return Sub(width + aSize.width, height + aSize.height);
|
||||
}
|
||||
Sub operator-(const Sub& aSize) const {
|
||||
return Sub(width - aSize.width, height - aSize.height);
|
||||
}
|
||||
Sub& operator+=(const Sub& aSize) {
|
||||
width += aSize.width;
|
||||
height += aSize.height;
|
||||
return *static_cast<Sub*>(this);
|
||||
}
|
||||
Sub& operator-=(const Sub& aSize) {
|
||||
width -= aSize.width;
|
||||
height -= aSize.height;
|
||||
return *static_cast<Sub*>(this);
|
||||
}
|
||||
|
||||
Sub operator*(T aScale) const {
|
||||
return Sub(width * aScale, height * aScale);
|
||||
}
|
||||
Sub operator/(T aScale) const {
|
||||
return Sub(width / aScale, height / aScale);
|
||||
}
|
||||
friend Sub operator*(T aScale, const Sub& aSize) {
|
||||
return Sub(aScale * aSize.width, aScale * aSize.height);
|
||||
}
|
||||
void Scale(T aXScale, T aYScale) {
|
||||
width *= aXScale;
|
||||
height *= aYScale;
|
||||
}
|
||||
|
||||
Sub operator*(const Sub& aSize) const {
|
||||
return Sub(width * aSize.width, height * aSize.height);
|
||||
}
|
||||
Sub operator/(const Sub& aSize) const {
|
||||
return Sub(width / aSize.width, height / aSize.height);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* MOZILLA_GFX_BASESIZE_H_ */
|
||||
339
gfx/2d/BezierUtils.cpp
Normal file
339
gfx/2d/BezierUtils.cpp
Normal file
|
|
@ -0,0 +1,339 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
// vim:cindent:ts=2:et:sw=2:
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "BezierUtils.h"
|
||||
|
||||
#include "PathHelpers.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
Point
|
||||
GetBezierPoint(const Bezier& aBezier, Float t)
|
||||
{
|
||||
Float s = 1.0f - t;
|
||||
|
||||
return Point(
|
||||
aBezier.mPoints[0].x * s * s * s +
|
||||
3.0f * aBezier.mPoints[1].x * t * s * s +
|
||||
3.0f * aBezier.mPoints[2].x * t * t * s +
|
||||
aBezier.mPoints[3].x * t * t * t,
|
||||
aBezier.mPoints[0].y * s * s * s +
|
||||
3.0f * aBezier.mPoints[1].y * t * s * s +
|
||||
3.0f * aBezier.mPoints[2].y * t * t * s +
|
||||
aBezier.mPoints[3].y * t * t * t
|
||||
);
|
||||
}
|
||||
|
||||
Point
|
||||
GetBezierDifferential(const Bezier& aBezier, Float t)
|
||||
{
|
||||
// Return P'(t).
|
||||
|
||||
Float s = 1.0f - t;
|
||||
|
||||
return Point(
|
||||
-3.0f * ((aBezier.mPoints[0].x - aBezier.mPoints[1].x) * s * s +
|
||||
2.0f * (aBezier.mPoints[1].x - aBezier.mPoints[2].x) * t * s +
|
||||
(aBezier.mPoints[2].x - aBezier.mPoints[3].x) * t * t),
|
||||
-3.0f * ((aBezier.mPoints[0].y - aBezier.mPoints[1].y) * s * s +
|
||||
2.0f * (aBezier.mPoints[1].y - aBezier.mPoints[2].y) * t * s+
|
||||
(aBezier.mPoints[2].y - aBezier.mPoints[3].y) * t * t)
|
||||
);
|
||||
}
|
||||
|
||||
Point
|
||||
GetBezierDifferential2(const Bezier& aBezier, Float t)
|
||||
{
|
||||
// Return P''(t).
|
||||
|
||||
Float s = 1.0f - t;
|
||||
|
||||
return Point(
|
||||
6.0f * ((aBezier.mPoints[0].x - aBezier.mPoints[1].x) * s -
|
||||
(aBezier.mPoints[1].x - aBezier.mPoints[2].x) * (s - t) -
|
||||
(aBezier.mPoints[2].x - aBezier.mPoints[3].x) * t),
|
||||
6.0f * ((aBezier.mPoints[0].y - aBezier.mPoints[1].y) * s -
|
||||
(aBezier.mPoints[1].y - aBezier.mPoints[2].y) * (s - t) -
|
||||
(aBezier.mPoints[2].y - aBezier.mPoints[3].y) * t)
|
||||
);
|
||||
}
|
||||
|
||||
Float
|
||||
GetBezierLength(const Bezier& aBezier, Float a, Float b)
|
||||
{
|
||||
if (a < 0.5f && b > 0.5f) {
|
||||
// To increase the accuracy, split into two parts.
|
||||
return GetBezierLength(aBezier, a, 0.5f) +
|
||||
GetBezierLength(aBezier, 0.5f, b);
|
||||
}
|
||||
|
||||
// Calculate length of simple bezier curve with Simpson's rule.
|
||||
// _
|
||||
// / b
|
||||
// length = | |P'(x)| dx
|
||||
// _/ a
|
||||
//
|
||||
// b - a a + b
|
||||
// = ----- [ |P'(a)| + 4 |P'(-----)| + |P'(b)| ]
|
||||
// 6 2
|
||||
|
||||
Float fa = GetBezierDifferential(aBezier, a).Length();
|
||||
Float fab = GetBezierDifferential(aBezier, (a + b) / 2.0f).Length();
|
||||
Float fb = GetBezierDifferential(aBezier, b).Length();
|
||||
|
||||
return (b - a) / 6.0f * (fa + 4.0f * fab + fb);
|
||||
}
|
||||
|
||||
static void
|
||||
SplitBezierA(Bezier* aSubBezier, const Bezier& aBezier, Float t)
|
||||
{
|
||||
// Split bezier curve into [0,t] and [t,1] parts, and return [0,t] part.
|
||||
|
||||
Float s = 1.0f - t;
|
||||
|
||||
Point tmp1;
|
||||
Point tmp2;
|
||||
|
||||
aSubBezier->mPoints[0] = aBezier.mPoints[0];
|
||||
|
||||
aSubBezier->mPoints[1] = aBezier.mPoints[0] * s + aBezier.mPoints[1] * t;
|
||||
tmp1 = aBezier.mPoints[1] * s + aBezier.mPoints[2] * t;
|
||||
tmp2 = aBezier.mPoints[2] * s + aBezier.mPoints[3] * t;
|
||||
|
||||
aSubBezier->mPoints[2] = aSubBezier->mPoints[1] * s + tmp1 * t;
|
||||
tmp1 = tmp1 * s + tmp2 * t;
|
||||
|
||||
aSubBezier->mPoints[3] = aSubBezier->mPoints[2] * s + tmp1 * t;
|
||||
}
|
||||
|
||||
static void
|
||||
SplitBezierB(Bezier* aSubBezier, const Bezier& aBezier, Float t)
|
||||
{
|
||||
// Split bezier curve into [0,t] and [t,1] parts, and return [t,1] part.
|
||||
|
||||
Float s = 1.0f - t;
|
||||
|
||||
Point tmp1;
|
||||
Point tmp2;
|
||||
|
||||
aSubBezier->mPoints[3] = aBezier.mPoints[3];
|
||||
|
||||
aSubBezier->mPoints[2] = aBezier.mPoints[2] * s + aBezier.mPoints[3] * t;
|
||||
tmp1 = aBezier.mPoints[1] * s + aBezier.mPoints[2] * t;
|
||||
tmp2 = aBezier.mPoints[0] * s + aBezier.mPoints[1] * t;
|
||||
|
||||
aSubBezier->mPoints[1] = tmp1 * s + aSubBezier->mPoints[2] * t;
|
||||
tmp1 = tmp2 * s + tmp1 * t;
|
||||
|
||||
aSubBezier->mPoints[0] = tmp1 * s + aSubBezier->mPoints[1] * t;
|
||||
}
|
||||
|
||||
void
|
||||
GetSubBezier(Bezier* aSubBezier, const Bezier& aBezier, Float t1, Float t2)
|
||||
{
|
||||
Bezier tmp;
|
||||
SplitBezierB(&tmp, aBezier, t1);
|
||||
|
||||
Float range = 1.0f - t1;
|
||||
if (range == 0.0f) {
|
||||
*aSubBezier = tmp;
|
||||
} else {
|
||||
SplitBezierA(aSubBezier, tmp, (t2 - t1) / range);
|
||||
}
|
||||
}
|
||||
|
||||
static Point
|
||||
BisectBezierNearestPoint(const Bezier& aBezier, const Point& aTarget,
|
||||
Float* aT)
|
||||
{
|
||||
// Find a nearest point on bezier curve with Binary search.
|
||||
// Called from FindBezierNearestPoint.
|
||||
|
||||
Float lower = 0.0f;
|
||||
Float upper = 1.0f;
|
||||
Float t;
|
||||
|
||||
Point P, lastP;
|
||||
const size_t MAX_LOOP = 32;
|
||||
const Float DIST_MARGIN = 0.1f;
|
||||
const Float DIST_MARGIN_SQUARE = DIST_MARGIN * DIST_MARGIN;
|
||||
const Float DIFF = 0.0001f;
|
||||
for (size_t i = 0; i < MAX_LOOP; i++) {
|
||||
t = (upper + lower) / 2.0f;
|
||||
P = GetBezierPoint(aBezier, t);
|
||||
|
||||
// Check if it converged.
|
||||
if (i > 0 && (lastP - P).LengthSquare() < DIST_MARGIN_SQUARE) {
|
||||
break;
|
||||
}
|
||||
|
||||
Float distSquare = (P - aTarget).LengthSquare();
|
||||
if ((GetBezierPoint(aBezier, t + DIFF) - aTarget).LengthSquare() <
|
||||
distSquare) {
|
||||
lower = t;
|
||||
} else if ((GetBezierPoint(aBezier, t - DIFF) - aTarget).LengthSquare() <
|
||||
distSquare) {
|
||||
upper = t;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
lastP = P;
|
||||
}
|
||||
|
||||
if (aT) {
|
||||
*aT = t;
|
||||
}
|
||||
|
||||
return P;
|
||||
}
|
||||
|
||||
Point
|
||||
FindBezierNearestPoint(const Bezier& aBezier, const Point& aTarget,
|
||||
Float aInitialT, Float* aT)
|
||||
{
|
||||
// Find a nearest point on bezier curve with Newton's method.
|
||||
// It converges within 4 iterations in most cases.
|
||||
//
|
||||
// f(t_n)
|
||||
// t_{n+1} = t_n - ---------
|
||||
// f'(t_n)
|
||||
//
|
||||
// d 2
|
||||
// f(t) = ---- | P(t) - aTarget |
|
||||
// dt
|
||||
|
||||
Float t = aInitialT;
|
||||
Point P;
|
||||
Point lastP = GetBezierPoint(aBezier, t);
|
||||
|
||||
const size_t MAX_LOOP = 4;
|
||||
const Float DIST_MARGIN = 0.1f;
|
||||
const Float DIST_MARGIN_SQUARE = DIST_MARGIN * DIST_MARGIN;
|
||||
for (size_t i = 0; i <= MAX_LOOP; i++) {
|
||||
Point dP = GetBezierDifferential(aBezier, t);
|
||||
Point ddP = GetBezierDifferential2(aBezier, t);
|
||||
Float f = 2.0f * (lastP.DotProduct(dP) - aTarget.DotProduct(dP));
|
||||
Float df = 2.0f * (dP.DotProduct(dP) + lastP.DotProduct(ddP) -
|
||||
aTarget.DotProduct(ddP));
|
||||
t = t - f / df;
|
||||
P = GetBezierPoint(aBezier, t);
|
||||
if ((P - lastP).LengthSquare() < DIST_MARGIN_SQUARE) {
|
||||
break;
|
||||
}
|
||||
lastP = P;
|
||||
|
||||
if (i == MAX_LOOP) {
|
||||
// If aInitialT is too bad, it won't converge in a few iterations,
|
||||
// fallback to binary search.
|
||||
return BisectBezierNearestPoint(aBezier, aTarget, aT);
|
||||
}
|
||||
}
|
||||
|
||||
if (aT) {
|
||||
*aT = t;
|
||||
}
|
||||
|
||||
return P;
|
||||
}
|
||||
|
||||
void
|
||||
GetBezierPointsForCorner(Bezier* aBezier, mozilla::css::Corner aCorner,
|
||||
const Point& aCornerPoint, const Size& aCornerSize)
|
||||
{
|
||||
// Calculate bezier control points for elliptic arc.
|
||||
|
||||
const Float signsList[4][2] = {
|
||||
{ +1.0f, +1.0f },
|
||||
{ -1.0f, +1.0f },
|
||||
{ -1.0f, -1.0f },
|
||||
{ +1.0f, -1.0f }
|
||||
};
|
||||
const Float (& signs)[2] = signsList[aCorner];
|
||||
|
||||
aBezier->mPoints[0] = aCornerPoint;
|
||||
aBezier->mPoints[0].x += signs[0] * aCornerSize.width;
|
||||
|
||||
aBezier->mPoints[1] = aBezier->mPoints[0];
|
||||
aBezier->mPoints[1].x -= signs[0] * aCornerSize.width * kKappaFactor;
|
||||
|
||||
aBezier->mPoints[3] = aCornerPoint;
|
||||
aBezier->mPoints[3].y += signs[1] * aCornerSize.height;
|
||||
|
||||
aBezier->mPoints[2] = aBezier->mPoints[3];
|
||||
aBezier->mPoints[2].y -= signs[1] * aCornerSize.height * kKappaFactor;
|
||||
}
|
||||
|
||||
Float
|
||||
GetQuarterEllipticArcLength(Float a, Float b)
|
||||
{
|
||||
// Calculate the approximate length of a quarter elliptic arc formed by radii
|
||||
// (a, b), by Ramanujan's approximation of the perimeter p of an ellipse.
|
||||
// _ _
|
||||
// | 2 |
|
||||
// | 3 * (a - b) |
|
||||
// p = PI | (a + b) + ------------------------------------------- |
|
||||
// | 2 2 |
|
||||
// |_ 10 * (a + b) + sqrt(a + 14 * a * b + b ) _|
|
||||
//
|
||||
// _ _
|
||||
// | 2 |
|
||||
// | 3 * (a - b) |
|
||||
// = PI | (a + b) + -------------------------------------------------- |
|
||||
// | 2 2 |
|
||||
// |_ 10 * (a + b) + sqrt(4 * (a + b) - 3 * (a - b) ) _|
|
||||
//
|
||||
// _ _
|
||||
// | 2 |
|
||||
// | 3 * S |
|
||||
// = PI | A + -------------------------------------- |
|
||||
// | 2 2 |
|
||||
// |_ 10 * A + sqrt(4 * A - 3 * S ) _|
|
||||
//
|
||||
// where A = a + b, S = a - b
|
||||
|
||||
Float A = a + b, S = a - b;
|
||||
Float A2 = A * A, S2 = S * S;
|
||||
Float p = M_PI * (A + 3.0f * S2 / (10.0f * A + sqrt(4.0f * A2 - 3.0f * S2)));
|
||||
return p / 4.0f;
|
||||
}
|
||||
|
||||
Float
|
||||
CalculateDistanceToEllipticArc(const Point& P, const Point& normal,
|
||||
const Point& origin, Float width, Float height)
|
||||
{
|
||||
// Solve following equations with n and return smaller n.
|
||||
//
|
||||
// / (x, y) = P + n * normal
|
||||
// |
|
||||
// < _ _ 2 _ _ 2
|
||||
// | | x - origin.x | | y - origin.y |
|
||||
// | | ------------ | + | ------------ | = 1
|
||||
// \ |_ width _| |_ height _|
|
||||
|
||||
Float a = (P.x - origin.x) / width;
|
||||
Float b = normal.x / width;
|
||||
Float c = (P.y - origin.y) / height;
|
||||
Float d = normal.y / height;
|
||||
|
||||
Float A = b * b + d * d;
|
||||
Float B = a * b + c * d;
|
||||
Float C = a * a + c * c - 1;
|
||||
|
||||
Float S = sqrt(B * B - A * C);
|
||||
|
||||
Float n1 = (- B + S) / A;
|
||||
Float n2 = (- B - S) / A;
|
||||
|
||||
MOZ_ASSERT(n1 >= 0);
|
||||
MOZ_ASSERT(n2 >= 0);
|
||||
|
||||
return n1 < n2 ? n1 : n2;
|
||||
}
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
||||
185
gfx/2d/BezierUtils.h
Normal file
185
gfx/2d/BezierUtils.h
Normal file
|
|
@ -0,0 +1,185 @@
|
|||
/* -*- 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_BezierUtils_h_
|
||||
#define mozilla_BezierUtils_h_
|
||||
|
||||
#include "mozilla/gfx/2D.h"
|
||||
#include "gfxRect.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
// Control points for bezier curve
|
||||
//
|
||||
// mPoints[2]
|
||||
// +-----___---+ mPoints[3]
|
||||
// __--
|
||||
// _--
|
||||
// /
|
||||
// /
|
||||
// mPoints[1] + |
|
||||
// | |
|
||||
// ||
|
||||
// ||
|
||||
// |
|
||||
// |
|
||||
// |
|
||||
// |
|
||||
// mPoints[0] +
|
||||
struct Bezier {
|
||||
Point mPoints[4];
|
||||
};
|
||||
|
||||
// Calculate a point or it's differential of a bezier curve formed by
|
||||
// aBezier and parameter t.
|
||||
//
|
||||
// GetBezierPoint = P(t)
|
||||
// GetBezierDifferential = P'(t)
|
||||
// GetBezierDifferential2 = P''(t)
|
||||
//
|
||||
// mPoints[2]
|
||||
// +-----___---+ mPoints[3]
|
||||
// __-- P(1)
|
||||
// _--
|
||||
// +
|
||||
// / P(t)
|
||||
// mPoints[1] + |
|
||||
// | |
|
||||
// ||
|
||||
// ||
|
||||
// |
|
||||
// |
|
||||
// |
|
||||
// |
|
||||
// mPoints[0] + P(0)
|
||||
Point GetBezierPoint(const Bezier& aBezier, Float t);
|
||||
Point GetBezierDifferential(const Bezier& aBezier, Float t);
|
||||
Point GetBezierDifferential2(const Bezier& aBezier, Float t);
|
||||
|
||||
// Calculate length of a simple bezier curve formed by aBezier and range [a, b].
|
||||
Float GetBezierLength(const Bezier& aBezier, Float a, Float b);
|
||||
|
||||
// Split bezier curve formed by aBezier into [0,t1], [t1,t2], [t2,1] parts, and
|
||||
// stores control points for [t1,t2] to aSubBezier.
|
||||
//
|
||||
// ___---+
|
||||
// __+- P(1)
|
||||
// _-- P(t2)
|
||||
// -
|
||||
// / <-- aSubBezier
|
||||
// |
|
||||
// |
|
||||
// +
|
||||
// | P(t1)
|
||||
// |
|
||||
// |
|
||||
// |
|
||||
// |
|
||||
// + P(0)
|
||||
void GetSubBezier(Bezier* aSubBezier, const Bezier& aBezier,
|
||||
Float t1, Float t2);
|
||||
|
||||
// Find a nearest point on bezier curve formed by aBezier to a point aTarget.
|
||||
// aInitialT is a hint to find the parameter t for the nearest point.
|
||||
// If aT is non-null, parameter for the nearest point is stored to *aT.
|
||||
// This function expects a bezier curve to be an approximation of elliptic arc.
|
||||
// Otherwise it will return wrong point.
|
||||
//
|
||||
// aTarget
|
||||
// + ___---+
|
||||
// __--
|
||||
// _--
|
||||
// +
|
||||
// / nearest point = P(t = *aT)
|
||||
// |
|
||||
// |
|
||||
// |
|
||||
// + P(aInitialT)
|
||||
// |
|
||||
// |
|
||||
// |
|
||||
// |
|
||||
// +
|
||||
Point FindBezierNearestPoint(const Bezier& aBezier, const Point& aTarget,
|
||||
Float aInitialT, Float* aT=nullptr);
|
||||
|
||||
// Calculate control points for a bezier curve that is an approximation of
|
||||
// an elliptic arc.
|
||||
//
|
||||
// aCornerSize.width
|
||||
// |<----------------->|
|
||||
// | |
|
||||
// aCornerPoint| mPoints[2] |
|
||||
// -------------+-------+-----___---+ mPoints[3]
|
||||
// ^ | __--
|
||||
// | | _--
|
||||
// | | -
|
||||
// | | /
|
||||
// aCornerSize.height | mPoints[1] + |
|
||||
// | | |
|
||||
// | ||
|
||||
// | ||
|
||||
// | |
|
||||
// | |
|
||||
// | |
|
||||
// v mPoints[0] |
|
||||
// -------------+
|
||||
void GetBezierPointsForCorner(Bezier* aBezier, mozilla::css::Corner aCorner,
|
||||
const Point& aCornerPoint,
|
||||
const Size& aCornerSize);
|
||||
|
||||
// Calculate the approximate length of a quarter elliptic arc formed by radii
|
||||
// (a, b).
|
||||
//
|
||||
// a
|
||||
// |<----------------->|
|
||||
// | |
|
||||
// ---+-------------___---+
|
||||
// ^ | __--
|
||||
// | | _--
|
||||
// | | -
|
||||
// | | /
|
||||
// b | | |
|
||||
// | | |
|
||||
// | ||
|
||||
// | ||
|
||||
// | |
|
||||
// | |
|
||||
// | |
|
||||
// v |
|
||||
// ---+
|
||||
Float GetQuarterEllipticArcLength(Float a, Float b);
|
||||
|
||||
// Calculate the distance between an elliptic arc formed by (origin, width,
|
||||
// height), and a point P, along a line formed by |P + n * normal|.
|
||||
// P should be outside of the ellipse, and the line should cross with the
|
||||
// ellipse twice at n > 0 points.
|
||||
//
|
||||
// width
|
||||
// |<----------------->|
|
||||
// origin | |
|
||||
// -----------+-------------___---+
|
||||
// ^ normal | __--
|
||||
// | P +->__ | _--
|
||||
// | --__ -
|
||||
// | | --+
|
||||
// height | | |
|
||||
// | | |
|
||||
// | ||
|
||||
// | ||
|
||||
// | |
|
||||
// | |
|
||||
// | |
|
||||
// v |
|
||||
// -----------+
|
||||
Float CalculateDistanceToEllipticArc(const Point& P, const Point& normal,
|
||||
const Point& origin,
|
||||
Float width, Float height);
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* mozilla_BezierUtils_h_ */
|
||||
80
gfx/2d/BigEndianInts.h
Normal file
80
gfx/2d/BigEndianInts.h
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* 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_BigEndianInts_h
|
||||
#define mozilla_BigEndianInts_h
|
||||
|
||||
#include "mozilla/EndianUtils.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
struct BigEndianUint16
|
||||
{
|
||||
#ifdef __SUNPRO_CC
|
||||
BigEndianUint16& operator=(const uint16_t aValue)
|
||||
{
|
||||
value = NativeEndian::swapToBigEndian(aValue);
|
||||
return *this;
|
||||
}
|
||||
#else
|
||||
MOZ_IMPLICIT BigEndianUint16(const uint16_t aValue)
|
||||
{
|
||||
value = NativeEndian::swapToBigEndian(aValue);
|
||||
}
|
||||
#endif
|
||||
|
||||
operator uint16_t() const
|
||||
{
|
||||
return NativeEndian::swapFromBigEndian(value);
|
||||
}
|
||||
|
||||
friend inline bool
|
||||
operator==(const BigEndianUint16& lhs, const BigEndianUint16& rhs)
|
||||
{
|
||||
return lhs.value == rhs.value;
|
||||
}
|
||||
|
||||
friend inline bool
|
||||
operator!=(const BigEndianUint16& lhs, const BigEndianUint16& rhs)
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
private:
|
||||
uint16_t value;
|
||||
};
|
||||
|
||||
struct BigEndianUint32
|
||||
{
|
||||
#ifdef __SUNPRO_CC
|
||||
BigEndianUint32& operator=(const uint32_t aValue)
|
||||
{
|
||||
value = NativeEndian::swapToBigEndian(aValue);
|
||||
return *this;
|
||||
}
|
||||
#else
|
||||
MOZ_IMPLICIT BigEndianUint32(const uint32_t aValue)
|
||||
{
|
||||
value = NativeEndian::swapToBigEndian(aValue);
|
||||
}
|
||||
#endif
|
||||
|
||||
operator uint32_t() const
|
||||
{
|
||||
return NativeEndian::swapFromBigEndian(value);
|
||||
}
|
||||
|
||||
private:
|
||||
uint32_t value;
|
||||
};
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
} // mozilla
|
||||
|
||||
#endif // mozilla_BigEndianInts_h
|
||||
770
gfx/2d/Blur.cpp
Normal file
770
gfx/2d/Blur.cpp
Normal file
|
|
@ -0,0 +1,770 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "Blur.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "mozilla/CheckedInt.h"
|
||||
|
||||
#include "2D.h"
|
||||
#include "DataSurfaceHelpers.h"
|
||||
#include "Tools.h"
|
||||
|
||||
#ifdef BUILD_ARM_NEON
|
||||
#include "mozilla/arm.h"
|
||||
#endif
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
/**
|
||||
* Box blur involves looking at one pixel, and setting its value to the average
|
||||
* of its neighbouring pixels.
|
||||
* @param aInput The input buffer.
|
||||
* @param aOutput The output buffer.
|
||||
* @param aLeftLobe The number of pixels to blend on the left.
|
||||
* @param aRightLobe The number of pixels to blend on the right.
|
||||
* @param aWidth The number of columns in the buffers.
|
||||
* @param aRows The number of rows in the buffers.
|
||||
* @param aSkipRect An area to skip blurring in.
|
||||
* XXX shouldn't we pass stride in separately here?
|
||||
*/
|
||||
static void
|
||||
BoxBlurHorizontal(unsigned char* aInput,
|
||||
unsigned char* aOutput,
|
||||
int32_t aLeftLobe,
|
||||
int32_t aRightLobe,
|
||||
int32_t aWidth,
|
||||
int32_t aRows,
|
||||
const IntRect& aSkipRect)
|
||||
{
|
||||
MOZ_ASSERT(aWidth > 0);
|
||||
|
||||
int32_t boxSize = aLeftLobe + aRightLobe + 1;
|
||||
bool skipRectCoversWholeRow = 0 >= aSkipRect.x &&
|
||||
aWidth <= aSkipRect.XMost();
|
||||
if (boxSize == 1) {
|
||||
memcpy(aOutput, aInput, aWidth*aRows);
|
||||
return;
|
||||
}
|
||||
uint32_t reciprocal = uint32_t((uint64_t(1) << 32) / boxSize);
|
||||
|
||||
for (int32_t y = 0; y < aRows; y++) {
|
||||
// Check whether the skip rect intersects this row. If the skip
|
||||
// rect covers the whole surface in this row, we can avoid
|
||||
// this row entirely (and any others along the skip rect).
|
||||
bool inSkipRectY = y >= aSkipRect.y &&
|
||||
y < aSkipRect.YMost();
|
||||
if (inSkipRectY && skipRectCoversWholeRow) {
|
||||
y = aSkipRect.YMost() - 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32_t alphaSum = 0;
|
||||
for (int32_t i = 0; i < boxSize; i++) {
|
||||
int32_t pos = i - aLeftLobe;
|
||||
// See assertion above; if aWidth is zero, then we would have no
|
||||
// valid position to clamp to.
|
||||
pos = max(pos, 0);
|
||||
pos = min(pos, aWidth - 1);
|
||||
alphaSum += aInput[aWidth * y + pos];
|
||||
}
|
||||
for (int32_t x = 0; x < aWidth; x++) {
|
||||
// Check whether we are within the skip rect. If so, go
|
||||
// to the next point outside the skip rect.
|
||||
if (inSkipRectY && x >= aSkipRect.x &&
|
||||
x < aSkipRect.XMost()) {
|
||||
x = aSkipRect.XMost();
|
||||
if (x >= aWidth)
|
||||
break;
|
||||
|
||||
// Recalculate the neighbouring alpha values for
|
||||
// our new point on the surface.
|
||||
alphaSum = 0;
|
||||
for (int32_t i = 0; i < boxSize; i++) {
|
||||
int32_t pos = x + i - aLeftLobe;
|
||||
// See assertion above; if aWidth is zero, then we would have no
|
||||
// valid position to clamp to.
|
||||
pos = max(pos, 0);
|
||||
pos = min(pos, aWidth - 1);
|
||||
alphaSum += aInput[aWidth * y + pos];
|
||||
}
|
||||
}
|
||||
int32_t tmp = x - aLeftLobe;
|
||||
int32_t last = max(tmp, 0);
|
||||
int32_t next = min(tmp + boxSize, aWidth - 1);
|
||||
|
||||
aOutput[aWidth * y + x] = (uint64_t(alphaSum) * reciprocal) >> 32;
|
||||
|
||||
alphaSum += aInput[aWidth * y + next] -
|
||||
aInput[aWidth * y + last];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Identical to BoxBlurHorizontal, except it blurs top and bottom instead of
|
||||
* left and right.
|
||||
* XXX shouldn't we pass stride in separately here?
|
||||
*/
|
||||
static void
|
||||
BoxBlurVertical(unsigned char* aInput,
|
||||
unsigned char* aOutput,
|
||||
int32_t aTopLobe,
|
||||
int32_t aBottomLobe,
|
||||
int32_t aWidth,
|
||||
int32_t aRows,
|
||||
const IntRect& aSkipRect)
|
||||
{
|
||||
MOZ_ASSERT(aRows > 0);
|
||||
|
||||
int32_t boxSize = aTopLobe + aBottomLobe + 1;
|
||||
bool skipRectCoversWholeColumn = 0 >= aSkipRect.y &&
|
||||
aRows <= aSkipRect.YMost();
|
||||
if (boxSize == 1) {
|
||||
memcpy(aOutput, aInput, aWidth*aRows);
|
||||
return;
|
||||
}
|
||||
uint32_t reciprocal = uint32_t((uint64_t(1) << 32) / boxSize);
|
||||
|
||||
for (int32_t x = 0; x < aWidth; x++) {
|
||||
bool inSkipRectX = x >= aSkipRect.x &&
|
||||
x < aSkipRect.XMost();
|
||||
if (inSkipRectX && skipRectCoversWholeColumn) {
|
||||
x = aSkipRect.XMost() - 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32_t alphaSum = 0;
|
||||
for (int32_t i = 0; i < boxSize; i++) {
|
||||
int32_t pos = i - aTopLobe;
|
||||
// See assertion above; if aRows is zero, then we would have no
|
||||
// valid position to clamp to.
|
||||
pos = max(pos, 0);
|
||||
pos = min(pos, aRows - 1);
|
||||
alphaSum += aInput[aWidth * pos + x];
|
||||
}
|
||||
for (int32_t y = 0; y < aRows; y++) {
|
||||
if (inSkipRectX && y >= aSkipRect.y &&
|
||||
y < aSkipRect.YMost()) {
|
||||
y = aSkipRect.YMost();
|
||||
if (y >= aRows)
|
||||
break;
|
||||
|
||||
alphaSum = 0;
|
||||
for (int32_t i = 0; i < boxSize; i++) {
|
||||
int32_t pos = y + i - aTopLobe;
|
||||
// See assertion above; if aRows is zero, then we would have no
|
||||
// valid position to clamp to.
|
||||
pos = max(pos, 0);
|
||||
pos = min(pos, aRows - 1);
|
||||
alphaSum += aInput[aWidth * pos + x];
|
||||
}
|
||||
}
|
||||
int32_t tmp = y - aTopLobe;
|
||||
int32_t last = max(tmp, 0);
|
||||
int32_t next = min(tmp + boxSize, aRows - 1);
|
||||
|
||||
aOutput[aWidth * y + x] = (uint64_t(alphaSum) * reciprocal) >> 32;
|
||||
|
||||
alphaSum += aInput[aWidth * next + x] -
|
||||
aInput[aWidth * last + x];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ComputeLobes(int32_t aRadius, int32_t aLobes[3][2])
|
||||
{
|
||||
int32_t major, minor, final;
|
||||
|
||||
/* See http://www.w3.org/TR/SVG/filters.html#feGaussianBlur for
|
||||
* some notes about approximating the Gaussian blur with box-blurs.
|
||||
* The comments below are in the terminology of that page.
|
||||
*/
|
||||
int32_t z = aRadius / 3;
|
||||
switch (aRadius % 3) {
|
||||
case 0:
|
||||
// aRadius = z*3; choose d = 2*z + 1
|
||||
major = minor = final = z;
|
||||
break;
|
||||
case 1:
|
||||
// aRadius = z*3 + 1
|
||||
// This is a tricky case since there is no value of d which will
|
||||
// yield a radius of exactly aRadius. If d is odd, i.e. d=2*k + 1
|
||||
// for some integer k, then the radius will be 3*k. If d is even,
|
||||
// i.e. d=2*k, then the radius will be 3*k - 1.
|
||||
// So we have to choose values that don't match the standard
|
||||
// algorithm.
|
||||
major = z + 1;
|
||||
minor = final = z;
|
||||
break;
|
||||
case 2:
|
||||
// aRadius = z*3 + 2; choose d = 2*z + 2
|
||||
major = final = z + 1;
|
||||
minor = z;
|
||||
break;
|
||||
default:
|
||||
// Mathematical impossibility!
|
||||
MOZ_ASSERT(false);
|
||||
major = minor = final = 0;
|
||||
}
|
||||
MOZ_ASSERT(major + minor + final == aRadius);
|
||||
|
||||
aLobes[0][0] = major;
|
||||
aLobes[0][1] = minor;
|
||||
aLobes[1][0] = minor;
|
||||
aLobes[1][1] = major;
|
||||
aLobes[2][0] = final;
|
||||
aLobes[2][1] = final;
|
||||
}
|
||||
|
||||
static void
|
||||
SpreadHorizontal(unsigned char* aInput,
|
||||
unsigned char* aOutput,
|
||||
int32_t aRadius,
|
||||
int32_t aWidth,
|
||||
int32_t aRows,
|
||||
int32_t aStride,
|
||||
const IntRect& aSkipRect)
|
||||
{
|
||||
if (aRadius == 0) {
|
||||
memcpy(aOutput, aInput, aStride * aRows);
|
||||
return;
|
||||
}
|
||||
|
||||
bool skipRectCoversWholeRow = 0 >= aSkipRect.x &&
|
||||
aWidth <= aSkipRect.XMost();
|
||||
for (int32_t y = 0; y < aRows; y++) {
|
||||
// Check whether the skip rect intersects this row. If the skip
|
||||
// rect covers the whole surface in this row, we can avoid
|
||||
// this row entirely (and any others along the skip rect).
|
||||
bool inSkipRectY = y >= aSkipRect.y &&
|
||||
y < aSkipRect.YMost();
|
||||
if (inSkipRectY && skipRectCoversWholeRow) {
|
||||
y = aSkipRect.YMost() - 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int32_t x = 0; x < aWidth; x++) {
|
||||
// Check whether we are within the skip rect. If so, go
|
||||
// to the next point outside the skip rect.
|
||||
if (inSkipRectY && x >= aSkipRect.x &&
|
||||
x < aSkipRect.XMost()) {
|
||||
x = aSkipRect.XMost();
|
||||
if (x >= aWidth)
|
||||
break;
|
||||
}
|
||||
|
||||
int32_t sMin = max(x - aRadius, 0);
|
||||
int32_t sMax = min(x + aRadius, aWidth - 1);
|
||||
int32_t v = 0;
|
||||
for (int32_t s = sMin; s <= sMax; ++s) {
|
||||
v = max<int32_t>(v, aInput[aStride * y + s]);
|
||||
}
|
||||
aOutput[aStride * y + x] = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
SpreadVertical(unsigned char* aInput,
|
||||
unsigned char* aOutput,
|
||||
int32_t aRadius,
|
||||
int32_t aWidth,
|
||||
int32_t aRows,
|
||||
int32_t aStride,
|
||||
const IntRect& aSkipRect)
|
||||
{
|
||||
if (aRadius == 0) {
|
||||
memcpy(aOutput, aInput, aStride * aRows);
|
||||
return;
|
||||
}
|
||||
|
||||
bool skipRectCoversWholeColumn = 0 >= aSkipRect.y &&
|
||||
aRows <= aSkipRect.YMost();
|
||||
for (int32_t x = 0; x < aWidth; x++) {
|
||||
bool inSkipRectX = x >= aSkipRect.x &&
|
||||
x < aSkipRect.XMost();
|
||||
if (inSkipRectX && skipRectCoversWholeColumn) {
|
||||
x = aSkipRect.XMost() - 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int32_t y = 0; y < aRows; y++) {
|
||||
// Check whether we are within the skip rect. If so, go
|
||||
// to the next point outside the skip rect.
|
||||
if (inSkipRectX && y >= aSkipRect.y &&
|
||||
y < aSkipRect.YMost()) {
|
||||
y = aSkipRect.YMost();
|
||||
if (y >= aRows)
|
||||
break;
|
||||
}
|
||||
|
||||
int32_t sMin = max(y - aRadius, 0);
|
||||
int32_t sMax = min(y + aRadius, aRows - 1);
|
||||
int32_t v = 0;
|
||||
for (int32_t s = sMin; s <= sMax; ++s) {
|
||||
v = max<int32_t>(v, aInput[aStride * s + x]);
|
||||
}
|
||||
aOutput[aStride * y + x] = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CheckedInt<int32_t>
|
||||
AlphaBoxBlur::RoundUpToMultipleOf4(int32_t aVal)
|
||||
{
|
||||
CheckedInt<int32_t> val(aVal);
|
||||
|
||||
val += 3;
|
||||
val /= 4;
|
||||
val *= 4;
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
AlphaBoxBlur::AlphaBoxBlur(const Rect& aRect,
|
||||
const IntSize& aSpreadRadius,
|
||||
const IntSize& aBlurRadius,
|
||||
const Rect* aDirtyRect,
|
||||
const Rect* aSkipRect)
|
||||
: mSpreadRadius(aSpreadRadius),
|
||||
mBlurRadius(aBlurRadius),
|
||||
mSurfaceAllocationSize(0)
|
||||
{
|
||||
Rect rect(aRect);
|
||||
rect.Inflate(Size(aBlurRadius + aSpreadRadius));
|
||||
rect.RoundOut();
|
||||
|
||||
if (aDirtyRect) {
|
||||
// If we get passed a dirty rect from layout, we can minimize the
|
||||
// shadow size and make painting faster.
|
||||
mHasDirtyRect = true;
|
||||
mDirtyRect = *aDirtyRect;
|
||||
Rect requiredBlurArea = mDirtyRect.Intersect(rect);
|
||||
requiredBlurArea.Inflate(Size(aBlurRadius + aSpreadRadius));
|
||||
rect = requiredBlurArea.Intersect(rect);
|
||||
} else {
|
||||
mHasDirtyRect = false;
|
||||
}
|
||||
|
||||
mRect = IntRect(int32_t(rect.x), int32_t(rect.y),
|
||||
int32_t(rect.width), int32_t(rect.height));
|
||||
if (mRect.IsEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (aSkipRect) {
|
||||
// If we get passed a skip rect, we can lower the amount of
|
||||
// blurring/spreading we need to do. We convert it to IntRect to avoid
|
||||
// expensive int<->float conversions if we were to use Rect instead.
|
||||
Rect skipRect = *aSkipRect;
|
||||
skipRect.RoundIn();
|
||||
skipRect.Deflate(Size(aBlurRadius + aSpreadRadius));
|
||||
mSkipRect = IntRect(int32_t(skipRect.x), int32_t(skipRect.y),
|
||||
int32_t(skipRect.width), int32_t(skipRect.height));
|
||||
|
||||
mSkipRect = mSkipRect.Intersect(mRect);
|
||||
if (mSkipRect.IsEqualInterior(mRect))
|
||||
return;
|
||||
|
||||
mSkipRect -= mRect.TopLeft();
|
||||
} else {
|
||||
mSkipRect = IntRect(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
CheckedInt<int32_t> stride = RoundUpToMultipleOf4(mRect.width);
|
||||
if (stride.isValid()) {
|
||||
mStride = stride.value();
|
||||
|
||||
// We need to leave room for an additional 3 bytes for a potential overrun
|
||||
// in our blurring code.
|
||||
size_t size = BufferSizeFromStrideAndHeight(mStride, mRect.height, 3);
|
||||
if (size != 0) {
|
||||
mSurfaceAllocationSize = size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AlphaBoxBlur::AlphaBoxBlur(const Rect& aRect,
|
||||
int32_t aStride,
|
||||
float aSigmaX,
|
||||
float aSigmaY)
|
||||
: mRect(int32_t(aRect.x), int32_t(aRect.y),
|
||||
int32_t(aRect.width), int32_t(aRect.height)),
|
||||
mSpreadRadius(),
|
||||
mBlurRadius(CalculateBlurRadius(Point(aSigmaX, aSigmaY))),
|
||||
mStride(aStride),
|
||||
mSurfaceAllocationSize(0)
|
||||
{
|
||||
IntRect intRect;
|
||||
if (aRect.ToIntRect(&intRect)) {
|
||||
size_t minDataSize = BufferSizeFromStrideAndHeight(intRect.width, intRect.height);
|
||||
if (minDataSize != 0) {
|
||||
mSurfaceAllocationSize = minDataSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
AlphaBoxBlur::~AlphaBoxBlur()
|
||||
{
|
||||
}
|
||||
|
||||
IntSize
|
||||
AlphaBoxBlur::GetSize()
|
||||
{
|
||||
IntSize size(mRect.width, mRect.height);
|
||||
return size;
|
||||
}
|
||||
|
||||
int32_t
|
||||
AlphaBoxBlur::GetStride()
|
||||
{
|
||||
return mStride;
|
||||
}
|
||||
|
||||
IntRect
|
||||
AlphaBoxBlur::GetRect()
|
||||
{
|
||||
return mRect;
|
||||
}
|
||||
|
||||
Rect*
|
||||
AlphaBoxBlur::GetDirtyRect()
|
||||
{
|
||||
if (mHasDirtyRect) {
|
||||
return &mDirtyRect;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
size_t
|
||||
AlphaBoxBlur::GetSurfaceAllocationSize() const
|
||||
{
|
||||
return mSurfaceAllocationSize;
|
||||
}
|
||||
|
||||
void
|
||||
AlphaBoxBlur::Blur(uint8_t* aData)
|
||||
{
|
||||
if (!aData) {
|
||||
return;
|
||||
}
|
||||
|
||||
// no need to do all this if not blurring or spreading
|
||||
if (mBlurRadius != IntSize(0,0) || mSpreadRadius != IntSize(0,0)) {
|
||||
int32_t stride = GetStride();
|
||||
|
||||
IntSize size = GetSize();
|
||||
|
||||
if (mSpreadRadius.width > 0 || mSpreadRadius.height > 0) {
|
||||
// No need to use CheckedInt here - we have validated it in the constructor.
|
||||
size_t szB = stride * size.height;
|
||||
unsigned char* tmpData = new (std::nothrow) uint8_t[szB];
|
||||
|
||||
if (!tmpData) {
|
||||
return;
|
||||
}
|
||||
|
||||
memset(tmpData, 0, szB);
|
||||
|
||||
SpreadHorizontal(aData, tmpData, mSpreadRadius.width, GetSize().width, GetSize().height, stride, mSkipRect);
|
||||
SpreadVertical(tmpData, aData, mSpreadRadius.height, GetSize().width, GetSize().height, stride, mSkipRect);
|
||||
|
||||
delete [] tmpData;
|
||||
}
|
||||
|
||||
int32_t horizontalLobes[3][2];
|
||||
ComputeLobes(mBlurRadius.width, horizontalLobes);
|
||||
int32_t verticalLobes[3][2];
|
||||
ComputeLobes(mBlurRadius.height, verticalLobes);
|
||||
|
||||
// We want to allow for some extra space on the left for alignment reasons.
|
||||
int32_t maxLeftLobe = RoundUpToMultipleOf4(horizontalLobes[0][0] + 1).value();
|
||||
|
||||
IntSize integralImageSize(size.width + maxLeftLobe + horizontalLobes[1][1],
|
||||
size.height + verticalLobes[0][0] + verticalLobes[1][1] + 1);
|
||||
|
||||
if ((integralImageSize.width * integralImageSize.height) > (1 << 24)) {
|
||||
// Fallback to old blurring code when the surface is so large it may
|
||||
// overflow our integral image!
|
||||
|
||||
// No need to use CheckedInt here - we have validated it in the constructor.
|
||||
size_t szB = stride * size.height;
|
||||
uint8_t* tmpData = new (std::nothrow) uint8_t[szB];
|
||||
if (!tmpData) {
|
||||
return;
|
||||
}
|
||||
|
||||
memset(tmpData, 0, szB);
|
||||
|
||||
uint8_t* a = aData;
|
||||
uint8_t* b = tmpData;
|
||||
if (mBlurRadius.width > 0) {
|
||||
BoxBlurHorizontal(a, b, horizontalLobes[0][0], horizontalLobes[0][1], stride, GetSize().height, mSkipRect);
|
||||
BoxBlurHorizontal(b, a, horizontalLobes[1][0], horizontalLobes[1][1], stride, GetSize().height, mSkipRect);
|
||||
BoxBlurHorizontal(a, b, horizontalLobes[2][0], horizontalLobes[2][1], stride, GetSize().height, mSkipRect);
|
||||
} else {
|
||||
a = tmpData;
|
||||
b = aData;
|
||||
}
|
||||
// The result is in 'b' here.
|
||||
if (mBlurRadius.height > 0) {
|
||||
BoxBlurVertical(b, a, verticalLobes[0][0], verticalLobes[0][1], stride, GetSize().height, mSkipRect);
|
||||
BoxBlurVertical(a, b, verticalLobes[1][0], verticalLobes[1][1], stride, GetSize().height, mSkipRect);
|
||||
BoxBlurVertical(b, a, verticalLobes[2][0], verticalLobes[2][1], stride, GetSize().height, mSkipRect);
|
||||
} else {
|
||||
a = b;
|
||||
}
|
||||
// The result is in 'a' here.
|
||||
if (a == tmpData) {
|
||||
memcpy(aData, tmpData, szB);
|
||||
}
|
||||
delete [] tmpData;
|
||||
} else {
|
||||
size_t integralImageStride = GetAlignedStride<16>(integralImageSize.width, 4);
|
||||
if (integralImageStride == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We need to leave room for an additional 12 bytes for a maximum overrun
|
||||
// of 3 pixels in the blurring code.
|
||||
size_t bufLen = BufferSizeFromStrideAndHeight(integralImageStride, integralImageSize.height, 12);
|
||||
if (bufLen == 0) {
|
||||
return;
|
||||
}
|
||||
// bufLen is a byte count, but here we want a multiple of 32-bit ints, so
|
||||
// we divide by 4.
|
||||
AlignedArray<uint32_t> integralImage((bufLen / 4) + ((bufLen % 4) ? 1 : 0));
|
||||
|
||||
if (!integralImage) {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef USE_SSE2
|
||||
if (Factory::HasSSE2()) {
|
||||
BoxBlur_SSE2(aData, horizontalLobes[0][0], horizontalLobes[0][1], verticalLobes[0][0],
|
||||
verticalLobes[0][1], integralImage, integralImageStride);
|
||||
BoxBlur_SSE2(aData, horizontalLobes[1][0], horizontalLobes[1][1], verticalLobes[1][0],
|
||||
verticalLobes[1][1], integralImage, integralImageStride);
|
||||
BoxBlur_SSE2(aData, horizontalLobes[2][0], horizontalLobes[2][1], verticalLobes[2][0],
|
||||
verticalLobes[2][1], integralImage, integralImageStride);
|
||||
} else
|
||||
#endif
|
||||
#ifdef BUILD_ARM_NEON
|
||||
if (mozilla::supports_neon()) {
|
||||
BoxBlur_NEON(aData, horizontalLobes[0][0], horizontalLobes[0][1], verticalLobes[0][0],
|
||||
verticalLobes[0][1], integralImage, integralImageStride);
|
||||
BoxBlur_NEON(aData, horizontalLobes[1][0], horizontalLobes[1][1], verticalLobes[1][0],
|
||||
verticalLobes[1][1], integralImage, integralImageStride);
|
||||
BoxBlur_NEON(aData, horizontalLobes[2][0], horizontalLobes[2][1], verticalLobes[2][0],
|
||||
verticalLobes[2][1], integralImage, integralImageStride);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
#ifdef _MIPS_ARCH_LOONGSON3A
|
||||
BoxBlur_LS3(aData, horizontalLobes[0][0], horizontalLobes[0][1], verticalLobes[0][0],
|
||||
verticalLobes[0][1], integralImage, integralImageStride);
|
||||
BoxBlur_LS3(aData, horizontalLobes[1][0], horizontalLobes[1][1], verticalLobes[1][0],
|
||||
verticalLobes[1][1], integralImage, integralImageStride);
|
||||
BoxBlur_LS3(aData, horizontalLobes[2][0], horizontalLobes[2][1], verticalLobes[2][0],
|
||||
verticalLobes[2][1], integralImage, integralImageStride);
|
||||
#else
|
||||
BoxBlur_C(aData, horizontalLobes[0][0], horizontalLobes[0][1], verticalLobes[0][0],
|
||||
verticalLobes[0][1], integralImage, integralImageStride);
|
||||
BoxBlur_C(aData, horizontalLobes[1][0], horizontalLobes[1][1], verticalLobes[1][0],
|
||||
verticalLobes[1][1], integralImage, integralImageStride);
|
||||
BoxBlur_C(aData, horizontalLobes[2][0], horizontalLobes[2][1], verticalLobes[2][0],
|
||||
verticalLobes[2][1], integralImage, integralImageStride);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE void
|
||||
GenerateIntegralRow(uint32_t *aDest, const uint8_t *aSource, uint32_t *aPreviousRow,
|
||||
const uint32_t &aSourceWidth, const uint32_t &aLeftInflation, const uint32_t &aRightInflation)
|
||||
{
|
||||
uint32_t currentRowSum = 0;
|
||||
uint32_t pixel = aSource[0];
|
||||
for (uint32_t x = 0; x < aLeftInflation; x++) {
|
||||
currentRowSum += pixel;
|
||||
*aDest++ = currentRowSum + *aPreviousRow++;
|
||||
}
|
||||
for (uint32_t x = aLeftInflation; x < (aSourceWidth + aLeftInflation); x += 4) {
|
||||
uint32_t alphaValues = *(uint32_t*)(aSource + (x - aLeftInflation));
|
||||
#if defined WORDS_BIGENDIAN || defined IS_BIG_ENDIAN || defined __BIG_ENDIAN__
|
||||
currentRowSum += (alphaValues >> 24) & 0xff;
|
||||
*aDest++ = *aPreviousRow++ + currentRowSum;
|
||||
currentRowSum += (alphaValues >> 16) & 0xff;
|
||||
*aDest++ = *aPreviousRow++ + currentRowSum;
|
||||
currentRowSum += (alphaValues >> 8) & 0xff;
|
||||
*aDest++ = *aPreviousRow++ + currentRowSum;
|
||||
currentRowSum += alphaValues & 0xff;
|
||||
*aDest++ = *aPreviousRow++ + currentRowSum;
|
||||
#else
|
||||
currentRowSum += alphaValues & 0xff;
|
||||
*aDest++ = *aPreviousRow++ + currentRowSum;
|
||||
alphaValues >>= 8;
|
||||
currentRowSum += alphaValues & 0xff;
|
||||
*aDest++ = *aPreviousRow++ + currentRowSum;
|
||||
alphaValues >>= 8;
|
||||
currentRowSum += alphaValues & 0xff;
|
||||
*aDest++ = *aPreviousRow++ + currentRowSum;
|
||||
alphaValues >>= 8;
|
||||
currentRowSum += alphaValues & 0xff;
|
||||
*aDest++ = *aPreviousRow++ + currentRowSum;
|
||||
#endif
|
||||
}
|
||||
pixel = aSource[aSourceWidth - 1];
|
||||
for (uint32_t x = (aSourceWidth + aLeftInflation); x < (aSourceWidth + aLeftInflation + aRightInflation); x++) {
|
||||
currentRowSum += pixel;
|
||||
*aDest++ = currentRowSum + *aPreviousRow++;
|
||||
}
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE void
|
||||
GenerateIntegralImage_C(int32_t aLeftInflation, int32_t aRightInflation,
|
||||
int32_t aTopInflation, int32_t aBottomInflation,
|
||||
uint32_t *aIntegralImage, size_t aIntegralImageStride,
|
||||
uint8_t *aSource, int32_t aSourceStride, const IntSize &aSize)
|
||||
{
|
||||
uint32_t stride32bit = aIntegralImageStride / 4;
|
||||
|
||||
IntSize integralImageSize(aSize.width + aLeftInflation + aRightInflation,
|
||||
aSize.height + aTopInflation + aBottomInflation);
|
||||
|
||||
memset(aIntegralImage, 0, aIntegralImageStride);
|
||||
|
||||
GenerateIntegralRow(aIntegralImage, aSource, aIntegralImage,
|
||||
aSize.width, aLeftInflation, aRightInflation);
|
||||
for (int y = 1; y < aTopInflation + 1; y++) {
|
||||
GenerateIntegralRow(aIntegralImage + (y * stride32bit), aSource, aIntegralImage + (y - 1) * stride32bit,
|
||||
aSize.width, aLeftInflation, aRightInflation);
|
||||
}
|
||||
|
||||
for (int y = aTopInflation + 1; y < (aSize.height + aTopInflation); y++) {
|
||||
GenerateIntegralRow(aIntegralImage + (y * stride32bit), aSource + aSourceStride * (y - aTopInflation),
|
||||
aIntegralImage + (y - 1) * stride32bit, aSize.width, aLeftInflation, aRightInflation);
|
||||
}
|
||||
|
||||
if (aBottomInflation) {
|
||||
for (int y = (aSize.height + aTopInflation); y < integralImageSize.height; y++) {
|
||||
GenerateIntegralRow(aIntegralImage + (y * stride32bit), aSource + ((aSize.height - 1) * aSourceStride),
|
||||
aIntegralImage + (y - 1) * stride32bit,
|
||||
aSize.width, aLeftInflation, aRightInflation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to do an in-place box blur using an integral image.
|
||||
*/
|
||||
void
|
||||
AlphaBoxBlur::BoxBlur_C(uint8_t* aData,
|
||||
int32_t aLeftLobe,
|
||||
int32_t aRightLobe,
|
||||
int32_t aTopLobe,
|
||||
int32_t aBottomLobe,
|
||||
uint32_t *aIntegralImage,
|
||||
size_t aIntegralImageStride)
|
||||
{
|
||||
IntSize size = GetSize();
|
||||
|
||||
MOZ_ASSERT(size.width > 0);
|
||||
|
||||
// Our 'left' or 'top' lobe will include the current pixel. i.e. when
|
||||
// looking at an integral image the value of a pixel at 'x,y' is calculated
|
||||
// using the value of the integral image values above/below that.
|
||||
aLeftLobe++;
|
||||
aTopLobe++;
|
||||
int32_t boxSize = (aLeftLobe + aRightLobe) * (aTopLobe + aBottomLobe);
|
||||
|
||||
MOZ_ASSERT(boxSize > 0);
|
||||
|
||||
if (boxSize == 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
int32_t stride32bit = aIntegralImageStride / 4;
|
||||
|
||||
int32_t leftInflation = RoundUpToMultipleOf4(aLeftLobe).value();
|
||||
|
||||
GenerateIntegralImage_C(leftInflation, aRightLobe, aTopLobe, aBottomLobe,
|
||||
aIntegralImage, aIntegralImageStride, aData,
|
||||
mStride, size);
|
||||
|
||||
uint32_t reciprocal = uint32_t((uint64_t(1) << 32) / boxSize);
|
||||
|
||||
uint32_t *innerIntegral = aIntegralImage + (aTopLobe * stride32bit) + leftInflation;
|
||||
|
||||
// Storing these locally makes this about 30% faster! Presumably the compiler
|
||||
// can't be sure we're not altering the member variables in this loop.
|
||||
IntRect skipRect = mSkipRect;
|
||||
uint8_t *data = aData;
|
||||
int32_t stride = mStride;
|
||||
for (int32_t y = 0; y < size.height; y++) {
|
||||
bool inSkipRectY = y > skipRect.y && y < skipRect.YMost();
|
||||
|
||||
uint32_t *topLeftBase = innerIntegral + ((y - aTopLobe) * stride32bit - aLeftLobe);
|
||||
uint32_t *topRightBase = innerIntegral + ((y - aTopLobe) * stride32bit + aRightLobe);
|
||||
uint32_t *bottomRightBase = innerIntegral + ((y + aBottomLobe) * stride32bit + aRightLobe);
|
||||
uint32_t *bottomLeftBase = innerIntegral + ((y + aBottomLobe) * stride32bit - aLeftLobe);
|
||||
|
||||
for (int32_t x = 0; x < size.width; x++) {
|
||||
if (inSkipRectY && x > skipRect.x && x < skipRect.XMost()) {
|
||||
x = skipRect.XMost() - 1;
|
||||
// Trigger early jump on coming loop iterations, this will be reset
|
||||
// next line anyway.
|
||||
inSkipRectY = false;
|
||||
continue;
|
||||
}
|
||||
int32_t topLeft = topLeftBase[x];
|
||||
int32_t topRight = topRightBase[x];
|
||||
int32_t bottomRight = bottomRightBase[x];
|
||||
int32_t bottomLeft = bottomLeftBase[x];
|
||||
|
||||
uint32_t value = bottomRight - topRight - bottomLeft;
|
||||
value += topLeft;
|
||||
|
||||
data[stride * y + x] = (uint64_t(reciprocal) * value + (uint64_t(1) << 31)) >> 32;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the box blur size (which we're calling the blur radius) from
|
||||
* the standard deviation.
|
||||
*
|
||||
* Much of this, the 3 * sqrt(2 * pi) / 4, is the known value for
|
||||
* approximating a Gaussian using box blurs. This yields quite a good
|
||||
* approximation for a Gaussian. Then we multiply this by 1.5 since our
|
||||
* code wants the radius of the entire triple-box-blur kernel instead of
|
||||
* the diameter of an individual box blur. For more details, see:
|
||||
* http://www.w3.org/TR/SVG11/filters.html#feGaussianBlurElement
|
||||
* https://bugzilla.mozilla.org/show_bug.cgi?id=590039#c19
|
||||
*/
|
||||
static const Float GAUSSIAN_SCALE_FACTOR = Float((3 * sqrt(2 * M_PI) / 4) * 1.5);
|
||||
|
||||
IntSize
|
||||
AlphaBoxBlur::CalculateBlurRadius(const Point& aStd)
|
||||
{
|
||||
IntSize size(static_cast<int32_t>(floor(aStd.x * GAUSSIAN_SCALE_FACTOR + 0.5f)),
|
||||
static_cast<int32_t>(floor(aStd.y * GAUSSIAN_SCALE_FACTOR + 0.5f)));
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
||||
186
gfx/2d/Blur.h
Normal file
186
gfx/2d/Blur.h
Normal file
|
|
@ -0,0 +1,186 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* 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_GFX_BLUR_H_
|
||||
#define MOZILLA_GFX_BLUR_H_
|
||||
|
||||
#include "mozilla/gfx/Rect.h"
|
||||
#include "mozilla/gfx/Point.h"
|
||||
#include "mozilla/CheckedInt.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning( disable : 4251 )
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Implementation of a triple box blur approximation of a Gaussian blur.
|
||||
*
|
||||
* A Gaussian blur is good for blurring because, when done independently
|
||||
* in the horizontal and vertical directions, it matches the result that
|
||||
* would be obtained using a different (rotated) set of axes. A triple
|
||||
* box blur is a very close approximation of a Gaussian.
|
||||
*
|
||||
* This is a "service" class; the constructors set up all the information
|
||||
* based on the values and compute the minimum size for an 8-bit alpha
|
||||
* channel context.
|
||||
* The callers are responsible for creating and managing the backing surface
|
||||
* and passing the pointer to the data to the Blur() method. This class does
|
||||
* not retain the pointer to the data outside of the Blur() call.
|
||||
*
|
||||
* A spread N makes each output pixel the maximum value of all source
|
||||
* pixels within a square of side length 2N+1 centered on the output pixel.
|
||||
*/
|
||||
class GFX2D_API AlphaBoxBlur
|
||||
{
|
||||
public:
|
||||
|
||||
/** Constructs a box blur and computes the backing surface size.
|
||||
*
|
||||
* @param aRect The coordinates of the surface to create in device units.
|
||||
*
|
||||
* @param aBlurRadius The blur radius in pixels. This is the radius of the
|
||||
* entire (triple) kernel function. Each individual box blur has radius
|
||||
* approximately 1/3 this value, or diameter approximately 2/3 this value.
|
||||
* This parameter should nearly always be computed using CalculateBlurRadius,
|
||||
* below.
|
||||
*
|
||||
* @param aDirtyRect A pointer to a dirty rect, measured in device units, if
|
||||
* available. This will be used for optimizing the blur operation. It is
|
||||
* safe to pass nullptr here.
|
||||
*
|
||||
* @param aSkipRect A pointer to a rect, measured in device units, that
|
||||
* represents an area where blurring is unnecessary and shouldn't be done for
|
||||
* speed reasons. It is safe to pass nullptr here.
|
||||
*/
|
||||
AlphaBoxBlur(const Rect& aRect,
|
||||
const IntSize& aSpreadRadius,
|
||||
const IntSize& aBlurRadius,
|
||||
const Rect* aDirtyRect,
|
||||
const Rect* aSkipRect);
|
||||
|
||||
AlphaBoxBlur(const Rect& aRect,
|
||||
int32_t aStride,
|
||||
float aSigmaX,
|
||||
float aSigmaY);
|
||||
|
||||
~AlphaBoxBlur();
|
||||
|
||||
/**
|
||||
* Return the size, in pixels, of the 8-bit alpha surface we'd use.
|
||||
*/
|
||||
IntSize GetSize();
|
||||
|
||||
/**
|
||||
* Return the stride, in bytes, of the 8-bit alpha surface we'd use.
|
||||
*/
|
||||
int32_t GetStride();
|
||||
|
||||
/**
|
||||
* Returns the device-space rectangle the 8-bit alpha surface covers.
|
||||
*/
|
||||
IntRect GetRect();
|
||||
|
||||
/**
|
||||
* Return a pointer to a dirty rect, as passed in to the constructor, or nullptr
|
||||
* if none was passed in.
|
||||
*/
|
||||
Rect* GetDirtyRect();
|
||||
|
||||
/**
|
||||
* Return the minimum buffer size that should be given to Blur() method. If
|
||||
* zero, the class is not properly setup for blurring. Note that this
|
||||
* includes the extra three bytes on top of the stride*width, where something
|
||||
* like gfxImageSurface::GetDataSize() would report without it, even if it
|
||||
* happens to have the extra bytes.
|
||||
*/
|
||||
size_t GetSurfaceAllocationSize() const;
|
||||
|
||||
/**
|
||||
* Perform the blur in-place on the surface backed by specified 8-bit
|
||||
* alpha surface data. The size must be at least that returned by
|
||||
* GetSurfaceAllocationSize() or bad things will happen.
|
||||
*/
|
||||
void Blur(uint8_t* aData);
|
||||
|
||||
/**
|
||||
* Calculates a blur radius that, when used with box blur, approximates a
|
||||
* Gaussian blur with the given standard deviation. The result of this
|
||||
* function should be used as the aBlurRadius parameter to AlphaBoxBlur's
|
||||
* constructor, above.
|
||||
*/
|
||||
static IntSize CalculateBlurRadius(const Point& aStandardDeviation);
|
||||
|
||||
private:
|
||||
|
||||
void BoxBlur_C(uint8_t* aData,
|
||||
int32_t aLeftLobe, int32_t aRightLobe, int32_t aTopLobe,
|
||||
int32_t aBottomLobe, uint32_t *aIntegralImage, size_t aIntegralImageStride);
|
||||
void BoxBlur_SSE2(uint8_t* aData,
|
||||
int32_t aLeftLobe, int32_t aRightLobe, int32_t aTopLobe,
|
||||
int32_t aBottomLobe, uint32_t *aIntegralImage, size_t aIntegralImageStride);
|
||||
#ifdef BUILD_ARM_NEON
|
||||
void BoxBlur_NEON(uint8_t* aData,
|
||||
int32_t aLeftLobe, int32_t aRightLobe, int32_t aTopLobe,
|
||||
int32_t aBottomLobe, uint32_t *aIntegralImage, size_t aIntegralImageStride);
|
||||
#endif
|
||||
#ifdef _MIPS_ARCH_LOONGSON3A
|
||||
void BoxBlur_LS3(uint8_t* aData,
|
||||
int32_t aLeftLobe, int32_t aRightLobe, int32_t aTopLobe,
|
||||
int32_t aBottomLobe, uint32_t *aIntegralImage, size_t aIntegralImageStride);
|
||||
#endif
|
||||
|
||||
static CheckedInt<int32_t> RoundUpToMultipleOf4(int32_t aVal);
|
||||
|
||||
/**
|
||||
* A rect indicating the area where blurring is unnecessary, and the blur
|
||||
* algorithm should skip over it.
|
||||
*/
|
||||
IntRect mSkipRect;
|
||||
|
||||
/**
|
||||
* The device-space rectangle the the backing 8-bit alpha surface covers.
|
||||
*/
|
||||
IntRect mRect;
|
||||
|
||||
/**
|
||||
* A copy of the dirty rect passed to the constructor. This will only be valid if
|
||||
* mHasDirtyRect is true.
|
||||
*/
|
||||
Rect mDirtyRect;
|
||||
|
||||
/**
|
||||
* The spread radius, in pixels.
|
||||
*/
|
||||
IntSize mSpreadRadius;
|
||||
|
||||
/**
|
||||
* The blur radius, in pixels.
|
||||
*/
|
||||
IntSize mBlurRadius;
|
||||
|
||||
/**
|
||||
* The stride of the data passed to Blur()
|
||||
*/
|
||||
int32_t mStride;
|
||||
|
||||
/**
|
||||
* The minimum size of the buffer needed for the Blur() operation.
|
||||
*/
|
||||
size_t mSurfaceAllocationSize;
|
||||
|
||||
/**
|
||||
* Whether mDirtyRect contains valid data.
|
||||
*/
|
||||
bool mHasDirtyRect;
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* MOZILLA_GFX_BLUR_H_ */
|
||||
588
gfx/2d/BlurLS3.cpp
Normal file
588
gfx/2d/BlurLS3.cpp
Normal file
|
|
@ -0,0 +1,588 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "Blur.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#ifdef _MIPS_ARCH_LOONGSON3A
|
||||
|
||||
#include "MMIHelpers.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
typedef struct { double l; double h; } __m128i;
|
||||
|
||||
MOZ_ALWAYS_INLINE
|
||||
__m128i loadUnaligned128(__m128i *p)
|
||||
{
|
||||
__m128i v;
|
||||
|
||||
asm volatile (
|
||||
".set push \n\t"
|
||||
".set arch=loongson3a \n\t"
|
||||
"gsldlc1 %[vh], 0xf(%[p]) \n\t"
|
||||
"gsldrc1 %[vh], 0x8(%[p]) \n\t"
|
||||
"gsldlc1 %[vl], 0x7(%[p]) \n\t"
|
||||
"gsldrc1 %[vl], 0x0(%[p]) \n\t"
|
||||
".set pop \n\t"
|
||||
:[vh]"=f"(v.h), [vl]"=f"(v.l)
|
||||
:[p]"r"(p)
|
||||
:"memory"
|
||||
);
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE
|
||||
__m128i Divide(__m128i aValues, __m128i aDivisor)
|
||||
{
|
||||
uint64_t tmp;
|
||||
double srl32;
|
||||
__m128i mask, ra, p4321, t1, t2;
|
||||
|
||||
asm volatile (
|
||||
".set push \n\t"
|
||||
".set arch=loongson3a \n\t"
|
||||
"li %[tmp], 0x80000000 \n\t"
|
||||
"mtc1 %[tmp], %[ral] \n\t"
|
||||
"xor %[maskl], %[maskl], %[maskl] \n\t"
|
||||
"mov.d %[rah], %[ral] \n\t"
|
||||
"li %[tmp], 0xffffffff \n\t"
|
||||
"mthc1 %[tmp], %[maskl] \n\t"
|
||||
"mov.d %[maskh], %[maskl] \n\t"
|
||||
".set pop \n\t"
|
||||
:[rah]"=f"(ra.h), [ral]"=f"(ra.l),
|
||||
[maskh]"=f"(mask.h), [maskl]"=f"(mask.l),
|
||||
[tmp]"=&r"(tmp)
|
||||
);
|
||||
|
||||
asm volatile (
|
||||
".set push \n\t"
|
||||
".set arch=loongson3a \n\t"
|
||||
"ori %[tmp], $0, 32 \n\t"
|
||||
"mtc1 %[tmp], %[srl32] \n\t"
|
||||
_mm_pmuluw(t1, av, ad)
|
||||
_mm_psrld(t2, av, srl32)
|
||||
_mm_pmuluw(t2, t2, ad)
|
||||
// Add 1 << 31 before shifting or masking the lower 32 bits away, so that the
|
||||
// result is rounded.
|
||||
_mm_paddd(t1, t1, ra)
|
||||
_mm_psrld(t1, t1, srl32)
|
||||
_mm_paddd(t2, t2, ra)
|
||||
_mm_and(t2, t2, mask)
|
||||
_mm_or(p4321, t1, t2)
|
||||
".set pop \n\t"
|
||||
:[p4321h]"=&f"(p4321.h), [p4321l]"=&f"(p4321.l),
|
||||
[t1h]"=&f"(t1.h), [t1l]"=&f"(t1.l),
|
||||
[t2h]"=&f"(t2.h), [t2l]"=&f"(t2.l),
|
||||
[srl32]"=&f"(srl32), [tmp]"=&r"(tmp)
|
||||
:[rah]"f"(ra.h), [ral]"f"(ra.l),
|
||||
[maskh]"f"(mask.h), [maskl]"f"(mask.l),
|
||||
[avh]"f"(aValues.h), [avl]"f"(aValues.l),
|
||||
[adh]"f"(aDivisor.h), [adl]"f"(aDivisor.l)
|
||||
);
|
||||
|
||||
return p4321;
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE
|
||||
__m128i BlurFourPixels(const __m128i& aTopLeft, const __m128i& aTopRight,
|
||||
const __m128i& aBottomRight, const __m128i& aBottomLeft,
|
||||
const __m128i& aDivisor)
|
||||
{
|
||||
__m128i values;
|
||||
|
||||
asm volatile (
|
||||
".set push \n\t"
|
||||
".set arch=loongson3a \n\t"
|
||||
_mm_psubw(val, abr, atr)
|
||||
_mm_psubw(val, val, abl)
|
||||
_mm_paddw(val, val, atl)
|
||||
".set pop \n\t"
|
||||
:[valh]"=&f"(values.h), [vall]"=&f"(values.l)
|
||||
:[abrh]"f"(aBottomRight.h), [abrl]"f"(aBottomRight.l),
|
||||
[atrh]"f"(aTopRight.h), [atrl]"f"(aTopRight.l),
|
||||
[ablh]"f"(aBottomLeft.h), [abll]"f"(aBottomLeft.l),
|
||||
[atlh]"f"(aTopLeft.h), [atll]"f"(aTopLeft.l)
|
||||
);
|
||||
|
||||
return Divide(values, aDivisor);
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE
|
||||
void LoadIntegralRowFromRow(uint32_t *aDest, const uint8_t *aSource,
|
||||
int32_t aSourceWidth, int32_t aLeftInflation,
|
||||
int32_t aRightInflation)
|
||||
{
|
||||
int32_t currentRowSum = 0;
|
||||
|
||||
for (int x = 0; x < aLeftInflation; x++) {
|
||||
currentRowSum += aSource[0];
|
||||
aDest[x] = currentRowSum;
|
||||
}
|
||||
for (int x = aLeftInflation; x < (aSourceWidth + aLeftInflation); x++) {
|
||||
currentRowSum += aSource[(x - aLeftInflation)];
|
||||
aDest[x] = currentRowSum;
|
||||
}
|
||||
for (int x = (aSourceWidth + aLeftInflation); x < (aSourceWidth + aLeftInflation + aRightInflation); x++) {
|
||||
currentRowSum += aSource[aSourceWidth - 1];
|
||||
aDest[x] = currentRowSum;
|
||||
}
|
||||
}
|
||||
|
||||
// This function calculates an integral of four pixels stored in the 4
|
||||
// 32-bit integers on aPixels. i.e. for { 30, 50, 80, 100 } this returns
|
||||
// { 30, 80, 160, 260 }. This seems to be the fastest way to do this after
|
||||
// much testing.
|
||||
MOZ_ALWAYS_INLINE
|
||||
__m128i AccumulatePixelSums(__m128i aPixels)
|
||||
{
|
||||
uint64_t tr;
|
||||
double tmp, s4, s64;
|
||||
__m128i sumPixels, currentPixels, zero;
|
||||
|
||||
asm volatile (
|
||||
".set push \n\t"
|
||||
".set arch=loongson3a \n\t"
|
||||
_mm_xor(z, z, z)
|
||||
"li %[tr], 64 \n\t"
|
||||
"mtc1 %[tr], %[s64] \n\t"
|
||||
"li %[tr], 32 \n\t"
|
||||
"mtc1 %[tr], %[s4] \n\t"
|
||||
_mm_psllq(cp, ap, s4, s64, t)
|
||||
_mm_paddw(sp, ap, cp)
|
||||
_mm_punpckldq(cp, z, sp)
|
||||
_mm_paddw(sp, sp, cp)
|
||||
".set pop \n\t"
|
||||
:[sph]"=&f"(sumPixels.h), [spl]"=&f"(sumPixels.l),
|
||||
[cph]"=&f"(currentPixels.h), [cpl]"=&f"(currentPixels.l),
|
||||
[zh]"=&f"(zero.h), [zl]"=&f"(zero.l),
|
||||
[s4]"=&f"(s4), [s64]"=&f"(s64), [t]"=&f"(tmp), [tr]"=&r"(tr)
|
||||
:[aph]"f"(aPixels.h), [apl]"f"(aPixels.l)
|
||||
);
|
||||
|
||||
return sumPixels;
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE
|
||||
void GenerateIntegralImage_LS3(int32_t aLeftInflation, int32_t aRightInflation,
|
||||
int32_t aTopInflation, int32_t aBottomInflation,
|
||||
uint32_t *aIntegralImage, size_t aIntegralImageStride,
|
||||
uint8_t *aSource, int32_t aSourceStride, const IntSize &aSize)
|
||||
{
|
||||
MOZ_ASSERT(!(aLeftInflation & 3));
|
||||
|
||||
uint32_t stride32bit = aIntegralImageStride / 4;
|
||||
|
||||
IntSize integralImageSize(aSize.width + aLeftInflation + aRightInflation,
|
||||
aSize.height + aTopInflation + aBottomInflation);
|
||||
|
||||
LoadIntegralRowFromRow(aIntegralImage, aSource, aSize.width, aLeftInflation, aRightInflation);
|
||||
|
||||
for (int y = 1; y < aTopInflation + 1; y++) {
|
||||
uint32_t *intRow = aIntegralImage + (y * stride32bit);
|
||||
uint32_t *intPrevRow = aIntegralImage + (y - 1) * stride32bit;
|
||||
uint32_t *intFirstRow = aIntegralImage;
|
||||
|
||||
for (int x = 0; x < integralImageSize.width; x += 4) {
|
||||
__m128i firstRow, previousRow;
|
||||
|
||||
asm volatile (
|
||||
".set push \n\t"
|
||||
".set arch=loongson3a \n\t"
|
||||
"gslqc1 %[frh], %[frl], (%[fr]) \n\t"
|
||||
"gslqc1 %[prh], %[prl], (%[pr]) \n\t"
|
||||
_mm_paddw(fr, fr, pr)
|
||||
"gssqc1 %[frh], %[frl], (%[r]) \n\t"
|
||||
".set pop \n\t"
|
||||
:[frh]"=&f"(firstRow.h), [frl]"=&f"(firstRow.l),
|
||||
[prh]"=&f"(previousRow.h), [prl]"=&f"(previousRow.l)
|
||||
:[fr]"r"(intFirstRow + x), [pr]"r"(intPrevRow + x),
|
||||
[r]"r"(intRow + x)
|
||||
:"memory"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t tmp;
|
||||
double s44, see;
|
||||
__m128i zero;
|
||||
asm volatile (
|
||||
".set push \n\t"
|
||||
".set arch=loongson3a \n\t"
|
||||
"li %[tmp], 0xee \n\t"
|
||||
"mtc1 %[tmp], %[see] \n\t"
|
||||
"li %[tmp], 0x44 \n\t"
|
||||
"mtc1 %[tmp], %[s44] \n\t"
|
||||
_mm_xor(zero, zero, zero)
|
||||
".set pop \n\t"
|
||||
:[tmp]"=&r"(tmp), [s44]"=f"(s44), [see]"=f"(see),
|
||||
[zeroh]"=f"(zero.h), [zerol]"=f"(zero.l)
|
||||
);
|
||||
for (int y = aTopInflation + 1; y < (aSize.height + aTopInflation); y++) {
|
||||
__m128i currentRowSum;
|
||||
uint32_t *intRow = aIntegralImage + (y * stride32bit);
|
||||
uint32_t *intPrevRow = aIntegralImage + (y - 1) * stride32bit;
|
||||
uint8_t *sourceRow = aSource + aSourceStride * (y - aTopInflation);
|
||||
uint32_t pixel = sourceRow[0];
|
||||
|
||||
asm volatile (
|
||||
".set push \n\t"
|
||||
".set arch=loongson3a \n\t"
|
||||
_mm_xor(cr, cr, cr)
|
||||
".set pop \n\t"
|
||||
:[crh]"=f"(currentRowSum.h), [crl]"=f"(currentRowSum.l)
|
||||
);
|
||||
for (int x = 0; x < aLeftInflation; x += 4) {
|
||||
__m128i sumPixels, t;
|
||||
asm volatile (
|
||||
".set push \n\t"
|
||||
".set arch=loongson3a \n\t"
|
||||
"mtc1 %[pix], %[spl] \n\t"
|
||||
"punpcklwd %[spl], %[spl], %[spl] \n\t"
|
||||
"mov.d %[sph], %[spl] \n\t"
|
||||
"pshufh %[sph], %[spl], %[s44] \n\t"
|
||||
"pshufh %[spl], %[spl], %[s44] \n\t"
|
||||
".set pop \n\t"
|
||||
:[sph]"=&f"(sumPixels.h), [spl]"=&f"(sumPixels.l)
|
||||
:[pix]"r"(pixel), [s44]"f"(s44)
|
||||
);
|
||||
sumPixels = AccumulatePixelSums(sumPixels);
|
||||
asm volatile (
|
||||
".set push \n\t"
|
||||
".set arch=loongson3a \n\t"
|
||||
_mm_paddw(sp, sp, cr)
|
||||
"pshufh %[crh], %[sph], %[see] \n\t"
|
||||
"pshufh %[crl], %[sph], %[see] \n\t"
|
||||
"gslqc1 %[th], %[tl], (%[pr]) \n\t"
|
||||
_mm_paddw(t, sp, t)
|
||||
"gssqc1 %[th], %[tl], (%[r]) \n\t"
|
||||
".set pop \n\t"
|
||||
:[th]"=&f"(t.h), [tl]"=&f"(t.l),
|
||||
[sph]"+f"(sumPixels.h), [spl]"+f"(sumPixels.l),
|
||||
[crh]"+f"(currentRowSum.h), [crl]"+f"(currentRowSum.l)
|
||||
:[r]"r"(intRow + x), [pr]"r"(intPrevRow + x), [see]"f"(see)
|
||||
:"memory"
|
||||
);
|
||||
}
|
||||
for (int x = aLeftInflation; x < (aSize.width + aLeftInflation); x += 4) {
|
||||
uint32_t pixels = *(uint32_t*)(sourceRow + (x - aLeftInflation));
|
||||
__m128i sumPixels, t;
|
||||
|
||||
// It's important to shuffle here. When we exit this loop currentRowSum
|
||||
// has to be set to sumPixels, so that the following loop can get the
|
||||
// correct pixel for the currentRowSum. The highest order pixel in
|
||||
// currentRowSum could've originated from accumulation in the stride.
|
||||
asm volatile (
|
||||
".set push \n\t"
|
||||
".set arch=loongson3a \n\t"
|
||||
"pshufh %[crl], %[crh], %[see] \n\t"
|
||||
"pshufh %[crh], %[crh], %[see] \n\t"
|
||||
"mtc1 %[pix], %[spl] \n\t"
|
||||
"punpcklwd %[spl], %[spl], %[spl] \n\t"
|
||||
"mov.d %[sph], %[spl] \n\t"
|
||||
_mm_punpcklbh(sp, sp, zero)
|
||||
_mm_punpcklhw(sp, sp, zero)
|
||||
".set pop \n\t"
|
||||
:[sph]"=&f"(sumPixels.h), [spl]"=&f"(sumPixels.l),
|
||||
[crh]"+f"(currentRowSum.h), [crl]"+f"(currentRowSum.l)
|
||||
:[pix]"r"(pixels), [see]"f"(see),
|
||||
[zeroh]"f"(zero.h), [zerol]"f"(zero.l)
|
||||
);
|
||||
sumPixels = AccumulatePixelSums(sumPixels);
|
||||
asm volatile (
|
||||
".set push \n\t"
|
||||
".set arch=loongson3a \n\t"
|
||||
_mm_paddw(sp, sp, cr)
|
||||
"mov.d %[crh], %[sph] \n\t"
|
||||
"mov.d %[crl], %[spl] \n\t"
|
||||
"gslqc1 %[th], %[tl], (%[pr]) \n\t"
|
||||
_mm_paddw(t, sp, t)
|
||||
"gssqc1 %[th], %[tl], (%[r]) \n\t"
|
||||
".set pop \n\t"
|
||||
:[th]"=&f"(t.h), [tl]"=&f"(t.l),
|
||||
[sph]"+f"(sumPixels.h), [spl]"+f"(sumPixels.l),
|
||||
[crh]"+f"(currentRowSum.h), [crl]"+f"(currentRowSum.l)
|
||||
:[r]"r"(intRow + x), [pr]"r"(intPrevRow + x)
|
||||
:"memory"
|
||||
);
|
||||
}
|
||||
|
||||
pixel = sourceRow[aSize.width - 1];
|
||||
int x = (aSize.width + aLeftInflation);
|
||||
if ((aSize.width & 3)) {
|
||||
// Deal with unaligned portion. Get the correct pixel from currentRowSum,
|
||||
// see explanation above.
|
||||
uint32_t intCurrentRowSum = ((uint32_t*)¤tRowSum)[(aSize.width % 4) - 1];
|
||||
for (; x < integralImageSize.width; x++) {
|
||||
// We could be unaligned here!
|
||||
if (!(x & 3)) {
|
||||
// aligned!
|
||||
asm volatile (
|
||||
".set push \n\t"
|
||||
".set arch=loongson3a \n\t"
|
||||
"mtc1 %[cr], %[crl] \n\t"
|
||||
"punpcklwd %[crl], %[crl], %[crl] \n\t"
|
||||
"mov.d %[crh], %[crl] \n\t"
|
||||
".set pop \n\t"
|
||||
:[crh]"=f"(currentRowSum.h), [crl]"=f"(currentRowSum.l)
|
||||
:[cr]"r"(intCurrentRowSum)
|
||||
);
|
||||
break;
|
||||
}
|
||||
intCurrentRowSum += pixel;
|
||||
intRow[x] = intPrevRow[x] + intCurrentRowSum;
|
||||
}
|
||||
} else {
|
||||
asm volatile (
|
||||
".set push \n\t"
|
||||
".set arch=loongson3a \n\t"
|
||||
"pshufh %[crl], %[crh], %[see] \n\t"
|
||||
"pshufh %[crh], %[crh], %[see] \n\t"
|
||||
".set pop \n\t"
|
||||
:[crh]"+f"(currentRowSum.h), [crl]"+f"(currentRowSum.l)
|
||||
:[see]"f"(see)
|
||||
);
|
||||
}
|
||||
for (; x < integralImageSize.width; x += 4) {
|
||||
__m128i sumPixels, t;
|
||||
asm volatile (
|
||||
".set push \n\t"
|
||||
".set arch=loongson3a \n\t"
|
||||
"mtc1 %[pix], %[spl] \n\t"
|
||||
"punpcklwd %[spl], %[spl], %[spl] \n\t"
|
||||
"mov.d %[sph], %[spl] \n\t"
|
||||
".set pop \n\t"
|
||||
:[sph]"=f"(sumPixels.h), [spl]"=f"(sumPixels.l)
|
||||
:[pix]"r"(pixel)
|
||||
);
|
||||
sumPixels = AccumulatePixelSums(sumPixels);
|
||||
asm volatile (
|
||||
".set push \n\t"
|
||||
".set arch=loongson3a \n\t"
|
||||
_mm_paddw(sp, sp, cr)
|
||||
"pshufh %[crh], %[sph], %[see] \n\t"
|
||||
"pshufh %[crl], %[sph], %[see] \n\t"
|
||||
"gslqc1 %[th], %[tl], (%[pr]) \n\t"
|
||||
_mm_paddw(t, sp, t)
|
||||
"gssqc1 %[th], %[tl], (%[r]) \n\t"
|
||||
".set pop \n\t"
|
||||
:[th]"=&f"(t.h), [tl]"=&f"(t.l),
|
||||
[sph]"+f"(sumPixels.h), [spl]"+f"(sumPixels.l),
|
||||
[crh]"+f"(currentRowSum.h), [crl]"+f"(currentRowSum.l)
|
||||
:[r]"r"(intRow + x), [pr]"r"(intPrevRow + x), [see]"f"(see)
|
||||
:"memory"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (aBottomInflation) {
|
||||
// Store the last valid row of our source image in the last row of
|
||||
// our integral image. This will be overwritten with the correct values
|
||||
// in the upcoming loop.
|
||||
LoadIntegralRowFromRow(aIntegralImage + (integralImageSize.height - 1) * stride32bit,
|
||||
aSource + (aSize.height - 1) * aSourceStride, aSize.width, aLeftInflation, aRightInflation);
|
||||
|
||||
|
||||
for (int y = aSize.height + aTopInflation; y < integralImageSize.height; y++) {
|
||||
__m128i *intRow = (__m128i*)(aIntegralImage + (y * stride32bit));
|
||||
__m128i *intPrevRow = (__m128i*)(aIntegralImage + (y - 1) * stride32bit);
|
||||
__m128i *intLastRow = (__m128i*)(aIntegralImage + (integralImageSize.height - 1) * stride32bit);
|
||||
|
||||
for (int x = 0; x < integralImageSize.width; x += 4) {
|
||||
__m128i t1, t2;
|
||||
asm volatile (
|
||||
".set push \n\t"
|
||||
".set arch=loongson3a \n\t"
|
||||
"gslqc1 %[t1h], %[t1l], (%[lr]) \n\t"
|
||||
"gslqc1 %[t2h], %[t2l], (%[pr]) \n\t"
|
||||
_mm_paddw(t1, t1, t2)
|
||||
"gssqc1 %[t1h], %[t1l], (%[r]) \n\t"
|
||||
".set pop \n\t"
|
||||
:[t1h]"=&f"(t1.h), [t1l]"=&f"(t1.l),
|
||||
[t2h]"=&f"(t2.h), [t2l]"=&f"(t2.l)
|
||||
:[r]"r"(intRow + (x / 4)),
|
||||
[lr]"r"(intLastRow + (x / 4)),
|
||||
[pr]"r"(intPrevRow + (x / 4))
|
||||
:"memory"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to do an in-place box blur using an integral image.
|
||||
*/
|
||||
void
|
||||
AlphaBoxBlur::BoxBlur_LS3(uint8_t* aData,
|
||||
int32_t aLeftLobe,
|
||||
int32_t aRightLobe,
|
||||
int32_t aTopLobe,
|
||||
int32_t aBottomLobe,
|
||||
uint32_t *aIntegralImage,
|
||||
size_t aIntegralImageStride)
|
||||
{
|
||||
IntSize size = GetSize();
|
||||
|
||||
MOZ_ASSERT(size.height > 0);
|
||||
|
||||
// Our 'left' or 'top' lobe will include the current pixel. i.e. when
|
||||
// looking at an integral image the value of a pixel at 'x,y' is calculated
|
||||
// using the value of the integral image values above/below that.
|
||||
aLeftLobe++;
|
||||
aTopLobe++;
|
||||
int32_t boxSize = (aLeftLobe + aRightLobe) * (aTopLobe + aBottomLobe);
|
||||
|
||||
MOZ_ASSERT(boxSize > 0);
|
||||
|
||||
if (boxSize == 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t reciprocal = uint32_t((uint64_t(1) << 32) / boxSize);
|
||||
|
||||
uint32_t stride32bit = aIntegralImageStride / 4;
|
||||
int32_t leftInflation = RoundUpToMultipleOf4(aLeftLobe).value();
|
||||
|
||||
GenerateIntegralImage_LS3(leftInflation, aRightLobe, aTopLobe, aBottomLobe,
|
||||
aIntegralImage, aIntegralImageStride, aData,
|
||||
mStride, size);
|
||||
|
||||
__m128i divisor, zero;
|
||||
asm volatile (
|
||||
".set push \n\t"
|
||||
".set arch=loongson3a \n\t"
|
||||
"mtc1 %[rec], %[divl] \n\t"
|
||||
"punpcklwd %[divl], %[divl], %[divl] \n\t"
|
||||
"mov.d %[divh], %[divl] \n\t"
|
||||
_mm_xor(zero, zero, zero)
|
||||
".set pop \n\t"
|
||||
:[divh]"=f"(divisor.h), [divl]"=f"(divisor.l),
|
||||
[zeroh]"=f"(zero.h), [zerol]"=f"(zero.l)
|
||||
:[rec]"r"(reciprocal)
|
||||
);
|
||||
|
||||
// This points to the start of the rectangle within the IntegralImage that overlaps
|
||||
// the surface being blurred.
|
||||
uint32_t *innerIntegral = aIntegralImage + (aTopLobe * stride32bit) + leftInflation;
|
||||
|
||||
IntRect skipRect = mSkipRect;
|
||||
int32_t stride = mStride;
|
||||
uint8_t *data = aData;
|
||||
for (int32_t y = 0; y < size.height; y++) {
|
||||
bool inSkipRectY = y > skipRect.y && y < skipRect.YMost();
|
||||
|
||||
uint32_t *topLeftBase = innerIntegral + ((y - aTopLobe) * ptrdiff_t(stride32bit) - aLeftLobe);
|
||||
uint32_t *topRightBase = innerIntegral + ((y - aTopLobe) * ptrdiff_t(stride32bit) + aRightLobe);
|
||||
uint32_t *bottomRightBase = innerIntegral + ((y + aBottomLobe) * ptrdiff_t(stride32bit) + aRightLobe);
|
||||
uint32_t *bottomLeftBase = innerIntegral + ((y + aBottomLobe) * ptrdiff_t(stride32bit) - aLeftLobe);
|
||||
|
||||
int32_t x = 0;
|
||||
// Process 16 pixels at a time for as long as possible.
|
||||
for (; x <= size.width - 16; x += 16) {
|
||||
if (inSkipRectY && x > skipRect.x && x < skipRect.XMost()) {
|
||||
x = skipRect.XMost() - 16;
|
||||
// Trigger early jump on coming loop iterations, this will be reset
|
||||
// next line anyway.
|
||||
inSkipRectY = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
__m128i topLeft;
|
||||
__m128i topRight;
|
||||
__m128i bottomRight;
|
||||
__m128i bottomLeft;
|
||||
|
||||
topLeft = loadUnaligned128((__m128i*)(topLeftBase + x));
|
||||
topRight = loadUnaligned128((__m128i*)(topRightBase + x));
|
||||
bottomRight = loadUnaligned128((__m128i*)(bottomRightBase + x));
|
||||
bottomLeft = loadUnaligned128((__m128i*)(bottomLeftBase + x));
|
||||
__m128i result1 = BlurFourPixels(topLeft, topRight, bottomRight, bottomLeft, divisor);
|
||||
|
||||
topLeft = loadUnaligned128((__m128i*)(topLeftBase + x + 4));
|
||||
topRight = loadUnaligned128((__m128i*)(topRightBase + x + 4));
|
||||
bottomRight = loadUnaligned128((__m128i*)(bottomRightBase + x + 4));
|
||||
bottomLeft = loadUnaligned128((__m128i*)(bottomLeftBase + x + 4));
|
||||
__m128i result2 = BlurFourPixels(topLeft, topRight, bottomRight, bottomLeft, divisor);
|
||||
|
||||
topLeft = loadUnaligned128((__m128i*)(topLeftBase + x + 8));
|
||||
topRight = loadUnaligned128((__m128i*)(topRightBase + x + 8));
|
||||
bottomRight = loadUnaligned128((__m128i*)(bottomRightBase + x + 8));
|
||||
bottomLeft = loadUnaligned128((__m128i*)(bottomLeftBase + x + 8));
|
||||
__m128i result3 = BlurFourPixels(topLeft, topRight, bottomRight, bottomLeft, divisor);
|
||||
|
||||
topLeft = loadUnaligned128((__m128i*)(topLeftBase + x + 12));
|
||||
topRight = loadUnaligned128((__m128i*)(topRightBase + x + 12));
|
||||
bottomRight = loadUnaligned128((__m128i*)(bottomRightBase + x + 12));
|
||||
bottomLeft = loadUnaligned128((__m128i*)(bottomLeftBase + x + 12));
|
||||
__m128i result4 = BlurFourPixels(topLeft, topRight, bottomRight, bottomLeft, divisor);
|
||||
|
||||
double t;
|
||||
__m128i final;
|
||||
asm volatile (
|
||||
".set push \n\t"
|
||||
".set arch=loongson3a \n\t"
|
||||
_mm_packsswh(r3, r3, r4, t)
|
||||
_mm_packsswh(f, r1, r2, t)
|
||||
_mm_packushb(f, f, r3, t)
|
||||
"gssdlc1 %[fh], 0xf(%[d]) \n\t"
|
||||
"gssdrc1 %[fh], 0x8(%[d]) \n\t"
|
||||
"gssdlc1 %[fl], 0x7(%[d]) \n\t"
|
||||
"gssdrc1 %[fl], 0x0(%[d]) \n\t"
|
||||
".set pop \n\t"
|
||||
:[fh]"=&f"(final.h), [fl]"=&f"(final.l),
|
||||
[r3h]"+f"(result3.h), [r3l]"+f"(result3.l),
|
||||
[t]"=&f"(t)
|
||||
:[r1h]"f"(result1.h), [r1l]"f"(result1.l),
|
||||
[r2h]"f"(result2.h), [r2l]"f"(result2.l),
|
||||
[r4h]"f"(result4.h), [r4l]"f"(result4.l),
|
||||
[d]"r"(data + stride * y + x)
|
||||
:"memory"
|
||||
);
|
||||
}
|
||||
|
||||
// Process the remaining pixels 4 bytes at a time.
|
||||
for (; x < size.width; x += 4) {
|
||||
if (inSkipRectY && x > skipRect.x && x < skipRect.XMost()) {
|
||||
x = skipRect.XMost() - 4;
|
||||
// Trigger early jump on coming loop iterations, this will be reset
|
||||
// next line anyway.
|
||||
inSkipRectY = false;
|
||||
continue;
|
||||
}
|
||||
__m128i topLeft = loadUnaligned128((__m128i*)(topLeftBase + x));
|
||||
__m128i topRight = loadUnaligned128((__m128i*)(topRightBase + x));
|
||||
__m128i bottomRight = loadUnaligned128((__m128i*)(bottomRightBase + x));
|
||||
__m128i bottomLeft = loadUnaligned128((__m128i*)(bottomLeftBase + x));
|
||||
|
||||
__m128i result = BlurFourPixels(topLeft, topRight, bottomRight, bottomLeft, divisor);
|
||||
|
||||
double t;
|
||||
__m128i final;
|
||||
asm volatile (
|
||||
".set push \n\t"
|
||||
".set arch=loongson3a \n\t"
|
||||
_mm_packsswh(f, r, zero, t)
|
||||
_mm_packushb(f, f, zero, t)
|
||||
"swc1 %[fl], (%[d]) \n\t"
|
||||
".set pop \n\t"
|
||||
:[fh]"=&f"(final.h), [fl]"=&f"(final.l),
|
||||
[t]"=&f"(t)
|
||||
:[d]"r"(data + stride * y + x),
|
||||
[rh]"f"(result.h), [rl]"f"(result.l),
|
||||
[zeroh]"f"(zero.h), [zerol]"f"(zero.l)
|
||||
:"memory"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* _MIPS_ARCH_LOONGSON3A */
|
||||
288
gfx/2d/BlurNEON.cpp
Normal file
288
gfx/2d/BlurNEON.cpp
Normal file
|
|
@ -0,0 +1,288 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "Blur.h"
|
||||
#include <arm_neon.h>
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
MOZ_ALWAYS_INLINE
|
||||
uint16x4_t Divide(uint32x4_t aValues, uint32x2_t aDivisor)
|
||||
{
|
||||
uint64x2_t roundingAddition = vdupq_n_u64(int64_t(1) << 31);
|
||||
uint64x2_t multiplied21 = vmull_u32(vget_low_u32(aValues), aDivisor);
|
||||
uint64x2_t multiplied43 = vmull_u32(vget_high_u32(aValues), aDivisor);
|
||||
return vqmovn_u32(vcombine_u32(vshrn_n_u64(vaddq_u64(multiplied21, roundingAddition), 32),
|
||||
vshrn_n_u64(vaddq_u64(multiplied43, roundingAddition), 32)));
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE
|
||||
uint16x4_t BlurFourPixels(const uint32x4_t& aTopLeft, const uint32x4_t& aTopRight,
|
||||
const uint32x4_t& aBottomRight, const uint32x4_t& aBottomLeft,
|
||||
const uint32x2_t& aDivisor)
|
||||
{
|
||||
uint32x4_t values = vaddq_u32(vsubq_u32(vsubq_u32(aBottomRight, aTopRight), aBottomLeft), aTopLeft);
|
||||
return Divide(values, aDivisor);
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE
|
||||
void LoadIntegralRowFromRow(uint32_t *aDest, const uint8_t *aSource,
|
||||
int32_t aSourceWidth, int32_t aLeftInflation,
|
||||
int32_t aRightInflation)
|
||||
{
|
||||
int32_t currentRowSum = 0;
|
||||
|
||||
for (int x = 0; x < aLeftInflation; x++) {
|
||||
currentRowSum += aSource[0];
|
||||
aDest[x] = currentRowSum;
|
||||
}
|
||||
for (int x = aLeftInflation; x < (aSourceWidth + aLeftInflation); x++) {
|
||||
currentRowSum += aSource[(x - aLeftInflation)];
|
||||
aDest[x] = currentRowSum;
|
||||
}
|
||||
for (int x = (aSourceWidth + aLeftInflation); x < (aSourceWidth + aLeftInflation + aRightInflation); x++) {
|
||||
currentRowSum += aSource[aSourceWidth - 1];
|
||||
aDest[x] = currentRowSum;
|
||||
}
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE void
|
||||
GenerateIntegralImage_NEON(int32_t aLeftInflation, int32_t aRightInflation,
|
||||
int32_t aTopInflation, int32_t aBottomInflation,
|
||||
uint32_t *aIntegralImage, size_t aIntegralImageStride,
|
||||
uint8_t *aSource, int32_t aSourceStride, const IntSize &aSize)
|
||||
{
|
||||
MOZ_ASSERT(!(aLeftInflation & 3));
|
||||
|
||||
uint32_t stride32bit = aIntegralImageStride / 4;
|
||||
IntSize integralImageSize(aSize.width + aLeftInflation + aRightInflation,
|
||||
aSize.height + aTopInflation + aBottomInflation);
|
||||
|
||||
LoadIntegralRowFromRow(aIntegralImage, aSource, aSize.width, aLeftInflation, aRightInflation);
|
||||
|
||||
for (int y = 1; y < aTopInflation + 1; y++) {
|
||||
uint32_t *intRow = aIntegralImage + (y * stride32bit);
|
||||
uint32_t *intPrevRow = aIntegralImage + (y - 1) * stride32bit;
|
||||
uint32_t *intFirstRow = aIntegralImage;
|
||||
|
||||
for (int x = 0; x < integralImageSize.width; x += 4) {
|
||||
uint32x4_t firstRow = vld1q_u32(intFirstRow + x);
|
||||
uint32x4_t previousRow = vld1q_u32(intPrevRow + x);
|
||||
vst1q_u32(intRow + x, vaddq_u32(firstRow, previousRow));
|
||||
}
|
||||
}
|
||||
|
||||
for (int y = aTopInflation + 1; y < (aSize.height + aTopInflation); y++) {
|
||||
uint32x4_t currentRowSum = vdupq_n_u32(0);
|
||||
uint32_t *intRow = aIntegralImage + (y * stride32bit);
|
||||
uint32_t *intPrevRow = aIntegralImage + (y - 1) * stride32bit;
|
||||
uint8_t *sourceRow = aSource + aSourceStride * (y - aTopInflation);
|
||||
|
||||
uint32_t pixel = sourceRow[0];
|
||||
for (int x = 0; x < aLeftInflation; x += 4) {
|
||||
uint32_t temp[4];
|
||||
temp[0] = pixel;
|
||||
temp[1] = temp[0] + pixel;
|
||||
temp[2] = temp[1] + pixel;
|
||||
temp[3] = temp[2] + pixel;
|
||||
uint32x4_t sumPixels = vld1q_u32(temp);
|
||||
sumPixels = vaddq_u32(sumPixels, currentRowSum);
|
||||
currentRowSum = vdupq_n_u32(vgetq_lane_u32(sumPixels, 3));
|
||||
vst1q_u32(intRow + x, vaddq_u32(sumPixels, vld1q_u32(intPrevRow + x)));
|
||||
}
|
||||
|
||||
for (int x = aLeftInflation; x < (aSize.width + aLeftInflation); x += 4) {
|
||||
// It's important to shuffle here. When we exit this loop currentRowSum
|
||||
// has to be set to sumPixels, so that the following loop can get the
|
||||
// correct pixel for the currentRowSum. The highest order pixel in
|
||||
// currentRowSum could've originated from accumulation in the stride.
|
||||
currentRowSum = vdupq_n_u32(vgetq_lane_u32(currentRowSum, 3));
|
||||
|
||||
uint32_t temp[4];
|
||||
temp[0] = *(sourceRow + (x - aLeftInflation));
|
||||
temp[1] = temp[0] + *(sourceRow + (x - aLeftInflation) + 1);
|
||||
temp[2] = temp[1] + *(sourceRow + (x - aLeftInflation) + 2);
|
||||
temp[3] = temp[2] + *(sourceRow + (x - aLeftInflation) + 3);
|
||||
uint32x4_t sumPixels = vld1q_u32(temp);
|
||||
sumPixels = vaddq_u32(sumPixels, currentRowSum);
|
||||
currentRowSum = sumPixels;
|
||||
vst1q_u32(intRow + x, vaddq_u32(sumPixels, vld1q_u32(intPrevRow + x)));
|
||||
}
|
||||
|
||||
pixel = sourceRow[aSize.width - 1];
|
||||
int x = (aSize.width + aLeftInflation);
|
||||
if ((aSize.width & 3)) {
|
||||
// Deal with unaligned portion. Get the correct pixel from currentRowSum,
|
||||
// see explanation above.
|
||||
uint32_t intCurrentRowSum = ((uint32_t*)¤tRowSum)[(aSize.width % 4) - 1];
|
||||
for (; x < integralImageSize.width; x++) {
|
||||
// We could be unaligned here!
|
||||
if (!(x & 3)) {
|
||||
// aligned!
|
||||
currentRowSum = vdupq_n_u32(intCurrentRowSum);
|
||||
break;
|
||||
}
|
||||
intCurrentRowSum += pixel;
|
||||
intRow[x] = intPrevRow[x] + intCurrentRowSum;
|
||||
}
|
||||
} else {
|
||||
currentRowSum = vdupq_n_u32(vgetq_lane_u32(currentRowSum, 3));
|
||||
}
|
||||
|
||||
for (; x < integralImageSize.width; x += 4) {
|
||||
uint32_t temp[4];
|
||||
temp[0] = pixel;
|
||||
temp[1] = temp[0] + pixel;
|
||||
temp[2] = temp[1] + pixel;
|
||||
temp[3] = temp[2] + pixel;
|
||||
uint32x4_t sumPixels = vld1q_u32(temp);
|
||||
sumPixels = vaddq_u32(sumPixels, currentRowSum);
|
||||
currentRowSum = vdupq_n_u32(vgetq_lane_u32(sumPixels, 3));
|
||||
vst1q_u32(intRow + x, vaddq_u32(sumPixels, vld1q_u32(intPrevRow + x)));
|
||||
}
|
||||
}
|
||||
|
||||
if (aBottomInflation) {
|
||||
// Store the last valid row of our source image in the last row of
|
||||
// our integral image. This will be overwritten with the correct values
|
||||
// in the upcoming loop.
|
||||
LoadIntegralRowFromRow(aIntegralImage + (integralImageSize.height - 1) * stride32bit,
|
||||
aSource + (aSize.height - 1) * aSourceStride, aSize.width, aLeftInflation, aRightInflation);
|
||||
|
||||
for (int y = aSize.height + aTopInflation; y < integralImageSize.height; y++) {
|
||||
uint32_t *intRow = aIntegralImage + (y * stride32bit);
|
||||
uint32_t *intPrevRow = aIntegralImage + (y - 1) * stride32bit;
|
||||
uint32_t *intLastRow = aIntegralImage + (integralImageSize.height - 1) * stride32bit;
|
||||
for (int x = 0; x < integralImageSize.width; x += 4) {
|
||||
vst1q_u32(intRow + x,
|
||||
vaddq_u32(vld1q_u32(intLastRow + x),
|
||||
vld1q_u32(intPrevRow + x)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to do an in-place box blur using an integral image.
|
||||
*/
|
||||
void
|
||||
AlphaBoxBlur::BoxBlur_NEON(uint8_t* aData,
|
||||
int32_t aLeftLobe,
|
||||
int32_t aRightLobe,
|
||||
int32_t aTopLobe,
|
||||
int32_t aBottomLobe,
|
||||
uint32_t *aIntegralImage,
|
||||
size_t aIntegralImageStride)
|
||||
{
|
||||
IntSize size = GetSize();
|
||||
|
||||
MOZ_ASSERT(size.height > 0);
|
||||
|
||||
// Our 'left' or 'top' lobe will include the current pixel. i.e. when
|
||||
// looking at an integral image the value of a pixel at 'x,y' is calculated
|
||||
// using the value of the integral image values above/below that.
|
||||
aLeftLobe++;
|
||||
aTopLobe++;
|
||||
int32_t boxSize = (aLeftLobe + aRightLobe) * (aTopLobe + aBottomLobe);
|
||||
|
||||
MOZ_ASSERT(boxSize > 0);
|
||||
|
||||
if (boxSize == 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t reciprocal = uint32_t((uint64_t(1) << 32) / boxSize);
|
||||
uint32_t stride32bit = aIntegralImageStride / 4;
|
||||
int32_t leftInflation = RoundUpToMultipleOf4(aLeftLobe).value();
|
||||
|
||||
GenerateIntegralImage_NEON(leftInflation, aRightLobe, aTopLobe, aBottomLobe,
|
||||
aIntegralImage, aIntegralImageStride, aData,
|
||||
mStride, size);
|
||||
|
||||
uint32x2_t divisor = vdup_n_u32(reciprocal);
|
||||
|
||||
// This points to the start of the rectangle within the IntegralImage that overlaps
|
||||
// the surface being blurred.
|
||||
uint32_t *innerIntegral = aIntegralImage + (aTopLobe * stride32bit) + leftInflation;
|
||||
IntRect skipRect = mSkipRect;
|
||||
int32_t stride = mStride;
|
||||
uint8_t *data = aData;
|
||||
|
||||
for (int32_t y = 0; y < size.height; y++) {
|
||||
bool inSkipRectY = y > skipRect.y && y < skipRect.YMost();
|
||||
uint32_t *topLeftBase = innerIntegral + ((y - aTopLobe) * ptrdiff_t(stride32bit) - aLeftLobe);
|
||||
uint32_t *topRightBase = innerIntegral + ((y - aTopLobe) * ptrdiff_t(stride32bit) + aRightLobe);
|
||||
uint32_t *bottomRightBase = innerIntegral + ((y + aBottomLobe) * ptrdiff_t(stride32bit) + aRightLobe);
|
||||
uint32_t *bottomLeftBase = innerIntegral + ((y + aBottomLobe) * ptrdiff_t(stride32bit) - aLeftLobe);
|
||||
|
||||
int32_t x = 0;
|
||||
// Process 16 pixels at a time for as long as possible.
|
||||
for (; x <= size.width - 16; x += 16) {
|
||||
if (inSkipRectY && x > skipRect.x && x < skipRect.XMost()) {
|
||||
x = skipRect.XMost() - 16;
|
||||
// Trigger early jump on coming loop iterations, this will be reset
|
||||
// next line anyway.
|
||||
inSkipRectY = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32x4_t topLeft;
|
||||
uint32x4_t topRight;
|
||||
uint32x4_t bottomRight;
|
||||
uint32x4_t bottomLeft;
|
||||
topLeft = vld1q_u32(topLeftBase + x);
|
||||
topRight = vld1q_u32(topRightBase + x);
|
||||
bottomRight = vld1q_u32(bottomRightBase + x);
|
||||
bottomLeft = vld1q_u32(bottomLeftBase + x);
|
||||
uint16x4_t result1 = BlurFourPixels(topLeft, topRight, bottomRight, bottomLeft, divisor);
|
||||
|
||||
topLeft = vld1q_u32(topLeftBase + x + 4);
|
||||
topRight = vld1q_u32(topRightBase + x + 4);
|
||||
bottomRight = vld1q_u32(bottomRightBase + x + 4);
|
||||
bottomLeft = vld1q_u32(bottomLeftBase + x + 4);
|
||||
uint16x4_t result2 = BlurFourPixels(topLeft, topRight, bottomRight, bottomLeft, divisor);
|
||||
|
||||
topLeft = vld1q_u32(topLeftBase + x + 8);
|
||||
topRight = vld1q_u32(topRightBase + x + 8);
|
||||
bottomRight = vld1q_u32(bottomRightBase + x + 8);
|
||||
bottomLeft = vld1q_u32(bottomLeftBase + x + 8);
|
||||
uint16x4_t result3 = BlurFourPixels(topLeft, topRight, bottomRight, bottomLeft, divisor);
|
||||
|
||||
topLeft = vld1q_u32(topLeftBase + x + 12);
|
||||
topRight = vld1q_u32(topRightBase + x + 12);
|
||||
bottomRight = vld1q_u32(bottomRightBase + x + 12);
|
||||
bottomLeft = vld1q_u32(bottomLeftBase + x + 12);
|
||||
uint16x4_t result4 = BlurFourPixels(topLeft, topRight, bottomRight, bottomLeft, divisor);
|
||||
|
||||
uint8x8_t combine1 = vqmovn_u16(vcombine_u16(result1, result2));
|
||||
uint8x8_t combine2 = vqmovn_u16(vcombine_u16(result3, result4));
|
||||
uint8x16_t final = vcombine_u8(combine1, combine2);
|
||||
vst1q_u8(data + stride * y + x, final);
|
||||
}
|
||||
|
||||
// Process the remaining pixels 4 bytes at a time.
|
||||
for (; x < size.width; x += 4) {
|
||||
if (inSkipRectY && x > skipRect.x && x < skipRect.XMost()) {
|
||||
x = skipRect.XMost() - 4;
|
||||
// Trigger early jump on coming loop iterations, this will be reset
|
||||
// next line anyway.
|
||||
inSkipRectY = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32x4_t topLeft = vld1q_u32(topLeftBase + x);
|
||||
uint32x4_t topRight = vld1q_u32(topRightBase + x);
|
||||
uint32x4_t bottomRight = vld1q_u32(bottomRightBase + x);
|
||||
uint32x4_t bottomLeft = vld1q_u32(bottomLeftBase + x);
|
||||
uint16x4_t result = BlurFourPixels(topLeft, topRight, bottomRight, bottomLeft, divisor);
|
||||
uint32x2_t final = vreinterpret_u32_u8(vmovn_u16(vcombine_u16(result, vdup_n_u16(0))));
|
||||
*(uint32_t*)(data + stride * y + x) = vget_lane_u32(final, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
315
gfx/2d/BlurSSE2.cpp
Normal file
315
gfx/2d/BlurSSE2.cpp
Normal file
|
|
@ -0,0 +1,315 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "Blur.h"
|
||||
|
||||
#include "SSEHelpers.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
MOZ_ALWAYS_INLINE
|
||||
__m128i Divide(__m128i aValues, __m128i aDivisor)
|
||||
{
|
||||
const __m128i mask = _mm_setr_epi32(0x0, 0xffffffff, 0x0, 0xffffffff);
|
||||
static const union {
|
||||
int64_t i64[2];
|
||||
__m128i m;
|
||||
} roundingAddition = { { int64_t(1) << 31, int64_t(1) << 31 } };
|
||||
|
||||
__m128i multiplied31 = _mm_mul_epu32(aValues, aDivisor);
|
||||
__m128i multiplied42 = _mm_mul_epu32(_mm_srli_epi64(aValues, 32), aDivisor);
|
||||
|
||||
// Add 1 << 31 before shifting or masking the lower 32 bits away, so that the
|
||||
// result is rounded.
|
||||
__m128i p_3_1 = _mm_srli_epi64(_mm_add_epi64(multiplied31, roundingAddition.m), 32);
|
||||
__m128i p4_2_ = _mm_and_si128(_mm_add_epi64(multiplied42, roundingAddition.m), mask);
|
||||
__m128i p4321 = _mm_or_si128(p_3_1, p4_2_);
|
||||
return p4321;
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE
|
||||
__m128i BlurFourPixels(const __m128i& aTopLeft, const __m128i& aTopRight,
|
||||
const __m128i& aBottomRight, const __m128i& aBottomLeft,
|
||||
const __m128i& aDivisor)
|
||||
{
|
||||
__m128i values = _mm_add_epi32(_mm_sub_epi32(_mm_sub_epi32(aBottomRight, aTopRight), aBottomLeft), aTopLeft);
|
||||
return Divide(values, aDivisor);
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE
|
||||
void LoadIntegralRowFromRow(uint32_t *aDest, const uint8_t *aSource,
|
||||
int32_t aSourceWidth, int32_t aLeftInflation,
|
||||
int32_t aRightInflation)
|
||||
{
|
||||
int32_t currentRowSum = 0;
|
||||
|
||||
for (int x = 0; x < aLeftInflation; x++) {
|
||||
currentRowSum += aSource[0];
|
||||
aDest[x] = currentRowSum;
|
||||
}
|
||||
for (int x = aLeftInflation; x < (aSourceWidth + aLeftInflation); x++) {
|
||||
currentRowSum += aSource[(x - aLeftInflation)];
|
||||
aDest[x] = currentRowSum;
|
||||
}
|
||||
for (int x = (aSourceWidth + aLeftInflation); x < (aSourceWidth + aLeftInflation + aRightInflation); x++) {
|
||||
currentRowSum += aSource[aSourceWidth - 1];
|
||||
aDest[x] = currentRowSum;
|
||||
}
|
||||
}
|
||||
|
||||
// This function calculates an integral of four pixels stored in the 4
|
||||
// 32-bit integers on aPixels. i.e. for { 30, 50, 80, 100 } this returns
|
||||
// { 30, 80, 160, 260 }. This seems to be the fastest way to do this after
|
||||
// much testing.
|
||||
MOZ_ALWAYS_INLINE
|
||||
__m128i AccumulatePixelSums(__m128i aPixels)
|
||||
{
|
||||
__m128i sumPixels = aPixels;
|
||||
__m128i currentPixels = _mm_slli_si128(aPixels, 4);
|
||||
sumPixels = _mm_add_epi32(sumPixels, currentPixels);
|
||||
currentPixels = _mm_unpacklo_epi64(_mm_setzero_si128(), sumPixels);
|
||||
|
||||
return _mm_add_epi32(sumPixels, currentPixels);
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE void
|
||||
GenerateIntegralImage_SSE2(int32_t aLeftInflation, int32_t aRightInflation,
|
||||
int32_t aTopInflation, int32_t aBottomInflation,
|
||||
uint32_t *aIntegralImage, size_t aIntegralImageStride,
|
||||
uint8_t *aSource, int32_t aSourceStride, const IntSize &aSize)
|
||||
{
|
||||
MOZ_ASSERT(!(aLeftInflation & 3));
|
||||
|
||||
uint32_t stride32bit = aIntegralImageStride / 4;
|
||||
|
||||
IntSize integralImageSize(aSize.width + aLeftInflation + aRightInflation,
|
||||
aSize.height + aTopInflation + aBottomInflation);
|
||||
|
||||
LoadIntegralRowFromRow(aIntegralImage, aSource, aSize.width, aLeftInflation, aRightInflation);
|
||||
|
||||
for (int y = 1; y < aTopInflation + 1; y++) {
|
||||
uint32_t *intRow = aIntegralImage + (y * stride32bit);
|
||||
uint32_t *intPrevRow = aIntegralImage + (y - 1) * stride32bit;
|
||||
uint32_t *intFirstRow = aIntegralImage;
|
||||
|
||||
for (int x = 0; x < integralImageSize.width; x += 4) {
|
||||
__m128i firstRow = _mm_load_si128((__m128i*)(intFirstRow + x));
|
||||
__m128i previousRow = _mm_load_si128((__m128i*)(intPrevRow + x));
|
||||
_mm_store_si128((__m128i*)(intRow + x), _mm_add_epi32(firstRow, previousRow));
|
||||
}
|
||||
}
|
||||
|
||||
for (int y = aTopInflation + 1; y < (aSize.height + aTopInflation); y++) {
|
||||
__m128i currentRowSum = _mm_setzero_si128();
|
||||
uint32_t *intRow = aIntegralImage + (y * stride32bit);
|
||||
uint32_t *intPrevRow = aIntegralImage + (y - 1) * stride32bit;
|
||||
uint8_t *sourceRow = aSource + aSourceStride * (y - aTopInflation);
|
||||
|
||||
uint32_t pixel = sourceRow[0];
|
||||
for (int x = 0; x < aLeftInflation; x += 4) {
|
||||
__m128i sumPixels = AccumulatePixelSums(_mm_shuffle_epi32(_mm_set1_epi32(pixel), _MM_SHUFFLE(0, 0, 0, 0)));
|
||||
|
||||
sumPixels = _mm_add_epi32(sumPixels, currentRowSum);
|
||||
|
||||
currentRowSum = _mm_shuffle_epi32(sumPixels, _MM_SHUFFLE(3, 3, 3, 3));
|
||||
|
||||
_mm_store_si128((__m128i*)(intRow + x), _mm_add_epi32(sumPixels, _mm_load_si128((__m128i*)(intPrevRow + x))));
|
||||
}
|
||||
for (int x = aLeftInflation; x < (aSize.width + aLeftInflation); x += 4) {
|
||||
uint32_t pixels = *(uint32_t*)(sourceRow + (x - aLeftInflation));
|
||||
|
||||
// It's important to shuffle here. When we exit this loop currentRowSum
|
||||
// has to be set to sumPixels, so that the following loop can get the
|
||||
// correct pixel for the currentRowSum. The highest order pixel in
|
||||
// currentRowSum could've originated from accumulation in the stride.
|
||||
currentRowSum = _mm_shuffle_epi32(currentRowSum, _MM_SHUFFLE(3, 3, 3, 3));
|
||||
|
||||
__m128i sumPixels = AccumulatePixelSums(_mm_unpacklo_epi16(_mm_unpacklo_epi8( _mm_set1_epi32(pixels), _mm_setzero_si128()), _mm_setzero_si128()));
|
||||
sumPixels = _mm_add_epi32(sumPixels, currentRowSum);
|
||||
|
||||
currentRowSum = sumPixels;
|
||||
|
||||
_mm_store_si128((__m128i*)(intRow + x), _mm_add_epi32(sumPixels, _mm_load_si128((__m128i*)(intPrevRow + x))));
|
||||
}
|
||||
|
||||
pixel = sourceRow[aSize.width - 1];
|
||||
int x = (aSize.width + aLeftInflation);
|
||||
if ((aSize.width & 3)) {
|
||||
// Deal with unaligned portion. Get the correct pixel from currentRowSum,
|
||||
// see explanation above.
|
||||
uint32_t intCurrentRowSum = ((uint32_t*)¤tRowSum)[(aSize.width % 4) - 1];
|
||||
for (; x < integralImageSize.width; x++) {
|
||||
// We could be unaligned here!
|
||||
if (!(x & 3)) {
|
||||
// aligned!
|
||||
currentRowSum = _mm_set1_epi32(intCurrentRowSum);
|
||||
break;
|
||||
}
|
||||
intCurrentRowSum += pixel;
|
||||
intRow[x] = intPrevRow[x] + intCurrentRowSum;
|
||||
}
|
||||
} else {
|
||||
currentRowSum = _mm_shuffle_epi32(currentRowSum, _MM_SHUFFLE(3, 3, 3, 3));
|
||||
}
|
||||
for (; x < integralImageSize.width; x += 4) {
|
||||
__m128i sumPixels = AccumulatePixelSums(_mm_set1_epi32(pixel));
|
||||
|
||||
sumPixels = _mm_add_epi32(sumPixels, currentRowSum);
|
||||
|
||||
currentRowSum = _mm_shuffle_epi32(sumPixels, _MM_SHUFFLE(3, 3, 3, 3));
|
||||
|
||||
_mm_store_si128((__m128i*)(intRow + x), _mm_add_epi32(sumPixels, _mm_load_si128((__m128i*)(intPrevRow + x))));
|
||||
}
|
||||
}
|
||||
|
||||
if (aBottomInflation) {
|
||||
// Store the last valid row of our source image in the last row of
|
||||
// our integral image. This will be overwritten with the correct values
|
||||
// in the upcoming loop.
|
||||
LoadIntegralRowFromRow(aIntegralImage + (integralImageSize.height - 1) * stride32bit,
|
||||
aSource + (aSize.height - 1) * aSourceStride, aSize.width, aLeftInflation, aRightInflation);
|
||||
|
||||
|
||||
for (int y = aSize.height + aTopInflation; y < integralImageSize.height; y++) {
|
||||
__m128i *intRow = (__m128i*)(aIntegralImage + (y * stride32bit));
|
||||
__m128i *intPrevRow = (__m128i*)(aIntegralImage + (y - 1) * stride32bit);
|
||||
__m128i *intLastRow = (__m128i*)(aIntegralImage + (integralImageSize.height - 1) * stride32bit);
|
||||
|
||||
for (int x = 0; x < integralImageSize.width; x += 4) {
|
||||
_mm_store_si128(intRow + (x / 4),
|
||||
_mm_add_epi32(_mm_load_si128(intLastRow + (x / 4)),
|
||||
_mm_load_si128(intPrevRow + (x / 4))));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to do an in-place box blur using an integral image.
|
||||
*/
|
||||
void
|
||||
AlphaBoxBlur::BoxBlur_SSE2(uint8_t* aData,
|
||||
int32_t aLeftLobe,
|
||||
int32_t aRightLobe,
|
||||
int32_t aTopLobe,
|
||||
int32_t aBottomLobe,
|
||||
uint32_t *aIntegralImage,
|
||||
size_t aIntegralImageStride)
|
||||
{
|
||||
IntSize size = GetSize();
|
||||
|
||||
MOZ_ASSERT(size.height > 0);
|
||||
|
||||
// Our 'left' or 'top' lobe will include the current pixel. i.e. when
|
||||
// looking at an integral image the value of a pixel at 'x,y' is calculated
|
||||
// using the value of the integral image values above/below that.
|
||||
aLeftLobe++;
|
||||
aTopLobe++;
|
||||
int32_t boxSize = (aLeftLobe + aRightLobe) * (aTopLobe + aBottomLobe);
|
||||
|
||||
MOZ_ASSERT(boxSize > 0);
|
||||
|
||||
if (boxSize == 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t reciprocal = uint32_t((uint64_t(1) << 32) / boxSize);
|
||||
|
||||
uint32_t stride32bit = aIntegralImageStride / 4;
|
||||
int32_t leftInflation = RoundUpToMultipleOf4(aLeftLobe).value();
|
||||
|
||||
GenerateIntegralImage_SSE2(leftInflation, aRightLobe, aTopLobe, aBottomLobe,
|
||||
aIntegralImage, aIntegralImageStride, aData,
|
||||
mStride, size);
|
||||
|
||||
__m128i divisor = _mm_set1_epi32(reciprocal);
|
||||
|
||||
// This points to the start of the rectangle within the IntegralImage that overlaps
|
||||
// the surface being blurred.
|
||||
uint32_t *innerIntegral = aIntegralImage + (aTopLobe * stride32bit) + leftInflation;
|
||||
|
||||
IntRect skipRect = mSkipRect;
|
||||
int32_t stride = mStride;
|
||||
uint8_t *data = aData;
|
||||
for (int32_t y = 0; y < size.height; y++) {
|
||||
bool inSkipRectY = y > skipRect.y && y < skipRect.YMost();
|
||||
|
||||
uint32_t *topLeftBase = innerIntegral + ((y - aTopLobe) * ptrdiff_t(stride32bit) - aLeftLobe);
|
||||
uint32_t *topRightBase = innerIntegral + ((y - aTopLobe) * ptrdiff_t(stride32bit) + aRightLobe);
|
||||
uint32_t *bottomRightBase = innerIntegral + ((y + aBottomLobe) * ptrdiff_t(stride32bit) + aRightLobe);
|
||||
uint32_t *bottomLeftBase = innerIntegral + ((y + aBottomLobe) * ptrdiff_t(stride32bit) - aLeftLobe);
|
||||
|
||||
int32_t x = 0;
|
||||
// Process 16 pixels at a time for as long as possible.
|
||||
for (; x <= size.width - 16; x += 16) {
|
||||
if (inSkipRectY && x > skipRect.x && x < skipRect.XMost()) {
|
||||
x = skipRect.XMost() - 16;
|
||||
// Trigger early jump on coming loop iterations, this will be reset
|
||||
// next line anyway.
|
||||
inSkipRectY = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
__m128i topLeft;
|
||||
__m128i topRight;
|
||||
__m128i bottomRight;
|
||||
__m128i bottomLeft;
|
||||
|
||||
topLeft = loadUnaligned128((__m128i*)(topLeftBase + x));
|
||||
topRight = loadUnaligned128((__m128i*)(topRightBase + x));
|
||||
bottomRight = loadUnaligned128((__m128i*)(bottomRightBase + x));
|
||||
bottomLeft = loadUnaligned128((__m128i*)(bottomLeftBase + x));
|
||||
__m128i result1 = BlurFourPixels(topLeft, topRight, bottomRight, bottomLeft, divisor);
|
||||
|
||||
topLeft = loadUnaligned128((__m128i*)(topLeftBase + x + 4));
|
||||
topRight = loadUnaligned128((__m128i*)(topRightBase + x + 4));
|
||||
bottomRight = loadUnaligned128((__m128i*)(bottomRightBase + x + 4));
|
||||
bottomLeft = loadUnaligned128((__m128i*)(bottomLeftBase + x + 4));
|
||||
__m128i result2 = BlurFourPixels(topLeft, topRight, bottomRight, bottomLeft, divisor);
|
||||
|
||||
topLeft = loadUnaligned128((__m128i*)(topLeftBase + x + 8));
|
||||
topRight = loadUnaligned128((__m128i*)(topRightBase + x + 8));
|
||||
bottomRight = loadUnaligned128((__m128i*)(bottomRightBase + x + 8));
|
||||
bottomLeft = loadUnaligned128((__m128i*)(bottomLeftBase + x + 8));
|
||||
__m128i result3 = BlurFourPixels(topLeft, topRight, bottomRight, bottomLeft, divisor);
|
||||
|
||||
topLeft = loadUnaligned128((__m128i*)(topLeftBase + x + 12));
|
||||
topRight = loadUnaligned128((__m128i*)(topRightBase + x + 12));
|
||||
bottomRight = loadUnaligned128((__m128i*)(bottomRightBase + x + 12));
|
||||
bottomLeft = loadUnaligned128((__m128i*)(bottomLeftBase + x + 12));
|
||||
__m128i result4 = BlurFourPixels(topLeft, topRight, bottomRight, bottomLeft, divisor);
|
||||
|
||||
__m128i final = _mm_packus_epi16(_mm_packs_epi32(result1, result2), _mm_packs_epi32(result3, result4));
|
||||
|
||||
_mm_storeu_si128((__m128i*)(data + stride * y + x), final);
|
||||
}
|
||||
|
||||
// Process the remaining pixels 4 bytes at a time.
|
||||
for (; x < size.width; x += 4) {
|
||||
if (inSkipRectY && x > skipRect.x && x < skipRect.XMost()) {
|
||||
x = skipRect.XMost() - 4;
|
||||
// Trigger early jump on coming loop iterations, this will be reset
|
||||
// next line anyway.
|
||||
inSkipRectY = false;
|
||||
continue;
|
||||
}
|
||||
__m128i topLeft = loadUnaligned128((__m128i*)(topLeftBase + x));
|
||||
__m128i topRight = loadUnaligned128((__m128i*)(topRightBase + x));
|
||||
__m128i bottomRight = loadUnaligned128((__m128i*)(bottomRightBase + x));
|
||||
__m128i bottomLeft = loadUnaligned128((__m128i*)(bottomLeftBase + x));
|
||||
|
||||
__m128i result = BlurFourPixels(topLeft, topRight, bottomRight, bottomLeft, divisor);
|
||||
__m128i final = _mm_packus_epi16(_mm_packs_epi32(result, _mm_setzero_si128()), _mm_setzero_si128());
|
||||
|
||||
*(uint32_t*)(data + stride * y + x) = _mm_cvtsi128_si32(final);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
||||
217
gfx/2d/BorrowedContext.h
Normal file
217
gfx/2d/BorrowedContext.h
Normal file
|
|
@ -0,0 +1,217 @@
|
|||
/* -*- Mode: C++; tab-width: 20; 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_GFX_BORROWED_CONTEXT_H
|
||||
#define _MOZILLA_GFX_BORROWED_CONTEXT_H
|
||||
|
||||
#include "2D.h"
|
||||
|
||||
#ifdef MOZ_X11
|
||||
#include <X11/extensions/Xrender.h>
|
||||
#include <X11/Xlib.h>
|
||||
#include "X11UndefineNone.h"
|
||||
#endif
|
||||
|
||||
struct _cairo;
|
||||
typedef struct _cairo cairo_t;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace gfx {
|
||||
|
||||
/* This is a helper class that let's you borrow a cairo_t from a
|
||||
* DrawTargetCairo. This is used for drawing themed widgets.
|
||||
*
|
||||
* Callers should check the cr member after constructing the object
|
||||
* to see if it succeeded. The DrawTarget should not be used while
|
||||
* the context is borrowed. */
|
||||
class BorrowedCairoContext
|
||||
{
|
||||
public:
|
||||
BorrowedCairoContext()
|
||||
: mCairo(nullptr)
|
||||
, mDT(nullptr)
|
||||
{ }
|
||||
|
||||
explicit BorrowedCairoContext(DrawTarget *aDT)
|
||||
: mDT(aDT)
|
||||
{
|
||||
mCairo = BorrowCairoContextFromDrawTarget(aDT);
|
||||
}
|
||||
|
||||
// We can optionally Init after construction in
|
||||
// case we don't know what the DT will be at construction
|
||||
// time.
|
||||
cairo_t *Init(DrawTarget *aDT)
|
||||
{
|
||||
MOZ_ASSERT(!mDT, "Can't initialize twice!");
|
||||
mDT = aDT;
|
||||
return mCairo = BorrowCairoContextFromDrawTarget(aDT);
|
||||
}
|
||||
|
||||
// The caller needs to call Finish if cr is non-null when
|
||||
// they are done with the context. This is currently explicit
|
||||
// instead of happening implicitly in the destructor to make
|
||||
// what's happening in the caller more clear. It also
|
||||
// let's you resume using the DrawTarget in the same scope.
|
||||
void Finish()
|
||||
{
|
||||
if (mCairo) {
|
||||
ReturnCairoContextToDrawTarget(mDT, mCairo);
|
||||
mCairo = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
~BorrowedCairoContext() {
|
||||
MOZ_ASSERT(!mCairo);
|
||||
}
|
||||
|
||||
cairo_t *mCairo;
|
||||
private:
|
||||
static cairo_t* BorrowCairoContextFromDrawTarget(DrawTarget *aDT);
|
||||
static void ReturnCairoContextToDrawTarget(DrawTarget *aDT, cairo_t *aCairo);
|
||||
DrawTarget *mDT;
|
||||
};
|
||||
|
||||
#ifdef MOZ_X11
|
||||
/* This is a helper class that let's you borrow an Xlib drawable from
|
||||
* a DrawTarget. This is used for drawing themed widgets.
|
||||
*
|
||||
* Callers should check the Xlib drawable after constructing the object
|
||||
* to see if it succeeded. The DrawTarget should not be used while
|
||||
* the drawable is borrowed. */
|
||||
class BorrowedXlibDrawable
|
||||
{
|
||||
public:
|
||||
BorrowedXlibDrawable()
|
||||
: mDT(nullptr),
|
||||
mDisplay(nullptr),
|
||||
mDrawable(X11None),
|
||||
mScreen(nullptr),
|
||||
mVisual(nullptr),
|
||||
mXRenderFormat(nullptr)
|
||||
{}
|
||||
|
||||
explicit BorrowedXlibDrawable(DrawTarget *aDT)
|
||||
: mDT(nullptr),
|
||||
mDisplay(nullptr),
|
||||
mDrawable(X11None),
|
||||
mScreen(nullptr),
|
||||
mVisual(nullptr),
|
||||
mXRenderFormat(nullptr)
|
||||
{
|
||||
Init(aDT);
|
||||
}
|
||||
|
||||
// We can optionally Init after construction in
|
||||
// case we don't know what the DT will be at construction
|
||||
// time.
|
||||
bool Init(DrawTarget *aDT);
|
||||
|
||||
// The caller needs to call Finish if drawable is non-zero when
|
||||
// they are done with the context. This is currently explicit
|
||||
// instead of happening implicitly in the destructor to make
|
||||
// what's happening in the caller more clear. It also
|
||||
// let's you resume using the DrawTarget in the same scope.
|
||||
void Finish();
|
||||
|
||||
~BorrowedXlibDrawable() {
|
||||
MOZ_ASSERT(!mDrawable);
|
||||
}
|
||||
|
||||
Display *GetDisplay() const { return mDisplay; }
|
||||
Drawable GetDrawable() const { return mDrawable; }
|
||||
Screen *GetScreen() const { return mScreen; }
|
||||
Visual *GetVisual() const { return mVisual; }
|
||||
IntSize GetSize() const { return mSize; }
|
||||
Point GetOffset() const { return mOffset; }
|
||||
|
||||
XRenderPictFormat* GetXRenderFormat() const { return mXRenderFormat; }
|
||||
|
||||
private:
|
||||
DrawTarget *mDT;
|
||||
Display *mDisplay;
|
||||
Drawable mDrawable;
|
||||
Screen *mScreen;
|
||||
Visual *mVisual;
|
||||
XRenderPictFormat *mXRenderFormat;
|
||||
IntSize mSize;
|
||||
Point mOffset;
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef XP_DARWIN
|
||||
/* This is a helper class that let's you borrow a CGContextRef from a
|
||||
* DrawTargetCG. This is used for drawing themed widgets.
|
||||
*
|
||||
* Callers should check the cg member after constructing the object
|
||||
* to see if it succeeded. The DrawTarget should not be used while
|
||||
* the context is borrowed. */
|
||||
class BorrowedCGContext
|
||||
{
|
||||
public:
|
||||
BorrowedCGContext()
|
||||
: cg(nullptr)
|
||||
, mDT(nullptr)
|
||||
{ }
|
||||
|
||||
explicit BorrowedCGContext(DrawTarget *aDT)
|
||||
: mDT(aDT)
|
||||
{
|
||||
MOZ_ASSERT(aDT, "Caller should check for nullptr");
|
||||
cg = BorrowCGContextFromDrawTarget(aDT);
|
||||
}
|
||||
|
||||
// We can optionally Init after construction in
|
||||
// case we don't know what the DT will be at construction
|
||||
// time.
|
||||
CGContextRef Init(DrawTarget *aDT)
|
||||
{
|
||||
MOZ_ASSERT(aDT, "Caller should check for nullptr");
|
||||
MOZ_ASSERT(!mDT, "Can't initialize twice!");
|
||||
mDT = aDT;
|
||||
cg = BorrowCGContextFromDrawTarget(aDT);
|
||||
return cg;
|
||||
}
|
||||
|
||||
// The caller needs to call Finish if cg is non-null when
|
||||
// they are done with the context. This is currently explicit
|
||||
// instead of happening implicitly in the destructor to make
|
||||
// what's happening in the caller more clear. It also
|
||||
// let's you resume using the DrawTarget in the same scope.
|
||||
void Finish()
|
||||
{
|
||||
if (cg) {
|
||||
ReturnCGContextToDrawTarget(mDT, cg);
|
||||
cg = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
~BorrowedCGContext() {
|
||||
MOZ_ASSERT(!cg);
|
||||
}
|
||||
|
||||
CGContextRef cg;
|
||||
private:
|
||||
#ifdef USE_SKIA
|
||||
static CGContextRef BorrowCGContextFromDrawTarget(DrawTarget *aDT);
|
||||
static void ReturnCGContextToDrawTarget(DrawTarget *aDT, CGContextRef cg);
|
||||
#else
|
||||
static CGContextRef BorrowCGContextFromDrawTarget(DrawTarget *aDT) {
|
||||
MOZ_CRASH("Not supported without Skia");
|
||||
}
|
||||
|
||||
static void ReturnCGContextToDrawTarget(DrawTarget *aDT, CGContextRef cg) {
|
||||
MOZ_CRASH("not supported without Skia");
|
||||
}
|
||||
#endif
|
||||
DrawTarget *mDT;
|
||||
};
|
||||
#endif
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // _MOZILLA_GFX_BORROWED_CONTEXT_H
|
||||
144
gfx/2d/CGTextDrawing.h
Normal file
144
gfx/2d/CGTextDrawing.h
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
/* -*- Mode: C++; tab-width: 20; 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_GFX_SKIACGPOPUPDRAWER_H
|
||||
#define _MOZILLA_GFX_SKIACGPOPUPDRAWER_H
|
||||
|
||||
#include <ApplicationServices/ApplicationServices.h>
|
||||
#include "nsDebug.h"
|
||||
#include "mozilla/Vector.h"
|
||||
#include "ScaledFontMac.h"
|
||||
#include "PathCG.h"
|
||||
#include <dlfcn.h>
|
||||
|
||||
// This is used when we explicitly need CG to draw text to support things such
|
||||
// as vibrancy and subpixel AA on transparent backgrounds. The current use cases
|
||||
// are really only to enable Skia to support drawing text in those situations.
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
typedef void (*CGContextSetFontSmoothingBackgroundColorFunc) (CGContextRef cgContext, CGColorRef color);
|
||||
|
||||
static CGContextSetFontSmoothingBackgroundColorFunc
|
||||
GetCGContextSetFontSmoothingBackgroundColorFunc()
|
||||
{
|
||||
static CGContextSetFontSmoothingBackgroundColorFunc func = nullptr;
|
||||
static bool lookedUpFunc = false;
|
||||
if (!lookedUpFunc) {
|
||||
func = (CGContextSetFontSmoothingBackgroundColorFunc)dlsym(
|
||||
RTLD_DEFAULT, "CGContextSetFontSmoothingBackgroundColor");
|
||||
lookedUpFunc = true;
|
||||
}
|
||||
return func;
|
||||
}
|
||||
|
||||
static CGColorRef
|
||||
ColorToCGColor(CGColorSpaceRef aColorSpace, const Color& aColor)
|
||||
{
|
||||
CGFloat components[4] = {aColor.r, aColor.g, aColor.b, aColor.a};
|
||||
return CGColorCreate(aColorSpace, components);
|
||||
}
|
||||
|
||||
static bool
|
||||
SetFontSmoothingBackgroundColor(CGContextRef aCGContext, CGColorSpaceRef aColorSpace,
|
||||
const GlyphRenderingOptions* aRenderingOptions)
|
||||
{
|
||||
if (aRenderingOptions) {
|
||||
Color fontSmoothingBackgroundColor =
|
||||
static_cast<const GlyphRenderingOptionsCG*>(aRenderingOptions)->FontSmoothingBackgroundColor();
|
||||
if (fontSmoothingBackgroundColor.a > 0) {
|
||||
CGContextSetFontSmoothingBackgroundColorFunc setFontSmoothingBGColorFunc =
|
||||
GetCGContextSetFontSmoothingBackgroundColorFunc();
|
||||
if (setFontSmoothingBGColorFunc) {
|
||||
CGColorRef color = ColorToCGColor(aColorSpace, fontSmoothingBackgroundColor);
|
||||
setFontSmoothingBGColorFunc(aCGContext, color);
|
||||
CGColorRelease(color);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Font rendering with a non-transparent font smoothing background color
|
||||
// can leave pixels in our buffer where the rgb components exceed the alpha
|
||||
// component. When this happens we need to clean up the data afterwards.
|
||||
// The purpose of this is probably the following: Correct compositing of
|
||||
// subpixel anti-aliased fonts on transparent backgrounds requires
|
||||
// different alpha values per RGB component. Usually, premultiplied color
|
||||
// values are derived by multiplying all components with the same per-pixel
|
||||
// alpha value. However, if you multiply each component with a *different*
|
||||
// alpha, and set the alpha component of the pixel to, say, the average
|
||||
// of the alpha values that you used during the premultiplication of the
|
||||
// RGB components, you can trick OVER compositing into doing a simplified
|
||||
// form of component alpha compositing. (You just need to make sure to
|
||||
// clamp the components of the result pixel to [0,255] afterwards.)
|
||||
static void
|
||||
EnsureValidPremultipliedData(CGContextRef aContext,
|
||||
CGRect aTextBounds = CGRectInfinite)
|
||||
{
|
||||
if (CGBitmapContextGetBitsPerPixel(aContext) != 32 ||
|
||||
CGBitmapContextGetAlphaInfo(aContext) != kCGImageAlphaPremultipliedFirst) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t* bitmapData = (uint8_t*)CGBitmapContextGetData(aContext);
|
||||
CGRect bitmapBounds = CGRectMake(0, 0, CGBitmapContextGetWidth(aContext), CGBitmapContextGetHeight(aContext));
|
||||
int stride = CGBitmapContextGetBytesPerRow(aContext);
|
||||
|
||||
CGRect bounds = CGRectIntersection(bitmapBounds, aTextBounds);
|
||||
int startX = bounds.origin.x;
|
||||
int endX = startX + bounds.size.width;
|
||||
MOZ_ASSERT(endX <= bitmapBounds.size.width);
|
||||
|
||||
|
||||
// CGRect assume that our origin is the bottom left.
|
||||
// The data assumes that the origin is the top left.
|
||||
// Have to switch the Y axis so that our coordinates are correct
|
||||
int startY = bitmapBounds.size.height - (bounds.origin.y + bounds.size.height);
|
||||
int endY = startY + bounds.size.height;
|
||||
MOZ_ASSERT(endY <= (int)CGBitmapContextGetHeight(aContext));
|
||||
|
||||
for (int y = startY; y < endY; y++) {
|
||||
for (int x = startX; x < endX; x++) {
|
||||
int i = y * stride + x * 4;
|
||||
uint8_t a = bitmapData[i + 3];
|
||||
|
||||
bitmapData[i + 0] = std::min(a, bitmapData[i+0]);
|
||||
bitmapData[i + 1] = std::min(a, bitmapData[i+1]);
|
||||
bitmapData[i + 2] = std::min(a, bitmapData[i+2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static CGRect
|
||||
ComputeGlyphsExtents(CGRect *bboxes, CGPoint *positions, CFIndex count, float scale)
|
||||
{
|
||||
CGFloat x1, x2, y1, y2;
|
||||
if (count < 1)
|
||||
return CGRectZero;
|
||||
|
||||
x1 = bboxes[0].origin.x + positions[0].x;
|
||||
x2 = bboxes[0].origin.x + positions[0].x + scale*bboxes[0].size.width;
|
||||
y1 = bboxes[0].origin.y + positions[0].y;
|
||||
y2 = bboxes[0].origin.y + positions[0].y + scale*bboxes[0].size.height;
|
||||
|
||||
// accumulate max and minimum coordinates
|
||||
for (int i = 1; i < count; i++) {
|
||||
x1 = std::min(x1, bboxes[i].origin.x + positions[i].x);
|
||||
y1 = std::min(y1, bboxes[i].origin.y + positions[i].y);
|
||||
x2 = std::max(x2, bboxes[i].origin.x + positions[i].x + scale*bboxes[i].size.width);
|
||||
y2 = std::max(y2, bboxes[i].origin.y + positions[i].y + scale*bboxes[i].size.height);
|
||||
}
|
||||
|
||||
CGRect extents = {{x1, y1}, {x2-x1, y2-y1}};
|
||||
return extents;
|
||||
}
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
151
gfx/2d/Coord.h
Normal file
151
gfx/2d/Coord.h
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
/* -*- Mode: C++; tab-width: 20; 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_GFX_COORD_H_
|
||||
#define MOZILLA_GFX_COORD_H_
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/TypeTraits.h" // For IsSame
|
||||
#include "Types.h"
|
||||
#include "BaseCoord.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
template <typename> struct IsPixel;
|
||||
|
||||
namespace gfx {
|
||||
|
||||
template <class units> struct IntCoordTyped;
|
||||
template <class units, class F = Float> struct CoordTyped;
|
||||
|
||||
// CommonType<coord, primitive> is a metafunction that returns the type of the
|
||||
// result of an arithmetic operation on the underlying type of a strongly-typed
|
||||
// coordinate type 'coord', and a primitive type 'primitive'. C++ rules for
|
||||
// arithmetic conversions are designed to avoid losing information - for
|
||||
// example, the result of adding an int and a float is a float - and we want
|
||||
// the same behaviour when mixing our coordinate types with primitive types.
|
||||
// We get C++ to compute the desired result type using 'decltype'.
|
||||
|
||||
template <class coord, class primitive>
|
||||
struct CommonType;
|
||||
|
||||
template <class units, class primitive>
|
||||
struct CommonType<IntCoordTyped<units>, primitive> {
|
||||
typedef decltype(int32_t() + primitive()) type;
|
||||
};
|
||||
|
||||
template <class units, class F, class primitive>
|
||||
struct CommonType<CoordTyped<units, F>, primitive> {
|
||||
typedef decltype(F() + primitive()) type;
|
||||
};
|
||||
|
||||
// This is a base class that provides mixed-type operator overloads between
|
||||
// a strongly-typed Coord and a primitive value. It is needed to avoid
|
||||
// ambiguities at mixed-type call sites, because Coord classes are implicitly
|
||||
// convertible to their underlying value type. As we transition more of our code
|
||||
// to strongly-typed classes, we may be able to remove some or all of these
|
||||
// overloads.
|
||||
|
||||
template <bool B, class coord, class primitive>
|
||||
struct CoordOperatorsHelper {
|
||||
// Using SFINAE (Substitution Failure Is Not An Error) to suppress redundant
|
||||
// operators
|
||||
};
|
||||
|
||||
template <class coord, class primitive>
|
||||
struct CoordOperatorsHelper<true, coord, primitive> {
|
||||
friend bool operator==(coord aA, primitive aB) {
|
||||
return aA.value == aB;
|
||||
}
|
||||
friend bool operator==(primitive aA, coord aB) {
|
||||
return aA == aB.value;
|
||||
}
|
||||
friend bool operator!=(coord aA, primitive aB) {
|
||||
return aA.value != aB;
|
||||
}
|
||||
friend bool operator!=(primitive aA, coord aB) {
|
||||
return aA != aB.value;
|
||||
}
|
||||
|
||||
typedef typename CommonType<coord, primitive>::type result_type;
|
||||
|
||||
friend result_type operator+(coord aA, primitive aB) {
|
||||
return aA.value + aB;
|
||||
}
|
||||
friend result_type operator+(primitive aA, coord aB) {
|
||||
return aA + aB.value;
|
||||
}
|
||||
friend result_type operator-(coord aA, primitive aB) {
|
||||
return aA.value - aB;
|
||||
}
|
||||
friend result_type operator-(primitive aA, coord aB) {
|
||||
return aA - aB.value;
|
||||
}
|
||||
friend result_type operator*(coord aCoord, primitive aScale) {
|
||||
return aCoord.value * aScale;
|
||||
}
|
||||
friend result_type operator*(primitive aScale, coord aCoord) {
|
||||
return aScale * aCoord.value;
|
||||
}
|
||||
friend result_type operator/(coord aCoord, primitive aScale) {
|
||||
return aCoord.value / aScale;
|
||||
}
|
||||
// 'scale / coord' is intentionally omitted because it doesn't make sense.
|
||||
};
|
||||
|
||||
// Note: 'IntCoordTyped<units>' and 'CoordTyped<units>' do not derive from
|
||||
// 'units' to work around https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61959.
|
||||
|
||||
template<class units>
|
||||
struct IntCoordTyped :
|
||||
public BaseCoord< int32_t, IntCoordTyped<units> >,
|
||||
public CoordOperatorsHelper< true, IntCoordTyped<units>, float >,
|
||||
public CoordOperatorsHelper< true, IntCoordTyped<units>, double > {
|
||||
static_assert(IsPixel<units>::value,
|
||||
"'units' must be a coordinate system tag");
|
||||
|
||||
typedef BaseCoord< int32_t, IntCoordTyped<units> > Super;
|
||||
|
||||
constexpr IntCoordTyped() : Super() {}
|
||||
constexpr MOZ_IMPLICIT IntCoordTyped(int32_t aValue) : Super(aValue) {}
|
||||
};
|
||||
|
||||
template<class units, class F>
|
||||
struct CoordTyped :
|
||||
public BaseCoord< F, CoordTyped<units, F> >,
|
||||
public CoordOperatorsHelper< !IsSame<F, int32_t>::value, CoordTyped<units, F>, int32_t >,
|
||||
public CoordOperatorsHelper< !IsSame<F, uint32_t>::value, CoordTyped<units, F>, uint32_t >,
|
||||
public CoordOperatorsHelper< !IsSame<F, double>::value, CoordTyped<units, F>, double >,
|
||||
public CoordOperatorsHelper< !IsSame<F, float>::value, CoordTyped<units, F>, float > {
|
||||
static_assert(IsPixel<units>::value,
|
||||
"'units' must be a coordinate system tag");
|
||||
|
||||
typedef BaseCoord< F, CoordTyped<units, F> > Super;
|
||||
|
||||
constexpr CoordTyped() : Super() {}
|
||||
constexpr MOZ_IMPLICIT CoordTyped(F aValue) : Super(aValue) {}
|
||||
explicit constexpr CoordTyped(const IntCoordTyped<units>& aCoord) : Super(F(aCoord.value)) {}
|
||||
|
||||
void Round() {
|
||||
this->value = floor(this->value + 0.5);
|
||||
}
|
||||
void Truncate() {
|
||||
this->value = int32_t(this->value);
|
||||
}
|
||||
|
||||
IntCoordTyped<units> Rounded() const {
|
||||
return IntCoordTyped<units>(int32_t(floor(this->value + 0.5)));
|
||||
}
|
||||
IntCoordTyped<units> Truncated() const {
|
||||
return IntCoordTyped<units>(int32_t(this->value));
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* MOZILLA_GFX_COORD_H_ */
|
||||
80
gfx/2d/CriticalSection.h
Normal file
80
gfx/2d/CriticalSection.h
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
/* -*- Mode: C++; tab-width: 20; 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_GFX_CRITICALSECTION_H_
|
||||
#define MOZILLA_GFX_CRITICALSECTION_H_
|
||||
|
||||
#ifdef WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <pthread.h>
|
||||
#include "mozilla/DebugOnly.h"
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
#ifdef WIN32
|
||||
|
||||
class CriticalSection {
|
||||
public:
|
||||
CriticalSection() { ::InitializeCriticalSection(&mCriticalSection); }
|
||||
|
||||
~CriticalSection() { ::DeleteCriticalSection(&mCriticalSection); }
|
||||
|
||||
void Enter() { ::EnterCriticalSection(&mCriticalSection); }
|
||||
|
||||
void Leave() { ::LeaveCriticalSection(&mCriticalSection); }
|
||||
|
||||
protected:
|
||||
CRITICAL_SECTION mCriticalSection;
|
||||
};
|
||||
|
||||
#else
|
||||
// posix
|
||||
|
||||
class PosixCondvar;
|
||||
class CriticalSection {
|
||||
public:
|
||||
CriticalSection() {
|
||||
DebugOnly<int> err = pthread_mutex_init(&mMutex, nullptr);
|
||||
MOZ_ASSERT(!err);
|
||||
}
|
||||
|
||||
~CriticalSection() {
|
||||
DebugOnly<int> err = pthread_mutex_destroy(&mMutex);
|
||||
MOZ_ASSERT(!err);
|
||||
}
|
||||
|
||||
void Enter() {
|
||||
DebugOnly<int> err = pthread_mutex_lock(&mMutex);
|
||||
MOZ_ASSERT(!err);
|
||||
}
|
||||
|
||||
void Leave() {
|
||||
DebugOnly<int> err = pthread_mutex_unlock(&mMutex);
|
||||
MOZ_ASSERT(!err);
|
||||
}
|
||||
|
||||
protected:
|
||||
pthread_mutex_t mMutex;
|
||||
friend class PosixCondVar;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
/// RAII helper.
|
||||
struct CriticalSectionAutoEnter {
|
||||
explicit CriticalSectionAutoEnter(CriticalSection* aSection) : mSection(aSection) { mSection->Enter(); }
|
||||
~CriticalSectionAutoEnter() { mSection->Leave(); }
|
||||
protected:
|
||||
CriticalSection* mSection;
|
||||
};
|
||||
|
||||
|
||||
} // namespace
|
||||
} // namespace
|
||||
|
||||
#endif
|
||||
21
gfx/2d/DataSourceSurface.cpp
Normal file
21
gfx/2d/DataSourceSurface.cpp
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "2D.h"
|
||||
#include "DataSourceSurfaceWrapper.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
already_AddRefed<DataSourceSurface>
|
||||
DataSourceSurface::GetDataSurface()
|
||||
{
|
||||
RefPtr<DataSourceSurface> surface =
|
||||
(GetType() == SurfaceType::DATA) ? this : new DataSourceSurfaceWrapper(this);
|
||||
return surface.forget();
|
||||
}
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
||||
39
gfx/2d/DataSourceSurfaceWrapper.h
Normal file
39
gfx/2d/DataSourceSurfaceWrapper.h
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
/* -*- Mode: C++; tab-width: 20; 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_GFX_DATASOURCESURFACEWRAPPER_H_
|
||||
#define MOZILLA_GFX_DATASOURCESURFACEWRAPPER_H_
|
||||
|
||||
#include "2D.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
// Wraps a DataSourceSurface and forwards all methods except for GetType(),
|
||||
// from which it always returns SurfaceType::DATA.
|
||||
class DataSourceSurfaceWrapper : public DataSourceSurface
|
||||
{
|
||||
public:
|
||||
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DataSourceSurfaceWrapper, override)
|
||||
explicit DataSourceSurfaceWrapper(DataSourceSurface *aSurface)
|
||||
: mSurface(aSurface)
|
||||
{}
|
||||
|
||||
virtual SurfaceType GetType() const override { return SurfaceType::DATA; }
|
||||
|
||||
virtual uint8_t *GetData() override { return mSurface->GetData(); }
|
||||
virtual int32_t Stride() override { return mSurface->Stride(); }
|
||||
virtual IntSize GetSize() const override { return mSurface->GetSize(); }
|
||||
virtual SurfaceFormat GetFormat() const override { return mSurface->GetFormat(); }
|
||||
virtual bool IsValid() const override { return mSurface->IsValid(); }
|
||||
|
||||
private:
|
||||
RefPtr<DataSourceSurface> mSurface;
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* MOZILLA_GFX_DATASOURCESURFACEWRAPPER_H_ */
|
||||
374
gfx/2d/DataSurfaceHelpers.cpp
Normal file
374
gfx/2d/DataSurfaceHelpers.cpp
Normal file
|
|
@ -0,0 +1,374 @@
|
|||
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "2D.h"
|
||||
#include "DataSurfaceHelpers.h"
|
||||
#include "Logging.h"
|
||||
#include "mozilla/MathAlgorithms.h"
|
||||
#include "mozilla/PodOperations.h"
|
||||
#include "Tools.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
already_AddRefed<DataSourceSurface>
|
||||
CreateDataSourceSurfaceFromData(const IntSize& aSize,
|
||||
SurfaceFormat aFormat,
|
||||
const uint8_t* aData,
|
||||
int32_t aDataStride)
|
||||
{
|
||||
RefPtr<DataSourceSurface> srcSurface =
|
||||
Factory::CreateWrappingDataSourceSurface(const_cast<uint8_t*>(aData),
|
||||
aDataStride,
|
||||
aSize,
|
||||
aFormat);
|
||||
RefPtr<DataSourceSurface> destSurface =
|
||||
Factory::CreateDataSourceSurface(aSize, aFormat, false);
|
||||
|
||||
if (!srcSurface || !destSurface) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (CopyRect(srcSurface,
|
||||
destSurface,
|
||||
IntRect(IntPoint(), srcSurface->GetSize()),
|
||||
IntPoint())) {
|
||||
return destSurface.forget();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
already_AddRefed<DataSourceSurface>
|
||||
CreateDataSourceSurfaceWithStrideFromData(const IntSize &aSize,
|
||||
SurfaceFormat aFormat,
|
||||
int32_t aStride,
|
||||
const uint8_t* aData,
|
||||
int32_t aDataStride)
|
||||
{
|
||||
RefPtr<DataSourceSurface> srcSurface =
|
||||
Factory::CreateWrappingDataSourceSurface(const_cast<uint8_t*>(aData),
|
||||
aDataStride,
|
||||
aSize,
|
||||
aFormat);
|
||||
RefPtr<DataSourceSurface> destSurface =
|
||||
Factory::CreateDataSourceSurfaceWithStride(aSize, aFormat, aStride, false);
|
||||
|
||||
if (!srcSurface || !destSurface) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (CopyRect(srcSurface,
|
||||
destSurface,
|
||||
IntRect(IntPoint(), srcSurface->GetSize()),
|
||||
IntPoint())) {
|
||||
return destSurface.forget();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint8_t*
|
||||
DataAtOffset(DataSourceSurface* aSurface,
|
||||
const DataSourceSurface::MappedSurface* aMap,
|
||||
IntPoint aPoint)
|
||||
{
|
||||
if (!SurfaceContainsPoint(aSurface, aPoint)) {
|
||||
MOZ_CRASH("GFX: sample position needs to be inside surface!");
|
||||
}
|
||||
|
||||
MOZ_ASSERT(Factory::CheckSurfaceSize(aSurface->GetSize()),
|
||||
"surface size overflows - this should have been prevented when the surface was created");
|
||||
|
||||
uint8_t* data = aMap->mData + aPoint.y * aMap->mStride +
|
||||
aPoint.x * BytesPerPixel(aSurface->GetFormat());
|
||||
|
||||
if (data < aMap->mData) {
|
||||
MOZ_CRASH("GFX: out-of-range data access");
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
// This check is safe against integer overflow.
|
||||
bool
|
||||
SurfaceContainsPoint(SourceSurface* aSurface, const IntPoint& aPoint)
|
||||
{
|
||||
IntSize size = aSurface->GetSize();
|
||||
return aPoint.x >= 0 && aPoint.x < size.width &&
|
||||
aPoint.y >= 0 && aPoint.y < size.height;
|
||||
}
|
||||
|
||||
void
|
||||
ConvertBGRXToBGRA(uint8_t* aData, const IntSize &aSize, const int32_t aStride)
|
||||
{
|
||||
int height = aSize.height, width = aSize.width * 4;
|
||||
|
||||
for (int row = 0; row < height; ++row) {
|
||||
for (int column = 0; column < width; column += 4) {
|
||||
#ifdef IS_BIG_ENDIAN
|
||||
aData[column] = 0xFF;
|
||||
#else
|
||||
aData[column + 3] = 0xFF;
|
||||
#endif
|
||||
}
|
||||
aData += aStride;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CopySurfaceDataToPackedArray(uint8_t* aSrc, uint8_t* aDst, IntSize aSrcSize,
|
||||
int32_t aSrcStride, int32_t aBytesPerPixel)
|
||||
{
|
||||
MOZ_ASSERT(aBytesPerPixel > 0,
|
||||
"Negative stride for aDst not currently supported");
|
||||
MOZ_ASSERT(BufferSizeFromStrideAndHeight(aSrcStride, aSrcSize.height) > 0,
|
||||
"How did we end up with a surface with such a big buffer?");
|
||||
|
||||
int packedStride = aSrcSize.width * aBytesPerPixel;
|
||||
|
||||
if (aSrcStride == packedStride) {
|
||||
// aSrc is already packed, so we can copy with a single memcpy.
|
||||
memcpy(aDst, aSrc, packedStride * aSrcSize.height);
|
||||
} else {
|
||||
// memcpy one row at a time.
|
||||
for (int row = 0; row < aSrcSize.height; ++row) {
|
||||
memcpy(aDst, aSrc, packedStride);
|
||||
aSrc += aSrcStride;
|
||||
aDst += packedStride;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CopyBGRXSurfaceDataToPackedBGRArray(uint8_t* aSrc, uint8_t* aDst,
|
||||
IntSize aSrcSize, int32_t aSrcStride)
|
||||
{
|
||||
int packedStride = aSrcSize.width * 3;
|
||||
|
||||
uint8_t* srcPx = aSrc;
|
||||
uint8_t* dstPx = aDst;
|
||||
|
||||
for (int row = 0; row < aSrcSize.height; ++row) {
|
||||
for (int col = 0; col < aSrcSize.width; ++col) {
|
||||
dstPx[0] = srcPx[0];
|
||||
dstPx[1] = srcPx[1];
|
||||
dstPx[2] = srcPx[2];
|
||||
// srcPx[3] (unused or alpha component) dropped on floor
|
||||
srcPx += 4;
|
||||
dstPx += 3;
|
||||
}
|
||||
srcPx = aSrc += aSrcStride;
|
||||
dstPx = aDst += packedStride;
|
||||
}
|
||||
}
|
||||
|
||||
UniquePtr<uint8_t[]>
|
||||
SurfaceToPackedBGRA(DataSourceSurface *aSurface)
|
||||
{
|
||||
SurfaceFormat format = aSurface->GetFormat();
|
||||
if (format != SurfaceFormat::B8G8R8A8 && format != SurfaceFormat::B8G8R8X8) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
IntSize size = aSurface->GetSize();
|
||||
|
||||
UniquePtr<uint8_t[]> imageBuffer(
|
||||
new (std::nothrow) uint8_t[size.width * size.height * sizeof(uint32_t)]);
|
||||
if (!imageBuffer) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
DataSourceSurface::MappedSurface map;
|
||||
if (!aSurface->Map(DataSourceSurface::MapType::READ, &map)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CopySurfaceDataToPackedArray(map.mData, imageBuffer.get(), size,
|
||||
map.mStride, 4 * sizeof(uint8_t));
|
||||
|
||||
aSurface->Unmap();
|
||||
|
||||
if (format == SurfaceFormat::B8G8R8X8) {
|
||||
// Convert BGRX to BGRA by setting a to 255.
|
||||
ConvertBGRXToBGRA(imageBuffer.get(), size, size.width * sizeof(uint32_t));
|
||||
}
|
||||
|
||||
return imageBuffer;
|
||||
}
|
||||
|
||||
uint8_t*
|
||||
SurfaceToPackedBGR(DataSourceSurface *aSurface)
|
||||
{
|
||||
SurfaceFormat format = aSurface->GetFormat();
|
||||
MOZ_ASSERT(format == SurfaceFormat::B8G8R8X8, "Format not supported");
|
||||
|
||||
if (format != SurfaceFormat::B8G8R8X8) {
|
||||
// To support B8G8R8A8 we'd need to un-pre-multiply alpha
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
IntSize size = aSurface->GetSize();
|
||||
|
||||
uint8_t* imageBuffer = new (std::nothrow) uint8_t[size.width * size.height * 3 * sizeof(uint8_t)];
|
||||
if (!imageBuffer) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
DataSourceSurface::MappedSurface map;
|
||||
if (!aSurface->Map(DataSourceSurface::MapType::READ, &map)) {
|
||||
delete [] imageBuffer;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CopyBGRXSurfaceDataToPackedBGRArray(map.mData, imageBuffer, size,
|
||||
map.mStride);
|
||||
|
||||
aSurface->Unmap();
|
||||
|
||||
return imageBuffer;
|
||||
}
|
||||
|
||||
void
|
||||
ClearDataSourceSurface(DataSourceSurface *aSurface)
|
||||
{
|
||||
DataSourceSurface::MappedSurface map;
|
||||
if (!aSurface->Map(DataSourceSurface::MapType::WRITE, &map)) {
|
||||
MOZ_ASSERT(false, "Failed to map DataSourceSurface");
|
||||
return;
|
||||
}
|
||||
|
||||
// We avoid writing into the gaps between the rows here since we can't be
|
||||
// sure that some drivers don't use those bytes.
|
||||
|
||||
uint32_t width = aSurface->GetSize().width;
|
||||
uint32_t bytesPerRow = width * BytesPerPixel(aSurface->GetFormat());
|
||||
uint8_t* row = map.mData;
|
||||
// converting to size_t here because otherwise the temporaries can overflow
|
||||
// and we can end up with |end| being a bad address!
|
||||
uint8_t* end = row + size_t(map.mStride) * size_t(aSurface->GetSize().height);
|
||||
|
||||
while (row != end) {
|
||||
memset(row, 0, bytesPerRow);
|
||||
row += map.mStride;
|
||||
}
|
||||
|
||||
aSurface->Unmap();
|
||||
}
|
||||
|
||||
size_t
|
||||
BufferSizeFromStrideAndHeight(int32_t aStride,
|
||||
int32_t aHeight,
|
||||
int32_t aExtraBytes)
|
||||
{
|
||||
if (MOZ_UNLIKELY(aHeight <= 0) || MOZ_UNLIKELY(aStride <= 0)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// We limit the length returned to values that can be represented by int32_t
|
||||
// because we don't want to allocate buffers any bigger than that. This
|
||||
// allows for a buffer size of over 2 GiB which is already rediculously
|
||||
// large and will make the process janky. (Note the choice of the signed type
|
||||
// is deliberate because we specifically don't want the returned value to
|
||||
// overflow if someone stores the buffer length in an int32_t variable.)
|
||||
|
||||
CheckedInt32 requiredBytes =
|
||||
CheckedInt32(aStride) * CheckedInt32(aHeight) + CheckedInt32(aExtraBytes);
|
||||
if (MOZ_UNLIKELY(!requiredBytes.isValid())) {
|
||||
gfxWarning() << "Buffer size too big; returning zero " << aStride << ", " << aHeight << ", " << aExtraBytes;
|
||||
return 0;
|
||||
}
|
||||
return requiredBytes.value();
|
||||
}
|
||||
|
||||
size_t
|
||||
BufferSizeFromDimensions(int32_t aWidth,
|
||||
int32_t aHeight,
|
||||
int32_t aDepth,
|
||||
int32_t aExtraBytes)
|
||||
{
|
||||
if (MOZ_UNLIKELY(aHeight <= 0) ||
|
||||
MOZ_UNLIKELY(aWidth <= 0) ||
|
||||
MOZ_UNLIKELY(aDepth <= 0)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Similar to BufferSizeFromStrideAndHeight, but with an extra parameter.
|
||||
|
||||
CheckedInt32 requiredBytes = CheckedInt32(aWidth) * CheckedInt32(aHeight) * CheckedInt32(aDepth) + CheckedInt32(aExtraBytes);
|
||||
if (MOZ_UNLIKELY(!requiredBytes.isValid())) {
|
||||
gfxWarning() << "Buffer size too big; returning zero " << aWidth << ", " << aHeight << ", " << aDepth << ", " << aExtraBytes;
|
||||
return 0;
|
||||
}
|
||||
return requiredBytes.value();
|
||||
}
|
||||
|
||||
/**
|
||||
* aSrcRect: Rect relative to the aSrc surface
|
||||
* aDestPoint: Point inside aDest surface
|
||||
*/
|
||||
bool
|
||||
CopyRect(DataSourceSurface* aSrc, DataSourceSurface* aDest,
|
||||
IntRect aSrcRect, IntPoint aDestPoint)
|
||||
{
|
||||
if (aSrcRect.Overflows() ||
|
||||
IntRect(aDestPoint, aSrcRect.Size()).Overflows()) {
|
||||
MOZ_CRASH("GFX: we should never be getting invalid rects at this point");
|
||||
}
|
||||
|
||||
MOZ_RELEASE_ASSERT(aSrc->GetFormat() == aDest->GetFormat(),
|
||||
"GFX: different surface formats");
|
||||
MOZ_RELEASE_ASSERT(IntRect(IntPoint(), aSrc->GetSize()).Contains(aSrcRect),
|
||||
"GFX: source rect too big for source surface");
|
||||
MOZ_RELEASE_ASSERT(IntRect(IntPoint(), aDest->GetSize()).Contains(IntRect(aDestPoint, aSrcRect.Size())),
|
||||
"GFX: dest surface too small");
|
||||
|
||||
if (aSrcRect.IsEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DataSourceSurface::ScopedMap srcMap(aSrc, DataSourceSurface::READ);
|
||||
DataSourceSurface::ScopedMap destMap(aDest, DataSourceSurface::WRITE);
|
||||
if (MOZ2D_WARN_IF(!srcMap.IsMapped() || !destMap.IsMapped())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t* sourceData = DataAtOffset(aSrc, srcMap.GetMappedSurface(), aSrcRect.TopLeft());
|
||||
uint32_t sourceStride = srcMap.GetStride();
|
||||
uint8_t* destData = DataAtOffset(aDest, destMap.GetMappedSurface(), aDestPoint);
|
||||
uint32_t destStride = destMap.GetStride();
|
||||
|
||||
if (BytesPerPixel(aSrc->GetFormat()) == 4) {
|
||||
for (int32_t y = 0; y < aSrcRect.height; y++) {
|
||||
PodCopy((int32_t*)destData, (int32_t*)sourceData, aSrcRect.width);
|
||||
sourceData += sourceStride;
|
||||
destData += destStride;
|
||||
}
|
||||
} else if (BytesPerPixel(aSrc->GetFormat()) == 1) {
|
||||
for (int32_t y = 0; y < aSrcRect.height; y++) {
|
||||
PodCopy(destData, sourceData, aSrcRect.width);
|
||||
sourceData += sourceStride;
|
||||
destData += destStride;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
already_AddRefed<DataSourceSurface>
|
||||
CreateDataSourceSurfaceByCloning(DataSourceSurface* aSource)
|
||||
{
|
||||
RefPtr<DataSourceSurface> copy =
|
||||
Factory::CreateDataSourceSurface(aSource->GetSize(), aSource->GetFormat(), true);
|
||||
if (copy) {
|
||||
CopyRect(aSource, copy, IntRect(IntPoint(), aSource->GetSize()), IntPoint());
|
||||
}
|
||||
return copy.forget();
|
||||
}
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
||||
147
gfx/2d/DataSurfaceHelpers.h
Normal file
147
gfx/2d/DataSurfaceHelpers.h
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
/* -*- Mode: C++; tab-width: 20; 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_GFX_DATASURFACEHELPERS_H
|
||||
#define _MOZILLA_GFX_DATASURFACEHELPERS_H
|
||||
|
||||
#include "2D.h"
|
||||
|
||||
#include "mozilla/UniquePtr.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
/**
|
||||
* Create a DataSourceSurface and init the surface with the |aData|. The stride
|
||||
* of this source surface might be different from the input data's |aDataStride|.
|
||||
* System will try to use the optimal one.
|
||||
*/
|
||||
already_AddRefed<DataSourceSurface>
|
||||
CreateDataSourceSurfaceFromData(const IntSize& aSize,
|
||||
SurfaceFormat aFormat,
|
||||
const uint8_t* aData,
|
||||
int32_t aDataStride);
|
||||
|
||||
/**
|
||||
* Similar to CreateDataSourceSurfaceFromData(), but could setup the stride for
|
||||
* this surface.
|
||||
*/
|
||||
already_AddRefed<DataSourceSurface>
|
||||
CreateDataSourceSurfaceWithStrideFromData(const IntSize &aSize,
|
||||
SurfaceFormat aFormat,
|
||||
int32_t aStride,
|
||||
const uint8_t* aData,
|
||||
int32_t aDataStride);
|
||||
|
||||
void
|
||||
ConvertBGRXToBGRA(uint8_t* aData, const IntSize &aSize, const int32_t aStride);
|
||||
|
||||
/**
|
||||
* Copy the pixel data from aSrc and pack it into aDst. aSrcSize, aSrcStride
|
||||
* and aBytesPerPixel give the size, stride and bytes per pixel for aSrc's
|
||||
* surface. Callers are responsible for making sure that aDst is big enough to
|
||||
* contain |aSrcSize.width * aSrcSize.height * aBytesPerPixel| bytes.
|
||||
*/
|
||||
void
|
||||
CopySurfaceDataToPackedArray(uint8_t* aSrc, uint8_t* aDst, IntSize aSrcSize,
|
||||
int32_t aSrcStride, int32_t aBytesPerPixel);
|
||||
|
||||
/**
|
||||
* Convert aSurface to a packed buffer in BGRA format.
|
||||
*/
|
||||
UniquePtr<uint8_t[]>
|
||||
SurfaceToPackedBGRA(DataSourceSurface *aSurface);
|
||||
|
||||
/**
|
||||
* Convert aSurface to a packed buffer in BGR format. The pixel data is
|
||||
* returned in a buffer allocated with new uint8_t[]. The caller then has
|
||||
* ownership of the buffer and is responsible for delete[]'ing it.
|
||||
*
|
||||
* This function is currently only intended for use with surfaces of format
|
||||
* SurfaceFormat::B8G8R8X8 since the X components of the pixel data (if any)
|
||||
* are simply dropped (no attempt is made to un-pre-multiply alpha from the
|
||||
* color components).
|
||||
*/
|
||||
uint8_t*
|
||||
SurfaceToPackedBGR(DataSourceSurface *aSurface);
|
||||
|
||||
/**
|
||||
* Clears all the bytes in a DataSourceSurface's data array to zero (so to
|
||||
* transparent black for SurfaceFormat::B8G8R8A8, for example).
|
||||
* Note that DataSourceSurfaces can be initialized to zero, which is
|
||||
* more efficient than zeroing the surface after initialization.
|
||||
*/
|
||||
void
|
||||
ClearDataSourceSurface(DataSourceSurface *aSurface);
|
||||
|
||||
/**
|
||||
* Multiplies aStride and aHeight and makes sure the result is limited to
|
||||
* something sane. To keep things consistent, this should always be used
|
||||
* wherever we allocate a buffer based on surface stride and height.
|
||||
*
|
||||
* @param aExtra Optional argument to specify an additional number of trailing
|
||||
* bytes (useful for creating intermediate surfaces for filters, for
|
||||
* example).
|
||||
*
|
||||
* @return The result of the multiplication if it is acceptable, or else zero.
|
||||
*/
|
||||
size_t
|
||||
BufferSizeFromStrideAndHeight(int32_t aStride,
|
||||
int32_t aHeight,
|
||||
int32_t aExtraBytes = 0);
|
||||
|
||||
/**
|
||||
* Multiplies aWidth, aHeight, aDepth and makes sure the result is limited to
|
||||
* something sane. To keep things consistent, this should always be used
|
||||
* wherever we allocate a buffer based on surface dimensions.
|
||||
*
|
||||
* @param aExtra Optional argument to specify an additional number of trailing
|
||||
* bytes (useful for creating intermediate surfaces for filters, for
|
||||
* example).
|
||||
*
|
||||
* @return The result of the multiplication if it is acceptable, or else zero.
|
||||
*/
|
||||
size_t
|
||||
BufferSizeFromDimensions(int32_t aWidth,
|
||||
int32_t aHeight,
|
||||
int32_t aDepth,
|
||||
int32_t aExtraBytes = 0);
|
||||
/**
|
||||
* Copy aSrcRect from aSrc to aDest starting at aDestPoint.
|
||||
* @returns false if the copy is not successful or the aSrc's size is empty.
|
||||
*/
|
||||
bool
|
||||
CopyRect(DataSourceSurface* aSrc, DataSourceSurface* aDest,
|
||||
IntRect aSrcRect, IntPoint aDestPoint);
|
||||
|
||||
/**
|
||||
* Create a non aliasing copy of aSource. This creates a new DataSourceSurface
|
||||
* using the factory and copies the bits.
|
||||
*
|
||||
* @return a dss allocated by Factory that contains a copy a aSource.
|
||||
*/
|
||||
already_AddRefed<DataSourceSurface>
|
||||
CreateDataSourceSurfaceByCloning(DataSourceSurface* aSource);
|
||||
|
||||
/**
|
||||
* Return the byte at aPoint.
|
||||
*/
|
||||
uint8_t*
|
||||
DataAtOffset(DataSourceSurface* aSurface,
|
||||
const DataSourceSurface::MappedSurface* aMap,
|
||||
IntPoint aPoint);
|
||||
|
||||
/**
|
||||
* Check if aPoint is contained by the surface.
|
||||
*
|
||||
* @returns true if and only if aPoint is inside the surface.
|
||||
*/
|
||||
bool
|
||||
SurfaceContainsPoint(SourceSurface* aSurface, const IntPoint& aPoint);
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // _MOZILLA_GFX_DATASURFACEHELPERS_H
|
||||
594
gfx/2d/DrawCommand.h
Normal file
594
gfx/2d/DrawCommand.h
Normal file
|
|
@ -0,0 +1,594 @@
|
|||
/* -*- 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_GFX_DRAWCOMMAND_H_
|
||||
#define MOZILLA_GFX_DRAWCOMMAND_H_
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#include "2D.h"
|
||||
#include "Filters.h"
|
||||
#include <vector>
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
enum class CommandType : int8_t {
|
||||
DRAWSURFACE = 0,
|
||||
DRAWFILTER,
|
||||
DRAWSURFACEWITHSHADOW,
|
||||
CLEARRECT,
|
||||
COPYSURFACE,
|
||||
COPYRECT,
|
||||
FILLRECT,
|
||||
STROKERECT,
|
||||
STROKELINE,
|
||||
STROKE,
|
||||
FILL,
|
||||
FILLGLYPHS,
|
||||
MASK,
|
||||
MASKSURFACE,
|
||||
PUSHCLIP,
|
||||
PUSHCLIPRECT,
|
||||
POPCLIP,
|
||||
SETTRANSFORM,
|
||||
FLUSH
|
||||
};
|
||||
|
||||
class DrawingCommand
|
||||
{
|
||||
public:
|
||||
virtual ~DrawingCommand() {}
|
||||
|
||||
virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix* aTransform = nullptr) const = 0;
|
||||
|
||||
virtual bool GetAffectedRect(Rect& aDeviceRect, const Matrix& aTransform) const { return false; }
|
||||
|
||||
protected:
|
||||
explicit DrawingCommand(CommandType aType)
|
||||
: mType(aType)
|
||||
{
|
||||
}
|
||||
|
||||
CommandType GetType() { return mType; }
|
||||
|
||||
private:
|
||||
CommandType mType;
|
||||
};
|
||||
|
||||
class StoredPattern
|
||||
{
|
||||
public:
|
||||
explicit StoredPattern(const Pattern& aPattern)
|
||||
{
|
||||
Assign(aPattern);
|
||||
}
|
||||
|
||||
void Assign(const Pattern& aPattern)
|
||||
{
|
||||
switch (aPattern.GetType()) {
|
||||
case PatternType::COLOR:
|
||||
new (mColor)ColorPattern(*static_cast<const ColorPattern*>(&aPattern));
|
||||
return;
|
||||
case PatternType::SURFACE:
|
||||
{
|
||||
SurfacePattern* surfPat = new (mSurface)SurfacePattern(*static_cast<const SurfacePattern*>(&aPattern));
|
||||
surfPat->mSurface->GuaranteePersistance();
|
||||
return;
|
||||
}
|
||||
case PatternType::LINEAR_GRADIENT:
|
||||
new (mLinear)LinearGradientPattern(*static_cast<const LinearGradientPattern*>(&aPattern));
|
||||
return;
|
||||
case PatternType::RADIAL_GRADIENT:
|
||||
new (mRadial)RadialGradientPattern(*static_cast<const RadialGradientPattern*>(&aPattern));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
~StoredPattern()
|
||||
{
|
||||
reinterpret_cast<Pattern*>(mPattern)->~Pattern();
|
||||
}
|
||||
|
||||
operator Pattern&()
|
||||
{
|
||||
return *reinterpret_cast<Pattern*>(mPattern);
|
||||
}
|
||||
|
||||
operator const Pattern&() const
|
||||
{
|
||||
return *reinterpret_cast<const Pattern*>(mPattern);
|
||||
}
|
||||
|
||||
StoredPattern(const StoredPattern& aPattern)
|
||||
{
|
||||
Assign(aPattern);
|
||||
}
|
||||
|
||||
private:
|
||||
StoredPattern operator=(const StoredPattern& aOther)
|
||||
{
|
||||
// Block this so that we notice if someone's doing excessive assigning.
|
||||
return *this;
|
||||
}
|
||||
|
||||
union {
|
||||
char mPattern[sizeof(Pattern)];
|
||||
char mColor[sizeof(ColorPattern)];
|
||||
char mLinear[sizeof(LinearGradientPattern)];
|
||||
char mRadial[sizeof(RadialGradientPattern)];
|
||||
char mSurface[sizeof(SurfacePattern)];
|
||||
};
|
||||
};
|
||||
|
||||
class DrawSurfaceCommand : public DrawingCommand
|
||||
{
|
||||
public:
|
||||
DrawSurfaceCommand(SourceSurface *aSurface, const Rect& aDest,
|
||||
const Rect& aSource, const DrawSurfaceOptions& aSurfOptions,
|
||||
const DrawOptions& aOptions)
|
||||
: DrawingCommand(CommandType::DRAWSURFACE)
|
||||
, mSurface(aSurface), mDest(aDest)
|
||||
, mSource(aSource), mSurfOptions(aSurfOptions)
|
||||
, mOptions(aOptions)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix*) const
|
||||
{
|
||||
aDT->DrawSurface(mSurface, mDest, mSource, mSurfOptions, mOptions);
|
||||
}
|
||||
|
||||
private:
|
||||
RefPtr<SourceSurface> mSurface;
|
||||
Rect mDest;
|
||||
Rect mSource;
|
||||
DrawSurfaceOptions mSurfOptions;
|
||||
DrawOptions mOptions;
|
||||
};
|
||||
|
||||
class DrawFilterCommand : public DrawingCommand
|
||||
{
|
||||
public:
|
||||
DrawFilterCommand(FilterNode* aFilter, const Rect& aSourceRect,
|
||||
const Point& aDestPoint, const DrawOptions& aOptions)
|
||||
: DrawingCommand(CommandType::DRAWSURFACE)
|
||||
, mFilter(aFilter), mSourceRect(aSourceRect)
|
||||
, mDestPoint(aDestPoint), mOptions(aOptions)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix*) const
|
||||
{
|
||||
aDT->DrawFilter(mFilter, mSourceRect, mDestPoint, mOptions);
|
||||
}
|
||||
|
||||
private:
|
||||
RefPtr<FilterNode> mFilter;
|
||||
Rect mSourceRect;
|
||||
Point mDestPoint;
|
||||
DrawOptions mOptions;
|
||||
};
|
||||
|
||||
class ClearRectCommand : public DrawingCommand
|
||||
{
|
||||
public:
|
||||
explicit ClearRectCommand(const Rect& aRect)
|
||||
: DrawingCommand(CommandType::CLEARRECT)
|
||||
, mRect(aRect)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix*) const
|
||||
{
|
||||
aDT->ClearRect(mRect);
|
||||
}
|
||||
|
||||
private:
|
||||
Rect mRect;
|
||||
};
|
||||
|
||||
class CopySurfaceCommand : public DrawingCommand
|
||||
{
|
||||
public:
|
||||
CopySurfaceCommand(SourceSurface* aSurface,
|
||||
const IntRect& aSourceRect,
|
||||
const IntPoint& aDestination)
|
||||
: DrawingCommand(CommandType::COPYSURFACE)
|
||||
, mSurface(aSurface)
|
||||
, mSourceRect(aSourceRect)
|
||||
, mDestination(aDestination)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix* aTransform) const
|
||||
{
|
||||
MOZ_ASSERT(!aTransform || !aTransform->HasNonIntegerTranslation());
|
||||
Point dest(Float(mDestination.x), Float(mDestination.y));
|
||||
if (aTransform) {
|
||||
dest = aTransform->TransformPoint(dest);
|
||||
}
|
||||
aDT->CopySurface(mSurface, mSourceRect, IntPoint(uint32_t(dest.x), uint32_t(dest.y)));
|
||||
}
|
||||
|
||||
private:
|
||||
RefPtr<SourceSurface> mSurface;
|
||||
IntRect mSourceRect;
|
||||
IntPoint mDestination;
|
||||
};
|
||||
|
||||
class FillRectCommand : public DrawingCommand
|
||||
{
|
||||
public:
|
||||
FillRectCommand(const Rect& aRect,
|
||||
const Pattern& aPattern,
|
||||
const DrawOptions& aOptions)
|
||||
: DrawingCommand(CommandType::FILLRECT)
|
||||
, mRect(aRect)
|
||||
, mPattern(aPattern)
|
||||
, mOptions(aOptions)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix*) const
|
||||
{
|
||||
aDT->FillRect(mRect, mPattern, mOptions);
|
||||
}
|
||||
|
||||
bool GetAffectedRect(Rect& aDeviceRect, const Matrix& aTransform) const
|
||||
{
|
||||
aDeviceRect = aTransform.TransformBounds(mRect);
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
Rect mRect;
|
||||
StoredPattern mPattern;
|
||||
DrawOptions mOptions;
|
||||
};
|
||||
|
||||
class StrokeRectCommand : public DrawingCommand
|
||||
{
|
||||
public:
|
||||
StrokeRectCommand(const Rect& aRect,
|
||||
const Pattern& aPattern,
|
||||
const StrokeOptions& aStrokeOptions,
|
||||
const DrawOptions& aOptions)
|
||||
: DrawingCommand(CommandType::STROKERECT)
|
||||
, mRect(aRect)
|
||||
, mPattern(aPattern)
|
||||
, mStrokeOptions(aStrokeOptions)
|
||||
, mOptions(aOptions)
|
||||
{
|
||||
if (aStrokeOptions.mDashLength) {
|
||||
mDashes.resize(aStrokeOptions.mDashLength);
|
||||
mStrokeOptions.mDashPattern = &mDashes.front();
|
||||
memcpy(&mDashes.front(), aStrokeOptions.mDashPattern, mStrokeOptions.mDashLength * sizeof(Float));
|
||||
}
|
||||
}
|
||||
|
||||
virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix*) const
|
||||
{
|
||||
aDT->StrokeRect(mRect, mPattern, mStrokeOptions, mOptions);
|
||||
}
|
||||
|
||||
private:
|
||||
Rect mRect;
|
||||
StoredPattern mPattern;
|
||||
StrokeOptions mStrokeOptions;
|
||||
DrawOptions mOptions;
|
||||
std::vector<Float> mDashes;
|
||||
};
|
||||
|
||||
class StrokeLineCommand : public DrawingCommand
|
||||
{
|
||||
public:
|
||||
StrokeLineCommand(const Point& aStart,
|
||||
const Point& aEnd,
|
||||
const Pattern& aPattern,
|
||||
const StrokeOptions& aStrokeOptions,
|
||||
const DrawOptions& aOptions)
|
||||
: DrawingCommand(CommandType::STROKELINE)
|
||||
, mStart(aStart)
|
||||
, mEnd(aEnd)
|
||||
, mPattern(aPattern)
|
||||
, mStrokeOptions(aStrokeOptions)
|
||||
, mOptions(aOptions)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix*) const
|
||||
{
|
||||
aDT->StrokeLine(mStart, mEnd, mPattern, mStrokeOptions, mOptions);
|
||||
}
|
||||
|
||||
private:
|
||||
Point mStart;
|
||||
Point mEnd;
|
||||
StoredPattern mPattern;
|
||||
StrokeOptions mStrokeOptions;
|
||||
DrawOptions mOptions;
|
||||
};
|
||||
|
||||
class FillCommand : public DrawingCommand
|
||||
{
|
||||
public:
|
||||
FillCommand(const Path* aPath,
|
||||
const Pattern& aPattern,
|
||||
const DrawOptions& aOptions)
|
||||
: DrawingCommand(CommandType::FILL)
|
||||
, mPath(const_cast<Path*>(aPath))
|
||||
, mPattern(aPattern)
|
||||
, mOptions(aOptions)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix*) const
|
||||
{
|
||||
aDT->Fill(mPath, mPattern, mOptions);
|
||||
}
|
||||
|
||||
bool GetAffectedRect(Rect& aDeviceRect, const Matrix& aTransform) const
|
||||
{
|
||||
aDeviceRect = mPath->GetBounds(aTransform);
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
RefPtr<Path> mPath;
|
||||
StoredPattern mPattern;
|
||||
DrawOptions mOptions;
|
||||
};
|
||||
|
||||
#ifndef M_SQRT2
|
||||
#define M_SQRT2 1.41421356237309504880
|
||||
#endif
|
||||
|
||||
#ifndef M_SQRT1_2
|
||||
#define M_SQRT1_2 0.707106781186547524400844362104849039
|
||||
#endif
|
||||
|
||||
// The logic for this comes from _cairo_stroke_style_max_distance_from_path
|
||||
static Rect
|
||||
PathExtentsToMaxStrokeExtents(const StrokeOptions &aStrokeOptions,
|
||||
const Rect &aRect,
|
||||
const Matrix &aTransform)
|
||||
{
|
||||
double styleExpansionFactor = 0.5f;
|
||||
|
||||
if (aStrokeOptions.mLineCap == CapStyle::SQUARE) {
|
||||
styleExpansionFactor = M_SQRT1_2;
|
||||
}
|
||||
|
||||
if (aStrokeOptions.mLineJoin == JoinStyle::MITER &&
|
||||
styleExpansionFactor < M_SQRT2 * aStrokeOptions.mMiterLimit) {
|
||||
styleExpansionFactor = M_SQRT2 * aStrokeOptions.mMiterLimit;
|
||||
}
|
||||
|
||||
styleExpansionFactor *= aStrokeOptions.mLineWidth;
|
||||
|
||||
double dx = styleExpansionFactor * hypot(aTransform._11, aTransform._21);
|
||||
double dy = styleExpansionFactor * hypot(aTransform._22, aTransform._12);
|
||||
|
||||
Rect result = aRect;
|
||||
result.Inflate(dx, dy);
|
||||
return result;
|
||||
}
|
||||
|
||||
class StrokeCommand : public DrawingCommand
|
||||
{
|
||||
public:
|
||||
StrokeCommand(const Path* aPath,
|
||||
const Pattern& aPattern,
|
||||
const StrokeOptions& aStrokeOptions,
|
||||
const DrawOptions& aOptions)
|
||||
: DrawingCommand(CommandType::STROKE)
|
||||
, mPath(const_cast<Path*>(aPath))
|
||||
, mPattern(aPattern)
|
||||
, mStrokeOptions(aStrokeOptions)
|
||||
, mOptions(aOptions)
|
||||
{
|
||||
if (aStrokeOptions.mDashLength) {
|
||||
mDashes.resize(aStrokeOptions.mDashLength);
|
||||
mStrokeOptions.mDashPattern = &mDashes.front();
|
||||
memcpy(&mDashes.front(), aStrokeOptions.mDashPattern, mStrokeOptions.mDashLength * sizeof(Float));
|
||||
}
|
||||
}
|
||||
|
||||
virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix*) const
|
||||
{
|
||||
aDT->Stroke(mPath, mPattern, mStrokeOptions, mOptions);
|
||||
}
|
||||
|
||||
bool GetAffectedRect(Rect& aDeviceRect, const Matrix& aTransform) const
|
||||
{
|
||||
aDeviceRect = PathExtentsToMaxStrokeExtents(mStrokeOptions, mPath->GetBounds(aTransform), aTransform);
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
RefPtr<Path> mPath;
|
||||
StoredPattern mPattern;
|
||||
StrokeOptions mStrokeOptions;
|
||||
DrawOptions mOptions;
|
||||
std::vector<Float> mDashes;
|
||||
};
|
||||
|
||||
class FillGlyphsCommand : public DrawingCommand
|
||||
{
|
||||
public:
|
||||
FillGlyphsCommand(ScaledFont* aFont,
|
||||
const GlyphBuffer& aBuffer,
|
||||
const Pattern& aPattern,
|
||||
const DrawOptions& aOptions,
|
||||
const GlyphRenderingOptions* aRenderingOptions)
|
||||
: DrawingCommand(CommandType::FILLGLYPHS)
|
||||
, mFont(aFont)
|
||||
, mPattern(aPattern)
|
||||
, mOptions(aOptions)
|
||||
, mRenderingOptions(const_cast<GlyphRenderingOptions*>(aRenderingOptions))
|
||||
{
|
||||
mGlyphs.resize(aBuffer.mNumGlyphs);
|
||||
memcpy(&mGlyphs.front(), aBuffer.mGlyphs, sizeof(Glyph) * aBuffer.mNumGlyphs);
|
||||
}
|
||||
|
||||
virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix*) const
|
||||
{
|
||||
GlyphBuffer buf;
|
||||
buf.mNumGlyphs = mGlyphs.size();
|
||||
buf.mGlyphs = &mGlyphs.front();
|
||||
aDT->FillGlyphs(mFont, buf, mPattern, mOptions, mRenderingOptions);
|
||||
}
|
||||
|
||||
private:
|
||||
RefPtr<ScaledFont> mFont;
|
||||
std::vector<Glyph> mGlyphs;
|
||||
StoredPattern mPattern;
|
||||
DrawOptions mOptions;
|
||||
RefPtr<GlyphRenderingOptions> mRenderingOptions;
|
||||
};
|
||||
|
||||
class MaskCommand : public DrawingCommand
|
||||
{
|
||||
public:
|
||||
MaskCommand(const Pattern& aSource,
|
||||
const Pattern& aMask,
|
||||
const DrawOptions& aOptions)
|
||||
: DrawingCommand(CommandType::MASK)
|
||||
, mSource(aSource)
|
||||
, mMask(aMask)
|
||||
, mOptions(aOptions)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix*) const
|
||||
{
|
||||
aDT->Mask(mSource, mMask, mOptions);
|
||||
}
|
||||
|
||||
private:
|
||||
StoredPattern mSource;
|
||||
StoredPattern mMask;
|
||||
DrawOptions mOptions;
|
||||
};
|
||||
|
||||
class MaskSurfaceCommand : public DrawingCommand
|
||||
{
|
||||
public:
|
||||
MaskSurfaceCommand(const Pattern& aSource,
|
||||
const SourceSurface* aMask,
|
||||
const Point& aOffset,
|
||||
const DrawOptions& aOptions)
|
||||
: DrawingCommand(CommandType::MASKSURFACE)
|
||||
, mSource(aSource)
|
||||
, mMask(const_cast<SourceSurface*>(aMask))
|
||||
, mOffset(aOffset)
|
||||
, mOptions(aOptions)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix*) const
|
||||
{
|
||||
aDT->MaskSurface(mSource, mMask, mOffset, mOptions);
|
||||
}
|
||||
|
||||
private:
|
||||
StoredPattern mSource;
|
||||
RefPtr<SourceSurface> mMask;
|
||||
Point mOffset;
|
||||
DrawOptions mOptions;
|
||||
};
|
||||
|
||||
class PushClipCommand : public DrawingCommand
|
||||
{
|
||||
public:
|
||||
explicit PushClipCommand(const Path* aPath)
|
||||
: DrawingCommand(CommandType::PUSHCLIP)
|
||||
, mPath(const_cast<Path*>(aPath))
|
||||
{
|
||||
}
|
||||
|
||||
virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix*) const
|
||||
{
|
||||
aDT->PushClip(mPath);
|
||||
}
|
||||
|
||||
private:
|
||||
RefPtr<Path> mPath;
|
||||
};
|
||||
|
||||
class PushClipRectCommand : public DrawingCommand
|
||||
{
|
||||
public:
|
||||
explicit PushClipRectCommand(const Rect& aRect)
|
||||
: DrawingCommand(CommandType::PUSHCLIPRECT)
|
||||
, mRect(aRect)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix*) const
|
||||
{
|
||||
aDT->PushClipRect(mRect);
|
||||
}
|
||||
|
||||
private:
|
||||
Rect mRect;
|
||||
};
|
||||
|
||||
class PopClipCommand : public DrawingCommand
|
||||
{
|
||||
public:
|
||||
PopClipCommand()
|
||||
: DrawingCommand(CommandType::POPCLIP)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix*) const
|
||||
{
|
||||
aDT->PopClip();
|
||||
}
|
||||
};
|
||||
|
||||
class SetTransformCommand : public DrawingCommand
|
||||
{
|
||||
public:
|
||||
explicit SetTransformCommand(const Matrix& aTransform)
|
||||
: DrawingCommand(CommandType::SETTRANSFORM)
|
||||
, mTransform(aTransform)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix* aMatrix) const
|
||||
{
|
||||
if (aMatrix) {
|
||||
aDT->SetTransform(mTransform * (*aMatrix));
|
||||
} else {
|
||||
aDT->SetTransform(mTransform);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Matrix mTransform;
|
||||
};
|
||||
|
||||
class FlushCommand : public DrawingCommand
|
||||
{
|
||||
public:
|
||||
explicit FlushCommand()
|
||||
: DrawingCommand(CommandType::FLUSH)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix*) const
|
||||
{
|
||||
aDT->Flush();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* MOZILLA_GFX_DRAWCOMMAND_H_ */
|
||||
117
gfx/2d/DrawEventRecorder.cpp
Normal file
117
gfx/2d/DrawEventRecorder.cpp
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "DrawEventRecorder.h"
|
||||
#include "PathRecording.h"
|
||||
#include "RecordingTypes.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
using namespace std;
|
||||
|
||||
DrawEventRecorderPrivate::DrawEventRecorderPrivate(std::ostream *aStream)
|
||||
: mOutputStream(aStream)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
DrawEventRecorderPrivate::WriteHeader()
|
||||
{
|
||||
WriteElement(*mOutputStream, kMagicInt);
|
||||
WriteElement(*mOutputStream, kMajorRevision);
|
||||
WriteElement(*mOutputStream, kMinorRevision);
|
||||
}
|
||||
|
||||
void
|
||||
DrawEventRecorderPrivate::RecordEvent(const RecordedEvent &aEvent)
|
||||
{
|
||||
WriteElement(*mOutputStream, aEvent.mType);
|
||||
|
||||
aEvent.RecordToStream(*mOutputStream);
|
||||
|
||||
Flush();
|
||||
}
|
||||
|
||||
DrawEventRecorderFile::DrawEventRecorderFile(const char *aFilename)
|
||||
: DrawEventRecorderPrivate(nullptr)
|
||||
, mOutputFile(aFilename, ofstream::binary)
|
||||
{
|
||||
mOutputStream = &mOutputFile;
|
||||
|
||||
WriteHeader();
|
||||
}
|
||||
|
||||
DrawEventRecorderFile::~DrawEventRecorderFile()
|
||||
{
|
||||
mOutputFile.close();
|
||||
}
|
||||
|
||||
void
|
||||
DrawEventRecorderFile::Flush()
|
||||
{
|
||||
mOutputFile.flush();
|
||||
}
|
||||
|
||||
bool
|
||||
DrawEventRecorderFile::IsOpen()
|
||||
{
|
||||
return mOutputFile.is_open();
|
||||
}
|
||||
|
||||
void
|
||||
DrawEventRecorderFile::OpenNew(const char *aFilename)
|
||||
{
|
||||
MOZ_ASSERT(!mOutputFile.is_open());
|
||||
|
||||
mOutputFile.open(aFilename, ofstream::binary);
|
||||
WriteHeader();
|
||||
}
|
||||
|
||||
void
|
||||
DrawEventRecorderFile::Close()
|
||||
{
|
||||
MOZ_ASSERT(mOutputFile.is_open());
|
||||
|
||||
mOutputFile.close();
|
||||
}
|
||||
|
||||
DrawEventRecorderMemory::DrawEventRecorderMemory()
|
||||
: DrawEventRecorderPrivate(nullptr)
|
||||
{
|
||||
mOutputStream = &mMemoryStream;
|
||||
|
||||
WriteHeader();
|
||||
}
|
||||
|
||||
void
|
||||
DrawEventRecorderMemory::Flush()
|
||||
{
|
||||
mOutputStream->flush();
|
||||
}
|
||||
|
||||
size_t
|
||||
DrawEventRecorderMemory::RecordingSize()
|
||||
{
|
||||
return mMemoryStream.tellp();
|
||||
}
|
||||
|
||||
bool
|
||||
DrawEventRecorderMemory::CopyRecording(char* aBuffer, size_t aBufferLen)
|
||||
{
|
||||
return !!mMemoryStream.read(aBuffer, aBufferLen);
|
||||
}
|
||||
|
||||
void
|
||||
DrawEventRecorderMemory::WipeRecording()
|
||||
{
|
||||
mMemoryStream.str(std::string());
|
||||
mMemoryStream.clear();
|
||||
|
||||
WriteHeader();
|
||||
}
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
||||
148
gfx/2d/DrawEventRecorder.h
Normal file
148
gfx/2d/DrawEventRecorder.h
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
/* -*- Mode: C++; tab-width: 20; 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_GFX_DRAWEVENTRECORDER_H_
|
||||
#define MOZILLA_GFX_DRAWEVENTRECORDER_H_
|
||||
|
||||
#include "2D.h"
|
||||
#include "RecordedEvent.h"
|
||||
#include <ostream>
|
||||
#include <fstream>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#include <unordered_set>
|
||||
#else
|
||||
#include <set>
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
class PathRecording;
|
||||
|
||||
class DrawEventRecorderPrivate : public DrawEventRecorder
|
||||
{
|
||||
public:
|
||||
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawEventRecorderPrivate)
|
||||
explicit DrawEventRecorderPrivate(std::ostream *aStream);
|
||||
virtual ~DrawEventRecorderPrivate() { }
|
||||
|
||||
void WriteHeader();
|
||||
|
||||
void RecordEvent(const RecordedEvent &aEvent);
|
||||
void WritePath(const PathRecording *aPath);
|
||||
|
||||
void AddStoredObject(const ReferencePtr aObject) {
|
||||
mStoredObjects.insert(aObject);
|
||||
}
|
||||
|
||||
void RemoveStoredObject(const ReferencePtr aObject) {
|
||||
mStoredObjects.erase(aObject);
|
||||
}
|
||||
|
||||
bool HasStoredObject(const ReferencePtr aObject) {
|
||||
return mStoredObjects.find(aObject) != mStoredObjects.end();
|
||||
}
|
||||
|
||||
void AddStoredFontData(const uint64_t aFontDataKey) {
|
||||
mStoredFontData.insert(aFontDataKey);
|
||||
}
|
||||
|
||||
bool HasStoredFontData(const uint64_t aFontDataKey) {
|
||||
return mStoredFontData.find(aFontDataKey) != mStoredFontData.end();
|
||||
}
|
||||
|
||||
protected:
|
||||
std::ostream *mOutputStream;
|
||||
|
||||
virtual void Flush() = 0;
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
typedef std::unordered_set<const void*> ObjectSet;
|
||||
typedef std::unordered_set<uint64_t> Uint64Set;
|
||||
#else
|
||||
typedef std::set<const void*> ObjectSet;
|
||||
typedef std::set<uint64_t> Uint64Set;
|
||||
#endif
|
||||
|
||||
ObjectSet mStoredObjects;
|
||||
Uint64Set mStoredFontData;
|
||||
};
|
||||
|
||||
class DrawEventRecorderFile : public DrawEventRecorderPrivate
|
||||
{
|
||||
public:
|
||||
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawEventRecorderFile)
|
||||
explicit DrawEventRecorderFile(const char *aFilename);
|
||||
~DrawEventRecorderFile();
|
||||
|
||||
/**
|
||||
* Returns whether a recording file is currently open.
|
||||
*/
|
||||
bool IsOpen();
|
||||
|
||||
/**
|
||||
* Opens new file with the provided name. The recorder does NOT forget which
|
||||
* objects it has recorded. This can be used with Close, so that a recording
|
||||
* can be processed in chunks. The file must not already be open.
|
||||
*/
|
||||
void OpenNew(const char *aFilename);
|
||||
|
||||
/**
|
||||
* Closes the file so that it can be processed. The recorder does NOT forget
|
||||
* which objects it has recorded. This can be used with OpenNew, so that a
|
||||
* recording can be processed in chunks. The file must be open.
|
||||
*/
|
||||
void Close();
|
||||
|
||||
private:
|
||||
virtual void Flush();
|
||||
|
||||
std::ofstream mOutputFile;
|
||||
};
|
||||
|
||||
class DrawEventRecorderMemory final : public DrawEventRecorderPrivate
|
||||
{
|
||||
public:
|
||||
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawEventRecorderMemory)
|
||||
|
||||
/**
|
||||
* Constructs a DrawEventRecorder that stores the recording in memory.
|
||||
*/
|
||||
DrawEventRecorderMemory();
|
||||
|
||||
/**
|
||||
* @return the current size of the recording (in chars).
|
||||
*/
|
||||
size_t RecordingSize();
|
||||
|
||||
/**
|
||||
* Copies at most aBufferLen chars of the recording into aBuffer.
|
||||
*
|
||||
* @param aBuffer buffer to receive the recording chars
|
||||
* @param aBufferLen length of aBuffer
|
||||
* @return true if copied successfully
|
||||
*/
|
||||
bool CopyRecording(char* aBuffer, size_t aBufferLen);
|
||||
|
||||
/**
|
||||
* Wipes the internal recording buffer, but the recorder does NOT forget which
|
||||
* objects it has recorded. This can be used so that a recording can be copied
|
||||
* and processed in chunks, releasing memory as it goes.
|
||||
*/
|
||||
void WipeRecording();
|
||||
|
||||
private:
|
||||
~DrawEventRecorderMemory() {};
|
||||
|
||||
void Flush() final;
|
||||
|
||||
std::stringstream mMemoryStream;
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* MOZILLA_GFX_DRAWEVENTRECORDER_H_ */
|
||||
56
gfx/2d/DrawTarget.cpp
Normal file
56
gfx/2d/DrawTarget.cpp
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "2D.h"
|
||||
#include "Logging.h"
|
||||
#include "PathHelpers.h"
|
||||
|
||||
#include "DrawTargetCapture.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
already_AddRefed<DrawTargetCapture>
|
||||
DrawTarget::CreateCaptureDT(const IntSize& aSize)
|
||||
{
|
||||
RefPtr<DrawTargetCaptureImpl> dt = new DrawTargetCaptureImpl();
|
||||
|
||||
if (!dt->Init(aSize, this)) {
|
||||
gfxWarning() << "Failed to initialize Capture DrawTarget!";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return dt.forget();
|
||||
}
|
||||
|
||||
void
|
||||
DrawTarget::DrawCapturedDT(DrawTargetCapture *aCaptureDT,
|
||||
const Matrix& aTransform)
|
||||
{
|
||||
if (aTransform.HasNonIntegerTranslation()) {
|
||||
gfxWarning() << "Non integer translations are not supported for DrawCaptureDT at this time!";
|
||||
return;
|
||||
}
|
||||
static_cast<DrawTargetCaptureImpl*>(aCaptureDT)->ReplayToDrawTarget(this, aTransform);
|
||||
}
|
||||
|
||||
void
|
||||
DrawTarget::PushDeviceSpaceClipRects(const IntRect* aRects, uint32_t aCount)
|
||||
{
|
||||
Matrix oldTransform = GetTransform();
|
||||
SetTransform(Matrix());
|
||||
|
||||
RefPtr<PathBuilder> pathBuilder = CreatePathBuilder();
|
||||
for (uint32_t i = 0; i < aCount; i++) {
|
||||
AppendRectToPath(pathBuilder, Rect(aRects[i]));
|
||||
}
|
||||
RefPtr<Path> path = pathBuilder->Finish();
|
||||
PushClip(path);
|
||||
|
||||
SetTransform(oldTransform);
|
||||
}
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
||||
2374
gfx/2d/DrawTargetCairo.cpp
Normal file
2374
gfx/2d/DrawTargetCairo.cpp
Normal file
File diff suppressed because it is too large
Load diff
262
gfx/2d/DrawTargetCairo.h
Normal file
262
gfx/2d/DrawTargetCairo.h
Normal file
|
|
@ -0,0 +1,262 @@
|
|||
/* -*- Mode: C++; tab-width: 20; 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_GFX_DRAWTARGET_CAIRO_H_
|
||||
#define _MOZILLA_GFX_DRAWTARGET_CAIRO_H_
|
||||
|
||||
#include "2D.h"
|
||||
#include "cairo.h"
|
||||
#include "PathCairo.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
class SourceSurfaceCairo;
|
||||
|
||||
class GradientStopsCairo : public GradientStops
|
||||
{
|
||||
public:
|
||||
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GradientStopsCairo)
|
||||
GradientStopsCairo(GradientStop* aStops, uint32_t aNumStops,
|
||||
ExtendMode aExtendMode)
|
||||
: mExtendMode(aExtendMode)
|
||||
{
|
||||
for (uint32_t i = 0; i < aNumStops; ++i) {
|
||||
mStops.push_back(aStops[i]);
|
||||
}
|
||||
}
|
||||
|
||||
virtual ~GradientStopsCairo() {}
|
||||
|
||||
const std::vector<GradientStop>& GetStops() const
|
||||
{
|
||||
return mStops;
|
||||
}
|
||||
|
||||
ExtendMode GetExtendMode() const
|
||||
{
|
||||
return mExtendMode;
|
||||
}
|
||||
|
||||
virtual BackendType GetBackendType() const { return BackendType::CAIRO; }
|
||||
|
||||
private:
|
||||
std::vector<GradientStop> mStops;
|
||||
ExtendMode mExtendMode;
|
||||
};
|
||||
|
||||
class DrawTargetCairo final : public DrawTarget
|
||||
{
|
||||
public:
|
||||
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawTargetCairo, override)
|
||||
friend class BorrowedCairoContext;
|
||||
friend class BorrowedXlibDrawable;
|
||||
|
||||
DrawTargetCairo();
|
||||
virtual ~DrawTargetCairo();
|
||||
|
||||
virtual bool IsValid() const override;
|
||||
virtual DrawTargetType GetType() const override;
|
||||
virtual BackendType GetBackendType() const override { return BackendType::CAIRO; }
|
||||
virtual already_AddRefed<SourceSurface> Snapshot() override;
|
||||
virtual IntSize GetSize() override;
|
||||
|
||||
virtual bool IsCurrentGroupOpaque() override;
|
||||
|
||||
virtual void SetPermitSubpixelAA(bool aPermitSubpixelAA) override;
|
||||
|
||||
virtual bool LockBits(uint8_t** aData, IntSize* aSize,
|
||||
int32_t* aStride, SurfaceFormat* aFormat,
|
||||
IntPoint* aOrigin = nullptr) override;
|
||||
virtual void ReleaseBits(uint8_t* aData) override;
|
||||
|
||||
virtual void Flush() override;
|
||||
virtual void DrawSurface(SourceSurface *aSurface,
|
||||
const Rect &aDest,
|
||||
const Rect &aSource,
|
||||
const DrawSurfaceOptions &aSurfOptions = DrawSurfaceOptions(),
|
||||
const DrawOptions &aOptions = DrawOptions()) override;
|
||||
virtual void DrawFilter(FilterNode *aNode,
|
||||
const Rect &aSourceRect,
|
||||
const Point &aDestPoint,
|
||||
const DrawOptions &aOptions = DrawOptions()) override;
|
||||
virtual void DrawSurfaceWithShadow(SourceSurface *aSurface,
|
||||
const Point &aDest,
|
||||
const Color &aColor,
|
||||
const Point &aOffset,
|
||||
Float aSigma,
|
||||
CompositionOp aOperator) override;
|
||||
|
||||
virtual void ClearRect(const Rect &aRect) override;
|
||||
|
||||
virtual void CopySurface(SourceSurface *aSurface,
|
||||
const IntRect &aSourceRect,
|
||||
const IntPoint &aDestination) override;
|
||||
virtual void CopyRect(const IntRect &aSourceRect,
|
||||
const IntPoint &aDestination) override;
|
||||
|
||||
virtual void FillRect(const Rect &aRect,
|
||||
const Pattern &aPattern,
|
||||
const DrawOptions &aOptions = DrawOptions()) override;
|
||||
virtual void StrokeRect(const Rect &aRect,
|
||||
const Pattern &aPattern,
|
||||
const StrokeOptions &aStrokeOptions = StrokeOptions(),
|
||||
const DrawOptions &aOptions = DrawOptions()) override;
|
||||
virtual void StrokeLine(const Point &aStart,
|
||||
const Point &aEnd,
|
||||
const Pattern &aPattern,
|
||||
const StrokeOptions &aStrokeOptions = StrokeOptions(),
|
||||
const DrawOptions &aOptions = DrawOptions()) override;
|
||||
|
||||
virtual void Stroke(const Path *aPath,
|
||||
const Pattern &aPattern,
|
||||
const StrokeOptions &aStrokeOptions = StrokeOptions(),
|
||||
const DrawOptions &aOptions = DrawOptions()) override;
|
||||
|
||||
virtual void Fill(const Path *aPath,
|
||||
const Pattern &aPattern,
|
||||
const DrawOptions &aOptions = DrawOptions()) override;
|
||||
|
||||
virtual void FillGlyphs(ScaledFont *aFont,
|
||||
const GlyphBuffer &aBuffer,
|
||||
const Pattern &aPattern,
|
||||
const DrawOptions &aOptions,
|
||||
const GlyphRenderingOptions *aRenderingOptions = nullptr) override;
|
||||
virtual void Mask(const Pattern &aSource,
|
||||
const Pattern &aMask,
|
||||
const DrawOptions &aOptions = DrawOptions()) override;
|
||||
virtual void MaskSurface(const Pattern &aSource,
|
||||
SourceSurface *aMask,
|
||||
Point aOffset,
|
||||
const DrawOptions &aOptions = DrawOptions()) override;
|
||||
|
||||
virtual bool Draw3DTransformedSurface(SourceSurface* aSurface,
|
||||
const Matrix4x4& aMatrix) override;
|
||||
|
||||
virtual void PushClip(const Path *aPath) override;
|
||||
virtual void PushClipRect(const Rect &aRect) override;
|
||||
virtual void PopClip() override;
|
||||
virtual void PushLayer(bool aOpaque, Float aOpacity,
|
||||
SourceSurface* aMask,
|
||||
const Matrix& aMaskTransform,
|
||||
const IntRect& aBounds = IntRect(),
|
||||
bool aCopyBackground = false) override;
|
||||
virtual void PopLayer() override;
|
||||
|
||||
virtual already_AddRefed<PathBuilder> CreatePathBuilder(FillRule aFillRule = FillRule::FILL_WINDING) const override;
|
||||
|
||||
virtual already_AddRefed<SourceSurface> CreateSourceSurfaceFromData(unsigned char *aData,
|
||||
const IntSize &aSize,
|
||||
int32_t aStride,
|
||||
SurfaceFormat aFormat) const override;
|
||||
virtual already_AddRefed<SourceSurface> OptimizeSourceSurface(SourceSurface *aSurface) const override;
|
||||
virtual already_AddRefed<SourceSurface>
|
||||
CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const override;
|
||||
virtual already_AddRefed<DrawTarget>
|
||||
CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const override;
|
||||
virtual already_AddRefed<DrawTarget>
|
||||
CreateShadowDrawTarget(const IntSize &aSize, SurfaceFormat aFormat,
|
||||
float aSigma) const override;
|
||||
|
||||
virtual already_AddRefed<GradientStops>
|
||||
CreateGradientStops(GradientStop *aStops,
|
||||
uint32_t aNumStops,
|
||||
ExtendMode aExtendMode = ExtendMode::CLAMP) const override;
|
||||
|
||||
virtual already_AddRefed<FilterNode> CreateFilter(FilterType aType) override;
|
||||
|
||||
virtual void GetGlyphRasterizationMetrics(ScaledFont *aScaledFont, const uint16_t* aGlyphIndices,
|
||||
uint32_t aNumGlyphs, GlyphMetrics* aGlyphMetrics) override;
|
||||
|
||||
virtual void *GetNativeSurface(NativeSurfaceType aType) override;
|
||||
|
||||
bool Init(cairo_surface_t* aSurface, const IntSize& aSize, SurfaceFormat* aFormat = nullptr);
|
||||
bool Init(const IntSize& aSize, SurfaceFormat aFormat);
|
||||
bool Init(unsigned char* aData, const IntSize &aSize, int32_t aStride, SurfaceFormat aFormat);
|
||||
|
||||
virtual void SetTransform(const Matrix& aTransform) override;
|
||||
|
||||
virtual void DetachAllSnapshots() override { MarkSnapshotIndependent(); }
|
||||
|
||||
// Call to set up aContext for drawing (with the current transform, etc).
|
||||
// Pass the path you're going to be using if you have one.
|
||||
// Implicitly calls WillChange(aPath).
|
||||
void PrepareForDrawing(cairo_t* aContext, const Path* aPath = nullptr);
|
||||
|
||||
static cairo_surface_t *GetDummySurface();
|
||||
|
||||
// Cairo hardcodes this as its maximum surface size.
|
||||
static size_t GetMaxSurfaceSize() {
|
||||
return 32767;
|
||||
}
|
||||
|
||||
private: // methods
|
||||
// Init cairo surface without doing a cairo_surface_reference() call.
|
||||
bool InitAlreadyReferenced(cairo_surface_t* aSurface, const IntSize& aSize, SurfaceFormat* aFormat = nullptr);
|
||||
enum DrawPatternType { DRAW_FILL, DRAW_STROKE };
|
||||
void DrawPattern(const Pattern& aPattern,
|
||||
const StrokeOptions& aStrokeOptions,
|
||||
const DrawOptions& aOptions,
|
||||
DrawPatternType aDrawType,
|
||||
bool aPathBoundsClip = false);
|
||||
|
||||
void CopySurfaceInternal(cairo_surface_t* aSurface,
|
||||
const IntRect& aSource,
|
||||
const IntPoint& aDest);
|
||||
|
||||
Rect GetUserSpaceClip();
|
||||
|
||||
// Call before you make any changes to the backing surface with which this
|
||||
// context is associated. Pass the path you're going to be using if you have
|
||||
// one.
|
||||
void WillChange(const Path* aPath = nullptr);
|
||||
|
||||
// Call if there is any reason to disassociate the snapshot from this draw
|
||||
// target; for example, because we're going to be destroyed.
|
||||
void MarkSnapshotIndependent();
|
||||
|
||||
// If the current operator is "source" then clear the destination before we
|
||||
// draw into it, to simulate the effect of an unbounded source operator.
|
||||
void ClearSurfaceForUnboundedSource(const CompositionOp &aOperator);
|
||||
|
||||
// Set the Cairo context font options according to the current draw target
|
||||
// font state.
|
||||
void SetFontOptions();
|
||||
|
||||
private: // data
|
||||
cairo_t* mContext;
|
||||
cairo_surface_t* mSurface;
|
||||
IntSize mSize;
|
||||
bool mTransformSingular;
|
||||
|
||||
uint8_t* mLockedBits;
|
||||
|
||||
cairo_font_options_t* mFontOptions;
|
||||
|
||||
struct PushedLayer
|
||||
{
|
||||
PushedLayer(Float aOpacity, bool aWasPermittingSubpixelAA)
|
||||
: mOpacity(aOpacity)
|
||||
, mMaskPattern(nullptr)
|
||||
, mWasPermittingSubpixelAA(aWasPermittingSubpixelAA)
|
||||
{}
|
||||
Float mOpacity;
|
||||
cairo_pattern_t* mMaskPattern;
|
||||
bool mWasPermittingSubpixelAA;
|
||||
};
|
||||
std::vector<PushedLayer> mPushedLayers;
|
||||
|
||||
// The latest snapshot of this surface. This needs to be told when this
|
||||
// target is modified. We keep it alive as a cache.
|
||||
RefPtr<SourceSurfaceCairo> mSnapshot;
|
||||
static cairo_surface_t *mDummySurface;
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // _MOZILLA_GFX_DRAWTARGET_CAIRO_H_
|
||||
201
gfx/2d/DrawTargetCapture.cpp
Normal file
201
gfx/2d/DrawTargetCapture.cpp
Normal file
|
|
@ -0,0 +1,201 @@
|
|||
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "DrawTargetCapture.h"
|
||||
#include "DrawCommand.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
|
||||
DrawTargetCaptureImpl::~DrawTargetCaptureImpl()
|
||||
{
|
||||
uint8_t* start = &mDrawCommandStorage.front();
|
||||
|
||||
uint8_t* current = start;
|
||||
|
||||
while (current < start + mDrawCommandStorage.size()) {
|
||||
reinterpret_cast<DrawingCommand*>(current + sizeof(uint32_t))->~DrawingCommand();
|
||||
current += *(uint32_t*)current;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
DrawTargetCaptureImpl::Init(const IntSize& aSize, DrawTarget* aRefDT)
|
||||
{
|
||||
if (!aRefDT) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mRefDT = aRefDT;
|
||||
|
||||
mSize = aSize;
|
||||
return true;
|
||||
}
|
||||
|
||||
already_AddRefed<SourceSurface>
|
||||
DrawTargetCaptureImpl::Snapshot()
|
||||
{
|
||||
RefPtr<DrawTarget> dt = mRefDT->CreateSimilarDrawTarget(mSize, mRefDT->GetFormat());
|
||||
|
||||
ReplayToDrawTarget(dt, Matrix());
|
||||
|
||||
return dt->Snapshot();
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetCaptureImpl::DetachAllSnapshots()
|
||||
{}
|
||||
|
||||
#define AppendCommand(arg) new (AppendToCommandList<arg>()) arg
|
||||
|
||||
void
|
||||
DrawTargetCaptureImpl::DrawSurface(SourceSurface *aSurface,
|
||||
const Rect &aDest,
|
||||
const Rect &aSource,
|
||||
const DrawSurfaceOptions &aSurfOptions,
|
||||
const DrawOptions &aOptions)
|
||||
{
|
||||
aSurface->GuaranteePersistance();
|
||||
AppendCommand(DrawSurfaceCommand)(aSurface, aDest, aSource, aSurfOptions, aOptions);
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetCaptureImpl::DrawFilter(FilterNode *aNode,
|
||||
const Rect &aSourceRect,
|
||||
const Point &aDestPoint,
|
||||
const DrawOptions &aOptions)
|
||||
{
|
||||
// @todo XXX - this won't work properly long term yet due to filternodes not
|
||||
// being immutable.
|
||||
AppendCommand(DrawFilterCommand)(aNode, aSourceRect, aDestPoint, aOptions);
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetCaptureImpl::ClearRect(const Rect &aRect)
|
||||
{
|
||||
AppendCommand(ClearRectCommand)(aRect);
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetCaptureImpl::MaskSurface(const Pattern &aSource,
|
||||
SourceSurface *aMask,
|
||||
Point aOffset,
|
||||
const DrawOptions &aOptions)
|
||||
{
|
||||
aMask->GuaranteePersistance();
|
||||
AppendCommand(MaskSurfaceCommand)(aSource, aMask, aOffset, aOptions);
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetCaptureImpl::CopySurface(SourceSurface* aSurface,
|
||||
const IntRect& aSourceRect,
|
||||
const IntPoint& aDestination)
|
||||
{
|
||||
aSurface->GuaranteePersistance();
|
||||
AppendCommand(CopySurfaceCommand)(aSurface, aSourceRect, aDestination);
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetCaptureImpl::FillRect(const Rect& aRect,
|
||||
const Pattern& aPattern,
|
||||
const DrawOptions& aOptions)
|
||||
{
|
||||
AppendCommand(FillRectCommand)(aRect, aPattern, aOptions);
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetCaptureImpl::StrokeRect(const Rect& aRect,
|
||||
const Pattern& aPattern,
|
||||
const StrokeOptions& aStrokeOptions,
|
||||
const DrawOptions& aOptions)
|
||||
{
|
||||
AppendCommand(StrokeRectCommand)(aRect, aPattern, aStrokeOptions, aOptions);
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetCaptureImpl::StrokeLine(const Point& aStart,
|
||||
const Point& aEnd,
|
||||
const Pattern& aPattern,
|
||||
const StrokeOptions& aStrokeOptions,
|
||||
const DrawOptions& aOptions)
|
||||
{
|
||||
AppendCommand(StrokeLineCommand)(aStart, aEnd, aPattern, aStrokeOptions, aOptions);
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetCaptureImpl::Stroke(const Path* aPath,
|
||||
const Pattern& aPattern,
|
||||
const StrokeOptions& aStrokeOptions,
|
||||
const DrawOptions& aOptions)
|
||||
{
|
||||
AppendCommand(StrokeCommand)(aPath, aPattern, aStrokeOptions, aOptions);
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetCaptureImpl::Fill(const Path* aPath,
|
||||
const Pattern& aPattern,
|
||||
const DrawOptions& aOptions)
|
||||
{
|
||||
AppendCommand(FillCommand)(aPath, aPattern, aOptions);
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetCaptureImpl::FillGlyphs(ScaledFont* aFont,
|
||||
const GlyphBuffer& aBuffer,
|
||||
const Pattern& aPattern,
|
||||
const DrawOptions& aOptions,
|
||||
const GlyphRenderingOptions* aRenderingOptions)
|
||||
{
|
||||
AppendCommand(FillGlyphsCommand)(aFont, aBuffer, aPattern, aOptions, aRenderingOptions);
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetCaptureImpl::Mask(const Pattern &aSource,
|
||||
const Pattern &aMask,
|
||||
const DrawOptions &aOptions)
|
||||
{
|
||||
AppendCommand(MaskCommand)(aSource, aMask, aOptions);
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetCaptureImpl::PushClip(const Path* aPath)
|
||||
{
|
||||
AppendCommand(PushClipCommand)(aPath);
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetCaptureImpl::PushClipRect(const Rect& aRect)
|
||||
{
|
||||
AppendCommand(PushClipRectCommand)(aRect);
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetCaptureImpl::PopClip()
|
||||
{
|
||||
AppendCommand(PopClipCommand)();
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetCaptureImpl::SetTransform(const Matrix& aTransform)
|
||||
{
|
||||
AppendCommand(SetTransformCommand)(aTransform);
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetCaptureImpl::ReplayToDrawTarget(DrawTarget* aDT, const Matrix& aTransform)
|
||||
{
|
||||
uint8_t* start = &mDrawCommandStorage.front();
|
||||
|
||||
uint8_t* current = start;
|
||||
|
||||
while (current < start + mDrawCommandStorage.size()) {
|
||||
reinterpret_cast<DrawingCommand*>(current + sizeof(uint32_t))->ExecuteOnDT(aDT, &aTransform);
|
||||
current += *(uint32_t*)current;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
||||
168
gfx/2d/DrawTargetCapture.h
Normal file
168
gfx/2d/DrawTargetCapture.h
Normal file
|
|
@ -0,0 +1,168 @@
|
|||
/* -*- 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_GFX_DRAWTARGETCAPTURE_H_
|
||||
#define MOZILLA_GFX_DRAWTARGETCAPTURE_H_
|
||||
|
||||
#include "2D.h"
|
||||
#include <vector>
|
||||
|
||||
#include "Filters.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
class DrawingCommand;
|
||||
|
||||
class DrawTargetCaptureImpl : public DrawTargetCapture
|
||||
{
|
||||
public:
|
||||
DrawTargetCaptureImpl()
|
||||
{}
|
||||
|
||||
bool Init(const IntSize& aSize, DrawTarget* aRefDT);
|
||||
|
||||
virtual BackendType GetBackendType() const { return mRefDT->GetBackendType(); }
|
||||
virtual DrawTargetType GetType() const { return mRefDT->GetType(); }
|
||||
|
||||
virtual already_AddRefed<SourceSurface> Snapshot();
|
||||
|
||||
virtual void DetachAllSnapshots();
|
||||
|
||||
virtual IntSize GetSize() { return mSize; }
|
||||
|
||||
virtual void Flush() {}
|
||||
virtual void DrawSurface(SourceSurface *aSurface,
|
||||
const Rect &aDest,
|
||||
const Rect &aSource,
|
||||
const DrawSurfaceOptions &aSurfOptions,
|
||||
const DrawOptions &aOptions);
|
||||
virtual void DrawFilter(FilterNode *aNode,
|
||||
const Rect &aSourceRect,
|
||||
const Point &aDestPoint,
|
||||
const DrawOptions &aOptions = DrawOptions());
|
||||
virtual void DrawSurfaceWithShadow(SourceSurface *aSurface,
|
||||
const Point &aDest,
|
||||
const Color &aColor,
|
||||
const Point &aOffset,
|
||||
Float aSigma,
|
||||
CompositionOp aOperator) { /* Not implemented */ }
|
||||
|
||||
virtual void ClearRect(const Rect &aRect);
|
||||
virtual void MaskSurface(const Pattern &aSource,
|
||||
SourceSurface *aMask,
|
||||
Point aOffset,
|
||||
const DrawOptions &aOptions = DrawOptions());
|
||||
|
||||
virtual void CopySurface(SourceSurface *aSurface,
|
||||
const IntRect &aSourceRect,
|
||||
const IntPoint &aDestination);
|
||||
|
||||
virtual void FillRect(const Rect &aRect,
|
||||
const Pattern &aPattern,
|
||||
const DrawOptions &aOptions = DrawOptions());
|
||||
virtual void StrokeRect(const Rect &aRect,
|
||||
const Pattern &aPattern,
|
||||
const StrokeOptions &aStrokeOptions = StrokeOptions(),
|
||||
const DrawOptions &aOptions = DrawOptions());
|
||||
virtual void StrokeLine(const Point &aStart,
|
||||
const Point &aEnd,
|
||||
const Pattern &aPattern,
|
||||
const StrokeOptions &aStrokeOptions = StrokeOptions(),
|
||||
const DrawOptions &aOptions = DrawOptions());
|
||||
virtual void Stroke(const Path *aPath,
|
||||
const Pattern &aPattern,
|
||||
const StrokeOptions &aStrokeOptions = StrokeOptions(),
|
||||
const DrawOptions &aOptions = DrawOptions());
|
||||
virtual void Fill(const Path *aPath,
|
||||
const Pattern &aPattern,
|
||||
const DrawOptions &aOptions = DrawOptions());
|
||||
virtual void FillGlyphs(ScaledFont *aFont,
|
||||
const GlyphBuffer &aBuffer,
|
||||
const Pattern &aPattern,
|
||||
const DrawOptions &aOptions = DrawOptions(),
|
||||
const GlyphRenderingOptions *aRenderingOptions = nullptr);
|
||||
virtual void Mask(const Pattern &aSource,
|
||||
const Pattern &aMask,
|
||||
const DrawOptions &aOptions = DrawOptions());
|
||||
virtual void PushClip(const Path *aPath);
|
||||
virtual void PushClipRect(const Rect &aRect);
|
||||
virtual void PopClip();
|
||||
|
||||
virtual void SetTransform(const Matrix &aTransform);
|
||||
|
||||
virtual already_AddRefed<SourceSurface> CreateSourceSurfaceFromData(unsigned char *aData,
|
||||
const IntSize &aSize,
|
||||
int32_t aStride,
|
||||
SurfaceFormat aFormat) const
|
||||
{
|
||||
return mRefDT->CreateSourceSurfaceFromData(aData, aSize, aStride, aFormat);
|
||||
}
|
||||
virtual already_AddRefed<SourceSurface> OptimizeSourceSurface(SourceSurface *aSurface) const
|
||||
{
|
||||
return mRefDT->OptimizeSourceSurface(aSurface);
|
||||
}
|
||||
|
||||
virtual already_AddRefed<SourceSurface>
|
||||
CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const
|
||||
{
|
||||
return mRefDT->CreateSourceSurfaceFromNativeSurface(aSurface);
|
||||
}
|
||||
|
||||
virtual already_AddRefed<DrawTarget>
|
||||
CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const
|
||||
{
|
||||
return mRefDT->CreateSimilarDrawTarget(aSize, aFormat);
|
||||
}
|
||||
|
||||
virtual already_AddRefed<PathBuilder> CreatePathBuilder(FillRule aFillRule = FillRule::FILL_WINDING) const
|
||||
{
|
||||
return mRefDT->CreatePathBuilder(aFillRule);
|
||||
}
|
||||
|
||||
virtual already_AddRefed<GradientStops>
|
||||
CreateGradientStops(GradientStop *aStops,
|
||||
uint32_t aNumStops,
|
||||
ExtendMode aExtendMode = ExtendMode::CLAMP) const
|
||||
{
|
||||
return mRefDT->CreateGradientStops(aStops, aNumStops, aExtendMode);
|
||||
}
|
||||
virtual already_AddRefed<FilterNode> CreateFilter(FilterType aType)
|
||||
{
|
||||
return mRefDT->CreateFilter(aType);
|
||||
}
|
||||
|
||||
void ReplayToDrawTarget(DrawTarget* aDT, const Matrix& aTransform);
|
||||
|
||||
protected:
|
||||
~DrawTargetCaptureImpl();
|
||||
|
||||
private:
|
||||
|
||||
// This storage system was used to minimize the amount of heap allocations
|
||||
// that are required while recording. It should be noted there's no
|
||||
// guarantees on the alignments of DrawingCommands allocated in this array.
|
||||
template<typename T>
|
||||
T* AppendToCommandList()
|
||||
{
|
||||
size_t oldSize = mDrawCommandStorage.size();
|
||||
mDrawCommandStorage.resize(mDrawCommandStorage.size() + sizeof(T) + sizeof(uint32_t));
|
||||
uint8_t* nextDrawLocation = &mDrawCommandStorage.front() + oldSize;
|
||||
*(uint32_t*)(nextDrawLocation) = sizeof(T) + sizeof(uint32_t);
|
||||
return reinterpret_cast<T*>(nextDrawLocation + sizeof(uint32_t));
|
||||
}
|
||||
RefPtr<DrawTarget> mRefDT;
|
||||
|
||||
IntSize mSize;
|
||||
|
||||
std::vector<uint8_t> mDrawCommandStorage;
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
|
||||
#endif /* MOZILLA_GFX_DRAWTARGETCAPTURE_H_ */
|
||||
1931
gfx/2d/DrawTargetD2D1.cpp
Normal file
1931
gfx/2d/DrawTargetD2D1.cpp
Normal file
File diff suppressed because it is too large
Load diff
297
gfx/2d/DrawTargetD2D1.h
Normal file
297
gfx/2d/DrawTargetD2D1.h
Normal file
|
|
@ -0,0 +1,297 @@
|
|||
/* -*- Mode: C++; tab-width: 20; 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_GFX_DRAWTARGETD2D1_H_
|
||||
#define MOZILLA_GFX_DRAWTARGETD2D1_H_
|
||||
|
||||
#include "2D.h"
|
||||
#include <d3d11.h>
|
||||
#include <d2d1_1.h>
|
||||
#include "PathD2D.h"
|
||||
#include "HelpersD2D.h"
|
||||
|
||||
#include <vector>
|
||||
#include <sstream>
|
||||
|
||||
#include <unordered_set>
|
||||
|
||||
struct IDWriteFactory;
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
class SourceSurfaceD2D1;
|
||||
|
||||
const int32_t kLayerCacheSize1 = 5;
|
||||
|
||||
class DrawTargetD2D1 : public DrawTarget
|
||||
{
|
||||
public:
|
||||
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawTargetD2D1, override)
|
||||
DrawTargetD2D1();
|
||||
virtual ~DrawTargetD2D1();
|
||||
|
||||
virtual DrawTargetType GetType() const override { return DrawTargetType::HARDWARE_RASTER; }
|
||||
virtual BackendType GetBackendType() const override { return BackendType::DIRECT2D1_1; }
|
||||
virtual already_AddRefed<SourceSurface> Snapshot() override;
|
||||
virtual IntSize GetSize() override { return mSize; }
|
||||
|
||||
virtual void Flush() override;
|
||||
virtual void DrawSurface(SourceSurface *aSurface,
|
||||
const Rect &aDest,
|
||||
const Rect &aSource,
|
||||
const DrawSurfaceOptions &aSurfOptions,
|
||||
const DrawOptions &aOptions) override;
|
||||
virtual void DrawFilter(FilterNode *aNode,
|
||||
const Rect &aSourceRect,
|
||||
const Point &aDestPoint,
|
||||
const DrawOptions &aOptions = DrawOptions()) override;
|
||||
virtual void DrawSurfaceWithShadow(SourceSurface *aSurface,
|
||||
const Point &aDest,
|
||||
const Color &aColor,
|
||||
const Point &aOffset,
|
||||
Float aSigma,
|
||||
CompositionOp aOperator) override;
|
||||
virtual void ClearRect(const Rect &aRect) override;
|
||||
virtual void MaskSurface(const Pattern &aSource,
|
||||
SourceSurface *aMask,
|
||||
Point aOffset,
|
||||
const DrawOptions &aOptions = DrawOptions()) override;
|
||||
|
||||
virtual void CopySurface(SourceSurface *aSurface,
|
||||
const IntRect &aSourceRect,
|
||||
const IntPoint &aDestination) override;
|
||||
|
||||
virtual void FillRect(const Rect &aRect,
|
||||
const Pattern &aPattern,
|
||||
const DrawOptions &aOptions = DrawOptions()) override;
|
||||
virtual void StrokeRect(const Rect &aRect,
|
||||
const Pattern &aPattern,
|
||||
const StrokeOptions &aStrokeOptions = StrokeOptions(),
|
||||
const DrawOptions &aOptions = DrawOptions()) override;
|
||||
virtual void StrokeLine(const Point &aStart,
|
||||
const Point &aEnd,
|
||||
const Pattern &aPattern,
|
||||
const StrokeOptions &aStrokeOptions = StrokeOptions(),
|
||||
const DrawOptions &aOptions = DrawOptions()) override;
|
||||
virtual void Stroke(const Path *aPath,
|
||||
const Pattern &aPattern,
|
||||
const StrokeOptions &aStrokeOptions = StrokeOptions(),
|
||||
const DrawOptions &aOptions = DrawOptions()) override;
|
||||
virtual void Fill(const Path *aPath,
|
||||
const Pattern &aPattern,
|
||||
const DrawOptions &aOptions = DrawOptions()) override;
|
||||
virtual void FillGlyphs(ScaledFont *aFont,
|
||||
const GlyphBuffer &aBuffer,
|
||||
const Pattern &aPattern,
|
||||
const DrawOptions &aOptions = DrawOptions(),
|
||||
const GlyphRenderingOptions *aRenderingOptions = nullptr) override;
|
||||
virtual void Mask(const Pattern &aSource,
|
||||
const Pattern &aMask,
|
||||
const DrawOptions &aOptions = DrawOptions()) override;
|
||||
virtual void PushClip(const Path *aPath) override;
|
||||
virtual void PushClipRect(const Rect &aRect) override;
|
||||
virtual void PushDeviceSpaceClipRects(const IntRect* aRects, uint32_t aCount) override;
|
||||
|
||||
virtual void PopClip() override;
|
||||
virtual void PushLayer(bool aOpaque, Float aOpacity,
|
||||
SourceSurface* aMask,
|
||||
const Matrix& aMaskTransform,
|
||||
const IntRect& aBounds = IntRect(),
|
||||
bool aCopyBackground = false) override;
|
||||
virtual void PopLayer() override;
|
||||
|
||||
virtual already_AddRefed<SourceSurface> CreateSourceSurfaceFromData(unsigned char *aData,
|
||||
const IntSize &aSize,
|
||||
int32_t aStride,
|
||||
SurfaceFormat aFormat) const override;
|
||||
virtual already_AddRefed<SourceSurface> OptimizeSourceSurface(SourceSurface *aSurface) const override;
|
||||
|
||||
virtual already_AddRefed<SourceSurface>
|
||||
CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const override { return nullptr; }
|
||||
|
||||
virtual already_AddRefed<DrawTarget>
|
||||
CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const override;
|
||||
|
||||
virtual already_AddRefed<PathBuilder> CreatePathBuilder(FillRule aFillRule = FillRule::FILL_WINDING) const override;
|
||||
|
||||
virtual already_AddRefed<GradientStops>
|
||||
CreateGradientStops(GradientStop *aStops,
|
||||
uint32_t aNumStops,
|
||||
ExtendMode aExtendMode = ExtendMode::CLAMP) const override;
|
||||
|
||||
virtual already_AddRefed<FilterNode> CreateFilter(FilterType aType) override;
|
||||
|
||||
virtual bool SupportsRegionClipping() const override { return false; }
|
||||
virtual bool IsCurrentGroupOpaque() override { return CurrentLayer().mIsOpaque; }
|
||||
|
||||
virtual void *GetNativeSurface(NativeSurfaceType aType) override { return nullptr; }
|
||||
|
||||
virtual void DetachAllSnapshots() override { MarkChanged(); }
|
||||
|
||||
virtual void GetGlyphRasterizationMetrics(ScaledFont *aScaledFont, const uint16_t* aGlyphIndices,
|
||||
uint32_t aNumGlyphs, GlyphMetrics* aGlyphMetrics) override;
|
||||
|
||||
bool Init(const IntSize &aSize, SurfaceFormat aFormat);
|
||||
bool Init(ID3D11Texture2D* aTexture, SurfaceFormat aFormat);
|
||||
uint32_t GetByteSize() const;
|
||||
|
||||
// This function will get an image for a surface, it may adjust the source
|
||||
// transform for any transformation of the resulting image relative to the
|
||||
// oritingal SourceSurface.
|
||||
already_AddRefed<ID2D1Image> GetImageForSurface(SourceSurface *aSurface, Matrix &aSourceTransform,
|
||||
ExtendMode aExtendMode, const IntRect* aSourceRect = nullptr);
|
||||
|
||||
already_AddRefed<ID2D1Image> GetImageForSurface(SourceSurface *aSurface, ExtendMode aExtendMode) {
|
||||
Matrix mat;
|
||||
return GetImageForSurface(aSurface, mat, aExtendMode, nullptr);
|
||||
}
|
||||
|
||||
static ID2D1Factory1 *factory();
|
||||
static void CleanupD2D();
|
||||
static IDWriteFactory *GetDWriteFactory();
|
||||
|
||||
operator std::string() const {
|
||||
std::stringstream stream;
|
||||
stream << "DrawTargetD2D 1.1 (" << this << ")";
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
static uint32_t GetMaxSurfaceSize() {
|
||||
return D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION;
|
||||
}
|
||||
|
||||
static uint64_t mVRAMUsageDT;
|
||||
static uint64_t mVRAMUsageSS;
|
||||
|
||||
private:
|
||||
friend class SourceSurfaceD2D1;
|
||||
|
||||
typedef std::unordered_set<DrawTargetD2D1*> TargetSet;
|
||||
|
||||
// This function will mark the surface as changing, and make sure any
|
||||
// copy-on-write snapshots are notified.
|
||||
void MarkChanged();
|
||||
bool ShouldClipTemporarySurfaceDrawing(CompositionOp aOp, const Pattern& aPattern, bool aClipIsComplex);
|
||||
void PrepareForDrawing(CompositionOp aOp, const Pattern &aPattern);
|
||||
void FinalizeDrawing(CompositionOp aOp, const Pattern &aPattern);
|
||||
void FlushTransformToDC() {
|
||||
if (mTransformDirty) {
|
||||
mDC->SetTransform(D2DMatrix(mTransform));
|
||||
mTransformDirty = false;
|
||||
}
|
||||
}
|
||||
void AddDependencyOnSource(SourceSurfaceD2D1* aSource);
|
||||
|
||||
// Must be called with all clips popped and an identity matrix set.
|
||||
already_AddRefed<ID2D1Image> GetImageForLayerContent(bool aShouldPreserveContent = true);
|
||||
|
||||
ID2D1Image* CurrentTarget()
|
||||
{
|
||||
if (CurrentLayer().mCurrentList) {
|
||||
return CurrentLayer().mCurrentList;
|
||||
}
|
||||
return mBitmap;
|
||||
}
|
||||
|
||||
// This returns the clipped geometry, in addition it returns aClipBounds which
|
||||
// represents the intersection of all pixel-aligned rectangular clips that
|
||||
// are currently set. The returned clipped geometry must be clipped by these
|
||||
// bounds to correctly reflect the total clip. This is in device space and
|
||||
// only for clips applied to the -current layer-.
|
||||
already_AddRefed<ID2D1Geometry> GetClippedGeometry(IntRect *aClipBounds);
|
||||
|
||||
already_AddRefed<ID2D1Geometry> GetInverseClippedGeometry();
|
||||
|
||||
// This gives the device space clip rect applied to the -current layer-.
|
||||
bool GetDeviceSpaceClipRect(D2D1_RECT_F& aClipRect, bool& aIsPixelAligned);
|
||||
|
||||
void PopAllClips();
|
||||
void PushAllClips();
|
||||
void PushClipsToDC(ID2D1DeviceContext *aDC, bool aForceIgnoreAlpha = false, const D2D1_RECT_F& aMaxRect = D2D1::InfiniteRect());
|
||||
void PopClipsFromDC(ID2D1DeviceContext *aDC);
|
||||
|
||||
already_AddRefed<ID2D1Brush> CreateTransparentBlackBrush();
|
||||
already_AddRefed<ID2D1SolidColorBrush> GetSolidColorBrush(const D2D_COLOR_F& aColor);
|
||||
already_AddRefed<ID2D1Brush> CreateBrushForPattern(const Pattern &aPattern, Float aAlpha = 1.0f);
|
||||
|
||||
void PushClipGeometry(ID2D1Geometry* aGeometry, const D2D1_MATRIX_3X2_F& aTransform, bool aPixelAligned = false);
|
||||
|
||||
void PushD2DLayer(ID2D1DeviceContext *aDC, ID2D1Geometry *aGeometry, const D2D1_MATRIX_3X2_F &aTransform,
|
||||
bool aPixelAligned = false, bool aForceIgnoreAlpha = false,
|
||||
const D2D1_RECT_F& aLayerRect = D2D1::InfiniteRect());
|
||||
|
||||
IntSize mSize;
|
||||
|
||||
RefPtr<ID3D11Device> mDevice;
|
||||
RefPtr<ID3D11Texture2D> mTexture;
|
||||
RefPtr<ID2D1Geometry> mCurrentClippedGeometry;
|
||||
// This is only valid if mCurrentClippedGeometry is non-null. And will
|
||||
// only be the intersection of all pixel-aligned retangular clips. This is in
|
||||
// device space.
|
||||
IntRect mCurrentClipBounds;
|
||||
mutable RefPtr<ID2D1DeviceContext> mDC;
|
||||
RefPtr<ID2D1Bitmap1> mBitmap;
|
||||
RefPtr<ID2D1CommandList> mCommandList;
|
||||
|
||||
RefPtr<ID2D1SolidColorBrush> mSolidColorBrush;
|
||||
|
||||
// We store this to prevent excessive SetTextRenderingParams calls.
|
||||
RefPtr<IDWriteRenderingParams> mTextRenderingParams;
|
||||
|
||||
// List of pushed clips.
|
||||
struct PushedClip
|
||||
{
|
||||
D2D1_RECT_F mBounds;
|
||||
// If mGeometry is non-null, the mTransform member will be used.
|
||||
D2D1_MATRIX_3X2_F mTransform;
|
||||
RefPtr<ID2D1Geometry> mGeometry;
|
||||
// Indicates if mBounds, and when non-null, mGeometry with mTransform
|
||||
// applied, are pixel-aligned.
|
||||
bool mIsPixelAligned;
|
||||
};
|
||||
|
||||
// List of pushed layers.
|
||||
struct PushedLayer
|
||||
{
|
||||
PushedLayer() : mClipsArePushed(false), mIsOpaque(false), mOldPermitSubpixelAA(false) {}
|
||||
|
||||
std::vector<PushedClip> mPushedClips;
|
||||
RefPtr<ID2D1CommandList> mCurrentList;
|
||||
// True if the current clip stack is pushed to the CurrentTarget().
|
||||
bool mClipsArePushed;
|
||||
bool mIsOpaque;
|
||||
bool mOldPermitSubpixelAA;
|
||||
};
|
||||
std::vector<PushedLayer> mPushedLayers;
|
||||
PushedLayer& CurrentLayer()
|
||||
{
|
||||
return mPushedLayers.back();
|
||||
}
|
||||
|
||||
// The latest snapshot of this surface. This needs to be told when this
|
||||
// target is modified. We keep it alive as a cache.
|
||||
RefPtr<SourceSurfaceD2D1> mSnapshot;
|
||||
// A list of targets we need to flush when we're modified.
|
||||
TargetSet mDependentTargets;
|
||||
// A list of targets which have this object in their mDependentTargets set
|
||||
TargetSet mDependingOnTargets;
|
||||
|
||||
uint32_t mUsedCommandListsSincePurge;
|
||||
// When a BlendEffect has been drawn to a command list, and that command list is
|
||||
// subsequently used -again- as an input to a blend effect for a command list,
|
||||
// this causes an infinite recursion inside D2D as it tries to resolve the bounds.
|
||||
// If we resolve the current command list before this happens
|
||||
// we can avoid the subsequent hang. (See bug 1293586)
|
||||
bool mDidComplexBlendWithListInList;
|
||||
|
||||
static ID2D1Factory1 *mFactory;
|
||||
static IDWriteFactory *mDWriteFactory;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* MOZILLA_GFX_DRAWTARGETD2D_H_ */
|
||||
223
gfx/2d/DrawTargetDual.cpp
Normal file
223
gfx/2d/DrawTargetDual.cpp
Normal file
|
|
@ -0,0 +1,223 @@
|
|||
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "DrawTargetDual.h"
|
||||
#include "Tools.h"
|
||||
#include "Logging.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
class DualSurface
|
||||
{
|
||||
public:
|
||||
inline explicit DualSurface(SourceSurface *aSurface)
|
||||
{
|
||||
if (!aSurface) {
|
||||
mA = mB = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
if (aSurface->GetType() != SurfaceType::DUAL_DT) {
|
||||
mA = mB = aSurface;
|
||||
return;
|
||||
}
|
||||
|
||||
SourceSurfaceDual *ssDual =
|
||||
static_cast<SourceSurfaceDual*>(aSurface);
|
||||
mA = ssDual->mA;
|
||||
mB = ssDual->mB;
|
||||
}
|
||||
|
||||
SourceSurface *mA;
|
||||
SourceSurface *mB;
|
||||
};
|
||||
|
||||
/* This only needs to split patterns up for SurfacePatterns. Only in that
|
||||
* case can we be dealing with a 'dual' source (SourceSurfaceDual) and do
|
||||
* we need to pass separate patterns into our destination DrawTargets.
|
||||
*/
|
||||
class DualPattern
|
||||
{
|
||||
public:
|
||||
inline explicit DualPattern(const Pattern &aPattern)
|
||||
: mPatternsInitialized(false)
|
||||
{
|
||||
if (aPattern.GetType() != PatternType::SURFACE) {
|
||||
mA = mB = &aPattern;
|
||||
return;
|
||||
}
|
||||
|
||||
const SurfacePattern *surfPat =
|
||||
static_cast<const SurfacePattern*>(&aPattern);
|
||||
|
||||
if (surfPat->mSurface->GetType() != SurfaceType::DUAL_DT) {
|
||||
mA = mB = &aPattern;
|
||||
return;
|
||||
}
|
||||
|
||||
const SourceSurfaceDual *ssDual =
|
||||
static_cast<const SourceSurfaceDual*>(surfPat->mSurface.get());
|
||||
mA = new (mSurfPatA.addr()) SurfacePattern(ssDual->mA, surfPat->mExtendMode,
|
||||
surfPat->mMatrix,
|
||||
surfPat->mSamplingFilter);
|
||||
mB = new (mSurfPatB.addr()) SurfacePattern(ssDual->mB, surfPat->mExtendMode,
|
||||
surfPat->mMatrix,
|
||||
surfPat->mSamplingFilter);
|
||||
mPatternsInitialized = true;
|
||||
}
|
||||
|
||||
inline ~DualPattern()
|
||||
{
|
||||
if (mPatternsInitialized) {
|
||||
mA->~Pattern();
|
||||
mB->~Pattern();
|
||||
}
|
||||
}
|
||||
|
||||
ClassStorage<SurfacePattern> mSurfPatA;
|
||||
ClassStorage<SurfacePattern> mSurfPatB;
|
||||
|
||||
const Pattern *mA;
|
||||
const Pattern *mB;
|
||||
|
||||
bool mPatternsInitialized;
|
||||
};
|
||||
|
||||
void
|
||||
DrawTargetDual::DetachAllSnapshots()
|
||||
{
|
||||
mA->DetachAllSnapshots();
|
||||
mB->DetachAllSnapshots();
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetDual::DrawSurface(SourceSurface *aSurface, const Rect &aDest, const Rect &aSource,
|
||||
const DrawSurfaceOptions &aSurfOptions, const DrawOptions &aOptions)
|
||||
{
|
||||
DualSurface surface(aSurface);
|
||||
mA->DrawSurface(surface.mA, aDest, aSource, aSurfOptions, aOptions);
|
||||
mB->DrawSurface(surface.mB, aDest, aSource, aSurfOptions, aOptions);
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetDual::DrawSurfaceWithShadow(SourceSurface *aSurface, const Point &aDest,
|
||||
const Color &aColor, const Point &aOffset,
|
||||
Float aSigma, CompositionOp aOp)
|
||||
{
|
||||
DualSurface surface(aSurface);
|
||||
mA->DrawSurfaceWithShadow(surface.mA, aDest, aColor, aOffset, aSigma, aOp);
|
||||
mB->DrawSurfaceWithShadow(surface.mB, aDest, aColor, aOffset, aSigma, aOp);
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetDual::MaskSurface(const Pattern &aSource,
|
||||
SourceSurface *aMask,
|
||||
Point aOffset,
|
||||
const DrawOptions &aOptions)
|
||||
{
|
||||
DualPattern source(aSource);
|
||||
DualSurface mask(aMask);
|
||||
mA->MaskSurface(*source.mA, mask.mA, aOffset, aOptions);
|
||||
mB->MaskSurface(*source.mB, mask.mB, aOffset, aOptions);
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetDual::CopySurface(SourceSurface *aSurface, const IntRect &aSourceRect,
|
||||
const IntPoint &aDestination)
|
||||
{
|
||||
DualSurface surface(aSurface);
|
||||
mA->CopySurface(surface.mA, aSourceRect, aDestination);
|
||||
mB->CopySurface(surface.mB, aSourceRect, aDestination);
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetDual::FillRect(const Rect &aRect, const Pattern &aPattern, const DrawOptions &aOptions)
|
||||
{
|
||||
DualPattern pattern(aPattern);
|
||||
mA->FillRect(aRect, *pattern.mA, aOptions);
|
||||
mB->FillRect(aRect, *pattern.mB, aOptions);
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetDual::StrokeRect(const Rect &aRect, const Pattern &aPattern,
|
||||
const StrokeOptions &aStrokeOptions, const DrawOptions &aOptions)
|
||||
{
|
||||
DualPattern pattern(aPattern);
|
||||
mA->StrokeRect(aRect, *pattern.mA, aStrokeOptions, aOptions);
|
||||
mB->StrokeRect(aRect, *pattern.mB, aStrokeOptions, aOptions);
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetDual::StrokeLine(const Point &aStart, const Point &aEnd, const Pattern &aPattern,
|
||||
const StrokeOptions &aStrokeOptions, const DrawOptions &aOptions)
|
||||
{
|
||||
DualPattern pattern(aPattern);
|
||||
mA->StrokeLine(aStart, aEnd, *pattern.mA, aStrokeOptions, aOptions);
|
||||
mB->StrokeLine(aStart, aEnd, *pattern.mB, aStrokeOptions, aOptions);
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetDual::Stroke(const Path *aPath, const Pattern &aPattern,
|
||||
const StrokeOptions &aStrokeOptions, const DrawOptions &aOptions)
|
||||
{
|
||||
DualPattern pattern(aPattern);
|
||||
mA->Stroke(aPath, *pattern.mA, aStrokeOptions, aOptions);
|
||||
mB->Stroke(aPath, *pattern.mB, aStrokeOptions, aOptions);
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetDual::Fill(const Path *aPath, const Pattern &aPattern, const DrawOptions &aOptions)
|
||||
{
|
||||
DualPattern pattern(aPattern);
|
||||
mA->Fill(aPath, *pattern.mA, aOptions);
|
||||
mB->Fill(aPath, *pattern.mB, aOptions);
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetDual::FillGlyphs(ScaledFont *aScaledFont, const GlyphBuffer &aBuffer,
|
||||
const Pattern &aPattern, const DrawOptions &aOptions,
|
||||
const GlyphRenderingOptions *aRenderingOptions)
|
||||
{
|
||||
DualPattern pattern(aPattern);
|
||||
mA->FillGlyphs(aScaledFont, aBuffer, *pattern.mA, aOptions, aRenderingOptions);
|
||||
mB->FillGlyphs(aScaledFont, aBuffer, *pattern.mB, aOptions, aRenderingOptions);
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetDual::Mask(const Pattern &aSource, const Pattern &aMask, const DrawOptions &aOptions)
|
||||
{
|
||||
DualPattern source(aSource);
|
||||
DualPattern mask(aMask);
|
||||
mA->Mask(*source.mA, *mask.mA, aOptions);
|
||||
mB->Mask(*source.mB, *mask.mB, aOptions);
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetDual::PushLayer(bool aOpaque, Float aOpacity, SourceSurface* aMask,
|
||||
const Matrix& aMaskTransform, const IntRect& aBounds,
|
||||
bool aCopyBackground)
|
||||
{
|
||||
DualSurface mask(aMask);
|
||||
mA->PushLayer(aOpaque, aOpacity, mask.mA, aMaskTransform, aBounds, aCopyBackground);
|
||||
mB->PushLayer(aOpaque, aOpacity, mask.mB, aMaskTransform, aBounds, aCopyBackground);
|
||||
}
|
||||
|
||||
already_AddRefed<DrawTarget>
|
||||
DrawTargetDual::CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const
|
||||
{
|
||||
RefPtr<DrawTarget> dtA = mA->CreateSimilarDrawTarget(aSize, aFormat);
|
||||
RefPtr<DrawTarget> dtB = mB->CreateSimilarDrawTarget(aSize, aFormat);
|
||||
|
||||
if (!dtA || !dtB) {
|
||||
gfxWarning() << "Failure to allocate a similar DrawTargetDual. Size: " << aSize;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return MakeAndAddRef<DrawTargetDual>(dtA, dtB);
|
||||
}
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
||||
178
gfx/2d/DrawTargetDual.h
Normal file
178
gfx/2d/DrawTargetDual.h
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
/* -*- Mode: C++; tab-width: 20; 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_GFX_DRAWTARGETDUAL_H_
|
||||
#define MOZILLA_GFX_DRAWTARGETDUAL_H_
|
||||
|
||||
#include <vector>
|
||||
#include <sstream>
|
||||
|
||||
#include "SourceSurfaceDual.h"
|
||||
|
||||
#include "2D.h"
|
||||
#include "Filters.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
#define FORWARD_FUNCTION(funcName) \
|
||||
virtual void funcName() override { mA->funcName(); mB->funcName(); }
|
||||
#define FORWARD_FUNCTION1(funcName, var1Type, var1Name) \
|
||||
virtual void funcName(var1Type var1Name) override { mA->funcName(var1Name); mB->funcName(var1Name); }
|
||||
|
||||
/* This is a special type of DrawTarget. It duplicates all drawing calls
|
||||
* accross two drawtargets. An exception to this is when a snapshot of another
|
||||
* dual DrawTarget is used as the source for any surface data. In this case
|
||||
* the snapshot of the first source DrawTarget is used as a source for the call
|
||||
* to the first destination DrawTarget (mA) and the snapshot of the second
|
||||
* source DrawTarget is used at the source for the second destination
|
||||
* DrawTarget (mB). This class facilitates black-background/white-background
|
||||
* drawing for per-component alpha extraction for backends which do not support
|
||||
* native component alpha.
|
||||
*/
|
||||
class DrawTargetDual : public DrawTarget
|
||||
{
|
||||
public:
|
||||
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawTargetDual, override)
|
||||
DrawTargetDual(DrawTarget *aA, DrawTarget *aB)
|
||||
: mA(aA)
|
||||
, mB(aB)
|
||||
{
|
||||
mFormat = aA->GetFormat();
|
||||
}
|
||||
|
||||
virtual DrawTargetType GetType() const override { return mA->GetType(); }
|
||||
virtual BackendType GetBackendType() const override { return mA->GetBackendType(); }
|
||||
virtual already_AddRefed<SourceSurface> Snapshot() override {
|
||||
return MakeAndAddRef<SourceSurfaceDual>(mA, mB);
|
||||
}
|
||||
virtual IntSize GetSize() override { return mA->GetSize(); }
|
||||
|
||||
virtual void DetachAllSnapshots() override;
|
||||
|
||||
FORWARD_FUNCTION(Flush)
|
||||
FORWARD_FUNCTION1(PushClip, const Path *, aPath)
|
||||
FORWARD_FUNCTION1(PushClipRect, const Rect &, aRect)
|
||||
FORWARD_FUNCTION(PopClip)
|
||||
FORWARD_FUNCTION(PopLayer)
|
||||
FORWARD_FUNCTION1(ClearRect, const Rect &, aRect)
|
||||
|
||||
virtual void SetTransform(const Matrix &aTransform) override {
|
||||
mTransform = aTransform;
|
||||
mA->SetTransform(aTransform);
|
||||
mB->SetTransform(aTransform);
|
||||
}
|
||||
|
||||
virtual void DrawSurface(SourceSurface *aSurface, const Rect &aDest, const Rect & aSource,
|
||||
const DrawSurfaceOptions &aSurfOptions, const DrawOptions &aOptions) override;
|
||||
|
||||
virtual void DrawFilter(FilterNode *aNode,
|
||||
const Rect &aSourceRect,
|
||||
const Point &aDestPoint,
|
||||
const DrawOptions &aOptions = DrawOptions()) override
|
||||
{
|
||||
mA->DrawFilter(aNode, aSourceRect, aDestPoint, aOptions);
|
||||
mB->DrawFilter(aNode, aSourceRect, aDestPoint, aOptions);
|
||||
}
|
||||
|
||||
virtual void MaskSurface(const Pattern &aSource,
|
||||
SourceSurface *aMask,
|
||||
Point aOffset,
|
||||
const DrawOptions &aOptions = DrawOptions()) override;
|
||||
|
||||
virtual void DrawSurfaceWithShadow(SourceSurface *aSurface, const Point &aDest,
|
||||
const Color &aColor, const Point &aOffset,
|
||||
Float aSigma, CompositionOp aOp) override;
|
||||
|
||||
virtual void CopySurface(SourceSurface *aSurface, const IntRect &aSourceRect,
|
||||
const IntPoint &aDestination) override;
|
||||
|
||||
virtual void FillRect(const Rect &aRect, const Pattern &aPattern, const DrawOptions &aOptions) override;
|
||||
|
||||
virtual void StrokeRect(const Rect &aRect, const Pattern &aPattern,
|
||||
const StrokeOptions &aStrokeOptions, const DrawOptions &aOptions) override;
|
||||
|
||||
virtual void StrokeLine(const Point &aStart, const Point &aEnd, const Pattern &aPattern,
|
||||
const StrokeOptions &aStrokeOptions, const DrawOptions &aOptions) override;
|
||||
|
||||
virtual void Stroke(const Path *aPath, const Pattern &aPattern,
|
||||
const StrokeOptions &aStrokeOptions, const DrawOptions &aOptions) override;
|
||||
|
||||
virtual void Fill(const Path *aPath, const Pattern &aPattern, const DrawOptions &aOptions) override;
|
||||
|
||||
virtual void FillGlyphs(ScaledFont *aScaledFont, const GlyphBuffer &aBuffer,
|
||||
const Pattern &aPattern, const DrawOptions &aOptions,
|
||||
const GlyphRenderingOptions *aRenderingOptions) override;
|
||||
|
||||
virtual void Mask(const Pattern &aSource, const Pattern &aMask, const DrawOptions &aOptions) override;
|
||||
|
||||
virtual void PushLayer(bool aOpaque, Float aOpacity,
|
||||
SourceSurface* aMask,
|
||||
const Matrix& aMaskTransform,
|
||||
const IntRect& aBounds = IntRect(),
|
||||
bool aCopyBackground = false) override;
|
||||
|
||||
virtual already_AddRefed<SourceSurface>
|
||||
CreateSourceSurfaceFromData(unsigned char *aData,
|
||||
const IntSize &aSize,
|
||||
int32_t aStride,
|
||||
SurfaceFormat aFormat) const override
|
||||
{
|
||||
return mA->CreateSourceSurfaceFromData(aData, aSize, aStride, aFormat);
|
||||
}
|
||||
|
||||
virtual already_AddRefed<SourceSurface> OptimizeSourceSurface(SourceSurface *aSurface) const override
|
||||
{
|
||||
return mA->OptimizeSourceSurface(aSurface);
|
||||
}
|
||||
|
||||
virtual already_AddRefed<SourceSurface>
|
||||
CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const override
|
||||
{
|
||||
return mA->CreateSourceSurfaceFromNativeSurface(aSurface);
|
||||
}
|
||||
|
||||
virtual already_AddRefed<DrawTarget>
|
||||
CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const override;
|
||||
|
||||
virtual already_AddRefed<PathBuilder> CreatePathBuilder(FillRule aFillRule = FillRule::FILL_WINDING) const override
|
||||
{
|
||||
return mA->CreatePathBuilder(aFillRule);
|
||||
}
|
||||
|
||||
virtual already_AddRefed<GradientStops>
|
||||
CreateGradientStops(GradientStop *aStops,
|
||||
uint32_t aNumStops,
|
||||
ExtendMode aExtendMode = ExtendMode::CLAMP) const override
|
||||
{
|
||||
return mA->CreateGradientStops(aStops, aNumStops, aExtendMode);
|
||||
}
|
||||
|
||||
virtual already_AddRefed<FilterNode> CreateFilter(FilterType aType) override
|
||||
{
|
||||
return mA->CreateFilter(aType);
|
||||
}
|
||||
|
||||
virtual void *GetNativeSurface(NativeSurfaceType aType) override
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
virtual bool IsDualDrawTarget() const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool IsCurrentGroupOpaque() override { return mA->IsCurrentGroupOpaque(); }
|
||||
|
||||
private:
|
||||
RefPtr<DrawTarget> mA;
|
||||
RefPtr<DrawTarget> mB;
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* MOZILLA_GFX_DRAWTARGETDUAL_H_ */
|
||||
737
gfx/2d/DrawTargetRecording.cpp
Normal file
737
gfx/2d/DrawTargetRecording.cpp
Normal file
|
|
@ -0,0 +1,737 @@
|
|||
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* vim: set ts=8 sts=2 et sw=2 tw=80:
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "DrawTargetRecording.h"
|
||||
#include "PathRecording.h"
|
||||
#include <stdio.h>
|
||||
|
||||
#include "Logging.h"
|
||||
#include "Tools.h"
|
||||
#include "Filters.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "RecordingTypes.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
struct RecordingSourceSurfaceUserData
|
||||
{
|
||||
void *refPtr;
|
||||
RefPtr<DrawEventRecorderPrivate> recorder;
|
||||
};
|
||||
|
||||
void RecordingSourceSurfaceUserDataFunc(void *aUserData)
|
||||
{
|
||||
RecordingSourceSurfaceUserData *userData =
|
||||
static_cast<RecordingSourceSurfaceUserData*>(aUserData);
|
||||
|
||||
userData->recorder->RemoveStoredObject(userData->refPtr);
|
||||
userData->recorder->RecordEvent(
|
||||
RecordedSourceSurfaceDestruction(userData->refPtr));
|
||||
|
||||
delete userData;
|
||||
}
|
||||
|
||||
static void
|
||||
StoreSourceSurface(DrawEventRecorderPrivate *aRecorder, SourceSurface *aSurface,
|
||||
DataSourceSurface *aDataSurf, const char *reason)
|
||||
{
|
||||
if (!aDataSurf) {
|
||||
gfxWarning() << "Recording failed to record SourceSurface for " << reason;
|
||||
// Insert a bogus source surface.
|
||||
int32_t stride = aSurface->GetSize().width * BytesPerPixel(aSurface->GetFormat());
|
||||
UniquePtr<uint8_t[]> sourceData(new uint8_t[stride * aSurface->GetSize().height]());
|
||||
aRecorder->RecordEvent(
|
||||
RecordedSourceSurfaceCreation(aSurface, sourceData.get(), stride,
|
||||
aSurface->GetSize(), aSurface->GetFormat()));
|
||||
} else {
|
||||
DataSourceSurface::ScopedMap map(aDataSurf, DataSourceSurface::READ);
|
||||
aRecorder->RecordEvent(
|
||||
RecordedSourceSurfaceCreation(aSurface, map.GetData(), map.GetStride(),
|
||||
aDataSurf->GetSize(), aDataSurf->GetFormat()));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
EnsureSurfaceStored(DrawEventRecorderPrivate *aRecorder, SourceSurface *aSurface,
|
||||
const char *reason)
|
||||
{
|
||||
if (aRecorder->HasStoredObject(aSurface)) {
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<DataSourceSurface> dataSurf = aSurface->GetDataSurface();
|
||||
StoreSourceSurface(aRecorder, aSurface, dataSurf, reason);
|
||||
aRecorder->AddStoredObject(aSurface);
|
||||
|
||||
RecordingSourceSurfaceUserData *userData = new RecordingSourceSurfaceUserData;
|
||||
userData->refPtr = aSurface;
|
||||
userData->recorder = aRecorder;
|
||||
aSurface->AddUserData(reinterpret_cast<UserDataKey*>(aRecorder),
|
||||
userData, &RecordingSourceSurfaceUserDataFunc);
|
||||
return;
|
||||
}
|
||||
|
||||
class SourceSurfaceRecording : public SourceSurface
|
||||
{
|
||||
public:
|
||||
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(SourceSurfaceRecording)
|
||||
SourceSurfaceRecording(SourceSurface *aFinalSurface, DrawEventRecorderPrivate *aRecorder)
|
||||
: mFinalSurface(aFinalSurface), mRecorder(aRecorder)
|
||||
{
|
||||
mRecorder->AddStoredObject(this);
|
||||
}
|
||||
|
||||
~SourceSurfaceRecording()
|
||||
{
|
||||
mRecorder->RemoveStoredObject(this);
|
||||
mRecorder->RecordEvent(RecordedSourceSurfaceDestruction(this));
|
||||
}
|
||||
|
||||
virtual SurfaceType GetType() const { return SurfaceType::RECORDING; }
|
||||
virtual IntSize GetSize() const { return mFinalSurface->GetSize(); }
|
||||
virtual SurfaceFormat GetFormat() const { return mFinalSurface->GetFormat(); }
|
||||
virtual already_AddRefed<DataSourceSurface> GetDataSurface() { return mFinalSurface->GetDataSurface(); }
|
||||
|
||||
RefPtr<SourceSurface> mFinalSurface;
|
||||
RefPtr<DrawEventRecorderPrivate> mRecorder;
|
||||
};
|
||||
|
||||
class GradientStopsRecording : public GradientStops
|
||||
{
|
||||
public:
|
||||
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GradientStopsRecording)
|
||||
GradientStopsRecording(GradientStops *aFinalGradientStops, DrawEventRecorderPrivate *aRecorder)
|
||||
: mFinalGradientStops(aFinalGradientStops), mRecorder(aRecorder)
|
||||
{
|
||||
mRecorder->AddStoredObject(this);
|
||||
}
|
||||
|
||||
~GradientStopsRecording()
|
||||
{
|
||||
mRecorder->RemoveStoredObject(this);
|
||||
mRecorder->RecordEvent(RecordedGradientStopsDestruction(this));
|
||||
}
|
||||
|
||||
virtual BackendType GetBackendType() const { return BackendType::RECORDING; }
|
||||
|
||||
RefPtr<GradientStops> mFinalGradientStops;
|
||||
RefPtr<DrawEventRecorderPrivate> mRecorder;
|
||||
};
|
||||
|
||||
static SourceSurface *
|
||||
GetSourceSurface(SourceSurface *aSurface)
|
||||
{
|
||||
if (aSurface->GetType() != SurfaceType::RECORDING) {
|
||||
return aSurface;
|
||||
}
|
||||
|
||||
return static_cast<SourceSurfaceRecording*>(aSurface)->mFinalSurface;
|
||||
}
|
||||
|
||||
static GradientStops *
|
||||
GetGradientStops(GradientStops *aStops)
|
||||
{
|
||||
if (aStops->GetBackendType() != BackendType::RECORDING) {
|
||||
return aStops;
|
||||
}
|
||||
|
||||
return static_cast<GradientStopsRecording*>(aStops)->mFinalGradientStops;
|
||||
}
|
||||
|
||||
class FilterNodeRecording : public FilterNode
|
||||
{
|
||||
public:
|
||||
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeRecording, override)
|
||||
using FilterNode::SetAttribute;
|
||||
|
||||
FilterNodeRecording(FilterNode *aFinalFilterNode, DrawEventRecorderPrivate *aRecorder)
|
||||
: mFinalFilterNode(aFinalFilterNode), mRecorder(aRecorder)
|
||||
{
|
||||
mRecorder->AddStoredObject(this);
|
||||
}
|
||||
|
||||
~FilterNodeRecording()
|
||||
{
|
||||
mRecorder->RemoveStoredObject(this);
|
||||
mRecorder->RecordEvent(RecordedFilterNodeDestruction(this));
|
||||
}
|
||||
|
||||
static FilterNode*
|
||||
GetFilterNode(FilterNode* aNode)
|
||||
{
|
||||
if (aNode->GetBackendType() != FILTER_BACKEND_RECORDING) {
|
||||
gfxWarning() << "Non recording filter node used with recording DrawTarget!";
|
||||
return aNode;
|
||||
}
|
||||
|
||||
return static_cast<FilterNodeRecording*>(aNode)->mFinalFilterNode;
|
||||
}
|
||||
|
||||
virtual void SetInput(uint32_t aIndex, SourceSurface *aSurface) override
|
||||
{
|
||||
EnsureSurfaceStored(mRecorder, aSurface, "SetInput");
|
||||
|
||||
mRecorder->RecordEvent(RecordedFilterNodeSetInput(this, aIndex, aSurface));
|
||||
mFinalFilterNode->SetInput(aIndex, GetSourceSurface(aSurface));
|
||||
}
|
||||
virtual void SetInput(uint32_t aIndex, FilterNode *aFilter) override
|
||||
{
|
||||
MOZ_ASSERT(mRecorder->HasStoredObject(aFilter));
|
||||
|
||||
mRecorder->RecordEvent(RecordedFilterNodeSetInput(this, aIndex, aFilter));
|
||||
mFinalFilterNode->SetInput(aIndex, GetFilterNode(aFilter));
|
||||
}
|
||||
|
||||
|
||||
#define FORWARD_SET_ATTRIBUTE(type, argtype) \
|
||||
virtual void SetAttribute(uint32_t aIndex, type aValue) override { \
|
||||
mRecorder->RecordEvent(RecordedFilterNodeSetAttribute(this, aIndex, aValue, RecordedFilterNodeSetAttribute::ARGTYPE_##argtype)); \
|
||||
mFinalFilterNode->SetAttribute(aIndex, aValue); \
|
||||
}
|
||||
|
||||
FORWARD_SET_ATTRIBUTE(bool, BOOL);
|
||||
FORWARD_SET_ATTRIBUTE(uint32_t, UINT32);
|
||||
FORWARD_SET_ATTRIBUTE(Float, FLOAT);
|
||||
FORWARD_SET_ATTRIBUTE(const Size&, SIZE);
|
||||
FORWARD_SET_ATTRIBUTE(const IntSize&, INTSIZE);
|
||||
FORWARD_SET_ATTRIBUTE(const IntPoint&, INTPOINT);
|
||||
FORWARD_SET_ATTRIBUTE(const Rect&, RECT);
|
||||
FORWARD_SET_ATTRIBUTE(const IntRect&, INTRECT);
|
||||
FORWARD_SET_ATTRIBUTE(const Point&, POINT);
|
||||
FORWARD_SET_ATTRIBUTE(const Matrix&, MATRIX);
|
||||
FORWARD_SET_ATTRIBUTE(const Matrix5x4&, MATRIX5X4);
|
||||
FORWARD_SET_ATTRIBUTE(const Point3D&, POINT3D);
|
||||
FORWARD_SET_ATTRIBUTE(const Color&, COLOR);
|
||||
|
||||
#undef FORWARD_SET_ATTRIBUTE
|
||||
|
||||
virtual void SetAttribute(uint32_t aIndex, const Float* aFloat, uint32_t aSize) override {
|
||||
mRecorder->RecordEvent(RecordedFilterNodeSetAttribute(this, aIndex, aFloat, aSize));
|
||||
mFinalFilterNode->SetAttribute(aIndex, aFloat, aSize);
|
||||
}
|
||||
|
||||
virtual FilterBackend GetBackendType() override { return FILTER_BACKEND_RECORDING; }
|
||||
|
||||
RefPtr<FilterNode> mFinalFilterNode;
|
||||
RefPtr<DrawEventRecorderPrivate> mRecorder;
|
||||
};
|
||||
|
||||
struct AdjustedPattern
|
||||
{
|
||||
explicit AdjustedPattern(const Pattern &aPattern)
|
||||
: mPattern(nullptr)
|
||||
{
|
||||
mOrigPattern = const_cast<Pattern*>(&aPattern);
|
||||
}
|
||||
|
||||
~AdjustedPattern() {
|
||||
if (mPattern) {
|
||||
mPattern->~Pattern();
|
||||
}
|
||||
}
|
||||
|
||||
operator Pattern*()
|
||||
{
|
||||
switch(mOrigPattern->GetType()) {
|
||||
case PatternType::COLOR:
|
||||
return mOrigPattern;
|
||||
case PatternType::SURFACE:
|
||||
{
|
||||
SurfacePattern *surfPat = static_cast<SurfacePattern*>(mOrigPattern);
|
||||
mPattern =
|
||||
new (mSurfPat) SurfacePattern(GetSourceSurface(surfPat->mSurface),
|
||||
surfPat->mExtendMode, surfPat->mMatrix,
|
||||
surfPat->mSamplingFilter,
|
||||
surfPat->mSamplingRect);
|
||||
return mPattern;
|
||||
}
|
||||
case PatternType::LINEAR_GRADIENT:
|
||||
{
|
||||
LinearGradientPattern *linGradPat = static_cast<LinearGradientPattern*>(mOrigPattern);
|
||||
mPattern =
|
||||
new (mLinGradPat) LinearGradientPattern(linGradPat->mBegin, linGradPat->mEnd,
|
||||
GetGradientStops(linGradPat->mStops),
|
||||
linGradPat->mMatrix);
|
||||
return mPattern;
|
||||
}
|
||||
case PatternType::RADIAL_GRADIENT:
|
||||
{
|
||||
RadialGradientPattern *radGradPat = static_cast<RadialGradientPattern*>(mOrigPattern);
|
||||
mPattern =
|
||||
new (mRadGradPat) RadialGradientPattern(radGradPat->mCenter1, radGradPat->mCenter2,
|
||||
radGradPat->mRadius1, radGradPat->mRadius2,
|
||||
GetGradientStops(radGradPat->mStops),
|
||||
radGradPat->mMatrix);
|
||||
return mPattern;
|
||||
}
|
||||
default:
|
||||
return new (mColPat) ColorPattern(Color());
|
||||
}
|
||||
|
||||
return mPattern;
|
||||
}
|
||||
|
||||
union {
|
||||
char mColPat[sizeof(ColorPattern)];
|
||||
char mLinGradPat[sizeof(LinearGradientPattern)];
|
||||
char mRadGradPat[sizeof(RadialGradientPattern)];
|
||||
char mSurfPat[sizeof(SurfacePattern)];
|
||||
};
|
||||
|
||||
Pattern *mOrigPattern;
|
||||
Pattern *mPattern;
|
||||
};
|
||||
|
||||
DrawTargetRecording::DrawTargetRecording(DrawEventRecorder *aRecorder, DrawTarget *aDT, bool aHasData)
|
||||
: mRecorder(static_cast<DrawEventRecorderPrivate*>(aRecorder))
|
||||
, mFinalDT(aDT)
|
||||
{
|
||||
RefPtr<SourceSurface> snapshot = aHasData ? mFinalDT->Snapshot() : nullptr;
|
||||
mRecorder->RecordEvent(RecordedDrawTargetCreation(this,
|
||||
mFinalDT->GetBackendType(),
|
||||
mFinalDT->GetSize(),
|
||||
mFinalDT->GetFormat(),
|
||||
aHasData, snapshot));
|
||||
mFormat = mFinalDT->GetFormat();
|
||||
}
|
||||
|
||||
DrawTargetRecording::DrawTargetRecording(const DrawTargetRecording *aDT,
|
||||
DrawTarget *aSimilarDT)
|
||||
: mRecorder(aDT->mRecorder)
|
||||
, mFinalDT(aSimilarDT)
|
||||
{
|
||||
mRecorder->RecordEvent(RecordedCreateSimilarDrawTarget(this,
|
||||
mFinalDT->GetSize(),
|
||||
mFinalDT->GetFormat()));
|
||||
mFormat = mFinalDT->GetFormat();
|
||||
}
|
||||
|
||||
DrawTargetRecording::~DrawTargetRecording()
|
||||
{
|
||||
mRecorder->RecordEvent(RecordedDrawTargetDestruction(this));
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetRecording::FillRect(const Rect &aRect,
|
||||
const Pattern &aPattern,
|
||||
const DrawOptions &aOptions)
|
||||
{
|
||||
EnsurePatternDependenciesStored(aPattern);
|
||||
|
||||
mRecorder->RecordEvent(RecordedFillRect(this, aRect, aPattern, aOptions));
|
||||
mFinalDT->FillRect(aRect, *AdjustedPattern(aPattern), aOptions);
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetRecording::StrokeRect(const Rect &aRect,
|
||||
const Pattern &aPattern,
|
||||
const StrokeOptions &aStrokeOptions,
|
||||
const DrawOptions &aOptions)
|
||||
{
|
||||
EnsurePatternDependenciesStored(aPattern);
|
||||
|
||||
mRecorder->RecordEvent(RecordedStrokeRect(this, aRect, aPattern, aStrokeOptions, aOptions));
|
||||
mFinalDT->StrokeRect(aRect, *AdjustedPattern(aPattern), aStrokeOptions, aOptions);
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetRecording::StrokeLine(const Point &aBegin,
|
||||
const Point &aEnd,
|
||||
const Pattern &aPattern,
|
||||
const StrokeOptions &aStrokeOptions,
|
||||
const DrawOptions &aOptions)
|
||||
{
|
||||
EnsurePatternDependenciesStored(aPattern);
|
||||
|
||||
mRecorder->RecordEvent(RecordedStrokeLine(this, aBegin, aEnd, aPattern, aStrokeOptions, aOptions));
|
||||
mFinalDT->StrokeLine(aBegin, aEnd, *AdjustedPattern(aPattern), aStrokeOptions, aOptions);
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetRecording::Fill(const Path *aPath,
|
||||
const Pattern &aPattern,
|
||||
const DrawOptions &aOptions)
|
||||
{
|
||||
RefPtr<PathRecording> pathRecording = EnsurePathStored(aPath);
|
||||
EnsurePatternDependenciesStored(aPattern);
|
||||
|
||||
mRecorder->RecordEvent(RecordedFill(this, pathRecording, aPattern, aOptions));
|
||||
mFinalDT->Fill(pathRecording->mPath, *AdjustedPattern(aPattern), aOptions);
|
||||
}
|
||||
|
||||
struct RecordingFontUserData
|
||||
{
|
||||
void *refPtr;
|
||||
RefPtr<DrawEventRecorderPrivate> recorder;
|
||||
};
|
||||
|
||||
void RecordingFontUserDataDestroyFunc(void *aUserData)
|
||||
{
|
||||
RecordingFontUserData *userData =
|
||||
static_cast<RecordingFontUserData*>(aUserData);
|
||||
|
||||
userData->recorder->RecordEvent(RecordedScaledFontDestruction(userData->refPtr));
|
||||
|
||||
delete userData;
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetRecording::FillGlyphs(ScaledFont *aFont,
|
||||
const GlyphBuffer &aBuffer,
|
||||
const Pattern &aPattern,
|
||||
const DrawOptions &aOptions,
|
||||
const GlyphRenderingOptions *aRenderingOptions)
|
||||
{
|
||||
EnsurePatternDependenciesStored(aPattern);
|
||||
|
||||
if (!aFont->GetUserData(reinterpret_cast<UserDataKey*>(mRecorder.get()))) {
|
||||
RecordedFontData fontData(aFont);
|
||||
RecordedFontDetails fontDetails;
|
||||
if (fontData.GetFontDetails(fontDetails)) {
|
||||
// Try to serialise the whole font, just in case this is a web font that
|
||||
// is not present on the system.
|
||||
if (!mRecorder->HasStoredFontData(fontDetails.fontDataKey)) {
|
||||
mRecorder->RecordEvent(fontData);
|
||||
mRecorder->AddStoredFontData(fontDetails.fontDataKey);
|
||||
}
|
||||
mRecorder->RecordEvent(RecordedScaledFontCreation(aFont, fontDetails));
|
||||
} else {
|
||||
// If that fails, record just the font description and try to load it from
|
||||
// the system on the other side.
|
||||
RecordedFontDescriptor fontDesc(aFont);
|
||||
if (fontDesc.IsValid()) {
|
||||
mRecorder->RecordEvent(fontDesc);
|
||||
} else {
|
||||
gfxWarning() << "DrawTargetRecording::FillGlyphs failed to serialise ScaledFont";
|
||||
}
|
||||
}
|
||||
RecordingFontUserData *userData = new RecordingFontUserData;
|
||||
userData->refPtr = aFont;
|
||||
userData->recorder = mRecorder;
|
||||
aFont->AddUserData(reinterpret_cast<UserDataKey*>(mRecorder.get()), userData,
|
||||
&RecordingFontUserDataDestroyFunc);
|
||||
}
|
||||
|
||||
mRecorder->RecordEvent(RecordedFillGlyphs(this, aFont, aPattern, aOptions, aBuffer.mGlyphs, aBuffer.mNumGlyphs));
|
||||
mFinalDT->FillGlyphs(aFont, aBuffer, *AdjustedPattern(aPattern), aOptions, aRenderingOptions);
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetRecording::Mask(const Pattern &aSource,
|
||||
const Pattern &aMask,
|
||||
const DrawOptions &aOptions)
|
||||
{
|
||||
EnsurePatternDependenciesStored(aSource);
|
||||
EnsurePatternDependenciesStored(aMask);
|
||||
|
||||
mRecorder->RecordEvent(RecordedMask(this, aSource, aMask, aOptions));
|
||||
mFinalDT->Mask(*AdjustedPattern(aSource), *AdjustedPattern(aMask), aOptions);
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetRecording::MaskSurface(const Pattern &aSource,
|
||||
SourceSurface *aMask,
|
||||
Point aOffset,
|
||||
const DrawOptions &aOptions)
|
||||
{
|
||||
EnsurePatternDependenciesStored(aSource);
|
||||
EnsureSurfaceStored(mRecorder, aMask, "MaskSurface");
|
||||
|
||||
mRecorder->RecordEvent(RecordedMaskSurface(this, aSource, aMask, aOffset, aOptions));
|
||||
mFinalDT->MaskSurface(*AdjustedPattern(aSource), GetSourceSurface(aMask), aOffset, aOptions);
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetRecording::Stroke(const Path *aPath,
|
||||
const Pattern &aPattern,
|
||||
const StrokeOptions &aStrokeOptions,
|
||||
const DrawOptions &aOptions)
|
||||
{
|
||||
RefPtr<PathRecording> pathRecording = EnsurePathStored(aPath);
|
||||
EnsurePatternDependenciesStored(aPattern);
|
||||
|
||||
mRecorder->RecordEvent(RecordedStroke(this, pathRecording, aPattern, aStrokeOptions, aOptions));
|
||||
mFinalDT->Stroke(pathRecording->mPath, *AdjustedPattern(aPattern), aStrokeOptions, aOptions);
|
||||
}
|
||||
|
||||
already_AddRefed<SourceSurface>
|
||||
DrawTargetRecording::Snapshot()
|
||||
{
|
||||
RefPtr<SourceSurface> surf = mFinalDT->Snapshot();
|
||||
|
||||
RefPtr<SourceSurface> retSurf = new SourceSurfaceRecording(surf, mRecorder);
|
||||
|
||||
mRecorder->RecordEvent(RecordedSnapshot(retSurf, this));
|
||||
|
||||
return retSurf.forget();
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetRecording::DetachAllSnapshots()
|
||||
{
|
||||
mFinalDT->DetachAllSnapshots();
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetRecording::DrawSurface(SourceSurface *aSurface,
|
||||
const Rect &aDest,
|
||||
const Rect &aSource,
|
||||
const DrawSurfaceOptions &aSurfOptions,
|
||||
const DrawOptions &aOptions)
|
||||
{
|
||||
EnsureSurfaceStored(mRecorder, aSurface, "DrawSurface");
|
||||
|
||||
mRecorder->RecordEvent(RecordedDrawSurface(this, aSurface, aDest, aSource, aSurfOptions, aOptions));
|
||||
mFinalDT->DrawSurface(GetSourceSurface(aSurface), aDest, aSource, aSurfOptions, aOptions);
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetRecording::DrawSurfaceWithShadow(SourceSurface *aSurface,
|
||||
const Point &aDest,
|
||||
const Color &aColor,
|
||||
const Point &aOffset,
|
||||
Float aSigma,
|
||||
CompositionOp aOp)
|
||||
{
|
||||
EnsureSurfaceStored(mRecorder, aSurface, "DrawSurfaceWithShadow");
|
||||
|
||||
mRecorder->RecordEvent(RecordedDrawSurfaceWithShadow(this, aSurface, aDest, aColor, aOffset, aSigma, aOp));
|
||||
mFinalDT->DrawSurfaceWithShadow(GetSourceSurface(aSurface), aDest, aColor, aOffset, aSigma, aOp);
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetRecording::DrawFilter(FilterNode *aNode,
|
||||
const Rect &aSourceRect,
|
||||
const Point &aDestPoint,
|
||||
const DrawOptions &aOptions)
|
||||
{
|
||||
MOZ_ASSERT(mRecorder->HasStoredObject(aNode));
|
||||
|
||||
mRecorder->RecordEvent(RecordedDrawFilter(this, aNode, aSourceRect, aDestPoint, aOptions));
|
||||
mFinalDT->DrawFilter(FilterNodeRecording::GetFilterNode(aNode), aSourceRect, aDestPoint, aOptions);
|
||||
}
|
||||
|
||||
already_AddRefed<FilterNode>
|
||||
DrawTargetRecording::CreateFilter(FilterType aType)
|
||||
{
|
||||
RefPtr<FilterNode> node = mFinalDT->CreateFilter(aType);
|
||||
|
||||
RefPtr<FilterNode> retNode = new FilterNodeRecording(node, mRecorder);
|
||||
|
||||
mRecorder->RecordEvent(RecordedFilterNodeCreation(retNode, aType));
|
||||
|
||||
return retNode.forget();
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetRecording::ClearRect(const Rect &aRect)
|
||||
{
|
||||
mRecorder->RecordEvent(RecordedClearRect(this, aRect));
|
||||
mFinalDT->ClearRect(aRect);
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetRecording::CopySurface(SourceSurface *aSurface,
|
||||
const IntRect &aSourceRect,
|
||||
const IntPoint &aDestination)
|
||||
{
|
||||
EnsureSurfaceStored(mRecorder, aSurface, "CopySurface");
|
||||
|
||||
mRecorder->RecordEvent(RecordedCopySurface(this, aSurface, aSourceRect, aDestination));
|
||||
mFinalDT->CopySurface(GetSourceSurface(aSurface), aSourceRect, aDestination);
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetRecording::PushClip(const Path *aPath)
|
||||
{
|
||||
RefPtr<PathRecording> pathRecording = EnsurePathStored(aPath);
|
||||
|
||||
mRecorder->RecordEvent(RecordedPushClip(this, pathRecording));
|
||||
mFinalDT->PushClip(pathRecording->mPath);
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetRecording::PushClipRect(const Rect &aRect)
|
||||
{
|
||||
mRecorder->RecordEvent(RecordedPushClipRect(this, aRect));
|
||||
mFinalDT->PushClipRect(aRect);
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetRecording::PopClip()
|
||||
{
|
||||
mRecorder->RecordEvent(RecordedPopClip(this));
|
||||
mFinalDT->PopClip();
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetRecording::PushLayer(bool aOpaque, Float aOpacity,
|
||||
SourceSurface* aMask,
|
||||
const Matrix& aMaskTransform,
|
||||
const IntRect& aBounds, bool aCopyBackground)
|
||||
{
|
||||
if (aMask) {
|
||||
EnsureSurfaceStored(mRecorder, aMask, "PushLayer");
|
||||
}
|
||||
|
||||
mRecorder->RecordEvent(RecordedPushLayer(this, aOpaque, aOpacity, aMask,
|
||||
aMaskTransform, aBounds,
|
||||
aCopyBackground));
|
||||
mFinalDT->PushLayer(aOpaque, aOpacity, aMask, aMaskTransform, aBounds,
|
||||
aCopyBackground);
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetRecording::PopLayer()
|
||||
{
|
||||
mRecorder->RecordEvent(RecordedPopLayer(this));
|
||||
mFinalDT->PopLayer();
|
||||
}
|
||||
|
||||
already_AddRefed<SourceSurface>
|
||||
DrawTargetRecording::CreateSourceSurfaceFromData(unsigned char *aData,
|
||||
const IntSize &aSize,
|
||||
int32_t aStride,
|
||||
SurfaceFormat aFormat) const
|
||||
{
|
||||
RefPtr<SourceSurface> surf = mFinalDT->CreateSourceSurfaceFromData(aData, aSize, aStride, aFormat);
|
||||
|
||||
RefPtr<SourceSurface> retSurf = new SourceSurfaceRecording(surf, mRecorder);
|
||||
|
||||
mRecorder->RecordEvent(RecordedSourceSurfaceCreation(retSurf, aData, aStride, aSize, aFormat));
|
||||
|
||||
return retSurf.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<SourceSurface>
|
||||
DrawTargetRecording::OptimizeSourceSurface(SourceSurface *aSurface) const
|
||||
{
|
||||
RefPtr<SourceSurface> surf = mFinalDT->OptimizeSourceSurface(aSurface);
|
||||
|
||||
RefPtr<SourceSurface> retSurf = new SourceSurfaceRecording(surf, mRecorder);
|
||||
|
||||
RefPtr<DataSourceSurface> dataSurf = surf->GetDataSurface();
|
||||
|
||||
if (!dataSurf) {
|
||||
// Let's try get it off the original surface.
|
||||
dataSurf = aSurface->GetDataSurface();
|
||||
}
|
||||
|
||||
StoreSourceSurface(mRecorder, retSurf, dataSurf, "OptimizeSourceSurface");
|
||||
|
||||
return retSurf.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<SourceSurface>
|
||||
DrawTargetRecording::CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const
|
||||
{
|
||||
RefPtr<SourceSurface> surf = mFinalDT->CreateSourceSurfaceFromNativeSurface(aSurface);
|
||||
|
||||
RefPtr<SourceSurface> retSurf = new SourceSurfaceRecording(surf, mRecorder);
|
||||
|
||||
RefPtr<DataSourceSurface> dataSurf = surf->GetDataSurface();
|
||||
StoreSourceSurface(mRecorder, retSurf, dataSurf, "CreateSourceSurfaceFromNativeSurface");
|
||||
|
||||
return retSurf.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<DrawTarget>
|
||||
DrawTargetRecording::CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const
|
||||
{
|
||||
RefPtr<DrawTarget> similarDT =
|
||||
mFinalDT->CreateSimilarDrawTarget(aSize, aFormat);
|
||||
if (!similarDT) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
similarDT = new DrawTargetRecording(this, similarDT);
|
||||
return similarDT.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<PathBuilder>
|
||||
DrawTargetRecording::CreatePathBuilder(FillRule aFillRule) const
|
||||
{
|
||||
RefPtr<PathBuilder> builder = mFinalDT->CreatePathBuilder(aFillRule);
|
||||
return MakeAndAddRef<PathBuilderRecording>(builder, aFillRule);
|
||||
}
|
||||
|
||||
already_AddRefed<GradientStops>
|
||||
DrawTargetRecording::CreateGradientStops(GradientStop *aStops,
|
||||
uint32_t aNumStops,
|
||||
ExtendMode aExtendMode) const
|
||||
{
|
||||
RefPtr<GradientStops> stops = mFinalDT->CreateGradientStops(aStops, aNumStops, aExtendMode);
|
||||
|
||||
RefPtr<GradientStops> retStops = new GradientStopsRecording(stops, mRecorder);
|
||||
|
||||
mRecorder->RecordEvent(RecordedGradientStopsCreation(retStops, aStops, aNumStops, aExtendMode));
|
||||
|
||||
return retStops.forget();
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetRecording::SetTransform(const Matrix &aTransform)
|
||||
{
|
||||
mRecorder->RecordEvent(RecordedSetTransform(this, aTransform));
|
||||
DrawTarget::SetTransform(aTransform);
|
||||
mFinalDT->SetTransform(aTransform);
|
||||
}
|
||||
|
||||
already_AddRefed<PathRecording>
|
||||
DrawTargetRecording::EnsurePathStored(const Path *aPath)
|
||||
{
|
||||
RefPtr<PathRecording> pathRecording;
|
||||
if (aPath->GetBackendType() == BackendType::RECORDING) {
|
||||
pathRecording = const_cast<PathRecording*>(static_cast<const PathRecording*>(aPath));
|
||||
if (mRecorder->HasStoredObject(aPath)) {
|
||||
return pathRecording.forget();
|
||||
}
|
||||
} else {
|
||||
MOZ_ASSERT(!mRecorder->HasStoredObject(aPath));
|
||||
FillRule fillRule = aPath->GetFillRule();
|
||||
RefPtr<PathBuilder> builder = mFinalDT->CreatePathBuilder(fillRule);
|
||||
RefPtr<PathBuilderRecording> builderRecording =
|
||||
new PathBuilderRecording(builder, fillRule);
|
||||
aPath->StreamToSink(builderRecording);
|
||||
pathRecording = builderRecording->Finish().downcast<PathRecording>();
|
||||
}
|
||||
|
||||
mRecorder->RecordEvent(RecordedPathCreation(pathRecording));
|
||||
mRecorder->AddStoredObject(pathRecording);
|
||||
pathRecording->mStoredRecorders.push_back(mRecorder);
|
||||
|
||||
return pathRecording.forget();
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetRecording::EnsurePatternDependenciesStored(const Pattern &aPattern)
|
||||
{
|
||||
switch (aPattern.GetType()) {
|
||||
case PatternType::COLOR:
|
||||
// No dependencies here.
|
||||
return;
|
||||
case PatternType::LINEAR_GRADIENT:
|
||||
{
|
||||
MOZ_ASSERT(mRecorder->HasStoredObject(static_cast<const LinearGradientPattern*>(&aPattern)->mStops));
|
||||
return;
|
||||
}
|
||||
case PatternType::RADIAL_GRADIENT:
|
||||
{
|
||||
MOZ_ASSERT(mRecorder->HasStoredObject(static_cast<const RadialGradientPattern*>(&aPattern)->mStops));
|
||||
return;
|
||||
}
|
||||
case PatternType::SURFACE:
|
||||
{
|
||||
const SurfacePattern *pat = static_cast<const SurfacePattern*>(&aPattern);
|
||||
EnsureSurfaceStored(mRecorder, pat->mSurface, "EnsurePatternDependenciesStored");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
||||
337
gfx/2d/DrawTargetRecording.h
Normal file
337
gfx/2d/DrawTargetRecording.h
Normal file
|
|
@ -0,0 +1,337 @@
|
|||
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* vim: set ts=8 sts=2 et sw=2 tw=80:
|
||||
* 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_GFX_DRAWTARGETRECORDING_H_
|
||||
#define MOZILLA_GFX_DRAWTARGETRECORDING_H_
|
||||
|
||||
#include "2D.h"
|
||||
#include "DrawEventRecorder.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
class DrawTargetRecording : public DrawTarget
|
||||
{
|
||||
public:
|
||||
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawTargetRecording, override)
|
||||
DrawTargetRecording(DrawEventRecorder *aRecorder, DrawTarget *aDT, bool aHasData = false);
|
||||
|
||||
~DrawTargetRecording();
|
||||
|
||||
virtual DrawTargetType GetType() const override { return mFinalDT->GetType(); }
|
||||
virtual BackendType GetBackendType() const override { return mFinalDT->GetBackendType(); }
|
||||
virtual bool IsRecording() const override { return true; }
|
||||
|
||||
virtual already_AddRefed<SourceSurface> Snapshot() override;
|
||||
|
||||
virtual void DetachAllSnapshots() override;
|
||||
|
||||
virtual IntSize GetSize() override { return mFinalDT->GetSize(); }
|
||||
|
||||
/* Ensure that the DrawTarget backend has flushed all drawing operations to
|
||||
* this draw target. This must be called before using the backing surface of
|
||||
* this draw target outside of GFX 2D code.
|
||||
*/
|
||||
virtual void Flush() override { mFinalDT->Flush(); }
|
||||
|
||||
/*
|
||||
* Draw a surface to the draw target. Possibly doing partial drawing or
|
||||
* applying scaling. No sampling happens outside the source.
|
||||
*
|
||||
* aSurface Source surface to draw
|
||||
* aDest Destination rectangle that this drawing operation should draw to
|
||||
* aSource Source rectangle in aSurface coordinates, this area of aSurface
|
||||
* will be stretched to the size of aDest.
|
||||
* aOptions General draw options that are applied to the operation
|
||||
* aSurfOptions DrawSurface options that are applied
|
||||
*/
|
||||
virtual void DrawSurface(SourceSurface *aSurface,
|
||||
const Rect &aDest,
|
||||
const Rect &aSource,
|
||||
const DrawSurfaceOptions &aSurfOptions = DrawSurfaceOptions(),
|
||||
const DrawOptions &aOptions = DrawOptions()) override;
|
||||
|
||||
virtual void DrawFilter(FilterNode *aNode,
|
||||
const Rect &aSourceRect,
|
||||
const Point &aDestPoint,
|
||||
const DrawOptions &aOptions = DrawOptions()) override;
|
||||
|
||||
/*
|
||||
* Blend a surface to the draw target with a shadow. The shadow is drawn as a
|
||||
* gaussian blur using a specified sigma. The shadow is clipped to the size
|
||||
* of the input surface, so the input surface should contain a transparent
|
||||
* border the size of the approximate coverage of the blur (3 * aSigma).
|
||||
* NOTE: This function works in device space!
|
||||
*
|
||||
* aSurface Source surface to draw.
|
||||
* aDest Destination point that this drawing operation should draw to.
|
||||
* aColor Color of the drawn shadow
|
||||
* aOffset Offset of the shadow
|
||||
* aSigma Sigma used for the guassian filter kernel
|
||||
* aOperator Composition operator used
|
||||
*/
|
||||
virtual void DrawSurfaceWithShadow(SourceSurface *aSurface,
|
||||
const Point &aDest,
|
||||
const Color &aColor,
|
||||
const Point &aOffset,
|
||||
Float aSigma,
|
||||
CompositionOp aOperator) override;
|
||||
|
||||
/*
|
||||
* Clear a rectangle on the draw target to transparent black. This will
|
||||
* respect the clipping region and transform.
|
||||
*
|
||||
* aRect Rectangle to clear
|
||||
*/
|
||||
virtual void ClearRect(const Rect &aRect) override;
|
||||
|
||||
/*
|
||||
* This is essentially a 'memcpy' between two surfaces. It moves a pixel
|
||||
* aligned area from the source surface unscaled directly onto the
|
||||
* drawtarget. This ignores both transform and clip.
|
||||
*
|
||||
* aSurface Surface to copy from
|
||||
* aSourceRect Source rectangle to be copied
|
||||
* aDest Destination point to copy the surface to
|
||||
*/
|
||||
virtual void CopySurface(SourceSurface *aSurface,
|
||||
const IntRect &aSourceRect,
|
||||
const IntPoint &aDestination) override;
|
||||
|
||||
/*
|
||||
* Fill a rectangle on the DrawTarget with a certain source pattern.
|
||||
*
|
||||
* aRect Rectangle that forms the mask of this filling operation
|
||||
* aPattern Pattern that forms the source of this filling operation
|
||||
* aOptions Options that are applied to this operation
|
||||
*/
|
||||
virtual void FillRect(const Rect &aRect,
|
||||
const Pattern &aPattern,
|
||||
const DrawOptions &aOptions = DrawOptions()) override;
|
||||
|
||||
/*
|
||||
* Stroke a rectangle on the DrawTarget with a certain source pattern.
|
||||
*
|
||||
* aRect Rectangle that forms the mask of this stroking operation
|
||||
* aPattern Pattern that forms the source of this stroking operation
|
||||
* aOptions Options that are applied to this operation
|
||||
*/
|
||||
virtual void StrokeRect(const Rect &aRect,
|
||||
const Pattern &aPattern,
|
||||
const StrokeOptions &aStrokeOptions = StrokeOptions(),
|
||||
const DrawOptions &aOptions = DrawOptions()) override;
|
||||
|
||||
/*
|
||||
* Stroke a line on the DrawTarget with a certain source pattern.
|
||||
*
|
||||
* aStart Starting point of the line
|
||||
* aEnd End point of the line
|
||||
* aPattern Pattern that forms the source of this stroking operation
|
||||
* aOptions Options that are applied to this operation
|
||||
*/
|
||||
virtual void StrokeLine(const Point &aStart,
|
||||
const Point &aEnd,
|
||||
const Pattern &aPattern,
|
||||
const StrokeOptions &aStrokeOptions = StrokeOptions(),
|
||||
const DrawOptions &aOptions = DrawOptions()) override;
|
||||
|
||||
/*
|
||||
* Stroke a path on the draw target with a certain source pattern.
|
||||
*
|
||||
* aPath Path that is to be stroked
|
||||
* aPattern Pattern that should be used for the stroke
|
||||
* aStrokeOptions Stroke options used for this operation
|
||||
* aOptions Draw options used for this operation
|
||||
*/
|
||||
virtual void Stroke(const Path *aPath,
|
||||
const Pattern &aPattern,
|
||||
const StrokeOptions &aStrokeOptions = StrokeOptions(),
|
||||
const DrawOptions &aOptions = DrawOptions()) override;
|
||||
|
||||
/*
|
||||
* Fill a path on the draw target with a certain source pattern.
|
||||
*
|
||||
* aPath Path that is to be filled
|
||||
* aPattern Pattern that should be used for the fill
|
||||
* aOptions Draw options used for this operation
|
||||
*/
|
||||
virtual void Fill(const Path *aPath,
|
||||
const Pattern &aPattern,
|
||||
const DrawOptions &aOptions = DrawOptions()) override;
|
||||
|
||||
/*
|
||||
* Fill a series of clyphs on the draw target with a certain source pattern.
|
||||
*/
|
||||
virtual void FillGlyphs(ScaledFont *aFont,
|
||||
const GlyphBuffer &aBuffer,
|
||||
const Pattern &aPattern,
|
||||
const DrawOptions &aOptions = DrawOptions(),
|
||||
const GlyphRenderingOptions *aRenderingOptions = nullptr) override;
|
||||
|
||||
/*
|
||||
* This takes a source pattern and a mask, and composites the source pattern
|
||||
* onto the destination surface using the alpha channel of the mask pattern
|
||||
* as a mask for the operation.
|
||||
*
|
||||
* aSource Source pattern
|
||||
* aMask Mask pattern
|
||||
* aOptions Drawing options
|
||||
*/
|
||||
virtual void Mask(const Pattern &aSource,
|
||||
const Pattern &aMask,
|
||||
const DrawOptions &aOptions = DrawOptions()) override;
|
||||
|
||||
virtual void MaskSurface(const Pattern &aSource,
|
||||
SourceSurface *aMask,
|
||||
Point aOffset,
|
||||
const DrawOptions &aOptions = DrawOptions()) override;
|
||||
|
||||
/*
|
||||
* Push a clip to the DrawTarget.
|
||||
*
|
||||
* aPath The path to clip to
|
||||
*/
|
||||
virtual void PushClip(const Path *aPath) override;
|
||||
|
||||
/*
|
||||
* Push an axis-aligned rectangular clip to the DrawTarget. This rectangle
|
||||
* is specified in user space.
|
||||
*
|
||||
* aRect The rect to clip to
|
||||
*/
|
||||
virtual void PushClipRect(const Rect &aRect) override;
|
||||
|
||||
/* Pop a clip from the DrawTarget. A pop without a corresponding push will
|
||||
* be ignored.
|
||||
*/
|
||||
virtual void PopClip() override;
|
||||
|
||||
/**
|
||||
* Push a 'layer' to the DrawTarget, a layer is a temporary surface that all
|
||||
* drawing will be redirected to, this is used for example to support group
|
||||
* opacity or the masking of groups. Clips must be balanced within a layer,
|
||||
* i.e. between a matching PushLayer/PopLayer pair there must be as many
|
||||
* PushClip(Rect) calls as there are PopClip calls.
|
||||
*
|
||||
* @param aOpaque Whether the layer will be opaque
|
||||
* @param aOpacity Opacity of the layer
|
||||
* @param aMask Mask applied to the layer
|
||||
* @param aMaskTransform Transform applied to the layer mask
|
||||
* @param aBounds Optional bounds in device space to which the layer is
|
||||
* limited in size.
|
||||
* @param aCopyBackground Whether to copy the background into the layer, this
|
||||
* is only supported when aOpaque is true.
|
||||
*/
|
||||
virtual void PushLayer(bool aOpaque, Float aOpacity,
|
||||
SourceSurface* aMask,
|
||||
const Matrix& aMaskTransform,
|
||||
const IntRect& aBounds = IntRect(),
|
||||
bool aCopyBackground = false) override;
|
||||
|
||||
/**
|
||||
* This balances a call to PushLayer and proceeds to blend the layer back
|
||||
* onto the background. This blend will blend the temporary surface back
|
||||
* onto the target in device space using POINT sampling and operator over.
|
||||
*/
|
||||
virtual void PopLayer() override;
|
||||
|
||||
/*
|
||||
* Create a SourceSurface optimized for use with this DrawTarget from
|
||||
* existing bitmap data in memory.
|
||||
*
|
||||
* The SourceSurface does not take ownership of aData, and may be freed at any time.
|
||||
*/
|
||||
virtual already_AddRefed<SourceSurface> CreateSourceSurfaceFromData(unsigned char *aData,
|
||||
const IntSize &aSize,
|
||||
int32_t aStride,
|
||||
SurfaceFormat aFormat) const override;
|
||||
|
||||
/*
|
||||
* Create a SourceSurface optimized for use with this DrawTarget from
|
||||
* an arbitrary other SourceSurface. This may return aSourceSurface or some
|
||||
* other existing surface.
|
||||
*/
|
||||
virtual already_AddRefed<SourceSurface> OptimizeSourceSurface(SourceSurface *aSurface) const override;
|
||||
|
||||
/*
|
||||
* Create a SourceSurface for a type of NativeSurface. This may fail if the
|
||||
* draw target does not know how to deal with the type of NativeSurface passed
|
||||
* in.
|
||||
*/
|
||||
virtual already_AddRefed<SourceSurface>
|
||||
CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const override;
|
||||
|
||||
/*
|
||||
* Create a DrawTarget whose snapshot is optimized for use with this DrawTarget.
|
||||
*/
|
||||
virtual already_AddRefed<DrawTarget>
|
||||
CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const override;
|
||||
|
||||
/*
|
||||
* Create a path builder with the specified fillmode.
|
||||
*
|
||||
* We need the fill mode up front because of Direct2D.
|
||||
* ID2D1SimplifiedGeometrySink requires the fill mode
|
||||
* to be set before calling BeginFigure().
|
||||
*/
|
||||
virtual already_AddRefed<PathBuilder> CreatePathBuilder(FillRule aFillRule = FillRule::FILL_WINDING) const override;
|
||||
|
||||
/*
|
||||
* Create a GradientStops object that holds information about a set of
|
||||
* gradient stops, this object is required for linear or radial gradient
|
||||
* patterns to represent the color stops in the gradient.
|
||||
*
|
||||
* aStops An array of gradient stops
|
||||
* aNumStops Number of stops in the array aStops
|
||||
* aExtendNone This describes how to extend the stop color outside of the
|
||||
* gradient area.
|
||||
*/
|
||||
virtual already_AddRefed<GradientStops>
|
||||
CreateGradientStops(GradientStop *aStops,
|
||||
uint32_t aNumStops,
|
||||
ExtendMode aExtendMode = ExtendMode::CLAMP) const override;
|
||||
|
||||
virtual already_AddRefed<FilterNode> CreateFilter(FilterType aType) override;
|
||||
|
||||
/*
|
||||
* Set a transform on the surface, this transform is applied at drawing time
|
||||
* to both the mask and source of the operation.
|
||||
*/
|
||||
virtual void SetTransform(const Matrix &aTransform) override;
|
||||
|
||||
/* Tries to get a native surface for a DrawTarget, this may fail if the
|
||||
* draw target cannot convert to this surface type.
|
||||
*/
|
||||
virtual void *GetNativeSurface(NativeSurfaceType aType) override { return mFinalDT->GetNativeSurface(aType); }
|
||||
|
||||
virtual bool IsCurrentGroupOpaque() override {
|
||||
return mFinalDT->IsCurrentGroupOpaque();
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* Used for creating a DrawTargetRecording for a CreateSimilarDrawTarget call.
|
||||
* We have to call CreateSimilarDrawTarget on mFinalDT up front and pass it in
|
||||
* as it can fail.
|
||||
*
|
||||
* @param aDT DrawTargetRecording on which CreateSimilarDrawTarget was called
|
||||
* @param aSimilarDT Similar DrawTarget created from aDT.mFinalDT.
|
||||
*/
|
||||
DrawTargetRecording(const DrawTargetRecording *aDT,
|
||||
DrawTarget *aSimilarDT);
|
||||
|
||||
Path *GetPathForPathRecording(const Path *aPath) const;
|
||||
already_AddRefed<PathRecording> EnsurePathStored(const Path *aPath);
|
||||
void EnsurePatternDependenciesStored(const Pattern &aPattern);
|
||||
|
||||
RefPtr<DrawEventRecorderPrivate> mRecorder;
|
||||
RefPtr<DrawTarget> mFinalDT;
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* MOZILLA_GFX_DRAWTARGETRECORDING_H_ */
|
||||
2135
gfx/2d/DrawTargetSkia.cpp
Normal file
2135
gfx/2d/DrawTargetSkia.cpp
Normal file
File diff suppressed because it is too large
Load diff
214
gfx/2d/DrawTargetSkia.h
Normal file
214
gfx/2d/DrawTargetSkia.h
Normal file
|
|
@ -0,0 +1,214 @@
|
|||
/* -*- Mode: C++; tab-width: 20; 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_GFX_SOURCESURFACESKIA_H
|
||||
#define _MOZILLA_GFX_SOURCESURFACESKIA_H
|
||||
|
||||
#include "skia/include/core/SkCanvas.h"
|
||||
#include "skia/include/core/SkSurface.h"
|
||||
|
||||
#include "2D.h"
|
||||
#include "HelpersSkia.h"
|
||||
#include "Rect.h"
|
||||
#include "PathSkia.h"
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#ifdef MOZ_WIDGET_COCOA
|
||||
#include <ApplicationServices/ApplicationServices.h>
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
class SourceSurfaceSkia;
|
||||
|
||||
class DrawTargetSkia : public DrawTarget
|
||||
{
|
||||
public:
|
||||
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawTargetSkia, override)
|
||||
DrawTargetSkia();
|
||||
virtual ~DrawTargetSkia();
|
||||
|
||||
virtual DrawTargetType GetType() const override;
|
||||
virtual BackendType GetBackendType() const override { return BackendType::SKIA; }
|
||||
virtual already_AddRefed<SourceSurface> Snapshot() override;
|
||||
virtual IntSize GetSize() override { return mSize; }
|
||||
virtual bool LockBits(uint8_t** aData, IntSize* aSize,
|
||||
int32_t* aStride, SurfaceFormat* aFormat,
|
||||
IntPoint* aOrigin = nullptr) override;
|
||||
virtual void ReleaseBits(uint8_t* aData) override;
|
||||
virtual void Flush() override;
|
||||
virtual void DrawSurface(SourceSurface *aSurface,
|
||||
const Rect &aDest,
|
||||
const Rect &aSource,
|
||||
const DrawSurfaceOptions &aSurfOptions = DrawSurfaceOptions(),
|
||||
const DrawOptions &aOptions = DrawOptions()) override;
|
||||
virtual void DrawFilter(FilterNode *aNode,
|
||||
const Rect &aSourceRect,
|
||||
const Point &aDestPoint,
|
||||
const DrawOptions &aOptions = DrawOptions()) override;
|
||||
virtual void DrawSurfaceWithShadow(SourceSurface *aSurface,
|
||||
const Point &aDest,
|
||||
const Color &aColor,
|
||||
const Point &aOffset,
|
||||
Float aSigma,
|
||||
CompositionOp aOperator) override;
|
||||
virtual void ClearRect(const Rect &aRect) override;
|
||||
virtual void CopySurface(SourceSurface *aSurface,
|
||||
const IntRect &aSourceRect,
|
||||
const IntPoint &aDestination) override;
|
||||
virtual void FillRect(const Rect &aRect,
|
||||
const Pattern &aPattern,
|
||||
const DrawOptions &aOptions = DrawOptions()) override;
|
||||
virtual void StrokeRect(const Rect &aRect,
|
||||
const Pattern &aPattern,
|
||||
const StrokeOptions &aStrokeOptions = StrokeOptions(),
|
||||
const DrawOptions &aOptions = DrawOptions()) override;
|
||||
virtual void StrokeLine(const Point &aStart,
|
||||
const Point &aEnd,
|
||||
const Pattern &aPattern,
|
||||
const StrokeOptions &aStrokeOptions = StrokeOptions(),
|
||||
const DrawOptions &aOptions = DrawOptions()) override;
|
||||
virtual void Stroke(const Path *aPath,
|
||||
const Pattern &aPattern,
|
||||
const StrokeOptions &aStrokeOptions = StrokeOptions(),
|
||||
const DrawOptions &aOptions = DrawOptions()) override;
|
||||
virtual void Fill(const Path *aPath,
|
||||
const Pattern &aPattern,
|
||||
const DrawOptions &aOptions = DrawOptions()) override;
|
||||
#ifdef MOZ_WIDGET_COCOA
|
||||
CGContextRef BorrowCGContext(const DrawOptions &aOptions);
|
||||
void ReturnCGContext(CGContextRef);
|
||||
bool FillGlyphsWithCG(ScaledFont *aFont,
|
||||
const GlyphBuffer &aBuffer,
|
||||
const Pattern &aPattern,
|
||||
const DrawOptions &aOptions = DrawOptions(),
|
||||
const GlyphRenderingOptions *aRenderingOptions = nullptr);
|
||||
#endif
|
||||
|
||||
virtual void FillGlyphs(ScaledFont *aFont,
|
||||
const GlyphBuffer &aBuffer,
|
||||
const Pattern &aPattern,
|
||||
const DrawOptions &aOptions = DrawOptions(),
|
||||
const GlyphRenderingOptions *aRenderingOptions = nullptr) override;
|
||||
virtual void Mask(const Pattern &aSource,
|
||||
const Pattern &aMask,
|
||||
const DrawOptions &aOptions = DrawOptions()) override;
|
||||
virtual void MaskSurface(const Pattern &aSource,
|
||||
SourceSurface *aMask,
|
||||
Point aOffset,
|
||||
const DrawOptions &aOptions = DrawOptions()) override;
|
||||
virtual bool Draw3DTransformedSurface(SourceSurface* aSurface,
|
||||
const Matrix4x4& aMatrix) override;
|
||||
virtual void PushClip(const Path *aPath) override;
|
||||
virtual void PushClipRect(const Rect& aRect) override;
|
||||
virtual void PushDeviceSpaceClipRects(const IntRect* aRects, uint32_t aCount) override;
|
||||
virtual void PopClip() override;
|
||||
virtual void PushLayer(bool aOpaque, Float aOpacity,
|
||||
SourceSurface* aMask,
|
||||
const Matrix& aMaskTransform,
|
||||
const IntRect& aBounds = IntRect(),
|
||||
bool aCopyBackground = false) override;
|
||||
virtual void PopLayer() override;
|
||||
virtual already_AddRefed<SourceSurface> CreateSourceSurfaceFromData(unsigned char *aData,
|
||||
const IntSize &aSize,
|
||||
int32_t aStride,
|
||||
SurfaceFormat aFormat) const override;
|
||||
virtual already_AddRefed<SourceSurface> OptimizeSourceSurface(SourceSurface *aSurface) const override;
|
||||
virtual already_AddRefed<SourceSurface> OptimizeSourceSurfaceForUnknownAlpha(SourceSurface *aSurface) const override;
|
||||
virtual already_AddRefed<SourceSurface>
|
||||
CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const override;
|
||||
virtual already_AddRefed<DrawTarget>
|
||||
CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const override;
|
||||
virtual already_AddRefed<PathBuilder> CreatePathBuilder(FillRule aFillRule = FillRule::FILL_WINDING) const override;
|
||||
virtual already_AddRefed<GradientStops> CreateGradientStops(GradientStop *aStops, uint32_t aNumStops, ExtendMode aExtendMode = ExtendMode::CLAMP) const override;
|
||||
virtual already_AddRefed<FilterNode> CreateFilter(FilterType aType) override;
|
||||
virtual void SetTransform(const Matrix &aTransform) override;
|
||||
virtual void *GetNativeSurface(NativeSurfaceType aType) override;
|
||||
virtual void DetachAllSnapshots() override { MarkChanged(); }
|
||||
|
||||
bool Init(const IntSize &aSize, SurfaceFormat aFormat);
|
||||
bool Init(unsigned char* aData, const IntSize &aSize, int32_t aStride, SurfaceFormat aFormat, bool aUninitialized = false);
|
||||
bool Init(SkCanvas* aCanvas);
|
||||
|
||||
#ifdef USE_SKIA_GPU
|
||||
bool InitWithGrContext(GrContext* aGrContext,
|
||||
const IntSize &aSize,
|
||||
SurfaceFormat aFormat,
|
||||
bool aCached);
|
||||
virtual bool
|
||||
InitWithGrContext(GrContext* aGrContext,
|
||||
const IntSize &aSize,
|
||||
SurfaceFormat aFormat) override {
|
||||
return InitWithGrContext(aGrContext, aSize, aFormat, false);
|
||||
}
|
||||
|
||||
already_AddRefed<SourceSurface> OptimizeGPUSourceSurface(SourceSurface *aSurface) const;
|
||||
#endif
|
||||
|
||||
// Skia assumes that texture sizes fit in 16-bit signed integers.
|
||||
static size_t GetMaxSurfaceSize() {
|
||||
return 32767;
|
||||
}
|
||||
|
||||
operator std::string() const {
|
||||
std::stringstream stream;
|
||||
stream << "DrawTargetSkia(" << this << ")";
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
private:
|
||||
friend class SourceSurfaceSkia;
|
||||
void SnapshotDestroyed();
|
||||
|
||||
void MarkChanged();
|
||||
|
||||
bool ShouldLCDRenderText(FontType aFontType, AntialiasMode aAntialiasMode);
|
||||
|
||||
bool UsingSkiaGPU() const;
|
||||
|
||||
struct PushedLayer
|
||||
{
|
||||
PushedLayer(bool aOldPermitSubpixelAA,
|
||||
bool aOpaque,
|
||||
Float aOpacity,
|
||||
SourceSurface* aMask,
|
||||
const Matrix& aMaskTransform)
|
||||
: mOldPermitSubpixelAA(aOldPermitSubpixelAA),
|
||||
mOpaque(aOpaque),
|
||||
mOpacity(aOpacity),
|
||||
mMask(aMask),
|
||||
mMaskTransform(aMaskTransform)
|
||||
{}
|
||||
bool mOldPermitSubpixelAA;
|
||||
bool mOpaque;
|
||||
Float mOpacity;
|
||||
RefPtr<SourceSurface> mMask;
|
||||
Matrix mMaskTransform;
|
||||
};
|
||||
std::vector<PushedLayer> mPushedLayers;
|
||||
|
||||
#ifdef USE_SKIA_GPU
|
||||
sk_sp<GrContext> mGrContext;
|
||||
#endif
|
||||
|
||||
IntSize mSize;
|
||||
sk_sp<SkSurface> mSurface;
|
||||
sk_sp<SkCanvas> mCanvas;
|
||||
SourceSurfaceSkia* mSnapshot;
|
||||
|
||||
#ifdef MOZ_WIDGET_COCOA
|
||||
CGContextRef mCG;
|
||||
CGColorSpaceRef mColorSpace;
|
||||
uint8_t* mCanvasData;
|
||||
IntSize mCGSize;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // _MOZILLA_GFX_SOURCESURFACESKIA_H
|
||||
337
gfx/2d/DrawTargetTiled.cpp
Normal file
337
gfx/2d/DrawTargetTiled.cpp
Normal file
|
|
@ -0,0 +1,337 @@
|
|||
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "DrawTargetTiled.h"
|
||||
#include "Logging.h"
|
||||
#include "PathHelpers.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
DrawTargetTiled::DrawTargetTiled()
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
DrawTargetTiled::Init(const TileSet& aTiles)
|
||||
{
|
||||
if (!aTiles.mTileCount) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mTiles.reserve(aTiles.mTileCount);
|
||||
for (size_t i = 0; i < aTiles.mTileCount; ++i) {
|
||||
mTiles.push_back(TileInternal(aTiles.mTiles[i]));
|
||||
if (!aTiles.mTiles[i].mDrawTarget) {
|
||||
return false;
|
||||
}
|
||||
if (mTiles[0].mDrawTarget->GetFormat() != mTiles.back().mDrawTarget->GetFormat() ||
|
||||
mTiles[0].mDrawTarget->GetBackendType() != mTiles.back().mDrawTarget->GetBackendType()) {
|
||||
return false;
|
||||
}
|
||||
uint32_t newXMost = max(mRect.XMost(),
|
||||
mTiles[i].mTileOrigin.x + mTiles[i].mDrawTarget->GetSize().width);
|
||||
uint32_t newYMost = max(mRect.YMost(),
|
||||
mTiles[i].mTileOrigin.y + mTiles[i].mDrawTarget->GetSize().height);
|
||||
mRect.x = min(mRect.x, mTiles[i].mTileOrigin.x);
|
||||
mRect.y = min(mRect.y, mTiles[i].mTileOrigin.y);
|
||||
mRect.width = newXMost - mRect.x;
|
||||
mRect.height = newYMost - mRect.y;
|
||||
mTiles[i].mDrawTarget->SetTransform(Matrix::Translation(mTiles[i].mTileOrigin.x,
|
||||
mTiles[i].mTileOrigin.y));
|
||||
}
|
||||
mFormat = mTiles[0].mDrawTarget->GetFormat();
|
||||
return true;
|
||||
}
|
||||
|
||||
already_AddRefed<SourceSurface>
|
||||
DrawTargetTiled::Snapshot()
|
||||
{
|
||||
return MakeAndAddRef<SnapshotTiled>(mTiles, mRect);
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetTiled::DetachAllSnapshots()
|
||||
{}
|
||||
|
||||
// Skip the mClippedOut check since this is only used for Flush() which
|
||||
// should happen even if we're clipped.
|
||||
#define TILED_COMMAND(command) \
|
||||
void \
|
||||
DrawTargetTiled::command() \
|
||||
{ \
|
||||
for (size_t i = 0; i < mTiles.size(); i++) { \
|
||||
mTiles[i].mDrawTarget->command(); \
|
||||
} \
|
||||
}
|
||||
#define TILED_COMMAND1(command, type1) \
|
||||
void \
|
||||
DrawTargetTiled::command(type1 arg1) \
|
||||
{ \
|
||||
for (size_t i = 0; i < mTiles.size(); i++) { \
|
||||
if (!mTiles[i].mClippedOut) \
|
||||
mTiles[i].mDrawTarget->command(arg1); \
|
||||
} \
|
||||
}
|
||||
#define TILED_COMMAND3(command, type1, type2, type3) \
|
||||
void \
|
||||
DrawTargetTiled::command(type1 arg1, type2 arg2, type3 arg3) \
|
||||
{ \
|
||||
for (size_t i = 0; i < mTiles.size(); i++) { \
|
||||
if (!mTiles[i].mClippedOut) \
|
||||
mTiles[i].mDrawTarget->command(arg1, arg2, arg3); \
|
||||
} \
|
||||
}
|
||||
#define TILED_COMMAND4(command, type1, type2, type3, type4) \
|
||||
void \
|
||||
DrawTargetTiled::command(type1 arg1, type2 arg2, type3 arg3, type4 arg4) \
|
||||
{ \
|
||||
for (size_t i = 0; i < mTiles.size(); i++) { \
|
||||
if (!mTiles[i].mClippedOut) \
|
||||
mTiles[i].mDrawTarget->command(arg1, arg2, arg3, arg4); \
|
||||
} \
|
||||
}
|
||||
#define TILED_COMMAND5(command, type1, type2, type3, type4, type5) \
|
||||
void \
|
||||
DrawTargetTiled::command(type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5) \
|
||||
{ \
|
||||
for (size_t i = 0; i < mTiles.size(); i++) { \
|
||||
if (!mTiles[i].mClippedOut) \
|
||||
mTiles[i].mDrawTarget->command(arg1, arg2, arg3, arg4, arg5); \
|
||||
} \
|
||||
}
|
||||
|
||||
TILED_COMMAND(Flush)
|
||||
TILED_COMMAND4(DrawFilter, FilterNode*, const Rect&, const Point&, const DrawOptions&)
|
||||
TILED_COMMAND1(ClearRect, const Rect&)
|
||||
TILED_COMMAND4(MaskSurface, const Pattern&, SourceSurface*, Point, const DrawOptions&)
|
||||
TILED_COMMAND5(FillGlyphs, ScaledFont*, const GlyphBuffer&, const Pattern&, const DrawOptions&, const GlyphRenderingOptions*)
|
||||
TILED_COMMAND3(Mask, const Pattern&, const Pattern&, const DrawOptions&)
|
||||
|
||||
void
|
||||
DrawTargetTiled::PushClip(const Path* aPath)
|
||||
{
|
||||
mClippedOutTilesStack.push_back(std::vector<uint32_t>());
|
||||
std::vector<uint32_t>& clippedTiles = mClippedOutTilesStack.back();
|
||||
|
||||
Rect deviceRect = aPath->GetBounds(mTransform);
|
||||
|
||||
for (size_t i = 0; i < mTiles.size(); i++) {
|
||||
if (!mTiles[i].mClippedOut) {
|
||||
if (deviceRect.Intersects(Rect(mTiles[i].mTileOrigin.x,
|
||||
mTiles[i].mTileOrigin.y,
|
||||
mTiles[i].mDrawTarget->GetSize().width,
|
||||
mTiles[i].mDrawTarget->GetSize().height))) {
|
||||
mTiles[i].mDrawTarget->PushClip(aPath);
|
||||
} else {
|
||||
mTiles[i].mClippedOut = true;
|
||||
clippedTiles.push_back(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetTiled::PushClipRect(const Rect& aRect)
|
||||
{
|
||||
mClippedOutTilesStack.push_back(std::vector<uint32_t>());
|
||||
std::vector<uint32_t>& clippedTiles = mClippedOutTilesStack.back();
|
||||
|
||||
Rect deviceRect = mTransform.TransformBounds(aRect);
|
||||
|
||||
for (size_t i = 0; i < mTiles.size(); i++) {
|
||||
if (!mTiles[i].mClippedOut) {
|
||||
if (deviceRect.Intersects(Rect(mTiles[i].mTileOrigin.x,
|
||||
mTiles[i].mTileOrigin.y,
|
||||
mTiles[i].mDrawTarget->GetSize().width,
|
||||
mTiles[i].mDrawTarget->GetSize().height))) {
|
||||
mTiles[i].mDrawTarget->PushClipRect(aRect);
|
||||
} else {
|
||||
mTiles[i].mClippedOut = true;
|
||||
clippedTiles.push_back(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetTiled::PopClip()
|
||||
{
|
||||
for (size_t i = 0; i < mTiles.size(); i++) {
|
||||
if (!mTiles[i].mClippedOut) {
|
||||
mTiles[i].mDrawTarget->PopClip();
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<uint32_t>& clippedTiles = mClippedOutTilesStack.back();
|
||||
for (size_t i = 0; i < clippedTiles.size(); i++) {
|
||||
mTiles[clippedTiles[i]].mClippedOut = false;
|
||||
}
|
||||
|
||||
mClippedOutTilesStack.pop_back();
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetTiled::CopySurface(SourceSurface *aSurface,
|
||||
const IntRect &aSourceRect,
|
||||
const IntPoint &aDestination)
|
||||
{
|
||||
for (size_t i = 0; i < mTiles.size(); i++) {
|
||||
IntPoint tileOrigin = mTiles[i].mTileOrigin;
|
||||
IntSize tileSize = mTiles[i].mDrawTarget->GetSize();
|
||||
if (!IntRect(aDestination, aSourceRect.Size()).Intersects(IntRect(tileOrigin, tileSize))) {
|
||||
continue;
|
||||
}
|
||||
// CopySurface ignores the transform, account for that here.
|
||||
mTiles[i].mDrawTarget->CopySurface(aSurface, aSourceRect, aDestination - tileOrigin);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetTiled::SetTransform(const Matrix& aTransform)
|
||||
{
|
||||
for (size_t i = 0; i < mTiles.size(); i++) {
|
||||
Matrix mat = aTransform;
|
||||
mat.PostTranslate(Float(-mTiles[i].mTileOrigin.x), Float(-mTiles[i].mTileOrigin.y));
|
||||
mTiles[i].mDrawTarget->SetTransform(mat);
|
||||
}
|
||||
DrawTarget::SetTransform(aTransform);
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetTiled::DrawSurface(SourceSurface* aSurface, const Rect& aDest, const Rect& aSource, const DrawSurfaceOptions& aSurfaceOptions, const DrawOptions& aDrawOptions)
|
||||
{
|
||||
Rect deviceRect = mTransform.TransformBounds(aDest);
|
||||
for (size_t i = 0; i < mTiles.size(); i++) {
|
||||
if (!mTiles[i].mClippedOut &&
|
||||
deviceRect.Intersects(Rect(mTiles[i].mTileOrigin.x,
|
||||
mTiles[i].mTileOrigin.y,
|
||||
mTiles[i].mDrawTarget->GetSize().width,
|
||||
mTiles[i].mDrawTarget->GetSize().height))) {
|
||||
mTiles[i].mDrawTarget->DrawSurface(aSurface, aDest, aSource, aSurfaceOptions, aDrawOptions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetTiled::FillRect(const Rect& aRect, const Pattern& aPattern, const DrawOptions& aDrawOptions)
|
||||
{
|
||||
Rect deviceRect = mTransform.TransformBounds(aRect);
|
||||
for (size_t i = 0; i < mTiles.size(); i++) {
|
||||
if (!mTiles[i].mClippedOut &&
|
||||
deviceRect.Intersects(Rect(mTiles[i].mTileOrigin.x,
|
||||
mTiles[i].mTileOrigin.y,
|
||||
mTiles[i].mDrawTarget->GetSize().width,
|
||||
mTiles[i].mDrawTarget->GetSize().height))) {
|
||||
mTiles[i].mDrawTarget->FillRect(aRect, aPattern, aDrawOptions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetTiled::Stroke(const Path* aPath, const Pattern& aPattern, const StrokeOptions& aStrokeOptions, const DrawOptions& aDrawOptions)
|
||||
{
|
||||
// Approximate the stroke extents, since Path::GetStrokeExtents can be slow
|
||||
Rect deviceRect = aPath->GetBounds(mTransform);
|
||||
deviceRect.Inflate(MaxStrokeExtents(aStrokeOptions, mTransform));
|
||||
for (size_t i = 0; i < mTiles.size(); i++) {
|
||||
if (!mTiles[i].mClippedOut &&
|
||||
deviceRect.Intersects(Rect(mTiles[i].mTileOrigin.x,
|
||||
mTiles[i].mTileOrigin.y,
|
||||
mTiles[i].mDrawTarget->GetSize().width,
|
||||
mTiles[i].mDrawTarget->GetSize().height))) {
|
||||
mTiles[i].mDrawTarget->Stroke(aPath, aPattern, aStrokeOptions, aDrawOptions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetTiled::StrokeRect(const Rect& aRect, const Pattern& aPattern, const StrokeOptions &aStrokeOptions, const DrawOptions& aDrawOptions)
|
||||
{
|
||||
Rect deviceRect = mTransform.TransformBounds(aRect);
|
||||
Margin strokeMargin = MaxStrokeExtents(aStrokeOptions, mTransform);
|
||||
Rect outerRect = deviceRect;
|
||||
outerRect.Inflate(strokeMargin);
|
||||
Rect innerRect;
|
||||
if (mTransform.IsRectilinear()) {
|
||||
// If rects are mapped to rects, we can compute the inner rect
|
||||
// of the stroked rect.
|
||||
innerRect = deviceRect;
|
||||
innerRect.Deflate(strokeMargin);
|
||||
}
|
||||
for (size_t i = 0; i < mTiles.size(); i++) {
|
||||
if (mTiles[i].mClippedOut) {
|
||||
continue;
|
||||
}
|
||||
Rect tileRect(mTiles[i].mTileOrigin.x,
|
||||
mTiles[i].mTileOrigin.y,
|
||||
mTiles[i].mDrawTarget->GetSize().width,
|
||||
mTiles[i].mDrawTarget->GetSize().height);
|
||||
if (outerRect.Intersects(tileRect) && !innerRect.Contains(tileRect)) {
|
||||
mTiles[i].mDrawTarget->StrokeRect(aRect, aPattern, aStrokeOptions, aDrawOptions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetTiled::StrokeLine(const Point& aStart, const Point& aEnd, const Pattern& aPattern, const StrokeOptions &aStrokeOptions, const DrawOptions& aDrawOptions)
|
||||
{
|
||||
Rect lineBounds = Rect(aStart, Size()).UnionEdges(Rect(aEnd, Size()));
|
||||
Rect deviceRect = mTransform.TransformBounds(lineBounds);
|
||||
deviceRect.Inflate(MaxStrokeExtents(aStrokeOptions, mTransform));
|
||||
for (size_t i = 0; i < mTiles.size(); i++) {
|
||||
if (!mTiles[i].mClippedOut &&
|
||||
deviceRect.Intersects(Rect(mTiles[i].mTileOrigin.x,
|
||||
mTiles[i].mTileOrigin.y,
|
||||
mTiles[i].mDrawTarget->GetSize().width,
|
||||
mTiles[i].mDrawTarget->GetSize().height))) {
|
||||
mTiles[i].mDrawTarget->StrokeLine(aStart, aEnd, aPattern, aStrokeOptions, aDrawOptions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetTiled::Fill(const Path* aPath, const Pattern& aPattern, const DrawOptions& aDrawOptions)
|
||||
{
|
||||
Rect deviceRect = aPath->GetBounds(mTransform);
|
||||
for (size_t i = 0; i < mTiles.size(); i++) {
|
||||
if (!mTiles[i].mClippedOut &&
|
||||
deviceRect.Intersects(Rect(mTiles[i].mTileOrigin.x,
|
||||
mTiles[i].mTileOrigin.y,
|
||||
mTiles[i].mDrawTarget->GetSize().width,
|
||||
mTiles[i].mDrawTarget->GetSize().height))) {
|
||||
mTiles[i].mDrawTarget->Fill(aPath, aPattern, aDrawOptions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetTiled::PushLayer(bool aOpaque, Float aOpacity, SourceSurface* aMask,
|
||||
const Matrix& aMaskTransform, const IntRect& aBounds,
|
||||
bool aCopyBackground)
|
||||
{
|
||||
// XXX - not sure this is what we want or whether we want to continue drawing to a larger
|
||||
// intermediate surface, that would require tweaking the code in here a little though.
|
||||
for (size_t i = 0; i < mTiles.size(); i++) {
|
||||
IntRect bounds = aBounds;
|
||||
bounds.MoveBy(-mTiles[i].mTileOrigin);
|
||||
mTiles[i].mDrawTarget->PushLayer(aOpaque, aOpacity, aMask, aMaskTransform, aBounds);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetTiled::PopLayer()
|
||||
{
|
||||
// XXX - not sure this is what we want or whether we want to continue drawing to a larger
|
||||
// intermediate surface, that would require tweaking the code in here a little though.
|
||||
for (size_t i = 0; i < mTiles.size(); i++) {
|
||||
mTiles[i].mDrawTarget->PopLayer();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
||||
221
gfx/2d/DrawTargetTiled.h
Normal file
221
gfx/2d/DrawTargetTiled.h
Normal file
|
|
@ -0,0 +1,221 @@
|
|||
/* -*- Mode: C++; tab-width: 20; 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_GFX_DRAWTARGETTILED_H_
|
||||
#define MOZILLA_GFX_DRAWTARGETTILED_H_
|
||||
|
||||
#include "2D.h"
|
||||
#include "Filters.h"
|
||||
#include "Logging.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
struct TileInternal : public Tile {
|
||||
TileInternal()
|
||||
: mClippedOut(false)
|
||||
{}
|
||||
|
||||
explicit TileInternal(const Tile& aOther)
|
||||
: Tile(aOther)
|
||||
, mClippedOut(false)
|
||||
{}
|
||||
|
||||
bool mClippedOut;
|
||||
};
|
||||
|
||||
|
||||
class DrawTargetTiled : public DrawTarget
|
||||
{
|
||||
public:
|
||||
DrawTargetTiled();
|
||||
|
||||
bool Init(const TileSet& mTiles);
|
||||
|
||||
virtual bool IsTiledDrawTarget() const override { return true; }
|
||||
|
||||
virtual DrawTargetType GetType() const override { return mTiles[0].mDrawTarget->GetType(); }
|
||||
virtual BackendType GetBackendType() const override { return mTiles[0].mDrawTarget->GetBackendType(); }
|
||||
virtual already_AddRefed<SourceSurface> Snapshot() override;
|
||||
virtual void DetachAllSnapshots() override;
|
||||
virtual IntSize GetSize() override {
|
||||
MOZ_ASSERT(mRect.width > 0 && mRect.height > 0);
|
||||
return IntSize(mRect.XMost(), mRect.YMost());
|
||||
}
|
||||
|
||||
virtual void Flush() override;
|
||||
virtual void DrawSurface(SourceSurface *aSurface,
|
||||
const Rect &aDest,
|
||||
const Rect &aSource,
|
||||
const DrawSurfaceOptions &aSurfOptions,
|
||||
const DrawOptions &aOptions) override;
|
||||
virtual void DrawFilter(FilterNode *aNode,
|
||||
const Rect &aSourceRect,
|
||||
const Point &aDestPoint,
|
||||
const DrawOptions &aOptions = DrawOptions()) override;
|
||||
virtual void DrawSurfaceWithShadow(SourceSurface *aSurface,
|
||||
const Point &aDest,
|
||||
const Color &aColor,
|
||||
const Point &aOffset,
|
||||
Float aSigma,
|
||||
CompositionOp aOperator) override { /* Not implemented */ MOZ_CRASH("GFX: DrawSurfaceWithShadow"); }
|
||||
|
||||
virtual void ClearRect(const Rect &aRect) override;
|
||||
virtual void MaskSurface(const Pattern &aSource,
|
||||
SourceSurface *aMask,
|
||||
Point aOffset,
|
||||
const DrawOptions &aOptions = DrawOptions()) override;
|
||||
|
||||
virtual void CopySurface(SourceSurface *aSurface,
|
||||
const IntRect &aSourceRect,
|
||||
const IntPoint &aDestination) override;
|
||||
|
||||
virtual void FillRect(const Rect &aRect,
|
||||
const Pattern &aPattern,
|
||||
const DrawOptions &aOptions = DrawOptions()) override;
|
||||
virtual void StrokeRect(const Rect &aRect,
|
||||
const Pattern &aPattern,
|
||||
const StrokeOptions &aStrokeOptions = StrokeOptions(),
|
||||
const DrawOptions &aOptions = DrawOptions()) override;
|
||||
virtual void StrokeLine(const Point &aStart,
|
||||
const Point &aEnd,
|
||||
const Pattern &aPattern,
|
||||
const StrokeOptions &aStrokeOptions = StrokeOptions(),
|
||||
const DrawOptions &aOptions = DrawOptions()) override;
|
||||
virtual void Stroke(const Path *aPath,
|
||||
const Pattern &aPattern,
|
||||
const StrokeOptions &aStrokeOptions = StrokeOptions(),
|
||||
const DrawOptions &aOptions = DrawOptions()) override;
|
||||
virtual void Fill(const Path *aPath,
|
||||
const Pattern &aPattern,
|
||||
const DrawOptions &aOptions = DrawOptions()) override;
|
||||
virtual void FillGlyphs(ScaledFont *aFont,
|
||||
const GlyphBuffer &aBuffer,
|
||||
const Pattern &aPattern,
|
||||
const DrawOptions &aOptions = DrawOptions(),
|
||||
const GlyphRenderingOptions *aRenderingOptions = nullptr) override;
|
||||
virtual void Mask(const Pattern &aSource,
|
||||
const Pattern &aMask,
|
||||
const DrawOptions &aOptions = DrawOptions()) override;
|
||||
virtual void PushClip(const Path *aPath) override;
|
||||
virtual void PushClipRect(const Rect &aRect) override;
|
||||
virtual void PopClip() override;
|
||||
virtual void PushLayer(bool aOpaque, Float aOpacity,
|
||||
SourceSurface* aMask,
|
||||
const Matrix& aMaskTransform,
|
||||
const IntRect& aBounds = IntRect(),
|
||||
bool aCopyBackground = false) override;
|
||||
virtual void PopLayer() override;
|
||||
|
||||
|
||||
virtual void SetTransform(const Matrix &aTransform) override;
|
||||
|
||||
virtual already_AddRefed<SourceSurface> CreateSourceSurfaceFromData(unsigned char *aData,
|
||||
const IntSize &aSize,
|
||||
int32_t aStride,
|
||||
SurfaceFormat aFormat) const override
|
||||
{
|
||||
return mTiles[0].mDrawTarget->CreateSourceSurfaceFromData(aData, aSize, aStride, aFormat);
|
||||
}
|
||||
virtual already_AddRefed<SourceSurface> OptimizeSourceSurface(SourceSurface *aSurface) const override
|
||||
{
|
||||
return mTiles[0].mDrawTarget->OptimizeSourceSurface(aSurface);
|
||||
}
|
||||
|
||||
virtual already_AddRefed<SourceSurface>
|
||||
CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const override
|
||||
{
|
||||
return mTiles[0].mDrawTarget->CreateSourceSurfaceFromNativeSurface(aSurface);
|
||||
}
|
||||
|
||||
virtual already_AddRefed<DrawTarget>
|
||||
CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const override
|
||||
{
|
||||
return mTiles[0].mDrawTarget->CreateSimilarDrawTarget(aSize, aFormat);
|
||||
}
|
||||
|
||||
virtual already_AddRefed<PathBuilder> CreatePathBuilder(FillRule aFillRule = FillRule::FILL_WINDING) const override
|
||||
{
|
||||
return mTiles[0].mDrawTarget->CreatePathBuilder(aFillRule);
|
||||
}
|
||||
|
||||
virtual already_AddRefed<GradientStops>
|
||||
CreateGradientStops(GradientStop *aStops,
|
||||
uint32_t aNumStops,
|
||||
ExtendMode aExtendMode = ExtendMode::CLAMP) const override
|
||||
{
|
||||
return mTiles[0].mDrawTarget->CreateGradientStops(aStops, aNumStops, aExtendMode);
|
||||
}
|
||||
virtual already_AddRefed<FilterNode> CreateFilter(FilterType aType) override
|
||||
{
|
||||
return mTiles[0].mDrawTarget->CreateFilter(aType);
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<TileInternal> mTiles;
|
||||
std::vector<std::vector<uint32_t> > mClippedOutTilesStack;
|
||||
IntRect mRect;
|
||||
};
|
||||
|
||||
class SnapshotTiled : public SourceSurface
|
||||
{
|
||||
public:
|
||||
SnapshotTiled(const std::vector<TileInternal>& aTiles, const IntRect& aRect)
|
||||
: mRect(aRect)
|
||||
{
|
||||
for (size_t i = 0; i < aTiles.size(); i++) {
|
||||
mSnapshots.push_back(aTiles[i].mDrawTarget->Snapshot());
|
||||
mOrigins.push_back(aTiles[i].mTileOrigin);
|
||||
}
|
||||
}
|
||||
|
||||
virtual SurfaceType GetType() const { return SurfaceType::TILED; }
|
||||
virtual IntSize GetSize() const {
|
||||
MOZ_ASSERT(mRect.width > 0 && mRect.height > 0);
|
||||
return IntSize(mRect.XMost(), mRect.YMost());
|
||||
}
|
||||
virtual SurfaceFormat GetFormat() const { return mSnapshots[0]->GetFormat(); }
|
||||
|
||||
virtual already_AddRefed<DataSourceSurface> GetDataSurface()
|
||||
{
|
||||
RefPtr<DataSourceSurface> surf = Factory::CreateDataSourceSurface(GetSize(), GetFormat());
|
||||
|
||||
DataSourceSurface::MappedSurface mappedSurf;
|
||||
if (!surf->Map(DataSourceSurface::MapType::WRITE, &mappedSurf)) {
|
||||
gfxCriticalError() << "DrawTargetTiled::GetDataSurface failed to map surface";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
{
|
||||
RefPtr<DrawTarget> dt =
|
||||
Factory::CreateDrawTargetForData(BackendType::CAIRO, mappedSurf.mData,
|
||||
GetSize(), mappedSurf.mStride, GetFormat());
|
||||
|
||||
if (!dt) {
|
||||
gfxWarning() << "DrawTargetTiled::GetDataSurface failed in CreateDrawTargetForData";
|
||||
surf->Unmap();
|
||||
return nullptr;
|
||||
}
|
||||
for (size_t i = 0; i < mSnapshots.size(); i++) {
|
||||
RefPtr<DataSourceSurface> dataSurf = mSnapshots[i]->GetDataSurface();
|
||||
dt->CopySurface(dataSurf, IntRect(IntPoint(0, 0), mSnapshots[i]->GetSize()), mOrigins[i]);
|
||||
}
|
||||
}
|
||||
surf->Unmap();
|
||||
|
||||
return surf.forget();
|
||||
}
|
||||
|
||||
std::vector<RefPtr<SourceSurface>> mSnapshots;
|
||||
std::vector<IntPoint> mOrigins;
|
||||
IntRect mRect;
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
120
gfx/2d/DrawingJob.cpp
Normal file
120
gfx/2d/DrawingJob.cpp
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "DrawingJob.h"
|
||||
#include "JobScheduler.h"
|
||||
#include "mozilla/gfx/2D.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
DrawingJobBuilder::DrawingJobBuilder()
|
||||
{}
|
||||
|
||||
DrawingJobBuilder::~DrawingJobBuilder()
|
||||
{
|
||||
MOZ_ASSERT(!mDrawTarget);
|
||||
}
|
||||
|
||||
void
|
||||
DrawingJob::Clear()
|
||||
{
|
||||
mCommandBuffer = nullptr;
|
||||
mCursor = 0;
|
||||
}
|
||||
|
||||
void
|
||||
DrawingJobBuilder::BeginDrawingJob(DrawTarget* aTarget, IntPoint aOffset,
|
||||
SyncObject* aStart)
|
||||
{
|
||||
MOZ_ASSERT(mCommandOffsets.empty());
|
||||
MOZ_ASSERT(aTarget);
|
||||
mDrawTarget = aTarget;
|
||||
mOffset = aOffset;
|
||||
mStart = aStart;
|
||||
}
|
||||
|
||||
DrawingJob*
|
||||
DrawingJobBuilder::EndDrawingJob(CommandBuffer* aCmdBuffer,
|
||||
SyncObject* aCompletion,
|
||||
WorkerThread* aPinToWorker)
|
||||
{
|
||||
MOZ_ASSERT(mDrawTarget);
|
||||
DrawingJob* task = new DrawingJob(mDrawTarget, mOffset, mStart, aCompletion, aPinToWorker);
|
||||
task->mCommandBuffer = aCmdBuffer;
|
||||
task->mCommandOffsets = Move(mCommandOffsets);
|
||||
|
||||
mDrawTarget = nullptr;
|
||||
mOffset = IntPoint();
|
||||
mStart = nullptr;
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
DrawingJob::DrawingJob(DrawTarget* aTarget, IntPoint aOffset,
|
||||
SyncObject* aStart, SyncObject* aCompletion,
|
||||
WorkerThread* aPinToWorker)
|
||||
: Job(aStart, aCompletion, aPinToWorker)
|
||||
, mCommandBuffer(nullptr)
|
||||
, mCursor(0)
|
||||
, mDrawTarget(aTarget)
|
||||
, mOffset(aOffset)
|
||||
{
|
||||
mCommandOffsets.reserve(64);
|
||||
}
|
||||
|
||||
JobStatus
|
||||
DrawingJob::Run()
|
||||
{
|
||||
while (mCursor < mCommandOffsets.size()) {
|
||||
|
||||
const DrawingCommand* cmd = mCommandBuffer->GetDrawingCommand(mCommandOffsets[mCursor]);
|
||||
|
||||
if (!cmd) {
|
||||
return JobStatus::Error;
|
||||
}
|
||||
|
||||
cmd->ExecuteOnDT(mDrawTarget);
|
||||
|
||||
++mCursor;
|
||||
}
|
||||
|
||||
return JobStatus::Complete;
|
||||
}
|
||||
|
||||
DrawingJob::~DrawingJob()
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
|
||||
const DrawingCommand*
|
||||
CommandBuffer::GetDrawingCommand(ptrdiff_t aId)
|
||||
{
|
||||
return static_cast<DrawingCommand*>(mStorage.GetStorage(aId));
|
||||
}
|
||||
|
||||
CommandBuffer::~CommandBuffer()
|
||||
{
|
||||
mStorage.ForEach([](void* item){
|
||||
static_cast<DrawingCommand*>(item)->~DrawingCommand();
|
||||
});
|
||||
mStorage.Clear();
|
||||
}
|
||||
|
||||
void
|
||||
CommandBufferBuilder::BeginCommandBuffer(size_t aBufferSize)
|
||||
{
|
||||
MOZ_ASSERT(!mCommands);
|
||||
mCommands = new CommandBuffer(aBufferSize);
|
||||
}
|
||||
|
||||
already_AddRefed<CommandBuffer>
|
||||
CommandBufferBuilder::EndCommandBuffer()
|
||||
{
|
||||
return mCommands.forget();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace
|
||||
158
gfx/2d/DrawingJob.h
Normal file
158
gfx/2d/DrawingJob.h
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
/* -*- Mode: C++; tab-width: 20; 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_GFX_COMMANDBUFFER_H_
|
||||
#define MOZILLA_GFX_COMMANDBUFFER_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/gfx/Matrix.h"
|
||||
#include "mozilla/gfx/JobScheduler.h"
|
||||
#include "mozilla/gfx/IterableArena.h"
|
||||
#include "mozilla/RefCounted.h"
|
||||
#include "DrawCommand.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
class DrawingCommand;
|
||||
class PrintCommand;
|
||||
class SignalCommand;
|
||||
class DrawingJob;
|
||||
class WaitCommand;
|
||||
|
||||
class SyncObject;
|
||||
class MultiThreadedJobQueue;
|
||||
|
||||
class DrawTarget;
|
||||
|
||||
class DrawingJobBuilder;
|
||||
class CommandBufferBuilder;
|
||||
|
||||
/// Contains a sequence of immutable drawing commands that are typically used by
|
||||
/// several DrawingJobs.
|
||||
///
|
||||
/// CommandBuffer objects are built using CommandBufferBuilder.
|
||||
class CommandBuffer : public external::AtomicRefCounted<CommandBuffer>
|
||||
{
|
||||
public:
|
||||
MOZ_DECLARE_REFCOUNTED_TYPENAME(CommandBuffer)
|
||||
|
||||
~CommandBuffer();
|
||||
|
||||
const DrawingCommand* GetDrawingCommand(ptrdiff_t aId);
|
||||
|
||||
protected:
|
||||
explicit CommandBuffer(size_t aSize = 256)
|
||||
: mStorage(IterableArena::GROWABLE, aSize)
|
||||
{}
|
||||
|
||||
IterableArena mStorage;
|
||||
friend class CommandBufferBuilder;
|
||||
};
|
||||
|
||||
/// Generates CommandBuffer objects.
|
||||
///
|
||||
/// The builder is a separate object to ensure that commands are not added to a
|
||||
/// submitted CommandBuffer.
|
||||
class CommandBufferBuilder
|
||||
{
|
||||
public:
|
||||
void BeginCommandBuffer(size_t aBufferSize = 256);
|
||||
|
||||
already_AddRefed<CommandBuffer> EndCommandBuffer();
|
||||
|
||||
/// Build the CommandBuffer, command after command.
|
||||
/// This must be used between BeginCommandBuffer and EndCommandBuffer.
|
||||
template<typename T, typename... Args>
|
||||
ptrdiff_t AddCommand(Args&&... aArgs)
|
||||
{
|
||||
static_assert(IsBaseOf<DrawingCommand, T>::value,
|
||||
"T must derive from DrawingCommand");
|
||||
return mCommands->mStorage.Alloc<T>(Forward<Args>(aArgs)...);
|
||||
}
|
||||
|
||||
bool HasCommands() const { return !!mCommands; }
|
||||
|
||||
protected:
|
||||
RefPtr<CommandBuffer> mCommands;
|
||||
};
|
||||
|
||||
/// Stores multiple commands to be executed sequencially.
|
||||
class DrawingJob : public Job {
|
||||
public:
|
||||
~DrawingJob();
|
||||
|
||||
virtual JobStatus Run() override;
|
||||
|
||||
protected:
|
||||
DrawingJob(DrawTarget* aTarget,
|
||||
IntPoint aOffset,
|
||||
SyncObject* aStart,
|
||||
SyncObject* aCompletion,
|
||||
WorkerThread* aPinToWorker = nullptr);
|
||||
|
||||
/// Runs the tasks's destructors and resets the buffer.
|
||||
void Clear();
|
||||
|
||||
std::vector<ptrdiff_t> mCommandOffsets;
|
||||
RefPtr<CommandBuffer> mCommandBuffer;
|
||||
uint32_t mCursor;
|
||||
|
||||
RefPtr<DrawTarget> mDrawTarget;
|
||||
IntPoint mOffset;
|
||||
|
||||
friend class DrawingJobBuilder;
|
||||
};
|
||||
|
||||
/// Generates DrawingJob objects.
|
||||
///
|
||||
/// The builder is a separate object to ensure that commands are not added to a
|
||||
/// submitted DrawingJob.
|
||||
class DrawingJobBuilder {
|
||||
public:
|
||||
DrawingJobBuilder();
|
||||
|
||||
~DrawingJobBuilder();
|
||||
|
||||
/// Allocates a DrawingJob.
|
||||
///
|
||||
/// call this method before starting to add commands.
|
||||
void BeginDrawingJob(DrawTarget* aTarget, IntPoint aOffset,
|
||||
SyncObject* aStart = nullptr);
|
||||
|
||||
/// Build the DrawingJob, command after command.
|
||||
/// This must be used between BeginDrawingJob and EndDrawingJob.
|
||||
void AddCommand(ptrdiff_t offset)
|
||||
{
|
||||
mCommandOffsets.push_back(offset);
|
||||
}
|
||||
|
||||
/// Finalizes and returns the drawing task.
|
||||
///
|
||||
/// If aCompletion is not null, the sync object will be signaled after the
|
||||
/// task buffer is destroyed (and after the destructor of the tasks have run).
|
||||
/// In most cases this means after the completion of all tasks in the task buffer,
|
||||
/// but also when the task buffer is destroyed due to an error.
|
||||
DrawingJob* EndDrawingJob(CommandBuffer* aCmdBuffer,
|
||||
SyncObject* aCompletion = nullptr,
|
||||
WorkerThread* aPinToWorker = nullptr);
|
||||
|
||||
/// Returns true between BeginDrawingJob and EndDrawingJob, false otherwise.
|
||||
bool HasDrawingJob() const { return !!mDrawTarget; }
|
||||
|
||||
protected:
|
||||
std::vector<ptrdiff_t> mCommandOffsets;
|
||||
RefPtr<DrawTarget> mDrawTarget;
|
||||
IntPoint mOffset;
|
||||
RefPtr<SyncObject> mStart;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
} // namespace
|
||||
|
||||
#endif
|
||||
204
gfx/2d/ExtendInputEffectD2D1.cpp
Normal file
204
gfx/2d/ExtendInputEffectD2D1.cpp
Normal file
|
|
@ -0,0 +1,204 @@
|
|||
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "ExtendInputEffectD2D1.h"
|
||||
|
||||
#include "Logging.h"
|
||||
|
||||
#include "ShadersD2D1.h"
|
||||
#include "HelpersD2D.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#define TEXTW(x) L##x
|
||||
#define XML(X) TEXTW(#X) // This macro creates a single string from multiple lines of text.
|
||||
|
||||
static const PCWSTR kXmlDescription =
|
||||
XML(
|
||||
<?xml version='1.0'?>
|
||||
<Effect>
|
||||
<!-- System Properties -->
|
||||
<Property name='DisplayName' type='string' value='ExtendInputEffect'/>
|
||||
<Property name='Author' type='string' value='Mozilla'/>
|
||||
<Property name='Category' type='string' value='Utility Effects'/>
|
||||
<Property name='Description' type='string' value='This effect is used to extend the output rect of any input effect to a specified rect.'/>
|
||||
<Inputs>
|
||||
<Input name='InputEffect'/>
|
||||
</Inputs>
|
||||
<Property name='OutputRect' type='vector4'>
|
||||
<Property name='DisplayName' type='string' value='Output Rect'/>
|
||||
</Property>
|
||||
</Effect>
|
||||
);
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
ExtendInputEffectD2D1::ExtendInputEffectD2D1()
|
||||
: mRefCount(0)
|
||||
, mOutputRect(D2D1::Vector4F(-FLT_MAX, -FLT_MAX, FLT_MAX, FLT_MAX))
|
||||
{
|
||||
}
|
||||
|
||||
IFACEMETHODIMP
|
||||
ExtendInputEffectD2D1::Initialize(ID2D1EffectContext* pContextInternal, ID2D1TransformGraph* pTransformGraph)
|
||||
{
|
||||
HRESULT hr;
|
||||
hr = pTransformGraph->SetSingleTransformNode(this);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP
|
||||
ExtendInputEffectD2D1::PrepareForRender(D2D1_CHANGE_TYPE changeType)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP
|
||||
ExtendInputEffectD2D1::SetGraph(ID2D1TransformGraph* pGraph)
|
||||
{
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP_(ULONG)
|
||||
ExtendInputEffectD2D1::AddRef()
|
||||
{
|
||||
return ++mRefCount;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP_(ULONG)
|
||||
ExtendInputEffectD2D1::Release()
|
||||
{
|
||||
if (!--mRefCount) {
|
||||
delete this;
|
||||
return 0;
|
||||
}
|
||||
return mRefCount;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP
|
||||
ExtendInputEffectD2D1::QueryInterface(const IID &aIID, void **aPtr)
|
||||
{
|
||||
if (!aPtr) {
|
||||
return E_POINTER;
|
||||
}
|
||||
|
||||
if (aIID == IID_IUnknown) {
|
||||
*aPtr = static_cast<IUnknown*>(static_cast<ID2D1EffectImpl*>(this));
|
||||
} else if (aIID == IID_ID2D1EffectImpl) {
|
||||
*aPtr = static_cast<ID2D1EffectImpl*>(this);
|
||||
} else if (aIID == IID_ID2D1DrawTransform) {
|
||||
*aPtr = static_cast<ID2D1DrawTransform*>(this);
|
||||
} else if (aIID == IID_ID2D1Transform) {
|
||||
*aPtr = static_cast<ID2D1Transform*>(this);
|
||||
} else if (aIID == IID_ID2D1TransformNode) {
|
||||
*aPtr = static_cast<ID2D1TransformNode*>(this);
|
||||
} else {
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
|
||||
static_cast<IUnknown*>(*aPtr)->AddRef();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static D2D1_RECT_L
|
||||
ConvertFloatToLongRect(const D2D1_VECTOR_4F& aRect)
|
||||
{
|
||||
// Clamp values to LONG range. We can't use std::min/max here because we want
|
||||
// the comparison to operate on a type that's different from the type of the
|
||||
// result.
|
||||
return D2D1::RectL(aRect.x <= LONG_MIN ? LONG_MIN : LONG(aRect.x),
|
||||
aRect.y <= LONG_MIN ? LONG_MIN : LONG(aRect.y),
|
||||
aRect.z >= LONG_MAX ? LONG_MAX : LONG(aRect.z),
|
||||
aRect.w >= LONG_MAX ? LONG_MAX : LONG(aRect.w));
|
||||
}
|
||||
|
||||
static D2D1_RECT_L
|
||||
IntersectRect(const D2D1_RECT_L& aRect1, const D2D1_RECT_L& aRect2)
|
||||
{
|
||||
return D2D1::RectL(std::max(aRect1.left, aRect2.left),
|
||||
std::max(aRect1.top, aRect2.top),
|
||||
std::min(aRect1.right, aRect2.right),
|
||||
std::min(aRect1.bottom, aRect2.bottom));
|
||||
}
|
||||
|
||||
IFACEMETHODIMP
|
||||
ExtendInputEffectD2D1::MapInputRectsToOutputRect(const D2D1_RECT_L* pInputRects,
|
||||
const D2D1_RECT_L* pInputOpaqueSubRects,
|
||||
UINT32 inputRectCount,
|
||||
D2D1_RECT_L* pOutputRect,
|
||||
D2D1_RECT_L* pOutputOpaqueSubRect)
|
||||
{
|
||||
// This transform only accepts one input, so there will only be one input rect.
|
||||
if (inputRectCount != 1) {
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
// Set the output rect to the specified rect. This is the whole purpose of this effect.
|
||||
*pOutputRect = ConvertFloatToLongRect(mOutputRect);
|
||||
*pOutputOpaqueSubRect = IntersectRect(*pOutputRect, pInputOpaqueSubRects[0]);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP
|
||||
ExtendInputEffectD2D1::MapOutputRectToInputRects(const D2D1_RECT_L* pOutputRect,
|
||||
D2D1_RECT_L* pInputRects,
|
||||
UINT32 inputRectCount) const
|
||||
{
|
||||
if (inputRectCount != 1) {
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
*pInputRects = *pOutputRect;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP
|
||||
ExtendInputEffectD2D1::MapInvalidRect(UINT32 inputIndex,
|
||||
D2D1_RECT_L invalidInputRect,
|
||||
D2D1_RECT_L* pInvalidOutputRect) const
|
||||
{
|
||||
MOZ_ASSERT(inputIndex == 0);
|
||||
|
||||
*pInvalidOutputRect = invalidInputRect;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT
|
||||
ExtendInputEffectD2D1::Register(ID2D1Factory1 *aFactory)
|
||||
{
|
||||
D2D1_PROPERTY_BINDING bindings[] = {
|
||||
D2D1_VALUE_TYPE_BINDING(L"OutputRect", &ExtendInputEffectD2D1::SetOutputRect, &ExtendInputEffectD2D1::GetOutputRect),
|
||||
};
|
||||
HRESULT hr = aFactory->RegisterEffectFromString(CLSID_ExtendInputEffect, kXmlDescription, bindings, 1, CreateEffect);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
gfxWarning() << "Failed to register extend input effect.";
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
|
||||
void
|
||||
ExtendInputEffectD2D1::Unregister(ID2D1Factory1 *aFactory)
|
||||
{
|
||||
aFactory->UnregisterEffect(CLSID_ExtendInputEffect);
|
||||
}
|
||||
|
||||
HRESULT __stdcall
|
||||
ExtendInputEffectD2D1::CreateEffect(IUnknown **aEffectImpl)
|
||||
{
|
||||
*aEffectImpl = static_cast<ID2D1EffectImpl*>(new ExtendInputEffectD2D1());
|
||||
(*aEffectImpl)->AddRef();
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
88
gfx/2d/ExtendInputEffectD2D1.h
Normal file
88
gfx/2d/ExtendInputEffectD2D1.h
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
/* -*- Mode: C++; tab-width: 20; 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_GFX_EXTENDINPUTEFFECTD2D1_H_
|
||||
#define MOZILLA_GFX_EXTENDINPUTEFFECTD2D1_H_
|
||||
|
||||
#include <d2d1_1.h>
|
||||
#include <d2d1effectauthor.h>
|
||||
#include <d2d1effecthelpers.h>
|
||||
|
||||
#include "2D.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
|
||||
// {97143DC6-CBC4-4DD4-A8BA-13342B0BA46D}
|
||||
DEFINE_GUID(CLSID_ExtendInputEffect,
|
||||
0x5fb55c7c, 0xd795, 0x4ba3, 0xa9, 0x5c, 0x22, 0x82, 0x5d, 0x0c, 0x4d, 0xf7);
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
enum {
|
||||
EXTENDINPUT_PROP_OUTPUT_RECT = 0
|
||||
};
|
||||
|
||||
// An effect type that passes through its input unchanged but sets the effect's
|
||||
// output rect to a specified rect. Unlike the built-in Crop effect, the
|
||||
// ExtendInput effect can extend the input rect, and not just make it smaller.
|
||||
// The added margins are filled with transparent black.
|
||||
// Some effects have different output depending on their input effect's output
|
||||
// rect, for example the Border effect (which repeats the edges of its input
|
||||
// effect's output rect) or the component transfer and color matrix effects
|
||||
// (which can transform transparent pixels into non-transparent ones, but only
|
||||
// inside their input effect's output rect).
|
||||
class ExtendInputEffectD2D1 final : public ID2D1EffectImpl
|
||||
, public ID2D1DrawTransform
|
||||
{
|
||||
public:
|
||||
// ID2D1EffectImpl
|
||||
IFACEMETHODIMP Initialize(ID2D1EffectContext* pContextInternal, ID2D1TransformGraph* pTransformGraph);
|
||||
IFACEMETHODIMP PrepareForRender(D2D1_CHANGE_TYPE changeType);
|
||||
IFACEMETHODIMP SetGraph(ID2D1TransformGraph* pGraph);
|
||||
|
||||
// IUnknown
|
||||
IFACEMETHODIMP_(ULONG) AddRef();
|
||||
IFACEMETHODIMP_(ULONG) Release();
|
||||
IFACEMETHODIMP QueryInterface(REFIID riid, void** ppOutput);
|
||||
|
||||
// ID2D1Transform
|
||||
IFACEMETHODIMP MapInputRectsToOutputRect(const D2D1_RECT_L* pInputRects,
|
||||
const D2D1_RECT_L* pInputOpaqueSubRects,
|
||||
UINT32 inputRectCount,
|
||||
D2D1_RECT_L* pOutputRect,
|
||||
D2D1_RECT_L* pOutputOpaqueSubRect);
|
||||
IFACEMETHODIMP MapOutputRectToInputRects(const D2D1_RECT_L* pOutputRect,
|
||||
D2D1_RECT_L* pInputRects,
|
||||
UINT32 inputRectCount) const;
|
||||
IFACEMETHODIMP MapInvalidRect(UINT32 inputIndex,
|
||||
D2D1_RECT_L invalidInputRect,
|
||||
D2D1_RECT_L* pInvalidOutputRect) const;
|
||||
|
||||
// ID2D1TransformNode
|
||||
IFACEMETHODIMP_(UINT32) GetInputCount() const { return 1; }
|
||||
|
||||
// ID2D1DrawTransform
|
||||
IFACEMETHODIMP SetDrawInfo(ID2D1DrawInfo *pDrawInfo) { return S_OK; }
|
||||
|
||||
static HRESULT Register(ID2D1Factory1* aFactory);
|
||||
static void Unregister(ID2D1Factory1* aFactory);
|
||||
static HRESULT __stdcall CreateEffect(IUnknown** aEffectImpl);
|
||||
|
||||
HRESULT SetOutputRect(D2D1_VECTOR_4F aOutputRect)
|
||||
{ mOutputRect = aOutputRect; return S_OK; }
|
||||
D2D1_VECTOR_4F GetOutputRect() const { return mOutputRect; }
|
||||
|
||||
private:
|
||||
ExtendInputEffectD2D1();
|
||||
|
||||
uint32_t mRefCount;
|
||||
D2D1_VECTOR_4F mOutputRect;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
#undef SIMPLE_PROP
|
||||
|
||||
#endif
|
||||
985
gfx/2d/Factory.cpp
Normal file
985
gfx/2d/Factory.cpp
Normal file
|
|
@ -0,0 +1,985 @@
|
|||
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "2D.h"
|
||||
|
||||
#ifdef USE_CAIRO
|
||||
#include "DrawTargetCairo.h"
|
||||
#include "ScaledFontCairo.h"
|
||||
#include "SourceSurfaceCairo.h"
|
||||
#endif
|
||||
|
||||
#ifdef USE_SKIA
|
||||
#include "DrawTargetSkia.h"
|
||||
#include "ScaledFontBase.h"
|
||||
#ifdef MOZ_ENABLE_FREETYPE
|
||||
#define USE_SKIA_FREETYPE
|
||||
#include "ScaledFontCairo.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(WIN32)
|
||||
#include "ScaledFontWin.h"
|
||||
#include "NativeFontResourceGDI.h"
|
||||
#endif
|
||||
|
||||
#ifdef XP_DARWIN
|
||||
#include "ScaledFontMac.h"
|
||||
#include "NativeFontResourceMac.h"
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_WIDGET_GTK
|
||||
#include "ScaledFontFontconfig.h"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "DrawTargetD2D1.h"
|
||||
#include "ScaledFontDWrite.h"
|
||||
#include "NativeFontResourceDWrite.h"
|
||||
#include <d3d10_1.h>
|
||||
#include "HelpersD2D.h"
|
||||
#endif
|
||||
|
||||
#include "DrawTargetDual.h"
|
||||
#include "DrawTargetTiled.h"
|
||||
#include "DrawTargetRecording.h"
|
||||
|
||||
#include "SourceSurfaceRawData.h"
|
||||
|
||||
#include "DrawEventRecorder.h"
|
||||
|
||||
#include "Logging.h"
|
||||
|
||||
#include "mozilla/CheckedInt.h"
|
||||
|
||||
#if defined(MOZ_LOGGING)
|
||||
GFX2D_API mozilla::LogModule*
|
||||
GetGFX2DLog()
|
||||
{
|
||||
static mozilla::LazyLogModule sLog("gfx2d");
|
||||
return sLog;
|
||||
}
|
||||
#endif
|
||||
|
||||
// The following code was largely taken from xpcom/glue/SSE.cpp and
|
||||
// made a little simpler.
|
||||
enum CPUIDRegister { eax = 0, ebx = 1, ecx = 2, edx = 3 };
|
||||
|
||||
#ifdef HAVE_CPUID_H
|
||||
|
||||
#if !(defined(__SSE2__) || defined(_M_X64) || \
|
||||
(defined(_M_IX86_FP) && _M_IX86_FP >= 2))
|
||||
// cpuid.h is available on gcc 4.3 and higher on i386 and x86_64
|
||||
#include <cpuid.h>
|
||||
|
||||
static inline bool
|
||||
HasCPUIDBit(unsigned int level, CPUIDRegister reg, unsigned int bit)
|
||||
{
|
||||
unsigned int regs[4];
|
||||
return __get_cpuid(level, ®s[0], ®s[1], ®s[2], ®s[3]) &&
|
||||
(regs[reg] & bit);
|
||||
}
|
||||
#endif
|
||||
|
||||
#define HAVE_CPU_DETECTION
|
||||
#else
|
||||
|
||||
#if defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_AMD64))
|
||||
// MSVC 2005 or later supports __cpuid by intrin.h
|
||||
#include <intrin.h>
|
||||
|
||||
#define HAVE_CPU_DETECTION
|
||||
#elif defined(__SUNPRO_CC) && (defined(__i386) || defined(__x86_64__))
|
||||
|
||||
// Define a function identical to MSVC function.
|
||||
#ifdef __i386
|
||||
static void
|
||||
__cpuid(int CPUInfo[4], int InfoType)
|
||||
{
|
||||
asm (
|
||||
"xchg %esi, %ebx\n"
|
||||
"cpuid\n"
|
||||
"movl %eax, (%edi)\n"
|
||||
"movl %ebx, 4(%edi)\n"
|
||||
"movl %ecx, 8(%edi)\n"
|
||||
"movl %edx, 12(%edi)\n"
|
||||
"xchg %esi, %ebx\n"
|
||||
:
|
||||
: "a"(InfoType), // %eax
|
||||
"D"(CPUInfo) // %edi
|
||||
: "%ecx", "%edx", "%esi"
|
||||
);
|
||||
}
|
||||
#else
|
||||
static void
|
||||
__cpuid(int CPUInfo[4], int InfoType)
|
||||
{
|
||||
asm (
|
||||
"xchg %rsi, %rbx\n"
|
||||
"cpuid\n"
|
||||
"movl %eax, (%rdi)\n"
|
||||
"movl %ebx, 4(%rdi)\n"
|
||||
"movl %ecx, 8(%rdi)\n"
|
||||
"movl %edx, 12(%rdi)\n"
|
||||
"xchg %rsi, %rbx\n"
|
||||
:
|
||||
: "a"(InfoType), // %eax
|
||||
"D"(CPUInfo) // %rdi
|
||||
: "%ecx", "%edx", "%rsi"
|
||||
);
|
||||
}
|
||||
|
||||
#define HAVE_CPU_DETECTION
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_CPU_DETECTION
|
||||
static inline bool
|
||||
HasCPUIDBit(unsigned int level, CPUIDRegister reg, unsigned int bit)
|
||||
{
|
||||
// Check that the level in question is supported.
|
||||
volatile int regs[4];
|
||||
__cpuid((int *)regs, level & 0x80000000u);
|
||||
if (unsigned(regs[0]) < level)
|
||||
return false;
|
||||
__cpuid((int *)regs, level);
|
||||
return !!(unsigned(regs[reg]) & bit);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
// In Gecko, this value is managed by gfx.logging.level in gfxPrefs.
|
||||
int32_t LoggingPrefs::sGfxLogLevel = LOG_DEFAULT;
|
||||
|
||||
#ifdef WIN32
|
||||
ID3D11Device *Factory::mD3D11Device = nullptr;
|
||||
ID2D1Device *Factory::mD2D1Device = nullptr;
|
||||
IDWriteFactory *Factory::mDWriteFactory = nullptr;
|
||||
#endif
|
||||
|
||||
DrawEventRecorder *Factory::mRecorder;
|
||||
|
||||
mozilla::gfx::Config* Factory::sConfig = nullptr;
|
||||
|
||||
void
|
||||
Factory::Init(const Config& aConfig)
|
||||
{
|
||||
MOZ_ASSERT(!sConfig);
|
||||
sConfig = new Config(aConfig);
|
||||
|
||||
// Make sure we don't completely break rendering because of a typo in the
|
||||
// pref or whatnot.
|
||||
const int32_t kMinAllocPref = 10000000;
|
||||
const int32_t kMinSizePref = 2048;
|
||||
if (sConfig->mMaxAllocSize < kMinAllocPref) {
|
||||
sConfig->mMaxAllocSize = kMinAllocPref;
|
||||
}
|
||||
if (sConfig->mMaxTextureSize < kMinSizePref) {
|
||||
sConfig->mMaxTextureSize = kMinSizePref;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Factory::ShutDown()
|
||||
{
|
||||
if (sConfig) {
|
||||
delete sConfig->mLogForwarder;
|
||||
delete sConfig;
|
||||
sConfig = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
Factory::HasSSE2()
|
||||
{
|
||||
#if defined(__SSE2__) || defined(_M_X64) || \
|
||||
(defined(_M_IX86_FP) && _M_IX86_FP >= 2)
|
||||
// gcc with -msse2 (default on OSX and x86-64)
|
||||
// cl.exe with -arch:SSE2 (default on x64 compiler)
|
||||
return true;
|
||||
#elif defined(HAVE_CPU_DETECTION)
|
||||
static enum {
|
||||
UNINITIALIZED,
|
||||
NO_SSE2,
|
||||
HAS_SSE2
|
||||
} sDetectionState = UNINITIALIZED;
|
||||
|
||||
if (sDetectionState == UNINITIALIZED) {
|
||||
sDetectionState = HasCPUIDBit(1u, edx, (1u<<26)) ? HAS_SSE2 : NO_SSE2;
|
||||
}
|
||||
return sDetectionState == HAS_SSE2;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
// If the size is "reasonable", we want gfxCriticalError to assert, so
|
||||
// this is the option set up for it.
|
||||
inline int LoggerOptionsBasedOnSize(const IntSize& aSize)
|
||||
{
|
||||
return CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(aSize));
|
||||
}
|
||||
|
||||
bool
|
||||
Factory::ReasonableSurfaceSize(const IntSize &aSize)
|
||||
{
|
||||
return Factory::CheckSurfaceSize(aSize, 8192);
|
||||
}
|
||||
|
||||
bool
|
||||
Factory::AllowedSurfaceSize(const IntSize &aSize)
|
||||
{
|
||||
if (sConfig) {
|
||||
return Factory::CheckSurfaceSize(aSize,
|
||||
sConfig->mMaxTextureSize,
|
||||
sConfig->mMaxAllocSize);
|
||||
}
|
||||
|
||||
return CheckSurfaceSize(aSize);
|
||||
}
|
||||
|
||||
bool
|
||||
Factory::CheckBufferSize(int32_t bufSize)
|
||||
{
|
||||
return !sConfig || bufSize < sConfig->mMaxAllocSize;
|
||||
}
|
||||
|
||||
bool
|
||||
Factory::CheckSurfaceSize(const IntSize &sz,
|
||||
int32_t extentLimit,
|
||||
int32_t allocLimit)
|
||||
{
|
||||
if (sz.width <= 0 || sz.height <= 0) {
|
||||
gfxDebug() << "Surface width or height <= 0!";
|
||||
return false;
|
||||
}
|
||||
|
||||
// reject images with sides bigger than limit
|
||||
if (extentLimit && (sz.width > extentLimit || sz.height > extentLimit)) {
|
||||
gfxDebug() << "Surface size too large (exceeds extent limit)!";
|
||||
return false;
|
||||
}
|
||||
|
||||
#if defined(XP_MACOSX)
|
||||
// CoreGraphics is limited to images < 32K in *height*,
|
||||
// so clamp all surfaces on the Mac to that height
|
||||
if (sz.height > SHRT_MAX) {
|
||||
gfxDebug() << "Surface size too large (exceeds CoreGraphics limit)!";
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
// assuming 4 bytes per pixel, make sure the allocation size
|
||||
// doesn't overflow a int32_t either
|
||||
CheckedInt<int32_t> stride = GetAlignedStride<16>(sz.width, 4);
|
||||
if (!stride.isValid() || stride.value() == 0) {
|
||||
gfxDebug() << "Surface size too large (stride overflows int32_t)!";
|
||||
return false;
|
||||
}
|
||||
|
||||
CheckedInt<int32_t> numBytes = stride * sz.height;
|
||||
if (!numBytes.isValid()) {
|
||||
gfxDebug() << "Surface size too large (allocation size would overflow int32_t)!";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (allocLimit && allocLimit < numBytes.value()) {
|
||||
gfxDebug() << "Surface size too large (exceeds allocation limit)!";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
already_AddRefed<DrawTarget>
|
||||
Factory::CreateDrawTarget(BackendType aBackend, const IntSize &aSize, SurfaceFormat aFormat)
|
||||
{
|
||||
if (!AllowedSurfaceSize(aSize)) {
|
||||
gfxCriticalError(LoggerOptionsBasedOnSize(aSize)) << "Failed to allocate a surface due to invalid size (CDT) " << aSize;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<DrawTarget> retVal;
|
||||
switch (aBackend) {
|
||||
#ifdef WIN32
|
||||
case BackendType::DIRECT2D1_1:
|
||||
{
|
||||
RefPtr<DrawTargetD2D1> newTarget;
|
||||
newTarget = new DrawTargetD2D1();
|
||||
if (newTarget->Init(aSize, aFormat)) {
|
||||
retVal = newTarget;
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_SKIA
|
||||
case BackendType::SKIA:
|
||||
{
|
||||
RefPtr<DrawTargetSkia> newTarget;
|
||||
newTarget = new DrawTargetSkia();
|
||||
if (newTarget->Init(aSize, aFormat)) {
|
||||
retVal = newTarget;
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_CAIRO
|
||||
case BackendType::CAIRO:
|
||||
{
|
||||
RefPtr<DrawTargetCairo> newTarget;
|
||||
newTarget = new DrawTargetCairo();
|
||||
if (newTarget->Init(aSize, aFormat)) {
|
||||
retVal = newTarget;
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (mRecorder && retVal) {
|
||||
return MakeAndAddRef<DrawTargetRecording>(mRecorder, retVal);
|
||||
}
|
||||
|
||||
if (!retVal) {
|
||||
// Failed
|
||||
gfxCriticalError(LoggerOptionsBasedOnSize(aSize)) << "Failed to create DrawTarget, Type: " << int(aBackend) << " Size: " << aSize;
|
||||
}
|
||||
|
||||
return retVal.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<DrawTarget>
|
||||
Factory::CreateRecordingDrawTarget(DrawEventRecorder *aRecorder, DrawTarget *aDT)
|
||||
{
|
||||
return MakeAndAddRef<DrawTargetRecording>(aRecorder, aDT);
|
||||
}
|
||||
|
||||
already_AddRefed<DrawTarget>
|
||||
Factory::CreateDrawTargetForData(BackendType aBackend,
|
||||
unsigned char *aData,
|
||||
const IntSize &aSize,
|
||||
int32_t aStride,
|
||||
SurfaceFormat aFormat,
|
||||
bool aUninitialized)
|
||||
{
|
||||
MOZ_ASSERT(aData);
|
||||
if (!AllowedSurfaceSize(aSize)) {
|
||||
gfxCriticalError(LoggerOptionsBasedOnSize(aSize)) << "Failed to allocate a surface due to invalid size (DTD) " << aSize;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<DrawTarget> retVal;
|
||||
|
||||
switch (aBackend) {
|
||||
#ifdef USE_SKIA
|
||||
case BackendType::SKIA:
|
||||
{
|
||||
RefPtr<DrawTargetSkia> newTarget;
|
||||
newTarget = new DrawTargetSkia();
|
||||
if (newTarget->Init(aData, aSize, aStride, aFormat, aUninitialized)) {
|
||||
retVal = newTarget;
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_CAIRO
|
||||
case BackendType::CAIRO:
|
||||
{
|
||||
RefPtr<DrawTargetCairo> newTarget;
|
||||
newTarget = new DrawTargetCairo();
|
||||
if (newTarget->Init(aData, aSize, aStride, aFormat)) {
|
||||
retVal = newTarget.forget();
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
gfxCriticalNote << "Invalid draw target type specified: " << (int)aBackend;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (mRecorder && retVal) {
|
||||
return MakeAndAddRef<DrawTargetRecording>(mRecorder, retVal, true);
|
||||
}
|
||||
|
||||
if (!retVal) {
|
||||
gfxCriticalNote << "Failed to create DrawTarget, Type: " << int(aBackend) << " Size: " << aSize << ", Data: " << hexa((void *)aData) << ", Stride: " << aStride;
|
||||
}
|
||||
|
||||
return retVal.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<DrawTarget>
|
||||
Factory::CreateTiledDrawTarget(const TileSet& aTileSet)
|
||||
{
|
||||
RefPtr<DrawTargetTiled> dt = new DrawTargetTiled();
|
||||
|
||||
if (!dt->Init(aTileSet)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return dt.forget();
|
||||
}
|
||||
|
||||
bool
|
||||
Factory::DoesBackendSupportDataDrawtarget(BackendType aType)
|
||||
{
|
||||
switch (aType) {
|
||||
case BackendType::DIRECT2D:
|
||||
case BackendType::DIRECT2D1_1:
|
||||
case BackendType::RECORDING:
|
||||
case BackendType::NONE:
|
||||
case BackendType::BACKEND_LAST:
|
||||
return false;
|
||||
case BackendType::CAIRO:
|
||||
case BackendType::SKIA:
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
Factory::GetMaxSurfaceSize(BackendType aType)
|
||||
{
|
||||
switch (aType) {
|
||||
case BackendType::CAIRO:
|
||||
return DrawTargetCairo::GetMaxSurfaceSize();
|
||||
#ifdef USE_SKIA
|
||||
case BackendType::SKIA:
|
||||
return DrawTargetSkia::GetMaxSurfaceSize();
|
||||
#endif
|
||||
#ifdef WIN32
|
||||
case BackendType::DIRECT2D1_1:
|
||||
return DrawTargetD2D1::GetMaxSurfaceSize();
|
||||
#endif
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
already_AddRefed<ScaledFont>
|
||||
Factory::CreateScaledFontForNativeFont(const NativeFont &aNativeFont, Float aSize)
|
||||
{
|
||||
switch (aNativeFont.mType) {
|
||||
#ifdef WIN32
|
||||
case NativeFontType::DWRITE_FONT_FACE:
|
||||
{
|
||||
return MakeAndAddRef<ScaledFontDWrite>(static_cast<IDWriteFontFace*>(aNativeFont.mFont), aSize);
|
||||
}
|
||||
#if defined(USE_CAIRO) || defined(USE_SKIA)
|
||||
case NativeFontType::GDI_FONT_FACE:
|
||||
{
|
||||
return MakeAndAddRef<ScaledFontWin>(static_cast<LOGFONT*>(aNativeFont.mFont), aSize);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
#ifdef XP_DARWIN
|
||||
case NativeFontType::MAC_FONT_FACE:
|
||||
{
|
||||
return MakeAndAddRef<ScaledFontMac>(static_cast<CGFontRef>(aNativeFont.mFont), aSize);
|
||||
}
|
||||
#endif
|
||||
#if defined(USE_CAIRO) || defined(USE_SKIA_FREETYPE)
|
||||
case NativeFontType::CAIRO_FONT_FACE:
|
||||
{
|
||||
return MakeAndAddRef<ScaledFontCairo>(static_cast<cairo_scaled_font_t*>(aNativeFont.mFont), aSize);
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
gfxWarning() << "Invalid native font type specified.";
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
already_AddRefed<NativeFontResource>
|
||||
Factory::CreateNativeFontResource(uint8_t *aData, uint32_t aSize,
|
||||
FontType aType)
|
||||
{
|
||||
switch (aType) {
|
||||
#ifdef WIN32
|
||||
case FontType::DWRITE:
|
||||
{
|
||||
return NativeFontResourceDWrite::Create(aData, aSize,
|
||||
/* aNeedsCairo = */ false);
|
||||
}
|
||||
#endif
|
||||
case FontType::CAIRO:
|
||||
#ifdef USE_SKIA
|
||||
case FontType::SKIA:
|
||||
#endif
|
||||
{
|
||||
#ifdef WIN32
|
||||
if (GetDWriteFactory()) {
|
||||
return NativeFontResourceDWrite::Create(aData, aSize,
|
||||
/* aNeedsCairo = */ true);
|
||||
} else {
|
||||
return NativeFontResourceGDI::Create(aData, aSize,
|
||||
/* aNeedsCairo = */ true);
|
||||
}
|
||||
#elif XP_DARWIN
|
||||
return NativeFontResourceMac::Create(aData, aSize);
|
||||
#else
|
||||
gfxWarning() << "Unable to create cairo scaled font from truetype data";
|
||||
return nullptr;
|
||||
#endif
|
||||
}
|
||||
default:
|
||||
gfxWarning() << "Unable to create requested font resource from truetype data";
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
already_AddRefed<ScaledFont>
|
||||
Factory::CreateScaledFontWithCairo(const NativeFont& aNativeFont, Float aSize, cairo_scaled_font_t* aScaledFont)
|
||||
{
|
||||
#ifdef USE_CAIRO
|
||||
// In theory, we could pull the NativeFont out of the cairo_scaled_font_t*,
|
||||
// but that would require a lot of code that would be otherwise repeated in
|
||||
// various backends.
|
||||
// Therefore, we just reuse CreateScaledFontForNativeFont's implementation.
|
||||
RefPtr<ScaledFont> font = CreateScaledFontForNativeFont(aNativeFont, aSize);
|
||||
static_cast<ScaledFontBase*>(font.get())->SetCairoScaledFont(aScaledFont);
|
||||
return font.forget();
|
||||
#else
|
||||
return nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef MOZ_WIDGET_GTK
|
||||
already_AddRefed<ScaledFont>
|
||||
Factory::CreateScaledFontForFontconfigFont(cairo_scaled_font_t* aScaledFont, FcPattern* aPattern, Float aSize)
|
||||
{
|
||||
return MakeAndAddRef<ScaledFontFontconfig>(aScaledFont, aPattern, aSize);
|
||||
}
|
||||
#endif
|
||||
|
||||
already_AddRefed<DrawTarget>
|
||||
Factory::CreateDualDrawTarget(DrawTarget *targetA, DrawTarget *targetB)
|
||||
{
|
||||
MOZ_ASSERT(targetA && targetB);
|
||||
|
||||
RefPtr<DrawTarget> newTarget =
|
||||
new DrawTargetDual(targetA, targetB);
|
||||
|
||||
RefPtr<DrawTarget> retVal = newTarget;
|
||||
|
||||
if (mRecorder) {
|
||||
retVal = new DrawTargetRecording(mRecorder, retVal);
|
||||
}
|
||||
|
||||
return retVal.forget();
|
||||
}
|
||||
|
||||
|
||||
#ifdef WIN32
|
||||
already_AddRefed<DrawTarget>
|
||||
Factory::CreateDrawTargetForD3D11Texture(ID3D11Texture2D *aTexture, SurfaceFormat aFormat)
|
||||
{
|
||||
MOZ_ASSERT(aTexture);
|
||||
|
||||
RefPtr<DrawTargetD2D1> newTarget;
|
||||
|
||||
newTarget = new DrawTargetD2D1();
|
||||
if (newTarget->Init(aTexture, aFormat)) {
|
||||
RefPtr<DrawTarget> retVal = newTarget;
|
||||
|
||||
if (mRecorder) {
|
||||
retVal = new DrawTargetRecording(mRecorder, retVal, true);
|
||||
}
|
||||
|
||||
return retVal.forget();
|
||||
}
|
||||
|
||||
gfxWarning() << "Failed to create draw target for D3D11 texture.";
|
||||
|
||||
// Failed
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
Factory::SetDWriteFactory(IDWriteFactory *aFactory)
|
||||
{
|
||||
mDWriteFactory = aFactory;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
Factory::SetDirect3D11Device(ID3D11Device *aDevice)
|
||||
{
|
||||
mD3D11Device = aDevice;
|
||||
|
||||
if (mD2D1Device) {
|
||||
mD2D1Device->Release();
|
||||
mD2D1Device = nullptr;
|
||||
}
|
||||
|
||||
if (!aDevice) {
|
||||
return true;
|
||||
}
|
||||
|
||||
RefPtr<ID2D1Factory1> factory = D2DFactory1();
|
||||
|
||||
RefPtr<IDXGIDevice> device;
|
||||
aDevice->QueryInterface((IDXGIDevice**)getter_AddRefs(device));
|
||||
HRESULT hr = factory->CreateDevice(device, &mD2D1Device);
|
||||
if (FAILED(hr)) {
|
||||
gfxCriticalError() << "[D2D1] Failed to create gfx factory's D2D1 device, code: " << hexa(hr);
|
||||
|
||||
mD3D11Device = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ID3D11Device*
|
||||
Factory::GetDirect3D11Device()
|
||||
{
|
||||
return mD3D11Device;
|
||||
}
|
||||
|
||||
ID2D1Device*
|
||||
Factory::GetD2D1Device()
|
||||
{
|
||||
return mD2D1Device;
|
||||
}
|
||||
|
||||
IDWriteFactory*
|
||||
Factory::GetDWriteFactory()
|
||||
{
|
||||
return mDWriteFactory;
|
||||
}
|
||||
|
||||
bool
|
||||
Factory::SupportsD2D1()
|
||||
{
|
||||
return !!D2DFactory1();
|
||||
}
|
||||
|
||||
already_AddRefed<GlyphRenderingOptions>
|
||||
Factory::CreateDWriteGlyphRenderingOptions(IDWriteRenderingParams *aParams)
|
||||
{
|
||||
return MakeAndAddRef<GlyphRenderingOptionsDWrite>(aParams);
|
||||
}
|
||||
|
||||
uint64_t
|
||||
Factory::GetD2DVRAMUsageDrawTarget()
|
||||
{
|
||||
return DrawTargetD2D1::mVRAMUsageDT;
|
||||
}
|
||||
|
||||
uint64_t
|
||||
Factory::GetD2DVRAMUsageSourceSurface()
|
||||
{
|
||||
return DrawTargetD2D1::mVRAMUsageSS;
|
||||
}
|
||||
|
||||
void
|
||||
Factory::D2DCleanup()
|
||||
{
|
||||
if (mD2D1Device) {
|
||||
mD2D1Device->Release();
|
||||
mD2D1Device = nullptr;
|
||||
}
|
||||
DrawTargetD2D1::CleanupD2D();
|
||||
}
|
||||
|
||||
already_AddRefed<ScaledFont>
|
||||
Factory::CreateScaledFontForDWriteFont(IDWriteFontFace* aFontFace,
|
||||
const gfxFontStyle* aStyle,
|
||||
float aSize,
|
||||
bool aUseEmbeddedBitmap,
|
||||
bool aForceGDIMode)
|
||||
{
|
||||
return MakeAndAddRef<ScaledFontDWrite>(aFontFace, aSize,
|
||||
aUseEmbeddedBitmap, aForceGDIMode,
|
||||
aStyle);
|
||||
}
|
||||
|
||||
#endif // XP_WIN
|
||||
|
||||
#ifdef USE_SKIA_GPU
|
||||
already_AddRefed<DrawTarget>
|
||||
Factory::CreateDrawTargetSkiaWithGrContext(GrContext* aGrContext,
|
||||
const IntSize &aSize,
|
||||
SurfaceFormat aFormat)
|
||||
{
|
||||
RefPtr<DrawTarget> newTarget = new DrawTargetSkia();
|
||||
if (!newTarget->InitWithGrContext(aGrContext, aSize, aFormat)) {
|
||||
return nullptr;
|
||||
}
|
||||
return newTarget.forget();
|
||||
}
|
||||
|
||||
#endif // USE_SKIA_GPU
|
||||
|
||||
#ifdef USE_SKIA
|
||||
already_AddRefed<DrawTarget>
|
||||
Factory::CreateDrawTargetWithSkCanvas(SkCanvas* aCanvas)
|
||||
{
|
||||
RefPtr<DrawTargetSkia> newTarget = new DrawTargetSkia();
|
||||
if (!newTarget->Init(aCanvas)) {
|
||||
return nullptr;
|
||||
}
|
||||
return newTarget.forget();
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
Factory::PurgeAllCaches()
|
||||
{
|
||||
}
|
||||
|
||||
already_AddRefed<DrawTarget>
|
||||
Factory::CreateDrawTargetForCairoSurface(cairo_surface_t* aSurface, const IntSize& aSize, SurfaceFormat* aFormat)
|
||||
{
|
||||
if (!AllowedSurfaceSize(aSize)) {
|
||||
gfxWarning() << "Allowing surface with invalid size (Cairo) " << aSize;
|
||||
}
|
||||
|
||||
RefPtr<DrawTarget> retVal;
|
||||
|
||||
#ifdef USE_CAIRO
|
||||
RefPtr<DrawTargetCairo> newTarget = new DrawTargetCairo();
|
||||
|
||||
if (newTarget->Init(aSurface, aSize, aFormat)) {
|
||||
retVal = newTarget;
|
||||
}
|
||||
|
||||
if (mRecorder && retVal) {
|
||||
return MakeAndAddRef<DrawTargetRecording>(mRecorder, retVal, true);
|
||||
}
|
||||
#endif
|
||||
return retVal.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<SourceSurface>
|
||||
Factory::CreateSourceSurfaceForCairoSurface(cairo_surface_t* aSurface, const IntSize& aSize, SurfaceFormat aFormat)
|
||||
{
|
||||
if (aSize.width <= 0 || aSize.height <= 0) {
|
||||
gfxWarning() << "Can't create a SourceSurface without a valid size";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#ifdef USE_CAIRO
|
||||
return MakeAndAddRef<SourceSurfaceCairo>(aSurface, aSize, aFormat);
|
||||
#else
|
||||
return nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
already_AddRefed<DataSourceSurface>
|
||||
Factory::CreateWrappingDataSourceSurface(uint8_t *aData,
|
||||
int32_t aStride,
|
||||
const IntSize &aSize,
|
||||
SurfaceFormat aFormat,
|
||||
SourceSurfaceDeallocator aDeallocator /* = nullptr */,
|
||||
void* aClosure /* = nullptr */)
|
||||
{
|
||||
// Just check for negative/zero size instead of the full AllowedSurfaceSize() - since
|
||||
// the data is already allocated we do not need to check for a possible overflow - it
|
||||
// already worked.
|
||||
if (aSize.width <= 0 || aSize.height <= 0) {
|
||||
return nullptr;
|
||||
}
|
||||
if (!aDeallocator && aClosure) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(aData);
|
||||
|
||||
RefPtr<SourceSurfaceRawData> newSurf = new SourceSurfaceRawData();
|
||||
newSurf->InitWrappingData(aData, aSize, aStride, aFormat, aDeallocator, aClosure);
|
||||
|
||||
return newSurf.forget();
|
||||
}
|
||||
|
||||
#ifdef XP_DARWIN
|
||||
already_AddRefed<GlyphRenderingOptions>
|
||||
Factory::CreateCGGlyphRenderingOptions(const Color &aFontSmoothingBackgroundColor)
|
||||
{
|
||||
return MakeAndAddRef<GlyphRenderingOptionsCG>(aFontSmoothingBackgroundColor);
|
||||
}
|
||||
#endif
|
||||
|
||||
already_AddRefed<DataSourceSurface>
|
||||
Factory::CreateDataSourceSurface(const IntSize &aSize,
|
||||
SurfaceFormat aFormat,
|
||||
bool aZero)
|
||||
{
|
||||
if (!AllowedSurfaceSize(aSize)) {
|
||||
gfxCriticalError(LoggerOptionsBasedOnSize(aSize)) << "Failed to allocate a surface due to invalid size (DSS) " << aSize;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Skia doesn't support RGBX, so memset RGBX to 0xFF
|
||||
bool clearSurface = aZero || aFormat == SurfaceFormat::B8G8R8X8;
|
||||
uint8_t clearValue = aFormat == SurfaceFormat::B8G8R8X8 ? 0xFF : 0;
|
||||
|
||||
RefPtr<SourceSurfaceAlignedRawData> newSurf = new SourceSurfaceAlignedRawData();
|
||||
if (newSurf->Init(aSize, aFormat, clearSurface, clearValue)) {
|
||||
return newSurf.forget();
|
||||
}
|
||||
|
||||
gfxWarning() << "CreateDataSourceSurface failed in init";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
already_AddRefed<DataSourceSurface>
|
||||
Factory::CreateDataSourceSurfaceWithStride(const IntSize &aSize,
|
||||
SurfaceFormat aFormat,
|
||||
int32_t aStride,
|
||||
bool aZero)
|
||||
{
|
||||
if (!AllowedSurfaceSize(aSize) ||
|
||||
aStride < aSize.width * BytesPerPixel(aFormat)) {
|
||||
gfxCriticalError(LoggerOptionsBasedOnSize(aSize)) << "CreateDataSourceSurfaceWithStride failed with bad stride " << aStride << ", " << aSize << ", " << aFormat;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Skia doesn't support RGBX, so memset RGBX to 0xFF
|
||||
bool clearSurface = aZero || aFormat == SurfaceFormat::B8G8R8X8;
|
||||
uint8_t clearValue = aFormat == SurfaceFormat::B8G8R8X8 ? 0xFF : 0;
|
||||
|
||||
RefPtr<SourceSurfaceAlignedRawData> newSurf = new SourceSurfaceAlignedRawData();
|
||||
if (newSurf->Init(aSize, aFormat, clearSurface, clearValue, aStride)) {
|
||||
return newSurf.forget();
|
||||
}
|
||||
|
||||
gfxCriticalError(LoggerOptionsBasedOnSize(aSize)) << "CreateDataSourceSurfaceWithStride failed to initialize " << aSize << ", " << aFormat << ", " << aStride << ", " << aZero;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static uint16_t
|
||||
PackRGB565(uint8_t r, uint8_t g, uint8_t b)
|
||||
{
|
||||
uint16_t pixel = ((r << 11) & 0xf800) |
|
||||
((g << 5) & 0x07e0) |
|
||||
((b ) & 0x001f);
|
||||
|
||||
return pixel;
|
||||
}
|
||||
|
||||
void
|
||||
Factory::CopyDataSourceSurface(DataSourceSurface* aSource,
|
||||
DataSourceSurface* aDest)
|
||||
{
|
||||
// Don't worry too much about speed.
|
||||
MOZ_ASSERT(aSource->GetSize() == aDest->GetSize());
|
||||
MOZ_ASSERT(aSource->GetFormat() == SurfaceFormat::R8G8B8A8 ||
|
||||
aSource->GetFormat() == SurfaceFormat::R8G8B8X8 ||
|
||||
aSource->GetFormat() == SurfaceFormat::B8G8R8A8 ||
|
||||
aSource->GetFormat() == SurfaceFormat::B8G8R8X8);
|
||||
MOZ_ASSERT(aDest->GetFormat() == SurfaceFormat::R8G8B8A8 ||
|
||||
aDest->GetFormat() == SurfaceFormat::R8G8B8X8 ||
|
||||
aDest->GetFormat() == SurfaceFormat::B8G8R8A8 ||
|
||||
aDest->GetFormat() == SurfaceFormat::B8G8R8X8 ||
|
||||
aDest->GetFormat() == SurfaceFormat::R5G6B5_UINT16);
|
||||
|
||||
const bool isSrcBGR = aSource->GetFormat() == SurfaceFormat::B8G8R8A8 ||
|
||||
aSource->GetFormat() == SurfaceFormat::B8G8R8X8;
|
||||
const bool isDestBGR = aDest->GetFormat() == SurfaceFormat::B8G8R8A8 ||
|
||||
aDest->GetFormat() == SurfaceFormat::B8G8R8X8;
|
||||
const bool needsSwap02 = isSrcBGR != isDestBGR;
|
||||
|
||||
const bool srcHasAlpha = aSource->GetFormat() == SurfaceFormat::R8G8B8A8 ||
|
||||
aSource->GetFormat() == SurfaceFormat::B8G8R8A8;
|
||||
const bool destHasAlpha = aDest->GetFormat() == SurfaceFormat::R8G8B8A8 ||
|
||||
aDest->GetFormat() == SurfaceFormat::B8G8R8A8;
|
||||
const bool needsAlphaMask = !srcHasAlpha && destHasAlpha;
|
||||
|
||||
const bool needsConvertTo16Bits = aDest->GetFormat() == SurfaceFormat::R5G6B5_UINT16;
|
||||
|
||||
DataSourceSurface::MappedSurface srcMap;
|
||||
DataSourceSurface::MappedSurface destMap;
|
||||
if (!aSource->Map(DataSourceSurface::MapType::READ, &srcMap) ||
|
||||
!aDest->Map(DataSourceSurface::MapType::WRITE, &destMap)) {
|
||||
MOZ_ASSERT(false, "CopyDataSourceSurface: Failed to map surface.");
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(srcMap.mStride >= 0);
|
||||
MOZ_ASSERT(destMap.mStride >= 0);
|
||||
|
||||
const size_t srcBPP = BytesPerPixel(aSource->GetFormat());
|
||||
const size_t srcRowBytes = aSource->GetSize().width * srcBPP;
|
||||
const size_t srcRowHole = srcMap.mStride - srcRowBytes;
|
||||
|
||||
const size_t destBPP = BytesPerPixel(aDest->GetFormat());
|
||||
const size_t destRowBytes = aDest->GetSize().width * destBPP;
|
||||
const size_t destRowHole = destMap.mStride - destRowBytes;
|
||||
|
||||
uint8_t* srcRow = srcMap.mData;
|
||||
uint8_t* destRow = destMap.mData;
|
||||
const size_t rows = aSource->GetSize().height;
|
||||
for (size_t i = 0; i < rows; i++) {
|
||||
const uint8_t* srcRowEnd = srcRow + srcRowBytes;
|
||||
|
||||
while (srcRow != srcRowEnd) {
|
||||
uint8_t d0 = needsSwap02 ? srcRow[2] : srcRow[0];
|
||||
uint8_t d1 = srcRow[1];
|
||||
uint8_t d2 = needsSwap02 ? srcRow[0] : srcRow[2];
|
||||
uint8_t d3 = needsAlphaMask ? 0xff : srcRow[3];
|
||||
|
||||
if (needsConvertTo16Bits) {
|
||||
*(uint16_t*)destRow = PackRGB565(d0, d1, d2);
|
||||
} else {
|
||||
destRow[0] = d0;
|
||||
destRow[1] = d1;
|
||||
destRow[2] = d2;
|
||||
destRow[3] = d3;
|
||||
}
|
||||
srcRow += srcBPP;
|
||||
destRow += destBPP;
|
||||
}
|
||||
|
||||
srcRow += srcRowHole;
|
||||
destRow += destRowHole;
|
||||
}
|
||||
|
||||
aSource->Unmap();
|
||||
aDest->Unmap();
|
||||
}
|
||||
|
||||
already_AddRefed<DrawEventRecorder>
|
||||
Factory::CreateEventRecorderForFile(const char *aFilename)
|
||||
{
|
||||
return MakeAndAddRef<DrawEventRecorderFile>(aFilename);
|
||||
}
|
||||
|
||||
void
|
||||
Factory::SetGlobalEventRecorder(DrawEventRecorder *aRecorder)
|
||||
{
|
||||
mRecorder = aRecorder;
|
||||
}
|
||||
|
||||
// static
|
||||
void
|
||||
CriticalLogger::OutputMessage(const std::string &aString,
|
||||
int aLevel, bool aNoNewline)
|
||||
{
|
||||
if (Factory::GetLogForwarder()) {
|
||||
Factory::GetLogForwarder()->Log(aString);
|
||||
}
|
||||
|
||||
BasicLogger::OutputMessage(aString, aLevel, aNoNewline);
|
||||
}
|
||||
|
||||
void
|
||||
CriticalLogger::CrashAction(LogReason aReason)
|
||||
{
|
||||
if (Factory::GetLogForwarder()) {
|
||||
Factory::GetLogForwarder()->CrashAction(aReason);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
||||
1102
gfx/2d/FilterNodeD2D1.cpp
Normal file
1102
gfx/2d/FilterNodeD2D1.cpp
Normal file
File diff suppressed because it is too large
Load diff
133
gfx/2d/FilterNodeD2D1.h
Normal file
133
gfx/2d/FilterNodeD2D1.h
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
/* -*- Mode: C++; tab-width: 20; 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_GFX_FILTERNODED2D1_H_
|
||||
#define MOZILLA_GFX_FILTERNODED2D1_H_
|
||||
|
||||
#include "2D.h"
|
||||
#include "Filters.h"
|
||||
#include <vector>
|
||||
#include <d2d1_1.h>
|
||||
#include <cguid.h>
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
class FilterNodeD2D1 : public FilterNode
|
||||
{
|
||||
public:
|
||||
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeD2D1)
|
||||
static already_AddRefed<FilterNode> Create(ID2D1DeviceContext *aDC, FilterType aType);
|
||||
|
||||
FilterNodeD2D1(ID2D1Effect *aEffect, FilterType aType)
|
||||
: mEffect(aEffect)
|
||||
, mType(aType)
|
||||
{
|
||||
InitUnmappedProperties();
|
||||
}
|
||||
|
||||
virtual FilterBackend GetBackendType() { return FILTER_BACKEND_DIRECT2D1_1; }
|
||||
|
||||
virtual void SetInput(uint32_t aIndex, SourceSurface *aSurface);
|
||||
virtual void SetInput(uint32_t aIndex, FilterNode *aFilter);
|
||||
|
||||
virtual void SetAttribute(uint32_t aIndex, uint32_t aValue);
|
||||
virtual void SetAttribute(uint32_t aIndex, Float aValue);
|
||||
virtual void SetAttribute(uint32_t aIndex, const Point &aValue);
|
||||
virtual void SetAttribute(uint32_t aIndex, const Matrix5x4 &aValue);
|
||||
virtual void SetAttribute(uint32_t aIndex, const Point3D &aValue);
|
||||
virtual void SetAttribute(uint32_t aIndex, const Size &aValue);
|
||||
virtual void SetAttribute(uint32_t aIndex, const IntSize &aValue);
|
||||
virtual void SetAttribute(uint32_t aIndex, const Color &aValue);
|
||||
virtual void SetAttribute(uint32_t aIndex, const Rect &aValue);
|
||||
virtual void SetAttribute(uint32_t aIndex, const IntRect &aValue);
|
||||
virtual void SetAttribute(uint32_t aIndex, bool aValue);
|
||||
virtual void SetAttribute(uint32_t aIndex, const Float *aValues, uint32_t aSize);
|
||||
virtual void SetAttribute(uint32_t aIndex, const IntPoint &aValue);
|
||||
virtual void SetAttribute(uint32_t aIndex, const Matrix &aValue);
|
||||
|
||||
// Called by DrawTarget before it draws our OutputEffect, and recursively
|
||||
// by the filter nodes that have this filter as one of their inputs. This
|
||||
// gives us a chance to convert any input surfaces to the target format for
|
||||
// the DrawTarget that we will draw to.
|
||||
virtual void WillDraw(DrawTarget *aDT);
|
||||
|
||||
virtual ID2D1Effect* MainEffect() { return mEffect.get(); }
|
||||
virtual ID2D1Effect* InputEffect() { return mEffect.get(); }
|
||||
virtual ID2D1Effect* OutputEffect() { return mEffect.get(); }
|
||||
|
||||
protected:
|
||||
friend class DrawTargetD2D1;
|
||||
friend class DrawTargetD2D;
|
||||
friend class FilterNodeConvolveD2D1;
|
||||
|
||||
void InitUnmappedProperties();
|
||||
|
||||
RefPtr<ID2D1Effect> mEffect;
|
||||
std::vector<RefPtr<FilterNodeD2D1>> mInputFilters;
|
||||
std::vector<RefPtr<SourceSurface>> mInputSurfaces;
|
||||
FilterType mType;
|
||||
};
|
||||
|
||||
class FilterNodeConvolveD2D1 : public FilterNodeD2D1
|
||||
{
|
||||
public:
|
||||
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeConvolveD2D1, override)
|
||||
FilterNodeConvolveD2D1(ID2D1DeviceContext *aDC);
|
||||
|
||||
virtual void SetInput(uint32_t aIndex, FilterNode *aFilter) override;
|
||||
|
||||
virtual void SetAttribute(uint32_t aIndex, uint32_t aValue) override;
|
||||
virtual void SetAttribute(uint32_t aIndex, const IntSize &aValue) override;
|
||||
virtual void SetAttribute(uint32_t aIndex, const IntPoint &aValue) override;
|
||||
virtual void SetAttribute(uint32_t aIndex, const IntRect &aValue) override;
|
||||
|
||||
virtual ID2D1Effect* InputEffect() override;
|
||||
|
||||
private:
|
||||
void UpdateChain();
|
||||
void UpdateOffset();
|
||||
void UpdateSourceRect();
|
||||
|
||||
RefPtr<ID2D1Effect> mExtendInputEffect;
|
||||
RefPtr<ID2D1Effect> mBorderEffect;
|
||||
ConvolveMatrixEdgeMode mEdgeMode;
|
||||
IntPoint mTarget;
|
||||
IntSize mKernelSize;
|
||||
IntRect mSourceRect;
|
||||
};
|
||||
|
||||
class FilterNodeExtendInputAdapterD2D1 : public FilterNodeD2D1
|
||||
{
|
||||
public:
|
||||
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeExtendInputAdapterD2D1, override)
|
||||
FilterNodeExtendInputAdapterD2D1(ID2D1DeviceContext *aDC, FilterNodeD2D1 *aFilterNode, FilterType aType);
|
||||
|
||||
virtual ID2D1Effect* InputEffect() override { return mExtendInputEffect.get(); }
|
||||
virtual ID2D1Effect* OutputEffect() override { return mWrappedFilterNode->OutputEffect(); }
|
||||
|
||||
private:
|
||||
RefPtr<FilterNodeD2D1> mWrappedFilterNode;
|
||||
RefPtr<ID2D1Effect> mExtendInputEffect;
|
||||
};
|
||||
|
||||
class FilterNodePremultiplyAdapterD2D1 : public FilterNodeD2D1
|
||||
{
|
||||
public:
|
||||
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodePremultiplyAdapterD2D1, override)
|
||||
FilterNodePremultiplyAdapterD2D1(ID2D1DeviceContext *aDC, FilterNodeD2D1 *aFilterNode, FilterType aType);
|
||||
|
||||
virtual ID2D1Effect* InputEffect() override { return mPrePremultiplyEffect.get(); }
|
||||
virtual ID2D1Effect* OutputEffect() override { return mPostUnpremultiplyEffect.get(); }
|
||||
|
||||
private:
|
||||
RefPtr<ID2D1Effect> mPrePremultiplyEffect;
|
||||
RefPtr<ID2D1Effect> mPostUnpremultiplyEffect;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
3689
gfx/2d/FilterNodeSoftware.cpp
Normal file
3689
gfx/2d/FilterNodeSoftware.cpp
Normal file
File diff suppressed because it is too large
Load diff
724
gfx/2d/FilterNodeSoftware.h
Normal file
724
gfx/2d/FilterNodeSoftware.h
Normal file
|
|
@ -0,0 +1,724 @@
|
|||
/* -*- Mode: C++; tab-width: 20; 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_GFX_FILTERNODESOFTWARE_H_
|
||||
#define _MOZILLA_GFX_FILTERNODESOFTWARE_H_
|
||||
|
||||
#include "Filters.h"
|
||||
#include <vector>
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
class DataSourceSurface;
|
||||
class DrawTarget;
|
||||
struct DrawOptions;
|
||||
class FilterNodeSoftware;
|
||||
|
||||
/**
|
||||
* Can be attached to FilterNodeSoftware instances using
|
||||
* AddInvalidationListener. FilterInvalidated is called whenever the output of
|
||||
* the observed filter may have changed; that is, whenever cached GetOutput()
|
||||
* results (and results derived from them) need to discarded.
|
||||
*/
|
||||
class FilterInvalidationListener
|
||||
{
|
||||
public:
|
||||
virtual void FilterInvalidated(FilterNodeSoftware* aFilter) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* This is the base class for the software (i.e. pure CPU, non-accelerated)
|
||||
* FilterNode implementation. The software implementation is backend-agnostic,
|
||||
* so it can be used as a fallback for all DrawTarget implementations.
|
||||
*/
|
||||
class FilterNodeSoftware : public FilterNode,
|
||||
public FilterInvalidationListener
|
||||
{
|
||||
public:
|
||||
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeSoftware, override)
|
||||
virtual ~FilterNodeSoftware();
|
||||
|
||||
// Factory method, intended to be called from DrawTarget*::CreateFilter.
|
||||
static already_AddRefed<FilterNode> Create(FilterType aType);
|
||||
|
||||
// Draw the filter, intended to be called by DrawTarget*::DrawFilter.
|
||||
void Draw(DrawTarget* aDrawTarget, const Rect &aSourceRect,
|
||||
const Point &aDestPoint, const DrawOptions &aOptions);
|
||||
|
||||
virtual FilterBackend GetBackendType() override { return FILTER_BACKEND_SOFTWARE; }
|
||||
virtual void SetInput(uint32_t aIndex, SourceSurface *aSurface) override;
|
||||
virtual void SetInput(uint32_t aIndex, FilterNode *aFilter) override;
|
||||
|
||||
virtual const char* GetName() { return "Unknown"; }
|
||||
|
||||
virtual void AddInvalidationListener(FilterInvalidationListener* aListener);
|
||||
virtual void RemoveInvalidationListener(FilterInvalidationListener* aListener);
|
||||
|
||||
// FilterInvalidationListener implementation
|
||||
virtual void FilterInvalidated(FilterNodeSoftware* aFilter) override;
|
||||
|
||||
protected:
|
||||
|
||||
// The following methods are intended to be overriden by subclasses.
|
||||
|
||||
/**
|
||||
* Translates a *FilterInputs enum value into an index for the
|
||||
* mInputFilters / mInputSurfaces arrays. Returns -1 for invalid inputs.
|
||||
* If somebody calls SetInput(enumValue, input) with an enumValue for which
|
||||
* InputIndex(enumValue) is -1, we abort.
|
||||
*/
|
||||
virtual int32_t InputIndex(uint32_t aInputEnumIndex) { return -1; }
|
||||
|
||||
/**
|
||||
* Every filter node has an output rect, which can also be infinite. The
|
||||
* output rect can depend on the values of any set attributes and on the
|
||||
* output rects of any input filters or surfaces.
|
||||
* This method returns the intersection of the filter's output rect with
|
||||
* aInRect. Filters with unconstrained output always return aInRect.
|
||||
*/
|
||||
virtual IntRect GetOutputRectInRect(const IntRect& aInRect) = 0;
|
||||
|
||||
/**
|
||||
* Return a surface with the rendered output which is of size aRect.Size().
|
||||
* aRect is required to be a subrect of this filter's output rect; in other
|
||||
* words, aRect == GetOutputRectInRect(aRect) must always be true.
|
||||
* May return nullptr in error conditions or for an empty aRect.
|
||||
* Implementations are not required to allocate a new surface and may even
|
||||
* pass through input surfaces unchanged.
|
||||
* Callers need to treat the returned surface as immutable.
|
||||
*/
|
||||
virtual already_AddRefed<DataSourceSurface> Render(const IntRect& aRect) = 0;
|
||||
|
||||
/**
|
||||
* Call RequestRect (see below) on any input filters with the desired input
|
||||
* rect, so that the input filter knows what to cache the next time it
|
||||
* renders.
|
||||
*/
|
||||
virtual void RequestFromInputsForRect(const IntRect &aRect) {}
|
||||
|
||||
/**
|
||||
* This method provides a caching default implementation but can be overriden
|
||||
* by subclasses that don't want to cache their output. Those classes should
|
||||
* call Render(aRect) directly from here.
|
||||
*/
|
||||
virtual already_AddRefed<DataSourceSurface> GetOutput(const IntRect &aRect);
|
||||
|
||||
// The following methods are non-virtual helper methods.
|
||||
|
||||
/**
|
||||
* Format hints for GetInputDataSourceSurface. Some callers of
|
||||
* GetInputDataSourceSurface can handle both B8G8R8A8 and A8 surfaces, these
|
||||
* should pass CAN_HANDLE_A8 in order to avoid unnecessary conversions.
|
||||
* Callers that can only handle B8G8R8A8 surfaces pass NEED_COLOR_CHANNELS.
|
||||
*/
|
||||
enum FormatHint {
|
||||
CAN_HANDLE_A8,
|
||||
NEED_COLOR_CHANNELS
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns SurfaceFormat::B8G8R8A8 or SurfaceFormat::A8, depending on the current surface
|
||||
* format and the format hint.
|
||||
*/
|
||||
SurfaceFormat DesiredFormat(SurfaceFormat aCurrentFormat,
|
||||
FormatHint aFormatHint);
|
||||
|
||||
/**
|
||||
* Intended to be called by FilterNodeSoftware::Render implementations.
|
||||
* Returns a surface of size aRect.Size() or nullptr in error conditions. The
|
||||
* returned surface contains the output of the specified input filter or
|
||||
* input surface in aRect. If aRect extends beyond the input filter's output
|
||||
* rect (or the input surface's dimensions), the remaining area is filled
|
||||
* according to aEdgeMode: The default, EDGE_MODE_NONE, simply pads with
|
||||
* transparent black.
|
||||
* If non-null, the returned surface is guaranteed to be of SurfaceFormat::A8 or
|
||||
* SurfaceFormat::B8G8R8A8. If aFormatHint is NEED_COLOR_CHANNELS, the returned
|
||||
* surface is guaranteed to be of SurfaceFormat::B8G8R8A8 always.
|
||||
* Each pixel row of the returned surface is guaranteed to be 16-byte aligned.
|
||||
*/
|
||||
already_AddRefed<DataSourceSurface>
|
||||
GetInputDataSourceSurface(uint32_t aInputEnumIndex, const IntRect& aRect,
|
||||
FormatHint aFormatHint = CAN_HANDLE_A8,
|
||||
ConvolveMatrixEdgeMode aEdgeMode = EDGE_MODE_NONE,
|
||||
const IntRect *aTransparencyPaddedSourceRect = nullptr);
|
||||
|
||||
/**
|
||||
* Returns the intersection of the input filter's or surface's output rect
|
||||
* with aInRect.
|
||||
*/
|
||||
IntRect GetInputRectInRect(uint32_t aInputEnumIndex, const IntRect& aInRect);
|
||||
|
||||
/**
|
||||
* Calls RequestRect on the specified input, if it's a filter.
|
||||
*/
|
||||
void RequestInputRect(uint32_t aInputEnumIndex, const IntRect& aRect);
|
||||
|
||||
/**
|
||||
* Returns the number of set input filters or surfaces. Needed for filters
|
||||
* which can have an arbitrary number of inputs.
|
||||
*/
|
||||
size_t NumberOfSetInputs();
|
||||
|
||||
/**
|
||||
* Discard the cached surface that was stored in the GetOutput default
|
||||
* implementation. Needs to be called whenever attributes or inputs are set
|
||||
* that might change the result of a Render() call.
|
||||
*/
|
||||
void Invalidate();
|
||||
|
||||
/**
|
||||
* Called in order to let this filter know what to cache during the next
|
||||
* GetOutput call. Expected to call RequestRect on this filter's input
|
||||
* filters.
|
||||
*/
|
||||
void RequestRect(const IntRect &aRect);
|
||||
|
||||
/**
|
||||
* Set input filter and clear input surface for this input index, or set
|
||||
* input surface and clear input filter. One of aSurface and aFilter should
|
||||
* be null.
|
||||
*/
|
||||
void SetInput(uint32_t aIndex, SourceSurface *aSurface,
|
||||
FilterNodeSoftware *aFilter);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* mInputSurfaces / mInputFilters: For each input index, either a surface or
|
||||
* a filter is set, and the other is null.
|
||||
*/
|
||||
std::vector<RefPtr<SourceSurface> > mInputSurfaces;
|
||||
std::vector<RefPtr<FilterNodeSoftware> > mInputFilters;
|
||||
|
||||
/**
|
||||
* Weak pointers to our invalidation listeners, i.e. to those filters who
|
||||
* have this filter as an input. Invalidation listeners are required to
|
||||
* unsubscribe themselves from us when they let go of their reference to us.
|
||||
* This ensures that the pointers in this array are never stale.
|
||||
*/
|
||||
std::vector<FilterInvalidationListener*> mInvalidationListeners;
|
||||
|
||||
/**
|
||||
* Stores the rect which we want to render and cache on the next call to
|
||||
* GetOutput.
|
||||
*/
|
||||
IntRect mRequestedRect;
|
||||
|
||||
/**
|
||||
* Stores our cached output.
|
||||
*/
|
||||
IntRect mCachedRect;
|
||||
RefPtr<DataSourceSurface> mCachedOutput;
|
||||
};
|
||||
|
||||
// Subclasses for specific filters.
|
||||
|
||||
class FilterNodeTransformSoftware : public FilterNodeSoftware
|
||||
{
|
||||
public:
|
||||
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeTransformSoftware, override)
|
||||
FilterNodeTransformSoftware();
|
||||
virtual const char* GetName() override { return "Transform"; }
|
||||
using FilterNodeSoftware::SetAttribute;
|
||||
virtual void SetAttribute(uint32_t aIndex, uint32_t aGraphicsFilter) override;
|
||||
virtual void SetAttribute(uint32_t aIndex, const Matrix &aMatrix) override;
|
||||
|
||||
protected:
|
||||
virtual already_AddRefed<DataSourceSurface> Render(const IntRect& aRect) override;
|
||||
virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
|
||||
virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
|
||||
virtual void RequestFromInputsForRect(const IntRect &aRect) override;
|
||||
IntRect SourceRectForOutputRect(const IntRect &aRect);
|
||||
|
||||
private:
|
||||
Matrix mMatrix;
|
||||
SamplingFilter mSamplingFilter;
|
||||
};
|
||||
|
||||
class FilterNodeBlendSoftware : public FilterNodeSoftware
|
||||
{
|
||||
public:
|
||||
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeBlendSoftware, override)
|
||||
FilterNodeBlendSoftware();
|
||||
virtual const char* GetName() override { return "Blend"; }
|
||||
using FilterNodeSoftware::SetAttribute;
|
||||
virtual void SetAttribute(uint32_t aIndex, uint32_t aBlendMode) override;
|
||||
|
||||
protected:
|
||||
virtual already_AddRefed<DataSourceSurface> Render(const IntRect& aRect) override;
|
||||
virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
|
||||
virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
|
||||
virtual void RequestFromInputsForRect(const IntRect &aRect) override;
|
||||
|
||||
private:
|
||||
BlendMode mBlendMode;
|
||||
};
|
||||
|
||||
class FilterNodeMorphologySoftware : public FilterNodeSoftware
|
||||
{
|
||||
public:
|
||||
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeMorphologySoftware, override)
|
||||
FilterNodeMorphologySoftware();
|
||||
virtual const char* GetName() override { return "Morphology"; }
|
||||
using FilterNodeSoftware::SetAttribute;
|
||||
virtual void SetAttribute(uint32_t aIndex, const IntSize &aRadii) override;
|
||||
virtual void SetAttribute(uint32_t aIndex, uint32_t aOperator) override;
|
||||
|
||||
protected:
|
||||
virtual already_AddRefed<DataSourceSurface> Render(const IntRect& aRect) override;
|
||||
virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
|
||||
virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
|
||||
virtual void RequestFromInputsForRect(const IntRect &aRect) override;
|
||||
|
||||
private:
|
||||
IntSize mRadii;
|
||||
MorphologyOperator mOperator;
|
||||
};
|
||||
|
||||
class FilterNodeColorMatrixSoftware : public FilterNodeSoftware
|
||||
{
|
||||
public:
|
||||
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeColorMatrixSoftware, override)
|
||||
virtual const char* GetName() override { return "ColorMatrix"; }
|
||||
using FilterNodeSoftware::SetAttribute;
|
||||
virtual void SetAttribute(uint32_t aIndex, const Matrix5x4 &aMatrix) override;
|
||||
virtual void SetAttribute(uint32_t aIndex, uint32_t aAlphaMode) override;
|
||||
|
||||
protected:
|
||||
virtual already_AddRefed<DataSourceSurface> Render(const IntRect& aRect) override;
|
||||
virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
|
||||
virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
|
||||
virtual void RequestFromInputsForRect(const IntRect &aRect) override;
|
||||
|
||||
private:
|
||||
Matrix5x4 mMatrix;
|
||||
AlphaMode mAlphaMode;
|
||||
};
|
||||
|
||||
class FilterNodeFloodSoftware : public FilterNodeSoftware
|
||||
{
|
||||
public:
|
||||
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeFloodSoftware, override)
|
||||
virtual const char* GetName() override { return "Flood"; }
|
||||
using FilterNodeSoftware::SetAttribute;
|
||||
virtual void SetAttribute(uint32_t aIndex, const Color &aColor) override;
|
||||
|
||||
protected:
|
||||
virtual already_AddRefed<DataSourceSurface> GetOutput(const IntRect &aRect) override;
|
||||
virtual already_AddRefed<DataSourceSurface> Render(const IntRect& aRect) override;
|
||||
virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
|
||||
|
||||
private:
|
||||
Color mColor;
|
||||
};
|
||||
|
||||
class FilterNodeTileSoftware : public FilterNodeSoftware
|
||||
{
|
||||
public:
|
||||
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeTileSoftware, override)
|
||||
virtual const char* GetName() override { return "Tile"; }
|
||||
using FilterNodeSoftware::SetAttribute;
|
||||
virtual void SetAttribute(uint32_t aIndex, const IntRect &aSourceRect) override;
|
||||
|
||||
protected:
|
||||
virtual already_AddRefed<DataSourceSurface> Render(const IntRect& aRect) override;
|
||||
virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
|
||||
virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
|
||||
virtual void RequestFromInputsForRect(const IntRect &aRect) override;
|
||||
|
||||
private:
|
||||
IntRect mSourceRect;
|
||||
};
|
||||
|
||||
/**
|
||||
* Baseclass for the four different component transfer filters.
|
||||
*/
|
||||
class FilterNodeComponentTransferSoftware : public FilterNodeSoftware
|
||||
{
|
||||
public:
|
||||
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeComponentTransferSoftware, override)
|
||||
FilterNodeComponentTransferSoftware();
|
||||
|
||||
using FilterNodeSoftware::SetAttribute;
|
||||
virtual void SetAttribute(uint32_t aIndex, bool aDisable) override;
|
||||
|
||||
protected:
|
||||
virtual already_AddRefed<DataSourceSurface> Render(const IntRect& aRect) override;
|
||||
virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
|
||||
virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
|
||||
virtual void RequestFromInputsForRect(const IntRect &aRect) override;
|
||||
virtual void GenerateLookupTable(ptrdiff_t aComponent, uint8_t aTables[4][256],
|
||||
bool aDisabled);
|
||||
virtual void FillLookupTable(ptrdiff_t aComponent, uint8_t aTable[256]) = 0;
|
||||
|
||||
bool mDisableR;
|
||||
bool mDisableG;
|
||||
bool mDisableB;
|
||||
bool mDisableA;
|
||||
};
|
||||
|
||||
class FilterNodeTableTransferSoftware : public FilterNodeComponentTransferSoftware
|
||||
{
|
||||
public:
|
||||
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeTableTransferSoftware, override)
|
||||
virtual const char* GetName() override { return "TableTransfer"; }
|
||||
using FilterNodeComponentTransferSoftware::SetAttribute;
|
||||
virtual void SetAttribute(uint32_t aIndex, const Float* aFloat, uint32_t aSize) override;
|
||||
|
||||
protected:
|
||||
virtual void FillLookupTable(ptrdiff_t aComponent, uint8_t aTable[256]) override;
|
||||
|
||||
private:
|
||||
void FillLookupTableImpl(std::vector<Float>& aTableValues, uint8_t aTable[256]);
|
||||
|
||||
std::vector<Float> mTableR;
|
||||
std::vector<Float> mTableG;
|
||||
std::vector<Float> mTableB;
|
||||
std::vector<Float> mTableA;
|
||||
};
|
||||
|
||||
class FilterNodeDiscreteTransferSoftware : public FilterNodeComponentTransferSoftware
|
||||
{
|
||||
public:
|
||||
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeDiscreteTransferSoftware, override)
|
||||
virtual const char* GetName() override { return "DiscreteTransfer"; }
|
||||
using FilterNodeComponentTransferSoftware::SetAttribute;
|
||||
virtual void SetAttribute(uint32_t aIndex, const Float* aFloat, uint32_t aSize) override;
|
||||
|
||||
protected:
|
||||
virtual void FillLookupTable(ptrdiff_t aComponent, uint8_t aTable[256]) override;
|
||||
|
||||
private:
|
||||
void FillLookupTableImpl(std::vector<Float>& aTableValues, uint8_t aTable[256]);
|
||||
|
||||
std::vector<Float> mTableR;
|
||||
std::vector<Float> mTableG;
|
||||
std::vector<Float> mTableB;
|
||||
std::vector<Float> mTableA;
|
||||
};
|
||||
|
||||
class FilterNodeLinearTransferSoftware : public FilterNodeComponentTransferSoftware
|
||||
{
|
||||
public:
|
||||
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeLinearTransformSoftware, override)
|
||||
FilterNodeLinearTransferSoftware();
|
||||
virtual const char* GetName() override { return "LinearTransfer"; }
|
||||
using FilterNodeComponentTransferSoftware::SetAttribute;
|
||||
virtual void SetAttribute(uint32_t aIndex, Float aValue) override;
|
||||
|
||||
protected:
|
||||
virtual void FillLookupTable(ptrdiff_t aComponent, uint8_t aTable[256]) override;
|
||||
|
||||
private:
|
||||
void FillLookupTableImpl(Float aSlope, Float aIntercept, uint8_t aTable[256]);
|
||||
|
||||
Float mSlopeR;
|
||||
Float mSlopeG;
|
||||
Float mSlopeB;
|
||||
Float mSlopeA;
|
||||
Float mInterceptR;
|
||||
Float mInterceptG;
|
||||
Float mInterceptB;
|
||||
Float mInterceptA;
|
||||
};
|
||||
|
||||
class FilterNodeGammaTransferSoftware : public FilterNodeComponentTransferSoftware
|
||||
{
|
||||
public:
|
||||
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeGammaTransferSoftware, override)
|
||||
FilterNodeGammaTransferSoftware();
|
||||
virtual const char* GetName() override { return "GammaTransfer"; }
|
||||
using FilterNodeComponentTransferSoftware::SetAttribute;
|
||||
virtual void SetAttribute(uint32_t aIndex, Float aValue) override;
|
||||
|
||||
protected:
|
||||
virtual void FillLookupTable(ptrdiff_t aComponent, uint8_t aTable[256]) override;
|
||||
|
||||
private:
|
||||
void FillLookupTableImpl(Float aAmplitude, Float aExponent, Float aOffset, uint8_t aTable[256]);
|
||||
|
||||
Float mAmplitudeR;
|
||||
Float mAmplitudeG;
|
||||
Float mAmplitudeB;
|
||||
Float mAmplitudeA;
|
||||
Float mExponentR;
|
||||
Float mExponentG;
|
||||
Float mExponentB;
|
||||
Float mExponentA;
|
||||
Float mOffsetR;
|
||||
Float mOffsetG;
|
||||
Float mOffsetB;
|
||||
Float mOffsetA;
|
||||
};
|
||||
|
||||
class FilterNodeConvolveMatrixSoftware : public FilterNodeSoftware
|
||||
{
|
||||
public:
|
||||
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeConvolveMatrixSoftware, override)
|
||||
FilterNodeConvolveMatrixSoftware();
|
||||
virtual const char* GetName() override { return "ConvolveMatrix"; }
|
||||
using FilterNodeSoftware::SetAttribute;
|
||||
virtual void SetAttribute(uint32_t aIndex, const IntSize &aKernelSize) override;
|
||||
virtual void SetAttribute(uint32_t aIndex, const Float* aMatrix, uint32_t aSize) override;
|
||||
virtual void SetAttribute(uint32_t aIndex, Float aValue) override;
|
||||
virtual void SetAttribute(uint32_t aIndex, const Size &aKernelUnitLength) override;
|
||||
virtual void SetAttribute(uint32_t aIndex, const IntRect &aSourceRect) override;
|
||||
virtual void SetAttribute(uint32_t aIndex, const IntPoint &aTarget) override;
|
||||
virtual void SetAttribute(uint32_t aIndex, uint32_t aEdgeMode) override;
|
||||
virtual void SetAttribute(uint32_t aIndex, bool aPreserveAlpha) override;
|
||||
|
||||
protected:
|
||||
virtual already_AddRefed<DataSourceSurface> Render(const IntRect& aRect) override;
|
||||
virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
|
||||
virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
|
||||
virtual void RequestFromInputsForRect(const IntRect &aRect) override;
|
||||
|
||||
private:
|
||||
template<typename CoordType>
|
||||
already_AddRefed<DataSourceSurface> DoRender(const IntRect& aRect,
|
||||
CoordType aKernelUnitLengthX,
|
||||
CoordType aKernelUnitLengthY);
|
||||
|
||||
IntRect InflatedSourceRect(const IntRect &aDestRect);
|
||||
IntRect InflatedDestRect(const IntRect &aSourceRect);
|
||||
|
||||
IntSize mKernelSize;
|
||||
std::vector<Float> mKernelMatrix;
|
||||
Float mDivisor;
|
||||
Float mBias;
|
||||
IntPoint mTarget;
|
||||
IntRect mSourceRect;
|
||||
ConvolveMatrixEdgeMode mEdgeMode;
|
||||
Size mKernelUnitLength;
|
||||
bool mPreserveAlpha;
|
||||
};
|
||||
|
||||
class FilterNodeDisplacementMapSoftware : public FilterNodeSoftware
|
||||
{
|
||||
public:
|
||||
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeDisplacementMapSoftware, override)
|
||||
FilterNodeDisplacementMapSoftware();
|
||||
virtual const char* GetName() override { return "DisplacementMap"; }
|
||||
using FilterNodeSoftware::SetAttribute;
|
||||
virtual void SetAttribute(uint32_t aIndex, Float aScale) override;
|
||||
virtual void SetAttribute(uint32_t aIndex, uint32_t aValue) override;
|
||||
|
||||
protected:
|
||||
virtual already_AddRefed<DataSourceSurface> Render(const IntRect& aRect) override;
|
||||
virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
|
||||
virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
|
||||
virtual void RequestFromInputsForRect(const IntRect &aRect) override;
|
||||
|
||||
private:
|
||||
IntRect InflatedSourceOrDestRect(const IntRect &aDestOrSourceRect);
|
||||
|
||||
Float mScale;
|
||||
ColorChannel mChannelX;
|
||||
ColorChannel mChannelY;
|
||||
};
|
||||
|
||||
class FilterNodeTurbulenceSoftware : public FilterNodeSoftware
|
||||
{
|
||||
public:
|
||||
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeTurbulenceSoftware, override)
|
||||
FilterNodeTurbulenceSoftware();
|
||||
virtual const char* GetName() override { return "Turbulence"; }
|
||||
using FilterNodeSoftware::SetAttribute;
|
||||
virtual void SetAttribute(uint32_t aIndex, const Size &aSize) override;
|
||||
virtual void SetAttribute(uint32_t aIndex, const IntRect &aRenderRect) override;
|
||||
virtual void SetAttribute(uint32_t aIndex, bool aStitchable) override;
|
||||
virtual void SetAttribute(uint32_t aIndex, uint32_t aValue) override;
|
||||
|
||||
protected:
|
||||
virtual already_AddRefed<DataSourceSurface> Render(const IntRect& aRect) override;
|
||||
virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
|
||||
virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
|
||||
|
||||
private:
|
||||
IntRect mRenderRect;
|
||||
Size mBaseFrequency;
|
||||
uint32_t mNumOctaves;
|
||||
uint32_t mSeed;
|
||||
bool mStitchable;
|
||||
TurbulenceType mType;
|
||||
};
|
||||
|
||||
class FilterNodeArithmeticCombineSoftware : public FilterNodeSoftware
|
||||
{
|
||||
public:
|
||||
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeArithmeticCombineSoftware, override)
|
||||
FilterNodeArithmeticCombineSoftware();
|
||||
virtual const char* GetName() override { return "ArithmeticCombine"; }
|
||||
using FilterNodeSoftware::SetAttribute;
|
||||
virtual void SetAttribute(uint32_t aIndex, const Float* aFloat, uint32_t aSize) override;
|
||||
|
||||
protected:
|
||||
virtual already_AddRefed<DataSourceSurface> Render(const IntRect& aRect) override;
|
||||
virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
|
||||
virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
|
||||
virtual void RequestFromInputsForRect(const IntRect &aRect) override;
|
||||
|
||||
private:
|
||||
Float mK1;
|
||||
Float mK2;
|
||||
Float mK3;
|
||||
Float mK4;
|
||||
};
|
||||
|
||||
class FilterNodeCompositeSoftware : public FilterNodeSoftware
|
||||
{
|
||||
public:
|
||||
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeCompositeSoftware, override)
|
||||
FilterNodeCompositeSoftware();
|
||||
virtual const char* GetName() override { return "Composite"; }
|
||||
using FilterNodeSoftware::SetAttribute;
|
||||
virtual void SetAttribute(uint32_t aIndex, uint32_t aOperator) override;
|
||||
|
||||
protected:
|
||||
virtual already_AddRefed<DataSourceSurface> Render(const IntRect& aRect) override;
|
||||
virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
|
||||
virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
|
||||
virtual void RequestFromInputsForRect(const IntRect &aRect) override;
|
||||
|
||||
private:
|
||||
CompositeOperator mOperator;
|
||||
};
|
||||
|
||||
// Base class for FilterNodeGaussianBlurSoftware and
|
||||
// FilterNodeDirectionalBlurSoftware.
|
||||
class FilterNodeBlurXYSoftware : public FilterNodeSoftware
|
||||
{
|
||||
public:
|
||||
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeBlurXYSoftware, override)
|
||||
protected:
|
||||
virtual already_AddRefed<DataSourceSurface> Render(const IntRect& aRect) override;
|
||||
virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
|
||||
virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
|
||||
IntRect InflatedSourceOrDestRect(const IntRect &aDestRect);
|
||||
virtual void RequestFromInputsForRect(const IntRect &aRect) override;
|
||||
|
||||
// Implemented by subclasses.
|
||||
virtual Size StdDeviationXY() = 0;
|
||||
};
|
||||
|
||||
class FilterNodeGaussianBlurSoftware : public FilterNodeBlurXYSoftware
|
||||
{
|
||||
public:
|
||||
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeGaussianBlurSoftware, override)
|
||||
FilterNodeGaussianBlurSoftware();
|
||||
virtual const char* GetName() override { return "GaussianBlur"; }
|
||||
using FilterNodeSoftware::SetAttribute;
|
||||
virtual void SetAttribute(uint32_t aIndex, Float aStdDeviation) override;
|
||||
|
||||
protected:
|
||||
virtual Size StdDeviationXY() override;
|
||||
|
||||
private:
|
||||
Float mStdDeviation;
|
||||
};
|
||||
|
||||
class FilterNodeDirectionalBlurSoftware : public FilterNodeBlurXYSoftware
|
||||
{
|
||||
public:
|
||||
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeDirectionalBlurSoftware, override)
|
||||
FilterNodeDirectionalBlurSoftware();
|
||||
virtual const char* GetName() override { return "DirectionalBlur"; }
|
||||
using FilterNodeSoftware::SetAttribute;
|
||||
virtual void SetAttribute(uint32_t aIndex, Float aStdDeviation) override;
|
||||
virtual void SetAttribute(uint32_t aIndex, uint32_t aBlurDirection) override;
|
||||
|
||||
protected:
|
||||
virtual Size StdDeviationXY() override;
|
||||
|
||||
private:
|
||||
Float mStdDeviation;
|
||||
BlurDirection mBlurDirection;
|
||||
};
|
||||
|
||||
class FilterNodeCropSoftware : public FilterNodeSoftware
|
||||
{
|
||||
public:
|
||||
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeCropSoftware, override)
|
||||
virtual const char* GetName() override { return "Crop"; }
|
||||
using FilterNodeSoftware::SetAttribute;
|
||||
virtual void SetAttribute(uint32_t aIndex, const Rect &aSourceRect) override;
|
||||
|
||||
protected:
|
||||
virtual already_AddRefed<DataSourceSurface> Render(const IntRect& aRect) override;
|
||||
virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
|
||||
virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
|
||||
virtual void RequestFromInputsForRect(const IntRect &aRect) override;
|
||||
|
||||
private:
|
||||
IntRect mCropRect;
|
||||
};
|
||||
|
||||
class FilterNodePremultiplySoftware : public FilterNodeSoftware
|
||||
{
|
||||
public:
|
||||
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodePremultiplySoftware, override)
|
||||
virtual const char* GetName() override { return "Premultiply"; }
|
||||
protected:
|
||||
virtual already_AddRefed<DataSourceSurface> Render(const IntRect& aRect) override;
|
||||
virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
|
||||
virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
|
||||
virtual void RequestFromInputsForRect(const IntRect &aRect) override;
|
||||
};
|
||||
|
||||
class FilterNodeUnpremultiplySoftware : public FilterNodeSoftware
|
||||
{
|
||||
public:
|
||||
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeUnpremultiplySoftware, override)
|
||||
virtual const char* GetName() override { return "Unpremultiply"; }
|
||||
protected:
|
||||
virtual already_AddRefed<DataSourceSurface> Render(const IntRect& aRect) override;
|
||||
virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
|
||||
virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
|
||||
virtual void RequestFromInputsForRect(const IntRect &aRect) override;
|
||||
};
|
||||
|
||||
template<typename LightType, typename LightingType>
|
||||
class FilterNodeLightingSoftware : public FilterNodeSoftware
|
||||
{
|
||||
public:
|
||||
#if defined(MOZILLA_INTERNAL_API) && (defined(DEBUG) || defined(FORCE_BUILD_REFCNT_LOGGING))
|
||||
// Helpers for refcounted
|
||||
virtual const char* typeName() const override { return mTypeName; }
|
||||
virtual size_t typeSize() const override { return sizeof(*this); }
|
||||
#endif
|
||||
explicit FilterNodeLightingSoftware(const char* aTypeName);
|
||||
virtual const char* GetName() override { return "Lighting"; }
|
||||
using FilterNodeSoftware::SetAttribute;
|
||||
virtual void SetAttribute(uint32_t aIndex, Float) override;
|
||||
virtual void SetAttribute(uint32_t aIndex, const Size &) override;
|
||||
virtual void SetAttribute(uint32_t aIndex, const Point3D &) override;
|
||||
virtual void SetAttribute(uint32_t aIndex, const Color &) override;
|
||||
|
||||
protected:
|
||||
virtual already_AddRefed<DataSourceSurface> Render(const IntRect& aRect) override;
|
||||
virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
|
||||
virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
|
||||
virtual void RequestFromInputsForRect(const IntRect &aRect) override;
|
||||
|
||||
private:
|
||||
template<typename CoordType>
|
||||
already_AddRefed<DataSourceSurface> DoRender(const IntRect& aRect,
|
||||
CoordType aKernelUnitLengthX,
|
||||
CoordType aKernelUnitLengthY);
|
||||
|
||||
LightType mLight;
|
||||
LightingType mLighting;
|
||||
Float mSurfaceScale;
|
||||
Size mKernelUnitLength;
|
||||
Color mColor;
|
||||
#if defined(MOZILLA_INTERNAL_API) && (defined(DEBUG) || defined(FORCE_BUILD_REFCNT_LOGGING))
|
||||
const char* mTypeName;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // _MOZILLA_GFX_FILTERNODESOFTWARE_H_
|
||||
262
gfx/2d/FilterProcessing.cpp
Normal file
262
gfx/2d/FilterProcessing.cpp
Normal file
|
|
@ -0,0 +1,262 @@
|
|||
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "FilterProcessing.h"
|
||||
#include "Logging.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
already_AddRefed<DataSourceSurface>
|
||||
FilterProcessing::ExtractAlpha(DataSourceSurface* aSource)
|
||||
{
|
||||
IntSize size = aSource->GetSize();
|
||||
RefPtr<DataSourceSurface> alpha = Factory::CreateDataSourceSurface(size, SurfaceFormat::A8);
|
||||
if (MOZ2D_WARN_IF(!alpha)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
DataSourceSurface::ScopedMap sourceMap(aSource, DataSourceSurface::READ);
|
||||
DataSourceSurface::ScopedMap alphaMap(alpha, DataSourceSurface::WRITE);
|
||||
if (MOZ2D_WARN_IF(!sourceMap.IsMapped() || !alphaMap.IsMapped())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint8_t* sourceData = sourceMap.GetData();
|
||||
int32_t sourceStride = sourceMap.GetStride();
|
||||
uint8_t* alphaData = alphaMap.GetData();
|
||||
int32_t alphaStride = alphaMap.GetStride();
|
||||
|
||||
if (Factory::HasSSE2()) {
|
||||
#ifdef USE_SSE2
|
||||
ExtractAlpha_SSE2(size, sourceData, sourceStride, alphaData, alphaStride);
|
||||
#endif
|
||||
} else {
|
||||
ExtractAlpha_Scalar(size, sourceData, sourceStride, alphaData, alphaStride);
|
||||
}
|
||||
|
||||
return alpha.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<DataSourceSurface>
|
||||
FilterProcessing::ConvertToB8G8R8A8(SourceSurface* aSurface)
|
||||
{
|
||||
if (Factory::HasSSE2()) {
|
||||
#ifdef USE_SSE2
|
||||
return ConvertToB8G8R8A8_SSE2(aSurface);
|
||||
#endif
|
||||
}
|
||||
return ConvertToB8G8R8A8_Scalar(aSurface);
|
||||
}
|
||||
|
||||
already_AddRefed<DataSourceSurface>
|
||||
FilterProcessing::ApplyBlending(DataSourceSurface* aInput1, DataSourceSurface* aInput2,
|
||||
BlendMode aBlendMode)
|
||||
{
|
||||
if (Factory::HasSSE2()) {
|
||||
#ifdef USE_SSE2
|
||||
return ApplyBlending_SSE2(aInput1, aInput2, aBlendMode);
|
||||
#endif
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
FilterProcessing::ApplyMorphologyHorizontal(uint8_t* aSourceData, int32_t aSourceStride,
|
||||
uint8_t* aDestData, int32_t aDestStride,
|
||||
const IntRect& aDestRect, int32_t aRadius,
|
||||
MorphologyOperator aOp)
|
||||
{
|
||||
if (Factory::HasSSE2()) {
|
||||
#ifdef USE_SSE2
|
||||
ApplyMorphologyHorizontal_SSE2(
|
||||
aSourceData, aSourceStride, aDestData, aDestStride, aDestRect, aRadius, aOp);
|
||||
#endif
|
||||
} else {
|
||||
ApplyMorphologyHorizontal_Scalar(
|
||||
aSourceData, aSourceStride, aDestData, aDestStride, aDestRect, aRadius, aOp);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FilterProcessing::ApplyMorphologyVertical(uint8_t* aSourceData, int32_t aSourceStride,
|
||||
uint8_t* aDestData, int32_t aDestStride,
|
||||
const IntRect& aDestRect, int32_t aRadius,
|
||||
MorphologyOperator aOp)
|
||||
{
|
||||
if (Factory::HasSSE2()) {
|
||||
#ifdef USE_SSE2
|
||||
ApplyMorphologyVertical_SSE2(
|
||||
aSourceData, aSourceStride, aDestData, aDestStride, aDestRect, aRadius, aOp);
|
||||
#endif
|
||||
} else {
|
||||
ApplyMorphologyVertical_Scalar(
|
||||
aSourceData, aSourceStride, aDestData, aDestStride, aDestRect, aRadius, aOp);
|
||||
}
|
||||
}
|
||||
|
||||
already_AddRefed<DataSourceSurface>
|
||||
FilterProcessing::ApplyColorMatrix(DataSourceSurface* aInput, const Matrix5x4 &aMatrix)
|
||||
{
|
||||
if (Factory::HasSSE2()) {
|
||||
#ifdef USE_SSE2
|
||||
return ApplyColorMatrix_SSE2(aInput, aMatrix);
|
||||
#endif
|
||||
}
|
||||
return ApplyColorMatrix_Scalar(aInput, aMatrix);
|
||||
}
|
||||
|
||||
void
|
||||
FilterProcessing::ApplyComposition(DataSourceSurface* aSource, DataSourceSurface* aDest,
|
||||
CompositeOperator aOperator)
|
||||
{
|
||||
if (Factory::HasSSE2()) {
|
||||
#ifdef USE_SSE2
|
||||
ApplyComposition_SSE2(aSource, aDest, aOperator);
|
||||
#endif
|
||||
} else {
|
||||
ApplyComposition_Scalar(aSource, aDest, aOperator);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FilterProcessing::SeparateColorChannels(DataSourceSurface* aSource,
|
||||
RefPtr<DataSourceSurface>& aChannel0,
|
||||
RefPtr<DataSourceSurface>& aChannel1,
|
||||
RefPtr<DataSourceSurface>& aChannel2,
|
||||
RefPtr<DataSourceSurface>& aChannel3)
|
||||
{
|
||||
IntSize size = aSource->GetSize();
|
||||
aChannel0 = Factory::CreateDataSourceSurface(size, SurfaceFormat::A8);
|
||||
aChannel1 = Factory::CreateDataSourceSurface(size, SurfaceFormat::A8);
|
||||
aChannel2 = Factory::CreateDataSourceSurface(size, SurfaceFormat::A8);
|
||||
aChannel3 = Factory::CreateDataSourceSurface(size, SurfaceFormat::A8);
|
||||
if (MOZ2D_WARN_IF(!(aChannel0 && aChannel1 && aChannel2 && aChannel3))) {
|
||||
return;
|
||||
}
|
||||
|
||||
DataSourceSurface::ScopedMap sourceMap(aSource, DataSourceSurface::READ);
|
||||
DataSourceSurface::ScopedMap channel0Map(aChannel0, DataSourceSurface::WRITE);
|
||||
DataSourceSurface::ScopedMap channel1Map(aChannel1, DataSourceSurface::WRITE);
|
||||
DataSourceSurface::ScopedMap channel2Map(aChannel2, DataSourceSurface::WRITE);
|
||||
DataSourceSurface::ScopedMap channel3Map(aChannel3, DataSourceSurface::WRITE);
|
||||
if (MOZ2D_WARN_IF(!(sourceMap.IsMapped() &&
|
||||
channel0Map.IsMapped() && channel1Map.IsMapped() &&
|
||||
channel2Map.IsMapped() && channel3Map.IsMapped()))) {
|
||||
return;
|
||||
}
|
||||
uint8_t* sourceData = sourceMap.GetData();
|
||||
int32_t sourceStride = sourceMap.GetStride();
|
||||
uint8_t* channel0Data = channel0Map.GetData();
|
||||
uint8_t* channel1Data = channel1Map.GetData();
|
||||
uint8_t* channel2Data = channel2Map.GetData();
|
||||
uint8_t* channel3Data = channel3Map.GetData();
|
||||
int32_t channelStride = channel0Map.GetStride();
|
||||
|
||||
if (Factory::HasSSE2()) {
|
||||
#ifdef USE_SSE2
|
||||
SeparateColorChannels_SSE2(size, sourceData, sourceStride, channel0Data, channel1Data, channel2Data, channel3Data, channelStride);
|
||||
#endif
|
||||
} else {
|
||||
SeparateColorChannels_Scalar(size, sourceData, sourceStride, channel0Data, channel1Data, channel2Data, channel3Data, channelStride);
|
||||
}
|
||||
}
|
||||
|
||||
already_AddRefed<DataSourceSurface>
|
||||
FilterProcessing::CombineColorChannels(DataSourceSurface* aChannel0, DataSourceSurface* aChannel1,
|
||||
DataSourceSurface* aChannel2, DataSourceSurface* aChannel3)
|
||||
{
|
||||
IntSize size = aChannel0->GetSize();
|
||||
RefPtr<DataSourceSurface> result =
|
||||
Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8);
|
||||
if (MOZ2D_WARN_IF(!result)) {
|
||||
return nullptr;
|
||||
}
|
||||
DataSourceSurface::ScopedMap resultMap(result, DataSourceSurface::WRITE);
|
||||
DataSourceSurface::ScopedMap channel0Map(aChannel0, DataSourceSurface::READ);
|
||||
DataSourceSurface::ScopedMap channel1Map(aChannel1, DataSourceSurface::READ);
|
||||
DataSourceSurface::ScopedMap channel2Map(aChannel2, DataSourceSurface::READ);
|
||||
DataSourceSurface::ScopedMap channel3Map(aChannel3, DataSourceSurface::READ);
|
||||
if (MOZ2D_WARN_IF(!(resultMap.IsMapped() &&
|
||||
channel0Map.IsMapped() && channel1Map.IsMapped() &&
|
||||
channel2Map.IsMapped() && channel3Map.IsMapped()))) {
|
||||
return nullptr;
|
||||
}
|
||||
int32_t resultStride = resultMap.GetStride();
|
||||
uint8_t* resultData = resultMap.GetData();
|
||||
int32_t channelStride = channel0Map.GetStride();
|
||||
uint8_t* channel0Data = channel0Map.GetData();
|
||||
uint8_t* channel1Data = channel1Map.GetData();
|
||||
uint8_t* channel2Data = channel2Map.GetData();
|
||||
uint8_t* channel3Data = channel3Map.GetData();
|
||||
|
||||
if (Factory::HasSSE2()) {
|
||||
#ifdef USE_SSE2
|
||||
CombineColorChannels_SSE2(size, resultStride, resultData, channelStride, channel0Data, channel1Data, channel2Data, channel3Data);
|
||||
#endif
|
||||
} else {
|
||||
CombineColorChannels_Scalar(size, resultStride, resultData, channelStride, channel0Data, channel1Data, channel2Data, channel3Data);
|
||||
}
|
||||
|
||||
return result.forget();
|
||||
}
|
||||
|
||||
void
|
||||
FilterProcessing::DoPremultiplicationCalculation(const IntSize& aSize,
|
||||
uint8_t* aTargetData, int32_t aTargetStride,
|
||||
uint8_t* aSourceData, int32_t aSourceStride)
|
||||
{
|
||||
if (Factory::HasSSE2()) {
|
||||
#ifdef USE_SSE2
|
||||
DoPremultiplicationCalculation_SSE2(
|
||||
aSize, aTargetData, aTargetStride, aSourceData, aSourceStride);
|
||||
#endif
|
||||
} else {
|
||||
DoPremultiplicationCalculation_Scalar(
|
||||
aSize, aTargetData, aTargetStride, aSourceData, aSourceStride);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FilterProcessing::DoUnpremultiplicationCalculation(const IntSize& aSize,
|
||||
uint8_t* aTargetData, int32_t aTargetStride,
|
||||
uint8_t* aSourceData, int32_t aSourceStride)
|
||||
{
|
||||
if (Factory::HasSSE2()) {
|
||||
#ifdef USE_SSE2
|
||||
DoUnpremultiplicationCalculation_SSE2(
|
||||
aSize, aTargetData, aTargetStride, aSourceData, aSourceStride);
|
||||
#endif
|
||||
} else {
|
||||
DoUnpremultiplicationCalculation_Scalar(
|
||||
aSize, aTargetData, aTargetStride, aSourceData, aSourceStride);
|
||||
}
|
||||
}
|
||||
|
||||
already_AddRefed<DataSourceSurface>
|
||||
FilterProcessing::RenderTurbulence(const IntSize &aSize, const Point &aOffset, const Size &aBaseFrequency,
|
||||
int32_t aSeed, int aNumOctaves, TurbulenceType aType, bool aStitch, const Rect &aTileRect)
|
||||
{
|
||||
if (Factory::HasSSE2()) {
|
||||
#ifdef USE_SSE2
|
||||
return RenderTurbulence_SSE2(aSize, aOffset, aBaseFrequency, aSeed, aNumOctaves, aType, aStitch, aTileRect);
|
||||
#endif
|
||||
}
|
||||
return RenderTurbulence_Scalar(aSize, aOffset, aBaseFrequency, aSeed, aNumOctaves, aType, aStitch, aTileRect);
|
||||
}
|
||||
|
||||
already_AddRefed<DataSourceSurface>
|
||||
FilterProcessing::ApplyArithmeticCombine(DataSourceSurface* aInput1, DataSourceSurface* aInput2, Float aK1, Float aK2, Float aK3, Float aK4)
|
||||
{
|
||||
if (Factory::HasSSE2()) {
|
||||
#ifdef USE_SSE2
|
||||
return ApplyArithmeticCombine_SSE2(aInput1, aInput2, aK1, aK2, aK3, aK4);
|
||||
#endif
|
||||
}
|
||||
return ApplyArithmeticCombine_Scalar(aInput1, aInput2, aK1, aK2, aK3, aK4);
|
||||
}
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
||||
141
gfx/2d/FilterProcessing.h
Normal file
141
gfx/2d/FilterProcessing.h
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
/* -*- Mode: C++; tab-width: 20; 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_GFX_FILTERPROCESSING_H_
|
||||
#define _MOZILLA_GFX_FILTERPROCESSING_H_
|
||||
|
||||
#include "2D.h"
|
||||
#include "Filters.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
const ptrdiff_t B8G8R8A8_COMPONENT_BYTEOFFSET_B = 0;
|
||||
const ptrdiff_t B8G8R8A8_COMPONENT_BYTEOFFSET_G = 1;
|
||||
const ptrdiff_t B8G8R8A8_COMPONENT_BYTEOFFSET_R = 2;
|
||||
const ptrdiff_t B8G8R8A8_COMPONENT_BYTEOFFSET_A = 3;
|
||||
|
||||
class FilterProcessing
|
||||
{
|
||||
public:
|
||||
|
||||
// Fast approximate division by 255. It has the property that
|
||||
// for all 0 <= v <= 255*255, FastDivideBy255(v) == v/255.
|
||||
// But it only uses two adds and two shifts instead of an
|
||||
// integer division (which is expensive on many processors).
|
||||
template<class B, class A>
|
||||
static B FastDivideBy255(A v)
|
||||
{
|
||||
return ((v << 8) + v + 255) >> 16;
|
||||
}
|
||||
|
||||
static already_AddRefed<DataSourceSurface> ExtractAlpha(DataSourceSurface* aSource);
|
||||
static already_AddRefed<DataSourceSurface> ConvertToB8G8R8A8(SourceSurface* aSurface);
|
||||
static already_AddRefed<DataSourceSurface> ApplyBlending(DataSourceSurface* aInput1, DataSourceSurface* aInput2, BlendMode aBlendMode);
|
||||
static void ApplyMorphologyHorizontal(uint8_t* aSourceData, int32_t aSourceStride,
|
||||
uint8_t* aDestData, int32_t aDestStride,
|
||||
const IntRect& aDestRect, int32_t aRadius,
|
||||
MorphologyOperator aOperator);
|
||||
static void ApplyMorphologyVertical(uint8_t* aSourceData, int32_t aSourceStride,
|
||||
uint8_t* aDestData, int32_t aDestStride,
|
||||
const IntRect& aDestRect, int32_t aRadius,
|
||||
MorphologyOperator aOperator);
|
||||
static already_AddRefed<DataSourceSurface> ApplyColorMatrix(DataSourceSurface* aInput, const Matrix5x4 &aMatrix);
|
||||
static void ApplyComposition(DataSourceSurface* aSource, DataSourceSurface* aDest, CompositeOperator aOperator);
|
||||
static void SeparateColorChannels(DataSourceSurface* aSource,
|
||||
RefPtr<DataSourceSurface>& aChannel0,
|
||||
RefPtr<DataSourceSurface>& aChannel1,
|
||||
RefPtr<DataSourceSurface>& aChannel2,
|
||||
RefPtr<DataSourceSurface>& aChannel3);
|
||||
static already_AddRefed<DataSourceSurface>
|
||||
CombineColorChannels(DataSourceSurface* aChannel0, DataSourceSurface* aChannel1,
|
||||
DataSourceSurface* aChannel2, DataSourceSurface* aChannel3);
|
||||
static void DoPremultiplicationCalculation(const IntSize& aSize,
|
||||
uint8_t* aTargetData, int32_t aTargetStride,
|
||||
uint8_t* aSourceData, int32_t aSourceStride);
|
||||
static void DoUnpremultiplicationCalculation(const IntSize& aSize,
|
||||
uint8_t* aTargetData, int32_t aTargetStride,
|
||||
uint8_t* aSourceData, int32_t aSourceStride);
|
||||
static already_AddRefed<DataSourceSurface>
|
||||
RenderTurbulence(const IntSize &aSize, const Point &aOffset, const Size &aBaseFrequency,
|
||||
int32_t aSeed, int aNumOctaves, TurbulenceType aType, bool aStitch, const Rect &aTileRect);
|
||||
static already_AddRefed<DataSourceSurface>
|
||||
ApplyArithmeticCombine(DataSourceSurface* aInput1, DataSourceSurface* aInput2, Float aK1, Float aK2, Float aK3, Float aK4);
|
||||
|
||||
protected:
|
||||
static void ExtractAlpha_Scalar(const IntSize& size, uint8_t* sourceData, int32_t sourceStride, uint8_t* alphaData, int32_t alphaStride);
|
||||
static already_AddRefed<DataSourceSurface> ConvertToB8G8R8A8_Scalar(SourceSurface* aSurface);
|
||||
static void ApplyMorphologyHorizontal_Scalar(uint8_t* aSourceData, int32_t aSourceStride,
|
||||
uint8_t* aDestData, int32_t aDestStride,
|
||||
const IntRect& aDestRect, int32_t aRadius,
|
||||
MorphologyOperator aOperator);
|
||||
static void ApplyMorphologyVertical_Scalar(uint8_t* aSourceData, int32_t aSourceStride,
|
||||
uint8_t* aDestData, int32_t aDestStride,
|
||||
const IntRect& aDestRect, int32_t aRadius,
|
||||
MorphologyOperator aOperator);
|
||||
static already_AddRefed<DataSourceSurface> ApplyColorMatrix_Scalar(DataSourceSurface* aInput, const Matrix5x4 &aMatrix);
|
||||
static void ApplyComposition_Scalar(DataSourceSurface* aSource, DataSourceSurface* aDest, CompositeOperator aOperator);
|
||||
|
||||
static void SeparateColorChannels_Scalar(const IntSize &size, uint8_t* sourceData, int32_t sourceStride, uint8_t* channel0Data, uint8_t* channel1Data, uint8_t* channel2Data, uint8_t* channel3Data, int32_t channelStride);
|
||||
static void CombineColorChannels_Scalar(const IntSize &size, int32_t resultStride, uint8_t* resultData, int32_t channelStride, uint8_t* channel0Data, uint8_t* channel1Data, uint8_t* channel2Data, uint8_t* channel3Data);
|
||||
static void DoPremultiplicationCalculation_Scalar(const IntSize& aSize,
|
||||
uint8_t* aTargetData, int32_t aTargetStride,
|
||||
uint8_t* aSourceData, int32_t aSourceStride);
|
||||
static void DoUnpremultiplicationCalculation_Scalar(const IntSize& aSize,
|
||||
uint8_t* aTargetData, int32_t aTargetStride,
|
||||
uint8_t* aSourceData, int32_t aSourceStride);
|
||||
static already_AddRefed<DataSourceSurface>
|
||||
RenderTurbulence_Scalar(const IntSize &aSize, const Point &aOffset, const Size &aBaseFrequency,
|
||||
int32_t aSeed, int aNumOctaves, TurbulenceType aType, bool aStitch, const Rect &aTileRect);
|
||||
static already_AddRefed<DataSourceSurface>
|
||||
ApplyArithmeticCombine_Scalar(DataSourceSurface* aInput1, DataSourceSurface* aInput2, Float aK1, Float aK2, Float aK3, Float aK4);
|
||||
|
||||
#ifdef USE_SSE2
|
||||
static void ExtractAlpha_SSE2(const IntSize& size, uint8_t* sourceData, int32_t sourceStride, uint8_t* alphaData, int32_t alphaStride);
|
||||
static already_AddRefed<DataSourceSurface> ConvertToB8G8R8A8_SSE2(SourceSurface* aSurface);
|
||||
static already_AddRefed<DataSourceSurface> ApplyBlending_SSE2(DataSourceSurface* aInput1, DataSourceSurface* aInput2, BlendMode aBlendMode);
|
||||
static void ApplyMorphologyHorizontal_SSE2(uint8_t* aSourceData, int32_t aSourceStride,
|
||||
uint8_t* aDestData, int32_t aDestStride,
|
||||
const IntRect& aDestRect, int32_t aRadius,
|
||||
MorphologyOperator aOperator);
|
||||
static void ApplyMorphologyVertical_SSE2(uint8_t* aSourceData, int32_t aSourceStride,
|
||||
uint8_t* aDestData, int32_t aDestStride,
|
||||
const IntRect& aDestRect, int32_t aRadius,
|
||||
MorphologyOperator aOperator);
|
||||
static already_AddRefed<DataSourceSurface> ApplyColorMatrix_SSE2(DataSourceSurface* aInput, const Matrix5x4 &aMatrix);
|
||||
static void ApplyComposition_SSE2(DataSourceSurface* aSource, DataSourceSurface* aDest, CompositeOperator aOperator);
|
||||
static void SeparateColorChannels_SSE2(const IntSize &size, uint8_t* sourceData, int32_t sourceStride, uint8_t* channel0Data, uint8_t* channel1Data, uint8_t* channel2Data, uint8_t* channel3Data, int32_t channelStride);
|
||||
static void CombineColorChannels_SSE2(const IntSize &size, int32_t resultStride, uint8_t* resultData, int32_t channelStride, uint8_t* channel0Data, uint8_t* channel1Data, uint8_t* channel2Data, uint8_t* channel3Data);
|
||||
static void DoPremultiplicationCalculation_SSE2(const IntSize& aSize,
|
||||
uint8_t* aTargetData, int32_t aTargetStride,
|
||||
uint8_t* aSourceData, int32_t aSourceStride);
|
||||
static void DoUnpremultiplicationCalculation_SSE2(const IntSize& aSize,
|
||||
uint8_t* aTargetData, int32_t aTargetStride,
|
||||
uint8_t* aSourceData, int32_t aSourceStride);
|
||||
static already_AddRefed<DataSourceSurface>
|
||||
RenderTurbulence_SSE2(const IntSize &aSize, const Point &aOffset, const Size &aBaseFrequency,
|
||||
int32_t aSeed, int aNumOctaves, TurbulenceType aType, bool aStitch, const Rect &aTileRect);
|
||||
static already_AddRefed<DataSourceSurface>
|
||||
ApplyArithmeticCombine_SSE2(DataSourceSurface* aInput1, DataSourceSurface* aInput2, Float aK1, Float aK2, Float aK3, Float aK4);
|
||||
#endif
|
||||
};
|
||||
|
||||
// Constant-time max and min functions for unsigned arguments
|
||||
static inline unsigned
|
||||
umax(unsigned a, unsigned b)
|
||||
{
|
||||
return a - ((a - b) & -(a < b));
|
||||
}
|
||||
|
||||
static inline unsigned
|
||||
umin(unsigned a, unsigned b)
|
||||
{
|
||||
return a - ((a - b) & -(a > b));
|
||||
}
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // _MOZILLA_GFX_FILTERPROCESSING_H_
|
||||
1081
gfx/2d/FilterProcessingSIMD-inl.h
Normal file
1081
gfx/2d/FilterProcessingSIMD-inl.h
Normal file
File diff suppressed because it is too large
Load diff
112
gfx/2d/FilterProcessingSSE2.cpp
Normal file
112
gfx/2d/FilterProcessingSSE2.cpp
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
/* -*- Mode: C++; tab-width: 20; 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/. */
|
||||
|
||||
#define SIMD_COMPILE_SSE2
|
||||
|
||||
#include "FilterProcessingSIMD-inl.h"
|
||||
|
||||
#ifndef USE_SSE2
|
||||
static_assert(false, "If this file is built, FilterProcessing.h should know about it!");
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
void
|
||||
FilterProcessing::ExtractAlpha_SSE2(const IntSize& size, uint8_t* sourceData, int32_t sourceStride, uint8_t* alphaData, int32_t alphaStride)
|
||||
{
|
||||
ExtractAlpha_SIMD<__m128i>(size, sourceData, sourceStride, alphaData, alphaStride);
|
||||
}
|
||||
|
||||
already_AddRefed<DataSourceSurface>
|
||||
FilterProcessing::ConvertToB8G8R8A8_SSE2(SourceSurface* aSurface)
|
||||
{
|
||||
return ConvertToB8G8R8A8_SIMD<__m128i>(aSurface);
|
||||
}
|
||||
|
||||
already_AddRefed<DataSourceSurface>
|
||||
FilterProcessing::ApplyBlending_SSE2(DataSourceSurface* aInput1, DataSourceSurface* aInput2,
|
||||
BlendMode aBlendMode)
|
||||
{
|
||||
return ApplyBlending_SIMD<__m128i,__m128i,__m128i>(aInput1, aInput2, aBlendMode);
|
||||
}
|
||||
|
||||
void
|
||||
FilterProcessing::ApplyMorphologyHorizontal_SSE2(uint8_t* aSourceData, int32_t aSourceStride,
|
||||
uint8_t* aDestData, int32_t aDestStride,
|
||||
const IntRect& aDestRect, int32_t aRadius,
|
||||
MorphologyOperator aOp)
|
||||
{
|
||||
ApplyMorphologyHorizontal_SIMD<__m128i,__m128i>(
|
||||
aSourceData, aSourceStride, aDestData, aDestStride, aDestRect, aRadius, aOp);
|
||||
}
|
||||
|
||||
void
|
||||
FilterProcessing::ApplyMorphologyVertical_SSE2(uint8_t* aSourceData, int32_t aSourceStride,
|
||||
uint8_t* aDestData, int32_t aDestStride,
|
||||
const IntRect& aDestRect, int32_t aRadius,
|
||||
MorphologyOperator aOp)
|
||||
{
|
||||
ApplyMorphologyVertical_SIMD<__m128i,__m128i>(
|
||||
aSourceData, aSourceStride, aDestData, aDestStride, aDestRect, aRadius, aOp);
|
||||
}
|
||||
|
||||
already_AddRefed<DataSourceSurface>
|
||||
FilterProcessing::ApplyColorMatrix_SSE2(DataSourceSurface* aInput, const Matrix5x4 &aMatrix)
|
||||
{
|
||||
return ApplyColorMatrix_SIMD<__m128i,__m128i,__m128i>(aInput, aMatrix);
|
||||
}
|
||||
|
||||
void
|
||||
FilterProcessing::ApplyComposition_SSE2(DataSourceSurface* aSource, DataSourceSurface* aDest,
|
||||
CompositeOperator aOperator)
|
||||
{
|
||||
return ApplyComposition_SIMD<__m128i,__m128i,__m128i>(aSource, aDest, aOperator);
|
||||
}
|
||||
|
||||
void
|
||||
FilterProcessing::SeparateColorChannels_SSE2(const IntSize &size, uint8_t* sourceData, int32_t sourceStride, uint8_t* channel0Data, uint8_t* channel1Data, uint8_t* channel2Data, uint8_t* channel3Data, int32_t channelStride)
|
||||
{
|
||||
SeparateColorChannels_SIMD<__m128i>(size, sourceData, sourceStride, channel0Data, channel1Data, channel2Data, channel3Data, channelStride);
|
||||
}
|
||||
|
||||
void
|
||||
FilterProcessing::CombineColorChannels_SSE2(const IntSize &size, int32_t resultStride, uint8_t* resultData, int32_t channelStride, uint8_t* channel0Data, uint8_t* channel1Data, uint8_t* channel2Data, uint8_t* channel3Data)
|
||||
{
|
||||
CombineColorChannels_SIMD<__m128i>(size, resultStride, resultData, channelStride, channel0Data, channel1Data, channel2Data, channel3Data);
|
||||
}
|
||||
|
||||
void
|
||||
FilterProcessing::DoPremultiplicationCalculation_SSE2(const IntSize& aSize,
|
||||
uint8_t* aTargetData, int32_t aTargetStride,
|
||||
uint8_t* aSourceData, int32_t aSourceStride)
|
||||
{
|
||||
DoPremultiplicationCalculation_SIMD<__m128i,__m128i,__m128i>(aSize, aTargetData, aTargetStride, aSourceData, aSourceStride);
|
||||
}
|
||||
|
||||
void
|
||||
FilterProcessing::DoUnpremultiplicationCalculation_SSE2(
|
||||
const IntSize& aSize,
|
||||
uint8_t* aTargetData, int32_t aTargetStride,
|
||||
uint8_t* aSourceData, int32_t aSourceStride)
|
||||
{
|
||||
DoUnpremultiplicationCalculation_SIMD<__m128i,__m128i>(aSize, aTargetData, aTargetStride, aSourceData, aSourceStride);
|
||||
}
|
||||
|
||||
already_AddRefed<DataSourceSurface>
|
||||
FilterProcessing::RenderTurbulence_SSE2(const IntSize &aSize, const Point &aOffset, const Size &aBaseFrequency,
|
||||
int32_t aSeed, int aNumOctaves, TurbulenceType aType, bool aStitch, const Rect &aTileRect)
|
||||
{
|
||||
return RenderTurbulence_SIMD<__m128,__m128i,__m128i>(aSize, aOffset, aBaseFrequency, aSeed, aNumOctaves, aType, aStitch, aTileRect);
|
||||
}
|
||||
|
||||
already_AddRefed<DataSourceSurface>
|
||||
FilterProcessing::ApplyArithmeticCombine_SSE2(DataSourceSurface* aInput1, DataSourceSurface* aInput2, Float aK1, Float aK2, Float aK3, Float aK4)
|
||||
{
|
||||
return ApplyArithmeticCombine_SIMD<__m128i,__m128i,__m128i>(aInput1, aInput2, aK1, aK2, aK3, aK4);
|
||||
}
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
||||
244
gfx/2d/FilterProcessingScalar.cpp
Normal file
244
gfx/2d/FilterProcessingScalar.cpp
Normal file
|
|
@ -0,0 +1,244 @@
|
|||
/* -*- Mode: C++; tab-width: 20; 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/. */
|
||||
|
||||
#define FILTER_PROCESSING_SCALAR
|
||||
|
||||
#include "FilterProcessingSIMD-inl.h"
|
||||
#include "Logging.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
void
|
||||
FilterProcessing::ExtractAlpha_Scalar(const IntSize& size, uint8_t* sourceData, int32_t sourceStride, uint8_t* alphaData, int32_t alphaStride)
|
||||
{
|
||||
for (int32_t y = 0; y < size.height; y++) {
|
||||
for (int32_t x = 0; x < size.width; x++) {
|
||||
int32_t sourceIndex = y * sourceStride + 4 * x;
|
||||
int32_t targetIndex = y * alphaStride + x;
|
||||
alphaData[targetIndex] = sourceData[sourceIndex + B8G8R8A8_COMPONENT_BYTEOFFSET_A];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
already_AddRefed<DataSourceSurface>
|
||||
FilterProcessing::ConvertToB8G8R8A8_Scalar(SourceSurface* aSurface)
|
||||
{
|
||||
return ConvertToB8G8R8A8_SIMD<simd::Scalaru8x16_t>(aSurface);
|
||||
}
|
||||
|
||||
template<MorphologyOperator Operator>
|
||||
static void
|
||||
ApplyMorphologyHorizontal_Scalar(uint8_t* aSourceData, int32_t aSourceStride,
|
||||
uint8_t* aDestData, int32_t aDestStride,
|
||||
const IntRect& aDestRect, int32_t aRadius)
|
||||
{
|
||||
static_assert(Operator == MORPHOLOGY_OPERATOR_ERODE ||
|
||||
Operator == MORPHOLOGY_OPERATOR_DILATE,
|
||||
"unexpected morphology operator");
|
||||
|
||||
for (int32_t y = aDestRect.y; y < aDestRect.YMost(); y++) {
|
||||
int32_t startX = aDestRect.x - aRadius;
|
||||
int32_t endX = aDestRect.x + aRadius;
|
||||
for (int32_t x = aDestRect.x; x < aDestRect.XMost(); x++, startX++, endX++) {
|
||||
int32_t sourceIndex = y * aSourceStride + 4 * startX;
|
||||
uint8_t u[4];
|
||||
for (size_t i = 0; i < 4; i++) {
|
||||
u[i] = aSourceData[sourceIndex + i];
|
||||
}
|
||||
sourceIndex += 4;
|
||||
for (int32_t ix = startX + 1; ix <= endX; ix++, sourceIndex += 4) {
|
||||
for (size_t i = 0; i < 4; i++) {
|
||||
if (Operator == MORPHOLOGY_OPERATOR_ERODE) {
|
||||
u[i] = umin(u[i], aSourceData[sourceIndex + i]);
|
||||
} else {
|
||||
u[i] = umax(u[i], aSourceData[sourceIndex + i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int32_t destIndex = y * aDestStride + 4 * x;
|
||||
for (size_t i = 0; i < 4; i++) {
|
||||
aDestData[destIndex+i] = u[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FilterProcessing::ApplyMorphologyHorizontal_Scalar(uint8_t* aSourceData, int32_t aSourceStride,
|
||||
uint8_t* aDestData, int32_t aDestStride,
|
||||
const IntRect& aDestRect, int32_t aRadius,
|
||||
MorphologyOperator aOp)
|
||||
{
|
||||
if (aOp == MORPHOLOGY_OPERATOR_ERODE) {
|
||||
gfx::ApplyMorphologyHorizontal_Scalar<MORPHOLOGY_OPERATOR_ERODE>(
|
||||
aSourceData, aSourceStride, aDestData, aDestStride, aDestRect, aRadius);
|
||||
} else {
|
||||
gfx::ApplyMorphologyHorizontal_Scalar<MORPHOLOGY_OPERATOR_DILATE>(
|
||||
aSourceData, aSourceStride, aDestData, aDestStride, aDestRect, aRadius);
|
||||
}
|
||||
}
|
||||
|
||||
template<MorphologyOperator Operator>
|
||||
static void ApplyMorphologyVertical_Scalar(uint8_t* aSourceData, int32_t aSourceStride,
|
||||
uint8_t* aDestData, int32_t aDestStride,
|
||||
const IntRect& aDestRect, int32_t aRadius)
|
||||
{
|
||||
static_assert(Operator == MORPHOLOGY_OPERATOR_ERODE ||
|
||||
Operator == MORPHOLOGY_OPERATOR_DILATE,
|
||||
"unexpected morphology operator");
|
||||
|
||||
int32_t startY = aDestRect.y - aRadius;
|
||||
int32_t endY = aDestRect.y + aRadius;
|
||||
for (int32_t y = aDestRect.y; y < aDestRect.YMost(); y++, startY++, endY++) {
|
||||
for (int32_t x = aDestRect.x; x < aDestRect.XMost(); x++) {
|
||||
int32_t sourceIndex = startY * aSourceStride + 4 * x;
|
||||
uint8_t u[4];
|
||||
for (size_t i = 0; i < 4; i++) {
|
||||
u[i] = aSourceData[sourceIndex + i];
|
||||
}
|
||||
sourceIndex += aSourceStride;
|
||||
for (int32_t iy = startY + 1; iy <= endY; iy++, sourceIndex += aSourceStride) {
|
||||
for (size_t i = 0; i < 4; i++) {
|
||||
if (Operator == MORPHOLOGY_OPERATOR_ERODE) {
|
||||
u[i] = umin(u[i], aSourceData[sourceIndex + i]);
|
||||
} else {
|
||||
u[i] = umax(u[i], aSourceData[sourceIndex + i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int32_t destIndex = y * aDestStride + 4 * x;
|
||||
for (size_t i = 0; i < 4; i++) {
|
||||
aDestData[destIndex+i] = u[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FilterProcessing::ApplyMorphologyVertical_Scalar(uint8_t* aSourceData, int32_t aSourceStride,
|
||||
uint8_t* aDestData, int32_t aDestStride,
|
||||
const IntRect& aDestRect, int32_t aRadius,
|
||||
MorphologyOperator aOp)
|
||||
{
|
||||
if (aOp == MORPHOLOGY_OPERATOR_ERODE) {
|
||||
gfx::ApplyMorphologyVertical_Scalar<MORPHOLOGY_OPERATOR_ERODE>(
|
||||
aSourceData, aSourceStride, aDestData, aDestStride, aDestRect, aRadius);
|
||||
} else {
|
||||
gfx::ApplyMorphologyVertical_Scalar<MORPHOLOGY_OPERATOR_DILATE>(
|
||||
aSourceData, aSourceStride, aDestData, aDestStride, aDestRect, aRadius);
|
||||
}
|
||||
}
|
||||
|
||||
already_AddRefed<DataSourceSurface>
|
||||
FilterProcessing::ApplyColorMatrix_Scalar(DataSourceSurface* aInput, const Matrix5x4 &aMatrix)
|
||||
{
|
||||
return ApplyColorMatrix_SIMD<simd::Scalari32x4_t,simd::Scalari16x8_t,simd::Scalaru8x16_t>(aInput, aMatrix);
|
||||
}
|
||||
|
||||
void
|
||||
FilterProcessing::ApplyComposition_Scalar(DataSourceSurface* aSource, DataSourceSurface* aDest,
|
||||
CompositeOperator aOperator)
|
||||
{
|
||||
return ApplyComposition_SIMD<simd::Scalari32x4_t,simd::Scalaru16x8_t,simd::Scalaru8x16_t>(aSource, aDest, aOperator);
|
||||
}
|
||||
|
||||
void
|
||||
FilterProcessing::SeparateColorChannels_Scalar(const IntSize &size, uint8_t* sourceData, int32_t sourceStride, uint8_t* channel0Data, uint8_t* channel1Data, uint8_t* channel2Data, uint8_t* channel3Data, int32_t channelStride)
|
||||
{
|
||||
for (int32_t y = 0; y < size.height; y++) {
|
||||
for (int32_t x = 0; x < size.width; x++) {
|
||||
int32_t sourceIndex = y * sourceStride + 4 * x;
|
||||
int32_t targetIndex = y * channelStride + x;
|
||||
channel0Data[targetIndex] = sourceData[sourceIndex];
|
||||
channel1Data[targetIndex] = sourceData[sourceIndex+1];
|
||||
channel2Data[targetIndex] = sourceData[sourceIndex+2];
|
||||
channel3Data[targetIndex] = sourceData[sourceIndex+3];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FilterProcessing::CombineColorChannels_Scalar(const IntSize &size, int32_t resultStride, uint8_t* resultData, int32_t channelStride, uint8_t* channel0Data, uint8_t* channel1Data, uint8_t* channel2Data, uint8_t* channel3Data)
|
||||
{
|
||||
for (int32_t y = 0; y < size.height; y++) {
|
||||
for (int32_t x = 0; x < size.width; x++) {
|
||||
int32_t resultIndex = y * resultStride + 4 * x;
|
||||
int32_t channelIndex = y * channelStride + x;
|
||||
resultData[resultIndex] = channel0Data[channelIndex];
|
||||
resultData[resultIndex+1] = channel1Data[channelIndex];
|
||||
resultData[resultIndex+2] = channel2Data[channelIndex];
|
||||
resultData[resultIndex+3] = channel3Data[channelIndex];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FilterProcessing::DoPremultiplicationCalculation_Scalar(const IntSize& aSize,
|
||||
uint8_t* aTargetData, int32_t aTargetStride,
|
||||
uint8_t* aSourceData, int32_t aSourceStride)
|
||||
{
|
||||
for (int32_t y = 0; y < aSize.height; y++) {
|
||||
for (int32_t x = 0; x < aSize.width; x++) {
|
||||
int32_t inputIndex = y * aSourceStride + 4 * x;
|
||||
int32_t targetIndex = y * aTargetStride + 4 * x;
|
||||
uint8_t alpha = aSourceData[inputIndex + B8G8R8A8_COMPONENT_BYTEOFFSET_A];
|
||||
aTargetData[targetIndex + B8G8R8A8_COMPONENT_BYTEOFFSET_R] =
|
||||
FastDivideBy255<uint8_t>(aSourceData[inputIndex + B8G8R8A8_COMPONENT_BYTEOFFSET_R] * alpha);
|
||||
aTargetData[targetIndex + B8G8R8A8_COMPONENT_BYTEOFFSET_G] =
|
||||
FastDivideBy255<uint8_t>(aSourceData[inputIndex + B8G8R8A8_COMPONENT_BYTEOFFSET_G] * alpha);
|
||||
aTargetData[targetIndex + B8G8R8A8_COMPONENT_BYTEOFFSET_B] =
|
||||
FastDivideBy255<uint8_t>(aSourceData[inputIndex + B8G8R8A8_COMPONENT_BYTEOFFSET_B] * alpha);
|
||||
aTargetData[targetIndex + B8G8R8A8_COMPONENT_BYTEOFFSET_A] = alpha;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FilterProcessing::DoUnpremultiplicationCalculation_Scalar(
|
||||
const IntSize& aSize,
|
||||
uint8_t* aTargetData, int32_t aTargetStride,
|
||||
uint8_t* aSourceData, int32_t aSourceStride)
|
||||
{
|
||||
for (int32_t y = 0; y < aSize.height; y++) {
|
||||
for (int32_t x = 0; x < aSize.width; x++) {
|
||||
int32_t inputIndex = y * aSourceStride + 4 * x;
|
||||
int32_t targetIndex = y * aTargetStride + 4 * x;
|
||||
uint8_t alpha = aSourceData[inputIndex + B8G8R8A8_COMPONENT_BYTEOFFSET_A];
|
||||
uint16_t alphaFactor = sAlphaFactors[alpha];
|
||||
// inputColor * alphaFactor + 128 is guaranteed to fit into uint16_t
|
||||
// because the input is premultiplied and thus inputColor <= inputAlpha.
|
||||
// The maximum value this can attain is 65520 (which is less than 65535)
|
||||
// for color == alpha == 244:
|
||||
// 244 * sAlphaFactors[244] + 128 == 244 * 268 + 128 == 65520
|
||||
aTargetData[targetIndex + B8G8R8A8_COMPONENT_BYTEOFFSET_R] =
|
||||
(aSourceData[inputIndex + B8G8R8A8_COMPONENT_BYTEOFFSET_R] * alphaFactor + 128) >> 8;
|
||||
aTargetData[targetIndex + B8G8R8A8_COMPONENT_BYTEOFFSET_G] =
|
||||
(aSourceData[inputIndex + B8G8R8A8_COMPONENT_BYTEOFFSET_G] * alphaFactor + 128) >> 8;
|
||||
aTargetData[targetIndex + B8G8R8A8_COMPONENT_BYTEOFFSET_B] =
|
||||
(aSourceData[inputIndex + B8G8R8A8_COMPONENT_BYTEOFFSET_B] * alphaFactor + 128) >> 8;
|
||||
aTargetData[targetIndex + B8G8R8A8_COMPONENT_BYTEOFFSET_A] = alpha;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
already_AddRefed<DataSourceSurface>
|
||||
FilterProcessing::RenderTurbulence_Scalar(const IntSize &aSize, const Point &aOffset, const Size &aBaseFrequency,
|
||||
int32_t aSeed, int aNumOctaves, TurbulenceType aType, bool aStitch, const Rect &aTileRect)
|
||||
{
|
||||
return RenderTurbulence_SIMD<simd::Scalarf32x4_t,simd::Scalari32x4_t,simd::Scalaru8x16_t>(
|
||||
aSize, aOffset, aBaseFrequency, aSeed, aNumOctaves, aType, aStitch, aTileRect);
|
||||
}
|
||||
|
||||
already_AddRefed<DataSourceSurface>
|
||||
FilterProcessing::ApplyArithmeticCombine_Scalar(DataSourceSurface* aInput1, DataSourceSurface* aInput2, Float aK1, Float aK2, Float aK3, Float aK4)
|
||||
{
|
||||
return ApplyArithmeticCombine_SIMD<simd::Scalari32x4_t,simd::Scalari16x8_t,simd::Scalaru8x16_t>(aInput1, aInput2, aK1, aK2, aK3, aK4);
|
||||
}
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
||||
512
gfx/2d/Filters.h
Normal file
512
gfx/2d/Filters.h
Normal file
|
|
@ -0,0 +1,512 @@
|
|||
/* -*- Mode: C++; tab-width: 20; 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_GFX_FILTERS_H_
|
||||
#define MOZILLA_GFX_FILTERS_H_
|
||||
|
||||
#include "Types.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
|
||||
#include "Point.h"
|
||||
#include "Matrix.h"
|
||||
#include <vector>
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
class SourceSurface;
|
||||
|
||||
enum FilterBackend {
|
||||
FILTER_BACKEND_SOFTWARE = 0,
|
||||
FILTER_BACKEND_DIRECT2D1_1,
|
||||
FILTER_BACKEND_RECORDING
|
||||
};
|
||||
|
||||
enum TransformFilterAtts
|
||||
{
|
||||
ATT_TRANSFORM_MATRIX = 0, // Matrix
|
||||
ATT_TRANSFORM_FILTER // Filter
|
||||
};
|
||||
|
||||
enum TransformFilterInputs
|
||||
{
|
||||
IN_TRANSFORM_IN = 0
|
||||
};
|
||||
|
||||
enum BlendFilterAtts
|
||||
{
|
||||
ATT_BLEND_BLENDMODE = 0 // uint32_t
|
||||
};
|
||||
|
||||
enum BlendMode
|
||||
{
|
||||
BLEND_MODE_MULTIPLY = 0,
|
||||
BLEND_MODE_SCREEN,
|
||||
BLEND_MODE_DARKEN,
|
||||
BLEND_MODE_LIGHTEN,
|
||||
BLEND_MODE_OVERLAY,
|
||||
BLEND_MODE_COLOR_DODGE,
|
||||
BLEND_MODE_COLOR_BURN,
|
||||
BLEND_MODE_HARD_LIGHT,
|
||||
BLEND_MODE_SOFT_LIGHT,
|
||||
BLEND_MODE_DIFFERENCE,
|
||||
BLEND_MODE_EXCLUSION,
|
||||
BLEND_MODE_HUE,
|
||||
BLEND_MODE_SATURATION,
|
||||
BLEND_MODE_COLOR,
|
||||
BLEND_MODE_LUMINOSITY
|
||||
};
|
||||
|
||||
enum BlendFilterInputs
|
||||
{
|
||||
IN_BLEND_IN = 0,
|
||||
IN_BLEND_IN2
|
||||
};
|
||||
|
||||
enum MorphologyFilterAtts
|
||||
{
|
||||
ATT_MORPHOLOGY_RADII = 0, // IntSize
|
||||
ATT_MORPHOLOGY_OPERATOR // MorphologyOperator
|
||||
};
|
||||
|
||||
enum MorphologyOperator
|
||||
{
|
||||
MORPHOLOGY_OPERATOR_ERODE = 0,
|
||||
MORPHOLOGY_OPERATOR_DILATE
|
||||
};
|
||||
|
||||
enum MorphologyFilterInputs
|
||||
{
|
||||
IN_MORPHOLOGY_IN = 0
|
||||
};
|
||||
|
||||
enum AlphaMode
|
||||
{
|
||||
ALPHA_MODE_PREMULTIPLIED = 0,
|
||||
ALPHA_MODE_STRAIGHT
|
||||
};
|
||||
|
||||
enum ColorMatrixFilterAtts
|
||||
{
|
||||
ATT_COLOR_MATRIX_MATRIX = 0, // Matrix5x4
|
||||
ATT_COLOR_MATRIX_ALPHA_MODE // AlphaMode
|
||||
};
|
||||
|
||||
enum ColorMatrixFilterInputs
|
||||
{
|
||||
IN_COLOR_MATRIX_IN = 0
|
||||
};
|
||||
|
||||
enum FloodFilterAtts
|
||||
{
|
||||
ATT_FLOOD_COLOR = 0 // Color
|
||||
};
|
||||
|
||||
enum FloodFilterInputs
|
||||
{
|
||||
IN_FLOOD_IN = 0
|
||||
};
|
||||
|
||||
enum TileFilterAtts
|
||||
{
|
||||
ATT_TILE_SOURCE_RECT = 0 // IntRect
|
||||
};
|
||||
|
||||
enum TileFilterInputs
|
||||
{
|
||||
IN_TILE_IN = 0
|
||||
};
|
||||
|
||||
enum TransferAtts
|
||||
{
|
||||
ATT_TRANSFER_DISABLE_R = 0, // bool
|
||||
ATT_TRANSFER_DISABLE_G, // bool
|
||||
ATT_TRANSFER_DISABLE_B, // bool
|
||||
ATT_TRANSFER_DISABLE_A // bool
|
||||
};
|
||||
|
||||
enum TransferInputs
|
||||
{
|
||||
IN_TRANSFER_IN = 0
|
||||
};
|
||||
|
||||
enum TableTransferAtts
|
||||
{
|
||||
ATT_TABLE_TRANSFER_DISABLE_R = ATT_TRANSFER_DISABLE_R,
|
||||
ATT_TABLE_TRANSFER_DISABLE_G = ATT_TRANSFER_DISABLE_G,
|
||||
ATT_TABLE_TRANSFER_DISABLE_B = ATT_TRANSFER_DISABLE_B,
|
||||
ATT_TABLE_TRANSFER_DISABLE_A = ATT_TRANSFER_DISABLE_A,
|
||||
ATT_TABLE_TRANSFER_TABLE_R, // Float[]
|
||||
ATT_TABLE_TRANSFER_TABLE_G, // Float[]
|
||||
ATT_TABLE_TRANSFER_TABLE_B, // Float[]
|
||||
ATT_TABLE_TRANSFER_TABLE_A // Float[]
|
||||
};
|
||||
|
||||
enum TableTransferInputs
|
||||
{
|
||||
IN_TABLE_TRANSFER_IN = IN_TRANSFER_IN
|
||||
};
|
||||
|
||||
enum DiscreteTransferAtts
|
||||
{
|
||||
ATT_DISCRETE_TRANSFER_DISABLE_R = ATT_TRANSFER_DISABLE_R,
|
||||
ATT_DISCRETE_TRANSFER_DISABLE_G = ATT_TRANSFER_DISABLE_G,
|
||||
ATT_DISCRETE_TRANSFER_DISABLE_B = ATT_TRANSFER_DISABLE_B,
|
||||
ATT_DISCRETE_TRANSFER_DISABLE_A = ATT_TRANSFER_DISABLE_A,
|
||||
ATT_DISCRETE_TRANSFER_TABLE_R, // Float[]
|
||||
ATT_DISCRETE_TRANSFER_TABLE_G, // Float[]
|
||||
ATT_DISCRETE_TRANSFER_TABLE_B, // Float[]
|
||||
ATT_DISCRETE_TRANSFER_TABLE_A // Float[]
|
||||
};
|
||||
|
||||
enum DiscreteTransferInputs
|
||||
{
|
||||
IN_DISCRETE_TRANSFER_IN = IN_TRANSFER_IN
|
||||
};
|
||||
|
||||
enum LinearTransferAtts
|
||||
{
|
||||
ATT_LINEAR_TRANSFER_DISABLE_R = ATT_TRANSFER_DISABLE_R,
|
||||
ATT_LINEAR_TRANSFER_DISABLE_G = ATT_TRANSFER_DISABLE_G,
|
||||
ATT_LINEAR_TRANSFER_DISABLE_B = ATT_TRANSFER_DISABLE_B,
|
||||
ATT_LINEAR_TRANSFER_DISABLE_A = ATT_TRANSFER_DISABLE_A,
|
||||
ATT_LINEAR_TRANSFER_SLOPE_R, // Float
|
||||
ATT_LINEAR_TRANSFER_SLOPE_G, // Float
|
||||
ATT_LINEAR_TRANSFER_SLOPE_B, // Float
|
||||
ATT_LINEAR_TRANSFER_SLOPE_A, // Float
|
||||
ATT_LINEAR_TRANSFER_INTERCEPT_R, // Float
|
||||
ATT_LINEAR_TRANSFER_INTERCEPT_G, // Float
|
||||
ATT_LINEAR_TRANSFER_INTERCEPT_B, // Float
|
||||
ATT_LINEAR_TRANSFER_INTERCEPT_A // Float
|
||||
};
|
||||
|
||||
enum LinearTransferInputs
|
||||
{
|
||||
IN_LINEAR_TRANSFER_IN = IN_TRANSFER_IN
|
||||
};
|
||||
|
||||
enum GammaTransferAtts
|
||||
{
|
||||
ATT_GAMMA_TRANSFER_DISABLE_R = ATT_TRANSFER_DISABLE_R,
|
||||
ATT_GAMMA_TRANSFER_DISABLE_G = ATT_TRANSFER_DISABLE_G,
|
||||
ATT_GAMMA_TRANSFER_DISABLE_B = ATT_TRANSFER_DISABLE_B,
|
||||
ATT_GAMMA_TRANSFER_DISABLE_A = ATT_TRANSFER_DISABLE_A,
|
||||
ATT_GAMMA_TRANSFER_AMPLITUDE_R, // Float
|
||||
ATT_GAMMA_TRANSFER_AMPLITUDE_G, // Float
|
||||
ATT_GAMMA_TRANSFER_AMPLITUDE_B, // Float
|
||||
ATT_GAMMA_TRANSFER_AMPLITUDE_A, // Float
|
||||
ATT_GAMMA_TRANSFER_EXPONENT_R, // Float
|
||||
ATT_GAMMA_TRANSFER_EXPONENT_G, // Float
|
||||
ATT_GAMMA_TRANSFER_EXPONENT_B, // Float
|
||||
ATT_GAMMA_TRANSFER_EXPONENT_A, // Float
|
||||
ATT_GAMMA_TRANSFER_OFFSET_R, // Float
|
||||
ATT_GAMMA_TRANSFER_OFFSET_G, // Float
|
||||
ATT_GAMMA_TRANSFER_OFFSET_B, // Float
|
||||
ATT_GAMMA_TRANSFER_OFFSET_A // Float
|
||||
};
|
||||
|
||||
enum GammaTransferInputs
|
||||
{
|
||||
IN_GAMMA_TRANSFER_IN = IN_TRANSFER_IN
|
||||
};
|
||||
|
||||
enum ConvolveMatrixAtts
|
||||
{
|
||||
ATT_CONVOLVE_MATRIX_KERNEL_SIZE = 0, // IntSize
|
||||
ATT_CONVOLVE_MATRIX_KERNEL_MATRIX, // Float[]
|
||||
ATT_CONVOLVE_MATRIX_DIVISOR, // Float
|
||||
ATT_CONVOLVE_MATRIX_BIAS, // Float
|
||||
ATT_CONVOLVE_MATRIX_TARGET, // IntPoint
|
||||
ATT_CONVOLVE_MATRIX_SOURCE_RECT, // IntRect
|
||||
ATT_CONVOLVE_MATRIX_EDGE_MODE, // ConvolveMatrixEdgeMode
|
||||
ATT_CONVOLVE_MATRIX_KERNEL_UNIT_LENGTH, // Size
|
||||
ATT_CONVOLVE_MATRIX_PRESERVE_ALPHA, // bool
|
||||
};
|
||||
|
||||
enum ConvolveMatrixEdgeMode
|
||||
{
|
||||
EDGE_MODE_DUPLICATE = 0,
|
||||
EDGE_MODE_WRAP,
|
||||
EDGE_MODE_NONE
|
||||
};
|
||||
|
||||
enum ConvolveMatrixInputs
|
||||
{
|
||||
IN_CONVOLVE_MATRIX_IN = 0
|
||||
};
|
||||
|
||||
enum DisplacementMapAtts
|
||||
{
|
||||
ATT_DISPLACEMENT_MAP_SCALE = 0, // Float
|
||||
ATT_DISPLACEMENT_MAP_X_CHANNEL, // ColorChannel
|
||||
ATT_DISPLACEMENT_MAP_Y_CHANNEL // ColorChannel
|
||||
};
|
||||
|
||||
enum ColorChannel
|
||||
{
|
||||
COLOR_CHANNEL_R = 0,
|
||||
COLOR_CHANNEL_G,
|
||||
COLOR_CHANNEL_B,
|
||||
COLOR_CHANNEL_A
|
||||
};
|
||||
|
||||
enum DisplacementMapInputs
|
||||
{
|
||||
IN_DISPLACEMENT_MAP_IN = 0,
|
||||
IN_DISPLACEMENT_MAP_IN2
|
||||
};
|
||||
|
||||
enum TurbulenceAtts
|
||||
{
|
||||
ATT_TURBULENCE_BASE_FREQUENCY = 0, // Size
|
||||
ATT_TURBULENCE_NUM_OCTAVES, // uint32_t
|
||||
ATT_TURBULENCE_SEED, // uint32_t
|
||||
ATT_TURBULENCE_STITCHABLE, // bool
|
||||
ATT_TURBULENCE_TYPE, // TurbulenceType
|
||||
ATT_TURBULENCE_RECT // IntRect
|
||||
};
|
||||
|
||||
enum TurbulenceType
|
||||
{
|
||||
TURBULENCE_TYPE_TURBULENCE = 0,
|
||||
TURBULENCE_TYPE_FRACTAL_NOISE
|
||||
};
|
||||
|
||||
enum ArithmeticCombineAtts
|
||||
{
|
||||
ATT_ARITHMETIC_COMBINE_COEFFICIENTS = 0 // Float[4]
|
||||
};
|
||||
|
||||
enum ArithmeticCombineInputs
|
||||
{
|
||||
IN_ARITHMETIC_COMBINE_IN = 0,
|
||||
IN_ARITHMETIC_COMBINE_IN2
|
||||
};
|
||||
|
||||
enum CompositeAtts
|
||||
{
|
||||
ATT_COMPOSITE_OPERATOR = 0 // CompositeOperator
|
||||
};
|
||||
|
||||
enum CompositeOperator
|
||||
{
|
||||
COMPOSITE_OPERATOR_OVER = 0,
|
||||
COMPOSITE_OPERATOR_IN,
|
||||
COMPOSITE_OPERATOR_OUT,
|
||||
COMPOSITE_OPERATOR_ATOP,
|
||||
COMPOSITE_OPERATOR_XOR
|
||||
};
|
||||
|
||||
enum CompositeInputs
|
||||
{
|
||||
// arbitrary number of inputs
|
||||
IN_COMPOSITE_IN_START = 0
|
||||
};
|
||||
|
||||
enum GaussianBlurAtts
|
||||
{
|
||||
ATT_GAUSSIAN_BLUR_STD_DEVIATION = 0 // Float
|
||||
};
|
||||
|
||||
enum GaussianBlurInputs
|
||||
{
|
||||
IN_GAUSSIAN_BLUR_IN = 0
|
||||
};
|
||||
|
||||
enum DirectionalBlurAtts
|
||||
{
|
||||
ATT_DIRECTIONAL_BLUR_STD_DEVIATION = 0, // Float
|
||||
ATT_DIRECTIONAL_BLUR_DIRECTION // BlurDirection
|
||||
};
|
||||
|
||||
enum BlurDirection
|
||||
{
|
||||
BLUR_DIRECTION_X = 0,
|
||||
BLUR_DIRECTION_Y
|
||||
};
|
||||
|
||||
enum DirectionalBlurInputs
|
||||
{
|
||||
IN_DIRECTIONAL_BLUR_IN = 0
|
||||
};
|
||||
|
||||
enum LightingAtts
|
||||
{
|
||||
ATT_POINT_LIGHT_POSITION = 0, // Point3D
|
||||
|
||||
ATT_SPOT_LIGHT_POSITION, // Point3D
|
||||
ATT_SPOT_LIGHT_POINTS_AT, // Point3D
|
||||
ATT_SPOT_LIGHT_FOCUS, // Float
|
||||
ATT_SPOT_LIGHT_LIMITING_CONE_ANGLE, // Float
|
||||
|
||||
ATT_DISTANT_LIGHT_AZIMUTH, // Float
|
||||
ATT_DISTANT_LIGHT_ELEVATION, // Float
|
||||
|
||||
ATT_LIGHTING_COLOR, // Color
|
||||
ATT_LIGHTING_SURFACE_SCALE, // Float
|
||||
ATT_LIGHTING_KERNEL_UNIT_LENGTH, // Size
|
||||
|
||||
ATT_DIFFUSE_LIGHTING_DIFFUSE_CONSTANT, // Float
|
||||
|
||||
ATT_SPECULAR_LIGHTING_SPECULAR_CONSTANT, // Float
|
||||
ATT_SPECULAR_LIGHTING_SPECULAR_EXPONENT // Float
|
||||
};
|
||||
|
||||
enum LightingInputs
|
||||
{
|
||||
IN_LIGHTING_IN = 0
|
||||
};
|
||||
|
||||
enum PointDiffuseAtts
|
||||
{
|
||||
ATT_POINT_DIFFUSE_POSITION = ATT_POINT_LIGHT_POSITION,
|
||||
ATT_POINT_DIFFUSE_COLOR = ATT_LIGHTING_COLOR,
|
||||
ATT_POINT_DIFFUSE_SURFACE_SCALE = ATT_LIGHTING_SURFACE_SCALE,
|
||||
ATT_POINT_DIFFUSE_KERNEL_UNIT_LENGTH = ATT_LIGHTING_KERNEL_UNIT_LENGTH,
|
||||
ATT_POINT_DIFFUSE_DIFFUSE_CONSTANT = ATT_DIFFUSE_LIGHTING_DIFFUSE_CONSTANT
|
||||
};
|
||||
|
||||
enum PointDiffuseInputs
|
||||
{
|
||||
IN_POINT_DIFFUSE_IN = IN_LIGHTING_IN
|
||||
};
|
||||
|
||||
enum SpotDiffuseAtts
|
||||
{
|
||||
ATT_SPOT_DIFFUSE_POSITION = ATT_SPOT_LIGHT_POSITION,
|
||||
ATT_SPOT_DIFFUSE_POINTS_AT = ATT_SPOT_LIGHT_POINTS_AT,
|
||||
ATT_SPOT_DIFFUSE_FOCUS = ATT_SPOT_LIGHT_FOCUS,
|
||||
ATT_SPOT_DIFFUSE_LIMITING_CONE_ANGLE = ATT_SPOT_LIGHT_LIMITING_CONE_ANGLE,
|
||||
ATT_SPOT_DIFFUSE_COLOR = ATT_LIGHTING_COLOR,
|
||||
ATT_SPOT_DIFFUSE_SURFACE_SCALE = ATT_LIGHTING_SURFACE_SCALE,
|
||||
ATT_SPOT_DIFFUSE_KERNEL_UNIT_LENGTH = ATT_LIGHTING_KERNEL_UNIT_LENGTH,
|
||||
ATT_SPOT_DIFFUSE_DIFFUSE_CONSTANT = ATT_DIFFUSE_LIGHTING_DIFFUSE_CONSTANT
|
||||
};
|
||||
|
||||
enum SpotDiffuseInputs
|
||||
{
|
||||
IN_SPOT_DIFFUSE_IN = IN_LIGHTING_IN
|
||||
};
|
||||
|
||||
enum DistantDiffuseAtts
|
||||
{
|
||||
ATT_DISTANT_DIFFUSE_AZIMUTH = ATT_DISTANT_LIGHT_AZIMUTH,
|
||||
ATT_DISTANT_DIFFUSE_ELEVATION = ATT_DISTANT_LIGHT_ELEVATION,
|
||||
ATT_DISTANT_DIFFUSE_COLOR = ATT_LIGHTING_COLOR,
|
||||
ATT_DISTANT_DIFFUSE_SURFACE_SCALE = ATT_LIGHTING_SURFACE_SCALE,
|
||||
ATT_DISTANT_DIFFUSE_KERNEL_UNIT_LENGTH = ATT_LIGHTING_KERNEL_UNIT_LENGTH,
|
||||
ATT_DISTANT_DIFFUSE_DIFFUSE_CONSTANT = ATT_DIFFUSE_LIGHTING_DIFFUSE_CONSTANT
|
||||
};
|
||||
|
||||
enum DistantDiffuseInputs
|
||||
{
|
||||
IN_DISTANT_DIFFUSE_IN = IN_LIGHTING_IN
|
||||
};
|
||||
|
||||
enum PointSpecularAtts
|
||||
{
|
||||
ATT_POINT_SPECULAR_POSITION = ATT_POINT_LIGHT_POSITION,
|
||||
ATT_POINT_SPECULAR_COLOR = ATT_LIGHTING_COLOR,
|
||||
ATT_POINT_SPECULAR_SURFACE_SCALE = ATT_LIGHTING_SURFACE_SCALE,
|
||||
ATT_POINT_SPECULAR_KERNEL_UNIT_LENGTH = ATT_LIGHTING_KERNEL_UNIT_LENGTH,
|
||||
ATT_POINT_SPECULAR_SPECULAR_CONSTANT = ATT_SPECULAR_LIGHTING_SPECULAR_CONSTANT,
|
||||
ATT_POINT_SPECULAR_SPECULAR_EXPONENT = ATT_SPECULAR_LIGHTING_SPECULAR_EXPONENT
|
||||
};
|
||||
|
||||
enum PointSpecularInputs
|
||||
{
|
||||
IN_POINT_SPECULAR_IN = IN_LIGHTING_IN
|
||||
};
|
||||
|
||||
enum SpotSpecularAtts
|
||||
{
|
||||
ATT_SPOT_SPECULAR_POSITION = ATT_SPOT_LIGHT_POSITION,
|
||||
ATT_SPOT_SPECULAR_POINTS_AT = ATT_SPOT_LIGHT_POINTS_AT,
|
||||
ATT_SPOT_SPECULAR_FOCUS = ATT_SPOT_LIGHT_FOCUS,
|
||||
ATT_SPOT_SPECULAR_LIMITING_CONE_ANGLE = ATT_SPOT_LIGHT_LIMITING_CONE_ANGLE,
|
||||
ATT_SPOT_SPECULAR_COLOR = ATT_LIGHTING_COLOR,
|
||||
ATT_SPOT_SPECULAR_SURFACE_SCALE = ATT_LIGHTING_SURFACE_SCALE,
|
||||
ATT_SPOT_SPECULAR_KERNEL_UNIT_LENGTH = ATT_LIGHTING_KERNEL_UNIT_LENGTH,
|
||||
ATT_SPOT_SPECULAR_SPECULAR_CONSTANT = ATT_SPECULAR_LIGHTING_SPECULAR_CONSTANT,
|
||||
ATT_SPOT_SPECULAR_SPECULAR_EXPONENT = ATT_SPECULAR_LIGHTING_SPECULAR_EXPONENT
|
||||
};
|
||||
|
||||
enum SpotSpecularInputs
|
||||
{
|
||||
IN_SPOT_SPECULAR_IN = IN_LIGHTING_IN
|
||||
};
|
||||
|
||||
enum DistantSpecularAtts
|
||||
{
|
||||
ATT_DISTANT_SPECULAR_AZIMUTH = ATT_DISTANT_LIGHT_AZIMUTH,
|
||||
ATT_DISTANT_SPECULAR_ELEVATION = ATT_DISTANT_LIGHT_ELEVATION,
|
||||
ATT_DISTANT_SPECULAR_COLOR = ATT_LIGHTING_COLOR,
|
||||
ATT_DISTANT_SPECULAR_SURFACE_SCALE = ATT_LIGHTING_SURFACE_SCALE,
|
||||
ATT_DISTANT_SPECULAR_KERNEL_UNIT_LENGTH = ATT_LIGHTING_KERNEL_UNIT_LENGTH,
|
||||
ATT_DISTANT_SPECULAR_SPECULAR_CONSTANT = ATT_SPECULAR_LIGHTING_SPECULAR_CONSTANT,
|
||||
ATT_DISTANT_SPECULAR_SPECULAR_EXPONENT = ATT_SPECULAR_LIGHTING_SPECULAR_EXPONENT
|
||||
};
|
||||
|
||||
enum DistantSpecularInputs
|
||||
{
|
||||
IN_DISTANT_SPECULAR_IN = IN_LIGHTING_IN
|
||||
};
|
||||
|
||||
enum CropAtts
|
||||
{
|
||||
ATT_CROP_RECT = 0 // Rect
|
||||
};
|
||||
|
||||
enum CropInputs
|
||||
{
|
||||
IN_CROP_IN = 0
|
||||
};
|
||||
|
||||
enum PremultiplyInputs
|
||||
{
|
||||
IN_PREMULTIPLY_IN = 0
|
||||
};
|
||||
|
||||
enum UnpremultiplyInputs
|
||||
{
|
||||
IN_UNPREMULTIPLY_IN = 0
|
||||
};
|
||||
|
||||
class FilterNode : public RefCounted<FilterNode>
|
||||
{
|
||||
public:
|
||||
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNode)
|
||||
virtual ~FilterNode() {}
|
||||
|
||||
virtual FilterBackend GetBackendType() = 0;
|
||||
|
||||
virtual void SetInput(uint32_t aIndex, SourceSurface *aSurface) { MOZ_CRASH("GFX: FilterNode"); }
|
||||
virtual void SetInput(uint32_t aIndex, FilterNode *aFilter) { MOZ_CRASH("GFX: FilterNode"); }
|
||||
|
||||
virtual void SetAttribute(uint32_t aIndex, bool) { MOZ_CRASH("GFX: FilterNode"); }
|
||||
virtual void SetAttribute(uint32_t aIndex, uint32_t) { MOZ_CRASH("GFX: FilterNode"); }
|
||||
virtual void SetAttribute(uint32_t aIndex, Float) { MOZ_CRASH("GFX: FilterNode"); }
|
||||
virtual void SetAttribute(uint32_t aIndex, const Size &) { MOZ_CRASH("GFX: FilterNode"); }
|
||||
virtual void SetAttribute(uint32_t aIndex, const IntSize &) { MOZ_CRASH("GFX: FilterNode"); }
|
||||
virtual void SetAttribute(uint32_t aIndex, const IntPoint &) { MOZ_CRASH("GFX: FilterNode"); }
|
||||
virtual void SetAttribute(uint32_t aIndex, const Rect &) { MOZ_CRASH("GFX: FilterNode"); }
|
||||
virtual void SetAttribute(uint32_t aIndex, const IntRect &) { MOZ_CRASH("GFX: FilterNode"); }
|
||||
virtual void SetAttribute(uint32_t aIndex, const Point &) { MOZ_CRASH("GFX: FilterNode"); }
|
||||
virtual void SetAttribute(uint32_t aIndex, const Matrix &) { MOZ_CRASH("GFX: FilterNode"); }
|
||||
virtual void SetAttribute(uint32_t aIndex, const Matrix5x4 &) { MOZ_CRASH("GFX: FilterNode"); }
|
||||
virtual void SetAttribute(uint32_t aIndex, const Point3D &) { MOZ_CRASH("GFX: FilterNode"); }
|
||||
virtual void SetAttribute(uint32_t aIndex, const Color &) { MOZ_CRASH("GFX: FilterNode"); }
|
||||
virtual void SetAttribute(uint32_t aIndex, const Float* aFloat, uint32_t aSize) { MOZ_CRASH("GFX: FilterNode"); }
|
||||
|
||||
protected:
|
||||
friend class Factory;
|
||||
|
||||
FilterNode() {}
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
132
gfx/2d/GenericRefCounted.h
Normal file
132
gfx/2d/GenericRefCounted.h
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
/* -*- Mode: C++; tab-width: 20; 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/. */
|
||||
|
||||
// This header provides virtual, non-templated alternatives to MFBT's RefCounted<T>.
|
||||
// It intentionally uses MFBT coding style with the intention of moving there
|
||||
// should there be other use cases for it.
|
||||
|
||||
#ifndef MOZILLA_GENERICREFCOUNTED_H_
|
||||
#define MOZILLA_GENERICREFCOUNTED_H_
|
||||
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "mozilla/RefCounted.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
/**
|
||||
* Common base class for GenericRefCounted and GenericAtomicRefCounted.
|
||||
*
|
||||
* Having this shared base class, common to both the atomic and non-atomic
|
||||
* cases, allows to have RefPtr's that don't care about whether the
|
||||
* objects they're managing have atomic refcounts or not.
|
||||
*/
|
||||
class GenericRefCountedBase
|
||||
{
|
||||
protected:
|
||||
virtual ~GenericRefCountedBase() {};
|
||||
|
||||
public:
|
||||
// AddRef() and Release() method names are for compatibility with nsRefPtr.
|
||||
virtual void AddRef() = 0;
|
||||
|
||||
virtual void Release() = 0;
|
||||
|
||||
// ref() and deref() method names are for compatibility with wtf::RefPtr.
|
||||
// No virtual keywords here: if a subclass wants to override the refcounting
|
||||
// mechanism, it is welcome to do so by overriding AddRef() and Release().
|
||||
void ref() { AddRef(); }
|
||||
void deref() { Release(); }
|
||||
|
||||
#ifdef MOZ_REFCOUNTED_LEAK_CHECKING
|
||||
virtual const char* typeName() const = 0;
|
||||
virtual size_t typeSize() const = 0;
|
||||
#endif
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<RefCountAtomicity Atomicity>
|
||||
class GenericRefCounted : public GenericRefCountedBase
|
||||
{
|
||||
protected:
|
||||
GenericRefCounted() : refCnt(0) { }
|
||||
|
||||
virtual ~GenericRefCounted() {
|
||||
MOZ_ASSERT(refCnt == detail::DEAD);
|
||||
}
|
||||
|
||||
public:
|
||||
virtual void AddRef() override {
|
||||
// Note: this method must be thread safe for GenericAtomicRefCounted.
|
||||
MOZ_ASSERT(int32_t(refCnt) >= 0);
|
||||
#ifndef MOZ_REFCOUNTED_LEAK_CHECKING
|
||||
++refCnt;
|
||||
#else
|
||||
const char* type = typeName();
|
||||
uint32_t size = typeSize();
|
||||
const void* ptr = this;
|
||||
MozRefCountType cnt = ++refCnt;
|
||||
detail::RefCountLogger::logAddRef(ptr, cnt, type, size);
|
||||
#endif
|
||||
}
|
||||
|
||||
virtual void Release() override {
|
||||
// Note: this method must be thread safe for GenericAtomicRefCounted.
|
||||
MOZ_ASSERT(int32_t(refCnt) > 0);
|
||||
#ifndef MOZ_REFCOUNTED_LEAK_CHECKING
|
||||
MozRefCountType cnt = --refCnt;
|
||||
#else
|
||||
const char* type = typeName();
|
||||
const void* ptr = this;
|
||||
MozRefCountType cnt = --refCnt;
|
||||
// Note: it's not safe to touch |this| after decrementing the refcount,
|
||||
// except for below.
|
||||
detail::RefCountLogger::logRelease(ptr, cnt, type);
|
||||
#endif
|
||||
if (0 == cnt) {
|
||||
// Because we have atomically decremented the refcount above, only
|
||||
// one thread can get a 0 count here, so as long as we can assume that
|
||||
// everything else in the system is accessing this object through
|
||||
// RefPtrs, it's safe to access |this| here.
|
||||
#ifdef DEBUG
|
||||
refCnt = detail::DEAD;
|
||||
#endif
|
||||
delete this;
|
||||
}
|
||||
}
|
||||
|
||||
MozRefCountType refCount() const { return refCnt; }
|
||||
bool hasOneRef() const {
|
||||
MOZ_ASSERT(refCnt > 0);
|
||||
return refCnt == 1;
|
||||
}
|
||||
|
||||
private:
|
||||
typename Conditional<Atomicity == AtomicRefCount, Atomic<MozRefCountType>, MozRefCountType>::Type refCnt;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
* This reference-counting base class is virtual instead of
|
||||
* being templated, which is useful in cases where one needs
|
||||
* genericity at binary code level, but comes at the cost
|
||||
* of a moderate performance and size overhead, like anything virtual.
|
||||
*/
|
||||
class GenericRefCounted : public detail::GenericRefCounted<detail::NonAtomicRefCount>
|
||||
{
|
||||
};
|
||||
|
||||
/**
|
||||
* GenericAtomicRefCounted is like GenericRefCounted, with an atomically updated
|
||||
* reference counter.
|
||||
*/
|
||||
class GenericAtomicRefCounted : public detail::GenericRefCounted<detail::AtomicRefCount>
|
||||
{
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
40
gfx/2d/GradientStopsD2D.h
Normal file
40
gfx/2d/GradientStopsD2D.h
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
/* -*- Mode: C++; tab-width: 20; 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_GFX_GRADIENTSTOPSD2D_H_
|
||||
#define MOZILLA_GFX_GRADIENTSTOPSD2D_H_
|
||||
|
||||
#include "2D.h"
|
||||
|
||||
#include <d2d1.h>
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
class GradientStopsD2D : public GradientStops
|
||||
{
|
||||
public:
|
||||
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GradientStopsD2D)
|
||||
GradientStopsD2D(ID2D1GradientStopCollection *aStopCollection, ID3D11Device *aDevice)
|
||||
: mStopCollection(aStopCollection)
|
||||
, mDevice(aDevice)
|
||||
{}
|
||||
|
||||
virtual BackendType GetBackendType() const { return BackendType::DIRECT2D; }
|
||||
|
||||
virtual bool IsValid() const final{ return mDevice == Factory::GetDirect3D11Device(); }
|
||||
|
||||
private:
|
||||
friend class DrawTargetD2D;
|
||||
friend class DrawTargetD2D1;
|
||||
|
||||
mutable RefPtr<ID2D1GradientStopCollection> mStopCollection;
|
||||
RefPtr<ID3D11Device> mDevice;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* MOZILLA_GFX_GRADIENTSTOPSD2D_H_ */
|
||||
97
gfx/2d/Helpers.h
Normal file
97
gfx/2d/Helpers.h
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
/* -*- Mode: C++; tab-width: 20; 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_GFX_2D_HELPERS_H_
|
||||
#define MOZILLA_GFX_2D_HELPERS_H_
|
||||
|
||||
#include "2D.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
class AutoRestoreTransform
|
||||
{
|
||||
public:
|
||||
AutoRestoreTransform()
|
||||
{
|
||||
}
|
||||
|
||||
explicit AutoRestoreTransform(DrawTarget *aTarget)
|
||||
: mDrawTarget(aTarget),
|
||||
mOldTransform(aTarget->GetTransform())
|
||||
{
|
||||
}
|
||||
|
||||
void Init(DrawTarget *aTarget)
|
||||
{
|
||||
MOZ_ASSERT(!mDrawTarget || aTarget == mDrawTarget);
|
||||
if (!mDrawTarget) {
|
||||
mDrawTarget = aTarget;
|
||||
mOldTransform = aTarget->GetTransform();
|
||||
}
|
||||
}
|
||||
|
||||
~AutoRestoreTransform()
|
||||
{
|
||||
if (mDrawTarget) {
|
||||
mDrawTarget->SetTransform(mOldTransform);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
RefPtr<DrawTarget> mDrawTarget;
|
||||
Matrix mOldTransform;
|
||||
};
|
||||
|
||||
class AutoPopClips
|
||||
{
|
||||
public:
|
||||
explicit AutoPopClips(DrawTarget *aTarget)
|
||||
: mDrawTarget(aTarget)
|
||||
, mPushCount(0)
|
||||
{
|
||||
MOZ_ASSERT(mDrawTarget);
|
||||
}
|
||||
|
||||
~AutoPopClips()
|
||||
{
|
||||
PopAll();
|
||||
}
|
||||
|
||||
void PushClip(const Path *aPath)
|
||||
{
|
||||
mDrawTarget->PushClip(aPath);
|
||||
++mPushCount;
|
||||
}
|
||||
|
||||
void PushClipRect(const Rect &aRect)
|
||||
{
|
||||
mDrawTarget->PushClipRect(aRect);
|
||||
++mPushCount;
|
||||
}
|
||||
|
||||
void PopClip()
|
||||
{
|
||||
MOZ_ASSERT(mPushCount > 0);
|
||||
mDrawTarget->PopClip();
|
||||
--mPushCount;
|
||||
}
|
||||
|
||||
void PopAll()
|
||||
{
|
||||
while (mPushCount-- > 0) {
|
||||
mDrawTarget->PopClip();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
RefPtr<DrawTarget> mDrawTarget;
|
||||
int32_t mPushCount;
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // MOZILLA_GFX_2D_HELPERS_H_
|
||||
352
gfx/2d/HelpersCairo.h
Normal file
352
gfx/2d/HelpersCairo.h
Normal file
|
|
@ -0,0 +1,352 @@
|
|||
/* -*- Mode: C++; tab-width: 20; 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_GFX_HELPERSCAIRO_H_
|
||||
#define MOZILLA_GFX_HELPERSCAIRO_H_
|
||||
|
||||
#include "2D.h"
|
||||
#include "cairo.h"
|
||||
#include "Logging.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
static inline cairo_operator_t
|
||||
GfxOpToCairoOp(CompositionOp op)
|
||||
{
|
||||
switch (op)
|
||||
{
|
||||
case CompositionOp::OP_OVER:
|
||||
return CAIRO_OPERATOR_OVER;
|
||||
case CompositionOp::OP_ADD:
|
||||
return CAIRO_OPERATOR_ADD;
|
||||
case CompositionOp::OP_ATOP:
|
||||
return CAIRO_OPERATOR_ATOP;
|
||||
case CompositionOp::OP_OUT:
|
||||
return CAIRO_OPERATOR_OUT;
|
||||
case CompositionOp::OP_IN:
|
||||
return CAIRO_OPERATOR_IN;
|
||||
case CompositionOp::OP_SOURCE:
|
||||
return CAIRO_OPERATOR_SOURCE;
|
||||
case CompositionOp::OP_DEST_IN:
|
||||
return CAIRO_OPERATOR_DEST_IN;
|
||||
case CompositionOp::OP_DEST_OUT:
|
||||
return CAIRO_OPERATOR_DEST_OUT;
|
||||
case CompositionOp::OP_DEST_OVER:
|
||||
return CAIRO_OPERATOR_DEST_OVER;
|
||||
case CompositionOp::OP_DEST_ATOP:
|
||||
return CAIRO_OPERATOR_DEST_ATOP;
|
||||
case CompositionOp::OP_XOR:
|
||||
return CAIRO_OPERATOR_XOR;
|
||||
case CompositionOp::OP_MULTIPLY:
|
||||
return CAIRO_OPERATOR_MULTIPLY;
|
||||
case CompositionOp::OP_SCREEN:
|
||||
return CAIRO_OPERATOR_SCREEN;
|
||||
case CompositionOp::OP_OVERLAY:
|
||||
return CAIRO_OPERATOR_OVERLAY;
|
||||
case CompositionOp::OP_DARKEN:
|
||||
return CAIRO_OPERATOR_DARKEN;
|
||||
case CompositionOp::OP_LIGHTEN:
|
||||
return CAIRO_OPERATOR_LIGHTEN;
|
||||
case CompositionOp::OP_COLOR_DODGE:
|
||||
return CAIRO_OPERATOR_COLOR_DODGE;
|
||||
case CompositionOp::OP_COLOR_BURN:
|
||||
return CAIRO_OPERATOR_COLOR_BURN;
|
||||
case CompositionOp::OP_HARD_LIGHT:
|
||||
return CAIRO_OPERATOR_HARD_LIGHT;
|
||||
case CompositionOp::OP_SOFT_LIGHT:
|
||||
return CAIRO_OPERATOR_SOFT_LIGHT;
|
||||
case CompositionOp::OP_DIFFERENCE:
|
||||
return CAIRO_OPERATOR_DIFFERENCE;
|
||||
case CompositionOp::OP_EXCLUSION:
|
||||
return CAIRO_OPERATOR_EXCLUSION;
|
||||
case CompositionOp::OP_HUE:
|
||||
return CAIRO_OPERATOR_HSL_HUE;
|
||||
case CompositionOp::OP_SATURATION:
|
||||
return CAIRO_OPERATOR_HSL_SATURATION;
|
||||
case CompositionOp::OP_COLOR:
|
||||
return CAIRO_OPERATOR_HSL_COLOR;
|
||||
case CompositionOp::OP_LUMINOSITY:
|
||||
return CAIRO_OPERATOR_HSL_LUMINOSITY;
|
||||
case CompositionOp::OP_COUNT:
|
||||
break;
|
||||
}
|
||||
|
||||
return CAIRO_OPERATOR_OVER;
|
||||
}
|
||||
|
||||
static inline cairo_antialias_t
|
||||
GfxAntialiasToCairoAntialias(AntialiasMode antialias)
|
||||
{
|
||||
switch (antialias)
|
||||
{
|
||||
case AntialiasMode::NONE:
|
||||
return CAIRO_ANTIALIAS_NONE;
|
||||
case AntialiasMode::GRAY:
|
||||
return CAIRO_ANTIALIAS_GRAY;
|
||||
case AntialiasMode::SUBPIXEL:
|
||||
return CAIRO_ANTIALIAS_SUBPIXEL;
|
||||
default:
|
||||
return CAIRO_ANTIALIAS_DEFAULT;
|
||||
}
|
||||
}
|
||||
|
||||
static inline AntialiasMode
|
||||
CairoAntialiasToGfxAntialias(cairo_antialias_t aAntialias)
|
||||
{
|
||||
switch(aAntialias) {
|
||||
case CAIRO_ANTIALIAS_NONE:
|
||||
return AntialiasMode::NONE;
|
||||
case CAIRO_ANTIALIAS_GRAY:
|
||||
return AntialiasMode::GRAY;
|
||||
case CAIRO_ANTIALIAS_SUBPIXEL:
|
||||
return AntialiasMode::SUBPIXEL;
|
||||
default:
|
||||
return AntialiasMode::DEFAULT;
|
||||
}
|
||||
}
|
||||
|
||||
static inline cairo_filter_t
|
||||
GfxSamplingFilterToCairoFilter(SamplingFilter filter)
|
||||
{
|
||||
switch (filter)
|
||||
{
|
||||
case SamplingFilter::GOOD:
|
||||
return CAIRO_FILTER_GOOD;
|
||||
case SamplingFilter::LINEAR:
|
||||
return CAIRO_FILTER_BILINEAR;
|
||||
case SamplingFilter::POINT:
|
||||
return CAIRO_FILTER_NEAREST;
|
||||
default:
|
||||
MOZ_CRASH("GFX: bad Cairo filter");
|
||||
}
|
||||
|
||||
return CAIRO_FILTER_BILINEAR;
|
||||
}
|
||||
|
||||
static inline cairo_extend_t
|
||||
GfxExtendToCairoExtend(ExtendMode extend)
|
||||
{
|
||||
switch (extend)
|
||||
{
|
||||
case ExtendMode::CLAMP:
|
||||
return CAIRO_EXTEND_PAD;
|
||||
// Cairo doesn't support tiling in only 1 direction,
|
||||
// So we have to fallback and tile in both.
|
||||
case ExtendMode::REPEAT_X:
|
||||
case ExtendMode::REPEAT_Y:
|
||||
case ExtendMode::REPEAT:
|
||||
return CAIRO_EXTEND_REPEAT;
|
||||
case ExtendMode::REFLECT:
|
||||
return CAIRO_EXTEND_REFLECT;
|
||||
}
|
||||
|
||||
return CAIRO_EXTEND_PAD;
|
||||
}
|
||||
|
||||
static inline cairo_format_t
|
||||
GfxFormatToCairoFormat(SurfaceFormat format)
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
case SurfaceFormat::A8R8G8B8_UINT32:
|
||||
return CAIRO_FORMAT_ARGB32;
|
||||
case SurfaceFormat::X8R8G8B8_UINT32:
|
||||
return CAIRO_FORMAT_RGB24;
|
||||
case SurfaceFormat::A8:
|
||||
return CAIRO_FORMAT_A8;
|
||||
case SurfaceFormat::R5G6B5_UINT16:
|
||||
return CAIRO_FORMAT_RGB16_565;
|
||||
default:
|
||||
gfxCriticalError() << "Unknown image format " << (int)format;
|
||||
return CAIRO_FORMAT_ARGB32;
|
||||
}
|
||||
}
|
||||
|
||||
static inline cairo_content_t
|
||||
GfxFormatToCairoContent(SurfaceFormat format)
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
case SurfaceFormat::A8R8G8B8_UINT32:
|
||||
return CAIRO_CONTENT_COLOR_ALPHA;
|
||||
case SurfaceFormat::X8R8G8B8_UINT32:
|
||||
case SurfaceFormat::R5G6B5_UINT16: //fall through
|
||||
return CAIRO_CONTENT_COLOR;
|
||||
case SurfaceFormat::A8:
|
||||
return CAIRO_CONTENT_ALPHA;
|
||||
default:
|
||||
gfxCriticalError() << "Unknown image content format " << (int)format;
|
||||
return CAIRO_CONTENT_COLOR_ALPHA;
|
||||
}
|
||||
}
|
||||
|
||||
static inline cairo_line_join_t
|
||||
GfxLineJoinToCairoLineJoin(JoinStyle style)
|
||||
{
|
||||
switch (style)
|
||||
{
|
||||
case JoinStyle::BEVEL:
|
||||
return CAIRO_LINE_JOIN_BEVEL;
|
||||
case JoinStyle::ROUND:
|
||||
return CAIRO_LINE_JOIN_ROUND;
|
||||
case JoinStyle::MITER:
|
||||
return CAIRO_LINE_JOIN_MITER;
|
||||
case JoinStyle::MITER_OR_BEVEL:
|
||||
return CAIRO_LINE_JOIN_MITER;
|
||||
}
|
||||
|
||||
return CAIRO_LINE_JOIN_MITER;
|
||||
}
|
||||
|
||||
static inline cairo_line_cap_t
|
||||
GfxLineCapToCairoLineCap(CapStyle style)
|
||||
{
|
||||
switch (style)
|
||||
{
|
||||
case CapStyle::BUTT:
|
||||
return CAIRO_LINE_CAP_BUTT;
|
||||
case CapStyle::ROUND:
|
||||
return CAIRO_LINE_CAP_ROUND;
|
||||
case CapStyle::SQUARE:
|
||||
return CAIRO_LINE_CAP_SQUARE;
|
||||
}
|
||||
|
||||
return CAIRO_LINE_CAP_BUTT;
|
||||
}
|
||||
|
||||
static inline SurfaceFormat
|
||||
CairoContentToGfxFormat(cairo_content_t content)
|
||||
{
|
||||
switch (content)
|
||||
{
|
||||
case CAIRO_CONTENT_COLOR_ALPHA:
|
||||
return SurfaceFormat::A8R8G8B8_UINT32;
|
||||
case CAIRO_CONTENT_COLOR:
|
||||
// BEWARE! format may be 565
|
||||
return SurfaceFormat::X8R8G8B8_UINT32;
|
||||
case CAIRO_CONTENT_ALPHA:
|
||||
return SurfaceFormat::A8;
|
||||
}
|
||||
|
||||
return SurfaceFormat::B8G8R8A8;
|
||||
}
|
||||
|
||||
static inline SurfaceFormat
|
||||
CairoFormatToGfxFormat(cairo_format_t format)
|
||||
{
|
||||
switch (format) {
|
||||
case CAIRO_FORMAT_ARGB32:
|
||||
return SurfaceFormat::A8R8G8B8_UINT32;
|
||||
case CAIRO_FORMAT_RGB24:
|
||||
return SurfaceFormat::X8R8G8B8_UINT32;
|
||||
case CAIRO_FORMAT_A8:
|
||||
return SurfaceFormat::A8;
|
||||
case CAIRO_FORMAT_RGB16_565:
|
||||
return SurfaceFormat::R5G6B5_UINT16;
|
||||
default:
|
||||
gfxCriticalError() << "Unknown cairo format " << format;
|
||||
return SurfaceFormat::UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
static inline FontHinting
|
||||
CairoHintingToGfxHinting(cairo_hint_style_t aHintStyle)
|
||||
{
|
||||
switch (aHintStyle) {
|
||||
case CAIRO_HINT_STYLE_NONE:
|
||||
return FontHinting::NONE;
|
||||
case CAIRO_HINT_STYLE_SLIGHT:
|
||||
return FontHinting::LIGHT;
|
||||
case CAIRO_HINT_STYLE_MEDIUM:
|
||||
return FontHinting::NORMAL;
|
||||
case CAIRO_HINT_STYLE_FULL:
|
||||
return FontHinting::FULL;
|
||||
default:
|
||||
return FontHinting::NORMAL;
|
||||
}
|
||||
}
|
||||
|
||||
SurfaceFormat GfxFormatForCairoSurface(cairo_surface_t* surface);
|
||||
|
||||
static inline void
|
||||
GfxMatrixToCairoMatrix(const Matrix& mat, cairo_matrix_t& retval)
|
||||
{
|
||||
cairo_matrix_init(&retval, mat._11, mat._12, mat._21, mat._22, mat._31, mat._32);
|
||||
}
|
||||
|
||||
static inline void
|
||||
SetCairoStrokeOptions(cairo_t* aCtx, const StrokeOptions& aStrokeOptions)
|
||||
{
|
||||
cairo_set_line_width(aCtx, aStrokeOptions.mLineWidth);
|
||||
|
||||
cairo_set_miter_limit(aCtx, aStrokeOptions.mMiterLimit);
|
||||
|
||||
if (aStrokeOptions.mDashPattern) {
|
||||
// Convert array of floats to array of doubles
|
||||
std::vector<double> dashes(aStrokeOptions.mDashLength);
|
||||
bool nonZero = false;
|
||||
for (size_t i = 0; i < aStrokeOptions.mDashLength; ++i) {
|
||||
if (aStrokeOptions.mDashPattern[i] != 0) {
|
||||
nonZero = true;
|
||||
}
|
||||
dashes[i] = aStrokeOptions.mDashPattern[i];
|
||||
}
|
||||
// Avoid all-zero patterns that would trigger the CAIRO_STATUS_INVALID_DASH context error state.
|
||||
if (nonZero) {
|
||||
cairo_set_dash(aCtx, &dashes[0], aStrokeOptions.mDashLength,
|
||||
aStrokeOptions.mDashOffset);
|
||||
}
|
||||
}
|
||||
|
||||
cairo_set_line_join(aCtx, GfxLineJoinToCairoLineJoin(aStrokeOptions.mLineJoin));
|
||||
|
||||
cairo_set_line_cap(aCtx, GfxLineCapToCairoLineCap(aStrokeOptions.mLineCap));
|
||||
}
|
||||
|
||||
static inline cairo_fill_rule_t
|
||||
GfxFillRuleToCairoFillRule(FillRule rule)
|
||||
{
|
||||
switch (rule)
|
||||
{
|
||||
case FillRule::FILL_WINDING:
|
||||
return CAIRO_FILL_RULE_WINDING;
|
||||
case FillRule::FILL_EVEN_ODD:
|
||||
return CAIRO_FILL_RULE_EVEN_ODD;
|
||||
}
|
||||
|
||||
return CAIRO_FILL_RULE_WINDING;
|
||||
}
|
||||
|
||||
// RAII class for temporarily changing the cairo matrix transform. It will use
|
||||
// the given matrix transform while it is in scope. When it goes out of scope
|
||||
// it will put the cairo context back the way it was.
|
||||
|
||||
class CairoTempMatrix
|
||||
{
|
||||
public:
|
||||
CairoTempMatrix(cairo_t* aCtx, const Matrix& aMatrix)
|
||||
: mCtx(aCtx)
|
||||
{
|
||||
cairo_get_matrix(aCtx, &mSaveMatrix);
|
||||
cairo_matrix_t matrix;
|
||||
GfxMatrixToCairoMatrix(aMatrix, matrix);
|
||||
cairo_set_matrix(aCtx, &matrix);
|
||||
}
|
||||
|
||||
~CairoTempMatrix()
|
||||
{
|
||||
cairo_set_matrix(mCtx, &mSaveMatrix);
|
||||
}
|
||||
|
||||
private:
|
||||
cairo_t* mCtx;
|
||||
cairo_matrix_t mSaveMatrix;
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* MOZILLA_GFX_HELPERSCAIRO_H_ */
|
||||
967
gfx/2d/HelpersD2D.h
Normal file
967
gfx/2d/HelpersD2D.h
Normal file
|
|
@ -0,0 +1,967 @@
|
|||
/* -*- Mode: C++; tab-width: 20; 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_GFX_HELPERSD2D_H_
|
||||
#define MOZILLA_GFX_HELPERSD2D_H_
|
||||
|
||||
#include <d2d1_1.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <dwrite.h>
|
||||
#include <versionhelpers.h>
|
||||
#include "2D.h"
|
||||
#include "Logging.h"
|
||||
#include "Tools.h"
|
||||
#include "ImageScaling.h"
|
||||
|
||||
#include "ScaledFontDWrite.h"
|
||||
|
||||
#undef min
|
||||
#undef max
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
ID2D1Factory1* D2DFactory1();
|
||||
static ID2D1Factory* D2DFactory() { return D2DFactory1(); }
|
||||
|
||||
static inline D2D1_POINT_2F D2DPoint(const Point &aPoint)
|
||||
{
|
||||
return D2D1::Point2F(aPoint.x, aPoint.y);
|
||||
}
|
||||
|
||||
static inline D2D1_SIZE_U D2DIntSize(const IntSize &aSize)
|
||||
{
|
||||
return D2D1::SizeU(aSize.width, aSize.height);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline D2D1_RECT_F D2DRect(const T &aRect)
|
||||
{
|
||||
return D2D1::RectF(aRect.x, aRect.y, aRect.XMost(), aRect.YMost());
|
||||
}
|
||||
|
||||
static inline D2D1_EXTEND_MODE D2DExtend(ExtendMode aExtendMode, Axis aAxis)
|
||||
{
|
||||
D2D1_EXTEND_MODE extend;
|
||||
switch (aExtendMode) {
|
||||
case ExtendMode::REPEAT:
|
||||
extend = D2D1_EXTEND_MODE_WRAP;
|
||||
break;
|
||||
case ExtendMode::REPEAT_X:
|
||||
{
|
||||
extend = aAxis == Axis::X_AXIS
|
||||
? D2D1_EXTEND_MODE_WRAP
|
||||
: D2D1_EXTEND_MODE_CLAMP;
|
||||
break;
|
||||
}
|
||||
case ExtendMode::REPEAT_Y:
|
||||
{
|
||||
extend = aAxis == Axis::Y_AXIS
|
||||
? D2D1_EXTEND_MODE_WRAP
|
||||
: D2D1_EXTEND_MODE_CLAMP;
|
||||
break;
|
||||
}
|
||||
case ExtendMode::REFLECT:
|
||||
extend = D2D1_EXTEND_MODE_MIRROR;
|
||||
break;
|
||||
default:
|
||||
extend = D2D1_EXTEND_MODE_CLAMP;
|
||||
}
|
||||
|
||||
return extend;
|
||||
}
|
||||
|
||||
static inline D2D1_BITMAP_INTERPOLATION_MODE D2DFilter(const SamplingFilter aSamplingFilter)
|
||||
{
|
||||
switch (aSamplingFilter) {
|
||||
case SamplingFilter::POINT:
|
||||
return D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR;
|
||||
default:
|
||||
return D2D1_BITMAP_INTERPOLATION_MODE_LINEAR;
|
||||
}
|
||||
}
|
||||
|
||||
static inline D2D1_INTERPOLATION_MODE D2DInterpolationMode(const SamplingFilter aSamplingFilter)
|
||||
{
|
||||
switch (aSamplingFilter) {
|
||||
case SamplingFilter::POINT:
|
||||
return D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR;
|
||||
default:
|
||||
return D2D1_INTERPOLATION_MODE_LINEAR;
|
||||
}
|
||||
}
|
||||
|
||||
static inline D2D1_MATRIX_5X4_F D2DMatrix5x4(const Matrix5x4 &aMatrix)
|
||||
{
|
||||
return D2D1::Matrix5x4F(aMatrix._11, aMatrix._12, aMatrix._13, aMatrix._14,
|
||||
aMatrix._21, aMatrix._22, aMatrix._23, aMatrix._24,
|
||||
aMatrix._31, aMatrix._32, aMatrix._33, aMatrix._34,
|
||||
aMatrix._41, aMatrix._42, aMatrix._43, aMatrix._44,
|
||||
aMatrix._51, aMatrix._52, aMatrix._53, aMatrix._54);
|
||||
}
|
||||
|
||||
static inline D2D1_VECTOR_3F D2DVector3D(const Point3D &aPoint)
|
||||
{
|
||||
return D2D1::Vector3F(aPoint.x, aPoint.y, aPoint.z);
|
||||
}
|
||||
|
||||
static inline D2D1_ANTIALIAS_MODE D2DAAMode(AntialiasMode aMode)
|
||||
{
|
||||
switch (aMode) {
|
||||
case AntialiasMode::NONE:
|
||||
return D2D1_ANTIALIAS_MODE_ALIASED;
|
||||
default:
|
||||
return D2D1_ANTIALIAS_MODE_PER_PRIMITIVE;
|
||||
}
|
||||
}
|
||||
|
||||
static inline D2D1_MATRIX_3X2_F D2DMatrix(const Matrix &aTransform)
|
||||
{
|
||||
return D2D1::Matrix3x2F(aTransform._11, aTransform._12,
|
||||
aTransform._21, aTransform._22,
|
||||
aTransform._31, aTransform._32);
|
||||
}
|
||||
|
||||
static inline D2D1_COLOR_F D2DColor(const Color &aColor)
|
||||
{
|
||||
return D2D1::ColorF(aColor.r, aColor.g, aColor.b, aColor.a);
|
||||
}
|
||||
|
||||
static inline IntSize ToIntSize(const D2D1_SIZE_U &aSize)
|
||||
{
|
||||
return IntSize(aSize.width, aSize.height);
|
||||
}
|
||||
|
||||
static inline SurfaceFormat ToPixelFormat(const D2D1_PIXEL_FORMAT &aFormat)
|
||||
{
|
||||
switch(aFormat.format) {
|
||||
case DXGI_FORMAT_A8_UNORM:
|
||||
case DXGI_FORMAT_R8_UNORM:
|
||||
return SurfaceFormat::A8;
|
||||
case DXGI_FORMAT_B8G8R8A8_UNORM:
|
||||
if (aFormat.alphaMode == D2D1_ALPHA_MODE_IGNORE) {
|
||||
return SurfaceFormat::B8G8R8X8;
|
||||
} else {
|
||||
return SurfaceFormat::B8G8R8A8;
|
||||
}
|
||||
default:
|
||||
return SurfaceFormat::B8G8R8A8;
|
||||
}
|
||||
}
|
||||
|
||||
static inline Rect ToRect(const D2D1_RECT_F &aRect)
|
||||
{
|
||||
return Rect(aRect.left, aRect.top, aRect.right - aRect.left, aRect.bottom - aRect.top);
|
||||
}
|
||||
|
||||
static inline Matrix ToMatrix(const D2D1_MATRIX_3X2_F &aTransform)
|
||||
{
|
||||
return Matrix(aTransform._11, aTransform._12,
|
||||
aTransform._21, aTransform._22,
|
||||
aTransform._31, aTransform._32);
|
||||
}
|
||||
|
||||
static inline Point ToPoint(const D2D1_POINT_2F &aPoint)
|
||||
{
|
||||
return Point(aPoint.x, aPoint.y);
|
||||
}
|
||||
|
||||
static inline DXGI_FORMAT DXGIFormat(SurfaceFormat aFormat)
|
||||
{
|
||||
switch (aFormat) {
|
||||
case SurfaceFormat::B8G8R8A8:
|
||||
return DXGI_FORMAT_B8G8R8A8_UNORM;
|
||||
case SurfaceFormat::B8G8R8X8:
|
||||
return DXGI_FORMAT_B8G8R8A8_UNORM;
|
||||
case SurfaceFormat::A8:
|
||||
return DXGI_FORMAT_A8_UNORM;
|
||||
default:
|
||||
return DXGI_FORMAT_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
static inline D2D1_ALPHA_MODE D2DAlphaModeForFormat(SurfaceFormat aFormat)
|
||||
{
|
||||
switch (aFormat) {
|
||||
case SurfaceFormat::B8G8R8X8:
|
||||
return D2D1_ALPHA_MODE_IGNORE;
|
||||
default:
|
||||
return D2D1_ALPHA_MODE_PREMULTIPLIED;
|
||||
}
|
||||
}
|
||||
|
||||
static inline D2D1_PIXEL_FORMAT D2DPixelFormat(SurfaceFormat aFormat)
|
||||
{
|
||||
return D2D1::PixelFormat(DXGIFormat(aFormat), D2DAlphaModeForFormat(aFormat));
|
||||
}
|
||||
|
||||
static inline bool D2DSupportsCompositeMode(CompositionOp aOp)
|
||||
{
|
||||
switch(aOp) {
|
||||
case CompositionOp::OP_OVER:
|
||||
case CompositionOp::OP_ADD:
|
||||
case CompositionOp::OP_ATOP:
|
||||
case CompositionOp::OP_OUT:
|
||||
case CompositionOp::OP_IN:
|
||||
case CompositionOp::OP_SOURCE:
|
||||
case CompositionOp::OP_DEST_IN:
|
||||
case CompositionOp::OP_DEST_OUT:
|
||||
case CompositionOp::OP_DEST_OVER:
|
||||
case CompositionOp::OP_DEST_ATOP:
|
||||
case CompositionOp::OP_XOR:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static inline D2D1_COMPOSITE_MODE D2DCompositionMode(CompositionOp aOp)
|
||||
{
|
||||
switch(aOp) {
|
||||
case CompositionOp::OP_OVER:
|
||||
return D2D1_COMPOSITE_MODE_SOURCE_OVER;
|
||||
case CompositionOp::OP_ADD:
|
||||
return D2D1_COMPOSITE_MODE_PLUS;
|
||||
case CompositionOp::OP_ATOP:
|
||||
return D2D1_COMPOSITE_MODE_SOURCE_ATOP;
|
||||
case CompositionOp::OP_OUT:
|
||||
return D2D1_COMPOSITE_MODE_SOURCE_OUT;
|
||||
case CompositionOp::OP_IN:
|
||||
return D2D1_COMPOSITE_MODE_SOURCE_IN;
|
||||
case CompositionOp::OP_SOURCE:
|
||||
return D2D1_COMPOSITE_MODE_SOURCE_COPY;
|
||||
case CompositionOp::OP_DEST_IN:
|
||||
return D2D1_COMPOSITE_MODE_DESTINATION_IN;
|
||||
case CompositionOp::OP_DEST_OUT:
|
||||
return D2D1_COMPOSITE_MODE_DESTINATION_OUT;
|
||||
case CompositionOp::OP_DEST_OVER:
|
||||
return D2D1_COMPOSITE_MODE_DESTINATION_OVER;
|
||||
case CompositionOp::OP_DEST_ATOP:
|
||||
return D2D1_COMPOSITE_MODE_DESTINATION_ATOP;
|
||||
case CompositionOp::OP_XOR:
|
||||
return D2D1_COMPOSITE_MODE_XOR;
|
||||
default:
|
||||
return D2D1_COMPOSITE_MODE_SOURCE_OVER;
|
||||
}
|
||||
}
|
||||
|
||||
static inline D2D1_BLEND_MODE D2DBlendMode(CompositionOp aOp)
|
||||
{
|
||||
switch (aOp) {
|
||||
case CompositionOp::OP_MULTIPLY:
|
||||
return D2D1_BLEND_MODE_MULTIPLY;
|
||||
case CompositionOp::OP_SCREEN:
|
||||
return D2D1_BLEND_MODE_SCREEN;
|
||||
case CompositionOp::OP_OVERLAY:
|
||||
return D2D1_BLEND_MODE_OVERLAY;
|
||||
case CompositionOp::OP_DARKEN:
|
||||
return D2D1_BLEND_MODE_DARKEN;
|
||||
case CompositionOp::OP_LIGHTEN:
|
||||
return D2D1_BLEND_MODE_LIGHTEN;
|
||||
case CompositionOp::OP_COLOR_DODGE:
|
||||
return D2D1_BLEND_MODE_COLOR_DODGE;
|
||||
case CompositionOp::OP_COLOR_BURN:
|
||||
return D2D1_BLEND_MODE_COLOR_BURN;
|
||||
case CompositionOp::OP_HARD_LIGHT:
|
||||
return D2D1_BLEND_MODE_HARD_LIGHT;
|
||||
case CompositionOp::OP_SOFT_LIGHT:
|
||||
return D2D1_BLEND_MODE_SOFT_LIGHT;
|
||||
case CompositionOp::OP_DIFFERENCE:
|
||||
return D2D1_BLEND_MODE_DIFFERENCE;
|
||||
case CompositionOp::OP_EXCLUSION:
|
||||
return D2D1_BLEND_MODE_EXCLUSION;
|
||||
case CompositionOp::OP_HUE:
|
||||
return D2D1_BLEND_MODE_HUE;
|
||||
case CompositionOp::OP_SATURATION:
|
||||
return D2D1_BLEND_MODE_SATURATION;
|
||||
case CompositionOp::OP_COLOR:
|
||||
return D2D1_BLEND_MODE_COLOR;
|
||||
case CompositionOp::OP_LUMINOSITY:
|
||||
return D2D1_BLEND_MODE_LUMINOSITY;
|
||||
default:
|
||||
return D2D1_BLEND_MODE_MULTIPLY;
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool D2DSupportsPrimitiveBlendMode(CompositionOp aOp)
|
||||
{
|
||||
switch (aOp) {
|
||||
case CompositionOp::OP_OVER:
|
||||
// case CompositionOp::OP_SOURCE:
|
||||
return true;
|
||||
// case CompositionOp::OP_DARKEN:
|
||||
case CompositionOp::OP_ADD:
|
||||
return IsWindows8Point1OrGreater();
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static inline D2D1_PRIMITIVE_BLEND D2DPrimitiveBlendMode(CompositionOp aOp)
|
||||
{
|
||||
switch (aOp) {
|
||||
case CompositionOp::OP_OVER:
|
||||
return D2D1_PRIMITIVE_BLEND_SOURCE_OVER;
|
||||
// D2D1_PRIMITIVE_BLEND_COPY should leave pixels out of the source's
|
||||
// bounds unchanged, but doesn't- breaking unbounded ops.
|
||||
// D2D1_PRIMITIVE_BLEND_MIN doesn't quite work like darken either, as it
|
||||
// accounts for the source alpha.
|
||||
//
|
||||
// case CompositionOp::OP_SOURCE:
|
||||
// return D2D1_PRIMITIVE_BLEND_COPY;
|
||||
// case CompositionOp::OP_DARKEN:
|
||||
// return D2D1_PRIMITIVE_BLEND_MIN;
|
||||
case CompositionOp::OP_ADD:
|
||||
return D2D1_PRIMITIVE_BLEND_ADD;
|
||||
default:
|
||||
return D2D1_PRIMITIVE_BLEND_SOURCE_OVER;
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool IsPatternSupportedByD2D(const Pattern &aPattern)
|
||||
{
|
||||
if (aPattern.GetType() != PatternType::RADIAL_GRADIENT) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const RadialGradientPattern *pat =
|
||||
static_cast<const RadialGradientPattern*>(&aPattern);
|
||||
|
||||
if (pat->mRadius1 != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Point diff = pat->mCenter2 - pat->mCenter1;
|
||||
|
||||
if (sqrt(diff.x * diff.x + diff.y * diff.y) >= pat->mRadius2) {
|
||||
// Inner point lies outside the circle.
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* This structure is used to pass rectangles to our shader constant. We can use
|
||||
* this for passing rectangular areas to SetVertexShaderConstant. In the format
|
||||
* of a 4 component float(x,y,width,height). Our vertex shader can then use
|
||||
* this to construct rectangular positions from the 0,0-1,1 quad that we source
|
||||
* it with.
|
||||
*/
|
||||
struct ShaderConstantRectD3D10
|
||||
{
|
||||
float mX, mY, mWidth, mHeight;
|
||||
ShaderConstantRectD3D10(float aX, float aY, float aWidth, float aHeight)
|
||||
: mX(aX), mY(aY), mWidth(aWidth), mHeight(aHeight)
|
||||
{ }
|
||||
|
||||
// For easy passing to SetVertexShaderConstantF.
|
||||
operator float* () { return &mX; }
|
||||
};
|
||||
|
||||
static inline DWRITE_MATRIX
|
||||
DWriteMatrixFromMatrix(Matrix &aMatrix)
|
||||
{
|
||||
DWRITE_MATRIX mat;
|
||||
mat.m11 = aMatrix._11;
|
||||
mat.m12 = aMatrix._12;
|
||||
mat.m21 = aMatrix._21;
|
||||
mat.m22 = aMatrix._22;
|
||||
mat.dx = aMatrix._31;
|
||||
mat.dy = aMatrix._32;
|
||||
return mat;
|
||||
}
|
||||
|
||||
class AutoDWriteGlyphRun : public DWRITE_GLYPH_RUN
|
||||
{
|
||||
static const unsigned kNumAutoGlyphs = 256;
|
||||
|
||||
public:
|
||||
AutoDWriteGlyphRun() {
|
||||
glyphCount = 0;
|
||||
}
|
||||
|
||||
~AutoDWriteGlyphRun() {
|
||||
if (glyphCount > kNumAutoGlyphs) {
|
||||
delete[] glyphIndices;
|
||||
delete[] glyphAdvances;
|
||||
delete[] glyphOffsets;
|
||||
}
|
||||
}
|
||||
|
||||
void allocate(unsigned aNumGlyphs) {
|
||||
glyphCount = aNumGlyphs;
|
||||
if (aNumGlyphs <= kNumAutoGlyphs) {
|
||||
glyphIndices = &mAutoIndices[0];
|
||||
glyphAdvances = &mAutoAdvances[0];
|
||||
glyphOffsets = &mAutoOffsets[0];
|
||||
} else {
|
||||
glyphIndices = new UINT16[aNumGlyphs];
|
||||
glyphAdvances = new FLOAT[aNumGlyphs];
|
||||
glyphOffsets = new DWRITE_GLYPH_OFFSET[aNumGlyphs];
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
DWRITE_GLYPH_OFFSET mAutoOffsets[kNumAutoGlyphs];
|
||||
FLOAT mAutoAdvances[kNumAutoGlyphs];
|
||||
UINT16 mAutoIndices[kNumAutoGlyphs];
|
||||
};
|
||||
|
||||
static inline void
|
||||
DWriteGlyphRunFromGlyphs(const GlyphBuffer &aGlyphs, ScaledFontDWrite *aFont, AutoDWriteGlyphRun *run)
|
||||
{
|
||||
run->allocate(aGlyphs.mNumGlyphs);
|
||||
|
||||
FLOAT *advances = const_cast<FLOAT*>(run->glyphAdvances);
|
||||
UINT16 *indices = const_cast<UINT16*>(run->glyphIndices);
|
||||
DWRITE_GLYPH_OFFSET *offsets = const_cast<DWRITE_GLYPH_OFFSET*>(run->glyphOffsets);
|
||||
|
||||
memset(advances, 0, sizeof(FLOAT) * aGlyphs.mNumGlyphs);
|
||||
for (unsigned int i = 0; i < aGlyphs.mNumGlyphs; i++) {
|
||||
indices[i] = aGlyphs.mGlyphs[i].mIndex;
|
||||
offsets[i].advanceOffset = aGlyphs.mGlyphs[i].mPosition.x;
|
||||
offsets[i].ascenderOffset = -aGlyphs.mGlyphs[i].mPosition.y;
|
||||
}
|
||||
|
||||
run->bidiLevel = 0;
|
||||
run->fontFace = aFont->mFontFace;
|
||||
run->fontEmSize = aFont->GetSize();
|
||||
run->glyphCount = aGlyphs.mNumGlyphs;
|
||||
run->isSideways = FALSE;
|
||||
}
|
||||
|
||||
static inline already_AddRefed<ID2D1Geometry>
|
||||
ConvertRectToGeometry(const D2D1_RECT_F& aRect)
|
||||
{
|
||||
RefPtr<ID2D1RectangleGeometry> rectGeom;
|
||||
D2DFactory()->CreateRectangleGeometry(&aRect, getter_AddRefs(rectGeom));
|
||||
return rectGeom.forget();
|
||||
}
|
||||
|
||||
static inline already_AddRefed<ID2D1Geometry>
|
||||
GetTransformedGeometry(ID2D1Geometry *aGeometry, const D2D1_MATRIX_3X2_F &aTransform)
|
||||
{
|
||||
RefPtr<ID2D1PathGeometry> tmpGeometry;
|
||||
D2DFactory()->CreatePathGeometry(getter_AddRefs(tmpGeometry));
|
||||
RefPtr<ID2D1GeometrySink> currentSink;
|
||||
tmpGeometry->Open(getter_AddRefs(currentSink));
|
||||
aGeometry->Simplify(D2D1_GEOMETRY_SIMPLIFICATION_OPTION_CUBICS_AND_LINES,
|
||||
aTransform, currentSink);
|
||||
currentSink->Close();
|
||||
return tmpGeometry.forget();
|
||||
}
|
||||
|
||||
static inline already_AddRefed<ID2D1Geometry>
|
||||
IntersectGeometry(ID2D1Geometry *aGeometryA, ID2D1Geometry *aGeometryB)
|
||||
{
|
||||
RefPtr<ID2D1PathGeometry> pathGeom;
|
||||
D2DFactory()->CreatePathGeometry(getter_AddRefs(pathGeom));
|
||||
RefPtr<ID2D1GeometrySink> sink;
|
||||
pathGeom->Open(getter_AddRefs(sink));
|
||||
aGeometryA->CombineWithGeometry(aGeometryB, D2D1_COMBINE_MODE_INTERSECT, nullptr, sink);
|
||||
sink->Close();
|
||||
|
||||
return pathGeom.forget();
|
||||
}
|
||||
|
||||
static inline already_AddRefed<ID2D1StrokeStyle>
|
||||
CreateStrokeStyleForOptions(const StrokeOptions &aStrokeOptions)
|
||||
{
|
||||
RefPtr<ID2D1StrokeStyle> style;
|
||||
|
||||
D2D1_CAP_STYLE capStyle;
|
||||
D2D1_LINE_JOIN joinStyle;
|
||||
|
||||
switch (aStrokeOptions.mLineCap) {
|
||||
case CapStyle::BUTT:
|
||||
capStyle = D2D1_CAP_STYLE_FLAT;
|
||||
break;
|
||||
case CapStyle::ROUND:
|
||||
capStyle = D2D1_CAP_STYLE_ROUND;
|
||||
break;
|
||||
case CapStyle::SQUARE:
|
||||
capStyle = D2D1_CAP_STYLE_SQUARE;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (aStrokeOptions.mLineJoin) {
|
||||
case JoinStyle::MITER:
|
||||
joinStyle = D2D1_LINE_JOIN_MITER;
|
||||
break;
|
||||
case JoinStyle::MITER_OR_BEVEL:
|
||||
joinStyle = D2D1_LINE_JOIN_MITER_OR_BEVEL;
|
||||
break;
|
||||
case JoinStyle::ROUND:
|
||||
joinStyle = D2D1_LINE_JOIN_ROUND;
|
||||
break;
|
||||
case JoinStyle::BEVEL:
|
||||
joinStyle = D2D1_LINE_JOIN_BEVEL;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
HRESULT hr;
|
||||
// We need to check mDashLength in addition to mDashPattern here since if
|
||||
// mDashPattern is set but mDashLength is zero then the stroke will fail to
|
||||
// paint.
|
||||
if (aStrokeOptions.mDashLength > 0 && aStrokeOptions.mDashPattern) {
|
||||
typedef std::vector<Float> FloatVector;
|
||||
// D2D "helpfully" multiplies the dash pattern by the line width.
|
||||
// That's not what cairo does, or is what <canvas>'s dash wants.
|
||||
// So fix the multiplication in advance.
|
||||
Float lineWidth = aStrokeOptions.mLineWidth;
|
||||
FloatVector dash(aStrokeOptions.mDashPattern,
|
||||
aStrokeOptions.mDashPattern + aStrokeOptions.mDashLength);
|
||||
for (FloatVector::iterator it = dash.begin(); it != dash.end(); ++it) {
|
||||
*it /= lineWidth;
|
||||
}
|
||||
|
||||
hr = D2DFactory()->CreateStrokeStyle(
|
||||
D2D1::StrokeStyleProperties(capStyle, capStyle,
|
||||
capStyle, joinStyle,
|
||||
aStrokeOptions.mMiterLimit,
|
||||
D2D1_DASH_STYLE_CUSTOM,
|
||||
aStrokeOptions.mDashOffset / lineWidth),
|
||||
&dash[0], // data() is not C++98, although it's in recent gcc
|
||||
// and VC10's STL
|
||||
dash.size(),
|
||||
getter_AddRefs(style));
|
||||
} else {
|
||||
hr = D2DFactory()->CreateStrokeStyle(
|
||||
D2D1::StrokeStyleProperties(capStyle, capStyle,
|
||||
capStyle, joinStyle,
|
||||
aStrokeOptions.mMiterLimit),
|
||||
nullptr, 0, getter_AddRefs(style));
|
||||
}
|
||||
|
||||
if (FAILED(hr)) {
|
||||
gfxWarning() << "Failed to create Direct2D stroke style.";
|
||||
}
|
||||
|
||||
return style.forget();
|
||||
}
|
||||
|
||||
// This creates a (partially) uploaded bitmap for a DataSourceSurface. It
|
||||
// uploads the minimum requirement and possibly downscales. It adjusts the
|
||||
// input Matrix to compensate.
|
||||
static inline already_AddRefed<ID2D1Bitmap>
|
||||
CreatePartialBitmapForSurface(DataSourceSurface *aSurface, const Matrix &aDestinationTransform,
|
||||
const IntSize &aDestinationSize, ExtendMode aExtendMode,
|
||||
Matrix &aSourceTransform, ID2D1RenderTarget *aRT,
|
||||
const IntRect* aSourceRect = nullptr)
|
||||
{
|
||||
RefPtr<ID2D1Bitmap> bitmap;
|
||||
|
||||
// This is where things get complicated. The source surface was
|
||||
// created for a surface that was too large to fit in a texture.
|
||||
// We'll need to figure out if we can work with a partial upload
|
||||
// or downsample in software.
|
||||
|
||||
Matrix transform = aDestinationTransform;
|
||||
Matrix invTransform = transform = aSourceTransform * transform;
|
||||
if (!invTransform.Invert()) {
|
||||
// Singular transform, nothing to be drawn.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Rect rect(0, 0, Float(aDestinationSize.width), Float(aDestinationSize.height));
|
||||
|
||||
// Calculate the rectangle of the source mapped to our surface.
|
||||
rect = invTransform.TransformBounds(rect);
|
||||
rect.RoundOut();
|
||||
|
||||
IntSize size = aSurface->GetSize();
|
||||
|
||||
Rect uploadRect(0, 0, Float(size.width), Float(size.height));
|
||||
if (aSourceRect) {
|
||||
uploadRect = Rect(aSourceRect->x, aSourceRect->y, aSourceRect->width, aSourceRect->height);
|
||||
}
|
||||
|
||||
// Limit the uploadRect as much as possible without supporting discontiguous uploads
|
||||
//
|
||||
// region we will paint from
|
||||
// uploadRect
|
||||
// .---------------. .---------------. resulting uploadRect
|
||||
// | |rect | |
|
||||
// | .---------. .----. .----. .---------------.
|
||||
// | | | ----> | | | | ----> | |
|
||||
// | '---------' '----' '----' '---------------'
|
||||
// '---------------' '---------------'
|
||||
//
|
||||
//
|
||||
|
||||
if (uploadRect.Contains(rect)) {
|
||||
// Extend mode is irrelevant, the displayed rect is completely contained
|
||||
// by the source bitmap.
|
||||
uploadRect = rect;
|
||||
} else if (aExtendMode == ExtendMode::CLAMP && uploadRect.Intersects(rect)) {
|
||||
// Calculate the rectangle on the source bitmap that touches our
|
||||
// surface, and upload that, for ExtendMode::CLAMP we can actually guarantee
|
||||
// correct behaviour in this case.
|
||||
uploadRect = uploadRect.Intersect(rect);
|
||||
|
||||
// We now proceed to check if we can limit at least one dimension of the
|
||||
// upload rect safely without looking at extend mode.
|
||||
} else if (rect.x >= 0 && rect.XMost() < size.width) {
|
||||
uploadRect.x = rect.x;
|
||||
uploadRect.width = rect.width;
|
||||
} else if (rect.y >= 0 && rect.YMost() < size.height) {
|
||||
uploadRect.y = rect.y;
|
||||
uploadRect.height = rect.height;
|
||||
}
|
||||
|
||||
if (uploadRect.IsEmpty()) {
|
||||
// Nothing to be drawn.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (uploadRect.width <= aRT->GetMaximumBitmapSize() &&
|
||||
uploadRect.height <= aRT->GetMaximumBitmapSize()) {
|
||||
{
|
||||
// Scope to auto-Unmap() |mapping|.
|
||||
DataSourceSurface::ScopedMap mapping(aSurface, DataSourceSurface::READ);
|
||||
if (MOZ2D_WARN_IF(!mapping.IsMapped())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// A partial upload will suffice.
|
||||
aRT->CreateBitmap(D2D1::SizeU(uint32_t(uploadRect.width), uint32_t(uploadRect.height)),
|
||||
mapping.GetData() + int(uploadRect.x) * 4 + int(uploadRect.y) * mapping.GetStride(),
|
||||
mapping.GetStride(),
|
||||
D2D1::BitmapProperties(D2DPixelFormat(aSurface->GetFormat())),
|
||||
getter_AddRefs(bitmap));
|
||||
}
|
||||
|
||||
aSourceTransform.PreTranslate(uploadRect.x, uploadRect.y);
|
||||
|
||||
return bitmap.forget();
|
||||
} else {
|
||||
int Bpp = BytesPerPixel(aSurface->GetFormat());
|
||||
|
||||
if (Bpp != 4) {
|
||||
// This shouldn't actually happen in practice!
|
||||
MOZ_ASSERT(false);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
{
|
||||
// Scope to auto-Unmap() |mapping|.
|
||||
DataSourceSurface::ScopedMap mapping(aSurface, DataSourceSurface::READ);
|
||||
if (MOZ2D_WARN_IF(!mapping.IsMapped())) {
|
||||
return nullptr;
|
||||
}
|
||||
ImageHalfScaler scaler(mapping.GetData(), mapping.GetStride(), size);
|
||||
|
||||
// Calculate the maximum width/height of the image post transform.
|
||||
Point topRight = transform.TransformPoint(Point(Float(size.width), 0));
|
||||
Point topLeft = transform.TransformPoint(Point(0, 0));
|
||||
Point bottomRight = transform.TransformPoint(Point(Float(size.width), Float(size.height)));
|
||||
Point bottomLeft = transform.TransformPoint(Point(0, Float(size.height)));
|
||||
|
||||
IntSize scaleSize;
|
||||
|
||||
scaleSize.width = int32_t(std::max(Distance(topRight, topLeft),
|
||||
Distance(bottomRight, bottomLeft)));
|
||||
scaleSize.height = int32_t(std::max(Distance(topRight, bottomRight),
|
||||
Distance(topLeft, bottomLeft)));
|
||||
|
||||
if (unsigned(scaleSize.width) > aRT->GetMaximumBitmapSize()) {
|
||||
// Ok, in this case we'd really want a downscale of a part of the bitmap,
|
||||
// perhaps we can do this later but for simplicity let's do something
|
||||
// different here and assume it's good enough, this should be rare!
|
||||
scaleSize.width = 4095;
|
||||
}
|
||||
if (unsigned(scaleSize.height) > aRT->GetMaximumBitmapSize()) {
|
||||
scaleSize.height = 4095;
|
||||
}
|
||||
|
||||
scaler.ScaleForSize(scaleSize);
|
||||
|
||||
IntSize newSize = scaler.GetSize();
|
||||
|
||||
if (newSize.IsEmpty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
aRT->CreateBitmap(D2D1::SizeU(newSize.width, newSize.height),
|
||||
scaler.GetScaledData(), scaler.GetStride(),
|
||||
D2D1::BitmapProperties(D2DPixelFormat(aSurface->GetFormat())),
|
||||
getter_AddRefs(bitmap));
|
||||
|
||||
aSourceTransform.PreScale(Float(size.width) / newSize.width,
|
||||
Float(size.height) / newSize.height);
|
||||
}
|
||||
return bitmap.forget();
|
||||
}
|
||||
}
|
||||
|
||||
static inline void AddRectToSink(ID2D1GeometrySink* aSink, const D2D1_RECT_F& aRect)
|
||||
{
|
||||
aSink->BeginFigure(D2D1::Point2F(aRect.left, aRect.top), D2D1_FIGURE_BEGIN_FILLED);
|
||||
aSink->AddLine(D2D1::Point2F(aRect.right, aRect.top));
|
||||
aSink->AddLine(D2D1::Point2F(aRect.right, aRect.bottom));
|
||||
aSink->AddLine(D2D1::Point2F(aRect.left, aRect.bottom));
|
||||
aSink->EndFigure(D2D1_FIGURE_END_CLOSED);
|
||||
}
|
||||
|
||||
class DCCommandSink : public ID2D1CommandSink
|
||||
{
|
||||
public:
|
||||
DCCommandSink(ID2D1DeviceContext* aCtx) : mCtx(aCtx)
|
||||
{
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE QueryInterface(const IID &aIID, void **aPtr)
|
||||
{
|
||||
if (!aPtr) {
|
||||
return E_POINTER;
|
||||
}
|
||||
|
||||
if (aIID == IID_IUnknown) {
|
||||
*aPtr = static_cast<IUnknown*>(this);
|
||||
return S_OK;
|
||||
} else if (aIID == IID_ID2D1CommandSink) {
|
||||
*aPtr = static_cast<ID2D1CommandSink*>(this);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
|
||||
ULONG STDMETHODCALLTYPE AddRef()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
ULONG STDMETHODCALLTYPE Release()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
STDMETHODIMP BeginDraw()
|
||||
{
|
||||
// We don't want to do anything here!
|
||||
return S_OK;
|
||||
}
|
||||
STDMETHODIMP EndDraw()
|
||||
{
|
||||
// We don't want to do anything here!
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP SetAntialiasMode(
|
||||
D2D1_ANTIALIAS_MODE antialiasMode
|
||||
)
|
||||
{
|
||||
mCtx->SetAntialiasMode(antialiasMode);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP SetTags(D2D1_TAG tag1, D2D1_TAG tag2)
|
||||
{
|
||||
mCtx->SetTags(tag1, tag2);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE textAntialiasMode)
|
||||
{
|
||||
mCtx->SetTextAntialiasMode(textAntialiasMode);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP SetTextRenderingParams(_In_opt_ IDWriteRenderingParams *textRenderingParams)
|
||||
{
|
||||
mCtx->SetTextRenderingParams(textRenderingParams);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP SetTransform(_In_ CONST D2D1_MATRIX_3X2_F *transform)
|
||||
{
|
||||
mCtx->SetTransform(transform);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND primitiveBlend)
|
||||
{
|
||||
mCtx->SetPrimitiveBlend(primitiveBlend);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP SetUnitMode(D2D1_UNIT_MODE unitMode)
|
||||
{
|
||||
mCtx->SetUnitMode(unitMode);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP Clear(_In_opt_ CONST D2D1_COLOR_F *color)
|
||||
{
|
||||
mCtx->Clear(color);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP DrawGlyphRun(
|
||||
D2D1_POINT_2F baselineOrigin,
|
||||
_In_ CONST DWRITE_GLYPH_RUN *glyphRun,
|
||||
_In_opt_ CONST DWRITE_GLYPH_RUN_DESCRIPTION *glyphRunDescription,
|
||||
_In_ ID2D1Brush *foregroundBrush,
|
||||
DWRITE_MEASURING_MODE measuringMode
|
||||
)
|
||||
{
|
||||
mCtx->DrawGlyphRun(baselineOrigin, glyphRun, glyphRunDescription,
|
||||
foregroundBrush, measuringMode);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP DrawLine(
|
||||
D2D1_POINT_2F point0,
|
||||
D2D1_POINT_2F point1,
|
||||
_In_ ID2D1Brush *brush,
|
||||
FLOAT strokeWidth,
|
||||
_In_opt_ ID2D1StrokeStyle *strokeStyle
|
||||
)
|
||||
{
|
||||
mCtx->DrawLine(point0, point1, brush, strokeWidth, strokeStyle);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP DrawGeometry(
|
||||
_In_ ID2D1Geometry *geometry,
|
||||
_In_ ID2D1Brush *brush,
|
||||
FLOAT strokeWidth,
|
||||
_In_opt_ ID2D1StrokeStyle *strokeStyle
|
||||
)
|
||||
{
|
||||
mCtx->DrawGeometry(geometry, brush, strokeWidth, strokeStyle);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP DrawRectangle(
|
||||
_In_ CONST D2D1_RECT_F *rect,
|
||||
_In_ ID2D1Brush *brush,
|
||||
FLOAT strokeWidth,
|
||||
_In_opt_ ID2D1StrokeStyle *strokeStyle
|
||||
)
|
||||
{
|
||||
mCtx->DrawRectangle(rect, brush, strokeWidth, strokeStyle);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP DrawBitmap(
|
||||
_In_ ID2D1Bitmap *bitmap,
|
||||
_In_opt_ CONST D2D1_RECT_F *destinationRectangle,
|
||||
FLOAT opacity,
|
||||
D2D1_INTERPOLATION_MODE interpolationMode,
|
||||
_In_opt_ CONST D2D1_RECT_F *sourceRectangle,
|
||||
_In_opt_ CONST D2D1_MATRIX_4X4_F *perspectiveTransform
|
||||
)
|
||||
{
|
||||
mCtx->DrawBitmap(bitmap, destinationRectangle, opacity,
|
||||
interpolationMode, sourceRectangle,
|
||||
perspectiveTransform);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP DrawImage(
|
||||
_In_ ID2D1Image *image,
|
||||
_In_opt_ CONST D2D1_POINT_2F *targetOffset,
|
||||
_In_opt_ CONST D2D1_RECT_F *imageRectangle,
|
||||
D2D1_INTERPOLATION_MODE interpolationMode,
|
||||
D2D1_COMPOSITE_MODE compositeMode
|
||||
)
|
||||
{
|
||||
mCtx->DrawImage(image, targetOffset, imageRectangle,
|
||||
interpolationMode, compositeMode);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP DrawGdiMetafile(
|
||||
_In_ ID2D1GdiMetafile *gdiMetafile,
|
||||
_In_opt_ CONST D2D1_POINT_2F *targetOffset
|
||||
)
|
||||
{
|
||||
mCtx->DrawGdiMetafile(gdiMetafile, targetOffset);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP FillMesh(
|
||||
_In_ ID2D1Mesh *mesh,
|
||||
_In_ ID2D1Brush *brush
|
||||
)
|
||||
{
|
||||
mCtx->FillMesh(mesh, brush);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP FillOpacityMask(
|
||||
_In_ ID2D1Bitmap *opacityMask,
|
||||
_In_ ID2D1Brush *brush,
|
||||
_In_opt_ CONST D2D1_RECT_F *destinationRectangle,
|
||||
_In_opt_ CONST D2D1_RECT_F *sourceRectangle
|
||||
)
|
||||
{
|
||||
mCtx->FillOpacityMask(opacityMask, brush, destinationRectangle,
|
||||
sourceRectangle);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP FillGeometry(
|
||||
_In_ ID2D1Geometry *geometry,
|
||||
_In_ ID2D1Brush *brush,
|
||||
_In_opt_ ID2D1Brush *opacityBrush
|
||||
)
|
||||
{
|
||||
mCtx->FillGeometry(geometry, brush, opacityBrush);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP FillRectangle(
|
||||
_In_ CONST D2D1_RECT_F *rect,
|
||||
_In_ ID2D1Brush *brush
|
||||
)
|
||||
{
|
||||
mCtx->FillRectangle(rect, brush);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP PushAxisAlignedClip(
|
||||
_In_ CONST D2D1_RECT_F *clipRect,
|
||||
D2D1_ANTIALIAS_MODE antialiasMode
|
||||
)
|
||||
{
|
||||
mCtx->PushAxisAlignedClip(clipRect, antialiasMode);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP PushLayer(
|
||||
_In_ CONST D2D1_LAYER_PARAMETERS1 *layerParameters1,
|
||||
_In_opt_ ID2D1Layer *layer
|
||||
)
|
||||
{
|
||||
mCtx->PushLayer(layerParameters1, layer);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP PopAxisAlignedClip()
|
||||
{
|
||||
mCtx->PopAxisAlignedClip();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP PopLayer()
|
||||
{
|
||||
mCtx->PopLayer();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
ID2D1DeviceContext* mCtx;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* MOZILLA_GFX_HELPERSD2D_H_ */
|
||||
396
gfx/2d/HelpersSkia.h
Normal file
396
gfx/2d/HelpersSkia.h
Normal file
|
|
@ -0,0 +1,396 @@
|
|||
/* -*- Mode: C++; tab-width: 20; 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_GFX_HELPERSSKIA_H_
|
||||
#define MOZILLA_GFX_HELPERSSKIA_H_
|
||||
|
||||
#include "2D.h"
|
||||
#include "skia/include/core/SkCanvas.h"
|
||||
#include "skia/include/effects/SkDashPathEffect.h"
|
||||
#include "skia/include/core/SkShader.h"
|
||||
#ifdef USE_SKIA_GPU
|
||||
#include "skia/include/gpu/GrTypes.h"
|
||||
#endif
|
||||
#include "mozilla/Assertions.h"
|
||||
#include <vector>
|
||||
#include "nsDebug.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
static inline SkColorType
|
||||
GfxFormatToSkiaColorType(SurfaceFormat format)
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
case SurfaceFormat::B8G8R8A8:
|
||||
return kBGRA_8888_SkColorType;
|
||||
case SurfaceFormat::B8G8R8X8:
|
||||
// We probably need to do something here.
|
||||
return kBGRA_8888_SkColorType;
|
||||
case SurfaceFormat::R5G6B5_UINT16:
|
||||
return kRGB_565_SkColorType;
|
||||
case SurfaceFormat::A8:
|
||||
return kAlpha_8_SkColorType;
|
||||
default:
|
||||
return kRGBA_8888_SkColorType;
|
||||
}
|
||||
}
|
||||
|
||||
static inline SurfaceFormat
|
||||
SkiaColorTypeToGfxFormat(SkColorType aColorType, SkAlphaType aAlphaType = kPremul_SkAlphaType)
|
||||
{
|
||||
switch (aColorType)
|
||||
{
|
||||
case kBGRA_8888_SkColorType:
|
||||
return aAlphaType == kOpaque_SkAlphaType ?
|
||||
SurfaceFormat::B8G8R8X8 : SurfaceFormat::B8G8R8A8;
|
||||
case kRGB_565_SkColorType:
|
||||
return SurfaceFormat::R5G6B5_UINT16;
|
||||
case kAlpha_8_SkColorType:
|
||||
return SurfaceFormat::A8;
|
||||
default:
|
||||
return SurfaceFormat::B8G8R8A8;
|
||||
}
|
||||
}
|
||||
|
||||
static inline SkAlphaType
|
||||
GfxFormatToSkiaAlphaType(SurfaceFormat format)
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
case SurfaceFormat::B8G8R8X8:
|
||||
case SurfaceFormat::R5G6B5_UINT16:
|
||||
return kOpaque_SkAlphaType;
|
||||
default:
|
||||
return kPremul_SkAlphaType;
|
||||
}
|
||||
}
|
||||
|
||||
static inline SkImageInfo
|
||||
MakeSkiaImageInfo(const IntSize& aSize, SurfaceFormat aFormat)
|
||||
{
|
||||
return SkImageInfo::Make(aSize.width, aSize.height,
|
||||
GfxFormatToSkiaColorType(aFormat),
|
||||
GfxFormatToSkiaAlphaType(aFormat));
|
||||
}
|
||||
|
||||
#ifdef USE_SKIA_GPU
|
||||
static inline GrPixelConfig
|
||||
GfxFormatToGrConfig(SurfaceFormat format)
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
case SurfaceFormat::B8G8R8A8:
|
||||
return kBGRA_8888_GrPixelConfig;
|
||||
case SurfaceFormat::B8G8R8X8:
|
||||
// We probably need to do something here.
|
||||
return kBGRA_8888_GrPixelConfig;
|
||||
case SurfaceFormat::R5G6B5_UINT16:
|
||||
return kRGB_565_GrPixelConfig;
|
||||
case SurfaceFormat::A8:
|
||||
return kAlpha_8_GrPixelConfig;
|
||||
default:
|
||||
return kRGBA_8888_GrPixelConfig;
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
static inline void
|
||||
GfxMatrixToSkiaMatrix(const Matrix& mat, SkMatrix& retval)
|
||||
{
|
||||
retval.setAll(SkFloatToScalar(mat._11), SkFloatToScalar(mat._21), SkFloatToScalar(mat._31),
|
||||
SkFloatToScalar(mat._12), SkFloatToScalar(mat._22), SkFloatToScalar(mat._32),
|
||||
0, 0, SK_Scalar1);
|
||||
}
|
||||
|
||||
static inline void
|
||||
GfxMatrixToSkiaMatrix(const Matrix4x4& aMatrix, SkMatrix& aResult)
|
||||
{
|
||||
aResult.setAll(SkFloatToScalar(aMatrix._11), SkFloatToScalar(aMatrix._21), SkFloatToScalar(aMatrix._41),
|
||||
SkFloatToScalar(aMatrix._12), SkFloatToScalar(aMatrix._22), SkFloatToScalar(aMatrix._42),
|
||||
SkFloatToScalar(aMatrix._14), SkFloatToScalar(aMatrix._24), SkFloatToScalar(aMatrix._44));
|
||||
}
|
||||
|
||||
static inline SkPaint::Cap
|
||||
CapStyleToSkiaCap(CapStyle aCap)
|
||||
{
|
||||
switch (aCap)
|
||||
{
|
||||
case CapStyle::BUTT:
|
||||
return SkPaint::kButt_Cap;
|
||||
case CapStyle::ROUND:
|
||||
return SkPaint::kRound_Cap;
|
||||
case CapStyle::SQUARE:
|
||||
return SkPaint::kSquare_Cap;
|
||||
}
|
||||
return SkPaint::kDefault_Cap;
|
||||
}
|
||||
|
||||
static inline SkPaint::Join
|
||||
JoinStyleToSkiaJoin(JoinStyle aJoin)
|
||||
{
|
||||
switch (aJoin)
|
||||
{
|
||||
case JoinStyle::BEVEL:
|
||||
return SkPaint::kBevel_Join;
|
||||
case JoinStyle::ROUND:
|
||||
return SkPaint::kRound_Join;
|
||||
case JoinStyle::MITER:
|
||||
case JoinStyle::MITER_OR_BEVEL:
|
||||
return SkPaint::kMiter_Join;
|
||||
}
|
||||
return SkPaint::kDefault_Join;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
StrokeOptionsToPaint(SkPaint& aPaint, const StrokeOptions &aOptions)
|
||||
{
|
||||
// Skia renders 0 width strokes with a width of 1 (and in black),
|
||||
// so we should just skip the draw call entirely.
|
||||
// Skia does not handle non-finite line widths.
|
||||
if (!aOptions.mLineWidth || !IsFinite(aOptions.mLineWidth)) {
|
||||
return false;
|
||||
}
|
||||
aPaint.setStrokeWidth(SkFloatToScalar(aOptions.mLineWidth));
|
||||
aPaint.setStrokeMiter(SkFloatToScalar(aOptions.mMiterLimit));
|
||||
aPaint.setStrokeCap(CapStyleToSkiaCap(aOptions.mLineCap));
|
||||
aPaint.setStrokeJoin(JoinStyleToSkiaJoin(aOptions.mLineJoin));
|
||||
|
||||
if (aOptions.mDashLength > 0) {
|
||||
// Skia only supports dash arrays that are multiples of 2.
|
||||
uint32_t dashCount;
|
||||
|
||||
if (aOptions.mDashLength % 2 == 0) {
|
||||
dashCount = aOptions.mDashLength;
|
||||
} else {
|
||||
dashCount = aOptions.mDashLength * 2;
|
||||
}
|
||||
|
||||
std::vector<SkScalar> pattern;
|
||||
pattern.resize(dashCount);
|
||||
|
||||
for (uint32_t i = 0; i < dashCount; i++) {
|
||||
pattern[i] = SkFloatToScalar(aOptions.mDashPattern[i % aOptions.mDashLength]);
|
||||
}
|
||||
|
||||
sk_sp<SkPathEffect> dash = SkDashPathEffect::Make(&pattern.front(),
|
||||
dashCount,
|
||||
SkFloatToScalar(aOptions.mDashOffset));
|
||||
aPaint.setPathEffect(dash);
|
||||
}
|
||||
|
||||
aPaint.setStyle(SkPaint::kStroke_Style);
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline SkBlendMode
|
||||
GfxOpToSkiaOp(CompositionOp op)
|
||||
{
|
||||
switch (op)
|
||||
{
|
||||
case CompositionOp::OP_OVER:
|
||||
return SkBlendMode::kSrcOver;
|
||||
case CompositionOp::OP_ADD:
|
||||
return SkBlendMode::kPlus;
|
||||
case CompositionOp::OP_ATOP:
|
||||
return SkBlendMode::kSrcATop;
|
||||
case CompositionOp::OP_OUT:
|
||||
return SkBlendMode::kSrcOut;
|
||||
case CompositionOp::OP_IN:
|
||||
return SkBlendMode::kSrcIn;
|
||||
case CompositionOp::OP_SOURCE:
|
||||
return SkBlendMode::kSrc;
|
||||
case CompositionOp::OP_DEST_IN:
|
||||
return SkBlendMode::kDstIn;
|
||||
case CompositionOp::OP_DEST_OUT:
|
||||
return SkBlendMode::kDstOut;
|
||||
case CompositionOp::OP_DEST_OVER:
|
||||
return SkBlendMode::kDstOver;
|
||||
case CompositionOp::OP_DEST_ATOP:
|
||||
return SkBlendMode::kDstATop;
|
||||
case CompositionOp::OP_XOR:
|
||||
return SkBlendMode::kXor;
|
||||
case CompositionOp::OP_MULTIPLY:
|
||||
return SkBlendMode::kMultiply;
|
||||
case CompositionOp::OP_SCREEN:
|
||||
return SkBlendMode::kScreen;
|
||||
case CompositionOp::OP_OVERLAY:
|
||||
return SkBlendMode::kOverlay;
|
||||
case CompositionOp::OP_DARKEN:
|
||||
return SkBlendMode::kDarken;
|
||||
case CompositionOp::OP_LIGHTEN:
|
||||
return SkBlendMode::kLighten;
|
||||
case CompositionOp::OP_COLOR_DODGE:
|
||||
return SkBlendMode::kColorDodge;
|
||||
case CompositionOp::OP_COLOR_BURN:
|
||||
return SkBlendMode::kColorBurn;
|
||||
case CompositionOp::OP_HARD_LIGHT:
|
||||
return SkBlendMode::kHardLight;
|
||||
case CompositionOp::OP_SOFT_LIGHT:
|
||||
return SkBlendMode::kSoftLight;
|
||||
case CompositionOp::OP_DIFFERENCE:
|
||||
return SkBlendMode::kDifference;
|
||||
case CompositionOp::OP_EXCLUSION:
|
||||
return SkBlendMode::kExclusion;
|
||||
case CompositionOp::OP_HUE:
|
||||
return SkBlendMode::kHue;
|
||||
case CompositionOp::OP_SATURATION:
|
||||
return SkBlendMode::kSaturation;
|
||||
case CompositionOp::OP_COLOR:
|
||||
return SkBlendMode::kColor;
|
||||
case CompositionOp::OP_LUMINOSITY:
|
||||
return SkBlendMode::kLuminosity;
|
||||
default:
|
||||
return SkBlendMode::kSrcOver;
|
||||
}
|
||||
}
|
||||
|
||||
/* There's quite a bit of inconsistency about
|
||||
* whether float colors should be rounded with .5f.
|
||||
* We choose to do it to match cairo which also
|
||||
* happens to match the Direct3D specs */
|
||||
static inline U8CPU ColorFloatToByte(Float color)
|
||||
{
|
||||
//XXX: do a better job converting to int
|
||||
return U8CPU(color*255.f + .5f);
|
||||
};
|
||||
|
||||
static inline SkColor ColorToSkColor(const Color &color, Float aAlpha)
|
||||
{
|
||||
return SkColorSetARGB(ColorFloatToByte(color.a*aAlpha), ColorFloatToByte(color.r),
|
||||
ColorFloatToByte(color.g), ColorFloatToByte(color.b));
|
||||
}
|
||||
|
||||
static inline SkPoint
|
||||
PointToSkPoint(const Point &aPoint)
|
||||
{
|
||||
return SkPoint::Make(SkFloatToScalar(aPoint.x), SkFloatToScalar(aPoint.y));
|
||||
}
|
||||
|
||||
static inline SkRect
|
||||
RectToSkRect(const Rect& aRect)
|
||||
{
|
||||
return SkRect::MakeXYWH(SkFloatToScalar(aRect.x), SkFloatToScalar(aRect.y),
|
||||
SkFloatToScalar(aRect.width), SkFloatToScalar(aRect.height));
|
||||
}
|
||||
|
||||
static inline SkRect
|
||||
IntRectToSkRect(const IntRect& aRect)
|
||||
{
|
||||
return SkRect::MakeXYWH(SkIntToScalar(aRect.x), SkIntToScalar(aRect.y),
|
||||
SkIntToScalar(aRect.width), SkIntToScalar(aRect.height));
|
||||
}
|
||||
|
||||
static inline SkIRect
|
||||
RectToSkIRect(const Rect& aRect)
|
||||
{
|
||||
return SkIRect::MakeXYWH(int32_t(aRect.x), int32_t(aRect.y),
|
||||
int32_t(aRect.width), int32_t(aRect.height));
|
||||
}
|
||||
|
||||
static inline SkIRect
|
||||
IntRectToSkIRect(const IntRect& aRect)
|
||||
{
|
||||
return SkIRect::MakeXYWH(aRect.x, aRect.y, aRect.width, aRect.height);
|
||||
}
|
||||
|
||||
static inline Point
|
||||
SkPointToPoint(const SkPoint &aPoint)
|
||||
{
|
||||
return Point(SkScalarToFloat(aPoint.x()), SkScalarToFloat(aPoint.y()));
|
||||
}
|
||||
|
||||
static inline Rect
|
||||
SkRectToRect(const SkRect &aRect)
|
||||
{
|
||||
return Rect(SkScalarToFloat(aRect.x()), SkScalarToFloat(aRect.y()),
|
||||
SkScalarToFloat(aRect.width()), SkScalarToFloat(aRect.height()));
|
||||
}
|
||||
|
||||
static inline SkShader::TileMode
|
||||
ExtendModeToTileMode(ExtendMode aMode, Axis aAxis)
|
||||
{
|
||||
switch (aMode)
|
||||
{
|
||||
case ExtendMode::CLAMP:
|
||||
return SkShader::kClamp_TileMode;
|
||||
case ExtendMode::REPEAT:
|
||||
return SkShader::kRepeat_TileMode;
|
||||
case ExtendMode::REFLECT:
|
||||
return SkShader::kMirror_TileMode;
|
||||
case ExtendMode::REPEAT_X:
|
||||
{
|
||||
return aAxis == Axis::X_AXIS
|
||||
? SkShader::kRepeat_TileMode
|
||||
: SkShader::kClamp_TileMode;
|
||||
}
|
||||
case ExtendMode::REPEAT_Y:
|
||||
{
|
||||
return aAxis == Axis::Y_AXIS
|
||||
? SkShader::kRepeat_TileMode
|
||||
: SkShader::kClamp_TileMode;
|
||||
}
|
||||
}
|
||||
return SkShader::kClamp_TileMode;
|
||||
}
|
||||
|
||||
static inline SkPaint::Hinting
|
||||
GfxHintingToSkiaHinting(FontHinting aHinting)
|
||||
{
|
||||
switch (aHinting) {
|
||||
case FontHinting::NONE:
|
||||
return SkPaint::kNo_Hinting;
|
||||
case FontHinting::LIGHT:
|
||||
return SkPaint::kSlight_Hinting;
|
||||
case FontHinting::NORMAL:
|
||||
return SkPaint::kNormal_Hinting;
|
||||
case FontHinting::FULL:
|
||||
return SkPaint::kFull_Hinting;
|
||||
}
|
||||
return SkPaint::kNormal_Hinting;
|
||||
}
|
||||
|
||||
static inline FillRule GetFillRule(SkPath::FillType aFillType)
|
||||
{
|
||||
switch (aFillType)
|
||||
{
|
||||
case SkPath::kWinding_FillType:
|
||||
return FillRule::FILL_WINDING;
|
||||
case SkPath::kEvenOdd_FillType:
|
||||
return FillRule::FILL_EVEN_ODD;
|
||||
case SkPath::kInverseWinding_FillType:
|
||||
case SkPath::kInverseEvenOdd_FillType:
|
||||
default:
|
||||
NS_WARNING("Unsupported fill type\n");
|
||||
break;
|
||||
}
|
||||
|
||||
return FillRule::FILL_EVEN_ODD;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the canvas is backed by pixels. Returns false if the canvas
|
||||
* wraps an SkPDFDocument, for example.
|
||||
*
|
||||
* Note: It is not clear whether the test used to implement this function may
|
||||
* result in it returning false in some circumstances even when the canvas
|
||||
* _is_ pixel backed. In other words maybe it is possible for such a canvas to
|
||||
* have kUnknown_SkPixelGeometry?
|
||||
*/
|
||||
static inline bool IsBackedByPixels(const SkCanvas* aCanvas)
|
||||
{
|
||||
SkSurfaceProps props(0, kUnknown_SkPixelGeometry);
|
||||
if (!aCanvas->getProps(&props) ||
|
||||
props.pixelGeometry() == kUnknown_SkPixelGeometry) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* MOZILLA_GFX_HELPERSSKIA_H_ */
|
||||
61
gfx/2d/HelpersWinFonts.h
Normal file
61
gfx/2d/HelpersWinFonts.h
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
/* -*- Mode: C++; tab-width: 20; 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/. */
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
// Cleartype can be dynamically enabled/disabled, so we have to check it
|
||||
// everytime we want to render some text.
|
||||
static BYTE
|
||||
GetSystemTextQuality()
|
||||
{
|
||||
BOOL font_smoothing;
|
||||
UINT smoothing_type;
|
||||
|
||||
if (!SystemParametersInfo(SPI_GETFONTSMOOTHING, 0, &font_smoothing, 0)) {
|
||||
return DEFAULT_QUALITY;
|
||||
}
|
||||
|
||||
if (font_smoothing) {
|
||||
if (!SystemParametersInfo(SPI_GETFONTSMOOTHINGTYPE,
|
||||
0, &smoothing_type, 0)) {
|
||||
return DEFAULT_QUALITY;
|
||||
}
|
||||
|
||||
if (smoothing_type == FE_FONTSMOOTHINGCLEARTYPE) {
|
||||
return CLEARTYPE_QUALITY;
|
||||
}
|
||||
|
||||
return ANTIALIASED_QUALITY;
|
||||
}
|
||||
|
||||
return DEFAULT_QUALITY;
|
||||
}
|
||||
|
||||
static AntialiasMode
|
||||
GetSystemDefaultAAMode()
|
||||
{
|
||||
AntialiasMode defaultMode = AntialiasMode::SUBPIXEL;
|
||||
if (gfxPrefs::DisableAllTextAA()) {
|
||||
return AntialiasMode::NONE;
|
||||
}
|
||||
|
||||
switch (GetSystemTextQuality()) {
|
||||
case CLEARTYPE_QUALITY:
|
||||
defaultMode = AntialiasMode::SUBPIXEL;
|
||||
break;
|
||||
case ANTIALIASED_QUALITY:
|
||||
defaultMode = AntialiasMode::GRAY;
|
||||
break;
|
||||
case DEFAULT_QUALITY:
|
||||
defaultMode = AntialiasMode::NONE;
|
||||
break;
|
||||
}
|
||||
|
||||
return defaultMode;
|
||||
}
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
||||
251
gfx/2d/ImageScaling.cpp
Normal file
251
gfx/2d/ImageScaling.cpp
Normal file
|
|
@ -0,0 +1,251 @@
|
|||
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "ImageScaling.h"
|
||||
#include "2D.h"
|
||||
#include "DataSurfaceHelpers.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <algorithm>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
inline uint32_t Avg2x2(uint32_t a, uint32_t b, uint32_t c, uint32_t d)
|
||||
{
|
||||
// Prepare half-adder work
|
||||
uint32_t sum = a ^ b ^ c;
|
||||
uint32_t carry = (a & b) | (a & c) | (b & c);
|
||||
|
||||
// Before shifting, mask lower order bits of each byte to avoid underflow.
|
||||
uint32_t mask = 0xfefefefe;
|
||||
|
||||
// Add d to sum and divide by 2.
|
||||
sum = (((sum ^ d) & mask) >> 1) + (sum & d);
|
||||
|
||||
// Sum is now shifted into place relative to carry, add them together.
|
||||
return (((sum ^ carry) & mask) >> 1) + (sum & carry);
|
||||
}
|
||||
|
||||
inline uint32_t Avg2(uint32_t a, uint32_t b)
|
||||
{
|
||||
// Prepare half-adder work
|
||||
uint32_t sum = a ^ b;
|
||||
uint32_t carry = (a & b);
|
||||
|
||||
// Before shifting, mask lower order bits of each byte to avoid underflow.
|
||||
uint32_t mask = 0xfefefefe;
|
||||
|
||||
// Add d to sum and divide by 2.
|
||||
return ((sum & mask) >> 1) + carry;
|
||||
}
|
||||
|
||||
void
|
||||
ImageHalfScaler::ScaleForSize(const IntSize &aSize)
|
||||
{
|
||||
uint32_t horizontalDownscales = 0;
|
||||
uint32_t verticalDownscales = 0;
|
||||
|
||||
IntSize scaleSize = mOrigSize;
|
||||
while ((scaleSize.height / 2) > aSize.height) {
|
||||
verticalDownscales++;
|
||||
scaleSize.height /= 2;
|
||||
}
|
||||
|
||||
while ((scaleSize.width / 2) > aSize.width) {
|
||||
horizontalDownscales++;
|
||||
scaleSize.width /= 2;
|
||||
}
|
||||
|
||||
if (scaleSize == mOrigSize) {
|
||||
return;
|
||||
}
|
||||
|
||||
delete [] mDataStorage;
|
||||
|
||||
IntSize internalSurfSize;
|
||||
internalSurfSize.width = max(scaleSize.width, mOrigSize.width / 2);
|
||||
internalSurfSize.height = max(scaleSize.height, mOrigSize.height / 2);
|
||||
|
||||
size_t bufLen = 0;
|
||||
mStride = GetAlignedStride<16>(internalSurfSize.width, 4);
|
||||
if (mStride > 0) {
|
||||
// Allocate 15 bytes extra to make sure we can get 16 byte alignment. We
|
||||
// should add tools for this, see bug 751696.
|
||||
bufLen = BufferSizeFromStrideAndHeight(mStride, internalSurfSize.height, 15);
|
||||
}
|
||||
|
||||
if (bufLen == 0) {
|
||||
mSize.SizeTo(0, 0);
|
||||
mDataStorage = nullptr;
|
||||
return;
|
||||
}
|
||||
mDataStorage = new uint8_t[bufLen];
|
||||
|
||||
if (uintptr_t(mDataStorage) % 16) {
|
||||
// Our storage does not start at a 16-byte boundary. Make sure mData does!
|
||||
mData = (uint8_t*)(uintptr_t(mDataStorage) +
|
||||
(16 - (uintptr_t(mDataStorage) % 16)));
|
||||
} else {
|
||||
mData = mDataStorage;
|
||||
}
|
||||
|
||||
mSize = scaleSize;
|
||||
|
||||
/* The surface we sample from might not be even sized, if it's not we will
|
||||
* ignore the last row/column. This means we lose some data but it keeps the
|
||||
* code very simple. There's also no perfect answer that provides a better
|
||||
* solution.
|
||||
*/
|
||||
IntSize currentSampledSize = mOrigSize;
|
||||
uint32_t currentSampledStride = mOrigStride;
|
||||
uint8_t *currentSampledData = mOrigData;
|
||||
|
||||
while (verticalDownscales && horizontalDownscales) {
|
||||
if (currentSampledSize.width % 2) {
|
||||
currentSampledSize.width -= 1;
|
||||
}
|
||||
if (currentSampledSize.height % 2) {
|
||||
currentSampledSize.height -= 1;
|
||||
}
|
||||
|
||||
HalfImage2D(currentSampledData, currentSampledStride, currentSampledSize,
|
||||
mData, mStride);
|
||||
|
||||
verticalDownscales--;
|
||||
horizontalDownscales--;
|
||||
currentSampledSize.width /= 2;
|
||||
currentSampledSize.height /= 2;
|
||||
currentSampledData = mData;
|
||||
currentSampledStride = mStride;
|
||||
}
|
||||
|
||||
while (verticalDownscales) {
|
||||
if (currentSampledSize.height % 2) {
|
||||
currentSampledSize.height -= 1;
|
||||
}
|
||||
|
||||
HalfImageVertical(currentSampledData, currentSampledStride, currentSampledSize,
|
||||
mData, mStride);
|
||||
|
||||
verticalDownscales--;
|
||||
currentSampledSize.height /= 2;
|
||||
currentSampledData = mData;
|
||||
currentSampledStride = mStride;
|
||||
}
|
||||
|
||||
|
||||
while (horizontalDownscales) {
|
||||
if (currentSampledSize.width % 2) {
|
||||
currentSampledSize.width -= 1;
|
||||
}
|
||||
|
||||
HalfImageHorizontal(currentSampledData, currentSampledStride, currentSampledSize,
|
||||
mData, mStride);
|
||||
|
||||
horizontalDownscales--;
|
||||
currentSampledSize.width /= 2;
|
||||
currentSampledData = mData;
|
||||
currentSampledStride = mStride;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ImageHalfScaler::HalfImage2D(uint8_t *aSource, int32_t aSourceStride,
|
||||
const IntSize &aSourceSize, uint8_t *aDest,
|
||||
uint32_t aDestStride)
|
||||
{
|
||||
#ifdef USE_SSE2
|
||||
if (Factory::HasSSE2()) {
|
||||
HalfImage2D_SSE2(aSource, aSourceStride, aSourceSize, aDest, aDestStride);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
HalfImage2D_C(aSource, aSourceStride, aSourceSize, aDest, aDestStride);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ImageHalfScaler::HalfImageVertical(uint8_t *aSource, int32_t aSourceStride,
|
||||
const IntSize &aSourceSize, uint8_t *aDest,
|
||||
uint32_t aDestStride)
|
||||
{
|
||||
#ifdef USE_SSE2
|
||||
if (Factory::HasSSE2()) {
|
||||
HalfImageVertical_SSE2(aSource, aSourceStride, aSourceSize, aDest, aDestStride);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
HalfImageVertical_C(aSource, aSourceStride, aSourceSize, aDest, aDestStride);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ImageHalfScaler::HalfImageHorizontal(uint8_t *aSource, int32_t aSourceStride,
|
||||
const IntSize &aSourceSize, uint8_t *aDest,
|
||||
uint32_t aDestStride)
|
||||
{
|
||||
#ifdef USE_SSE2
|
||||
if (Factory::HasSSE2()) {
|
||||
HalfImageHorizontal_SSE2(aSource, aSourceStride, aSourceSize, aDest, aDestStride);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
HalfImageHorizontal_C(aSource, aSourceStride, aSourceSize, aDest, aDestStride);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ImageHalfScaler::HalfImage2D_C(uint8_t *aSource, int32_t aSourceStride,
|
||||
const IntSize &aSourceSize, uint8_t *aDest,
|
||||
uint32_t aDestStride)
|
||||
{
|
||||
for (int y = 0; y < aSourceSize.height; y += 2) {
|
||||
uint32_t *storage = (uint32_t*)(aDest + (y / 2) * aDestStride);
|
||||
for (int x = 0; x < aSourceSize.width; x += 2) {
|
||||
uint8_t *upperRow = aSource + (y * aSourceStride + x * 4);
|
||||
uint8_t *lowerRow = aSource + ((y + 1) * aSourceStride + x * 4);
|
||||
|
||||
*storage++ = Avg2x2(*(uint32_t*)upperRow, *((uint32_t*)upperRow + 1),
|
||||
*(uint32_t*)lowerRow, *((uint32_t*)lowerRow + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ImageHalfScaler::HalfImageVertical_C(uint8_t *aSource, int32_t aSourceStride,
|
||||
const IntSize &aSourceSize, uint8_t *aDest,
|
||||
uint32_t aDestStride)
|
||||
{
|
||||
for (int y = 0; y < aSourceSize.height; y += 2) {
|
||||
uint32_t *storage = (uint32_t*)(aDest + (y / 2) * aDestStride);
|
||||
for (int x = 0; x < aSourceSize.width; x++) {
|
||||
uint32_t *upperRow = (uint32_t*)(aSource + (y * aSourceStride + x * 4));
|
||||
uint32_t *lowerRow = (uint32_t*)(aSource + ((y + 1) * aSourceStride + x * 4));
|
||||
|
||||
*storage++ = Avg2(*upperRow, *lowerRow);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ImageHalfScaler::HalfImageHorizontal_C(uint8_t *aSource, int32_t aSourceStride,
|
||||
const IntSize &aSourceSize, uint8_t *aDest,
|
||||
uint32_t aDestStride)
|
||||
{
|
||||
for (int y = 0; y < aSourceSize.height; y++) {
|
||||
uint32_t *storage = (uint32_t*)(aDest + y * aDestStride);
|
||||
for (int x = 0; x < aSourceSize.width; x+= 2) {
|
||||
uint32_t *pixels = (uint32_t*)(aSource + (y * aSourceStride + x * 4));
|
||||
|
||||
*storage++ = Avg2(*pixels, *(pixels + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
||||
76
gfx/2d/ImageScaling.h
Normal file
76
gfx/2d/ImageScaling.h
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
/* -*- Mode: C++; tab-width: 20; 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_GFX_IMAGESCALING_H
|
||||
#define _MOZILLA_GFX_IMAGESCALING_H
|
||||
|
||||
#include "Types.h"
|
||||
|
||||
#include <vector>
|
||||
#include "Point.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
class ImageHalfScaler
|
||||
{
|
||||
public:
|
||||
ImageHalfScaler(uint8_t *aData, int32_t aStride, const IntSize &aSize)
|
||||
: mOrigData(aData), mOrigStride(aStride), mOrigSize(aSize)
|
||||
, mDataStorage(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
~ImageHalfScaler()
|
||||
{
|
||||
delete [] mDataStorage;
|
||||
}
|
||||
|
||||
void ScaleForSize(const IntSize &aSize);
|
||||
|
||||
uint8_t *GetScaledData() const { return mData; }
|
||||
IntSize GetSize() const { return mSize; }
|
||||
uint32_t GetStride() const { return mStride; }
|
||||
|
||||
private:
|
||||
void HalfImage2D(uint8_t *aSource, int32_t aSourceStride, const IntSize &aSourceSize,
|
||||
uint8_t *aDest, uint32_t aDestStride);
|
||||
void HalfImageVertical(uint8_t *aSource, int32_t aSourceStride, const IntSize &aSourceSize,
|
||||
uint8_t *aDest, uint32_t aDestStride);
|
||||
void HalfImageHorizontal(uint8_t *aSource, int32_t aSourceStride, const IntSize &aSourceSize,
|
||||
uint8_t *aDest, uint32_t aDestStride);
|
||||
|
||||
// This is our SSE2 scaling function. Our destination must always be 16-byte
|
||||
// aligned and use a 16-byte aligned stride.
|
||||
void HalfImage2D_SSE2(uint8_t *aSource, int32_t aSourceStride, const IntSize &aSourceSize,
|
||||
uint8_t *aDest, uint32_t aDestStride);
|
||||
void HalfImageVertical_SSE2(uint8_t *aSource, int32_t aSourceStride, const IntSize &aSourceSize,
|
||||
uint8_t *aDest, uint32_t aDestStride);
|
||||
void HalfImageHorizontal_SSE2(uint8_t *aSource, int32_t aSourceStride, const IntSize &aSourceSize,
|
||||
uint8_t *aDest, uint32_t aDestStride);
|
||||
|
||||
void HalfImage2D_C(uint8_t *aSource, int32_t aSourceStride, const IntSize &aSourceSize,
|
||||
uint8_t *aDest, uint32_t aDestStride);
|
||||
void HalfImageVertical_C(uint8_t *aSource, int32_t aSourceStride, const IntSize &aSourceSize,
|
||||
uint8_t *aDest, uint32_t aDestStride);
|
||||
void HalfImageHorizontal_C(uint8_t *aSource, int32_t aSourceStride, const IntSize &aSourceSize,
|
||||
uint8_t *aDest, uint32_t aDestStride);
|
||||
|
||||
uint8_t *mOrigData;
|
||||
int32_t mOrigStride;
|
||||
IntSize mOrigSize;
|
||||
|
||||
uint8_t *mDataStorage;
|
||||
// Guaranteed 16-byte aligned
|
||||
uint8_t *mData;
|
||||
IntSize mSize;
|
||||
// Guaranteed 16-byte aligned
|
||||
uint32_t mStride;
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
330
gfx/2d/ImageScalingSSE2.cpp
Normal file
330
gfx/2d/ImageScalingSSE2.cpp
Normal file
|
|
@ -0,0 +1,330 @@
|
|||
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "ImageScaling.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
|
||||
#include "SSEHelpers.h"
|
||||
|
||||
/* The functions below use the following system for averaging 4 pixels:
|
||||
*
|
||||
* The first observation is that a half-adder is implemented as follows:
|
||||
* R = S + 2C or in the case of a and b (a ^ b) + ((a & b) << 1);
|
||||
*
|
||||
* This can be trivially extended to three pixels by observaring that when
|
||||
* doing (a ^ b ^ c) as the sum, the carry is simply the bitwise-or of the
|
||||
* carries of the individual numbers, since the sum of 3 bits can only ever
|
||||
* have a carry of one.
|
||||
*
|
||||
* We then observe that the average is then ((carry << 1) + sum) >> 1, or,
|
||||
* assuming eliminating overflows and underflows, carry + (sum >> 1).
|
||||
*
|
||||
* We now average our existing sum with the fourth number, so we get:
|
||||
* sum2 = (sum + d) >> 1 or (sum >> 1) + (d >> 1).
|
||||
*
|
||||
* We now observe that our sum has been moved into place relative to the
|
||||
* carry, so we can now average with the carry to get the final 4 input
|
||||
* average: avg = (sum2 + carry) >> 1;
|
||||
*
|
||||
* Or to reverse the proof:
|
||||
* avg = ((sum >> 1) + carry + d >> 1) >> 1
|
||||
* avg = ((a + b + c) >> 1 + d >> 1) >> 1
|
||||
* avg = ((a + b + c + d) >> 2)
|
||||
*
|
||||
* An additional fact used in the SSE versions is the concept that we can
|
||||
* trivially convert a rounded average to a truncated average:
|
||||
*
|
||||
* We have:
|
||||
* f(a, b) = (a + b + 1) >> 1
|
||||
*
|
||||
* And want:
|
||||
* g(a, b) = (a + b) >> 1
|
||||
*
|
||||
* Observe:
|
||||
* ~f(~a, ~b) == ~((~a + ~b + 1) >> 1)
|
||||
* == ~((-a - 1 + -b - 1 + 1) >> 1)
|
||||
* == ~((-a - 1 + -b) >> 1)
|
||||
* == ~((-(a + b) - 1) >> 1)
|
||||
* == ~((~(a + b)) >> 1)
|
||||
* == (a + b) >> 1
|
||||
* == g(a, b)
|
||||
*/
|
||||
|
||||
MOZ_ALWAYS_INLINE __m128i _mm_not_si128(__m128i arg)
|
||||
{
|
||||
__m128i minusone = _mm_set1_epi32(0xffffffff);
|
||||
return _mm_xor_si128(arg, minusone);
|
||||
}
|
||||
|
||||
/* We have to pass pointers here, MSVC does not allow passing more than 3
|
||||
* __m128i arguments on the stack. And it does not allow 16-byte aligned
|
||||
* stack variables. This inlines properly on MSVC 2010. It does -not- inline
|
||||
* with just the inline directive.
|
||||
*/
|
||||
MOZ_ALWAYS_INLINE __m128i avg_sse2_8x2(__m128i *a, __m128i *b, __m128i *c, __m128i *d)
|
||||
{
|
||||
#define shuf1 _MM_SHUFFLE(2, 0, 2, 0)
|
||||
#define shuf2 _MM_SHUFFLE(3, 1, 3, 1)
|
||||
|
||||
// This cannot be an inline function as the __Imm argument to _mm_shuffle_ps
|
||||
// needs to be a compile time constant.
|
||||
#define shuffle_si128(arga, argb, imm) \
|
||||
_mm_castps_si128(_mm_shuffle_ps(_mm_castsi128_ps((arga)), _mm_castsi128_ps((argb)), (imm)));
|
||||
|
||||
__m128i t = shuffle_si128(*a, *b, shuf1);
|
||||
*b = shuffle_si128(*a, *b, shuf2);
|
||||
*a = t;
|
||||
t = shuffle_si128(*c, *d, shuf1);
|
||||
*d = shuffle_si128(*c, *d, shuf2);
|
||||
*c = t;
|
||||
|
||||
#undef shuf1
|
||||
#undef shuf2
|
||||
#undef shuffle_si128
|
||||
|
||||
__m128i sum = _mm_xor_si128(*a, _mm_xor_si128(*b, *c));
|
||||
|
||||
__m128i carry = _mm_or_si128(_mm_and_si128(*a, *b), _mm_or_si128(_mm_and_si128(*a, *c), _mm_and_si128(*b, *c)));
|
||||
|
||||
sum = _mm_avg_epu8(_mm_not_si128(sum), _mm_not_si128(*d));
|
||||
|
||||
return _mm_not_si128(_mm_avg_epu8(sum, _mm_not_si128(carry)));
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE __m128i avg_sse2_4x2_4x1(__m128i a, __m128i b)
|
||||
{
|
||||
return _mm_not_si128(_mm_avg_epu8(_mm_not_si128(a), _mm_not_si128(b)));
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE __m128i avg_sse2_8x1_4x1(__m128i a, __m128i b)
|
||||
{
|
||||
__m128i t = _mm_castps_si128(_mm_shuffle_ps(_mm_castsi128_ps(a), _mm_castsi128_ps(b), _MM_SHUFFLE(3, 1, 3, 1)));
|
||||
b = _mm_castps_si128(_mm_shuffle_ps(_mm_castsi128_ps(a), _mm_castsi128_ps(b), _MM_SHUFFLE(2, 0, 2, 0)));
|
||||
a = t;
|
||||
|
||||
return _mm_not_si128(_mm_avg_epu8(_mm_not_si128(a), _mm_not_si128(b)));
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE uint32_t Avg2x2(uint32_t a, uint32_t b, uint32_t c, uint32_t d)
|
||||
{
|
||||
uint32_t sum = a ^ b ^ c;
|
||||
uint32_t carry = (a & b) | (a & c) | (b & c);
|
||||
|
||||
uint32_t mask = 0xfefefefe;
|
||||
|
||||
// Not having a byte based average instruction means we should mask to avoid
|
||||
// underflow.
|
||||
sum = (((sum ^ d) & mask) >> 1) + (sum & d);
|
||||
|
||||
return (((sum ^ carry) & mask) >> 1) + (sum & carry);
|
||||
}
|
||||
|
||||
// Simple 2 pixel average version of the function above.
|
||||
MOZ_ALWAYS_INLINE uint32_t Avg2(uint32_t a, uint32_t b)
|
||||
{
|
||||
uint32_t sum = a ^ b;
|
||||
uint32_t carry = (a & b);
|
||||
|
||||
uint32_t mask = 0xfefefefe;
|
||||
|
||||
return ((sum & mask) >> 1) + carry;
|
||||
}
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
void
|
||||
ImageHalfScaler::HalfImage2D_SSE2(uint8_t *aSource, int32_t aSourceStride,
|
||||
const IntSize &aSourceSize, uint8_t *aDest,
|
||||
uint32_t aDestStride)
|
||||
{
|
||||
const int Bpp = 4;
|
||||
|
||||
for (int y = 0; y < aSourceSize.height; y += 2) {
|
||||
__m128i *storage = (__m128i*)(aDest + (y / 2) * aDestStride);
|
||||
int x = 0;
|
||||
// Run a loop depending on alignment.
|
||||
if (!(uintptr_t(aSource + (y * aSourceStride)) % 16) &&
|
||||
!(uintptr_t(aSource + ((y + 1) * aSourceStride)) % 16)) {
|
||||
for (; x < (aSourceSize.width - 7); x += 8) {
|
||||
__m128i *upperRow = (__m128i*)(aSource + (y * aSourceStride + x * Bpp));
|
||||
__m128i *lowerRow = (__m128i*)(aSource + ((y + 1) * aSourceStride + x * Bpp));
|
||||
|
||||
__m128i a = _mm_load_si128(upperRow);
|
||||
__m128i b = _mm_load_si128(upperRow + 1);
|
||||
__m128i c = _mm_load_si128(lowerRow);
|
||||
__m128i d = _mm_load_si128(lowerRow + 1);
|
||||
|
||||
*storage++ = avg_sse2_8x2(&a, &b, &c, &d);
|
||||
}
|
||||
} else if (!(uintptr_t(aSource + (y * aSourceStride)) % 16)) {
|
||||
for (; x < (aSourceSize.width - 7); x += 8) {
|
||||
__m128i *upperRow = (__m128i*)(aSource + (y * aSourceStride + x * Bpp));
|
||||
__m128i *lowerRow = (__m128i*)(aSource + ((y + 1) * aSourceStride + x * Bpp));
|
||||
|
||||
__m128i a = _mm_load_si128(upperRow);
|
||||
__m128i b = _mm_load_si128(upperRow + 1);
|
||||
__m128i c = loadUnaligned128(lowerRow);
|
||||
__m128i d = loadUnaligned128(lowerRow + 1);
|
||||
|
||||
*storage++ = avg_sse2_8x2(&a, &b, &c, &d);
|
||||
}
|
||||
} else if (!(uintptr_t(aSource + ((y + 1) * aSourceStride)) % 16)) {
|
||||
for (; x < (aSourceSize.width - 7); x += 8) {
|
||||
__m128i *upperRow = (__m128i*)(aSource + (y * aSourceStride + x * Bpp));
|
||||
__m128i *lowerRow = (__m128i*)(aSource + ((y + 1) * aSourceStride + x * Bpp));
|
||||
|
||||
__m128i a = loadUnaligned128((__m128i*)upperRow);
|
||||
__m128i b = loadUnaligned128((__m128i*)upperRow + 1);
|
||||
__m128i c = _mm_load_si128((__m128i*)lowerRow);
|
||||
__m128i d = _mm_load_si128((__m128i*)lowerRow + 1);
|
||||
|
||||
*storage++ = avg_sse2_8x2(&a, &b, &c, &d);
|
||||
}
|
||||
} else {
|
||||
for (; x < (aSourceSize.width - 7); x += 8) {
|
||||
__m128i *upperRow = (__m128i*)(aSource + (y * aSourceStride + x * Bpp));
|
||||
__m128i *lowerRow = (__m128i*)(aSource + ((y + 1) * aSourceStride + x * Bpp));
|
||||
|
||||
__m128i a = loadUnaligned128(upperRow);
|
||||
__m128i b = loadUnaligned128(upperRow + 1);
|
||||
__m128i c = loadUnaligned128(lowerRow);
|
||||
__m128i d = loadUnaligned128(lowerRow + 1);
|
||||
|
||||
*storage++ = avg_sse2_8x2(&a, &b, &c, &d);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t *unalignedStorage = (uint32_t*)storage;
|
||||
// Take care of the final pixels, we know there's an even number of pixels
|
||||
// in the source rectangle. We use a 2x2 'simd' implementation for this.
|
||||
//
|
||||
// Potentially we only have to do this in the last row since overflowing
|
||||
// 8 pixels in an earlier row would appear to be harmless as it doesn't
|
||||
// touch invalid memory. Even when reading and writing to the same surface.
|
||||
// in practice we only do this when doing an additional downscale pass, and
|
||||
// in this situation we have unused stride to write into harmlessly.
|
||||
// I do not believe the additional code complexity would be worth it though.
|
||||
for (; x < aSourceSize.width; x += 2) {
|
||||
uint8_t *upperRow = aSource + (y * aSourceStride + x * Bpp);
|
||||
uint8_t *lowerRow = aSource + ((y + 1) * aSourceStride + x * Bpp);
|
||||
|
||||
*unalignedStorage++ = Avg2x2(*(uint32_t*)upperRow, *((uint32_t*)upperRow + 1),
|
||||
*(uint32_t*)lowerRow, *((uint32_t*)lowerRow + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ImageHalfScaler::HalfImageVertical_SSE2(uint8_t *aSource, int32_t aSourceStride,
|
||||
const IntSize &aSourceSize, uint8_t *aDest,
|
||||
uint32_t aDestStride)
|
||||
{
|
||||
for (int y = 0; y < aSourceSize.height; y += 2) {
|
||||
__m128i *storage = (__m128i*)(aDest + (y / 2) * aDestStride);
|
||||
int x = 0;
|
||||
// Run a loop depending on alignment.
|
||||
if (!(uintptr_t(aSource + (y * aSourceStride)) % 16) &&
|
||||
!(uintptr_t(aSource + ((y + 1) * aSourceStride)) % 16)) {
|
||||
for (; x < (aSourceSize.width - 3); x += 4) {
|
||||
uint8_t *upperRow = aSource + (y * aSourceStride + x * 4);
|
||||
uint8_t *lowerRow = aSource + ((y + 1) * aSourceStride + x * 4);
|
||||
|
||||
__m128i a = _mm_load_si128((__m128i*)upperRow);
|
||||
__m128i b = _mm_load_si128((__m128i*)lowerRow);
|
||||
|
||||
*storage++ = avg_sse2_4x2_4x1(a, b);
|
||||
}
|
||||
} else if (!(uintptr_t(aSource + (y * aSourceStride)) % 16)) {
|
||||
// This line doesn't align well.
|
||||
for (; x < (aSourceSize.width - 3); x += 4) {
|
||||
uint8_t *upperRow = aSource + (y * aSourceStride + x * 4);
|
||||
uint8_t *lowerRow = aSource + ((y + 1) * aSourceStride + x * 4);
|
||||
|
||||
__m128i a = _mm_load_si128((__m128i*)upperRow);
|
||||
__m128i b = loadUnaligned128((__m128i*)lowerRow);
|
||||
|
||||
*storage++ = avg_sse2_4x2_4x1(a, b);
|
||||
}
|
||||
} else if (!(uintptr_t(aSource + ((y + 1) * aSourceStride)) % 16)) {
|
||||
for (; x < (aSourceSize.width - 3); x += 4) {
|
||||
uint8_t *upperRow = aSource + (y * aSourceStride + x * 4);
|
||||
uint8_t *lowerRow = aSource + ((y + 1) * aSourceStride + x * 4);
|
||||
|
||||
__m128i a = loadUnaligned128((__m128i*)upperRow);
|
||||
__m128i b = _mm_load_si128((__m128i*)lowerRow);
|
||||
|
||||
*storage++ = avg_sse2_4x2_4x1(a, b);
|
||||
}
|
||||
} else {
|
||||
for (; x < (aSourceSize.width - 3); x += 4) {
|
||||
uint8_t *upperRow = aSource + (y * aSourceStride + x * 4);
|
||||
uint8_t *lowerRow = aSource + ((y + 1) * aSourceStride + x * 4);
|
||||
|
||||
__m128i a = loadUnaligned128((__m128i*)upperRow);
|
||||
__m128i b = loadUnaligned128((__m128i*)lowerRow);
|
||||
|
||||
*storage++ = avg_sse2_4x2_4x1(a, b);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t *unalignedStorage = (uint32_t*)storage;
|
||||
// Take care of the final pixels, we know there's an even number of pixels
|
||||
// in the source rectangle.
|
||||
//
|
||||
// Similar overflow considerations are valid as in the previous function.
|
||||
for (; x < aSourceSize.width; x++) {
|
||||
uint8_t *upperRow = aSource + (y * aSourceStride + x * 4);
|
||||
uint8_t *lowerRow = aSource + ((y + 1) * aSourceStride + x * 4);
|
||||
|
||||
*unalignedStorage++ = Avg2(*(uint32_t*)upperRow, *(uint32_t*)lowerRow);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ImageHalfScaler::HalfImageHorizontal_SSE2(uint8_t *aSource, int32_t aSourceStride,
|
||||
const IntSize &aSourceSize, uint8_t *aDest,
|
||||
uint32_t aDestStride)
|
||||
{
|
||||
for (int y = 0; y < aSourceSize.height; y++) {
|
||||
__m128i *storage = (__m128i*)(aDest + (y * aDestStride));
|
||||
int x = 0;
|
||||
// Run a loop depending on alignment.
|
||||
if (!(uintptr_t(aSource + (y * aSourceStride)) % 16)) {
|
||||
for (; x < (aSourceSize.width - 7); x += 8) {
|
||||
__m128i* pixels = (__m128i*)(aSource + (y * aSourceStride + x * 4));
|
||||
|
||||
__m128i a = _mm_load_si128(pixels);
|
||||
__m128i b = _mm_load_si128(pixels + 1);
|
||||
|
||||
*storage++ = avg_sse2_8x1_4x1(a, b);
|
||||
}
|
||||
} else {
|
||||
for (; x < (aSourceSize.width - 7); x += 8) {
|
||||
__m128i* pixels = (__m128i*)(aSource + (y * aSourceStride + x * 4));
|
||||
|
||||
__m128i a = loadUnaligned128(pixels);
|
||||
__m128i b = loadUnaligned128(pixels + 1);
|
||||
|
||||
*storage++ = avg_sse2_8x1_4x1(a, b);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t *unalignedStorage = (uint32_t*)storage;
|
||||
// Take care of the final pixels, we know there's an even number of pixels
|
||||
// in the source rectangle.
|
||||
//
|
||||
// Similar overflow considerations are valid as in the previous function.
|
||||
for (; x < aSourceSize.width; x += 2) {
|
||||
uint32_t *pixels = (uint32_t*)(aSource + (y * aSourceStride + x * 4));
|
||||
|
||||
*unalignedStorage++ = Avg2(*pixels, *(pixels + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
||||
193
gfx/2d/IterableArena.h
Normal file
193
gfx/2d/IterableArena.h
Normal file
|
|
@ -0,0 +1,193 @@
|
|||
/* -*- Mode: C++; tab-width: 20; 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_GFX_ITERABLEARENA_H_
|
||||
#define MOZILLA_GFX_ITERABLEARENA_H_
|
||||
|
||||
#include "mozilla/Move.h"
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/gfx/Logging.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <vector>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
/// A simple pool allocator for plain data structures.
|
||||
///
|
||||
/// Beware that the pool will not attempt to run the destructors. It is the
|
||||
/// responsibility of the user of this class to either use objects with no
|
||||
/// destructor or to manually call the allocated objects destructors.
|
||||
/// If the pool is growable, its allocated objects must be safely moveable in
|
||||
/// in memory (through memcpy).
|
||||
class IterableArena {
|
||||
protected:
|
||||
struct Header
|
||||
{
|
||||
size_t mBlocSize;
|
||||
};
|
||||
public:
|
||||
enum ArenaType {
|
||||
FIXED_SIZE,
|
||||
GROWABLE
|
||||
};
|
||||
|
||||
IterableArena(ArenaType aType, size_t aStorageSize)
|
||||
: mSize(aStorageSize)
|
||||
, mCursor(0)
|
||||
, mIsGrowable(aType == GROWABLE)
|
||||
{
|
||||
if (mSize == 0) {
|
||||
mSize = 128;
|
||||
}
|
||||
|
||||
mStorage = (uint8_t*)malloc(mSize);
|
||||
if (mStorage == nullptr) {
|
||||
gfxCriticalError() << "Not enough Memory allocate a memory pool of size " << aStorageSize;
|
||||
MOZ_CRASH("GFX: Out of memory IterableArena");
|
||||
}
|
||||
}
|
||||
|
||||
~IterableArena()
|
||||
{
|
||||
free(mStorage);
|
||||
}
|
||||
|
||||
/// Constructs a new item in the pool and returns a positive offset in case of
|
||||
/// success.
|
||||
///
|
||||
/// The offset never changes even if the storage is reallocated, so users
|
||||
/// of this class should prefer storing offsets rather than direct pointers
|
||||
/// to the allocated objects.
|
||||
/// Alloc can cause the storage to be reallocated if the pool was initialized
|
||||
/// with IterableArena::GROWABLE.
|
||||
/// If for any reason the pool fails to allocate enough space for the new item
|
||||
/// Alloc returns a negative offset and the object's constructor is not called.
|
||||
template<typename T, typename... Args>
|
||||
ptrdiff_t
|
||||
Alloc(Args&&... aArgs)
|
||||
{
|
||||
void* storage = nullptr;
|
||||
auto offset = AllocRaw(sizeof(T), &storage);
|
||||
if (offset < 0) {
|
||||
return offset;
|
||||
}
|
||||
new (storage) T(Forward<Args>(aArgs)...);
|
||||
return offset;
|
||||
}
|
||||
|
||||
ptrdiff_t AllocRaw(size_t aSize, void** aOutPtr = nullptr)
|
||||
{
|
||||
const size_t blocSize = AlignedSize(sizeof(Header) + aSize);
|
||||
|
||||
if (AlignedSize(mCursor + blocSize) > mSize) {
|
||||
if (!mIsGrowable) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t newSize = mSize * 2;
|
||||
while (AlignedSize(mCursor + blocSize) > newSize) {
|
||||
newSize *= 2;
|
||||
}
|
||||
|
||||
uint8_t* newStorage = (uint8_t*)realloc(mStorage, newSize);
|
||||
if (!newStorage) {
|
||||
gfxCriticalError() << "Not enough Memory to grow the memory pool, size: " << newSize;
|
||||
return -1;
|
||||
}
|
||||
|
||||
mStorage = newStorage;
|
||||
mSize = newSize;
|
||||
}
|
||||
ptrdiff_t offset = mCursor;
|
||||
GetHeader(offset)->mBlocSize = blocSize;
|
||||
mCursor += blocSize;
|
||||
if (aOutPtr) {
|
||||
*aOutPtr = GetStorage(offset);
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
/// Get access to an allocated item at a given offset (only use offsets returned
|
||||
/// by Alloc or AllocRaw).
|
||||
///
|
||||
/// If the pool is growable, the returned pointer is only valid temporarily. The
|
||||
/// underlying storage can be reallocated in Alloc or AllocRaw, so do not keep
|
||||
/// these pointers around and store the offset instead.
|
||||
void* GetStorage(ptrdiff_t offset = 0)
|
||||
{
|
||||
MOZ_ASSERT(offset >= 0);
|
||||
MOZ_ASSERT(offset < mCursor);
|
||||
return offset >= 0 ? mStorage + offset + sizeof(Header) : nullptr;
|
||||
}
|
||||
|
||||
/// Clears the storage without running any destructor and without deallocating it.
|
||||
void Clear()
|
||||
{
|
||||
mCursor = 0;
|
||||
}
|
||||
|
||||
/// Iterate over the elements allocated in this pool.
|
||||
///
|
||||
/// Takes a lambda or function object accepting a void* as parameter.
|
||||
template<typename Func>
|
||||
void ForEach(Func cb)
|
||||
{
|
||||
Iterator it;
|
||||
while (void* ptr = it.Next(this)) {
|
||||
cb(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
/// A simple iterator over an arena.
|
||||
class Iterator {
|
||||
public:
|
||||
Iterator()
|
||||
: mCursor(0)
|
||||
{}
|
||||
|
||||
void* Next(IterableArena* aArena)
|
||||
{
|
||||
if (mCursor >= aArena->mCursor) {
|
||||
return nullptr;
|
||||
}
|
||||
void* result = aArena->GetStorage(mCursor);
|
||||
const size_t blocSize = aArena->GetHeader(mCursor)->mBlocSize;
|
||||
MOZ_ASSERT(blocSize != 0);
|
||||
mCursor += blocSize;
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
ptrdiff_t mCursor;
|
||||
};
|
||||
|
||||
protected:
|
||||
Header* GetHeader(ptrdiff_t offset)
|
||||
{
|
||||
return (Header*) (mStorage + offset);
|
||||
}
|
||||
|
||||
size_t AlignedSize(size_t aSize) const
|
||||
{
|
||||
const size_t alignment = sizeof(uintptr_t);
|
||||
return aSize + (alignment - (aSize % alignment)) % alignment;
|
||||
}
|
||||
|
||||
uint8_t* mStorage;
|
||||
uint32_t mSize;
|
||||
ptrdiff_t mCursor;
|
||||
bool mIsGrowable;
|
||||
|
||||
friend class Iterator;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
} // namespace
|
||||
|
||||
#endif
|
||||
288
gfx/2d/JobScheduler.cpp
Normal file
288
gfx/2d/JobScheduler.cpp
Normal file
|
|
@ -0,0 +1,288 @@
|
|||
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "JobScheduler.h"
|
||||
#include "Logging.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
JobScheduler* JobScheduler::sSingleton = nullptr;
|
||||
|
||||
bool JobScheduler::Init(uint32_t aNumThreads, uint32_t aNumQueues)
|
||||
{
|
||||
MOZ_ASSERT(!sSingleton);
|
||||
MOZ_ASSERT(aNumThreads >= aNumQueues);
|
||||
|
||||
sSingleton = new JobScheduler();
|
||||
sSingleton->mNextQueue = 0;
|
||||
|
||||
for (uint32_t i = 0; i < aNumQueues; ++i) {
|
||||
sSingleton->mDrawingQueues.push_back(new MultiThreadedJobQueue());
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < aNumThreads; ++i) {
|
||||
sSingleton->mWorkerThreads.push_back(WorkerThread::Create(sSingleton->mDrawingQueues[i%aNumQueues]));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void JobScheduler::ShutDown()
|
||||
{
|
||||
MOZ_ASSERT(IsEnabled());
|
||||
if (!IsEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto queue : sSingleton->mDrawingQueues) {
|
||||
queue->ShutDown();
|
||||
delete queue;
|
||||
}
|
||||
|
||||
for (WorkerThread* thread : sSingleton->mWorkerThreads) {
|
||||
// this will block until the thread is joined.
|
||||
delete thread;
|
||||
}
|
||||
|
||||
sSingleton->mWorkerThreads.clear();
|
||||
delete sSingleton;
|
||||
sSingleton = nullptr;
|
||||
}
|
||||
|
||||
JobStatus
|
||||
JobScheduler::ProcessJob(Job* aJob)
|
||||
{
|
||||
MOZ_ASSERT(aJob);
|
||||
auto status = aJob->Run();
|
||||
if (status == JobStatus::Error || status == JobStatus::Complete) {
|
||||
delete aJob;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
void
|
||||
JobScheduler::SubmitJob(Job* aJob)
|
||||
{
|
||||
MOZ_ASSERT(aJob);
|
||||
RefPtr<SyncObject> start = aJob->GetStartSync();
|
||||
if (start && start->Register(aJob)) {
|
||||
// The Job buffer starts with a non-signaled sync object, it
|
||||
// is now registered in the list of task buffers waiting on the
|
||||
// sync object, so we should not place it in the queue.
|
||||
return;
|
||||
}
|
||||
|
||||
GetQueueForJob(aJob)->SubmitJob(aJob);
|
||||
}
|
||||
|
||||
void
|
||||
JobScheduler::Join(SyncObject* aCompletion)
|
||||
{
|
||||
RefPtr<EventObject> waitForCompletion = new EventObject();
|
||||
JobScheduler::SubmitJob(new SetEventJob(waitForCompletion, aCompletion));
|
||||
waitForCompletion->Wait();
|
||||
}
|
||||
|
||||
MultiThreadedJobQueue*
|
||||
JobScheduler::GetQueueForJob(Job* aJob)
|
||||
{
|
||||
return aJob->IsPinnedToAThread() ? aJob->GetWorkerThread()->GetJobQueue()
|
||||
: GetDrawingQueue();
|
||||
}
|
||||
|
||||
Job::Job(SyncObject* aStart, SyncObject* aCompletion, WorkerThread* aThread)
|
||||
: mNextWaitingJob(nullptr)
|
||||
, mStartSync(aStart)
|
||||
, mCompletionSync(aCompletion)
|
||||
, mPinToThread(aThread)
|
||||
{
|
||||
if (mStartSync) {
|
||||
mStartSync->AddSubsequent(this);
|
||||
}
|
||||
if (mCompletionSync) {
|
||||
mCompletionSync->AddPrerequisite(this);
|
||||
}
|
||||
}
|
||||
|
||||
Job::~Job()
|
||||
{
|
||||
if (mCompletionSync) {
|
||||
//printf(" -- Job %p dtor completion %p\n", this, mCompletionSync);
|
||||
mCompletionSync->Signal();
|
||||
mCompletionSync = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
JobStatus
|
||||
SetEventJob::Run()
|
||||
{
|
||||
mEvent->Set();
|
||||
return JobStatus::Complete;
|
||||
}
|
||||
|
||||
SetEventJob::SetEventJob(EventObject* aEvent,
|
||||
SyncObject* aStart, SyncObject* aCompletion,
|
||||
WorkerThread* aWorker)
|
||||
: Job(aStart, aCompletion, aWorker)
|
||||
, mEvent(aEvent)
|
||||
{}
|
||||
|
||||
SetEventJob::~SetEventJob()
|
||||
{}
|
||||
|
||||
SyncObject::SyncObject(uint32_t aNumPrerequisites)
|
||||
: mSignals(aNumPrerequisites)
|
||||
, mFirstWaitingJob(nullptr)
|
||||
#ifdef DEBUG
|
||||
, mNumPrerequisites(aNumPrerequisites)
|
||||
, mAddedPrerequisites(0)
|
||||
#endif
|
||||
{}
|
||||
|
||||
SyncObject::~SyncObject()
|
||||
{
|
||||
MOZ_ASSERT(mFirstWaitingJob == nullptr);
|
||||
}
|
||||
|
||||
bool
|
||||
SyncObject::Register(Job* aJob)
|
||||
{
|
||||
MOZ_ASSERT(aJob);
|
||||
|
||||
// For now, ensure that when we schedule the first subsequent, we have already
|
||||
// created all of the prerequisites. This is an arbitrary restriction because
|
||||
// we specify the number of prerequisites in the constructor, but in the typical
|
||||
// scenario, if the assertion FreezePrerequisite blows up here it probably means
|
||||
// we got the initial nmber of prerequisites wrong. We can decide to remove
|
||||
// this restriction if needed.
|
||||
FreezePrerequisites();
|
||||
|
||||
int32_t signals = mSignals;
|
||||
|
||||
if (signals > 0) {
|
||||
AddWaitingJob(aJob);
|
||||
// Since Register and Signal can be called concurrently, it can happen that
|
||||
// reading mSignals in Register happens before decrementing mSignals in Signal,
|
||||
// but SubmitWaitingJobs happens before AddWaitingJob. This ordering means
|
||||
// the SyncObject ends up in the signaled state with a task sitting in the
|
||||
// waiting list. To prevent that we check mSignals a second time and submit
|
||||
// again if signals reached zero in the mean time.
|
||||
// We do this instead of holding a mutex around mSignals+mJobs to reduce
|
||||
// lock contention.
|
||||
int32_t signals2 = mSignals;
|
||||
if (signals2 == 0) {
|
||||
SubmitWaitingJobs();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
SyncObject::Signal()
|
||||
{
|
||||
int32_t signals = --mSignals;
|
||||
MOZ_ASSERT(signals >= 0);
|
||||
|
||||
if (signals == 0) {
|
||||
SubmitWaitingJobs();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SyncObject::AddWaitingJob(Job* aJob)
|
||||
{
|
||||
// Push (using atomics) the task into the list of waiting tasks.
|
||||
for (;;) {
|
||||
Job* first = mFirstWaitingJob;
|
||||
aJob->mNextWaitingJob = first;
|
||||
if (mFirstWaitingJob.compareExchange(first, aJob)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SyncObject::SubmitWaitingJobs()
|
||||
{
|
||||
// Scheduling the tasks can cause code that modifies <this>'s reference
|
||||
// count to run concurrently, and cause the caller of this function to
|
||||
// be owned by another thread. We need to make sure the reference count
|
||||
// does not reach 0 on another thread before the end of this method, so
|
||||
// hold a strong ref to prevent that!
|
||||
RefPtr<SyncObject> kungFuDeathGrip(this);
|
||||
|
||||
// First atomically swap mFirstWaitingJob and waitingJobs...
|
||||
Job* waitingJobs = nullptr;
|
||||
for (;;) {
|
||||
waitingJobs = mFirstWaitingJob;
|
||||
if (mFirstWaitingJob.compareExchange(waitingJobs, nullptr)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// ... and submit all of the waiting tasks in waitingJob now that they belong
|
||||
// to this thread.
|
||||
while (waitingJobs) {
|
||||
Job* next = waitingJobs->mNextWaitingJob;
|
||||
waitingJobs->mNextWaitingJob = nullptr;
|
||||
JobScheduler::GetQueueForJob(waitingJobs)->SubmitJob(waitingJobs);
|
||||
waitingJobs = next;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
SyncObject::IsSignaled()
|
||||
{
|
||||
return mSignals == 0;
|
||||
}
|
||||
|
||||
void
|
||||
SyncObject::FreezePrerequisites()
|
||||
{
|
||||
MOZ_ASSERT(mAddedPrerequisites == mNumPrerequisites);
|
||||
}
|
||||
|
||||
void
|
||||
SyncObject::AddPrerequisite(Job* aJob)
|
||||
{
|
||||
MOZ_ASSERT(++mAddedPrerequisites <= mNumPrerequisites);
|
||||
}
|
||||
|
||||
void
|
||||
SyncObject::AddSubsequent(Job* aJob)
|
||||
{
|
||||
}
|
||||
|
||||
WorkerThread::WorkerThread(MultiThreadedJobQueue* aJobQueue)
|
||||
: mQueue(aJobQueue)
|
||||
{
|
||||
aJobQueue->RegisterThread();
|
||||
}
|
||||
|
||||
void
|
||||
WorkerThread::Run()
|
||||
{
|
||||
SetName("gfx worker");
|
||||
|
||||
for (;;) {
|
||||
Job* commands = nullptr;
|
||||
if (!mQueue->WaitForJob(commands)) {
|
||||
mQueue->UnregisterThread();
|
||||
return;
|
||||
}
|
||||
|
||||
JobStatus status = JobScheduler::ProcessJob(commands);
|
||||
|
||||
if (status == JobStatus::Error) {
|
||||
// Don't try to handle errors for now, but that's open to discussions.
|
||||
// I expect errors to be mostly OOM issues.
|
||||
gfxDevCrash(LogReason::JobStatusError) << "Invalid job status " << (int)status;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} //namespace
|
||||
} //namespace
|
||||
257
gfx/2d/JobScheduler.h
Normal file
257
gfx/2d/JobScheduler.h
Normal file
|
|
@ -0,0 +1,257 @@
|
|||
/* -*- Mode: C++; tab-width: 20; 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_GFX_TASKSCHEDULER_H_
|
||||
#define MOZILLA_GFX_TASKSCHEDULER_H_
|
||||
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "mozilla/gfx/Types.h"
|
||||
#include "mozilla/RefCounted.h"
|
||||
|
||||
#ifdef WIN32
|
||||
#include "mozilla/gfx/JobScheduler_win32.h"
|
||||
#else
|
||||
#include "mozilla/gfx/JobScheduler_posix.h"
|
||||
#endif
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
class MultiThreadedJobQueue;
|
||||
class SyncObject;
|
||||
class WorkerThread;
|
||||
|
||||
class JobScheduler {
|
||||
public:
|
||||
/// Return one of the queues that the drawing worker threads pull from, chosen
|
||||
/// pseudo-randomly.
|
||||
static MultiThreadedJobQueue* GetDrawingQueue()
|
||||
{
|
||||
return sSingleton->mDrawingQueues[
|
||||
sSingleton->mNextQueue++ % sSingleton->mDrawingQueues.size()
|
||||
];
|
||||
}
|
||||
|
||||
/// Return one of the queues that the drawing worker threads pull from with a
|
||||
/// hash to choose the queue.
|
||||
///
|
||||
/// Calling this function several times with the same hash will yield the same queue.
|
||||
static MultiThreadedJobQueue* GetDrawingQueue(uint32_t aHash)
|
||||
{
|
||||
return sSingleton->mDrawingQueues[
|
||||
aHash % sSingleton->mDrawingQueues.size()
|
||||
];
|
||||
}
|
||||
|
||||
/// Return the task queue associated to the worker the task is pinned to if
|
||||
/// the task is pinned to a worker, or a random queue.
|
||||
static MultiThreadedJobQueue* GetQueueForJob(Job* aJob);
|
||||
|
||||
/// Initialize the task scheduler with aNumThreads worker threads for drawing
|
||||
/// and aNumQueues task queues.
|
||||
///
|
||||
/// The number of threads must be superior or equal to the number of queues
|
||||
/// (since for now a worker thread only pulls from one queue).
|
||||
static bool Init(uint32_t aNumThreads, uint32_t aNumQueues);
|
||||
|
||||
/// Shut the scheduler down.
|
||||
///
|
||||
/// This will block until worker threads are joined and deleted.
|
||||
static void ShutDown();
|
||||
|
||||
/// Returns true if there is a successfully initialized JobScheduler singleton.
|
||||
static bool IsEnabled() { return !!sSingleton; }
|
||||
|
||||
/// Submit a task buffer to its associated queue.
|
||||
///
|
||||
/// The caller looses ownership of the task buffer.
|
||||
static void SubmitJob(Job* aJobs);
|
||||
|
||||
/// Convenience function to block the current thread until a given SyncObject
|
||||
/// is in the signaled state.
|
||||
///
|
||||
/// The current thread will first try to steal jobs before blocking.
|
||||
static void Join(SyncObject* aCompletionSync);
|
||||
|
||||
/// Process commands until the command buffer needs to block on a sync object,
|
||||
/// completes, yields, or encounters an error.
|
||||
///
|
||||
/// Can be used on any thread. Worker threads basically loop over this, but the
|
||||
/// main thread can also dequeue pending task buffers and process them alongside
|
||||
/// the worker threads if it is about to block until completion anyway.
|
||||
///
|
||||
/// The caller looses ownership of the task buffer.
|
||||
static JobStatus ProcessJob(Job* aJobs);
|
||||
|
||||
protected:
|
||||
static JobScheduler* sSingleton;
|
||||
|
||||
// queues of Job that are ready to be processed
|
||||
std::vector<MultiThreadedJobQueue*> mDrawingQueues;
|
||||
std::vector<WorkerThread*> mWorkerThreads;
|
||||
Atomic<uint32_t> mNextQueue;
|
||||
};
|
||||
|
||||
/// Jobs are not reference-counted because they don't have shared ownership.
|
||||
/// The ownership of tasks can change when they are passed to certain methods
|
||||
/// of JobScheduler and SyncObject. See the docuumentaion of these classes.
|
||||
class Job {
|
||||
public:
|
||||
Job(SyncObject* aStart, SyncObject* aCompletion, WorkerThread* aThread = nullptr);
|
||||
|
||||
virtual ~Job();
|
||||
|
||||
virtual JobStatus Run() = 0;
|
||||
|
||||
/// For use in JobScheduler::SubmitJob. Don't use it anywhere else.
|
||||
//already_AddRefed<SyncObject> GetAndResetStartSync();
|
||||
SyncObject* GetStartSync() { return mStartSync; }
|
||||
|
||||
bool IsPinnedToAThread() const { return !!mPinToThread; }
|
||||
|
||||
WorkerThread* GetWorkerThread() { return mPinToThread; }
|
||||
|
||||
protected:
|
||||
// An intrusive linked list of tasks waiting for a sync object to enter the
|
||||
// signaled state. When the task is not waiting for a sync object, mNextWaitingJob
|
||||
// should be null. This is only accessed from the thread that owns the task.
|
||||
Job* mNextWaitingJob;
|
||||
|
||||
RefPtr<SyncObject> mStartSync;
|
||||
RefPtr<SyncObject> mCompletionSync;
|
||||
WorkerThread* mPinToThread;
|
||||
|
||||
friend class SyncObject;
|
||||
};
|
||||
|
||||
class EventObject;
|
||||
|
||||
/// This task will set an EventObject.
|
||||
///
|
||||
/// Typically used as the final task, so that the main thread can block on the
|
||||
/// corresponfing EventObject until all of the tasks are processed.
|
||||
class SetEventJob : public Job
|
||||
{
|
||||
public:
|
||||
explicit SetEventJob(EventObject* aEvent,
|
||||
SyncObject* aStart, SyncObject* aCompletion = nullptr,
|
||||
WorkerThread* aPinToWorker = nullptr);
|
||||
|
||||
~SetEventJob();
|
||||
|
||||
JobStatus Run() override;
|
||||
|
||||
EventObject* GetEvent() { return mEvent; }
|
||||
|
||||
protected:
|
||||
RefPtr<EventObject> mEvent;
|
||||
};
|
||||
|
||||
/// A synchronization object that can be used to express dependencies and ordering between
|
||||
/// tasks.
|
||||
///
|
||||
/// Jobs can register to SyncObjects in order to asynchronously wait for a signal.
|
||||
/// In practice, Job objects usually start with a sync object (startSyc) and end
|
||||
/// with another one (completionSync).
|
||||
/// a Job never gets processed before its startSync is in the signaled state, and
|
||||
/// signals its completionSync as soon as it finishes. This is how dependencies
|
||||
/// between tasks is expressed.
|
||||
class SyncObject final : public external::AtomicRefCounted<SyncObject> {
|
||||
public:
|
||||
MOZ_DECLARE_REFCOUNTED_TYPENAME(SyncObject)
|
||||
|
||||
/// Create a synchronization object.
|
||||
///
|
||||
/// aNumPrerequisites represents the number of times the object must be signaled
|
||||
/// before actually entering the signaled state (in other words, it means the
|
||||
/// number of dependencies of this sync object).
|
||||
///
|
||||
/// Explicitly specifying the number of prerequisites when creating sync objects
|
||||
/// makes it easy to start scheduling some of the prerequisite tasks while
|
||||
/// creating the others, which is how we typically use the task scheduler.
|
||||
/// Automatically determining the number of prerequisites using Job's constructor
|
||||
/// brings the risk that the sync object enters the signaled state while we
|
||||
/// are still adding prerequisites which is hard to fix without using muteces.
|
||||
explicit SyncObject(uint32_t aNumPrerequisites = 1);
|
||||
|
||||
~SyncObject();
|
||||
|
||||
/// Attempt to register a task.
|
||||
///
|
||||
/// If the sync object is already in the signaled state, the buffer is *not*
|
||||
/// registered and the sync object does not take ownership of the task.
|
||||
/// If the object is not yet in the signaled state, it takes ownership of
|
||||
/// the task and places it in a list of pending tasks.
|
||||
/// Pending tasks will not be processed by the worker thread.
|
||||
/// When the SyncObject reaches the signaled state, it places the pending
|
||||
/// tasks back in the available buffer queue, so that they can be
|
||||
/// scheduled again.
|
||||
///
|
||||
/// Returns true if the SyncOject is not already in the signaled state.
|
||||
/// This means that if this method returns true, the SyncObject has taken
|
||||
/// ownership of the Job.
|
||||
bool Register(Job* aJob);
|
||||
|
||||
/// Signal the SyncObject.
|
||||
///
|
||||
/// This decrements an internal counter. The sync object reaches the signaled
|
||||
/// state when the counter gets to zero.
|
||||
void Signal();
|
||||
|
||||
/// Returns true if mSignals is equal to zero. In other words, returns true
|
||||
/// if all prerequisite tasks have already signaled the sync object.
|
||||
bool IsSignaled();
|
||||
|
||||
/// Asserts that the number of added prerequisites is equal to the number
|
||||
/// specified in the constructor (does nothin in release builds).
|
||||
void FreezePrerequisites();
|
||||
|
||||
private:
|
||||
// Called by Job's constructor
|
||||
void AddSubsequent(Job* aJob);
|
||||
void AddPrerequisite(Job* aJob);
|
||||
|
||||
void AddWaitingJob(Job* aJob);
|
||||
|
||||
void SubmitWaitingJobs();
|
||||
|
||||
Atomic<int32_t> mSignals;
|
||||
Atomic<Job*> mFirstWaitingJob;
|
||||
|
||||
#ifdef DEBUG
|
||||
uint32_t mNumPrerequisites;
|
||||
Atomic<uint32_t> mAddedPrerequisites;
|
||||
#endif
|
||||
|
||||
friend class Job;
|
||||
friend class JobScheduler;
|
||||
};
|
||||
|
||||
/// Base class for worker threads.
|
||||
class WorkerThread
|
||||
{
|
||||
public:
|
||||
static WorkerThread* Create(MultiThreadedJobQueue* aJobQueue);
|
||||
|
||||
virtual ~WorkerThread() {}
|
||||
|
||||
void Run();
|
||||
|
||||
MultiThreadedJobQueue* GetJobQueue() { return mQueue; }
|
||||
|
||||
protected:
|
||||
explicit WorkerThread(MultiThreadedJobQueue* aJobQueue);
|
||||
|
||||
virtual void SetName(const char* aName) {}
|
||||
|
||||
MultiThreadedJobQueue* mQueue;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
} // namespace
|
||||
|
||||
#endif
|
||||
194
gfx/2d/JobScheduler_posix.cpp
Normal file
194
gfx/2d/JobScheduler_posix.cpp
Normal file
|
|
@ -0,0 +1,194 @@
|
|||
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "JobScheduler.h"
|
||||
#include "mozilla/gfx/Logging.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
void* ThreadCallback(void* threadData);
|
||||
|
||||
class WorkerThreadPosix : public WorkerThread {
|
||||
public:
|
||||
explicit WorkerThreadPosix(MultiThreadedJobQueue* aJobQueue)
|
||||
: WorkerThread(aJobQueue)
|
||||
{
|
||||
pthread_create(&mThread, nullptr, ThreadCallback, static_cast<WorkerThread*>(this));
|
||||
}
|
||||
|
||||
~WorkerThreadPosix()
|
||||
{
|
||||
pthread_join(mThread, nullptr);
|
||||
}
|
||||
|
||||
virtual void SetName(const char*) override
|
||||
{
|
||||
// XXX - temporarily disabled, see bug 1209039
|
||||
//
|
||||
// // Call this from the thread itself because of Mac.
|
||||
//#ifdef XP_MACOSX
|
||||
// pthread_setname_np(aName);
|
||||
//#elif defined(__DragonFly__) || defined(__FreeBSD__) || defined(__OpenBSD__)
|
||||
// pthread_set_name_np(mThread, aName);
|
||||
//#elif defined(__NetBSD__)
|
||||
// pthread_setname_np(mThread, "%s", (void*)aName);
|
||||
//#else
|
||||
// pthread_setname_np(mThread, aName);
|
||||
//#endif
|
||||
}
|
||||
|
||||
protected:
|
||||
pthread_t mThread;
|
||||
};
|
||||
|
||||
void* ThreadCallback(void* threadData)
|
||||
{
|
||||
WorkerThread* thread = static_cast<WorkerThread*>(threadData);
|
||||
thread->Run();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
WorkerThread*
|
||||
WorkerThread::Create(MultiThreadedJobQueue* aJobQueue)
|
||||
{
|
||||
return new WorkerThreadPosix(aJobQueue);
|
||||
}
|
||||
|
||||
MultiThreadedJobQueue::MultiThreadedJobQueue()
|
||||
: mThreadsCount(0)
|
||||
, mShuttingDown(false)
|
||||
{}
|
||||
|
||||
MultiThreadedJobQueue::~MultiThreadedJobQueue()
|
||||
{
|
||||
MOZ_ASSERT(mJobs.empty());
|
||||
}
|
||||
|
||||
bool
|
||||
MultiThreadedJobQueue::WaitForJob(Job*& aOutJob)
|
||||
{
|
||||
return PopJob(aOutJob, BLOCKING);
|
||||
}
|
||||
|
||||
bool
|
||||
MultiThreadedJobQueue::PopJob(Job*& aOutJobs, AccessType aAccess)
|
||||
{
|
||||
for (;;) {
|
||||
CriticalSectionAutoEnter lock(&mMutex);
|
||||
|
||||
while (aAccess == BLOCKING && !mShuttingDown && mJobs.empty()) {
|
||||
mAvailableCondvar.Wait(&mMutex);
|
||||
}
|
||||
|
||||
if (mShuttingDown) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mJobs.empty()) {
|
||||
if (aAccess == NON_BLOCKING) {
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
Job* task = mJobs.front();
|
||||
MOZ_ASSERT(task);
|
||||
|
||||
mJobs.pop_front();
|
||||
|
||||
aOutJobs = task;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MultiThreadedJobQueue::SubmitJob(Job* aJobs)
|
||||
{
|
||||
MOZ_ASSERT(aJobs);
|
||||
CriticalSectionAutoEnter lock(&mMutex);
|
||||
mJobs.push_back(aJobs);
|
||||
mAvailableCondvar.Broadcast();
|
||||
}
|
||||
|
||||
size_t
|
||||
MultiThreadedJobQueue::NumJobs()
|
||||
{
|
||||
CriticalSectionAutoEnter lock(&mMutex);
|
||||
return mJobs.size();
|
||||
}
|
||||
|
||||
bool
|
||||
MultiThreadedJobQueue::IsEmpty()
|
||||
{
|
||||
CriticalSectionAutoEnter lock(&mMutex);
|
||||
return mJobs.empty();
|
||||
}
|
||||
|
||||
void
|
||||
MultiThreadedJobQueue::ShutDown()
|
||||
{
|
||||
CriticalSectionAutoEnter lock(&mMutex);
|
||||
mShuttingDown = true;
|
||||
while (mThreadsCount) {
|
||||
mAvailableCondvar.Broadcast();
|
||||
mShutdownCondvar.Wait(&mMutex);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MultiThreadedJobQueue::RegisterThread()
|
||||
{
|
||||
mThreadsCount += 1;
|
||||
}
|
||||
|
||||
void
|
||||
MultiThreadedJobQueue::UnregisterThread()
|
||||
{
|
||||
CriticalSectionAutoEnter lock(&mMutex);
|
||||
mThreadsCount -= 1;
|
||||
if (mThreadsCount == 0) {
|
||||
mShutdownCondvar.Broadcast();
|
||||
}
|
||||
}
|
||||
|
||||
EventObject::EventObject()
|
||||
: mIsSet(false)
|
||||
{}
|
||||
|
||||
EventObject::~EventObject()
|
||||
{}
|
||||
|
||||
bool
|
||||
EventObject::Peak()
|
||||
{
|
||||
CriticalSectionAutoEnter lock(&mMutex);
|
||||
return mIsSet;
|
||||
}
|
||||
|
||||
void
|
||||
EventObject::Set()
|
||||
{
|
||||
CriticalSectionAutoEnter lock(&mMutex);
|
||||
if (!mIsSet) {
|
||||
mIsSet = true;
|
||||
mCond.Broadcast();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
EventObject::Wait()
|
||||
{
|
||||
CriticalSectionAutoEnter lock(&mMutex);
|
||||
if (mIsSet) {
|
||||
return;
|
||||
}
|
||||
mCond.Wait(&mMutex);
|
||||
}
|
||||
|
||||
} // namespce
|
||||
} // namespce
|
||||
142
gfx/2d/JobScheduler_posix.h
Normal file
142
gfx/2d/JobScheduler_posix.h
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
/* -*- Mode: C++; tab-width: 20; 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 WIN32
|
||||
#ifndef MOZILLA_GFX_TASKSCHEDULER_POSIX_H_
|
||||
#define MOZILLA_GFX_TASKSCHEDULER_POSIX_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <pthread.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "mozilla/DebugOnly.h"
|
||||
#include "mozilla/gfx/CriticalSection.h"
|
||||
#include "mozilla/RefCounted.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
class Job;
|
||||
class PosixCondVar;
|
||||
class WorkerThread;
|
||||
|
||||
// posix platforms only!
|
||||
class PosixCondVar {
|
||||
public:
|
||||
PosixCondVar() {
|
||||
DebugOnly<int> err = pthread_cond_init(&mCond, nullptr);
|
||||
MOZ_ASSERT(!err);
|
||||
}
|
||||
|
||||
~PosixCondVar() {
|
||||
DebugOnly<int> err = pthread_cond_destroy(&mCond);
|
||||
MOZ_ASSERT(!err);
|
||||
}
|
||||
|
||||
void Wait(CriticalSection* aMutex) {
|
||||
DebugOnly<int> err = pthread_cond_wait(&mCond, &aMutex->mMutex);
|
||||
MOZ_ASSERT(!err);
|
||||
}
|
||||
|
||||
void Broadcast() {
|
||||
DebugOnly<int> err = pthread_cond_broadcast(&mCond);
|
||||
MOZ_ASSERT(!err);
|
||||
}
|
||||
|
||||
protected:
|
||||
pthread_cond_t mCond;
|
||||
};
|
||||
|
||||
|
||||
/// A simple and naive multithreaded task queue
|
||||
///
|
||||
/// The public interface of this class must remain identical to its equivalent
|
||||
/// in JobScheduler_win32.h
|
||||
class MultiThreadedJobQueue {
|
||||
public:
|
||||
enum AccessType {
|
||||
BLOCKING,
|
||||
NON_BLOCKING
|
||||
};
|
||||
|
||||
// Producer thread
|
||||
MultiThreadedJobQueue();
|
||||
|
||||
// Producer thread
|
||||
~MultiThreadedJobQueue();
|
||||
|
||||
// Worker threads
|
||||
bool WaitForJob(Job*& aOutJob);
|
||||
|
||||
// Any thread
|
||||
bool PopJob(Job*& aOutJob, AccessType aAccess);
|
||||
|
||||
// Any threads
|
||||
void SubmitJob(Job* aJob);
|
||||
|
||||
// Producer thread
|
||||
void ShutDown();
|
||||
|
||||
// Any thread
|
||||
size_t NumJobs();
|
||||
|
||||
// Any thread
|
||||
bool IsEmpty();
|
||||
|
||||
// Producer thread
|
||||
void RegisterThread();
|
||||
|
||||
// Worker threads
|
||||
void UnregisterThread();
|
||||
|
||||
protected:
|
||||
|
||||
std::list<Job*> mJobs;
|
||||
CriticalSection mMutex;
|
||||
PosixCondVar mAvailableCondvar;
|
||||
PosixCondVar mShutdownCondvar;
|
||||
int32_t mThreadsCount;
|
||||
bool mShuttingDown;
|
||||
|
||||
friend class WorkerThread;
|
||||
};
|
||||
|
||||
/// An object that a thread can synchronously wait on.
|
||||
/// Usually set by a SetEventJob.
|
||||
class EventObject : public external::AtomicRefCounted<EventObject>
|
||||
{
|
||||
public:
|
||||
MOZ_DECLARE_REFCOUNTED_TYPENAME(EventObject)
|
||||
|
||||
EventObject();
|
||||
|
||||
~EventObject();
|
||||
|
||||
/// Synchronously wait until the event is set.
|
||||
void Wait();
|
||||
|
||||
/// Return true if the event is set, without blocking.
|
||||
bool Peak();
|
||||
|
||||
/// Set the event.
|
||||
void Set();
|
||||
|
||||
protected:
|
||||
CriticalSection mMutex;
|
||||
PosixCondVar mCond;
|
||||
bool mIsSet;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
} // namespace
|
||||
|
||||
#include "JobScheduler.h"
|
||||
|
||||
#endif
|
||||
#endif
|
||||
148
gfx/2d/JobScheduler_win32.cpp
Normal file
148
gfx/2d/JobScheduler_win32.cpp
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "JobScheduler.h"
|
||||
#include "mozilla/gfx/Logging.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
DWORD __stdcall ThreadCallback(void* threadData);
|
||||
|
||||
class WorkerThreadWin32 : public WorkerThread {
|
||||
public:
|
||||
explicit WorkerThreadWin32(MultiThreadedJobQueue* aJobQueue)
|
||||
: WorkerThread(aJobQueue)
|
||||
{
|
||||
mThread = ::CreateThread(nullptr, 0, ThreadCallback, static_cast<WorkerThread*>(this), 0, nullptr);
|
||||
}
|
||||
|
||||
~WorkerThreadWin32()
|
||||
{
|
||||
::WaitForSingleObject(mThread, INFINITE);
|
||||
::CloseHandle(mThread);
|
||||
}
|
||||
|
||||
protected:
|
||||
HANDLE mThread;
|
||||
};
|
||||
|
||||
DWORD __stdcall ThreadCallback(void* threadData)
|
||||
{
|
||||
WorkerThread* thread = static_cast<WorkerThread*>(threadData);
|
||||
thread->Run();
|
||||
return 0;
|
||||
}
|
||||
|
||||
WorkerThread*
|
||||
WorkerThread::Create(MultiThreadedJobQueue* aJobQueue)
|
||||
{
|
||||
return new WorkerThreadWin32(aJobQueue);
|
||||
}
|
||||
|
||||
bool
|
||||
MultiThreadedJobQueue::PopJob(Job*& aOutJob, AccessType aAccess)
|
||||
{
|
||||
for (;;) {
|
||||
while (aAccess == BLOCKING && mJobs.empty()) {
|
||||
{
|
||||
CriticalSectionAutoEnter lock(&mSection);
|
||||
if (mShuttingDown) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
HANDLE handles[] = { mAvailableEvent, mShutdownEvent };
|
||||
::WaitForMultipleObjects(2, handles, FALSE, INFINITE);
|
||||
}
|
||||
|
||||
CriticalSectionAutoEnter lock(&mSection);
|
||||
|
||||
if (mShuttingDown) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mJobs.empty()) {
|
||||
if (aAccess == NON_BLOCKING) {
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
Job* task = mJobs.front();
|
||||
MOZ_ASSERT(task);
|
||||
|
||||
mJobs.pop_front();
|
||||
|
||||
if (mJobs.empty()) {
|
||||
::ResetEvent(mAvailableEvent);
|
||||
}
|
||||
|
||||
aOutJob = task;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MultiThreadedJobQueue::SubmitJob(Job* aJob)
|
||||
{
|
||||
MOZ_ASSERT(aJob);
|
||||
CriticalSectionAutoEnter lock(&mSection);
|
||||
mJobs.push_back(aJob);
|
||||
::SetEvent(mAvailableEvent);
|
||||
}
|
||||
|
||||
void
|
||||
MultiThreadedJobQueue::ShutDown()
|
||||
{
|
||||
{
|
||||
CriticalSectionAutoEnter lock(&mSection);
|
||||
mShuttingDown = true;
|
||||
}
|
||||
while (mThreadsCount) {
|
||||
::SetEvent(mAvailableEvent);
|
||||
::WaitForSingleObject(mShutdownEvent, INFINITE);
|
||||
}
|
||||
}
|
||||
|
||||
size_t
|
||||
MultiThreadedJobQueue::NumJobs()
|
||||
{
|
||||
CriticalSectionAutoEnter lock(&mSection);
|
||||
return mJobs.size();
|
||||
}
|
||||
|
||||
bool
|
||||
MultiThreadedJobQueue::IsEmpty()
|
||||
{
|
||||
CriticalSectionAutoEnter lock(&mSection);
|
||||
return mJobs.empty();
|
||||
}
|
||||
|
||||
void
|
||||
MultiThreadedJobQueue::RegisterThread()
|
||||
{
|
||||
mThreadsCount += 1;
|
||||
}
|
||||
|
||||
void
|
||||
MultiThreadedJobQueue::UnregisterThread()
|
||||
{
|
||||
mSection.Enter();
|
||||
mThreadsCount -= 1;
|
||||
bool finishShutdown = mThreadsCount == 0;
|
||||
mSection.Leave();
|
||||
|
||||
if (finishShutdown) {
|
||||
// Can't touch mSection or any other member from now on because this object
|
||||
// may get deleted on the main thread after mShutdownEvent is set.
|
||||
::SetEvent(mShutdownEvent);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace
|
||||
99
gfx/2d/JobScheduler_win32.h
Normal file
99
gfx/2d/JobScheduler_win32.h
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
/* -*- Mode: C++; tab-width: 20; 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/. */
|
||||
|
||||
#ifdef WIN32
|
||||
#ifndef MOZILLA_GFX_TASKSCHEDULER_WIN32_H_
|
||||
#define MOZILLA_GFX_TASKSCHEDULER_WIN32_H_
|
||||
|
||||
#include <windows.h>
|
||||
#include <list>
|
||||
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "mozilla/gfx/CriticalSection.h"
|
||||
#include "mozilla/RefCounted.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
class WorkerThread;
|
||||
class Job;
|
||||
|
||||
// The public interface of this class must remain identical to its equivalent
|
||||
// in JobScheduler_posix.h
|
||||
class MultiThreadedJobQueue {
|
||||
public:
|
||||
enum AccessType {
|
||||
BLOCKING,
|
||||
NON_BLOCKING
|
||||
};
|
||||
|
||||
MultiThreadedJobQueue()
|
||||
: mThreadsCount(0)
|
||||
, mShuttingDown(false)
|
||||
{
|
||||
mAvailableEvent = ::CreateEventW(nullptr, TRUE, FALSE, nullptr);
|
||||
mShutdownEvent = ::CreateEventW(nullptr, TRUE, FALSE, nullptr);
|
||||
}
|
||||
|
||||
~MultiThreadedJobQueue()
|
||||
{
|
||||
::CloseHandle(mAvailableEvent);
|
||||
::CloseHandle(mShutdownEvent);
|
||||
}
|
||||
|
||||
bool WaitForJob(Job*& aOutJob) { return PopJob(aOutJob, BLOCKING); }
|
||||
|
||||
bool PopJob(Job*& aOutJob, AccessType aAccess);
|
||||
|
||||
void SubmitJob(Job* aJob);
|
||||
|
||||
void ShutDown();
|
||||
|
||||
size_t NumJobs();
|
||||
|
||||
bool IsEmpty();
|
||||
|
||||
void RegisterThread();
|
||||
|
||||
void UnregisterThread();
|
||||
|
||||
protected:
|
||||
std::list<Job*> mJobs;
|
||||
CriticalSection mSection;
|
||||
HANDLE mAvailableEvent;
|
||||
HANDLE mShutdownEvent;
|
||||
int32_t mThreadsCount;
|
||||
bool mShuttingDown;
|
||||
|
||||
friend class WorkerThread;
|
||||
};
|
||||
|
||||
|
||||
// The public interface of this class must remain identical to its equivalent
|
||||
// in JobScheduler_posix.h
|
||||
class EventObject : public external::AtomicRefCounted<EventObject>
|
||||
{
|
||||
public:
|
||||
MOZ_DECLARE_REFCOUNTED_TYPENAME(EventObject)
|
||||
|
||||
EventObject() { mEvent = ::CreateEventW(nullptr, TRUE, FALSE, nullptr); }
|
||||
|
||||
~EventObject() { ::CloseHandle(mEvent); }
|
||||
|
||||
void Wait() { ::WaitForSingleObject(mEvent, INFINITE); }
|
||||
|
||||
bool Peak() { return ::WaitForSingleObject(mEvent, 0) == WAIT_OBJECT_0; }
|
||||
|
||||
void Set() { ::SetEvent(mEvent); }
|
||||
protected:
|
||||
// TODO: it's expensive to create events so we should try to reuse them
|
||||
HANDLE mEvent;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
} // namespace
|
||||
|
||||
#endif
|
||||
#endif
|
||||
706
gfx/2d/Logging.h
Normal file
706
gfx/2d/Logging.h
Normal file
|
|
@ -0,0 +1,706 @@
|
|||
/* -*- Mode: C++; tab-width: 20; 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_GFX_LOGGING_H_
|
||||
#define MOZILLA_GFX_LOGGING_H_
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <stdio.h>
|
||||
#include <vector>
|
||||
|
||||
#ifdef MOZ_LOGGING
|
||||
#include "mozilla/Logging.h"
|
||||
#endif
|
||||
#include "mozilla/Tuple.h"
|
||||
|
||||
#if defined(MOZ_WIDGET_ANDROID)
|
||||
#include "nsDebug.h"
|
||||
#endif
|
||||
#include "Point.h"
|
||||
#include "BaseRect.h"
|
||||
#include "Matrix.h"
|
||||
#include "LoggingConstants.h"
|
||||
|
||||
#if defined(MOZ_LOGGING)
|
||||
extern GFX2D_API mozilla::LogModule* GetGFX2DLog();
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
#if defined(MOZ_LOGGING)
|
||||
inline mozilla::LogLevel PRLogLevelForLevel(int aLevel) {
|
||||
switch (aLevel) {
|
||||
case LOG_CRITICAL:
|
||||
return LogLevel::Error;
|
||||
case LOG_WARNING:
|
||||
return LogLevel::Warning;
|
||||
case LOG_DEBUG:
|
||||
return LogLevel::Debug;
|
||||
case LOG_DEBUG_PRLOG:
|
||||
return LogLevel::Debug;
|
||||
case LOG_EVERYTHING:
|
||||
return LogLevel::Error;
|
||||
}
|
||||
return LogLevel::Debug;
|
||||
}
|
||||
#endif
|
||||
|
||||
class LoggingPrefs
|
||||
{
|
||||
public:
|
||||
// Used to choose the level of logging we get. The higher the number,
|
||||
// the more logging we get. Value of zero will give you no logging,
|
||||
// 1 just errors, 2 adds warnings and 3 or 4 add debug logging.
|
||||
// In addition to setting the value to 4, you will need to set the
|
||||
// environment variable MOZ_LOG to gfx:4. See mozilla/Logging.h for details.
|
||||
static int32_t sGfxLogLevel;
|
||||
};
|
||||
|
||||
/// Graphics logging is available in both debug and release builds and is
|
||||
/// controlled with a gfx.logging.level preference. If not set, the default
|
||||
/// for the preference is 5 in the debug builds, 1 in the release builds.
|
||||
///
|
||||
/// gfxDebug only works in the debug builds, and is used for information
|
||||
/// level messages, helping with debugging. In addition to only working
|
||||
/// in the debug builds, the value of the above preference of 3 or higher
|
||||
/// is required.
|
||||
///
|
||||
/// gfxWarning messages are available in both debug and release builds,
|
||||
/// on by default in the debug builds, and off by default in the release builds.
|
||||
/// Setting the preference gfx.logging.level to a value of 2 or higher will
|
||||
/// show the warnings.
|
||||
///
|
||||
/// gfxCriticalError is available in debug and release builds by default.
|
||||
/// It is only unavailable if gfx.logging.level is set to 0 (or less.)
|
||||
/// It outputs the message to stderr or equivalent, like gfxWarning.
|
||||
/// In the event of a crash, the crash report is annotated with first and
|
||||
/// the last few of these errors, under the key GraphicsCriticalError.
|
||||
/// The total number of errors stored in the crash report is controlled
|
||||
/// by preference gfx.logging.crash.length.
|
||||
///
|
||||
/// On platforms that support MOZ_LOGGING, the story is slightly more involved.
|
||||
/// In that case, unless gfx.logging.level is set to 4 or higher, the output
|
||||
/// is further controlled by the "gfx2d" logging module. However, in the case
|
||||
/// where such module would disable the output, in all but gfxDebug cases,
|
||||
/// we will still send a printf.
|
||||
|
||||
// The range is due to the values set in Histograms.json
|
||||
enum class LogReason : int {
|
||||
MustBeMoreThanThis = -1,
|
||||
// Start. Do not insert, always add at end. If you remove items,
|
||||
// make sure the other items retain their values.
|
||||
D3D11InvalidCallDeviceRemoved = 0,
|
||||
D3D11InvalidCall,
|
||||
D3DLockTimeout,
|
||||
D3D10FinalizeFrame,
|
||||
D3D11FinalizeFrame,
|
||||
D3D10SyncLock,
|
||||
D3D11SyncLock,
|
||||
D2D1NoWriteMap,
|
||||
JobStatusError,
|
||||
FilterInputError,
|
||||
FilterInputData, // 10
|
||||
FilterInputRect,
|
||||
FilterInputSet,
|
||||
FilterInputFormat,
|
||||
FilterNodeD2D1Target,
|
||||
FilterNodeD2D1Backend,
|
||||
SourceSurfaceIncompatible,
|
||||
GlyphAllocFailedCairo,
|
||||
GlyphAllocFailedCG,
|
||||
InvalidRect,
|
||||
CannotDraw3D, // 20
|
||||
IncompatibleBasicTexturedEffect,
|
||||
InvalidFont,
|
||||
PAllocTextureBackendMismatch,
|
||||
GetFontFileDataFailed,
|
||||
MessageChannelCloseFailure,
|
||||
MessageChannelInvalidHandle,
|
||||
TextureAliveAfterShutdown,
|
||||
InvalidContext,
|
||||
InvalidCommandList,
|
||||
AsyncTransactionTimeout, // 30
|
||||
TextureCreation,
|
||||
InvalidCacheSurface,
|
||||
AlphaWithBasicClient,
|
||||
UnbalancedClipStack,
|
||||
ProcessingError,
|
||||
NativeFontResourceNotFound,
|
||||
// End
|
||||
MustBeLessThanThis = 101,
|
||||
};
|
||||
|
||||
struct BasicLogger
|
||||
{
|
||||
// For efficiency, this method exists and copies the logic of the
|
||||
// OutputMessage below. If making any changes here, also make it
|
||||
// in the appropriate places in that method.
|
||||
static bool ShouldOutputMessage(int aLevel) {
|
||||
if (LoggingPrefs::sGfxLogLevel >= aLevel) {
|
||||
#if defined(MOZ_WIDGET_ANDROID)
|
||||
return true;
|
||||
#else
|
||||
#if defined(MOZ_LOGGING)
|
||||
if (MOZ_LOG_TEST(GetGFX2DLog(), PRLogLevelForLevel(aLevel))) {
|
||||
return true;
|
||||
} else
|
||||
#endif
|
||||
if ((LoggingPrefs::sGfxLogLevel >= LOG_DEBUG_PRLOG) ||
|
||||
(aLevel < LOG_DEBUG)) {
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Only for really critical errors.
|
||||
static void CrashAction(LogReason aReason) {}
|
||||
|
||||
static void OutputMessage(const std::string &aString,
|
||||
int aLevel,
|
||||
bool aNoNewline) {
|
||||
// This behavior (the higher the preference, the more we log)
|
||||
// is consistent with what prlog does in general. Note that if prlog
|
||||
// is in the build, but disabled, we will printf if the preferences
|
||||
// requires us to log something (see sGfxLogLevel for the special
|
||||
// treatment of LOG_DEBUG and LOG_DEBUG_PRLOG)
|
||||
//
|
||||
// If making any logic changes to this method, you should probably
|
||||
// make the corresponding change in the ShouldOutputMessage method
|
||||
// above.
|
||||
if (LoggingPrefs::sGfxLogLevel >= aLevel) {
|
||||
#if defined(MOZ_WIDGET_ANDROID)
|
||||
printf_stderr("%s%s", aString.c_str(), aNoNewline ? "" : "\n");
|
||||
#else
|
||||
#if defined(MOZ_LOGGING)
|
||||
if (MOZ_LOG_TEST(GetGFX2DLog(), PRLogLevelForLevel(aLevel))) {
|
||||
PR_LogPrint("%s%s", aString.c_str(), aNoNewline ? "" : "\n");
|
||||
} else
|
||||
#endif
|
||||
if ((LoggingPrefs::sGfxLogLevel >= LOG_DEBUG_PRLOG) ||
|
||||
(aLevel < LOG_DEBUG)) {
|
||||
printf("%s%s", aString.c_str(), aNoNewline ? "" : "\n");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct CriticalLogger {
|
||||
static void OutputMessage(const std::string &aString, int aLevel, bool aNoNewline);
|
||||
static void CrashAction(LogReason aReason);
|
||||
};
|
||||
|
||||
// The int is the index of the Log call; if the number of logs exceeds some preset
|
||||
// capacity we may not get all of them, so the indices help figure out which
|
||||
// ones we did save. The double is expected to be the "TimeDuration",
|
||||
// time in seconds since the process creation.
|
||||
typedef mozilla::Tuple<int32_t,std::string,double> LoggingRecordEntry;
|
||||
|
||||
// Implement this interface and init the Factory with an instance to
|
||||
// forward critical logs.
|
||||
typedef std::vector<LoggingRecordEntry> LoggingRecord;
|
||||
class LogForwarder {
|
||||
public:
|
||||
virtual ~LogForwarder() {}
|
||||
virtual void Log(const std::string &aString) = 0;
|
||||
virtual void CrashAction(LogReason aReason) = 0;
|
||||
virtual bool UpdateStringsVector(const std::string& aString) = 0;
|
||||
|
||||
// Provide a copy of the logs to the caller.
|
||||
virtual LoggingRecord LoggingRecordCopy() = 0;
|
||||
};
|
||||
|
||||
class NoLog
|
||||
{
|
||||
public:
|
||||
NoLog() {}
|
||||
~NoLog() {}
|
||||
|
||||
// No-op
|
||||
MOZ_IMPLICIT NoLog(const NoLog&) {}
|
||||
|
||||
template<typename T>
|
||||
NoLog &operator <<(const T &aLogText) { return *this; }
|
||||
};
|
||||
|
||||
enum class LogOptions : int {
|
||||
NoNewline = 0x01,
|
||||
AutoPrefix = 0x02,
|
||||
AssertOnCall = 0x04,
|
||||
CrashAction = 0x08,
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct Hexa {
|
||||
explicit Hexa(T aVal) : mVal(aVal) {}
|
||||
T mVal;
|
||||
};
|
||||
template<typename T>
|
||||
Hexa<T> hexa(T val) { return Hexa<T>(val); }
|
||||
|
||||
template<int L, typename Logger = BasicLogger>
|
||||
class Log
|
||||
{
|
||||
public:
|
||||
// The default is to have the prefix, have the new line, and for critical
|
||||
// logs assert on each call.
|
||||
static int DefaultOptions(bool aWithAssert = true) {
|
||||
return (int(LogOptions::AutoPrefix) |
|
||||
(aWithAssert ? int(LogOptions::AssertOnCall) : 0));
|
||||
}
|
||||
|
||||
// Note that we're calling BasicLogger::ShouldOutputMessage, rather than
|
||||
// Logger::ShouldOutputMessage. Since we currently don't have a different
|
||||
// version of that method for different loggers, this is OK. Once we do,
|
||||
// change BasicLogger::ShouldOutputMessage to Logger::ShouldOutputMessage.
|
||||
explicit Log(int aOptions = Log::DefaultOptions(L == LOG_CRITICAL),
|
||||
LogReason aReason = LogReason::MustBeMoreThanThis)
|
||||
: mOptions(0)
|
||||
, mLogIt(false)
|
||||
{
|
||||
Init(aOptions, BasicLogger::ShouldOutputMessage(L), aReason);
|
||||
}
|
||||
|
||||
~Log() {
|
||||
Flush();
|
||||
}
|
||||
|
||||
void Flush() {
|
||||
if (MOZ_LIKELY(!LogIt())) return;
|
||||
|
||||
std::string str = mMessage.str();
|
||||
if (!str.empty()) {
|
||||
WriteLog(str);
|
||||
}
|
||||
mMessage.str("");
|
||||
}
|
||||
|
||||
Log &operator <<(char aChar) {
|
||||
if (MOZ_UNLIKELY(LogIt())) {
|
||||
mMessage << aChar;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
Log &operator <<(const std::string &aLogText) {
|
||||
if (MOZ_UNLIKELY(LogIt())) {
|
||||
mMessage << aLogText;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
Log &operator <<(const char aStr[]) {
|
||||
if (MOZ_UNLIKELY(LogIt())) {
|
||||
mMessage << static_cast<const char*>(aStr);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
Log &operator <<(bool aBool) {
|
||||
if (MOZ_UNLIKELY(LogIt())) {
|
||||
mMessage << (aBool ? "true" : "false");
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
Log &operator <<(int aInt) {
|
||||
if (MOZ_UNLIKELY(LogIt())) {
|
||||
mMessage << aInt;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
Log &operator <<(unsigned int aInt) {
|
||||
if (MOZ_UNLIKELY(LogIt())) {
|
||||
mMessage << aInt;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
Log &operator <<(long aLong) {
|
||||
if (MOZ_UNLIKELY(LogIt())) {
|
||||
mMessage << aLong;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
Log &operator <<(unsigned long aLong) {
|
||||
if (MOZ_UNLIKELY(LogIt())) {
|
||||
mMessage << aLong;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
Log &operator <<(long long aLong) {
|
||||
if (MOZ_UNLIKELY(LogIt())) {
|
||||
mMessage << aLong;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
Log &operator <<(unsigned long long aLong) {
|
||||
if (MOZ_UNLIKELY(LogIt())) {
|
||||
mMessage << aLong;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
Log &operator <<(Float aFloat) {
|
||||
if (MOZ_UNLIKELY(LogIt())) {
|
||||
mMessage << aFloat;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
Log &operator <<(double aDouble) {
|
||||
if (MOZ_UNLIKELY(LogIt())) {
|
||||
mMessage << aDouble;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
template <typename T, typename Sub, typename Coord>
|
||||
Log &operator <<(const BasePoint<T, Sub, Coord>& aPoint) {
|
||||
if (MOZ_UNLIKELY(LogIt())) {
|
||||
mMessage << "Point" << aPoint;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
template <typename T, typename Sub>
|
||||
Log &operator <<(const BaseSize<T, Sub>& aSize) {
|
||||
if (MOZ_UNLIKELY(LogIt())) {
|
||||
mMessage << "Size(" << aSize.width << "," << aSize.height << ")";
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
template <typename T, typename Sub, typename Point, typename SizeT, typename Margin>
|
||||
Log &operator <<(const BaseRect<T, Sub, Point, SizeT, Margin>& aRect) {
|
||||
if (MOZ_UNLIKELY(LogIt())) {
|
||||
mMessage << "Rect" << aRect;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
Log &operator<<(const Matrix& aMatrix) {
|
||||
if (MOZ_UNLIKELY(LogIt())) {
|
||||
mMessage << "Matrix(" << aMatrix._11 << " " << aMatrix._12 << " ; " << aMatrix._21 << " " << aMatrix._22 << " ; " << aMatrix._31 << " " << aMatrix._32 << ")";
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
template<typename T>
|
||||
Log &operator<<(Hexa<T> aHex) {
|
||||
if (MOZ_UNLIKELY(LogIt())) {
|
||||
mMessage << std::showbase << std::hex
|
||||
<< aHex.mVal
|
||||
<< std::noshowbase << std::dec;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
Log& operator<<(SurfaceFormat aFormat) {
|
||||
if (MOZ_UNLIKELY(LogIt())) {
|
||||
switch(aFormat) {
|
||||
case SurfaceFormat::B8G8R8A8:
|
||||
mMessage << "SurfaceFormat::B8G8R8A8";
|
||||
break;
|
||||
case SurfaceFormat::B8G8R8X8:
|
||||
mMessage << "SurfaceFormat::B8G8R8X8";
|
||||
break;
|
||||
case SurfaceFormat::R8G8B8A8:
|
||||
mMessage << "SurfaceFormat::R8G8B8A8";
|
||||
break;
|
||||
case SurfaceFormat::R8G8B8X8:
|
||||
mMessage << "SurfaceFormat::R8G8B8X8";
|
||||
break;
|
||||
case SurfaceFormat::R5G6B5_UINT16:
|
||||
mMessage << "SurfaceFormat::R5G6B5_UINT16";
|
||||
break;
|
||||
case SurfaceFormat::A8:
|
||||
mMessage << "SurfaceFormat::A8";
|
||||
break;
|
||||
case SurfaceFormat::YUV:
|
||||
mMessage << "SurfaceFormat::YUV";
|
||||
break;
|
||||
case SurfaceFormat::UNKNOWN:
|
||||
mMessage << "SurfaceFormat::UNKNOWN";
|
||||
break;
|
||||
default:
|
||||
mMessage << "Invalid SurfaceFormat (" << (int)aFormat << ")";
|
||||
break;
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
Log& operator<<(SurfaceType aType) {
|
||||
if (MOZ_UNLIKELY(LogIt())) {
|
||||
switch(aType) {
|
||||
case SurfaceType::DATA:
|
||||
mMessage << "SurfaceType::DATA";
|
||||
break;
|
||||
case SurfaceType::D2D1_BITMAP:
|
||||
mMessage << "SurfaceType::D2D1_BITMAP";
|
||||
break;
|
||||
case SurfaceType::D2D1_DRAWTARGET:
|
||||
mMessage << "SurfaceType::D2D1_DRAWTARGET";
|
||||
break;
|
||||
case SurfaceType::CAIRO:
|
||||
mMessage << "SurfaceType::CAIRO";
|
||||
break;
|
||||
case SurfaceType::CAIRO_IMAGE:
|
||||
mMessage << "SurfaceType::CAIRO_IMAGE";
|
||||
break;
|
||||
case SurfaceType::COREGRAPHICS_IMAGE:
|
||||
mMessage << "SurfaceType::COREGRAPHICS_IMAGE";
|
||||
break;
|
||||
case SurfaceType::COREGRAPHICS_CGCONTEXT:
|
||||
mMessage << "SurfaceType::COREGRAPHICS_CGCONTEXT";
|
||||
break;
|
||||
case SurfaceType::SKIA:
|
||||
mMessage << "SurfaceType::SKIA";
|
||||
break;
|
||||
case SurfaceType::DUAL_DT:
|
||||
mMessage << "SurfaceType::DUAL_DT";
|
||||
break;
|
||||
case SurfaceType::D2D1_1_IMAGE:
|
||||
mMessage << "SurfaceType::D2D1_1_IMAGE";
|
||||
break;
|
||||
case SurfaceType::RECORDING:
|
||||
mMessage << "SurfaceType::RECORDING";
|
||||
break;
|
||||
case SurfaceType::TILED:
|
||||
mMessage << "SurfaceType::TILED";
|
||||
break;
|
||||
default:
|
||||
mMessage << "Invalid SurfaceType (" << (int)aType << ")";
|
||||
break;
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline bool LogIt() const { return mLogIt; }
|
||||
inline bool NoNewline() const { return mOptions & int(LogOptions::NoNewline); }
|
||||
inline bool AutoPrefix() const { return mOptions & int(LogOptions::AutoPrefix); }
|
||||
inline bool ValidReason() const { return (int)mReason > (int)LogReason::MustBeMoreThanThis && (int)mReason < (int)LogReason::MustBeLessThanThis; }
|
||||
|
||||
// We do not want this version to do any work, and stringstream can't be
|
||||
// copied anyway. It does come in handy for the "Once" macro defined below.
|
||||
MOZ_IMPLICIT Log(const Log& log) { Init(log.mOptions, false, log.mReason); }
|
||||
|
||||
private:
|
||||
// Initialization common to two constructors
|
||||
void Init(int aOptions, bool aLogIt, LogReason aReason) {
|
||||
mOptions = aOptions;
|
||||
mReason = aReason;
|
||||
mLogIt = aLogIt;
|
||||
if (mLogIt) {
|
||||
if (AutoPrefix()) {
|
||||
if (mOptions & int(LogOptions::AssertOnCall)) {
|
||||
mMessage << "[GFX" << L;
|
||||
} else {
|
||||
mMessage << "[GFX" << L << "-";
|
||||
}
|
||||
}
|
||||
if ((mOptions & int(LogOptions::CrashAction)) && ValidReason()) {
|
||||
mMessage << " " << (int)mReason;
|
||||
}
|
||||
if (AutoPrefix()) {
|
||||
mMessage << "]: ";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WriteLog(const std::string &aString) {
|
||||
if (MOZ_UNLIKELY(LogIt())) {
|
||||
Logger::OutputMessage(aString, L, NoNewline());
|
||||
// Assert if required. We don't have a three parameter MOZ_ASSERT
|
||||
// so use the underlying functions instead (see bug 1281702):
|
||||
#ifdef DEBUG
|
||||
if (mOptions & int(LogOptions::AssertOnCall)) {
|
||||
MOZ_ReportAssertionFailure(aString.c_str(), __FILE__, __LINE__);
|
||||
MOZ_CRASH("GFX: An assert from the graphics logger");
|
||||
}
|
||||
#endif
|
||||
if ((mOptions & int(LogOptions::CrashAction)) && ValidReason()) {
|
||||
Logger::CrashAction(mReason);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::stringstream mMessage;
|
||||
int mOptions;
|
||||
LogReason mReason;
|
||||
bool mLogIt;
|
||||
};
|
||||
|
||||
typedef Log<LOG_DEBUG> DebugLog;
|
||||
typedef Log<LOG_WARNING> WarningLog;
|
||||
typedef Log<LOG_CRITICAL, CriticalLogger> CriticalLog;
|
||||
|
||||
// Macro to glue names to get us less chance of name clashing.
|
||||
#if defined GFX_LOGGING_GLUE1 || defined GFX_LOGGING_GLUE
|
||||
#error "Clash of the macro GFX_LOGGING_GLUE1 or GFX_LOGGING_GLUE"
|
||||
#endif
|
||||
#define GFX_LOGGING_GLUE1(x, y) x##y
|
||||
#define GFX_LOGGING_GLUE(x, y) GFX_LOGGING_GLUE1(x, y)
|
||||
|
||||
// This log goes into crash reports, use with care.
|
||||
#define gfxCriticalError mozilla::gfx::CriticalLog
|
||||
#define gfxCriticalErrorOnce static gfxCriticalError GFX_LOGGING_GLUE(sOnceAtLine,__LINE__) = gfxCriticalError
|
||||
|
||||
// This is a shortcut for errors we want logged in crash reports/about support
|
||||
// but we do not want asserting. These are available in all builds, so it is
|
||||
// not worth trying to do magic to avoid matching the syntax of gfxCriticalError.
|
||||
// So, this one is used as
|
||||
// gfxCriticalNote << "Something to report and not assert";
|
||||
// while the critical error is
|
||||
// gfxCriticalError() << "Something to report and assert";
|
||||
#define gfxCriticalNote gfxCriticalError(gfxCriticalError::DefaultOptions(false))
|
||||
#define gfxCriticalNoteOnce static gfxCriticalError GFX_LOGGING_GLUE(sOnceAtLine,__LINE__) = gfxCriticalNote
|
||||
|
||||
// The "once" versions will only trigger the first time through. You can do this:
|
||||
// gfxCriticalErrorOnce() << "This message only shows up once;
|
||||
// instead of the usual:
|
||||
// static bool firstTime = true;
|
||||
// if (firstTime) {
|
||||
// firstTime = false;
|
||||
// gfxCriticalError() << "This message only shows up once;
|
||||
// }
|
||||
#if defined(DEBUG)
|
||||
#define gfxDebug mozilla::gfx::DebugLog
|
||||
#define gfxDebugOnce static gfxDebug GFX_LOGGING_GLUE(sOnceAtLine,__LINE__) = gfxDebug
|
||||
#else
|
||||
#define gfxDebug if (1) ; else mozilla::gfx::NoLog
|
||||
#define gfxDebugOnce if (1) ; else mozilla::gfx::NoLog
|
||||
#endif
|
||||
|
||||
// Have gfxWarning available (behind a runtime preference)
|
||||
#define gfxWarning mozilla::gfx::WarningLog
|
||||
#define gfxWarningOnce static gfxWarning GFX_LOGGING_GLUE(sOnceAtLine,__LINE__) = gfxWarning
|
||||
|
||||
// In the debug build, this is equivalent to the default gfxCriticalError.
|
||||
// In the non-debug build, on nightly and dev edition, it will MOZ_CRASH.
|
||||
// On beta and release versions, it will telemetry count, but proceed.
|
||||
//
|
||||
// You should create a (new) enum in the LogReason and use it for the reason
|
||||
// parameter to ensure uniqueness.
|
||||
#define gfxDevCrash(reason) gfxCriticalError(int(gfx::LogOptions::AutoPrefix) | int(gfx::LogOptions::AssertOnCall) | int(gfx::LogOptions::CrashAction), (reason))
|
||||
|
||||
// See nsDebug.h and the NS_WARN_IF macro
|
||||
|
||||
#ifdef __cplusplus
|
||||
// For now, have MOZ2D_ERROR_IF available in debug and non-debug builds
|
||||
inline bool MOZ2D_error_if_impl(bool aCondition, const char* aExpr,
|
||||
const char* aFile, int32_t aLine)
|
||||
{
|
||||
if (MOZ_UNLIKELY(aCondition)) {
|
||||
gfxCriticalError() << aExpr << " at " << aFile << ":" << aLine;
|
||||
}
|
||||
return aCondition;
|
||||
}
|
||||
#define MOZ2D_ERROR_IF(condition) \
|
||||
MOZ2D_error_if_impl(condition, #condition, __FILE__, __LINE__)
|
||||
|
||||
#ifdef DEBUG
|
||||
inline bool MOZ2D_warn_if_impl(bool aCondition, const char* aExpr,
|
||||
const char* aFile, int32_t aLine)
|
||||
{
|
||||
if (MOZ_UNLIKELY(aCondition)) {
|
||||
gfxWarning() << aExpr << " at " << aFile << ":" << aLine;
|
||||
}
|
||||
return aCondition;
|
||||
}
|
||||
#define MOZ2D_WARN_IF(condition) \
|
||||
MOZ2D_warn_if_impl(condition, #condition, __FILE__, __LINE__)
|
||||
#else
|
||||
#define MOZ2D_WARN_IF(condition) (bool)(condition)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
const int INDENT_PER_LEVEL = 2;
|
||||
|
||||
class TreeLog
|
||||
{
|
||||
public:
|
||||
explicit TreeLog(const std::string& aPrefix = "")
|
||||
: mLog(int(LogOptions::NoNewline)),
|
||||
mPrefix(aPrefix),
|
||||
mDepth(0),
|
||||
mStartOfLine(true),
|
||||
mConditionedOnPref(false),
|
||||
mPrefFunction(nullptr) {}
|
||||
|
||||
template <typename T>
|
||||
TreeLog& operator<<(const T& aObject) {
|
||||
if (mConditionedOnPref && !mPrefFunction()) {
|
||||
return *this;
|
||||
}
|
||||
if (mStartOfLine) {
|
||||
mLog << '[' << mPrefix << "] " << std::string(mDepth * INDENT_PER_LEVEL, ' ');
|
||||
mStartOfLine = false;
|
||||
}
|
||||
mLog << aObject;
|
||||
if (EndsInNewline(aObject)) {
|
||||
// Don't indent right here as the user may change the indent
|
||||
// between now and the first output to the next line.
|
||||
mLog.Flush();
|
||||
mStartOfLine = true;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
void IncreaseIndent() { ++mDepth; }
|
||||
void DecreaseIndent() {
|
||||
MOZ_ASSERT(mDepth > 0);
|
||||
--mDepth;
|
||||
}
|
||||
|
||||
void ConditionOnPrefFunction(bool(*aPrefFunction)()) {
|
||||
mConditionedOnPref = true;
|
||||
mPrefFunction = aPrefFunction;
|
||||
}
|
||||
private:
|
||||
Log<LOG_DEBUG> mLog;
|
||||
std::string mPrefix;
|
||||
uint32_t mDepth;
|
||||
bool mStartOfLine;
|
||||
bool mConditionedOnPref;
|
||||
bool (*mPrefFunction)();
|
||||
|
||||
template <typename T>
|
||||
static bool EndsInNewline(const T& aObject) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool EndsInNewline(const std::string& aString) {
|
||||
return !aString.empty() && aString[aString.length() - 1] == '\n';
|
||||
}
|
||||
|
||||
static bool EndsInNewline(char aChar) {
|
||||
return aChar == '\n';
|
||||
}
|
||||
|
||||
static bool EndsInNewline(const char* aString) {
|
||||
return EndsInNewline(std::string(aString));
|
||||
}
|
||||
};
|
||||
|
||||
class TreeAutoIndent
|
||||
{
|
||||
public:
|
||||
explicit TreeAutoIndent(TreeLog& aTreeLog) : mTreeLog(aTreeLog) {
|
||||
mTreeLog.IncreaseIndent();
|
||||
}
|
||||
|
||||
TreeAutoIndent(const TreeAutoIndent& aTreeAutoIndent) :
|
||||
TreeAutoIndent(aTreeAutoIndent.mTreeLog) {
|
||||
mTreeLog.IncreaseIndent();
|
||||
}
|
||||
|
||||
TreeAutoIndent& operator=(const TreeAutoIndent& aTreeAutoIndent) = delete;
|
||||
|
||||
~TreeAutoIndent() {
|
||||
mTreeLog.DecreaseIndent();
|
||||
}
|
||||
private:
|
||||
TreeLog& mTreeLog;
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* MOZILLA_GFX_LOGGING_H_ */
|
||||
30
gfx/2d/LoggingConstants.h
Normal file
30
gfx/2d/LoggingConstants.h
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
/* -*- Mode: C++; tab-width: 20; 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_GFX_LOGGING_CONSTANTS_H_
|
||||
#define MOZILLA_GFX_LOGGING_CONSTANTS_H_
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
// Attempting to be consistent with prlog values, but that isn't critical
|
||||
// (and note that 5 has a special meaning - see the description
|
||||
// with LoggingPrefs::sGfxLogLevel)
|
||||
const int LOG_CRITICAL = 1;
|
||||
const int LOG_WARNING = 2;
|
||||
const int LOG_DEBUG = 3;
|
||||
const int LOG_DEBUG_PRLOG = 4;
|
||||
const int LOG_EVERYTHING = 5; // This needs to be the highest value
|
||||
|
||||
#if defined(DEBUG)
|
||||
const int LOG_DEFAULT = LOG_EVERYTHING;
|
||||
#else
|
||||
const int LOG_DEFAULT = LOG_CRITICAL;
|
||||
#endif
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* MOZILLA_GFX_LOGGING_CONSTANTS_H_ */
|
||||
196
gfx/2d/MMIHelpers.h
Normal file
196
gfx/2d/MMIHelpers.h
Normal file
|
|
@ -0,0 +1,196 @@
|
|||
/*
|
||||
============================================================================
|
||||
Name : MMIHelpers.h
|
||||
Author : Heiher <r@hev.cc>
|
||||
Version : 0.0.1
|
||||
Copyright : Copyright (c) 2015 everyone.
|
||||
Description : The helpers for x86 SSE to Loongson MMI.
|
||||
============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __MMI_HELPERS_H__
|
||||
#define __MMI_HELPERS_H__
|
||||
|
||||
#define __mm_packxxxx(_f, _D, _d, _s, _t) \
|
||||
#_f" %["#_t"], %["#_d"h], %["#_s"h] \n\t" \
|
||||
#_f" %["#_D"l], %["#_d"l], %["#_s"l] \n\t" \
|
||||
"punpckhwd %["#_D"h], %["#_D"l], %["#_t"] \n\t" \
|
||||
"punpcklwd %["#_D"l], %["#_D"l], %["#_t"] \n\t"
|
||||
|
||||
#define _mm_or(_D, _d, _s) \
|
||||
"or %["#_D"h], %["#_d"h], %["#_s"h] \n\t" \
|
||||
"or %["#_D"l], %["#_d"l], %["#_s"l] \n\t"
|
||||
|
||||
#define _mm_xor(_D, _d, _s) \
|
||||
"xor %["#_D"h], %["#_d"h], %["#_s"h] \n\t" \
|
||||
"xor %["#_D"l], %["#_d"l], %["#_s"l] \n\t"
|
||||
|
||||
#define _mm_and(_D, _d, _s) \
|
||||
"and %["#_D"h], %["#_d"h], %["#_s"h] \n\t" \
|
||||
"and %["#_D"l], %["#_d"l], %["#_s"l] \n\t"
|
||||
|
||||
/* SSE: pandn */
|
||||
#define _mm_pandn(_D, _d, _s) \
|
||||
"pandn %["#_D"h], %["#_d"h], %["#_s"h] \n\t" \
|
||||
"pandn %["#_D"l], %["#_d"l], %["#_s"l] \n\t"
|
||||
|
||||
/* SSE: pshuflw */
|
||||
#define _mm_pshuflh(_D, _d, _s) \
|
||||
"mov.d %["#_D"h], %["#_d"h] \n\t" \
|
||||
"pshufh %["#_D"l], %["#_d"l], %["#_s"] \n\t"
|
||||
|
||||
/* SSE: psllw (bits) */
|
||||
#define _mm_psllh(_D, _d, _s) \
|
||||
"psllh %["#_D"h], %["#_d"h], %["#_s"] \n\t" \
|
||||
"psllh %["#_D"l], %["#_d"l], %["#_s"] \n\t"
|
||||
|
||||
/* SSE: pslld (bits) */
|
||||
#define _mm_psllw(_D, _d, _s) \
|
||||
"psllw %["#_D"h], %["#_d"h], %["#_s"] \n\t" \
|
||||
"psllw %["#_D"l], %["#_d"l], %["#_s"] \n\t"
|
||||
|
||||
/* SSE: psllq (bits) */
|
||||
#define _mm_pslld(_D, _d, _s) \
|
||||
"dsll %["#_D"h], %["#_d"h], %["#_s"] \n\t" \
|
||||
"dsll %["#_D"l], %["#_d"l], %["#_s"] \n\t"
|
||||
|
||||
/* SSE: pslldq (bytes) */
|
||||
#define _mm_psllq(_D, _d, _s, _s64, _tf) \
|
||||
"subu %["#_tf"], %["#_s64"], %["#_s"] \n\t" \
|
||||
"dsrl %["#_tf"], %["#_d"l], %["#_tf"] \n\t" \
|
||||
"dsll %["#_D"h], %["#_d"h], %["#_s"] \n\t" \
|
||||
"dsll %["#_D"l], %["#_d"l], %["#_s"] \n\t" \
|
||||
"or %["#_D"h], %["#_D"h], %["#_tf"] \n\t"
|
||||
|
||||
/* SSE: psrlw (bits) */
|
||||
#define _mm_psrlh(_D, _d, _s) \
|
||||
"psrlh %["#_D"h], %["#_d"h], %["#_s"] \n\t" \
|
||||
"psrlh %["#_D"l], %["#_d"l], %["#_s"] \n\t"
|
||||
|
||||
/* SSE: psrld (bits) */
|
||||
#define _mm_psrlw(_D, _d, _s) \
|
||||
"psrlw %["#_D"h], %["#_d"h], %["#_s"] \n\t" \
|
||||
"psrlw %["#_D"l], %["#_d"l], %["#_s"] \n\t"
|
||||
|
||||
/* SSE: psrlq (bits) */
|
||||
#define _mm_psrld(_D, _d, _s) \
|
||||
"dsrl %["#_D"h], %["#_d"h], %["#_s"] \n\t" \
|
||||
"dsrl %["#_D"l], %["#_d"l], %["#_s"] \n\t"
|
||||
|
||||
/* SSE: psrldq (bytes) */
|
||||
#define _mm_psrlq(_D, _d, _s, _s64, _tf) \
|
||||
"subu %["#_tf"], %["#_s64"], %["#_s"] \n\t" \
|
||||
"dsll %["#_tf"], %["#_d"h], %["#_tf"] \n\t" \
|
||||
"dsrl %["#_D"h], %["#_d"h], %["#_s"] \n\t" \
|
||||
"dsrl %["#_D"l], %["#_d"l], %["#_s"] \n\t" \
|
||||
"or %["#_D"l], %["#_D"l], %["#_tf"] \n\t"
|
||||
|
||||
/* SSE: psrad */
|
||||
#define _mm_psraw(_D, _d, _s) \
|
||||
"psraw %["#_D"h], %["#_d"h], %["#_s"] \n\t" \
|
||||
"psraw %["#_D"l], %["#_d"l], %["#_s"] \n\t"
|
||||
|
||||
/* SSE: paddb */
|
||||
#define _mm_paddb(_D, _d, _s) \
|
||||
"paddb %["#_D"h], %["#_d"h], %["#_s"h] \n\t" \
|
||||
"paddb %["#_D"l], %["#_d"l], %["#_s"l] \n\t"
|
||||
|
||||
/* SSE: paddw */
|
||||
#define _mm_paddh(_D, _d, _s) \
|
||||
"paddh %["#_D"h], %["#_d"h], %["#_s"h] \n\t" \
|
||||
"paddh %["#_D"l], %["#_d"l], %["#_s"l] \n\t"
|
||||
|
||||
/* SSE: paddd */
|
||||
#define _mm_paddw(_D, _d, _s) \
|
||||
"paddw %["#_D"h], %["#_d"h], %["#_s"h] \n\t" \
|
||||
"paddw %["#_D"l], %["#_d"l], %["#_s"l] \n\t"
|
||||
|
||||
/* SSE: paddq */
|
||||
#define _mm_paddd(_D, _d, _s) \
|
||||
"dadd %["#_D"h], %["#_d"h], %["#_s"h] \n\t" \
|
||||
"dadd %["#_D"l], %["#_d"l], %["#_s"l] \n\t"
|
||||
|
||||
/* SSE: psubw */
|
||||
#define _mm_psubh(_D, _d, _s) \
|
||||
"psubh %["#_D"h], %["#_d"h], %["#_s"h] \n\t" \
|
||||
"psubh %["#_D"l], %["#_d"l], %["#_s"l] \n\t"
|
||||
|
||||
/* SSE: psubd */
|
||||
#define _mm_psubw(_D, _d, _s) \
|
||||
"psubw %["#_D"h], %["#_d"h], %["#_s"h] \n\t" \
|
||||
"psubw %["#_D"l], %["#_d"l], %["#_s"l] \n\t"
|
||||
|
||||
/* SSE: pmaxub */
|
||||
#define _mm_pmaxub(_D, _d, _s) \
|
||||
"pmaxub %["#_D"h], %["#_d"h], %["#_s"h] \n\t" \
|
||||
"pmaxub %["#_D"l], %["#_d"l], %["#_s"l] \n\t"
|
||||
|
||||
/* SSE: pmullw */
|
||||
#define _mm_pmullh(_D, _d, _s) \
|
||||
"pmullh %["#_D"h], %["#_d"h], %["#_s"h] \n\t" \
|
||||
"pmullh %["#_D"l], %["#_d"l], %["#_s"l] \n\t"
|
||||
|
||||
/* SSE: pmulhw */
|
||||
#define _mm_pmulhh(_D, _d, _s) \
|
||||
"pmulhh %["#_D"h], %["#_d"h], %["#_s"h] \n\t" \
|
||||
"pmulhh %["#_D"l], %["#_d"l], %["#_s"l] \n\t"
|
||||
|
||||
/* SSE: pmuludq */
|
||||
#define _mm_pmuluw(_D, _d, _s) \
|
||||
"pmuluw %["#_D"h], %["#_d"h], %["#_s"h] \n\t" \
|
||||
"pmuluw %["#_D"l], %["#_d"l], %["#_s"l] \n\t"
|
||||
|
||||
/* SSE: packsswb */
|
||||
#define _mm_packsshb(_D, _d, _s, _t) \
|
||||
__mm_packxxxx(packsshb, _D, _d, _s, _t)
|
||||
|
||||
/* SSE: packssdw */
|
||||
#define _mm_packsswh(_D, _d, _s, _t) \
|
||||
__mm_packxxxx(packsswh, _D, _d, _s, _t)
|
||||
|
||||
/* SSE: packuswb */
|
||||
#define _mm_packushb(_D, _d, _s, _t) \
|
||||
__mm_packxxxx(packushb, _D, _d, _s, _t)
|
||||
|
||||
/* SSE: punpcklbw */
|
||||
#define _mm_punpcklbh(_D, _d, _s) \
|
||||
"punpckhbh %["#_D"h], %["#_d"l], %["#_s"l] \n\t" \
|
||||
"punpcklbh %["#_D"l], %["#_d"l], %["#_s"l] \n\t"
|
||||
|
||||
/* SSE: punpcklwd */
|
||||
#define _mm_punpcklhw(_D, _d, _s) \
|
||||
"punpckhhw %["#_D"h], %["#_d"l], %["#_s"l] \n\t" \
|
||||
"punpcklhw %["#_D"l], %["#_d"l], %["#_s"l] \n\t"
|
||||
|
||||
/* SSE: punpckldq */
|
||||
#define _mm_punpcklwd(_D, _d, _s) \
|
||||
"punpckhwd %["#_D"h], %["#_d"l], %["#_s"l] \n\t" \
|
||||
"punpcklwd %["#_D"l], %["#_d"l], %["#_s"l] \n\t"
|
||||
|
||||
/* SSE: punpcklqdq */
|
||||
#define _mm_punpckldq(_D, _d, _s) \
|
||||
"mov.d %["#_D"h], %["#_s"l] \n\t" \
|
||||
"mov.d %["#_D"l], %["#_d"l] \n\t"
|
||||
|
||||
/* SSE: punpckhbw */
|
||||
#define _mm_punpckhbh(_D, _d, _s) \
|
||||
"punpcklbh %["#_D"l], %["#_d"h], %["#_s"h] \n\t" \
|
||||
"punpckhbh %["#_D"h], %["#_d"h], %["#_s"h] \n\t"
|
||||
|
||||
/* SSE: punpckhwd */
|
||||
#define _mm_punpckhhw(_D, _d, _s) \
|
||||
"punpcklhw %["#_D"l], %["#_d"h], %["#_s"h] \n\t" \
|
||||
"punpckhhw %["#_D"h], %["#_d"h], %["#_s"h] \n\t"
|
||||
|
||||
/* SSE: punpckhdq */
|
||||
#define _mm_punpckhwd(_D, _d, _s) \
|
||||
"punpcklwd %["#_D"l], %["#_d"h], %["#_s"h] \n\t" \
|
||||
"punpckhwd %["#_D"h], %["#_d"h], %["#_s"h] \n\t"
|
||||
|
||||
/* SSE: punpckhqdq */
|
||||
#define _mm_punpckhdq(_D, _d, _s) \
|
||||
"mov.d %["#_D"l], %["#_d"h] \n\t" \
|
||||
"mov.d %["#_D"h], %["#_s"h] \n\t"
|
||||
|
||||
#endif /* __MMI_HELPERS_H__ */
|
||||
|
||||
615
gfx/2d/MacIOSurface.cpp
Normal file
615
gfx/2d/MacIOSurface.cpp
Normal file
|
|
@ -0,0 +1,615 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
// vim:set ts=2 sts=2 sw=2 et cin:
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "MacIOSurface.h"
|
||||
#include <OpenGL/gl.h>
|
||||
#include <QuartzCore/QuartzCore.h>
|
||||
#include <dlfcn.h>
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "GLConsts.h"
|
||||
|
||||
using namespace mozilla;
|
||||
// IOSurface signatures
|
||||
#define IOSURFACE_FRAMEWORK_PATH \
|
||||
"/System/Library/Frameworks/IOSurface.framework/IOSurface"
|
||||
#define OPENGL_FRAMEWORK_PATH \
|
||||
"/System/Library/Frameworks/OpenGL.framework/OpenGL"
|
||||
#define COREGRAPHICS_FRAMEWORK_PATH \
|
||||
"/System/Library/Frameworks/ApplicationServices.framework/Frameworks/" \
|
||||
"CoreGraphics.framework/CoreGraphics"
|
||||
#define COREVIDEO_FRAMEWORK_PATH \
|
||||
"/System/Library/Frameworks/ApplicationServices.framework/Frameworks/" \
|
||||
"CoreVideo.framework/CoreVideo"
|
||||
|
||||
#define GET_CONST(const_name) \
|
||||
((CFStringRef*) dlsym(sIOSurfaceFramework, const_name))
|
||||
#define GET_IOSYM(dest,sym_name) \
|
||||
(typeof(dest)) dlsym(sIOSurfaceFramework, sym_name)
|
||||
#define GET_CGLSYM(dest,sym_name) \
|
||||
(typeof(dest)) dlsym(sOpenGLFramework, sym_name)
|
||||
#define GET_CGSYM(dest,sym_name) \
|
||||
(typeof(dest)) dlsym(sCoreGraphicsFramework, sym_name)
|
||||
#define GET_CVSYM(dest, sym_name) \
|
||||
(typeof(dest)) dlsym(sCoreVideoFramework, sym_name)
|
||||
|
||||
MacIOSurfaceLib::LibraryUnloader MacIOSurfaceLib::sLibraryUnloader;
|
||||
bool MacIOSurfaceLib::isLoaded = false;
|
||||
void* MacIOSurfaceLib::sIOSurfaceFramework;
|
||||
void* MacIOSurfaceLib::sOpenGLFramework;
|
||||
void* MacIOSurfaceLib::sCoreGraphicsFramework;
|
||||
void* MacIOSurfaceLib::sCoreVideoFramework;
|
||||
IOSurfaceCreateFunc MacIOSurfaceLib::sCreate;
|
||||
IOSurfaceGetIDFunc MacIOSurfaceLib::sGetID;
|
||||
IOSurfaceLookupFunc MacIOSurfaceLib::sLookup;
|
||||
IOSurfaceGetBaseAddressFunc MacIOSurfaceLib::sGetBaseAddress;
|
||||
IOSurfaceGetBaseAddressOfPlaneFunc MacIOSurfaceLib::sGetBaseAddressOfPlane;
|
||||
IOSurfaceSizePlaneTFunc MacIOSurfaceLib::sWidth;
|
||||
IOSurfaceSizePlaneTFunc MacIOSurfaceLib::sHeight;
|
||||
IOSurfaceSizeTFunc MacIOSurfaceLib::sPlaneCount;
|
||||
IOSurfaceSizePlaneTFunc MacIOSurfaceLib::sBytesPerRow;
|
||||
IOSurfaceGetPropertyMaximumFunc MacIOSurfaceLib::sGetPropertyMaximum;
|
||||
IOSurfaceVoidFunc MacIOSurfaceLib::sIncrementUseCount;
|
||||
IOSurfaceVoidFunc MacIOSurfaceLib::sDecrementUseCount;
|
||||
IOSurfaceLockFunc MacIOSurfaceLib::sLock;
|
||||
IOSurfaceUnlockFunc MacIOSurfaceLib::sUnlock;
|
||||
CGLTexImageIOSurface2DFunc MacIOSurfaceLib::sTexImage;
|
||||
IOSurfaceContextCreateFunc MacIOSurfaceLib::sIOSurfaceContextCreate;
|
||||
IOSurfaceContextCreateImageFunc MacIOSurfaceLib::sIOSurfaceContextCreateImage;
|
||||
IOSurfaceContextGetSurfaceFunc MacIOSurfaceLib::sIOSurfaceContextGetSurface;
|
||||
CVPixelBufferGetIOSurfaceFunc MacIOSurfaceLib::sCVPixelBufferGetIOSurface;
|
||||
unsigned int (*MacIOSurfaceLib::sCGContextGetTypePtr) (CGContextRef) = nullptr;
|
||||
IOSurfacePixelFormatFunc MacIOSurfaceLib::sPixelFormat;
|
||||
|
||||
CFStringRef MacIOSurfaceLib::kPropWidth;
|
||||
CFStringRef MacIOSurfaceLib::kPropHeight;
|
||||
CFStringRef MacIOSurfaceLib::kPropBytesPerElem;
|
||||
CFStringRef MacIOSurfaceLib::kPropBytesPerRow;
|
||||
CFStringRef MacIOSurfaceLib::kPropIsGlobal;
|
||||
|
||||
bool MacIOSurfaceLib::isInit() {
|
||||
// Guard against trying to reload the library
|
||||
// if it is not available.
|
||||
if (!isLoaded)
|
||||
LoadLibrary();
|
||||
MOZ_ASSERT(sIOSurfaceFramework);
|
||||
return sIOSurfaceFramework;
|
||||
}
|
||||
|
||||
IOSurfacePtr MacIOSurfaceLib::IOSurfaceCreate(CFDictionaryRef properties) {
|
||||
return sCreate(properties);
|
||||
}
|
||||
|
||||
IOSurfacePtr MacIOSurfaceLib::IOSurfaceLookup(IOSurfaceID aIOSurfaceID) {
|
||||
return sLookup(aIOSurfaceID);
|
||||
}
|
||||
|
||||
IOSurfaceID MacIOSurfaceLib::IOSurfaceGetID(IOSurfacePtr aIOSurfacePtr) {
|
||||
return sGetID(aIOSurfacePtr);
|
||||
}
|
||||
|
||||
void* MacIOSurfaceLib::IOSurfaceGetBaseAddress(IOSurfacePtr aIOSurfacePtr) {
|
||||
return sGetBaseAddress(aIOSurfacePtr);
|
||||
}
|
||||
|
||||
void* MacIOSurfaceLib::IOSurfaceGetBaseAddressOfPlane(IOSurfacePtr aIOSurfacePtr,
|
||||
size_t planeIndex) {
|
||||
return sGetBaseAddressOfPlane(aIOSurfacePtr, planeIndex);
|
||||
}
|
||||
|
||||
size_t MacIOSurfaceLib::IOSurfaceGetPlaneCount(IOSurfacePtr aIOSurfacePtr) {
|
||||
return sPlaneCount(aIOSurfacePtr);
|
||||
}
|
||||
|
||||
size_t MacIOSurfaceLib::IOSurfaceGetWidth(IOSurfacePtr aIOSurfacePtr, size_t plane) {
|
||||
return sWidth(aIOSurfacePtr, plane);
|
||||
}
|
||||
|
||||
size_t MacIOSurfaceLib::IOSurfaceGetHeight(IOSurfacePtr aIOSurfacePtr, size_t plane) {
|
||||
return sHeight(aIOSurfacePtr, plane);
|
||||
}
|
||||
|
||||
size_t MacIOSurfaceLib::IOSurfaceGetBytesPerRow(IOSurfacePtr aIOSurfacePtr, size_t plane) {
|
||||
return sBytesPerRow(aIOSurfacePtr, plane);
|
||||
}
|
||||
|
||||
size_t MacIOSurfaceLib::IOSurfaceGetPropertyMaximum(CFStringRef property) {
|
||||
return sGetPropertyMaximum(property);
|
||||
}
|
||||
|
||||
OSType MacIOSurfaceLib::IOSurfaceGetPixelFormat(IOSurfacePtr aIOSurfacePtr) {
|
||||
return sPixelFormat(aIOSurfacePtr);
|
||||
}
|
||||
|
||||
IOReturn MacIOSurfaceLib::IOSurfaceLock(IOSurfacePtr aIOSurfacePtr,
|
||||
uint32_t options, uint32_t* seed) {
|
||||
return sLock(aIOSurfacePtr, options, seed);
|
||||
}
|
||||
|
||||
IOReturn MacIOSurfaceLib::IOSurfaceUnlock(IOSurfacePtr aIOSurfacePtr,
|
||||
uint32_t options, uint32_t *seed) {
|
||||
return sUnlock(aIOSurfacePtr, options, seed);
|
||||
}
|
||||
|
||||
void MacIOSurfaceLib::IOSurfaceIncrementUseCount(IOSurfacePtr aIOSurfacePtr) {
|
||||
sIncrementUseCount(aIOSurfacePtr);
|
||||
}
|
||||
|
||||
void MacIOSurfaceLib::IOSurfaceDecrementUseCount(IOSurfacePtr aIOSurfacePtr) {
|
||||
sDecrementUseCount(aIOSurfacePtr);
|
||||
}
|
||||
|
||||
CGLError MacIOSurfaceLib::CGLTexImageIOSurface2D(CGLContextObj ctxt,
|
||||
GLenum target, GLenum internalFormat,
|
||||
GLsizei width, GLsizei height,
|
||||
GLenum format, GLenum type,
|
||||
IOSurfacePtr ioSurface, GLuint plane) {
|
||||
return sTexImage(ctxt, target, internalFormat, width, height,
|
||||
format, type, ioSurface, plane);
|
||||
}
|
||||
|
||||
IOSurfacePtr MacIOSurfaceLib::CVPixelBufferGetIOSurface(CVPixelBufferRef aPixelBuffer) {
|
||||
return sCVPixelBufferGetIOSurface(aPixelBuffer);
|
||||
}
|
||||
|
||||
CGContextRef MacIOSurfaceLib::IOSurfaceContextCreate(IOSurfacePtr aIOSurfacePtr,
|
||||
unsigned aWidth, unsigned aHeight,
|
||||
unsigned aBitsPerComponent, unsigned aBytes,
|
||||
CGColorSpaceRef aColorSpace, CGBitmapInfo bitmapInfo) {
|
||||
if (!sIOSurfaceContextCreate)
|
||||
return nullptr;
|
||||
return sIOSurfaceContextCreate(aIOSurfacePtr, aWidth, aHeight, aBitsPerComponent, aBytes, aColorSpace, bitmapInfo);
|
||||
}
|
||||
|
||||
CGImageRef MacIOSurfaceLib::IOSurfaceContextCreateImage(CGContextRef aContext) {
|
||||
if (!sIOSurfaceContextCreateImage)
|
||||
return nullptr;
|
||||
return sIOSurfaceContextCreateImage(aContext);
|
||||
}
|
||||
|
||||
IOSurfacePtr MacIOSurfaceLib::IOSurfaceContextGetSurface(CGContextRef aContext) {
|
||||
if (!sIOSurfaceContextGetSurface)
|
||||
return nullptr;
|
||||
return sIOSurfaceContextGetSurface(aContext);
|
||||
}
|
||||
|
||||
CFStringRef MacIOSurfaceLib::GetIOConst(const char* symbole) {
|
||||
CFStringRef *address = (CFStringRef*)dlsym(sIOSurfaceFramework, symbole);
|
||||
if (!address)
|
||||
return nullptr;
|
||||
|
||||
return *address;
|
||||
}
|
||||
|
||||
void MacIOSurfaceLib::LoadLibrary() {
|
||||
if (isLoaded) {
|
||||
return;
|
||||
}
|
||||
isLoaded = true;
|
||||
sIOSurfaceFramework = dlopen(IOSURFACE_FRAMEWORK_PATH,
|
||||
RTLD_LAZY | RTLD_LOCAL);
|
||||
sOpenGLFramework = dlopen(OPENGL_FRAMEWORK_PATH,
|
||||
RTLD_LAZY | RTLD_LOCAL);
|
||||
|
||||
sCoreGraphicsFramework = dlopen(COREGRAPHICS_FRAMEWORK_PATH,
|
||||
RTLD_LAZY | RTLD_LOCAL);
|
||||
|
||||
sCoreVideoFramework = dlopen(COREVIDEO_FRAMEWORK_PATH,
|
||||
RTLD_LAZY | RTLD_LOCAL);
|
||||
|
||||
if (!sIOSurfaceFramework || !sOpenGLFramework || !sCoreGraphicsFramework ||
|
||||
!sCoreVideoFramework) {
|
||||
if (sIOSurfaceFramework)
|
||||
dlclose(sIOSurfaceFramework);
|
||||
if (sOpenGLFramework)
|
||||
dlclose(sOpenGLFramework);
|
||||
if (sCoreGraphicsFramework)
|
||||
dlclose(sCoreGraphicsFramework);
|
||||
if (sCoreVideoFramework)
|
||||
dlclose(sCoreVideoFramework);
|
||||
sIOSurfaceFramework = nullptr;
|
||||
sOpenGLFramework = nullptr;
|
||||
sCoreGraphicsFramework = nullptr;
|
||||
sCoreVideoFramework = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
kPropWidth = GetIOConst("kIOSurfaceWidth");
|
||||
kPropHeight = GetIOConst("kIOSurfaceHeight");
|
||||
kPropBytesPerElem = GetIOConst("kIOSurfaceBytesPerElement");
|
||||
kPropBytesPerRow = GetIOConst("kIOSurfaceBytesPerRow");
|
||||
kPropIsGlobal = GetIOConst("kIOSurfaceIsGlobal");
|
||||
sCreate = GET_IOSYM(sCreate, "IOSurfaceCreate");
|
||||
sGetID = GET_IOSYM(sGetID, "IOSurfaceGetID");
|
||||
sWidth = GET_IOSYM(sWidth, "IOSurfaceGetWidthOfPlane");
|
||||
sHeight = GET_IOSYM(sHeight, "IOSurfaceGetHeightOfPlane");
|
||||
sBytesPerRow = GET_IOSYM(sBytesPerRow, "IOSurfaceGetBytesPerRowOfPlane");
|
||||
sGetPropertyMaximum = GET_IOSYM(sGetPropertyMaximum, "IOSurfaceGetPropertyMaximum");
|
||||
sLookup = GET_IOSYM(sLookup, "IOSurfaceLookup");
|
||||
sLock = GET_IOSYM(sLock, "IOSurfaceLock");
|
||||
sUnlock = GET_IOSYM(sUnlock, "IOSurfaceUnlock");
|
||||
sIncrementUseCount =
|
||||
GET_IOSYM(sIncrementUseCount, "IOSurfaceIncrementUseCount");
|
||||
sDecrementUseCount =
|
||||
GET_IOSYM(sDecrementUseCount, "IOSurfaceDecrementUseCount");
|
||||
sGetBaseAddress = GET_IOSYM(sGetBaseAddress, "IOSurfaceGetBaseAddress");
|
||||
sGetBaseAddressOfPlane =
|
||||
GET_IOSYM(sGetBaseAddressOfPlane, "IOSurfaceGetBaseAddressOfPlane");
|
||||
sPlaneCount = GET_IOSYM(sPlaneCount, "IOSurfaceGetPlaneCount");
|
||||
sPixelFormat = GET_IOSYM(sPixelFormat, "IOSurfaceGetPixelFormat");
|
||||
|
||||
sTexImage = GET_CGLSYM(sTexImage, "CGLTexImageIOSurface2D");
|
||||
sCGContextGetTypePtr = (unsigned int (*)(CGContext*))dlsym(RTLD_DEFAULT, "CGContextGetType");
|
||||
|
||||
sCVPixelBufferGetIOSurface =
|
||||
GET_CVSYM(sCVPixelBufferGetIOSurface, "CVPixelBufferGetIOSurface");
|
||||
|
||||
// Optional symbols
|
||||
sIOSurfaceContextCreate = GET_CGSYM(sIOSurfaceContextCreate, "CGIOSurfaceContextCreate");
|
||||
sIOSurfaceContextCreateImage = GET_CGSYM(sIOSurfaceContextCreateImage, "CGIOSurfaceContextCreateImage");
|
||||
sIOSurfaceContextGetSurface = GET_CGSYM(sIOSurfaceContextGetSurface, "CGIOSurfaceContextGetSurface");
|
||||
|
||||
if (!sCreate || !sGetID || !sLookup || !sTexImage || !sGetBaseAddress ||
|
||||
!sGetBaseAddressOfPlane || !sPlaneCount ||
|
||||
!kPropWidth || !kPropHeight || !kPropBytesPerElem || !kPropIsGlobal ||
|
||||
!sLock || !sUnlock || !sIncrementUseCount || !sDecrementUseCount ||
|
||||
!sWidth || !sHeight || !kPropBytesPerRow ||
|
||||
!sBytesPerRow || !sGetPropertyMaximum || !sCVPixelBufferGetIOSurface) {
|
||||
CloseLibrary();
|
||||
}
|
||||
}
|
||||
|
||||
void MacIOSurfaceLib::CloseLibrary() {
|
||||
if (sIOSurfaceFramework) {
|
||||
dlclose(sIOSurfaceFramework);
|
||||
}
|
||||
if (sOpenGLFramework) {
|
||||
dlclose(sOpenGLFramework);
|
||||
}
|
||||
if (sCoreVideoFramework) {
|
||||
dlclose(sCoreVideoFramework);
|
||||
}
|
||||
sIOSurfaceFramework = nullptr;
|
||||
sOpenGLFramework = nullptr;
|
||||
sCoreVideoFramework = nullptr;
|
||||
}
|
||||
|
||||
MacIOSurface::MacIOSurface(const void* aIOSurfacePtr,
|
||||
double aContentsScaleFactor, bool aHasAlpha)
|
||||
: mIOSurfacePtr(aIOSurfacePtr)
|
||||
, mContentsScaleFactor(aContentsScaleFactor)
|
||||
, mHasAlpha(aHasAlpha)
|
||||
{
|
||||
CFRetain(mIOSurfacePtr);
|
||||
IncrementUseCount();
|
||||
}
|
||||
|
||||
MacIOSurface::~MacIOSurface() {
|
||||
DecrementUseCount();
|
||||
CFRelease(mIOSurfacePtr);
|
||||
}
|
||||
|
||||
already_AddRefed<MacIOSurface> MacIOSurface::CreateIOSurface(int aWidth, int aHeight,
|
||||
double aContentsScaleFactor,
|
||||
bool aHasAlpha) {
|
||||
if (!MacIOSurfaceLib::isInit() || aContentsScaleFactor <= 0)
|
||||
return nullptr;
|
||||
|
||||
CFMutableDictionaryRef props = ::CFDictionaryCreateMutable(
|
||||
kCFAllocatorDefault, 4,
|
||||
&kCFTypeDictionaryKeyCallBacks,
|
||||
&kCFTypeDictionaryValueCallBacks);
|
||||
if (!props)
|
||||
return nullptr;
|
||||
|
||||
MOZ_ASSERT((size_t)aWidth <= GetMaxWidth());
|
||||
MOZ_ASSERT((size_t)aHeight <= GetMaxHeight());
|
||||
|
||||
int32_t bytesPerElem = 4;
|
||||
size_t intScaleFactor = ceil(aContentsScaleFactor);
|
||||
aWidth *= intScaleFactor;
|
||||
aHeight *= intScaleFactor;
|
||||
CFNumberRef cfWidth = ::CFNumberCreate(nullptr, kCFNumberSInt32Type, &aWidth);
|
||||
CFNumberRef cfHeight = ::CFNumberCreate(nullptr, kCFNumberSInt32Type, &aHeight);
|
||||
CFNumberRef cfBytesPerElem = ::CFNumberCreate(nullptr, kCFNumberSInt32Type, &bytesPerElem);
|
||||
::CFDictionaryAddValue(props, MacIOSurfaceLib::kPropWidth,
|
||||
cfWidth);
|
||||
::CFRelease(cfWidth);
|
||||
::CFDictionaryAddValue(props, MacIOSurfaceLib::kPropHeight,
|
||||
cfHeight);
|
||||
::CFRelease(cfHeight);
|
||||
::CFDictionaryAddValue(props, MacIOSurfaceLib::kPropBytesPerElem,
|
||||
cfBytesPerElem);
|
||||
::CFRelease(cfBytesPerElem);
|
||||
::CFDictionaryAddValue(props, MacIOSurfaceLib::kPropIsGlobal,
|
||||
kCFBooleanTrue);
|
||||
|
||||
IOSurfacePtr surfaceRef = MacIOSurfaceLib::IOSurfaceCreate(props);
|
||||
::CFRelease(props);
|
||||
|
||||
if (!surfaceRef)
|
||||
return nullptr;
|
||||
|
||||
RefPtr<MacIOSurface> ioSurface = new MacIOSurface(surfaceRef, aContentsScaleFactor, aHasAlpha);
|
||||
if (!ioSurface) {
|
||||
::CFRelease(surfaceRef);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Release the IOSurface because MacIOSurface retained it
|
||||
CFRelease(surfaceRef);
|
||||
|
||||
return ioSurface.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<MacIOSurface> MacIOSurface::LookupSurface(IOSurfaceID aIOSurfaceID,
|
||||
double aContentsScaleFactor,
|
||||
bool aHasAlpha) {
|
||||
if (!MacIOSurfaceLib::isInit() || aContentsScaleFactor <= 0)
|
||||
return nullptr;
|
||||
|
||||
IOSurfacePtr surfaceRef = MacIOSurfaceLib::IOSurfaceLookup(aIOSurfaceID);
|
||||
if (!surfaceRef)
|
||||
return nullptr;
|
||||
|
||||
RefPtr<MacIOSurface> ioSurface = new MacIOSurface(surfaceRef, aContentsScaleFactor, aHasAlpha);
|
||||
if (!ioSurface) {
|
||||
::CFRelease(surfaceRef);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Release the IOSurface because MacIOSurface retained it
|
||||
CFRelease(surfaceRef);
|
||||
|
||||
return ioSurface.forget();
|
||||
}
|
||||
|
||||
IOSurfaceID MacIOSurface::GetIOSurfaceID() {
|
||||
return MacIOSurfaceLib::IOSurfaceGetID(mIOSurfacePtr);
|
||||
}
|
||||
|
||||
void* MacIOSurface::GetBaseAddress() {
|
||||
return MacIOSurfaceLib::IOSurfaceGetBaseAddress(mIOSurfacePtr);
|
||||
}
|
||||
|
||||
void* MacIOSurface::GetBaseAddressOfPlane(size_t aPlaneIndex)
|
||||
{
|
||||
return MacIOSurfaceLib::IOSurfaceGetBaseAddressOfPlane(mIOSurfacePtr,
|
||||
aPlaneIndex);
|
||||
}
|
||||
|
||||
size_t MacIOSurface::GetWidth(size_t plane) {
|
||||
size_t intScaleFactor = ceil(mContentsScaleFactor);
|
||||
return GetDevicePixelWidth(plane) / intScaleFactor;
|
||||
}
|
||||
|
||||
size_t MacIOSurface::GetHeight(size_t plane) {
|
||||
size_t intScaleFactor = ceil(mContentsScaleFactor);
|
||||
return GetDevicePixelHeight(plane) / intScaleFactor;
|
||||
}
|
||||
|
||||
size_t MacIOSurface::GetPlaneCount() {
|
||||
return MacIOSurfaceLib::IOSurfaceGetPlaneCount(mIOSurfacePtr);
|
||||
}
|
||||
|
||||
/*static*/ size_t MacIOSurface::GetMaxWidth() {
|
||||
if (!MacIOSurfaceLib::isInit())
|
||||
return -1;
|
||||
return MacIOSurfaceLib::IOSurfaceGetPropertyMaximum(MacIOSurfaceLib::kPropWidth);
|
||||
}
|
||||
|
||||
/*static*/ size_t MacIOSurface::GetMaxHeight() {
|
||||
if (!MacIOSurfaceLib::isInit())
|
||||
return -1;
|
||||
return MacIOSurfaceLib::IOSurfaceGetPropertyMaximum(MacIOSurfaceLib::kPropHeight);
|
||||
}
|
||||
|
||||
size_t MacIOSurface::GetDevicePixelWidth(size_t plane) {
|
||||
return MacIOSurfaceLib::IOSurfaceGetWidth(mIOSurfacePtr, plane);
|
||||
}
|
||||
|
||||
size_t MacIOSurface::GetDevicePixelHeight(size_t plane) {
|
||||
return MacIOSurfaceLib::IOSurfaceGetHeight(mIOSurfacePtr, plane);
|
||||
}
|
||||
|
||||
size_t MacIOSurface::GetBytesPerRow(size_t plane) {
|
||||
return MacIOSurfaceLib::IOSurfaceGetBytesPerRow(mIOSurfacePtr, plane);
|
||||
}
|
||||
|
||||
OSType MacIOSurface::GetPixelFormat() {
|
||||
return MacIOSurfaceLib::IOSurfaceGetPixelFormat(mIOSurfacePtr);
|
||||
}
|
||||
|
||||
void MacIOSurface::IncrementUseCount() {
|
||||
MacIOSurfaceLib::IOSurfaceIncrementUseCount(mIOSurfacePtr);
|
||||
}
|
||||
|
||||
void MacIOSurface::DecrementUseCount() {
|
||||
MacIOSurfaceLib::IOSurfaceDecrementUseCount(mIOSurfacePtr);
|
||||
}
|
||||
|
||||
#define READ_ONLY 0x1
|
||||
void MacIOSurface::Lock(bool aReadOnly) {
|
||||
MacIOSurfaceLib::IOSurfaceLock(mIOSurfacePtr, aReadOnly ? READ_ONLY : 0, nullptr);
|
||||
}
|
||||
|
||||
void MacIOSurface::Unlock(bool aReadOnly) {
|
||||
MacIOSurfaceLib::IOSurfaceUnlock(mIOSurfacePtr, aReadOnly ? READ_ONLY : 0, nullptr);
|
||||
}
|
||||
|
||||
using mozilla::gfx::SourceSurface;
|
||||
using mozilla::gfx::IntSize;
|
||||
using mozilla::gfx::SurfaceFormat;
|
||||
|
||||
void
|
||||
MacIOSurfaceBufferDeallocator(void* aClosure)
|
||||
{
|
||||
MOZ_ASSERT(aClosure);
|
||||
|
||||
delete [] static_cast<unsigned char*>(aClosure);
|
||||
}
|
||||
|
||||
already_AddRefed<SourceSurface>
|
||||
MacIOSurface::GetAsSurface() {
|
||||
Lock();
|
||||
size_t bytesPerRow = GetBytesPerRow();
|
||||
size_t ioWidth = GetDevicePixelWidth();
|
||||
size_t ioHeight = GetDevicePixelHeight();
|
||||
|
||||
unsigned char* ioData = (unsigned char*)GetBaseAddress();
|
||||
unsigned char* dataCpy =
|
||||
new unsigned char[bytesPerRow * ioHeight / sizeof(unsigned char)];
|
||||
for (size_t i = 0; i < ioHeight; i++) {
|
||||
memcpy(dataCpy + i * bytesPerRow,
|
||||
ioData + i * bytesPerRow, ioWidth * 4);
|
||||
}
|
||||
|
||||
Unlock();
|
||||
|
||||
SurfaceFormat format = HasAlpha() ? mozilla::gfx::SurfaceFormat::B8G8R8A8 :
|
||||
mozilla::gfx::SurfaceFormat::B8G8R8X8;
|
||||
|
||||
RefPtr<mozilla::gfx::DataSourceSurface> surf =
|
||||
mozilla::gfx::Factory::CreateWrappingDataSourceSurface(dataCpy,
|
||||
bytesPerRow,
|
||||
IntSize(ioWidth, ioHeight),
|
||||
format,
|
||||
&MacIOSurfaceBufferDeallocator,
|
||||
static_cast<void*>(dataCpy));
|
||||
|
||||
return surf.forget();
|
||||
}
|
||||
|
||||
SurfaceFormat
|
||||
MacIOSurface::GetFormat()
|
||||
{
|
||||
OSType pixelFormat = GetPixelFormat();
|
||||
if (pixelFormat == '420v') {
|
||||
return SurfaceFormat::NV12;
|
||||
} else if (pixelFormat == '2vuy') {
|
||||
return SurfaceFormat::YUV422;
|
||||
} else {
|
||||
return HasAlpha() ? SurfaceFormat::R8G8B8A8 : SurfaceFormat::R8G8B8X8;
|
||||
}
|
||||
}
|
||||
|
||||
SurfaceFormat
|
||||
MacIOSurface::GetReadFormat()
|
||||
{
|
||||
OSType pixelFormat = GetPixelFormat();
|
||||
if (pixelFormat == '420v') {
|
||||
return SurfaceFormat::NV12;
|
||||
} else if (pixelFormat == '2vuy') {
|
||||
return SurfaceFormat::R8G8B8X8;
|
||||
} else {
|
||||
return HasAlpha() ? SurfaceFormat::R8G8B8A8 : SurfaceFormat::R8G8B8X8;
|
||||
}
|
||||
}
|
||||
|
||||
CGLError
|
||||
MacIOSurface::CGLTexImageIOSurface2D(CGLContextObj ctx, size_t plane)
|
||||
{
|
||||
MOZ_ASSERT(plane >= 0);
|
||||
OSType pixelFormat = GetPixelFormat();
|
||||
|
||||
GLenum internalFormat;
|
||||
GLenum format;
|
||||
GLenum type;
|
||||
if (pixelFormat == '420v') {
|
||||
MOZ_ASSERT(GetPlaneCount() == 2);
|
||||
MOZ_ASSERT(plane < 2);
|
||||
|
||||
if (plane == 0) {
|
||||
internalFormat = format = GL_LUMINANCE;
|
||||
} else {
|
||||
internalFormat = format = GL_LUMINANCE_ALPHA;
|
||||
}
|
||||
type = GL_UNSIGNED_BYTE;
|
||||
} else if (pixelFormat == '2vuy') {
|
||||
MOZ_ASSERT(plane == 0);
|
||||
|
||||
internalFormat = GL_RGB;
|
||||
format = LOCAL_GL_YCBCR_422_APPLE;
|
||||
type = GL_UNSIGNED_SHORT_8_8_APPLE;
|
||||
} else {
|
||||
MOZ_ASSERT(plane == 0);
|
||||
|
||||
internalFormat = HasAlpha() ? GL_RGBA : GL_RGB;
|
||||
format = GL_BGRA;
|
||||
type = GL_UNSIGNED_INT_8_8_8_8_REV;
|
||||
}
|
||||
CGLError temp = MacIOSurfaceLib::CGLTexImageIOSurface2D(ctx,
|
||||
GL_TEXTURE_RECTANGLE_ARB,
|
||||
internalFormat,
|
||||
GetDevicePixelWidth(plane),
|
||||
GetDevicePixelHeight(plane),
|
||||
format,
|
||||
type,
|
||||
mIOSurfacePtr, plane);
|
||||
return temp;
|
||||
}
|
||||
|
||||
static
|
||||
CGColorSpaceRef CreateSystemColorSpace() {
|
||||
CGColorSpaceRef cspace = ::CGDisplayCopyColorSpace(::CGMainDisplayID());
|
||||
if (!cspace) {
|
||||
cspace = ::CGColorSpaceCreateDeviceRGB();
|
||||
}
|
||||
return cspace;
|
||||
}
|
||||
|
||||
CGContextRef MacIOSurface::CreateIOSurfaceContext() {
|
||||
CGColorSpaceRef cspace = CreateSystemColorSpace();
|
||||
CGContextRef ref = MacIOSurfaceLib::IOSurfaceContextCreate(mIOSurfacePtr,
|
||||
GetDevicePixelWidth(),
|
||||
GetDevicePixelHeight(),
|
||||
8, 32, cspace, 0x2002);
|
||||
::CGColorSpaceRelease(cspace);
|
||||
return ref;
|
||||
}
|
||||
|
||||
CGImageRef MacIOSurface::CreateImageFromIOSurfaceContext(CGContextRef aContext) {
|
||||
if (!MacIOSurfaceLib::isInit())
|
||||
return nullptr;
|
||||
|
||||
return MacIOSurfaceLib::IOSurfaceContextCreateImage(aContext);
|
||||
}
|
||||
|
||||
already_AddRefed<MacIOSurface> MacIOSurface::IOSurfaceContextGetSurface(CGContextRef aContext,
|
||||
double aContentsScaleFactor,
|
||||
bool aHasAlpha) {
|
||||
if (!MacIOSurfaceLib::isInit() || aContentsScaleFactor <= 0)
|
||||
return nullptr;
|
||||
|
||||
IOSurfacePtr surfaceRef = MacIOSurfaceLib::IOSurfaceContextGetSurface(aContext);
|
||||
if (!surfaceRef)
|
||||
return nullptr;
|
||||
|
||||
RefPtr<MacIOSurface> ioSurface = new MacIOSurface(surfaceRef, aContentsScaleFactor, aHasAlpha);
|
||||
if (!ioSurface) {
|
||||
::CFRelease(surfaceRef);
|
||||
return nullptr;
|
||||
}
|
||||
return ioSurface.forget();
|
||||
}
|
||||
|
||||
|
||||
CGContextType GetContextType(CGContextRef ref)
|
||||
{
|
||||
if (!MacIOSurfaceLib::isInit() || !MacIOSurfaceLib::sCGContextGetTypePtr)
|
||||
return CG_CONTEXT_TYPE_UNKNOWN;
|
||||
|
||||
unsigned int type = MacIOSurfaceLib::sCGContextGetTypePtr(ref);
|
||||
if (type == CG_CONTEXT_TYPE_BITMAP) {
|
||||
return CG_CONTEXT_TYPE_BITMAP;
|
||||
} else if (type == CG_CONTEXT_TYPE_IOSURFACE) {
|
||||
return CG_CONTEXT_TYPE_IOSURFACE;
|
||||
} else {
|
||||
return CG_CONTEXT_TYPE_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
222
gfx/2d/MacIOSurface.h
Normal file
222
gfx/2d/MacIOSurface.h
Normal file
|
|
@ -0,0 +1,222 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
// vim:set ts=2 sts=2 sw=2 et cin:
|
||||
/* 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 MacIOSurface_h__
|
||||
#define MacIOSurface_h__
|
||||
#ifdef XP_DARWIN
|
||||
#include <QuartzCore/QuartzCore.h>
|
||||
#include <CoreVideo/CoreVideo.h>
|
||||
#include <dlfcn.h>
|
||||
|
||||
struct _CGLContextObject;
|
||||
|
||||
typedef _CGLContextObject* CGLContextObj;
|
||||
typedef struct CGContext* CGContextRef;
|
||||
typedef struct CGImage* CGImageRef;
|
||||
typedef uint32_t IOSurfaceID;
|
||||
|
||||
#ifdef XP_IOS
|
||||
typedef kern_return_t IOReturn;
|
||||
typedef int CGLError;
|
||||
#endif
|
||||
|
||||
typedef CFTypeRef IOSurfacePtr;
|
||||
typedef IOSurfacePtr (*IOSurfaceCreateFunc) (CFDictionaryRef properties);
|
||||
typedef IOSurfacePtr (*IOSurfaceLookupFunc) (uint32_t io_surface_id);
|
||||
typedef IOSurfaceID (*IOSurfaceGetIDFunc)(IOSurfacePtr io_surface);
|
||||
typedef void (*IOSurfaceVoidFunc)(IOSurfacePtr io_surface);
|
||||
typedef IOReturn (*IOSurfaceLockFunc)(IOSurfacePtr io_surface, uint32_t options,
|
||||
uint32_t *seed);
|
||||
typedef IOReturn (*IOSurfaceUnlockFunc)(IOSurfacePtr io_surface,
|
||||
uint32_t options, uint32_t *seed);
|
||||
typedef void* (*IOSurfaceGetBaseAddressFunc)(IOSurfacePtr io_surface);
|
||||
typedef void* (*IOSurfaceGetBaseAddressOfPlaneFunc)(IOSurfacePtr io_surface,
|
||||
size_t planeIndex);
|
||||
typedef size_t (*IOSurfaceSizeTFunc)(IOSurfacePtr io_surface);
|
||||
typedef size_t (*IOSurfaceSizePlaneTFunc)(IOSurfacePtr io_surface, size_t plane);
|
||||
typedef size_t (*IOSurfaceGetPropertyMaximumFunc) (CFStringRef property);
|
||||
typedef CGLError (*CGLTexImageIOSurface2DFunc) (CGLContextObj ctxt,
|
||||
GLenum target, GLenum internalFormat,
|
||||
GLsizei width, GLsizei height,
|
||||
GLenum format, GLenum type,
|
||||
IOSurfacePtr ioSurface, GLuint plane);
|
||||
typedef CGContextRef (*IOSurfaceContextCreateFunc)(CFTypeRef io_surface,
|
||||
unsigned width, unsigned height,
|
||||
unsigned bitsPerComponent, unsigned bytes,
|
||||
CGColorSpaceRef colorSpace, CGBitmapInfo bitmapInfo);
|
||||
typedef CGImageRef (*IOSurfaceContextCreateImageFunc)(CGContextRef ref);
|
||||
typedef IOSurfacePtr (*IOSurfaceContextGetSurfaceFunc)(CGContextRef ref);
|
||||
|
||||
typedef IOSurfacePtr (*CVPixelBufferGetIOSurfaceFunc)(
|
||||
CVPixelBufferRef pixelBuffer);
|
||||
|
||||
typedef OSType (*IOSurfacePixelFormatFunc)(IOSurfacePtr io_surface);
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
#import <OpenGL/OpenGL.h>
|
||||
#else
|
||||
#import <OpenGLES/ES2/gl.h>
|
||||
#endif
|
||||
|
||||
#include "2D.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "mozilla/RefCounted.h"
|
||||
|
||||
enum CGContextType {
|
||||
CG_CONTEXT_TYPE_UNKNOWN = 0,
|
||||
// These are found by inspection, it's possible they could be changed
|
||||
CG_CONTEXT_TYPE_BITMAP = 4,
|
||||
CG_CONTEXT_TYPE_IOSURFACE = 8
|
||||
};
|
||||
|
||||
CGContextType GetContextType(CGContextRef ref);
|
||||
|
||||
class MacIOSurface final : public mozilla::external::AtomicRefCounted<MacIOSurface> {
|
||||
public:
|
||||
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(MacIOSurface)
|
||||
typedef mozilla::gfx::SourceSurface SourceSurface;
|
||||
|
||||
// The usage count of the IOSurface is increased by 1 during the lifetime
|
||||
// of the MacIOSurface instance.
|
||||
// MacIOSurface holds a reference to the corresponding IOSurface.
|
||||
|
||||
static already_AddRefed<MacIOSurface> CreateIOSurface(int aWidth, int aHeight,
|
||||
double aContentsScaleFactor = 1.0,
|
||||
bool aHasAlpha = true);
|
||||
static void ReleaseIOSurface(MacIOSurface *aIOSurface);
|
||||
static already_AddRefed<MacIOSurface> LookupSurface(IOSurfaceID aSurfaceID,
|
||||
double aContentsScaleFactor = 1.0,
|
||||
bool aHasAlpha = true);
|
||||
|
||||
explicit MacIOSurface(const void *aIOSurfacePtr,
|
||||
double aContentsScaleFactor = 1.0,
|
||||
bool aHasAlpha = true);
|
||||
~MacIOSurface();
|
||||
IOSurfaceID GetIOSurfaceID();
|
||||
void *GetBaseAddress();
|
||||
void *GetBaseAddressOfPlane(size_t planeIndex);
|
||||
size_t GetPlaneCount();
|
||||
OSType GetPixelFormat();
|
||||
// GetWidth() and GetHeight() return values in "display pixels". A
|
||||
// "display pixel" is the smallest fully addressable part of a display.
|
||||
// But in HiDPI modes each "display pixel" corresponds to more than one
|
||||
// device pixel. Use GetDevicePixel**() to get device pixels.
|
||||
size_t GetWidth(size_t plane = 0);
|
||||
size_t GetHeight(size_t plane = 0);
|
||||
double GetContentsScaleFactor() { return mContentsScaleFactor; }
|
||||
size_t GetDevicePixelWidth(size_t plane = 0);
|
||||
size_t GetDevicePixelHeight(size_t plane = 0);
|
||||
size_t GetBytesPerRow(size_t plane = 0);
|
||||
void Lock(bool aReadOnly = true);
|
||||
void Unlock(bool aReadOnly = true);
|
||||
void IncrementUseCount();
|
||||
void DecrementUseCount();
|
||||
bool HasAlpha() { return mHasAlpha; }
|
||||
mozilla::gfx::SurfaceFormat GetFormat();
|
||||
mozilla::gfx::SurfaceFormat GetReadFormat();
|
||||
|
||||
// We would like to forward declare NSOpenGLContext, but it is an @interface
|
||||
// and this file is also used from c++, so we use a void *.
|
||||
CGLError CGLTexImageIOSurface2D(CGLContextObj ctxt, size_t plane = 0);
|
||||
already_AddRefed<SourceSurface> GetAsSurface();
|
||||
CGContextRef CreateIOSurfaceContext();
|
||||
|
||||
// FIXME This doesn't really belong here
|
||||
static CGImageRef CreateImageFromIOSurfaceContext(CGContextRef aContext);
|
||||
static already_AddRefed<MacIOSurface> IOSurfaceContextGetSurface(CGContextRef aContext,
|
||||
double aContentsScaleFactor = 1.0,
|
||||
bool aHasAlpha = true);
|
||||
static size_t GetMaxWidth();
|
||||
static size_t GetMaxHeight();
|
||||
|
||||
private:
|
||||
friend class nsCARenderer;
|
||||
const void* mIOSurfacePtr;
|
||||
double mContentsScaleFactor;
|
||||
bool mHasAlpha;
|
||||
};
|
||||
|
||||
class MacIOSurfaceLib {
|
||||
public:
|
||||
MacIOSurfaceLib() = delete;
|
||||
static void *sIOSurfaceFramework;
|
||||
static void *sOpenGLFramework;
|
||||
static void *sCoreGraphicsFramework;
|
||||
static void *sCoreVideoFramework;
|
||||
static bool isLoaded;
|
||||
static IOSurfaceCreateFunc sCreate;
|
||||
static IOSurfaceGetIDFunc sGetID;
|
||||
static IOSurfaceLookupFunc sLookup;
|
||||
static IOSurfaceGetBaseAddressFunc sGetBaseAddress;
|
||||
static IOSurfaceGetBaseAddressOfPlaneFunc sGetBaseAddressOfPlane;
|
||||
static IOSurfaceSizeTFunc sPlaneCount;
|
||||
static IOSurfaceLockFunc sLock;
|
||||
static IOSurfaceUnlockFunc sUnlock;
|
||||
static IOSurfaceVoidFunc sIncrementUseCount;
|
||||
static IOSurfaceVoidFunc sDecrementUseCount;
|
||||
static IOSurfaceSizePlaneTFunc sWidth;
|
||||
static IOSurfaceSizePlaneTFunc sHeight;
|
||||
static IOSurfaceSizePlaneTFunc sBytesPerRow;
|
||||
static IOSurfaceGetPropertyMaximumFunc sGetPropertyMaximum;
|
||||
static CGLTexImageIOSurface2DFunc sTexImage;
|
||||
static IOSurfaceContextCreateFunc sIOSurfaceContextCreate;
|
||||
static IOSurfaceContextCreateImageFunc sIOSurfaceContextCreateImage;
|
||||
static IOSurfaceContextGetSurfaceFunc sIOSurfaceContextGetSurface;
|
||||
static CVPixelBufferGetIOSurfaceFunc sCVPixelBufferGetIOSurface;
|
||||
static IOSurfacePixelFormatFunc sPixelFormat;
|
||||
static CFStringRef kPropWidth;
|
||||
static CFStringRef kPropHeight;
|
||||
static CFStringRef kPropBytesPerElem;
|
||||
static CFStringRef kPropBytesPerRow;
|
||||
static CFStringRef kPropIsGlobal;
|
||||
|
||||
static bool isInit();
|
||||
static CFStringRef GetIOConst(const char* symbole);
|
||||
static IOSurfacePtr IOSurfaceCreate(CFDictionaryRef properties);
|
||||
static IOSurfacePtr IOSurfaceLookup(IOSurfaceID aIOSurfaceID);
|
||||
static IOSurfaceID IOSurfaceGetID(IOSurfacePtr aIOSurfacePtr);
|
||||
static void* IOSurfaceGetBaseAddress(IOSurfacePtr aIOSurfacePtr);
|
||||
static void* IOSurfaceGetBaseAddressOfPlane(IOSurfacePtr aIOSurfacePtr,
|
||||
size_t aPlaneIndex);
|
||||
static size_t IOSurfaceGetPlaneCount(IOSurfacePtr aIOSurfacePtr);
|
||||
static size_t IOSurfaceGetWidth(IOSurfacePtr aIOSurfacePtr, size_t plane);
|
||||
static size_t IOSurfaceGetHeight(IOSurfacePtr aIOSurfacePtr, size_t plane);
|
||||
static size_t IOSurfaceGetBytesPerRow(IOSurfacePtr aIOSurfacePtr, size_t plane);
|
||||
static size_t IOSurfaceGetPropertyMaximum(CFStringRef property);
|
||||
static IOReturn IOSurfaceLock(IOSurfacePtr aIOSurfacePtr,
|
||||
uint32_t options, uint32_t *seed);
|
||||
static IOReturn IOSurfaceUnlock(IOSurfacePtr aIOSurfacePtr,
|
||||
uint32_t options, uint32_t *seed);
|
||||
static void IOSurfaceIncrementUseCount(IOSurfacePtr aIOSurfacePtr);
|
||||
static void IOSurfaceDecrementUseCount(IOSurfacePtr aIOSurfacePtr);
|
||||
static CGLError CGLTexImageIOSurface2D(CGLContextObj ctxt,
|
||||
GLenum target, GLenum internalFormat,
|
||||
GLsizei width, GLsizei height,
|
||||
GLenum format, GLenum type,
|
||||
IOSurfacePtr ioSurface, GLuint plane);
|
||||
static CGContextRef IOSurfaceContextCreate(IOSurfacePtr aIOSurfacePtr,
|
||||
unsigned aWidth, unsigned aHeight,
|
||||
unsigned aBitsPerCompoent, unsigned aBytes,
|
||||
CGColorSpaceRef aColorSpace, CGBitmapInfo bitmapInfo);
|
||||
static CGImageRef IOSurfaceContextCreateImage(CGContextRef ref);
|
||||
static IOSurfacePtr IOSurfaceContextGetSurface(CGContextRef ref);
|
||||
static IOSurfacePtr CVPixelBufferGetIOSurface(CVPixelBufferRef apixelBuffer);
|
||||
static OSType IOSurfaceGetPixelFormat(IOSurfacePtr aIOSurfacePtr);
|
||||
static unsigned int (*sCGContextGetTypePtr) (CGContextRef);
|
||||
static void LoadLibrary();
|
||||
static void CloseLibrary();
|
||||
|
||||
// Static deconstructor
|
||||
static class LibraryUnloader {
|
||||
public:
|
||||
~LibraryUnloader() {
|
||||
CloseLibrary();
|
||||
}
|
||||
} sLibraryUnloader;
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
134
gfx/2d/Matrix.cpp
Normal file
134
gfx/2d/Matrix.cpp
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "Matrix.h"
|
||||
#include "Quaternion.h"
|
||||
#include "Tools.h"
|
||||
#include <algorithm>
|
||||
#include <ostream>
|
||||
#include <math.h>
|
||||
#include <float.h> // for FLT_EPSILON
|
||||
|
||||
#include "mozilla/FloatingPoint.h" // for UnspecifiedNaN
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
/* Force small values to zero. We do this to avoid having sin(360deg)
|
||||
* evaluate to a tiny but nonzero value.
|
||||
*/
|
||||
double
|
||||
FlushToZero(double aVal)
|
||||
{
|
||||
// XXX Is double precision really necessary here
|
||||
if (-FLT_EPSILON < aVal && aVal < FLT_EPSILON) {
|
||||
return 0.0f;
|
||||
} else {
|
||||
return aVal;
|
||||
}
|
||||
}
|
||||
|
||||
/* Computes tan(aTheta). For values of aTheta such that tan(aTheta) is
|
||||
* undefined or very large, SafeTangent returns a manageably large value
|
||||
* of the correct sign.
|
||||
*/
|
||||
double
|
||||
SafeTangent(double aTheta)
|
||||
{
|
||||
// XXX Is double precision really necessary here
|
||||
const double kEpsilon = 0.0001;
|
||||
|
||||
/* tan(theta) = sin(theta)/cos(theta); problems arise when
|
||||
* cos(theta) is too close to zero. Limit cos(theta) to the
|
||||
* range [-1, -epsilon] U [epsilon, 1].
|
||||
*/
|
||||
|
||||
double sinTheta = sin(aTheta);
|
||||
double cosTheta = cos(aTheta);
|
||||
|
||||
if (cosTheta >= 0 && cosTheta < kEpsilon) {
|
||||
cosTheta = kEpsilon;
|
||||
} else if (cosTheta < 0 && cosTheta >= -kEpsilon) {
|
||||
cosTheta = -kEpsilon;
|
||||
}
|
||||
return FlushToZero(sinTheta / cosTheta);
|
||||
}
|
||||
|
||||
std::ostream&
|
||||
operator<<(std::ostream& aStream, const Matrix& aMatrix)
|
||||
{
|
||||
return aStream << "[ " << aMatrix._11
|
||||
<< " " << aMatrix._12
|
||||
<< "; " << aMatrix._21
|
||||
<< " " << aMatrix._22
|
||||
<< "; " << aMatrix._31
|
||||
<< " " << aMatrix._32
|
||||
<< "; ]";
|
||||
}
|
||||
|
||||
Matrix
|
||||
Matrix::Rotation(Float aAngle)
|
||||
{
|
||||
Matrix newMatrix;
|
||||
|
||||
Float s = sinf(aAngle);
|
||||
Float c = cosf(aAngle);
|
||||
|
||||
newMatrix._11 = c;
|
||||
newMatrix._12 = s;
|
||||
newMatrix._21 = -s;
|
||||
newMatrix._22 = c;
|
||||
|
||||
return newMatrix;
|
||||
}
|
||||
|
||||
Rect
|
||||
Matrix::TransformBounds(const Rect &aRect) const
|
||||
{
|
||||
int i;
|
||||
Point quad[4];
|
||||
Float min_x, max_x;
|
||||
Float min_y, max_y;
|
||||
|
||||
quad[0] = TransformPoint(aRect.TopLeft());
|
||||
quad[1] = TransformPoint(aRect.TopRight());
|
||||
quad[2] = TransformPoint(aRect.BottomLeft());
|
||||
quad[3] = TransformPoint(aRect.BottomRight());
|
||||
|
||||
min_x = max_x = quad[0].x;
|
||||
min_y = max_y = quad[0].y;
|
||||
|
||||
for (i = 1; i < 4; i++) {
|
||||
if (quad[i].x < min_x)
|
||||
min_x = quad[i].x;
|
||||
if (quad[i].x > max_x)
|
||||
max_x = quad[i].x;
|
||||
|
||||
if (quad[i].y < min_y)
|
||||
min_y = quad[i].y;
|
||||
if (quad[i].y > max_y)
|
||||
max_y = quad[i].y;
|
||||
}
|
||||
|
||||
return Rect(min_x, min_y, max_x - min_x, max_y - min_y);
|
||||
}
|
||||
|
||||
Matrix&
|
||||
Matrix::NudgeToIntegers()
|
||||
{
|
||||
NudgeToInteger(&_11);
|
||||
NudgeToInteger(&_12);
|
||||
NudgeToInteger(&_21);
|
||||
NudgeToInteger(&_22);
|
||||
NudgeToInteger(&_31);
|
||||
NudgeToInteger(&_32);
|
||||
return *this;
|
||||
}
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
||||
1676
gfx/2d/Matrix.h
Normal file
1676
gfx/2d/Matrix.h
Normal file
File diff suppressed because it is too large
Load diff
26
gfx/2d/MatrixFwd.h
Normal file
26
gfx/2d/MatrixFwd.h
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
/* -*- Mode: C++; tab-width: 20; 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_GFX_MATRIX_FWD_H_
|
||||
#define MOZILLA_GFX_MATRIX_FWD_H_
|
||||
|
||||
|
||||
// Forward declare enough things to define the typedef |Matrix4x4|.
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
struct UnknownUnits;
|
||||
|
||||
template<class SourceUnits, class TargetUnits>
|
||||
class Matrix4x4Typed;
|
||||
|
||||
typedef Matrix4x4Typed<UnknownUnits, UnknownUnits> Matrix4x4;
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
288
gfx/2d/NativeFontResourceDWrite.cpp
Normal file
288
gfx/2d/NativeFontResourceDWrite.cpp
Normal file
|
|
@ -0,0 +1,288 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "NativeFontResourceDWrite.h"
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
#include "DrawTargetD2D1.h"
|
||||
#include "Logging.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
static Atomic<uint64_t> sNextFontFileKey;
|
||||
static std::unordered_map<uint64_t, IDWriteFontFileStream*> sFontFileStreams;
|
||||
|
||||
class DWriteFontFileLoader : public IDWriteFontFileLoader
|
||||
{
|
||||
public:
|
||||
DWriteFontFileLoader()
|
||||
{
|
||||
}
|
||||
|
||||
// IUnknown interface
|
||||
IFACEMETHOD(QueryInterface)(IID const& iid, OUT void** ppObject)
|
||||
{
|
||||
if (iid == __uuidof(IDWriteFontFileLoader)) {
|
||||
*ppObject = static_cast<IDWriteFontFileLoader*>(this);
|
||||
return S_OK;
|
||||
} else if (iid == __uuidof(IUnknown)) {
|
||||
*ppObject = static_cast<IUnknown*>(this);
|
||||
return S_OK;
|
||||
} else {
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
}
|
||||
|
||||
IFACEMETHOD_(ULONG, AddRef)()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
IFACEMETHOD_(ULONG, Release)()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
// IDWriteFontFileLoader methods
|
||||
/**
|
||||
* Important! Note the key here has to be a uint64_t that will have been
|
||||
* generated by incrementing sNextFontFileKey.
|
||||
*/
|
||||
virtual HRESULT STDMETHODCALLTYPE
|
||||
CreateStreamFromKey(void const* fontFileReferenceKey,
|
||||
UINT32 fontFileReferenceKeySize,
|
||||
OUT IDWriteFontFileStream** fontFileStream);
|
||||
|
||||
/**
|
||||
* Gets the singleton loader instance. Note that when using this font
|
||||
* loader, the key must be a uint64_t that has been generated by incrementing
|
||||
* sNextFontFileKey.
|
||||
* Also note that this is _not_ threadsafe.
|
||||
*/
|
||||
static IDWriteFontFileLoader* Instance()
|
||||
{
|
||||
if (!mInstance) {
|
||||
mInstance = new DWriteFontFileLoader();
|
||||
DrawTargetD2D1::GetDWriteFactory()->
|
||||
RegisterFontFileLoader(mInstance);
|
||||
}
|
||||
return mInstance;
|
||||
}
|
||||
|
||||
private:
|
||||
static IDWriteFontFileLoader* mInstance;
|
||||
};
|
||||
|
||||
class DWriteFontFileStream : public IDWriteFontFileStream
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Used by the FontFileLoader to create a new font stream,
|
||||
* this font stream is created from data in memory. The memory
|
||||
* passed may be released after object creation, it will be
|
||||
* copied internally.
|
||||
*
|
||||
* @param aData Font data
|
||||
*/
|
||||
DWriteFontFileStream(uint8_t *aData, uint32_t aSize, uint64_t aFontFileKey);
|
||||
~DWriteFontFileStream();
|
||||
|
||||
// IUnknown interface
|
||||
IFACEMETHOD(QueryInterface)(IID const& iid, OUT void** ppObject)
|
||||
{
|
||||
if (iid == __uuidof(IDWriteFontFileStream)) {
|
||||
*ppObject = static_cast<IDWriteFontFileStream*>(this);
|
||||
return S_OK;
|
||||
} else if (iid == __uuidof(IUnknown)) {
|
||||
*ppObject = static_cast<IUnknown*>(this);
|
||||
return S_OK;
|
||||
} else {
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
}
|
||||
|
||||
IFACEMETHOD_(ULONG, AddRef)()
|
||||
{
|
||||
++mRefCnt;
|
||||
return mRefCnt;
|
||||
}
|
||||
|
||||
IFACEMETHOD_(ULONG, Release)()
|
||||
{
|
||||
--mRefCnt;
|
||||
if (mRefCnt == 0) {
|
||||
delete this;
|
||||
return 0;
|
||||
}
|
||||
return mRefCnt;
|
||||
}
|
||||
|
||||
// IDWriteFontFileStream methods
|
||||
virtual HRESULT STDMETHODCALLTYPE ReadFileFragment(void const** fragmentStart,
|
||||
UINT64 fileOffset,
|
||||
UINT64 fragmentSize,
|
||||
OUT void** fragmentContext);
|
||||
|
||||
virtual void STDMETHODCALLTYPE ReleaseFileFragment(void* fragmentContext);
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE GetFileSize(OUT UINT64* fileSize);
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE GetLastWriteTime(OUT UINT64* lastWriteTime);
|
||||
|
||||
private:
|
||||
std::vector<uint8_t> mData;
|
||||
uint32_t mRefCnt;
|
||||
uint64_t mFontFileKey;
|
||||
};
|
||||
|
||||
IDWriteFontFileLoader* DWriteFontFileLoader::mInstance = nullptr;
|
||||
|
||||
HRESULT STDMETHODCALLTYPE
|
||||
DWriteFontFileLoader::CreateStreamFromKey(const void *fontFileReferenceKey,
|
||||
UINT32 fontFileReferenceKeySize,
|
||||
IDWriteFontFileStream **fontFileStream)
|
||||
{
|
||||
if (!fontFileReferenceKey || !fontFileStream) {
|
||||
return E_POINTER;
|
||||
}
|
||||
|
||||
uint64_t fontFileKey = *static_cast<const uint64_t*>(fontFileReferenceKey);
|
||||
auto found = sFontFileStreams.find(fontFileKey);
|
||||
if (found == sFontFileStreams.end()) {
|
||||
*fontFileStream = nullptr;
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
found->second->AddRef();
|
||||
*fontFileStream = found->second;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
DWriteFontFileStream::DWriteFontFileStream(uint8_t *aData, uint32_t aSize,
|
||||
uint64_t aFontFileKey)
|
||||
: mRefCnt(0)
|
||||
, mFontFileKey(aFontFileKey)
|
||||
{
|
||||
mData.resize(aSize);
|
||||
memcpy(&mData.front(), aData, aSize);
|
||||
}
|
||||
|
||||
DWriteFontFileStream::~DWriteFontFileStream()
|
||||
{
|
||||
sFontFileStreams.erase(mFontFileKey);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE
|
||||
DWriteFontFileStream::GetFileSize(UINT64 *fileSize)
|
||||
{
|
||||
*fileSize = mData.size();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE
|
||||
DWriteFontFileStream::GetLastWriteTime(UINT64 *lastWriteTime)
|
||||
{
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE
|
||||
DWriteFontFileStream::ReadFileFragment(const void **fragmentStart,
|
||||
UINT64 fileOffset,
|
||||
UINT64 fragmentSize,
|
||||
void **fragmentContext)
|
||||
{
|
||||
// We are required to do bounds checking.
|
||||
if (fileOffset + fragmentSize > mData.size()) {
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
// truncate the 64 bit fileOffset to size_t sized index into mData
|
||||
size_t index = static_cast<size_t>(fileOffset);
|
||||
|
||||
// We should be alive for the duration of this.
|
||||
*fragmentStart = &mData[index];
|
||||
*fragmentContext = nullptr;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
void STDMETHODCALLTYPE
|
||||
DWriteFontFileStream::ReleaseFileFragment(void *fragmentContext)
|
||||
{
|
||||
}
|
||||
|
||||
/* static */
|
||||
already_AddRefed<NativeFontResourceDWrite>
|
||||
NativeFontResourceDWrite::Create(uint8_t *aFontData, uint32_t aDataLength,
|
||||
bool aNeedsCairo)
|
||||
{
|
||||
IDWriteFactory *factory = DrawTargetD2D1::GetDWriteFactory();
|
||||
if (!factory) {
|
||||
gfxWarning() << "Failed to get DWrite Factory.";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint64_t fontFileKey = sNextFontFileKey++;
|
||||
RefPtr<IDWriteFontFileStream> ffsRef =
|
||||
new DWriteFontFileStream(aFontData, aDataLength, fontFileKey);
|
||||
sFontFileStreams[fontFileKey] = ffsRef;
|
||||
|
||||
RefPtr<IDWriteFontFile> fontFile;
|
||||
HRESULT hr =
|
||||
factory->CreateCustomFontFileReference(&fontFileKey, sizeof(fontFileKey),
|
||||
DWriteFontFileLoader::Instance(),
|
||||
getter_AddRefs(fontFile));
|
||||
if (FAILED(hr)) {
|
||||
gfxWarning() << "Failed to load font file from data!";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BOOL isSupported;
|
||||
DWRITE_FONT_FILE_TYPE fileType;
|
||||
DWRITE_FONT_FACE_TYPE faceType;
|
||||
UINT32 numberOfFaces;
|
||||
hr = fontFile->Analyze(&isSupported, &fileType, &faceType, &numberOfFaces);
|
||||
if (FAILED(hr) || !isSupported) {
|
||||
gfxWarning() << "Font file is not supported.";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<NativeFontResourceDWrite> fontResource =
|
||||
new NativeFontResourceDWrite(factory, fontFile.forget(), faceType,
|
||||
numberOfFaces, aNeedsCairo);
|
||||
return fontResource.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<ScaledFont>
|
||||
NativeFontResourceDWrite::CreateScaledFont(uint32_t aIndex, Float aGlyphSize,
|
||||
const uint8_t* aInstanceData, uint32_t aInstanceDataLength)
|
||||
{
|
||||
if (aIndex >= mNumberOfFaces) {
|
||||
gfxWarning() << "Font face index is too high for font resource.";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
IDWriteFontFile *fontFile = mFontFile;
|
||||
RefPtr<IDWriteFontFace> fontFace;
|
||||
if (FAILED(mFactory->CreateFontFace(mFaceType, 1, &fontFile, aIndex,
|
||||
DWRITE_FONT_SIMULATIONS_NONE, getter_AddRefs(fontFace)))) {
|
||||
gfxWarning() << "Failed to create font face from font file data.";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<ScaledFontBase> scaledFont = new ScaledFontDWrite(fontFace, aGlyphSize);
|
||||
if (mNeedsCairo && !scaledFont->PopulateCairoScaledFont()) {
|
||||
gfxWarning() << "Unable to create cairo scaled font DWrite font.";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return scaledFont.forget();
|
||||
}
|
||||
|
||||
} // gfx
|
||||
} // mozilla
|
||||
57
gfx/2d/NativeFontResourceDWrite.h
Normal file
57
gfx/2d/NativeFontResourceDWrite.h
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* 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_gfx_NativeFontResourceDWrite_h
|
||||
#define mozilla_gfx_NativeFontResourceDWrite_h
|
||||
|
||||
#include <dwrite.h>
|
||||
|
||||
#include "2D.h"
|
||||
#include "mozilla/AlreadyAddRefed.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
class NativeFontResourceDWrite final : public NativeFontResource
|
||||
{
|
||||
public:
|
||||
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(NativeFontResourceDWrite)
|
||||
/**
|
||||
* Creates a NativeFontResourceDWrite if data is valid. Note aFontData will be
|
||||
* copied if required and so can be released after calling.
|
||||
*
|
||||
* @param aFontData the SFNT data.
|
||||
* @param aDataLength length of data.
|
||||
* @param aNeedsCairo whether the ScaledFont created needs a cairo scaled font
|
||||
* @return Referenced NativeFontResourceDWrite or nullptr if invalid.
|
||||
*/
|
||||
static already_AddRefed<NativeFontResourceDWrite>
|
||||
Create(uint8_t *aFontData, uint32_t aDataLength, bool aNeedsCairo);
|
||||
|
||||
already_AddRefed<ScaledFont>
|
||||
CreateScaledFont(uint32_t aIndex, Float aGlyphSize,
|
||||
const uint8_t* aInstanceData, uint32_t aInstanceDataLength) final;
|
||||
|
||||
private:
|
||||
NativeFontResourceDWrite(IDWriteFactory *aFactory,
|
||||
already_AddRefed<IDWriteFontFile> aFontFile,
|
||||
DWRITE_FONT_FACE_TYPE aFaceType,
|
||||
uint32_t aNumberOfFaces, bool aNeedsCairo)
|
||||
: mFactory(aFactory), mFontFile(aFontFile), mFaceType(aFaceType)
|
||||
, mNumberOfFaces(aNumberOfFaces), mNeedsCairo(aNeedsCairo)
|
||||
{}
|
||||
|
||||
IDWriteFactory *mFactory;
|
||||
RefPtr<IDWriteFontFile> mFontFile;
|
||||
DWRITE_FONT_FACE_TYPE mFaceType;
|
||||
uint32_t mNumberOfFaces;
|
||||
bool mNeedsCairo;
|
||||
};
|
||||
|
||||
} // gfx
|
||||
} // mozilla
|
||||
|
||||
#endif // mozilla_gfx_NativeFontResourceDWrite_h
|
||||
63
gfx/2d/NativeFontResourceGDI.cpp
Normal file
63
gfx/2d/NativeFontResourceGDI.cpp
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "NativeFontResourceGDI.h"
|
||||
|
||||
#include "Logging.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "ScaledFontWin.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
/* static */
|
||||
already_AddRefed<NativeFontResourceGDI>
|
||||
NativeFontResourceGDI::Create(uint8_t *aFontData, uint32_t aDataLength,
|
||||
bool aNeedsCairo)
|
||||
{
|
||||
DWORD numberOfFontsAdded;
|
||||
HANDLE fontResourceHandle = ::AddFontMemResourceEx(aFontData, aDataLength,
|
||||
0, &numberOfFontsAdded);
|
||||
if (!fontResourceHandle) {
|
||||
gfxWarning() << "Failed to add memory font resource.";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<NativeFontResourceGDI> fontResouce =
|
||||
new NativeFontResourceGDI(fontResourceHandle, aNeedsCairo);
|
||||
|
||||
return fontResouce.forget();
|
||||
}
|
||||
|
||||
NativeFontResourceGDI::~NativeFontResourceGDI()
|
||||
{
|
||||
::RemoveFontMemResourceEx(mFontResourceHandle);
|
||||
}
|
||||
|
||||
already_AddRefed<ScaledFont>
|
||||
NativeFontResourceGDI::CreateScaledFont(uint32_t aIndex, Float aGlyphSize,
|
||||
const uint8_t* aInstanceData, uint32_t aInstanceDataLength)
|
||||
{
|
||||
if (aInstanceDataLength < sizeof(LOGFONT)) {
|
||||
gfxWarning() << "GDI scaled font instance data is truncated.";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const LOGFONT* logFont = reinterpret_cast<const LOGFONT*>(aInstanceData);
|
||||
|
||||
// Constructor for ScaledFontWin dereferences and copies the LOGFONT, so we
|
||||
// are safe to pass this reference.
|
||||
RefPtr<ScaledFontBase> scaledFont = new ScaledFontWin(logFont, aGlyphSize);
|
||||
if (mNeedsCairo && !scaledFont->PopulateCairoScaledFont()) {
|
||||
gfxWarning() << "Unable to create cairo scaled font GDI font.";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return scaledFont.forget();
|
||||
}
|
||||
|
||||
} // gfx
|
||||
} // mozilla
|
||||
55
gfx/2d/NativeFontResourceGDI.h
Normal file
55
gfx/2d/NativeFontResourceGDI.h
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* 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_gfx_NativeFontResourceGDI_h
|
||||
#define mozilla_gfx_NativeFontResourceGDI_h
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "2D.h"
|
||||
#include "mozilla/AlreadyAddRefed.h"
|
||||
#include "mozilla/Vector.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
class NativeFontResourceGDI final : public NativeFontResource
|
||||
{
|
||||
public:
|
||||
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(NativeFontResourceGDI)
|
||||
/**
|
||||
* Creates a NativeFontResourceGDI if data is valid. Note aFontData will be
|
||||
* copied if required and so can be released after calling.
|
||||
*
|
||||
* @param aFontData the SFNT data.
|
||||
* @param aDataLength length of data.
|
||||
* @param aNeedsCairo whether the ScaledFont created need a cairo scaled font
|
||||
* @return Referenced NativeFontResourceGDI or nullptr if invalid.
|
||||
*/
|
||||
static already_AddRefed<NativeFontResourceGDI>
|
||||
Create(uint8_t *aFontData, uint32_t aDataLength, bool aNeedsCairo);
|
||||
|
||||
~NativeFontResourceGDI();
|
||||
|
||||
already_AddRefed<ScaledFont>
|
||||
CreateScaledFont(uint32_t aIndex, Float aGlyphSize,
|
||||
const uint8_t* aInstanceData, uint32_t aInstanceDataLength) final;
|
||||
|
||||
private:
|
||||
NativeFontResourceGDI(HANDLE aFontResourceHandle,
|
||||
bool aNeedsCairo)
|
||||
: mFontResourceHandle(aFontResourceHandle)
|
||||
, mNeedsCairo(aNeedsCairo)
|
||||
{}
|
||||
|
||||
HANDLE mFontResourceHandle;
|
||||
bool mNeedsCairo;
|
||||
};
|
||||
|
||||
} // gfx
|
||||
} // mozilla
|
||||
|
||||
#endif // mozilla_gfx_NativeFontResourceGDI_h
|
||||
67
gfx/2d/NativeFontResourceMac.cpp
Normal file
67
gfx/2d/NativeFontResourceMac.cpp
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "NativeFontResourceMac.h"
|
||||
#include "Types.h"
|
||||
|
||||
#include "mozilla/RefPtr.h"
|
||||
|
||||
#ifdef MOZ_WIDGET_UIKIT
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
/* static */
|
||||
already_AddRefed<NativeFontResourceMac>
|
||||
NativeFontResourceMac::Create(uint8_t *aFontData, uint32_t aDataLength)
|
||||
{
|
||||
// copy font data
|
||||
CFDataRef data = CFDataCreate(kCFAllocatorDefault, aFontData, aDataLength);
|
||||
if (!data) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// create a provider
|
||||
CGDataProviderRef provider = CGDataProviderCreateWithCFData(data);
|
||||
|
||||
// release our reference to the CFData, provider keeps it alive
|
||||
CFRelease(data);
|
||||
|
||||
// create the font object
|
||||
CGFontRef fontRef = CGFontCreateWithDataProvider(provider);
|
||||
|
||||
// release our reference, font will keep it alive as long as needed
|
||||
CGDataProviderRelease(provider);
|
||||
|
||||
if (!fontRef) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// passes ownership of fontRef to the NativeFontResourceMac instance
|
||||
RefPtr<NativeFontResourceMac> fontResource =
|
||||
new NativeFontResourceMac(fontRef);
|
||||
|
||||
return fontResource.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<ScaledFont>
|
||||
NativeFontResourceMac::CreateScaledFont(uint32_t aIndex, Float aGlyphSize,
|
||||
const uint8_t* aInstanceData, uint32_t aInstanceDataLength)
|
||||
{
|
||||
RefPtr<ScaledFontBase> scaledFont = new ScaledFontMac(mFontRef, aGlyphSize);
|
||||
|
||||
if (!scaledFont->PopulateCairoScaledFont()) {
|
||||
gfxWarning() << "Unable to create cairo scaled Mac font.";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return scaledFont.forget();
|
||||
}
|
||||
|
||||
} // gfx
|
||||
} // mozilla
|
||||
43
gfx/2d/NativeFontResourceMac.h
Normal file
43
gfx/2d/NativeFontResourceMac.h
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* 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_gfx_NativeFontResourceMac_h
|
||||
#define mozilla_gfx_NativeFontResourceMac_h
|
||||
|
||||
#include "2D.h"
|
||||
#include "mozilla/AlreadyAddRefed.h"
|
||||
#include "ScaledFontMac.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
class NativeFontResourceMac final : public NativeFontResource
|
||||
{
|
||||
public:
|
||||
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(NativeFontResourceMac)
|
||||
|
||||
static already_AddRefed<NativeFontResourceMac>
|
||||
Create(uint8_t *aFontData, uint32_t aDataLength);
|
||||
|
||||
already_AddRefed<ScaledFont>
|
||||
CreateScaledFont(uint32_t aIndex, Float aGlyphSize,
|
||||
const uint8_t* aInstanceData, uint32_t aInstanceDataLength) final;
|
||||
|
||||
~NativeFontResourceMac()
|
||||
{
|
||||
CFRelease(mFontRef);
|
||||
}
|
||||
|
||||
private:
|
||||
explicit NativeFontResourceMac(CGFontRef aFontRef) : mFontRef(aFontRef) {}
|
||||
|
||||
CGFontRef mFontRef;
|
||||
};
|
||||
|
||||
} // gfx
|
||||
} // mozilla
|
||||
|
||||
#endif // mozilla_gfx_NativeFontResourceMac_h
|
||||
43
gfx/2d/NumericTools.h
Normal file
43
gfx/2d/NumericTools.h
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
/* -*- Mode: C++; tab-width: 20; 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_GFX_NUMERICTOOLS_H_
|
||||
#define MOZILLA_GFX_NUMERICTOOLS_H_
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
// XXX - Move these into mfbt/MathAlgorithms.h?
|
||||
|
||||
// Returns the largest multiple of aMultiplied that's <= x.
|
||||
// Same as int32_t(floor(double(x) / aMultiplier)) * aMultiplier,
|
||||
// but faster.
|
||||
inline int32_t
|
||||
RoundDownToMultiple(int32_t x, int32_t aMultiplier)
|
||||
{
|
||||
// We don't use float division + floor because that's hard for the compiler
|
||||
// to optimize.
|
||||
int mod = x % aMultiplier;
|
||||
if (x > 0) {
|
||||
return x - mod;
|
||||
}
|
||||
return mod ? x - aMultiplier - mod : x;
|
||||
}
|
||||
|
||||
// Returns the smallest multiple of aMultiplied that's >= x.
|
||||
// Same as int32_t(ceil(double(x) / aMultiplier)) * aMultiplier,
|
||||
// but faster.
|
||||
inline int32_t
|
||||
RoundUpToMultiple(int32_t x, int32_t aMultiplier)
|
||||
{
|
||||
int mod = x % aMultiplier;
|
||||
if (x > 0) {
|
||||
return mod ? x + aMultiplier - mod : x;
|
||||
}
|
||||
return x - mod;
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* MOZILLA_GFX_NUMERICTOOLS_H_ */
|
||||
550
gfx/2d/Path.cpp
Normal file
550
gfx/2d/Path.cpp
Normal file
|
|
@ -0,0 +1,550 @@
|
|||
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "2D.h"
|
||||
#include "PathAnalysis.h"
|
||||
#include "PathHelpers.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
static double CubicRoot(double aValue) {
|
||||
if (aValue < 0.0) {
|
||||
return -CubicRoot(-aValue);
|
||||
}
|
||||
else {
|
||||
return pow(aValue, 1.0 / 3.0);
|
||||
}
|
||||
}
|
||||
|
||||
struct PointD : public BasePoint<double, PointD> {
|
||||
typedef BasePoint<double, PointD> Super;
|
||||
|
||||
PointD() : Super() {}
|
||||
PointD(double aX, double aY) : Super(aX, aY) {}
|
||||
MOZ_IMPLICIT PointD(const Point& aPoint) : Super(aPoint.x, aPoint.y) {}
|
||||
|
||||
Point ToPoint() const {
|
||||
return Point(static_cast<Float>(x), static_cast<Float>(y));
|
||||
}
|
||||
};
|
||||
|
||||
struct BezierControlPoints
|
||||
{
|
||||
BezierControlPoints() {}
|
||||
BezierControlPoints(const PointD &aCP1, const PointD &aCP2,
|
||||
const PointD &aCP3, const PointD &aCP4)
|
||||
: mCP1(aCP1), mCP2(aCP2), mCP3(aCP3), mCP4(aCP4)
|
||||
{
|
||||
}
|
||||
|
||||
PointD mCP1, mCP2, mCP3, mCP4;
|
||||
};
|
||||
|
||||
void
|
||||
FlattenBezier(const BezierControlPoints &aPoints,
|
||||
PathSink *aSink, double aTolerance);
|
||||
|
||||
|
||||
Path::Path()
|
||||
{
|
||||
}
|
||||
|
||||
Path::~Path()
|
||||
{
|
||||
}
|
||||
|
||||
Float
|
||||
Path::ComputeLength()
|
||||
{
|
||||
EnsureFlattenedPath();
|
||||
return mFlattenedPath->ComputeLength();
|
||||
}
|
||||
|
||||
Point
|
||||
Path::ComputePointAtLength(Float aLength, Point* aTangent)
|
||||
{
|
||||
EnsureFlattenedPath();
|
||||
return mFlattenedPath->ComputePointAtLength(aLength, aTangent);
|
||||
}
|
||||
|
||||
void
|
||||
Path::EnsureFlattenedPath()
|
||||
{
|
||||
if (!mFlattenedPath) {
|
||||
mFlattenedPath = new FlattenedPath();
|
||||
StreamToSink(mFlattenedPath);
|
||||
}
|
||||
}
|
||||
|
||||
// This is the maximum deviation we allow (with an additional ~20% margin of
|
||||
// error) of the approximation from the actual Bezier curve.
|
||||
const Float kFlatteningTolerance = 0.0001f;
|
||||
|
||||
void
|
||||
FlattenedPath::MoveTo(const Point &aPoint)
|
||||
{
|
||||
MOZ_ASSERT(!mCalculatedLength);
|
||||
FlatPathOp op;
|
||||
op.mType = FlatPathOp::OP_MOVETO;
|
||||
op.mPoint = aPoint;
|
||||
mPathOps.push_back(op);
|
||||
|
||||
mLastMove = aPoint;
|
||||
}
|
||||
|
||||
void
|
||||
FlattenedPath::LineTo(const Point &aPoint)
|
||||
{
|
||||
MOZ_ASSERT(!mCalculatedLength);
|
||||
FlatPathOp op;
|
||||
op.mType = FlatPathOp::OP_LINETO;
|
||||
op.mPoint = aPoint;
|
||||
mPathOps.push_back(op);
|
||||
}
|
||||
|
||||
void
|
||||
FlattenedPath::BezierTo(const Point &aCP1,
|
||||
const Point &aCP2,
|
||||
const Point &aCP3)
|
||||
{
|
||||
MOZ_ASSERT(!mCalculatedLength);
|
||||
FlattenBezier(BezierControlPoints(CurrentPoint(), aCP1, aCP2, aCP3), this, kFlatteningTolerance);
|
||||
}
|
||||
|
||||
void
|
||||
FlattenedPath::QuadraticBezierTo(const Point &aCP1,
|
||||
const Point &aCP2)
|
||||
{
|
||||
MOZ_ASSERT(!mCalculatedLength);
|
||||
// We need to elevate the degree of this quadratic B<>zier to cubic, so we're
|
||||
// going to add an intermediate control point, and recompute control point 1.
|
||||
// The first and last control points remain the same.
|
||||
// This formula can be found on http://fontforge.sourceforge.net/bezier.html
|
||||
Point CP0 = CurrentPoint();
|
||||
Point CP1 = (CP0 + aCP1 * 2.0) / 3.0;
|
||||
Point CP2 = (aCP2 + aCP1 * 2.0) / 3.0;
|
||||
Point CP3 = aCP2;
|
||||
|
||||
BezierTo(CP1, CP2, CP3);
|
||||
}
|
||||
|
||||
void
|
||||
FlattenedPath::Close()
|
||||
{
|
||||
MOZ_ASSERT(!mCalculatedLength);
|
||||
LineTo(mLastMove);
|
||||
}
|
||||
|
||||
void
|
||||
FlattenedPath::Arc(const Point &aOrigin, float aRadius, float aStartAngle,
|
||||
float aEndAngle, bool aAntiClockwise)
|
||||
{
|
||||
ArcToBezier(this, aOrigin, Size(aRadius, aRadius), aStartAngle, aEndAngle, aAntiClockwise);
|
||||
}
|
||||
|
||||
Float
|
||||
FlattenedPath::ComputeLength()
|
||||
{
|
||||
if (!mCalculatedLength) {
|
||||
Point currentPoint;
|
||||
|
||||
for (uint32_t i = 0; i < mPathOps.size(); i++) {
|
||||
if (mPathOps[i].mType == FlatPathOp::OP_MOVETO) {
|
||||
currentPoint = mPathOps[i].mPoint;
|
||||
} else {
|
||||
mCachedLength += Distance(currentPoint, mPathOps[i].mPoint);
|
||||
currentPoint = mPathOps[i].mPoint;
|
||||
}
|
||||
}
|
||||
|
||||
mCalculatedLength = true;
|
||||
}
|
||||
|
||||
return mCachedLength;
|
||||
}
|
||||
|
||||
Point
|
||||
FlattenedPath::ComputePointAtLength(Float aLength, Point *aTangent)
|
||||
{
|
||||
// We track the last point that -wasn't- in the same place as the current
|
||||
// point so if we pass the edge of the path with a bunch of zero length
|
||||
// paths we still get the correct tangent vector.
|
||||
Point lastPointSinceMove;
|
||||
Point currentPoint;
|
||||
for (uint32_t i = 0; i < mPathOps.size(); i++) {
|
||||
if (mPathOps[i].mType == FlatPathOp::OP_MOVETO) {
|
||||
if (Distance(currentPoint, mPathOps[i].mPoint)) {
|
||||
lastPointSinceMove = currentPoint;
|
||||
}
|
||||
currentPoint = mPathOps[i].mPoint;
|
||||
} else {
|
||||
Float segmentLength = Distance(currentPoint, mPathOps[i].mPoint);
|
||||
|
||||
if (segmentLength) {
|
||||
lastPointSinceMove = currentPoint;
|
||||
if (segmentLength > aLength) {
|
||||
Point currentVector = mPathOps[i].mPoint - currentPoint;
|
||||
Point tangent = currentVector / segmentLength;
|
||||
if (aTangent) {
|
||||
*aTangent = tangent;
|
||||
}
|
||||
return currentPoint + tangent * aLength;
|
||||
}
|
||||
}
|
||||
|
||||
aLength -= segmentLength;
|
||||
currentPoint = mPathOps[i].mPoint;
|
||||
}
|
||||
}
|
||||
|
||||
Point currentVector = currentPoint - lastPointSinceMove;
|
||||
if (aTangent) {
|
||||
if (hypotf(currentVector.x, currentVector.y)) {
|
||||
*aTangent = currentVector / hypotf(currentVector.x, currentVector.y);
|
||||
} else {
|
||||
*aTangent = Point();
|
||||
}
|
||||
}
|
||||
return currentPoint;
|
||||
}
|
||||
|
||||
// This function explicitly permits aControlPoints to refer to the same object
|
||||
// as either of the other arguments.
|
||||
static void
|
||||
SplitBezier(const BezierControlPoints &aControlPoints,
|
||||
BezierControlPoints *aFirstSegmentControlPoints,
|
||||
BezierControlPoints *aSecondSegmentControlPoints,
|
||||
double t)
|
||||
{
|
||||
MOZ_ASSERT(aSecondSegmentControlPoints);
|
||||
|
||||
*aSecondSegmentControlPoints = aControlPoints;
|
||||
|
||||
PointD cp1a = aControlPoints.mCP1 + (aControlPoints.mCP2 - aControlPoints.mCP1) * t;
|
||||
PointD cp2a = aControlPoints.mCP2 + (aControlPoints.mCP3 - aControlPoints.mCP2) * t;
|
||||
PointD cp1aa = cp1a + (cp2a - cp1a) * t;
|
||||
PointD cp3a = aControlPoints.mCP3 + (aControlPoints.mCP4 - aControlPoints.mCP3) * t;
|
||||
PointD cp2aa = cp2a + (cp3a - cp2a) * t;
|
||||
PointD cp1aaa = cp1aa + (cp2aa - cp1aa) * t;
|
||||
aSecondSegmentControlPoints->mCP4 = aControlPoints.mCP4;
|
||||
|
||||
if(aFirstSegmentControlPoints) {
|
||||
aFirstSegmentControlPoints->mCP1 = aControlPoints.mCP1;
|
||||
aFirstSegmentControlPoints->mCP2 = cp1a;
|
||||
aFirstSegmentControlPoints->mCP3 = cp1aa;
|
||||
aFirstSegmentControlPoints->mCP4 = cp1aaa;
|
||||
}
|
||||
aSecondSegmentControlPoints->mCP1 = cp1aaa;
|
||||
aSecondSegmentControlPoints->mCP2 = cp2aa;
|
||||
aSecondSegmentControlPoints->mCP3 = cp3a;
|
||||
}
|
||||
|
||||
static void
|
||||
FlattenBezierCurveSegment(const BezierControlPoints &aControlPoints,
|
||||
PathSink *aSink,
|
||||
double aTolerance)
|
||||
{
|
||||
/* The algorithm implemented here is based on:
|
||||
* http://cis.usouthal.edu/~hain/general/Publications/Bezier/Bezier%20Offset%20Curves.pdf
|
||||
*
|
||||
* The basic premise is that for a small t the third order term in the
|
||||
* equation of a cubic bezier curve is insignificantly small. This can
|
||||
* then be approximated by a quadratic equation for which the maximum
|
||||
* difference from a linear approximation can be much more easily determined.
|
||||
*/
|
||||
BezierControlPoints currentCP = aControlPoints;
|
||||
|
||||
double t = 0;
|
||||
while (t < 1.0) {
|
||||
PointD cp21 = currentCP.mCP2 - currentCP.mCP1;
|
||||
PointD cp31 = currentCP.mCP3 - currentCP.mCP1;
|
||||
|
||||
/* To remove divisions and check for divide-by-zero, this is optimized from:
|
||||
* Float s3 = (cp31.x * cp21.y - cp31.y * cp21.x) / hypotf(cp21.x, cp21.y);
|
||||
* t = 2 * Float(sqrt(aTolerance / (3. * std::abs(s3))));
|
||||
*/
|
||||
double cp21x31 = cp31.x * cp21.y - cp31.y * cp21.x;
|
||||
double h = hypot(cp21.x, cp21.y);
|
||||
if (cp21x31 * h == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
double s3inv = h / cp21x31;
|
||||
t = 2 * sqrt(aTolerance * std::abs(s3inv) / 3.);
|
||||
if (t >= 1.0) {
|
||||
break;
|
||||
}
|
||||
|
||||
SplitBezier(currentCP, nullptr, ¤tCP, t);
|
||||
|
||||
aSink->LineTo(currentCP.mCP1.ToPoint());
|
||||
}
|
||||
|
||||
aSink->LineTo(currentCP.mCP4.ToPoint());
|
||||
}
|
||||
|
||||
static inline void
|
||||
FindInflectionApproximationRange(BezierControlPoints aControlPoints,
|
||||
double *aMin, double *aMax, double aT,
|
||||
double aTolerance)
|
||||
{
|
||||
SplitBezier(aControlPoints, nullptr, &aControlPoints, aT);
|
||||
|
||||
PointD cp21 = aControlPoints.mCP2 - aControlPoints.mCP1;
|
||||
PointD cp41 = aControlPoints.mCP4 - aControlPoints.mCP1;
|
||||
|
||||
if (cp21.x == 0. && cp21.y == 0.) {
|
||||
// In this case s3 becomes lim[n->0] (cp41.x * n) / n - (cp41.y * n) / n = cp41.x - cp41.y.
|
||||
|
||||
// Use the absolute value so that Min and Max will correspond with the
|
||||
// minimum and maximum of the range.
|
||||
*aMin = aT - CubicRoot(std::abs(aTolerance / (cp41.x - cp41.y)));
|
||||
*aMax = aT + CubicRoot(std::abs(aTolerance / (cp41.x - cp41.y)));
|
||||
return;
|
||||
}
|
||||
|
||||
double s3 = (cp41.x * cp21.y - cp41.y * cp21.x) / hypot(cp21.x, cp21.y);
|
||||
|
||||
if (s3 == 0) {
|
||||
// This means within the precision we have it can be approximated
|
||||
// infinitely by a linear segment. Deal with this by specifying the
|
||||
// approximation range as extending beyond the entire curve.
|
||||
*aMin = -1.0;
|
||||
*aMax = 2.0;
|
||||
return;
|
||||
}
|
||||
|
||||
double tf = CubicRoot(std::abs(aTolerance / s3));
|
||||
|
||||
*aMin = aT - tf * (1 - aT);
|
||||
*aMax = aT + tf * (1 - aT);
|
||||
}
|
||||
|
||||
/* Find the inflection points of a bezier curve. Will return false if the
|
||||
* curve is degenerate in such a way that it is best approximated by a straight
|
||||
* line.
|
||||
*
|
||||
* The below algorithm was written by Jeff Muizelaar <jmuizelaar@mozilla.com>, explanation follows:
|
||||
*
|
||||
* The lower inflection point is returned in aT1, the higher one in aT2. In the
|
||||
* case of a single inflection point this will be in aT1.
|
||||
*
|
||||
* The method is inspired by the algorithm in "analysis of in?ection points for planar cubic bezier curve"
|
||||
*
|
||||
* Here are some differences between this algorithm and versions discussed elsewhere in the literature:
|
||||
*
|
||||
* zhang et. al compute a0, d0 and e0 incrementally using the follow formula:
|
||||
*
|
||||
* Point a0 = CP2 - CP1
|
||||
* Point a1 = CP3 - CP2
|
||||
* Point a2 = CP4 - CP1
|
||||
*
|
||||
* Point d0 = a1 - a0
|
||||
* Point d1 = a2 - a1
|
||||
|
||||
* Point e0 = d1 - d0
|
||||
*
|
||||
* this avoids any multiplications and may or may not be faster than the approach take below.
|
||||
*
|
||||
* "fast, precise flattening of cubic bezier path and ofset curves" by hain et. al
|
||||
* Point a = CP1 + 3 * CP2 - 3 * CP3 + CP4
|
||||
* Point b = 3 * CP1 - 6 * CP2 + 3 * CP3
|
||||
* Point c = -3 * CP1 + 3 * CP2
|
||||
* Point d = CP1
|
||||
* the a, b, c, d can be expressed in terms of a0, d0 and e0 defined above as:
|
||||
* c = 3 * a0
|
||||
* b = 3 * d0
|
||||
* a = e0
|
||||
*
|
||||
*
|
||||
* a = 3a = a.y * b.x - a.x * b.y
|
||||
* b = 3b = a.y * c.x - a.x * c.y
|
||||
* c = 9c = b.y * c.x - b.x * c.y
|
||||
*
|
||||
* The additional multiples of 3 cancel each other out as show below:
|
||||
*
|
||||
* x = (-b + sqrt(b * b - 4 * a * c)) / (2 * a)
|
||||
* x = (-3 * b + sqrt(3 * b * 3 * b - 4 * a * 3 * 9 * c / 3)) / (2 * 3 * a)
|
||||
* x = 3 * (-b + sqrt(b * b - 4 * a * c)) / (2 * 3 * a)
|
||||
* x = (-b + sqrt(b * b - 4 * a * c)) / (2 * a)
|
||||
*
|
||||
* I haven't looked into whether the formulation of the quadratic formula in
|
||||
* hain has any numerical advantages over the one used below.
|
||||
*/
|
||||
static inline void
|
||||
FindInflectionPoints(const BezierControlPoints &aControlPoints,
|
||||
double *aT1, double *aT2, uint32_t *aCount)
|
||||
{
|
||||
// Find inflection points.
|
||||
// See www.faculty.idc.ac.il/arik/quality/appendixa.html for an explanation
|
||||
// of this approach.
|
||||
PointD A = aControlPoints.mCP2 - aControlPoints.mCP1;
|
||||
PointD B = aControlPoints.mCP3 - (aControlPoints.mCP2 * 2) + aControlPoints.mCP1;
|
||||
PointD C = aControlPoints.mCP4 - (aControlPoints.mCP3 * 3) + (aControlPoints.mCP2 * 3) - aControlPoints.mCP1;
|
||||
|
||||
double a = B.x * C.y - B.y * C.x;
|
||||
double b = A.x * C.y - A.y * C.x;
|
||||
double c = A.x * B.y - A.y * B.x;
|
||||
|
||||
if (a == 0) {
|
||||
// Not a quadratic equation.
|
||||
if (b == 0) {
|
||||
// Instead of a linear acceleration change we have a constant
|
||||
// acceleration change. This means the equation has no solution
|
||||
// and there are no inflection points, unless the constant is 0.
|
||||
// In that case the curve is a straight line, essentially that means
|
||||
// the easiest way to deal with is is by saying there's an inflection
|
||||
// point at t == 0. The inflection point approximation range found will
|
||||
// automatically extend into infinity.
|
||||
if (c == 0) {
|
||||
*aCount = 1;
|
||||
*aT1 = 0;
|
||||
return;
|
||||
}
|
||||
*aCount = 0;
|
||||
return;
|
||||
}
|
||||
*aT1 = -c / b;
|
||||
*aCount = 1;
|
||||
return;
|
||||
} else {
|
||||
double discriminant = b * b - 4 * a * c;
|
||||
|
||||
if (discriminant < 0) {
|
||||
// No inflection points.
|
||||
*aCount = 0;
|
||||
} else if (discriminant == 0) {
|
||||
*aCount = 1;
|
||||
*aT1 = -b / (2 * a);
|
||||
} else {
|
||||
/* Use the following formula for computing the roots:
|
||||
*
|
||||
* q = -1/2 * (b + sign(b) * sqrt(b^2 - 4ac))
|
||||
* t1 = q / a
|
||||
* t2 = c / q
|
||||
*/
|
||||
double q = sqrt(discriminant);
|
||||
if (b < 0) {
|
||||
q = b - q;
|
||||
} else {
|
||||
q = b + q;
|
||||
}
|
||||
q *= -1./2;
|
||||
|
||||
*aT1 = q / a;
|
||||
*aT2 = c / q;
|
||||
if (*aT1 > *aT2) {
|
||||
std::swap(*aT1, *aT2);
|
||||
}
|
||||
*aCount = 2;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
FlattenBezier(const BezierControlPoints &aControlPoints,
|
||||
PathSink *aSink, double aTolerance)
|
||||
{
|
||||
double t1;
|
||||
double t2;
|
||||
uint32_t count;
|
||||
|
||||
FindInflectionPoints(aControlPoints, &t1, &t2, &count);
|
||||
|
||||
// Check that at least one of the inflection points is inside [0..1]
|
||||
if (count == 0 || ((t1 < 0.0 || t1 >= 1.0) && (count == 1 || (t2 < 0.0 || t2 >= 1.0))) ) {
|
||||
FlattenBezierCurveSegment(aControlPoints, aSink, aTolerance);
|
||||
return;
|
||||
}
|
||||
|
||||
double t1min = t1, t1max = t1, t2min = t2, t2max = t2;
|
||||
|
||||
BezierControlPoints remainingCP = aControlPoints;
|
||||
|
||||
// For both inflection points, calulate the range where they can be linearly
|
||||
// approximated if they are positioned within [0,1]
|
||||
if (count > 0 && t1 >= 0 && t1 < 1.0) {
|
||||
FindInflectionApproximationRange(aControlPoints, &t1min, &t1max, t1, aTolerance);
|
||||
}
|
||||
if (count > 1 && t2 >= 0 && t2 < 1.0) {
|
||||
FindInflectionApproximationRange(aControlPoints, &t2min, &t2max, t2, aTolerance);
|
||||
}
|
||||
BezierControlPoints nextCPs = aControlPoints;
|
||||
BezierControlPoints prevCPs;
|
||||
|
||||
// Process ranges. [t1min, t1max] and [t2min, t2max] are approximated by line
|
||||
// segments.
|
||||
if (count == 1 && t1min <= 0 && t1max >= 1.0) {
|
||||
// The whole range can be approximated by a line segment.
|
||||
aSink->LineTo(aControlPoints.mCP4.ToPoint());
|
||||
return;
|
||||
}
|
||||
|
||||
if (t1min > 0) {
|
||||
// Flatten the Bezier up until the first inflection point's approximation
|
||||
// point.
|
||||
SplitBezier(aControlPoints, &prevCPs,
|
||||
&remainingCP, t1min);
|
||||
FlattenBezierCurveSegment(prevCPs, aSink, aTolerance);
|
||||
}
|
||||
if (t1max >= 0 && t1max < 1.0 && (count == 1 || t2min > t1max)) {
|
||||
// The second inflection point's approximation range begins after the end
|
||||
// of the first, approximate the first inflection point by a line and
|
||||
// subsequently flatten up until the end or the next inflection point.
|
||||
SplitBezier(aControlPoints, nullptr, &nextCPs, t1max);
|
||||
|
||||
aSink->LineTo(nextCPs.mCP1.ToPoint());
|
||||
|
||||
if (count == 1 || (count > 1 && t2min >= 1.0)) {
|
||||
// No more inflection points to deal with, flatten the rest of the curve.
|
||||
FlattenBezierCurveSegment(nextCPs, aSink, aTolerance);
|
||||
}
|
||||
} else if (count > 1 && t2min > 1.0) {
|
||||
// We've already concluded t2min <= t1max, so if this is true the
|
||||
// approximation range for the first inflection point runs past the
|
||||
// end of the curve, draw a line to the end and we're done.
|
||||
aSink->LineTo(aControlPoints.mCP4.ToPoint());
|
||||
return;
|
||||
}
|
||||
|
||||
if (count > 1 && t2min < 1.0 && t2max > 0) {
|
||||
if (t2min > 0 && t2min < t1max) {
|
||||
// In this case the t2 approximation range starts inside the t1
|
||||
// approximation range.
|
||||
SplitBezier(aControlPoints, nullptr, &nextCPs, t1max);
|
||||
aSink->LineTo(nextCPs.mCP1.ToPoint());
|
||||
} else if (t2min > 0 && t1max > 0) {
|
||||
SplitBezier(aControlPoints, nullptr, &nextCPs, t1max);
|
||||
|
||||
// Find a control points describing the portion of the curve between t1max and t2min.
|
||||
double t2mina = (t2min - t1max) / (1 - t1max);
|
||||
SplitBezier(nextCPs, &prevCPs, &nextCPs, t2mina);
|
||||
FlattenBezierCurveSegment(prevCPs, aSink, aTolerance);
|
||||
} else if (t2min > 0) {
|
||||
// We have nothing interesting before t2min, find that bit and flatten it.
|
||||
SplitBezier(aControlPoints, &prevCPs, &nextCPs, t2min);
|
||||
FlattenBezierCurveSegment(prevCPs, aSink, aTolerance);
|
||||
}
|
||||
if (t2max < 1.0) {
|
||||
// Flatten the portion of the curve after t2max
|
||||
SplitBezier(aControlPoints, nullptr, &nextCPs, t2max);
|
||||
|
||||
// Draw a line to the start, this is the approximation between t2min and
|
||||
// t2max.
|
||||
aSink->LineTo(nextCPs.mCP1.ToPoint());
|
||||
FlattenBezierCurveSegment(nextCPs, aSink, aTolerance);
|
||||
} else {
|
||||
// Our approximation range extends beyond the end of the curve.
|
||||
aSink->LineTo(aControlPoints.mCP4.ToPoint());
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
||||
57
gfx/2d/PathAnalysis.h
Normal file
57
gfx/2d/PathAnalysis.h
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "2D.h"
|
||||
#include <vector>
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
struct FlatPathOp
|
||||
{
|
||||
enum OpType {
|
||||
OP_MOVETO,
|
||||
OP_LINETO,
|
||||
};
|
||||
|
||||
OpType mType;
|
||||
Point mPoint;
|
||||
};
|
||||
|
||||
class FlattenedPath : public PathSink
|
||||
{
|
||||
public:
|
||||
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FlattenedPath)
|
||||
FlattenedPath() : mCachedLength(0)
|
||||
, mCalculatedLength(false)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void MoveTo(const Point &aPoint);
|
||||
virtual void LineTo(const Point &aPoint);
|
||||
virtual void BezierTo(const Point &aCP1,
|
||||
const Point &aCP2,
|
||||
const Point &aCP3);
|
||||
virtual void QuadraticBezierTo(const Point &aCP1,
|
||||
const Point &aCP2);
|
||||
virtual void Close();
|
||||
virtual void Arc(const Point &aOrigin, float aRadius, float aStartAngle,
|
||||
float aEndAngle, bool aAntiClockwise = false);
|
||||
|
||||
virtual Point CurrentPoint() const { return mPathOps.empty() ? Point() : mPathOps[mPathOps.size() - 1].mPoint; }
|
||||
|
||||
Float ComputeLength();
|
||||
Point ComputePointAtLength(Float aLength, Point *aTangent);
|
||||
|
||||
private:
|
||||
Float mCachedLength;
|
||||
bool mCalculatedLength;
|
||||
Point mLastMove;
|
||||
|
||||
std::vector<FlatPathOp> mPathOps;
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
||||
435
gfx/2d/PathCG.cpp
Normal file
435
gfx/2d/PathCG.cpp
Normal file
|
|
@ -0,0 +1,435 @@
|
|||
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "PathCG.h"
|
||||
#include <math.h>
|
||||
#include "Logging.h"
|
||||
#include "PathHelpers.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
static inline Rect
|
||||
CGRectToRect(CGRect rect)
|
||||
{
|
||||
return Rect(rect.origin.x,
|
||||
rect.origin.y,
|
||||
rect.size.width,
|
||||
rect.size.height);
|
||||
}
|
||||
|
||||
static inline Point
|
||||
CGPointToPoint(CGPoint point)
|
||||
{
|
||||
return Point(point.x, point.y);
|
||||
}
|
||||
|
||||
static inline void
|
||||
SetStrokeOptions(CGContextRef cg, const StrokeOptions &aStrokeOptions)
|
||||
{
|
||||
switch (aStrokeOptions.mLineCap)
|
||||
{
|
||||
case CapStyle::BUTT:
|
||||
CGContextSetLineCap(cg, kCGLineCapButt);
|
||||
break;
|
||||
case CapStyle::ROUND:
|
||||
CGContextSetLineCap(cg, kCGLineCapRound);
|
||||
break;
|
||||
case CapStyle::SQUARE:
|
||||
CGContextSetLineCap(cg, kCGLineCapSquare);
|
||||
break;
|
||||
}
|
||||
|
||||
switch (aStrokeOptions.mLineJoin)
|
||||
{
|
||||
case JoinStyle::BEVEL:
|
||||
CGContextSetLineJoin(cg, kCGLineJoinBevel);
|
||||
break;
|
||||
case JoinStyle::ROUND:
|
||||
CGContextSetLineJoin(cg, kCGLineJoinRound);
|
||||
break;
|
||||
case JoinStyle::MITER:
|
||||
case JoinStyle::MITER_OR_BEVEL:
|
||||
CGContextSetLineJoin(cg, kCGLineJoinMiter);
|
||||
break;
|
||||
}
|
||||
|
||||
CGContextSetLineWidth(cg, aStrokeOptions.mLineWidth);
|
||||
CGContextSetMiterLimit(cg, aStrokeOptions.mMiterLimit);
|
||||
|
||||
// XXX: rename mDashLength to dashLength
|
||||
if (aStrokeOptions.mDashLength > 0) {
|
||||
// we use a regular array instead of a std::vector here because we don't want to leak the <vector> include
|
||||
CGFloat *dashes = new CGFloat[aStrokeOptions.mDashLength];
|
||||
for (size_t i=0; i<aStrokeOptions.mDashLength; i++) {
|
||||
dashes[i] = aStrokeOptions.mDashPattern[i];
|
||||
}
|
||||
CGContextSetLineDash(cg, aStrokeOptions.mDashOffset, dashes, aStrokeOptions.mDashLength);
|
||||
delete[] dashes;
|
||||
}
|
||||
}
|
||||
|
||||
static inline CGAffineTransform
|
||||
GfxMatrixToCGAffineTransform(const Matrix &m)
|
||||
{
|
||||
CGAffineTransform t;
|
||||
t.a = m._11;
|
||||
t.b = m._12;
|
||||
t.c = m._21;
|
||||
t.d = m._22;
|
||||
t.tx = m._31;
|
||||
t.ty = m._32;
|
||||
return t;
|
||||
}
|
||||
|
||||
PathBuilderCG::~PathBuilderCG()
|
||||
{
|
||||
CGPathRelease(mCGPath);
|
||||
}
|
||||
|
||||
void
|
||||
PathBuilderCG::MoveTo(const Point &aPoint)
|
||||
{
|
||||
if (!aPoint.IsFinite()) {
|
||||
return;
|
||||
}
|
||||
CGPathMoveToPoint(mCGPath, nullptr, aPoint.x, aPoint.y);
|
||||
}
|
||||
|
||||
void
|
||||
PathBuilderCG::LineTo(const Point &aPoint)
|
||||
{
|
||||
if (!aPoint.IsFinite()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (CGPathIsEmpty(mCGPath))
|
||||
MoveTo(aPoint);
|
||||
else
|
||||
CGPathAddLineToPoint(mCGPath, nullptr, aPoint.x, aPoint.y);
|
||||
}
|
||||
|
||||
void
|
||||
PathBuilderCG::BezierTo(const Point &aCP1,
|
||||
const Point &aCP2,
|
||||
const Point &aCP3)
|
||||
{
|
||||
if (!aCP1.IsFinite() || !aCP2.IsFinite() || !aCP3.IsFinite()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (CGPathIsEmpty(mCGPath))
|
||||
MoveTo(aCP1);
|
||||
CGPathAddCurveToPoint(mCGPath, nullptr,
|
||||
aCP1.x, aCP1.y,
|
||||
aCP2.x, aCP2.y,
|
||||
aCP3.x, aCP3.y);
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
PathBuilderCG::QuadraticBezierTo(const Point &aCP1,
|
||||
const Point &aCP2)
|
||||
{
|
||||
if (!aCP1.IsFinite() || !aCP2.IsFinite()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (CGPathIsEmpty(mCGPath))
|
||||
MoveTo(aCP1);
|
||||
CGPathAddQuadCurveToPoint(mCGPath, nullptr,
|
||||
aCP1.x, aCP1.y,
|
||||
aCP2.x, aCP2.y);
|
||||
}
|
||||
|
||||
void
|
||||
PathBuilderCG::Close()
|
||||
{
|
||||
if (!CGPathIsEmpty(mCGPath))
|
||||
CGPathCloseSubpath(mCGPath);
|
||||
}
|
||||
|
||||
void
|
||||
PathBuilderCG::Arc(const Point &aOrigin, Float aRadius, Float aStartAngle,
|
||||
Float aEndAngle, bool aAntiClockwise)
|
||||
{
|
||||
if (!aOrigin.IsFinite() || !IsFinite(aRadius) ||
|
||||
!IsFinite(aStartAngle) || !IsFinite(aEndAngle)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Disabled for now due to a CG bug when using CGPathAddArc with stroke
|
||||
// dashing and rotation transforms that are multiples of 90 degrees. See:
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=949661#c8
|
||||
#if 0
|
||||
// Core Graphic's initial coordinate system is y-axis up, whereas Moz2D's is
|
||||
// y-axis down. Core Graphics therefore considers "clockwise" to mean "sweep
|
||||
// in the direction of decreasing angle" whereas Moz2D considers it to mean
|
||||
// "sweep in the direction of increasing angle". In other words if this
|
||||
// Moz2D method is instructed to sweep anti-clockwise we need to tell
|
||||
// CGPathAddArc to sweep clockwise, and vice versa. Hence why we pass the
|
||||
// value of aAntiClockwise directly to CGPathAddArc's "clockwise" bool
|
||||
// parameter.
|
||||
CGPathAddArc(mCGPath, nullptr,
|
||||
aOrigin.x, aOrigin.y,
|
||||
aRadius,
|
||||
aStartAngle,
|
||||
aEndAngle,
|
||||
aAntiClockwise);
|
||||
#endif
|
||||
ArcToBezier(this, aOrigin, Size(aRadius, aRadius), aStartAngle, aEndAngle,
|
||||
aAntiClockwise);
|
||||
}
|
||||
|
||||
Point
|
||||
PathBuilderCG::CurrentPoint() const
|
||||
{
|
||||
Point ret;
|
||||
if (!CGPathIsEmpty(mCGPath)) {
|
||||
CGPoint pt = CGPathGetCurrentPoint(mCGPath);
|
||||
ret.MoveTo(pt.x, pt.y);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
PathBuilderCG::EnsureActive(const Point &aPoint)
|
||||
{
|
||||
}
|
||||
|
||||
already_AddRefed<Path>
|
||||
PathBuilderCG::Finish()
|
||||
{
|
||||
return MakeAndAddRef<PathCG>(mCGPath, mFillRule);
|
||||
}
|
||||
|
||||
already_AddRefed<PathBuilder>
|
||||
PathCG::CopyToBuilder(FillRule aFillRule) const
|
||||
{
|
||||
CGMutablePathRef path = CGPathCreateMutableCopy(mPath);
|
||||
return MakeAndAddRef<PathBuilderCG>(path, aFillRule);
|
||||
}
|
||||
|
||||
|
||||
|
||||
already_AddRefed<PathBuilder>
|
||||
PathCG::TransformedCopyToBuilder(const Matrix &aTransform, FillRule aFillRule) const
|
||||
{
|
||||
// 10.7 adds CGPathCreateMutableCopyByTransformingPath it might be faster than doing
|
||||
// this by hand
|
||||
|
||||
struct TransformApplier {
|
||||
CGMutablePathRef path;
|
||||
CGAffineTransform transform;
|
||||
static void
|
||||
TranformCGPathApplierFunc(void *vinfo, const CGPathElement *element)
|
||||
{
|
||||
TransformApplier *info = reinterpret_cast<TransformApplier*>(vinfo);
|
||||
switch (element->type) {
|
||||
case kCGPathElementMoveToPoint:
|
||||
{
|
||||
CGPoint pt = element->points[0];
|
||||
CGPathMoveToPoint(info->path, &info->transform, pt.x, pt.y);
|
||||
break;
|
||||
}
|
||||
case kCGPathElementAddLineToPoint:
|
||||
{
|
||||
CGPoint pt = element->points[0];
|
||||
CGPathAddLineToPoint(info->path, &info->transform, pt.x, pt.y);
|
||||
break;
|
||||
}
|
||||
case kCGPathElementAddQuadCurveToPoint:
|
||||
{
|
||||
CGPoint cpt = element->points[0];
|
||||
CGPoint pt = element->points[1];
|
||||
CGPathAddQuadCurveToPoint(info->path, &info->transform, cpt.x, cpt.y, pt.x, pt.y);
|
||||
break;
|
||||
}
|
||||
case kCGPathElementAddCurveToPoint:
|
||||
{
|
||||
CGPoint cpt1 = element->points[0];
|
||||
CGPoint cpt2 = element->points[1];
|
||||
CGPoint pt = element->points[2];
|
||||
CGPathAddCurveToPoint(info->path, &info->transform, cpt1.x, cpt1.y, cpt2.x, cpt2.y, pt.x, pt.y);
|
||||
break;
|
||||
}
|
||||
case kCGPathElementCloseSubpath:
|
||||
{
|
||||
CGPathCloseSubpath(info->path);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TransformApplier ta;
|
||||
ta.path = CGPathCreateMutable();
|
||||
ta.transform = GfxMatrixToCGAffineTransform(aTransform);
|
||||
|
||||
CGPathApply(mPath, &ta, TransformApplier::TranformCGPathApplierFunc);
|
||||
return MakeAndAddRef<PathBuilderCG>(ta.path, aFillRule);
|
||||
}
|
||||
|
||||
static void
|
||||
StreamPathToSinkApplierFunc(void *vinfo, const CGPathElement *element)
|
||||
{
|
||||
PathSink *sink = reinterpret_cast<PathSink*>(vinfo);
|
||||
switch (element->type) {
|
||||
case kCGPathElementMoveToPoint:
|
||||
{
|
||||
CGPoint pt = element->points[0];
|
||||
sink->MoveTo(CGPointToPoint(pt));
|
||||
break;
|
||||
}
|
||||
case kCGPathElementAddLineToPoint:
|
||||
{
|
||||
CGPoint pt = element->points[0];
|
||||
sink->LineTo(CGPointToPoint(pt));
|
||||
break;
|
||||
}
|
||||
case kCGPathElementAddQuadCurveToPoint:
|
||||
{
|
||||
CGPoint cpt = element->points[0];
|
||||
CGPoint pt = element->points[1];
|
||||
sink->QuadraticBezierTo(CGPointToPoint(cpt),
|
||||
CGPointToPoint(pt));
|
||||
break;
|
||||
}
|
||||
case kCGPathElementAddCurveToPoint:
|
||||
{
|
||||
CGPoint cpt1 = element->points[0];
|
||||
CGPoint cpt2 = element->points[1];
|
||||
CGPoint pt = element->points[2];
|
||||
sink->BezierTo(CGPointToPoint(cpt1),
|
||||
CGPointToPoint(cpt2),
|
||||
CGPointToPoint(pt));
|
||||
break;
|
||||
}
|
||||
case kCGPathElementCloseSubpath:
|
||||
{
|
||||
sink->Close();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PathCG::StreamToSink(PathSink *aSink) const
|
||||
{
|
||||
CGPathApply(mPath, aSink, StreamPathToSinkApplierFunc);
|
||||
}
|
||||
|
||||
bool
|
||||
PathCG::ContainsPoint(const Point &aPoint, const Matrix &aTransform) const
|
||||
{
|
||||
Matrix inverse = aTransform;
|
||||
inverse.Invert();
|
||||
Point transformedPoint = inverse.TransformPoint(aPoint);
|
||||
// We could probably drop the input transform and just transform the point at the caller?
|
||||
CGPoint point = {transformedPoint.x, transformedPoint.y};
|
||||
|
||||
// The transform parameter of CGPathContainsPoint doesn't seem to work properly on OS X 10.5
|
||||
// so we transform aPoint ourselves.
|
||||
return CGPathContainsPoint(mPath, nullptr, point, mFillRule == FillRule::FILL_EVEN_ODD);
|
||||
}
|
||||
|
||||
static size_t
|
||||
PutBytesNull(void *info, const void *buffer, size_t count)
|
||||
{
|
||||
return count;
|
||||
}
|
||||
|
||||
/* The idea of a scratch context comes from WebKit */
|
||||
static CGContextRef
|
||||
CreateScratchContext()
|
||||
{
|
||||
CGDataConsumerCallbacks callbacks = {PutBytesNull, nullptr};
|
||||
CGDataConsumerRef consumer = CGDataConsumerCreate(nullptr, &callbacks);
|
||||
CGContextRef cg = CGPDFContextCreate(consumer, nullptr, nullptr);
|
||||
CGDataConsumerRelease(consumer);
|
||||
return cg;
|
||||
}
|
||||
|
||||
static CGContextRef
|
||||
ScratchContext()
|
||||
{
|
||||
static CGContextRef cg = CreateScratchContext();
|
||||
return cg;
|
||||
}
|
||||
|
||||
bool
|
||||
PathCG::StrokeContainsPoint(const StrokeOptions &aStrokeOptions,
|
||||
const Point &aPoint,
|
||||
const Matrix &aTransform) const
|
||||
{
|
||||
Matrix inverse = aTransform;
|
||||
inverse.Invert();
|
||||
Point transformedPoint = inverse.TransformPoint(aPoint);
|
||||
// We could probably drop the input transform and just transform the point at the caller?
|
||||
CGPoint point = {transformedPoint.x, transformedPoint.y};
|
||||
|
||||
CGContextRef cg = ScratchContext();
|
||||
|
||||
CGContextSaveGState(cg);
|
||||
|
||||
CGContextBeginPath(cg);
|
||||
CGContextAddPath(cg, mPath);
|
||||
|
||||
SetStrokeOptions(cg, aStrokeOptions);
|
||||
|
||||
CGContextReplacePathWithStrokedPath(cg);
|
||||
CGContextRestoreGState(cg);
|
||||
|
||||
CGPathRef sPath = CGContextCopyPath(cg);
|
||||
bool inStroke = CGPathContainsPoint(sPath, nullptr, point, false);
|
||||
CGPathRelease(sPath);
|
||||
|
||||
return inStroke;
|
||||
}
|
||||
|
||||
//XXX: what should these functions return for an empty path?
|
||||
// currently they return CGRectNull {inf,inf, 0, 0}
|
||||
Rect
|
||||
PathCG::GetBounds(const Matrix &aTransform) const
|
||||
{
|
||||
//XXX: are these bounds tight enough
|
||||
Rect bounds = CGRectToRect(CGPathGetBoundingBox(mPath));
|
||||
|
||||
//XXX: currently this returns the bounds of the transformed bounds
|
||||
// this is strictly looser than the bounds of the transformed path
|
||||
return aTransform.TransformBounds(bounds);
|
||||
}
|
||||
|
||||
Rect
|
||||
PathCG::GetStrokedBounds(const StrokeOptions &aStrokeOptions,
|
||||
const Matrix &aTransform) const
|
||||
{
|
||||
// 10.7 has CGPathCreateCopyByStrokingPath which we could use
|
||||
// instead of this scratch context business
|
||||
CGContextRef cg = ScratchContext();
|
||||
|
||||
CGContextSaveGState(cg);
|
||||
|
||||
CGContextBeginPath(cg);
|
||||
CGContextAddPath(cg, mPath);
|
||||
|
||||
SetStrokeOptions(cg, aStrokeOptions);
|
||||
|
||||
CGContextReplacePathWithStrokedPath(cg);
|
||||
Rect bounds = CGRectToRect(CGContextGetPathBoundingBox(cg));
|
||||
|
||||
CGContextRestoreGState(cg);
|
||||
|
||||
if (!bounds.IsFinite()) {
|
||||
return Rect();
|
||||
}
|
||||
|
||||
return aTransform.TransformBounds(bounds);
|
||||
}
|
||||
|
||||
|
||||
} // namespace gfx
|
||||
|
||||
} // namespace mozilla
|
||||
114
gfx/2d/PathCG.h
Normal file
114
gfx/2d/PathCG.h
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
/* -*- Mode: C++; tab-width: 20; 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_GFX_PATHCG_H_
|
||||
#define MOZILLA_GFX_PATHCG_H_
|
||||
|
||||
#ifdef MOZ_WIDGET_COCOA
|
||||
#include <ApplicationServices/ApplicationServices.h>
|
||||
#else
|
||||
#include <CoreGraphics/CoreGraphics.h>
|
||||
#endif
|
||||
|
||||
#include "2D.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
class PathCG;
|
||||
|
||||
class PathBuilderCG : public PathBuilder
|
||||
{
|
||||
public:
|
||||
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(PathBuilderCG)
|
||||
// absorbs a reference of aPath
|
||||
PathBuilderCG(CGMutablePathRef aPath, FillRule aFillRule)
|
||||
: mFillRule(aFillRule)
|
||||
{
|
||||
mCGPath = aPath;
|
||||
}
|
||||
|
||||
explicit PathBuilderCG(FillRule aFillRule)
|
||||
: mFillRule(aFillRule)
|
||||
{
|
||||
mCGPath = CGPathCreateMutable();
|
||||
}
|
||||
|
||||
virtual ~PathBuilderCG();
|
||||
|
||||
virtual void MoveTo(const Point &aPoint);
|
||||
virtual void LineTo(const Point &aPoint);
|
||||
virtual void BezierTo(const Point &aCP1,
|
||||
const Point &aCP2,
|
||||
const Point &aCP3);
|
||||
virtual void QuadraticBezierTo(const Point &aCP1,
|
||||
const Point &aCP2);
|
||||
virtual void Close();
|
||||
virtual void Arc(const Point &aOrigin, Float aRadius, Float aStartAngle,
|
||||
Float aEndAngle, bool aAntiClockwise = false);
|
||||
virtual Point CurrentPoint() const;
|
||||
|
||||
virtual already_AddRefed<Path> Finish();
|
||||
|
||||
virtual BackendType GetBackendType() const { return BackendType::SKIA; }
|
||||
|
||||
private:
|
||||
friend class PathCG;
|
||||
friend class ScaledFontMac;
|
||||
|
||||
void EnsureActive(const Point &aPoint);
|
||||
|
||||
CGMutablePathRef mCGPath;
|
||||
Point mCurrentPoint;
|
||||
Point mBeginPoint;
|
||||
FillRule mFillRule;
|
||||
};
|
||||
|
||||
class PathCG : public Path
|
||||
{
|
||||
public:
|
||||
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(PathCG)
|
||||
PathCG(CGMutablePathRef aPath, FillRule aFillRule)
|
||||
: mPath(aPath)
|
||||
, mFillRule(aFillRule)
|
||||
{
|
||||
CGPathRetain(mPath);
|
||||
}
|
||||
virtual ~PathCG() { CGPathRelease(mPath); }
|
||||
|
||||
// Paths will always return BackendType::COREGRAPHICS, but note that they
|
||||
// are compatible with BackendType::COREGRAPHICS_ACCELERATED backend.
|
||||
virtual BackendType GetBackendType() const { return BackendType::SKIA; }
|
||||
|
||||
virtual already_AddRefed<PathBuilder> CopyToBuilder(FillRule aFillRule) const;
|
||||
virtual already_AddRefed<PathBuilder> TransformedCopyToBuilder(const Matrix &aTransform,
|
||||
FillRule aFillRule) const;
|
||||
|
||||
virtual bool ContainsPoint(const Point &aPoint, const Matrix &aTransform) const;
|
||||
virtual bool StrokeContainsPoint(const StrokeOptions &aStrokeOptions,
|
||||
const Point &aPoint,
|
||||
const Matrix &aTransform) const;
|
||||
virtual Rect GetBounds(const Matrix &aTransform = Matrix()) const;
|
||||
virtual Rect GetStrokedBounds(const StrokeOptions &aStrokeOptions,
|
||||
const Matrix &aTransform = Matrix()) const;
|
||||
|
||||
virtual void StreamToSink(PathSink *aSink) const;
|
||||
|
||||
virtual FillRule GetFillRule() const { return mFillRule; }
|
||||
|
||||
CGMutablePathRef GetPath() const { return mPath; }
|
||||
|
||||
private:
|
||||
friend class DrawTargetCG;
|
||||
|
||||
CGMutablePathRef mPath;
|
||||
Point mEndPoint;
|
||||
FillRule mFillRule;
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
331
gfx/2d/PathCairo.cpp
Normal file
331
gfx/2d/PathCairo.cpp
Normal file
|
|
@ -0,0 +1,331 @@
|
|||
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "PathCairo.h"
|
||||
#include <math.h>
|
||||
#include "DrawTargetCairo.h"
|
||||
#include "Logging.h"
|
||||
#include "PathHelpers.h"
|
||||
#include "HelpersCairo.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
PathBuilderCairo::PathBuilderCairo(FillRule aFillRule)
|
||||
: mFillRule(aFillRule)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
PathBuilderCairo::MoveTo(const Point &aPoint)
|
||||
{
|
||||
cairo_path_data_t data;
|
||||
data.header.type = CAIRO_PATH_MOVE_TO;
|
||||
data.header.length = 2;
|
||||
mPathData.push_back(data);
|
||||
data.point.x = aPoint.x;
|
||||
data.point.y = aPoint.y;
|
||||
mPathData.push_back(data);
|
||||
|
||||
mBeginPoint = mCurrentPoint = aPoint;
|
||||
}
|
||||
|
||||
void
|
||||
PathBuilderCairo::LineTo(const Point &aPoint)
|
||||
{
|
||||
cairo_path_data_t data;
|
||||
data.header.type = CAIRO_PATH_LINE_TO;
|
||||
data.header.length = 2;
|
||||
mPathData.push_back(data);
|
||||
data.point.x = aPoint.x;
|
||||
data.point.y = aPoint.y;
|
||||
mPathData.push_back(data);
|
||||
|
||||
mCurrentPoint = aPoint;
|
||||
}
|
||||
|
||||
void
|
||||
PathBuilderCairo::BezierTo(const Point &aCP1,
|
||||
const Point &aCP2,
|
||||
const Point &aCP3)
|
||||
{
|
||||
cairo_path_data_t data;
|
||||
data.header.type = CAIRO_PATH_CURVE_TO;
|
||||
data.header.length = 4;
|
||||
mPathData.push_back(data);
|
||||
data.point.x = aCP1.x;
|
||||
data.point.y = aCP1.y;
|
||||
mPathData.push_back(data);
|
||||
data.point.x = aCP2.x;
|
||||
data.point.y = aCP2.y;
|
||||
mPathData.push_back(data);
|
||||
data.point.x = aCP3.x;
|
||||
data.point.y = aCP3.y;
|
||||
mPathData.push_back(data);
|
||||
|
||||
mCurrentPoint = aCP3;
|
||||
}
|
||||
|
||||
void
|
||||
PathBuilderCairo::QuadraticBezierTo(const Point &aCP1,
|
||||
const Point &aCP2)
|
||||
{
|
||||
// We need to elevate the degree of this quadratic Bézier to cubic, so we're
|
||||
// going to add an intermediate control point, and recompute control point 1.
|
||||
// The first and last control points remain the same.
|
||||
// This formula can be found on http://fontforge.sourceforge.net/bezier.html
|
||||
Point CP0 = CurrentPoint();
|
||||
Point CP1 = (CP0 + aCP1 * 2.0) / 3.0;
|
||||
Point CP2 = (aCP2 + aCP1 * 2.0) / 3.0;
|
||||
Point CP3 = aCP2;
|
||||
|
||||
cairo_path_data_t data;
|
||||
data.header.type = CAIRO_PATH_CURVE_TO;
|
||||
data.header.length = 4;
|
||||
mPathData.push_back(data);
|
||||
data.point.x = CP1.x;
|
||||
data.point.y = CP1.y;
|
||||
mPathData.push_back(data);
|
||||
data.point.x = CP2.x;
|
||||
data.point.y = CP2.y;
|
||||
mPathData.push_back(data);
|
||||
data.point.x = CP3.x;
|
||||
data.point.y = CP3.y;
|
||||
mPathData.push_back(data);
|
||||
|
||||
mCurrentPoint = aCP2;
|
||||
}
|
||||
|
||||
void
|
||||
PathBuilderCairo::Close()
|
||||
{
|
||||
cairo_path_data_t data;
|
||||
data.header.type = CAIRO_PATH_CLOSE_PATH;
|
||||
data.header.length = 1;
|
||||
mPathData.push_back(data);
|
||||
|
||||
mCurrentPoint = mBeginPoint;
|
||||
}
|
||||
|
||||
void
|
||||
PathBuilderCairo::Arc(const Point &aOrigin, float aRadius, float aStartAngle,
|
||||
float aEndAngle, bool aAntiClockwise)
|
||||
{
|
||||
ArcToBezier(this, aOrigin, Size(aRadius, aRadius), aStartAngle, aEndAngle, aAntiClockwise);
|
||||
}
|
||||
|
||||
Point
|
||||
PathBuilderCairo::CurrentPoint() const
|
||||
{
|
||||
return mCurrentPoint;
|
||||
}
|
||||
|
||||
already_AddRefed<Path>
|
||||
PathBuilderCairo::Finish()
|
||||
{
|
||||
return MakeAndAddRef<PathCairo>(mFillRule, mPathData, mCurrentPoint);
|
||||
}
|
||||
|
||||
PathCairo::PathCairo(FillRule aFillRule, std::vector<cairo_path_data_t> &aPathData, const Point &aCurrentPoint)
|
||||
: mFillRule(aFillRule)
|
||||
, mContainingContext(nullptr)
|
||||
, mCurrentPoint(aCurrentPoint)
|
||||
{
|
||||
mPathData.swap(aPathData);
|
||||
}
|
||||
|
||||
PathCairo::PathCairo(cairo_t *aContext)
|
||||
: mFillRule(FillRule::FILL_WINDING)
|
||||
, mContainingContext(nullptr)
|
||||
{
|
||||
cairo_path_t *path = cairo_copy_path(aContext);
|
||||
|
||||
// XXX - mCurrentPoint is not properly set here, the same is true for the
|
||||
// D2D Path code, we never require current point when hitting this codepath
|
||||
// but this should be fixed.
|
||||
for (int i = 0; i < path->num_data; i++) {
|
||||
mPathData.push_back(path->data[i]);
|
||||
}
|
||||
|
||||
cairo_path_destroy(path);
|
||||
}
|
||||
|
||||
PathCairo::~PathCairo()
|
||||
{
|
||||
if (mContainingContext) {
|
||||
cairo_destroy(mContainingContext);
|
||||
}
|
||||
}
|
||||
|
||||
already_AddRefed<PathBuilder>
|
||||
PathCairo::CopyToBuilder(FillRule aFillRule) const
|
||||
{
|
||||
RefPtr<PathBuilderCairo> builder = new PathBuilderCairo(aFillRule);
|
||||
|
||||
builder->mPathData = mPathData;
|
||||
builder->mCurrentPoint = mCurrentPoint;
|
||||
|
||||
return builder.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<PathBuilder>
|
||||
PathCairo::TransformedCopyToBuilder(const Matrix &aTransform, FillRule aFillRule) const
|
||||
{
|
||||
RefPtr<PathBuilderCairo> builder = new PathBuilderCairo(aFillRule);
|
||||
|
||||
AppendPathToBuilder(builder, &aTransform);
|
||||
builder->mCurrentPoint = aTransform.TransformPoint(mCurrentPoint);
|
||||
|
||||
return builder.forget();
|
||||
}
|
||||
|
||||
bool
|
||||
PathCairo::ContainsPoint(const Point &aPoint, const Matrix &aTransform) const
|
||||
{
|
||||
Matrix inverse = aTransform;
|
||||
inverse.Invert();
|
||||
Point transformed = inverse.TransformPoint(aPoint);
|
||||
|
||||
EnsureContainingContext(aTransform);
|
||||
|
||||
return cairo_in_fill(mContainingContext, transformed.x, transformed.y);
|
||||
}
|
||||
|
||||
bool
|
||||
PathCairo::StrokeContainsPoint(const StrokeOptions &aStrokeOptions,
|
||||
const Point &aPoint,
|
||||
const Matrix &aTransform) const
|
||||
{
|
||||
Matrix inverse = aTransform;
|
||||
inverse.Invert();
|
||||
Point transformed = inverse.TransformPoint(aPoint);
|
||||
|
||||
EnsureContainingContext(aTransform);
|
||||
|
||||
SetCairoStrokeOptions(mContainingContext, aStrokeOptions);
|
||||
|
||||
return cairo_in_stroke(mContainingContext, transformed.x, transformed.y);
|
||||
}
|
||||
|
||||
Rect
|
||||
PathCairo::GetBounds(const Matrix &aTransform) const
|
||||
{
|
||||
EnsureContainingContext(aTransform);
|
||||
|
||||
double x1, y1, x2, y2;
|
||||
|
||||
cairo_path_extents(mContainingContext, &x1, &y1, &x2, &y2);
|
||||
Rect bounds(Float(x1), Float(y1), Float(x2 - x1), Float(y2 - y1));
|
||||
return aTransform.TransformBounds(bounds);
|
||||
}
|
||||
|
||||
Rect
|
||||
PathCairo::GetStrokedBounds(const StrokeOptions &aStrokeOptions,
|
||||
const Matrix &aTransform) const
|
||||
{
|
||||
EnsureContainingContext(aTransform);
|
||||
|
||||
double x1, y1, x2, y2;
|
||||
|
||||
SetCairoStrokeOptions(mContainingContext, aStrokeOptions);
|
||||
|
||||
cairo_stroke_extents(mContainingContext, &x1, &y1, &x2, &y2);
|
||||
Rect bounds((Float)x1, (Float)y1, (Float)(x2 - x1), (Float)(y2 - y1));
|
||||
return aTransform.TransformBounds(bounds);
|
||||
}
|
||||
|
||||
void
|
||||
PathCairo::StreamToSink(PathSink *aSink) const
|
||||
{
|
||||
for (size_t i = 0; i < mPathData.size(); i++) {
|
||||
switch (mPathData[i].header.type) {
|
||||
case CAIRO_PATH_MOVE_TO:
|
||||
i++;
|
||||
aSink->MoveTo(Point(mPathData[i].point.x, mPathData[i].point.y));
|
||||
break;
|
||||
case CAIRO_PATH_LINE_TO:
|
||||
i++;
|
||||
aSink->LineTo(Point(mPathData[i].point.x, mPathData[i].point.y));
|
||||
break;
|
||||
case CAIRO_PATH_CURVE_TO:
|
||||
aSink->BezierTo(Point(mPathData[i + 1].point.x, mPathData[i + 1].point.y),
|
||||
Point(mPathData[i + 2].point.x, mPathData[i + 2].point.y),
|
||||
Point(mPathData[i + 3].point.x, mPathData[i + 3].point.y));
|
||||
i += 3;
|
||||
break;
|
||||
case CAIRO_PATH_CLOSE_PATH:
|
||||
aSink->Close();
|
||||
break;
|
||||
default:
|
||||
// Corrupt path data!
|
||||
MOZ_ASSERT(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PathCairo::EnsureContainingContext(const Matrix &aTransform) const
|
||||
{
|
||||
if (mContainingContext) {
|
||||
if (mContainingTransform.ExactlyEquals(aTransform)) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
mContainingContext = cairo_create(DrawTargetCairo::GetDummySurface());
|
||||
}
|
||||
|
||||
mContainingTransform = aTransform;
|
||||
|
||||
cairo_matrix_t mat;
|
||||
GfxMatrixToCairoMatrix(mContainingTransform, mat);
|
||||
cairo_set_matrix(mContainingContext, &mat);
|
||||
|
||||
SetPathOnContext(mContainingContext);
|
||||
}
|
||||
|
||||
void
|
||||
PathCairo::SetPathOnContext(cairo_t *aContext) const
|
||||
{
|
||||
// Needs the correct fill rule set.
|
||||
cairo_set_fill_rule(aContext, GfxFillRuleToCairoFillRule(mFillRule));
|
||||
|
||||
cairo_new_path(aContext);
|
||||
|
||||
if (mPathData.size()) {
|
||||
cairo_path_t path;
|
||||
path.data = const_cast<cairo_path_data_t*>(&mPathData.front());
|
||||
path.num_data = mPathData.size();
|
||||
path.status = CAIRO_STATUS_SUCCESS;
|
||||
cairo_append_path(aContext, &path);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PathCairo::AppendPathToBuilder(PathBuilderCairo *aBuilder, const Matrix *aTransform) const
|
||||
{
|
||||
if (aTransform) {
|
||||
size_t i = 0;
|
||||
while (i < mPathData.size()) {
|
||||
uint32_t pointCount = mPathData[i].header.length - 1;
|
||||
aBuilder->mPathData.push_back(mPathData[i]);
|
||||
i++;
|
||||
for (uint32_t c = 0; c < pointCount; c++) {
|
||||
cairo_path_data_t data;
|
||||
Point newPoint = aTransform->TransformPoint(Point(mPathData[i].point.x, mPathData[i].point.y));
|
||||
data.point.x = newPoint.x;
|
||||
data.point.y = newPoint.y;
|
||||
aBuilder->mPathData.push_back(data);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (size_t i = 0; i < mPathData.size(); i++) {
|
||||
aBuilder->mPathData.push_back(mPathData[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
||||
95
gfx/2d/PathCairo.h
Normal file
95
gfx/2d/PathCairo.h
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
/* -*- Mode: C++; tab-width: 20; 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_GFX_PATH_CAIRO_H_
|
||||
#define MOZILLA_GFX_PATH_CAIRO_H_
|
||||
|
||||
#include "2D.h"
|
||||
#include "cairo.h"
|
||||
#include <vector>
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
class PathCairo;
|
||||
|
||||
class PathBuilderCairo : public PathBuilder
|
||||
{
|
||||
public:
|
||||
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(PathBuilderCairo)
|
||||
explicit PathBuilderCairo(FillRule aFillRule);
|
||||
|
||||
virtual void MoveTo(const Point &aPoint);
|
||||
virtual void LineTo(const Point &aPoint);
|
||||
virtual void BezierTo(const Point &aCP1,
|
||||
const Point &aCP2,
|
||||
const Point &aCP3);
|
||||
virtual void QuadraticBezierTo(const Point &aCP1,
|
||||
const Point &aCP2);
|
||||
virtual void Close();
|
||||
virtual void Arc(const Point &aOrigin, float aRadius, float aStartAngle,
|
||||
float aEndAngle, bool aAntiClockwise = false);
|
||||
virtual Point CurrentPoint() const;
|
||||
virtual already_AddRefed<Path> Finish();
|
||||
|
||||
virtual BackendType GetBackendType() const { return BackendType::CAIRO; }
|
||||
|
||||
private: // data
|
||||
friend class PathCairo;
|
||||
|
||||
FillRule mFillRule;
|
||||
std::vector<cairo_path_data_t> mPathData;
|
||||
// It's easiest to track this here, parsing the path data to find the current
|
||||
// point is a little tricky.
|
||||
Point mCurrentPoint;
|
||||
Point mBeginPoint;
|
||||
};
|
||||
|
||||
class PathCairo : public Path
|
||||
{
|
||||
public:
|
||||
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(PathCairo)
|
||||
PathCairo(FillRule aFillRule, std::vector<cairo_path_data_t> &aPathData, const Point &aCurrentPoint);
|
||||
explicit PathCairo(cairo_t *aContext);
|
||||
~PathCairo();
|
||||
|
||||
virtual BackendType GetBackendType() const { return BackendType::CAIRO; }
|
||||
|
||||
virtual already_AddRefed<PathBuilder> CopyToBuilder(FillRule aFillRule) const;
|
||||
virtual already_AddRefed<PathBuilder> TransformedCopyToBuilder(const Matrix &aTransform,
|
||||
FillRule aFillRule) const;
|
||||
|
||||
virtual bool ContainsPoint(const Point &aPoint, const Matrix &aTransform) const;
|
||||
|
||||
virtual bool StrokeContainsPoint(const StrokeOptions &aStrokeOptions,
|
||||
const Point &aPoint,
|
||||
const Matrix &aTransform) const;
|
||||
|
||||
virtual Rect GetBounds(const Matrix &aTransform = Matrix()) const;
|
||||
|
||||
virtual Rect GetStrokedBounds(const StrokeOptions &aStrokeOptions,
|
||||
const Matrix &aTransform = Matrix()) const;
|
||||
|
||||
virtual void StreamToSink(PathSink *aSink) const;
|
||||
|
||||
virtual FillRule GetFillRule() const { return mFillRule; }
|
||||
|
||||
void SetPathOnContext(cairo_t *aContext) const;
|
||||
|
||||
void AppendPathToBuilder(PathBuilderCairo *aBuilder, const Matrix *aTransform = nullptr) const;
|
||||
private:
|
||||
void EnsureContainingContext(const Matrix &aTransform) const;
|
||||
|
||||
FillRule mFillRule;
|
||||
std::vector<cairo_path_data_t> mPathData;
|
||||
mutable cairo_t *mContainingContext;
|
||||
mutable Matrix mContainingTransform;
|
||||
Point mCurrentPoint;
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* MOZILLA_GFX_PATH_CAIRO_H_ */
|
||||
523
gfx/2d/PathD2D.cpp
Normal file
523
gfx/2d/PathD2D.cpp
Normal file
|
|
@ -0,0 +1,523 @@
|
|||
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "PathD2D.h"
|
||||
#include "HelpersD2D.h"
|
||||
#include <math.h>
|
||||
#include "DrawTargetD2D1.h"
|
||||
#include "Logging.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
// This class exists as a wrapper for ID2D1SimplifiedGeometry sink, it allows
|
||||
// a geometry to be duplicated into a geometry sink, while removing the final
|
||||
// figure end and thus allowing a figure that was implicitly closed to be
|
||||
// continued.
|
||||
class OpeningGeometrySink : public ID2D1SimplifiedGeometrySink
|
||||
{
|
||||
public:
|
||||
OpeningGeometrySink(ID2D1SimplifiedGeometrySink *aSink)
|
||||
: mSink(aSink)
|
||||
, mNeedsFigureEnded(false)
|
||||
{
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE QueryInterface(const IID &aIID, void **aPtr)
|
||||
{
|
||||
if (!aPtr) {
|
||||
return E_POINTER;
|
||||
}
|
||||
|
||||
if (aIID == IID_IUnknown) {
|
||||
*aPtr = static_cast<IUnknown*>(this);
|
||||
return S_OK;
|
||||
} else if (aIID == IID_ID2D1SimplifiedGeometrySink) {
|
||||
*aPtr = static_cast<ID2D1SimplifiedGeometrySink*>(this);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
|
||||
ULONG STDMETHODCALLTYPE AddRef()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
ULONG STDMETHODCALLTYPE Release()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
// We ignore SetFillMode, the copier will decide.
|
||||
STDMETHOD_(void, SetFillMode)(D2D1_FILL_MODE aMode)
|
||||
{ EnsureFigureEnded(); return; }
|
||||
STDMETHOD_(void, BeginFigure)(D2D1_POINT_2F aPoint, D2D1_FIGURE_BEGIN aBegin)
|
||||
{ EnsureFigureEnded(); return mSink->BeginFigure(aPoint, aBegin); }
|
||||
STDMETHOD_(void, AddLines)(const D2D1_POINT_2F *aLines, UINT aCount)
|
||||
{ EnsureFigureEnded(); return mSink->AddLines(aLines, aCount); }
|
||||
STDMETHOD_(void, AddBeziers)(const D2D1_BEZIER_SEGMENT *aSegments, UINT aCount)
|
||||
{ EnsureFigureEnded(); return mSink->AddBeziers(aSegments, aCount); }
|
||||
STDMETHOD(Close)()
|
||||
{ /* Should never be called! */ return S_OK; }
|
||||
STDMETHOD_(void, SetSegmentFlags)(D2D1_PATH_SEGMENT aFlags)
|
||||
{ return mSink->SetSegmentFlags(aFlags); }
|
||||
|
||||
// This function is special - it's the reason this class exists.
|
||||
// It needs to intercept the very last endfigure. So that a user can
|
||||
// continue writing to this sink as if they never stopped.
|
||||
STDMETHOD_(void, EndFigure)(D2D1_FIGURE_END aEnd)
|
||||
{
|
||||
if (aEnd == D2D1_FIGURE_END_CLOSED) {
|
||||
return mSink->EndFigure(aEnd);
|
||||
} else {
|
||||
mNeedsFigureEnded = true;
|
||||
}
|
||||
}
|
||||
private:
|
||||
void EnsureFigureEnded()
|
||||
{
|
||||
if (mNeedsFigureEnded) {
|
||||
mSink->EndFigure(D2D1_FIGURE_END_OPEN);
|
||||
mNeedsFigureEnded = false;
|
||||
}
|
||||
}
|
||||
|
||||
ID2D1SimplifiedGeometrySink *mSink;
|
||||
bool mNeedsFigureEnded;
|
||||
};
|
||||
|
||||
class MOZ_STACK_CLASS AutoRestoreFP
|
||||
{
|
||||
public:
|
||||
AutoRestoreFP()
|
||||
{
|
||||
// save the current floating point control word
|
||||
_controlfp_s(&savedFPSetting, 0, 0);
|
||||
UINT unused;
|
||||
// set the floating point control word to its default value
|
||||
_controlfp_s(&unused, _CW_DEFAULT, MCW_PC);
|
||||
}
|
||||
~AutoRestoreFP()
|
||||
{
|
||||
UINT unused;
|
||||
// restore the saved floating point control word
|
||||
_controlfp_s(&unused, savedFPSetting, MCW_PC);
|
||||
}
|
||||
private:
|
||||
UINT savedFPSetting;
|
||||
};
|
||||
|
||||
// Note that overrides of ID2D1SimplifiedGeometrySink methods in this class may
|
||||
// get called from D2D with nonstandard floating point settings (see comments in
|
||||
// bug 1134549) - use AutoRestoreFP to reset the floating point control word to
|
||||
// what we expect
|
||||
class StreamingGeometrySink : public ID2D1SimplifiedGeometrySink
|
||||
{
|
||||
public:
|
||||
StreamingGeometrySink(PathSink *aSink)
|
||||
: mSink(aSink)
|
||||
{
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE QueryInterface(const IID &aIID, void **aPtr)
|
||||
{
|
||||
if (!aPtr) {
|
||||
return E_POINTER;
|
||||
}
|
||||
|
||||
if (aIID == IID_IUnknown) {
|
||||
*aPtr = static_cast<IUnknown*>(this);
|
||||
return S_OK;
|
||||
} else if (aIID == IID_ID2D1SimplifiedGeometrySink) {
|
||||
*aPtr = static_cast<ID2D1SimplifiedGeometrySink*>(this);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
|
||||
ULONG STDMETHODCALLTYPE AddRef()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
ULONG STDMETHODCALLTYPE Release()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
// We ignore SetFillMode, this depends on the destination sink.
|
||||
STDMETHOD_(void, SetFillMode)(D2D1_FILL_MODE aMode)
|
||||
{ return; }
|
||||
STDMETHOD_(void, BeginFigure)(D2D1_POINT_2F aPoint, D2D1_FIGURE_BEGIN aBegin)
|
||||
{
|
||||
AutoRestoreFP resetFloatingPoint;
|
||||
mSink->MoveTo(ToPoint(aPoint));
|
||||
}
|
||||
STDMETHOD_(void, AddLines)(const D2D1_POINT_2F *aLines, UINT aCount)
|
||||
{
|
||||
AutoRestoreFP resetFloatingPoint;
|
||||
for (UINT i = 0; i < aCount; i++) { mSink->LineTo(ToPoint(aLines[i])); }
|
||||
}
|
||||
STDMETHOD_(void, AddBeziers)(const D2D1_BEZIER_SEGMENT *aSegments, UINT aCount)
|
||||
{
|
||||
AutoRestoreFP resetFloatingPoint;
|
||||
for (UINT i = 0; i < aCount; i++) {
|
||||
mSink->BezierTo(ToPoint(aSegments[i].point1), ToPoint(aSegments[i].point2), ToPoint(aSegments[i].point3));
|
||||
}
|
||||
}
|
||||
STDMETHOD(Close)()
|
||||
{ /* Should never be called! */ return S_OK; }
|
||||
STDMETHOD_(void, SetSegmentFlags)(D2D1_PATH_SEGMENT aFlags)
|
||||
{ /* Should never be called! */ }
|
||||
|
||||
STDMETHOD_(void, EndFigure)(D2D1_FIGURE_END aEnd)
|
||||
{
|
||||
AutoRestoreFP resetFloatingPoint;
|
||||
if (aEnd == D2D1_FIGURE_END_CLOSED) {
|
||||
return mSink->Close();
|
||||
}
|
||||
}
|
||||
private:
|
||||
|
||||
PathSink *mSink;
|
||||
};
|
||||
|
||||
PathBuilderD2D::~PathBuilderD2D()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
PathBuilderD2D::MoveTo(const Point &aPoint)
|
||||
{
|
||||
if (mFigureActive) {
|
||||
mSink->EndFigure(D2D1_FIGURE_END_OPEN);
|
||||
mFigureActive = false;
|
||||
}
|
||||
EnsureActive(aPoint);
|
||||
mCurrentPoint = aPoint;
|
||||
}
|
||||
|
||||
void
|
||||
PathBuilderD2D::LineTo(const Point &aPoint)
|
||||
{
|
||||
EnsureActive(aPoint);
|
||||
mSink->AddLine(D2DPoint(aPoint));
|
||||
|
||||
mCurrentPoint = aPoint;
|
||||
}
|
||||
|
||||
void
|
||||
PathBuilderD2D::BezierTo(const Point &aCP1,
|
||||
const Point &aCP2,
|
||||
const Point &aCP3)
|
||||
{
|
||||
EnsureActive(aCP1);
|
||||
mSink->AddBezier(D2D1::BezierSegment(D2DPoint(aCP1),
|
||||
D2DPoint(aCP2),
|
||||
D2DPoint(aCP3)));
|
||||
|
||||
mCurrentPoint = aCP3;
|
||||
}
|
||||
|
||||
void
|
||||
PathBuilderD2D::QuadraticBezierTo(const Point &aCP1,
|
||||
const Point &aCP2)
|
||||
{
|
||||
EnsureActive(aCP1);
|
||||
mSink->AddQuadraticBezier(D2D1::QuadraticBezierSegment(D2DPoint(aCP1),
|
||||
D2DPoint(aCP2)));
|
||||
|
||||
mCurrentPoint = aCP2;
|
||||
}
|
||||
|
||||
void
|
||||
PathBuilderD2D::Close()
|
||||
{
|
||||
if (mFigureActive) {
|
||||
mSink->EndFigure(D2D1_FIGURE_END_CLOSED);
|
||||
|
||||
mFigureActive = false;
|
||||
|
||||
EnsureActive(mBeginPoint);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PathBuilderD2D::Arc(const Point &aOrigin, Float aRadius, Float aStartAngle,
|
||||
Float aEndAngle, bool aAntiClockwise)
|
||||
{
|
||||
MOZ_ASSERT(aRadius >= 0);
|
||||
|
||||
if (aAntiClockwise && aStartAngle < aEndAngle) {
|
||||
// D2D does things a little differently, and draws the arc by specifying an
|
||||
// beginning and an end point. This means the circle will be the wrong way
|
||||
// around if the start angle is smaller than the end angle. It might seem
|
||||
// tempting to invert aAntiClockwise but that would change the sweeping
|
||||
// direction of the arc so instead we exchange start/begin.
|
||||
Float oldStart = aStartAngle;
|
||||
aStartAngle = aEndAngle;
|
||||
aEndAngle = oldStart;
|
||||
}
|
||||
|
||||
// XXX - Workaround for now, D2D does not appear to do the desired thing when
|
||||
// the angle sweeps a complete circle.
|
||||
bool fullCircle = false;
|
||||
if (aEndAngle - aStartAngle >= 2 * M_PI) {
|
||||
fullCircle = true;
|
||||
aEndAngle = Float(aStartAngle + M_PI * 1.9999);
|
||||
} else if (aStartAngle - aEndAngle >= 2 * M_PI) {
|
||||
fullCircle = true;
|
||||
aStartAngle = Float(aEndAngle + M_PI * 1.9999);
|
||||
}
|
||||
|
||||
Point startPoint;
|
||||
startPoint.x = aOrigin.x + aRadius * cos(aStartAngle);
|
||||
startPoint.y = aOrigin.y + aRadius * sin(aStartAngle);
|
||||
|
||||
if (!mFigureActive) {
|
||||
EnsureActive(startPoint);
|
||||
} else {
|
||||
mSink->AddLine(D2DPoint(startPoint));
|
||||
}
|
||||
|
||||
Point endPoint;
|
||||
endPoint.x = aOrigin.x + aRadius * cosf(aEndAngle);
|
||||
endPoint.y = aOrigin.y + aRadius * sinf(aEndAngle);
|
||||
|
||||
D2D1_ARC_SIZE arcSize = D2D1_ARC_SIZE_SMALL;
|
||||
D2D1_SWEEP_DIRECTION direction =
|
||||
aAntiClockwise ? D2D1_SWEEP_DIRECTION_COUNTER_CLOCKWISE :
|
||||
D2D1_SWEEP_DIRECTION_CLOCKWISE;
|
||||
|
||||
// if startPoint and endPoint of our circle are too close there are D2D issues
|
||||
// with drawing the circle as a single arc
|
||||
const Float kEpsilon = 1e-5f;
|
||||
if (!fullCircle ||
|
||||
(std::abs(startPoint.x - endPoint.x) +
|
||||
std::abs(startPoint.y - endPoint.y) > kEpsilon)) {
|
||||
|
||||
if (aAntiClockwise) {
|
||||
if (aStartAngle - aEndAngle > M_PI) {
|
||||
arcSize = D2D1_ARC_SIZE_LARGE;
|
||||
}
|
||||
} else {
|
||||
if (aEndAngle - aStartAngle > M_PI) {
|
||||
arcSize = D2D1_ARC_SIZE_LARGE;
|
||||
}
|
||||
}
|
||||
|
||||
mSink->AddArc(D2D1::ArcSegment(D2DPoint(endPoint),
|
||||
D2D1::SizeF(aRadius, aRadius),
|
||||
0.0f,
|
||||
direction,
|
||||
arcSize));
|
||||
}
|
||||
else {
|
||||
// our first workaround attempt didn't work, so instead draw the circle as
|
||||
// two half-circles
|
||||
Float midAngle = aEndAngle > aStartAngle ?
|
||||
Float(aStartAngle + M_PI) : Float(aEndAngle + M_PI);
|
||||
Point midPoint;
|
||||
midPoint.x = aOrigin.x + aRadius * cosf(midAngle);
|
||||
midPoint.y = aOrigin.y + aRadius * sinf(midAngle);
|
||||
|
||||
mSink->AddArc(D2D1::ArcSegment(D2DPoint(midPoint),
|
||||
D2D1::SizeF(aRadius, aRadius),
|
||||
0.0f,
|
||||
direction,
|
||||
arcSize));
|
||||
|
||||
// if the adjusted endPoint computed above is used here and endPoint !=
|
||||
// startPoint then this half of the circle won't render...
|
||||
mSink->AddArc(D2D1::ArcSegment(D2DPoint(startPoint),
|
||||
D2D1::SizeF(aRadius, aRadius),
|
||||
0.0f,
|
||||
direction,
|
||||
arcSize));
|
||||
}
|
||||
|
||||
mCurrentPoint = endPoint;
|
||||
}
|
||||
|
||||
Point
|
||||
PathBuilderD2D::CurrentPoint() const
|
||||
{
|
||||
return mCurrentPoint;
|
||||
}
|
||||
|
||||
void
|
||||
PathBuilderD2D::EnsureActive(const Point &aPoint)
|
||||
{
|
||||
if (!mFigureActive) {
|
||||
mSink->BeginFigure(D2DPoint(aPoint), D2D1_FIGURE_BEGIN_FILLED);
|
||||
mBeginPoint = aPoint;
|
||||
mFigureActive = true;
|
||||
}
|
||||
}
|
||||
|
||||
already_AddRefed<Path>
|
||||
PathBuilderD2D::Finish()
|
||||
{
|
||||
if (mFigureActive) {
|
||||
mSink->EndFigure(D2D1_FIGURE_END_OPEN);
|
||||
}
|
||||
|
||||
HRESULT hr = mSink->Close();
|
||||
if (FAILED(hr)) {
|
||||
gfxCriticalNote << "Failed to close PathSink. Code: " << hexa(hr);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return MakeAndAddRef<PathD2D>(mGeometry, mFigureActive, mCurrentPoint, mFillRule, mBackendType);
|
||||
}
|
||||
|
||||
already_AddRefed<PathBuilder>
|
||||
PathD2D::CopyToBuilder(FillRule aFillRule) const
|
||||
{
|
||||
return TransformedCopyToBuilder(Matrix(), aFillRule);
|
||||
}
|
||||
|
||||
already_AddRefed<PathBuilder>
|
||||
PathD2D::TransformedCopyToBuilder(const Matrix &aTransform, FillRule aFillRule) const
|
||||
{
|
||||
RefPtr<ID2D1PathGeometry> path;
|
||||
HRESULT hr = DrawTargetD2D1::factory()->CreatePathGeometry(getter_AddRefs(path));
|
||||
|
||||
if (FAILED(hr)) {
|
||||
gfxWarning() << "Failed to create PathGeometry. Code: " << hexa(hr);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<ID2D1GeometrySink> sink;
|
||||
hr = path->Open(getter_AddRefs(sink));
|
||||
if (FAILED(hr)) {
|
||||
gfxWarning() << "Failed to open Geometry for writing. Code: " << hexa(hr);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (aFillRule == FillRule::FILL_WINDING) {
|
||||
sink->SetFillMode(D2D1_FILL_MODE_WINDING);
|
||||
}
|
||||
|
||||
if (mEndedActive) {
|
||||
OpeningGeometrySink wrapSink(sink);
|
||||
hr = mGeometry->Simplify(D2D1_GEOMETRY_SIMPLIFICATION_OPTION_CUBICS_AND_LINES,
|
||||
D2DMatrix(aTransform),
|
||||
&wrapSink);
|
||||
} else {
|
||||
hr = mGeometry->Simplify(D2D1_GEOMETRY_SIMPLIFICATION_OPTION_CUBICS_AND_LINES,
|
||||
D2DMatrix(aTransform),
|
||||
sink);
|
||||
}
|
||||
if (FAILED(hr)) {
|
||||
gfxWarning() << "Failed to simplify PathGeometry to tranformed copy. Code: " << hexa(hr) << " Active: " << mEndedActive;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<PathBuilderD2D> pathBuilder = new PathBuilderD2D(sink, path, aFillRule, mBackendType);
|
||||
|
||||
pathBuilder->mCurrentPoint = aTransform.TransformPoint(mEndPoint);
|
||||
|
||||
if (mEndedActive) {
|
||||
pathBuilder->mFigureActive = true;
|
||||
}
|
||||
|
||||
return pathBuilder.forget();
|
||||
}
|
||||
|
||||
void
|
||||
PathD2D::StreamToSink(PathSink *aSink) const
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
StreamingGeometrySink sink(aSink);
|
||||
|
||||
hr = mGeometry->Simplify(D2D1_GEOMETRY_SIMPLIFICATION_OPTION_CUBICS_AND_LINES,
|
||||
D2D1::IdentityMatrix(), &sink);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
gfxWarning() << "Failed to stream D2D path to sink. Code: " << hexa(hr);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
PathD2D::ContainsPoint(const Point &aPoint, const Matrix &aTransform) const
|
||||
{
|
||||
BOOL result;
|
||||
|
||||
HRESULT hr = mGeometry->FillContainsPoint(D2DPoint(aPoint), D2DMatrix(aTransform), 0.001f, &result);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
// Log
|
||||
return false;
|
||||
}
|
||||
|
||||
return !!result;
|
||||
}
|
||||
|
||||
bool
|
||||
PathD2D::StrokeContainsPoint(const StrokeOptions &aStrokeOptions,
|
||||
const Point &aPoint,
|
||||
const Matrix &aTransform) const
|
||||
{
|
||||
BOOL result;
|
||||
|
||||
RefPtr<ID2D1StrokeStyle> strokeStyle = CreateStrokeStyleForOptions(aStrokeOptions);
|
||||
HRESULT hr = mGeometry->StrokeContainsPoint(D2DPoint(aPoint),
|
||||
aStrokeOptions.mLineWidth,
|
||||
strokeStyle,
|
||||
D2DMatrix(aTransform),
|
||||
&result);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
// Log
|
||||
return false;
|
||||
}
|
||||
|
||||
return !!result;
|
||||
}
|
||||
|
||||
Rect
|
||||
PathD2D::GetBounds(const Matrix &aTransform) const
|
||||
{
|
||||
D2D1_RECT_F d2dBounds;
|
||||
|
||||
HRESULT hr = mGeometry->GetBounds(D2DMatrix(aTransform), &d2dBounds);
|
||||
|
||||
Rect bounds = ToRect(d2dBounds);
|
||||
if (FAILED(hr) || !bounds.IsFinite()) {
|
||||
gfxWarning() << "Failed to get stroked bounds for path. Code: " << hexa(hr);
|
||||
return Rect();
|
||||
}
|
||||
|
||||
return bounds;
|
||||
}
|
||||
|
||||
Rect
|
||||
PathD2D::GetStrokedBounds(const StrokeOptions &aStrokeOptions,
|
||||
const Matrix &aTransform) const
|
||||
{
|
||||
D2D1_RECT_F d2dBounds;
|
||||
|
||||
RefPtr<ID2D1StrokeStyle> strokeStyle = CreateStrokeStyleForOptions(aStrokeOptions);
|
||||
HRESULT hr =
|
||||
mGeometry->GetWidenedBounds(aStrokeOptions.mLineWidth, strokeStyle,
|
||||
D2DMatrix(aTransform), &d2dBounds);
|
||||
|
||||
Rect bounds = ToRect(d2dBounds);
|
||||
if (FAILED(hr) || !bounds.IsFinite()) {
|
||||
gfxWarning() << "Failed to get stroked bounds for path. Code: " << hexa(hr);
|
||||
return Rect();
|
||||
}
|
||||
|
||||
return bounds;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
117
gfx/2d/PathD2D.h
Normal file
117
gfx/2d/PathD2D.h
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
/* -*- Mode: C++; tab-width: 20; 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_GFX_PATHD2D_H_
|
||||
#define MOZILLA_GFX_PATHD2D_H_
|
||||
|
||||
#include <d2d1.h>
|
||||
|
||||
#include "2D.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
class PathD2D;
|
||||
|
||||
class PathBuilderD2D : public PathBuilder
|
||||
{
|
||||
public:
|
||||
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(PathBuilderD2D)
|
||||
PathBuilderD2D(ID2D1GeometrySink *aSink, ID2D1PathGeometry *aGeom, FillRule aFillRule, BackendType aBackendType)
|
||||
: mSink(aSink)
|
||||
, mGeometry(aGeom)
|
||||
, mFigureActive(false)
|
||||
, mFillRule(aFillRule)
|
||||
, mBackendType(aBackendType)
|
||||
{
|
||||
}
|
||||
virtual ~PathBuilderD2D();
|
||||
|
||||
virtual void MoveTo(const Point &aPoint);
|
||||
virtual void LineTo(const Point &aPoint);
|
||||
virtual void BezierTo(const Point &aCP1,
|
||||
const Point &aCP2,
|
||||
const Point &aCP3);
|
||||
virtual void QuadraticBezierTo(const Point &aCP1,
|
||||
const Point &aCP2);
|
||||
virtual void Close();
|
||||
virtual void Arc(const Point &aOrigin, Float aRadius, Float aStartAngle,
|
||||
Float aEndAngle, bool aAntiClockwise = false);
|
||||
virtual Point CurrentPoint() const;
|
||||
|
||||
virtual already_AddRefed<Path> Finish();
|
||||
|
||||
virtual BackendType GetBackendType() const { return mBackendType; }
|
||||
|
||||
ID2D1GeometrySink *GetSink() { return mSink; }
|
||||
|
||||
bool IsFigureActive() const { return mFigureActive; }
|
||||
|
||||
private:
|
||||
friend class PathD2D;
|
||||
|
||||
void EnsureActive(const Point &aPoint);
|
||||
|
||||
RefPtr<ID2D1GeometrySink> mSink;
|
||||
RefPtr<ID2D1PathGeometry> mGeometry;
|
||||
|
||||
bool mFigureActive;
|
||||
Point mCurrentPoint;
|
||||
Point mBeginPoint;
|
||||
FillRule mFillRule;
|
||||
BackendType mBackendType;
|
||||
};
|
||||
|
||||
class PathD2D : public Path
|
||||
{
|
||||
public:
|
||||
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(PathD2D)
|
||||
PathD2D(ID2D1PathGeometry *aGeometry, bool aEndedActive,
|
||||
const Point &aEndPoint, FillRule aFillRule, BackendType aBackendType)
|
||||
: mGeometry(aGeometry)
|
||||
, mEndedActive(aEndedActive)
|
||||
, mEndPoint(aEndPoint)
|
||||
, mFillRule(aFillRule)
|
||||
, mBackendType(aBackendType)
|
||||
{}
|
||||
|
||||
virtual BackendType GetBackendType() const { return mBackendType; }
|
||||
|
||||
virtual already_AddRefed<PathBuilder> CopyToBuilder(FillRule aFillRule) const;
|
||||
virtual already_AddRefed<PathBuilder> TransformedCopyToBuilder(const Matrix &aTransform,
|
||||
FillRule aFillRule) const;
|
||||
|
||||
virtual bool ContainsPoint(const Point &aPoint, const Matrix &aTransform) const;
|
||||
|
||||
virtual bool StrokeContainsPoint(const StrokeOptions &aStrokeOptions,
|
||||
const Point &aPoint,
|
||||
const Matrix &aTransform) const;
|
||||
|
||||
virtual Rect GetBounds(const Matrix &aTransform = Matrix()) const;
|
||||
|
||||
virtual Rect GetStrokedBounds(const StrokeOptions &aStrokeOptions,
|
||||
const Matrix &aTransform = Matrix()) const;
|
||||
|
||||
virtual void StreamToSink(PathSink *aSink) const;
|
||||
|
||||
virtual FillRule GetFillRule() const { return mFillRule; }
|
||||
|
||||
ID2D1Geometry *GetGeometry() { return mGeometry; }
|
||||
|
||||
private:
|
||||
friend class DrawTargetD2D;
|
||||
friend class DrawTargetD2D1;
|
||||
|
||||
mutable RefPtr<ID2D1PathGeometry> mGeometry;
|
||||
bool mEndedActive;
|
||||
Point mEndPoint;
|
||||
FillRule mFillRule;
|
||||
BackendType mBackendType;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* MOZILLA_GFX_PATHD2D_H_ */
|
||||
277
gfx/2d/PathHelpers.cpp
Normal file
277
gfx/2d/PathHelpers.cpp
Normal file
|
|
@ -0,0 +1,277 @@
|
|||
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "PathHelpers.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
UserDataKey sDisablePixelSnapping;
|
||||
|
||||
void
|
||||
AppendRectToPath(PathBuilder* aPathBuilder,
|
||||
const Rect& aRect,
|
||||
bool aDrawClockwise)
|
||||
{
|
||||
if (aDrawClockwise) {
|
||||
aPathBuilder->MoveTo(aRect.TopLeft());
|
||||
aPathBuilder->LineTo(aRect.TopRight());
|
||||
aPathBuilder->LineTo(aRect.BottomRight());
|
||||
aPathBuilder->LineTo(aRect.BottomLeft());
|
||||
} else {
|
||||
aPathBuilder->MoveTo(aRect.TopRight());
|
||||
aPathBuilder->LineTo(aRect.TopLeft());
|
||||
aPathBuilder->LineTo(aRect.BottomLeft());
|
||||
aPathBuilder->LineTo(aRect.BottomRight());
|
||||
}
|
||||
aPathBuilder->Close();
|
||||
}
|
||||
|
||||
void
|
||||
AppendRoundedRectToPath(PathBuilder* aPathBuilder,
|
||||
const Rect& aRect,
|
||||
const RectCornerRadii& aRadii,
|
||||
bool aDrawClockwise)
|
||||
{
|
||||
// For CW drawing, this looks like:
|
||||
//
|
||||
// ...******0** 1 C
|
||||
// ****
|
||||
// *** 2
|
||||
// **
|
||||
// *
|
||||
// *
|
||||
// 3
|
||||
// *
|
||||
// *
|
||||
//
|
||||
// Where 0, 1, 2, 3 are the control points of the Bezier curve for
|
||||
// the corner, and C is the actual corner point.
|
||||
//
|
||||
// At the start of the loop, the current point is assumed to be
|
||||
// the point adjacent to the top left corner on the top
|
||||
// horizontal. Note that corner indices start at the top left and
|
||||
// continue clockwise, whereas in our loop i = 0 refers to the top
|
||||
// right corner.
|
||||
//
|
||||
// When going CCW, the control points are swapped, and the first
|
||||
// corner that's drawn is the top left (along with the top segment).
|
||||
//
|
||||
// There is considerable latitude in how one chooses the four
|
||||
// control points for a Bezier curve approximation to an ellipse.
|
||||
// For the overall path to be continuous and show no corner at the
|
||||
// endpoints of the arc, points 0 and 3 must be at the ends of the
|
||||
// straight segments of the rectangle; points 0, 1, and C must be
|
||||
// collinear; and points 3, 2, and C must also be collinear. This
|
||||
// leaves only two free parameters: the ratio of the line segments
|
||||
// 01 and 0C, and the ratio of the line segments 32 and 3C. See
|
||||
// the following papers for extensive discussion of how to choose
|
||||
// these ratios:
|
||||
//
|
||||
// Dokken, Tor, et al. "Good approximation of circles by
|
||||
// curvature-continuous Bezier curves." Computer-Aided
|
||||
// Geometric Design 7(1990) 33--41.
|
||||
// Goldapp, Michael. "Approximation of circular arcs by cubic
|
||||
// polynomials." Computer-Aided Geometric Design 8(1991) 227--238.
|
||||
// Maisonobe, Luc. "Drawing an elliptical arc using polylines,
|
||||
// quadratic, or cubic Bezier curves."
|
||||
// http://www.spaceroots.org/documents/ellipse/elliptical-arc.pdf
|
||||
//
|
||||
// We follow the approach in section 2 of Goldapp (least-error,
|
||||
// Hermite-type approximation) and make both ratios equal to
|
||||
//
|
||||
// 2 2 + n - sqrt(2n + 28)
|
||||
// alpha = - * ---------------------
|
||||
// 3 n - 4
|
||||
//
|
||||
// where n = 3( cbrt(sqrt(2)+1) - cbrt(sqrt(2)-1) ).
|
||||
//
|
||||
// This is the result of Goldapp's equation (10b) when the angle
|
||||
// swept out by the arc is pi/2, and the parameter "a-bar" is the
|
||||
// expression given immediately below equation (21).
|
||||
//
|
||||
// Using this value, the maximum radial error for a circle, as a
|
||||
// fraction of the radius, is on the order of 0.2 x 10^-3.
|
||||
// Neither Dokken nor Goldapp discusses error for a general
|
||||
// ellipse; Maisonobe does, but his choice of control points
|
||||
// follows different constraints, and Goldapp's expression for
|
||||
// 'alpha' gives much smaller radial error, even for very flat
|
||||
// ellipses, than Maisonobe's equivalent.
|
||||
//
|
||||
// For the various corners and for each axis, the sign of this
|
||||
// constant changes, or it might be 0 -- it's multiplied by the
|
||||
// appropriate multiplier from the list before using.
|
||||
|
||||
const Float alpha = Float(0.55191497064665766025);
|
||||
|
||||
typedef struct { Float a, b; } twoFloats;
|
||||
|
||||
twoFloats cwCornerMults[4] = { { -1, 0 }, // cc == clockwise
|
||||
{ 0, -1 },
|
||||
{ +1, 0 },
|
||||
{ 0, +1 } };
|
||||
twoFloats ccwCornerMults[4] = { { +1, 0 }, // ccw == counter-clockwise
|
||||
{ 0, -1 },
|
||||
{ -1, 0 },
|
||||
{ 0, +1 } };
|
||||
|
||||
twoFloats *cornerMults = aDrawClockwise ? cwCornerMults : ccwCornerMults;
|
||||
|
||||
Point cornerCoords[] = { aRect.TopLeft(), aRect.TopRight(),
|
||||
aRect.BottomRight(), aRect.BottomLeft() };
|
||||
|
||||
Point pc, p0, p1, p2, p3;
|
||||
|
||||
if (aDrawClockwise) {
|
||||
aPathBuilder->MoveTo(Point(aRect.X() + aRadii[RectCorner::TopLeft].width,
|
||||
aRect.Y()));
|
||||
} else {
|
||||
aPathBuilder->MoveTo(Point(aRect.X() + aRect.Width() - aRadii[RectCorner::TopRight].width,
|
||||
aRect.Y()));
|
||||
}
|
||||
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
// the corner index -- either 1 2 3 0 (cw) or 0 3 2 1 (ccw)
|
||||
int c = aDrawClockwise ? ((i+1) % 4) : ((4-i) % 4);
|
||||
|
||||
// i+2 and i+3 respectively. These are used to index into the corner
|
||||
// multiplier table, and were deduced by calculating out the long form
|
||||
// of each corner and finding a pattern in the signs and values.
|
||||
int i2 = (i+2) % 4;
|
||||
int i3 = (i+3) % 4;
|
||||
|
||||
pc = cornerCoords[c];
|
||||
|
||||
if (aRadii[c].width > 0.0 && aRadii[c].height > 0.0) {
|
||||
p0.x = pc.x + cornerMults[i].a * aRadii[c].width;
|
||||
p0.y = pc.y + cornerMults[i].b * aRadii[c].height;
|
||||
|
||||
p3.x = pc.x + cornerMults[i3].a * aRadii[c].width;
|
||||
p3.y = pc.y + cornerMults[i3].b * aRadii[c].height;
|
||||
|
||||
p1.x = p0.x + alpha * cornerMults[i2].a * aRadii[c].width;
|
||||
p1.y = p0.y + alpha * cornerMults[i2].b * aRadii[c].height;
|
||||
|
||||
p2.x = p3.x - alpha * cornerMults[i3].a * aRadii[c].width;
|
||||
p2.y = p3.y - alpha * cornerMults[i3].b * aRadii[c].height;
|
||||
|
||||
aPathBuilder->LineTo(p0);
|
||||
aPathBuilder->BezierTo(p1, p2, p3);
|
||||
} else {
|
||||
aPathBuilder->LineTo(pc);
|
||||
}
|
||||
}
|
||||
|
||||
aPathBuilder->Close();
|
||||
}
|
||||
|
||||
void
|
||||
AppendEllipseToPath(PathBuilder* aPathBuilder,
|
||||
const Point& aCenter,
|
||||
const Size& aDimensions)
|
||||
{
|
||||
Size halfDim = aDimensions / 2.f;
|
||||
Rect rect(aCenter - Point(halfDim.width, halfDim.height), aDimensions);
|
||||
RectCornerRadii radii(halfDim.width, halfDim.height);
|
||||
|
||||
AppendRoundedRectToPath(aPathBuilder, rect, radii);
|
||||
}
|
||||
|
||||
bool
|
||||
SnapLineToDevicePixelsForStroking(Point& aP1, Point& aP2,
|
||||
const DrawTarget& aDrawTarget,
|
||||
Float aLineWidth)
|
||||
{
|
||||
Matrix mat = aDrawTarget.GetTransform();
|
||||
if (mat.HasNonTranslation()) {
|
||||
return false;
|
||||
}
|
||||
if (aP1.x != aP2.x && aP1.y != aP2.y) {
|
||||
return false; // not a horizontal or vertical line
|
||||
}
|
||||
Point p1 = aP1 + mat.GetTranslation(); // into device space
|
||||
Point p2 = aP2 + mat.GetTranslation();
|
||||
p1.Round();
|
||||
p2.Round();
|
||||
p1 -= mat.GetTranslation(); // back into user space
|
||||
p2 -= mat.GetTranslation();
|
||||
|
||||
aP1 = p1;
|
||||
aP2 = p2;
|
||||
|
||||
bool lineWidthIsOdd = (int(aLineWidth) % 2) == 1;
|
||||
if (lineWidthIsOdd) {
|
||||
if (aP1.x == aP2.x) {
|
||||
// snap vertical line, adding 0.5 to align it to be mid-pixel:
|
||||
aP1 += Point(0.5, 0);
|
||||
aP2 += Point(0.5, 0);
|
||||
} else {
|
||||
// snap horizontal line, adding 0.5 to align it to be mid-pixel:
|
||||
aP1 += Point(0, 0.5);
|
||||
aP2 += Point(0, 0.5);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
StrokeSnappedEdgesOfRect(const Rect& aRect, DrawTarget& aDrawTarget,
|
||||
const ColorPattern& aColor,
|
||||
const StrokeOptions& aStrokeOptions)
|
||||
{
|
||||
if (aRect.IsEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Point p1 = aRect.TopLeft();
|
||||
Point p2 = aRect.BottomLeft();
|
||||
SnapLineToDevicePixelsForStroking(p1, p2, aDrawTarget,
|
||||
aStrokeOptions.mLineWidth);
|
||||
aDrawTarget.StrokeLine(p1, p2, aColor, aStrokeOptions);
|
||||
|
||||
p1 = aRect.BottomLeft();
|
||||
p2 = aRect.BottomRight();
|
||||
SnapLineToDevicePixelsForStroking(p1, p2, aDrawTarget,
|
||||
aStrokeOptions.mLineWidth);
|
||||
aDrawTarget.StrokeLine(p1, p2, aColor, aStrokeOptions);
|
||||
|
||||
p1 = aRect.TopLeft();
|
||||
p2 = aRect.TopRight();
|
||||
SnapLineToDevicePixelsForStroking(p1, p2, aDrawTarget,
|
||||
aStrokeOptions.mLineWidth);
|
||||
aDrawTarget.StrokeLine(p1, p2, aColor, aStrokeOptions);
|
||||
|
||||
p1 = aRect.TopRight();
|
||||
p2 = aRect.BottomRight();
|
||||
SnapLineToDevicePixelsForStroking(p1, p2, aDrawTarget,
|
||||
aStrokeOptions.mLineWidth);
|
||||
aDrawTarget.StrokeLine(p1, p2, aColor, aStrokeOptions);
|
||||
}
|
||||
|
||||
// The logic for this comes from _cairo_stroke_style_max_distance_from_path
|
||||
Margin
|
||||
MaxStrokeExtents(const StrokeOptions& aStrokeOptions,
|
||||
const Matrix& aTransform)
|
||||
{
|
||||
double styleExpansionFactor = 0.5f;
|
||||
|
||||
if (aStrokeOptions.mLineCap == CapStyle::SQUARE) {
|
||||
styleExpansionFactor = M_SQRT1_2;
|
||||
}
|
||||
|
||||
if (aStrokeOptions.mLineJoin == JoinStyle::MITER &&
|
||||
styleExpansionFactor < M_SQRT2 * aStrokeOptions.mMiterLimit) {
|
||||
styleExpansionFactor = M_SQRT2 * aStrokeOptions.mMiterLimit;
|
||||
}
|
||||
|
||||
styleExpansionFactor *= aStrokeOptions.mLineWidth;
|
||||
|
||||
double dx = styleExpansionFactor * hypot(aTransform._11, aTransform._21);
|
||||
double dy = styleExpansionFactor * hypot(aTransform._22, aTransform._12);
|
||||
return Margin(dy, dx, dy, dx);
|
||||
}
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
||||
424
gfx/2d/PathHelpers.h
Normal file
424
gfx/2d/PathHelpers.h
Normal file
|
|
@ -0,0 +1,424 @@
|
|||
/* -*- Mode: C++; tab-width: 20; 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_GFX_PATHHELPERS_H_
|
||||
#define MOZILLA_GFX_PATHHELPERS_H_
|
||||
|
||||
#include "2D.h"
|
||||
#include "UserData.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
// Kappa constant for 90-degree angle
|
||||
const Float kKappaFactor = 0.55191497064665766025f;
|
||||
|
||||
// Calculate kappa constant for partial curve. The sign of angle in the
|
||||
// tangent will actually ensure this is negative for a counter clockwise
|
||||
// sweep, so changing signs later isn't needed.
|
||||
inline Float ComputeKappaFactor(Float aAngle)
|
||||
{
|
||||
return (4.0f / 3.0f) * tanf(aAngle / 4.0f);
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws a partial arc <= 90 degrees given exact start and end points.
|
||||
* Assumes that it is continuing from an already specified start point.
|
||||
*/
|
||||
template <typename T>
|
||||
inline void PartialArcToBezier(T* aSink,
|
||||
const Point& aStartOffset, const Point& aEndOffset,
|
||||
const Matrix& aTransform,
|
||||
Float aKappaFactor = kKappaFactor)
|
||||
{
|
||||
Point cp1 =
|
||||
aStartOffset + Point(-aStartOffset.y, aStartOffset.x) * aKappaFactor;
|
||||
|
||||
Point cp2 =
|
||||
aEndOffset + Point(aEndOffset.y, -aEndOffset.x) * aKappaFactor;
|
||||
|
||||
aSink->BezierTo(aTransform.TransformPoint(cp1),
|
||||
aTransform.TransformPoint(cp2),
|
||||
aTransform.TransformPoint(aEndOffset));
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws an acute arc (<= 90 degrees) given exact start and end points.
|
||||
* Specialized version avoiding kappa calculation.
|
||||
*/
|
||||
template <typename T>
|
||||
inline void AcuteArcToBezier(T* aSink,
|
||||
const Point& aOrigin, const Size& aRadius,
|
||||
const Point& aStartPoint, const Point& aEndPoint,
|
||||
Float aKappaFactor = kKappaFactor)
|
||||
{
|
||||
aSink->LineTo(aStartPoint);
|
||||
if (!aRadius.IsEmpty()) {
|
||||
Float kappaX = aKappaFactor * aRadius.width / aRadius.height;
|
||||
Float kappaY = aKappaFactor * aRadius.height / aRadius.width;
|
||||
Point startOffset = aStartPoint - aOrigin;
|
||||
Point endOffset = aEndPoint - aOrigin;
|
||||
aSink->BezierTo(aStartPoint + Point(-startOffset.y * kappaX, startOffset.x * kappaY),
|
||||
aEndPoint + Point(endOffset.y * kappaX, -endOffset.x * kappaY),
|
||||
aEndPoint);
|
||||
} else if (aEndPoint != aStartPoint) {
|
||||
aSink->LineTo(aEndPoint);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws an acute arc (<= 90 degrees) given exact start and end points.
|
||||
*/
|
||||
template <typename T>
|
||||
inline void AcuteArcToBezier(T* aSink,
|
||||
const Point& aOrigin, const Size& aRadius,
|
||||
const Point& aStartPoint, const Point& aEndPoint,
|
||||
Float aStartAngle, Float aEndAngle)
|
||||
{
|
||||
AcuteArcToBezier(aSink, aOrigin, aRadius, aStartPoint, aEndPoint,
|
||||
ComputeKappaFactor(aEndAngle - aStartAngle));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void ArcToBezier(T* aSink, const Point &aOrigin, const Size &aRadius,
|
||||
float aStartAngle, float aEndAngle, bool aAntiClockwise,
|
||||
float aRotation = 0.0f)
|
||||
{
|
||||
Float sweepDirection = aAntiClockwise ? -1.0f : 1.0f;
|
||||
|
||||
// Calculate the total arc we're going to sweep.
|
||||
Float arcSweepLeft = (aEndAngle - aStartAngle) * sweepDirection;
|
||||
|
||||
// Clockwise we always sweep from the smaller to the larger angle, ccw
|
||||
// it's vice versa.
|
||||
if (arcSweepLeft < 0) {
|
||||
// Rerverse sweep is modulo'd into range rather than clamped.
|
||||
arcSweepLeft = Float(2.0f * M_PI) + fmodf(arcSweepLeft, Float(2.0f * M_PI));
|
||||
// Recalculate the start angle to land closer to end angle.
|
||||
aStartAngle = aEndAngle - arcSweepLeft * sweepDirection;
|
||||
} else if (arcSweepLeft > Float(2.0f * M_PI)) {
|
||||
// Sweeping more than 2 * pi is a full circle.
|
||||
arcSweepLeft = Float(2.0f * M_PI);
|
||||
}
|
||||
|
||||
Float currentStartAngle = aStartAngle;
|
||||
Point currentStartOffset(cosf(aStartAngle), sinf(aStartAngle));
|
||||
Matrix transform = Matrix::Scaling(aRadius.width, aRadius.height);
|
||||
if (aRotation != 0.0f) {
|
||||
transform *= Matrix::Rotation(aRotation);
|
||||
}
|
||||
transform.PostTranslate(aOrigin);
|
||||
aSink->LineTo(transform.TransformPoint(currentStartOffset));
|
||||
|
||||
while (arcSweepLeft > 0) {
|
||||
Float currentEndAngle =
|
||||
currentStartAngle + std::min(arcSweepLeft, Float(M_PI / 2.0f)) * sweepDirection;
|
||||
Point currentEndOffset(cosf(currentEndAngle), sinf(currentEndAngle));
|
||||
|
||||
PartialArcToBezier(aSink, currentStartOffset, currentEndOffset, transform,
|
||||
ComputeKappaFactor(currentEndAngle - currentStartAngle));
|
||||
|
||||
// We guarantee here the current point is the start point of the next
|
||||
// curve segment.
|
||||
arcSweepLeft -= Float(M_PI / 2.0f);
|
||||
currentStartAngle = currentEndAngle;
|
||||
currentStartOffset = currentEndOffset;
|
||||
}
|
||||
}
|
||||
|
||||
/* This is basically the ArcToBezier with the parameters for drawing a circle
|
||||
* inlined which vastly simplifies it and avoids a bunch of transcedental function
|
||||
* calls which should make it faster. */
|
||||
template <typename T>
|
||||
void EllipseToBezier(T* aSink, const Point &aOrigin, const Size &aRadius)
|
||||
{
|
||||
Matrix transform(aRadius.width, 0, 0, aRadius.height, aOrigin.x, aOrigin.y);
|
||||
Point currentStartOffset(1, 0);
|
||||
|
||||
aSink->LineTo(transform.TransformPoint(currentStartOffset));
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
// cos(x+pi/2) == -sin(x)
|
||||
// sin(x+pi/2) == cos(x)
|
||||
Point currentEndOffset(-currentStartOffset.y, currentStartOffset.x);
|
||||
|
||||
PartialArcToBezier(aSink, currentStartOffset, currentEndOffset, transform);
|
||||
|
||||
// We guarantee here the current point is the start point of the next
|
||||
// curve segment.
|
||||
currentStartOffset = currentEndOffset;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a path represending a rectangle to the path being built by
|
||||
* aPathBuilder.
|
||||
*
|
||||
* aRect The rectangle to append.
|
||||
* aDrawClockwise If set to true, the path will start at the left of the top
|
||||
* left edge and draw clockwise. If set to false the path will
|
||||
* start at the right of the top left edge and draw counter-
|
||||
* clockwise.
|
||||
*/
|
||||
GFX2D_API void AppendRectToPath(PathBuilder* aPathBuilder,
|
||||
const Rect& aRect,
|
||||
bool aDrawClockwise = true);
|
||||
|
||||
inline already_AddRefed<Path> MakePathForRect(const DrawTarget& aDrawTarget,
|
||||
const Rect& aRect,
|
||||
bool aDrawClockwise = true)
|
||||
{
|
||||
RefPtr<PathBuilder> builder = aDrawTarget.CreatePathBuilder();
|
||||
AppendRectToPath(builder, aRect, aDrawClockwise);
|
||||
return builder->Finish();
|
||||
}
|
||||
|
||||
struct RectCornerRadii {
|
||||
Size radii[RectCorner::Count];
|
||||
|
||||
RectCornerRadii() {}
|
||||
|
||||
explicit RectCornerRadii(Float radius) {
|
||||
for (int i = 0; i < RectCorner::Count; i++) {
|
||||
radii[i].SizeTo(radius, radius);
|
||||
}
|
||||
}
|
||||
|
||||
explicit RectCornerRadii(Float radiusX, Float radiusY) {
|
||||
for (int i = 0; i < RectCorner::Count; i++) {
|
||||
radii[i].SizeTo(radiusX, radiusY);
|
||||
}
|
||||
}
|
||||
|
||||
RectCornerRadii(Float tl, Float tr, Float br, Float bl) {
|
||||
radii[RectCorner::TopLeft].SizeTo(tl, tl);
|
||||
radii[RectCorner::TopRight].SizeTo(tr, tr);
|
||||
radii[RectCorner::BottomRight].SizeTo(br, br);
|
||||
radii[RectCorner::BottomLeft].SizeTo(bl, bl);
|
||||
}
|
||||
|
||||
RectCornerRadii(const Size& tl, const Size& tr,
|
||||
const Size& br, const Size& bl) {
|
||||
radii[RectCorner::TopLeft] = tl;
|
||||
radii[RectCorner::TopRight] = tr;
|
||||
radii[RectCorner::BottomRight] = br;
|
||||
radii[RectCorner::BottomLeft] = bl;
|
||||
}
|
||||
|
||||
const Size& operator[](size_t aCorner) const {
|
||||
return radii[aCorner];
|
||||
}
|
||||
|
||||
Size& operator[](size_t aCorner) {
|
||||
return radii[aCorner];
|
||||
}
|
||||
|
||||
bool operator==(const RectCornerRadii& aOther) const {
|
||||
for (size_t i = 0; i < RectCorner::Count; i++) {
|
||||
if (radii[i] != aOther.radii[i]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Scale(Float aXScale, Float aYScale) {
|
||||
for (int i = 0; i < RectCorner::Count; i++) {
|
||||
radii[i].Scale(aXScale, aYScale);
|
||||
}
|
||||
}
|
||||
|
||||
const Size TopLeft() const { return radii[RectCorner::TopLeft]; }
|
||||
Size& TopLeft() { return radii[RectCorner::TopLeft]; }
|
||||
|
||||
const Size TopRight() const { return radii[RectCorner::TopRight]; }
|
||||
Size& TopRight() { return radii[RectCorner::TopRight]; }
|
||||
|
||||
const Size BottomRight() const { return radii[RectCorner::BottomRight]; }
|
||||
Size& BottomRight() { return radii[RectCorner::BottomRight]; }
|
||||
|
||||
const Size BottomLeft() const { return radii[RectCorner::BottomLeft]; }
|
||||
Size& BottomLeft() { return radii[RectCorner::BottomLeft]; }
|
||||
};
|
||||
|
||||
/**
|
||||
* Appends a path represending a rounded rectangle to the path being built by
|
||||
* aPathBuilder.
|
||||
*
|
||||
* aRect The rectangle to append.
|
||||
* aCornerRadii Contains the radii of the top-left, top-right, bottom-right
|
||||
* and bottom-left corners, in that order.
|
||||
* aDrawClockwise If set to true, the path will start at the left of the top
|
||||
* left edge and draw clockwise. If set to false the path will
|
||||
* start at the right of the top left edge and draw counter-
|
||||
* clockwise.
|
||||
*/
|
||||
GFX2D_API void AppendRoundedRectToPath(PathBuilder* aPathBuilder,
|
||||
const Rect& aRect,
|
||||
const RectCornerRadii& aRadii,
|
||||
bool aDrawClockwise = true);
|
||||
|
||||
inline already_AddRefed<Path> MakePathForRoundedRect(const DrawTarget& aDrawTarget,
|
||||
const Rect& aRect,
|
||||
const RectCornerRadii& aRadii,
|
||||
bool aDrawClockwise = true)
|
||||
{
|
||||
RefPtr<PathBuilder> builder = aDrawTarget.CreatePathBuilder();
|
||||
AppendRoundedRectToPath(builder, aRect, aRadii, aDrawClockwise);
|
||||
return builder->Finish();
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a path represending an ellipse to the path being built by
|
||||
* aPathBuilder.
|
||||
*
|
||||
* The ellipse extends aDimensions.width / 2.0 in the horizontal direction
|
||||
* from aCenter, and aDimensions.height / 2.0 in the vertical direction.
|
||||
*/
|
||||
GFX2D_API void AppendEllipseToPath(PathBuilder* aPathBuilder,
|
||||
const Point& aCenter,
|
||||
const Size& aDimensions);
|
||||
|
||||
inline already_AddRefed<Path> MakePathForEllipse(const DrawTarget& aDrawTarget,
|
||||
const Point& aCenter,
|
||||
const Size& aDimensions)
|
||||
{
|
||||
RefPtr<PathBuilder> builder = aDrawTarget.CreatePathBuilder();
|
||||
AppendEllipseToPath(builder, aCenter, aDimensions);
|
||||
return builder->Finish();
|
||||
}
|
||||
|
||||
/**
|
||||
* If aDrawTarget's transform only contains a translation, and if this line is
|
||||
* a horizontal or vertical line, this function will snap the line's vertices
|
||||
* to align with the device pixel grid so that stroking the line with a one
|
||||
* pixel wide stroke will result in a crisp line that is not antialiased over
|
||||
* two pixels across its width.
|
||||
*
|
||||
* @return Returns true if this function snaps aRect's vertices, else returns
|
||||
* false.
|
||||
*/
|
||||
GFX2D_API bool SnapLineToDevicePixelsForStroking(Point& aP1, Point& aP2,
|
||||
const DrawTarget& aDrawTarget,
|
||||
Float aLineWidth);
|
||||
|
||||
/**
|
||||
* This function paints each edge of aRect separately, snapping the edges using
|
||||
* SnapLineToDevicePixelsForStroking. Stroking the edges as separate paths
|
||||
* helps ensure not only that the stroke spans a single row of device pixels if
|
||||
* possible, but also that the ends of stroke dashes start and end on device
|
||||
* pixels too.
|
||||
*/
|
||||
GFX2D_API void StrokeSnappedEdgesOfRect(const Rect& aRect,
|
||||
DrawTarget& aDrawTarget,
|
||||
const ColorPattern& aColor,
|
||||
const StrokeOptions& aStrokeOptions);
|
||||
|
||||
/**
|
||||
* Return the margin, in device space, by which a stroke can extend beyond the
|
||||
* rendered shape.
|
||||
* @param aStrokeOptions The stroke options that the stroke is drawn with.
|
||||
* @param aTransform The user space to device space transform.
|
||||
* @return The stroke margin.
|
||||
*/
|
||||
GFX2D_API Margin MaxStrokeExtents(const StrokeOptions& aStrokeOptions,
|
||||
const Matrix& aTransform);
|
||||
|
||||
extern UserDataKey sDisablePixelSnapping;
|
||||
|
||||
/**
|
||||
* If aDrawTarget's transform only contains a translation or, if
|
||||
* aAllowScaleOr90DegreeRotate is true, and/or a scale/90 degree rotation, this
|
||||
* function will convert aRect to device space and snap it to device pixels.
|
||||
* This function returns true if aRect is modified, otherwise it returns false.
|
||||
*
|
||||
* Note that the snapping is such that filling the rect using a DrawTarget
|
||||
* which has the identity matrix as its transform will result in crisp edges.
|
||||
* (That is, aRect will have integer values, aligning its edges between pixel
|
||||
* boundaries.) If on the other hand you stroking the rect with an odd valued
|
||||
* stroke width then the edges of the stroke will be antialiased (assuming an
|
||||
* AntialiasMode that does antialiasing).
|
||||
*
|
||||
* Empty snaps are those which result in a rectangle of 0 area. If they are
|
||||
* disallowed, an axis is left unsnapped if the rounding process results in a
|
||||
* length of 0.
|
||||
*/
|
||||
inline bool UserToDevicePixelSnapped(Rect& aRect, const DrawTarget& aDrawTarget,
|
||||
bool aAllowScaleOr90DegreeRotate = false,
|
||||
bool aAllowEmptySnaps = true)
|
||||
{
|
||||
if (aDrawTarget.GetUserData(&sDisablePixelSnapping)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Matrix mat = aDrawTarget.GetTransform();
|
||||
|
||||
const Float epsilon = 0.0000001f;
|
||||
#define WITHIN_E(a,b) (fabs((a)-(b)) < epsilon)
|
||||
if (!aAllowScaleOr90DegreeRotate &&
|
||||
(!WITHIN_E(mat._11, 1.f) || !WITHIN_E(mat._22, 1.f) ||
|
||||
!WITHIN_E(mat._12, 0.f) || !WITHIN_E(mat._21, 0.f))) {
|
||||
// We have non-translation, but only translation is allowed.
|
||||
return false;
|
||||
}
|
||||
#undef WITHIN_E
|
||||
|
||||
Point p1 = mat.TransformPoint(aRect.TopLeft());
|
||||
Point p2 = mat.TransformPoint(aRect.TopRight());
|
||||
Point p3 = mat.TransformPoint(aRect.BottomRight());
|
||||
|
||||
// Check that the rectangle is axis-aligned. For an axis-aligned rectangle,
|
||||
// two opposite corners define the entire rectangle. So check if
|
||||
// the axis-aligned rectangle with opposite corners p1 and p3
|
||||
// define an axis-aligned rectangle whose other corners are p2 and p4.
|
||||
// We actually only need to check one of p2 and p4, since an affine
|
||||
// transform maps parallelograms to parallelograms.
|
||||
if (p2 == Point(p1.x, p3.y) || p2 == Point(p3.x, p1.y)) {
|
||||
Point p1r = p1;
|
||||
Point p3r = p3;
|
||||
p1r.Round();
|
||||
p3r.Round();
|
||||
if (aAllowEmptySnaps || p1r.x != p3r.x) {
|
||||
p1.x = p1r.x;
|
||||
p3.x = p3r.x;
|
||||
}
|
||||
if (aAllowEmptySnaps || p1r.y != p3r.y) {
|
||||
p1.y = p1r.y;
|
||||
p3.y = p3r.y;
|
||||
}
|
||||
|
||||
aRect.MoveTo(Point(std::min(p1.x, p3.x), std::min(p1.y, p3.y)));
|
||||
aRect.SizeTo(Size(std::max(p1.x, p3.x) - aRect.X(),
|
||||
std::max(p1.y, p3.y) - aRect.Y()));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function has the same behavior as UserToDevicePixelSnapped except that
|
||||
* aRect is not transformed to device space.
|
||||
*/
|
||||
inline bool MaybeSnapToDevicePixels(Rect& aRect, const DrawTarget& aDrawTarget,
|
||||
bool aAllowScaleOr90DegreeRotate = false,
|
||||
bool aAllowEmptySnaps = true)
|
||||
{
|
||||
if (UserToDevicePixelSnapped(aRect, aDrawTarget,
|
||||
aAllowScaleOr90DegreeRotate, aAllowEmptySnaps)) {
|
||||
// Since UserToDevicePixelSnapped returned true we know there is no
|
||||
// rotation/skew in 'mat', so we can just use TransformBounds() here.
|
||||
Matrix mat = aDrawTarget.GetTransform();
|
||||
mat.Invert();
|
||||
aRect = mat.TransformBounds(aRect);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* MOZILLA_GFX_PATHHELPERS_H_ */
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue