Keep auxilliary media objects on the heap always.
[chromium-blink-merge.git] / third_party / WebKit / Source / core / html / HTMLCanvasElement.cpp
blob50e6afbb428e0bea70f3aace7a0a07cf64e98161
1 /*
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
8 * are met:
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.
28 #include "config.h"
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"
57 #include <math.h>
58 #include <v8.h>
60 namespace blink {
62 using namespace HTMLNames;
64 namespace {
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)
80 if (size.isEmpty())
81 return false;
82 if (size.width() * size.height() > MaxCanvasArea)
83 return false;
84 if (size.width() > MaxSkiaDim || size.height() > MaxSkiaDim)
85 return false;
86 return true;
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()));
97 } // namespace
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);
120 #if !ENABLE(OILPAN)
121 for (CanvasObserver* canvasObserver : m_observers)
122 canvasObserver->canvasDestroyed(this);
123 // Ensure these go away before the ImageBuffer.
124 m_context.clear();
125 #endif
128 void HTMLCanvasElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
130 if (name == widthAttr || name == heightAttr)
131 reset();
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;
149 } else {
150 filterQuality = kLow_SkFilterQuality;
153 if (is3D()) {
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);
211 if (!context) {
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);
221 // Unknown type.
222 if (contextType == CanvasRenderingContext::ContextTypeCount)
223 return nullptr;
225 // Log the aliased context type used.
226 if (!m_context)
227 Platform::current()->histogramEnumeration("Canvas.ContextType", contextType, CanvasRenderingContext::ContextTypeCount);
229 contextType = CanvasRenderingContext::resolveContextTypeAliases(contextType);
231 CanvasRenderingContextFactory* factory = getRenderingContextFactory(contextType);
232 if (!factory)
233 return nullptr;
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.
238 if (m_context) {
239 if (m_context->contextType() == contextType)
240 return m_context.get();
242 factory->onError(this, "Canvas has an existing context of a different type");
243 return nullptr;
246 m_context = factory->create(this, attributes, document());
247 if (!m_context)
248 return nullptr;
250 if (m_context->is3d()) {
251 const ComputedStyle* style = ensureComputedStyle();
252 if (style)
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
268 if (!m_context)
269 return canCreateImageBuffer(size());
270 return buffer();
273 void HTMLCanvasElement::didDraw(const FloatRect& rect)
275 if (rect.isEmpty())
276 return;
277 m_imageBufferIsClear = false;
278 clearCopiedImage();
279 if (layoutObject())
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())
290 return;
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
312 if (m_context)
313 m_context->restoreCanvasMatrixClipStack(canvas);
316 void HTMLCanvasElement::doDeferredPaintInvalidation()
318 ASSERT(!m_dirtyRect.isEmpty());
319 if (is3D()) {
320 didFinalizeFrame();
321 } else {
322 ASSERT(hasImageBuffer());
323 FloatRect srcRect(0, 0, size().width(), size().height());
324 m_dirtyRect.intersect(srcRect);
325 LayoutBox* lb = layoutBox();
326 if (lb) {
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);
334 } else {
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()
349 if (m_ignoreReset)
350 return;
352 m_dirtyRect = FloatRect();
354 bool ok;
355 bool hadImageBuffer = hasImageBuffer();
357 int w = getAttribute(widthAttr).toInt(&ok);
358 if (!ok || w < 0)
359 w = DefaultWidth;
361 int h = getAttribute(heightAttr).toInt(&ok);
362 if (!ok || h < 0)
363 h = DefaultHeight;
365 if (m_context && m_context->is2d())
366 m_context->reset();
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());
378 return;
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);
393 if (hadImageBuffer)
394 layoutObject->setShouldDoFullPaintInvalidation();
398 for (CanvasObserver* canvasObserver : m_observers)
399 canvasObserver->canvasResized(this);
402 bool HTMLCanvasElement::paintsIntoCanvasBuffer() const
404 ASSERT(m_context);
406 if (!m_context->isAccelerated())
407 return true;
409 if (layoutBox() && layoutBox()->hasAcceleratedCompositing())
410 return false;
412 return true;
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.
418 if (!m_context)
419 return;
420 if (!paintsIntoCanvasBuffer() && !document().printing())
421 return;
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);
429 } else {
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)
451 m_size = size;
452 m_didFailToCreateImageBuffer = false;
453 discardImageBuffer();
454 clearCopiedImage();
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;
479 if (is3D()) {
480 // Get non-premultiplied data because of inaccurate premultiplied alpha conversion of buffer()->toDataURL().
481 imageData = m_context->paintRenderingResultsToImageData(sourceBuffer);
482 if (imageData)
483 return imageData;
485 m_context->paintRenderingResultsToCanvas(sourceBuffer);
486 imageData = ImageData::create(m_size);
487 RefPtr<SkImage> snapshot = buffer()->newSkImageSnapshot();
488 if (snapshot) {
489 SkImageInfo imageInfo = SkImageInfo::Make(width(), height(), kRGBA_8888_SkColorType, kUnpremul_SkAlphaType);
490 snapshot->readPixels(imageInfo, imageData->data()->data(), imageInfo.minRowBytes(), 0, 0);
492 return imageData;
495 imageData = ImageData::create(m_size);
497 if (!m_context)
498 return imageData;
500 ASSERT(m_context->is2d());
501 RefPtr<SkImage> snapshot = buffer()->newSkImageSnapshot();
502 if (snapshot) {
503 SkImageInfo imageInfo = SkImageInfo::Make(width(), height(), kRGBA_8888_SkColorType, kUnpremul_SkAlphaType);
504 snapshot->readPixels(imageInfo, imageData->data()->data(), imageInfo.minRowBytes(), 0, 0);
507 return imageData;
510 String HTMLCanvasElement::toDataURLInternal(const String& mimeType, const double* quality, SourceDrawingBuffer sourceBuffer) const
512 if (!isPaintable())
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.");
527 return String();
529 double quality;
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())
549 return false;
550 return m_originClean;
553 bool HTMLCanvasElement::shouldAccelerate(const IntSize& size) const
555 if (m_context && !m_context->is2d())
556 return false;
558 if (m_accelerationDisabled)
559 return false;
561 if (RuntimeEnabledFeatures::forceDisplayList2dCanvasEnabled())
562 return false;
564 Settings* settings = document().settings();
565 if (!settings || !settings->accelerated2dCanvasEnabled())
566 return false;
568 int canvasPixelCount = size.width() * size.height();
570 if (RuntimeEnabledFeatures::displayList2dCanvasEnabled()) {
571 #if 0
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())
578 return false;
579 #endif
580 // If the GPU resources would be very expensive, prefer a display list.
581 if (canvasPixelCount > ExpensiveCanvasHeuristicParameters::PreferDisplayListOverGpuSizeThreshold)
582 return false;
585 // Do not use acceleration for small canvas.
586 if (canvasPixelCount < settings->minimumAccelerated2dCanvasSize())
587 return false;
589 if (!Platform::current()->canAccelerate2dCanvas())
590 return false;
592 return true;
595 class UnacceleratedSurfaceFactory : public RecordingImageBufferFallbackSurfaceFactory {
596 public:
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())
608 return true;
610 if (!RuntimeEnabledFeatures::displayList2dCanvasEnabled())
611 return false;
613 return true;
616 PassOwnPtr<ImageBufferSurface> HTMLCanvasElement::createImageBufferSurface(const IntSize& deviceSize, int* msaaSampleCount)
618 OpacityMode opacityMode = !m_context || m_context->hasAlpha() ? NonOpaque : Opaque;
620 *msaaSampleCount = 0;
621 if (is3D()) {
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()))
668 return;
670 int msaaSampleCount = 0;
671 OwnPtr<ImageBufferSurface> surface;
672 if (externalSurface) {
673 surface = externalSurface;
674 } else {
675 surface = createImageBufferSurface(size(), &msaaSampleCount);
677 m_imageBuffer = ImageBuffer::create(surface.release());
678 if (!m_imageBuffer)
679 return;
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();
690 if (is3D()) {
691 // Early out for WebGL canvases
692 return;
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);
702 if (m_context)
703 setNeedsCompositingUpdate();
706 void HTMLCanvasElement::notifySurfaceInvalid()
708 if (m_context && m_context->is2d())
709 m_context->loseContext(CanvasRenderingContext::RealLostContext);
712 DEFINE_TRACE(HTMLCanvasElement)
714 #if ENABLE(OILPAN)
715 visitor->trace(m_observers);
716 visitor->trace(m_context);
717 #endif
718 DocumentVisibilityObserver::trace(visitor);
719 HTMLElement::trace(visitor);
722 void HTMLCanvasElement::updateExternallyAllocatedMemory() const
724 int bufferCount = 0;
725 if (m_imageBuffer) {
726 bufferCount++;
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.
732 bufferCount += 2;
735 if (m_copiedImage)
736 bufferCount++;
738 // Four bytes per pixel per buffer.
739 Checked<intptr_t, RecordOverflow> checkedExternallyAllocatedMemory = 4 * bufferCount;
740 if (is3D())
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
761 if (buffer())
762 m_imageBuffer->disableDeferral();
765 SkCanvas* HTMLCanvasElement::existingDrawingCanvas() const
767 if (!hasImageBuffer())
768 return nullptr;
770 return m_imageBuffer->canvas();
773 ImageBuffer* HTMLCanvasElement::buffer() const
775 ASSERT(m_context);
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()
791 ASSERT(m_context);
792 if ((hasImageBuffer() && !m_imageBuffer->isAccelerated()) || m_didFailToCreateImageBuffer)
793 return;
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
802 if (!isPaintable())
803 return nullptr;
804 if (!m_context)
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()
827 if (m_copiedImage) {
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)
841 if (!m_context)
842 return;
843 bool hidden = visibility != PageVisibilityStateVisible;
844 m_context->setIsHidden(hidden);
845 if (hidden) {
846 clearCopiedImage();
847 if (is3D()) {
848 discardImageBuffer();
853 void HTMLCanvasElement::styleDidChange(const ComputedStyle* oldStyle, const ComputedStyle& newStyle)
855 if (m_context)
856 m_context->styleDidChange(oldStyle, newStyle);
859 void HTMLCanvasElement::didMoveToNewDocument(Document& oldDocument)
861 setObservedDocument(document());
862 if (m_context)
863 m_context->didMoveToNewDocument(&document());
864 HTMLElement::didMoveToNewDocument(oldDocument);
867 PassRefPtr<Image> HTMLCanvasElement::getSourceImageForCanvas(SourceImageStatus* status) const
869 if (!width() || !height()) {
870 *status = ZeroSizeCanvasSourceImageStatus;
871 return nullptr;
874 if (!isPaintable()) {
875 *status = InvalidSourceImageStatus;
876 return nullptr;
879 if (!m_context) {
880 *status = NormalSourceImageStatus;
881 return createTransparentImage(size());
884 if (m_context->is3d()) {
885 m_context->paintRenderingResultsToCanvas(BackBuffer);
888 RefPtr<SkImage> image = buffer()->newSkImageSnapshot();
889 if (image) {
890 *status = NormalSourceImageStatus;
891 return StaticBitmapImage::create(image.release());
894 *status = InvalidSourceImageStatus;
895 return nullptr;
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();
913 } // blink