1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is the Mozilla SVG project.
16 : *
17 : * The Initial Developer of the Original Code is IBM Corporation.
18 : * Portions created by the Initial Developer are Copyright (C) 2004
19 : * the Initial Developer. All Rights Reserved.
20 : *
21 : * Contributor(s):
22 : *
23 : * Alternatively, the contents of this file may be used under the terms of
24 : * either the GNU General Public License Version 2 or later (the "GPL"), or
25 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
26 : * in which case the provisions of the GPL or the LGPL are applicable instead
27 : * of those above. If you wish to allow use of your version of this file only
28 : * under the terms of either the GPL or the LGPL, and not to allow others to
29 : * use your version of this file under the terms of the MPL, indicate your
30 : * decision by deleting the provisions above and replace them with the notice
31 : * and other provisions required by the GPL or the LGPL. If you do not delete
32 : * the provisions above, a recipient may use your version of this file under
33 : * the terms of any one of the MPL, the GPL or the LGPL.
34 : *
35 : * ***** END LICENSE BLOCK ***** */
36 :
37 : #include "nsIDocument.h"
38 : #include "nsSVGMaskFrame.h"
39 : #include "nsSVGContainerFrame.h"
40 : #include "nsSVGMaskElement.h"
41 : #include "nsSVGEffects.h"
42 : #include "gfxContext.h"
43 : #include "gfxImageSurface.h"
44 :
45 : //----------------------------------------------------------------------
46 : // Implementation
47 :
48 : nsIFrame*
49 0 : NS_NewSVGMaskFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
50 : {
51 0 : return new (aPresShell) nsSVGMaskFrame(aContext);
52 : }
53 :
54 0 : NS_IMPL_FRAMEARENA_HELPERS(nsSVGMaskFrame)
55 :
56 : already_AddRefed<gfxPattern>
57 0 : nsSVGMaskFrame::ComputeMaskAlpha(nsRenderingContext *aContext,
58 : nsIFrame* aParent,
59 : const gfxMatrix &aMatrix,
60 : float aOpacity)
61 : {
62 : // If the flag is set when we get here, it means this mask frame
63 : // has already been used painting the current mask, and the document
64 : // has a mask reference loop.
65 0 : if (mInUse) {
66 0 : NS_WARNING("Mask loop detected!");
67 0 : return nsnull;
68 : }
69 0 : AutoMaskReferencer maskRef(this);
70 :
71 0 : nsSVGMaskElement *mask = static_cast<nsSVGMaskElement*>(mContent);
72 :
73 : PRUint16 units =
74 0 : mask->mEnumAttributes[nsSVGMaskElement::MASKUNITS].GetAnimValue();
75 0 : gfxRect bbox;
76 0 : if (units == nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
77 0 : bbox = nsSVGUtils::GetBBox(aParent);
78 : }
79 :
80 : gfxRect maskArea = nsSVGUtils::GetRelativeRect(units,
81 0 : &mask->mLengthAttributes[nsSVGMaskElement::X], bbox, aParent);
82 :
83 0 : gfxContext *gfx = aContext->ThebesContext();
84 :
85 0 : gfx->Save();
86 0 : nsSVGUtils::SetClipRect(gfx, aMatrix, maskArea);
87 0 : gfxRect clipExtents = gfx->GetClipExtents();
88 0 : clipExtents.RoundOut();
89 0 : gfx->Restore();
90 :
91 : #ifdef DEBUG_tor
92 : fprintf(stderr, "clip extent: %f,%f %fx%f\n",
93 : clipExtents.X(), clipExtents.Y(),
94 : clipExtents.Width(), clipExtents.Height());
95 : #endif
96 :
97 : bool resultOverflows;
98 : gfxIntSize surfaceSize =
99 : nsSVGUtils::ConvertToSurfaceSize(gfxSize(clipExtents.Width(),
100 : clipExtents.Height()),
101 0 : &resultOverflows);
102 :
103 : // 0 disables mask, < 0 is an error
104 0 : if (surfaceSize.width <= 0 || surfaceSize.height <= 0)
105 0 : return nsnull;
106 :
107 0 : if (resultOverflows)
108 0 : return nsnull;
109 :
110 : nsRefPtr<gfxImageSurface> image =
111 0 : new gfxImageSurface(surfaceSize, gfxASurface::ImageFormatARGB32);
112 0 : if (!image || image->CairoStatus())
113 0 : return nsnull;
114 0 : image->SetDeviceOffset(-clipExtents.TopLeft());
115 :
116 0 : nsRenderingContext tmpCtx;
117 0 : tmpCtx.Init(this->PresContext()->DeviceContext(), image);
118 :
119 0 : mMaskParent = aParent;
120 0 : if (mMaskParentMatrix) {
121 0 : *mMaskParentMatrix = aMatrix;
122 : } else {
123 0 : mMaskParentMatrix = new gfxMatrix(aMatrix);
124 : }
125 :
126 0 : for (nsIFrame* kid = mFrames.FirstChild(); kid;
127 : kid = kid->GetNextSibling()) {
128 : // The CTM of each frame referencing us can be different
129 0 : nsISVGChildFrame* SVGFrame = do_QueryFrame(kid);
130 0 : if (SVGFrame) {
131 : SVGFrame->NotifySVGChanged(
132 : nsISVGChildFrame::DO_NOT_NOTIFY_RENDERING_OBSERVERS |
133 0 : nsISVGChildFrame::TRANSFORM_CHANGED);
134 : }
135 0 : nsSVGUtils::PaintFrameWithEffects(&tmpCtx, nsnull, kid);
136 : }
137 :
138 0 : PRUint8 *data = image->Data();
139 0 : PRInt32 stride = image->Stride();
140 :
141 0 : nsIntRect rect(0, 0, surfaceSize.width, surfaceSize.height);
142 0 : nsSVGUtils::UnPremultiplyImageDataAlpha(data, stride, rect);
143 0 : if (GetStyleSVG()->mColorInterpolation ==
144 : NS_STYLE_COLOR_INTERPOLATION_LINEARRGB) {
145 0 : nsSVGUtils::ConvertImageDataToLinearRGB(data, stride, rect);
146 : }
147 :
148 0 : for (PRInt32 y = 0; y < surfaceSize.height; y++)
149 0 : for (PRInt32 x = 0; x < surfaceSize.width; x++) {
150 0 : PRUint8 *pixel = data + stride * y + 4 * x;
151 :
152 : /* linearRGB -> intensity */
153 : PRUint8 alpha =
154 : static_cast<PRUint8>
155 0 : ((pixel[GFX_ARGB32_OFFSET_R] * 0.2125 +
156 0 : pixel[GFX_ARGB32_OFFSET_G] * 0.7154 +
157 0 : pixel[GFX_ARGB32_OFFSET_B] * 0.0721) *
158 0 : (pixel[GFX_ARGB32_OFFSET_A] / 255.0) * aOpacity);
159 :
160 0 : memset(pixel, alpha, 4);
161 : }
162 :
163 0 : gfxPattern *retval = new gfxPattern(image);
164 0 : NS_IF_ADDREF(retval);
165 0 : return retval;
166 : }
167 :
168 : /* virtual */ void
169 0 : nsSVGMaskFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
170 : {
171 0 : nsSVGEffects::InvalidateRenderingObservers(this);
172 0 : nsSVGMaskFrameBase::DidSetStyleContext(aOldStyleContext);
173 0 : }
174 :
175 : NS_IMETHODIMP
176 0 : nsSVGMaskFrame::AttributeChanged(PRInt32 aNameSpaceID,
177 : nsIAtom* aAttribute,
178 : PRInt32 aModType)
179 : {
180 0 : if (aNameSpaceID == kNameSpaceID_None &&
181 : (aAttribute == nsGkAtoms::x ||
182 : aAttribute == nsGkAtoms::y ||
183 : aAttribute == nsGkAtoms::width ||
184 : aAttribute == nsGkAtoms::height||
185 : aAttribute == nsGkAtoms::maskUnits ||
186 : aAttribute == nsGkAtoms::maskContentUnits)) {
187 0 : nsSVGEffects::InvalidateRenderingObservers(this);
188 : }
189 :
190 : return nsSVGMaskFrameBase::AttributeChanged(aNameSpaceID,
191 0 : aAttribute, aModType);
192 : }
193 :
194 : #ifdef DEBUG
195 : NS_IMETHODIMP
196 0 : nsSVGMaskFrame::Init(nsIContent* aContent,
197 : nsIFrame* aParent,
198 : nsIFrame* aPrevInFlow)
199 : {
200 0 : nsCOMPtr<nsIDOMSVGMaskElement> mask = do_QueryInterface(aContent);
201 0 : NS_ASSERTION(mask, "Content is not an SVG mask");
202 :
203 0 : return nsSVGMaskFrameBase::Init(aContent, aParent, aPrevInFlow);
204 : }
205 : #endif /* DEBUG */
206 :
207 : nsIAtom *
208 0 : nsSVGMaskFrame::GetType() const
209 : {
210 0 : return nsGkAtoms::svgMaskFrame;
211 : }
212 :
213 : gfxMatrix
214 0 : nsSVGMaskFrame::GetCanvasTM()
215 : {
216 0 : NS_ASSERTION(mMaskParentMatrix, "null parent matrix");
217 :
218 0 : nsSVGMaskElement *mask = static_cast<nsSVGMaskElement*>(mContent);
219 :
220 : return nsSVGUtils::AdjustMatrixForUnits(
221 0 : mMaskParentMatrix ? *mMaskParentMatrix : gfxMatrix(),
222 : &mask->mEnumAttributes[nsSVGMaskElement::MASKCONTENTUNITS],
223 0 : mMaskParent);
224 : }
225 :
|