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)
16   , mHeight(0)
17 {
18 }
19 
~ImageBitmapRenderingContext()20 ImageBitmapRenderingContext::~ImageBitmapRenderingContext()
21 {
22   RemovePostRefreshObserver();
23 }
24 
25 JSObject*
WrapObject(JSContext * aCx,JS::Handle<JSObject * > aGivenProto)26 ImageBitmapRenderingContext::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
27 {
28   return ImageBitmapRenderingContextBinding::Wrap(aCx, this, aGivenProto);
29 }
30 
31 already_AddRefed<layers::Image>
ClipToIntrinsicSize()32 ImageBitmapRenderingContext::ClipToIntrinsicSize()
33 {
34   if (!mImage) {
35     return nullptr;
36   }
37 
38   // If image is larger than canvas intrinsic size, clip it to the intrinsic size.
39   RefPtr<gfx::SourceSurface> surface;
40   RefPtr<layers::Image> result;
41   if (mWidth < mImage->GetSize().width ||
42       mHeight < mImage->GetSize().height) {
43     surface = MatchWithIntrinsicSize();
44   } else {
45     surface = mImage->GetAsSourceSurface();
46   }
47   result = new layers::SourceSurfaceImage(gfx::IntSize(mWidth, mHeight), surface);
48   return result.forget();
49 }
50 
51 void
TransferImageBitmap(ImageBitmap & aImageBitmap)52 ImageBitmapRenderingContext::TransferImageBitmap(ImageBitmap& aImageBitmap)
53 {
54   TransferFromImageBitmap(aImageBitmap);
55 }
56 
57 void
TransferFromImageBitmap(ImageBitmap & aImageBitmap)58 ImageBitmapRenderingContext::TransferFromImageBitmap(ImageBitmap& aImageBitmap)
59 {
60   Reset();
61   mImage = aImageBitmap.TransferAsImage();
62 
63   if (!mImage) {
64     return;
65   }
66 
67   Redraw(gfxRect(0, 0, mWidth, mHeight));
68 }
69 
70 int32_t
GetWidth() const71 ImageBitmapRenderingContext::GetWidth() const
72 {
73   return mWidth;
74 }
75 
76 int32_t
GetHeight() const77 ImageBitmapRenderingContext::GetHeight() const
78 {
79   return mHeight;
80 }
81 
82 NS_IMETHODIMP
SetDimensions(int32_t aWidth,int32_t aHeight)83 ImageBitmapRenderingContext::SetDimensions(int32_t aWidth, int32_t aHeight)
84 {
85   mWidth = aWidth;
86   mHeight = aHeight;
87   return NS_OK;
88 }
89 
90 NS_IMETHODIMP
InitializeWithDrawTarget(nsIDocShell * aDocShell,NotNull<gfx::DrawTarget * > aTarget)91 ImageBitmapRenderingContext::InitializeWithDrawTarget(nsIDocShell* aDocShell,
92                                                       NotNull<gfx::DrawTarget*> aTarget)
93 {
94   return NS_ERROR_NOT_IMPLEMENTED;
95 }
96 
97 already_AddRefed<DataSourceSurface>
MatchWithIntrinsicSize()98 ImageBitmapRenderingContext::MatchWithIntrinsicSize()
99 {
100   RefPtr<SourceSurface> surface = mImage->GetAsSourceSurface();
101   RefPtr<DataSourceSurface> temp =
102     Factory::CreateDataSourceSurface(IntSize(mWidth, mHeight), surface->GetFormat());
103   if (!temp) {
104     return nullptr;
105   }
106 
107   DataSourceSurface::ScopedMap map(temp, DataSourceSurface::READ_WRITE);
108   if (!map.IsMapped()) {
109     return nullptr;
110   }
111 
112   RefPtr<DrawTarget> dt =
113     Factory::CreateDrawTargetForData(BackendType::CAIRO,
114                                      map.GetData(),
115                                      temp->GetSize(),
116                                      map.GetStride(),
117                                      temp->GetFormat());
118   if (!dt || !dt->IsValid()) {
119     gfxWarning() << "ImageBitmapRenderingContext::MatchWithIntrinsicSize failed";
120     return nullptr;
121   }
122 
123 
124   dt->ClearRect(Rect(0, 0, mWidth, mHeight));
125   dt->CopySurface(surface,
126                   IntRect(0, 0, surface->GetSize().width,
127                                 surface->GetSize().height),
128                   IntPoint(0, 0));
129 
130   return temp.forget();
131 }
132 
133 mozilla::UniquePtr<uint8_t[]>
GetImageBuffer(int32_t * aFormat)134 ImageBitmapRenderingContext::GetImageBuffer(int32_t* aFormat)
135 {
136   *aFormat = 0;
137 
138   if (!mImage) {
139     return nullptr;
140   }
141 
142   RefPtr<SourceSurface> surface = mImage->GetAsSourceSurface();
143   RefPtr<DataSourceSurface> data = surface->GetDataSurface();
144   if (!data) {
145     return nullptr;
146   }
147 
148   if (data->GetSize() != IntSize(mWidth, mHeight)) {
149     data = MatchWithIntrinsicSize();
150   }
151 
152   *aFormat = imgIEncoder::INPUT_FORMAT_HOSTARGB;
153   return SurfaceToPackedBGRA(data);
154 }
155 
156 NS_IMETHODIMP
GetInputStream(const char * aMimeType,const char16_t * aEncoderOptions,nsIInputStream ** aStream)157 ImageBitmapRenderingContext::GetInputStream(const char* aMimeType,
158                                             const char16_t* aEncoderOptions,
159                                             nsIInputStream** aStream)
160 {
161   nsCString enccid("@mozilla.org/image/encoder;2?type=");
162   enccid += aMimeType;
163   nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(enccid.get());
164   if (!encoder) {
165     return NS_ERROR_FAILURE;
166   }
167 
168   int32_t format = 0;
169   UniquePtr<uint8_t[]> imageBuffer = GetImageBuffer(&format);
170   if (!imageBuffer) {
171     return NS_ERROR_FAILURE;
172   }
173 
174   return ImageEncoder::GetInputStream(mWidth, mHeight, imageBuffer.get(), format,
175                                       encoder, aEncoderOptions, aStream);
176 }
177 
178 already_AddRefed<mozilla::gfx::SourceSurface>
GetSurfaceSnapshot(bool * aPremultAlpha)179 ImageBitmapRenderingContext::GetSurfaceSnapshot(bool* aPremultAlpha)
180 {
181   if (!mImage) {
182     return nullptr;
183   }
184 
185   if (aPremultAlpha) {
186     *aPremultAlpha = true;
187   }
188 
189   RefPtr<SourceSurface> surface = mImage->GetAsSourceSurface();
190   if (surface->GetSize() != IntSize(mWidth, mHeight)) {
191     return MatchWithIntrinsicSize();
192   }
193 
194   return surface.forget();
195 }
196 
197 NS_IMETHODIMP
SetIsOpaque(bool aIsOpaque)198 ImageBitmapRenderingContext::SetIsOpaque(bool aIsOpaque)
199 {
200   return NS_OK;
201 }
202 
203 bool
GetIsOpaque()204 ImageBitmapRenderingContext::GetIsOpaque()
205 {
206   return false;
207 }
208 
209 NS_IMETHODIMP
Reset()210 ImageBitmapRenderingContext::Reset()
211 {
212   if (mCanvasElement) {
213     mCanvasElement->InvalidateCanvas();
214   }
215 
216   mImage = nullptr;
217 
218   return NS_OK;
219 }
220 
221 already_AddRefed<Layer>
GetCanvasLayer(nsDisplayListBuilder * aBuilder,Layer * aOldLayer,LayerManager * aManager,bool aMirror)222 ImageBitmapRenderingContext::GetCanvasLayer(nsDisplayListBuilder* aBuilder,
223                                             Layer* aOldLayer,
224                                             LayerManager* aManager,
225                                             bool aMirror /* = false */)
226 {
227   if (aMirror) {
228     // Not supported for ImageBitmapRenderingContext
229     return nullptr;
230   }
231 
232   if (!mImage) {
233     // No DidTransactionCallback will be received, so mark the context clean
234     // now so future invalidations will be dispatched.
235     MarkContextClean();
236     return nullptr;
237   }
238 
239   RefPtr<ImageLayer> imageLayer;
240 
241   if (aOldLayer) {
242     imageLayer = static_cast<ImageLayer*>(aOldLayer);
243   } else {
244     imageLayer = aManager->CreateImageLayer();
245   }
246 
247   RefPtr<ImageContainer> imageContainer = imageLayer->GetContainer();
248   if (!imageContainer) {
249     imageContainer = aManager->CreateImageContainer();
250     imageLayer->SetContainer(imageContainer);
251   }
252 
253   AutoTArray<ImageContainer::NonOwningImage, 1> imageList;
254   RefPtr<layers::Image> image = ClipToIntrinsicSize();
255   imageList.AppendElement(ImageContainer::NonOwningImage(image));
256   imageContainer->SetCurrentImages(imageList);
257 
258   return imageLayer.forget();
259 }
260 
261 void
MarkContextClean()262 ImageBitmapRenderingContext::MarkContextClean()
263 {
264 }
265 
266 NS_IMETHODIMP
Redraw(const gfxRect & aDirty)267 ImageBitmapRenderingContext::Redraw(const gfxRect& aDirty)
268 {
269   if (!mCanvasElement) {
270     return NS_OK;
271   }
272 
273   mozilla::gfx::Rect rect = ToRect(aDirty);
274   mCanvasElement->InvalidateCanvasContent(&rect);
275   return NS_OK;
276 }
277 
278 NS_IMETHODIMP
SetIsIPC(bool aIsIPC)279 ImageBitmapRenderingContext::SetIsIPC(bool aIsIPC)
280 {
281   return NS_OK;
282 }
283 
284 void
DidRefresh()285 ImageBitmapRenderingContext::DidRefresh()
286 {
287 }
288 
289 void
MarkContextCleanForFrameCapture()290 ImageBitmapRenderingContext::MarkContextCleanForFrameCapture()
291 {
292 }
293 
294 bool
IsContextCleanForFrameCapture()295 ImageBitmapRenderingContext::IsContextCleanForFrameCapture()
296 {
297   return true;
298 }
299 
300 NS_IMPL_CYCLE_COLLECTING_ADDREF(ImageBitmapRenderingContext)
301 NS_IMPL_CYCLE_COLLECTING_RELEASE(ImageBitmapRenderingContext)
302 
303 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(ImageBitmapRenderingContext,
304   mCanvasElement,
305   mOffscreenCanvas)
306 
307 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ImageBitmapRenderingContext)
308   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
309   NS_INTERFACE_MAP_ENTRY(nsICanvasRenderingContextInternal)
310   NS_INTERFACE_MAP_ENTRY(nsISupports)
311 NS_INTERFACE_MAP_END
312 
313 }
314 }
315