Bug 470455 - test_database_sync_embed_visits.js leaks, r=sdwilsh
[wine-gecko.git] / layout / svg / base / src / nsSVGFilterInstance.cpp
blob64618ba6a1bb2cf896ff3523f49eb04069ded809
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
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/
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.
15 * The Original Code is the Mozilla SVG project.
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.
21 * Contributor(s):
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.
35 * ***** END LICENSE BLOCK ***** */
37 #include "nsSVGFilterInstance.h"
38 #include "nsSVGUtils.h"
39 #include "nsIDOMSVGUnitTypes.h"
40 #include "nsSVGMatrix.h"
41 #include "gfxPlatform.h"
42 #include "nsSVGFilterPaintCallback.h"
43 #include "nsSVGFilterElement.h"
45 static double Square(double aX)
47 return aX*aX;
50 float
51 nsSVGFilterInstance::GetPrimitiveLength(nsSVGLength2 *aLength) const
53 float value;
54 if (mPrimitiveUnits == nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
55 value = nsSVGUtils::ObjectSpace(mTargetBBox, aLength);
56 } else {
57 value = nsSVGUtils::UserSpace(mTargetFrame, aLength);
60 switch (aLength->GetCtxType()) {
61 case nsSVGUtils::X:
62 return value * mFilterSpaceSize.width / mFilterRect.Width();
63 case nsSVGUtils::Y:
64 return value * mFilterSpaceSize.height / mFilterRect.Height();
65 case nsSVGUtils::XY:
66 default:
67 return value *
68 sqrt(Square(mFilterSpaceSize.width) + Square(mFilterSpaceSize.height)) /
69 sqrt(Square(mFilterRect.Width()) + Square(mFilterRect.Height()));
73 already_AddRefed<gfxImageSurface>
74 nsSVGFilterInstance::CreateImage()
76 nsRefPtr<gfxImageSurface> surface =
77 new gfxImageSurface(gfxIntSize(mSurfaceRect.width, mSurfaceRect.height),
78 gfxASurface::ImageFormatARGB32);
80 if (!surface || surface->CairoStatus())
81 return nsnull;
83 surface->SetDeviceOffset(gfxPoint(-mSurfaceRect.x, -mSurfaceRect.y));
85 gfxImageSurface *retval = nsnull;
86 surface.swap(retval);
87 return retval;
90 gfxRect
91 nsSVGFilterInstance::UserSpaceToFilterSpace(const gfxRect& aRect) const
93 gfxRect r = aRect - mFilterRect.TopLeft();
94 r.Scale(mFilterSpaceSize.width / mFilterRect.Width(),
95 mFilterSpaceSize.height / mFilterRect.Height());
96 return r;
99 already_AddRefed<nsIDOMSVGMatrix>
100 nsSVGFilterInstance::GetUserSpaceToFilterSpaceTransform() const
102 nsCOMPtr<nsIDOMSVGMatrix> filterTransform;
103 gfxFloat widthScale = mFilterSpaceSize.width / mFilterRect.Width();
104 gfxFloat heightScale = mFilterSpaceSize.height / mFilterRect.Height();
105 NS_NewSVGMatrix(getter_AddRefs(filterTransform),
106 widthScale, 0.0f,
107 0.0f, heightScale,
108 -mFilterRect.X() * widthScale,
109 -mFilterRect.Y() * heightScale);
110 return filterTransform.forget();
113 void
114 nsSVGFilterInstance::ComputeFilterPrimitiveSubregion(PrimitiveInfo* aPrimitive)
116 nsSVGFE* fE = aPrimitive->mFE;
118 gfxRect defaultFilterSubregion(0,0,0,0);
119 if (fE->SubregionIsUnionOfRegions()) {
120 for (PRUint32 i = 0; i < aPrimitive->mInputs.Length(); ++i) {
121 defaultFilterSubregion =
122 defaultFilterSubregion.Union(
123 aPrimitive->mInputs[i]->mImage.mFilterPrimitiveSubregion);
125 } else {
126 defaultFilterSubregion =
127 gfxRect(0, 0, mFilterSpaceSize.width, mFilterSpaceSize.height);
130 gfxRect feArea = nsSVGUtils::GetRelativeRect(mPrimitiveUnits,
131 &fE->mLengthAttributes[nsSVGFE::X], mTargetBBox, mTargetFrame);
132 gfxRect region = UserSpaceToFilterSpace(feArea);
134 if (!fE->HasAttr(kNameSpaceID_None, nsGkAtoms::x))
135 region.pos.x = defaultFilterSubregion.X();
136 if (!fE->HasAttr(kNameSpaceID_None, nsGkAtoms::y))
137 region.pos.y = defaultFilterSubregion.Y();
138 if (!fE->HasAttr(kNameSpaceID_None, nsGkAtoms::width))
139 region.size.width = defaultFilterSubregion.Width();
140 if (!fE->HasAttr(kNameSpaceID_None, nsGkAtoms::height))
141 region.size.height = defaultFilterSubregion.Height();
143 // We currently require filter primitive subregions to be pixel-aligned.
144 // Following the spec, any pixel partially in the region is included
145 // in the region.
146 region.RoundOut();
147 aPrimitive->mImage.mFilterPrimitiveSubregion = region;
150 nsresult
151 nsSVGFilterInstance::BuildSources()
153 gfxRect filterRegion = gfxRect(0, 0, mFilterSpaceSize.width, mFilterSpaceSize.height);
154 mSourceColorAlpha.mImage.mFilterPrimitiveSubregion = filterRegion;
155 mSourceAlpha.mImage.mFilterPrimitiveSubregion = filterRegion;
157 nsIntRect sourceBoundsInt;
158 if (mTargetBBox) {
159 float x, y, w, h;
160 mTargetBBox->GetX(&x);
161 mTargetBBox->GetY(&y);
162 mTargetBBox->GetWidth(&w);
163 mTargetBBox->GetHeight(&h);
165 gfxRect sourceBounds = UserSpaceToFilterSpace(gfxRect(x, y, w, h));
167 sourceBounds.RoundOut();
168 // Detect possible float->int overflow
169 if (NS_FAILED(nsSVGUtils::GfxRectToIntRect(sourceBounds, &sourceBoundsInt)))
170 return NS_ERROR_FAILURE;
173 mSourceColorAlpha.mResultBoundingBox = sourceBoundsInt;
174 mSourceAlpha.mResultBoundingBox = sourceBoundsInt;
175 return NS_OK;
178 nsresult
179 nsSVGFilterInstance::BuildPrimitives()
181 // First build mFilterInfo. It's important that we don't change that
182 // array after we start storing pointers to its elements!
183 PRUint32 count = mFilterElement->GetChildCount();
184 PRUint32 i;
185 for (i = 0; i < count; ++i) {
186 nsIContent* child = mFilterElement->GetChildAt(i);
187 nsRefPtr<nsSVGFE> primitive;
188 CallQueryInterface(child, (nsSVGFE**)getter_AddRefs(primitive));
189 if (!primitive)
190 continue;
192 PrimitiveInfo* info = mPrimitives.AppendElement();
193 info->mFE = primitive;
196 // Now fill in all the links
197 nsTHashtable<ImageAnalysisEntry> imageTable;
198 imageTable.Init(10);
200 for (i = 0; i < mPrimitives.Length(); ++i) {
201 PrimitiveInfo* info = &mPrimitives[i];
202 nsSVGFE* filter = info->mFE;
203 nsAutoTArray<nsSVGString*,2> sources;
204 filter->GetSourceImageNames(&sources);
206 for (PRUint32 j=0; j<sources.Length(); ++j) {
207 const nsString& str = sources[j]->GetAnimValue();
208 PrimitiveInfo* sourceInfo;
210 if (str.EqualsLiteral("SourceGraphic")) {
211 sourceInfo = &mSourceColorAlpha;
212 } else if (str.EqualsLiteral("SourceAlpha")) {
213 sourceInfo = &mSourceAlpha;
214 } else if (str.EqualsLiteral("BackgroundImage") ||
215 str.EqualsLiteral("BackgroundAlpha") ||
216 str.EqualsLiteral("FillPaint") ||
217 str.EqualsLiteral("StrokePaint")) {
218 return NS_ERROR_NOT_IMPLEMENTED;
219 } else if (str.EqualsLiteral("")) {
220 sourceInfo = i == 0 ? &mSourceColorAlpha : &mPrimitives[i - 1];
221 } else {
222 ImageAnalysisEntry* entry = imageTable.GetEntry(str);
223 if (!entry)
224 return NS_ERROR_FAILURE;
225 sourceInfo = entry->mInfo;
228 ++sourceInfo->mImageUsers;
229 info->mInputs.AppendElement(sourceInfo);
232 ComputeFilterPrimitiveSubregion(info);
234 ImageAnalysisEntry* entry =
235 imageTable.PutEntry(filter->GetResultImageName()->GetAnimValue());
236 if (entry) {
237 entry->mInfo = info;
240 // The last filter primitive is the filter result, so mark it used
241 if (i == mPrimitives.Length() - 1) {
242 ++info->mImageUsers;
246 return NS_OK;
249 void
250 nsSVGFilterInstance::ComputeResultBoundingBoxes()
252 for (PRUint32 i = 0; i < mPrimitives.Length(); ++i) {
253 PrimitiveInfo* info = &mPrimitives[i];
254 nsAutoTArray<nsIntRect,2> sourceBBoxes;
255 for (PRUint32 j = 0; j < info->mInputs.Length(); ++j) {
256 sourceBBoxes.AppendElement(info->mInputs[j]->mResultBoundingBox);
259 nsIntRect resultBBox = info->mFE->ComputeTargetBBox(sourceBBoxes, *this);
260 ClipToFilterSpace(&resultBBox);
261 nsSVGUtils::ClipToGfxRect(&resultBBox, info->mImage.mFilterPrimitiveSubregion);
262 info->mResultBoundingBox = resultBBox;
266 void
267 nsSVGFilterInstance::ComputeResultChangeBoxes()
269 for (PRUint32 i = 0; i < mPrimitives.Length(); ++i) {
270 PrimitiveInfo* info = &mPrimitives[i];
271 nsAutoTArray<nsIntRect,2> sourceChangeBoxes;
272 for (PRUint32 j = 0; j < info->mInputs.Length(); ++j) {
273 sourceChangeBoxes.AppendElement(info->mInputs[j]->mResultChangeBox);
276 nsIntRect resultChangeBox = info->mFE->ComputeChangeBBox(sourceChangeBoxes, *this);
277 info->mResultChangeBox.IntersectRect(resultChangeBox, info->mResultBoundingBox);
281 void
282 nsSVGFilterInstance::ComputeNeededBoxes()
284 if (mPrimitives.IsEmpty())
285 return;
287 // In the end, we need whatever the final filter primitive will draw that
288 // intersects the destination dirty area.
289 mPrimitives[mPrimitives.Length() - 1].mResultNeededBox.IntersectRect(
290 mPrimitives[mPrimitives.Length() - 1].mResultBoundingBox, mDirtyOutputRect);
292 for (PRInt32 i = mPrimitives.Length() - 1; i >= 0; --i) {
293 PrimitiveInfo* info = &mPrimitives[i];
294 nsAutoTArray<nsIntRect,2> sourceBBoxes;
295 for (PRUint32 j = 0; j < info->mInputs.Length(); ++j) {
296 sourceBBoxes.AppendElement(info->mInputs[j]->mResultBoundingBox);
299 info->mFE->ComputeNeededSourceBBoxes(
300 info->mResultNeededBox, sourceBBoxes, *this);
301 // Update each source with the rectangle we need
302 for (PRUint32 j = 0; j < info->mInputs.Length(); ++j) {
303 nsIntRect* r = &info->mInputs[j]->mResultNeededBox;
304 r->UnionRect(*r, sourceBBoxes[j]);
305 // Keep everything within the filter effects region
306 ClipToFilterSpace(r);
307 nsSVGUtils::ClipToGfxRect(r, info->mInputs[j]->mImage.mFilterPrimitiveSubregion);
312 nsIntRect
313 nsSVGFilterInstance::ComputeUnionOfAllNeededBoxes()
315 nsIntRect r;
316 r.UnionRect(mSourceColorAlpha.mResultNeededBox,
317 mSourceAlpha.mResultNeededBox);
318 for (PRUint32 i = 0; i < mPrimitives.Length(); ++i) {
319 r.UnionRect(r, mPrimitives[i].mResultNeededBox);
321 return r;
324 nsresult
325 nsSVGFilterInstance::BuildSourceImages()
327 nsIntRect neededRect;
328 neededRect.UnionRect(mSourceColorAlpha.mResultNeededBox,
329 mSourceAlpha.mResultNeededBox);
330 if (neededRect.IsEmpty())
331 return NS_OK;
333 nsRefPtr<gfxImageSurface> sourceColorAlpha = CreateImage();
334 if (!sourceColorAlpha)
335 return NS_ERROR_OUT_OF_MEMORY;
338 // Paint to an offscreen surface first, then copy it to an image
339 // surface. This can be faster especially when the stuff we're painting
340 // contains native themes.
341 nsRefPtr<gfxASurface> offscreen =
342 gfxPlatform::GetPlatform()->CreateOffscreenSurface(
343 gfxIntSize(mSurfaceRect.width, mSurfaceRect.height),
344 gfxASurface::ImageFormatARGB32);
345 if (!offscreen || offscreen->CairoStatus())
346 return NS_ERROR_OUT_OF_MEMORY;
347 offscreen->SetDeviceOffset(gfxPoint(-mSurfaceRect.x, -mSurfaceRect.y));
349 nsSVGRenderState tmpState(offscreen);
350 nsCOMPtr<nsIDOMSVGMatrix> userSpaceToFilterSpaceTransform
351 = GetUserSpaceToFilterSpaceTransform();
352 if (!userSpaceToFilterSpaceTransform)
353 return NS_ERROR_OUT_OF_MEMORY;
354 gfxMatrix userSpaceToFilterSpace =
355 nsSVGUtils::ConvertSVGMatrixToThebes(userSpaceToFilterSpaceTransform);
357 gfxRect r(neededRect.x, neededRect.y, neededRect.width, neededRect.height);
358 gfxMatrix m = userSpaceToFilterSpace;
359 m.Invert();
360 r = m.TransformBounds(r);
361 r.RoundOut();
362 nsIntRect dirty;
363 nsresult rv = nsSVGUtils::GfxRectToIntRect(r, &dirty);
364 if (NS_FAILED(rv))
365 return rv;
367 tmpState.GetGfxContext()->Multiply(userSpaceToFilterSpace);
368 mPaintCallback->Paint(&tmpState, mTargetFrame, &dirty);
370 gfxContext copyContext(sourceColorAlpha);
371 copyContext.SetSource(offscreen);
372 copyContext.Paint();
375 if (!mSourceColorAlpha.mResultNeededBox.IsEmpty()) {
376 NS_ASSERTION(mSourceColorAlpha.mImageUsers > 0, "Some user must have needed this");
377 mSourceColorAlpha.mImage.mImage = sourceColorAlpha;
378 // color model is PREMULTIPLIED SRGB by default.
381 if (!mSourceAlpha.mResultNeededBox.IsEmpty()) {
382 NS_ASSERTION(mSourceAlpha.mImageUsers > 0, "Some user must have needed this");
384 mSourceAlpha.mImage.mImage = CreateImage();
385 if (!mSourceAlpha.mImage.mImage)
386 return NS_ERROR_OUT_OF_MEMORY;
387 // color model is PREMULTIPLIED SRGB by default.
389 // Clear the color channel
390 const PRUint32* src = reinterpret_cast<PRUint32*>(sourceColorAlpha->Data());
391 PRUint32* dest = reinterpret_cast<PRUint32*>(mSourceAlpha.mImage.mImage->Data());
392 for (PRInt32 y = 0; y < mSurfaceRect.height; y++) {
393 PRUint32 rowOffset = (mSourceAlpha.mImage.mImage->Stride()*y) >> 2;
394 for (PRInt32 x = 0; x < mSurfaceRect.width; x++) {
395 dest[rowOffset + x] = src[rowOffset + x] & 0xFF000000U;
398 mSourceAlpha.mImage.mConstantColorChannels = PR_TRUE;
401 return NS_OK;
404 void
405 nsSVGFilterInstance::EnsureColorModel(PrimitiveInfo* aPrimitive,
406 ColorModel aColorModel)
408 ColorModel currentModel = aPrimitive->mImage.mColorModel;
409 if (aColorModel == currentModel)
410 return;
412 PRUint8* data = aPrimitive->mImage.mImage->Data();
413 PRInt32 stride = aPrimitive->mImage.mImage->Stride();
415 nsIntRect r = aPrimitive->mResultNeededBox - mSurfaceRect.TopLeft();
417 if (currentModel.mAlphaChannel == ColorModel::PREMULTIPLIED) {
418 nsSVGUtils::UnPremultiplyImageDataAlpha(data, stride, r);
420 if (aColorModel.mColorSpace != currentModel.mColorSpace) {
421 if (aColorModel.mColorSpace == ColorModel::LINEAR_RGB) {
422 nsSVGUtils::ConvertImageDataToLinearRGB(data, stride, r);
423 } else {
424 nsSVGUtils::ConvertImageDataFromLinearRGB(data, stride, r);
427 if (aColorModel.mAlphaChannel == ColorModel::PREMULTIPLIED) {
428 nsSVGUtils::PremultiplyImageDataAlpha(data, stride, r);
430 aPrimitive->mImage.mColorModel = aColorModel;
433 nsresult
434 nsSVGFilterInstance::Render(gfxASurface** aOutput)
436 *aOutput = nsnull;
438 nsresult rv = BuildSources();
439 if (NS_FAILED(rv))
440 return rv;
442 rv = BuildPrimitives();
443 if (NS_FAILED(rv))
444 return rv;
446 if (mPrimitives.IsEmpty()) {
447 // Nothing should be rendered.
448 return NS_OK;
451 ComputeResultBoundingBoxes();
452 ComputeNeededBoxes();
453 // For now, we make all surface sizes equal to the union of the
454 // bounding boxes needed for each temporary image
455 mSurfaceRect = ComputeUnionOfAllNeededBoxes();
457 rv = BuildSourceImages();
458 if (NS_FAILED(rv))
459 return rv;
461 for (PRUint32 i = 0; i < mPrimitives.Length(); ++i) {
462 PrimitiveInfo* primitive = &mPrimitives[i];
464 nsIntRect dataRect;
465 // Since mResultNeededBox is clipped to the filter primitive subregion,
466 // dataRect is also limited to the filter primitive subregion.
467 if (!dataRect.IntersectRect(primitive->mResultNeededBox, mSurfaceRect))
468 continue;
469 dataRect -= mSurfaceRect.TopLeft();
471 primitive->mImage.mImage = CreateImage();
472 if (!primitive->mImage.mImage)
473 return NS_ERROR_OUT_OF_MEMORY;
475 nsAutoTArray<const Image*,2> inputs;
476 for (PRUint32 j = 0; j < primitive->mInputs.Length(); ++j) {
477 PrimitiveInfo* input = primitive->mInputs[j];
479 if (!input->mImage.mImage) {
480 // This image data is not really going to be used, but we'd better
481 // have an image object here so the filter primitive doesn't die.
482 input->mImage.mImage = CreateImage();
483 if (!input->mImage.mImage)
484 return NS_ERROR_OUT_OF_MEMORY;
487 ColorModel desiredColorModel =
488 primitive->mFE->GetInputColorModel(this, j, &input->mImage);
489 EnsureColorModel(input, desiredColorModel);
490 NS_ASSERTION(input->mImage.mImage->Stride() == primitive->mImage.mImage->Stride(),
491 "stride mismatch");
492 inputs.AppendElement(&input->mImage);
495 primitive->mImage.mColorModel = primitive->mFE->GetOutputColorModel(this);
497 rv = primitive->mFE->Filter(this, inputs, &primitive->mImage, dataRect);
498 if (NS_FAILED(rv))
499 return rv;
501 for (PRUint32 j = 0; j < primitive->mInputs.Length(); ++j) {
502 PrimitiveInfo* input = primitive->mInputs[j];
503 --input->mImageUsers;
504 NS_ASSERTION(input->mImageUsers >= 0, "Bad mImageUsers tracking");
505 if (input->mImageUsers == 0) {
506 // Release the image, it's no longer needed
507 input->mImage.mImage = nsnull;
512 PrimitiveInfo* result = &mPrimitives[mPrimitives.Length() - 1];
513 ColorModel premulSRGB; // default
514 EnsureColorModel(result, premulSRGB);
515 gfxImageSurface* surf = nsnull;
516 result->mImage.mImage.swap(surf);
517 *aOutput = surf;
518 return NS_OK;
521 nsresult
522 nsSVGFilterInstance::ComputeOutputDirtyRect(nsIntRect* aDirty)
524 *aDirty = nsIntRect();
526 nsresult rv = BuildSources();
527 if (NS_FAILED(rv))
528 return rv;
530 rv = BuildPrimitives();
531 if (NS_FAILED(rv))
532 return rv;
534 if (mPrimitives.IsEmpty()) {
535 // Nothing should be rendered, so nothing can be dirty.
536 return NS_OK;
539 ComputeResultBoundingBoxes();
541 mSourceColorAlpha.mResultChangeBox = mDirtyInputRect;
542 mSourceAlpha.mResultChangeBox = mDirtyInputRect;
543 ComputeResultChangeBoxes();
545 PrimitiveInfo* result = &mPrimitives[mPrimitives.Length() - 1];
546 *aDirty = result->mResultChangeBox;
547 return NS_OK;
550 nsresult
551 nsSVGFilterInstance::ComputeSourceNeededRect(nsIntRect* aDirty)
553 nsresult rv = BuildSources();
554 if (NS_FAILED(rv))
555 return rv;
557 rv = BuildPrimitives();
558 if (NS_FAILED(rv))
559 return rv;
561 if (mPrimitives.IsEmpty()) {
562 // Nothing should be rendered, so nothing is needed.
563 return NS_OK;
566 ComputeResultBoundingBoxes();
567 ComputeNeededBoxes();
568 aDirty->UnionRect(mSourceColorAlpha.mResultNeededBox,
569 mSourceAlpha.mResultNeededBox);
570 return NS_OK;
573 nsresult
574 nsSVGFilterInstance::ComputeOutputBBox(nsIntRect* aDirty)
576 nsresult rv = BuildSources();
577 if (NS_FAILED(rv))
578 return rv;
580 rv = BuildPrimitives();
581 if (NS_FAILED(rv))
582 return rv;
584 if (mPrimitives.IsEmpty()) {
585 // Nothing should be rendered.
586 *aDirty = nsIntRect();
587 return NS_OK;
590 ComputeResultBoundingBoxes();
592 PrimitiveInfo* result = &mPrimitives[mPrimitives.Length() - 1];
593 *aDirty = result->mResultBoundingBox;
594 return NS_OK;