/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
SVGImageFrame::~SVGImageFrame() { // set the frame to null so we don't send messages to a dead object. if (mListener) {
nsCOMPtr<nsIImageLoadingContent> imageLoader =
do_QueryInterface(GetContent()); if (imageLoader) {
imageLoader->RemoveNativeObserver(mListener);
} reinterpret_cast<SVGImageListener*>(mListener.get())->SetFrame(nullptr);
}
mListener = nullptr;
}
void SVGImageFrame::Init(nsIContent* aContent, nsContainerFrame* aParent,
nsIFrame* aPrevInFlow) {
NS_ASSERTION(aContent->IsSVGElement(nsGkAtoms::image), "Content is not an SVG image!");
if (HasAnyStateBits(NS_FRAME_IS_NONDISPLAY)) { // Non-display frames are likely to be patterns, masks or the like. // Treat them as always visible. // This call must happen before the FrameCreated. This is because the // primary frame pointer on our content node isn't set until after this // function ends, so there is no way for the resulting OnVisibilityChange // notification to get a frame. FrameCreated has a workaround for this in // that it passes our frame around so it can be accessed. OnVisibilityChange // doesn't have that workaround.
IncApproximateVisibleCount();
}
mListener = new SVGImageListener(this);
nsCOMPtr<nsIImageLoadingContent> imageLoader =
do_QueryInterface(GetContent()); if (!imageLoader) {
MOZ_CRASH("Why is this not an image loading content?");
}
// We should have a PresContext now, so let's notify our image loader that // we need to register any image animations with the refresh driver.
imageLoader->FrameCreated(this);
nsresult SVGImageFrame::AttributeChanged(int32_t aNameSpaceID,
nsAtom* aAttribute, int32_t aModType) { if (aNameSpaceID == kNameSpaceID_None) { if (aAttribute == nsGkAtoms::preserveAspectRatio) { // We don't paint the content of the image using display lists, therefore // we have to invalidate for this children-only transform changes since // there is no layer tree to notice that the transform changed and // recomposite.
InvalidateFrame(); return NS_OK;
}
} if (aModType == dom::MutationEvent_Binding::REMOVAL &&
(aNameSpaceID == kNameSpaceID_None ||
aNameSpaceID == kNameSpaceID_XLink) &&
aAttribute == nsGkAtoms::href) { auto* element = static_cast<SVGImageElement*>(GetContent()); if (aNameSpaceID == kNameSpaceID_None ||
!element->mStringAttributes[SVGImageElement::HREF].IsExplicitlySet()) {
mImageContainer = nullptr;
InvalidateFrame();
}
}
// No viewBoxTM needed here -- our height/width overrides any concept of // "native size" that the SVG image has, and it will handle viewBox and // preserveAspectRatio on its own once we give it a region to draw into.
// NOTE: We need to cancel out the effects of Full-Page-Zoom, or else // it'll get applied an extra time by DrawSingleUnscaledImage.
nscoord appUnitsPerDevPx = PresContext()->AppUnitsPerDevPixel();
gfxFloat pageZoomFactor =
nsPresContext::AppUnitsToFloatCSSPixels(appUnitsPerDevPx);
imageTransform.PreScale(pageZoomFactor, pageZoomFactor);
}
if (!TransformContextForPainting(&aContext, aTransform)) { return;
}
// fill-opacity doesn't affect <image>, so if we're allowed to // optimize group opacity, the opacity used for compositing the // image into the current canvas is just the group opacity. float opacity = 1.0f; if (SVGUtils::CanOptimizeOpacity(this)) {
opacity = StyleEffects()->mOpacity;
}
if (mImageContainer->GetType() == imgIContainer::TYPE_VECTOR) { // Package up the attributes of this image element which can override the // attributes of mImageContainer's internal SVG document. The 'width' & // 'height' values we're passing in here are in CSS units (though they // come from width/height *attributes* in SVG). They influence the region // of the SVG image's internal document that is visible, in combination // with preserveAspectRatio and viewBox. const SVGImageContext context(
Some(CSSIntSize::Ceil(width, height)),
Some(imgElem->mPreserveAspectRatio.GetAnimValue()));
// For the actual draw operation to draw crisply (and at the right size), // our destination rect needs to be |width|x|height|, *in dev pixels*.
LayoutDeviceSize devPxSize(width, height);
nsRect destRect(nsPoint(), LayoutDevicePixel::ToAppUnits(
devPxSize, appUnitsPerDevPx));
nsCOMPtr<imgIRequest> currentRequest = GetCurrentRequest(); if (currentRequest) {
LCPHelpers::FinalizeLCPEntryForImage(
GetContent()->AsElement(), static_cast<imgRequestProxy*>(currentRequest.get()), destRect);
}
// Note: Can't use DrawSingleUnscaledImage for the TYPE_VECTOR case. // That method needs our image to have a fixed native width & height, // and that's not always true for TYPE_VECTOR images.
aImgParams.result &= nsLayoutUtils::DrawSingleImage(
aContext, PresContext(), mImageContainer,
nsLayoutUtils::GetSamplingFilterForFrame(this), destRect, destRect,
context, flags);
} else { // mImageContainer->GetType() == TYPE_RASTER
aImgParams.result &= nsLayoutUtils::DrawSingleUnscaledImage(
aContext, PresContext(), mImageContainer,
nsLayoutUtils::GetSamplingFilterForFrame(this), nsPoint(0, 0),
nullptr, SVGImageContext(), flags);
}
}
}
if (opacity != 1.0f) { // FIXME: not implemented, might be trivial returnfalse;
} if (StyleEffects()->HasMixBlendMode()) { // FIXME: not implemented returnfalse;
}
// try to setup the image if (!mImageContainer) {
nsCOMPtr<imgIRequest> currentRequest = GetCurrentRequest(); if (currentRequest) {
currentRequest->GetImage(getter_AddRefs(mImageContainer));
}
}
if (!mImageContainer) { // nothing to draw (yet) returntrue;
}
// "Meet" is "fit image to view"; "Slice" is "cover view with image". // // Whether we meet or slice, one side of the destRect will always be // perfectly spanned by our image. The only questions to answer are // "which side won't span perfectly" and "should that side be grown // or shrunk". // // Because we fit our image to the destRect, this all just reduces to: // "if meet, shrink to fit. if slice, grow to fit." if (align != SVG_PRESERVEASPECTRATIO_NONE && nativeAspect != viewAspect) { // Slightly redundant bools, but they make the conditions clearer bool tooTall = nativeAspect > viewAspect; bool tooWide = nativeAspect < viewAspect; if ((meetOrSlice == SVG_MEETORSLICE_MEET && tooTall) ||
(meetOrSlice == SVG_MEETORSLICE_SLICE && tooWide)) { // Adjust height and realign y auto oldHeight = destRect.height;
destRect.height = destRect.width / nativeAspect; auto heightChange = oldHeight - destRect.height; switch (align) { case SVG_PRESERVEASPECTRATIO_XMINYMIN: case SVG_PRESERVEASPECTRATIO_XMIDYMIN: case SVG_PRESERVEASPECTRATIO_XMAXYMIN: // align to top (no-op) break; case SVG_PRESERVEASPECTRATIO_XMINYMID: case SVG_PRESERVEASPECTRATIO_XMIDYMID: case SVG_PRESERVEASPECTRATIO_XMAXYMID: // align to center
destRect.y += heightChange / 2.0f; break; case SVG_PRESERVEASPECTRATIO_XMINYMAX: case SVG_PRESERVEASPECTRATIO_XMIDYMAX: case SVG_PRESERVEASPECTRATIO_XMAXYMAX: // align to bottom
destRect.y += heightChange; break; default:
MOZ_ASSERT_UNREACHABLE("Unknown value for align");
}
} elseif ((meetOrSlice == SVG_MEETORSLICE_MEET && tooWide) ||
(meetOrSlice == SVG_MEETORSLICE_SLICE && tooTall)) { // Adjust width and realign x auto oldWidth = destRect.width;
destRect.width = destRect.height * nativeAspect; auto widthChange = oldWidth - destRect.width; switch (align) { case SVG_PRESERVEASPECTRATIO_XMINYMIN: case SVG_PRESERVEASPECTRATIO_XMINYMID: case SVG_PRESERVEASPECTRATIO_XMINYMAX: // align to left (no-op) break; case SVG_PRESERVEASPECTRATIO_XMIDYMIN: case SVG_PRESERVEASPECTRATIO_XMIDYMID: case SVG_PRESERVEASPECTRATIO_XMIDYMAX: // align to center
destRect.x += widthChange / 2.0f; break; case SVG_PRESERVEASPECTRATIO_XMAXYMIN: case SVG_PRESERVEASPECTRATIO_XMAXYMID: case SVG_PRESERVEASPECTRATIO_XMAXYMAX: // align to right
destRect.x += widthChange; break; default:
MOZ_ASSERT_UNREACHABLE("Unknown value for align");
}
}
}
}
}
SVGImageContext svgContext; if (mImageContainer->GetType() == imgIContainer::TYPE_VECTOR) { if (StaticPrefs::image_svg_blob_image()) {
flags |= imgIContainer::FLAG_RECORD_BLOB;
} // Forward preserveAspectRatio to inner SVGs
svgContext.SetViewportSize(Some(CSSIntSize::Ceil(width, height)));
svgContext.SetPreserveAspectRatio(
Some(imgElem->mPreserveAspectRatio.GetAnimValue()));
}
// While we got a container, it may not contain a fully decoded surface. If // that is the case, and we have an image we were previously displaying which // has a fully decoded surface, then we should prefer the previous image. switch (drawResult) { case ImgDrawResult::NOT_READY: case ImgDrawResult::TEMPORARY_ERROR: // nothing to draw (yet) returntrue; case ImgDrawResult::NOT_SUPPORTED: // things we haven't implemented for WR yet returnfalse; default: // image is ready to draw break;
}
// Don't do any actual mutations to state if we're doing a dry run // (used to decide if we're making this into an active layer) if (!aDryRun) { // If the image container is empty, we don't want to fallback. Any other // failure will be due to resource constraints and fallback is unlikely to // help us. Hence we can ignore the return value from PushImage. if (provider) {
aManager->CommandBuilder().PushImageProvider(aItem, provider, drawResult,
aBuilder, aResources,
destRect, clipRect);
}
}
Rect rect;
SVGImageElement* element = static_cast<SVGImageElement*>(GetContent());
SVGGeometryProperty::ResolveAll<SVGT::X, SVGT::Y, SVGT::Width, SVGT::Height>(
element, &rect.x, &rect.y, &rect.width, &rect.height);
if (!rect.Contains(ToPoint(aPoint))) { return nullptr;
}
// Special case for raster images -- we only want to accept points that fall // in the underlying image's (scaled to fit) native bounds. That region // doesn't necessarily map to our <image> element's [x,y,width,height] if the // raster image's aspect ratio is being preserved. We have to look up the // native image size & our viewBox transform in order to filter out points // that fall outside that area. (This special case doesn't apply to vector // images because they don't limit their drawing to explicit "native // bounds" -- they have an infinite canvas on which to place content.) if (StyleDisplay()->IsScrollableOverflow() && mImageContainer) { if (mImageContainer->GetType() == imgIContainer::TYPE_RASTER) {
int32_t nativeWidth, nativeHeight; if (NS_FAILED(mImageContainer->GetWidth(&nativeWidth)) ||
NS_FAILED(mImageContainer->GetHeight(&nativeHeight)) ||
nativeWidth == 0 || nativeHeight == 0) { return nullptr;
}
mImageContainer->GetResolution().ApplyTo(nativeWidth, nativeHeight);
Matrix viewBoxTM = SVGContentUtils::GetViewBoxTransform(
rect.width, rect.height, 0, 0, nativeWidth, nativeHeight,
element->mPreserveAspectRatio); if (!SVGUtils::HitTestRect(viewBoxTM, 0, 0, nativeWidth, nativeHeight,
aPoint.x - rect.x, aPoint.y - rect.y)) { return nullptr;
}
}
}
returnthis;
}
void SVGImageFrame::ReflowSVG() {
NS_ASSERTION(SVGUtils::OuterSVGIsCallingReflowSVG(this), "This call is probably a wasteful mistake");
MOZ_ASSERT(!HasAnyStateBits(NS_FRAME_IS_NONDISPLAY), "ReflowSVG mechanism not designed for this");
if (!SVGUtils::NeedsReflowSVG(this)) { return;
}
float x, y, width, height;
SVGImageElement* element = static_cast<SVGImageElement*>(GetContent());
SVGGeometryProperty::ResolveAll<SVGT::X, SVGT::Y, SVGT::Width, SVGT::Height>(
element, &x, &y, &width, &height);
if (HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) { // Make sure we have our filter property (if any) before calling // FinishAndStoreOverflow (subsequent filter changes are handled off // nsChangeHint_UpdateEffects):
SVGObserverUtils::UpdateEffects(this);
if (!mReflowCallbackPosted) {
mReflowCallbackPosted = true;
PresShell()->PostReflowCallback(this);
}
}
// Invalidate, but only if this is not our first reflow (since if it is our // first reflow then we haven't had our first paint yet). if (!GetParent()->HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) {
InvalidateFrame();
}
}
// XXX(seth): We don't need this. The purpose of updating visibility // synchronously is to ensure that animated images start animating // immediately. In the short term, however, // nsImageLoadingContent::OnUnlockedDraw() is enough to ensure that // animations start as soon as the image is painted for the first time, and in // the long term we want to update visibility information from the display // list whenever we paint, so we don't actually need to do this. However, to // avoid behavior changes during the transition from the old image visibility // code, we'll leave it in for now.
UpdateVisibilitySynchronously();
bool SVGImageFrame::IgnoreHitTest() const { switch (Style()->PointerEvents()) { case StylePointerEvents::None: break; case StylePointerEvents::Visiblepainted: case StylePointerEvents::Auto: if (StyleVisibility()->IsVisible()) { /* XXX: should check pixel transparency */ returnfalse;
} break; case StylePointerEvents::Visiblefill: case StylePointerEvents::Visiblestroke: case StylePointerEvents::Visible: if (StyleVisibility()->IsVisible()) { returnfalse;
} break; case StylePointerEvents::Painted: /* XXX: should check pixel transparency */ returnfalse; case StylePointerEvents::Fill: case StylePointerEvents::Stroke: case StylePointerEvents::All: returnfalse; default:
NS_ERROR("not reached"); break;
}
returntrue;
}
void SVGImageFrame::NotifySVGChanged(uint32_t aFlags) {
MOZ_ASSERT(aFlags & (TRANSFORM_CHANGED | COORD_CONTEXT_CHANGED), "Invalidation logic may need adjusting");
}
if ((aFlags & SVGUtils::eForGetClientRects) &&
aToBBoxUserspace.PreservesAxisAlignedRectangles()) {
Rect rect = NSRectToRect(mRect, AppUnitsPerCSSPixel()); return aToBBoxUserspace.TransformBounds(rect);
}
auto* element = static_cast<SVGImageElement*>(GetContent());
if (aType == imgINotificationObserver::FRAME_UPDATE) { // No new dimensions, so we don't need to call // SVGUtils::InvalidateAndScheduleBoundsUpdate.
nsLayoutUtils::PostRestyleEvent(mFrame->GetContent()->AsElement(),
RestyleHint{0},
nsChangeHint_InvalidateRenderingObservers);
mFrame->InvalidateFrame();
}
if (aType == imgINotificationObserver::SIZE_AVAILABLE) { // Called once the resource's dimensions have been obtained.
nsCOMPtr<imgIContainer> image;
aRequest->GetImage(getter_AddRefs(image)); if (image) {
StyleImageOrientation orientation =
mFrame->StyleVisibility()->UsedImageOrientation(aRequest);
image = nsLayoutUtils::OrientImage(image, orientation);
image->SetAnimationMode(mFrame->PresContext()->ImageAnimationMode());
mFrame->mImageContainer = std::move(image);
}
mFrame->InvalidateFrame();
nsLayoutUtils::PostRestyleEvent(mFrame->GetContent()->AsElement(),
RestyleHint{0},
nsChangeHint_InvalidateRenderingObservers);
SVGUtils::ScheduleReflowSVG(mFrame);
}
}
} // namespace mozilla
¤ Dauer der Verarbeitung: 0.16 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.