Source code

Revision control

Other Tools

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 "nsICanvasRenderingContextInternal.h"
15
#include "mozilla/layers/BufferTexture.h"
16
#include "mozilla/layers/CanvasClient.h"
17
#include "mozilla/layers/CompositableForwarder.h"
18
#include "mozilla/layers/TextureClient.h"
19
#include "mozilla/layers/TextureClientSharedSurface.h"
20
#include "mozilla/layers/ShadowLayers.h"
21
#include "mozilla/ReentrantMonitor.h"
22
#include "nsIRunnable.h"
23
#include "nsThreadUtils.h"
24
25
namespace mozilla {
26
namespace layers {
27
28
AsyncCanvasRenderer::AsyncCanvasRenderer()
29
: mHTMLCanvasElement(nullptr),
30
mContext(nullptr),
31
mGLContext(nullptr),
32
mIsAlphaPremultiplied(true),
33
mWidth(0),
34
mHeight(0),
35
mCanvasClient(nullptr),
36
mMutex("AsyncCanvasRenderer::mMutex"),
37
mContextType(dom::CanvasContextType::NoContext) {
38
MOZ_COUNT_CTOR(AsyncCanvasRenderer);
39
}
40
41
AsyncCanvasRenderer::~AsyncCanvasRenderer() {
42
MOZ_COUNT_DTOR(AsyncCanvasRenderer);
43
}
44
45
void AsyncCanvasRenderer::NotifyElementAboutAttributesChanged() {
46
class Runnable final : public mozilla::Runnable {
47
public:
48
explicit Runnable(AsyncCanvasRenderer* aRenderer)
49
: mozilla::Runnable("Runnable"), mRenderer(aRenderer) {}
50
51
NS_IMETHOD Run() override {
52
if (mRenderer) {
53
dom::HTMLCanvasElement::SetAttrFromAsyncCanvasRenderer(mRenderer);
54
}
55
56
return NS_OK;
57
}
58
59
private:
60
RefPtr<AsyncCanvasRenderer> mRenderer;
61
};
62
63
nsCOMPtr<nsIRunnable> runnable = new Runnable(this);
64
nsresult rv = NS_DispatchToMainThread(runnable);
65
if (NS_FAILED(rv)) {
66
NS_WARNING("Failed to dispatch a runnable to the main-thread.");
67
}
68
}
69
70
void AsyncCanvasRenderer::NotifyElementAboutInvalidation() {
71
class Runnable final : public mozilla::Runnable {
72
public:
73
explicit Runnable(AsyncCanvasRenderer* aRenderer)
74
: mozilla::Runnable("Runnable"), mRenderer(aRenderer) {}
75
76
NS_IMETHOD Run() override {
77
if (mRenderer) {
78
dom::HTMLCanvasElement::InvalidateFromAsyncCanvasRenderer(mRenderer);
79
}
80
81
return NS_OK;
82
}
83
84
private:
85
RefPtr<AsyncCanvasRenderer> mRenderer;
86
};
87
88
nsCOMPtr<nsIRunnable> runnable = new Runnable(this);
89
nsresult rv = NS_DispatchToMainThread(runnable);
90
if (NS_FAILED(rv)) {
91
NS_WARNING("Failed to dispatch a runnable to the main-thread.");
92
}
93
}
94
95
void AsyncCanvasRenderer::SetCanvasClient(CanvasClient* aClient) {
96
mCanvasClient = aClient;
97
if (aClient) {
98
mCanvasClientAsyncHandle = aClient->GetAsyncHandle();
99
if (mContext) {
100
MOZ_ASSERT(mCanvasClient->GetForwarder() &&
101
mCanvasClient->GetForwarder()->AsLayerForwarder() &&
102
mCanvasClient->GetForwarder()
103
->AsLayerForwarder()
104
->GetShadowManager());
105
LayerTransactionChild* ltc =
106
mCanvasClient->GetForwarder()->AsLayerForwarder()->GetShadowManager();
107
DebugOnly<bool> success =
108
mContext->UpdateCompositableHandle(ltc, mCanvasClientAsyncHandle);
109
MOZ_ASSERT(success);
110
}
111
} else {
112
mCanvasClientAsyncHandle = CompositableHandle();
113
}
114
}
115
116
void AsyncCanvasRenderer::SetActiveEventTarget() {
117
MutexAutoLock lock(mMutex);
118
mActiveEventTarget = GetCurrentThreadSerialEventTarget();
119
}
120
121
void AsyncCanvasRenderer::ResetActiveEventTarget() {
122
MutexAutoLock lock(mMutex);
123
mActiveEventTarget = nullptr;
124
}
125
126
already_AddRefed<nsISerialEventTarget>
127
AsyncCanvasRenderer::GetActiveEventTarget() {
128
MutexAutoLock lock(mMutex);
129
nsCOMPtr<nsISerialEventTarget> result = mActiveEventTarget;
130
return result.forget();
131
}
132
133
ImageContainer* AsyncCanvasRenderer::GetImageContainer() {
134
MOZ_ASSERT(mContextType == dom::CanvasContextType::ImageBitmap);
135
MutexAutoLock lock(mMutex);
136
if (!mImageContainer) {
137
mImageContainer =
138
LayerManager::CreateImageContainer(ImageContainer::ASYNCHRONOUS);
139
}
140
return mImageContainer;
141
}
142
143
dom::CanvasContextType AsyncCanvasRenderer::GetContextType() {
144
MutexAutoLock lock(mMutex);
145
return mContextType;
146
}
147
148
void AsyncCanvasRenderer::SetContextType(dom::CanvasContextType aContextType) {
149
MutexAutoLock lock(mMutex);
150
mContextType = aContextType;
151
}
152
153
void AsyncCanvasRenderer::CopyFromTextureClient(TextureClient* aTextureClient) {
154
MutexAutoLock lock(mMutex);
155
156
if (!aTextureClient) {
157
mSurfaceForBasic = nullptr;
158
return;
159
}
160
161
TextureClientAutoLock texLock(aTextureClient, layers::OpenMode::OPEN_READ);
162
if (!texLock.Succeeded()) {
163
return;
164
}
165
166
const gfx::IntSize& size = aTextureClient->GetSize();
167
// This buffer would be used later for content rendering. So we choose
168
// B8G8R8A8 format here.
169
const gfx::SurfaceFormat format = gfx::SurfaceFormat::B8G8R8A8;
170
// Avoid to create buffer every time.
171
if (!mSurfaceForBasic || size != mSurfaceForBasic->GetSize() ||
172
format != mSurfaceForBasic->GetFormat()) {
173
uint32_t stride =
174
gfx::GetAlignedStride<8>(size.width, BytesPerPixel(format));
175
mSurfaceForBasic =
176
gfx::Factory::CreateDataSourceSurfaceWithStride(size, format, stride);
177
if (!mSurfaceForBasic) {
178
return;
179
}
180
}
181
182
MappedTextureData mapped;
183
if (!aTextureClient->BorrowMappedData(mapped)) {
184
return;
185
}
186
187
const uint8_t* lockedBytes = mapped.data;
188
gfx::DataSourceSurface::ScopedMap map(mSurfaceForBasic,
189
gfx::DataSourceSurface::MapType::WRITE);
190
if (!map.IsMapped()) {
191
return;
192
}
193
194
MOZ_ASSERT(map.GetStride() == mapped.stride);
195
memcpy(map.GetData(), lockedBytes,
196
map.GetStride() * mSurfaceForBasic->GetSize().height);
197
198
if (mSurfaceForBasic->GetFormat() == gfx::SurfaceFormat::R8G8B8A8 ||
199
mSurfaceForBasic->GetFormat() == gfx::SurfaceFormat::R8G8B8X8) {
200
gl::SwapRAndBComponents(mSurfaceForBasic);
201
}
202
}
203
204
already_AddRefed<gfx::DataSourceSurface> AsyncCanvasRenderer::UpdateTarget() {
205
if (!mGLContext) {
206
return nullptr;
207
}
208
209
gl::SharedSurface* frontbuffer = nullptr;
210
gl::GLScreenBuffer* screen = mGLContext->Screen();
211
const auto& front = screen->Front();
212
if (front) {
213
frontbuffer = front->Surf();
214
}
215
216
if (!frontbuffer) {
217
return nullptr;
218
}
219
220
if (frontbuffer->mType == gl::SharedSurfaceType::Basic) {
221
return nullptr;
222
}
223
224
const gfx::IntSize& size = frontbuffer->mSize;
225
// This buffer would be used later for content rendering. So we choose
226
// B8G8R8A8 format here.
227
const gfx::SurfaceFormat format = gfx::SurfaceFormat::B8G8R8A8;
228
uint32_t stride = gfx::GetAlignedStride<8>(size.width, BytesPerPixel(format));
229
RefPtr<gfx::DataSourceSurface> surface =
230
gfx::Factory::CreateDataSourceSurfaceWithStride(size, format, stride);
231
232
if (NS_WARN_IF(!surface)) {
233
return nullptr;
234
}
235
236
if (!frontbuffer->ReadbackBySharedHandle(surface)) {
237
return nullptr;
238
}
239
240
bool needsPremult = frontbuffer->mHasAlpha && !mIsAlphaPremultiplied;
241
if (needsPremult) {
242
gfxUtils::PremultiplyDataSurface(surface, surface);
243
}
244
245
return surface.forget();
246
}
247
248
already_AddRefed<gfx::DataSourceSurface> AsyncCanvasRenderer::GetSurface() {
249
MOZ_ASSERT(NS_IsMainThread());
250
MutexAutoLock lock(mMutex);
251
if (mSurfaceForBasic) {
252
// Since SourceSurface isn't thread-safe, we need copy to a new
253
// SourceSurface.
254
gfx::DataSourceSurface::ScopedMap srcMap(mSurfaceForBasic,
255
gfx::DataSourceSurface::READ);
256
257
RefPtr<gfx::DataSourceSurface> result =
258
gfx::Factory::CreateDataSourceSurfaceWithStride(
259
mSurfaceForBasic->GetSize(), mSurfaceForBasic->GetFormat(),
260
srcMap.GetStride());
261
if (NS_WARN_IF(!result)) {
262
return nullptr;
263
}
264
265
gfx::DataSourceSurface::ScopedMap dstMap(result,
266
gfx::DataSourceSurface::WRITE);
267
268
if (NS_WARN_IF(!srcMap.IsMapped()) || NS_WARN_IF(!dstMap.IsMapped())) {
269
return nullptr;
270
}
271
272
memcpy(dstMap.GetData(), srcMap.GetData(),
273
srcMap.GetStride() * mSurfaceForBasic->GetSize().height);
274
return result.forget();
275
} else {
276
return UpdateTarget();
277
}
278
}
279
280
nsresult AsyncCanvasRenderer::GetInputStream(const char* aMimeType,
281
const nsAString& aEncoderOptions,
282
nsIInputStream** aStream) {
283
MOZ_ASSERT(NS_IsMainThread());
284
RefPtr<gfx::DataSourceSurface> surface = GetSurface();
285
if (!surface) {
286
return NS_ERROR_FAILURE;
287
}
288
289
gfx::DataSourceSurface::ScopedMap map(surface, gfx::DataSourceSurface::READ);
290
291
// Handle y flip.
292
RefPtr<gfx::DataSourceSurface> dataSurf =
293
gl::YInvertImageSurface(surface, map.GetStride());
294
295
return gfxUtils::GetInputStream(dataSurf, false, aMimeType, aEncoderOptions,
296
aStream);
297
}
298
299
} // namespace layers
300
} // namespace mozilla