mirror of
https://github.com/Feodor2/Mypal68.git
synced 2025-06-18 14:55:44 -04:00
68.14.8 - gfx
This commit is contained in:
parent
4efeab7ecb
commit
789a0fa277
@ -95,7 +95,7 @@ struct BasePoint {
|
||||
// "Finite" means not inf and not NaN
|
||||
bool IsFinite() const {
|
||||
using FloatType =
|
||||
std::conditional_t<mozilla::IsSame<T, float>::value, float, double>;
|
||||
std::conditional_t<std::is_same_v<T, float>, float, double>;
|
||||
return (mozilla::IsFinite(FloatType(x)) && mozilla::IsFinite(FloatType(y)));
|
||||
return true;
|
||||
}
|
||||
|
@ -64,7 +64,7 @@ struct BaseRect {
|
||||
// "Finite" means not inf and not NaN
|
||||
bool IsFinite() const {
|
||||
using FloatType =
|
||||
std::conditional_t<mozilla::IsSame<T, float>::value, float, double>;
|
||||
std::conditional_t<std::is_same_v<T, float>, float, double>;
|
||||
return (mozilla::IsFinite(FloatType(x)) &&
|
||||
mozilla::IsFinite(FloatType(y)) &&
|
||||
mozilla::IsFinite(FloatType(width)) &&
|
||||
@ -108,7 +108,7 @@ struct BaseRect {
|
||||
// (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 {
|
||||
[[nodiscard]] Sub Intersect(const Sub& aRect) const {
|
||||
Sub result;
|
||||
result.x = std::max<T>(x, aRect.x);
|
||||
result.y = std::max<T>(y, aRect.y);
|
||||
@ -151,7 +151,7 @@ struct BaseRect {
|
||||
// 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 {
|
||||
[[nodiscard]] Sub Union(const Sub& aRect) const {
|
||||
if (IsEmpty()) {
|
||||
return aRect;
|
||||
} else if (aRect.IsEmpty()) {
|
||||
@ -165,7 +165,7 @@ struct BaseRect {
|
||||
// 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 {
|
||||
[[nodiscard]] Sub UnionEdges(const Sub& aRect) const {
|
||||
Sub result;
|
||||
result.x = std::min(x, aRect.x);
|
||||
result.y = std::min(y, aRect.y);
|
||||
@ -608,7 +608,7 @@ struct BaseRect {
|
||||
* 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 {
|
||||
[[nodiscard]] Point ClampPoint(const Point& aPoint) const {
|
||||
return Point(std::max(x, std::min(XMost(), aPoint.x)),
|
||||
std::max(y, std::min(YMost(), aPoint.y)));
|
||||
}
|
||||
@ -618,7 +618,7 @@ struct BaseRect {
|
||||
* 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 {
|
||||
[[nodiscard]] 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;
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "BaseCoord.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <type_traits>
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
@ -19,7 +20,7 @@ struct IsPixel;
|
||||
|
||||
namespace gfx {
|
||||
|
||||
template <class units>
|
||||
template <class units, class Rep = int32_t>
|
||||
struct IntCoordTyped;
|
||||
template <class units, class F = Float>
|
||||
struct CoordTyped;
|
||||
@ -35,14 +36,14 @@ struct CoordTyped;
|
||||
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 Rep, class primitive>
|
||||
struct CommonType<IntCoordTyped<units, Rep>, primitive> {
|
||||
using type = decltype(Rep() + primitive());
|
||||
};
|
||||
|
||||
template <class units, class F, class primitive>
|
||||
struct CommonType<CoordTyped<units, F>, primitive> {
|
||||
typedef decltype(F() + primitive()) type;
|
||||
using type = decltype(F() + primitive());
|
||||
};
|
||||
|
||||
// This is a base class that provides mixed-type operator overloads between
|
||||
@ -65,7 +66,7 @@ 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; }
|
||||
|
||||
typedef typename CommonType<coord, primitive>::type result_type;
|
||||
using result_type = typename CommonType<coord, primitive>::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; }
|
||||
@ -83,37 +84,36 @@ struct CoordOperatorsHelper<true, coord, primitive> {
|
||||
// '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>
|
||||
template <class units, class Rep>
|
||||
struct IntCoordTyped
|
||||
: public BaseCoord<int32_t, IntCoordTyped<units> >,
|
||||
public CoordOperatorsHelper<true, IntCoordTyped<units>, float>,
|
||||
public CoordOperatorsHelper<true, IntCoordTyped<units>, double> {
|
||||
: public BaseCoord<Rep, IntCoordTyped<units, Rep>>,
|
||||
public units,
|
||||
public CoordOperatorsHelper<true, IntCoordTyped<units, Rep>, float>,
|
||||
public CoordOperatorsHelper<true, IntCoordTyped<units, Rep>, double> {
|
||||
static_assert(IsPixel<units>::value,
|
||||
"'units' must be a coordinate system tag");
|
||||
|
||||
typedef BaseCoord<int32_t, IntCoordTyped<units> > Super;
|
||||
using Super = BaseCoord<Rep, IntCoordTyped<units, Rep>>;
|
||||
|
||||
constexpr IntCoordTyped() : Super() {}
|
||||
constexpr MOZ_IMPLICIT IntCoordTyped(int32_t aValue) : Super(aValue) {}
|
||||
constexpr MOZ_IMPLICIT IntCoordTyped(Rep aValue) : Super(aValue) {}
|
||||
};
|
||||
|
||||
template <class units, class F>
|
||||
struct CoordTyped : public BaseCoord<F, CoordTyped<units, F> >,
|
||||
public CoordOperatorsHelper<!IsSame<F, int32_t>::value,
|
||||
struct CoordTyped : public BaseCoord<F, CoordTyped<units, F>>,
|
||||
public units,
|
||||
public CoordOperatorsHelper<!std::is_same_v<F, int32_t>,
|
||||
CoordTyped<units, F>, int32_t>,
|
||||
public CoordOperatorsHelper<!IsSame<F, uint32_t>::value,
|
||||
public CoordOperatorsHelper<!std::is_same_v<F, uint32_t>,
|
||||
CoordTyped<units, F>, uint32_t>,
|
||||
public CoordOperatorsHelper<!IsSame<F, double>::value,
|
||||
public CoordOperatorsHelper<!std::is_same_v<F, double>,
|
||||
CoordTyped<units, F>, double>,
|
||||
public CoordOperatorsHelper<!IsSame<F, float>::value,
|
||||
public CoordOperatorsHelper<!std::is_same_v<F, float>,
|
||||
CoordTyped<units, F>, float> {
|
||||
static_assert(IsPixel<units>::value,
|
||||
"'units' must be a coordinate system tag");
|
||||
|
||||
typedef BaseCoord<F, CoordTyped<units, F> > Super;
|
||||
using Super = BaseCoord<F, CoordTyped<units, F>>;
|
||||
|
||||
constexpr CoordTyped() : Super() {}
|
||||
constexpr MOZ_IMPLICIT CoordTyped(F aValue) : Super(aValue) {}
|
||||
|
@ -1,97 +0,0 @@
|
||||
/* 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() = default;
|
||||
|
||||
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 = std::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 gfx
|
||||
} // namespace mozilla
|
@ -1,147 +0,0 @@
|
||||
/* 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(std::is_base_of<DrawingCommand, T>::value,
|
||||
"T must derive from DrawingCommand");
|
||||
return mCommands->mStorage.Alloc<T>(std::forward<Args>(aArgs)...);
|
||||
}
|
||||
|
||||
bool HasCommands() const { return !!mCommands; }
|
||||
|
||||
protected:
|
||||
RefPtr<CommandBuffer> mCommands;
|
||||
};
|
||||
|
||||
/// Stores multiple commands to be executed sequencially.
|
||||
class DrawingJob : public Job {
|
||||
public:
|
||||
virtual ~DrawingJob();
|
||||
|
||||
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 final {
|
||||
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 gfx
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
@ -1,248 +0,0 @@
|
||||
/* 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() = default;
|
||||
|
||||
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 gfx
|
||||
} // namespace mozilla
|
@ -1,255 +0,0 @@
|
||||
/* 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);
|
||||
|
||||
virtual ~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);
|
||||
|
||||
virtual ~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() = default;
|
||||
|
||||
void Run();
|
||||
|
||||
MultiThreadedJobQueue* GetJobQueue() { return mQueue; }
|
||||
|
||||
protected:
|
||||
explicit WorkerThread(MultiThreadedJobQueue* aJobQueue);
|
||||
|
||||
virtual void SetName(const char* aName) {}
|
||||
|
||||
MultiThreadedJobQueue* mQueue;
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
@ -1,155 +0,0 @@
|
||||
/* 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"
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
virtual ~WorkerThreadPosix() { pthread_join(mThread, nullptr); }
|
||||
|
||||
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() = default;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
@ -1,135 +0,0 @@
|
||||
/* 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 gfx
|
||||
} // namespace mozilla
|
||||
|
||||
# include "JobScheduler.h"
|
||||
|
||||
# endif
|
||||
#endif
|
@ -1,125 +0,0 @@
|
||||
/* 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"
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
virtual ~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 gfx
|
||||
} // namespace mozilla
|
@ -1,90 +0,0 @@
|
||||
/* 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 gfx
|
||||
} // namespace mozilla
|
||||
|
||||
# endif
|
||||
#endif
|
@ -181,7 +181,7 @@ struct IntRectTyped
|
||||
// Same as Union(), but in the cases where aRect is non-empty, the union is
|
||||
// done while guarding against overflow. If an overflow is detected, Nothing
|
||||
// is returned.
|
||||
MOZ_MUST_USE Maybe<Self> SafeUnion(const Self& aRect) const {
|
||||
[[nodiscard]] Maybe<Self> SafeUnion(const Self& aRect) const {
|
||||
if (this->IsEmpty()) {
|
||||
return aRect.Overflows() ? Nothing() : Some(aRect);
|
||||
} else if (aRect.IsEmpty()) {
|
||||
@ -193,7 +193,7 @@ struct IntRectTyped
|
||||
|
||||
// Same as UnionEdges, but guards against overflow. If an overflow is
|
||||
// detected, Nothing is returned.
|
||||
MOZ_MUST_USE Maybe<Self> SafeUnionEdges(const Self& aRect) const {
|
||||
[[nodiscard]] Maybe<Self> SafeUnionEdges(const Self& aRect) const {
|
||||
if (this->Overflows() || aRect.Overflows()) {
|
||||
return Nothing();
|
||||
}
|
||||
|
@ -84,7 +84,7 @@ struct BaseRectAbsolute {
|
||||
return Sub(aRect.x, aRect.y, aRect.XMost(), aRect.YMost());
|
||||
}
|
||||
|
||||
MOZ_MUST_USE Sub Intersect(const Sub& aOther) const {
|
||||
[[nodiscard]] Sub Intersect(const Sub& aOther) const {
|
||||
Sub result;
|
||||
result.left = std::max<T>(left, aOther.left);
|
||||
result.top = std::max<T>(top, aOther.top);
|
||||
@ -145,7 +145,7 @@ struct BaseRectAbsolute {
|
||||
// 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 {
|
||||
[[nodiscard]] Sub Union(const Sub& aRect) const {
|
||||
if (IsEmpty()) {
|
||||
return aRect;
|
||||
} else if (aRect.IsEmpty()) {
|
||||
@ -159,7 +159,7 @@ struct BaseRectAbsolute {
|
||||
// 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 {
|
||||
[[nodiscard]] Sub UnionEdges(const Sub& aRect) const {
|
||||
Sub result;
|
||||
result.left = std::min(left, aRect.left);
|
||||
result.top = std::min(top, aRect.top);
|
||||
@ -243,7 +243,7 @@ struct BaseRectAbsolute {
|
||||
* 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 {
|
||||
[[nodiscard]] Sub MoveInsideAndClamp(const Sub& aRect) const {
|
||||
T newLeft = std::max(aRect.left, left);
|
||||
T newTop = std::max(aRect.top, top);
|
||||
T width = std::min(aRect.Width(), Width());
|
||||
|
@ -32,9 +32,6 @@ EXPORTS.mozilla.gfx += [
|
||||
'HelpersCairo.h',
|
||||
'InlineTranslator.h',
|
||||
'IterableArena.h',
|
||||
'JobScheduler.h',
|
||||
'JobScheduler_posix.h',
|
||||
'JobScheduler_win32.h',
|
||||
'Logging.h',
|
||||
'LoggingConstants.h',
|
||||
'Matrix.h',
|
||||
@ -83,7 +80,6 @@ elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
|
||||
'DrawTargetD2D1.cpp',
|
||||
'ExtendInputEffectD2D1.cpp',
|
||||
'FilterNodeD2D1.cpp',
|
||||
'JobScheduler_win32.cpp',
|
||||
'NativeFontResourceDWrite.cpp',
|
||||
'NativeFontResourceGDI.cpp',
|
||||
'PathD2D.cpp',
|
||||
@ -94,11 +90,6 @@ elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
|
||||
]
|
||||
DEFINES['WIN32'] = True
|
||||
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'windows':
|
||||
SOURCES += [
|
||||
'JobScheduler_posix.cpp',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('android', 'gtk3'):
|
||||
EXPORTS.mozilla.gfx += [
|
||||
'UnscaledFontFreeType.h',
|
||||
@ -170,7 +161,6 @@ UNIFIED_SOURCES += [
|
||||
'DataSourceSurface.cpp',
|
||||
'DataSurfaceHelpers.cpp',
|
||||
'DrawEventRecorder.cpp',
|
||||
'DrawingJob.cpp',
|
||||
'DrawTarget.cpp',
|
||||
'DrawTargetCairo.cpp',
|
||||
'DrawTargetCapture.cpp',
|
||||
@ -184,7 +174,6 @@ UNIFIED_SOURCES += [
|
||||
'FilterProcessing.cpp',
|
||||
'FilterProcessingScalar.cpp',
|
||||
'ImageScaling.cpp',
|
||||
'JobScheduler.cpp',
|
||||
'Matrix.cpp',
|
||||
'Path.cpp',
|
||||
'PathCairo.cpp',
|
||||
|
@ -406,8 +406,7 @@ void TiledTextureImage::Resize(const gfx::IntSize& aSize) {
|
||||
}
|
||||
|
||||
// Prune any unused tiles at the end of the store.
|
||||
unsigned int length = mImages.Length();
|
||||
for (; i < length; i++) mImages.RemoveLastElement();
|
||||
mImages.RemoveLastElements(mImages.Length() - i);
|
||||
|
||||
// Reset tile-store properties.
|
||||
mRows = rows;
|
||||
|
@ -82,11 +82,11 @@ struct BaseTransactionId {
|
||||
|
||||
bool IsValid() const { return mId != 0; }
|
||||
|
||||
MOZ_MUST_USE BaseTransactionId<T> Next() const {
|
||||
[[nodiscard]] BaseTransactionId<T> Next() const {
|
||||
return BaseTransactionId<T>{mId + 1};
|
||||
}
|
||||
|
||||
MOZ_MUST_USE BaseTransactionId<T> Prev() const {
|
||||
[[nodiscard]] BaseTransactionId<T> Prev() const {
|
||||
return BaseTransactionId<T>{mId - 1};
|
||||
}
|
||||
|
||||
@ -124,7 +124,7 @@ typedef BaseTransactionId<TransactionIdType> TransactionId;
|
||||
struct LayersObserverEpoch {
|
||||
uint64_t mId;
|
||||
|
||||
MOZ_MUST_USE LayersObserverEpoch Next() const {
|
||||
[[nodiscard]] LayersObserverEpoch Next() const {
|
||||
return LayersObserverEpoch{mId + 1};
|
||||
}
|
||||
|
||||
|
@ -87,8 +87,8 @@ template <typename Iterator, typename Node, typename PreAction,
|
||||
static auto ForEachNode(Node aRoot, const PreAction& aPreAction,
|
||||
const PostAction& aPostAction)
|
||||
-> std::enable_if_t<
|
||||
IsSame<decltype(aPreAction(aRoot)), TraversalFlag>::value &&
|
||||
IsSame<decltype(aPostAction(aRoot)), TraversalFlag>::value,
|
||||
std::is_same_v<decltype(aPreAction(aRoot)), TraversalFlag> &&
|
||||
std::is_same_v<decltype(aPostAction(aRoot)), TraversalFlag>,
|
||||
bool> {
|
||||
if (!aRoot) {
|
||||
return false;
|
||||
@ -127,8 +127,8 @@ template <typename Iterator, typename Node, typename PreAction,
|
||||
typename PostAction>
|
||||
static auto ForEachNode(Node aRoot, const PreAction& aPreAction,
|
||||
const PostAction& aPostAction)
|
||||
-> std::enable_if_t<IsSame<decltype(aPreAction(aRoot)), void>::value &&
|
||||
IsSame<decltype(aPostAction(aRoot)), void>::value,
|
||||
-> std::enable_if_t<std::is_same_v<decltype(aPreAction(aRoot)), void> &&
|
||||
std::is_same_v<decltype(aPostAction(aRoot)), void>,
|
||||
void> {
|
||||
if (!aRoot) {
|
||||
return;
|
||||
@ -149,7 +149,7 @@ static auto ForEachNode(Node aRoot, const PreAction& aPreAction,
|
||||
*/
|
||||
template <typename Iterator, typename Node, typename PreAction>
|
||||
auto ForEachNode(Node aRoot, const PreAction& aPreAction) -> std::enable_if_t<
|
||||
IsSame<decltype(aPreAction(aRoot)), TraversalFlag>::value, bool> {
|
||||
std::is_same_v<decltype(aPreAction(aRoot)), TraversalFlag>, bool> {
|
||||
return ForEachNode<Iterator>(
|
||||
aRoot, aPreAction, [](Node aNode) { return TraversalFlag::Continue; });
|
||||
}
|
||||
@ -159,7 +159,7 @@ auto ForEachNode(Node aRoot, const PreAction& aPreAction) -> std::enable_if_t<
|
||||
*/
|
||||
template <typename Iterator, typename Node, typename PreAction>
|
||||
auto ForEachNode(Node aRoot, const PreAction& aPreAction)
|
||||
-> std::enable_if_t<IsSame<decltype(aPreAction(aRoot)), void>::value,
|
||||
-> std::enable_if_t<std::is_same_v<decltype(aPreAction(aRoot)), void>,
|
||||
void> {
|
||||
ForEachNode<Iterator>(aRoot, aPreAction, [](Node aNode) {});
|
||||
}
|
||||
@ -170,7 +170,7 @@ auto ForEachNode(Node aRoot, const PreAction& aPreAction)
|
||||
template <typename Iterator, typename Node, typename PostAction>
|
||||
auto ForEachNodePostOrder(Node aRoot, const PostAction& aPostAction)
|
||||
-> std::enable_if_t<
|
||||
IsSame<decltype(aPostAction(aRoot)), TraversalFlag>::value, bool> {
|
||||
std::is_same_v<decltype(aPostAction(aRoot)), TraversalFlag>, bool> {
|
||||
return ForEachNode<Iterator>(
|
||||
aRoot, [](Node aNode) { return TraversalFlag::Continue; }, aPostAction);
|
||||
}
|
||||
@ -180,7 +180,7 @@ auto ForEachNodePostOrder(Node aRoot, const PostAction& aPostAction)
|
||||
*/
|
||||
template <typename Iterator, typename Node, typename PostAction>
|
||||
auto ForEachNodePostOrder(Node aRoot, const PostAction& aPostAction)
|
||||
-> std::enable_if_t<IsSame<decltype(aPostAction(aRoot)), void>::value,
|
||||
-> std::enable_if_t<std::is_same_v<decltype(aPostAction(aRoot)), void>,
|
||||
void> {
|
||||
ForEachNode<Iterator>(
|
||||
aRoot, [](Node aNode) {}, aPostAction);
|
||||
|
@ -83,7 +83,7 @@ struct APZTestDataToJSConverter {
|
||||
}
|
||||
|
||||
static void ConvertString(const std::string& aFrom, nsString& aOutTo) {
|
||||
aOutTo = NS_ConvertUTF8toUTF16(aFrom.c_str(), aFrom.size());
|
||||
CopyUTF8toUTF16(aFrom, aOutTo);
|
||||
}
|
||||
|
||||
static void ConvertHitResult(const APZTestData::HitResult& aResult,
|
||||
|
@ -135,7 +135,7 @@ class ClientRefLayer : public RefLayer, public ClientLayer {
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual ~ClientRefLayer() { MOZ_COUNT_DTOR(ClientRefLayer); }
|
||||
MOZ_COUNTED_DTOR_OVERRIDE(ClientRefLayer)
|
||||
|
||||
public:
|
||||
Layer* AsLayer() override { return this; }
|
||||
|
@ -247,7 +247,7 @@ class TextureData {
|
||||
|
||||
TextureData() { MOZ_COUNT_CTOR(TextureData); }
|
||||
|
||||
virtual ~TextureData() { MOZ_COUNT_DTOR(TextureData); }
|
||||
MOZ_COUNTED_DTOR_VIRTUAL(TextureData)
|
||||
|
||||
virtual void FillInfo(TextureData::Info& aInfo) const = 0;
|
||||
|
||||
|
@ -324,6 +324,9 @@ CompositorBridgeParent::CompositorBridgeParent(
|
||||
mPendingTransaction{0},
|
||||
mPaused(false),
|
||||
mHaveCompositionRecorder(false),
|
||||
#ifdef MOZ_BUILD_WEBRENDER
|
||||
mIsForcedFirstPaint(false),
|
||||
#endif
|
||||
mUseExternalSurfaceSize(aUseExternalSurfaceSize),
|
||||
mEGLSurfaceSize(aSurfaceSize),
|
||||
mOptions(aOptions),
|
||||
@ -427,7 +430,7 @@ CompositorBridgeParent::~CompositorBridgeParent() {
|
||||
void CompositorBridgeParent::ForceIsFirstPaint() {
|
||||
#ifdef MOZ_BUILD_WEBRENDER
|
||||
if (mWrBridge)
|
||||
mWrBridge->ForceIsFirstPaint();
|
||||
mIsForcedFirstPaint = true;
|
||||
else
|
||||
#endif
|
||||
mCompositionManager->ForceIsFirstPaint();
|
||||
@ -2235,18 +2238,21 @@ void CompositorBridgeParent::NotifyPipelineRendered(
|
||||
if (mWrBridge->PipelineId() == aPipelineId) {
|
||||
mWrBridge->RemoveEpochDataPriorTo(aEpoch);
|
||||
|
||||
if (!mPaused) {
|
||||
TransactionId transactionId = mWrBridge->FlushTransactionIdsForEpoch(
|
||||
aEpoch, aCompositeStartId, aCompositeStart, aRenderStart,
|
||||
aCompositeEnd, uiController);
|
||||
Unused << SendDidComposite(LayersId{0}, transactionId, aCompositeStart,
|
||||
aCompositeEnd);
|
||||
if (mIsForcedFirstPaint) {
|
||||
uiController->NotifyFirstPaint();
|
||||
mIsForcedFirstPaint = false;
|
||||
}
|
||||
|
||||
nsTArray<ImageCompositeNotificationInfo> notifications;
|
||||
mWrBridge->ExtractImageCompositeNotifications(¬ifications);
|
||||
if (!notifications.IsEmpty()) {
|
||||
Unused << ImageBridgeParent::NotifyImageComposites(notifications);
|
||||
}
|
||||
TransactionId transactionId = mWrBridge->FlushTransactionIdsForEpoch(
|
||||
aEpoch, aCompositeStartId, aCompositeStart, aRenderStart, aCompositeEnd,
|
||||
uiController);
|
||||
Unused << SendDidComposite(LayersId{0}, transactionId, aCompositeStart,
|
||||
aCompositeEnd);
|
||||
|
||||
nsTArray<ImageCompositeNotificationInfo> notifications;
|
||||
mWrBridge->ExtractImageCompositeNotifications(¬ifications);
|
||||
if (!notifications.IsEmpty()) {
|
||||
Unused << ImageBridgeParent::NotifyImageComposites(notifications);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -2255,14 +2261,11 @@ void CompositorBridgeParent::NotifyPipelineRendered(
|
||||
if (wrBridge && wrBridge->GetCompositorBridge()) {
|
||||
MOZ_ASSERT(!wrBridge->IsRootWebRenderBridgeParent());
|
||||
wrBridge->RemoveEpochDataPriorTo(aEpoch);
|
||||
if (!mPaused) {
|
||||
TransactionId transactionId = wrBridge->FlushTransactionIdsForEpoch(
|
||||
aEpoch, aCompositeStartId, aCompositeStart, aRenderStart,
|
||||
aCompositeEnd, uiController, aStats, &stats);
|
||||
Unused << wrBridge->GetCompositorBridge()->SendDidComposite(
|
||||
wrBridge->GetLayersId(), transactionId, aCompositeStart,
|
||||
aCompositeEnd);
|
||||
}
|
||||
TransactionId transactionId = wrBridge->FlushTransactionIdsForEpoch(
|
||||
aEpoch, aCompositeStartId, aCompositeStart, aRenderStart, aCompositeEnd,
|
||||
uiController, aStats, &stats);
|
||||
Unused << wrBridge->GetCompositorBridge()->SendDidComposite(
|
||||
wrBridge->GetLayersId(), transactionId, aCompositeStart, aCompositeEnd);
|
||||
}
|
||||
|
||||
if (!stats.IsEmpty()) {
|
||||
|
@ -761,6 +761,7 @@ class CompositorBridgeParent final : public CompositorBridgeParentBase,
|
||||
#ifdef MOZ_BUILD_WEBRENDER
|
||||
RefPtr<AsyncImagePipelineManager> mAsyncImageManager;
|
||||
RefPtr<WebRenderBridgeParent> mWrBridge;
|
||||
bool mIsForcedFirstPaint;
|
||||
#endif
|
||||
widget::CompositorWidget* mWidget;
|
||||
Maybe<TimeStamp> mTestTime;
|
||||
|
@ -31,6 +31,7 @@ struct nsTArray_RelocationStrategy<mozilla::layers::AnimationImageKeyData> {
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
class SourceSurface;
|
||||
class SourceSurfaceSharedData;
|
||||
} // namespace gfx
|
||||
|
||||
|
@ -257,16 +257,6 @@ class WebRenderBridgeParent final : public PWebRenderBridgeParent,
|
||||
|
||||
void RemoveEpochDataPriorTo(const wr::Epoch& aRenderedEpoch);
|
||||
|
||||
/**
|
||||
* This sets the is-first-paint flag to true for the next received
|
||||
* display list. This is intended to be called by the widget code when it
|
||||
* loses its viewport information (or for whatever reason wants to refresh
|
||||
* the viewport information). The message will sent back to the widget code
|
||||
* via UiCompositorControllerParent::NotifyFirstPaint() when the corresponding
|
||||
* transaction is flushed.
|
||||
*/
|
||||
void ForceIsFirstPaint() { mIsFirstPaint = true; }
|
||||
|
||||
void PushDeferredPipelineData(RenderRootDeferredData&& aDeferredData);
|
||||
|
||||
/**
|
||||
|
@ -2,7 +2,7 @@ This is the Sanitiser for OpenType project, from http://code.google.com/p/ots/.
|
||||
|
||||
Our reference repository is https://github.com/khaledhosny/ots/.
|
||||
|
||||
Current revision: 3f26ef1579e2f5bc9627bd852952f42986ccf1af (7.1.9)
|
||||
Current revision: 8bba749d9d5401726a7d7609ab914fdb5e92bfbe (8.0.0)
|
||||
|
||||
Upstream files included: LICENSE, src/, include/, tests/*.cc
|
||||
|
||||
|
@ -12,11 +12,11 @@ diff --git a/gfx/ots/src/glat.cc b/gfx/ots/src/glat.cc
|
||||
|
||||
namespace ots {
|
||||
|
||||
@@ -200,16 +200,17 @@ bool OpenTypeGLAT_v3::Parse(const uint8_
|
||||
if (prevent_decompression) {
|
||||
return DropGraphite("Illegal nested compression");
|
||||
@@ -212,16 +212,17 @@ bool OpenTypeGLAT_v3::Parse(const uint8_
|
||||
return DropGraphite("Decompressed size exceeds 30MB: %gMB",
|
||||
decompressed_size / (1024.0 * 1024.0));
|
||||
}
|
||||
std::vector<uint8_t> decompressed(this->compHead & FULL_SIZE);
|
||||
std::vector<uint8_t> decompressed(decompressed_size);
|
||||
- int ret = LZ4_decompress_safe_partial(
|
||||
+ size_t outputSize = 0;
|
||||
+ bool ret = mozilla::Compression::LZ4::decompressPartial(
|
||||
@ -49,11 +49,11 @@ diff --git a/gfx/ots/src/silf.cc b/gfx/ots/src/silf.cc
|
||||
|
||||
namespace ots {
|
||||
|
||||
@@ -38,16 +38,17 @@ bool OpenTypeSILF::Parse(const uint8_t*
|
||||
if (prevent_decompression) {
|
||||
return DropGraphite("Illegal nested compression");
|
||||
@@ -50,16 +50,17 @@ bool OpenTypeSILF::Parse(const uint8_t*
|
||||
return DropGraphite("Decompressed size exceeds 30MB: %gMB",
|
||||
decompressed_size / (1024.0 * 1024.0));
|
||||
}
|
||||
std::vector<uint8_t> decompressed(this->compHead & FULL_SIZE);
|
||||
std::vector<uint8_t> decompressed(decompressed_size);
|
||||
- int ret = LZ4_decompress_safe_partial(
|
||||
+ size_t outputSize = 0;
|
||||
+ bool ret = mozilla::Compression::LZ4::decompressPartial(
|
||||
|
1433
gfx/ots/src/cff.cc
1433
gfx/ots/src/cff.cc
File diff suppressed because it is too large
Load Diff
@ -11,22 +11,28 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#undef major // glibc defines major!
|
||||
|
||||
namespace ots {
|
||||
|
||||
struct CFFIndex {
|
||||
CFFIndex()
|
||||
: count(0), off_size(0), offset_to_next(0) {}
|
||||
uint16_t count;
|
||||
uint32_t count;
|
||||
uint8_t off_size;
|
||||
std::vector<uint32_t> offsets;
|
||||
uint32_t offset_to_next;
|
||||
};
|
||||
|
||||
typedef std::map<uint32_t, uint16_t> CFFFDSelect;
|
||||
|
||||
class OpenTypeCFF : public Table {
|
||||
public:
|
||||
explicit OpenTypeCFF(Font *font, uint32_t tag)
|
||||
: Table(font, tag, tag),
|
||||
major(0),
|
||||
font_dict_length(0),
|
||||
charstrings_index(NULL),
|
||||
local_subrs(NULL),
|
||||
m_data(NULL),
|
||||
m_length(0) {
|
||||
@ -37,21 +43,46 @@ class OpenTypeCFF : public Table {
|
||||
bool Parse(const uint8_t *data, size_t length);
|
||||
bool Serialize(OTSStream *out);
|
||||
|
||||
// Major version number.
|
||||
uint8_t major;
|
||||
|
||||
// Name INDEX. This name is used in name.cc as a postscript font name.
|
||||
std::string name;
|
||||
|
||||
// The number of fonts the file has.
|
||||
size_t font_dict_length;
|
||||
// A map from glyph # to font #.
|
||||
std::map<uint16_t, uint8_t> fd_select;
|
||||
CFFFDSelect fd_select;
|
||||
|
||||
// A list of char strings.
|
||||
std::vector<CFFIndex *> char_strings_array;
|
||||
CFFIndex* charstrings_index;
|
||||
// A list of Local Subrs associated with FDArrays. Can be empty.
|
||||
std::vector<CFFIndex *> local_subrs_per_font;
|
||||
// A Local Subrs associated with Top DICT. Can be NULL.
|
||||
CFFIndex *local_subrs;
|
||||
|
||||
// CFF2 VariationStore regionIndexCount.
|
||||
std::vector<uint16_t> region_index_count;
|
||||
|
||||
protected:
|
||||
bool ValidateFDSelect(uint16_t num_glyphs);
|
||||
|
||||
private:
|
||||
const uint8_t *m_data;
|
||||
size_t m_length;
|
||||
};
|
||||
|
||||
class OpenTypeCFF2 : public OpenTypeCFF {
|
||||
public:
|
||||
explicit OpenTypeCFF2(Font *font, uint32_t tag)
|
||||
: OpenTypeCFF(font, tag),
|
||||
m_data(NULL),
|
||||
m_length(0) {
|
||||
}
|
||||
|
||||
bool Parse(const uint8_t *data, size_t length);
|
||||
bool Serialize(OTSStream *out);
|
||||
|
||||
private:
|
||||
const uint8_t *m_data;
|
||||
size_t m_length;
|
||||
|
@ -5,7 +5,7 @@
|
||||
// A parser for the Type 2 Charstring Format.
|
||||
// http://www.adobe.com/devnet/font/pdfs/5177.Type2.pdf
|
||||
|
||||
#include "cff_type2_charstring.h"
|
||||
#include "cff_charstring.h"
|
||||
|
||||
#include <climits>
|
||||
#include <cstdio>
|
||||
@ -22,7 +22,6 @@ namespace {
|
||||
// Note #5177.
|
||||
const int32_t kMaxSubrsCount = 65536;
|
||||
const size_t kMaxCharStringLength = 65535;
|
||||
const size_t kMaxArgumentStack = 48;
|
||||
const size_t kMaxNumberOfStemHints = 96;
|
||||
const size_t kMaxSubrNesting = 10;
|
||||
|
||||
@ -30,117 +29,130 @@ const size_t kMaxSubrNesting = 10;
|
||||
// will fail with the dummy value.
|
||||
const int32_t dummy_result = INT_MAX;
|
||||
|
||||
bool ExecuteType2CharString(ots::Font *font,
|
||||
size_t call_depth,
|
||||
const ots::CFFIndex& global_subrs_index,
|
||||
const ots::CFFIndex& local_subrs_index,
|
||||
ots::Buffer *cff_table,
|
||||
ots::Buffer *char_string,
|
||||
std::stack<int32_t> *argument_stack,
|
||||
bool *out_found_endchar,
|
||||
bool *out_found_width,
|
||||
size_t *in_out_num_stems);
|
||||
bool ExecuteCharString(ots::OpenTypeCFF& cff,
|
||||
size_t call_depth,
|
||||
const ots::CFFIndex& global_subrs_index,
|
||||
const ots::CFFIndex& local_subrs_index,
|
||||
ots::Buffer *cff_table,
|
||||
ots::Buffer *char_string,
|
||||
std::stack<int32_t> *argument_stack,
|
||||
bool *out_found_endchar,
|
||||
bool *out_found_width,
|
||||
size_t *in_out_num_stems,
|
||||
bool cff2);
|
||||
|
||||
bool ArgumentStackOverflows(std::stack<int32_t> *argument_stack, bool cff2) {
|
||||
if ((cff2 && argument_stack->size() > ots::kMaxCFF2ArgumentStack) ||
|
||||
(!cff2 && argument_stack->size() > ots::kMaxCFF1ArgumentStack)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef DUMP_T2CHARSTRING
|
||||
// Converts |op| to a string and returns it.
|
||||
const char *Type2CharStringOperatorToString(ots::Type2CharStringOperator op) {
|
||||
const char *CharStringOperatorToString(ots::CharStringOperator op) {
|
||||
switch (op) {
|
||||
case ots::kHStem:
|
||||
return "HStem";
|
||||
return "hstem";
|
||||
case ots::kVStem:
|
||||
return "VStem";
|
||||
return "vstem";
|
||||
case ots::kVMoveTo:
|
||||
return "VMoveTo";
|
||||
return "vmoveto";
|
||||
case ots::kRLineTo:
|
||||
return "RLineTo";
|
||||
return "rlineto";
|
||||
case ots::kHLineTo:
|
||||
return "HLineTo";
|
||||
return "hlineto";
|
||||
case ots::kVLineTo:
|
||||
return "VLineTo";
|
||||
return "vlineto";
|
||||
case ots::kRRCurveTo:
|
||||
return "RRCurveTo";
|
||||
return "rrcurveto";
|
||||
case ots::kCallSubr:
|
||||
return "CallSubr";
|
||||
return "callsubr";
|
||||
case ots::kReturn:
|
||||
return "Return";
|
||||
return "return";
|
||||
case ots::kEndChar:
|
||||
return "EndChar";
|
||||
return "endchar";
|
||||
case ots::kVSIndex:
|
||||
return "vsindex";
|
||||
case ots::kBlend:
|
||||
return "blend";
|
||||
case ots::kHStemHm:
|
||||
return "HStemHm";
|
||||
return "hstemhm";
|
||||
case ots::kHintMask:
|
||||
return "HintMask";
|
||||
return "hintmask";
|
||||
case ots::kCntrMask:
|
||||
return "CntrMask";
|
||||
return "cntrmask";
|
||||
case ots::kRMoveTo:
|
||||
return "RMoveTo";
|
||||
return "rmoveto";
|
||||
case ots::kHMoveTo:
|
||||
return "HMoveTo";
|
||||
return "hmoveto";
|
||||
case ots::kVStemHm:
|
||||
return "VStemHm";
|
||||
return "vstemhm";
|
||||
case ots::kRCurveLine:
|
||||
return "RCurveLine";
|
||||
return "rcurveline";
|
||||
case ots::kRLineCurve:
|
||||
return "RLineCurve";
|
||||
return "rlinecurve";
|
||||
case ots::kVVCurveTo:
|
||||
return "VVCurveTo";
|
||||
case ots::kHHCurveTo:
|
||||
return "HHCurveTo";
|
||||
return "hhcurveto";
|
||||
case ots::kCallGSubr:
|
||||
return "CallGSubr";
|
||||
return "callgsubr";
|
||||
case ots::kVHCurveTo:
|
||||
return "VHCurveTo";
|
||||
return "vhcurveto";
|
||||
case ots::kHVCurveTo:
|
||||
return "HVCurveTo";
|
||||
case ots::kDotSection:
|
||||
return "DotSection";
|
||||
return "dotsection";
|
||||
case ots::kAnd:
|
||||
return "And";
|
||||
return "and";
|
||||
case ots::kOr:
|
||||
return "Or";
|
||||
return "or";
|
||||
case ots::kNot:
|
||||
return "Not";
|
||||
return "not";
|
||||
case ots::kAbs:
|
||||
return "Abs";
|
||||
return "abs";
|
||||
case ots::kAdd:
|
||||
return "Add";
|
||||
return "add";
|
||||
case ots::kSub:
|
||||
return "Sub";
|
||||
return "sub";
|
||||
case ots::kDiv:
|
||||
return "Div";
|
||||
return "div";
|
||||
case ots::kNeg:
|
||||
return "Neg";
|
||||
return "neg";
|
||||
case ots::kEq:
|
||||
return "Eq";
|
||||
return "eq";
|
||||
case ots::kDrop:
|
||||
return "Drop";
|
||||
return "drop";
|
||||
case ots::kPut:
|
||||
return "Put";
|
||||
return "put";
|
||||
case ots::kGet:
|
||||
return "Get";
|
||||
return "get";
|
||||
case ots::kIfElse:
|
||||
return "IfElse";
|
||||
return "ifelse";
|
||||
case ots::kRandom:
|
||||
return "Random";
|
||||
return "random";
|
||||
case ots::kMul:
|
||||
return "Mul";
|
||||
return "mul";
|
||||
case ots::kSqrt:
|
||||
return "Sqrt";
|
||||
return "sqrt";
|
||||
case ots::kDup:
|
||||
return "Dup";
|
||||
return "dup";
|
||||
case ots::kExch:
|
||||
return "Exch";
|
||||
return "exch";
|
||||
case ots::kIndex:
|
||||
return "Index";
|
||||
return "index";
|
||||
case ots::kRoll:
|
||||
return "Roll";
|
||||
return "roll";
|
||||
case ots::kHFlex:
|
||||
return "HFlex";
|
||||
return "hflex";
|
||||
case ots::kFlex:
|
||||
return "Flex";
|
||||
return "flex";
|
||||
case ots::kHFlex1:
|
||||
return "HFlex1";
|
||||
return "hflex1";
|
||||
case ots::kFlex1:
|
||||
return "Flex1";
|
||||
return "flex1";
|
||||
}
|
||||
|
||||
return "UNKNOWN";
|
||||
@ -150,9 +162,9 @@ const char *Type2CharStringOperatorToString(ots::Type2CharStringOperator op) {
|
||||
// Read one or more bytes from the |char_string| buffer and stores the number
|
||||
// read on |out_number|. If the number read is an operator (ex 'vstem'), sets
|
||||
// true on |out_is_operator|. Returns true if the function read a number.
|
||||
bool ReadNextNumberFromType2CharString(ots::Buffer *char_string,
|
||||
int32_t *out_number,
|
||||
bool *out_is_operator) {
|
||||
bool ReadNextNumberFromCharString(ots::Buffer *char_string,
|
||||
int32_t *out_number,
|
||||
bool *out_is_operator) {
|
||||
uint8_t v = 0;
|
||||
if (!char_string->ReadU8(&v)) {
|
||||
return OTS_FAILURE();
|
||||
@ -174,7 +186,7 @@ bool ReadNextNumberFromType2CharString(ots::Buffer *char_string,
|
||||
*out_is_operator = true;
|
||||
} else if (v <= 27) {
|
||||
// Special handling for v==19 and v==20 are implemented in
|
||||
// ExecuteType2CharStringOperator().
|
||||
// ExecuteCharStringOperator().
|
||||
*out_number = v;
|
||||
*out_is_operator = true;
|
||||
} else if (v == 28) {
|
||||
@ -221,23 +233,63 @@ bool ReadNextNumberFromType2CharString(ots::Buffer *char_string,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ValidCFF2Operator(int32_t op) {
|
||||
switch (op) {
|
||||
case ots::kReturn:
|
||||
case ots::kEndChar:
|
||||
case ots::kAbs:
|
||||
case ots::kAdd:
|
||||
case ots::kSub:
|
||||
case ots::kDiv:
|
||||
case ots::kNeg:
|
||||
case ots::kRandom:
|
||||
case ots::kMul:
|
||||
case ots::kSqrt:
|
||||
case ots::kDrop:
|
||||
case ots::kExch:
|
||||
case ots::kIndex:
|
||||
case ots::kRoll:
|
||||
case ots::kDup:
|
||||
case ots::kPut:
|
||||
case ots::kGet:
|
||||
case ots::kDotSection:
|
||||
case ots::kAnd:
|
||||
case ots::kOr:
|
||||
case ots::kNot:
|
||||
case ots::kEq:
|
||||
case ots::kIfElse:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Executes |op| and updates |argument_stack|. Returns true if the execution
|
||||
// succeeds. If the |op| is kCallSubr or kCallGSubr, the function recursively
|
||||
// calls ExecuteType2CharString() function. The arguments other than |op| and
|
||||
// calls ExecuteCharString() function. The arguments other than |op| and
|
||||
// |argument_stack| are passed for that reason.
|
||||
bool ExecuteType2CharStringOperator(ots::Font *font,
|
||||
int32_t op,
|
||||
size_t call_depth,
|
||||
const ots::CFFIndex& global_subrs_index,
|
||||
const ots::CFFIndex& local_subrs_index,
|
||||
ots::Buffer *cff_table,
|
||||
ots::Buffer *char_string,
|
||||
std::stack<int32_t> *argument_stack,
|
||||
bool *out_found_endchar,
|
||||
bool *in_out_found_width,
|
||||
size_t *in_out_num_stems) {
|
||||
bool ExecuteCharStringOperator(ots::OpenTypeCFF& cff,
|
||||
int32_t op,
|
||||
size_t call_depth,
|
||||
const ots::CFFIndex& global_subrs_index,
|
||||
const ots::CFFIndex& local_subrs_index,
|
||||
ots::Buffer *cff_table,
|
||||
ots::Buffer *char_string,
|
||||
std::stack<int32_t> *argument_stack,
|
||||
bool *out_found_endchar,
|
||||
bool *in_out_found_width,
|
||||
size_t *in_out_num_stems,
|
||||
bool *in_out_have_blend,
|
||||
bool *in_out_have_visindex,
|
||||
int32_t *in_out_vsindex,
|
||||
bool cff2) {
|
||||
ots::Font* font = cff.GetFont();
|
||||
const size_t stack_size = argument_stack->size();
|
||||
|
||||
if (cff2 && !ValidCFF2Operator(op)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
switch (op) {
|
||||
case ots::kCallSubr:
|
||||
case ots::kCallGSubr: {
|
||||
@ -290,16 +342,17 @@ bool ExecuteType2CharStringOperator(ots::Font *font,
|
||||
}
|
||||
ots::Buffer char_string_to_jump(cff_table->buffer() + offset, length);
|
||||
|
||||
return ExecuteType2CharString(font,
|
||||
call_depth + 1,
|
||||
global_subrs_index,
|
||||
local_subrs_index,
|
||||
cff_table,
|
||||
&char_string_to_jump,
|
||||
argument_stack,
|
||||
out_found_endchar,
|
||||
in_out_found_width,
|
||||
in_out_num_stems);
|
||||
return ExecuteCharString(cff,
|
||||
call_depth + 1,
|
||||
global_subrs_index,
|
||||
local_subrs_index,
|
||||
cff_table,
|
||||
&char_string_to_jump,
|
||||
argument_stack,
|
||||
out_found_endchar,
|
||||
in_out_found_width,
|
||||
in_out_num_stems,
|
||||
cff2);
|
||||
}
|
||||
|
||||
case ots::kReturn:
|
||||
@ -310,6 +363,51 @@ bool ExecuteType2CharStringOperator(ots::Font *font,
|
||||
*in_out_found_width = true; // just in case.
|
||||
return true;
|
||||
|
||||
case ots::kVSIndex: {
|
||||
if (!cff2) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (stack_size != 1) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (*in_out_have_blend || *in_out_have_visindex) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (argument_stack->top() >= cff.region_index_count.size()) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
*in_out_have_visindex = true;
|
||||
*in_out_vsindex = argument_stack->top();
|
||||
while (!argument_stack->empty())
|
||||
argument_stack->pop();
|
||||
return true;
|
||||
}
|
||||
|
||||
case ots::kBlend: {
|
||||
if (!cff2) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (stack_size < 1) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (*in_out_vsindex >= cff.region_index_count.size()) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
uint16_t k = cff.region_index_count.at(*in_out_vsindex);
|
||||
uint16_t n = argument_stack->top();
|
||||
if (stack_size < n * (k + 1) + 1) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
// Keep the 1st n operands on the stack for the next operator to use and
|
||||
// pop the rest. There can be multiple consecutive blend operator, so this
|
||||
// makes sure the operands of all of them are kept on the stack.
|
||||
while (argument_stack->size() > stack_size - ((n * k) + 1))
|
||||
argument_stack->pop();
|
||||
*in_out_have_blend = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
case ots::kHStem:
|
||||
case ots::kVStem:
|
||||
case ots::kHStemHm:
|
||||
@ -649,7 +747,7 @@ bool ExecuteType2CharStringOperator(ots::Font *font,
|
||||
argument_stack->pop();
|
||||
argument_stack->push(dummy_result);
|
||||
argument_stack->push(dummy_result);
|
||||
if (argument_stack->size() > kMaxArgumentStack) {
|
||||
if (ArgumentStackOverflows(argument_stack, cff2)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
// TODO(yusukes): Implement this. We should push a real value for all
|
||||
@ -729,26 +827,29 @@ bool ExecuteType2CharStringOperator(ots::Font *font,
|
||||
// in_out_found_width: true is set if |char_string| contains 'width' byte (which
|
||||
// is 0 or 1 byte.)
|
||||
// in_out_num_stems: total number of hstems and vstems processed so far.
|
||||
bool ExecuteType2CharString(ots::Font *font,
|
||||
size_t call_depth,
|
||||
const ots::CFFIndex& global_subrs_index,
|
||||
const ots::CFFIndex& local_subrs_index,
|
||||
ots::Buffer *cff_table,
|
||||
ots::Buffer *char_string,
|
||||
std::stack<int32_t> *argument_stack,
|
||||
bool *out_found_endchar,
|
||||
bool *in_out_found_width,
|
||||
size_t *in_out_num_stems) {
|
||||
bool ExecuteCharString(ots::OpenTypeCFF& cff,
|
||||
size_t call_depth,
|
||||
const ots::CFFIndex& global_subrs_index,
|
||||
const ots::CFFIndex& local_subrs_index,
|
||||
ots::Buffer *cff_table,
|
||||
ots::Buffer *char_string,
|
||||
std::stack<int32_t> *argument_stack,
|
||||
bool *out_found_endchar,
|
||||
bool *in_out_found_width,
|
||||
size_t *in_out_num_stems,
|
||||
bool cff2) {
|
||||
if (call_depth > kMaxSubrNesting) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
*out_found_endchar = false;
|
||||
|
||||
bool in_out_have_blend = false, in_out_have_visindex = false;
|
||||
int32_t in_out_vsindex = 0;
|
||||
const size_t length = char_string->length();
|
||||
while (char_string->offset() < length) {
|
||||
int32_t operator_or_operand = 0;
|
||||
bool is_operator = false;
|
||||
if (!ReadNextNumberFromType2CharString(char_string,
|
||||
if (!ReadNextNumberFromCharString(char_string,
|
||||
&operator_or_operand,
|
||||
&is_operator)) {
|
||||
return OTS_FAILURE();
|
||||
@ -761,35 +862,39 @@ bool ExecuteType2CharString(ots::Font *font,
|
||||
*/
|
||||
|
||||
if (!is_operator) {
|
||||
std::fprintf(stderr, "#%d# ", operator_or_operand);
|
||||
std::fprintf(stderr, "%d ", operator_or_operand);
|
||||
} else {
|
||||
std::fprintf(stderr, "#%s#\n",
|
||||
Type2CharStringOperatorToString(
|
||||
ots::Type2CharStringOperator(operator_or_operand))
|
||||
std::fprintf(stderr, "%s\n",
|
||||
CharStringOperatorToString(
|
||||
ots::CharStringOperator(operator_or_operand))
|
||||
);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!is_operator) {
|
||||
argument_stack->push(operator_or_operand);
|
||||
if (argument_stack->size() > kMaxArgumentStack) {
|
||||
if (ArgumentStackOverflows(argument_stack, cff2)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// An operator is found. Execute it.
|
||||
if (!ExecuteType2CharStringOperator(font,
|
||||
operator_or_operand,
|
||||
call_depth,
|
||||
global_subrs_index,
|
||||
local_subrs_index,
|
||||
cff_table,
|
||||
char_string,
|
||||
argument_stack,
|
||||
out_found_endchar,
|
||||
in_out_found_width,
|
||||
in_out_num_stems)) {
|
||||
if (!ExecuteCharStringOperator(cff,
|
||||
operator_or_operand,
|
||||
call_depth,
|
||||
global_subrs_index,
|
||||
local_subrs_index,
|
||||
cff_table,
|
||||
char_string,
|
||||
argument_stack,
|
||||
out_found_endchar,
|
||||
in_out_found_width,
|
||||
in_out_num_stems,
|
||||
&in_out_have_blend,
|
||||
&in_out_have_visindex,
|
||||
&in_out_vsindex,
|
||||
cff2)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (*out_found_endchar) {
|
||||
@ -801,37 +906,39 @@ bool ExecuteType2CharString(ots::Font *font,
|
||||
}
|
||||
|
||||
// No endchar operator is found.
|
||||
if (cff2)
|
||||
return true;
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
// Selects a set of subroutings for |glyph_index| from |cff| and sets it on
|
||||
// |out_local_subrs_to_use|. Returns true on success.
|
||||
bool SelectLocalSubr(const std::map<uint16_t, uint8_t> &fd_select,
|
||||
const std::vector<ots::CFFIndex *> &local_subrs_per_font,
|
||||
const ots::CFFIndex *local_subrs,
|
||||
bool SelectLocalSubr(const ots::OpenTypeCFF& cff,
|
||||
uint16_t glyph_index, // 0-origin
|
||||
const ots::CFFIndex **out_local_subrs_to_use) {
|
||||
bool cff2 = (cff.major == 2);
|
||||
*out_local_subrs_to_use = NULL;
|
||||
|
||||
// First, find local subrs from |local_subrs_per_font|.
|
||||
if ((fd_select.size() > 0) &&
|
||||
(!local_subrs_per_font.empty())) {
|
||||
if ((cff.fd_select.size() > 0) &&
|
||||
(!cff.local_subrs_per_font.empty())) {
|
||||
// Look up FDArray index for the glyph.
|
||||
std::map<uint16_t, uint8_t>::const_iterator iter =
|
||||
fd_select.find(glyph_index);
|
||||
if (iter == fd_select.end()) {
|
||||
const auto& iter = cff.fd_select.find(glyph_index);
|
||||
if (iter == cff.fd_select.end()) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
const uint8_t fd_index = iter->second;
|
||||
if (fd_index >= local_subrs_per_font.size()) {
|
||||
const auto fd_index = iter->second;
|
||||
if (fd_index >= cff.local_subrs_per_font.size()) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
*out_local_subrs_to_use = local_subrs_per_font.at(fd_index);
|
||||
} else if (local_subrs) {
|
||||
*out_local_subrs_to_use = cff.local_subrs_per_font.at(fd_index);
|
||||
} else if (cff.local_subrs) {
|
||||
// Second, try to use |local_subrs|. Most Latin fonts don't have FDSelect
|
||||
// entries. If The font has a local subrs index associated with the Top
|
||||
// DICT (not FDArrays), use it.
|
||||
*out_local_subrs_to_use = local_subrs;
|
||||
*out_local_subrs_to_use = cff.local_subrs;
|
||||
} else if (cff2 && cff.local_subrs_per_font.size() == 1) {
|
||||
*out_local_subrs_to_use = cff.local_subrs_per_font.at(0);
|
||||
} else {
|
||||
// Just return NULL.
|
||||
*out_local_subrs_to_use = NULL;
|
||||
@ -844,18 +951,16 @@ bool SelectLocalSubr(const std::map<uint16_t, uint8_t> &fd_select,
|
||||
|
||||
namespace ots {
|
||||
|
||||
bool ValidateType2CharStringIndex(
|
||||
ots::Font *font,
|
||||
const CFFIndex& char_strings_index,
|
||||
bool ValidateCFFCharStrings(
|
||||
ots::OpenTypeCFF& cff,
|
||||
const CFFIndex& global_subrs_index,
|
||||
const std::map<uint16_t, uint8_t> &fd_select,
|
||||
const std::vector<CFFIndex *> &local_subrs_per_font,
|
||||
const CFFIndex *local_subrs,
|
||||
Buffer* cff_table) {
|
||||
const CFFIndex& char_strings_index = *(cff.charstrings_index);
|
||||
if (char_strings_index.offsets.size() == 0) {
|
||||
return OTS_FAILURE(); // no charstring.
|
||||
}
|
||||
|
||||
bool cff2 = (cff.major == 2);
|
||||
// For each glyph, validate the corresponding charstring.
|
||||
for (unsigned i = 1; i < char_strings_index.offsets.size(); ++i) {
|
||||
// Prepare a Buffer object, |char_string|, which contains the charstring
|
||||
@ -875,9 +980,7 @@ bool ValidateType2CharStringIndex(
|
||||
// Get a local subrs for the glyph.
|
||||
const unsigned glyph_index = i - 1; // index in the map is 0-origin.
|
||||
const CFFIndex *local_subrs_to_use = NULL;
|
||||
if (!SelectLocalSubr(fd_select,
|
||||
local_subrs_per_font,
|
||||
local_subrs,
|
||||
if (!SelectLocalSubr(cff,
|
||||
glyph_index,
|
||||
&local_subrs_to_use)) {
|
||||
return OTS_FAILURE();
|
||||
@ -891,16 +994,19 @@ bool ValidateType2CharStringIndex(
|
||||
// Check a charstring for the |i|-th glyph.
|
||||
std::stack<int32_t> argument_stack;
|
||||
bool found_endchar = false;
|
||||
bool found_width = false;
|
||||
// CFF2 CharString has no value for width, so we start with true here to
|
||||
// error out if width is found.
|
||||
bool found_width = cff2;
|
||||
size_t num_stems = 0;
|
||||
if (!ExecuteType2CharString(font,
|
||||
0 /* initial call_depth is zero */,
|
||||
global_subrs_index, *local_subrs_to_use,
|
||||
cff_table, &char_string, &argument_stack,
|
||||
&found_endchar, &found_width, &num_stems)) {
|
||||
if (!ExecuteCharString(cff,
|
||||
0 /* initial call_depth is zero */,
|
||||
global_subrs_index, *local_subrs_to_use,
|
||||
cff_table, &char_string, &argument_stack,
|
||||
&found_endchar, &found_width, &num_stems,
|
||||
cff2)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (!found_endchar) {
|
||||
if (!cff2 && !found_endchar) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
}
|
@ -13,6 +13,9 @@
|
||||
|
||||
namespace ots {
|
||||
|
||||
const size_t kMaxCFF1ArgumentStack = 48;
|
||||
const size_t kMaxCFF2ArgumentStack = 513;
|
||||
|
||||
// Validates all charstrings in |char_strings_index|. Charstring is a small
|
||||
// language for font hinting defined in Adobe Technical Note #5177.
|
||||
// http://www.adobe.com/devnet/font/pdfs/5177.Type2.pdf
|
||||
@ -34,17 +37,14 @@ namespace ots {
|
||||
// local_subrs: A Local Subrs associated with Top DICT. Can be NULL.
|
||||
// cff_table: A buffer which contains actual byte code of charstring, global
|
||||
// subroutines and local subroutines.
|
||||
bool ValidateType2CharStringIndex(
|
||||
Font *font,
|
||||
const CFFIndex &char_strings_index,
|
||||
bool ValidateCFFCharStrings(
|
||||
OpenTypeCFF& cff,
|
||||
const CFFIndex &global_subrs_index,
|
||||
const std::map<uint16_t, uint8_t> &fd_select,
|
||||
const std::vector<CFFIndex *> &local_subrs_per_font,
|
||||
const CFFIndex *local_subrs,
|
||||
Buffer *cff_table);
|
||||
|
||||
// The list of Operators. See Appendix. A in Adobe Technical Note #5177.
|
||||
enum Type2CharStringOperator {
|
||||
// and https://docs.microsoft.com/en-us/typography/opentype/spec/cff2charstr
|
||||
enum CharStringOperator {
|
||||
kHStem = 1,
|
||||
kVStem = 3,
|
||||
kVMoveTo = 4,
|
||||
@ -55,6 +55,8 @@ enum Type2CharStringOperator {
|
||||
kCallSubr = 10,
|
||||
kReturn = 11,
|
||||
kEndChar = 14,
|
||||
kVSIndex = 15,
|
||||
kBlend = 16,
|
||||
kHStemHm = 18,
|
||||
kHintMask = 19,
|
||||
kCntrMask = 20,
|
@ -11,10 +11,7 @@
|
||||
#include "gsub.h"
|
||||
#include "layout.h"
|
||||
#include "maxp.h"
|
||||
|
||||
#ifdef OTS_VARIATIONS
|
||||
#include "variations.h"
|
||||
#endif
|
||||
|
||||
// GDEF - The Glyph Definition Table
|
||||
// http://www.microsoft.com/typography/otspec/gdef.htm
|
||||
@ -345,12 +342,10 @@ bool OpenTypeGDEF::Parse(const uint8_t *data, size_t length) {
|
||||
item_var_store_offset < gdef_header_end) {
|
||||
return Error("invalid offset to item variation store");
|
||||
}
|
||||
#ifdef OTS_VARIATIONS
|
||||
if (!ParseItemVariationStore(GetFont(), data + item_var_store_offset,
|
||||
length - item_var_store_offset)) {
|
||||
return Error("Invalid item variation store");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
this->m_data = data;
|
||||
|
@ -200,7 +200,19 @@ bool OpenTypeGLAT_v3::Parse(const uint8_t* data, size_t length,
|
||||
if (prevent_decompression) {
|
||||
return DropGraphite("Illegal nested compression");
|
||||
}
|
||||
std::vector<uint8_t> decompressed(this->compHead & FULL_SIZE);
|
||||
size_t decompressed_size = this->compHead & FULL_SIZE;
|
||||
if (decompressed_size < length) {
|
||||
return DropGraphite("Decompressed size is less than compressed size");
|
||||
}
|
||||
if (decompressed_size == 0) {
|
||||
return DropGraphite("Decompressed size is set to 0");
|
||||
}
|
||||
// decompressed table must be <= 30MB
|
||||
if (decompressed_size > 30 * 1024 * 1024) {
|
||||
return DropGraphite("Decompressed size exceeds 30MB: %gMB",
|
||||
decompressed_size / (1024.0 * 1024.0));
|
||||
}
|
||||
std::vector<uint8_t> decompressed(decompressed_size);
|
||||
size_t outputSize = 0;
|
||||
bool ret = mozilla::Compression::LZ4::decompressPartial(
|
||||
reinterpret_cast<const char*>(data + table.offset()),
|
||||
|
@ -6,8 +6,6 @@
|
||||
|
||||
#include "variations.h"
|
||||
|
||||
#define TABLE_NAME "HVAR"
|
||||
|
||||
namespace ots {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
@ -85,5 +83,3 @@ bool OpenTypeHVAR::Serialize(OTSStream* out) {
|
||||
}
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#undef TABLE_NAME
|
||||
|
@ -7,9 +7,7 @@
|
||||
#include <limits>
|
||||
#include <vector>
|
||||
|
||||
#ifdef OTS_VARIATIONS
|
||||
#include "fvar.h"
|
||||
#endif
|
||||
#include "gdef.h"
|
||||
|
||||
// OpenType Layout Common Table Formats
|
||||
@ -1530,11 +1528,9 @@ bool ParseConditionTable(const Font *font,
|
||||
return OTS_FAILURE_MSG("Failed to read condition table (format 1)");
|
||||
}
|
||||
|
||||
#ifdef OTS_VARIATIONS // we can't check this if we're not parsing variation tables
|
||||
if (axis_index >= axis_count) {
|
||||
return OTS_FAILURE_MSG("Axis index out of range in condition");
|
||||
}
|
||||
#endif
|
||||
|
||||
// Check min/max values are within range -1.0 .. 1.0 and properly ordered
|
||||
if (filter_range_min_value < -0x4000 || // -1.0 in F2DOT14 format
|
||||
@ -1629,15 +1625,11 @@ bool ParseFeatureVariationsTable(const Font *font,
|
||||
return OTS_FAILURE_MSG("Failed to read feature variations table header");
|
||||
}
|
||||
|
||||
#ifdef OTS_VARIATIONS
|
||||
OpenTypeFVAR* fvar = static_cast<OpenTypeFVAR*>(font->GetTypedTable(OTS_TAG_FVAR));
|
||||
if (!fvar) {
|
||||
return OTS_FAILURE_MSG("Not a variation font");
|
||||
}
|
||||
const uint16_t axis_count = fvar->AxisCount();
|
||||
#else
|
||||
const uint16_t axis_count = 0;
|
||||
#endif
|
||||
|
||||
const size_t kEndOfFeatureVariationRecords =
|
||||
2 * sizeof(uint16_t) + sizeof(uint32_t) +
|
||||
|
@ -7,15 +7,10 @@ EXPORTS += [
|
||||
'../include/ots-memory-stream.h',
|
||||
]
|
||||
|
||||
SOURCES += [
|
||||
# needs to be separate because gpos.cc also defines kMaxClassDefValue
|
||||
'gdef.cc',
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'avar.cc',
|
||||
'cff.cc',
|
||||
'cff_type2_charstring.cc',
|
||||
'cff_charstring.cc',
|
||||
'cmap.cc',
|
||||
'cvar.cc',
|
||||
'cvt.cc',
|
||||
@ -23,6 +18,7 @@ UNIFIED_SOURCES += [
|
||||
'fpgm.cc',
|
||||
'fvar.cc',
|
||||
'gasp.cc',
|
||||
'gdef.cc',
|
||||
'glat.cc',
|
||||
'gloc.cc',
|
||||
'glyf.cc',
|
||||
|
@ -6,8 +6,6 @@
|
||||
|
||||
#include "variations.h"
|
||||
|
||||
#define TABLE_NAME "MVAR"
|
||||
|
||||
namespace ots {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
@ -105,5 +103,3 @@ bool OpenTypeMVAR::Serialize(OTSStream* out) {
|
||||
}
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#undef TABLE_NAME
|
||||
|
@ -211,17 +211,16 @@ bool OpenTypeNAME::Parse(const uint8_t* data, size_t length) {
|
||||
// if not, we'll add our fixed versions here
|
||||
bool mac_name[kStdNameCount] = { 0 };
|
||||
bool win_name[kStdNameCount] = { 0 };
|
||||
for (std::vector<NameRecord>::iterator name_iter = this->names.begin();
|
||||
name_iter != this->names.end(); ++name_iter) {
|
||||
const uint16_t id = name_iter->name_id;
|
||||
for (const auto& name : this->names) {
|
||||
const uint16_t id = name.name_id;
|
||||
if (id >= kStdNameCount || kStdNames[id] == NULL) {
|
||||
continue;
|
||||
}
|
||||
if (name_iter->platform_id == 1) {
|
||||
if (name.platform_id == 1) {
|
||||
mac_name[id] = true;
|
||||
continue;
|
||||
}
|
||||
if (name_iter->platform_id == 3) {
|
||||
if (name.platform_id == 3) {
|
||||
win_name[id] = true;
|
||||
continue;
|
||||
}
|
||||
@ -274,9 +273,7 @@ bool OpenTypeNAME::Serialize(OTSStream* out) {
|
||||
}
|
||||
|
||||
std::string string_data;
|
||||
for (std::vector<NameRecord>::const_iterator name_iter = this->names.begin();
|
||||
name_iter != this->names.end(); ++name_iter) {
|
||||
const NameRecord& rec = *name_iter;
|
||||
for (const auto& rec : this->names) {
|
||||
if (string_data.size() + rec.text.size() >
|
||||
std::numeric_limits<uint16_t>::max() ||
|
||||
!out->WriteU16(rec.platform_id) ||
|
||||
@ -294,16 +291,14 @@ bool OpenTypeNAME::Serialize(OTSStream* out) {
|
||||
if (!out->WriteU16(lang_tag_count)) {
|
||||
return Error("Faile to write langTagCount");
|
||||
}
|
||||
for (std::vector<std::string>::const_iterator tag_iter =
|
||||
this->lang_tags.begin();
|
||||
tag_iter != this->lang_tags.end(); ++tag_iter) {
|
||||
if (string_data.size() + tag_iter->size() >
|
||||
for (const auto& tag : this->lang_tags) {
|
||||
if (string_data.size() + tag.size() >
|
||||
std::numeric_limits<uint16_t>::max() ||
|
||||
!out->WriteU16(static_cast<uint16_t>(tag_iter->size())) ||
|
||||
!out->WriteU16(static_cast<uint16_t>(tag.size())) ||
|
||||
!out->WriteU16(static_cast<uint16_t>(string_data.size()))) {
|
||||
return Error("Failed to write langTagRecord");
|
||||
}
|
||||
string_data.append(*tag_iter);
|
||||
string_data.append(tag);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -19,33 +19,41 @@
|
||||
// The OpenType Font File
|
||||
// http://www.microsoft.com/typography/otspec/otff.htm
|
||||
|
||||
#include "avar.h"
|
||||
#include "cff.h"
|
||||
#include "cmap.h"
|
||||
#include "cvar.h"
|
||||
#include "cvt.h"
|
||||
#include "fpgm.h"
|
||||
#include "fvar.h"
|
||||
#include "gasp.h"
|
||||
#include "gdef.h"
|
||||
#include "glyf.h"
|
||||
#include "gpos.h"
|
||||
#include "gsub.h"
|
||||
#include "gvar.h"
|
||||
#include "hdmx.h"
|
||||
#include "head.h"
|
||||
#include "hhea.h"
|
||||
#include "hmtx.h"
|
||||
#include "hvar.h"
|
||||
#include "kern.h"
|
||||
#include "loca.h"
|
||||
#include "ltsh.h"
|
||||
#include "math_.h"
|
||||
#include "maxp.h"
|
||||
#include "mvar.h"
|
||||
#include "name.h"
|
||||
#include "os2.h"
|
||||
#include "ots.h"
|
||||
#include "post.h"
|
||||
#include "prep.h"
|
||||
#include "stat.h"
|
||||
#include "vdmx.h"
|
||||
#include "vhea.h"
|
||||
#include "vmtx.h"
|
||||
#include "vorg.h"
|
||||
#include "vvar.h"
|
||||
|
||||
// Graphite tables
|
||||
#ifdef OTS_GRAPHITE
|
||||
@ -57,25 +65,13 @@
|
||||
#include "sill.h"
|
||||
#endif
|
||||
|
||||
#ifdef OTS_VARIATIONS
|
||||
#include "avar.h"
|
||||
#include "cvar.h"
|
||||
#include "fvar.h"
|
||||
#include "gvar.h"
|
||||
#include "hvar.h"
|
||||
#include "mvar.h"
|
||||
#include "stat.h"
|
||||
#include "vvar.h"
|
||||
#endif
|
||||
|
||||
namespace ots {
|
||||
|
||||
struct Arena {
|
||||
public:
|
||||
~Arena() {
|
||||
for (std::vector<uint8_t*>::iterator
|
||||
i = hunks_.begin(); i != hunks_.end(); ++i) {
|
||||
delete[] *i;
|
||||
for (auto& hunk : hunks_) {
|
||||
delete[] hunk;
|
||||
}
|
||||
}
|
||||
|
||||
@ -139,7 +135,6 @@ const struct {
|
||||
{ OTS_TAG_KERN, false },
|
||||
// We need to parse fvar table before other tables that may need to know
|
||||
// the number of variation axes (if any)
|
||||
#ifdef OTS_VARIATIONS
|
||||
{ OTS_TAG_FVAR, false },
|
||||
{ OTS_TAG_AVAR, false },
|
||||
{ OTS_TAG_CVAR, false },
|
||||
@ -148,7 +143,7 @@ const struct {
|
||||
{ OTS_TAG_MVAR, false },
|
||||
{ OTS_TAG_STAT, false },
|
||||
{ OTS_TAG_VVAR, false },
|
||||
#endif
|
||||
{ OTS_TAG_CFF2, false },
|
||||
// We need to parse GDEF table in advance of parsing GSUB/GPOS tables
|
||||
// because they could refer GDEF table.
|
||||
{ OTS_TAG_GDEF, false },
|
||||
@ -709,7 +704,7 @@ bool ProcessGeneric(ots::FontFile *header,
|
||||
}
|
||||
}
|
||||
|
||||
if (font->GetTable(OTS_TAG_CFF) || font->GetTable(OTS_TAG('C', 'F', 'F', '2'))) {
|
||||
if (font->GetTable(OTS_TAG_CFF) || font->GetTable(OTS_TAG_CFF2)) {
|
||||
// font with PostScript glyph
|
||||
if (font->version != OTS_TAG('O','T','T','O')) {
|
||||
return OTS_FAILURE_MSG_HDR("wrong font version for PostScript glyph data");
|
||||
@ -885,32 +880,41 @@ bool Font::ParseTable(const TableEntry& table_entry, const uint8_t* data,
|
||||
table = new TablePassthru(this, tag);
|
||||
} else {
|
||||
switch (tag) {
|
||||
case OTS_TAG_AVAR: table = new OpenTypeAVAR(this, tag); break;
|
||||
case OTS_TAG_CFF: table = new OpenTypeCFF(this, tag); break;
|
||||
case OTS_TAG_CFF2: table = new OpenTypeCFF2(this, tag); break;
|
||||
case OTS_TAG_CMAP: table = new OpenTypeCMAP(this, tag); break;
|
||||
case OTS_TAG_CVAR: table = new OpenTypeCVAR(this, tag); break;
|
||||
case OTS_TAG_CVT: table = new OpenTypeCVT(this, tag); break;
|
||||
case OTS_TAG_FPGM: table = new OpenTypeFPGM(this, tag); break;
|
||||
case OTS_TAG_FVAR: table = new OpenTypeFVAR(this, tag); break;
|
||||
case OTS_TAG_GASP: table = new OpenTypeGASP(this, tag); break;
|
||||
case OTS_TAG_GDEF: table = new OpenTypeGDEF(this, tag); break;
|
||||
case OTS_TAG_GLYF: table = new OpenTypeGLYF(this, tag); break;
|
||||
case OTS_TAG_GPOS: table = new OpenTypeGPOS(this, tag); break;
|
||||
case OTS_TAG_GSUB: table = new OpenTypeGSUB(this, tag); break;
|
||||
case OTS_TAG_GVAR: table = new OpenTypeGVAR(this, tag); break;
|
||||
case OTS_TAG_HDMX: table = new OpenTypeHDMX(this, tag); break;
|
||||
case OTS_TAG_HEAD: table = new OpenTypeHEAD(this, tag); break;
|
||||
case OTS_TAG_HHEA: table = new OpenTypeHHEA(this, tag); break;
|
||||
case OTS_TAG_HMTX: table = new OpenTypeHMTX(this, tag); break;
|
||||
case OTS_TAG_HVAR: table = new OpenTypeHVAR(this, tag); break;
|
||||
case OTS_TAG_KERN: table = new OpenTypeKERN(this, tag); break;
|
||||
case OTS_TAG_LOCA: table = new OpenTypeLOCA(this, tag); break;
|
||||
case OTS_TAG_LTSH: table = new OpenTypeLTSH(this, tag); break;
|
||||
case OTS_TAG_MATH: table = new OpenTypeMATH(this, tag); break;
|
||||
case OTS_TAG_MAXP: table = new OpenTypeMAXP(this, tag); break;
|
||||
case OTS_TAG_MVAR: table = new OpenTypeMVAR(this, tag); break;
|
||||
case OTS_TAG_NAME: table = new OpenTypeNAME(this, tag); break;
|
||||
case OTS_TAG_OS2: table = new OpenTypeOS2(this, tag); break;
|
||||
case OTS_TAG_POST: table = new OpenTypePOST(this, tag); break;
|
||||
case OTS_TAG_PREP: table = new OpenTypePREP(this, tag); break;
|
||||
case OTS_TAG_STAT: table = new OpenTypeSTAT(this, tag); break;
|
||||
case OTS_TAG_VDMX: table = new OpenTypeVDMX(this, tag); break;
|
||||
case OTS_TAG_VORG: table = new OpenTypeVORG(this, tag); break;
|
||||
case OTS_TAG_VHEA: table = new OpenTypeVHEA(this, tag); break;
|
||||
case OTS_TAG_VMTX: table = new OpenTypeVMTX(this, tag); break;
|
||||
case OTS_TAG_VORG: table = new OpenTypeVORG(this, tag); break;
|
||||
case OTS_TAG_VVAR: table = new OpenTypeVVAR(this, tag); break;
|
||||
// Graphite tables
|
||||
#ifdef OTS_GRAPHITE
|
||||
case OTS_TAG_FEAT: table = new OpenTypeFEAT(this, tag); break;
|
||||
@ -919,16 +923,6 @@ bool Font::ParseTable(const TableEntry& table_entry, const uint8_t* data,
|
||||
case OTS_TAG_SILE: table = new OpenTypeSILE(this, tag); break;
|
||||
case OTS_TAG_SILF: table = new OpenTypeSILF(this, tag); break;
|
||||
case OTS_TAG_SILL: table = new OpenTypeSILL(this, tag); break;
|
||||
#endif
|
||||
#ifdef OTS_VARIATIONS
|
||||
case OTS_TAG_AVAR: table = new OpenTypeAVAR(this, tag); break;
|
||||
case OTS_TAG_CVAR: table = new OpenTypeCVAR(this, tag); break;
|
||||
case OTS_TAG_FVAR: table = new OpenTypeFVAR(this, tag); break;
|
||||
case OTS_TAG_GVAR: table = new OpenTypeGVAR(this, tag); break;
|
||||
case OTS_TAG_HVAR: table = new OpenTypeHVAR(this, tag); break;
|
||||
case OTS_TAG_MVAR: table = new OpenTypeMVAR(this, tag); break;
|
||||
case OTS_TAG_STAT: table = new OpenTypeSTAT(this, tag); break;
|
||||
case OTS_TAG_VVAR: table = new OpenTypeVVAR(this, tag); break;
|
||||
#endif
|
||||
default: break;
|
||||
}
|
||||
|
@ -190,6 +190,7 @@ bool CheckTag(uint32_t tag_value);
|
||||
bool IsValidVersionTag(uint32_t tag);
|
||||
|
||||
#define OTS_TAG_CFF OTS_TAG('C','F','F',' ')
|
||||
#define OTS_TAG_CFF2 OTS_TAG('C','F','F','2')
|
||||
#define OTS_TAG_CMAP OTS_TAG('c','m','a','p')
|
||||
#define OTS_TAG_CVT OTS_TAG('c','v','t',' ')
|
||||
#define OTS_TAG_FEAT OTS_TAG('F','e','a','t')
|
||||
|
@ -38,7 +38,19 @@ bool OpenTypeSILF::Parse(const uint8_t* data, size_t length,
|
||||
if (prevent_decompression) {
|
||||
return DropGraphite("Illegal nested compression");
|
||||
}
|
||||
std::vector<uint8_t> decompressed(this->compHead & FULL_SIZE);
|
||||
size_t decompressed_size = this->compHead & FULL_SIZE;
|
||||
if (decompressed_size < length) {
|
||||
return DropGraphite("Decompressed size is less than compressed size");
|
||||
}
|
||||
if (decompressed_size == 0) {
|
||||
return DropGraphite("Decompressed size is set to 0");
|
||||
}
|
||||
// decompressed table must be <= 30MB
|
||||
if (decompressed_size > 30 * 1024 * 1024) {
|
||||
return DropGraphite("Decompressed size exceeds 30MB: %gMB",
|
||||
decompressed_size / (1024.0 * 1024.0));
|
||||
}
|
||||
std::vector<uint8_t> decompressed(decompressed_size);
|
||||
size_t outputSize = 0;
|
||||
bool ret = mozilla::Compression::LZ4::decompressPartial(
|
||||
reinterpret_cast<const char*>(data + table.offset()),
|
||||
@ -165,15 +177,9 @@ bool OpenTypeSILF::SILSub::ParsePart(Buffer& table) {
|
||||
if (!table.ReadU8(&this->attrMirroring)) {
|
||||
return parent->Error("SILSub: Failed to read attrMirroring");
|
||||
}
|
||||
if (parent->version >> 16 < 4 && this->attrMirroring != 0) {
|
||||
parent->Warning("SILSub: Nonzero attrMirroring (reserved before v4)");
|
||||
}
|
||||
if (!table.ReadU8(&this->attrSkipPasses)) {
|
||||
return parent->Error("SILSub: Failed to read attrSkipPasses");
|
||||
}
|
||||
if (parent->version >> 16 < 4 && this->attrSkipPasses != 0) {
|
||||
parent->Warning("SILSub: Nonzero attrSkipPasses (reserved2 before v4)");
|
||||
}
|
||||
|
||||
if (!table.ReadU8(&this->numJLevels)) {
|
||||
return parent->Error("SILSub: Failed to read numJLevels");
|
||||
@ -642,9 +648,6 @@ SILPass::ParsePart(Buffer& table, const size_t SILSub_init_offset,
|
||||
if (!table.ReadU16(&this->fsmOffset)) {
|
||||
return parent->Error("SILPass: Failed to read fsmOffset");
|
||||
}
|
||||
if (parent->version >> 16 == 2 && this->fsmOffset != 0) {
|
||||
parent->Warning("SILPass: Nonzero fsmOffset (reserved in SILSub v2)");
|
||||
}
|
||||
if (!table.ReadU32(&this->pcCode) ||
|
||||
(parent->version >= 3 && this->pcCode < this->fsmOffset)) {
|
||||
return parent->Error("SILPass: Failed to read pcCode");
|
||||
@ -770,10 +773,6 @@ SILPass::ParsePart(Buffer& table, const size_t SILSub_init_offset,
|
||||
if (!table.ReadU8(&this->collisionThreshold)) {
|
||||
return parent->Error("SILPass: Failed to read collisionThreshold");
|
||||
}
|
||||
if (parent->version >> 16 < 5 && this->collisionThreshold != 0) {
|
||||
parent->Warning("SILPass: Nonzero collisionThreshold"
|
||||
" (reserved before v5)");
|
||||
}
|
||||
if (!table.ReadU16(&this->pConstraint)) {
|
||||
return parent->Error("SILPass: Failed to read pConstraint");
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ bool OpenTypeSTAT::ValidateNameId(uint16_t nameid, bool allowPredefined) {
|
||||
OpenTypeNAME* name = static_cast<OpenTypeNAME*>(
|
||||
GetFont()->GetTypedTable(OTS_TAG_NAME));
|
||||
|
||||
if (!name->IsValidNameId(nameid)) {
|
||||
if (!name || !name->IsValidNameId(nameid)) {
|
||||
Drop("Invalid nameID: %d", nameid);
|
||||
return false;
|
||||
}
|
||||
|
@ -23,6 +23,10 @@ bool ParseVariationRegionList(const ots::Font* font, const uint8_t* data, const
|
||||
return OTS_FAILURE_MSG("Failed to read variation region list header");
|
||||
}
|
||||
|
||||
if (*regionCount == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const ots::OpenTypeFVAR* fvar =
|
||||
static_cast<ots::OpenTypeFVAR*>(font->GetTypedTable(OTS_TAG_FVAR));
|
||||
if (!fvar) {
|
||||
@ -58,27 +62,27 @@ bool ParseVariationRegionList(const ots::Font* font, const uint8_t* data, const
|
||||
|
||||
bool
|
||||
ParseVariationDataSubtable(const ots::Font* font, const uint8_t* data, const size_t length,
|
||||
const uint16_t regionCount) {
|
||||
const uint16_t regionCount,
|
||||
uint16_t* regionIndexCount) {
|
||||
ots::Buffer subtable(data, length);
|
||||
|
||||
uint16_t itemCount;
|
||||
uint16_t shortDeltaCount;
|
||||
uint16_t regionIndexCount;
|
||||
|
||||
if (!subtable.ReadU16(&itemCount) ||
|
||||
!subtable.ReadU16(&shortDeltaCount) ||
|
||||
!subtable.ReadU16(®ionIndexCount)) {
|
||||
!subtable.ReadU16(regionIndexCount)) {
|
||||
return OTS_FAILURE_MSG("Failed to read variation data subtable header");
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < regionIndexCount; i++) {
|
||||
for (unsigned i = 0; i < *regionIndexCount; i++) {
|
||||
uint16_t regionIndex;
|
||||
if (!subtable.ReadU16(®ionIndex) || regionIndex >= regionCount) {
|
||||
return OTS_FAILURE_MSG("Bad region index");
|
||||
}
|
||||
}
|
||||
|
||||
if (!subtable.Skip(size_t(itemCount) * (size_t(shortDeltaCount) + size_t(regionIndexCount)))) {
|
||||
if (!subtable.Skip(size_t(itemCount) * (size_t(shortDeltaCount) + size_t(*regionIndexCount)))) {
|
||||
return OTS_FAILURE_MSG("Failed to read delta data");
|
||||
}
|
||||
|
||||
@ -90,7 +94,9 @@ ParseVariationDataSubtable(const ots::Font* font, const uint8_t* data, const siz
|
||||
namespace ots {
|
||||
|
||||
bool
|
||||
ParseItemVariationStore(const Font* font, const uint8_t* data, const size_t length) {
|
||||
ParseItemVariationStore(const Font* font,
|
||||
const uint8_t* data, const size_t length,
|
||||
std::vector<uint16_t>* regionIndexCounts) {
|
||||
Buffer subtable(data, length);
|
||||
|
||||
uint16_t format;
|
||||
@ -128,9 +134,15 @@ ParseItemVariationStore(const Font* font, const uint8_t* data, const size_t leng
|
||||
if (offset >= length) {
|
||||
return OTS_FAILURE_MSG("Bad offset to variation data subtable");
|
||||
}
|
||||
if (!ParseVariationDataSubtable(font, data + offset, length - offset, regionCount)) {
|
||||
uint16_t regionIndexCount = 0;
|
||||
if (!ParseVariationDataSubtable(font, data + offset, length - offset,
|
||||
regionCount,
|
||||
®ionIndexCount)) {
|
||||
return OTS_FAILURE_MSG("Failed to parse variation data subtable");
|
||||
}
|
||||
if (regionIndexCounts) {
|
||||
regionIndexCounts->push_back(regionIndexCount);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -147,7 +159,6 @@ bool ParseDeltaSetIndexMap(const Font* font, const uint8_t* data, const size_t l
|
||||
return OTS_FAILURE_MSG("Failed to read delta set index map header");
|
||||
}
|
||||
|
||||
const uint16_t INNER_INDEX_BIT_COUNT_MASK = 0x000F;
|
||||
const uint16_t MAP_ENTRY_SIZE_MASK = 0x0030;
|
||||
|
||||
const uint16_t entrySize = (((entryFormat & MAP_ENTRY_SIZE_MASK) >> 4) + 1);
|
||||
|
@ -5,13 +5,16 @@
|
||||
#ifndef OTS_VARIATIONS_H_
|
||||
#define OTS_VARIATIONS_H_
|
||||
|
||||
#include <vector>
|
||||
#include "ots.h"
|
||||
|
||||
// Utility functions for OpenType variations common table formats.
|
||||
|
||||
namespace ots {
|
||||
|
||||
bool ParseItemVariationStore(const Font* font, const uint8_t* data, const size_t length);
|
||||
bool ParseItemVariationStore(const Font* font,
|
||||
const uint8_t* data, const size_t length,
|
||||
std::vector<uint16_t>* out_region_index_count = NULL);
|
||||
|
||||
bool ParseDeltaSetIndexMap(const Font* font, const uint8_t* data, const size_t length);
|
||||
|
||||
|
@ -6,8 +6,6 @@
|
||||
|
||||
#include "variations.h"
|
||||
|
||||
#define TABLE_NAME "VVAR"
|
||||
|
||||
namespace ots {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
@ -95,5 +93,3 @@ bool OpenTypeVVAR::Serialize(OTSStream* out) {
|
||||
}
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#undef TABLE_NAME
|
||||
|
@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "cff_type2_charstring.h"
|
||||
#include "cff_charstring.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
@ -77,7 +77,7 @@ bool EncodeNumber(int num, std::vector<uint8_t> *out_bytes) {
|
||||
out_bytes->push_back(w);
|
||||
return true;
|
||||
}
|
||||
if (num <= -32768 && num >= 32767) {
|
||||
if (num <= 32768 && num >= -32767) {
|
||||
const uint8_t v = (num % 0xff00u) >> 8;
|
||||
const uint8_t w = num % 0xffu;
|
||||
out_bytes->push_back(28);
|
||||
@ -124,13 +124,13 @@ bool Validate(const int *char_string, size_t char_string_len,
|
||||
const int *global_subrs, size_t global_subrs_len,
|
||||
const int *local_subrs, size_t local_subrs_len) {
|
||||
std::vector<uint8_t> buffer;
|
||||
ots::CFFIndex char_strings_index;
|
||||
ots::CFFIndex* char_strings_index = new ots::CFFIndex;
|
||||
ots::CFFIndex global_subrs_index;
|
||||
ots::CFFIndex local_subrs_index;
|
||||
ots::CFFIndex* local_subrs_index = new ots::CFFIndex;
|
||||
|
||||
if (char_string) {
|
||||
if (!AddSubr(char_string, char_string_len,
|
||||
&buffer, &char_strings_index)) {
|
||||
&buffer, char_strings_index)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -142,28 +142,26 @@ bool Validate(const int *char_string, size_t char_string_len,
|
||||
}
|
||||
if (local_subrs) {
|
||||
if (!AddSubr(local_subrs, local_subrs_len,
|
||||
&buffer, &local_subrs_index)) {
|
||||
&buffer, local_subrs_index)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const std::map<uint16_t, uint8_t> fd_select; // empty
|
||||
const std::vector<ots::CFFIndex *> local_subrs_per_font; // empty
|
||||
ots::Buffer ots_buffer(&buffer[0], buffer.size());
|
||||
|
||||
ots::FontFile* file = new ots::FontFile();
|
||||
ots::Font* font = new ots::Font(file);
|
||||
file->context = new ots::OTSContext();
|
||||
bool ret = ots::ValidateType2CharStringIndex(font,
|
||||
char_strings_index,
|
||||
global_subrs_index,
|
||||
fd_select,
|
||||
local_subrs_per_font,
|
||||
&local_subrs_index,
|
||||
&ots_buffer);
|
||||
ots::Font* font = new ots::Font(file);
|
||||
ots::OpenTypeCFF* cff = new ots::OpenTypeCFF(font, OTS_TAG_CFF);
|
||||
cff->charstrings_index = char_strings_index;
|
||||
cff->local_subrs = local_subrs_index;
|
||||
bool ret = ots::ValidateCFFCharStrings(*cff,
|
||||
global_subrs_index,
|
||||
&ots_buffer);
|
||||
delete file->context;
|
||||
delete file;
|
||||
delete font;
|
||||
delete cff;
|
||||
|
||||
return ret;
|
||||
}
|
@ -30,6 +30,10 @@ struct precache_output
|
||||
#define ALIGN __attribute__(( aligned (16) ))
|
||||
#endif
|
||||
|
||||
struct _qcms_transform;
|
||||
|
||||
typedef void (*transform_fn_t)(const struct _qcms_transform *transform, const unsigned char *src, unsigned char *dest, size_t length);
|
||||
|
||||
struct _qcms_transform {
|
||||
float ALIGN matrix[3][4];
|
||||
float *input_gamma_table_r;
|
||||
@ -73,7 +77,7 @@ struct _qcms_transform {
|
||||
struct precache_output *output_table_g;
|
||||
struct precache_output *output_table_b;
|
||||
|
||||
void (*transform_fn)(const struct _qcms_transform *transform, const unsigned char *src, unsigned char *dest, size_t length);
|
||||
transform_fn_t transform_fn;
|
||||
};
|
||||
|
||||
struct matrix {
|
||||
@ -263,6 +267,32 @@ void precache_release(struct precache_output *p);
|
||||
bool set_rgb_colorants(qcms_profile *profile, qcms_CIE_xyY white_point, qcms_CIE_xyYTRIPLE primaries);
|
||||
bool get_rgb_colorants(struct matrix *colorants, qcms_CIE_xyY white_point, qcms_CIE_xyYTRIPLE primaries);
|
||||
|
||||
void qcms_transform_data_rgb_out_lut(const qcms_transform *transform,
|
||||
const unsigned char *src,
|
||||
unsigned char *dest,
|
||||
size_t length);
|
||||
void qcms_transform_data_rgba_out_lut(const qcms_transform *transform,
|
||||
const unsigned char *src,
|
||||
unsigned char *dest,
|
||||
size_t length);
|
||||
void qcms_transform_data_bgra_out_lut(const qcms_transform *transform,
|
||||
const unsigned char *src,
|
||||
unsigned char *dest,
|
||||
size_t length);
|
||||
|
||||
void qcms_transform_data_rgb_out_lut_precache(const qcms_transform *transform,
|
||||
const unsigned char *src,
|
||||
unsigned char *dest,
|
||||
size_t length);
|
||||
void qcms_transform_data_rgba_out_lut_precache(const qcms_transform *transform,
|
||||
const unsigned char *src,
|
||||
unsigned char *dest,
|
||||
size_t length);
|
||||
void qcms_transform_data_bgra_out_lut_precache(const qcms_transform *transform,
|
||||
const unsigned char *src,
|
||||
unsigned char *dest,
|
||||
size_t length);
|
||||
|
||||
void qcms_transform_data_rgb_out_lut_sse2(const qcms_transform *transform,
|
||||
const unsigned char *src,
|
||||
unsigned char *dest,
|
||||
|
@ -513,17 +513,17 @@ static void qcms_transform_data_template_lut_precache(const qcms_transform *tran
|
||||
}
|
||||
}
|
||||
|
||||
static void qcms_transform_data_rgb_out_lut_precache(const qcms_transform *transform, const unsigned char *src, unsigned char *dest, size_t length)
|
||||
void qcms_transform_data_rgb_out_lut_precache(const qcms_transform *transform, const unsigned char *src, unsigned char *dest, size_t length)
|
||||
{
|
||||
qcms_transform_data_template_lut_precache<RGBA_R_INDEX, RGBA_G_INDEX, RGBA_B_INDEX>(transform, src, dest, length);
|
||||
}
|
||||
|
||||
static void qcms_transform_data_rgba_out_lut_precache(const qcms_transform *transform, const unsigned char *src, unsigned char *dest, size_t length)
|
||||
void qcms_transform_data_rgba_out_lut_precache(const qcms_transform *transform, const unsigned char *src, unsigned char *dest, size_t length)
|
||||
{
|
||||
qcms_transform_data_template_lut_precache<RGBA_R_INDEX, RGBA_G_INDEX, RGBA_B_INDEX, RGBA_A_INDEX>(transform, src, dest, length);
|
||||
}
|
||||
|
||||
static void qcms_transform_data_bgra_out_lut_precache(const qcms_transform *transform, const unsigned char *src, unsigned char *dest, size_t length)
|
||||
void qcms_transform_data_bgra_out_lut_precache(const qcms_transform *transform, const unsigned char *src, unsigned char *dest, size_t length)
|
||||
{
|
||||
qcms_transform_data_template_lut_precache<BGRA_R_INDEX, BGRA_G_INDEX, BGRA_B_INDEX, BGRA_A_INDEX>(transform, src, dest, length);
|
||||
}
|
||||
@ -773,17 +773,17 @@ static void qcms_transform_data_template_lut(const qcms_transform *transform, co
|
||||
}
|
||||
}
|
||||
|
||||
static void qcms_transform_data_rgb_out_lut(const qcms_transform *transform, const unsigned char *src, unsigned char *dest, size_t length)
|
||||
void qcms_transform_data_rgb_out_lut(const qcms_transform *transform, const unsigned char *src, unsigned char *dest, size_t length)
|
||||
{
|
||||
qcms_transform_data_template_lut<RGBA_R_INDEX, RGBA_G_INDEX, RGBA_B_INDEX>(transform, src, dest, length);
|
||||
}
|
||||
|
||||
static void qcms_transform_data_rgba_out_lut(const qcms_transform *transform, const unsigned char *src, unsigned char *dest, size_t length)
|
||||
void qcms_transform_data_rgba_out_lut(const qcms_transform *transform, const unsigned char *src, unsigned char *dest, size_t length)
|
||||
{
|
||||
qcms_transform_data_template_lut<RGBA_R_INDEX, RGBA_G_INDEX, RGBA_B_INDEX, RGBA_A_INDEX>(transform, src, dest, length);
|
||||
}
|
||||
|
||||
static void qcms_transform_data_bgra_out_lut(const qcms_transform *transform, const unsigned char *src, unsigned char *dest, size_t length)
|
||||
void qcms_transform_data_bgra_out_lut(const qcms_transform *transform, const unsigned char *src, unsigned char *dest, size_t length)
|
||||
{
|
||||
qcms_transform_data_template_lut<BGRA_R_INDEX, BGRA_G_INDEX, BGRA_B_INDEX, BGRA_A_INDEX>(transform, src, dest, length);
|
||||
}
|
||||
|
@ -5,9 +5,7 @@
|
||||
#ifndef nsColor_h___
|
||||
#define nsColor_h___
|
||||
|
||||
#include <stddef.h> // for size_t
|
||||
#include <stdint.h> // for uint8_t, uint32_t
|
||||
#include "nscore.h" // for nsAString
|
||||
#include "nsCoord.h" // for NSToIntRound
|
||||
#include "nsStringFwd.h"
|
||||
|
||||
|
@ -5,17 +5,14 @@
|
||||
#ifndef NSCOORD_H
|
||||
#define NSCOORD_H
|
||||
|
||||
#include "mozilla/FloatingPoint.h"
|
||||
|
||||
#include "nsAlgorithm.h"
|
||||
#include "nscore.h"
|
||||
#include "nsMathUtils.h"
|
||||
#include <math.h>
|
||||
#include <float.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "nsDebug.h"
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <math.h>
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/gfx/Coord.h"
|
||||
#include "nsMathUtils.h"
|
||||
|
||||
/*
|
||||
* Basic type used for the geometry classes.
|
||||
@ -46,6 +43,60 @@ inline void VERIFY_COORD(nscoord aCoord) {
|
||||
#endif
|
||||
}
|
||||
|
||||
namespace mozilla {
|
||||
struct AppUnit {};
|
||||
|
||||
// Declare AppUnit as a coordinate system tag.
|
||||
template <>
|
||||
struct IsPixel<AppUnit> : std::true_type {};
|
||||
|
||||
namespace detail {
|
||||
template <typename Rep>
|
||||
struct AuCoordImpl : public gfx::IntCoordTyped<AppUnit, Rep> {
|
||||
using Super = gfx::IntCoordTyped<AppUnit, Rep>;
|
||||
|
||||
constexpr AuCoordImpl() : Super() {}
|
||||
constexpr MOZ_IMPLICIT AuCoordImpl(Rep aValue) : Super(aValue) {}
|
||||
constexpr MOZ_IMPLICIT AuCoordImpl(Super aValue) : Super(aValue) {}
|
||||
|
||||
template <typename F>
|
||||
static AuCoordImpl FromRound(F aValue) {
|
||||
// Note: aValue is *not* rounding to nearest integer if it is negative. See
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=410748#c14
|
||||
return AuCoordImpl(std::floor(aValue + 0.5f));
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
static AuCoordImpl FromTruncate(F aValue) {
|
||||
return AuCoordImpl(std::trunc(aValue));
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
static AuCoordImpl FromCeil(F aValue) {
|
||||
return AuCoordImpl(std::ceil(aValue));
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
static AuCoordImpl FromFloor(F aValue) {
|
||||
return AuCoordImpl(std::floor(aValue));
|
||||
}
|
||||
|
||||
// Note: this returns the result of the operation, without modifying the
|
||||
// original value.
|
||||
[[nodiscard]] AuCoordImpl ToMinMaxClamped() const {
|
||||
return std::clamp(this->value, kMin, kMax);
|
||||
}
|
||||
|
||||
static constexpr Rep kMax = nscoord_MAX;
|
||||
static constexpr Rep kMin = nscoord_MIN;
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
using AuCoord = detail::AuCoordImpl<int32_t>;
|
||||
using AuCoord64 = detail::AuCoordImpl<int64_t>;
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
/**
|
||||
* Divide aSpace by aN. Assign the resulting quotient to aQuotient and
|
||||
* return the remainder.
|
||||
|
@ -14,7 +14,8 @@
|
||||
#include "nsError.h" // for nsresult
|
||||
#include "nsFont.h" // for nsFont
|
||||
#include "nsISupports.h" // for NS_INLINE_DECL_REFCOUNTING
|
||||
#include "nscore.h" // for char16_t
|
||||
#include "nsStyleConsts.h"
|
||||
#include "nscore.h" // for char16_t
|
||||
|
||||
class gfxContext;
|
||||
class gfxFontGroup;
|
||||
|
@ -7,9 +7,10 @@
|
||||
#ifndef nsITheme_h_
|
||||
#define nsITheme_h_
|
||||
|
||||
#include "mozilla/AlreadyAddRefed.h"
|
||||
#include "nsISupports.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsColor.h"
|
||||
#include "nsID.h"
|
||||
#include "nscore.h"
|
||||
#include "Units.h"
|
||||
|
||||
struct nsRect;
|
||||
@ -92,9 +93,9 @@ class nsITheme : public nsISupports {
|
||||
/**
|
||||
* Return the border for the widget, in device pixels.
|
||||
*/
|
||||
virtual MOZ_MUST_USE LayoutDeviceIntMargin
|
||||
GetWidgetBorder(nsDeviceContext* aContext, nsIFrame* aFrame,
|
||||
StyleAppearance aWidgetType) = 0;
|
||||
[[nodiscard]] virtual LayoutDeviceIntMargin GetWidgetBorder(
|
||||
nsDeviceContext* aContext, nsIFrame* aFrame,
|
||||
StyleAppearance aWidgetType) = 0;
|
||||
|
||||
/**
|
||||
* This method can return false to indicate that the CSS padding
|
||||
|
@ -6,7 +6,6 @@
|
||||
#define NSMARGIN_H
|
||||
|
||||
#include "nsCoord.h"
|
||||
#include "nsPoint.h"
|
||||
#include "mozilla/gfx/BaseMargin.h"
|
||||
#include "mozilla/gfx/Rect.h"
|
||||
|
||||
|
@ -5,10 +5,9 @@
|
||||
#ifndef NSPOINT_H
|
||||
#define NSPOINT_H
|
||||
|
||||
#include <cstdint>
|
||||
#include "nsCoord.h"
|
||||
#include "mozilla/gfx/BaseSize.h"
|
||||
#include "mozilla/gfx/BasePoint.h"
|
||||
#include "nsSize.h"
|
||||
#include "mozilla/gfx/Point.h"
|
||||
|
||||
// nsIntPoint represents a point in one of the types of pixels.
|
||||
@ -34,11 +33,11 @@ struct nsPoint : public mozilla::gfx::BasePoint<nscoord, nsPoint> {
|
||||
* @param aFromAPP the APP to scale from
|
||||
* @param aToAPP the APP to scale to
|
||||
*/
|
||||
MOZ_MUST_USE inline nsPoint ScaleToOtherAppUnits(int32_t aFromAPP,
|
||||
int32_t aToAPP) const;
|
||||
[[nodiscard]] inline nsPoint ScaleToOtherAppUnits(int32_t aFromAPP,
|
||||
int32_t aToAPP) const;
|
||||
|
||||
MOZ_MUST_USE inline nsPoint RemoveResolution(const float resolution) const;
|
||||
MOZ_MUST_USE inline nsPoint ApplyResolution(const float resolution) const;
|
||||
[[nodiscard]] inline nsPoint RemoveResolution(const float resolution) const;
|
||||
[[nodiscard]] inline nsPoint ApplyResolution(const float resolution) const;
|
||||
};
|
||||
|
||||
inline nsPoint ToAppUnits(const nsIntPoint& aPoint, nscoord aAppUnitsPerPixel);
|
||||
|
@ -5,19 +5,15 @@
|
||||
#ifndef NSRECT_H
|
||||
#define NSRECT_H
|
||||
|
||||
#include <stdio.h> // for FILE
|
||||
#include <stdint.h> // for int32_t, int64_t
|
||||
#include <algorithm> // for min/max
|
||||
#include "mozilla/Likely.h" // for MOZ_UNLIKELY
|
||||
#include "mozilla/gfx/BaseRect.h"
|
||||
#include "mozilla/gfx/Rect.h"
|
||||
#include "mozilla/gfx/2D.h"
|
||||
#include "mozilla/gfx/Logging.h"
|
||||
#include "nsCoord.h" // for nscoord, etc
|
||||
#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
|
||||
#include "nsPoint.h" // for nsIntPoint, nsPoint
|
||||
#include "nsMargin.h" // for nsIntMargin, nsMargin
|
||||
#include "nsSize.h" // for IntSize, nsSize
|
||||
#include "nscore.h" // for NS_BUILD_REFCNT_LOGGING
|
||||
#include "nsCoord.h" // for nscoord, etc
|
||||
#include "nsISupports.h" // for MOZ_COUNT_CTOR, etc
|
||||
#include "nsPoint.h" // for nsIntPoint, nsPoint
|
||||
#include "nsSize.h" // for IntSize, nsSize
|
||||
#if !defined(ANDROID) && (defined(__SSE2__) || defined(_M_X64) || \
|
||||
(defined(_M_IX86_FP) && _M_IX86_FP >= 2))
|
||||
# if defined(_MSC_VER) && !defined(__clang__)
|
||||
@ -27,6 +23,8 @@
|
||||
# endif
|
||||
#endif
|
||||
|
||||
struct nsMargin;
|
||||
|
||||
typedef mozilla::gfx::IntRect nsIntRect;
|
||||
|
||||
struct nsRect : public mozilla::gfx::BaseRect<nscoord, nsRect, nsPoint, nsSize,
|
||||
@ -46,16 +44,15 @@ struct nsRect : public mozilla::gfx::BaseRect<nscoord, nsRect, nsPoint, nsSize,
|
||||
: Super(aX, aY, aWidth, aHeight) {
|
||||
MOZ_COUNT_CTOR(nsRect);
|
||||
}
|
||||
nsRect& operator=(const nsRect&) = default;
|
||||
|
||||
#ifdef NS_BUILD_REFCNT_LOGGING
|
||||
~nsRect() { MOZ_COUNT_DTOR(nsRect); }
|
||||
#endif
|
||||
MOZ_COUNTED_DTOR(nsRect)
|
||||
|
||||
// We have saturating versions of all the Union methods. These avoid
|
||||
// overflowing nscoord values in the 'width' and 'height' fields by
|
||||
// clamping the width and height values to nscoord_MAX if necessary.
|
||||
|
||||
MOZ_MUST_USE nsRect SaturatingUnion(const nsRect& aRect) const {
|
||||
[[nodiscard]] nsRect SaturatingUnion(const nsRect& aRect) const {
|
||||
if (IsEmpty()) {
|
||||
return aRect;
|
||||
} else if (aRect.IsEmpty()) {
|
||||
@ -65,7 +62,7 @@ struct nsRect : public mozilla::gfx::BaseRect<nscoord, nsRect, nsPoint, nsSize,
|
||||
}
|
||||
}
|
||||
|
||||
MOZ_MUST_USE nsRect SaturatingUnionEdges(const nsRect& aRect) const {
|
||||
[[nodiscard]] nsRect SaturatingUnionEdges(const nsRect& aRect) const {
|
||||
#ifdef NS_COORD_IS_FLOAT
|
||||
return UnionEdges(aRect);
|
||||
#else
|
||||
@ -102,16 +99,13 @@ struct nsRect : public mozilla::gfx::BaseRect<nscoord, nsRect, nsPoint, nsSize,
|
||||
|
||||
#ifndef NS_COORD_IS_FLOAT
|
||||
// Make all nsRect Union methods be saturating.
|
||||
MOZ_MUST_USE nsRect UnionEdges(const nsRect& aRect) const {
|
||||
[[nodiscard]] nsRect UnionEdges(const nsRect& aRect) const {
|
||||
return SaturatingUnionEdges(aRect);
|
||||
}
|
||||
void UnionRectEdges(const nsRect& aRect1, const nsRect& aRect2) {
|
||||
*this = aRect1.UnionEdges(aRect2);
|
||||
}
|
||||
MOZ_MUST_USE nsRect Union(const nsRect& aRect) const {
|
||||
[[nodiscard]] nsRect Union(const nsRect& aRect) const {
|
||||
return SaturatingUnion(aRect);
|
||||
}
|
||||
MOZ_MUST_USE nsRect UnsafeUnion(const nsRect& aRect) const {
|
||||
[[nodiscard]] nsRect UnsafeUnion(const nsRect& aRect) const {
|
||||
return Super::Union(aRect);
|
||||
}
|
||||
void UnionRect(const nsRect& aRect1, const nsRect& aRect2) {
|
||||
@ -121,7 +115,7 @@ struct nsRect : public mozilla::gfx::BaseRect<nscoord, nsRect, nsPoint, nsSize,
|
||||
# if defined(_MSC_VER) && !defined(__clang__) && \
|
||||
(defined(_M_X64) || defined(_M_IX86))
|
||||
// Only MSVC supports inlining intrinsics for archs you're not compiling for.
|
||||
MOZ_MUST_USE nsRect Intersect(const nsRect& aRect) const {
|
||||
[[nodiscard]] nsRect Intersect(const nsRect& aRect) const {
|
||||
nsRect result;
|
||||
if (mozilla::gfx::Factory::HasSSE4()) {
|
||||
__m128i rect1 = _mm_loadu_si128((__m128i*)&aRect); // x1, y1, w1, h1
|
||||
@ -241,35 +235,35 @@ struct nsRect : public mozilla::gfx::BaseRect<nscoord, nsRect, nsPoint, nsSize,
|
||||
* @param aToAPP the APP to scale to
|
||||
* @note this can turn an empty rectangle into a non-empty rectangle
|
||||
*/
|
||||
MOZ_MUST_USE inline nsRect ScaleToOtherAppUnitsRoundOut(int32_t aFromAPP,
|
||||
[[nodiscard]] inline nsRect ScaleToOtherAppUnitsRoundOut(
|
||||
int32_t aFromAPP, int32_t aToAPP) const;
|
||||
[[nodiscard]] inline nsRect ScaleToOtherAppUnitsRoundIn(int32_t aFromAPP,
|
||||
int32_t aToAPP) const;
|
||||
MOZ_MUST_USE inline nsRect ScaleToOtherAppUnitsRoundIn(int32_t aFromAPP,
|
||||
int32_t aToAPP) const;
|
||||
|
||||
MOZ_MUST_USE inline mozilla::gfx::IntRect ScaleToNearestPixels(
|
||||
[[nodiscard]] inline mozilla::gfx::IntRect ScaleToNearestPixels(
|
||||
float aXScale, float aYScale, nscoord aAppUnitsPerPixel) const;
|
||||
|
||||
MOZ_MUST_USE inline mozilla::gfx::IntRect ToNearestPixels(
|
||||
[[nodiscard]] inline mozilla::gfx::IntRect ToNearestPixels(
|
||||
nscoord aAppUnitsPerPixel) const;
|
||||
|
||||
// Note: this can turn an empty rectangle into a non-empty rectangle
|
||||
MOZ_MUST_USE inline mozilla::gfx::IntRect ScaleToOutsidePixels(
|
||||
[[nodiscard]] inline mozilla::gfx::IntRect ScaleToOutsidePixels(
|
||||
float aXScale, float aYScale, nscoord aAppUnitsPerPixel) const;
|
||||
|
||||
// Note: this can turn an empty rectangle into a non-empty rectangle
|
||||
MOZ_MUST_USE inline mozilla::gfx::IntRect ToOutsidePixels(
|
||||
[[nodiscard]] inline mozilla::gfx::IntRect ToOutsidePixels(
|
||||
nscoord aAppUnitsPerPixel) const;
|
||||
|
||||
MOZ_MUST_USE inline mozilla::gfx::IntRect ScaleToInsidePixels(
|
||||
[[nodiscard]] inline mozilla::gfx::IntRect ScaleToInsidePixels(
|
||||
float aXScale, float aYScale, nscoord aAppUnitsPerPixel) const;
|
||||
|
||||
MOZ_MUST_USE inline mozilla::gfx::IntRect ToInsidePixels(
|
||||
[[nodiscard]] inline mozilla::gfx::IntRect ToInsidePixels(
|
||||
nscoord aAppUnitsPerPixel) const;
|
||||
|
||||
// This is here only to keep IPDL-generated code happy. DO NOT USE.
|
||||
bool operator==(const nsRect& aRect) const { return IsEqualEdges(aRect); }
|
||||
|
||||
MOZ_MUST_USE inline nsRect RemoveResolution(const float aResolution) const;
|
||||
[[nodiscard]] inline nsRect RemoveResolution(const float aResolution) const;
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -7,7 +7,7 @@
|
||||
|
||||
#include "mozilla/gfx/RectAbsolute.h"
|
||||
#include "nsCoord.h"
|
||||
#include "nsPoint.h"
|
||||
#include "nsMargin.h"
|
||||
#include "nsRect.h"
|
||||
|
||||
struct nsRectAbsolute
|
||||
@ -38,7 +38,7 @@ struct nsRectAbsolute
|
||||
return nsRect(left, top, nscoord(SafeWidth()), nscoord(SafeHeight()));
|
||||
}
|
||||
|
||||
MOZ_MUST_USE nsRectAbsolute UnsafeUnion(const nsRectAbsolute& aRect) const {
|
||||
[[nodiscard]] nsRectAbsolute UnsafeUnion(const nsRectAbsolute& aRect) const {
|
||||
return Super::Union(aRect);
|
||||
}
|
||||
|
||||
|
@ -5,18 +5,15 @@
|
||||
#ifndef nsRegion_h__
|
||||
#define nsRegion_h__
|
||||
|
||||
#include <stddef.h> // for size_t
|
||||
#include <stdint.h> // for uint32_t, uint64_t
|
||||
#include <sys/types.h> // for int32_t
|
||||
#include <stddef.h> // for size_t
|
||||
#include <stdint.h> // for uint32_t, uint64_t
|
||||
|
||||
#include <ostream> // for std::ostream
|
||||
#include <utility> // for mozilla::Move
|
||||
|
||||
#include "mozilla/ArrayView.h" // for ArrayView
|
||||
#include "mozilla/gfx/Logging.h"
|
||||
#include "mozilla/ArrayView.h" // for ArrayView
|
||||
#include "mozilla/gfx/MatrixFwd.h" // for mozilla::gfx::Matrix4x4
|
||||
#include "nsCoord.h" // for nscoord
|
||||
#include "nsError.h" // for nsresult
|
||||
#include "nsMargin.h" // for nsIntMargin
|
||||
#include "nsPoint.h" // for nsIntPoint, nsPoint
|
||||
#include "nsRect.h" // for mozilla::gfx::IntRect, nsRect
|
||||
@ -1819,10 +1816,10 @@ class nsRegion {
|
||||
* @param aToAPP the APP to scale to
|
||||
* @note this can turn an empty region into a non-empty region
|
||||
*/
|
||||
MOZ_MUST_USE nsRegion ScaleToOtherAppUnitsRoundOut(int32_t aFromAPP,
|
||||
[[nodiscard]] nsRegion ScaleToOtherAppUnitsRoundOut(int32_t aFromAPP,
|
||||
int32_t aToAPP) const;
|
||||
[[nodiscard]] nsRegion ScaleToOtherAppUnitsRoundIn(int32_t aFromAPP,
|
||||
int32_t aToAPP) const;
|
||||
MOZ_MUST_USE nsRegion ScaleToOtherAppUnitsRoundIn(int32_t aFromAPP,
|
||||
int32_t aToAPP) const;
|
||||
nsRegion& ScaleRoundOut(float aXScale, float aYScale);
|
||||
nsRegion& ScaleInverseRoundOut(float aXScale, float aYScale);
|
||||
nsRegion& Transform(const mozilla::gfx::Matrix4x4& aTransform);
|
||||
|
@ -29,8 +29,8 @@ struct nsSize : public mozilla::gfx::BaseSize<nscoord, nsSize> {
|
||||
* @param aFromAPP the APP to scale from
|
||||
* @param aToAPP the APP to scale to
|
||||
*/
|
||||
MOZ_MUST_USE inline nsSize ScaleToOtherAppUnits(int32_t aFromAPP,
|
||||
int32_t aToAPP) const;
|
||||
[[nodiscard]] inline nsSize ScaleToOtherAppUnits(int32_t aFromAPP,
|
||||
int32_t aToAPP) const;
|
||||
};
|
||||
|
||||
inline mozilla::gfx::IntSize nsSize::ScaleToNearestPixels(
|
||||
|
@ -1,23 +0,0 @@
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" style="font-family: monospace;">
|
||||
<head>
|
||||
|
||||
<bindings xmlns="http://www.mozilla.org/xbl">
|
||||
<binding id="w"><content><div xmlns="http://www.w3.org/1999/xhtml"><children xmlns="http://www.mozilla.org/xbl"/></div></content></binding>
|
||||
<binding id="empty"><content/></binding>
|
||||
</bindings>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
function boom()
|
||||
{
|
||||
document.createElementNS("http://www.w3.org/2000/svg", "rect");
|
||||
document.getElementById("d").previousSibling.data += "C";
|
||||
document.getElementById("d").style.MozBinding = "url('#empty')";
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
<body onload="boom();"><span>A‮<span style="-moz-binding: url('#w');"></span>B<span id="d" style="font-size: 80%; color: green;">D</span></span></body>
|
||||
</html>
|
@ -1,22 +0,0 @@
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" style="font-family: monospace;">
|
||||
<head>
|
||||
|
||||
<bindings xmlns="http://www.mozilla.org/xbl">
|
||||
<binding id="w"><content><div xmlns="http://www.w3.org/1999/xhtml"><children xmlns="http://www.mozilla.org/xbl"/></div></content></binding>
|
||||
<binding id="empty"><content/></binding>
|
||||
</bindings>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
function boom()
|
||||
{
|
||||
document.getElementById("d").previousSibling.data += "C";
|
||||
document.getElementById("d").style.MozBinding = "url('#empty')";
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
<body onload="boom();"><span>A‮<span style="-moz-binding: url('#w');"></span>B<span id="d" style="font-size: 80%; color: green;">D</span></span></body>
|
||||
</html>
|
@ -72,8 +72,6 @@ load 467703-1.xhtml
|
||||
load 467873-1.html
|
||||
load 470418-1.html
|
||||
load 474410-1.html
|
||||
load 483120-1.xhtml
|
||||
load 483120-2.xhtml
|
||||
load 487549-1.html
|
||||
load 487724-1.html
|
||||
load 490777-1.html
|
||||
@ -88,7 +86,7 @@ load 580212-1.html
|
||||
load 580233-1.html
|
||||
load 580719-1.html
|
||||
load 593526.html
|
||||
load 593526.xul
|
||||
load chrome://reftest/content/crashtests/gfx/tests/crashtests/593526.xhtml
|
||||
load 594654-1.xhtml
|
||||
load 595042-1.html
|
||||
load 595727-1.html
|
||||
|
@ -5,6 +5,7 @@
|
||||
#ifndef GTEST_MOCKWIDGET_H
|
||||
#define GTEST_MOCKWIDGET_H
|
||||
|
||||
#include "mozilla/gfx/Point.h"
|
||||
#include "mozilla/widget/InProcessCompositorWidget.h"
|
||||
#include "nsBaseWidget.h"
|
||||
#include "GLContext.h"
|
||||
@ -14,6 +15,8 @@ using mozilla::gl::CreateContextFlags;
|
||||
using mozilla::gl::GLContext;
|
||||
using mozilla::gl::GLContextProvider;
|
||||
|
||||
using mozilla::gfx::IntSize;
|
||||
|
||||
class MockWidget : public nsBaseWidget {
|
||||
public:
|
||||
MockWidget() : mCompWidth(0), mCompHeight(0) {}
|
||||
|
@ -6,8 +6,15 @@
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include "Point.h"
|
||||
#include "Triangle.h"
|
||||
|
||||
typedef mozilla::gfx::Polygon MozPolygon;
|
||||
|
||||
using mozilla::gfx::Point;
|
||||
using mozilla::gfx::Point4D;
|
||||
using mozilla::gfx::Triangle;
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
|
@ -6,6 +6,8 @@
|
||||
|
||||
#include "BufferUnrotate.h"
|
||||
|
||||
using mozilla::gfx::BufferUnrotate;
|
||||
|
||||
static unsigned char* GenerateBuffer(int bytesPerPixel, int width, int height,
|
||||
int stride, int xBoundary, int yBoundary) {
|
||||
unsigned char* buffer = new unsigned char[stride * height];
|
||||
|
87
gfx/tests/gtest/TestCoord.cpp
Normal file
87
gfx/tests/gtest/TestCoord.cpp
Normal file
@ -0,0 +1,87 @@
|
||||
/* 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 "gtest/gtest.h"
|
||||
|
||||
#include "nsCoord.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
template <typename CoordTyped>
|
||||
void TestConstructors() {
|
||||
CoordTyped coord1;
|
||||
EXPECT_EQ(coord1.value, 0);
|
||||
|
||||
CoordTyped coord2 = 6000;
|
||||
EXPECT_EQ(coord2.value, 6000);
|
||||
|
||||
EXPECT_EQ(CoordTyped::FromRound(5.3).value, 5);
|
||||
EXPECT_EQ(CoordTyped::FromRound(5.5).value, 6);
|
||||
EXPECT_EQ(CoordTyped::FromRound(-2.5).value, -2);
|
||||
|
||||
EXPECT_EQ(CoordTyped::FromTruncate(5.9).value, 5);
|
||||
EXPECT_EQ(CoordTyped::FromTruncate(-2.7).value, -2);
|
||||
|
||||
EXPECT_EQ(CoordTyped::FromCeil(5.3).value, 6);
|
||||
EXPECT_EQ(CoordTyped::FromCeil(-2.7).value, -2);
|
||||
|
||||
EXPECT_EQ(CoordTyped::FromFloor(5.9).value, 5);
|
||||
EXPECT_EQ(CoordTyped::FromFloor(-2.7).value, -3);
|
||||
}
|
||||
|
||||
template <typename CoordTyped>
|
||||
void TestComparisonOperators() {
|
||||
CoordTyped coord1 = 6000;
|
||||
CoordTyped coord2 = 10000;
|
||||
|
||||
EXPECT_LT(coord1, CoordTyped::kMax);
|
||||
EXPECT_GT(coord1, CoordTyped::kMin);
|
||||
EXPECT_NE(coord1, coord2);
|
||||
}
|
||||
|
||||
template <typename CoordTyped>
|
||||
void TestArithmeticOperators() {
|
||||
CoordTyped coord1 = 6000;
|
||||
CoordTyped coord2 = 10000;
|
||||
|
||||
EXPECT_EQ(coord1 + coord2, CoordTyped(16000));
|
||||
EXPECT_EQ(coord2 - coord1, CoordTyped(4000));
|
||||
|
||||
decltype(CoordTyped::value) scaleInt = 2;
|
||||
EXPECT_EQ(coord1 * scaleInt, CoordTyped(12000));
|
||||
EXPECT_EQ(coord1 / scaleInt, CoordTyped(3000));
|
||||
|
||||
EXPECT_EQ(coord1 * 2.0, 12000.0);
|
||||
EXPECT_EQ(coord1 / 2.0, 3000.0);
|
||||
}
|
||||
|
||||
template <typename CoordTyped>
|
||||
void TestClamp() {
|
||||
CoordTyped coord1 = CoordTyped::kMax + 1000;
|
||||
EXPECT_EQ(coord1.ToMinMaxClamped(), CoordTyped::kMax);
|
||||
|
||||
CoordTyped coord2 = CoordTyped::kMin - 1000;
|
||||
EXPECT_EQ(coord2.ToMinMaxClamped(), CoordTyped::kMin);
|
||||
|
||||
CoordTyped coord3 = 12000;
|
||||
EXPECT_EQ(coord3.ToMinMaxClamped(), CoordTyped(12000));
|
||||
}
|
||||
|
||||
TEST(Gfx, TestAuCoord)
|
||||
{
|
||||
TestConstructors<AuCoord>();
|
||||
TestComparisonOperators<AuCoord>();
|
||||
TestArithmeticOperators<AuCoord>();
|
||||
TestClamp<AuCoord>();
|
||||
}
|
||||
|
||||
TEST(Gfx, TestAuCoord64)
|
||||
{
|
||||
TestConstructors<AuCoord64>();
|
||||
TestComparisonOperators<AuCoord64>();
|
||||
TestArithmeticOperators<AuCoord64>();
|
||||
TestClamp<AuCoord64>();
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
@ -1,238 +0,0 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "gmock/gmock.h"
|
||||
|
||||
#include "mozilla/gfx/JobScheduler.h"
|
||||
|
||||
#ifndef WIN32
|
||||
# include <pthread.h>
|
||||
# include <sched.h>
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
|
||||
namespace test_scheduler {
|
||||
|
||||
using namespace mozilla::gfx;
|
||||
using namespace mozilla;
|
||||
using mozilla::gfx::SyncObject;
|
||||
|
||||
// Artificially cause threads to yield randomly in an attempt to make racy
|
||||
// things more apparent (if any).
|
||||
static void MaybeYieldThread() {
|
||||
#ifndef WIN32
|
||||
if (rand() % 5 == 0) {
|
||||
sched_yield();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Used by the TestCommand to check that tasks are processed in the right
|
||||
/// order.
|
||||
struct SanityChecker {
|
||||
std::vector<uint64_t> mAdvancements;
|
||||
mozilla::gfx::CriticalSection mSection;
|
||||
|
||||
explicit SanityChecker(uint64_t aNumCmdBuffers) {
|
||||
for (uint32_t i = 0; i < aNumCmdBuffers; ++i) {
|
||||
mAdvancements.push_back(0);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void Check(uint64_t aJobId, uint64_t aCmdId) {
|
||||
MaybeYieldThread();
|
||||
CriticalSectionAutoEnter lock(&mSection);
|
||||
MOZ_RELEASE_ASSERT(mAdvancements[aJobId] == aCmdId - 1);
|
||||
mAdvancements[aJobId] = aCmdId;
|
||||
}
|
||||
};
|
||||
|
||||
/// Run checks that are specific to TestSchulerJoin.
|
||||
struct JoinTestSanityCheck : public SanityChecker {
|
||||
bool mSpecialJobHasRun;
|
||||
|
||||
explicit JoinTestSanityCheck(uint64_t aNumCmdBuffers)
|
||||
: SanityChecker(aNumCmdBuffers), mSpecialJobHasRun(false) {}
|
||||
|
||||
virtual void Check(uint64_t aJobId, uint64_t aCmdId) override {
|
||||
// Job 0 is the special task executed when everything is joined after task 1
|
||||
if (aCmdId == 0) {
|
||||
MOZ_RELEASE_ASSERT(!mSpecialJobHasRun,
|
||||
"GFX: A special task has been executed.");
|
||||
mSpecialJobHasRun = true;
|
||||
for (auto advancement : mAdvancements) {
|
||||
// Because of the synchronization point (beforeFilter), all
|
||||
// task buffers should have run task 1 when task 0 is run.
|
||||
MOZ_RELEASE_ASSERT(advancement == 1,
|
||||
"GFX: task buffer has not run task 1.");
|
||||
}
|
||||
} else {
|
||||
// This check does not apply to task 0.
|
||||
SanityChecker::Check(aJobId, aCmdId);
|
||||
}
|
||||
|
||||
if (aCmdId == 2) {
|
||||
MOZ_RELEASE_ASSERT(mSpecialJobHasRun, "GFX: Special job has not run.");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class TestJob : public Job {
|
||||
public:
|
||||
TestJob(uint64_t aCmdId, uint64_t aJobId, SanityChecker* aChecker,
|
||||
SyncObject* aStart, SyncObject* aCompletion)
|
||||
: Job(aStart, aCompletion, nullptr),
|
||||
mCmdId(aCmdId),
|
||||
mCmdBufferId(aJobId),
|
||||
mSanityChecker(aChecker) {}
|
||||
|
||||
JobStatus Run() {
|
||||
MaybeYieldThread();
|
||||
mSanityChecker->Check(mCmdBufferId, mCmdId);
|
||||
MaybeYieldThread();
|
||||
return JobStatus::Complete;
|
||||
}
|
||||
|
||||
uint64_t mCmdId;
|
||||
uint64_t mCmdBufferId;
|
||||
SanityChecker* mSanityChecker;
|
||||
};
|
||||
|
||||
/// This test creates aNumCmdBuffers task buffers with sync objects set up
|
||||
/// so that all tasks will join after command 5 before a task buffer runs
|
||||
/// a special task (task 0) after which all task buffers fork again.
|
||||
/// This simulates the kind of scenario where all tiles must join at
|
||||
/// a certain point to execute, say, a filter, and fork again after the filter
|
||||
/// has been processed.
|
||||
/// The main thread is only blocked when waiting for the completion of the
|
||||
/// entire task stream (it doesn't have to wait at the filter's sync points to
|
||||
/// orchestrate it).
|
||||
static void TestSchedulerJoin(uint32_t aNumThreads, uint32_t aNumCmdBuffers) {
|
||||
JoinTestSanityCheck check(aNumCmdBuffers);
|
||||
|
||||
RefPtr<SyncObject> beforeFilter = new SyncObject(aNumCmdBuffers);
|
||||
RefPtr<SyncObject> afterFilter = new SyncObject();
|
||||
RefPtr<SyncObject> completion = new SyncObject(aNumCmdBuffers);
|
||||
|
||||
for (uint32_t i = 0; i < aNumCmdBuffers; ++i) {
|
||||
Job* t1 = new TestJob(1, i, &check, nullptr, beforeFilter);
|
||||
JobScheduler::SubmitJob(t1);
|
||||
MaybeYieldThread();
|
||||
}
|
||||
beforeFilter->FreezePrerequisites();
|
||||
|
||||
// This task buffer is executed when all other tasks have joined after task 1
|
||||
JobScheduler::SubmitJob(new TestJob(0, 0, &check, beforeFilter, afterFilter));
|
||||
afterFilter->FreezePrerequisites();
|
||||
|
||||
for (uint32_t i = 0; i < aNumCmdBuffers; ++i) {
|
||||
Job* t2 = new TestJob(2, i, &check, afterFilter, completion);
|
||||
JobScheduler::SubmitJob(t2);
|
||||
MaybeYieldThread();
|
||||
}
|
||||
completion->FreezePrerequisites();
|
||||
|
||||
JobScheduler::Join(completion);
|
||||
|
||||
MaybeYieldThread();
|
||||
|
||||
for (auto advancement : check.mAdvancements) {
|
||||
EXPECT_TRUE(advancement == 2);
|
||||
}
|
||||
}
|
||||
|
||||
/// This test creates several chains of 10 task, tasks of a given chain are
|
||||
/// executed sequentially, and chains are exectuted in parallel. This simulates
|
||||
/// the typical scenario where we want to process sequences of drawing commands
|
||||
/// for several tiles in parallel.
|
||||
static void TestSchedulerChain(uint32_t aNumThreads, uint32_t aNumCmdBuffers) {
|
||||
SanityChecker check(aNumCmdBuffers);
|
||||
|
||||
RefPtr<SyncObject> completion = new SyncObject(aNumCmdBuffers);
|
||||
|
||||
uint32_t numJobs = 10;
|
||||
|
||||
for (uint32_t i = 0; i < aNumCmdBuffers; ++i) {
|
||||
std::vector<RefPtr<SyncObject>> syncs;
|
||||
std::vector<Job*> tasks;
|
||||
syncs.reserve(numJobs);
|
||||
tasks.reserve(numJobs);
|
||||
|
||||
for (uint32_t t = 0; t < numJobs - 1; ++t) {
|
||||
syncs.push_back(new SyncObject());
|
||||
tasks.push_back(new TestJob(
|
||||
t + 1, i, &check, t == 0 ? nullptr : syncs[t - 1].get(), syncs[t]));
|
||||
syncs.back()->FreezePrerequisites();
|
||||
}
|
||||
|
||||
tasks.push_back(new TestJob(numJobs, i, &check, syncs.back(), completion));
|
||||
|
||||
if (i % 2 == 0) {
|
||||
// submit half of the tasks in order
|
||||
for (Job* task : tasks) {
|
||||
JobScheduler::SubmitJob(task);
|
||||
MaybeYieldThread();
|
||||
}
|
||||
} else {
|
||||
// ... and submit the other half in reverse order
|
||||
for (int32_t reverse = numJobs - 1; reverse >= 0; --reverse) {
|
||||
JobScheduler::SubmitJob(tasks[reverse]);
|
||||
MaybeYieldThread();
|
||||
}
|
||||
}
|
||||
}
|
||||
completion->FreezePrerequisites();
|
||||
|
||||
JobScheduler::Join(completion);
|
||||
|
||||
for (auto advancement : check.mAdvancements) {
|
||||
EXPECT_TRUE(advancement == numJobs);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace test_scheduler
|
||||
|
||||
#if !defined(MOZ_CODE_COVERAGE) || !defined(XP_WIN)
|
||||
TEST(Moz2D, JobScheduler_Shutdown)
|
||||
{
|
||||
srand(time(nullptr));
|
||||
for (uint32_t threads = 1; threads < 16; ++threads) {
|
||||
for (uint32_t i = 1; i < 1000; ++i) {
|
||||
mozilla::gfx::JobScheduler::Init(threads, threads);
|
||||
mozilla::gfx::JobScheduler::ShutDown();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST(Moz2D, JobScheduler_Join)
|
||||
{
|
||||
srand(time(nullptr));
|
||||
for (uint32_t threads = 1; threads < 8; ++threads) {
|
||||
for (uint32_t queues = 1; queues < threads; ++queues) {
|
||||
for (uint32_t buffers = 1; buffers < 100; buffers += 3) {
|
||||
mozilla::gfx::JobScheduler::Init(threads, queues);
|
||||
test_scheduler::TestSchedulerJoin(threads, buffers);
|
||||
mozilla::gfx::JobScheduler::ShutDown();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Moz2D, JobScheduler_Chain)
|
||||
{
|
||||
srand(time(nullptr));
|
||||
for (uint32_t threads = 1; threads < 8; ++threads) {
|
||||
for (uint32_t queues = 1; queues < threads; ++queues) {
|
||||
for (uint32_t buffers = 1; buffers < 100; buffers += 3) {
|
||||
mozilla::gfx::JobScheduler::Init(threads, queues);
|
||||
test_scheduler::TestSchedulerChain(threads, buffers);
|
||||
mozilla::gfx::JobScheduler::ShutDown();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -3,63 +3,25 @@
|
||||
*/
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "gtest/MozGTestBench.h"
|
||||
#include "gmock/gmock.h"
|
||||
|
||||
#include "mozilla/ArrayUtils.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "mozilla/SSE.h"
|
||||
#include "mozilla/arm.h"
|
||||
#include "qcms.h"
|
||||
#include "qcmsint.h"
|
||||
#include "transform_util.h"
|
||||
|
||||
const size_t allGBSize = 1 * 256 * 256 * 4;
|
||||
static unsigned char* createAllGB() {
|
||||
unsigned char* buff = (unsigned char*)malloc(allGBSize);
|
||||
int pos = 0;
|
||||
for (int r = 0; r < 1; r++) { // Skip all R values for speed
|
||||
for (int g = 0; g < 256; g++) {
|
||||
for (int b = 0; b < 256; b++) {
|
||||
buff[pos * 4 + 0] = r;
|
||||
buff[pos * 4 + 1] = g;
|
||||
buff[pos * 4 + 2] = b;
|
||||
buff[pos * 4 + 3] = 0x80;
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
}
|
||||
#include <cmath>
|
||||
|
||||
return buff;
|
||||
}
|
||||
/* SSEv1 is only included in non-Windows or non-x86-64-bit builds. */
|
||||
#if defined(MOZILLA_MAY_SUPPORT_SSE) && (!(defined(_MSC_VER) && defined(_M_AMD64)))
|
||||
#define QCMS_MAY_SUPPORT_SSE
|
||||
#endif
|
||||
|
||||
TEST(GfxQcms, Identity)
|
||||
{
|
||||
// XXX: This means that we can't have qcms v2 unit test
|
||||
// without changing the qcms API.
|
||||
qcms_enable_iccv4();
|
||||
|
||||
qcms_profile* input_profile = qcms_profile_sRGB();
|
||||
qcms_profile* output_profile = qcms_profile_sRGB();
|
||||
|
||||
EXPECT_FALSE(qcms_profile_is_bogus(input_profile));
|
||||
EXPECT_FALSE(qcms_profile_is_bogus(output_profile));
|
||||
|
||||
const qcms_intent intent = QCMS_INTENT_DEFAULT;
|
||||
qcms_data_type input_type = QCMS_DATA_RGBA_8;
|
||||
qcms_data_type output_type = QCMS_DATA_RGBA_8;
|
||||
|
||||
qcms_transform* transform = qcms_transform_create(
|
||||
input_profile, input_type, output_profile, output_type, intent);
|
||||
|
||||
unsigned char* data_in = createAllGB();
|
||||
;
|
||||
unsigned char* data_out = (unsigned char*)malloc(allGBSize);
|
||||
qcms_transform_data(transform, data_in, data_out, allGBSize / 4);
|
||||
|
||||
qcms_profile_release(input_profile);
|
||||
qcms_profile_release(output_profile);
|
||||
qcms_transform_release(transform);
|
||||
|
||||
free(data_in);
|
||||
free(data_out);
|
||||
}
|
||||
using namespace mozilla;
|
||||
|
||||
TEST(GfxQcms, LutInverseCrash)
|
||||
{
|
||||
@ -173,3 +135,484 @@ TEST(GfxQcms, LutInverseNonMonotonic)
|
||||
|
||||
// Make sure we don't crash, hang or let sanitizers do their magic
|
||||
}
|
||||
|
||||
static bool CmpRgbChannel(const uint8_t* aRef, const uint8_t* aTest,
|
||||
size_t aIndex) {
|
||||
return std::abs(static_cast<int8_t>(aRef[aIndex] - aTest[aIndex])) <= 1;
|
||||
}
|
||||
|
||||
template <bool kSwapRB, bool kHasAlpha>
|
||||
static bool CmpRgbBufferImpl(const uint8_t* aRefBuffer,
|
||||
const uint8_t* aTestBuffer, size_t aPixels) {
|
||||
const size_t pixelSize = kHasAlpha ? 4 : 3;
|
||||
if (memcmp(aRefBuffer, aTestBuffer, aPixels * pixelSize) == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const size_t kRIndex = kSwapRB ? 2 : 0;
|
||||
const size_t kGIndex = 1;
|
||||
const size_t kBIndex = kSwapRB ? 0 : 2;
|
||||
const size_t kAIndex = 3;
|
||||
|
||||
size_t remaining = aPixels;
|
||||
const uint8_t* ref = aRefBuffer;
|
||||
const uint8_t* test = aTestBuffer;
|
||||
while (remaining > 0) {
|
||||
if (!CmpRgbChannel(ref, test, kRIndex) ||
|
||||
!CmpRgbChannel(ref, test, kGIndex) ||
|
||||
!CmpRgbChannel(ref, test, kBIndex) ||
|
||||
(kHasAlpha && ref[kAIndex] != test[kAIndex])) {
|
||||
EXPECT_EQ(test[kRIndex], ref[kRIndex]);
|
||||
EXPECT_EQ(test[kGIndex], ref[kGIndex]);
|
||||
EXPECT_EQ(test[kBIndex], ref[kBIndex]);
|
||||
if (kHasAlpha) {
|
||||
EXPECT_EQ(test[kAIndex], ref[kAIndex]);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
--remaining;
|
||||
ref += pixelSize;
|
||||
test += pixelSize;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <bool kSwapRB, bool kHasAlpha>
|
||||
static size_t GetRgbInputBufferImpl(UniquePtr<uint8_t[]>& aOutBuffer) {
|
||||
const uint8_t colorSamples[] = {0, 5, 16, 43, 101, 127, 182, 255};
|
||||
const size_t colorSampleMax = sizeof(colorSamples) / sizeof(uint8_t);
|
||||
const size_t pixelSize = kHasAlpha ? 4 : 3;
|
||||
const size_t pixelCount = colorSampleMax * colorSampleMax * 256 * 3;
|
||||
|
||||
aOutBuffer = MakeUnique<uint8_t[]>(pixelCount * pixelSize);
|
||||
if (!aOutBuffer) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const size_t kRIndex = kSwapRB ? 2 : 0;
|
||||
const size_t kGIndex = 1;
|
||||
const size_t kBIndex = kSwapRB ? 0 : 2;
|
||||
const size_t kAIndex = 3;
|
||||
|
||||
// Sample every red pixel value with a subset of green and blue.
|
||||
uint8_t* color = aOutBuffer.get();
|
||||
for (uint16_t r = 0; r < 256; ++r) {
|
||||
for (uint8_t g : colorSamples) {
|
||||
for (uint8_t b : colorSamples) {
|
||||
color[kRIndex] = r;
|
||||
color[kGIndex] = g;
|
||||
color[kBIndex] = b;
|
||||
if (kHasAlpha) {
|
||||
color[kAIndex] = 0x80;
|
||||
}
|
||||
color += pixelSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sample every green pixel value with a subset of red and blue.
|
||||
for (uint8_t r : colorSamples) {
|
||||
for (uint16_t g = 0; g < 256; ++g) {
|
||||
for (uint8_t b : colorSamples) {
|
||||
color[kRIndex] = r;
|
||||
color[kGIndex] = g;
|
||||
color[kBIndex] = b;
|
||||
if (kHasAlpha) {
|
||||
color[kAIndex] = 0x80;
|
||||
}
|
||||
color += pixelSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sample every blue pixel value with a subset of red and green.
|
||||
for (uint8_t r : colorSamples) {
|
||||
for (uint8_t g : colorSamples) {
|
||||
for (uint16_t b = 0; b < 256; ++b) {
|
||||
color[kRIndex] = r;
|
||||
color[kGIndex] = g;
|
||||
color[kBIndex] = b;
|
||||
if (kHasAlpha) {
|
||||
color[kAIndex] = 0x80;
|
||||
}
|
||||
color += pixelSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return pixelCount;
|
||||
}
|
||||
|
||||
static size_t GetRgbInputBuffer(UniquePtr<uint8_t[]>& aOutBuffer) {
|
||||
return GetRgbInputBufferImpl<false, false>(aOutBuffer);
|
||||
}
|
||||
|
||||
static size_t GetRgbaInputBuffer(UniquePtr<uint8_t[]>& aOutBuffer) {
|
||||
return GetRgbInputBufferImpl<false, true>(aOutBuffer);
|
||||
}
|
||||
|
||||
static size_t GetBgraInputBuffer(UniquePtr<uint8_t[]>& aOutBuffer) {
|
||||
return GetRgbInputBufferImpl<true, true>(aOutBuffer);
|
||||
}
|
||||
|
||||
static bool CmpRgbBuffer(const uint8_t* aRefBuffer, const uint8_t* aTestBuffer,
|
||||
size_t aPixels) {
|
||||
return CmpRgbBufferImpl<false, false>(aRefBuffer, aTestBuffer, aPixels);
|
||||
}
|
||||
|
||||
static bool CmpRgbaBuffer(const uint8_t* aRefBuffer, const uint8_t* aTestBuffer,
|
||||
size_t aPixels) {
|
||||
return CmpRgbBufferImpl<false, true>(aRefBuffer, aTestBuffer, aPixels);
|
||||
}
|
||||
|
||||
static bool CmpBgraBuffer(const uint8_t* aRefBuffer, const uint8_t* aTestBuffer,
|
||||
size_t aPixels) {
|
||||
return CmpRgbBufferImpl<true, true>(aRefBuffer, aTestBuffer, aPixels);
|
||||
}
|
||||
|
||||
static void ClearRgbBuffer(uint8_t* aBuffer, size_t aPixels) {
|
||||
if (aBuffer) {
|
||||
memset(aBuffer, 0, aPixels * 3);
|
||||
}
|
||||
}
|
||||
|
||||
static void ClearRgbaBuffer(uint8_t* aBuffer, size_t aPixels) {
|
||||
if (aBuffer) {
|
||||
memset(aBuffer, 0, aPixels * 4);
|
||||
}
|
||||
}
|
||||
|
||||
static UniquePtr<uint8_t[]> GetRgbOutputBuffer(size_t aPixels) {
|
||||
UniquePtr<uint8_t[]> buffer = MakeUnique<uint8_t[]>(aPixels * 3);
|
||||
ClearRgbBuffer(buffer.get(), aPixels);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static UniquePtr<uint8_t[]> GetRgbaOutputBuffer(size_t aPixels) {
|
||||
UniquePtr<uint8_t[]> buffer = MakeUnique<uint8_t[]>(aPixels * 4);
|
||||
ClearRgbaBuffer(buffer.get(), aPixels);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
class GfxQcms_ProfilePairBase : public ::testing::Test {
|
||||
protected:
|
||||
GfxQcms_ProfilePairBase()
|
||||
: mInProfile(nullptr),
|
||||
mOutProfile(nullptr),
|
||||
mTransform(nullptr),
|
||||
mPixels(0),
|
||||
mStorageType(QCMS_DATA_RGB_8),
|
||||
mPrecache(false) {}
|
||||
|
||||
void TransformPrecache();
|
||||
void TransformPrecachePlatformExt();
|
||||
|
||||
void SetUp() override {
|
||||
// XXX: This means that we can't have qcms v2 unit test
|
||||
// without changing the qcms API.
|
||||
qcms_enable_iccv4();
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
if (mInProfile) {
|
||||
qcms_profile_release(mInProfile);
|
||||
}
|
||||
if (mOutProfile) {
|
||||
qcms_profile_release(mOutProfile);
|
||||
}
|
||||
if (mTransform) {
|
||||
qcms_transform_release(mTransform);
|
||||
}
|
||||
}
|
||||
|
||||
bool SetTransform(qcms_transform* aTransform) {
|
||||
if (mTransform) {
|
||||
qcms_transform_release(mTransform);
|
||||
}
|
||||
mTransform = aTransform;
|
||||
return !!mTransform;
|
||||
}
|
||||
|
||||
bool SetTransform(qcms_data_type aType) {
|
||||
return SetTransform(qcms_transform_create(mInProfile, aType, mOutProfile,
|
||||
aType, QCMS_INTENT_DEFAULT));
|
||||
}
|
||||
|
||||
bool SetBuffers(qcms_data_type aType) {
|
||||
switch (aType) {
|
||||
case QCMS_DATA_RGB_8:
|
||||
mPixels = GetRgbInputBuffer(mInput);
|
||||
mRef = GetRgbOutputBuffer(mPixels);
|
||||
mOutput = GetRgbOutputBuffer(mPixels);
|
||||
break;
|
||||
case QCMS_DATA_RGBA_8:
|
||||
mPixels = GetRgbaInputBuffer(mInput);
|
||||
mRef = GetRgbaOutputBuffer(mPixels);
|
||||
mOutput = GetRgbaOutputBuffer(mPixels);
|
||||
break;
|
||||
case QCMS_DATA_BGRA_8:
|
||||
mPixels = GetBgraInputBuffer(mInput);
|
||||
mRef = GetRgbaOutputBuffer(mPixels);
|
||||
mOutput = GetRgbaOutputBuffer(mPixels);
|
||||
break;
|
||||
default:
|
||||
MOZ_ASSERT_UNREACHABLE("Unknown type!");
|
||||
break;
|
||||
}
|
||||
|
||||
mStorageType = aType;
|
||||
return mInput && mOutput && mRef && mPixels > 0u;
|
||||
}
|
||||
|
||||
void ClearOutputBuffer() {
|
||||
switch (mStorageType) {
|
||||
case QCMS_DATA_RGB_8:
|
||||
ClearRgbBuffer(mOutput.get(), mPixels);
|
||||
break;
|
||||
case QCMS_DATA_RGBA_8:
|
||||
case QCMS_DATA_BGRA_8:
|
||||
ClearRgbaBuffer(mOutput.get(), mPixels);
|
||||
break;
|
||||
default:
|
||||
MOZ_ASSERT_UNREACHABLE("Unknown type!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ProduceRef(transform_fn_t aFn) {
|
||||
aFn(mTransform, mInput.get(), mRef.get(), mPixels);
|
||||
}
|
||||
|
||||
void CopyInputToRef() {
|
||||
size_t pixelSize = 0;
|
||||
switch (mStorageType) {
|
||||
case QCMS_DATA_RGB_8:
|
||||
pixelSize = 3;
|
||||
break;
|
||||
case QCMS_DATA_RGBA_8:
|
||||
case QCMS_DATA_BGRA_8:
|
||||
pixelSize = 4;
|
||||
break;
|
||||
default:
|
||||
MOZ_ASSERT_UNREACHABLE("Unknown type!");
|
||||
break;
|
||||
}
|
||||
|
||||
memcpy(mRef.get(), mInput.get(), mPixels * pixelSize);
|
||||
}
|
||||
|
||||
void ProduceOutput(transform_fn_t aFn) {
|
||||
ClearOutputBuffer();
|
||||
aFn(mTransform, mInput.get(), mOutput.get(), mPixels);
|
||||
}
|
||||
|
||||
bool VerifyOutput(const UniquePtr<uint8_t[]>& aBuf) {
|
||||
switch (mStorageType) {
|
||||
case QCMS_DATA_RGB_8:
|
||||
return CmpRgbBuffer(aBuf.get(), mOutput.get(), mPixels);
|
||||
case QCMS_DATA_RGBA_8:
|
||||
return CmpRgbaBuffer(aBuf.get(), mOutput.get(), mPixels);
|
||||
case QCMS_DATA_BGRA_8:
|
||||
return CmpBgraBuffer(aBuf.get(), mOutput.get(), mPixels);
|
||||
default:
|
||||
MOZ_ASSERT_UNREACHABLE("Unknown type!");
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ProduceVerifyOutput(transform_fn_t aFn) {
|
||||
ProduceOutput(aFn);
|
||||
return VerifyOutput(mRef);
|
||||
}
|
||||
|
||||
void PrecacheOutput() {
|
||||
qcms_profile_precache_output_transform(mOutProfile);
|
||||
mPrecache = true;
|
||||
}
|
||||
|
||||
qcms_profile* mInProfile;
|
||||
qcms_profile* mOutProfile;
|
||||
qcms_transform* mTransform;
|
||||
|
||||
UniquePtr<uint8_t[]> mInput;
|
||||
UniquePtr<uint8_t[]> mOutput;
|
||||
UniquePtr<uint8_t[]> mRef;
|
||||
size_t mPixels;
|
||||
qcms_data_type mStorageType;
|
||||
bool mPrecache;
|
||||
};
|
||||
|
||||
void GfxQcms_ProfilePairBase::TransformPrecache() {
|
||||
// Produce reference using interpolation and the lookup tables.
|
||||
ASSERT_FALSE(mPrecache);
|
||||
ASSERT_TRUE(SetBuffers(QCMS_DATA_RGB_8));
|
||||
ASSERT_TRUE(SetTransform(QCMS_DATA_RGB_8));
|
||||
ProduceRef(qcms_transform_data_rgb_out_lut);
|
||||
|
||||
// Produce output using lut and precaching.
|
||||
PrecacheOutput();
|
||||
ASSERT_TRUE(SetTransform(QCMS_DATA_RGB_8));
|
||||
EXPECT_TRUE(ProduceVerifyOutput(qcms_transform_data_rgb_out_lut_precache));
|
||||
}
|
||||
|
||||
void GfxQcms_ProfilePairBase::TransformPrecachePlatformExt() {
|
||||
PrecacheOutput();
|
||||
|
||||
// Verify RGB transforms.
|
||||
ASSERT_TRUE(SetBuffers(QCMS_DATA_RGB_8));
|
||||
ASSERT_TRUE(SetTransform(QCMS_DATA_RGB_8));
|
||||
ProduceRef(qcms_transform_data_rgb_out_lut_precache);
|
||||
#ifdef QCMS_MAY_SUPPORT_SSE
|
||||
if (mozilla::supports_sse()) {
|
||||
EXPECT_TRUE(ProduceVerifyOutput(qcms_transform_data_rgb_out_lut_sse1));
|
||||
}
|
||||
#endif
|
||||
#ifdef MOZILLA_MAY_SUPPORT_SSE2
|
||||
if (mozilla::supports_sse2()) {
|
||||
EXPECT_TRUE(ProduceVerifyOutput(qcms_transform_data_rgb_out_lut_sse2));
|
||||
}
|
||||
#endif
|
||||
#ifdef MOZILLA_MAY_SUPPORT_NEON
|
||||
if (mozilla::supports_neon()) {
|
||||
EXPECT_TRUE(ProduceVerifyOutput(qcms_transform_data_rgb_out_lut_neon));
|
||||
}
|
||||
#endif
|
||||
|
||||
// Verify RGBA transforms.
|
||||
ASSERT_TRUE(SetBuffers(QCMS_DATA_RGBA_8));
|
||||
ASSERT_TRUE(SetTransform(QCMS_DATA_RGBA_8));
|
||||
ProduceRef(qcms_transform_data_rgba_out_lut_precache);
|
||||
#ifdef QCMS_MAY_SUPPORT_SSE
|
||||
if (mozilla::supports_sse()) {
|
||||
EXPECT_TRUE(ProduceVerifyOutput(qcms_transform_data_rgba_out_lut_sse1));
|
||||
}
|
||||
#endif
|
||||
#ifdef MOZILLA_MAY_SUPPORT_SSE2
|
||||
if (mozilla::supports_sse2()) {
|
||||
EXPECT_TRUE(ProduceVerifyOutput(qcms_transform_data_rgba_out_lut_sse2));
|
||||
}
|
||||
#endif
|
||||
#ifdef MOZILLA_MAY_SUPPORT_NEON
|
||||
if (mozilla::supports_neon()) {
|
||||
EXPECT_TRUE(ProduceVerifyOutput(qcms_transform_data_rgba_out_lut_neon));
|
||||
}
|
||||
#endif
|
||||
|
||||
// Verify BGRA transforms.
|
||||
ASSERT_TRUE(SetBuffers(QCMS_DATA_BGRA_8));
|
||||
ASSERT_TRUE(SetTransform(QCMS_DATA_BGRA_8));
|
||||
ProduceRef(qcms_transform_data_bgra_out_lut_precache);
|
||||
#ifdef QCMS_MAY_SUPPORT_SSE
|
||||
if (mozilla::supports_sse()) {
|
||||
EXPECT_TRUE(ProduceVerifyOutput(qcms_transform_data_bgra_out_lut_sse1));
|
||||
}
|
||||
#endif
|
||||
#ifdef MOZILLA_MAY_SUPPORT_SSE2
|
||||
if (mozilla::supports_sse2()) {
|
||||
EXPECT_TRUE(ProduceVerifyOutput(qcms_transform_data_bgra_out_lut_sse2));
|
||||
}
|
||||
#endif
|
||||
#ifdef MOZILLA_MAY_SUPPORT_NEON
|
||||
if (mozilla::supports_neon()) {
|
||||
EXPECT_TRUE(ProduceVerifyOutput(qcms_transform_data_bgra_out_lut_neon));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
class GfxQcms_sRGB_To_sRGB : public GfxQcms_ProfilePairBase {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
GfxQcms_ProfilePairBase::SetUp();
|
||||
mInProfile = qcms_profile_sRGB();
|
||||
mOutProfile = qcms_profile_sRGB();
|
||||
}
|
||||
};
|
||||
|
||||
class GfxQcms_sRGB_To_SamsungSyncmaster : public GfxQcms_ProfilePairBase {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
GfxQcms_ProfilePairBase::SetUp();
|
||||
mInProfile = qcms_profile_sRGB();
|
||||
mOutProfile = qcms_profile_from_path("lcms_samsung_syncmaster.icc");
|
||||
}
|
||||
};
|
||||
|
||||
class GfxQcms_sRGB_To_ThinkpadW540 : public GfxQcms_ProfilePairBase {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
GfxQcms_ProfilePairBase::SetUp();
|
||||
mInProfile = qcms_profile_sRGB();
|
||||
mOutProfile = qcms_profile_from_path("lcms_thinkpad_w540.icc");
|
||||
}
|
||||
};
|
||||
|
||||
#define TEST_QCMS_PROFILE_F(test_fixture) \
|
||||
TEST_F(test_fixture, TransformPrecachePlatformExt) { \
|
||||
GfxQcms_ProfilePairBase::TransformPrecachePlatformExt(); \
|
||||
}
|
||||
|
||||
TEST_F(GfxQcms_sRGB_To_sRGB, TransformPrecache) {
|
||||
// TODO(aosmond): This doesn't pass for the non-identity transform. Should
|
||||
// they produce the same results?
|
||||
GfxQcms_ProfilePairBase::TransformPrecache();
|
||||
}
|
||||
|
||||
TEST_QCMS_PROFILE_F(GfxQcms_sRGB_To_sRGB)
|
||||
|
||||
TEST_F(GfxQcms_sRGB_To_sRGB, TransformIdentity) {
|
||||
PrecacheOutput();
|
||||
SetBuffers(QCMS_DATA_RGB_8);
|
||||
SetTransform(QCMS_DATA_RGB_8);
|
||||
qcms_transform_data(mTransform, mInput.get(), mOutput.get(), mPixels);
|
||||
EXPECT_TRUE(VerifyOutput(mInput));
|
||||
}
|
||||
|
||||
TEST_QCMS_PROFILE_F(GfxQcms_sRGB_To_SamsungSyncmaster)
|
||||
TEST_QCMS_PROFILE_F(GfxQcms_sRGB_To_ThinkpadW540)
|
||||
|
||||
class GfxQcmsPerf_Base : public GfxQcms_sRGB_To_ThinkpadW540 {
|
||||
protected:
|
||||
explicit GfxQcmsPerf_Base(qcms_data_type aType) { mStorageType = aType; }
|
||||
|
||||
void TransformPerf() { ProduceRef(qcms_transform_data_rgb_out_lut_precache); }
|
||||
|
||||
void TransformPlatformPerf() {
|
||||
qcms_transform_data(mTransform, mInput.get(), mRef.get(), mPixels);
|
||||
}
|
||||
|
||||
void SetUp() override {
|
||||
GfxQcms_sRGB_To_ThinkpadW540::SetUp();
|
||||
PrecacheOutput();
|
||||
SetBuffers(mStorageType);
|
||||
SetTransform(mStorageType);
|
||||
}
|
||||
};
|
||||
|
||||
class GfxQcmsPerf_Rgb : public GfxQcmsPerf_Base {
|
||||
protected:
|
||||
GfxQcmsPerf_Rgb() : GfxQcmsPerf_Base(QCMS_DATA_RGB_8) {}
|
||||
};
|
||||
|
||||
class GfxQcmsPerf_Rgba : public GfxQcmsPerf_Base {
|
||||
protected:
|
||||
GfxQcmsPerf_Rgba() : GfxQcmsPerf_Base(QCMS_DATA_RGBA_8) {}
|
||||
};
|
||||
|
||||
class GfxQcmsPerf_Bgra : public GfxQcmsPerf_Base {
|
||||
protected:
|
||||
GfxQcmsPerf_Bgra() : GfxQcmsPerf_Base(QCMS_DATA_BGRA_8) {}
|
||||
};
|
||||
|
||||
MOZ_GTEST_BENCH_F(GfxQcmsPerf_Rgb, TransformC, [this] { TransformPerf(); });
|
||||
MOZ_GTEST_BENCH_F(GfxQcmsPerf_Rgb, TransformPlatform,
|
||||
[this] { TransformPlatformPerf(); });
|
||||
MOZ_GTEST_BENCH_F(GfxQcmsPerf_Rgba, TransformC, [this] { TransformPerf(); });
|
||||
MOZ_GTEST_BENCH_F(GfxQcmsPerf_Rgba, TransformPlatform,
|
||||
[this] { TransformPlatformPerf(); });
|
||||
MOZ_GTEST_BENCH_F(GfxQcmsPerf_Bgra, TransformC, [this] { TransformPerf(); });
|
||||
MOZ_GTEST_BENCH_F(GfxQcmsPerf_Bgra, TransformPlatform,
|
||||
[this] { TransformPlatformPerf(); });
|
||||
|
@ -5,13 +5,19 @@
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "gfxTypes.h"
|
||||
#include "nsRect.h"
|
||||
#include "gfxRect.h"
|
||||
#include "mozilla/gfx/Rect.h"
|
||||
#include "mozilla/gfx/RectAbsolute.h"
|
||||
#include "mozilla/WritingModes.h"
|
||||
#ifdef XP_WIN
|
||||
# include <windows.h>
|
||||
#endif
|
||||
|
||||
using mozilla::gfx::IntRect;
|
||||
using mozilla::gfx::IntRectAbsolute;
|
||||
|
||||
template <class RectType>
|
||||
static bool TestConstructors() {
|
||||
// Create a rectangle
|
||||
|
@ -9,6 +9,8 @@
|
||||
#include "gfxASurface.h"
|
||||
#include "gfxImageSurface.h"
|
||||
|
||||
#include "nsISupportsUtils.h" // for NS_ADDREF
|
||||
|
||||
#include "cairo.h"
|
||||
|
||||
static int GetASurfaceRefCount(gfxASurface* s) {
|
||||
@ -41,8 +43,9 @@ static int TestNewSurface() {
|
||||
int failures = 0;
|
||||
int destroyed = 0;
|
||||
|
||||
RefPtr<gfxASurface> s = new gfxImageSurface(mozilla::gfx::IntSize(10, 10),
|
||||
SurfaceFormat::A8R8G8B8_UINT32);
|
||||
RefPtr<gfxASurface> s =
|
||||
new gfxImageSurface(mozilla::gfx::IntSize(10, 10),
|
||||
mozilla::gfx::SurfaceFormat::A8R8G8B8_UINT32);
|
||||
cairo_surface_t* cs = s->CairoSurface();
|
||||
|
||||
cairo_surface_set_user_data(cs, &destruction_key, &destroyed,
|
||||
|
BIN
gfx/tests/gtest/icc_profiles/lcms_samsung_syncmaster.icc
Normal file
BIN
gfx/tests/gtest/icc_profiles/lcms_samsung_syncmaster.icc
Normal file
Binary file not shown.
BIN
gfx/tests/gtest/icc_profiles/lcms_thinkpad_w540.icc
Normal file
BIN
gfx/tests/gtest/icc_profiles/lcms_thinkpad_w540.icc
Normal file
Binary file not shown.
@ -3,73 +3,87 @@
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'gfxSurfaceRefCountTest.cpp',
|
||||
'MockWidget.cpp',
|
||||
'PolygonTestUtils.cpp',
|
||||
'TestArena.cpp',
|
||||
'TestArrayView.cpp',
|
||||
'TestBSPTree.cpp',
|
||||
'TestBufferRotation.cpp',
|
||||
'TestColorNames.cpp',
|
||||
'TestGfxWidgets.cpp',
|
||||
'TestJobScheduler.cpp',
|
||||
'TestLayers.cpp',
|
||||
'TestMatrix.cpp',
|
||||
'TestMoz2D.cpp',
|
||||
'TestPolygon.cpp',
|
||||
'TestQcms.cpp',
|
||||
'TestRegion.cpp',
|
||||
'TestSkipChars.cpp',
|
||||
'TestSwizzle.cpp',
|
||||
'TestTextures.cpp',
|
||||
'TestTreeTraversal.cpp',
|
||||
"gfxSurfaceRefCountTest.cpp",
|
||||
"MockWidget.cpp",
|
||||
"PolygonTestUtils.cpp",
|
||||
"TestArena.cpp",
|
||||
"TestArrayView.cpp",
|
||||
"TestBSPTree.cpp",
|
||||
"TestBufferRotation.cpp",
|
||||
"TestColorNames.cpp",
|
||||
"TestCoord.cpp",
|
||||
"TestGfxWidgets.cpp",
|
||||
"TestLayers.cpp",
|
||||
"TestMatrix.cpp",
|
||||
"TestMoz2D.cpp",
|
||||
"TestPolygon.cpp",
|
||||
"TestQcms.cpp",
|
||||
"TestRegion.cpp",
|
||||
"TestSkipChars.cpp",
|
||||
"TestSwizzle.cpp",
|
||||
"TestTextures.cpp",
|
||||
"TestTreeTraversal.cpp",
|
||||
]
|
||||
|
||||
# skip the test on windows10-aarch64 due to perma-crash - bug 1544961
|
||||
if not(CONFIG['OS_TARGET'] == 'WINNT' and CONFIG['CPU_ARCH'] == 'aarch64'):
|
||||
if not (CONFIG["OS_TARGET"] == "WINNT" and CONFIG["CPU_ARCH"] == "aarch64"):
|
||||
UNIFIED_SOURCES += [
|
||||
'TestVsync.cpp',
|
||||
"TestVsync.cpp",
|
||||
]
|
||||
|
||||
if CONFIG['OS_TARGET'] != 'Android':
|
||||
if CONFIG["OS_TARGET"] != "Android":
|
||||
UNIFIED_SOURCES += [
|
||||
'TestCompositor.cpp',
|
||||
'TestRect.cpp',
|
||||
'TestTextureCompatibility.cpp',
|
||||
"TestCompositor.cpp",
|
||||
"TestRect.cpp",
|
||||
"TestTextureCompatibility.cpp",
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [ '/gfx/2d/unittest/%s' % p for p in [
|
||||
'TestBase.cpp',
|
||||
'TestBugs.cpp',
|
||||
'TestCairo.cpp',
|
||||
'TestPoint.cpp',
|
||||
'TestScaling.cpp',
|
||||
]]
|
||||
UNIFIED_SOURCES += [
|
||||
"/gfx/2d/unittest/%s" % p
|
||||
for p in [
|
||||
"TestBase.cpp",
|
||||
"TestBugs.cpp",
|
||||
"TestCairo.cpp",
|
||||
"TestPoint.cpp",
|
||||
"TestScaling.cpp",
|
||||
]
|
||||
]
|
||||
|
||||
# not UNIFIED_SOURCES because layout_common_table_test.cc has classes
|
||||
# in an anonymous namespace which result in a GCC error when used in
|
||||
# tests (e g. "error: 'ScriptListTableTest_TestSuccess_Test' has a field
|
||||
# 'ScriptListTableTest_TestSuccess_Test::<anonymous>' whose type uses
|
||||
# the anonymous namespace").
|
||||
SOURCES += [ '/gfx/ots/tests/%s' % p for p in [
|
||||
'cff_type2_charstring_test.cc',
|
||||
'layout_common_table_test.cc',
|
||||
]]
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'/gfx/2d',
|
||||
'/gfx/2d/unittest',
|
||||
'/gfx/config',
|
||||
'/gfx/layers',
|
||||
'/gfx/ots/src',
|
||||
'/gfx/qcms',
|
||||
SOURCES += [
|
||||
"/gfx/ots/tests/%s" % p
|
||||
for p in [
|
||||
"cff_charstring_test.cc",
|
||||
"layout_common_table_test.cc",
|
||||
]
|
||||
]
|
||||
|
||||
FINAL_LIBRARY = 'xul-gtest'
|
||||
# ICC profiles used for verifying QCMS transformations. The copyright
|
||||
# notice embedded in the profiles should be reviewed to ensure there are
|
||||
# no known restrictions on distribution.
|
||||
TEST_HARNESS_FILES.gtest += [
|
||||
"icc_profiles/lcms_samsung_syncmaster.icc",
|
||||
"icc_profiles/lcms_thinkpad_w540.icc",
|
||||
]
|
||||
|
||||
CXXFLAGS += CONFIG['MOZ_CAIRO_CFLAGS']
|
||||
include("/ipc/chromium/chromium-config.mozbuild")
|
||||
|
||||
if CONFIG['CC_TYPE'] in ('clang', 'gcc'):
|
||||
CXXFLAGS += ['-Wno-error=shadow']
|
||||
LOCAL_INCLUDES += [
|
||||
"/gfx/2d",
|
||||
"/gfx/2d/unittest",
|
||||
"/gfx/config",
|
||||
"/gfx/layers",
|
||||
"/gfx/ots/src",
|
||||
"/gfx/qcms",
|
||||
]
|
||||
|
||||
FINAL_LIBRARY = "xul-gtest"
|
||||
|
||||
CXXFLAGS += CONFIG["MOZ_CAIRO_CFLAGS"]
|
||||
|
||||
if CONFIG["CC_TYPE"] in ("clang", "gcc"):
|
||||
CXXFLAGS += ["-Wno-error=shadow"]
|
||||
|
@ -302,7 +302,7 @@ class FontList {
|
||||
* Used by child processes to ensure all the blocks are registered.
|
||||
* Returns false on failure.
|
||||
*/
|
||||
MOZ_MUST_USE bool UpdateShmBlocks();
|
||||
[[nodiscard]] bool UpdateShmBlocks();
|
||||
|
||||
/**
|
||||
* This makes a *sync* IPC call to get a shared block from the parent.
|
||||
|
@ -10,8 +10,9 @@
|
||||
#include "gfxRect.h"
|
||||
#include "gfxTypes.h"
|
||||
#include "mozilla/gfx/Matrix.h"
|
||||
#include "mozilla/gfx/Point.h"
|
||||
#include "mozilla/gfx/Rect.h"
|
||||
#include "mozilla/gfx/2D.h"
|
||||
#include "mozilla/gfx/Types.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
#include "gfxAlphaRecovery.h"
|
||||
#include "gfxImageSurface.h"
|
||||
#include "nsDebug.h"
|
||||
#include <emmintrin.h>
|
||||
|
||||
// This file should only be compiled on x86 and x64 systems. Additionally,
|
||||
|
@ -1661,10 +1661,6 @@ class GlyphBufferAzure {
|
||||
|
||||
// Render the buffered glyphs to the draw target.
|
||||
void FlushGlyphs() {
|
||||
if (mRunParams.isRTL) {
|
||||
std::reverse(mBuffer, mBuffer + mNumGlyphs);
|
||||
}
|
||||
|
||||
gfx::GlyphBuffer buf;
|
||||
buf.mGlyphs = mBuffer;
|
||||
buf.mNumGlyphs = mNumGlyphs;
|
||||
|
@ -272,7 +272,7 @@ class gfxPlatformFontList : public gfxFontInfoLoader {
|
||||
void SetupFamilyCharMap(uint32_t aGeneration,
|
||||
const mozilla::fontlist::Pointer& aFamilyPtr);
|
||||
|
||||
MOZ_MUST_USE bool InitializeFamily(mozilla::fontlist::Family* aFamily);
|
||||
[[nodiscard]] bool InitializeFamily(mozilla::fontlist::Family* aFamily);
|
||||
void InitializeFamily(uint32_t aGeneration, uint32_t aFamilyIndex);
|
||||
|
||||
// name lookup table methods
|
||||
|
@ -18,6 +18,7 @@ replay = ["api/deserialize", "ron", "serde", "smallvec/serde"]
|
||||
display_list_stats = ["api/display_list_stats"]
|
||||
serialize_program = ["serde", "webrender_build/serialize_program"]
|
||||
no_static_freetype = []
|
||||
leak_checks = []
|
||||
|
||||
[build-dependencies]
|
||||
webrender_build = { version = "0.0.1", path = "../webrender_build" }
|
||||
|
@ -1958,11 +1958,18 @@ impl ResourceCache {
|
||||
self.delete_image_template(key);
|
||||
}
|
||||
|
||||
let blob_f = |key: &BlobImageKey| { f(&key.as_image()) };
|
||||
debug_assert!(!self.resources.image_templates.images.keys().any(&f));
|
||||
debug_assert!(!self.cached_images.resources.keys().any(&f));
|
||||
debug_assert!(!self.blob_image_templates.keys().any(&blob_f));
|
||||
debug_assert!(!self.rasterized_blob_images.keys().any(&blob_f));
|
||||
#[cfg(features="leak_checks")]
|
||||
let check_leaks = true;
|
||||
#[cfg(not(features="leak_checks"))]
|
||||
let check_leaks = false;
|
||||
|
||||
if check_leaks {
|
||||
let blob_f = |key: &BlobImageKey| { f(&key.as_image()) };
|
||||
assert!(!self.resources.image_templates.images.keys().any(&f));
|
||||
assert!(!self.cached_images.resources.keys().any(&f));
|
||||
assert!(!self.blob_image_templates.keys().any(&blob_f));
|
||||
assert!(!self.rasterized_blob_images.keys().any(&blob_f));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -25,7 +25,7 @@ time = "0.1"
|
||||
crossbeam = "0.2"
|
||||
osmesa-sys = { version = "0.1.2", optional = true }
|
||||
osmesa-src = { git = "https://github.com/servo/osmesa-src", optional = true }
|
||||
webrender = {path = "../webrender", features=["capture","replay","debugger","png","profiler","no_static_freetype"]}
|
||||
webrender = {path = "../webrender", features=["capture","replay","debugger","png","profiler","no_static_freetype", "leak_checks"]}
|
||||
webrender_api = {path = "../webrender_api", features=["serialize","deserialize"]}
|
||||
winit = "0.19"
|
||||
serde = {version = "1.0", features = ["derive"] }
|
||||
|
Loading…
Reference in New Issue
Block a user