blob: e3dc53e2f88956610b99d58c6047b0644c626f0d [file] [log] [blame]
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "config.h"
#include "CanvasAsyncBlobCreator.h"
#include "core/fileapi/File.h"
#include "platform/Task.h"
#include "platform/ThreadSafeFunctional.h"
#include "platform/graphics/ImageBuffer.h"
#include "platform/image-encoders/skia/PNGImageEncoder.h"
#include "public/platform/Platform.h"
#include "public/platform/WebScheduler.h"
#include "public/platform/WebTaskRunner.h"
#include "public/platform/WebThread.h"
#include "public/platform/WebTraceLocation.h"
#include "wtf/Functional.h"
namespace blink {
namespace {
const double SlackBeforeDeadline = 0.001; // a small slack period between deadline and current time for safety
const int NumChannelsPng = 4;
bool isDeadlineNearOrPassed(double deadlineSeconds)
{
return (deadlineSeconds - SlackBeforeDeadline - Platform::current()->monotonicallyIncreasingTimeSeconds() <= 0);
}
} // anonymous namespace
PassRefPtr<CanvasAsyncBlobCreator> CanvasAsyncBlobCreator::create(PassRefPtr<DOMUint8ClampedArray> unpremultipliedRGBAImageData, const String& mimeType, const IntSize& size, FileCallback* callback)
{
return adoptRef(new CanvasAsyncBlobCreator(unpremultipliedRGBAImageData, mimeType, size, callback));
}
CanvasAsyncBlobCreator::CanvasAsyncBlobCreator(PassRefPtr<DOMUint8ClampedArray> data, const String& mimeType, const IntSize& size, FileCallback* callback)
: m_data(data)
, m_size(size)
, m_mimeType(mimeType)
, m_callback(callback)
{
ASSERT(m_data->length() == (unsigned) (size.height() * size.width() * 4));
m_encodedImage = adoptPtr(new Vector<unsigned char>());
m_pixelRowStride = size.width() * NumChannelsPng;
m_numRowsCompleted = 0;
}
CanvasAsyncBlobCreator::~CanvasAsyncBlobCreator() { }
void CanvasAsyncBlobCreator::scheduleAsyncBlobCreation(bool canUseIdlePeriodScheduling, double quality)
{
// TODO: async blob creation should be supported in worker_pool threads as well. but right now blink does not have that
ASSERT(isMainThread());
// Make self-reference to keep this object alive until the final task completes
m_selfRef = this;
if (canUseIdlePeriodScheduling) {
ASSERT(m_mimeType == "image/png");
Platform::current()->mainThread()->scheduler()->postIdleTask(BLINK_FROM_HERE, WTF::bind<double>(&CanvasAsyncBlobCreator::initiatePngEncoding, this));
} else {
getToBlobThreadInstance()->taskRunner()->postTask(BLINK_FROM_HERE, new Task(threadSafeBind(&CanvasAsyncBlobCreator::encodeImageOnAsyncThread, AllowCrossThreadAccess(this), quality)));
}
}
void CanvasAsyncBlobCreator::initiatePngEncoding(double deadlineSeconds)
{
m_encoderState = PNGImageEncoderState::create(m_size, m_encodedImage.get());
if (!m_encoderState) {
Platform::current()->mainThread()->taskRunner()->postTask(BLINK_FROM_HERE, bind(&FileCallback::handleEvent, m_callback, nullptr));
m_selfRef.clear();
return;
}
CanvasAsyncBlobCreator::idleEncodeRowsPng(deadlineSeconds);
}
void CanvasAsyncBlobCreator::scheduleIdleEncodeRowsPng()
{
Platform::current()->currentThread()->scheduler()->postIdleTask(BLINK_FROM_HERE, WTF::bind<double>(&CanvasAsyncBlobCreator::idleEncodeRowsPng, this));
}
void CanvasAsyncBlobCreator::idleEncodeRowsPng(double deadlineSeconds)
{
unsigned char* inputPixels = m_data->data() + m_pixelRowStride * m_numRowsCompleted;
for (int y = m_numRowsCompleted; y < m_size.height(); ++y) {
if (isDeadlineNearOrPassed(deadlineSeconds)) {
m_numRowsCompleted = y;
CanvasAsyncBlobCreator::scheduleIdleEncodeRowsPng();
return;
}
PNGImageEncoder::writeOneRowToPng(inputPixels, m_encoderState.get());
inputPixels += m_pixelRowStride;
}
m_numRowsCompleted = m_size.height();
PNGImageEncoder::finalizePng(m_encoderState.get());
if (isDeadlineNearOrPassed(deadlineSeconds)) {
Platform::current()->mainThread()->taskRunner()->postTask(BLINK_FROM_HERE, bind(&CanvasAsyncBlobCreator::createBlobAndCall, this));
} else {
this->createBlobAndCall();
}
}
void CanvasAsyncBlobCreator::createBlobAndCall()
{
File* resultBlob = File::create(m_encodedImage->data(), m_encodedImage->size(), m_mimeType);
Platform::current()->mainThread()->taskRunner()->postTask(BLINK_FROM_HERE, bind(&FileCallback::handleEvent, m_callback, resultBlob));
m_selfRef.clear(); // self-destruct once job is done.
}
void CanvasAsyncBlobCreator::encodeImageOnAsyncThread(double quality)
{
ASSERT(!isMainThread());
if (!ImageDataBuffer(m_size, m_data->data()).encodeImage(m_mimeType, quality, m_encodedImage.get())) {
Platform::current()->mainThread()->taskRunner()->postTask(BLINK_FROM_HERE, bind(&FileCallback::handleEvent, m_callback, nullptr));
} else {
Platform::current()->mainThread()->taskRunner()->postTask(BLINK_FROM_HERE, threadSafeBind(&CanvasAsyncBlobCreator::createBlobAndCall, AllowCrossThreadAccess(this)));
}
}
WebThread* CanvasAsyncBlobCreator::getToBlobThreadInstance()
{
DEFINE_STATIC_LOCAL(OwnPtr<WebThread>, s_toBlobThread, ());
if (!s_toBlobThread) {
s_toBlobThread = adoptPtr(Platform::current()->createThread("Async toBlob"));
}
return s_toBlobThread.get();
}
} // namespace blink