Commit 13fe9c87 authored by Yi Xu's avatar Yi Xu Committed by Commit Bot

Update ImageRendering quality on OffscreenCanvas

Enable web developers to be able to update ImageRendering quality on
OffscreenCanvas. When ImageRendering quality changed between auto and
pixelated, the OffscreenCanvasPlaceHolder sends the update to
CanvasResourceDispatcher to update the CanvasResource of OffscreenCanvas.

Demonstration of classes interaction:
https://docs.google.com/document/d/1M-E7euqa7fLLcUlgPkPAtVMck7s7u0hmsA-uLL1H7J0/edit

tests added:
OffscreenCanvasImageRenderingAuto.html
OffscreenCanvasImageRenderingPixelated.html

Bug: 1001335

Change-Id: I9846d8ba851d55270a2a3e0380bd76a6b924a742
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1779188
Commit-Queue: Yi Xu <yiyix@chromium.org>
Reviewed-by: default avatarFernando Serboncini <fserb@chromium.org>
Cr-Commit-Position: refs/heads/master@{#713562}
parent c48045bd
...@@ -1287,13 +1287,10 @@ ScriptPromise HTMLCanvasElement::CreateImageBitmap( ...@@ -1287,13 +1287,10 @@ ScriptPromise HTMLCanvasElement::CreateImageBitmap(
script_state, ImageBitmap::Create(this, crop_rect, options)); script_state, ImageBitmap::Create(this, crop_rect, options));
} }
void HTMLCanvasElement::SetOffscreenCanvasFrame( void HTMLCanvasElement::SetOffscreenCanvasResource(
scoped_refptr<CanvasResource> image, scoped_refptr<CanvasResource> image,
base::WeakPtr<CanvasResourceDispatcher> dispatcher,
scoped_refptr<base::SingleThreadTaskRunner> task_runner,
unsigned resource_id) { unsigned resource_id) {
OffscreenCanvasPlaceholder::SetOffscreenCanvasFrame( OffscreenCanvasPlaceholder::SetOffscreenCanvasResource(std::move(image),
std::move(image), std::move(dispatcher), std::move(task_runner),
resource_id); resource_id);
SetSize(OffscreenCanvasFrame()->Size()); SetSize(OffscreenCanvasFrame()->Size());
NotifyListenersCanvasChanged(); NotifyListenersCanvasChanged();
......
...@@ -237,9 +237,7 @@ class CORE_EXPORT HTMLCanvasElement final ...@@ -237,9 +237,7 @@ class CORE_EXPORT HTMLCanvasElement final
const ImageBitmapOptions*) override; const ImageBitmapOptions*) override;
// OffscreenCanvasPlaceholder implementation. // OffscreenCanvasPlaceholder implementation.
void SetOffscreenCanvasFrame(scoped_refptr<CanvasResource>, void SetOffscreenCanvasResource(scoped_refptr<CanvasResource>,
base::WeakPtr<CanvasResourceDispatcher>,
scoped_refptr<base::SingleThreadTaskRunner>,
unsigned resource_id) override; unsigned resource_id) override;
void Trace(Visitor*) override; void Trace(Visitor*) override;
......
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
#include "third_party/blink/renderer/platform/instrumentation/histogram.h" #include "third_party/blink/renderer/platform/instrumentation/histogram.h"
#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h" #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
#include "third_party/blink/renderer/platform/wtf/math_extras.h" #include "third_party/blink/renderer/platform/wtf/math_extras.h"
#include "third_party/skia/include/core/SkFilterQuality.h"
#include "third_party/skia/include/core/SkSurface.h" #include "third_party/skia/include/core/SkSurface.h"
namespace blink { namespace blink {
...@@ -105,6 +106,9 @@ void OffscreenCanvas::SetPlaceholderCanvasId(DOMNodeId canvas_id) { ...@@ -105,6 +106,9 @@ void OffscreenCanvas::SetPlaceholderCanvasId(DOMNodeId canvas_id) {
if (animation_frame_provider) if (animation_frame_provider)
animation_frame_provider->RegisterOffscreenCanvas(this); animation_frame_provider->RegisterOffscreenCanvas(this);
} }
if (frame_dispatcher_) {
frame_dispatcher_->SetPlaceholderCanvasDispatcher(placeholder_canvas_id_);
}
} }
void OffscreenCanvas::setWidth(unsigned width) { void OffscreenCanvas::setWidth(unsigned width) {
...@@ -313,6 +317,9 @@ CanvasResourceDispatcher* OffscreenCanvas::GetOrCreateResourceDispatcher() { ...@@ -313,6 +317,9 @@ CanvasResourceDispatcher* OffscreenCanvas::GetOrCreateResourceDispatcher() {
// throughout the lifetime of this OffscreenCanvas. // throughout the lifetime of this OffscreenCanvas.
frame_dispatcher_ = std::make_unique<CanvasResourceDispatcher>( frame_dispatcher_ = std::make_unique<CanvasResourceDispatcher>(
this, client_id_, sink_id_, placeholder_canvas_id_, size_); this, client_id_, sink_id_, placeholder_canvas_id_, size_);
if (HasPlaceholderCanvas())
frame_dispatcher_->SetPlaceholderCanvasDispatcher(placeholder_canvas_id_);
} }
return frame_dispatcher_.get(); return frame_dispatcher_.get();
} }
...@@ -411,6 +418,16 @@ bool OffscreenCanvas::BeginFrame() { ...@@ -411,6 +418,16 @@ bool OffscreenCanvas::BeginFrame() {
return PushFrameIfNeeded(); return PushFrameIfNeeded();
} }
void OffscreenCanvas::SetFilterQualityInResource(
SkFilterQuality filter_quality) {
if (filter_quality_ == filter_quality)
return;
filter_quality_ = filter_quality;
if (ResourceProvider())
GetOrCreateResourceProvider()->SetFilterQuality(filter_quality);
}
bool OffscreenCanvas::PushFrameIfNeeded() { bool OffscreenCanvas::PushFrameIfNeeded() {
if (needs_push_frame_ && context_) { if (needs_push_frame_ && context_) {
return context_->PushFrame(); return context_->PushFrame();
......
...@@ -63,6 +63,7 @@ class CORE_EXPORT OffscreenCanvas final ...@@ -63,6 +63,7 @@ class CORE_EXPORT OffscreenCanvas final
// CanvasResourceDispatcherClient // CanvasResourceDispatcherClient
bool BeginFrame() override; bool BeginFrame() override;
void SetFilterQualityInResource(SkFilterQuality filter_quality) override;
// API Methods // API Methods
ImageBitmap* transferToImageBitmap(ScriptState*, ExceptionState&); ImageBitmap* transferToImageBitmap(ScriptState*, ExceptionState&);
......
...@@ -37,6 +37,8 @@ void HTMLCanvasPainter::PaintReplaced(const PaintInfo& paint_info, ...@@ -37,6 +37,8 @@ void HTMLCanvasPainter::PaintReplaced(const PaintInfo& paint_info,
paint_rect.Move(paint_offset); paint_rect.Move(paint_offset);
auto* canvas = To<HTMLCanvasElement>(layout_html_canvas_.GetNode()); auto* canvas = To<HTMLCanvasElement>(layout_html_canvas_.GetNode());
if (canvas->IsOffscreenCanvasRegistered())
canvas->UpdateOffscreenCanvasFilterQuality(canvas->FilterQuality());
if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) { if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
if (auto* layer = canvas->ContentsCcLayer()) { if (auto* layer = canvas->ContentsCcLayer()) {
......
...@@ -77,8 +77,8 @@ OffscreenCanvas* HTMLCanvasElementModule::TransferControlToOffscreenInternal( ...@@ -77,8 +77,8 @@ OffscreenCanvas* HTMLCanvasElementModule::TransferControlToOffscreenInternal(
offscreen_canvas->AllowHighPerformancePowerPreference(); offscreen_canvas->AllowHighPerformancePowerPreference();
DOMNodeId canvas_id = DOMNodeIds::IdForNode(&canvas); DOMNodeId canvas_id = DOMNodeIds::IdForNode(&canvas);
offscreen_canvas->SetPlaceholderCanvasId(canvas_id);
canvas.RegisterPlaceholderCanvas(static_cast<int>(canvas_id)); canvas.RegisterPlaceholderCanvas(static_cast<int>(canvas_id));
offscreen_canvas->SetPlaceholderCanvasId(canvas_id);
SurfaceLayerBridge* bridge = canvas.SurfaceLayerBridge(); SurfaceLayerBridge* bridge = canvas.SurfaceLayerBridge();
if (bridge) { if (bridge) {
......
...@@ -160,6 +160,7 @@ class PLATFORM_EXPORT CanvasResource ...@@ -160,6 +160,7 @@ class PLATFORM_EXPORT CanvasResource
Abandon(); Abandon();
} }
void SetFilterQuality(SkFilterQuality filter) { filter_quality_ = filter; }
// The filter quality to use when the resource is drawn by the compositor. // The filter quality to use when the resource is drawn by the compositor.
SkFilterQuality FilterQuality() const { return filter_quality_; } SkFilterQuality FilterQuality() const { return filter_quality_; }
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include <utility> #include <utility>
#include "base/debug/stack_trace.h"
#include "base/single_thread_task_runner.h" #include "base/single_thread_task_runner.h"
#include "components/viz/common/quads/compositor_frame.h" #include "components/viz/common/quads/compositor_frame.h"
#include "components/viz/common/quads/texture_draw_quad.h" #include "components/viz/common/quads/texture_draw_quad.h"
...@@ -84,8 +85,6 @@ CanvasResourceDispatcher::~CanvasResourceDispatcher() = default; ...@@ -84,8 +85,6 @@ CanvasResourceDispatcher::~CanvasResourceDispatcher() = default;
namespace { namespace {
void UpdatePlaceholderImage( void UpdatePlaceholderImage(
base::WeakPtr<CanvasResourceDispatcher> dispatcher,
scoped_refptr<base::SingleThreadTaskRunner> task_runner,
int placeholder_canvas_id, int placeholder_canvas_id,
scoped_refptr<blink::CanvasResource> canvas_resource, scoped_refptr<blink::CanvasResource> canvas_resource,
viz::ResourceId resource_id) { viz::ResourceId resource_id) {
...@@ -94,12 +93,24 @@ void UpdatePlaceholderImage( ...@@ -94,12 +93,24 @@ void UpdatePlaceholderImage(
OffscreenCanvasPlaceholder::GetPlaceholderCanvasById( OffscreenCanvasPlaceholder::GetPlaceholderCanvasById(
placeholder_canvas_id); placeholder_canvas_id);
if (placeholder_canvas) { if (placeholder_canvas) {
placeholder_canvas->SetOffscreenCanvasFrame( placeholder_canvas->SetOffscreenCanvasResource(std::move(canvas_resource),
std::move(canvas_resource), std::move(dispatcher), resource_id);
std::move(task_runner), resource_id);
} }
} }
void UpdatePlaceholderDispatcher(
base::WeakPtr<CanvasResourceDispatcher> dispatcher,
scoped_refptr<base::SingleThreadTaskRunner> task_runner,
int placeholder_canvas_id) {
OffscreenCanvasPlaceholder* placeholder_canvas =
OffscreenCanvasPlaceholder::GetPlaceholderCanvasById(
placeholder_canvas_id);
// Note that the placeholder canvas may be destroyed when this post task get
// to executed.
if (placeholder_canvas)
placeholder_canvas->SetOffscreenCanvasDispatcher(dispatcher, task_runner);
}
} // namespace } // namespace
void CanvasResourceDispatcher::PostImageToPlaceholderIfNotBlocked( void CanvasResourceDispatcher::PostImageToPlaceholderIfNotBlocked(
...@@ -131,17 +142,13 @@ void CanvasResourceDispatcher::PostImageToPlaceholder( ...@@ -131,17 +142,13 @@ void CanvasResourceDispatcher::PostImageToPlaceholder(
viz::ResourceId resource_id) { viz::ResourceId resource_id) {
scoped_refptr<base::SingleThreadTaskRunner> dispatcher_task_runner = scoped_refptr<base::SingleThreadTaskRunner> dispatcher_task_runner =
Thread::Current()->GetTaskRunner(); Thread::Current()->GetTaskRunner();
// After this point, |canvas_resource| can only be used on the main thread, // After this point, |canvas_resource| can only be used on the main thread,
// until it is returned. // until it is returned.
canvas_resource->Transfer(); canvas_resource->Transfer();
PostCrossThreadTask( PostCrossThreadTask(
*Thread::MainThread()->Scheduler()->CompositorTaskRunner(), FROM_HERE, *Thread::MainThread()->Scheduler()->CompositorTaskRunner(), FROM_HERE,
CrossThreadBindOnce(UpdatePlaceholderImage, this->GetWeakPtr(), CrossThreadBindOnce(UpdatePlaceholderImage, placeholder_canvas_id_,
WTF::Passed(std::move(dispatcher_task_runner)), std::move(canvas_resource), resource_id));
placeholder_canvas_id_, std::move(canvas_resource),
resource_id));
} }
void CanvasResourceDispatcher::DispatchFrameSync( void CanvasResourceDispatcher::DispatchFrameSync(
...@@ -405,6 +412,32 @@ void CanvasResourceDispatcher::DidDeleteSharedBitmap( ...@@ -405,6 +412,32 @@ void CanvasResourceDispatcher::DidDeleteSharedBitmap(
sink_->DidDeleteSharedBitmap(std::move(id)); sink_->DidDeleteSharedBitmap(std::move(id));
} }
void CanvasResourceDispatcher::SetFilterQuality(
SkFilterQuality filter_quality) {
if (Client())
Client()->SetFilterQualityInResource(filter_quality);
}
void CanvasResourceDispatcher::SetPlaceholderCanvasDispatcher(
int placeholder_canvas_id) {
scoped_refptr<base::SingleThreadTaskRunner> dispatcher_task_runner =
Thread::Current()->GetTaskRunner();
// If the offscreencanvas is in the same tread as the canvas, we will update
// the canvas resource dispatcher directly. So Offscreen Canvas can behave in
// a more synchronous way when it's on the main thread.
if (IsMainThread()) {
UpdatePlaceholderDispatcher(this->GetWeakPtr(), dispatcher_task_runner,
placeholder_canvas_id);
} else {
PostCrossThreadTask(
*Thread::MainThread()->Scheduler()->CompositorTaskRunner(), FROM_HERE,
CrossThreadBindOnce(UpdatePlaceholderDispatcher, this->GetWeakPtr(),
WTF::Passed(std::move(dispatcher_task_runner)),
placeholder_canvas_id));
}
}
void CanvasResourceDispatcher::ReclaimResourceInternal( void CanvasResourceDispatcher::ReclaimResourceInternal(
viz::ResourceId resource_id) { viz::ResourceId resource_id) {
auto it = resources_.find(resource_id); auto it = resources_.find(resource_id);
......
...@@ -23,6 +23,7 @@ class CanvasResource; ...@@ -23,6 +23,7 @@ class CanvasResource;
class CanvasResourceDispatcherClient { class CanvasResourceDispatcherClient {
public: public:
virtual bool BeginFrame() = 0; virtual bool BeginFrame() = 0;
virtual void SetFilterQualityInResource(SkFilterQuality filter_quality) = 0;
}; };
class PLATFORM_EXPORT CanvasResourceDispatcher class PLATFORM_EXPORT CanvasResourceDispatcher
...@@ -81,6 +82,9 @@ class PLATFORM_EXPORT CanvasResourceDispatcher ...@@ -81,6 +82,9 @@ class PLATFORM_EXPORT CanvasResourceDispatcher
::gpu::mojom::blink::MailboxPtr id); ::gpu::mojom::blink::MailboxPtr id);
void DidDeleteSharedBitmap(::gpu::mojom::blink::MailboxPtr id); void DidDeleteSharedBitmap(::gpu::mojom::blink::MailboxPtr id);
void SetFilterQuality(SkFilterQuality filter_quality);
void SetPlaceholderCanvasDispatcher(int placeholder_canvas_id);
private: private:
friend class CanvasResourceDispatcherTest; friend class CanvasResourceDispatcherTest;
struct FrameResource; struct FrameResource;
......
...@@ -222,6 +222,7 @@ class CanvasResourceProviderSharedImage : public CanvasResourceProvider { ...@@ -222,6 +222,7 @@ class CanvasResourceProviderSharedImage : public CanvasResourceProvider {
// readers. // readers.
EndWriteAccess(); EndWriteAccess();
scoped_refptr<CanvasResource> resource = resource_; scoped_refptr<CanvasResource> resource = resource_;
resource->SetFilterQuality(FilterQuality());
if (ContextProviderWrapper() if (ContextProviderWrapper()
->ContextProvider() ->ContextProvider()
->GetCapabilities() ->GetCapabilities()
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include "third_party/blink/renderer/platform/graphics/test/gpu_memory_buffer_test_platform.h" #include "third_party/blink/renderer/platform/graphics/test/gpu_memory_buffer_test_platform.h"
#include "third_party/blink/renderer/platform/graphics/test/gpu_test_utils.h" #include "third_party/blink/renderer/platform/graphics/test/gpu_test_utils.h"
#include "third_party/blink/renderer/platform/wtf/functional.h" #include "third_party/blink/renderer/platform/wtf/functional.h"
#include "third_party/skia/include/core/SkFilterQuality.h"
using testing::_; using testing::_;
using testing::InSequence; using testing::InSequence;
...@@ -34,6 +35,7 @@ class MockCanvasResourceDispatcherClient ...@@ -34,6 +35,7 @@ class MockCanvasResourceDispatcherClient
MockCanvasResourceDispatcherClient() = default; MockCanvasResourceDispatcherClient() = default;
MOCK_METHOD0(BeginFrame, bool()); MOCK_METHOD0(BeginFrame, bool());
MOCK_METHOD1(SetFilterQualityInResource, void(SkFilterQuality));
}; };
} // anonymous namespace } // anonymous namespace
......
...@@ -17,7 +17,6 @@ namespace { ...@@ -17,7 +17,6 @@ namespace {
typedef HashMap<int, blink::OffscreenCanvasPlaceholder*> PlaceholderIdMap; typedef HashMap<int, blink::OffscreenCanvasPlaceholder*> PlaceholderIdMap;
PlaceholderIdMap& placeholderRegistry() { PlaceholderIdMap& placeholderRegistry() {
DCHECK(IsMainThread());
DEFINE_STATIC_LOCAL(PlaceholderIdMap, s_placeholderRegistry, ()); DEFINE_STATIC_LOCAL(PlaceholderIdMap, s_placeholderRegistry, ());
return s_placeholderRegistry; return s_placeholderRegistry;
} }
...@@ -40,6 +39,14 @@ void SetSuspendAnimation( ...@@ -40,6 +39,14 @@ void SetSuspendAnimation(
} }
} }
void UpdateDispatcherFilterQuality(
base::WeakPtr<blink::CanvasResourceDispatcher> dispatcher,
SkFilterQuality filter) {
if (dispatcher) {
dispatcher->SetFilterQuality(filter);
}
}
} // unnamed namespace } // unnamed namespace
namespace blink { namespace blink {
...@@ -48,17 +55,13 @@ OffscreenCanvasPlaceholder::~OffscreenCanvasPlaceholder() { ...@@ -48,17 +55,13 @@ OffscreenCanvasPlaceholder::~OffscreenCanvasPlaceholder() {
UnregisterPlaceholderCanvas(); UnregisterPlaceholderCanvas();
} }
void OffscreenCanvasPlaceholder::SetOffscreenCanvasFrame( void OffscreenCanvasPlaceholder::SetOffscreenCanvasResource(
scoped_refptr<CanvasResource> new_frame, scoped_refptr<CanvasResource> new_frame,
base::WeakPtr<CanvasResourceDispatcher> dispatcher,
scoped_refptr<base::SingleThreadTaskRunner> task_runner,
viz::ResourceId resource_id) { viz::ResourceId resource_id) {
DCHECK(IsOffscreenCanvasRegistered()); DCHECK(IsOffscreenCanvasRegistered());
DCHECK(new_frame); DCHECK(new_frame);
ReleaseOffscreenCanvasFrame(); ReleaseOffscreenCanvasFrame();
placeholder_frame_ = std::move(new_frame); placeholder_frame_ = std::move(new_frame);
frame_dispatcher_ = std::move(dispatcher);
frame_dispatcher_task_runner_ = std::move(task_runner);
placeholder_frame_resource_id_ = resource_id; placeholder_frame_resource_id_ = resource_id;
if (animation_state_ == kShouldSuspendAnimation) { if (animation_state_ == kShouldSuspendAnimation) {
...@@ -72,16 +75,56 @@ void OffscreenCanvasPlaceholder::SetOffscreenCanvasFrame( ...@@ -72,16 +75,56 @@ void OffscreenCanvasPlaceholder::SetOffscreenCanvasFrame(
} }
} }
void OffscreenCanvasPlaceholder::SetOffscreenCanvasDispatcher(
base::WeakPtr<CanvasResourceDispatcher> dispatcher,
scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
DCHECK(IsOffscreenCanvasRegistered());
frame_dispatcher_ = std::move(dispatcher);
frame_dispatcher_task_runner_ = std::move(task_runner);
// The UpdateOffscreenCanvasFilterQuality could be called to change the filter
// quality before this function. We need to first apply the filter changes to
// the corresponding offscreen canvas.
if (filter_quality_) {
SkFilterQuality quality = filter_quality_.value();
filter_quality_ = base::nullopt;
UpdateOffscreenCanvasFilterQuality(quality);
}
}
void OffscreenCanvasPlaceholder::ReleaseOffscreenCanvasFrame() { void OffscreenCanvasPlaceholder::ReleaseOffscreenCanvasFrame() {
DCHECK(IsOffscreenCanvasRegistered()); DCHECK(IsOffscreenCanvasRegistered());
if (placeholder_frame_) { if (!placeholder_frame_)
return;
DCHECK(frame_dispatcher_task_runner_); DCHECK(frame_dispatcher_task_runner_);
placeholder_frame_->Transfer(); placeholder_frame_->Transfer();
PostCrossThreadTask(*frame_dispatcher_task_runner_, FROM_HERE, PostCrossThreadTask(
CrossThreadBindOnce(releaseFrameToDispatcher, *frame_dispatcher_task_runner_, FROM_HERE,
std::move(frame_dispatcher_), CrossThreadBindOnce(releaseFrameToDispatcher, frame_dispatcher_,
std::move(placeholder_frame_), std::move(placeholder_frame_),
placeholder_frame_resource_id_)); placeholder_frame_resource_id_));
}
void OffscreenCanvasPlaceholder::UpdateOffscreenCanvasFilterQuality(
SkFilterQuality filter_quality) {
DCHECK(IsOffscreenCanvasRegistered());
if (!frame_dispatcher_task_runner_) {
filter_quality_ = filter_quality;
return;
}
if (filter_quality_ == filter_quality)
return;
filter_quality_ = filter_quality;
scoped_refptr<base::SingleThreadTaskRunner> task_runner =
Thread::Current()->GetTaskRunner();
if (task_runner == frame_dispatcher_task_runner_) {
UpdateDispatcherFilterQuality(frame_dispatcher_, filter_quality);
} else {
PostCrossThreadTask(*frame_dispatcher_task_runner_, FROM_HERE,
CrossThreadBindOnce(UpdateDispatcherFilterQuality,
frame_dispatcher_, filter_quality));
} }
} }
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include "components/viz/common/resources/resource_id.h" #include "components/viz/common/resources/resource_id.h"
#include "third_party/blink/renderer/platform/platform_export.h" #include "third_party/blink/renderer/platform/platform_export.h"
#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h" #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
#include "third_party/skia/include/core/SkFilterQuality.h"
namespace blink { namespace blink {
...@@ -24,11 +25,12 @@ class PLATFORM_EXPORT OffscreenCanvasPlaceholder { ...@@ -24,11 +25,12 @@ class PLATFORM_EXPORT OffscreenCanvasPlaceholder {
public: public:
~OffscreenCanvasPlaceholder(); ~OffscreenCanvasPlaceholder();
virtual void SetOffscreenCanvasFrame( virtual void SetOffscreenCanvasResource(scoped_refptr<CanvasResource>,
scoped_refptr<CanvasResource>,
base::WeakPtr<CanvasResourceDispatcher>,
scoped_refptr<base::SingleThreadTaskRunner>,
viz::ResourceId resource_id); viz::ResourceId resource_id);
void SetOffscreenCanvasDispatcher(
base::WeakPtr<CanvasResourceDispatcher>,
scoped_refptr<base::SingleThreadTaskRunner>);
void ReleaseOffscreenCanvasFrame(); void ReleaseOffscreenCanvasFrame();
void SetSuspendOffscreenCanvasAnimation(bool); void SetSuspendOffscreenCanvasAnimation(bool);
...@@ -46,6 +48,8 @@ class PLATFORM_EXPORT OffscreenCanvasPlaceholder { ...@@ -46,6 +48,8 @@ class PLATFORM_EXPORT OffscreenCanvasPlaceholder {
return placeholder_id_ != kNoPlaceholderId; return placeholder_id_ != kNoPlaceholderId;
} }
void UpdateOffscreenCanvasFilterQuality(SkFilterQuality filter_quality);
private: private:
bool PostSetSuspendAnimationToOffscreenCanvasThread(bool suspend); bool PostSetSuspendAnimationToOffscreenCanvasThread(bool suspend);
...@@ -67,6 +71,7 @@ class PLATFORM_EXPORT OffscreenCanvasPlaceholder { ...@@ -67,6 +71,7 @@ class PLATFORM_EXPORT OffscreenCanvasPlaceholder {
kShouldActivateAnimation, kShouldActivateAnimation,
}; };
AnimationState animation_state_ = kActiveAnimation; AnimationState animation_state_ = kActiveAnimation;
base::Optional<SkFilterQuality> filter_quality_ = base::nullopt;
}; };
} // namespace blink } // namespace blink
......
<!doctype html>
<html>
<head>
<style type="text/css">
#can {
width: 500px;
height: 500px;
image-rendering: auto;
}
</style>
</head>
<body>
<canvas id="can" width="50" height="50"></canvas>
<script>
var outputCanvas = document.getElementById("can");
var outputContext = outputCanvas.getContext("2d");
outputContext.fillRect(10, 10, 30, 30);
</script>
</body>
</html>
\ No newline at end of file
<!doctype html>
<html>
<head>
<title>Test if the users can change Image Rendering Quality in Offscreen Canvas</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<style type="text/css">
#can {
width: 500px;
height: 500px;
image-rendering: pixelated;
}
</style>
</head>
<body>
<canvas id="can" width="50" height="50"></canvas>
<script>
const c2 = document.getElementById("can");
const offscreen_canvas = c2.transferControlToOffscreen();
const ctx_o = offscreen_canvas.getContext('2d');
c2.style.imageRendering = "auto";
ctx_o.clearRect(0, 0, 50, 50);
ctx_o.fillRect(10, 10, 30, 30);
function waitForCanvasToDraw() {
return new Promise(resolve => {
const testPixel = function() {
const d = ctx_o.getImageData(20, 20, 1, 1);
if (d.data[0] == 0 && d.data[3] == 255) {
setTimeout(resolve, 2000);
} else {
requestAnimationFrame(testPixel);
}
}
testPixel();
})
}
if (window.testRunner) {
testRunner.waitUntilDone();
}
waitForCanvasToDraw().then(() => {
if (window.testRunner) {
testRunner.notifyDone();
}
}).catch(err => {
testRunner.notifyDone();
throw err;
});
</script>
</body>
</html>
<!doctype html>
<html>
<head>
<style type="text/css">
#can {
width: 500px;
height: 500px;
image-rendering: auto;
}
</style>
</head>
<body>
<canvas id="can" width="50" height="50"></canvas>
<script>
var outputCanvas = document.getElementById("can");
var outputContext = outputCanvas.getContext("2d");
outputContext.fillRect(10, 10, 30, 30);
</script>
</body>
</html>
\ No newline at end of file
<!DOCTYPE html>
<title>Test using a placeholder canvas as an image source.</title>
<head>
<title>Test if the users can change Image Rendering Quality in Offscreen Canvas</title>
<style type="text/css">
#can {
width: 500px;
height: 500px;
image-rendering: pixelated;
}
</style>
</head>
<body>
<canvas id="can" width="50" height="50"></canvas>
<script id='myWorker' type='text/worker'>
var ctx_o;
self.onmessage = function(msg) {
ctx_o = msg.data.getContext('2d');
draw();
};
function draw() {
ctx_o.clearRect(0, 0, 50, 50);
ctx_o.fillRect(10, 10, 30, 30);
const d = ctx_o.getImageData(20, 20, 1, 1);
if (d.data[0] == 0 && d.data[3] == 255) {
self.postMessage("done");
} else {
requestAnimationFrame(draw);
}
}
</script>
<script>
var canvas = document.getElementById("can");
canvas.width = 50;
canvas.height = 50;
var offscreen = canvas.transferControlToOffscreen();
var blob = new Blob([document.getElementById('myWorker').textContent]);
var worker = new Worker(URL.createObjectURL(blob));
canvas.style.imageRendering = "auto";
worker.postMessage(offscreen, [offscreen]);
function waitForCanvasToDraw() {
return new Promise(resolve => {
var i = 0;
const testPixel = function() {
canvas.toBlob(blob => {
createImageBitmap(blob).then(image => {
if (verifyImage(image, "verify if image is drawn on offscreencanvas.")) {
setTimeout(resolve, 2000);
} else {
requestAnimationFrame(testPixel);
}
})
});
}
testPixel();
})
}
function verifyImage(image, description) {
var testCanvas = document.createElement('canvas');
var testCtx = testCanvas.getContext('2d');
testCtx.drawImage(image, 0, 0);
const d = testCtx.getImageData(20, 20, 1, 1);
return (d.data[0] == 0 && d.data[3] == 255);
}
if (window.testRunner) {
testRunner.waitUntilDone();
}
waitForCanvasToDraw().then(() => {
if (window.testRunner) {
testRunner.notifyDone();
}
}).catch(err => {
testRunner.notifyDone();
throw err;
});
</script>
</body>
\ No newline at end of file
<!doctype html>
<html>
<head>
<style type="text/css">
#can {
width: 500px;
height: 500px;
image-rendering: pixelated;
}
</style>
</head>
<body>
<canvas id="can" width="50" height="50"></canvas>
<script>
var outputCanvas = document.getElementById("can");
var outputContext = outputCanvas.getContext("2d");
outputContext.fillRect(10, 10, 30, 30);
</script>
</body>
</html>
\ No newline at end of file
<!doctype html>
<html>
<head>
<title>Test if the users can change Image Rendering Quality in Offscreen Canvas</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<style type="text/css">
#can {
width: 500px;
height: 500px;
image-rendering: auto;
}
</style>
</head>
<body>
<canvas id="can" width="50" height="50"></canvas>
<script>
const c2 = document.getElementById("can");
const offscreen_canvas = c2.transferControlToOffscreen();
const ctx_o = offscreen_canvas.getContext('2d');
c2.style.imageRendering = "pixelated";
ctx_o.clearRect(0, 0, 50, 50);
ctx_o.fillRect(10, 10, 30, 30);
function waitForCanvasToDraw() {
return new Promise(resolve => {
const testPixel = function() {
const d = ctx_o.getImageData(20, 20, 1, 1);
if (d.data[0] == 0 && d.data[3] == 255) {
setTimeout(resolve, 2000);
} else {
requestAnimationFrame(testPixel);
}
}
testPixel();
})
}
if (window.testRunner) {
testRunner.waitUntilDone();
}
waitForCanvasToDraw().then(() => {
if (window.testRunner) {
testRunner.notifyDone();
}
}).catch(err => {
testRunner.notifyDone();
throw err;
});
</script>
</body>
</html>
<!doctype html>
<html>
<head>
<style type="text/css">
#can {
width: 500px;
height: 500px;
image-rendering: pixelated;
}
</style>
</head>
<body>
<canvas id="can" width="50" height="50"></canvas>
<script>
var outputCanvas = document.getElementById("can");
var outputContext = outputCanvas.getContext("2d");
outputContext.fillRect(10, 10, 30, 30);
</script>
</body>
</html>
\ No newline at end of file
<!DOCTYPE html>
<title>Test using a placeholder canvas as an image source.</title>
<head>
<title>Test if the users can change Image Rendering Quality in Offscreen Canvas</title>
<style type="text/css">
#can {
width: 500px;
height: 500px;
image-rendering: auto;
}
</style>
</head>
<body>
<canvas id="can" width="50" height="50"></canvas>
<script id='myWorker' type='text/worker'>
var ctx_o;
self.onmessage = function(msg) {
ctx_o = msg.data.getContext('2d');
draw();
};
function draw() {
ctx_o.clearRect(0, 0, 50, 50);
ctx_o.fillRect(10, 10, 30, 30);
const d = ctx_o.getImageData(20, 20, 1, 1);
if (d.data[0] == 0 && d.data[3] == 255) {
self.postMessage("done");
} else {
requestAnimationFrame(draw);
}
}
</script>
<script>
var canvas = document.getElementById("can");
canvas.width = 50;
canvas.height = 50;
var offscreen = canvas.transferControlToOffscreen();
var blob = new Blob([document.getElementById('myWorker').textContent]);
var worker = new Worker(URL.createObjectURL(blob));
canvas.style.imageRendering = "pixelated";
worker.postMessage(offscreen, [offscreen]);
function waitForCanvasToDraw() {
return new Promise(resolve => {
var i = 0;
const testPixel = function() {
canvas.toBlob(blob => {
createImageBitmap(blob).then(image => {
if (verifyImage(image, "verify if image is drawn on offscreencanvas.")) {
setTimeout(resolve, 2000);
} else {
requestAnimationFrame(testPixel);
}
})
});
}
testPixel();
})
}
function verifyImage(image, description) {
var testCanvas = document.createElement('canvas');
var testCtx = testCanvas.getContext('2d');
testCtx.drawImage(image, 0, 0);
const d = testCtx.getImageData(20, 20, 1, 1);
return (d.data[0] == 0 && d.data[3] == 255);
}
if (window.testRunner) {
testRunner.waitUntilDone();
}
waitForCanvasToDraw().then(() => {
if (window.testRunner) {
testRunner.notifyDone();
}
}).catch(err => {
testRunner.notifyDone();
throw err;
});
</script>
</body>
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment