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/frame/LocalFrame.h"
38 #include "core/frame/Settings.h"
39 #include "core/html/ImageData.h"
40 #include "core/html/canvas/CanvasContextCreationAttributes.h"
41 #include "core/html/canvas/CanvasFontCache.h"
42 #include "core/html/canvas/CanvasRenderingContext.h"
43 #include "core/html/canvas/CanvasRenderingContextFactory.h"
44 #include "core/layout/LayoutHTMLCanvas.h"
45 #include "core/paint/DeprecatedPaintLayer.h"
46 #include "platform/MIMETypeRegistry.h"
47 #include "platform/RuntimeEnabledFeatures.h"
48 #include "platform/graphics/Canvas2DImageBufferSurface.h"
49 #include "platform/graphics/ExpensiveCanvasHeuristicParameters.h"
50 #include "platform/graphics/ImageBuffer.h"
51 #include "platform/graphics/RecordingImageBufferSurface.h"
52 #include "platform/graphics/StaticBitmapImage.h"
53 #include "platform/graphics/UnacceleratedImageBufferSurface.h"
54 #include "platform/graphics/gpu/AcceleratedImageBufferSurface.h"
55 #include "platform/transforms/AffineTransform.h"
56 #include "public/platform/Platform.h"
62 using namespace HTMLNames
;
66 // These values come from the WhatWG spec.
67 const int DefaultWidth
= 300;
68 const int DefaultHeight
= 150;
70 // Firefox limits width/height to 32767 pixels, but slows down dramatically before it
71 // reaches that limit. We limit by area instead, giving us larger maximum dimensions,
72 // in exchange for a smaller maximum canvas size.
73 const int MaxCanvasArea
= 32768 * 8192; // Maximum canvas area in CSS pixels
75 // In Skia, we will also limit width/height to 32767.
76 const int MaxSkiaDim
= 32767; // Maximum width/height in CSS pixels.
78 bool canCreateImageBuffer(const IntSize
& size
)
82 if (size
.width() * size
.height() > MaxCanvasArea
)
84 if (size
.width() > MaxSkiaDim
|| size
.height() > MaxSkiaDim
)
89 PassRefPtr
<Image
> createTransparentImage(const IntSize
& size
)
91 ASSERT(canCreateImageBuffer(size
));
92 RefPtr
<SkSurface
> surface
= adoptRef(SkSurface::NewRasterN32Premul(size
.width(), size
.height()));
93 surface
->getCanvas()->clear(SK_ColorTRANSPARENT
);
94 return StaticBitmapImage::create(adoptRef(surface
->newImageSnapshot()));
99 DEFINE_EMPTY_DESTRUCTOR_WILL_BE_REMOVED(CanvasObserver
);
101 inline HTMLCanvasElement::HTMLCanvasElement(Document
& document
)
102 : HTMLElement(canvasTag
, document
)
103 , DocumentVisibilityObserver(document
)
104 , m_size(DefaultWidth
, DefaultHeight
)
105 , m_ignoreReset(false)
106 , m_accelerationDisabled(false)
107 , m_externallyAllocatedMemory(0)
108 , m_originClean(true)
109 , m_didFailToCreateImageBuffer(false)
110 , m_imageBufferIsClear(false)
112 setHasCustomStyleCallbacks();
115 DEFINE_NODE_FACTORY(HTMLCanvasElement
)
117 HTMLCanvasElement::~HTMLCanvasElement()
119 v8::Isolate::GetCurrent()->AdjustAmountOfExternalAllocatedMemory(-m_externallyAllocatedMemory
);
121 for (CanvasObserver
* canvasObserver
: m_observers
)
122 canvasObserver
->canvasDestroyed(this);
123 // Ensure these go away before the ImageBuffer.
128 void HTMLCanvasElement::parseAttribute(const QualifiedName
& name
, const AtomicString
& value
)
130 if (name
== widthAttr
|| name
== heightAttr
)
132 HTMLElement::parseAttribute(name
, value
);
135 LayoutObject
* HTMLCanvasElement::createLayoutObject(const ComputedStyle
& style
)
137 LocalFrame
* frame
= document().frame();
138 if (frame
&& frame
->script().canExecuteScripts(NotAboutToExecuteScript
))
139 return new LayoutHTMLCanvas(this);
140 return HTMLElement::createLayoutObject(style
);
143 void HTMLCanvasElement::didRecalcStyle(StyleRecalcChange
)
145 SkFilterQuality filterQuality
;
146 const ComputedStyle
* style
= ensureComputedStyle();
147 if (style
&& style
->imageRendering() == ImageRenderingPixelated
) {
148 filterQuality
= kNone_SkFilterQuality
;
150 filterQuality
= kLow_SkFilterQuality
;
154 m_context
->setFilterQuality(filterQuality
);
155 setNeedsCompositingUpdate();
156 } else if (hasImageBuffer()) {
157 m_imageBuffer
->setFilterQuality(filterQuality
);
161 Node::InsertionNotificationRequest
HTMLCanvasElement::insertedInto(ContainerNode
* node
)
163 setIsInCanvasSubtree(true);
164 return HTMLElement::insertedInto(node
);
167 void HTMLCanvasElement::addObserver(CanvasObserver
* observer
)
169 m_observers
.add(observer
);
172 void HTMLCanvasElement::removeObserver(CanvasObserver
* observer
)
174 m_observers
.remove(observer
);
177 void HTMLCanvasElement::setHeight(int value
)
179 setIntegralAttribute(heightAttr
, value
);
182 void HTMLCanvasElement::setWidth(int value
)
184 setIntegralAttribute(widthAttr
, value
);
187 HTMLCanvasElement::ContextFactoryVector
& HTMLCanvasElement::renderingContextFactories()
189 ASSERT(isMainThread());
190 DEFINE_STATIC_LOCAL(ContextFactoryVector
, s_contextFactories
, (CanvasRenderingContext::ContextTypeCount
));
191 return s_contextFactories
;
194 CanvasRenderingContextFactory
* HTMLCanvasElement::getRenderingContextFactory(int type
)
196 ASSERT(type
< CanvasRenderingContext::ContextTypeCount
);
197 return renderingContextFactories()[type
].get();
200 void HTMLCanvasElement::registerRenderingContextFactory(PassOwnPtr
<CanvasRenderingContextFactory
> renderingContextFactory
)
202 CanvasRenderingContext::ContextType type
= renderingContextFactory
->contextType();
203 ASSERT(type
< CanvasRenderingContext::ContextTypeCount
);
204 ASSERT(!renderingContextFactories()[type
]);
205 renderingContextFactories()[type
] = renderingContextFactory
;
208 ScriptValue
HTMLCanvasElement::getContext(ScriptState
* scriptState
, const String
& type
, const CanvasContextCreationAttributes
& attributes
)
210 CanvasRenderingContext
* context
= getCanvasRenderingContext(type
, attributes
);
212 return ScriptValue::createNull(scriptState
);
214 return ScriptValue(scriptState
, toV8(context
, scriptState
->context()->Global(), scriptState
->isolate()));
217 CanvasRenderingContext
* HTMLCanvasElement::getCanvasRenderingContext(const String
& type
, const CanvasContextCreationAttributes
& attributes
)
219 CanvasRenderingContext::ContextType contextType
= CanvasRenderingContext::contextTypeFromId(type
);
222 if (contextType
== CanvasRenderingContext::ContextTypeCount
)
225 // Log the aliased context type used.
227 Platform::current()->histogramEnumeration("Canvas.ContextType", contextType
, CanvasRenderingContext::ContextTypeCount
);
229 contextType
= CanvasRenderingContext::resolveContextTypeAliases(contextType
);
231 CanvasRenderingContextFactory
* factory
= getRenderingContextFactory(contextType
);
235 // FIXME - The code depends on the context not going away once created, to prevent JS from
236 // seeing a dangling pointer. So for now we will disallow the context from being changed
237 // once it is created.
239 if (m_context
->contextType() == contextType
)
240 return m_context
.get();
242 factory
->onError(this, "Canvas has an existing context of a different type");
246 m_context
= factory
->create(this, attributes
, document());
250 if (m_context
->is3d()) {
251 const ComputedStyle
* style
= ensureComputedStyle();
253 m_context
->setFilterQuality(style
->imageRendering() == ImageRenderingPixelated
? kNone_SkFilterQuality
: kLow_SkFilterQuality
);
254 updateExternallyAllocatedMemory();
256 setNeedsCompositingUpdate();
258 return m_context
.get();
261 bool HTMLCanvasElement::shouldBeDirectComposited() const
263 return (m_context
&& m_context
->isAccelerated()) || (hasImageBuffer() && buffer()->isExpensiveToPaint());
266 bool HTMLCanvasElement::isPaintable() const
269 return canCreateImageBuffer(size());
273 void HTMLCanvasElement::didDraw(const FloatRect
& rect
)
277 m_imageBufferIsClear
= false;
280 layoutObject()->setMayNeedPaintInvalidation();
281 m_dirtyRect
.unite(rect
);
282 if (m_context
&& m_context
->is2d() && hasImageBuffer())
283 buffer()->didDraw(rect
);
284 notifyObserversCanvasChanged(rect
);
287 void HTMLCanvasElement::didFinalizeFrame()
289 if (m_dirtyRect
.isEmpty())
292 // Propagate the m_dirtyRect accumulated so far to the compositor
293 // before restarting with a blank dirty rect.
294 FloatRect
srcRect(0, 0, size().width(), size().height());
295 m_dirtyRect
.intersect(srcRect
);
296 LayoutBox
* ro
= layoutBox();
297 // Canvas content updates do not need to be propagated as
298 // paint invalidations if the canvas is accelerated, since
299 // the canvas contents are sent separately through a texture layer.
300 if (ro
&& (!m_context
|| !m_context
->isAccelerated())) {
301 LayoutRect
mappedDirtyRect(enclosingIntRect(mapRect(m_dirtyRect
, srcRect
, ro
->contentBoxRect())));
302 // For querying DeprecatedPaintLayer::compositingState()
303 // FIXME: is this invalidation using the correct compositing state?
304 DisableCompositingQueryAsserts disabler
;
305 ro
->invalidatePaintRectangle(mappedDirtyRect
);
307 m_dirtyRect
= FloatRect();
310 void HTMLCanvasElement::restoreCanvasMatrixClipStack(SkCanvas
* canvas
) const
313 m_context
->restoreCanvasMatrixClipStack(canvas
);
316 void HTMLCanvasElement::doDeferredPaintInvalidation()
318 ASSERT(!m_dirtyRect
.isEmpty());
322 ASSERT(hasImageBuffer());
323 FloatRect
srcRect(0, 0, size().width(), size().height());
324 m_dirtyRect
.intersect(srcRect
);
325 LayoutBox
* lb
= layoutBox();
327 FloatRect mappedDirtyRect
= mapRect(m_dirtyRect
, srcRect
, lb
->contentBoxRect());
328 if (m_context
->isAccelerated()) {
329 // Accelerated 2D canvases need the dirty rect to be expressed relative to the
330 // content box, as opposed to the layout box.
331 mappedDirtyRect
.move(-lb
->contentBoxOffset());
333 m_imageBuffer
->finalizeFrame(mappedDirtyRect
);
335 m_imageBuffer
->finalizeFrame(m_dirtyRect
);
338 ASSERT(m_dirtyRect
.isEmpty());
341 void HTMLCanvasElement::notifyObserversCanvasChanged(const FloatRect
& rect
)
343 for (CanvasObserver
* canvasObserver
: m_observers
)
344 canvasObserver
->canvasChanged(this, rect
);
347 void HTMLCanvasElement::reset()
352 m_dirtyRect
= FloatRect();
355 bool hadImageBuffer
= hasImageBuffer();
357 int w
= getAttribute(widthAttr
).toInt(&ok
);
361 int h
= getAttribute(heightAttr
).toInt(&ok
);
365 if (m_context
&& m_context
->is2d())
368 IntSize oldSize
= size();
369 IntSize
newSize(w
, h
);
371 // If the size of an existing buffer matches, we can just clear it instead of reallocating.
372 // This optimization is only done for 2D canvases for now.
373 if (hadImageBuffer
&& oldSize
== newSize
&& m_context
&& m_context
->is2d() && !buffer()->isRecording()) {
374 if (!m_imageBufferIsClear
) {
375 m_imageBufferIsClear
= true;
376 m_context
->clearRect(0, 0, width(), height());
381 setSurfaceSize(newSize
);
383 if (m_context
&& m_context
->is3d() && oldSize
!= size())
384 m_context
->reshape(width(), height());
386 if (LayoutObject
* layoutObject
= this->layoutObject()) {
387 if (layoutObject
->isCanvas()) {
388 if (oldSize
!= size()) {
389 toLayoutHTMLCanvas(layoutObject
)->canvasSizeChanged();
390 if (layoutBox() && layoutBox()->hasAcceleratedCompositing())
391 layoutBox()->contentChanged(CanvasChanged
);
394 layoutObject
->setShouldDoFullPaintInvalidation();
398 for (CanvasObserver
* canvasObserver
: m_observers
)
399 canvasObserver
->canvasResized(this);
402 bool HTMLCanvasElement::paintsIntoCanvasBuffer() const
406 if (!m_context
->isAccelerated())
409 if (layoutBox() && layoutBox()->hasAcceleratedCompositing())
415 void HTMLCanvasElement::paint(GraphicsContext
* context
, const LayoutRect
& r
)
417 // FIXME: crbug.com/438240; there is a bug with the new CSS blending and compositing feature.
420 if (!paintsIntoCanvasBuffer() && !document().printing())
423 m_context
->paintRenderingResultsToCanvas(FrontBuffer
);
424 if (hasImageBuffer()) {
425 if (!context
->contextDisabled()) {
426 SkXfermode::Mode compositeOperator
= !m_context
|| m_context
->hasAlpha() ? SkXfermode::kSrcOver_Mode
: SkXfermode::kSrc_Mode
;
427 buffer()->draw(context
, pixelSnappedIntRect(r
), 0, compositeOperator
);
430 // When alpha is false, we should draw to opaque black.
431 if (!m_context
->hasAlpha())
432 context
->fillRect(FloatRect(r
), Color(0, 0, 0));
435 if (is3D() && paintsIntoCanvasBuffer())
436 m_context
->markLayerComposited();
439 bool HTMLCanvasElement::is3D() const
441 return m_context
&& m_context
->is3d();
444 bool HTMLCanvasElement::isAnimated2D() const
446 return m_context
&& m_context
->is2d() && hasImageBuffer() && m_imageBuffer
->wasDrawnToAfterSnapshot();
449 void HTMLCanvasElement::setSurfaceSize(const IntSize
& size
)
452 m_didFailToCreateImageBuffer
= false;
453 discardImageBuffer();
455 if (m_context
&& m_context
->is2d() && m_context
->isContextLost()) {
456 m_context
->didSetSurfaceSize();
460 String
HTMLCanvasElement::toEncodingMimeType(const String
& mimeType
)
462 String lowercaseMimeType
= mimeType
.lower();
464 // FIXME: Make isSupportedImageMIMETypeForEncoding threadsafe (to allow this method to be used on a worker thread).
465 if (mimeType
.isNull() || !MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(lowercaseMimeType
))
466 lowercaseMimeType
= "image/png";
468 return lowercaseMimeType
;
471 const AtomicString
HTMLCanvasElement::imageSourceURL() const
473 return AtomicString(toDataURLInternal("image/png", 0, FrontBuffer
));
476 ImageData
* HTMLCanvasElement::toImageData(SourceDrawingBuffer sourceBuffer
) const
478 ImageData
* imageData
;
480 // Get non-premultiplied data because of inaccurate premultiplied alpha conversion of buffer()->toDataURL().
481 imageData
= m_context
->paintRenderingResultsToImageData(sourceBuffer
);
485 m_context
->paintRenderingResultsToCanvas(sourceBuffer
);
486 imageData
= ImageData::create(m_size
);
487 RefPtr
<SkImage
> snapshot
= buffer()->newSkImageSnapshot();
489 SkImageInfo imageInfo
= SkImageInfo::Make(width(), height(), kRGBA_8888_SkColorType
, kUnpremul_SkAlphaType
);
490 snapshot
->readPixels(imageInfo
, imageData
->data()->data(), imageInfo
.minRowBytes(), 0, 0);
495 imageData
= ImageData::create(m_size
);
500 ASSERT(m_context
->is2d());
501 RefPtr
<SkImage
> snapshot
= buffer()->newSkImageSnapshot();
503 SkImageInfo imageInfo
= SkImageInfo::Make(width(), height(), kRGBA_8888_SkColorType
, kUnpremul_SkAlphaType
);
504 snapshot
->readPixels(imageInfo
, imageData
->data()->data(), imageInfo
.minRowBytes(), 0, 0);
510 String
HTMLCanvasElement::toDataURLInternal(const String
& mimeType
, const double* quality
, SourceDrawingBuffer sourceBuffer
) const
513 return String("data:,");
515 String encodingMimeType
= toEncodingMimeType(mimeType
);
517 ImageData
* imageData
= toImageData(sourceBuffer
);
518 ScopedDisposal
<ImageData
> disposer(imageData
);
520 return ImageDataBuffer(imageData
->size(), imageData
->data()->data()).toDataURL(encodingMimeType
, quality
);
523 String
HTMLCanvasElement::toDataURL(const String
& mimeType
, const ScriptValue
& qualityArgument
, ExceptionState
& exceptionState
) const
525 if (!originClean()) {
526 exceptionState
.throwSecurityError("Tainted canvases may not be exported.");
530 double* qualityPtr
= nullptr;
531 if (!qualityArgument
.isEmpty()) {
532 v8::Local
<v8::Value
> v8Value
= qualityArgument
.v8Value();
533 if (v8Value
->IsNumber()) {
534 quality
= v8Value
.As
<v8::Number
>()->Value();
535 qualityPtr
= &quality
;
538 return toDataURLInternal(mimeType
, qualityPtr
, BackBuffer
);
541 SecurityOrigin
* HTMLCanvasElement::securityOrigin() const
543 return document().securityOrigin();
546 bool HTMLCanvasElement::originClean() const
548 if (document().settings() && document().settings()->disableReadingFromCanvas())
550 return m_originClean
;
553 bool HTMLCanvasElement::shouldAccelerate(const IntSize
& size
) const
555 if (m_context
&& !m_context
->is2d())
558 if (m_accelerationDisabled
)
561 if (RuntimeEnabledFeatures::forceDisplayList2dCanvasEnabled())
564 Settings
* settings
= document().settings();
565 if (!settings
|| !settings
->accelerated2dCanvasEnabled())
568 int canvasPixelCount
= size
.width() * size
.height();
570 if (RuntimeEnabledFeatures::displayList2dCanvasEnabled()) {
572 // TODO(junov): re-enable this code once we solve the problem of recording
573 // GPU-backed images to an SkPicture for cross-context rendering crbug.com/490328
575 // If the compositor provides GPU acceleration to display list canvases, we
576 // prefer that over direct acceleration.
577 if (document().viewportDescription().matchesHeuristicsForGpuRasterization())
580 // If the GPU resources would be very expensive, prefer a display list.
581 if (canvasPixelCount
> ExpensiveCanvasHeuristicParameters::PreferDisplayListOverGpuSizeThreshold
)
585 // Do not use acceleration for small canvas.
586 if (canvasPixelCount
< settings
->minimumAccelerated2dCanvasSize())
589 if (!Platform::current()->canAccelerate2dCanvas())
595 class UnacceleratedSurfaceFactory
: public RecordingImageBufferFallbackSurfaceFactory
{
597 virtual PassOwnPtr
<ImageBufferSurface
> createSurface(const IntSize
& size
, OpacityMode opacityMode
)
599 return adoptPtr(new UnacceleratedImageBufferSurface(size
, opacityMode
));
602 virtual ~UnacceleratedSurfaceFactory() { }
605 bool HTMLCanvasElement::shouldUseDisplayList(const IntSize
& deviceSize
)
607 if (RuntimeEnabledFeatures::forceDisplayList2dCanvasEnabled())
610 if (!RuntimeEnabledFeatures::displayList2dCanvasEnabled())
616 PassOwnPtr
<ImageBufferSurface
> HTMLCanvasElement::createImageBufferSurface(const IntSize
& deviceSize
, int* msaaSampleCount
)
618 OpacityMode opacityMode
= !m_context
|| m_context
->hasAlpha() ? NonOpaque
: Opaque
;
620 *msaaSampleCount
= 0;
622 // If 3d, but the use of the canvas will be for non-accelerated content
623 // (such as -webkit-canvas, then then make a non-accelerated
624 // ImageBuffer. This means copying the internal Image will require a
625 // pixel readback, but that is unavoidable in this case.
626 // FIXME: Actually, avoid setting m_accelerationDisabled at all when
627 // doing GPU-based rasterization.
628 if (m_accelerationDisabled
)
629 return adoptPtr(new UnacceleratedImageBufferSurface(deviceSize
, opacityMode
));
630 return adoptPtr(new AcceleratedImageBufferSurface(deviceSize
, opacityMode
));
633 if (shouldAccelerate(deviceSize
)) {
634 if (document().settings())
635 *msaaSampleCount
= document().settings()->accelerated2dCanvasMSAASampleCount();
636 OwnPtr
<ImageBufferSurface
> surface
= adoptPtr(new Canvas2DImageBufferSurface(deviceSize
, opacityMode
, *msaaSampleCount
));
637 if (surface
->isValid())
638 return surface
.release();
641 OwnPtr
<RecordingImageBufferFallbackSurfaceFactory
> surfaceFactory
= adoptPtr(new UnacceleratedSurfaceFactory());
643 if (shouldUseDisplayList(deviceSize
)) {
644 OwnPtr
<ImageBufferSurface
> surface
= adoptPtr(new RecordingImageBufferSurface(deviceSize
, surfaceFactory
.release(), opacityMode
));
645 if (surface
->isValid())
646 return surface
.release();
647 surfaceFactory
= adoptPtr(new UnacceleratedSurfaceFactory()); // recreate because previous one was released
650 return surfaceFactory
->createSurface(deviceSize
, opacityMode
);
653 void HTMLCanvasElement::createImageBuffer()
655 createImageBufferInternal(nullptr);
656 if (m_didFailToCreateImageBuffer
&& m_context
->is2d())
657 m_context
->loseContext(CanvasRenderingContext::SyntheticLostContext
);
660 void HTMLCanvasElement::createImageBufferInternal(PassOwnPtr
<ImageBufferSurface
> externalSurface
)
662 ASSERT(!m_imageBuffer
);
664 m_didFailToCreateImageBuffer
= true;
665 m_imageBufferIsClear
= true;
667 if (!canCreateImageBuffer(size()))
670 int msaaSampleCount
= 0;
671 OwnPtr
<ImageBufferSurface
> surface
;
672 if (externalSurface
) {
673 surface
= externalSurface
;
675 surface
= createImageBufferSurface(size(), &msaaSampleCount
);
677 m_imageBuffer
= ImageBuffer::create(surface
.release());
680 m_imageBuffer
->setClient(this);
682 document().updateLayoutTreeIfNeeded();
683 const ComputedStyle
* style
= ensureComputedStyle();
684 m_imageBuffer
->setFilterQuality((style
&& (style
->imageRendering() == ImageRenderingPixelated
)) ? kNone_SkFilterQuality
: kLow_SkFilterQuality
);
686 m_didFailToCreateImageBuffer
= false;
688 updateExternallyAllocatedMemory();
691 // Early out for WebGL canvases
695 m_imageBuffer
->setClient(this);
696 // Enabling MSAA overrides a request to disable antialiasing. This is true regardless of whether the
697 // rendering mode is accelerated or not. For consistency, we don't want to apply AA in accelerated
698 // canvases but not in unaccelerated canvases.
699 if (!msaaSampleCount
&& document().settings() && !document().settings()->antialiased2dCanvasEnabled())
700 m_context
->setShouldAntialias(false);
703 setNeedsCompositingUpdate();
706 void HTMLCanvasElement::notifySurfaceInvalid()
708 if (m_context
&& m_context
->is2d())
709 m_context
->loseContext(CanvasRenderingContext::RealLostContext
);
712 DEFINE_TRACE(HTMLCanvasElement
)
715 visitor
->trace(m_observers
);
716 visitor
->trace(m_context
);
718 DocumentVisibilityObserver::trace(visitor
);
719 HTMLElement::trace(visitor
);
722 void HTMLCanvasElement::updateExternallyAllocatedMemory() const
727 if (m_imageBuffer
->isAccelerated()) {
728 // The number of internal GPU buffers vary between one (stable
729 // non-displayed state) and three (triple-buffered animations).
730 // Adding 2 is a pessimistic but relevant estimate.
731 // Note: These buffers might be allocated in GPU memory.
738 // Four bytes per pixel per buffer.
739 Checked
<intptr_t, RecordOverflow
> checkedExternallyAllocatedMemory
= 4 * bufferCount
;
741 checkedExternallyAllocatedMemory
+= m_context
->externallyAllocatedBytesPerPixel();
743 checkedExternallyAllocatedMemory
*= width();
744 checkedExternallyAllocatedMemory
*= height();
745 intptr_t externallyAllocatedMemory
;
746 if (checkedExternallyAllocatedMemory
.safeGet(externallyAllocatedMemory
) == CheckedState::DidOverflow
)
747 externallyAllocatedMemory
= std::numeric_limits
<intptr_t>::max();
749 // Subtracting two intptr_t that are known to be positive will never underflow.
750 v8::Isolate::GetCurrent()->AdjustAmountOfExternalAllocatedMemory(externallyAllocatedMemory
- m_externallyAllocatedMemory
);
751 m_externallyAllocatedMemory
= externallyAllocatedMemory
;
754 SkCanvas
* HTMLCanvasElement::drawingCanvas() const
756 return buffer() ? m_imageBuffer
->canvas() : nullptr;
759 void HTMLCanvasElement::disableDeferral() const
762 m_imageBuffer
->disableDeferral();
765 SkCanvas
* HTMLCanvasElement::existingDrawingCanvas() const
767 if (!hasImageBuffer())
770 return m_imageBuffer
->canvas();
773 ImageBuffer
* HTMLCanvasElement::buffer() const
776 if (!hasImageBuffer() && !m_didFailToCreateImageBuffer
)
777 const_cast<HTMLCanvasElement
*>(this)->createImageBuffer();
778 return m_imageBuffer
.get();
781 void HTMLCanvasElement::createImageBufferUsingSurfaceForTesting(PassOwnPtr
<ImageBufferSurface
> surface
)
783 discardImageBuffer();
784 setWidth(surface
->size().width());
785 setHeight(surface
->size().height());
786 createImageBufferInternal(surface
);
789 void HTMLCanvasElement::ensureUnacceleratedImageBuffer()
792 if ((hasImageBuffer() && !m_imageBuffer
->isAccelerated()) || m_didFailToCreateImageBuffer
)
794 discardImageBuffer();
795 OpacityMode opacityMode
= m_context
->hasAlpha() ? NonOpaque
: Opaque
;
796 m_imageBuffer
= ImageBuffer::create(size(), opacityMode
);
797 m_didFailToCreateImageBuffer
= !m_imageBuffer
;
800 PassRefPtr
<Image
> HTMLCanvasElement::copiedImage(SourceDrawingBuffer sourceBuffer
) const
805 return createTransparentImage(size());
807 bool needToUpdate
= !m_copiedImage
;
808 // The concept of SourceDrawingBuffer is valid on only WebGL.
809 if (m_context
->is3d())
810 needToUpdate
|= m_context
->paintRenderingResultsToCanvas(sourceBuffer
);
811 if (needToUpdate
&& buffer()) {
812 m_copiedImage
= buffer()->newImageSnapshot();
813 updateExternallyAllocatedMemory();
815 return m_copiedImage
;
818 void HTMLCanvasElement::discardImageBuffer()
820 m_imageBuffer
.clear();
821 m_dirtyRect
= FloatRect();
822 updateExternallyAllocatedMemory();
825 void HTMLCanvasElement::clearCopiedImage()
828 m_copiedImage
.clear();
829 updateExternallyAllocatedMemory();
833 AffineTransform
HTMLCanvasElement::baseTransform() const
835 ASSERT(hasImageBuffer() && !m_didFailToCreateImageBuffer
);
836 return m_imageBuffer
->baseTransform();
839 void HTMLCanvasElement::didChangeVisibilityState(PageVisibilityState visibility
)
843 bool hidden
= visibility
!= PageVisibilityStateVisible
;
844 m_context
->setIsHidden(hidden
);
848 discardImageBuffer();
853 void HTMLCanvasElement::styleDidChange(const ComputedStyle
* oldStyle
, const ComputedStyle
& newStyle
)
856 m_context
->styleDidChange(oldStyle
, newStyle
);
859 void HTMLCanvasElement::didMoveToNewDocument(Document
& oldDocument
)
861 setObservedDocument(document());
863 m_context
->didMoveToNewDocument(&document());
864 HTMLElement::didMoveToNewDocument(oldDocument
);
867 PassRefPtr
<Image
> HTMLCanvasElement::getSourceImageForCanvas(SourceImageStatus
* status
) const
869 if (!width() || !height()) {
870 *status
= ZeroSizeCanvasSourceImageStatus
;
874 if (!isPaintable()) {
875 *status
= InvalidSourceImageStatus
;
880 *status
= NormalSourceImageStatus
;
881 return createTransparentImage(size());
884 if (m_context
->is3d()) {
885 m_context
->paintRenderingResultsToCanvas(BackBuffer
);
888 RefPtr
<SkImage
> image
= buffer()->newSkImageSnapshot();
890 *status
= NormalSourceImageStatus
;
891 return StaticBitmapImage::create(image
.release());
894 *status
= InvalidSourceImageStatus
;
898 bool HTMLCanvasElement::wouldTaintOrigin(SecurityOrigin
*) const
900 return !originClean();
903 FloatSize
HTMLCanvasElement::elementSize() const
905 return FloatSize(width(), height());
908 bool HTMLCanvasElement::isOpaque() const
910 return m_context
&& !m_context
->hasAlpha();