Bug 1292870 - Record/replay clips and transforms properly in CanvasRenderingContext2D. r=Bas

This commit is contained in:
Nicolas Silva 2016-08-10 14:30:51 +02:00
Родитель 70353ca51d
Коммит be105fa438
2 изменённых файлов: 74 добавлений и 35 удалений

Просмотреть файл

@ -1529,12 +1529,17 @@ CanvasRenderingContext2D::EnsureTarget(const gfx::Rect* aCoveredRect,
// If a clip is active we don't know for sure that the next drawing command
// will really cover the entire canvas.
for (uint32_t i = 0; i < mStyleStack.Length(); i++) {
if (!mStyleStack[i].clipsPushed.IsEmpty()) {
for (const auto& style : mStyleStack) {
if (!canDiscardContent) {
break;
}
for (const auto& clipOrTransform : style.clipsAndTransforms) {
if (clipOrTransform.IsClip()) {
canDiscardContent = false;
break;
}
}
}
ScheduleStableStateCallback();
@ -1642,6 +1647,9 @@ CanvasRenderingContext2D::EnsureTarget(const gfx::Rect* aCoveredRect,
Redraw();
}
// Restore clips and transform.
mTarget->SetTransform(Matrix());
if (mTarget->GetBackendType() == gfx::BackendType::CAIRO) {
// Cairo doesn't play well with huge clips. When given a very big clip it
// will try to allocate big mask surface without taking the target
@ -1652,11 +1660,13 @@ CanvasRenderingContext2D::EnsureTarget(const gfx::Rect* aCoveredRect,
mTarget->PushClipRect(canvasRect);
}
// Restore clip and transform.
for (uint32_t i = 0; i < mStyleStack.Length(); i++) {
mTarget->SetTransform(mStyleStack[i].transform);
for (uint32_t c = 0; c < mStyleStack[i].clipsPushed.Length(); c++) {
mTarget->PushClip(mStyleStack[i].clipsPushed[c]);
for (const auto& style : mStyleStack) {
for (const auto& clipOrTransform : style.clipsAndTransforms) {
if (clipOrTransform.IsClip()) {
mTarget->PushClip(clipOrTransform.clip);
} else {
mTarget->SetTransform(clipOrTransform.transform);
}
}
}
} else {
@ -1768,11 +1778,13 @@ CanvasRenderingContext2D::ReturnTarget()
{
if (mTarget && mBufferProvider && mTarget != sErrorTarget) {
CurrentState().transform = mTarget->GetTransform();
for (uint32_t i = 0; i < mStyleStack.Length(); i++) {
for (uint32_t c = 0; c < mStyleStack[i].clipsPushed.Length(); c++) {
for (const auto& style : mStyleStack) {
for (const auto& clipOrTransform : style.clipsAndTransforms) {
if (clipOrTransform.IsClip()) {
mTarget->PopClip();
}
}
}
if (mTarget->GetBackendType() == gfx::BackendType::CAIRO) {
// With the cairo backend we pushed an extra clip rect which we have to
@ -1950,9 +1962,11 @@ CanvasRenderingContext2D::Restore()
TransformWillUpdate();
for (uint32_t i = 0; i < CurrentState().clipsPushed.Length(); i++) {
for (const auto& clipOrTransform : CurrentState().clipsAndTransforms) {
if (clipOrTransform.IsClip()) {
mTarget->PopClip();
}
}
mStyleStack.RemoveElementAt(mStyleStack.Length() - 1);
@ -1974,10 +1988,8 @@ CanvasRenderingContext2D::Scale(double aX, double aY, ErrorResult& aError)
Matrix newMatrix = mTarget->GetTransform();
newMatrix.PreScale(aX, aY);
if (!newMatrix.IsFinite()) {
return;
}
mTarget->SetTransform(newMatrix);
SetTransformInternal(newMatrix);
}
void
@ -1990,10 +2002,8 @@ CanvasRenderingContext2D::Rotate(double aAngle, ErrorResult& aError)
}
Matrix newMatrix = Matrix::Rotation(aAngle) * mTarget->GetTransform();
if (!newMatrix.IsFinite()) {
return;
}
mTarget->SetTransform(newMatrix);
SetTransformInternal(newMatrix);
}
void
@ -2007,10 +2017,8 @@ CanvasRenderingContext2D::Translate(double aX, double aY, ErrorResult& aError)
Matrix newMatrix = mTarget->GetTransform();
newMatrix.PreTranslate(aX, aY);
if (!newMatrix.IsFinite()) {
return;
}
mTarget->SetTransform(newMatrix);
SetTransformInternal(newMatrix);
}
void
@ -2026,10 +2034,8 @@ CanvasRenderingContext2D::Transform(double aM11, double aM12, double aM21,
Matrix newMatrix(aM11, aM12, aM21, aM22, aDx, aDy);
newMatrix *= mTarget->GetTransform();
if (!newMatrix.IsFinite()) {
return;
}
mTarget->SetTransform(newMatrix);
SetTransformInternal(newMatrix);
}
void
@ -2044,11 +2050,26 @@ CanvasRenderingContext2D::SetTransform(double aM11, double aM12,
return;
}
Matrix matrix(aM11, aM12, aM21, aM22, aDx, aDy);
if (!matrix.IsFinite()) {
SetTransformInternal(Matrix(aM11, aM12, aM21, aM22, aDx, aDy));
}
void
CanvasRenderingContext2D::SetTransformInternal(const Matrix& aTransform)
{
if (!aTransform.IsFinite()) {
return;
}
mTarget->SetTransform(matrix);
// Save the transform in the clip stack to be able to replay clips properly.
auto& clipsAndTransforms = CurrentState().clipsAndTransforms;
if (clipsAndTransforms.IsEmpty() || clipsAndTransforms.LastElement().IsClip()) {
clipsAndTransforms.AppendElement(ClipState(aTransform));
} else {
// If the last item is a transform we can replace it instead of appending
// a new item.
clipsAndTransforms.LastElement().transform = aTransform;
}
mTarget->SetTransform(aTransform);
}
void
@ -3118,7 +3139,7 @@ CanvasRenderingContext2D::Clip(const CanvasWindingRule& aWinding)
}
mTarget->PushClip(mPath);
CurrentState().clipsPushed.AppendElement(mPath);
CurrentState().clipsAndTransforms.AppendElement(ClipState(mPath));
}
void
@ -3133,7 +3154,7 @@ CanvasRenderingContext2D::Clip(const CanvasPath& aPath, const CanvasWindingRule&
}
mTarget->PushClip(gfxpath);
CurrentState().clipsPushed.AppendElement(gfxpath);
CurrentState().clipsAndTransforms.AppendElement(ClipState(gfxpath));
}
void

Просмотреть файл

@ -574,6 +574,8 @@ protected:
static mozilla::gfx::DrawTarget* sErrorTarget;
void SetTransformInternal(const mozilla::gfx::Matrix& aTransform);
// Some helpers. Doesn't modify a color on failure.
void SetStyleFromUnion(const StringOrCanvasGradientOrCanvasPattern& aValue,
Style aWhichStyle);
@ -917,6 +919,22 @@ protected:
bool CheckSizeForSkiaGL(mozilla::gfx::IntSize aSize);
// A clip or a transform, recorded and restored in order.
struct ClipState {
explicit ClipState(mozilla::gfx::Path* aClip)
: clip(aClip)
{}
explicit ClipState(const mozilla::gfx::Matrix& aTransform)
: transform(aTransform)
{}
bool IsClip() const { return !!clip; }
RefPtr<mozilla::gfx::Path> clip;
mozilla::gfx::Matrix transform;
};
// state stack handling
class ContextState {
public:
@ -1009,7 +1027,7 @@ protected:
return std::min(SIGMA_MAX, shadowBlur / 2.0f);
}
nsTArray<RefPtr<mozilla::gfx::Path> > clipsPushed;
nsTArray<ClipState> clipsAndTransforms;
RefPtr<gfxFontGroup> fontGroup;
nsCOMPtr<nsIAtom> fontLanguage;