blob: f5dc56f1ab7224d176d08aa43a23f8c1d9330bfc [file] [log] [blame]
// Copyright 2016 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 "modules/offscreencanvas2d/OffscreenCanvasRenderingContext2D.h"
#include "bindings/modules/v8/OffscreenCanvasRenderingContext2DOrWebGLRenderingContextOrWebGL2RenderingContext.h"
#include "core/frame/ImageBitmap.h"
#include "core/frame/Settings.h"
#include "core/workers/WorkerGlobalScope.h"
#include "core/workers/WorkerSettings.h"
#include "platform/graphics/ImageBuffer.h"
#include "platform/graphics/StaticBitmapImage.h"
#include "platform/graphics/UnacceleratedImageBufferSurface.h"
#include "platform/graphics/gpu/AcceleratedImageBufferSurface.h"
#include "wtf/Assertions.h"
#include "wtf/CurrentTime.h"
#define UNIMPLEMENTED ASSERT_NOT_REACHED
namespace blink {
OffscreenCanvasRenderingContext2D::~OffscreenCanvasRenderingContext2D() {}
OffscreenCanvasRenderingContext2D::OffscreenCanvasRenderingContext2D(
ScriptState* scriptState,
OffscreenCanvas* canvas,
const CanvasContextCreationAttributes& attrs)
: CanvasRenderingContext(nullptr, canvas, attrs) {
ExecutionContext* executionContext = scriptState->getExecutionContext();
if (executionContext->isDocument()) {
if (toDocument(executionContext)->settings()->disableReadingFromCanvas())
canvas->setDisableReadingFromCanvasTrue();
return;
}
WorkerSettings* workerSettings =
toWorkerGlobalScope(executionContext)->workerSettings();
if (workerSettings && workerSettings->disableReadingFromCanvas())
canvas->setDisableReadingFromCanvasTrue();
}
DEFINE_TRACE(OffscreenCanvasRenderingContext2D) {
CanvasRenderingContext::trace(visitor);
BaseRenderingContext2D::trace(visitor);
}
void OffscreenCanvasRenderingContext2D::commit(ScriptState* scriptState,
ExceptionState& exceptionState) {
UseCounter::Feature feature = UseCounter::OffscreenCanvasCommit2D;
UseCounter::count(scriptState->getExecutionContext(), feature);
if (!getOffscreenCanvas()->hasPlaceholderCanvas()) {
// If an OffscreenCanvas has no associated canvas Id, it indicates that
// it is not an OffscreenCanvas created by transfering control from html
// canvas.
exceptionState.throwDOMException(InvalidStateError,
"Commit() was called on a context whose "
"OffscreenCanvas is not associated with a "
"canvas element.");
return;
}
double commitStartTime = WTF::monotonicallyIncreasingTime();
RefPtr<StaticBitmapImage> image = this->transferToStaticBitmapImage();
getOffscreenCanvas()->getOrCreateFrameDispatcher()->dispatchFrame(
std::move(image), commitStartTime);
}
// BaseRenderingContext2D implementation
bool OffscreenCanvasRenderingContext2D::originClean() const {
return getOffscreenCanvas()->originClean();
}
void OffscreenCanvasRenderingContext2D::setOriginTainted() {
return getOffscreenCanvas()->setOriginTainted();
}
bool OffscreenCanvasRenderingContext2D::wouldTaintOrigin(
CanvasImageSource* source,
ExecutionContext* executionContext) {
if (executionContext->isWorkerGlobalScope()) {
// We only support passing in ImageBitmap and OffscreenCanvases as source
// images in drawImage() or createPattern() in a OffscreenCanvas2d in
// worker.
DCHECK(source->isImageBitmap() || source->isOffscreenCanvas());
}
return CanvasRenderingContext::wouldTaintOrigin(
source, executionContext->getSecurityOrigin());
}
int OffscreenCanvasRenderingContext2D::width() const {
return getOffscreenCanvas()->width();
}
int OffscreenCanvasRenderingContext2D::height() const {
return getOffscreenCanvas()->height();
}
bool OffscreenCanvasRenderingContext2D::hasImageBuffer() const {
return !!m_imageBuffer;
}
void OffscreenCanvasRenderingContext2D::reset() {
m_imageBuffer = nullptr;
BaseRenderingContext2D::reset();
}
ImageBuffer* OffscreenCanvasRenderingContext2D::imageBuffer() const {
if (!m_imageBuffer) {
IntSize surfaceSize(width(), height());
OpacityMode opacityMode = hasAlpha() ? NonOpaque : Opaque;
std::unique_ptr<ImageBufferSurface> surface;
if (RuntimeEnabledFeatures::accelerated2dCanvasEnabled()) {
surface.reset(
new AcceleratedImageBufferSurface(surfaceSize, opacityMode));
}
if (!surface || !surface->isValid()) {
surface.reset(new UnacceleratedImageBufferSurface(
surfaceSize, opacityMode, InitializeImagePixels));
}
OffscreenCanvasRenderingContext2D* nonConstThis =
const_cast<OffscreenCanvasRenderingContext2D*>(this);
nonConstThis->m_imageBuffer = ImageBuffer::create(std::move(surface));
if (m_needsMatrixClipRestore) {
restoreMatrixClipStack(m_imageBuffer->canvas());
nonConstThis->m_needsMatrixClipRestore = false;
}
}
return m_imageBuffer.get();
}
RefPtr<StaticBitmapImage>
OffscreenCanvasRenderingContext2D::transferToStaticBitmapImage() {
if (!imageBuffer())
return nullptr;
sk_sp<SkImage> skImage = m_imageBuffer->newSkImageSnapshot(
PreferAcceleration, SnapshotReasonTransferToImageBitmap);
RefPtr<StaticBitmapImage> image =
StaticBitmapImage::create(std::move(skImage));
image->setOriginClean(this->originClean());
return image;
}
ImageBitmap* OffscreenCanvasRenderingContext2D::transferToImageBitmap(
ScriptState* scriptState) {
UseCounter::Feature feature =
UseCounter::OffscreenCanvasTransferToImageBitmap2D;
UseCounter::count(scriptState->getExecutionContext(), feature);
RefPtr<StaticBitmapImage> image = transferToStaticBitmapImage();
if (!image)
return nullptr;
m_imageBuffer.reset(); // "Transfer" means no retained buffer
m_needsMatrixClipRestore = true;
return ImageBitmap::create(std::move(image));
}
PassRefPtr<Image> OffscreenCanvasRenderingContext2D::getImage(
AccelerationHint hint,
SnapshotReason reason) const {
if (!imageBuffer())
return nullptr;
sk_sp<SkImage> skImage = m_imageBuffer->newSkImageSnapshot(hint, reason);
RefPtr<StaticBitmapImage> image =
StaticBitmapImage::create(std::move(skImage));
return image;
}
ImageData* OffscreenCanvasRenderingContext2D::toImageData(
SnapshotReason reason) const {
if (!imageBuffer())
return nullptr;
sk_sp<SkImage> snapshot =
m_imageBuffer->newSkImageSnapshot(PreferNoAcceleration, reason);
ImageData* imageData = nullptr;
if (snapshot) {
imageData = ImageData::create(this->getOffscreenCanvas()->size());
SkImageInfo imageInfo =
SkImageInfo::Make(this->width(), this->height(), kRGBA_8888_SkColorType,
kUnpremul_SkAlphaType);
snapshot->readPixels(imageInfo, imageData->data()->data(),
imageInfo.minRowBytes(), 0, 0);
}
return imageData;
}
void OffscreenCanvasRenderingContext2D::setOffscreenCanvasGetContextResult(
OffscreenRenderingContext& result) {
result.setOffscreenCanvasRenderingContext2D(this);
}
bool OffscreenCanvasRenderingContext2D::parseColorOrCurrentColor(
Color& color,
const String& colorString) const {
return ::blink::parseColorOrCurrentColor(color, colorString, nullptr);
}
SkCanvas* OffscreenCanvasRenderingContext2D::drawingCanvas() const {
ImageBuffer* buffer = imageBuffer();
if (!buffer)
return nullptr;
return imageBuffer()->canvas();
}
SkCanvas* OffscreenCanvasRenderingContext2D::existingDrawingCanvas() const {
if (!m_imageBuffer)
return nullptr;
return m_imageBuffer->canvas();
}
void OffscreenCanvasRenderingContext2D::disableDeferral(DisableDeferralReason) {
}
AffineTransform OffscreenCanvasRenderingContext2D::baseTransform() const {
if (!m_imageBuffer)
return AffineTransform(); // identity
return m_imageBuffer->baseTransform();
}
void OffscreenCanvasRenderingContext2D::didDraw(const SkIRect& dirtyRect) {}
bool OffscreenCanvasRenderingContext2D::stateHasFilter() {
// TODO: crbug.com/593838 make hasFilter accept nullptr
// return state().hasFilter(nullptr, nullptr, IntSize(width(), height()),
// this);
return false;
}
sk_sp<SkImageFilter> OffscreenCanvasRenderingContext2D::stateGetFilter() {
// TODO: make getFilter accept nullptr
// return state().getFilter(nullptr, nullptr, IntSize(width(), height()),
// this);
return nullptr;
}
void OffscreenCanvasRenderingContext2D::validateStateStack() const {
#if DCHECK_IS_ON()
if (SkCanvas* skCanvas = existingDrawingCanvas()) {
DCHECK_EQ(static_cast<size_t>(skCanvas->getSaveCount()),
m_stateStack.size() + 1);
}
#endif
}
bool OffscreenCanvasRenderingContext2D::isContextLost() const {
return false;
}
bool OffscreenCanvasRenderingContext2D::isPaintable() const {
return this->imageBuffer();
}
}