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