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/. */
7 #include "AsyncCanvasRenderer.h"
10 #include "GLContext.h"
11 #include "GLReadTexImageHelper.h"
12 #include "GLScreenBuffer.h"
14 #include "mozilla/dom/HTMLCanvasElement.h"
15 #include "nsICanvasRenderingContextInternal.h"
16 #include "mozilla/layers/BufferTexture.h"
17 #include "mozilla/layers/CanvasClient.h"
18 #include "mozilla/layers/CompositableForwarder.h"
19 #include "mozilla/layers/TextureClient.h"
20 #include "mozilla/layers/TextureClientSharedSurface.h"
21 #include "mozilla/layers/ShadowLayers.h"
22 #include "mozilla/ReentrantMonitor.h"
23 #include "nsIRunnable.h"
24 #include "nsThreadUtils.h"
29 AsyncCanvasRenderer::AsyncCanvasRenderer()
30 : mHTMLCanvasElement(nullptr),
33 mIsAlphaPremultiplied(true),
36 mCanvasClient(nullptr),
37 mMutex("AsyncCanvasRenderer::mMutex"),
38 mContextType(dom::CanvasContextType::NoContext
) {
39 MOZ_COUNT_CTOR(AsyncCanvasRenderer
);
42 AsyncCanvasRenderer::~AsyncCanvasRenderer() {
43 MOZ_COUNT_DTOR(AsyncCanvasRenderer
);
46 void AsyncCanvasRenderer::NotifyElementAboutAttributesChanged() {
47 class Runnable final
: public mozilla::Runnable
{
49 explicit Runnable(AsyncCanvasRenderer
* aRenderer
)
50 : mozilla::Runnable("Runnable"), mRenderer(aRenderer
) {}
52 NS_IMETHOD
Run() override
{
54 dom::HTMLCanvasElement::SetAttrFromAsyncCanvasRenderer(mRenderer
);
61 RefPtr
<AsyncCanvasRenderer
> mRenderer
;
64 nsCOMPtr
<nsIRunnable
> runnable
= new Runnable(this);
65 nsresult rv
= NS_DispatchToMainThread(runnable
);
67 NS_WARNING("Failed to dispatch a runnable to the main-thread.");
71 void AsyncCanvasRenderer::NotifyElementAboutInvalidation() {
72 class Runnable final
: public mozilla::Runnable
{
74 explicit Runnable(AsyncCanvasRenderer
* aRenderer
)
75 : mozilla::Runnable("Runnable"), mRenderer(aRenderer
) {}
77 NS_IMETHOD
Run() override
{
79 dom::HTMLCanvasElement::InvalidateFromAsyncCanvasRenderer(mRenderer
);
86 RefPtr
<AsyncCanvasRenderer
> mRenderer
;
89 nsCOMPtr
<nsIRunnable
> runnable
= new Runnable(this);
90 nsresult rv
= NS_DispatchToMainThread(runnable
);
92 NS_WARNING("Failed to dispatch a runnable to the main-thread.");
96 void AsyncCanvasRenderer::SetCanvasClient(CanvasClient
* aClient
) {
97 mCanvasClient
= aClient
;
99 mCanvasClientAsyncHandle
= aClient
->GetAsyncHandle();
101 MOZ_ASSERT(mCanvasClient
->GetForwarder() &&
102 mCanvasClient
->GetForwarder()->AsLayerForwarder() &&
103 mCanvasClient
->GetForwarder()
105 ->GetShadowManager());
106 LayerTransactionChild
* ltc
=
107 mCanvasClient
->GetForwarder()->AsLayerForwarder()->GetShadowManager();
108 DebugOnly
<bool> success
=
109 mContext
->UpdateCompositableHandle(ltc
, mCanvasClientAsyncHandle
);
113 mCanvasClientAsyncHandle
= CompositableHandle();
117 void AsyncCanvasRenderer::SetActiveEventTarget() {
118 MutexAutoLock
lock(mMutex
);
119 mActiveEventTarget
= GetCurrentThreadSerialEventTarget();
122 void AsyncCanvasRenderer::ResetActiveEventTarget() {
123 MutexAutoLock
lock(mMutex
);
124 mActiveEventTarget
= nullptr;
127 already_AddRefed
<nsISerialEventTarget
>
128 AsyncCanvasRenderer::GetActiveEventTarget() {
129 MutexAutoLock
lock(mMutex
);
130 nsCOMPtr
<nsISerialEventTarget
> result
= mActiveEventTarget
;
131 return result
.forget();
134 ImageContainer
* AsyncCanvasRenderer::GetImageContainer() {
135 MOZ_ASSERT(mContextType
== dom::CanvasContextType::ImageBitmap
);
136 MutexAutoLock
lock(mMutex
);
137 if (!mImageContainer
) {
139 LayerManager::CreateImageContainer(ImageContainer::ASYNCHRONOUS
);
141 return mImageContainer
;
144 dom::CanvasContextType
AsyncCanvasRenderer::GetContextType() {
145 MutexAutoLock
lock(mMutex
);
149 void AsyncCanvasRenderer::SetContextType(dom::CanvasContextType aContextType
) {
150 MutexAutoLock
lock(mMutex
);
151 mContextType
= aContextType
;
154 void AsyncCanvasRenderer::CopyFromTextureClient(TextureClient
* aTextureClient
) {
155 MutexAutoLock
lock(mMutex
);
157 if (!aTextureClient
) {
158 mSurfaceForBasic
= nullptr;
162 TextureClientAutoLock
texLock(aTextureClient
, layers::OpenMode::OPEN_READ
);
163 if (!texLock
.Succeeded()) {
167 const gfx::IntSize
& size
= aTextureClient
->GetSize();
168 // This buffer would be used later for content rendering. So we choose
169 // B8G8R8A8 format here.
170 const gfx::SurfaceFormat format
= gfx::SurfaceFormat::B8G8R8A8
;
171 // Avoid to create buffer every time.
172 if (!mSurfaceForBasic
|| size
!= mSurfaceForBasic
->GetSize() ||
173 format
!= mSurfaceForBasic
->GetFormat()) {
175 gfx::GetAlignedStride
<8>(size
.width
, BytesPerPixel(format
));
177 gfx::Factory::CreateDataSourceSurfaceWithStride(size
, format
, stride
);
178 if (!mSurfaceForBasic
) {
183 MappedTextureData mapped
;
184 if (!aTextureClient
->BorrowMappedData(mapped
)) {
188 const uint8_t* lockedBytes
= mapped
.data
;
189 gfx::DataSourceSurface::ScopedMap
map(mSurfaceForBasic
,
190 gfx::DataSourceSurface::MapType::WRITE
);
191 if (!map
.IsMapped()) {
195 MOZ_ASSERT(map
.GetStride() == mapped
.stride
);
196 memcpy(map
.GetData(), lockedBytes
,
197 map
.GetStride() * mSurfaceForBasic
->GetSize().height
);
199 if (mSurfaceForBasic
->GetFormat() == gfx::SurfaceFormat::R8G8B8A8
||
200 mSurfaceForBasic
->GetFormat() == gfx::SurfaceFormat::R8G8B8X8
) {
201 gl::SwapRAndBComponents(mSurfaceForBasic
);
205 already_AddRefed
<gfx::DataSourceSurface
> AsyncCanvasRenderer::UpdateTarget() {
210 gl::SharedSurface
* frontbuffer
= nullptr;
211 gl::GLScreenBuffer
* screen
= mGLContext
->Screen();
212 const auto& front
= screen
->Front();
214 frontbuffer
= front
->Surf();
221 if (frontbuffer
->mType
== gl::SharedSurfaceType::Basic
) {
225 const gfx::IntSize
& size
= frontbuffer
->mSize
;
226 // This buffer would be used later for content rendering. So we choose
227 // B8G8R8A8 format here.
228 const gfx::SurfaceFormat format
= gfx::SurfaceFormat::B8G8R8A8
;
229 uint32_t stride
= gfx::GetAlignedStride
<8>(size
.width
, BytesPerPixel(format
));
230 RefPtr
<gfx::DataSourceSurface
> surface
=
231 gfx::Factory::CreateDataSourceSurfaceWithStride(size
, format
, stride
);
233 if (NS_WARN_IF(!surface
)) {
237 if (!frontbuffer
->ReadbackBySharedHandle(surface
)) {
241 bool needsPremult
= frontbuffer
->mHasAlpha
&& !mIsAlphaPremultiplied
;
243 gfxUtils::PremultiplyDataSurface(surface
, surface
);
246 return surface
.forget();
249 already_AddRefed
<gfx::DataSourceSurface
> AsyncCanvasRenderer::GetSurface() {
250 MOZ_ASSERT(NS_IsMainThread());
251 MutexAutoLock
lock(mMutex
);
252 if (mSurfaceForBasic
) {
253 // Since SourceSurface isn't thread-safe, we need copy to a new
255 gfx::DataSourceSurface::ScopedMap
srcMap(mSurfaceForBasic
,
256 gfx::DataSourceSurface::READ
);
258 RefPtr
<gfx::DataSourceSurface
> result
=
259 gfx::Factory::CreateDataSourceSurfaceWithStride(
260 mSurfaceForBasic
->GetSize(), mSurfaceForBasic
->GetFormat(),
262 if (NS_WARN_IF(!result
)) {
266 gfx::DataSourceSurface::ScopedMap
dstMap(result
,
267 gfx::DataSourceSurface::WRITE
);
269 if (NS_WARN_IF(!srcMap
.IsMapped()) || NS_WARN_IF(!dstMap
.IsMapped())) {
273 memcpy(dstMap
.GetData(), srcMap
.GetData(),
274 srcMap
.GetStride() * mSurfaceForBasic
->GetSize().height
);
275 return result
.forget();
277 return UpdateTarget();
281 nsresult
AsyncCanvasRenderer::GetInputStream(const char* aMimeType
,
282 const nsAString
& aEncoderOptions
,
283 nsIInputStream
** aStream
) {
284 MOZ_ASSERT(NS_IsMainThread());
285 RefPtr
<gfx::DataSourceSurface
> surface
= GetSurface();
287 return NS_ERROR_FAILURE
;
290 gfx::DataSourceSurface::ScopedMap
map(surface
, gfx::DataSourceSurface::READ
);
293 RefPtr
<gfx::DataSourceSurface
> dataSurf
=
294 gl::YInvertImageSurface(surface
, map
.GetStride());
296 return gfxUtils::GetInputStream(dataSurf
, false, aMimeType
, aEncoderOptions
,
300 } // namespace layers
301 } // namespace mozilla