Chromium Code Reviews| 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" |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 |
| OLD | NEW |