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 |