Move parseFontFaceDescriptor to CSSPropertyParser.cpp
[chromium-blink-merge.git] / third_party / WebKit / Source / core / html / HTMLCanvasElement.cpp
blob6dc6f92d1556336a741cd0d237d79c7bb972ab68
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/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"
60 #include <math.h>
61 #include <v8.h>
63 namespace blink {
65 using namespace HTMLNames;
67 namespace {
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)
83 if (size.isEmpty())
84 return false;
85 if (size.width() * size.height() > MaxCanvasArea)
86 return false;
87 if (size.width() > MaxSkiaDim || size.height() > MaxSkiaDim)
88 return false;
89 return true;
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()));
100 } // namespace
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);
123 #if !ENABLE(OILPAN)
124 for (CanvasObserver* canvasObserver : m_observers)
125 canvasObserver->canvasDestroyed(this);
126 // Ensure these go away before the ImageBuffer.
127 m_context.clear();
128 #endif
131 void HTMLCanvasElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
133 if (name == widthAttr || name == heightAttr)
134 reset();
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;
152 } else {
153 filterQuality = kLow_SkFilterQuality;
156 if (is3D()) {
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);
214 if (!context) {
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);
224 // Unknown type.
225 if (contextType == CanvasRenderingContext::ContextTypeCount)
226 return nullptr;
228 // Log the aliased context type used.
229 if (!m_context)
230 Platform::current()->histogramEnumeration("Canvas.ContextType", contextType, CanvasRenderingContext::ContextTypeCount);
232 contextType = CanvasRenderingContext::resolveContextTypeAliases(contextType);
234 CanvasRenderingContextFactory* factory = getRenderingContextFactory(contextType);
235 if (!factory)
236 return nullptr;
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.
241 if (m_context) {
242 if (m_context->contextType() == contextType)
243 return m_context.get();
245 factory->onError(this, "Canvas has an existing context of a different type");
246 return nullptr;
249 m_context = factory->create(this, attributes, document());
250 if (!m_context)
251 return nullptr;
253 if (m_context->is3d()) {
254 const ComputedStyle* style = ensureComputedStyle();
255 if (style)
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
271 if (!m_context)
272 return canCreateImageBuffer(size());
273 return buffer();
276 void HTMLCanvasElement::didDraw(const FloatRect& rect)
278 if (rect.isEmpty())
279 return;
280 m_imageBufferIsClear = false;
281 clearCopiedImage();
282 if (layoutObject())
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())
293 return;
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
315 if (m_context)
316 m_context->restoreCanvasMatrixClipStack(canvas);
319 void HTMLCanvasElement::doDeferredPaintInvalidation()
321 ASSERT(!m_dirtyRect.isEmpty());
322 if (is3D()) {
323 didFinalizeFrame();
324 } else {
325 ASSERT(hasImageBuffer());
326 FloatRect srcRect(0, 0, size().width(), size().height());
327 m_dirtyRect.intersect(srcRect);
328 LayoutBox* lb = layoutBox();
329 if (lb) {
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);
337 } else {
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()
352 if (m_ignoreReset)
353 return;
355 m_dirtyRect = FloatRect();
357 bool ok;
358 bool hadImageBuffer = hasImageBuffer();
360 int w = getAttribute(widthAttr).toInt(&ok);
361 if (!ok || w < 0)
362 w = DefaultWidth;
364 int h = getAttribute(heightAttr).toInt(&ok);
365 if (!ok || h < 0)
366 h = DefaultHeight;
368 if (m_context && m_context->is2d())
369 m_context->reset();
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());
381 return;
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);
396 if (hadImageBuffer)
397 layoutObject->setShouldDoFullPaintInvalidation();
401 for (CanvasObserver* canvasObserver : m_observers)
402 canvasObserver->canvasResized(this);
405 bool HTMLCanvasElement::paintsIntoCanvasBuffer() const
407 ASSERT(m_context);
409 if (!m_context->isAccelerated())
410 return true;
412 if (layoutBox() && layoutBox()->hasAcceleratedCompositing())
413 return false;
415 return true;
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.
421 if (!m_context)
422 return;
423 if (!paintsIntoCanvasBuffer() && !document().printing())
424 return;
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);
432 } else {
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)
454 m_size = size;
455 m_didFailToCreateImageBuffer = false;
456 discardImageBuffer();
457 clearCopiedImage();
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;
482 if (is3D()) {
483 // Get non-premultiplied data because of inaccurate premultiplied alpha conversion of buffer()->toDataURL().
484 imageData = m_context->paintRenderingResultsToImageData(sourceBuffer);
485 if (imageData)
486 return imageData;
488 m_context->paintRenderingResultsToCanvas(sourceBuffer);
489 imageData = ImageData::create(m_size);
490 RefPtr<SkImage> snapshot = buffer()->newSkImageSnapshot();
491 if (snapshot) {
492 SkImageInfo imageInfo = SkImageInfo::Make(width(), height(), kRGBA_8888_SkColorType, kUnpremul_SkAlphaType);
493 snapshot->readPixels(imageInfo, imageData->data()->data(), imageInfo.minRowBytes(), 0, 0);
495 return imageData;
498 imageData = ImageData::create(m_size);
500 if (!m_context)
501 return imageData;
503 ASSERT(m_context->is2d());
504 RefPtr<SkImage> snapshot = buffer()->newSkImageSnapshot();
505 if (snapshot) {
506 SkImageInfo imageInfo = SkImageInfo::Make(width(), height(), kRGBA_8888_SkColorType, kUnpremul_SkAlphaType);
507 snapshot->readPixels(imageInfo, imageData->data()->data(), imageInfo.minRowBytes(), 0, 0);
510 return imageData;
513 String HTMLCanvasElement::toDataURLInternal(const String& mimeType, const double* quality, SourceDrawingBuffer sourceBuffer) const
515 if (!isPaintable())
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.");
530 return String();
532 double quality;
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.");
548 return;
551 File* resultBlob = nullptr;
552 if (!isPaintable()) {
553 // If the canvas element's bitmap has no pixels
554 return;
557 double quality;
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())
588 return false;
589 return m_originClean;
592 bool HTMLCanvasElement::shouldAccelerate(const IntSize& size) const
594 if (m_context && !m_context->is2d())
595 return false;
597 if (m_accelerationDisabled)
598 return false;
600 if (RuntimeEnabledFeatures::forceDisplayList2dCanvasEnabled())
601 return false;
603 Settings* settings = document().settings();
604 if (!settings || !settings->accelerated2dCanvasEnabled())
605 return false;
607 int canvasPixelCount = size.width() * size.height();
609 if (RuntimeEnabledFeatures::displayList2dCanvasEnabled()) {
610 #if 0
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())
617 return false;
618 #endif
619 // If the GPU resources would be very expensive, prefer a display list.
620 if (canvasPixelCount > ExpensiveCanvasHeuristicParameters::PreferDisplayListOverGpuSizeThreshold)
621 return false;
624 // Do not use acceleration for small canvas.
625 if (canvasPixelCount < settings->minimumAccelerated2dCanvasSize())
626 return false;
628 if (!Platform::current()->canAccelerate2dCanvas())
629 return false;
631 return true;
634 class UnacceleratedSurfaceFactory : public RecordingImageBufferFallbackSurfaceFactory {
635 public:
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())
647 return true;
649 if (!RuntimeEnabledFeatures::displayList2dCanvasEnabled())
650 return false;
652 return true;
655 PassOwnPtr<ImageBufferSurface> HTMLCanvasElement::createImageBufferSurface(const IntSize& deviceSize, int* msaaSampleCount)
657 OpacityMode opacityMode = !m_context || m_context->hasAlpha() ? NonOpaque : Opaque;
659 *msaaSampleCount = 0;
660 if (is3D()) {
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()))
707 return;
709 int msaaSampleCount = 0;
710 OwnPtr<ImageBufferSurface> surface;
711 if (externalSurface) {
712 surface = externalSurface;
713 } else {
714 surface = createImageBufferSurface(size(), &msaaSampleCount);
716 m_imageBuffer = ImageBuffer::create(surface.release());
717 if (!m_imageBuffer)
718 return;
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();
729 if (is3D()) {
730 // Early out for WebGL canvases
731 return;
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);
741 if (m_context)
742 setNeedsCompositingUpdate();
745 void HTMLCanvasElement::notifySurfaceInvalid()
747 if (m_context && m_context->is2d())
748 m_context->loseContext(CanvasRenderingContext::RealLostContext);
751 DEFINE_TRACE(HTMLCanvasElement)
753 #if ENABLE(OILPAN)
754 visitor->trace(m_observers);
755 visitor->trace(m_context);
756 #endif
757 DocumentVisibilityObserver::trace(visitor);
758 HTMLElement::trace(visitor);
761 void HTMLCanvasElement::updateExternallyAllocatedMemory() const
763 int bufferCount = 0;
764 if (m_imageBuffer) {
765 bufferCount++;
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.
771 bufferCount += 2;
774 if (m_copiedImage)
775 bufferCount++;
777 // Four bytes per pixel per buffer.
778 Checked<intptr_t, RecordOverflow> checkedExternallyAllocatedMemory = 4 * bufferCount;
779 if (is3D())
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
800 if (buffer())
801 m_imageBuffer->disableDeferral();
804 SkCanvas* HTMLCanvasElement::existingDrawingCanvas() const
806 if (!hasImageBuffer())
807 return nullptr;
809 return m_imageBuffer->canvas();
812 ImageBuffer* HTMLCanvasElement::buffer() const
814 ASSERT(m_context);
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()
830 ASSERT(m_context);
831 if ((hasImageBuffer() && !m_imageBuffer->isAccelerated()) || m_didFailToCreateImageBuffer)
832 return;
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
841 if (!isPaintable())
842 return nullptr;
843 if (!m_context)
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()
866 if (m_copiedImage) {
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)
880 if (!m_context)
881 return;
882 bool hidden = visibility != PageVisibilityStateVisible;
883 m_context->setIsHidden(hidden);
884 if (hidden) {
885 clearCopiedImage();
886 if (is3D()) {
887 discardImageBuffer();
892 void HTMLCanvasElement::styleDidChange(const ComputedStyle* oldStyle, const ComputedStyle& newStyle)
894 if (m_context)
895 m_context->styleDidChange(oldStyle, newStyle);
898 void HTMLCanvasElement::didMoveToNewDocument(Document& oldDocument)
900 setObservedDocument(document());
901 if (m_context)
902 m_context->didMoveToNewDocument(&document());
903 HTMLElement::didMoveToNewDocument(oldDocument);
906 PassRefPtr<Image> HTMLCanvasElement::getSourceImageForCanvas(SourceImageStatus* status) const
908 if (!width() || !height()) {
909 *status = ZeroSizeCanvasSourceImageStatus;
910 return nullptr;
913 if (!isPaintable()) {
914 *status = InvalidSourceImageStatus;
915 return nullptr;
918 if (!m_context) {
919 *status = NormalSourceImageStatus;
920 return createTransparentImage(size());
923 if (m_context->is3d()) {
924 m_context->paintRenderingResultsToCanvas(BackBuffer);
927 RefPtr<SkImage> image = buffer()->newSkImageSnapshot();
928 if (image) {
929 *status = NormalSourceImageStatus;
930 return StaticBitmapImage::create(image.release());
933 *status = InvalidSourceImageStatus;
934 return nullptr;
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();
952 } // blink