Chromium Code Reviews| Index: third_party/WebKit/Source/core/html/canvas/CanvasAsyncBlobCreator.cpp |
| diff --git a/third_party/WebKit/Source/core/html/canvas/CanvasAsyncBlobCreator.cpp b/third_party/WebKit/Source/core/html/canvas/CanvasAsyncBlobCreator.cpp |
| index 0250b7938a27d7e4410ff773c317a84ef56ab812..1930a0db23253e57ab4c812670634ed79dda5caf 100644 |
| --- a/third_party/WebKit/Source/core/html/canvas/CanvasAsyncBlobCreator.cpp |
| +++ b/third_party/WebKit/Source/core/html/canvas/CanvasAsyncBlobCreator.cpp |
| @@ -12,8 +12,6 @@ |
| #include "platform/Histogram.h" |
| #include "platform/WebTaskRunner.h" |
| #include "platform/graphics/ImageBuffer.h" |
| -#include "platform/image-encoders/JPEGImageEncoder.h" |
| -#include "platform/image-encoders/PNGImageEncoder.h" |
| #include "platform/scheduler/child/web_scheduler.h" |
| #include "platform/threading/BackgroundTaskRunner.h" |
| #include "platform/wtf/CurrentTime.h" |
| @@ -22,6 +20,8 @@ |
| #include "public/platform/Platform.h" |
| #include "public/platform/WebThread.h" |
| #include "public/platform/WebTraceLocation.h" |
| +#include "third_party/skia/include/encode/SkJpegEncoder.h" |
| +#include "third_party/skia/include/encode/SkPngEncoder.h" |
| namespace blink { |
| @@ -29,7 +29,6 @@ namespace { |
| const double kSlackBeforeDeadline = |
| 0.001; // a small slack period between deadline and current time for safety |
| -const int kNumChannelsPng = 4; |
| // The encoding task is highly likely to switch from idle task to alternative |
| // code path when the startTimeoutDelay is set to be below 150ms. As we want the |
| @@ -191,15 +190,19 @@ CanvasAsyncBlobCreator::CanvasAsyncBlobCreator(DOMUint8ClampedArray* data, |
| ScriptPromiseResolver* resolver) |
| : data_(data), |
| document_(document), |
| - size_(size), |
| mime_type_(mime_type), |
| start_time_(start_time), |
| elapsed_time_(0), |
| callback_(callback), |
| script_promise_resolver_(resolver) { |
| - DCHECK(data_->length() == (unsigned)(size.Height() * size.Width() * 4)); |
| + size_t rowBytes = size.Width() * 4; |
| + DCHECK(data_->length() == (unsigned)(size.Height() * rowBytes)); |
| encoded_image_ = WTF::WrapUnique(new Vector<unsigned char>()); |
| - pixel_row_stride_ = size.Width() * kNumChannelsPng; |
| + 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
|
| + SkImageInfo info = |
| + SkImageInfo::Make(size.Width(), size.Height(), kRGBA_8888_SkColorType, |
| + kUnpremul_SkAlphaType, nullptr); |
| + src_data_.reset(info, data_->Data(), rowBytes); |
| idle_task_status_ = kIdleTaskNotSupported; |
| num_rows_completed_ = 0; |
| if (document) { |
| @@ -232,7 +235,8 @@ void CanvasAsyncBlobCreator::ScheduleAsyncBlobCreation(const double& quality) { |
| // When OffscreenCanvas.convertToBlob() occurs on worker thread, |
| // we do not need to use background task runner to reduce load on main. |
| // So we just directly encode images on the worker thread. |
| - if (!ImageDataBuffer(size_, data_->Data()) |
| + IntSize size(src_data_.width(), src_data_.height()); |
| + if (!ImageDataBuffer(size, data_->Data()) |
| .EncodeImage("image/webp", quality, encoded_image_.get())) { |
| TaskRunnerHelper::Get(TaskType::kCanvasBlobSerialization, document_) |
| ->PostTask( |
| @@ -256,18 +260,7 @@ void CanvasAsyncBlobCreator::ScheduleAsyncBlobCreation(const double& quality) { |
| } |
| } else { |
| idle_task_status_ = kIdleTaskNotStarted; |
| - if (mime_type_ == kMimeTypePng) { |
| - this->ScheduleInitiatePngEncoding(); |
| - } else if (mime_type_ == kMimeTypeJpeg) { |
| - this->ScheduleInitiateJpegEncoding(quality); |
| - } else { |
| - // Progressive encoding is only applicable to png and jpeg image format, |
| - // and thus idle tasks scheduling can only be applied to these image |
| - // formats. |
| - // TODO(xlai): Progressive encoding on webp image formats |
| - // (crbug.com/571399) |
| - NOTREACHED(); |
| - } |
| + this->ScheduleInitiateEncoding(quality); |
| // We post the below task to check if the above idle task isn't late. |
| // There's no risk of concurrency as both tasks are on the same thread. |
| @@ -279,18 +272,17 @@ void CanvasAsyncBlobCreator::ScheduleAsyncBlobCreation(const double& quality) { |
| } |
| } |
| -void CanvasAsyncBlobCreator::ScheduleInitiateJpegEncoding( |
| - const double& quality) { |
| +void CanvasAsyncBlobCreator::ScheduleInitiateEncoding(double quality) { |
| schedule_initiate_start_time_ = WTF::MonotonicallyIncreasingTime(); |
| Platform::Current()->CurrentThread()->Scheduler()->PostIdleTask( |
| - BLINK_FROM_HERE, WTF::Bind(&CanvasAsyncBlobCreator::InitiateJpegEncoding, |
| + BLINK_FROM_HERE, WTF::Bind(&CanvasAsyncBlobCreator::InitiateEncoding, |
| WrapPersistent(this), quality)); |
| } |
| -void CanvasAsyncBlobCreator::InitiateJpegEncoding(const double& quality, |
| - double deadline_seconds) { |
| +void CanvasAsyncBlobCreator::InitiateEncoding(double quality, |
| + double deadline_seconds) { |
| RecordElapsedTimeHistogram( |
| - kInitiateEncodingDelay, kMimeTypeJpeg, |
| + kInitiateEncodingDelay, mime_type_, |
| WTF::MonotonicallyIncreasingTime() - schedule_initiate_start_time_); |
| if (idle_task_status_ == kIdleTaskSwitchedToImmediateTask) { |
| return; |
| @@ -299,64 +291,41 @@ void CanvasAsyncBlobCreator::InitiateJpegEncoding(const double& quality, |
| DCHECK(idle_task_status_ == kIdleTaskNotStarted); |
| idle_task_status_ = kIdleTaskStarted; |
| - if (!InitializeJpegStruct(quality)) { |
| + if (!InitializeEncoder(quality)) { |
| idle_task_status_ = kIdleTaskFailed; |
| return; |
| } |
| - this->IdleEncodeRowsJpeg(deadline_seconds); |
| -} |
| - |
| -void CanvasAsyncBlobCreator::ScheduleInitiatePngEncoding() { |
| - schedule_initiate_start_time_ = WTF::MonotonicallyIncreasingTime(); |
| - Platform::Current()->CurrentThread()->Scheduler()->PostIdleTask( |
| - BLINK_FROM_HERE, WTF::Bind(&CanvasAsyncBlobCreator::InitiatePngEncoding, |
| - WrapPersistent(this))); |
| -} |
| - |
| -void CanvasAsyncBlobCreator::InitiatePngEncoding(double deadline_seconds) { |
| - RecordElapsedTimeHistogram( |
| - kInitiateEncodingDelay, kMimeTypePng, |
| - WTF::MonotonicallyIncreasingTime() - schedule_initiate_start_time_); |
| - if (idle_task_status_ == kIdleTaskSwitchedToImmediateTask) { |
| - return; |
| - } |
| - |
| - DCHECK(idle_task_status_ == kIdleTaskNotStarted); |
| - idle_task_status_ = kIdleTaskStarted; |
| - if (!InitializePngStruct()) { |
| - idle_task_status_ = kIdleTaskFailed; |
| - return; |
| - } |
| - this->IdleEncodeRowsPng(deadline_seconds); |
| + this->IdleEncodeRows(deadline_seconds); |
| } |
| -void CanvasAsyncBlobCreator::IdleEncodeRowsPng(double deadline_seconds) { |
| +void CanvasAsyncBlobCreator::IdleEncodeRows(double deadline_seconds) { |
| if (idle_task_status_ == kIdleTaskSwitchedToImmediateTask) { |
| return; |
| } |
| double start_time = WTF::MonotonicallyIncreasingTime(); |
| - unsigned char* input_pixels = |
| - data_->Data() + pixel_row_stride_ * num_rows_completed_; |
| - for (int y = num_rows_completed_; y < size_.Height(); ++y) { |
| + for (int y = num_rows_completed_; y < src_data_.height(); ++y) { |
| if (IsDeadlineNearOrPassed(deadline_seconds)) { |
| num_rows_completed_ = y; |
| elapsed_time_ += (WTF::MonotonicallyIncreasingTime() - start_time); |
| Platform::Current()->CurrentThread()->Scheduler()->PostIdleTask( |
| - BLINK_FROM_HERE, WTF::Bind(&CanvasAsyncBlobCreator::IdleEncodeRowsPng, |
| + BLINK_FROM_HERE, WTF::Bind(&CanvasAsyncBlobCreator::IdleEncodeRows, |
| WrapPersistent(this))); |
| return; |
| } |
| - PNGImageEncoder::WriteOneRowToPng(input_pixels, png_encoder_state_.get()); |
| - input_pixels += pixel_row_stride_; |
| + |
| + if (!encoder_->encodeRows(1)) { |
| + idle_task_status_ = kIdleTaskFailed; |
| + this->CreateNullAndReturnResult(); |
| + return; |
| + } |
| } |
| - num_rows_completed_ = size_.Height(); |
| - PNGImageEncoder::FinalizePng(png_encoder_state_.get()); |
| + num_rows_completed_ = src_data_.height(); |
| idle_task_status_ = kIdleTaskCompleted; |
| elapsed_time_ += (WTF::MonotonicallyIncreasingTime() - start_time); |
| - RecordElapsedTimeHistogram(kIdleEncodeDuration, kMimeTypePng, elapsed_time_); |
| + RecordElapsedTimeHistogram(kIdleEncodeDuration, mime_type_, elapsed_time_); |
| if (IsDeadlineNearOrPassed(deadline_seconds)) { |
| TaskRunnerHelper::Get(TaskType::kCanvasBlobSerialization, document_) |
| ->PostTask(BLINK_FROM_HERE, |
| @@ -367,52 +336,18 @@ void CanvasAsyncBlobCreator::IdleEncodeRowsPng(double deadline_seconds) { |
| } |
| } |
| -void CanvasAsyncBlobCreator::IdleEncodeRowsJpeg(double deadline_seconds) { |
| - if (idle_task_status_ == kIdleTaskSwitchedToImmediateTask) { |
| - return; |
| - } |
| - |
| - double start_time = WTF::MonotonicallyIncreasingTime(); |
| - num_rows_completed_ = JPEGImageEncoder::ProgressiveEncodeRowsJpegHelper( |
| - jpeg_encoder_state_.get(), data_->Data(), num_rows_completed_, |
| - kSlackBeforeDeadline, deadline_seconds); |
| - elapsed_time_ += (WTF::MonotonicallyIncreasingTime() - start_time); |
| - if (num_rows_completed_ == size_.Height()) { |
| - idle_task_status_ = kIdleTaskCompleted; |
| - RecordElapsedTimeHistogram(kIdleEncodeDuration, kMimeTypeJpeg, |
| - elapsed_time_); |
| - |
| - if (IsDeadlineNearOrPassed(deadline_seconds)) { |
| - TaskRunnerHelper::Get(TaskType::kCanvasBlobSerialization, document_) |
| - ->PostTask( |
| - BLINK_FROM_HERE, |
| - WTF::Bind(&CanvasAsyncBlobCreator::CreateBlobAndReturnResult, |
| - WrapPersistent(this))); |
| - } else { |
| - this->CreateBlobAndReturnResult(); |
| - } |
| - } else if (num_rows_completed_ == |
| - JPEGImageEncoder::kProgressiveEncodeFailed) { |
| - idle_task_status_ = kIdleTaskFailed; |
| - this->CreateNullAndReturnResult(); |
| - } else { |
| - Platform::Current()->CurrentThread()->Scheduler()->PostIdleTask( |
| - BLINK_FROM_HERE, WTF::Bind(&CanvasAsyncBlobCreator::IdleEncodeRowsJpeg, |
| - WrapPersistent(this))); |
| - } |
| -} |
| - |
| -void CanvasAsyncBlobCreator::ForceEncodeRowsPngOnCurrentThread() { |
| +void CanvasAsyncBlobCreator::ForceEncodeRowsOnCurrentThread() { |
| DCHECK(idle_task_status_ == kIdleTaskSwitchedToImmediateTask); |
| // Continue encoding from the last completed row |
| - unsigned char* input_pixels = |
| - data_->Data() + pixel_row_stride_ * num_rows_completed_; |
| - for (int y = num_rows_completed_; y < size_.Height(); ++y) { |
| - PNGImageEncoder::WriteOneRowToPng(input_pixels, png_encoder_state_.get()); |
| - input_pixels += pixel_row_stride_; |
| + for (int y = num_rows_completed_; y < src_data_.height(); ++y) { |
| + if (!encoder_->encodeRows(1)) { |
| + idle_task_status_ = kIdleTaskFailed; |
| + this->CreateNullAndReturnResult(); |
| + return; |
| + } |
| } |
| - PNGImageEncoder::FinalizePng(png_encoder_state_.get()); |
| + num_rows_completed_ = src_data_.height(); |
| if (IsMainThread()) { |
| this->CreateBlobAndReturnResult(); |
| @@ -427,30 +362,6 @@ void CanvasAsyncBlobCreator::ForceEncodeRowsPngOnCurrentThread() { |
| this->SignalAlternativeCodePathFinishedForTesting(); |
| } |
| -void CanvasAsyncBlobCreator::ForceEncodeRowsJpegOnCurrentThread() { |
| - DCHECK(idle_task_status_ == kIdleTaskSwitchedToImmediateTask); |
| - |
| - // Continue encoding from the last completed row |
| - void (CanvasAsyncBlobCreator::*function_to_be_called)(void); |
| - if (JPEGImageEncoder::EncodeWithPreInitializedState( |
| - std::move(jpeg_encoder_state_), data_->Data(), num_rows_completed_)) { |
| - function_to_be_called = &CanvasAsyncBlobCreator::CreateBlobAndReturnResult; |
| - } else { |
| - function_to_be_called = &CanvasAsyncBlobCreator::CreateNullAndReturnResult; |
| - } |
| - |
| - if (IsMainThread()) { |
| - (this->*function_to_be_called)(); |
| - } else { |
| - TaskRunnerHelper::Get(TaskType::kCanvasBlobSerialization, document_) |
| - ->PostTask(BLINK_FROM_HERE, |
| - CrossThreadBind(function_to_be_called, |
| - WrapCrossThreadPersistent(this))); |
| - } |
| - |
| - this->SignalAlternativeCodePathFinishedForTesting(); |
| -} |
| - |
| void CanvasAsyncBlobCreator::CreateBlobAndReturnResult() { |
| RecordIdleTaskStatusHistogram(idle_task_status_); |
| RecordElapsedTimeHistogram(kToBlobDuration, mime_type_, |
| @@ -492,7 +403,8 @@ void CanvasAsyncBlobCreator::EncodeImageOnEncoderThread(double quality) { |
| DCHECK(!IsMainThread()); |
| DCHECK(mime_type_ == kMimeTypeWebp); |
| - if (!ImageDataBuffer(size_, data_->Data()) |
| + IntSize size(src_data_.width(), src_data_.height()); |
| + if (!ImageDataBuffer(size, data_->Data()) |
| .EncodeImage("image/webp", quality, encoded_image_.get())) { |
| parent_frame_task_runner_->Get(TaskType::kCanvasBlobSerialization) |
| ->PostTask( |
| @@ -509,24 +421,28 @@ void CanvasAsyncBlobCreator::EncodeImageOnEncoderThread(double quality) { |
| WrapCrossThreadPersistent(this))); |
| } |
| -bool CanvasAsyncBlobCreator::InitializePngStruct() { |
| - png_encoder_state_ = |
| - PNGImageEncoderState::Create(size_, encoded_image_.get()); |
| - if (!png_encoder_state_) { |
| - this->CreateNullAndReturnResult(); |
| - return false; |
| +bool CanvasAsyncBlobCreator::InitializeEncoder(double quality) { |
| + 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.
|
| + SkJpegEncoder::Options options; |
| + options.fQuality = static_cast<int>(quality * 100 + 0.5); |
| + options.fAlphaOption = SkJpegEncoder::AlphaOption::kBlendOnBlack; |
| + options.fBlendBehavior = SkTransferFunctionBehavior::kIgnore; |
| + encoder_ = SkJpegEncoder::Make(dst_.get(), src_data_, options); |
| + } else { |
| + // Progressive encoding is only applicable to png and jpeg image format, |
| + // and thus idle tasks scheduling can only be applied to these image |
| + // formats. |
| + // TODO(xlai): Progressive encoding on webp image formats |
| + // (crbug.com/571399) |
| + DCHECK_EQ(kMimeTypePng, mime_type_); |
| + SkPngEncoder::Options options; |
| + options.fFilterFlags = SkPngEncoder::FilterFlag::kSub; |
| + options.fZLibLevel = 3; |
| + options.fUnpremulBehavior = SkTransferFunctionBehavior::kIgnore; |
| + encoder_ = SkPngEncoder::Make(dst_.get(), src_data_, options); |
| } |
| - return true; |
| -} |
| -bool CanvasAsyncBlobCreator::InitializeJpegStruct(double quality) { |
| - jpeg_encoder_state_ = |
| - JPEGImageEncoderState::Create(size_, quality, encoded_image_.get()); |
| - if (!jpeg_encoder_state_) { |
| - this->CreateNullAndReturnResult(); |
| - return false; |
| - } |
| - return true; |
| + return encoder_.get(); |
| } |
| void CanvasAsyncBlobCreator::IdleTaskStartTimeoutEvent(double quality) { |
| @@ -544,31 +460,16 @@ void CanvasAsyncBlobCreator::IdleTaskStartTimeoutEvent(double quality) { |
| idle_task_status_ = kIdleTaskSwitchedToImmediateTask; |
| SignalTaskSwitchInStartTimeoutEventForTesting(); |
| - if (mime_type_ == kMimeTypePng) { |
| - if (InitializePngStruct()) { |
| - TaskRunnerHelper::Get(TaskType::kCanvasBlobSerialization, document_) |
| - ->PostTask( |
| - BLINK_FROM_HERE, |
| - WTF::Bind( |
| - &CanvasAsyncBlobCreator::ForceEncodeRowsPngOnCurrentThread, |
| - WrapPersistent(this))); |
| - } else { |
| - // Failing in initialization of png struct |
| - this->SignalAlternativeCodePathFinishedForTesting(); |
| - } |
| + DCHECK(mime_type_ == kMimeTypePng || mime_type_ == kMimeTypeJpeg); |
| + if (InitializeEncoder(quality)) { |
| + TaskRunnerHelper::Get(TaskType::kCanvasBlobSerialization, document_) |
| + ->PostTask( |
| + BLINK_FROM_HERE, |
| + WTF::Bind(&CanvasAsyncBlobCreator::ForceEncodeRowsOnCurrentThread, |
| + WrapPersistent(this))); |
| } else { |
| - DCHECK(mime_type_ == kMimeTypeJpeg); |
| - if (InitializeJpegStruct(quality)) { |
| - TaskRunnerHelper::Get(TaskType::kCanvasBlobSerialization, document_) |
| - ->PostTask( |
| - BLINK_FROM_HERE, |
| - WTF::Bind( |
| - &CanvasAsyncBlobCreator::ForceEncodeRowsJpegOnCurrentThread, |
| - WrapPersistent(this))); |
| - } else { |
| - // Failing in initialization of jpeg struct |
| - this->SignalAlternativeCodePathFinishedForTesting(); |
| - } |
| + // Failing in initialization of encoder |
| + this->SignalAlternativeCodePathFinishedForTesting(); |
| } |
| } else { |
| DCHECK(idle_task_status_ == kIdleTaskFailed || |
| @@ -585,22 +486,12 @@ void CanvasAsyncBlobCreator::IdleTaskCompleteTimeoutEvent() { |
| idle_task_status_ = kIdleTaskSwitchedToImmediateTask; |
| SignalTaskSwitchInCompleteTimeoutEventForTesting(); |
| - if (mime_type_ == kMimeTypePng) { |
| - TaskRunnerHelper::Get(TaskType::kCanvasBlobSerialization, document_) |
| - ->PostTask( |
| - BLINK_FROM_HERE, |
| - WTF::Bind( |
| - &CanvasAsyncBlobCreator::ForceEncodeRowsPngOnCurrentThread, |
| - WrapPersistent(this))); |
| - } else { |
| - DCHECK(mime_type_ == kMimeTypeJpeg); |
| - TaskRunnerHelper::Get(TaskType::kCanvasBlobSerialization, document_) |
| - ->PostTask( |
| - BLINK_FROM_HERE, |
| - WTF::Bind( |
| - &CanvasAsyncBlobCreator::ForceEncodeRowsJpegOnCurrentThread, |
| - WrapPersistent(this))); |
| - } |
| + DCHECK(mime_type_ == kMimeTypePng || mime_type_ == kMimeTypeJpeg); |
| + TaskRunnerHelper::Get(TaskType::kCanvasBlobSerialization, document_) |
| + ->PostTask( |
| + BLINK_FROM_HERE, |
| + WTF::Bind(&CanvasAsyncBlobCreator::ForceEncodeRowsOnCurrentThread, |
| + WrapPersistent(this))); |
| } else { |
| DCHECK(idle_task_status_ == kIdleTaskFailed || |
| idle_task_status_ == kIdleTaskCompleted); |