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 |