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