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) 2005
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 of the GNU General Public License Version 2 or later (the "GPL"),
25 : * or 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 "nsSVGFilterInstance.h"
38 : #include "nsSVGUtils.h"
39 : #include "nsIDOMSVGUnitTypes.h"
40 : #include "gfxPlatform.h"
41 : #include "nsSVGFilterPaintCallback.h"
42 : #include "nsSVGFilterElement.h"
43 : #include "nsLayoutUtils.h"
44 : #include "gfxUtils.h"
45 :
46 : float
47 0 : nsSVGFilterInstance::GetPrimitiveNumber(PRUint8 aCtxType, float aValue) const
48 : {
49 : nsSVGLength2 val;
50 : val.Init(aCtxType, 0xff, aValue,
51 0 : nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER);
52 :
53 : float value;
54 0 : if (mPrimitiveUnits == nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
55 0 : value = nsSVGUtils::ObjectSpace(mTargetBBox, &val);
56 : } else {
57 0 : value = nsSVGUtils::UserSpace(mTargetFrame, &val);
58 : }
59 :
60 0 : switch (aCtxType) {
61 : case nsSVGUtils::X:
62 0 : return value * mFilterSpaceSize.width / mFilterRect.Width();
63 : case nsSVGUtils::Y:
64 0 : return value * mFilterSpaceSize.height / mFilterRect.Height();
65 : case nsSVGUtils::XY:
66 : default:
67 : return value * nsSVGUtils::ComputeNormalizedHypotenuse(
68 0 : mFilterSpaceSize.width / mFilterRect.Width(),
69 0 : mFilterSpaceSize.height / mFilterRect.Height());
70 : }
71 : }
72 :
73 : void
74 0 : nsSVGFilterInstance::ConvertLocation(float aValues[3]) const
75 : {
76 : nsSVGLength2 val[4];
77 : val[0].Init(nsSVGUtils::X, 0xff, aValues[0],
78 0 : nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER);
79 0 : val[1].Init(nsSVGUtils::Y, 0xff, aValues[1],
80 0 : nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER);
81 : // Dummy width/height values
82 : val[2].Init(nsSVGUtils::X, 0xff, 0,
83 0 : nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER);
84 : val[3].Init(nsSVGUtils::Y, 0xff, 0,
85 0 : nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER);
86 :
87 : gfxRect feArea = nsSVGUtils::GetRelativeRect(mPrimitiveUnits,
88 0 : val, mTargetBBox, mTargetFrame);
89 0 : aValues[0] = feArea.X();
90 0 : aValues[1] = feArea.Y();
91 0 : aValues[2] = GetPrimitiveNumber(nsSVGUtils::XY, aValues[2]);
92 0 : }
93 :
94 : already_AddRefed<gfxImageSurface>
95 0 : nsSVGFilterInstance::CreateImage()
96 : {
97 : nsRefPtr<gfxImageSurface> surface =
98 : new gfxImageSurface(gfxIntSize(mSurfaceRect.width, mSurfaceRect.height),
99 0 : gfxASurface::ImageFormatARGB32);
100 :
101 0 : if (!surface || surface->CairoStatus())
102 0 : return nsnull;
103 :
104 0 : surface->SetDeviceOffset(gfxPoint(-mSurfaceRect.x, -mSurfaceRect.y));
105 :
106 0 : gfxImageSurface *retval = nsnull;
107 0 : surface.swap(retval);
108 0 : return retval;
109 : }
110 :
111 : gfxRect
112 0 : nsSVGFilterInstance::UserSpaceToFilterSpace(const gfxRect& aRect) const
113 : {
114 0 : gfxRect r = aRect - mFilterRect.TopLeft();
115 0 : r.Scale(mFilterSpaceSize.width / mFilterRect.Width(),
116 0 : mFilterSpaceSize.height / mFilterRect.Height());
117 : return r;
118 : }
119 :
120 : gfxPoint
121 0 : nsSVGFilterInstance::FilterSpaceToUserSpace(const gfxPoint& aPt) const
122 : {
123 0 : return gfxPoint(aPt.x * mFilterRect.Width() / mFilterSpaceSize.width + mFilterRect.X(),
124 0 : aPt.y * mFilterRect.Height() / mFilterSpaceSize.height + mFilterRect.Y());
125 : }
126 :
127 : gfxMatrix
128 0 : nsSVGFilterInstance::GetUserSpaceToFilterSpaceTransform() const
129 : {
130 0 : gfxFloat widthScale = mFilterSpaceSize.width / mFilterRect.Width();
131 0 : gfxFloat heightScale = mFilterSpaceSize.height / mFilterRect.Height();
132 : return gfxMatrix(widthScale, 0.0f,
133 : 0.0f, heightScale,
134 0 : -mFilterRect.X() * widthScale, -mFilterRect.Y() * heightScale);
135 : }
136 :
137 : void
138 0 : nsSVGFilterInstance::ComputeFilterPrimitiveSubregion(PrimitiveInfo* aPrimitive)
139 : {
140 0 : nsSVGFE* fE = aPrimitive->mFE;
141 :
142 0 : gfxRect defaultFilterSubregion(0,0,0,0);
143 0 : if (fE->SubregionIsUnionOfRegions()) {
144 0 : for (PRUint32 i = 0; i < aPrimitive->mInputs.Length(); ++i) {
145 : defaultFilterSubregion =
146 : defaultFilterSubregion.Union(
147 0 : aPrimitive->mInputs[i]->mImage.mFilterPrimitiveSubregion);
148 : }
149 : } else {
150 : defaultFilterSubregion =
151 0 : gfxRect(0, 0, mFilterSpaceSize.width, mFilterSpaceSize.height);
152 : }
153 :
154 : gfxRect feArea = nsSVGUtils::GetRelativeRect(mPrimitiveUnits,
155 0 : &fE->mLengthAttributes[nsSVGFE::X], mTargetBBox, mTargetFrame);
156 0 : gfxRect region = UserSpaceToFilterSpace(feArea);
157 :
158 0 : if (!fE->mLengthAttributes[nsSVGFE::X].IsExplicitlySet())
159 0 : region.x = defaultFilterSubregion.X();
160 0 : if (!fE->mLengthAttributes[nsSVGFE::Y].IsExplicitlySet())
161 0 : region.y = defaultFilterSubregion.Y();
162 0 : if (!fE->mLengthAttributes[nsSVGFE::WIDTH].IsExplicitlySet())
163 0 : region.width = defaultFilterSubregion.Width();
164 0 : if (!fE->mLengthAttributes[nsSVGFE::HEIGHT].IsExplicitlySet())
165 0 : region.height = defaultFilterSubregion.Height();
166 :
167 : // We currently require filter primitive subregions to be pixel-aligned.
168 : // Following the spec, any pixel partially in the region is included
169 : // in the region.
170 0 : region.RoundOut();
171 0 : aPrimitive->mImage.mFilterPrimitiveSubregion = region;
172 0 : }
173 :
174 : nsresult
175 0 : nsSVGFilterInstance::BuildSources()
176 : {
177 0 : gfxRect filterRegion = gfxRect(0, 0, mFilterSpaceSize.width, mFilterSpaceSize.height);
178 0 : mSourceColorAlpha.mImage.mFilterPrimitiveSubregion = filterRegion;
179 0 : mSourceAlpha.mImage.mFilterPrimitiveSubregion = filterRegion;
180 :
181 0 : nsIntRect sourceBoundsInt;
182 0 : gfxRect sourceBounds = UserSpaceToFilterSpace(mTargetBBox);
183 0 : sourceBounds.RoundOut();
184 : // Detect possible float->int overflow
185 0 : if (!gfxUtils::GfxRectToIntRect(sourceBounds, &sourceBoundsInt))
186 0 : return NS_ERROR_FAILURE;
187 0 : sourceBoundsInt.UnionRect(sourceBoundsInt, mTargetBounds);
188 :
189 0 : mSourceColorAlpha.mResultBoundingBox = sourceBoundsInt;
190 0 : mSourceAlpha.mResultBoundingBox = sourceBoundsInt;
191 0 : return NS_OK;
192 : }
193 :
194 : nsresult
195 0 : nsSVGFilterInstance::BuildPrimitives()
196 : {
197 : // First build mFilterInfo. It's important that we don't change that
198 : // array after we start storing pointers to its elements!
199 0 : PRUint32 count = mFilterElement->GetChildCount();
200 : PRUint32 i;
201 0 : for (i = 0; i < count; ++i) {
202 0 : nsIContent* child = mFilterElement->GetChildAt(i);
203 0 : nsRefPtr<nsSVGFE> primitive;
204 0 : CallQueryInterface(child, (nsSVGFE**)getter_AddRefs(primitive));
205 0 : if (!primitive)
206 0 : continue;
207 :
208 0 : PrimitiveInfo* info = mPrimitives.AppendElement();
209 0 : info->mFE = primitive;
210 : }
211 :
212 : // Now fill in all the links
213 0 : nsTHashtable<ImageAnalysisEntry> imageTable;
214 0 : imageTable.Init(10);
215 :
216 0 : for (i = 0; i < mPrimitives.Length(); ++i) {
217 0 : PrimitiveInfo* info = &mPrimitives[i];
218 0 : nsSVGFE* filter = info->mFE;
219 0 : nsAutoTArray<nsSVGStringInfo,2> sources;
220 0 : filter->GetSourceImageNames(sources);
221 :
222 0 : for (PRUint32 j=0; j<sources.Length(); ++j) {
223 0 : nsAutoString str;
224 0 : sources[j].mString->GetAnimValue(str, sources[j].mElement);
225 : PrimitiveInfo* sourceInfo;
226 :
227 0 : if (str.EqualsLiteral("SourceGraphic")) {
228 0 : sourceInfo = &mSourceColorAlpha;
229 0 : } else if (str.EqualsLiteral("SourceAlpha")) {
230 0 : sourceInfo = &mSourceAlpha;
231 0 : } else if (str.EqualsLiteral("BackgroundImage") ||
232 0 : str.EqualsLiteral("BackgroundAlpha") ||
233 0 : str.EqualsLiteral("FillPaint") ||
234 0 : str.EqualsLiteral("StrokePaint")) {
235 0 : return NS_ERROR_NOT_IMPLEMENTED;
236 0 : } else if (str.EqualsLiteral("")) {
237 0 : sourceInfo = i == 0 ? &mSourceColorAlpha : &mPrimitives[i - 1];
238 : } else {
239 0 : ImageAnalysisEntry* entry = imageTable.GetEntry(str);
240 0 : if (!entry)
241 0 : return NS_ERROR_FAILURE;
242 0 : sourceInfo = entry->mInfo;
243 : }
244 :
245 0 : ++sourceInfo->mImageUsers;
246 0 : info->mInputs.AppendElement(sourceInfo);
247 : }
248 :
249 0 : ComputeFilterPrimitiveSubregion(info);
250 :
251 0 : nsAutoString str;
252 0 : filter->GetResultImageName().GetAnimValue(str, filter);
253 :
254 0 : ImageAnalysisEntry* entry = imageTable.PutEntry(str);
255 0 : if (entry) {
256 0 : entry->mInfo = info;
257 : }
258 :
259 : // The last filter primitive is the filter result, so mark it used
260 0 : if (i == mPrimitives.Length() - 1) {
261 0 : ++info->mImageUsers;
262 : }
263 : }
264 :
265 0 : return NS_OK;
266 : }
267 :
268 : void
269 0 : nsSVGFilterInstance::ComputeResultBoundingBoxes()
270 : {
271 0 : for (PRUint32 i = 0; i < mPrimitives.Length(); ++i) {
272 0 : PrimitiveInfo* info = &mPrimitives[i];
273 0 : nsAutoTArray<nsIntRect,2> sourceBBoxes;
274 0 : for (PRUint32 j = 0; j < info->mInputs.Length(); ++j) {
275 0 : sourceBBoxes.AppendElement(info->mInputs[j]->mResultBoundingBox);
276 : }
277 :
278 0 : nsIntRect resultBBox = info->mFE->ComputeTargetBBox(sourceBBoxes, *this);
279 0 : ClipToFilterSpace(&resultBBox);
280 0 : nsSVGUtils::ClipToGfxRect(&resultBBox, info->mImage.mFilterPrimitiveSubregion);
281 0 : info->mResultBoundingBox = resultBBox;
282 : }
283 0 : }
284 :
285 : void
286 0 : nsSVGFilterInstance::ComputeResultChangeBoxes()
287 : {
288 0 : for (PRUint32 i = 0; i < mPrimitives.Length(); ++i) {
289 0 : PrimitiveInfo* info = &mPrimitives[i];
290 0 : nsAutoTArray<nsIntRect,2> sourceChangeBoxes;
291 0 : for (PRUint32 j = 0; j < info->mInputs.Length(); ++j) {
292 0 : sourceChangeBoxes.AppendElement(info->mInputs[j]->mResultChangeBox);
293 : }
294 :
295 0 : nsIntRect resultChangeBox = info->mFE->ComputeChangeBBox(sourceChangeBoxes, *this);
296 0 : info->mResultChangeBox.IntersectRect(resultChangeBox, info->mResultBoundingBox);
297 : }
298 0 : }
299 :
300 : void
301 0 : nsSVGFilterInstance::ComputeNeededBoxes()
302 : {
303 0 : if (mPrimitives.IsEmpty())
304 0 : return;
305 :
306 : // In the end, we need whatever the final filter primitive will draw that
307 : // intersects the destination dirty area.
308 0 : mPrimitives[mPrimitives.Length() - 1].mResultNeededBox.IntersectRect(
309 0 : mPrimitives[mPrimitives.Length() - 1].mResultBoundingBox, mDirtyOutputRect);
310 :
311 0 : for (PRInt32 i = mPrimitives.Length() - 1; i >= 0; --i) {
312 0 : PrimitiveInfo* info = &mPrimitives[i];
313 0 : nsAutoTArray<nsIntRect,2> sourceBBoxes;
314 0 : for (PRUint32 j = 0; j < info->mInputs.Length(); ++j) {
315 0 : sourceBBoxes.AppendElement(info->mInputs[j]->mResultBoundingBox);
316 : }
317 :
318 : info->mFE->ComputeNeededSourceBBoxes(
319 0 : info->mResultNeededBox, sourceBBoxes, *this);
320 : // Update each source with the rectangle we need
321 0 : for (PRUint32 j = 0; j < info->mInputs.Length(); ++j) {
322 0 : nsIntRect* r = &info->mInputs[j]->mResultNeededBox;
323 0 : r->UnionRect(*r, sourceBBoxes[j]);
324 : // Keep everything within the filter effects region
325 0 : ClipToFilterSpace(r);
326 0 : nsSVGUtils::ClipToGfxRect(r, info->mInputs[j]->mImage.mFilterPrimitiveSubregion);
327 : }
328 : }
329 : }
330 :
331 : nsIntRect
332 0 : nsSVGFilterInstance::ComputeUnionOfAllNeededBoxes()
333 : {
334 0 : nsIntRect r;
335 : r.UnionRect(mSourceColorAlpha.mResultNeededBox,
336 0 : mSourceAlpha.mResultNeededBox);
337 0 : for (PRUint32 i = 0; i < mPrimitives.Length(); ++i) {
338 0 : r.UnionRect(r, mPrimitives[i].mResultNeededBox);
339 : }
340 : return r;
341 : }
342 :
343 : nsresult
344 0 : nsSVGFilterInstance::BuildSourceImages()
345 : {
346 0 : nsIntRect neededRect;
347 : neededRect.UnionRect(mSourceColorAlpha.mResultNeededBox,
348 0 : mSourceAlpha.mResultNeededBox);
349 0 : if (neededRect.IsEmpty())
350 0 : return NS_OK;
351 :
352 0 : nsRefPtr<gfxImageSurface> sourceColorAlpha = CreateImage();
353 0 : if (!sourceColorAlpha)
354 0 : return NS_ERROR_OUT_OF_MEMORY;
355 :
356 : {
357 : // Paint to an offscreen surface first, then copy it to an image
358 : // surface. This can be faster especially when the stuff we're painting
359 : // contains native themes.
360 : nsRefPtr<gfxASurface> offscreen =
361 0 : gfxPlatform::GetPlatform()->CreateOffscreenSurface(
362 : gfxIntSize(mSurfaceRect.width, mSurfaceRect.height),
363 0 : gfxASurface::CONTENT_COLOR_ALPHA);
364 0 : if (!offscreen || offscreen->CairoStatus())
365 0 : return NS_ERROR_OUT_OF_MEMORY;
366 0 : offscreen->SetDeviceOffset(gfxPoint(-mSurfaceRect.x, -mSurfaceRect.y));
367 :
368 0 : nsRenderingContext tmpCtx;
369 0 : tmpCtx.Init(mTargetFrame->PresContext()->DeviceContext(), offscreen);
370 0 : gfxMatrix userSpaceToFilterSpace = GetUserSpaceToFilterSpaceTransform();
371 :
372 0 : gfxRect r(neededRect.x, neededRect.y, neededRect.width, neededRect.height);
373 0 : gfxMatrix m = userSpaceToFilterSpace;
374 0 : m.Invert();
375 0 : r = m.TransformBounds(r);
376 0 : r.RoundOut();
377 0 : nsIntRect dirty;
378 0 : if (!gfxUtils::GfxRectToIntRect(r, &dirty))
379 0 : return NS_ERROR_FAILURE;
380 :
381 : // SVG graphics paint to device space, so we need to set an initial device
382 : // space to filter space transform on the gfxContext that SourceGraphic
383 : // and SourceAlpha will paint to.
384 : //
385 : // (In theory it would be better to minimize error by having filtered SVG
386 : // graphics temporarily paint to user space when painting the sources and
387 : // only set a user space to filter space transform on the gfxContext
388 : // (since that would eliminate the transform multiplications from user
389 : // space to device space and back again). However, that would make the
390 : // code more complex while being hard to get right without introducing
391 : // subtle bugs, and in practice it probably makes no real difference.)
392 0 : gfxMatrix deviceToFilterSpace = GetFilterSpaceToDeviceSpaceTransform().Invert();
393 0 : tmpCtx.ThebesContext()->Multiply(deviceToFilterSpace);
394 0 : mPaintCallback->Paint(&tmpCtx, mTargetFrame, &dirty);
395 :
396 0 : gfxContext copyContext(sourceColorAlpha);
397 0 : copyContext.SetSource(offscreen);
398 0 : copyContext.Paint();
399 : }
400 :
401 0 : if (!mSourceColorAlpha.mResultNeededBox.IsEmpty()) {
402 0 : NS_ASSERTION(mSourceColorAlpha.mImageUsers > 0, "Some user must have needed this");
403 0 : mSourceColorAlpha.mImage.mImage = sourceColorAlpha;
404 : // color model is PREMULTIPLIED SRGB by default.
405 : }
406 :
407 0 : if (!mSourceAlpha.mResultNeededBox.IsEmpty()) {
408 0 : NS_ASSERTION(mSourceAlpha.mImageUsers > 0, "Some user must have needed this");
409 :
410 0 : mSourceAlpha.mImage.mImage = CreateImage();
411 0 : if (!mSourceAlpha.mImage.mImage)
412 0 : return NS_ERROR_OUT_OF_MEMORY;
413 : // color model is PREMULTIPLIED SRGB by default.
414 :
415 : // Clear the color channel
416 0 : const PRUint32* src = reinterpret_cast<PRUint32*>(sourceColorAlpha->Data());
417 0 : PRUint32* dest = reinterpret_cast<PRUint32*>(mSourceAlpha.mImage.mImage->Data());
418 0 : for (PRInt32 y = 0; y < mSurfaceRect.height; y++) {
419 0 : PRUint32 rowOffset = (mSourceAlpha.mImage.mImage->Stride()*y) >> 2;
420 0 : for (PRInt32 x = 0; x < mSurfaceRect.width; x++) {
421 0 : dest[rowOffset + x] = src[rowOffset + x] & 0xFF000000U;
422 : }
423 : }
424 0 : mSourceAlpha.mImage.mConstantColorChannels = true;
425 : }
426 :
427 0 : return NS_OK;
428 : }
429 :
430 : void
431 0 : nsSVGFilterInstance::EnsureColorModel(PrimitiveInfo* aPrimitive,
432 : ColorModel aColorModel)
433 : {
434 0 : ColorModel currentModel = aPrimitive->mImage.mColorModel;
435 0 : if (aColorModel == currentModel)
436 0 : return;
437 :
438 0 : PRUint8* data = aPrimitive->mImage.mImage->Data();
439 0 : PRInt32 stride = aPrimitive->mImage.mImage->Stride();
440 :
441 0 : nsIntRect r = aPrimitive->mResultNeededBox - mSurfaceRect.TopLeft();
442 :
443 0 : if (currentModel.mAlphaChannel == ColorModel::PREMULTIPLIED) {
444 0 : nsSVGUtils::UnPremultiplyImageDataAlpha(data, stride, r);
445 : }
446 0 : if (aColorModel.mColorSpace != currentModel.mColorSpace) {
447 0 : if (aColorModel.mColorSpace == ColorModel::LINEAR_RGB) {
448 0 : nsSVGUtils::ConvertImageDataToLinearRGB(data, stride, r);
449 : } else {
450 0 : nsSVGUtils::ConvertImageDataFromLinearRGB(data, stride, r);
451 : }
452 : }
453 0 : if (aColorModel.mAlphaChannel == ColorModel::PREMULTIPLIED) {
454 0 : nsSVGUtils::PremultiplyImageDataAlpha(data, stride, r);
455 : }
456 0 : aPrimitive->mImage.mColorModel = aColorModel;
457 : }
458 :
459 : nsresult
460 0 : nsSVGFilterInstance::Render(gfxASurface** aOutput)
461 : {
462 0 : *aOutput = nsnull;
463 :
464 0 : nsresult rv = BuildSources();
465 0 : if (NS_FAILED(rv))
466 0 : return rv;
467 :
468 0 : rv = BuildPrimitives();
469 0 : if (NS_FAILED(rv))
470 0 : return rv;
471 :
472 0 : if (mPrimitives.IsEmpty()) {
473 : // Nothing should be rendered.
474 0 : return NS_OK;
475 : }
476 :
477 0 : ComputeResultBoundingBoxes();
478 0 : ComputeNeededBoxes();
479 : // For now, we make all surface sizes equal to the union of the
480 : // bounding boxes needed for each temporary image
481 0 : mSurfaceRect = ComputeUnionOfAllNeededBoxes();
482 :
483 0 : rv = BuildSourceImages();
484 0 : if (NS_FAILED(rv))
485 0 : return rv;
486 :
487 0 : for (PRUint32 i = 0; i < mPrimitives.Length(); ++i) {
488 0 : PrimitiveInfo* primitive = &mPrimitives[i];
489 :
490 0 : nsIntRect dataRect;
491 : // Since mResultNeededBox is clipped to the filter primitive subregion,
492 : // dataRect is also limited to the filter primitive subregion.
493 0 : if (!dataRect.IntersectRect(primitive->mResultNeededBox, mSurfaceRect))
494 0 : continue;
495 0 : dataRect -= mSurfaceRect.TopLeft();
496 :
497 0 : primitive->mImage.mImage = CreateImage();
498 0 : if (!primitive->mImage.mImage)
499 0 : return NS_ERROR_OUT_OF_MEMORY;
500 :
501 0 : nsAutoTArray<const Image*,2> inputs;
502 0 : for (PRUint32 j = 0; j < primitive->mInputs.Length(); ++j) {
503 0 : PrimitiveInfo* input = primitive->mInputs[j];
504 :
505 0 : if (!input->mImage.mImage) {
506 : // This image data is not really going to be used, but we'd better
507 : // have an image object here so the filter primitive doesn't die.
508 0 : input->mImage.mImage = CreateImage();
509 0 : if (!input->mImage.mImage)
510 0 : return NS_ERROR_OUT_OF_MEMORY;
511 : }
512 :
513 : ColorModel desiredColorModel =
514 0 : primitive->mFE->GetInputColorModel(this, j, &input->mImage);
515 0 : if (j == 0) {
516 : // the output colour model is whatever in1 is if there is an in1
517 0 : primitive->mImage.mColorModel = desiredColorModel;
518 : }
519 0 : EnsureColorModel(input, desiredColorModel);
520 0 : NS_ASSERTION(input->mImage.mImage->Stride() == primitive->mImage.mImage->Stride(),
521 : "stride mismatch");
522 0 : inputs.AppendElement(&input->mImage);
523 : }
524 :
525 0 : if (primitive->mInputs.Length() == 0) {
526 0 : primitive->mImage.mColorModel = primitive->mFE->GetOutputColorModel(this);
527 : }
528 :
529 0 : rv = primitive->mFE->Filter(this, inputs, &primitive->mImage, dataRect);
530 0 : if (NS_FAILED(rv))
531 0 : return rv;
532 :
533 0 : for (PRUint32 j = 0; j < primitive->mInputs.Length(); ++j) {
534 0 : PrimitiveInfo* input = primitive->mInputs[j];
535 0 : --input->mImageUsers;
536 0 : NS_ASSERTION(input->mImageUsers >= 0, "Bad mImageUsers tracking");
537 0 : if (input->mImageUsers == 0) {
538 : // Release the image, it's no longer needed
539 0 : input->mImage.mImage = nsnull;
540 : }
541 : }
542 : }
543 :
544 0 : PrimitiveInfo* result = &mPrimitives[mPrimitives.Length() - 1];
545 0 : ColorModel premulSRGB; // default
546 0 : EnsureColorModel(result, premulSRGB);
547 0 : gfxImageSurface* surf = nsnull;
548 0 : result->mImage.mImage.swap(surf);
549 0 : *aOutput = surf;
550 0 : return NS_OK;
551 : }
552 :
553 : nsresult
554 0 : nsSVGFilterInstance::ComputeOutputDirtyRect(nsIntRect* aDirty)
555 : {
556 0 : *aDirty = nsIntRect();
557 :
558 0 : nsresult rv = BuildSources();
559 0 : if (NS_FAILED(rv))
560 0 : return rv;
561 :
562 0 : rv = BuildPrimitives();
563 0 : if (NS_FAILED(rv))
564 0 : return rv;
565 :
566 0 : if (mPrimitives.IsEmpty()) {
567 : // Nothing should be rendered, so nothing can be dirty.
568 0 : return NS_OK;
569 : }
570 :
571 0 : ComputeResultBoundingBoxes();
572 :
573 0 : mSourceColorAlpha.mResultChangeBox = mDirtyInputRect;
574 0 : mSourceAlpha.mResultChangeBox = mDirtyInputRect;
575 0 : ComputeResultChangeBoxes();
576 :
577 0 : PrimitiveInfo* result = &mPrimitives[mPrimitives.Length() - 1];
578 0 : *aDirty = result->mResultChangeBox;
579 0 : return NS_OK;
580 : }
581 :
582 : nsresult
583 0 : nsSVGFilterInstance::ComputeSourceNeededRect(nsIntRect* aDirty)
584 : {
585 0 : nsresult rv = BuildSources();
586 0 : if (NS_FAILED(rv))
587 0 : return rv;
588 :
589 0 : rv = BuildPrimitives();
590 0 : if (NS_FAILED(rv))
591 0 : return rv;
592 :
593 0 : if (mPrimitives.IsEmpty()) {
594 : // Nothing should be rendered, so nothing is needed.
595 0 : return NS_OK;
596 : }
597 :
598 0 : ComputeResultBoundingBoxes();
599 0 : ComputeNeededBoxes();
600 : aDirty->UnionRect(mSourceColorAlpha.mResultNeededBox,
601 0 : mSourceAlpha.mResultNeededBox);
602 0 : return NS_OK;
603 : }
604 :
605 : nsresult
606 0 : nsSVGFilterInstance::ComputeOutputBBox(nsIntRect* aDirty)
607 : {
608 0 : nsresult rv = BuildSources();
609 0 : if (NS_FAILED(rv))
610 0 : return rv;
611 :
612 0 : rv = BuildPrimitives();
613 0 : if (NS_FAILED(rv))
614 0 : return rv;
615 :
616 0 : if (mPrimitives.IsEmpty()) {
617 : // Nothing should be rendered.
618 0 : *aDirty = nsIntRect();
619 0 : return NS_OK;
620 : }
621 :
622 0 : ComputeResultBoundingBoxes();
623 :
624 0 : PrimitiveInfo* result = &mPrimitives[mPrimitives.Length() - 1];
625 0 : *aDirty = result->mResultBoundingBox;
626 0 : return NS_OK;
627 : }
|