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