1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "third_party/blink/renderer/platform/graphics/offscreen_canvas_placeholder.h"
6 
7 #include "base/single_thread_task_runner.h"
8 #include "third_party/blink/renderer/platform/graphics/canvas_resource.h"
9 #include "third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.h"
10 #include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
11 #include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
12 #include "third_party/blink/renderer/platform/wtf/hash_map.h"
13 #include "third_party/blink/renderer/platform/wtf/wtf.h"
14 
15 namespace {
16 
17 typedef HashMap<int, blink::OffscreenCanvasPlaceholder*> PlaceholderIdMap;
18 
placeholderRegistry()19 PlaceholderIdMap& placeholderRegistry() {
20   DEFINE_STATIC_LOCAL(PlaceholderIdMap, s_placeholderRegistry, ());
21   return s_placeholderRegistry;
22 }
23 
releaseFrameToDispatcher(base::WeakPtr<blink::CanvasResourceDispatcher> dispatcher,scoped_refptr<blink::CanvasResource> oldImage,viz::ResourceId resourceId)24 void releaseFrameToDispatcher(
25     base::WeakPtr<blink::CanvasResourceDispatcher> dispatcher,
26     scoped_refptr<blink::CanvasResource> oldImage,
27     viz::ResourceId resourceId) {
28   oldImage = nullptr;  // Needed to unref'ed on the right thread
29   if (dispatcher) {
30     dispatcher->ReclaimResource(resourceId);
31   }
32 }
33 
SetSuspendAnimation(base::WeakPtr<blink::CanvasResourceDispatcher> dispatcher,bool suspend)34 void SetSuspendAnimation(
35     base::WeakPtr<blink::CanvasResourceDispatcher> dispatcher,
36     bool suspend) {
37   if (dispatcher) {
38     dispatcher->SetSuspendAnimation(suspend);
39   }
40 }
41 
UpdateDispatcherFilterQuality(base::WeakPtr<blink::CanvasResourceDispatcher> dispatcher,SkFilterQuality filter)42 void UpdateDispatcherFilterQuality(
43     base::WeakPtr<blink::CanvasResourceDispatcher> dispatcher,
44     SkFilterQuality filter) {
45   if (dispatcher) {
46     dispatcher->SetFilterQuality(filter);
47   }
48 }
49 
50 }  // unnamed namespace
51 
52 namespace blink {
53 
~OffscreenCanvasPlaceholder()54 OffscreenCanvasPlaceholder::~OffscreenCanvasPlaceholder() {
55   UnregisterPlaceholderCanvas();
56 }
57 
SetOffscreenCanvasResource(scoped_refptr<CanvasResource> new_frame,viz::ResourceId resource_id)58 void OffscreenCanvasPlaceholder::SetOffscreenCanvasResource(
59     scoped_refptr<CanvasResource> new_frame,
60     viz::ResourceId resource_id) {
61   DCHECK(IsOffscreenCanvasRegistered());
62   DCHECK(new_frame);
63   ReleaseOffscreenCanvasFrame();
64   placeholder_frame_ = std::move(new_frame);
65   placeholder_frame_resource_id_ = resource_id;
66 
67   if (animation_state_ == kShouldSuspendAnimation) {
68     bool success = PostSetSuspendAnimationToOffscreenCanvasThread(true);
69     DCHECK(success);
70     animation_state_ = kSuspendedAnimation;
71   } else if (animation_state_ == kShouldActivateAnimation) {
72     bool success = PostSetSuspendAnimationToOffscreenCanvasThread(false);
73     DCHECK(success);
74     animation_state_ = kActiveAnimation;
75   }
76 }
77 
SetOffscreenCanvasDispatcher(base::WeakPtr<CanvasResourceDispatcher> dispatcher,scoped_refptr<base::SingleThreadTaskRunner> task_runner)78 void OffscreenCanvasPlaceholder::SetOffscreenCanvasDispatcher(
79     base::WeakPtr<CanvasResourceDispatcher> dispatcher,
80     scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
81   DCHECK(IsOffscreenCanvasRegistered());
82   frame_dispatcher_ = std::move(dispatcher);
83   frame_dispatcher_task_runner_ = std::move(task_runner);
84   // The UpdateOffscreenCanvasFilterQuality could be called to change the filter
85   // quality before this function. We need to first apply the filter changes to
86   // the corresponding offscreen canvas.
87   if (filter_quality_) {
88     SkFilterQuality quality = filter_quality_.value();
89     filter_quality_ = base::nullopt;
90     UpdateOffscreenCanvasFilterQuality(quality);
91   }
92 }
93 
ReleaseOffscreenCanvasFrame()94 void OffscreenCanvasPlaceholder::ReleaseOffscreenCanvasFrame() {
95   DCHECK(IsOffscreenCanvasRegistered());
96   if (!placeholder_frame_)
97     return;
98 
99   DCHECK(frame_dispatcher_task_runner_);
100   placeholder_frame_->Transfer();
101   PostCrossThreadTask(
102       *frame_dispatcher_task_runner_, FROM_HERE,
103       CrossThreadBindOnce(releaseFrameToDispatcher, frame_dispatcher_,
104                           std::move(placeholder_frame_),
105                           placeholder_frame_resource_id_));
106 }
107 
UpdateOffscreenCanvasFilterQuality(SkFilterQuality filter_quality)108 void OffscreenCanvasPlaceholder::UpdateOffscreenCanvasFilterQuality(
109     SkFilterQuality filter_quality) {
110   DCHECK(IsOffscreenCanvasRegistered());
111   if (!frame_dispatcher_task_runner_) {
112     filter_quality_ = filter_quality;
113     return;
114   }
115 
116   if (filter_quality_ == filter_quality)
117     return;
118 
119   filter_quality_ = filter_quality;
120   scoped_refptr<base::SingleThreadTaskRunner> task_runner =
121       Thread::Current()->GetTaskRunner();
122   if (task_runner == frame_dispatcher_task_runner_) {
123     UpdateDispatcherFilterQuality(frame_dispatcher_, filter_quality);
124   } else {
125     PostCrossThreadTask(*frame_dispatcher_task_runner_, FROM_HERE,
126                         CrossThreadBindOnce(UpdateDispatcherFilterQuality,
127                                             frame_dispatcher_, filter_quality));
128   }
129 }
130 
SetSuspendOffscreenCanvasAnimation(bool suspend)131 void OffscreenCanvasPlaceholder::SetSuspendOffscreenCanvasAnimation(
132     bool suspend) {
133   switch (animation_state_) {
134     case kActiveAnimation:
135       if (suspend) {
136         if (PostSetSuspendAnimationToOffscreenCanvasThread(suspend)) {
137           animation_state_ = kSuspendedAnimation;
138         } else {
139           animation_state_ = kShouldSuspendAnimation;
140         }
141       }
142       break;
143     case kSuspendedAnimation:
144       if (!suspend) {
145         if (PostSetSuspendAnimationToOffscreenCanvasThread(suspend)) {
146           animation_state_ = kActiveAnimation;
147         } else {
148           animation_state_ = kShouldActivateAnimation;
149         }
150       }
151       break;
152     case kShouldSuspendAnimation:
153       if (!suspend) {
154         animation_state_ = kActiveAnimation;
155       }
156       break;
157     case kShouldActivateAnimation:
158       if (suspend) {
159         animation_state_ = kSuspendedAnimation;
160       }
161       break;
162     default:
163       NOTREACHED();
164   }
165 }
166 
167 OffscreenCanvasPlaceholder*
GetPlaceholderCanvasById(unsigned placeholder_id)168 OffscreenCanvasPlaceholder::GetPlaceholderCanvasById(unsigned placeholder_id) {
169   PlaceholderIdMap::iterator it = placeholderRegistry().find(placeholder_id);
170   if (it == placeholderRegistry().end())
171     return nullptr;
172   return it->value;
173 }
174 
RegisterPlaceholderCanvas(unsigned placeholder_id)175 void OffscreenCanvasPlaceholder::RegisterPlaceholderCanvas(
176     unsigned placeholder_id) {
177   DCHECK(!placeholderRegistry().Contains(placeholder_id));
178   DCHECK(!IsOffscreenCanvasRegistered());
179   placeholderRegistry().insert(placeholder_id, this);
180   placeholder_id_ = placeholder_id;
181 }
182 
UnregisterPlaceholderCanvas()183 void OffscreenCanvasPlaceholder::UnregisterPlaceholderCanvas() {
184   if (!IsOffscreenCanvasRegistered())
185     return;
186   DCHECK(placeholderRegistry().find(placeholder_id_)->value == this);
187   placeholderRegistry().erase(placeholder_id_);
188   placeholder_id_ = kNoPlaceholderId;
189 }
190 
PostSetSuspendAnimationToOffscreenCanvasThread(bool suspend)191 bool OffscreenCanvasPlaceholder::PostSetSuspendAnimationToOffscreenCanvasThread(
192     bool suspend) {
193   if (!frame_dispatcher_task_runner_)
194     return false;
195   PostCrossThreadTask(
196       *frame_dispatcher_task_runner_, FROM_HERE,
197       CrossThreadBindOnce(SetSuspendAnimation, frame_dispatcher_, suspend));
198   return true;
199 }
200 
201 }  // namespace blink
202