Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(73)

Side by Side Diff: third_party/WebKit/Source/core/html/canvas/CanvasAsyncBlobCreator.cpp

Issue 2878333004: Use SkJpegEncoder in WebKit platform (Closed)
Patch Set: Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "core/html/canvas/CanvasAsyncBlobCreator.h" 5 #include "core/html/canvas/CanvasAsyncBlobCreator.h"
6 6
7 #include "core/dom/DOMException.h" 7 #include "core/dom/DOMException.h"
8 #include "core/dom/Document.h" 8 #include "core/dom/Document.h"
9 #include "core/dom/TaskRunnerHelper.h" 9 #include "core/dom/TaskRunnerHelper.h"
10 #include "core/fileapi/Blob.h" 10 #include "core/fileapi/Blob.h"
11 #include "platform/CrossThreadFunctional.h" 11 #include "platform/CrossThreadFunctional.h"
12 #include "platform/Histogram.h" 12 #include "platform/Histogram.h"
13 #include "platform/WebTaskRunner.h" 13 #include "platform/WebTaskRunner.h"
14 #include "platform/graphics/ImageBuffer.h" 14 #include "platform/graphics/ImageBuffer.h"
15 #include "platform/image-encoders/JPEGImageEncoder.h"
16 #include "platform/image-encoders/PNGImageEncoder.h"
17 #include "platform/scheduler/child/web_scheduler.h" 15 #include "platform/scheduler/child/web_scheduler.h"
18 #include "platform/threading/BackgroundTaskRunner.h" 16 #include "platform/threading/BackgroundTaskRunner.h"
19 #include "platform/wtf/CurrentTime.h" 17 #include "platform/wtf/CurrentTime.h"
20 #include "platform/wtf/Functional.h" 18 #include "platform/wtf/Functional.h"
21 #include "platform/wtf/PtrUtil.h" 19 #include "platform/wtf/PtrUtil.h"
22 #include "public/platform/Platform.h" 20 #include "public/platform/Platform.h"
23 #include "public/platform/WebThread.h" 21 #include "public/platform/WebThread.h"
24 #include "public/platform/WebTraceLocation.h" 22 #include "public/platform/WebTraceLocation.h"
23 #include "third_party/skia/include/encode/SkJpegEncoder.h"
24 #include "third_party/skia/include/encode/SkPngEncoder.h"
25 25
26 namespace blink { 26 namespace blink {
27 27
28 namespace { 28 namespace {
29 29
30 const double kSlackBeforeDeadline = 30 const double kSlackBeforeDeadline =
31 0.001; // a small slack period between deadline and current time for safety 31 0.001; // a small slack period between deadline and current time for safety
32 const int kNumChannelsPng = 4;
33 32
34 // The encoding task is highly likely to switch from idle task to alternative 33 // The encoding task is highly likely to switch from idle task to alternative
35 // code path when the startTimeoutDelay is set to be below 150ms. As we want the 34 // code path when the startTimeoutDelay is set to be below 150ms. As we want the
36 // majority of encoding tasks to take the usual async idle task, we set a 35 // majority of encoding tasks to take the usual async idle task, we set a
37 // lenient limit -- 200ms here. This limit still needs to be short enough for 36 // lenient limit -- 200ms here. This limit still needs to be short enough for
38 // the latency to be negligible to the user. 37 // the latency to be negligible to the user.
39 const double kIdleTaskStartTimeoutDelay = 200.0; 38 const double kIdleTaskStartTimeoutDelay = 200.0;
40 // We should be more lenient on completion timeout delay to ensure that the 39 // We should be more lenient on completion timeout delay to ensure that the
41 // switch from idle to main thread only happens to a minority of toBlob calls 40 // switch from idle to main thread only happens to a minority of toBlob calls
42 #if !OS(ANDROID) 41 #if !OS(ANDROID)
(...skipping 141 matching lines...) Expand 10 before | Expand all | Expand 10 after
184 183
185 CanvasAsyncBlobCreator::CanvasAsyncBlobCreator(DOMUint8ClampedArray* data, 184 CanvasAsyncBlobCreator::CanvasAsyncBlobCreator(DOMUint8ClampedArray* data,
186 MimeType mime_type, 185 MimeType mime_type,
187 const IntSize& size, 186 const IntSize& size,
188 BlobCallback* callback, 187 BlobCallback* callback,
189 double start_time, 188 double start_time,
190 Document* document, 189 Document* document,
191 ScriptPromiseResolver* resolver) 190 ScriptPromiseResolver* resolver)
192 : data_(data), 191 : data_(data),
193 document_(document), 192 document_(document),
194 size_(size),
195 mime_type_(mime_type), 193 mime_type_(mime_type),
196 start_time_(start_time), 194 start_time_(start_time),
197 elapsed_time_(0), 195 elapsed_time_(0),
198 callback_(callback), 196 callback_(callback),
199 script_promise_resolver_(resolver) { 197 script_promise_resolver_(resolver) {
200 DCHECK(data_->length() == (unsigned)(size.Height() * size.Width() * 4)); 198 size_t rowBytes = size.Width() * 4;
199 DCHECK(data_->length() == (unsigned)(size.Height() * rowBytes));
201 encoded_image_ = WTF::WrapUnique(new Vector<unsigned char>()); 200 encoded_image_ = WTF::WrapUnique(new Vector<unsigned char>());
202 pixel_row_stride_ = size.Width() * kNumChannelsPng; 201 dst_ = WTF::WrapUnique(new VectorWStream(encoded_image_.get()));
f(malita) 2017/05/15 19:53:25 This is in part pre-existing, but it's unclear to
msarett1 2017/05/15 23:49:08 I thought this was odd too... I went ahead and ch
202 SkImageInfo info =
203 SkImageInfo::Make(size.Width(), size.Height(), kRGBA_8888_SkColorType,
204 kUnpremul_SkAlphaType, nullptr);
205 src_data_.reset(info, data_->Data(), rowBytes);
203 idle_task_status_ = kIdleTaskNotSupported; 206 idle_task_status_ = kIdleTaskNotSupported;
204 num_rows_completed_ = 0; 207 num_rows_completed_ = 0;
205 if (document) { 208 if (document) {
206 parent_frame_task_runner_ = 209 parent_frame_task_runner_ =
207 ParentFrameTaskRunners::Create(document->GetFrame()); 210 ParentFrameTaskRunners::Create(document->GetFrame());
208 } 211 }
209 if (script_promise_resolver_) { 212 if (script_promise_resolver_) {
210 function_type_ = kOffscreenCanvasToBlobPromise; 213 function_type_ = kOffscreenCanvasToBlobPromise;
211 } else { 214 } else {
212 function_type_ = kHTMLCanvasToBlobCallback; 215 function_type_ = kHTMLCanvasToBlobCallback;
(...skipping 12 matching lines...) Expand all
225 script_promise_resolver_.Clear(); 228 script_promise_resolver_.Clear();
226 } 229 }
227 230
228 void CanvasAsyncBlobCreator::ScheduleAsyncBlobCreation(const double& quality) { 231 void CanvasAsyncBlobCreator::ScheduleAsyncBlobCreation(const double& quality) {
229 if (mime_type_ == kMimeTypeWebp) { 232 if (mime_type_ == kMimeTypeWebp) {
230 if (!IsMainThread()) { 233 if (!IsMainThread()) {
231 DCHECK(function_type_ == kOffscreenCanvasToBlobPromise); 234 DCHECK(function_type_ == kOffscreenCanvasToBlobPromise);
232 // When OffscreenCanvas.convertToBlob() occurs on worker thread, 235 // When OffscreenCanvas.convertToBlob() occurs on worker thread,
233 // we do not need to use background task runner to reduce load on main. 236 // we do not need to use background task runner to reduce load on main.
234 // So we just directly encode images on the worker thread. 237 // So we just directly encode images on the worker thread.
235 if (!ImageDataBuffer(size_, data_->Data()) 238 IntSize size(src_data_.width(), src_data_.height());
239 if (!ImageDataBuffer(size, data_->Data())
236 .EncodeImage("image/webp", quality, encoded_image_.get())) { 240 .EncodeImage("image/webp", quality, encoded_image_.get())) {
237 TaskRunnerHelper::Get(TaskType::kCanvasBlobSerialization, document_) 241 TaskRunnerHelper::Get(TaskType::kCanvasBlobSerialization, document_)
238 ->PostTask( 242 ->PostTask(
239 BLINK_FROM_HERE, 243 BLINK_FROM_HERE,
240 WTF::Bind(&CanvasAsyncBlobCreator::CreateNullAndReturnResult, 244 WTF::Bind(&CanvasAsyncBlobCreator::CreateNullAndReturnResult,
241 WrapPersistent(this))); 245 WrapPersistent(this)));
242 246
243 return; 247 return;
244 } 248 }
245 TaskRunnerHelper::Get(TaskType::kCanvasBlobSerialization, document_) 249 TaskRunnerHelper::Get(TaskType::kCanvasBlobSerialization, document_)
246 ->PostTask( 250 ->PostTask(
247 BLINK_FROM_HERE, 251 BLINK_FROM_HERE,
248 WTF::Bind(&CanvasAsyncBlobCreator::CreateBlobAndReturnResult, 252 WTF::Bind(&CanvasAsyncBlobCreator::CreateBlobAndReturnResult,
249 WrapPersistent(this))); 253 WrapPersistent(this)));
250 254
251 } else { 255 } else {
252 BackgroundTaskRunner::PostOnBackgroundThread( 256 BackgroundTaskRunner::PostOnBackgroundThread(
253 BLINK_FROM_HERE, 257 BLINK_FROM_HERE,
254 CrossThreadBind(&CanvasAsyncBlobCreator::EncodeImageOnEncoderThread, 258 CrossThreadBind(&CanvasAsyncBlobCreator::EncodeImageOnEncoderThread,
255 WrapCrossThreadPersistent(this), quality)); 259 WrapCrossThreadPersistent(this), quality));
256 } 260 }
257 } else { 261 } else {
258 idle_task_status_ = kIdleTaskNotStarted; 262 idle_task_status_ = kIdleTaskNotStarted;
259 if (mime_type_ == kMimeTypePng) { 263 this->ScheduleInitiateEncoding(quality);
260 this->ScheduleInitiatePngEncoding();
261 } else if (mime_type_ == kMimeTypeJpeg) {
262 this->ScheduleInitiateJpegEncoding(quality);
263 } else {
264 // Progressive encoding is only applicable to png and jpeg image format,
265 // and thus idle tasks scheduling can only be applied to these image
266 // formats.
267 // TODO(xlai): Progressive encoding on webp image formats
268 // (crbug.com/571399)
269 NOTREACHED();
270 }
271 264
272 // We post the below task to check if the above idle task isn't late. 265 // We post the below task to check if the above idle task isn't late.
273 // There's no risk of concurrency as both tasks are on the same thread. 266 // There's no risk of concurrency as both tasks are on the same thread.
274 this->PostDelayedTaskToCurrentThread( 267 this->PostDelayedTaskToCurrentThread(
275 BLINK_FROM_HERE, 268 BLINK_FROM_HERE,
276 WTF::Bind(&CanvasAsyncBlobCreator::IdleTaskStartTimeoutEvent, 269 WTF::Bind(&CanvasAsyncBlobCreator::IdleTaskStartTimeoutEvent,
277 WrapPersistent(this), quality), 270 WrapPersistent(this), quality),
278 kIdleTaskStartTimeoutDelay); 271 kIdleTaskStartTimeoutDelay);
279 } 272 }
280 } 273 }
281 274
282 void CanvasAsyncBlobCreator::ScheduleInitiateJpegEncoding( 275 void CanvasAsyncBlobCreator::ScheduleInitiateEncoding(double quality) {
283 const double& quality) {
284 schedule_initiate_start_time_ = WTF::MonotonicallyIncreasingTime(); 276 schedule_initiate_start_time_ = WTF::MonotonicallyIncreasingTime();
285 Platform::Current()->CurrentThread()->Scheduler()->PostIdleTask( 277 Platform::Current()->CurrentThread()->Scheduler()->PostIdleTask(
286 BLINK_FROM_HERE, WTF::Bind(&CanvasAsyncBlobCreator::InitiateJpegEncoding, 278 BLINK_FROM_HERE, WTF::Bind(&CanvasAsyncBlobCreator::InitiateEncoding,
287 WrapPersistent(this), quality)); 279 WrapPersistent(this), quality));
288 } 280 }
289 281
290 void CanvasAsyncBlobCreator::InitiateJpegEncoding(const double& quality, 282 void CanvasAsyncBlobCreator::InitiateEncoding(double quality,
291 double deadline_seconds) { 283 double deadline_seconds) {
292 RecordElapsedTimeHistogram( 284 RecordElapsedTimeHistogram(
293 kInitiateEncodingDelay, kMimeTypeJpeg, 285 kInitiateEncodingDelay, mime_type_,
294 WTF::MonotonicallyIncreasingTime() - schedule_initiate_start_time_); 286 WTF::MonotonicallyIncreasingTime() - schedule_initiate_start_time_);
295 if (idle_task_status_ == kIdleTaskSwitchedToImmediateTask) { 287 if (idle_task_status_ == kIdleTaskSwitchedToImmediateTask) {
296 return; 288 return;
297 } 289 }
298 290
299 DCHECK(idle_task_status_ == kIdleTaskNotStarted); 291 DCHECK(idle_task_status_ == kIdleTaskNotStarted);
300 idle_task_status_ = kIdleTaskStarted; 292 idle_task_status_ = kIdleTaskStarted;
301 293
302 if (!InitializeJpegStruct(quality)) { 294 if (!InitializeEncoder(quality)) {
303 idle_task_status_ = kIdleTaskFailed; 295 idle_task_status_ = kIdleTaskFailed;
304 return; 296 return;
305 } 297 }
306 this->IdleEncodeRowsJpeg(deadline_seconds); 298
299 this->IdleEncodeRows(deadline_seconds);
307 } 300 }
308 301
309 void CanvasAsyncBlobCreator::ScheduleInitiatePngEncoding() { 302 void CanvasAsyncBlobCreator::IdleEncodeRows(double deadline_seconds) {
310 schedule_initiate_start_time_ = WTF::MonotonicallyIncreasingTime();
311 Platform::Current()->CurrentThread()->Scheduler()->PostIdleTask(
312 BLINK_FROM_HERE, WTF::Bind(&CanvasAsyncBlobCreator::InitiatePngEncoding,
313 WrapPersistent(this)));
314 }
315
316 void CanvasAsyncBlobCreator::InitiatePngEncoding(double deadline_seconds) {
317 RecordElapsedTimeHistogram(
318 kInitiateEncodingDelay, kMimeTypePng,
319 WTF::MonotonicallyIncreasingTime() - schedule_initiate_start_time_);
320 if (idle_task_status_ == kIdleTaskSwitchedToImmediateTask) {
321 return;
322 }
323
324 DCHECK(idle_task_status_ == kIdleTaskNotStarted);
325 idle_task_status_ = kIdleTaskStarted;
326
327 if (!InitializePngStruct()) {
328 idle_task_status_ = kIdleTaskFailed;
329 return;
330 }
331 this->IdleEncodeRowsPng(deadline_seconds);
332 }
333
334 void CanvasAsyncBlobCreator::IdleEncodeRowsPng(double deadline_seconds) {
335 if (idle_task_status_ == kIdleTaskSwitchedToImmediateTask) { 303 if (idle_task_status_ == kIdleTaskSwitchedToImmediateTask) {
336 return; 304 return;
337 } 305 }
338 306
339 double start_time = WTF::MonotonicallyIncreasingTime(); 307 double start_time = WTF::MonotonicallyIncreasingTime();
340 unsigned char* input_pixels = 308 for (int y = num_rows_completed_; y < src_data_.height(); ++y) {
341 data_->Data() + pixel_row_stride_ * num_rows_completed_;
342 for (int y = num_rows_completed_; y < size_.Height(); ++y) {
343 if (IsDeadlineNearOrPassed(deadline_seconds)) { 309 if (IsDeadlineNearOrPassed(deadline_seconds)) {
344 num_rows_completed_ = y; 310 num_rows_completed_ = y;
345 elapsed_time_ += (WTF::MonotonicallyIncreasingTime() - start_time); 311 elapsed_time_ += (WTF::MonotonicallyIncreasingTime() - start_time);
346 Platform::Current()->CurrentThread()->Scheduler()->PostIdleTask( 312 Platform::Current()->CurrentThread()->Scheduler()->PostIdleTask(
347 BLINK_FROM_HERE, WTF::Bind(&CanvasAsyncBlobCreator::IdleEncodeRowsPng, 313 BLINK_FROM_HERE, WTF::Bind(&CanvasAsyncBlobCreator::IdleEncodeRows,
348 WrapPersistent(this))); 314 WrapPersistent(this)));
349 return; 315 return;
350 } 316 }
351 PNGImageEncoder::WriteOneRowToPng(input_pixels, png_encoder_state_.get()); 317
352 input_pixels += pixel_row_stride_; 318 if (!encoder_->encodeRows(1)) {
319 idle_task_status_ = kIdleTaskFailed;
320 this->CreateNullAndReturnResult();
321 return;
322 }
353 } 323 }
354 num_rows_completed_ = size_.Height(); 324 num_rows_completed_ = src_data_.height();
355 PNGImageEncoder::FinalizePng(png_encoder_state_.get());
356 325
357 idle_task_status_ = kIdleTaskCompleted; 326 idle_task_status_ = kIdleTaskCompleted;
358 elapsed_time_ += (WTF::MonotonicallyIncreasingTime() - start_time); 327 elapsed_time_ += (WTF::MonotonicallyIncreasingTime() - start_time);
359 RecordElapsedTimeHistogram(kIdleEncodeDuration, kMimeTypePng, elapsed_time_); 328 RecordElapsedTimeHistogram(kIdleEncodeDuration, mime_type_, elapsed_time_);
360 if (IsDeadlineNearOrPassed(deadline_seconds)) { 329 if (IsDeadlineNearOrPassed(deadline_seconds)) {
361 TaskRunnerHelper::Get(TaskType::kCanvasBlobSerialization, document_) 330 TaskRunnerHelper::Get(TaskType::kCanvasBlobSerialization, document_)
362 ->PostTask(BLINK_FROM_HERE, 331 ->PostTask(BLINK_FROM_HERE,
363 WTF::Bind(&CanvasAsyncBlobCreator::CreateBlobAndReturnResult, 332 WTF::Bind(&CanvasAsyncBlobCreator::CreateBlobAndReturnResult,
364 WrapPersistent(this))); 333 WrapPersistent(this)));
365 } else { 334 } else {
366 this->CreateBlobAndReturnResult(); 335 this->CreateBlobAndReturnResult();
367 } 336 }
368 } 337 }
369 338
370 void CanvasAsyncBlobCreator::IdleEncodeRowsJpeg(double deadline_seconds) { 339 void CanvasAsyncBlobCreator::ForceEncodeRowsOnCurrentThread() {
371 if (idle_task_status_ == kIdleTaskSwitchedToImmediateTask) {
372 return;
373 }
374
375 double start_time = WTF::MonotonicallyIncreasingTime();
376 num_rows_completed_ = JPEGImageEncoder::ProgressiveEncodeRowsJpegHelper(
377 jpeg_encoder_state_.get(), data_->Data(), num_rows_completed_,
378 kSlackBeforeDeadline, deadline_seconds);
379 elapsed_time_ += (WTF::MonotonicallyIncreasingTime() - start_time);
380 if (num_rows_completed_ == size_.Height()) {
381 idle_task_status_ = kIdleTaskCompleted;
382 RecordElapsedTimeHistogram(kIdleEncodeDuration, kMimeTypeJpeg,
383 elapsed_time_);
384
385 if (IsDeadlineNearOrPassed(deadline_seconds)) {
386 TaskRunnerHelper::Get(TaskType::kCanvasBlobSerialization, document_)
387 ->PostTask(
388 BLINK_FROM_HERE,
389 WTF::Bind(&CanvasAsyncBlobCreator::CreateBlobAndReturnResult,
390 WrapPersistent(this)));
391 } else {
392 this->CreateBlobAndReturnResult();
393 }
394 } else if (num_rows_completed_ ==
395 JPEGImageEncoder::kProgressiveEncodeFailed) {
396 idle_task_status_ = kIdleTaskFailed;
397 this->CreateNullAndReturnResult();
398 } else {
399 Platform::Current()->CurrentThread()->Scheduler()->PostIdleTask(
400 BLINK_FROM_HERE, WTF::Bind(&CanvasAsyncBlobCreator::IdleEncodeRowsJpeg,
401 WrapPersistent(this)));
402 }
403 }
404
405 void CanvasAsyncBlobCreator::ForceEncodeRowsPngOnCurrentThread() {
406 DCHECK(idle_task_status_ == kIdleTaskSwitchedToImmediateTask); 340 DCHECK(idle_task_status_ == kIdleTaskSwitchedToImmediateTask);
407 341
408 // Continue encoding from the last completed row 342 // Continue encoding from the last completed row
409 unsigned char* input_pixels = 343 for (int y = num_rows_completed_; y < src_data_.height(); ++y) {
410 data_->Data() + pixel_row_stride_ * num_rows_completed_; 344 if (!encoder_->encodeRows(1)) {
411 for (int y = num_rows_completed_; y < size_.Height(); ++y) { 345 idle_task_status_ = kIdleTaskFailed;
412 PNGImageEncoder::WriteOneRowToPng(input_pixels, png_encoder_state_.get()); 346 this->CreateNullAndReturnResult();
413 input_pixels += pixel_row_stride_; 347 return;
348 }
414 } 349 }
415 PNGImageEncoder::FinalizePng(png_encoder_state_.get()); 350 num_rows_completed_ = src_data_.height();
416 351
417 if (IsMainThread()) { 352 if (IsMainThread()) {
418 this->CreateBlobAndReturnResult(); 353 this->CreateBlobAndReturnResult();
419 } else { 354 } else {
420 TaskRunnerHelper::Get(TaskType::kCanvasBlobSerialization, document_) 355 TaskRunnerHelper::Get(TaskType::kCanvasBlobSerialization, document_)
421 ->PostTask( 356 ->PostTask(
422 BLINK_FROM_HERE, 357 BLINK_FROM_HERE,
423 CrossThreadBind(&CanvasAsyncBlobCreator::CreateBlobAndReturnResult, 358 CrossThreadBind(&CanvasAsyncBlobCreator::CreateBlobAndReturnResult,
424 WrapCrossThreadPersistent(this))); 359 WrapCrossThreadPersistent(this)));
425 } 360 }
426 361
427 this->SignalAlternativeCodePathFinishedForTesting(); 362 this->SignalAlternativeCodePathFinishedForTesting();
428 } 363 }
429 364
430 void CanvasAsyncBlobCreator::ForceEncodeRowsJpegOnCurrentThread() {
431 DCHECK(idle_task_status_ == kIdleTaskSwitchedToImmediateTask);
432
433 // Continue encoding from the last completed row
434 void (CanvasAsyncBlobCreator::*function_to_be_called)(void);
435 if (JPEGImageEncoder::EncodeWithPreInitializedState(
436 std::move(jpeg_encoder_state_), data_->Data(), num_rows_completed_)) {
437 function_to_be_called = &CanvasAsyncBlobCreator::CreateBlobAndReturnResult;
438 } else {
439 function_to_be_called = &CanvasAsyncBlobCreator::CreateNullAndReturnResult;
440 }
441
442 if (IsMainThread()) {
443 (this->*function_to_be_called)();
444 } else {
445 TaskRunnerHelper::Get(TaskType::kCanvasBlobSerialization, document_)
446 ->PostTask(BLINK_FROM_HERE,
447 CrossThreadBind(function_to_be_called,
448 WrapCrossThreadPersistent(this)));
449 }
450
451 this->SignalAlternativeCodePathFinishedForTesting();
452 }
453
454 void CanvasAsyncBlobCreator::CreateBlobAndReturnResult() { 365 void CanvasAsyncBlobCreator::CreateBlobAndReturnResult() {
455 RecordIdleTaskStatusHistogram(idle_task_status_); 366 RecordIdleTaskStatusHistogram(idle_task_status_);
456 RecordElapsedTimeHistogram(kToBlobDuration, mime_type_, 367 RecordElapsedTimeHistogram(kToBlobDuration, mime_type_,
457 WTF::MonotonicallyIncreasingTime() - start_time_); 368 WTF::MonotonicallyIncreasingTime() - start_time_);
458 369
459 Blob* result_blob = 370 Blob* result_blob =
460 Blob::Create(encoded_image_->data(), encoded_image_->size(), 371 Blob::Create(encoded_image_->data(), encoded_image_->size(),
461 ConvertMimeTypeEnumToString(mime_type_)); 372 ConvertMimeTypeEnumToString(mime_type_));
462 if (function_type_ == kHTMLCanvasToBlobCallback) { 373 if (function_type_ == kHTMLCanvasToBlobCallback) {
463 TaskRunnerHelper::Get(TaskType::kCanvasBlobSerialization, document_) 374 TaskRunnerHelper::Get(TaskType::kCanvasBlobSerialization, document_)
(...skipping 21 matching lines...) Expand all
485 kEncodingError, "Encoding of the source image has failed.")); 396 kEncodingError, "Encoding of the source image has failed."));
486 } 397 }
487 // Avoid unwanted retention, see dispose(). 398 // Avoid unwanted retention, see dispose().
488 Dispose(); 399 Dispose();
489 } 400 }
490 401
491 void CanvasAsyncBlobCreator::EncodeImageOnEncoderThread(double quality) { 402 void CanvasAsyncBlobCreator::EncodeImageOnEncoderThread(double quality) {
492 DCHECK(!IsMainThread()); 403 DCHECK(!IsMainThread());
493 DCHECK(mime_type_ == kMimeTypeWebp); 404 DCHECK(mime_type_ == kMimeTypeWebp);
494 405
495 if (!ImageDataBuffer(size_, data_->Data()) 406 IntSize size(src_data_.width(), src_data_.height());
407 if (!ImageDataBuffer(size, data_->Data())
496 .EncodeImage("image/webp", quality, encoded_image_.get())) { 408 .EncodeImage("image/webp", quality, encoded_image_.get())) {
497 parent_frame_task_runner_->Get(TaskType::kCanvasBlobSerialization) 409 parent_frame_task_runner_->Get(TaskType::kCanvasBlobSerialization)
498 ->PostTask( 410 ->PostTask(
499 BLINK_FROM_HERE, 411 BLINK_FROM_HERE,
500 CrossThreadBind(&CanvasAsyncBlobCreator::CreateNullAndReturnResult, 412 CrossThreadBind(&CanvasAsyncBlobCreator::CreateNullAndReturnResult,
501 WrapCrossThreadPersistent(this))); 413 WrapCrossThreadPersistent(this)));
502 return; 414 return;
503 } 415 }
504 416
505 parent_frame_task_runner_->Get(TaskType::kCanvasBlobSerialization) 417 parent_frame_task_runner_->Get(TaskType::kCanvasBlobSerialization)
506 ->PostTask( 418 ->PostTask(
507 BLINK_FROM_HERE, 419 BLINK_FROM_HERE,
508 CrossThreadBind(&CanvasAsyncBlobCreator::CreateBlobAndReturnResult, 420 CrossThreadBind(&CanvasAsyncBlobCreator::CreateBlobAndReturnResult,
509 WrapCrossThreadPersistent(this))); 421 WrapCrossThreadPersistent(this)));
510 } 422 }
511 423
512 bool CanvasAsyncBlobCreator::InitializePngStruct() { 424 bool CanvasAsyncBlobCreator::InitializeEncoder(double quality) {
513 png_encoder_state_ = 425 if (kMimeTypeJpeg == mime_type_) {
f(malita) 2017/05/15 19:53:26 nit: I don't think Yoda conditions are favored in
msarett1 2017/05/15 23:49:08 Done.
514 PNGImageEncoderState::Create(size_, encoded_image_.get()); 426 SkJpegEncoder::Options options;
515 if (!png_encoder_state_) { 427 options.fQuality = static_cast<int>(quality * 100 + 0.5);
516 this->CreateNullAndReturnResult(); 428 options.fAlphaOption = SkJpegEncoder::AlphaOption::kBlendOnBlack;
517 return false; 429 options.fBlendBehavior = SkTransferFunctionBehavior::kIgnore;
430 encoder_ = SkJpegEncoder::Make(dst_.get(), src_data_, options);
431 } else {
432 // Progressive encoding is only applicable to png and jpeg image format,
433 // and thus idle tasks scheduling can only be applied to these image
434 // formats.
435 // TODO(xlai): Progressive encoding on webp image formats
436 // (crbug.com/571399)
437 DCHECK_EQ(kMimeTypePng, mime_type_);
438 SkPngEncoder::Options options;
439 options.fFilterFlags = SkPngEncoder::FilterFlag::kSub;
440 options.fZLibLevel = 3;
441 options.fUnpremulBehavior = SkTransferFunctionBehavior::kIgnore;
442 encoder_ = SkPngEncoder::Make(dst_.get(), src_data_, options);
518 } 443 }
519 return true;
520 }
521 444
522 bool CanvasAsyncBlobCreator::InitializeJpegStruct(double quality) { 445 return encoder_.get();
523 jpeg_encoder_state_ =
524 JPEGImageEncoderState::Create(size_, quality, encoded_image_.get());
525 if (!jpeg_encoder_state_) {
526 this->CreateNullAndReturnResult();
527 return false;
528 }
529 return true;
530 } 446 }
531 447
532 void CanvasAsyncBlobCreator::IdleTaskStartTimeoutEvent(double quality) { 448 void CanvasAsyncBlobCreator::IdleTaskStartTimeoutEvent(double quality) {
533 if (idle_task_status_ == kIdleTaskStarted) { 449 if (idle_task_status_ == kIdleTaskStarted) {
534 // Even if the task started quickly, we still want to ensure completion 450 // Even if the task started quickly, we still want to ensure completion
535 this->PostDelayedTaskToCurrentThread( 451 this->PostDelayedTaskToCurrentThread(
536 BLINK_FROM_HERE, 452 BLINK_FROM_HERE,
537 WTF::Bind(&CanvasAsyncBlobCreator::IdleTaskCompleteTimeoutEvent, 453 WTF::Bind(&CanvasAsyncBlobCreator::IdleTaskCompleteTimeoutEvent,
538 WrapPersistent(this)), 454 WrapPersistent(this)),
539 kIdleTaskCompleteTimeoutDelay); 455 kIdleTaskCompleteTimeoutDelay);
540 } else if (idle_task_status_ == kIdleTaskNotStarted) { 456 } else if (idle_task_status_ == kIdleTaskNotStarted) {
541 // If the idle task does not start after a delay threshold, we will 457 // If the idle task does not start after a delay threshold, we will
542 // force it to happen on main thread (even though it may cause more 458 // force it to happen on main thread (even though it may cause more
543 // janks) to prevent toBlob being postponed forever in extreme cases. 459 // janks) to prevent toBlob being postponed forever in extreme cases.
544 idle_task_status_ = kIdleTaskSwitchedToImmediateTask; 460 idle_task_status_ = kIdleTaskSwitchedToImmediateTask;
545 SignalTaskSwitchInStartTimeoutEventForTesting(); 461 SignalTaskSwitchInStartTimeoutEventForTesting();
546 462
547 if (mime_type_ == kMimeTypePng) { 463 DCHECK(mime_type_ == kMimeTypePng || mime_type_ == kMimeTypeJpeg);
548 if (InitializePngStruct()) { 464 if (InitializeEncoder(quality)) {
549 TaskRunnerHelper::Get(TaskType::kCanvasBlobSerialization, document_) 465 TaskRunnerHelper::Get(TaskType::kCanvasBlobSerialization, document_)
550 ->PostTask( 466 ->PostTask(
551 BLINK_FROM_HERE, 467 BLINK_FROM_HERE,
552 WTF::Bind( 468 WTF::Bind(&CanvasAsyncBlobCreator::ForceEncodeRowsOnCurrentThread,
553 &CanvasAsyncBlobCreator::ForceEncodeRowsPngOnCurrentThread, 469 WrapPersistent(this)));
554 WrapPersistent(this)));
555 } else {
556 // Failing in initialization of png struct
557 this->SignalAlternativeCodePathFinishedForTesting();
558 }
559 } else { 470 } else {
560 DCHECK(mime_type_ == kMimeTypeJpeg); 471 // Failing in initialization of encoder
561 if (InitializeJpegStruct(quality)) { 472 this->SignalAlternativeCodePathFinishedForTesting();
562 TaskRunnerHelper::Get(TaskType::kCanvasBlobSerialization, document_)
563 ->PostTask(
564 BLINK_FROM_HERE,
565 WTF::Bind(
566 &CanvasAsyncBlobCreator::ForceEncodeRowsJpegOnCurrentThread,
567 WrapPersistent(this)));
568 } else {
569 // Failing in initialization of jpeg struct
570 this->SignalAlternativeCodePathFinishedForTesting();
571 }
572 } 473 }
573 } else { 474 } else {
574 DCHECK(idle_task_status_ == kIdleTaskFailed || 475 DCHECK(idle_task_status_ == kIdleTaskFailed ||
575 idle_task_status_ == kIdleTaskCompleted); 476 idle_task_status_ == kIdleTaskCompleted);
576 this->SignalAlternativeCodePathFinishedForTesting(); 477 this->SignalAlternativeCodePathFinishedForTesting();
577 } 478 }
578 } 479 }
579 480
580 void CanvasAsyncBlobCreator::IdleTaskCompleteTimeoutEvent() { 481 void CanvasAsyncBlobCreator::IdleTaskCompleteTimeoutEvent() {
581 DCHECK(idle_task_status_ != kIdleTaskNotStarted); 482 DCHECK(idle_task_status_ != kIdleTaskNotStarted);
582 483
583 if (idle_task_status_ == kIdleTaskStarted) { 484 if (idle_task_status_ == kIdleTaskStarted) {
584 // It has taken too long to complete for the idle task. 485 // It has taken too long to complete for the idle task.
585 idle_task_status_ = kIdleTaskSwitchedToImmediateTask; 486 idle_task_status_ = kIdleTaskSwitchedToImmediateTask;
586 SignalTaskSwitchInCompleteTimeoutEventForTesting(); 487 SignalTaskSwitchInCompleteTimeoutEventForTesting();
587 488
588 if (mime_type_ == kMimeTypePng) { 489 DCHECK(mime_type_ == kMimeTypePng || mime_type_ == kMimeTypeJpeg);
589 TaskRunnerHelper::Get(TaskType::kCanvasBlobSerialization, document_) 490 TaskRunnerHelper::Get(TaskType::kCanvasBlobSerialization, document_)
590 ->PostTask( 491 ->PostTask(
591 BLINK_FROM_HERE, 492 BLINK_FROM_HERE,
592 WTF::Bind( 493 WTF::Bind(&CanvasAsyncBlobCreator::ForceEncodeRowsOnCurrentThread,
593 &CanvasAsyncBlobCreator::ForceEncodeRowsPngOnCurrentThread, 494 WrapPersistent(this)));
594 WrapPersistent(this)));
595 } else {
596 DCHECK(mime_type_ == kMimeTypeJpeg);
597 TaskRunnerHelper::Get(TaskType::kCanvasBlobSerialization, document_)
598 ->PostTask(
599 BLINK_FROM_HERE,
600 WTF::Bind(
601 &CanvasAsyncBlobCreator::ForceEncodeRowsJpegOnCurrentThread,
602 WrapPersistent(this)));
603 }
604 } else { 495 } else {
605 DCHECK(idle_task_status_ == kIdleTaskFailed || 496 DCHECK(idle_task_status_ == kIdleTaskFailed ||
606 idle_task_status_ == kIdleTaskCompleted); 497 idle_task_status_ == kIdleTaskCompleted);
607 this->SignalAlternativeCodePathFinishedForTesting(); 498 this->SignalAlternativeCodePathFinishedForTesting();
608 } 499 }
609 } 500 }
610 501
611 void CanvasAsyncBlobCreator::PostDelayedTaskToCurrentThread( 502 void CanvasAsyncBlobCreator::PostDelayedTaskToCurrentThread(
612 const WebTraceLocation& location, 503 const WebTraceLocation& location,
613 std::unique_ptr<WTF::Closure> task, 504 std::unique_ptr<WTF::Closure> task,
614 double delay_ms) { 505 double delay_ms) {
615 TaskRunnerHelper::Get(TaskType::kCanvasBlobSerialization, document_) 506 TaskRunnerHelper::Get(TaskType::kCanvasBlobSerialization, document_)
616 ->PostDelayedTask(location, std::move(task), 507 ->PostDelayedTask(location, std::move(task),
617 TimeDelta::FromMillisecondsD(delay_ms)); 508 TimeDelta::FromMillisecondsD(delay_ms));
618 } 509 }
619 510
620 DEFINE_TRACE(CanvasAsyncBlobCreator) { 511 DEFINE_TRACE(CanvasAsyncBlobCreator) {
621 visitor->Trace(document_); 512 visitor->Trace(document_);
622 visitor->Trace(data_); 513 visitor->Trace(data_);
623 visitor->Trace(callback_); 514 visitor->Trace(callback_);
624 visitor->Trace(parent_frame_task_runner_); 515 visitor->Trace(parent_frame_task_runner_);
625 visitor->Trace(script_promise_resolver_); 516 visitor->Trace(script_promise_resolver_);
626 } 517 }
627 518
628 } // namespace blink 519 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698