1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "AsyncCanvasRenderer.h"
8 
9 #include "gfxUtils.h"
10 #include "GLContext.h"
11 #include "GLReadTexImageHelper.h"
12 #include "GLScreenBuffer.h"
13 #include "mozilla/dom/HTMLCanvasElement.h"
14 #include "mozilla/layers/BufferTexture.h"
15 #include "mozilla/layers/CanvasClient.h"
16 #include "mozilla/layers/TextureClient.h"
17 #include "mozilla/layers/TextureClientSharedSurface.h"
18 #include "mozilla/ReentrantMonitor.h"
19 #include "nsIRunnable.h"
20 #include "nsThreadUtils.h"
21 
22 namespace mozilla {
23 namespace layers {
24 
AsyncCanvasRenderer()25 AsyncCanvasRenderer::AsyncCanvasRenderer()
26     : mHTMLCanvasElement(nullptr),
27       mContext(nullptr),
28       mGLContext(nullptr),
29       mIsAlphaPremultiplied(true),
30       mWidth(0),
31       mHeight(0),
32       mCanvasClient(nullptr),
33       mMutex("AsyncCanvasRenderer::mMutex") {
34   MOZ_COUNT_CTOR(AsyncCanvasRenderer);
35 }
36 
~AsyncCanvasRenderer()37 AsyncCanvasRenderer::~AsyncCanvasRenderer() {
38   MOZ_COUNT_DTOR(AsyncCanvasRenderer);
39 }
40 
NotifyElementAboutAttributesChanged()41 void AsyncCanvasRenderer::NotifyElementAboutAttributesChanged() {
42   class Runnable final : public mozilla::Runnable {
43    public:
44     explicit Runnable(AsyncCanvasRenderer* aRenderer)
45         : mozilla::Runnable("Runnable"), mRenderer(aRenderer) {}
46 
47     NS_IMETHOD Run() override {
48       if (mRenderer) {
49         dom::HTMLCanvasElement::SetAttrFromAsyncCanvasRenderer(mRenderer);
50       }
51 
52       return NS_OK;
53     }
54 
55    private:
56     RefPtr<AsyncCanvasRenderer> mRenderer;
57   };
58 
59   nsCOMPtr<nsIRunnable> runnable = new Runnable(this);
60   nsresult rv = NS_DispatchToMainThread(runnable);
61   if (NS_FAILED(rv)) {
62     NS_WARNING("Failed to dispatch a runnable to the main-thread.");
63   }
64 }
65 
NotifyElementAboutInvalidation()66 void AsyncCanvasRenderer::NotifyElementAboutInvalidation() {
67   class Runnable final : public mozilla::Runnable {
68    public:
69     explicit Runnable(AsyncCanvasRenderer* aRenderer)
70         : mozilla::Runnable("Runnable"), mRenderer(aRenderer) {}
71 
72     NS_IMETHOD Run() override {
73       if (mRenderer) {
74         dom::HTMLCanvasElement::InvalidateFromAsyncCanvasRenderer(mRenderer);
75       }
76 
77       return NS_OK;
78     }
79 
80    private:
81     RefPtr<AsyncCanvasRenderer> mRenderer;
82   };
83 
84   nsCOMPtr<nsIRunnable> runnable = new Runnable(this);
85   nsresult rv = NS_DispatchToMainThread(runnable);
86   if (NS_FAILED(rv)) {
87     NS_WARNING("Failed to dispatch a runnable to the main-thread.");
88   }
89 }
90 
SetCanvasClient(CanvasClient * aClient)91 void AsyncCanvasRenderer::SetCanvasClient(CanvasClient* aClient) {
92   mCanvasClient = aClient;
93   if (aClient) {
94     mCanvasClientAsyncHandle = aClient->GetAsyncHandle();
95   } else {
96     mCanvasClientAsyncHandle = CompositableHandle();
97   }
98 }
99 
SetActiveEventTarget()100 void AsyncCanvasRenderer::SetActiveEventTarget() {
101   MutexAutoLock lock(mMutex);
102   mActiveEventTarget = GetCurrentThreadSerialEventTarget();
103 }
104 
ResetActiveEventTarget()105 void AsyncCanvasRenderer::ResetActiveEventTarget() {
106   MutexAutoLock lock(mMutex);
107   mActiveEventTarget = nullptr;
108 }
109 
110 already_AddRefed<nsISerialEventTarget>
GetActiveEventTarget()111 AsyncCanvasRenderer::GetActiveEventTarget() {
112   MutexAutoLock lock(mMutex);
113   nsCOMPtr<nsISerialEventTarget> result = mActiveEventTarget;
114   return result.forget();
115 }
116 
CopyFromTextureClient(TextureClient * aTextureClient)117 void AsyncCanvasRenderer::CopyFromTextureClient(TextureClient* aTextureClient) {
118   MutexAutoLock lock(mMutex);
119 
120   if (!aTextureClient) {
121     mSurfaceForBasic = nullptr;
122     return;
123   }
124 
125   TextureClientAutoLock texLock(aTextureClient, layers::OpenMode::OPEN_READ);
126   if (!texLock.Succeeded()) {
127     return;
128   }
129 
130   const gfx::IntSize& size = aTextureClient->GetSize();
131   // This buffer would be used later for content rendering. So we choose
132   // B8G8R8A8 format here.
133   const gfx::SurfaceFormat format = gfx::SurfaceFormat::B8G8R8A8;
134   // Avoid to create buffer every time.
135   if (!mSurfaceForBasic || size != mSurfaceForBasic->GetSize() ||
136       format != mSurfaceForBasic->GetFormat()) {
137     uint32_t stride =
138         gfx::GetAlignedStride<8>(size.width, BytesPerPixel(format));
139     mSurfaceForBasic =
140         gfx::Factory::CreateDataSourceSurfaceWithStride(size, format, stride);
141     if (!mSurfaceForBasic) {
142       return;
143     }
144   }
145 
146   MappedTextureData mapped;
147   if (!aTextureClient->BorrowMappedData(mapped)) {
148     return;
149   }
150 
151   const uint8_t* lockedBytes = mapped.data;
152   gfx::DataSourceSurface::ScopedMap map(mSurfaceForBasic,
153                                         gfx::DataSourceSurface::MapType::WRITE);
154   if (!map.IsMapped()) {
155     return;
156   }
157 
158   MOZ_ASSERT(map.GetStride() == mapped.stride);
159   memcpy(map.GetData(), lockedBytes,
160          map.GetStride() * mSurfaceForBasic->GetSize().height);
161 
162   if (mSurfaceForBasic->GetFormat() == gfx::SurfaceFormat::R8G8B8A8 ||
163       mSurfaceForBasic->GetFormat() == gfx::SurfaceFormat::R8G8B8X8) {
164     gl::SwapRAndBComponents(mSurfaceForBasic);
165   }
166 }
167 
UpdateTarget()168 already_AddRefed<gfx::DataSourceSurface> AsyncCanvasRenderer::UpdateTarget() {
169   if (!mGLContext) {
170     return nullptr;
171   }
172 
173   gl::SharedSurface* frontbuffer = nullptr;
174   gl::GLScreenBuffer* screen = mGLContext->Screen();
175   const auto& front = screen->Front();
176   if (front) {
177     frontbuffer = front->Surf();
178   }
179 
180   if (!frontbuffer) {
181     return nullptr;
182   }
183 
184   if (frontbuffer->mType == gl::SharedSurfaceType::Basic) {
185     return nullptr;
186   }
187 
188   const gfx::IntSize& size = frontbuffer->mSize;
189   // This buffer would be used later for content rendering. So we choose
190   // B8G8R8A8 format here.
191   const gfx::SurfaceFormat format = gfx::SurfaceFormat::B8G8R8A8;
192   uint32_t stride = gfx::GetAlignedStride<8>(size.width, BytesPerPixel(format));
193   RefPtr<gfx::DataSourceSurface> surface =
194       gfx::Factory::CreateDataSourceSurfaceWithStride(size, format, stride);
195 
196   if (NS_WARN_IF(!surface)) {
197     return nullptr;
198   }
199 
200   if (!frontbuffer->ReadbackBySharedHandle(surface)) {
201     return nullptr;
202   }
203 
204   bool needsPremult = frontbuffer->mHasAlpha && !mIsAlphaPremultiplied;
205   if (needsPremult) {
206     gfxUtils::PremultiplyDataSurface(surface, surface);
207   }
208 
209   return surface.forget();
210 }
211 
GetSurface()212 already_AddRefed<gfx::DataSourceSurface> AsyncCanvasRenderer::GetSurface() {
213   MOZ_ASSERT(NS_IsMainThread());
214   MutexAutoLock lock(mMutex);
215   if (mSurfaceForBasic) {
216     // Since SourceSurface isn't thread-safe, we need copy to a new
217     // SourceSurface.
218     gfx::DataSourceSurface::ScopedMap srcMap(mSurfaceForBasic,
219                                              gfx::DataSourceSurface::READ);
220 
221     RefPtr<gfx::DataSourceSurface> result =
222         gfx::Factory::CreateDataSourceSurfaceWithStride(
223             mSurfaceForBasic->GetSize(), mSurfaceForBasic->GetFormat(),
224             srcMap.GetStride());
225     if (NS_WARN_IF(!result)) {
226       return nullptr;
227     }
228 
229     gfx::DataSourceSurface::ScopedMap dstMap(result,
230                                              gfx::DataSourceSurface::WRITE);
231 
232     if (NS_WARN_IF(!srcMap.IsMapped()) || NS_WARN_IF(!dstMap.IsMapped())) {
233       return nullptr;
234     }
235 
236     memcpy(dstMap.GetData(), srcMap.GetData(),
237            srcMap.GetStride() * mSurfaceForBasic->GetSize().height);
238     return result.forget();
239   } else {
240     return UpdateTarget();
241   }
242 }
243 
GetInputStream(const char * aMimeType,const char16_t * aEncoderOptions,nsIInputStream ** aStream)244 nsresult AsyncCanvasRenderer::GetInputStream(const char* aMimeType,
245                                              const char16_t* aEncoderOptions,
246                                              nsIInputStream** aStream) {
247   MOZ_ASSERT(NS_IsMainThread());
248   RefPtr<gfx::DataSourceSurface> surface = GetSurface();
249   if (!surface) {
250     return NS_ERROR_FAILURE;
251   }
252 
253   gfx::DataSourceSurface::ScopedMap map(surface, gfx::DataSourceSurface::READ);
254 
255   // Handle y flip.
256   RefPtr<gfx::DataSourceSurface> dataSurf =
257       gl::YInvertImageSurface(surface, map.GetStride());
258 
259   return gfxUtils::GetInputStream(dataSurf, false, aMimeType, aEncoderOptions,
260                                   aStream);
261 }
262 
263 }  // namespace layers
264 }  // namespace mozilla
265