2 * Copyright (C) 2004, 2006, 2007 Apple Inc. All rights reserved.
3 * Copyright (C) 2007 Alp Toker <alp@atoker.com>
4 * Copyright (C) 2010 Torch Mobile (Beijing) Co. Ltd. All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
16 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
19 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
23 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #include "core/html/HTMLCanvasElement.h"
31 #include "bindings/core/v8/ExceptionMessages.h"
32 #include "bindings/core/v8/ExceptionState.h"
33 #include "bindings/core/v8/ScriptController.h"
34 #include "core/HTMLNames.h"
35 #include "core/dom/Document.h"
36 #include "core/dom/ExceptionCode.h"
37 #include "core/fileapi/File.h"
38 #include "core/frame/LocalFrame.h"
39 #include "core/frame/Settings.h"
40 #include "core/html/ImageData.h"
41 #include "core/html/canvas/CanvasContextCreationAttributes.h"
42 #include "core/html/canvas/CanvasFontCache.h"
43 #include "core/html/canvas/CanvasRenderingContext.h"
44 #include "core/html/canvas/CanvasRenderingContextFactory.h"
45 #include "core/layout/LayoutHTMLCanvas.h"
46 #include "core/paint/DeprecatedPaintLayer.h"
47 #include "platform/MIMETypeRegistry.h"
48 #include "platform/RuntimeEnabledFeatures.h"
49 #include "platform/graphics/Canvas2DImageBufferSurface.h"
50 #include "platform/graphics/ExpensiveCanvasHeuristicParameters.h"
51 #include "platform/graphics/ImageBuffer.h"
52 #include "platform/graphics/RecordingImageBufferSurface.h"
53 #include "platform/graphics/StaticBitmapImage.h"
54 #include "platform/graphics/UnacceleratedImageBufferSurface.h"
55 #include "platform/graphics/gpu/AcceleratedImageBufferSurface.h"
56 #include "platform/transforms/AffineTransform.h"
57 #include "public/platform/Platform.h"
58 #include "public/platform/WebTraceLocation.h"
59 #include "wtf/Functional.h"
65 using namespace HTMLNames
;
69 // These values come from the WhatWG spec.
70 const int DefaultWidth
= 300;
71 const int DefaultHeight
= 150;
73 // Firefox limits width/height to 32767 pixels, but slows down dramatically before it
74 // reaches that limit. We limit by area instead, giving us larger maximum dimensions,
75 // in exchange for a smaller maximum canvas size.
76 const int MaxCanvasArea
= 32768 * 8192; // Maximum canvas area in CSS pixels
78 // In Skia, we will also limit width/height to 32767.
79 const int MaxSkiaDim
= 32767; // Maximum width/height in CSS pixels.
81 bool canCreateImageBuffer(const IntSize
& size
)
85 if (size
.width() * size
.height() > MaxCanvasArea
)
87 if (size
.width() > MaxSkiaDim
|| size
.height() > MaxSkiaDim
)
92 PassRefPtr
<Image
> createTransparentImage(const IntSize
& size
)
94 ASSERT(canCreateImageBuffer(size
));
95 RefPtr
<SkSurface
> surface
= adoptRef(SkSurface::NewRasterN32Premul(size
.width(), size
.height()));
96 surface
->getCanvas()->clear(SK_ColorTRANSPARENT
);
97 return StaticBitmapImage::create(adoptRef(surface
->newImageSnapshot()));
102 DEFINE_EMPTY_DESTRUCTOR_WILL_BE_REMOVED(CanvasObserver
);
104 inline HTMLCanvasElement::HTMLCanvasElement(Document
& document
)
105 : HTMLElement(canvasTag
, document
)
106 , DocumentVisibilityObserver(document
)
107 , m_size(DefaultWidth
, DefaultHeight
)
108 , m_ignoreReset(false)
109 , m_accelerationDisabled(false)
110 , m_externallyAllocatedMemory(0)
111 , m_originClean(true)
112 , m_didFailToCreateImageBuffer(false)
113 , m_imageBufferIsClear(false)
115 setHasCustomStyleCallbacks();
118 DEFINE_NODE_FACTORY(HTMLCanvasElement
)
120 HTMLCanvasElement::~HTMLCanvasElement()
122 v8::Isolate::GetCurrent()->AdjustAmountOfExternalAllocatedMemory(-m_externallyAllocatedMemory
);
124 for (CanvasObserver
* canvasObserver
: m_observers
)
125 canvasObserver
->canvasDestroyed(this);
126 // Ensure these go away before the ImageBuffer.
131 void HTMLCanvasElement::parseAttribute(const QualifiedName
& name
, const AtomicString
& value
)
133 if (name
== widthAttr
|| name
== heightAttr
)
135 HTMLElement::parseAttribute(name
, value
);
138 LayoutObject
* HTMLCanvasElement::createLayoutObject(const ComputedStyle
& style
)
140 LocalFrame
* frame
= document().frame();
141 if (frame
&& frame
->script().canExecuteScripts(NotAboutToExecuteScript
))
142 return new LayoutHTMLCanvas(this);
143 return HTMLElement::createLayoutObject(style
);
146 void HTMLCanvasElement::didRecalcStyle(StyleRecalcChange
)
148 SkFilterQuality filterQuality
;
149 const ComputedStyle
* style
= ensureComputedStyle();
150 if (style
&& style
->imageRendering() == ImageRenderingPixelated
) {
151 filterQuality
= kNone_SkFilterQuality
;
153 filterQuality
= kLow_SkFilterQuality
;
157 m_context
->setFilterQuality(filterQuality
);
158 setNeedsCompositingUpdate();
159 } else if (hasImageBuffer()) {
160 m_imageBuffer
->setFilterQuality(filterQuality
);
164 Node::InsertionNotificationRequest
HTMLCanvasElement::insertedInto(ContainerNode
* node
)
166 setIsInCanvasSubtree(true);
167 return HTMLElement::insertedInto(node
);
170 void HTMLCanvasElement::addObserver(CanvasObserver
* observer
)
172 m_observers
.add(observer
);
175 void HTMLCanvasElement::removeObserver(CanvasObserver
* observer
)
177 m_observers
.remove(observer
);
180 void HTMLCanvasElement::setHeight(int value
)
182 setIntegralAttribute(heightAttr
, value
);
185 void HTMLCanvasElement::setWidth(int value
)
187 setIntegralAttribute(widthAttr
, value
);
190 HTMLCanvasElement::ContextFactoryVector
& HTMLCanvasElement::renderingContextFactories()
192 ASSERT(isMainThread());
193 DEFINE_STATIC_LOCAL(ContextFactoryVector
, s_contextFactories
, (CanvasRenderingContext::ContextTypeCount
));
194 return s_contextFactories
;
197 CanvasRenderingContextFactory
* HTMLCanvasElement::getRenderingContextFactory(int type
)
199 ASSERT(type
< CanvasRenderingContext::ContextTypeCount
);
200 return renderingContextFactories()[type
].get();
203 void HTMLCanvasElement::registerRenderingContextFactory(PassOwnPtr
<CanvasRenderingContextFactory
> renderingContextFactory
)
205 CanvasRenderingContext::ContextType type
= renderingContextFactory
->contextType();
206 ASSERT(type
< CanvasRenderingContext::ContextTypeCount
);
207 ASSERT(!renderingContextFactories()[type
]);
208 renderingContextFactories()[type
] = renderingContextFactory
;
211 ScriptValue
HTMLCanvasElement::getContext(ScriptState
* scriptState
, const String
& type
, const CanvasContextCreationAttributes
& attributes
)
213 CanvasRenderingContext
* context
= getCanvasRenderingContext(type
, attributes
);
215 return ScriptValue::createNull(scriptState
);
217 return ScriptValue(scriptState
, toV8(context
, scriptState
->context()->Global(), scriptState
->isolate()));
220 CanvasRenderingContext
* HTMLCanvasElement::getCanvasRenderingContext(const String
& type
, const CanvasContextCreationAttributes
& attributes
)
222 CanvasRenderingContext::ContextType contextType
= CanvasRenderingContext::contextTypeFromId(type
);
225 if (contextType
== CanvasRenderingContext::ContextTypeCount
)
228 // Log the aliased context type used.
230 Platform::current()->histogramEnumeration("Canvas.ContextType", contextType
, CanvasRenderingContext::ContextTypeCount
);
232 contextType
= CanvasRenderingContext::resolveContextTypeAliases(contextType
);
234 CanvasRenderingContextFactory
* factory
= getRenderingContextFactory(contextType
);
238 // FIXME - The code depends on the context not going away once created, to prevent JS from
239 // seeing a dangling pointer. So for now we will disallow the context from being changed
240 // once it is created.
242 if (m_context
->contextType() == contextType
)
243 return m_context
.get();
245 factory
->onError(this, "Canvas has an existing context of a different type");
249 m_context
= factory
->create(this, attributes
, document());
253 if (m_context
->is3d()) {
254 const ComputedStyle
* style
= ensureComputedStyle();
256 m_context
->setFilterQuality(style
->imageRendering() == ImageRenderingPixelated
? kNone_SkFilterQuality
: kLow_SkFilterQuality
);
257 updateExternallyAllocatedMemory();
259 setNeedsCompositingUpdate();
261 return m_context
.get();
264 bool HTMLCanvasElement::shouldBeDirectComposited() const
266 return (m_context
&& m_context
->isAccelerated()) || (hasImageBuffer() && buffer()->isExpensiveToPaint());
269 bool HTMLCanvasElement::isPaintable() const
272 return canCreateImageBuffer(size());
276 void HTMLCanvasElement::didDraw(const FloatRect
& rect
)
280 m_imageBufferIsClear
= false;
283 layoutObject()->setMayNeedPaintInvalidation();
284 m_dirtyRect
.unite(rect
);
285 if (m_context
&& m_context
->is2d() && hasImageBuffer())
286 buffer()->didDraw(rect
);
287 notifyObserversCanvasChanged(rect
);
290 void HTMLCanvasElement::didFinalizeFrame()
292 if (m_dirtyRect
.isEmpty())
295 // Propagate the m_dirtyRect accumulated so far to the compositor
296 // before restarting with a blank dirty rect.
297 FloatRect
srcRect(0, 0, size().width(), size().height());
298 m_dirtyRect
.intersect(srcRect
);
299 LayoutBox
* ro
= layoutBox();
300 // Canvas content updates do not need to be propagated as
301 // paint invalidations if the canvas is accelerated, since
302 // the canvas contents are sent separately through a texture layer.
303 if (ro
&& (!m_context
|| !m_context
->isAccelerated())) {
304 LayoutRect
mappedDirtyRect(enclosingIntRect(mapRect(m_dirtyRect
, srcRect
, FloatRect(ro
->contentBoxRect()))));
305 // For querying DeprecatedPaintLayer::compositingState()
306 // FIXME: is this invalidation using the correct compositing state?
307 DisableCompositingQueryAsserts disabler
;
308 ro
->invalidatePaintRectangle(mappedDirtyRect
);
310 m_dirtyRect
= FloatRect();
313 void HTMLCanvasElement::restoreCanvasMatrixClipStack(SkCanvas
* canvas
) const
316 m_context
->restoreCanvasMatrixClipStack(canvas
);
319 void HTMLCanvasElement::doDeferredPaintInvalidation()
321 ASSERT(!m_dirtyRect
.isEmpty());
325 ASSERT(hasImageBuffer());
326 FloatRect
srcRect(0, 0, size().width(), size().height());
327 m_dirtyRect
.intersect(srcRect
);
328 LayoutBox
* lb
= layoutBox();
330 FloatRect mappedDirtyRect
= mapRect(m_dirtyRect
, srcRect
, FloatRect(lb
->contentBoxRect()));
331 if (m_context
->isAccelerated()) {
332 // Accelerated 2D canvases need the dirty rect to be expressed relative to the
333 // content box, as opposed to the layout box.
334 mappedDirtyRect
.move(-lb
->contentBoxOffset());
336 m_imageBuffer
->finalizeFrame(mappedDirtyRect
);
338 m_imageBuffer
->finalizeFrame(m_dirtyRect
);
341 ASSERT(m_dirtyRect
.isEmpty());
344 void HTMLCanvasElement::notifyObserversCanvasChanged(const FloatRect
& rect
)
346 for (CanvasObserver
* canvasObserver
: m_observers
)
347 canvasObserver
->canvasChanged(this, rect
);
350 void HTMLCanvasElement::reset()
355 m_dirtyRect
= FloatRect();
358 bool hadImageBuffer
= hasImageBuffer();
360 int w
= getAttribute(widthAttr
).toInt(&ok
);
364 int h
= getAttribute(heightAttr
).toInt(&ok
);
368 if (m_context
&& m_context
->is2d())
371 IntSize oldSize
= size();
372 IntSize
newSize(w
, h
);
374 // If the size of an existing buffer matches, we can just clear it instead of reallocating.
375 // This optimization is only done for 2D canvases for now.
376 if (hadImageBuffer
&& oldSize
== newSize
&& m_context
&& m_context
->is2d() && !buffer()->isRecording()) {
377 if (!m_imageBufferIsClear
) {
378 m_imageBufferIsClear
= true;
379 m_context
->clearRect(0, 0, width(), height());
384 setSurfaceSize(newSize
);
386 if (m_context
&& m_context
->is3d() && oldSize
!= size())
387 m_context
->reshape(width(), height());
389 if (LayoutObject
* layoutObject
= this->layoutObject()) {
390 if (layoutObject
->isCanvas()) {
391 if (oldSize
!= size()) {
392 toLayoutHTMLCanvas(layoutObject
)->canvasSizeChanged();
393 if (layoutBox() && layoutBox()->hasAcceleratedCompositing())
394 layoutBox()->contentChanged(CanvasChanged
);
397 layoutObject
->setShouldDoFullPaintInvalidation();
401 for (CanvasObserver
* canvasObserver
: m_observers
)
402 canvasObserver
->canvasResized(this);
405 bool HTMLCanvasElement::paintsIntoCanvasBuffer() const
409 if (!m_context
->isAccelerated())
412 if (layoutBox() && layoutBox()->hasAcceleratedCompositing())
418 void HTMLCanvasElement::paint(GraphicsContext
* context
, const LayoutRect
& r
)
420 // FIXME: crbug.com/438240; there is a bug with the new CSS blending and compositing feature.
423 if (!paintsIntoCanvasBuffer() && !document().printing())
426 m_context
->paintRenderingResultsToCanvas(FrontBuffer
);
427 if (hasImageBuffer()) {
428 if (!context
->contextDisabled()) {
429 SkXfermode::Mode compositeOperator
= !m_context
|| m_context
->hasAlpha() ? SkXfermode::kSrcOver_Mode
: SkXfermode::kSrc_Mode
;
430 buffer()->draw(context
, pixelSnappedIntRect(r
), 0, compositeOperator
);
433 // When alpha is false, we should draw to opaque black.
434 if (!m_context
->hasAlpha())
435 context
->fillRect(FloatRect(r
), Color(0, 0, 0));
438 if (is3D() && paintsIntoCanvasBuffer())
439 m_context
->markLayerComposited();
442 bool HTMLCanvasElement::is3D() const
444 return m_context
&& m_context
->is3d();
447 bool HTMLCanvasElement::isAnimated2D() const
449 return m_context
&& m_context
->is2d() && hasImageBuffer() && m_imageBuffer
->wasDrawnToAfterSnapshot();
452 void HTMLCanvasElement::setSurfaceSize(const IntSize
& size
)
455 m_didFailToCreateImageBuffer
= false;
456 discardImageBuffer();
458 if (m_context
&& m_context
->is2d() && m_context
->isContextLost()) {
459 m_context
->didSetSurfaceSize();
463 String
HTMLCanvasElement::toEncodingMimeType(const String
& mimeType
)
465 String lowercaseMimeType
= mimeType
.lower();
467 // FIXME: Make isSupportedImageMIMETypeForEncoding threadsafe (to allow this method to be used on a worker thread).
468 if (mimeType
.isNull() || !MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(lowercaseMimeType
))
469 lowercaseMimeType
= "image/png";
471 return lowercaseMimeType
;
474 const AtomicString
HTMLCanvasElement::imageSourceURL() const
476 return AtomicString(toDataURLInternal("image/png", 0, FrontBuffer
));
479 ImageData
* HTMLCanvasElement::toImageData(SourceDrawingBuffer sourceBuffer
) const
481 ImageData
* imageData
;
483 // Get non-premultiplied data because of inaccurate premultiplied alpha conversion of buffer()->toDataURL().
484 imageData
= m_context
->paintRenderingResultsToImageData(sourceBuffer
);
488 m_context
->paintRenderingResultsToCanvas(sourceBuffer
);
489 imageData
= ImageData::create(m_size
);
490 RefPtr
<SkImage
> snapshot
= buffer()->newSkImageSnapshot();
492 SkImageInfo imageInfo
= SkImageInfo::Make(width(), height(), kRGBA_8888_SkColorType
, kUnpremul_SkAlphaType
);
493 snapshot
->readPixels(imageInfo
, imageData
->data()->data(), imageInfo
.minRowBytes(), 0, 0);
498 imageData
= ImageData::create(m_size
);
503 ASSERT(m_context
->is2d());
504 RefPtr
<SkImage
> snapshot
= buffer()->newSkImageSnapshot();
506 SkImageInfo imageInfo
= SkImageInfo::Make(width(), height(), kRGBA_8888_SkColorType
, kUnpremul_SkAlphaType
);
507 snapshot
->readPixels(imageInfo
, imageData
->data()->data(), imageInfo
.minRowBytes(), 0, 0);
513 String
HTMLCanvasElement::toDataURLInternal(const String
& mimeType
, const double* quality
, SourceDrawingBuffer sourceBuffer
) const
516 return String("data:,");
518 String encodingMimeType
= toEncodingMimeType(mimeType
);
520 ImageData
* imageData
= toImageData(sourceBuffer
);
521 ScopedDisposal
<ImageData
> disposer(imageData
);
523 return ImageDataBuffer(imageData
->size(), imageData
->data()->data()).toDataURL(encodingMimeType
, quality
);
526 String
HTMLCanvasElement::toDataURL(const String
& mimeType
, const ScriptValue
& qualityArgument
, ExceptionState
& exceptionState
) const
528 if (!originClean()) {
529 exceptionState
.throwSecurityError("Tainted canvases may not be exported.");
533 double* qualityPtr
= nullptr;
534 if (!qualityArgument
.isEmpty()) {
535 v8::Local
<v8::Value
> v8Value
= qualityArgument
.v8Value();
536 if (v8Value
->IsNumber()) {
537 quality
= v8Value
.As
<v8::Number
>()->Value();
538 qualityPtr
= &quality
;
541 return toDataURLInternal(mimeType
, qualityPtr
, BackBuffer
);
544 void HTMLCanvasElement::toBlob(FileCallback
* callback
, const String
& mimeType
, const ScriptValue
& qualityArgument
, ExceptionState
& exceptionState
) const
546 if (!originClean()) {
547 exceptionState
.throwSecurityError("Tainted canvases may not be exported.");
551 File
* resultBlob
= nullptr;
552 if (!isPaintable()) {
553 // If the canvas element's bitmap has no pixels
558 double* qualityPtr
= nullptr;
559 if (!qualityArgument
.isEmpty()) {
560 v8::Local
<v8::Value
> v8Value
= qualityArgument
.v8Value();
561 if (v8Value
->IsNumber()) {
562 quality
= v8Value
.As
<v8::Number
>()->Value();
563 qualityPtr
= &quality
;
567 String encodingMimeType
= toEncodingMimeType(mimeType
);
569 ImageData
* imageData
= toImageData(BackBuffer
);
570 ScopedDisposal
<ImageData
> disposer(imageData
);
572 // Perform image encoding
573 Vector
<char> encodedImage
;
574 ImageDataBuffer(imageData
->size(), imageData
->data()->data()).encodeImage(encodingMimeType
, qualityPtr
, &encodedImage
);
575 resultBlob
= File::create(encodedImage
.data(), encodedImage
.size(), encodingMimeType
);
577 Platform::current()->mainThread()->taskRunner()->postTask(FROM_HERE
, bind(&FileCallback::handleEvent
, callback
, resultBlob
));
580 SecurityOrigin
* HTMLCanvasElement::securityOrigin() const
582 return document().securityOrigin();
585 bool HTMLCanvasElement::originClean() const
587 if (document().settings() && document().settings()->disableReadingFromCanvas())
589 return m_originClean
;
592 bool HTMLCanvasElement::shouldAccelerate(const IntSize
& size
) const
594 if (m_context
&& !m_context
->is2d())
597 if (m_accelerationDisabled
)
600 if (RuntimeEnabledFeatures::forceDisplayList2dCanvasEnabled())
603 Settings
* settings
= document().settings();
604 if (!settings
|| !settings
->accelerated2dCanvasEnabled())
607 int canvasPixelCount
= size
.width() * size
.height();
609 if (RuntimeEnabledFeatures::displayList2dCanvasEnabled()) {
611 // TODO(junov): re-enable this code once we solve the problem of recording
612 // GPU-backed images to an SkPicture for cross-context rendering crbug.com/490328
614 // If the compositor provides GPU acceleration to display list canvases, we
615 // prefer that over direct acceleration.
616 if (document().viewportDescription().matchesHeuristicsForGpuRasterization())
619 // If the GPU resources would be very expensive, prefer a display list.
620 if (canvasPixelCount
> ExpensiveCanvasHeuristicParameters::PreferDisplayListOverGpuSizeThreshold
)
624 // Do not use acceleration for small canvas.
625 if (canvasPixelCount
< settings
->minimumAccelerated2dCanvasSize())
628 if (!Platform::current()->canAccelerate2dCanvas())
634 class UnacceleratedSurfaceFactory
: public RecordingImageBufferFallbackSurfaceFactory
{
636 virtual PassOwnPtr
<ImageBufferSurface
> createSurface(const IntSize
& size
, OpacityMode opacityMode
)
638 return adoptPtr(new UnacceleratedImageBufferSurface(size
, opacityMode
));
641 virtual ~UnacceleratedSurfaceFactory() { }
644 bool HTMLCanvasElement::shouldUseDisplayList(const IntSize
& deviceSize
)
646 if (RuntimeEnabledFeatures::forceDisplayList2dCanvasEnabled())
649 if (!RuntimeEnabledFeatures::displayList2dCanvasEnabled())
655 PassOwnPtr
<ImageBufferSurface
> HTMLCanvasElement::createImageBufferSurface(const IntSize
& deviceSize
, int* msaaSampleCount
)
657 OpacityMode opacityMode
= !m_context
|| m_context
->hasAlpha() ? NonOpaque
: Opaque
;
659 *msaaSampleCount
= 0;
661 // If 3d, but the use of the canvas will be for non-accelerated content
662 // (such as -webkit-canvas, then then make a non-accelerated
663 // ImageBuffer. This means copying the internal Image will require a
664 // pixel readback, but that is unavoidable in this case.
665 // FIXME: Actually, avoid setting m_accelerationDisabled at all when
666 // doing GPU-based rasterization.
667 if (m_accelerationDisabled
)
668 return adoptPtr(new UnacceleratedImageBufferSurface(deviceSize
, opacityMode
));
669 return adoptPtr(new AcceleratedImageBufferSurface(deviceSize
, opacityMode
));
672 if (shouldAccelerate(deviceSize
)) {
673 if (document().settings())
674 *msaaSampleCount
= document().settings()->accelerated2dCanvasMSAASampleCount();
675 OwnPtr
<ImageBufferSurface
> surface
= adoptPtr(new Canvas2DImageBufferSurface(deviceSize
, opacityMode
, *msaaSampleCount
));
676 if (surface
->isValid())
677 return surface
.release();
680 OwnPtr
<RecordingImageBufferFallbackSurfaceFactory
> surfaceFactory
= adoptPtr(new UnacceleratedSurfaceFactory());
682 if (shouldUseDisplayList(deviceSize
)) {
683 OwnPtr
<ImageBufferSurface
> surface
= adoptPtr(new RecordingImageBufferSurface(deviceSize
, surfaceFactory
.release(), opacityMode
));
684 if (surface
->isValid())
685 return surface
.release();
686 surfaceFactory
= adoptPtr(new UnacceleratedSurfaceFactory()); // recreate because previous one was released
689 return surfaceFactory
->createSurface(deviceSize
, opacityMode
);
692 void HTMLCanvasElement::createImageBuffer()
694 createImageBufferInternal(nullptr);
695 if (m_didFailToCreateImageBuffer
&& m_context
->is2d())
696 m_context
->loseContext(CanvasRenderingContext::SyntheticLostContext
);
699 void HTMLCanvasElement::createImageBufferInternal(PassOwnPtr
<ImageBufferSurface
> externalSurface
)
701 ASSERT(!m_imageBuffer
);
703 m_didFailToCreateImageBuffer
= true;
704 m_imageBufferIsClear
= true;
706 if (!canCreateImageBuffer(size()))
709 int msaaSampleCount
= 0;
710 OwnPtr
<ImageBufferSurface
> surface
;
711 if (externalSurface
) {
712 surface
= externalSurface
;
714 surface
= createImageBufferSurface(size(), &msaaSampleCount
);
716 m_imageBuffer
= ImageBuffer::create(surface
.release());
719 m_imageBuffer
->setClient(this);
721 document().updateLayoutTreeIfNeeded();
722 const ComputedStyle
* style
= ensureComputedStyle();
723 m_imageBuffer
->setFilterQuality((style
&& (style
->imageRendering() == ImageRenderingPixelated
)) ? kNone_SkFilterQuality
: kLow_SkFilterQuality
);
725 m_didFailToCreateImageBuffer
= false;
727 updateExternallyAllocatedMemory();
730 // Early out for WebGL canvases
734 m_imageBuffer
->setClient(this);
735 // Enabling MSAA overrides a request to disable antialiasing. This is true regardless of whether the
736 // rendering mode is accelerated or not. For consistency, we don't want to apply AA in accelerated
737 // canvases but not in unaccelerated canvases.
738 if (!msaaSampleCount
&& document().settings() && !document().settings()->antialiased2dCanvasEnabled())
739 m_context
->setShouldAntialias(false);
742 setNeedsCompositingUpdate();
745 void HTMLCanvasElement::notifySurfaceInvalid()
747 if (m_context
&& m_context
->is2d())
748 m_context
->loseContext(CanvasRenderingContext::RealLostContext
);
751 DEFINE_TRACE(HTMLCanvasElement
)
754 visitor
->trace(m_observers
);
755 visitor
->trace(m_context
);
757 DocumentVisibilityObserver::trace(visitor
);
758 HTMLElement::trace(visitor
);
761 void HTMLCanvasElement::updateExternallyAllocatedMemory() const
766 if (m_imageBuffer
->isAccelerated()) {
767 // The number of internal GPU buffers vary between one (stable
768 // non-displayed state) and three (triple-buffered animations).
769 // Adding 2 is a pessimistic but relevant estimate.
770 // Note: These buffers might be allocated in GPU memory.
777 // Four bytes per pixel per buffer.
778 Checked
<intptr_t, RecordOverflow
> checkedExternallyAllocatedMemory
= 4 * bufferCount
;
780 checkedExternallyAllocatedMemory
+= m_context
->externallyAllocatedBytesPerPixel();
782 checkedExternallyAllocatedMemory
*= width();
783 checkedExternallyAllocatedMemory
*= height();
784 intptr_t externallyAllocatedMemory
;
785 if (checkedExternallyAllocatedMemory
.safeGet(externallyAllocatedMemory
) == CheckedState::DidOverflow
)
786 externallyAllocatedMemory
= std::numeric_limits
<intptr_t>::max();
788 // Subtracting two intptr_t that are known to be positive will never underflow.
789 v8::Isolate::GetCurrent()->AdjustAmountOfExternalAllocatedMemory(externallyAllocatedMemory
- m_externallyAllocatedMemory
);
790 m_externallyAllocatedMemory
= externallyAllocatedMemory
;
793 SkCanvas
* HTMLCanvasElement::drawingCanvas() const
795 return buffer() ? m_imageBuffer
->canvas() : nullptr;
798 void HTMLCanvasElement::disableDeferral() const
801 m_imageBuffer
->disableDeferral();
804 SkCanvas
* HTMLCanvasElement::existingDrawingCanvas() const
806 if (!hasImageBuffer())
809 return m_imageBuffer
->canvas();
812 ImageBuffer
* HTMLCanvasElement::buffer() const
815 if (!hasImageBuffer() && !m_didFailToCreateImageBuffer
)
816 const_cast<HTMLCanvasElement
*>(this)->createImageBuffer();
817 return m_imageBuffer
.get();
820 void HTMLCanvasElement::createImageBufferUsingSurfaceForTesting(PassOwnPtr
<ImageBufferSurface
> surface
)
822 discardImageBuffer();
823 setWidth(surface
->size().width());
824 setHeight(surface
->size().height());
825 createImageBufferInternal(surface
);
828 void HTMLCanvasElement::ensureUnacceleratedImageBuffer()
831 if ((hasImageBuffer() && !m_imageBuffer
->isAccelerated()) || m_didFailToCreateImageBuffer
)
833 discardImageBuffer();
834 OpacityMode opacityMode
= m_context
->hasAlpha() ? NonOpaque
: Opaque
;
835 m_imageBuffer
= ImageBuffer::create(size(), opacityMode
);
836 m_didFailToCreateImageBuffer
= !m_imageBuffer
;
839 PassRefPtr
<Image
> HTMLCanvasElement::copiedImage(SourceDrawingBuffer sourceBuffer
) const
844 return createTransparentImage(size());
846 bool needToUpdate
= !m_copiedImage
;
847 // The concept of SourceDrawingBuffer is valid on only WebGL.
848 if (m_context
->is3d())
849 needToUpdate
|= m_context
->paintRenderingResultsToCanvas(sourceBuffer
);
850 if (needToUpdate
&& buffer()) {
851 m_copiedImage
= buffer()->newImageSnapshot();
852 updateExternallyAllocatedMemory();
854 return m_copiedImage
;
857 void HTMLCanvasElement::discardImageBuffer()
859 m_imageBuffer
.clear();
860 m_dirtyRect
= FloatRect();
861 updateExternallyAllocatedMemory();
864 void HTMLCanvasElement::clearCopiedImage()
867 m_copiedImage
.clear();
868 updateExternallyAllocatedMemory();
872 AffineTransform
HTMLCanvasElement::baseTransform() const
874 ASSERT(hasImageBuffer() && !m_didFailToCreateImageBuffer
);
875 return m_imageBuffer
->baseTransform();
878 void HTMLCanvasElement::didChangeVisibilityState(PageVisibilityState visibility
)
882 bool hidden
= visibility
!= PageVisibilityStateVisible
;
883 m_context
->setIsHidden(hidden
);
887 discardImageBuffer();
892 void HTMLCanvasElement::styleDidChange(const ComputedStyle
* oldStyle
, const ComputedStyle
& newStyle
)
895 m_context
->styleDidChange(oldStyle
, newStyle
);
898 void HTMLCanvasElement::didMoveToNewDocument(Document
& oldDocument
)
900 setObservedDocument(document());
902 m_context
->didMoveToNewDocument(&document());
903 HTMLElement::didMoveToNewDocument(oldDocument
);
906 PassRefPtr
<Image
> HTMLCanvasElement::getSourceImageForCanvas(SourceImageStatus
* status
) const
908 if (!width() || !height()) {
909 *status
= ZeroSizeCanvasSourceImageStatus
;
913 if (!isPaintable()) {
914 *status
= InvalidSourceImageStatus
;
919 *status
= NormalSourceImageStatus
;
920 return createTransparentImage(size());
923 if (m_context
->is3d()) {
924 m_context
->paintRenderingResultsToCanvas(BackBuffer
);
927 RefPtr
<SkImage
> image
= buffer()->newSkImageSnapshot();
929 *status
= NormalSourceImageStatus
;
930 return StaticBitmapImage::create(image
.release());
933 *status
= InvalidSourceImageStatus
;
937 bool HTMLCanvasElement::wouldTaintOrigin(SecurityOrigin
*) const
939 return !originClean();
942 FloatSize
HTMLCanvasElement::elementSize() const
944 return FloatSize(width(), height());
947 bool HTMLCanvasElement::isOpaque() const
949 return m_context
&& !m_context
->hasAlpha();