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

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

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

Powered by Google App Engine
This is Rietveld 408576698