1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2  * This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 #include "ImageBitmapRenderingContext.h"
7 #include "mozilla/dom/ImageBitmapRenderingContextBinding.h"
8 #include "ImageContainer.h"
9 #include "ImageLayers.h"
10 
11 namespace mozilla {
12 namespace dom {
13 
ImageBitmapRenderingContext()14 ImageBitmapRenderingContext::ImageBitmapRenderingContext()
15     : mWidth(0), mHeight(0) {}
16 
~ImageBitmapRenderingContext()17 ImageBitmapRenderingContext::~ImageBitmapRenderingContext() {
18   RemovePostRefreshObserver();
19 }
20 
WrapObject(JSContext * aCx,JS::Handle<JSObject * > aGivenProto)21 JSObject* ImageBitmapRenderingContext::WrapObject(
22     JSContext* aCx, JS::Handle<JSObject*> aGivenProto) {
23   return ImageBitmapRenderingContextBinding::Wrap(aCx, this, aGivenProto);
24 }
25 
26 already_AddRefed<layers::Image>
ClipToIntrinsicSize()27 ImageBitmapRenderingContext::ClipToIntrinsicSize() {
28   if (!mImage) {
29     return nullptr;
30   }
31 
32   // If image is larger than canvas intrinsic size, clip it to the intrinsic
33   // size.
34   RefPtr<gfx::SourceSurface> surface;
35   RefPtr<layers::Image> result;
36   if (mWidth < mImage->GetSize().width || mHeight < mImage->GetSize().height) {
37     surface = MatchWithIntrinsicSize();
38   } else {
39     surface = mImage->GetAsSourceSurface();
40   }
41   if (!surface) {
42     return nullptr;
43   }
44   result =
45       new layers::SourceSurfaceImage(gfx::IntSize(mWidth, mHeight), surface);
46   return result.forget();
47 }
48 
TransferImageBitmap(ImageBitmap & aImageBitmap)49 void ImageBitmapRenderingContext::TransferImageBitmap(
50     ImageBitmap& aImageBitmap) {
51   TransferFromImageBitmap(aImageBitmap);
52 }
53 
TransferFromImageBitmap(ImageBitmap & aImageBitmap)54 void ImageBitmapRenderingContext::TransferFromImageBitmap(
55     ImageBitmap& aImageBitmap) {
56   Reset();
57   mImage = aImageBitmap.TransferAsImage();
58 
59   if (!mImage) {
60     return;
61   }
62 
63   if (aImageBitmap.IsWriteOnly() && mCanvasElement) {
64     mCanvasElement->SetWriteOnly();
65   }
66 
67   Redraw(gfxRect(0, 0, mWidth, mHeight));
68 }
69 
70 NS_IMETHODIMP
SetDimensions(int32_t aWidth,int32_t aHeight)71 ImageBitmapRenderingContext::SetDimensions(int32_t aWidth, int32_t aHeight) {
72   mWidth = aWidth;
73   mHeight = aHeight;
74   return NS_OK;
75 }
76 
77 NS_IMETHODIMP
InitializeWithDrawTarget(nsIDocShell * aDocShell,NotNull<gfx::DrawTarget * > aTarget)78 ImageBitmapRenderingContext::InitializeWithDrawTarget(
79     nsIDocShell* aDocShell, NotNull<gfx::DrawTarget*> aTarget) {
80   return NS_ERROR_NOT_IMPLEMENTED;
81 }
82 
83 already_AddRefed<DataSourceSurface>
MatchWithIntrinsicSize()84 ImageBitmapRenderingContext::MatchWithIntrinsicSize() {
85   RefPtr<SourceSurface> surface = mImage->GetAsSourceSurface();
86   RefPtr<DataSourceSurface> temp = Factory::CreateDataSourceSurface(
87       IntSize(mWidth, mHeight), surface->GetFormat());
88   if (!temp) {
89     return nullptr;
90   }
91 
92   DataSourceSurface::ScopedMap map(temp, DataSourceSurface::READ_WRITE);
93   if (!map.IsMapped()) {
94     return nullptr;
95   }
96 
97   RefPtr<DrawTarget> dt = Factory::CreateDrawTargetForData(
98       gfxPlatform::GetPlatform()->GetSoftwareBackend(), map.GetData(),
99       temp->GetSize(), map.GetStride(), temp->GetFormat());
100   if (!dt || !dt->IsValid()) {
101     gfxWarning()
102         << "ImageBitmapRenderingContext::MatchWithIntrinsicSize failed";
103     return nullptr;
104   }
105 
106   dt->ClearRect(Rect(0, 0, mWidth, mHeight));
107   dt->CopySurface(
108       surface,
109       IntRect(0, 0, surface->GetSize().width, surface->GetSize().height),
110       IntPoint(0, 0));
111 
112   return temp.forget();
113 }
114 
GetImageBuffer(int32_t * aFormat)115 mozilla::UniquePtr<uint8_t[]> ImageBitmapRenderingContext::GetImageBuffer(
116     int32_t* aFormat) {
117   *aFormat = 0;
118 
119   if (!mImage) {
120     return nullptr;
121   }
122 
123   RefPtr<SourceSurface> surface = mImage->GetAsSourceSurface();
124   RefPtr<DataSourceSurface> data = surface->GetDataSurface();
125   if (!data) {
126     return nullptr;
127   }
128 
129   if (data->GetSize() != IntSize(mWidth, mHeight)) {
130     data = MatchWithIntrinsicSize();
131     if (!data) {
132       return nullptr;
133     }
134   }
135 
136   *aFormat = imgIEncoder::INPUT_FORMAT_HOSTARGB;
137   return SurfaceToPackedBGRA(data);
138 }
139 
140 NS_IMETHODIMP
GetInputStream(const char * aMimeType,const char16_t * aEncoderOptions,nsIInputStream ** aStream)141 ImageBitmapRenderingContext::GetInputStream(const char* aMimeType,
142                                             const char16_t* aEncoderOptions,
143                                             nsIInputStream** aStream) {
144   nsCString enccid("@mozilla.org/image/encoder;2?type=");
145   enccid += aMimeType;
146   nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(enccid.get());
147   if (!encoder) {
148     return NS_ERROR_FAILURE;
149   }
150 
151   int32_t format = 0;
152   UniquePtr<uint8_t[]> imageBuffer = GetImageBuffer(&format);
153   if (!imageBuffer) {
154     return NS_ERROR_FAILURE;
155   }
156 
157   return ImageEncoder::GetInputStream(mWidth, mHeight, imageBuffer.get(),
158                                       format, encoder, aEncoderOptions,
159                                       aStream);
160 }
161 
162 already_AddRefed<mozilla::gfx::SourceSurface>
GetSurfaceSnapshot(gfxAlphaType * const aOutAlphaType)163 ImageBitmapRenderingContext::GetSurfaceSnapshot(
164     gfxAlphaType* const aOutAlphaType) {
165   if (!mImage) {
166     return nullptr;
167   }
168 
169   if (aOutAlphaType) {
170     *aOutAlphaType =
171         (GetIsOpaque() ? gfxAlphaType::Opaque : gfxAlphaType::Premult);
172   }
173 
174   RefPtr<SourceSurface> surface = mImage->GetAsSourceSurface();
175   if (surface->GetSize() != IntSize(mWidth, mHeight)) {
176     return MatchWithIntrinsicSize();
177   }
178 
179   return surface.forget();
180 }
181 
SetIsOpaque(bool aIsOpaque)182 void ImageBitmapRenderingContext::SetIsOpaque(bool aIsOpaque) {}
183 
GetIsOpaque()184 bool ImageBitmapRenderingContext::GetIsOpaque() { return false; }
185 
186 NS_IMETHODIMP
Reset()187 ImageBitmapRenderingContext::Reset() {
188   if (mCanvasElement) {
189     mCanvasElement->InvalidateCanvas();
190   }
191 
192   mImage = nullptr;
193 
194   return NS_OK;
195 }
196 
GetCanvasLayer(nsDisplayListBuilder * aBuilder,Layer * aOldLayer,LayerManager * aManager)197 already_AddRefed<Layer> ImageBitmapRenderingContext::GetCanvasLayer(
198     nsDisplayListBuilder* aBuilder, Layer* aOldLayer, LayerManager* aManager) {
199   if (!mImage) {
200     // No DidTransactionCallback will be received, so mark the context clean
201     // now so future invalidations will be dispatched.
202     MarkContextClean();
203     return nullptr;
204   }
205 
206   RefPtr<ImageLayer> imageLayer;
207 
208   if (aOldLayer) {
209     imageLayer = static_cast<ImageLayer*>(aOldLayer);
210   } else {
211     imageLayer = aManager->CreateImageLayer();
212   }
213 
214   RefPtr<ImageContainer> imageContainer = imageLayer->GetContainer();
215   if (!imageContainer) {
216     imageContainer = aManager->CreateImageContainer();
217     imageLayer->SetContainer(imageContainer);
218   }
219 
220   AutoTArray<ImageContainer::NonOwningImage, 1> imageList;
221   RefPtr<layers::Image> image = ClipToIntrinsicSize();
222   if (!image) {
223     return nullptr;
224   }
225   imageList.AppendElement(ImageContainer::NonOwningImage(image));
226   imageContainer->SetCurrentImages(imageList);
227 
228   return imageLayer.forget();
229 }
230 
MarkContextClean()231 void ImageBitmapRenderingContext::MarkContextClean() {}
232 
233 NS_IMETHODIMP
Redraw(const gfxRect & aDirty)234 ImageBitmapRenderingContext::Redraw(const gfxRect& aDirty) {
235   if (!mCanvasElement) {
236     return NS_OK;
237   }
238 
239   mozilla::gfx::Rect rect = ToRect(aDirty);
240   mCanvasElement->InvalidateCanvasContent(&rect);
241   return NS_OK;
242 }
243 
244 NS_IMETHODIMP
SetIsIPC(bool aIsIPC)245 ImageBitmapRenderingContext::SetIsIPC(bool aIsIPC) { return NS_OK; }
246 
DidRefresh()247 void ImageBitmapRenderingContext::DidRefresh() {}
248 
MarkContextCleanForFrameCapture()249 void ImageBitmapRenderingContext::MarkContextCleanForFrameCapture() {}
250 
IsContextCleanForFrameCapture()251 bool ImageBitmapRenderingContext::IsContextCleanForFrameCapture() {
252   return true;
253 }
254 
255 NS_IMPL_CYCLE_COLLECTING_ADDREF(ImageBitmapRenderingContext)
256 NS_IMPL_CYCLE_COLLECTING_RELEASE(ImageBitmapRenderingContext)
257 
258 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(ImageBitmapRenderingContext,
259                                       mCanvasElement, mOffscreenCanvas)
260 
261 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ImageBitmapRenderingContext)
262   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
263   NS_INTERFACE_MAP_ENTRY(nsICanvasRenderingContextInternal)
264   NS_INTERFACE_MAP_ENTRY(nsISupports)
265 NS_INTERFACE_MAP_END
266 
267 }  // namespace dom
268 }  // namespace mozilla
269