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