Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/layout/svg/nsSVGMaskFrame.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
 * License, v. 2.0. If a copy of the MPL was not distributed with this
5
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
// Main header first:
8
#include "nsSVGMaskFrame.h"
9
10
// Keep others in (case-insensitive) order:
11
#include "AutoReferenceChainGuard.h"
12
#include "gfx2DGlue.h"
13
#include "gfxContext.h"
14
#include "mozilla/gfx/2D.h"
15
#include "mozilla/RefPtr.h"
16
#include "SVGObserverUtils.h"
17
#include "mozilla/dom/SVGMaskElement.h"
18
#include "mozilla/dom/SVGUnitTypesBinding.h"
19
20
using namespace mozilla;
21
using namespace mozilla::dom;
22
using namespace mozilla::dom::SVGUnitTypes_Binding;
23
using namespace mozilla::gfx;
24
using namespace mozilla::image;
25
26
static LuminanceType
27
GetLuminanceType(uint8_t aNSMaskType)
28
0
{
29
0
  switch (aNSMaskType) {
30
0
    case NS_STYLE_MASK_TYPE_LUMINANCE:
31
0
      return LuminanceType::LUMINANCE;
32
0
    case NS_STYLE_COLOR_INTERPOLATION_LINEARRGB:
33
0
      return LuminanceType::LINEARRGB;
34
0
    default:
35
0
    {
36
0
      NS_WARNING("Unknown SVG mask type, defaulting to luminance");
37
0
      return LuminanceType::LUMINANCE;
38
0
    }
39
0
  }
40
0
}
41
42
nsIFrame*
43
NS_NewSVGMaskFrame(nsIPresShell* aPresShell, ComputedStyle* aStyle)
44
0
{
45
0
  return new (aPresShell) nsSVGMaskFrame(aStyle);
46
0
}
47
48
NS_IMPL_FRAMEARENA_HELPERS(nsSVGMaskFrame)
49
50
already_AddRefed<SourceSurface>
51
nsSVGMaskFrame::GetMaskForMaskedFrame(MaskParams& aParams)
52
0
{
53
0
  // Make sure we break reference loops and over long reference chains:
54
0
  static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain;
55
0
  AutoReferenceChainGuard refChainGuard(this, &mInUse,
56
0
                                        &sRefChainLengthCounter);
57
0
  if (MOZ_UNLIKELY(!refChainGuard.Reference())) {
58
0
    // Break reference chain
59
0
    return nullptr;
60
0
  }
61
0
62
0
  gfxRect maskArea = GetMaskArea(aParams.maskedFrame);
63
0
  gfxContext* context = aParams.ctx;
64
0
  // Get the clip extents in device space:
65
0
  // Minimizing the mask surface extents (using both the current clip extents
66
0
  // and maskArea) is important for performance.
67
0
  context->Save();
68
0
  nsSVGUtils::SetClipRect(context, aParams.toUserSpace, maskArea);
69
0
  gfxRect maskSurfaceRect = context->GetClipExtents(gfxContext::eDeviceSpace);
70
0
  maskSurfaceRect.RoundOut();
71
0
  context->Restore();
72
0
73
0
  bool resultOverflows;
74
0
  IntSize maskSurfaceSize =
75
0
    nsSVGUtils::ConvertToSurfaceSize(maskSurfaceRect.Size(), &resultOverflows);
76
0
77
0
  if (resultOverflows || maskSurfaceSize.IsEmpty()) {
78
0
    // Return value other then ImgDrawResult::SUCCESS, so the caller can skip
79
0
    // painting the masked frame(aParams.maskedFrame).
80
0
    return nullptr;
81
0
  }
82
0
83
0
  uint8_t maskType;
84
0
  if (aParams.maskMode == NS_STYLE_MASK_MODE_MATCH_SOURCE) {
85
0
    maskType = StyleSVGReset()->mMaskType;
86
0
  } else {
87
0
    maskType = aParams.maskMode == NS_STYLE_MASK_MODE_LUMINANCE
88
0
               ? NS_STYLE_MASK_TYPE_LUMINANCE : NS_STYLE_MASK_TYPE_ALPHA;
89
0
  }
90
0
91
0
  RefPtr<DrawTarget> maskDT;
92
0
  if (maskType == NS_STYLE_MASK_TYPE_LUMINANCE) {
93
0
    maskDT = context->GetDrawTarget()->CreateSimilarDrawTarget(
94
0
               maskSurfaceSize, SurfaceFormat::B8G8R8A8);
95
0
  } else {
96
0
    maskDT = context->GetDrawTarget()->CreateSimilarDrawTarget(
97
0
               maskSurfaceSize, SurfaceFormat::A8);
98
0
  }
99
0
100
0
  if (!maskDT || !maskDT->IsValid()) {
101
0
    return nullptr;
102
0
  }
103
0
104
0
  Matrix maskSurfaceMatrix =
105
0
    context->CurrentMatrix() * ToMatrix(gfxMatrix::Translation(-maskSurfaceRect.TopLeft()));
106
0
107
0
  RefPtr<gfxContext> tmpCtx = gfxContext::CreateOrNull(maskDT);
108
0
  MOZ_ASSERT(tmpCtx); // already checked the draw target above
109
0
  tmpCtx->SetMatrix(maskSurfaceMatrix);
110
0
111
0
  mMatrixForChildren = GetMaskTransform(aParams.maskedFrame) *
112
0
                       aParams.toUserSpace;
113
0
114
0
  for (nsIFrame* kid = mFrames.FirstChild(); kid;
115
0
       kid = kid->GetNextSibling()) {
116
0
    // The CTM of each frame referencing us can be different
117
0
    nsSVGDisplayableFrame* SVGFrame = do_QueryFrame(kid);
118
0
    if (SVGFrame) {
119
0
      SVGFrame->NotifySVGChanged(nsSVGDisplayableFrame::TRANSFORM_CHANGED);
120
0
    }
121
0
    gfxMatrix m = mMatrixForChildren;
122
0
    if (kid->GetContent()->IsSVGElement()) {
123
0
      m = static_cast<nsSVGElement*>(kid->GetContent())->
124
0
            PrependLocalTransformsTo(m, eUserSpaceToParent);
125
0
    }
126
0
    nsSVGUtils::PaintFrameWithEffects(kid, *tmpCtx, m, aParams.imgParams);
127
0
  }
128
0
129
0
  RefPtr<SourceSurface> surface;
130
0
  if (maskType == NS_STYLE_MASK_TYPE_LUMINANCE) {
131
0
    if (StyleSVG()->mColorInterpolation ==
132
0
        NS_STYLE_COLOR_INTERPOLATION_LINEARRGB) {
133
0
      maskType = NS_STYLE_COLOR_INTERPOLATION_LINEARRGB;
134
0
    }
135
0
136
0
    RefPtr<SourceSurface> maskSnapshot =
137
0
      maskDT->IntoLuminanceSource(GetLuminanceType(maskType),
138
0
                                  aParams.opacity);
139
0
    if (!maskSnapshot) {
140
0
      return nullptr;
141
0
    }
142
0
    surface = maskSnapshot.forget();
143
0
  } else {
144
0
    maskDT->SetTransform(Matrix());
145
0
    maskDT->FillRect(Rect(0, 0, maskSurfaceSize.width, maskSurfaceSize.height), ColorPattern(Color(1.0f, 1.0f, 1.0f, aParams.opacity)), DrawOptions(1, CompositionOp::OP_IN));
146
0
    RefPtr<SourceSurface> maskSnapshot = maskDT->Snapshot();
147
0
    if (!maskSnapshot) {
148
0
      return nullptr;
149
0
    }
150
0
    surface = maskSnapshot.forget();
151
0
  }
152
0
153
0
  // Moz2D transforms in the opposite direction to Thebes
154
0
  if (!maskSurfaceMatrix.Invert()) {
155
0
    return nullptr;
156
0
  }
157
0
158
0
  *aParams.maskTransform = maskSurfaceMatrix;
159
0
  return surface.forget();
160
0
}
161
162
gfxRect
163
nsSVGMaskFrame::GetMaskArea(nsIFrame* aMaskedFrame)
164
0
{
165
0
  SVGMaskElement *maskElem = static_cast<SVGMaskElement*>(GetContent());
166
0
167
0
  uint16_t units =
168
0
    maskElem->mEnumAttributes[SVGMaskElement::MASKUNITS].GetAnimValue();
169
0
  gfxRect bbox;
170
0
  if (units == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
171
0
    bbox =
172
0
      nsSVGUtils::GetBBox(aMaskedFrame,
173
0
                          nsSVGUtils::eUseFrameBoundsForOuterSVG |
174
0
                          nsSVGUtils::eBBoxIncludeFillGeometry);
175
0
  }
176
0
177
0
  // Bounds in the user space of aMaskedFrame
178
0
  gfxRect maskArea = nsSVGUtils::GetRelativeRect(units,
179
0
                       &maskElem->mLengthAttributes[SVGMaskElement::ATTR_X],
180
0
                       bbox, aMaskedFrame);
181
0
182
0
  return maskArea;
183
0
}
184
185
nsresult
186
nsSVGMaskFrame::AttributeChanged(int32_t  aNameSpaceID,
187
                                 nsAtom* aAttribute,
188
                                 int32_t  aModType)
189
0
{
190
0
  if (aNameSpaceID == kNameSpaceID_None &&
191
0
      (aAttribute == nsGkAtoms::x ||
192
0
       aAttribute == nsGkAtoms::y ||
193
0
       aAttribute == nsGkAtoms::width ||
194
0
       aAttribute == nsGkAtoms::height||
195
0
       aAttribute == nsGkAtoms::maskUnits ||
196
0
       aAttribute == nsGkAtoms::maskContentUnits)) {
197
0
    SVGObserverUtils::InvalidateDirectRenderingObservers(this);
198
0
  }
199
0
200
0
  return nsSVGContainerFrame::AttributeChanged(aNameSpaceID,
201
0
                                               aAttribute, aModType);
202
0
}
203
204
#ifdef DEBUG
205
void
206
nsSVGMaskFrame::Init(nsIContent*       aContent,
207
                     nsContainerFrame* aParent,
208
                     nsIFrame*         aPrevInFlow)
209
{
210
  NS_ASSERTION(aContent->IsSVGElement(nsGkAtoms::mask),
211
               "Content is not an SVG mask");
212
213
  nsSVGContainerFrame::Init(aContent, aParent, aPrevInFlow);
214
}
215
#endif /* DEBUG */
216
217
gfxMatrix
218
nsSVGMaskFrame::GetCanvasTM()
219
0
{
220
0
  return mMatrixForChildren;
221
0
}
222
223
gfxMatrix
224
nsSVGMaskFrame::GetMaskTransform(nsIFrame* aMaskedFrame)
225
0
{
226
0
  SVGMaskElement *content = static_cast<SVGMaskElement*>(GetContent());
227
0
228
0
  nsSVGEnum* maskContentUnits =
229
0
    &content->mEnumAttributes[SVGMaskElement::MASKCONTENTUNITS];
230
0
231
0
  uint32_t flags =
232
0
    nsSVGUtils::eBBoxIncludeFillGeometry |
233
0
    (aMaskedFrame->StyleBorder()->mBoxDecorationBreak == StyleBoxDecorationBreak::Clone
234
0
      ? nsSVGUtils::eIncludeOnlyCurrentFrameForNonSVGElement
235
0
      : 0);
236
0
237
0
  return nsSVGUtils::AdjustMatrixForUnits(gfxMatrix(), maskContentUnits,
238
0
                                          aMaskedFrame, flags);
239
0
}