summaryrefslogtreecommitdiffstats
path: root/gfx/layers/AsyncCanvasRenderer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/layers/AsyncCanvasRenderer.cpp')
-rw-r--r--gfx/layers/AsyncCanvasRenderer.cpp290
1 files changed, 290 insertions, 0 deletions
diff --git a/gfx/layers/AsyncCanvasRenderer.cpp b/gfx/layers/AsyncCanvasRenderer.cpp
new file mode 100644
index 000000000..d9a0e9886
--- /dev/null
+++ b/gfx/layers/AsyncCanvasRenderer.cpp
@@ -0,0 +1,290 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "AsyncCanvasRenderer.h"
+
+#include "gfxUtils.h"
+#include "GLContext.h"
+#include "GLReadTexImageHelper.h"
+#include "GLScreenBuffer.h"
+#include "mozilla/dom/HTMLCanvasElement.h"
+#include "mozilla/layers/BufferTexture.h"
+#include "mozilla/layers/CanvasClient.h"
+#include "mozilla/layers/TextureClient.h"
+#include "mozilla/layers/TextureClientSharedSurface.h"
+#include "mozilla/ReentrantMonitor.h"
+#include "nsIRunnable.h"
+#include "nsThreadUtils.h"
+
+namespace mozilla {
+namespace layers {
+
+AsyncCanvasRenderer::AsyncCanvasRenderer()
+ : mHTMLCanvasElement(nullptr)
+ , mContext(nullptr)
+ , mGLContext(nullptr)
+ , mIsAlphaPremultiplied(true)
+ , mWidth(0)
+ , mHeight(0)
+ , mCanvasClientAsyncID(0)
+ , mCanvasClient(nullptr)
+ , mMutex("AsyncCanvasRenderer::mMutex")
+{
+ MOZ_COUNT_CTOR(AsyncCanvasRenderer);
+}
+
+AsyncCanvasRenderer::~AsyncCanvasRenderer()
+{
+ MOZ_COUNT_DTOR(AsyncCanvasRenderer);
+}
+
+void
+AsyncCanvasRenderer::NotifyElementAboutAttributesChanged()
+{
+ class Runnable final : public mozilla::Runnable
+ {
+ public:
+ explicit Runnable(AsyncCanvasRenderer* aRenderer)
+ : mRenderer(aRenderer)
+ {}
+
+ NS_IMETHOD Run() override
+ {
+ if (mRenderer) {
+ dom::HTMLCanvasElement::SetAttrFromAsyncCanvasRenderer(mRenderer);
+ }
+
+ return NS_OK;
+ }
+
+ void Revoke()
+ {
+ mRenderer = nullptr;
+ }
+
+ private:
+ RefPtr<AsyncCanvasRenderer> mRenderer;
+ };
+
+ nsCOMPtr<nsIRunnable> runnable = new Runnable(this);
+ nsresult rv = NS_DispatchToMainThread(runnable);
+ if (NS_FAILED(rv)) {
+ NS_WARNING("Failed to dispatch a runnable to the main-thread.");
+ }
+}
+
+void
+AsyncCanvasRenderer::NotifyElementAboutInvalidation()
+{
+ class Runnable final : public mozilla::Runnable
+ {
+ public:
+ explicit Runnable(AsyncCanvasRenderer* aRenderer)
+ : mRenderer(aRenderer)
+ {}
+
+ NS_IMETHOD Run() override
+ {
+ if (mRenderer) {
+ dom::HTMLCanvasElement::InvalidateFromAsyncCanvasRenderer(mRenderer);
+ }
+
+ return NS_OK;
+ }
+
+ void Revoke()
+ {
+ mRenderer = nullptr;
+ }
+
+ private:
+ RefPtr<AsyncCanvasRenderer> mRenderer;
+ };
+
+ nsCOMPtr<nsIRunnable> runnable = new Runnable(this);
+ nsresult rv = NS_DispatchToMainThread(runnable);
+ if (NS_FAILED(rv)) {
+ NS_WARNING("Failed to dispatch a runnable to the main-thread.");
+ }
+}
+
+void
+AsyncCanvasRenderer::SetCanvasClient(CanvasClient* aClient)
+{
+ mCanvasClient = aClient;
+ if (aClient) {
+ mCanvasClientAsyncID = aClient->GetAsyncID();
+ } else {
+ mCanvasClientAsyncID = 0;
+ }
+}
+
+void
+AsyncCanvasRenderer::SetActiveThread()
+{
+ MutexAutoLock lock(mMutex);
+ mActiveThread = NS_GetCurrentThread();
+}
+
+void
+AsyncCanvasRenderer::ResetActiveThread()
+{
+ MutexAutoLock lock(mMutex);
+ mActiveThread = nullptr;
+}
+
+already_AddRefed<nsIThread>
+AsyncCanvasRenderer::GetActiveThread()
+{
+ MutexAutoLock lock(mMutex);
+ nsCOMPtr<nsIThread> result = mActiveThread;
+ return result.forget();
+}
+
+void
+AsyncCanvasRenderer::CopyFromTextureClient(TextureClient* aTextureClient)
+{
+ MutexAutoLock lock(mMutex);
+
+ if (!aTextureClient) {
+ mSurfaceForBasic = nullptr;
+ return;
+ }
+
+ TextureClientAutoLock texLock(aTextureClient, layers::OpenMode::OPEN_READ);
+ if (!texLock.Succeeded()) {
+ return;
+ }
+
+ const gfx::IntSize& size = aTextureClient->GetSize();
+ // This buffer would be used later for content rendering. So we choose
+ // B8G8R8A8 format here.
+ const gfx::SurfaceFormat format = gfx::SurfaceFormat::B8G8R8A8;
+ // Avoid to create buffer every time.
+ if (!mSurfaceForBasic ||
+ size != mSurfaceForBasic->GetSize() ||
+ format != mSurfaceForBasic->GetFormat())
+ {
+ uint32_t stride = gfx::GetAlignedStride<8>(size.width, BytesPerPixel(format));
+ mSurfaceForBasic = gfx::Factory::CreateDataSourceSurfaceWithStride(size, format, stride);
+ }
+
+ MappedTextureData mapped;
+ if (!aTextureClient->BorrowMappedData(mapped)) {
+ return;
+ }
+
+ const uint8_t* lockedBytes = mapped.data;
+ gfx::DataSourceSurface::ScopedMap map(mSurfaceForBasic,
+ gfx::DataSourceSurface::MapType::WRITE);
+ if (!map.IsMapped()) {
+ return;
+ }
+
+ MOZ_ASSERT(map.GetStride() == mapped.stride);
+ memcpy(map.GetData(), lockedBytes, map.GetStride() * mSurfaceForBasic->GetSize().height);
+
+ if (mSurfaceForBasic->GetFormat() == gfx::SurfaceFormat::R8G8B8A8 ||
+ mSurfaceForBasic->GetFormat() == gfx::SurfaceFormat::R8G8B8X8) {
+ gl::SwapRAndBComponents(mSurfaceForBasic);
+ }
+}
+
+already_AddRefed<gfx::DataSourceSurface>
+AsyncCanvasRenderer::UpdateTarget()
+{
+ if (!mGLContext) {
+ return nullptr;
+ }
+
+ gl::SharedSurface* frontbuffer = nullptr;
+ gl::GLScreenBuffer* screen = mGLContext->Screen();
+ const auto& front = screen->Front();
+ if (front) {
+ frontbuffer = front->Surf();
+ }
+
+ if (!frontbuffer) {
+ return nullptr;
+ }
+
+ if (frontbuffer->mType == gl::SharedSurfaceType::Basic) {
+ return nullptr;
+ }
+
+ const gfx::IntSize& size = frontbuffer->mSize;
+ // This buffer would be used later for content rendering. So we choose
+ // B8G8R8A8 format here.
+ const gfx::SurfaceFormat format = gfx::SurfaceFormat::B8G8R8A8;
+ uint32_t stride = gfx::GetAlignedStride<8>(size.width, BytesPerPixel(format));
+ RefPtr<gfx::DataSourceSurface> surface =
+ gfx::Factory::CreateDataSourceSurfaceWithStride(size, format, stride);
+
+
+ if (NS_WARN_IF(!surface)) {
+ return nullptr;
+ }
+
+ if (!frontbuffer->ReadbackBySharedHandle(surface)) {
+ return nullptr;
+ }
+
+ bool needsPremult = frontbuffer->mHasAlpha && !mIsAlphaPremultiplied;
+ if (needsPremult) {
+ gfxUtils::PremultiplyDataSurface(surface, surface);
+ }
+
+ return surface.forget();
+}
+
+already_AddRefed<gfx::DataSourceSurface>
+AsyncCanvasRenderer::GetSurface()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ MutexAutoLock lock(mMutex);
+ if (mSurfaceForBasic) {
+ // Since SourceSurface isn't thread-safe, we need copy to a new SourceSurface.
+ RefPtr<gfx::DataSourceSurface> result =
+ gfx::Factory::CreateDataSourceSurfaceWithStride(mSurfaceForBasic->GetSize(),
+ mSurfaceForBasic->GetFormat(),
+ mSurfaceForBasic->Stride());
+
+ gfx::DataSourceSurface::ScopedMap srcMap(mSurfaceForBasic, gfx::DataSourceSurface::READ);
+ gfx::DataSourceSurface::ScopedMap dstMap(result, gfx::DataSourceSurface::WRITE);
+
+ if (NS_WARN_IF(!srcMap.IsMapped()) ||
+ NS_WARN_IF(!dstMap.IsMapped())) {
+ return nullptr;
+ }
+
+ memcpy(dstMap.GetData(),
+ srcMap.GetData(),
+ srcMap.GetStride() * mSurfaceForBasic->GetSize().height);
+ return result.forget();
+ } else {
+ return UpdateTarget();
+ }
+}
+
+nsresult
+AsyncCanvasRenderer::GetInputStream(const char *aMimeType,
+ const char16_t *aEncoderOptions,
+ nsIInputStream **aStream)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ RefPtr<gfx::DataSourceSurface> surface = GetSurface();
+ if (!surface) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // Handle y flip.
+ RefPtr<gfx::DataSourceSurface> dataSurf = gl::YInvertImageSurface(surface);
+
+ return gfxUtils::GetInputStream(dataSurf, false, aMimeType, aEncoderOptions, aStream);
+}
+
+} // namespace layers
+} // namespace mozilla