68.14.8 - gfx

This commit is contained in:
Fedor 2025-04-19 19:12:57 +03:00
parent 4efeab7ecb
commit 789a0fa277
80 changed files with 2182 additions and 2649 deletions

View File

@ -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;
}

View File

@ -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;

View File

@ -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) {}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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();
}

View File

@ -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());

View File

@ -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',

View File

@ -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;

View File

@ -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};
}

View File

@ -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);

View File

@ -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,

View File

@ -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; }

View File

@ -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;

View File

@ -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(&notifications);
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(&notifications);
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()) {

View File

@ -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;

View File

@ -31,6 +31,7 @@ struct nsTArray_RelocationStrategy<mozilla::layers::AnimationImageKeyData> {
namespace mozilla {
namespace gfx {
class SourceSurface;
class SourceSurfaceSharedData;
} // namespace gfx

View File

@ -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);
/**

View File

@ -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

View File

@ -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(

File diff suppressed because it is too large Load Diff

View File

@ -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;

View File

@ -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();
}
}

View File

@ -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,

View File

@ -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;

View File

@ -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()),

View File

@ -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

View File

@ -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) +

View File

@ -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',

View File

@ -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

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -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')

View File

@ -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");
}

View File

@ -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;
}

View File

@ -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(&regionIndexCount)) {
!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(&regionIndex) || 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,
&regionIndexCount)) {
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);

View File

@ -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);

View File

@ -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

View File

@ -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;
}

View File

@ -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,

View File

@ -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);
}

View File

@ -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"

View File

@ -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.

View File

@ -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;

View File

@ -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

View File

@ -6,7 +6,6 @@
#define NSMARGIN_H
#include "nsCoord.h"
#include "nsPoint.h"
#include "mozilla/gfx/BaseMargin.h"
#include "mozilla/gfx/Rect.h"

View File

@ -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);

View File

@ -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;
};
/*

View File

@ -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);
}

View File

@ -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);

View File

@ -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(

View File

@ -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&#x202E;<span style="-moz-binding: url('#w');"></span>B<span id="d" style="font-size: 80%; color: green;">D</span></span></body>
</html>

View File

@ -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&#x202E;<span style="-moz-binding: url('#w');"></span>B<span id="d" style="font-size: 80%; color: green;">D</span></span></body>
</html>

View File

@ -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

View File

@ -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) {}

View File

@ -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 {

View File

@ -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];

View 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

View File

@ -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();
}
}
}
}

View File

@ -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(); });

View File

@ -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

View File

@ -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,

Binary file not shown.

View File

@ -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"]

View File

@ -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.

View File

@ -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 {

View File

@ -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,

View File

@ -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;

View File

@ -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

View File

@ -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" }

View File

@ -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));
}
}
}

View File

@ -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"] }