forked from mirrors/gecko-dev
		
	Bug 1711061 - Part 12. Change the display list to use WebRenderImageProvider. r=tnikkel
Differential Revision: https://phabricator.services.mozilla.com/D126605
This commit is contained in:
		
							parent
							
								
									77d044e944
								
							
						
					
					
						commit
						c6eb106f04
					
				
					 11 changed files with 174 additions and 177 deletions
				
			
		|  | @ -17,6 +17,7 @@ | ||||||
| #include "mozilla/gfx/2D.h" | #include "mozilla/gfx/2D.h" | ||||||
| #include "mozilla/gfx/Logging.h" | #include "mozilla/gfx/Logging.h" | ||||||
| #include "mozilla/gfx/Types.h" | #include "mozilla/gfx/Types.h" | ||||||
|  | #include "mozilla/image/WebRenderImageProvider.h" | ||||||
| #include "mozilla/layers/AnimationHelper.h" | #include "mozilla/layers/AnimationHelper.h" | ||||||
| #include "mozilla/layers/ClipManager.h" | #include "mozilla/layers/ClipManager.h" | ||||||
| #include "mozilla/layers/ImageClient.h" | #include "mozilla/layers/ImageClient.h" | ||||||
|  | @ -40,6 +41,7 @@ namespace mozilla { | ||||||
| namespace layers { | namespace layers { | ||||||
| 
 | 
 | ||||||
| using namespace gfx; | using namespace gfx; | ||||||
|  | using namespace image; | ||||||
| static int sIndent; | static int sIndent; | ||||||
| #include <stdarg.h> | #include <stdarg.h> | ||||||
| #include <stdio.h> | #include <stdio.h> | ||||||
|  | @ -2009,24 +2011,22 @@ bool WebRenderCommandBuilder::PushImage( | ||||||
|   return true; |   return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| Maybe<wr::BlobImageKey> WebRenderCommandBuilder::CreateBlobImageKey( | Maybe<wr::ImageKey> WebRenderCommandBuilder::CreateImageProviderKey( | ||||||
|     nsDisplayItem* aItem, ImageContainer* aContainer, |     nsDisplayItem* aItem, image::WebRenderImageProvider* aProvider, | ||||||
|     mozilla::wr::IpcResourceUpdateQueue& aResources) { |     mozilla::wr::IpcResourceUpdateQueue& aResources) { | ||||||
|   MOZ_ASSERT(!aContainer->IsAsync()); |   RefPtr<WebRenderImageProviderData> imageData = | ||||||
| 
 |       CreateOrRecycleWebRenderUserData<WebRenderImageProviderData>(aItem); | ||||||
|   RefPtr<WebRenderBlobImageData> imageData = |  | ||||||
|       CreateOrRecycleWebRenderUserData<WebRenderBlobImageData>(aItem); |  | ||||||
|   MOZ_ASSERT(imageData); |   MOZ_ASSERT(imageData); | ||||||
|   return imageData->UpdateImageKey(aContainer, aResources); |   return imageData->UpdateImageKey(aProvider, aResources); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool WebRenderCommandBuilder::PushBlobImage( | bool WebRenderCommandBuilder::PushImageProvider( | ||||||
|     nsDisplayItem* aItem, ImageContainer* aContainer, |     nsDisplayItem* aItem, image::WebRenderImageProvider* aProvider, | ||||||
|     mozilla::wr::DisplayListBuilder& aBuilder, |     mozilla::wr::DisplayListBuilder& aBuilder, | ||||||
|     mozilla::wr::IpcResourceUpdateQueue& aResources, |     mozilla::wr::IpcResourceUpdateQueue& aResources, | ||||||
|     const LayoutDeviceRect& aRect, const LayoutDeviceRect& aClip) { |     const LayoutDeviceRect& aRect, const LayoutDeviceRect& aClip) { | ||||||
|   Maybe<wr::BlobImageKey> key = |   Maybe<wr::ImageKey> key = | ||||||
|       CreateBlobImageKey(aItem, aContainer, aResources); |       CreateImageProviderKey(aItem, aProvider, aResources); | ||||||
|   if (!key) { |   if (!key) { | ||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
|  | @ -2034,8 +2034,7 @@ bool WebRenderCommandBuilder::PushBlobImage( | ||||||
|   auto rendering = wr::ToImageRendering(aItem->Frame()->UsedImageRendering()); |   auto rendering = wr::ToImageRendering(aItem->Frame()->UsedImageRendering()); | ||||||
|   auto r = wr::ToLayoutRect(aRect); |   auto r = wr::ToLayoutRect(aRect); | ||||||
|   auto c = wr::ToLayoutRect(aClip); |   auto c = wr::ToLayoutRect(aClip); | ||||||
|   aBuilder.PushImage(r, c, !aItem->BackfaceIsHidden(), rendering, |   aBuilder.PushImage(r, c, !aItem->BackfaceIsHidden(), rendering, key.value()); | ||||||
|                      wr::AsImageKey(key.value())); |  | ||||||
| 
 | 
 | ||||||
|   return true; |   return true; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -21,6 +21,10 @@ | ||||||
| 
 | 
 | ||||||
| namespace mozilla { | namespace mozilla { | ||||||
| 
 | 
 | ||||||
|  | namespace image { | ||||||
|  | class WebRenderImageProvider; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| namespace layers { | namespace layers { | ||||||
| 
 | 
 | ||||||
| class ImageClient; | class ImageClient; | ||||||
|  | @ -65,8 +69,8 @@ class WebRenderCommandBuilder final { | ||||||
|       mozilla::wr::ImageRendering aRendering, const StackingContextHelper& aSc, |       mozilla::wr::ImageRendering aRendering, const StackingContextHelper& aSc, | ||||||
|       gfx::IntSize& aSize, const Maybe<LayoutDeviceRect>& aAsyncImageBounds); |       gfx::IntSize& aSize, const Maybe<LayoutDeviceRect>& aAsyncImageBounds); | ||||||
| 
 | 
 | ||||||
|   Maybe<wr::BlobImageKey> CreateBlobImageKey( |   Maybe<wr::ImageKey> CreateImageProviderKey( | ||||||
|       nsDisplayItem* aItem, ImageContainer* aContainer, |       nsDisplayItem* aItem, image::WebRenderImageProvider* aProvider, | ||||||
|       mozilla::wr::IpcResourceUpdateQueue& aResources); |       mozilla::wr::IpcResourceUpdateQueue& aResources); | ||||||
| 
 | 
 | ||||||
|   WebRenderUserDataRefTable* GetWebRenderUserDataTable() { |   WebRenderUserDataRefTable* GetWebRenderUserDataTable() { | ||||||
|  | @ -79,11 +83,12 @@ class WebRenderCommandBuilder final { | ||||||
|                  const StackingContextHelper& aSc, |                  const StackingContextHelper& aSc, | ||||||
|                  const LayoutDeviceRect& aRect, const LayoutDeviceRect& aClip); |                  const LayoutDeviceRect& aRect, const LayoutDeviceRect& aClip); | ||||||
| 
 | 
 | ||||||
|   bool PushBlobImage(nsDisplayItem* aItem, ImageContainer* aContainer, |   bool PushImageProvider(nsDisplayItem* aItem, | ||||||
|                      mozilla::wr::DisplayListBuilder& aBuilder, |                          image::WebRenderImageProvider* aProvider, | ||||||
|                      mozilla::wr::IpcResourceUpdateQueue& aResources, |                          mozilla::wr::DisplayListBuilder& aBuilder, | ||||||
|                      const LayoutDeviceRect& aRect, |                          mozilla::wr::IpcResourceUpdateQueue& aResources, | ||||||
|                      const LayoutDeviceRect& aClip); |                          const LayoutDeviceRect& aRect, | ||||||
|  |                          const LayoutDeviceRect& aClip); | ||||||
| 
 | 
 | ||||||
|   Maybe<wr::ImageMask> BuildWrMaskImage( |   Maybe<wr::ImageMask> BuildWrMaskImage( | ||||||
|       nsDisplayMasksAndClipPaths* aMaskItem, wr::DisplayListBuilder& aBuilder, |       nsDisplayMasksAndClipPaths* aMaskItem, wr::DisplayListBuilder& aBuilder, | ||||||
|  |  | ||||||
|  | @ -6,6 +6,7 @@ | ||||||
| 
 | 
 | ||||||
| #include "WebRenderUserData.h" | #include "WebRenderUserData.h" | ||||||
| 
 | 
 | ||||||
|  | #include "mozilla/image/WebRenderImageProvider.h" | ||||||
| #include "mozilla/layers/AnimationHelper.h" | #include "mozilla/layers/AnimationHelper.h" | ||||||
| #include "mozilla/layers/CompositorBridgeChild.h" | #include "mozilla/layers/CompositorBridgeChild.h" | ||||||
| #include "mozilla/layers/ImageClient.h" | #include "mozilla/layers/ImageClient.h" | ||||||
|  | @ -19,6 +20,8 @@ | ||||||
| #include "nsIFrame.h" | #include "nsIFrame.h" | ||||||
| #include "WebRenderCanvasRenderer.h" | #include "WebRenderCanvasRenderer.h" | ||||||
| 
 | 
 | ||||||
|  | using namespace mozilla::image; | ||||||
|  | 
 | ||||||
| namespace mozilla { | namespace mozilla { | ||||||
| namespace layers { | namespace layers { | ||||||
| 
 | 
 | ||||||
|  | @ -42,8 +45,9 @@ bool WebRenderUserData::SupportsAsyncUpdate(nsIFrame* aFrame) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /* static */ | /* static */ | ||||||
| bool WebRenderUserData::ProcessInvalidateForImage( | bool WebRenderUserData::ProcessInvalidateForImage(nsIFrame* aFrame, | ||||||
|     nsIFrame* aFrame, DisplayItemType aType, ContainerProducerID aProducerId) { |                                                   DisplayItemType aType, | ||||||
|  |                                                   ImageProviderId aProviderId) { | ||||||
|   MOZ_ASSERT(aFrame); |   MOZ_ASSERT(aFrame); | ||||||
| 
 | 
 | ||||||
|   if (!aFrame->HasProperty(WebRenderUserDataProperty::Key())) { |   if (!aFrame->HasProperty(WebRenderUserDataProperty::Key())) { | ||||||
|  | @ -59,9 +63,9 @@ bool WebRenderUserData::ProcessInvalidateForImage( | ||||||
|     return true; |     return true; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   RefPtr<WebRenderImageData> image = |   RefPtr<WebRenderImageProviderData> image = | ||||||
|       GetWebRenderUserData<WebRenderImageData>(aFrame, type); |       GetWebRenderUserData<WebRenderImageProviderData>(aFrame, type); | ||||||
|   if (image && image->UsingSharedSurface(aProducerId)) { |   if (image && image->Invalidate(aProviderId)) { | ||||||
|     return true; |     return true; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | @ -281,33 +285,43 @@ void WebRenderImageData::CreateImageClientIfNeeded() { | ||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| WebRenderBlobImageData::WebRenderBlobImageData(RenderRootStateManager* aManager, | WebRenderImageProviderData::WebRenderImageProviderData( | ||||||
|                                                nsDisplayItem* aItem) |     RenderRootStateManager* aManager, nsDisplayItem* aItem) | ||||||
|     : WebRenderUserData(aManager, aItem) {} |     : WebRenderUserData(aManager, aItem) {} | ||||||
| 
 | 
 | ||||||
| WebRenderBlobImageData::WebRenderBlobImageData(RenderRootStateManager* aManager, | WebRenderImageProviderData::WebRenderImageProviderData( | ||||||
|                                                uint32_t aDisplayItemKey, |     RenderRootStateManager* aManager, uint32_t aDisplayItemKey, | ||||||
|                                                nsIFrame* aFrame) |     nsIFrame* aFrame) | ||||||
|     : WebRenderUserData(aManager, aDisplayItemKey, aFrame) {} |     : WebRenderUserData(aManager, aDisplayItemKey, aFrame) {} | ||||||
| 
 | 
 | ||||||
| Maybe<wr::BlobImageKey> WebRenderBlobImageData::UpdateImageKey( | WebRenderImageProviderData::~WebRenderImageProviderData() = default; | ||||||
|     ImageContainer* aContainer, wr::IpcResourceUpdateQueue& aResources) { |  | ||||||
|   MOZ_ASSERT(aContainer); |  | ||||||
| 
 | 
 | ||||||
|   if (mContainer != aContainer) { | Maybe<wr::ImageKey> WebRenderImageProviderData::UpdateImageKey( | ||||||
|     mContainer = aContainer; |     WebRenderImageProvider* aProvider, wr::IpcResourceUpdateQueue& aResources) { | ||||||
|  |   MOZ_ASSERT(aProvider); | ||||||
|  | 
 | ||||||
|  |   if (mProvider != aProvider) { | ||||||
|  |     mProvider = aProvider; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   wr::BlobImageKey key = {}; |   wr::ImageKey key = {}; | ||||||
|  |   nsresult rv = mProvider->UpdateKey(mManager, aResources, key); | ||||||
|  |   if (NS_FAILED(rv)) { | ||||||
|  |     return Nothing(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   return Some(key); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool WebRenderImageProviderData::Invalidate(ImageProviderId aProviderId) const { | ||||||
|  |   if (!aProviderId || mProvider->GetProviderId() != aProviderId) { | ||||||
|  |     return false; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   wr::ImageKey key = {}; | ||||||
|   nsresult rv = |   nsresult rv = | ||||||
|       SharedSurfacesChild::ShareBlob(aContainer, mManager, aResources, key); |       mProvider->UpdateKey(mManager, mManager->AsyncResourceUpdates(), key); | ||||||
|   if (NS_SUCCEEDED(rv)) { |   return NS_SUCCEEDED(rv); | ||||||
|     mKey = Some(key); |  | ||||||
|   } else { |  | ||||||
|     mKey.reset(); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   return mKey; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| WebRenderFallbackData::WebRenderFallbackData(RenderRootStateManager* aManager, | WebRenderFallbackData::WebRenderFallbackData(RenderRootStateManager* aManager, | ||||||
|  |  | ||||||
|  | @ -9,6 +9,7 @@ | ||||||
| 
 | 
 | ||||||
| #include <vector> | #include <vector> | ||||||
| #include "mozilla/webrender/WebRenderAPI.h" | #include "mozilla/webrender/WebRenderAPI.h" | ||||||
|  | #include "mozilla/image/WebRenderImageProvider.h" | ||||||
| #include "mozilla/layers/AnimationInfo.h" | #include "mozilla/layers/AnimationInfo.h" | ||||||
| #include "mozilla/dom/RemoteBrowser.h" | #include "mozilla/dom/RemoteBrowser.h" | ||||||
| #include "mozilla/UniquePtr.h" | #include "mozilla/UniquePtr.h" | ||||||
|  | @ -44,6 +45,7 @@ class WebRenderCanvasData; | ||||||
| class WebRenderCanvasRenderer; | class WebRenderCanvasRenderer; | ||||||
| class WebRenderCanvasRendererAsync; | class WebRenderCanvasRendererAsync; | ||||||
| class WebRenderImageData; | class WebRenderImageData; | ||||||
|  | class WebRenderImageProviderData; | ||||||
| class WebRenderFallbackData; | class WebRenderFallbackData; | ||||||
| class WebRenderLocalCanvasData; | class WebRenderLocalCanvasData; | ||||||
| class RenderRootStateManager; | class RenderRootStateManager; | ||||||
|  | @ -69,7 +71,7 @@ class WebRenderUserData { | ||||||
|   static bool SupportsAsyncUpdate(nsIFrame* aFrame); |   static bool SupportsAsyncUpdate(nsIFrame* aFrame); | ||||||
| 
 | 
 | ||||||
|   static bool ProcessInvalidateForImage(nsIFrame* aFrame, DisplayItemType aType, |   static bool ProcessInvalidateForImage(nsIFrame* aFrame, DisplayItemType aType, | ||||||
|                                         ContainerProducerID aProducerId); |                                         image::ImageProviderId aProviderId); | ||||||
| 
 | 
 | ||||||
|   NS_INLINE_DECL_REFCOUNTING(WebRenderUserData) |   NS_INLINE_DECL_REFCOUNTING(WebRenderUserData) | ||||||
| 
 | 
 | ||||||
|  | @ -78,6 +80,7 @@ class WebRenderUserData { | ||||||
|                     nsIFrame* aFrame); |                     nsIFrame* aFrame); | ||||||
| 
 | 
 | ||||||
|   virtual WebRenderImageData* AsImageData() { return nullptr; } |   virtual WebRenderImageData* AsImageData() { return nullptr; } | ||||||
|  |   virtual WebRenderImageProviderData* AsImageProviderData() { return nullptr; } | ||||||
|   virtual WebRenderFallbackData* AsFallbackData() { return nullptr; } |   virtual WebRenderFallbackData* AsFallbackData() { return nullptr; } | ||||||
|   virtual WebRenderCanvasData* AsCanvasData() { return nullptr; } |   virtual WebRenderCanvasData* AsCanvasData() { return nullptr; } | ||||||
|   virtual WebRenderLocalCanvasData* AsLocalCanvasData() { return nullptr; } |   virtual WebRenderLocalCanvasData* AsLocalCanvasData() { return nullptr; } | ||||||
|  | @ -93,7 +96,7 @@ class WebRenderUserData { | ||||||
|     eRemote, |     eRemote, | ||||||
|     eGroup, |     eGroup, | ||||||
|     eMask, |     eMask, | ||||||
|     eBlobImage,  // SVG image
 |     eImageProvider,  // ImageLib
 | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   virtual UserDataType GetType() = 0; |   virtual UserDataType GetType() = 0; | ||||||
|  | @ -186,26 +189,27 @@ class WebRenderImageData : public WebRenderUserData { | ||||||
|   bool mOwnsKey; |   bool mOwnsKey; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /// Holds some data used to share blob recordings from VectorImages with the
 | /// Holds some data used to share ImageLib results with the parent process.
 | ||||||
| /// parent process.
 | /// This may be either in the form of a blob recording or a rasterized surface.
 | ||||||
| class WebRenderBlobImageData : public WebRenderUserData { | class WebRenderImageProviderData final : public WebRenderUserData { | ||||||
|  public: |  public: | ||||||
|   WebRenderBlobImageData(RenderRootStateManager* aManager, |   WebRenderImageProviderData(RenderRootStateManager* aManager, | ||||||
|                          nsDisplayItem* aItem); |                              nsDisplayItem* aItem); | ||||||
|   WebRenderBlobImageData(RenderRootStateManager* aManager, |   WebRenderImageProviderData(RenderRootStateManager* aManager, | ||||||
|                          uint32_t aDisplayItemKey, nsIFrame* aFrame); |                              uint32_t aDisplayItemKey, nsIFrame* aFrame); | ||||||
|   virtual ~WebRenderBlobImageData() {} |   ~WebRenderImageProviderData() override; | ||||||
| 
 | 
 | ||||||
|   UserDataType GetType() override { return UserDataType::eBlobImage; } |   WebRenderImageProviderData* AsImageProviderData() override { return this; } | ||||||
|   static UserDataType Type() { return UserDataType::eBlobImage; } |   UserDataType GetType() override { return UserDataType::eImageProvider; } | ||||||
|   Maybe<wr::BlobImageKey> GetImageKey() { return mKey; } |   static UserDataType Type() { return UserDataType::eImageProvider; } | ||||||
| 
 | 
 | ||||||
|   Maybe<wr::BlobImageKey> UpdateImageKey( |   Maybe<wr::ImageKey> UpdateImageKey(image::WebRenderImageProvider* aProvider, | ||||||
|       ImageContainer* aContainer, wr::IpcResourceUpdateQueue& aResources); |                                      wr::IpcResourceUpdateQueue& aResources); | ||||||
|  | 
 | ||||||
|  |   bool Invalidate(image::ImageProviderId aProviderId) const; | ||||||
| 
 | 
 | ||||||
|  protected: |  protected: | ||||||
|   Maybe<wr::BlobImageKey> mKey; |   RefPtr<image::WebRenderImageProvider> mProvider; | ||||||
|   RefPtr<ImageContainer> mContainer; |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /// Used for fallback rendering.
 | /// Used for fallback rendering.
 | ||||||
|  |  | ||||||
|  | @ -6608,26 +6608,32 @@ IntSize nsLayoutUtils::ComputeImageContainerDrawingParameters( | ||||||
|         imgIContainer::FRAME_CURRENT, samplingFilter, aFlags); |         imgIContainer::FRAME_CURRENT, samplingFilter, aFlags); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   // If the dest rect contains the fill rect, then we are only displaying part
 |   // We only use the region rect with blob recordings. This is because when we
 | ||||||
|   // of the vector image. We need to calculate the restriction region to avoid
 |   // rasterize an SVG image in process, we always create a complete
 | ||||||
|   // drawing more than we need, and sampling outside the desired bounds.
 |   // rasterization of the whole image which can be given to any caller, while
 | ||||||
|   LayerIntRect clipRect = SnapRectForImage(itm, scaleFactors, aFillRect); |   // we support partial rasterization with the blob recordings.
 | ||||||
|   if (destRect.Contains(clipRect)) { |   if (aFlags & imgIContainer::FLAG_RECORD_BLOB) { | ||||||
|     LayerIntRect restrictRect = destRect.Intersect(clipRect); |     // If the dest rect contains the fill rect, then we are only displaying part
 | ||||||
|     restrictRect.MoveBy(-destRect.TopLeft()); |     // of the vector image. We need to calculate the restriction region to avoid
 | ||||||
|  |     // drawing more than we need, and sampling outside the desired bounds.
 | ||||||
|  |     LayerIntRect clipRect = SnapRectForImage(itm, scaleFactors, aFillRect); | ||||||
|  |     if (destRect.Contains(clipRect)) { | ||||||
|  |       LayerIntRect restrictRect = destRect.Intersect(clipRect); | ||||||
|  |       restrictRect.MoveBy(-destRect.TopLeft()); | ||||||
| 
 | 
 | ||||||
|     if (restrictRect.Width() < 1) { |       if (restrictRect.Width() < 1) { | ||||||
|       restrictRect.SetWidth(1); |         restrictRect.SetWidth(1); | ||||||
|     } |       } | ||||||
|     if (restrictRect.Height() < 1) { |       if (restrictRect.Height() < 1) { | ||||||
|       restrictRect.SetHeight(1); |         restrictRect.SetHeight(1); | ||||||
|     } |       } | ||||||
| 
 | 
 | ||||||
|     if (restrictRect.X() != 0 || restrictRect.Y() != 0 || |       if (restrictRect.X() != 0 || restrictRect.Y() != 0 || | ||||||
|         restrictRect.Size() != destRect.Size()) { |           restrictRect.Size() != destRect.Size()) { | ||||||
|       IntRect sampleRect = restrictRect.ToUnknownRect(); |         IntRect sampleRect = restrictRect.ToUnknownRect(); | ||||||
|       aRegion = Some(ImageIntRegion::CreateWithSamplingRestriction( |         aRegion = Some(ImageIntRegion::CreateWithSamplingRestriction( | ||||||
|           sampleRect, sampleRect, ExtendMode::CLAMP)); |             sampleRect, sampleRect, ExtendMode::CLAMP)); | ||||||
|  |       } | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -26,6 +26,7 @@ | ||||||
| #include "mozilla/dom/HTMLAreaElement.h" | #include "mozilla/dom/HTMLAreaElement.h" | ||||||
| #include "mozilla/dom/HTMLImageElement.h" | #include "mozilla/dom/HTMLImageElement.h" | ||||||
| #include "mozilla/dom/ResponsiveImageSelector.h" | #include "mozilla/dom/ResponsiveImageSelector.h" | ||||||
|  | #include "mozilla/image/WebRenderImageProvider.h" | ||||||
| #include "mozilla/layers/RenderRootStateManager.h" | #include "mozilla/layers/RenderRootStateManager.h" | ||||||
| #include "mozilla/layers/WebRenderLayerManager.h" | #include "mozilla/layers/WebRenderLayerManager.h" | ||||||
| #include "mozilla/MouseEvents.h" | #include "mozilla/MouseEvents.h" | ||||||
|  | @ -998,9 +999,8 @@ void nsImageFrame::InvalidateSelf(const nsIntRect* aLayerInvalidRect, | ||||||
|   // Check if WebRender has interacted with this frame. If it has
 |   // Check if WebRender has interacted with this frame. If it has
 | ||||||
|   // we need to let it know that things have changed.
 |   // we need to let it know that things have changed.
 | ||||||
|   const auto type = DisplayItemType::TYPE_IMAGE; |   const auto type = DisplayItemType::TYPE_IMAGE; | ||||||
|   const auto producerId = |   const auto providerId = mImage ? mImage->GetProviderId() : 0; | ||||||
|       mImage ? mImage->GetProducerId() : kContainerProducerID_Invalid; |   if (WebRenderUserData::ProcessInvalidateForImage(this, type, providerId)) { | ||||||
|   if (WebRenderUserData::ProcessInvalidateForImage(this, type, producerId)) { |  | ||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | @ -1843,13 +1843,13 @@ ImgDrawResult nsImageFrame::DisplayAltFeedbackWithoutLayer( | ||||||
|           nsLayoutUtils::ComputeImageContainerDrawingParameters( |           nsLayoutUtils::ComputeImageContainerDrawingParameters( | ||||||
|               imgCon, this, destRect, destRect, aSc, aFlags, svgContext, |               imgCon, this, destRect, destRect, aSc, aFlags, svgContext, | ||||||
|               region); |               region); | ||||||
|       RefPtr<ImageContainer> container; |       RefPtr<image::WebRenderImageProvider> provider; | ||||||
|       result = imgCon->GetImageContainerAtSize( |       result = imgCon->GetImageProvider(aManager->LayerManager(), decodeSize, | ||||||
|           aManager->LayerManager(), decodeSize, svgContext, region, aFlags, |                                         svgContext, region, aFlags, | ||||||
|           getter_AddRefs(container)); |                                         getter_AddRefs(provider)); | ||||||
|       if (container) { |       if (provider) { | ||||||
|         bool wrResult = aManager->CommandBuilder().PushImage( |         bool wrResult = aManager->CommandBuilder().PushImageProvider( | ||||||
|             aItem, container, aBuilder, aResources, aSc, destRect, bounds); |             aItem, provider, aBuilder, aResources, destRect, bounds); | ||||||
|         result &= wrResult ? ImgDrawResult::SUCCESS : ImgDrawResult::NOT_READY; |         result &= wrResult ? ImgDrawResult::SUCCESS : ImgDrawResult::NOT_READY; | ||||||
|       } else { |       } else { | ||||||
|         // We don't use &= here because we want the result to be NOT_READY so
 |         // We don't use &= here because we want the result to be NOT_READY so
 | ||||||
|  | @ -2098,10 +2098,10 @@ bool nsDisplayImage::CreateWebRenderCommands( | ||||||
|   IntSize decodeSize = nsLayoutUtils::ComputeImageContainerDrawingParameters( |   IntSize decodeSize = nsLayoutUtils::ComputeImageContainerDrawingParameters( | ||||||
|       mImage, mFrame, destRect, destRect, aSc, flags, svgContext, region); |       mImage, mFrame, destRect, destRect, aSc, flags, svgContext, region); | ||||||
| 
 | 
 | ||||||
|   RefPtr<layers::ImageContainer> container; |   RefPtr<image::WebRenderImageProvider> provider; | ||||||
|   ImgDrawResult drawResult = mImage->GetImageContainerAtSize( |   ImgDrawResult drawResult = | ||||||
|       aManager->LayerManager(), decodeSize, svgContext, region, flags, |       mImage->GetImageProvider(aManager->LayerManager(), decodeSize, svgContext, | ||||||
|       getter_AddRefs(container)); |                                region, flags, getter_AddRefs(provider)); | ||||||
| 
 | 
 | ||||||
|   // While we got a container, it may not contain a fully decoded surface. If
 |   // 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
 |   // that is the case, and we have an image we were previously displaying which
 | ||||||
|  | @ -2123,13 +2123,13 @@ bool nsDisplayImage::CreateWebRenderCommands( | ||||||
|           prevFlags &= ~imgIContainer::FLAG_RECORD_BLOB; |           prevFlags &= ~imgIContainer::FLAG_RECORD_BLOB; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         RefPtr<ImageContainer> prevContainer; |         RefPtr<image::WebRenderImageProvider> prevProvider; | ||||||
|         ImgDrawResult newDrawResult = mPrevImage->GetImageContainerAtSize( |         ImgDrawResult newDrawResult = mPrevImage->GetImageProvider( | ||||||
|             aManager->LayerManager(), decodeSize, svgContext, region, prevFlags, |             aManager->LayerManager(), decodeSize, svgContext, region, prevFlags, | ||||||
|             getter_AddRefs(prevContainer)); |             getter_AddRefs(prevProvider)); | ||||||
|         if (prevContainer && newDrawResult == ImgDrawResult::SUCCESS) { |         if (prevProvider && newDrawResult == ImgDrawResult::SUCCESS) { | ||||||
|           drawResult = newDrawResult; |           drawResult = newDrawResult; | ||||||
|           container = std::move(prevContainer); |           provider = std::move(prevProvider); | ||||||
|           flags = prevFlags; |           flags = prevFlags; | ||||||
|           break; |           break; | ||||||
|         } |         } | ||||||
|  | @ -2156,14 +2156,9 @@ bool nsDisplayImage::CreateWebRenderCommands( | ||||||
|   // If the image container is empty, we don't want to fallback. Any other
 |   // 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
 |   // failure will be due to resource constraints and fallback is unlikely to
 | ||||||
|   // help us. Hence we can ignore the return value from PushImage.
 |   // help us. Hence we can ignore the return value from PushImage.
 | ||||||
|   if (container) { |   if (provider) { | ||||||
|     if (flags & imgIContainer::FLAG_RECORD_BLOB) { |     aManager->CommandBuilder().PushImageProvider( | ||||||
|       aManager->CommandBuilder().PushBlobImage(this, container, aBuilder, |         this, provider, aBuilder, aResources, destRect, destRect); | ||||||
|                                                aResources, destRect, destRect); |  | ||||||
|     } else { |  | ||||||
|       aManager->CommandBuilder().PushImage(this, container, aBuilder, |  | ||||||
|                                            aResources, aSc, destRect, destRect); |  | ||||||
|     } |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   nsDisplayItemGenericImageGeometry::UpdateDrawResult(this, drawResult); |   nsDisplayItemGenericImageGeometry::UpdateDrawResult(this, drawResult); | ||||||
|  |  | ||||||
|  | @ -29,6 +29,7 @@ | ||||||
| #include "nsStyleStruct.h" | #include "nsStyleStruct.h" | ||||||
| #include "gfx2DGlue.h" | #include "gfx2DGlue.h" | ||||||
| #include "gfxGradientCache.h" | #include "gfxGradientCache.h" | ||||||
|  | #include "mozilla/image/WebRenderImageProvider.h" | ||||||
| #include "mozilla/layers/StackingContextHelper.h" | #include "mozilla/layers/StackingContextHelper.h" | ||||||
| #include "mozilla/layers/RenderRootStateManager.h" | #include "mozilla/layers/RenderRootStateManager.h" | ||||||
| #include "mozilla/layers/WebRenderLayerManager.h" | #include "mozilla/layers/WebRenderLayerManager.h" | ||||||
|  | @ -3628,20 +3629,17 @@ ImgDrawResult nsCSSBorderImageRenderer::CreateWebRenderCommands( | ||||||
|               img, aForFrame, imageRect, imageRect, aSc, flags, svgContext, |               img, aForFrame, imageRect, imageRect, aSc, flags, svgContext, | ||||||
|               region); |               region); | ||||||
| 
 | 
 | ||||||
|       RefPtr<layers::ImageContainer> container; |       RefPtr<WebRenderImageProvider> provider; | ||||||
|       drawResult = img->GetImageContainerAtSize( |       drawResult = img->GetImageProvider(aManager->LayerManager(), decodeSize, | ||||||
|           aManager->LayerManager(), decodeSize, svgContext, region, flags, |                                          svgContext, region, flags, | ||||||
|           getter_AddRefs(container)); |                                          getter_AddRefs(provider)); | ||||||
|       if (!container) { |       if (!provider) { | ||||||
|         break; |         break; | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       auto rendering = |       Maybe<wr::ImageKey> key = | ||||||
|           wr::ToImageRendering(aItem->Frame()->UsedImageRendering()); |           aManager->CommandBuilder().CreateImageProviderKey(aItem, provider, | ||||||
|       gfx::IntSize size; |                                                             aResources); | ||||||
|       Maybe<wr::ImageKey> key = aManager->CommandBuilder().CreateImageKey( |  | ||||||
|           aItem, container, aBuilder, aResources, rendering, aSc, size, |  | ||||||
|           Nothing()); |  | ||||||
|       if (key.isNothing()) { |       if (key.isNothing()) { | ||||||
|         break; |         break; | ||||||
|       } |       } | ||||||
|  | @ -3656,6 +3654,8 @@ ImgDrawResult nsCSSBorderImageRenderer::CreateWebRenderCommands( | ||||||
|         // but there are reftests that are sensible to the test going through a
 |         // but there are reftests that are sensible to the test going through a
 | ||||||
|         // blob while the reference doesn't.
 |         // blob while the reference doesn't.
 | ||||||
|         if (noVerticalBorders && noHorizontalBorders) { |         if (noVerticalBorders && noHorizontalBorders) { | ||||||
|  |           auto rendering = | ||||||
|  |               wr::ToImageRendering(aItem->Frame()->UsedImageRendering()); | ||||||
|           aBuilder.PushImage(dest, clip, !aItem->BackfaceIsHidden(), rendering, |           aBuilder.PushImage(dest, clip, !aItem->BackfaceIsHidden(), rendering, | ||||||
|                              key.value()); |                              key.value()); | ||||||
|           break; |           break; | ||||||
|  |  | ||||||
|  | @ -14,6 +14,7 @@ | ||||||
| #include "gfxDrawable.h" | #include "gfxDrawable.h" | ||||||
| #include "ImageOps.h" | #include "ImageOps.h" | ||||||
| #include "ImageRegion.h" | #include "ImageRegion.h" | ||||||
|  | #include "mozilla/image/WebRenderImageProvider.h" | ||||||
| #include "mozilla/layers/RenderRootStateManager.h" | #include "mozilla/layers/RenderRootStateManager.h" | ||||||
| #include "mozilla/layers/StackingContextHelper.h" | #include "mozilla/layers/StackingContextHelper.h" | ||||||
| #include "mozilla/layers/WebRenderLayerManager.h" | #include "mozilla/layers/WebRenderLayerManager.h" | ||||||
|  | @ -621,40 +622,30 @@ ImgDrawResult nsImageRenderer::BuildWebRenderDisplayItems( | ||||||
|               mImageContainer, mForFrame, destRect, clipRect, aSc, |               mImageContainer, mForFrame, destRect, clipRect, aSc, | ||||||
|               containerFlags, svgContext, region); |               containerFlags, svgContext, region); | ||||||
| 
 | 
 | ||||||
|       if (extendMode != ExtendMode::CLAMP) { |       RefPtr<image::WebRenderImageProvider> provider; | ||||||
|         region = Nothing(); |       drawResult = mImageContainer->GetImageProvider( | ||||||
|       } |  | ||||||
| 
 |  | ||||||
|       RefPtr<layers::ImageContainer> container; |  | ||||||
|       drawResult = mImageContainer->GetImageContainerAtSize( |  | ||||||
|           aManager->LayerManager(), decodeSize, svgContext, region, |           aManager->LayerManager(), decodeSize, svgContext, region, | ||||||
|           containerFlags, getter_AddRefs(container)); |           containerFlags, getter_AddRefs(provider)); | ||||||
|       if (!container) { |       if (!provider) { | ||||||
|         NS_WARNING("Failed to get image container"); |         NS_WARNING("Failed to get image container"); | ||||||
|         break; |         break; | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       if (containerFlags & imgIContainer::FLAG_RECORD_BLOB) { |       Maybe<wr::ImageKey> key = | ||||||
|         MOZ_ASSERT(extendMode == ExtendMode::CLAMP); |           aManager->CommandBuilder().CreateImageProviderKey(aItem, provider, | ||||||
|         aManager->CommandBuilder().PushBlobImage( |                                                             aResources); | ||||||
|             aItem, container, aBuilder, aResources, clipRect, clipRect); |       if (key.isNothing()) { | ||||||
|         break; |         break; | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       auto rendering = |       auto rendering = | ||||||
|           wr::ToImageRendering(aItem->Frame()->UsedImageRendering()); |           wr::ToImageRendering(aItem->Frame()->UsedImageRendering()); | ||||||
|       gfx::IntSize size; |  | ||||||
|       Maybe<wr::ImageKey> key = aManager->CommandBuilder().CreateImageKey( |  | ||||||
|           aItem, container, aBuilder, aResources, rendering, aSc, size, |  | ||||||
|           Nothing()); |  | ||||||
| 
 |  | ||||||
|       if (key.isNothing()) { |  | ||||||
|         break; |  | ||||||
|       } |  | ||||||
| 
 |  | ||||||
|       wr::LayoutRect dest = wr::ToLayoutRect(destRect); |  | ||||||
|       wr::LayoutRect clip = wr::ToLayoutRect(clipRect); |       wr::LayoutRect clip = wr::ToLayoutRect(clipRect); | ||||||
| 
 | 
 | ||||||
|  |       // If we provided a region to the provider, then it already took the
 | ||||||
|  |       // dest rect into account when it did the recording.
 | ||||||
|  |       wr::LayoutRect dest = region ? clip : wr::ToLayoutRect(destRect); | ||||||
|  | 
 | ||||||
|       if (extendMode == ExtendMode::CLAMP) { |       if (extendMode == ExtendMode::CLAMP) { | ||||||
|         // The image is not repeating. Just push as a regular image.
 |         // The image is not repeating. Just push as a regular image.
 | ||||||
|         aBuilder.PushImage(dest, clip, !aItem->BackfaceIsHidden(), rendering, |         aBuilder.PushImage(dest, clip, !aItem->BackfaceIsHidden(), rendering, | ||||||
|  |  | ||||||
|  | @ -540,9 +540,9 @@ static void InvalidateImages(nsIFrame* aFrame, imgIRequest* aRequest, | ||||||
|           static_cast<layers::WebRenderMaskData*>(data.get())->Invalidate(); |           static_cast<layers::WebRenderMaskData*>(data.get())->Invalidate(); | ||||||
|           invalidateFrame = true; |           invalidateFrame = true; | ||||||
|           break; |           break; | ||||||
|         case layers::WebRenderUserData::UserDataType::eImage: |         case layers::WebRenderUserData::UserDataType::eImageProvider: | ||||||
|           if (static_cast<layers::WebRenderImageData*>(data.get()) |           if (static_cast<layers::WebRenderImageProviderData*>(data.get()) | ||||||
|                   ->UsingSharedSurface(aRequest->GetProducerId())) { |                   ->Invalidate(aRequest->GetProviderId())) { | ||||||
|             break; |             break; | ||||||
|           } |           } | ||||||
|           [[fallthrough]]; |           [[fallthrough]]; | ||||||
|  |  | ||||||
|  | @ -10,6 +10,7 @@ | ||||||
| #include "gfxContext.h" | #include "gfxContext.h" | ||||||
| #include "gfxPlatform.h" | #include "gfxPlatform.h" | ||||||
| #include "mozilla/gfx/2D.h" | #include "mozilla/gfx/2D.h" | ||||||
|  | #include "mozilla/image/WebRenderImageProvider.h" | ||||||
| #include "mozilla/layers/RenderRootStateManager.h" | #include "mozilla/layers/RenderRootStateManager.h" | ||||||
| #include "mozilla/layers/WebRenderLayerManager.h" | #include "mozilla/layers/WebRenderLayerManager.h" | ||||||
| #include "imgIContainer.h" | #include "imgIContainer.h" | ||||||
|  | @ -620,10 +621,10 @@ bool SVGImageFrame::CreateWebRenderCommands( | ||||||
|       mImageContainer, this, destRect, clipRect, aSc, flags, svgContext, |       mImageContainer, this, destRect, clipRect, aSc, flags, svgContext, | ||||||
|       region); |       region); | ||||||
| 
 | 
 | ||||||
|   RefPtr<layers::ImageContainer> container; |   RefPtr<image::WebRenderImageProvider> provider; | ||||||
|   ImgDrawResult drawResult = mImageContainer->GetImageContainerAtSize( |   ImgDrawResult drawResult = mImageContainer->GetImageProvider( | ||||||
|       aManager->LayerManager(), decodeSize, svgContext, region, flags, |       aManager->LayerManager(), decodeSize, svgContext, region, flags, | ||||||
|       getter_AddRefs(container)); |       getter_AddRefs(provider)); | ||||||
| 
 | 
 | ||||||
|   // While we got a container, it may not contain a fully decoded surface. If
 |   // 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
 |   // that is the case, and we have an image we were previously displaying which
 | ||||||
|  | @ -647,14 +648,9 @@ bool SVGImageFrame::CreateWebRenderCommands( | ||||||
|     // If the image container is empty, we don't want to fallback. Any other
 |     // 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
 |     // failure will be due to resource constraints and fallback is unlikely to
 | ||||||
|     // help us. Hence we can ignore the return value from PushImage.
 |     // help us. Hence we can ignore the return value from PushImage.
 | ||||||
|     if (container) { |     if (provider) { | ||||||
|       if (flags & imgIContainer::FLAG_RECORD_BLOB) { |       aManager->CommandBuilder().PushImageProvider( | ||||||
|         aManager->CommandBuilder().PushBlobImage( |           aItem, provider, aBuilder, aResources, destRect, clipRect); | ||||||
|             aItem, container, aBuilder, aResources, destRect, clipRect); |  | ||||||
|       } else { |  | ||||||
|         aManager->CommandBuilder().PushImage( |  | ||||||
|             aItem, container, aBuilder, aResources, aSc, destRect, clipRect); |  | ||||||
|       } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     nsDisplayItemGenericImageGeometry::UpdateDrawResult(aItem, drawResult); |     nsDisplayItemGenericImageGeometry::UpdateDrawResult(aItem, drawResult); | ||||||
|  |  | ||||||
|  | @ -51,6 +51,7 @@ | ||||||
| #include "mozilla/StaticPrefs_image.h" | #include "mozilla/StaticPrefs_image.h" | ||||||
| #include "mozilla/SVGImageContext.h" | #include "mozilla/SVGImageContext.h" | ||||||
| #include "Units.h" | #include "Units.h" | ||||||
|  | #include "mozilla/image/WebRenderImageProvider.h" | ||||||
| #include "mozilla/layers/RenderRootStateManager.h" | #include "mozilla/layers/RenderRootStateManager.h" | ||||||
| #include "mozilla/layers/WebRenderLayerManager.h" | #include "mozilla/layers/WebRenderLayerManager.h" | ||||||
| #include "mozilla/dom/ImageTracker.h" | #include "mozilla/dom/ImageTracker.h" | ||||||
|  | @ -428,39 +429,25 @@ ImgDrawResult nsImageBoxFrame::CreateWebRenderCommands( | ||||||
|           imgCon, aItem->Frame(), fillRect, fillRect, aSc, aFlags, svgContext, |           imgCon, aItem->Frame(), fillRect, fillRect, aSc, aFlags, svgContext, | ||||||
|           region); |           region); | ||||||
| 
 | 
 | ||||||
|   RefPtr<layers::ImageContainer> container; |   RefPtr<image::WebRenderImageProvider> provider; | ||||||
|   result = imgCon->GetImageContainerAtSize(aManager->LayerManager(), decodeSize, |   result = | ||||||
|                                            svgContext, region, aFlags, |       imgCon->GetImageProvider(aManager->LayerManager(), decodeSize, svgContext, | ||||||
|                                            getter_AddRefs(container)); |                                region, aFlags, getter_AddRefs(provider)); | ||||||
|   if (!container) { |   if (!provider) { | ||||||
|     NS_WARNING("Failed to get image container"); |     NS_WARNING("Failed to get image provider"); | ||||||
|     return result; |     return result; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   auto rendering = wr::ToImageRendering(aItem->Frame()->UsedImageRendering()); |   auto rendering = wr::ToImageRendering(aItem->Frame()->UsedImageRendering()); | ||||||
|   wr::LayoutRect fill = wr::ToLayoutRect(fillRect); |   wr::LayoutRect fill = wr::ToLayoutRect(fillRect); | ||||||
| 
 | 
 | ||||||
|   if (aFlags & imgIContainer::FLAG_RECORD_BLOB) { |   Maybe<wr::ImageKey> key = aManager->CommandBuilder().CreateImageProviderKey( | ||||||
|     Maybe<wr::BlobImageKey> key = aManager->CommandBuilder().CreateBlobImageKey( |       aItem, provider, aResources); | ||||||
|         aItem, container, aResources); |  | ||||||
|     if (key.isNothing()) { |  | ||||||
|       return result; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     aBuilder.PushImage(fill, fill, !BackfaceIsHidden(), rendering, |  | ||||||
|                        wr::AsImageKey(key.value())); |  | ||||||
|     return result; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   gfx::IntSize size; |  | ||||||
|   Maybe<wr::ImageKey> key = aManager->CommandBuilder().CreateImageKey( |  | ||||||
|       aItem, container, aBuilder, aResources, rendering, aSc, size, Nothing()); |  | ||||||
|   if (key.isNothing()) { |   if (key.isNothing()) { | ||||||
|     return result; |     return result; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   aBuilder.PushImage(fill, fill, !BackfaceIsHidden(), rendering, key.value()); |   aBuilder.PushImage(fill, fill, !BackfaceIsHidden(), rendering, key.value()); | ||||||
| 
 |  | ||||||
|   return result; |   return result; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -841,8 +828,8 @@ void nsImageBoxFrame::OnFrameUpdate(imgIRequest* aRequest) { | ||||||
|   // Check if WebRender has interacted with this frame. If it has
 |   // Check if WebRender has interacted with this frame. If it has
 | ||||||
|   // we need to let it know that things have changed.
 |   // we need to let it know that things have changed.
 | ||||||
|   const auto type = DisplayItemType::TYPE_XUL_IMAGE; |   const auto type = DisplayItemType::TYPE_XUL_IMAGE; | ||||||
|   const auto producerId = aRequest->GetProducerId(); |   const auto providerId = aRequest->GetProviderId(); | ||||||
|   if (WebRenderUserData::ProcessInvalidateForImage(this, type, producerId)) { |   if (WebRenderUserData::ProcessInvalidateForImage(this, type, providerId)) { | ||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Andrew Osmond
						Andrew Osmond