nsresult nsSVGPatternFrame::ConstructCTM(nsIDOMSVGMatrix **aCTM, nsIDOMSVGRect *callerBBox, nsIDOMSVGMatrix *callerCTM) { nsCOMPtr<nsIDOMSVGMatrix> tCTM, tempTM; // Begin by handling the objectBoundingBox conversion since // this must be handled in the CTM PRUint16 type = GetPatternContentUnits(); if (type == nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) { // Use the bounding box float width, height; callerBBox->GetWidth(&width); callerBBox->GetHeight(&height); NS_NewSVGMatrix(getter_AddRefs(tCTM), width, 0.0f, 0.0f, height, 0.0f, 0.0f); } else { float scale = nsSVGUtils::MaxExpansion(callerCTM); NS_NewSVGMatrix(getter_AddRefs(tCTM), scale, 0, 0, scale, 0, 0); } // Do we have a viewbox? nsCOMPtr<nsIDOMSVGRect> viewRect; GetViewBox(getter_AddRefs(viewRect)); // See if we really have something float viewBoxX, viewBoxY, viewBoxHeight, viewBoxWidth; viewRect->GetX(&viewBoxX); viewRect->GetY(&viewBoxY); viewRect->GetHeight(&viewBoxHeight); viewRect->GetWidth(&viewBoxWidth); if (viewBoxHeight > 0.0f && viewBoxWidth > 0.0f) { float viewportWidth = GetLengthValue(GetWidth()); float viewportHeight = GetLengthValue(GetHeight()); float refX = GetLengthValue(GetX()); float refY = GetLengthValue(GetY()); nsCOMPtr<nsIDOMSVGAnimatedPreserveAspectRatio> par; GetPreserveAspectRatio(getter_AddRefs(par)); tempTM = nsSVGUtils::GetViewBoxTransform(viewportWidth, viewportHeight, viewBoxX + refX, viewBoxY + refY, viewBoxWidth, viewBoxHeight, par, PR_TRUE); } else { // No viewBox, construct from the (modified) parent matrix NS_NewSVGMatrix(getter_AddRefs(tempTM)); } tCTM->Multiply(tempTM, aCTM); return NS_OK; }
gfxMatrix nsSVGMarkerElement::GetViewBoxTransform() { if (!mViewBoxToViewportTransform) { float viewportWidth = mLengthAttributes[MARKERWIDTH].GetAnimValue(mCoordCtx); float viewportHeight = mLengthAttributes[MARKERHEIGHT].GetAnimValue(mCoordCtx); const nsSVGViewBoxRect& viewbox = mViewBox.GetAnimValue(); if (viewbox.width <= 0.0f || viewbox.height <= 0.0f) { return gfxMatrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0); // invalid - don't paint element } float refX = mLengthAttributes[REFX].GetAnimValue(mCoordCtx); float refY = mLengthAttributes[REFY].GetAnimValue(mCoordCtx); gfxMatrix viewBoxTM = nsSVGUtils::GetViewBoxTransform(this, viewportWidth, viewportHeight, viewbox.x, viewbox.y, viewbox.width, viewbox.height, mPreserveAspectRatio); gfxPoint ref = viewBoxTM.Transform(gfxPoint(refX, refY)); gfxMatrix TM = viewBoxTM * gfxMatrix().Translate(gfxPoint(-ref.x, -ref.y)); mViewBoxToViewportTransform = NS_NewSVGMatrix(TM); } return nsSVGUtils::ConvertSVGMatrixToThebes(mViewBoxToViewportTransform); }
gfxMatrix nsSVGInnerSVGFrame::GetCanvasTM() { if (!mCanvasTM) { NS_ASSERTION(mParent, "null parent"); nsSVGContainerFrame *parent = static_cast<nsSVGContainerFrame*>(mParent); nsSVGSVGElement *content = static_cast<nsSVGSVGElement*>(mContent); gfxMatrix tm = content->PrependLocalTransformTo(parent->GetCanvasTM()); mCanvasTM = NS_NewSVGMatrix(tm); } return nsSVGUtils::ConvertSVGMatrixToThebes(mCanvasTM); }
nsresult nsSVGTransformSMILAttr::GetSVGTransformFromSMILValue( const nsSVGSMILTransform& aSMILTransform, nsIDOMSVGTransform* aSVGTransform) { switch (aSMILTransform.mTransformType) { case nsSVGSMILTransform::TRANSFORM_TRANSLATE: return aSVGTransform->SetTranslate(aSMILTransform.mParams[0], aSMILTransform.mParams[1]); case nsSVGSMILTransform::TRANSFORM_SCALE: return aSVGTransform->SetScale(aSMILTransform.mParams[0], aSMILTransform.mParams[1]); case nsSVGSMILTransform::TRANSFORM_ROTATE: return aSVGTransform->SetRotate(aSMILTransform.mParams[0], aSMILTransform.mParams[1], aSMILTransform.mParams[2]); case nsSVGSMILTransform::TRANSFORM_SKEWX: return aSVGTransform->SetSkewX(aSMILTransform.mParams[0]); case nsSVGSMILTransform::TRANSFORM_SKEWY: return aSVGTransform->SetSkewY(aSMILTransform.mParams[0]); case nsSVGSMILTransform::TRANSFORM_MATRIX: { nsCOMPtr<nsIDOMSVGMatrix> svgMatrix; nsresult rv = NS_NewSVGMatrix(getter_AddRefs(svgMatrix), aSMILTransform.mParams[0], aSMILTransform.mParams[1], aSMILTransform.mParams[2], aSMILTransform.mParams[3], aSMILTransform.mParams[4], aSMILTransform.mParams[5]); NS_ENSURE_SUCCESS(rv, rv); NS_ABORT_IF_FALSE(svgMatrix, "NS_NewSVGMatrix succeeded, so it should have " "given us a non-null result"); return aSVGTransform->SetMatrix(svgMatrix); } default: NS_WARNING("Unexpected transform type"); return NS_ERROR_FAILURE; } }
// If our GetCanvasTM is getting called, we // need to return *our current* transformation // matrix, which depends on our units parameters // and X, Y, Width, and Height already_AddRefed<nsIDOMSVGMatrix> nsSVGPatternFrame::GetCanvasTM() { nsIDOMSVGMatrix *rCTM; if (mCTM) { rCTM = mCTM; NS_IF_ADDREF(rCTM); } else { // Do we know our rendering parent? if (mSource) { // Yes, use it! mSource->GetCanvasTM(&rCTM); } else { // No, return an identity // We get here when geometry in the <pattern> container is updated NS_NewSVGMatrix(&rCTM); } } return rCTM; }
already_AddRefed<nsIDOMSVGMatrix> nsSVGTransformList::GetConsolidationMatrix(nsIDOMSVGTransformList *transforms) { PRUint32 count; transforms->GetNumberOfItems(&count); if (!count) return nsnull; nsCOMPtr<nsIDOMSVGTransform> transform; nsCOMPtr<nsIDOMSVGMatrix> conmatrix; // single transform common case - shortcut if (count == 1) { transforms->GetItem(0, getter_AddRefs(transform)); transform->GetMatrix(getter_AddRefs(conmatrix)); } else { nsresult rv = NS_NewSVGMatrix(getter_AddRefs(conmatrix)); NS_ENSURE_SUCCESS(rv, nsnull); nsCOMPtr<nsIDOMSVGMatrix> temp1, temp2; for (PRUint32 i = 0; i < count; ++i) { transforms->GetItem(i, getter_AddRefs(transform)); transform->GetMatrix(getter_AddRefs(temp1)); conmatrix->Multiply(temp1, getter_AddRefs(temp2)); if (!temp2) return nsnull; conmatrix = temp2; } } nsIDOMSVGMatrix *_retval = nsnull; conmatrix.swap(_retval); return _retval; }
already_AddRefed<gfxPattern> nsSVGMaskFrame::ComputeMaskAlpha(nsSVGRenderState *aContext, nsIFrame* aParent, const gfxMatrix &aMatrix, float aOpacity) { // If the flag is set when we get here, it means this mask frame // has already been used painting the current mask, and the document // has a mask reference loop. if (mInUse) { NS_WARNING("Mask loop detected!"); return nsnull; } AutoMaskReferencer maskRef(this); nsSVGMaskElement *mask = static_cast<nsSVGMaskElement*>(mContent); PRUint16 units = mask->mEnumAttributes[nsSVGMaskElement::MASKUNITS].GetAnimValue(); gfxRect bbox; if (units == nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) { bbox = nsSVGUtils::GetBBox(aParent); } gfxRect maskArea = nsSVGUtils::GetRelativeRect(units, &mask->mLengthAttributes[nsSVGMaskElement::X], bbox, aParent); maskArea.RoundOut(); gfxContext *gfx = aContext->GetGfxContext(); gfx->Save(); nsSVGUtils::SetClipRect(gfx, aMatrix, maskArea); gfxRect clipExtents = gfx->GetClipExtents(); gfx->Restore(); #ifdef DEBUG_tor fprintf(stderr, "clip extent: %f,%f %fx%f\n", clipExtents.X(), clipExtents.Y(), clipExtents.Width(), clipExtents.Height()); #endif PRBool resultOverflows; gfxIntSize surfaceSize = nsSVGUtils::ConvertToSurfaceSize(gfxSize(clipExtents.Width(), clipExtents.Height()), &resultOverflows); // 0 disables mask, < 0 is an error if (surfaceSize.width <= 0 || surfaceSize.height <= 0) return nsnull; if (resultOverflows) return nsnull; nsRefPtr<gfxImageSurface> image = new gfxImageSurface(surfaceSize, gfxASurface::ImageFormatARGB32); if (!image || image->CairoStatus()) return nsnull; image->SetDeviceOffset(-clipExtents.TopLeft()); nsSVGRenderState tmpState(image); mMaskParent = aParent; mMaskParentMatrix = NS_NewSVGMatrix(aMatrix); for (nsIFrame* kid = mFrames.FirstChild(); kid; kid = kid->GetNextSibling()) { // The CTM of each frame referencing us can be different nsISVGChildFrame* SVGFrame = do_QueryFrame(kid); if (SVGFrame) { SVGFrame->NotifySVGChanged(nsISVGChildFrame::SUPPRESS_INVALIDATION | nsISVGChildFrame::TRANSFORM_CHANGED); } nsSVGUtils::PaintFrameWithEffects(&tmpState, nsnull, kid); } PRUint8 *data = image->Data(); PRInt32 stride = image->Stride(); nsIntRect rect(0, 0, surfaceSize.width, surfaceSize.height); nsSVGUtils::UnPremultiplyImageDataAlpha(data, stride, rect); nsSVGUtils::ConvertImageDataToLinearRGB(data, stride, rect); for (PRInt32 y = 0; y < surfaceSize.height; y++) for (PRInt32 x = 0; x < surfaceSize.width; x++) { PRUint8 *pixel = data + stride * y + 4 * x; /* linearRGB -> intensity */ PRUint8 alpha = static_cast<PRUint8> ((pixel[GFX_ARGB32_OFFSET_R] * 0.2125 + pixel[GFX_ARGB32_OFFSET_G] * 0.7154 + pixel[GFX_ARGB32_OFFSET_B] * 0.0721) * (pixel[GFX_ARGB32_OFFSET_A] / 255.0) * aOpacity); memset(pixel, alpha, 4); } gfxPattern *retval = new gfxPattern(image); NS_IF_ADDREF(retval); return retval; }
nsresult nsSVGPatternFrame::PaintPattern(gfxASurface** surface, gfxMatrix* patternMatrix, nsSVGGeometryFrame *aSource, float aGraphicOpacity) { /* * General approach: * Set the content geometry stuff * Calculate our bbox (using x,y,width,height & patternUnits & * patternTransform) * Create the surface * Calculate the content transformation matrix * Get our children (we may need to get them from another Pattern) * Call SVGPaint on all of our children * Return */ *surface = nsnull; // Get our child nsIFrame *firstKid; if (NS_FAILED(GetPatternFirstChild(&firstKid))) return NS_ERROR_FAILURE; // Either no kids or a bad reference /* * Get the content geometry information. This is a little tricky -- * our parent is probably a <defs>, but we are rendering in the context * of some geometry source. Our content geometry information needs to * come from our rendering parent as opposed to our content parent. We * get that information from aSource, which is passed to us from the * backend renderer. * * There are three "geometries" that we need: * 1) The bounding box for the pattern. We use this to get the * width and height for the surface, and as the return to * GetBBox. * 2) The transformation matrix for the pattern. This is not *quite* * the same as the canvas transformation matrix that we will * provide to our rendering children since we "fudge" it a little * to get the renderer to handle the translations correctly for us. * 3) The CTM that we return to our children who make up the pattern. */ // Get all of the information we need from our "caller" -- i.e. // the geometry that is being rendered with a pattern nsSVGElement *callerContent; nsCOMPtr<nsIDOMSVGRect> callerBBox; nsCOMPtr<nsIDOMSVGMatrix> callerCTM; if (NS_FAILED(GetCallerGeometry(getter_AddRefs(callerCTM), getter_AddRefs(callerBBox), &callerContent, aSource))) return NS_ERROR_FAILURE; // Construct the CTM that we will provide to our children when we // render them into the tile. if (NS_FAILED(ConstructCTM(getter_AddRefs(mCTM), callerBBox, callerCTM))) return NS_ERROR_FAILURE; // Get the bounding box of the pattern. This will be used to determine // the size of the surface, and will also be used to define the bounding // box for the pattern tile. nsCOMPtr<nsIDOMSVGRect> bbox; if (NS_FAILED(GetPatternRect(getter_AddRefs(bbox), callerBBox, callerCTM, callerContent))) return NS_ERROR_FAILURE; // Get the transformation matrix that we will hand to the renderer's pattern // routine. *patternMatrix = GetPatternMatrix(bbox, callerBBox, callerCTM); #ifdef DEBUG_scooter printRect("Geometry Rect: ", callerBBox); printRect("Pattern Rect: ", bbox); printCTM("Pattern TM ", *patternMatrix); printCTM("Child TM ", mCTM); #endif // Now that we have all of the necessary geometries, we can // create our surface. float patternWidth, patternHeight; bbox->GetWidth(&patternWidth); bbox->GetHeight(&patternHeight); PRBool resultOverflows; gfxIntSize surfaceSize = nsSVGUtils::ConvertToSurfaceSize(gfxSize(patternWidth, patternHeight), &resultOverflows); // 0 disables rendering, < 0 is an error if (surfaceSize.width <= 0 || surfaceSize.height <= 0) return NS_ERROR_FAILURE; if (resultOverflows) { // scale down drawing to new pattern surface size nsCOMPtr<nsIDOMSVGMatrix> tempTM, aCTM; NS_NewSVGMatrix(getter_AddRefs(tempTM), surfaceSize.width / patternWidth, 0.0f, 0.0f, surfaceSize.height / patternHeight, 0.0f, 0.0f); mCTM->Multiply(tempTM, getter_AddRefs(aCTM)); aCTM.swap(mCTM); // and magnify pattern to compensate patternMatrix->Scale(patternWidth / surfaceSize.width, patternHeight / surfaceSize.height); } #ifdef DEBUG_scooter printf("Creating %dX%d surface\n", int(surfaceSize.width), int(surfaceSize.height)); #endif nsRefPtr<gfxASurface> tmpSurface = gfxPlatform::GetPlatform()->CreateOffscreenSurface(surfaceSize, gfxASurface::ImageFormatARGB32); if (!tmpSurface || tmpSurface->CairoStatus()) return NS_ERROR_FAILURE; gfxContext tmpContext(tmpSurface); nsSVGRenderState tmpState(&tmpContext); // Fill with transparent black tmpContext.SetOperator(gfxContext::OPERATOR_CLEAR); tmpContext.Paint(); tmpContext.SetOperator(gfxContext::OPERATOR_OVER); if (aGraphicOpacity != 1.0f) { tmpContext.Save(); tmpContext.PushGroup(gfxASurface::CONTENT_COLOR_ALPHA); } // OK, now render -- note that we use "firstKid", which // we got at the beginning because it takes care of the // referenced pattern situation for us // Set our geometrical parent mSource = aSource; for (nsIFrame* kid = firstKid; kid; kid = kid->GetNextSibling()) { nsSVGUtils::PaintChildWithEffects(&tmpState, nsnull, kid); } mSource = nsnull; if (aGraphicOpacity != 1.0f) { tmpContext.PopGroupToSource(); tmpContext.Paint(aGraphicOpacity); tmpContext.Restore(); } // caller now owns the surface tmpSurface.swap(*surface); return NS_OK; }
nsresult nsSVGTransform::Init() { nsresult rv = NS_NewSVGMatrix(getter_AddRefs(mMatrix)); NS_ADD_SVGVALUE_OBSERVER(mMatrix); return rv; }