Bug 1345388. Draw the alt feedback of an image with WedRender so we don't have to use fallback. r=aosmond

This is a fairly direct copy of nsImageFrame::DisplayAltFeedback changed to use WebRender.
This commit is contained in:
Kevin Chen
2018-01-10 16:32:48 +08:00
parent 3583f33b75
commit 45671f8b05
2 changed files with 220 additions and 0 deletions

View File

@@ -8,6 +8,7 @@
#include "nsImageFrame.h"
#include "TextDrawTarget.h"
#include "gfx2DGlue.h"
#include "gfxContext.h"
#include "gfxUtils.h"
@@ -1316,6 +1317,24 @@ class nsDisplayAltFeedback final : public nsDisplayItem {
nsDisplayItemGenericImageGeometry::UpdateDrawResult(this, result);
}
bool CreateWebRenderCommands(
mozilla::wr::DisplayListBuilder& aBuilder,
mozilla::wr::IpcResourceUpdateQueue& aResources,
const StackingContextHelper& aSc,
mozilla::layers::WebRenderLayerManager* aManager,
nsDisplayListBuilder* aDisplayListBuilder) override {
uint32_t flags = imgIContainer::FLAG_ASYNC_NOTIFY;
nsImageFrame* f = static_cast<nsImageFrame*>(mFrame);
DebugOnly<ImgDrawResult> result = f->DisplayAltFeedbackWithoutLayer(
this, aBuilder, aResources, aSc, aManager, aDisplayListBuilder,
ToReferenceFrame(), flags);
// DisplayAltFeedbackWithoutLayer can only return success.
MOZ_ASSERT(result == ImgDrawResult::SUCCESS);
Unused << result;
return true;
}
NS_DISPLAY_DECL_NAME("AltFeedback", TYPE_ALT_FEEDBACK)
};
@@ -1473,6 +1492,200 @@ ImgDrawResult nsImageFrame::DisplayAltFeedback(gfxContext& aRenderingContext,
return result;
}
ImgDrawResult nsImageFrame::DisplayAltFeedbackWithoutLayer(
nsDisplayItem* aItem, mozilla::wr::DisplayListBuilder& aBuilder,
mozilla::wr::IpcResourceUpdateQueue& aResources,
const StackingContextHelper& aSc,
mozilla::layers::WebRenderLayerManager* aManager,
nsDisplayListBuilder* aDisplayListBuilder, nsPoint aPt, uint32_t aFlags) {
// We should definitely have a gIconLoad here.
MOZ_ASSERT(gIconLoad, "How did we succeed in Init then?");
// Whether we draw the broken or loading icon.
bool isLoading = IMAGE_OK(GetContent()->AsElement()->State(), true);
// Calculate the inner area
nsRect inner = GetInnerArea() + aPt;
// Display a recessed one pixel border
nscoord borderEdgeWidth =
nsPresContext::CSSPixelsToAppUnits(ALT_BORDER_WIDTH);
// if inner area is empty, then make it big enough for at least the icon
if (inner.IsEmpty()) {
inner.SizeTo(2 * (nsPresContext::CSSPixelsToAppUnits(
ICON_SIZE + ICON_PADDING + ALT_BORDER_WIDTH)),
2 * (nsPresContext::CSSPixelsToAppUnits(
ICON_SIZE + ICON_PADDING + ALT_BORDER_WIDTH)));
}
// Make sure we have enough room to actually render the border within
// our frame bounds
if ((inner.width < 2 * borderEdgeWidth) ||
(inner.height < 2 * borderEdgeWidth)) {
return ImgDrawResult::SUCCESS;
}
// Paint the border
if (!isLoading || gIconLoad->mPrefShowLoadingPlaceholder) {
nsRecessedBorder recessedBorder(borderEdgeWidth, PresContext());
// Assert that we're not drawing a border-image here; if we were, we
// couldn't ignore the ImgDrawResult that PaintBorderWithStyleBorder
// returns.
MOZ_ASSERT(recessedBorder.mBorderImageSource.GetType() ==
eStyleImageType_Null);
nsRect rect = nsRect(aPt, GetSize());
Unused << nsCSSRendering::CreateWebRenderCommandsForBorderWithStyleBorder(
aItem, this, rect, aBuilder, aResources, aSc, aManager,
aDisplayListBuilder, recessedBorder);
}
// Adjust the inner rect to account for the one pixel recessed border,
// and a six pixel padding on each edge
inner.Deflate(
nsPresContext::CSSPixelsToAppUnits(ICON_PADDING + ALT_BORDER_WIDTH),
nsPresContext::CSSPixelsToAppUnits(ICON_PADDING + ALT_BORDER_WIDTH));
if (inner.IsEmpty()) {
return ImgDrawResult::SUCCESS;
}
// Clip so we don't render outside the inner rect
LayoutDeviceRect bounds = LayoutDeviceRect::FromAppUnits(
inner, PresContext()->AppUnitsPerDevPixel());
wr::LayoutRect transformedRect = wr::ToRoundedLayoutRect(bounds);
auto clipId = aBuilder.DefineClip(Nothing(), transformedRect);
aBuilder.PushClip(clipId);
// Draw image
ImgDrawResult result = ImgDrawResult::NOT_READY;
// Check if we should display image placeholders
if (!ShouldShowBrokenImageIcon() || !gIconLoad->mPrefShowPlaceholders ||
(isLoading && !gIconLoad->mPrefShowLoadingPlaceholder)) {
result = ImgDrawResult::SUCCESS;
} else {
nscoord size = nsPresContext::CSSPixelsToAppUnits(ICON_SIZE);
imgIRequest* request = isLoading ? nsImageFrame::gIconLoad->mLoadingImage
: nsImageFrame::gIconLoad->mBrokenImage;
// If we weren't previously displaying an icon, register ourselves
// as an observer for load and animation updates and flag that we're
// doing so now.
if (request && !mDisplayingIcon) {
gIconLoad->AddIconObserver(this);
mDisplayingIcon = true;
}
WritingMode wm = GetWritingMode();
bool flushRight =
(!wm.IsVertical() && !wm.IsBidiLTR()) || wm.IsVerticalRL();
// If the icon in question is loaded, draw it.
uint32_t imageStatus = 0;
if (request) request->GetImageStatus(&imageStatus);
if (imageStatus & imgIRequest::STATUS_LOAD_COMPLETE &&
!(imageStatus & imgIRequest::STATUS_ERROR)) {
nsCOMPtr<imgIContainer> imgCon;
request->GetImage(getter_AddRefs(imgCon));
MOZ_ASSERT(imgCon, "Load complete, but no image container?");
nsRect dest(flushRight ? inner.XMost() - size : inner.x, inner.y, size,
size);
const int32_t factor = PresContext()->AppUnitsPerDevPixel();
const LayoutDeviceRect destRect(
LayoutDeviceRect::FromAppUnits(dest, factor));
Maybe<SVGImageContext> svgContext;
IntSize decodeSize =
nsLayoutUtils::ComputeImageContainerDrawingParameters(
imgCon, this, destRect, aSc, aFlags, svgContext);
RefPtr<ImageContainer> container;
result = imgCon->GetImageContainerAtSize(
aManager, decodeSize, svgContext, aFlags, getter_AddRefs(container));
if (container) {
bool wrResult = aManager->CommandBuilder().PushImage(
aItem, container, aBuilder, aResources, aSc, destRect);
result &= wrResult ? ImgDrawResult::SUCCESS : ImgDrawResult::NOT_READY;
} else {
// We don't use &= here because we want the result to be NOT_READY so
// the next block executes.
result = ImgDrawResult::NOT_READY;
}
}
// If we could not draw the icon, just draw some graffiti in the mean time.
if (result == ImgDrawResult::NOT_READY) {
auto color = wr::ColorF{1.0f, 0.0f, 0.0f, 1.0f};
bool isBackfaceVisible = !aItem->BackfaceIsHidden();
nscoord iconXPos = flushRight ? inner.XMost() - size : inner.x;
// stroked rect:
nsRect rect(iconXPos, inner.y, size, size);
auto devPxRect = LayoutDeviceRect::FromAppUnits(
rect, PresContext()->AppUnitsPerDevPixel());
auto dest = wr::ToRoundedLayoutRect(devPxRect);
auto borderWidths = wr::ToBorderWidths(1.0, 1.0, 1.0, 1.0);
wr::BorderSide side = {color, wr::BorderStyle::Solid};
wr::BorderSide sides[4] = {side, side, side, side};
Range<const wr::BorderSide> sidesRange(sides, 4);
aBuilder.PushBorder(dest, dest, isBackfaceVisible, borderWidths,
sidesRange, wr::EmptyBorderRadius());
// filled circle in bottom right quadrant of stroked rect:
nscoord twoPX = nsPresContext::CSSPixelsToAppUnits(2);
rect = nsRect(iconXPos + size / 2, inner.y + size / 2, size / 2 - twoPX,
size / 2 - twoPX);
devPxRect = LayoutDeviceRect::FromAppUnits(
rect, PresContext()->AppUnitsPerDevPixel());
dest = wr::ToRoundedLayoutRect(devPxRect);
AutoTArray<wr::ComplexClipRegion, 1> clips;
clips.AppendElement(wr::SimpleRadii(dest, dest.size.width / 2));
auto clipId = aBuilder.DefineClip(Nothing(), dest, &clips, nullptr);
aBuilder.PushClip(clipId);
aBuilder.PushRect(dest, dest, isBackfaceVisible, color);
aBuilder.PopClip();
}
// Reduce the inner rect by the width of the icon, and leave an
// additional ICON_PADDING pixels for padding
int32_t paddedIconSize =
nsPresContext::CSSPixelsToAppUnits(ICON_SIZE + ICON_PADDING);
if (wm.IsVertical()) {
inner.y += paddedIconSize;
inner.height -= paddedIconSize;
} else {
if (!flushRight) {
inner.x += paddedIconSize;
}
inner.width -= paddedIconSize;
}
}
// Draw text
if (!inner.IsEmpty()) {
RefPtr<TextDrawTarget> textDrawer =
new TextDrawTarget(aBuilder, aResources, aSc, aManager, aItem, inner);
RefPtr<gfxContext> captureCtx = gfxContext::CreateOrNull(textDrawer);
nsIContent* content = GetContent();
if (content) {
nsAutoString altText;
nsCSSFrameConstructor::GetAlternateTextFor(
content->AsElement(), content->NodeInfo()->NameAtom(), altText);
DisplayAltText(PresContext(), *captureCtx.get(), altText, inner);
}
}
aBuilder.PopClip();
// Purposely always return success and ignore DrawResult local because we
// handled it not being success already.
return ImgDrawResult::SUCCESS;
}
#ifdef DEBUG
static void PaintDebugImageMap(nsIFrame* aFrame, DrawTarget* aDrawTarget,
const nsRect& aDirtyRect, nsPoint aPt) {