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