Replace WTF::move with WTFMove
[WebKit-https.git] / Source / WebCore / html / HTMLCanvasElement.cpp
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.
5  *
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.
14  *
15  * THIS SOFTWARE IS PROVIDED BY APPLE 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 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. 
26  */
27
28 #include "config.h"
29 #include "HTMLCanvasElement.h"
30
31 #include "CanvasGradient.h"
32 #include "CanvasPattern.h"
33 #include "CanvasRenderingContext2D.h"
34 #include "Chrome.h"
35 #include "ChromeClient.h"
36 #include "Document.h"
37 #include "ExceptionCode.h"
38 #include "Frame.h"
39 #include "FrameLoaderClient.h"
40 #include "GeometryUtilities.h"
41 #include "GraphicsContext.h"
42 #include "HTMLNames.h"
43 #include "ImageData.h"
44 #include "MIMETypeRegistry.h"
45 #include "Page.h"
46 #include "RenderHTMLCanvas.h"
47 #include "ScriptController.h"
48 #include "Settings.h"
49 #include <math.h>
50 #include <wtf/RAMSize.h>
51
52 #include <runtime/JSCInlines.h>
53 #include <runtime/JSLock.h>
54
55 #if ENABLE(WEBGL)    
56 #include "WebGLContextAttributes.h"
57 #include "WebGLRenderingContextBase.h"
58 #endif
59
60 namespace WebCore {
61
62 using namespace HTMLNames;
63
64 // These values come from the WhatWG/W3C HTML spec.
65 static const int DefaultWidth = 300;
66 static const int DefaultHeight = 150;
67
68 // Firefox limits width/height to 32767 pixels, but slows down dramatically before it
69 // reaches that limit. We limit by area instead, giving us larger maximum dimensions,
70 // in exchange for a smaller maximum canvas size. The maximum canvas size is in device pixels.
71 #if PLATFORM(IOS)
72 static const unsigned MaxCanvasArea = 4096 * 4096;
73 #elif PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101100
74 static const unsigned MaxCanvasArea = 8192 * 8192;
75 #else
76 static const unsigned MaxCanvasArea = 16384 * 16384;
77 #endif
78
79 static size_t activePixelMemory = 0;
80
81 HTMLCanvasElement::HTMLCanvasElement(const QualifiedName& tagName, Document& document)
82     : HTMLElement(tagName, document)
83     , m_size(DefaultWidth, DefaultHeight)
84     , m_rendererIsCanvas(false)
85     , m_ignoreReset(false)
86     , m_originClean(true)
87     , m_hasCreatedImageBuffer(false)
88     , m_didClearImageBuffer(false)
89 {
90     ASSERT(hasTagName(canvasTag));
91 }
92
93 Ref<HTMLCanvasElement> HTMLCanvasElement::create(Document& document)
94 {
95     return adoptRef(*new HTMLCanvasElement(canvasTag, document));
96 }
97
98 Ref<HTMLCanvasElement> HTMLCanvasElement::create(const QualifiedName& tagName, Document& document)
99 {
100     return adoptRef(*new HTMLCanvasElement(tagName, document));
101 }
102
103 static void removeFromActivePixelMemory(size_t pixelsReleased)
104 {
105     if (!pixelsReleased)
106         return;
107
108     if (pixelsReleased < activePixelMemory)
109         activePixelMemory -= pixelsReleased;
110     else
111         activePixelMemory = 0;
112 }
113     
114 HTMLCanvasElement::~HTMLCanvasElement()
115 {
116     for (auto& observer : m_observers)
117         observer->canvasDestroyed(*this);
118
119     m_context = nullptr; // Ensure this goes away before the ImageBuffer.
120
121     releaseImageBufferAndContext();
122 }
123
124 void HTMLCanvasElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
125 {
126     if (name == widthAttr || name == heightAttr)
127         reset();
128     HTMLElement::parseAttribute(name, value);
129 }
130
131 RenderPtr<RenderElement> HTMLCanvasElement::createElementRenderer(Ref<RenderStyle>&& style, const RenderTreePosition& insertionPosition)
132 {
133     Frame* frame = document().frame();
134     if (frame && frame->script().canExecuteScripts(NotAboutToExecuteScript)) {
135         m_rendererIsCanvas = true;
136         return createRenderer<RenderHTMLCanvas>(*this, WTFMove(style));
137     }
138
139     m_rendererIsCanvas = false;
140     return HTMLElement::createElementRenderer(WTFMove(style), insertionPosition);
141 }
142
143 bool HTMLCanvasElement::canContainRangeEndPoint() const
144 {
145     return false;
146 }
147
148 bool HTMLCanvasElement::canStartSelection() const
149 {
150     return false;
151 }
152
153 void HTMLCanvasElement::addObserver(CanvasObserver& observer)
154 {
155     m_observers.add(&observer);
156 }
157
158 void HTMLCanvasElement::removeObserver(CanvasObserver& observer)
159 {
160     m_observers.remove(&observer);
161 }
162
163 void HTMLCanvasElement::setHeight(int value)
164 {
165     setIntegralAttribute(heightAttr, value);
166 }
167
168 void HTMLCanvasElement::setWidth(int value)
169 {
170     setIntegralAttribute(widthAttr, value);
171 }
172
173 #if ENABLE(WEBGL)
174 static bool requiresAcceleratedCompositingForWebGL()
175 {
176 #if PLATFORM(GTK) || PLATFORM(EFL)
177     return false;
178 #else
179     return true;
180 #endif
181
182 }
183 static bool shouldEnableWebGL(Settings* settings)
184 {
185     if (!settings)
186         return false;
187
188     if (!settings->webGLEnabled())
189         return false;
190
191     if (!requiresAcceleratedCompositingForWebGL())
192         return true;
193
194     return settings->acceleratedCompositingEnabled();
195 }
196 #endif
197
198 static inline size_t maxActivePixelMemory()
199 {
200     static size_t maxPixelMemory;
201     static std::once_flag onceFlag;
202     std::call_once(onceFlag, [] {
203         maxPixelMemory = std::max(ramSize() / 4, 2151 * MB);
204     });
205     return maxPixelMemory;
206 }
207
208 CanvasRenderingContext* HTMLCanvasElement::getContext(const String& type, CanvasContextAttributes* attrs)
209 {
210     // A Canvas can either be "2D" or "webgl" but never both. If you request a 2D canvas and the existing
211     // context is already 2D, just return that. If the existing context is WebGL, then destroy it
212     // before creating a new 2D context. Vice versa when requesting a WebGL canvas. Requesting a
213     // context with any other type string will destroy any existing context.
214     
215     // FIXME: The code depends on the context not going away once created, to prevent JS from
216     // seeing a dangling pointer. So for now we will disallow the context from being changed
217     // once it is created. https://bugs.webkit.org/show_bug.cgi?id=117095
218     if (is2dType(type)) {
219         if (m_context && !m_context->is2d())
220             return nullptr;
221         if (!m_context) {
222             bool usesDashbardCompatibilityMode = false;
223 #if ENABLE(DASHBOARD_SUPPORT)
224             if (Settings* settings = document().settings())
225                 usesDashbardCompatibilityMode = settings->usesDashboardBackwardCompatibilityMode();
226 #endif
227
228             // Make sure we don't use more pixel memory than the system can support.
229             size_t requestedPixelMemory = 4 * width() * height();
230             if (activePixelMemory + requestedPixelMemory > maxActivePixelMemory()) {
231                 StringBuilder stringBuilder;
232                 stringBuilder.appendLiteral("Total canvas memory use exceeds the maximum limit (");
233                 stringBuilder.appendNumber(maxActivePixelMemory() / 1024 / 1024);
234                 stringBuilder.appendLiteral(" MB).");
235                 document().addConsoleMessage(MessageSource::JS, MessageLevel::Warning, stringBuilder.toString());
236                 return nullptr;
237             }
238
239             m_context = std::make_unique<CanvasRenderingContext2D>(this, document().inQuirksMode(), usesDashbardCompatibilityMode);
240 #if USE(IOSURFACE_CANVAS_BACKING_STORE) || ENABLE(ACCELERATED_2D_CANVAS)
241             // Need to make sure a RenderLayer and compositing layer get created for the Canvas
242             setNeedsStyleRecalc(SyntheticStyleChange);
243 #endif
244         }
245         return m_context.get();
246     }
247 #if ENABLE(WEBGL)
248     if (shouldEnableWebGL(document().settings())) {
249
250         if (is3dType(type)) {
251             if (m_context && !m_context->is3d())
252                 return nullptr;
253             if (!m_context) {
254                 m_context = WebGLRenderingContextBase::create(this, static_cast<WebGLContextAttributes*>(attrs), type);
255                 if (m_context) {
256                     // Need to make sure a RenderLayer and compositing layer get created for the Canvas
257                     setNeedsStyleRecalc(SyntheticStyleChange);
258                 }
259             }
260             return m_context.get();
261         }
262     }
263 #else
264     UNUSED_PARAM(attrs);
265 #endif
266     return nullptr;
267 }
268     
269 bool HTMLCanvasElement::probablySupportsContext(const String& type, CanvasContextAttributes*)
270 {
271     // FIXME: Provide implementation that accounts for attributes. Bugzilla bug 117093
272     // https://bugs.webkit.org/show_bug.cgi?id=117093
273
274     // FIXME: The code depends on the context not going away once created (as getContext
275     // is implemented under this assumption) https://bugs.webkit.org/show_bug.cgi?id=117095
276     if (is2dType(type))
277         return !m_context || m_context->is2d();
278
279 #if ENABLE(WEBGL)
280     if (shouldEnableWebGL(document().settings())) {
281         if (is3dType(type))
282             return !m_context || m_context->is3d();
283     }
284 #endif
285     return false;
286 }
287
288 bool HTMLCanvasElement::is2dType(const String& type)
289 {
290     return type == "2d";
291 }
292
293 #if ENABLE(WEBGL)
294 bool HTMLCanvasElement::is3dType(const String& type)
295 {
296     // Retain support for the legacy "webkit-3d" name.
297     return type == "webgl" || type == "experimental-webgl"
298 #if ENABLE(WEBGL2)
299         || type == "experimental-webgl2"
300 #endif
301         || type == "webkit-3d";
302 }
303 #endif
304
305 void HTMLCanvasElement::didDraw(const FloatRect& rect)
306 {
307     clearCopiedImage();
308
309     if (RenderBox* ro = renderBox()) {
310         FloatRect destRect = ro->contentBoxRect();
311         FloatRect r = mapRect(rect, FloatRect(0, 0, size().width(), size().height()), destRect);
312         r.intersect(destRect);
313         if (r.isEmpty() || m_dirtyRect.contains(r))
314             return;
315
316         m_dirtyRect.unite(r);
317         ro->repaintRectangle(enclosingIntRect(m_dirtyRect));
318     }
319
320     notifyObserversCanvasChanged(rect);
321 }
322
323 void HTMLCanvasElement::notifyObserversCanvasChanged(const FloatRect& rect)
324 {
325     for (auto& observer : m_observers)
326         observer->canvasChanged(*this, rect);
327 }
328
329 void HTMLCanvasElement::reset()
330 {
331     if (m_ignoreReset)
332         return;
333
334     bool ok;
335     bool hadImageBuffer = hasCreatedImageBuffer();
336
337     int w = getAttribute(widthAttr).toInt(&ok);
338     if (!ok || w < 0)
339         w = DefaultWidth;
340
341     int h = getAttribute(heightAttr).toInt(&ok);
342     if (!ok || h < 0)
343         h = DefaultHeight;
344
345     if (m_contextStateSaver) {
346         // Reset to the initial graphics context state.
347         m_contextStateSaver->restore();
348         m_contextStateSaver->save();
349     }
350
351     if (m_context && m_context->is2d()) {
352         CanvasRenderingContext2D* context2D = static_cast<CanvasRenderingContext2D*>(m_context.get());
353         context2D->reset();
354     }
355
356     IntSize oldSize = size();
357     IntSize newSize(w, h);
358     // If the size of an existing buffer matches, we can just clear it instead of reallocating.
359     // This optimization is only done for 2D canvases for now.
360     if (m_hasCreatedImageBuffer && oldSize == newSize && m_context && m_context->is2d()) {
361         if (!m_didClearImageBuffer)
362             clearImageBuffer();
363         return;
364     }
365
366     setSurfaceSize(newSize);
367
368 #if ENABLE(WEBGL)
369     if (is3D() && oldSize != size())
370         static_cast<WebGLRenderingContextBase*>(m_context.get())->reshape(width(), height());
371 #endif
372
373     if (auto renderer = this->renderer()) {
374         if (m_rendererIsCanvas) {
375             if (oldSize != size()) {
376                 downcast<RenderHTMLCanvas>(*renderer).canvasSizeChanged();
377                 if (renderBox() && renderBox()->hasAcceleratedCompositing())
378                     renderBox()->contentChanged(CanvasChanged);
379             }
380             if (hadImageBuffer)
381                 renderer->repaint();
382         }
383     }
384
385     for (auto& observer : m_observers)
386         observer->canvasResized(*this);
387 }
388
389 bool HTMLCanvasElement::paintsIntoCanvasBuffer() const
390 {
391     ASSERT(m_context);
392 #if USE(IOSURFACE_CANVAS_BACKING_STORE)
393     if (m_context->is2d())
394         return true;
395 #endif
396
397     if (!m_context->isAccelerated())
398         return true;
399
400     if (renderBox() && renderBox()->hasAcceleratedCompositing())
401         return false;
402
403     return true;
404 }
405
406
407 void HTMLCanvasElement::paint(GraphicsContext& context, const LayoutRect& r, bool useLowQualityScale)
408 {
409     // Clear the dirty rect
410     m_dirtyRect = FloatRect();
411
412     if (context.paintingDisabled())
413         return;
414     
415     if (m_context) {
416         if (!paintsIntoCanvasBuffer() && !document().printing())
417             return;
418         m_context->paintRenderingResultsToCanvas();
419     }
420
421     if (hasCreatedImageBuffer()) {
422         ImageBuffer* imageBuffer = buffer();
423         if (imageBuffer) {
424             if (m_presentedImage) {
425                 ImageOrientationDescription orientationDescription;
426 #if ENABLE(CSS_IMAGE_ORIENTATION)
427                 orientationDescription.setImageOrientationEnum(renderer()->style().imageOrientation());
428 #endif 
429                 context.drawImage(*m_presentedImage, snappedIntRect(r), ImagePaintingOptions(orientationDescription, useLowQualityScale));
430             } else
431                 context.drawImageBuffer(*imageBuffer, snappedIntRect(r), useLowQualityScale);
432         }
433     }
434
435 #if ENABLE(WEBGL)    
436     if (is3D())
437         static_cast<WebGLRenderingContextBase*>(m_context.get())->markLayerComposited();
438 #endif
439 }
440
441 #if ENABLE(WEBGL)
442 bool HTMLCanvasElement::is3D() const
443 {
444     return m_context && m_context->is3d();
445 }
446 #endif
447
448 void HTMLCanvasElement::makeRenderingResultsAvailable()
449 {
450     if (m_context)
451         m_context->paintRenderingResultsToCanvas();
452 }
453
454 void HTMLCanvasElement::makePresentationCopy()
455 {
456     if (!m_presentedImage) {
457         // The buffer contains the last presented data, so save a copy of it.
458         m_presentedImage = buffer()->copyImage(CopyBackingStore, Unscaled);
459     }
460 }
461
462 void HTMLCanvasElement::clearPresentationCopy()
463 {
464     m_presentedImage = nullptr;
465 }
466
467 void HTMLCanvasElement::releaseImageBufferAndContext()
468 {
469     m_contextStateSaver = nullptr;
470     setImageBuffer(nullptr);
471 }
472     
473 void HTMLCanvasElement::setSurfaceSize(const IntSize& size)
474 {
475     m_size = size;
476     m_hasCreatedImageBuffer = false;
477     releaseImageBufferAndContext();
478     clearCopiedImage();
479 }
480
481 String HTMLCanvasElement::toEncodingMimeType(const String& mimeType)
482 {
483     String lowercaseMimeType = mimeType.lower();
484
485     // FIXME: Make isSupportedImageMIMETypeForEncoding threadsafe (to allow this method to be used on a worker thread).
486     if (mimeType.isNull() || !MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(lowercaseMimeType))
487         lowercaseMimeType = "image/png";
488
489     return lowercaseMimeType;
490 }
491
492 String HTMLCanvasElement::toDataURL(const String& mimeType, const double* quality, ExceptionCode& ec)
493 {
494     if (!m_originClean) {
495         ec = SECURITY_ERR;
496         return String();
497     }
498
499     if (m_size.isEmpty() || !buffer())
500         return String("data:,");
501
502     String encodingMimeType = toEncodingMimeType(mimeType);
503
504 #if USE(CG)
505     // Try to get ImageData first, as that may avoid lossy conversions.
506     RefPtr<ImageData> imageData = getImageData();
507
508     if (imageData)
509         return ImageDataToDataURL(*imageData, encodingMimeType, quality);
510 #endif
511
512     makeRenderingResultsAvailable();
513
514     return buffer()->toDataURL(encodingMimeType, quality);
515 }
516
517 RefPtr<ImageData> HTMLCanvasElement::getImageData()
518 {
519 #if ENABLE(WEBGL)
520     if (!is3D())
521         return nullptr;
522
523     WebGLRenderingContextBase* ctx = static_cast<WebGLRenderingContextBase*>(m_context.get());
524
525     return ctx->paintRenderingResultsToImageData();
526 #else
527     return nullptr;
528 #endif
529 }
530
531 FloatRect HTMLCanvasElement::convertLogicalToDevice(const FloatRect& logicalRect) const
532 {
533     FloatRect deviceRect(logicalRect);
534
535     float x = floorf(deviceRect.x());
536     float y = floorf(deviceRect.y());
537     float w = ceilf(deviceRect.maxX() - x);
538     float h = ceilf(deviceRect.maxY() - y);
539     deviceRect.setX(x);
540     deviceRect.setY(y);
541     deviceRect.setWidth(w);
542     deviceRect.setHeight(h);
543
544     return deviceRect;
545 }
546
547 FloatSize HTMLCanvasElement::convertLogicalToDevice(const FloatSize& logicalSize) const
548 {
549     float width = ceilf(logicalSize.width());
550     float height = ceilf(logicalSize.height());
551     return FloatSize(width, height);
552 }
553
554 FloatSize HTMLCanvasElement::convertDeviceToLogical(const FloatSize& deviceSize) const
555 {
556     float width = ceilf(deviceSize.width());
557     float height = ceilf(deviceSize.height());
558     return FloatSize(width, height);
559 }
560
561 SecurityOrigin* HTMLCanvasElement::securityOrigin() const
562 {
563     return document().securityOrigin();
564 }
565
566 bool HTMLCanvasElement::shouldAccelerate(const IntSize& size) const
567 {
568 #if USE(IOSURFACE_CANVAS_BACKING_STORE)
569     UNUSED_PARAM(size);
570     return document().settings() && document().settings()->canvasUsesAcceleratedDrawing();
571 #elif ENABLE(ACCELERATED_2D_CANVAS)
572     if (m_context && !m_context->is2d())
573         return false;
574
575     Settings* settings = document().settings();
576     if (!settings || !settings->accelerated2dCanvasEnabled())
577         return false;
578
579     // Do not use acceleration for small canvas.
580     if (size.width() * size.height() < settings->minimumAccelerated2dCanvasSize())
581         return false;
582
583     return true;
584 #else
585     UNUSED_PARAM(size);
586     return false;
587 #endif
588 }
589
590 size_t HTMLCanvasElement::memoryCost() const
591 {
592     if (!m_imageBuffer)
593         return 0;
594     return 4 * m_imageBuffer->internalSize().width() * m_imageBuffer->internalSize().height();
595 }
596
597 void HTMLCanvasElement::createImageBuffer() const
598 {
599     ASSERT(!m_imageBuffer);
600
601     m_hasCreatedImageBuffer = true;
602     m_didClearImageBuffer = true;
603
604     FloatSize logicalSize = size();
605     FloatSize deviceSize = convertLogicalToDevice(logicalSize);
606     if (!deviceSize.isExpressibleAsIntSize())
607         return;
608
609     if (deviceSize.width() * deviceSize.height() > MaxCanvasArea) {
610         StringBuilder stringBuilder;
611         stringBuilder.appendLiteral("Canvas area exceeds the maximum limit (width * height > ");
612         stringBuilder.appendNumber(MaxCanvasArea);
613         stringBuilder.appendLiteral(").");
614         document().addConsoleMessage(MessageSource::JS, MessageLevel::Warning, stringBuilder.toString());
615         return;
616     }
617     
618     // Make sure we don't use more pixel memory than the system can support.
619     size_t requestedPixelMemory = 4 * width() * height();
620     if (activePixelMemory + requestedPixelMemory > maxActivePixelMemory()) {
621         StringBuilder stringBuilder;
622         stringBuilder.appendLiteral("Total canvas memory use exceeds the maximum limit (");
623         stringBuilder.appendNumber(maxActivePixelMemory() / 1024 / 1024);
624         stringBuilder.appendLiteral(" MB).");
625         document().addConsoleMessage(MessageSource::JS, MessageLevel::Warning, stringBuilder.toString());
626         return;
627     }
628
629     IntSize bufferSize(deviceSize.width(), deviceSize.height());
630     if (!bufferSize.width() || !bufferSize.height())
631         return;
632
633     RenderingMode renderingMode = shouldAccelerate(bufferSize) ? Accelerated : Unaccelerated;
634
635     setImageBuffer(ImageBuffer::create(size(), renderingMode));
636     if (!m_imageBuffer)
637         return;
638     m_imageBuffer->context().setShadowsIgnoreTransforms(true);
639     m_imageBuffer->context().setImageInterpolationQuality(DefaultInterpolationQuality);
640     if (document().settings() && !document().settings()->antialiased2dCanvasEnabled())
641         m_imageBuffer->context().setShouldAntialias(false);
642     m_imageBuffer->context().setStrokeThickness(1);
643     m_contextStateSaver = std::make_unique<GraphicsContextStateSaver>(m_imageBuffer->context());
644
645     JSC::JSLockHolder lock(scriptExecutionContext()->vm());
646     scriptExecutionContext()->vm().heap.reportExtraMemoryAllocated(memoryCost());
647
648 #if USE(IOSURFACE_CANVAS_BACKING_STORE) || ENABLE(ACCELERATED_2D_CANVAS)
649     if (m_context && m_context->is2d())
650         // Recalculate compositing requirements if acceleration state changed.
651         const_cast<HTMLCanvasElement*>(this)->setNeedsStyleRecalc(SyntheticStyleChange);
652 #endif
653 }
654
655 void HTMLCanvasElement::setImageBuffer(std::unique_ptr<ImageBuffer> buffer) const
656 {
657     removeFromActivePixelMemory(memoryCost());
658
659     m_imageBuffer = WTFMove(buffer);
660
661     activePixelMemory += memoryCost();
662 }
663
664 GraphicsContext* HTMLCanvasElement::drawingContext() const
665 {
666     return buffer() ? &m_imageBuffer->context() : nullptr;
667 }
668
669 GraphicsContext* HTMLCanvasElement::existingDrawingContext() const
670 {
671     if (!m_hasCreatedImageBuffer)
672         return nullptr;
673
674     return drawingContext();
675 }
676
677 ImageBuffer* HTMLCanvasElement::buffer() const
678 {
679     if (!m_hasCreatedImageBuffer)
680         createImageBuffer();
681     return m_imageBuffer.get();
682 }
683
684 Image* HTMLCanvasElement::copiedImage() const
685 {
686     if (!m_copiedImage && buffer()) {
687         if (m_context)
688             m_context->paintRenderingResultsToCanvas();
689         m_copiedImage = buffer()->copyImage(CopyBackingStore, Unscaled);
690     }
691     return m_copiedImage.get();
692 }
693
694 void HTMLCanvasElement::clearImageBuffer() const
695 {
696     ASSERT(m_hasCreatedImageBuffer);
697     ASSERT(!m_didClearImageBuffer);
698     ASSERT(m_context);
699
700     m_didClearImageBuffer = true;
701
702     if (m_context->is2d()) {
703         CanvasRenderingContext2D* context2D = static_cast<CanvasRenderingContext2D*>(m_context.get());
704         // No need to undo transforms/clip/etc. because we are called right after the context is reset.
705         context2D->clearRect(0, 0, width(), height());
706     }
707 }
708
709 void HTMLCanvasElement::clearCopiedImage()
710 {
711     m_copiedImage = nullptr;
712     m_didClearImageBuffer = false;
713 }
714
715 AffineTransform HTMLCanvasElement::baseTransform() const
716 {
717     ASSERT(m_hasCreatedImageBuffer);
718     FloatSize unscaledSize = size();
719     FloatSize deviceSize = convertLogicalToDevice(unscaledSize);
720     IntSize size(deviceSize.width(), deviceSize.height());
721     AffineTransform transform;
722     if (size.width() && size.height())
723         transform.scaleNonUniform(size.width() / unscaledSize.width(), size.height() / unscaledSize.height());
724     return m_imageBuffer->baseTransform() * transform;
725 }
726
727 }