| OLD | NEW |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 |
| OLD | NEW |