| 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..2426d280ba0f46cff612e2834d89d143af2d922e 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"
|
| @@ -29,7 +27,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 +188,17 @@ 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));
|
| - encoded_image_ = WTF::WrapUnique(new Vector<unsigned char>());
|
| - pixel_row_stride_ = size.Width() * kNumChannelsPng;
|
| + size_t rowBytes = size.Width() * 4;
|
| + DCHECK(data_->length() == (unsigned)(size.Height() * rowBytes));
|
| + 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,8 +231,9 @@ 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())
|
| - .EncodeImage("image/webp", quality, encoded_image_.get())) {
|
| + IntSize size(src_data_.width(), src_data_.height());
|
| + if (!ImageDataBuffer(size, data_->Data())
|
| + .EncodeImage("image/webp", quality, &encoded_image_)) {
|
| TaskRunnerHelper::Get(TaskType::kCanvasBlobSerialization, document_)
|
| ->PostTask(
|
| BLINK_FROM_HERE,
|
| @@ -256,18 +256,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 +268,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 +287,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 +332,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,38 +358,13 @@ 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_,
|
| WTF::MonotonicallyIncreasingTime() - start_time_);
|
|
|
| - Blob* result_blob =
|
| - Blob::Create(encoded_image_->data(), encoded_image_->size(),
|
| - ConvertMimeTypeEnumToString(mime_type_));
|
| + Blob* result_blob = Blob::Create(encoded_image_.data(), encoded_image_.size(),
|
| + ConvertMimeTypeEnumToString(mime_type_));
|
| if (function_type_ == kHTMLCanvasToBlobCallback) {
|
| TaskRunnerHelper::Get(TaskType::kCanvasBlobSerialization, document_)
|
| ->PostTask(BLINK_FROM_HERE, WTF::Bind(&BlobCallback::handleEvent,
|
| @@ -492,8 +398,9 @@ void CanvasAsyncBlobCreator::EncodeImageOnEncoderThread(double quality) {
|
| DCHECK(!IsMainThread());
|
| DCHECK(mime_type_ == kMimeTypeWebp);
|
|
|
| - if (!ImageDataBuffer(size_, data_->Data())
|
| - .EncodeImage("image/webp", quality, encoded_image_.get())) {
|
| + IntSize size(src_data_.width(), src_data_.height());
|
| + if (!ImageDataBuffer(size, data_->Data())
|
| + .EncodeImage("image/webp", quality, &encoded_image_)) {
|
| parent_frame_task_runner_->Get(TaskType::kCanvasBlobSerialization)
|
| ->PostTask(
|
| BLINK_FROM_HERE,
|
| @@ -509,24 +416,31 @@ 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 (mime_type_ == kMimeTypeJpeg) {
|
| + SkJpegEncoder::Options options;
|
| + options.fQuality = ImageEncoder::ComputeJpegQuality(quality);
|
| + options.fAlphaOption = SkJpegEncoder::AlphaOption::kBlendOnBlack;
|
| + options.fBlendBehavior = SkTransferFunctionBehavior::kIgnore;
|
| + if (options.fQuality == 100) {
|
| + options.fDownsample = SkJpegEncoder::Downsample::k444;
|
| + }
|
| + encoder_ = ImageEncoder::Create(&encoded_image_, 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_ = ImageEncoder::Create(&encoded_image_, 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 +458,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 +484,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);
|
|
|