1 /* 2 * Copyright (C) 2004, 2006, 2007 Apple Inc. All rights reserved. 3 * Copyright (C) 2007 Alp Toker <alp (at) 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 "core/html/HTMLCanvasElement.h" 30 31 #include "bindings/v8/ExceptionMessages.h" 32 #include "bindings/v8/ExceptionState.h" 33 #include "bindings/v8/ScriptController.h" 34 #include "core/HTMLNames.h" 35 #include "core/dom/Document.h" 36 #include "core/dom/ExceptionCode.h" 37 #include "core/frame/LocalFrame.h" 38 #include "core/frame/Settings.h" 39 #include "core/html/ImageData.h" 40 #include "core/html/canvas/Canvas2DContextAttributes.h" 41 #include "core/html/canvas/CanvasRenderingContext2D.h" 42 #include "core/html/canvas/WebGLContextAttributes.h" 43 #include "core/html/canvas/WebGLContextEvent.h" 44 #include "core/html/canvas/WebGLRenderingContext.h" 45 #include "core/rendering/RenderHTMLCanvas.h" 46 #include "platform/MIMETypeRegistry.h" 47 #include "platform/RuntimeEnabledFeatures.h" 48 #include "platform/graphics/Canvas2DImageBufferSurface.h" 49 #include "platform/graphics/GraphicsContextStateSaver.h" 50 #include "platform/graphics/ImageBuffer.h" 51 #include "platform/graphics/UnacceleratedImageBufferSurface.h" 52 #include "platform/graphics/gpu/WebGLImageBufferSurface.h" 53 #include "platform/transforms/AffineTransform.h" 54 #include "public/platform/Platform.h" 55 #include <math.h> 56 #include <v8.h> 57 58 namespace WebCore { 59 60 using namespace HTMLNames; 61 62 // These values come from the WhatWG spec. 63 static const int DefaultWidth = 300; 64 static const int DefaultHeight = 150; 65 66 // Firefox limits width/height to 32767 pixels, but slows down dramatically before it 67 // reaches that limit. We limit by area instead, giving us larger maximum dimensions, 68 // in exchange for a smaller maximum canvas size. 69 static const int MaxCanvasArea = 32768 * 8192; // Maximum canvas area in CSS pixels 70 71 //In Skia, we will also limit width/height to 32767. 72 static const int MaxSkiaDim = 32767; // Maximum width/height in CSS pixels. 73 74 DEFINE_EMPTY_DESTRUCTOR_WILL_BE_REMOVED(CanvasObserver); 75 76 inline HTMLCanvasElement::HTMLCanvasElement(Document& document) 77 : HTMLElement(canvasTag, document) 78 , DocumentVisibilityObserver(document) 79 , m_size(DefaultWidth, DefaultHeight) 80 , m_ignoreReset(false) 81 , m_accelerationDisabled(false) 82 , m_externallyAllocatedMemory(0) 83 , m_originClean(true) 84 , m_didFailToCreateImageBuffer(false) 85 , m_didClearImageBuffer(false) 86 { 87 ScriptWrappable::init(this); 88 } 89 90 DEFINE_NODE_FACTORY(HTMLCanvasElement) 91 92 HTMLCanvasElement::~HTMLCanvasElement() 93 { 94 v8::Isolate::GetCurrent()->AdjustAmountOfExternalAllocatedMemory(-m_externallyAllocatedMemory); 95 #if !ENABLE(OILPAN) 96 HashSet<RawPtr<CanvasObserver> >::iterator end = m_observers.end(); 97 for (HashSet<RawPtr<CanvasObserver> >::iterator it = m_observers.begin(); it != end; ++it) 98 (*it)->canvasDestroyed(this); 99 // Ensure these go away before the ImageBuffer. 100 m_contextStateSaver.clear(); 101 m_context.clear(); 102 #endif 103 } 104 105 void HTMLCanvasElement::parseAttribute(const QualifiedName& name, const AtomicString& value) 106 { 107 if (name == widthAttr || name == heightAttr) 108 reset(); 109 HTMLElement::parseAttribute(name, value); 110 } 111 112 RenderObject* HTMLCanvasElement::createRenderer(RenderStyle* style) 113 { 114 LocalFrame* frame = document().frame(); 115 if (frame && frame->script().canExecuteScripts(NotAboutToExecuteScript)) 116 return new RenderHTMLCanvas(this); 117 return HTMLElement::createRenderer(style); 118 } 119 120 Node::InsertionNotificationRequest HTMLCanvasElement::insertedInto(ContainerNode* node) 121 { 122 setIsInCanvasSubtree(true); 123 return HTMLElement::insertedInto(node); 124 } 125 126 void HTMLCanvasElement::addObserver(CanvasObserver* observer) 127 { 128 m_observers.add(observer); 129 } 130 131 void HTMLCanvasElement::removeObserver(CanvasObserver* observer) 132 { 133 m_observers.remove(observer); 134 } 135 136 void HTMLCanvasElement::setHeight(int value) 137 { 138 setIntegralAttribute(heightAttr, value); 139 } 140 141 void HTMLCanvasElement::setWidth(int value) 142 { 143 setIntegralAttribute(widthAttr, value); 144 } 145 146 CanvasRenderingContext* HTMLCanvasElement::getContext(const String& type, CanvasContextAttributes* attrs) 147 { 148 // A Canvas can either be "2D" or "webgl" but never both. If you request a 2D canvas and the existing 149 // context is already 2D, just return that. If the existing context is WebGL, then destroy it 150 // before creating a new 2D context. Vice versa when requesting a WebGL canvas. Requesting a 151 // context with any other type string will destroy any existing context. 152 enum ContextType { 153 Context2d, 154 ContextWebkit3d, 155 ContextExperimentalWebgl, 156 ContextWebgl, 157 // Only add new items to the end and keep the order of existing items. 158 ContextTypeCount, 159 }; 160 161 // FIXME - The code depends on the context not going away once created, to prevent JS from 162 // seeing a dangling pointer. So for now we will disallow the context from being changed 163 // once it is created. 164 if (type == "2d") { 165 if (m_context && !m_context->is2d()) 166 return 0; 167 if (!m_context) { 168 blink::Platform::current()->histogramEnumeration("Canvas.ContextType", Context2d, ContextTypeCount); 169 m_context = CanvasRenderingContext2D::create(this, static_cast<Canvas2DContextAttributes*>(attrs), document().inQuirksMode()); 170 setNeedsCompositingUpdate(); 171 } 172 return m_context.get(); 173 } 174 175 // Accept the the provisional "experimental-webgl" or official "webgl" context ID. 176 ContextType contextType; 177 bool is3dContext = true; 178 if (type == "experimental-webgl") 179 contextType = ContextExperimentalWebgl; 180 else if (type == "webgl") 181 contextType = ContextWebgl; 182 else 183 is3dContext = false; 184 185 if (is3dContext) { 186 if (m_context && !m_context->is3d()) { 187 dispatchEvent(WebGLContextEvent::create(EventTypeNames::webglcontextcreationerror, false, true, "Canvas has an existing, non-WebGL context")); 188 return 0; 189 } 190 if (!m_context) { 191 blink::Platform::current()->histogramEnumeration("Canvas.ContextType", contextType, ContextTypeCount); 192 m_context = WebGLRenderingContext::create(this, static_cast<WebGLContextAttributes*>(attrs)); 193 setNeedsCompositingUpdate(); 194 updateExternallyAllocatedMemory(); 195 } 196 return m_context.get(); 197 } 198 return 0; 199 } 200 201 void HTMLCanvasElement::didDraw(const FloatRect& rect) 202 { 203 clearCopiedImage(); 204 205 if (RenderBox* ro = renderBox()) { 206 FloatRect destRect = ro->contentBoxRect(); 207 FloatRect r = mapRect(rect, FloatRect(0, 0, size().width(), size().height()), destRect); 208 r.intersect(destRect); 209 if (r.isEmpty() || m_dirtyRect.contains(r)) 210 return; 211 212 m_dirtyRect.unite(r); 213 ro->invalidatePaintRectangle(enclosingIntRect(m_dirtyRect)); 214 } 215 216 notifyObserversCanvasChanged(rect); 217 } 218 219 void HTMLCanvasElement::notifyObserversCanvasChanged(const FloatRect& rect) 220 { 221 WillBeHeapHashSet<RawPtrWillBeWeakMember<CanvasObserver> >::iterator end = m_observers.end(); 222 for (WillBeHeapHashSet<RawPtrWillBeWeakMember<CanvasObserver> >::iterator it = m_observers.begin(); it != end; ++it) 223 (*it)->canvasChanged(this, rect); 224 } 225 226 void HTMLCanvasElement::reset() 227 { 228 if (m_ignoreReset) 229 return; 230 231 bool ok; 232 bool hadImageBuffer = hasImageBuffer(); 233 234 int w = getAttribute(widthAttr).toInt(&ok); 235 if (!ok || w < 0) 236 w = DefaultWidth; 237 238 int h = getAttribute(heightAttr).toInt(&ok); 239 if (!ok || h < 0) 240 h = DefaultHeight; 241 242 if (m_contextStateSaver) { 243 // Reset to the initial graphics context state. 244 m_contextStateSaver->restore(); 245 m_contextStateSaver->save(); 246 } 247 248 if (m_context && m_context->is2d()) 249 toCanvasRenderingContext2D(m_context.get())->reset(); 250 251 IntSize oldSize = size(); 252 IntSize newSize(w, h); 253 254 // If the size of an existing buffer matches, we can just clear it instead of reallocating. 255 // This optimization is only done for 2D canvases for now. 256 if (hadImageBuffer && oldSize == newSize && m_context && m_context->is2d()) { 257 if (!m_didClearImageBuffer) 258 clearImageBuffer(); 259 return; 260 } 261 262 setSurfaceSize(newSize); 263 264 if (m_context && m_context->is3d() && oldSize != size()) 265 toWebGLRenderingContext(m_context.get())->reshape(width(), height()); 266 267 if (RenderObject* renderer = this->renderer()) { 268 if (renderer->isCanvas()) { 269 if (oldSize != size()) { 270 toRenderHTMLCanvas(renderer)->canvasSizeChanged(); 271 if (renderBox() && renderBox()->hasAcceleratedCompositing()) 272 renderBox()->contentChanged(CanvasChanged); 273 } 274 if (hadImageBuffer) 275 renderer->paintInvalidationForWholeRenderer(); 276 } 277 } 278 279 WillBeHeapHashSet<RawPtrWillBeWeakMember<CanvasObserver> >::iterator end = m_observers.end(); 280 for (WillBeHeapHashSet<RawPtrWillBeWeakMember<CanvasObserver> >::iterator it = m_observers.begin(); it != end; ++it) 281 (*it)->canvasResized(this); 282 } 283 284 bool HTMLCanvasElement::paintsIntoCanvasBuffer() const 285 { 286 ASSERT(m_context); 287 288 if (!m_context->isAccelerated()) 289 return true; 290 291 if (renderBox() && renderBox()->hasAcceleratedCompositing()) 292 return false; 293 294 return true; 295 } 296 297 298 void HTMLCanvasElement::paint(GraphicsContext* context, const LayoutRect& r) 299 { 300 // Clear the dirty rect 301 m_dirtyRect = FloatRect(); 302 303 if (context->paintingDisabled()) 304 return; 305 306 if (m_context) { 307 if (!paintsIntoCanvasBuffer() && !document().printing()) 308 return; 309 m_context->paintRenderingResultsToCanvas(); 310 } 311 312 if (hasImageBuffer()) { 313 ImageBuffer* imageBuffer = buffer(); 314 if (imageBuffer) { 315 CompositeOperator compositeOperator = !m_context || m_context->hasAlpha() ? CompositeSourceOver : CompositeCopy; 316 if (m_presentedImage) 317 context->drawImage(m_presentedImage.get(), pixelSnappedIntRect(r), compositeOperator, DoNotRespectImageOrientation); 318 else 319 context->drawImageBuffer(imageBuffer, pixelSnappedIntRect(r), 0, compositeOperator); 320 } 321 } else { 322 // When alpha is false, we should draw to opaque black. 323 if (m_context && !m_context->hasAlpha()) 324 context->fillRect(FloatRect(r), Color(0, 0, 0)); 325 } 326 327 if (is3D()) 328 toWebGLRenderingContext(m_context.get())->markLayerComposited(); 329 } 330 331 bool HTMLCanvasElement::is3D() const 332 { 333 return m_context && m_context->is3d(); 334 } 335 336 void HTMLCanvasElement::makePresentationCopy() 337 { 338 if (!m_presentedImage) { 339 // The buffer contains the last presented data, so save a copy of it. 340 m_presentedImage = buffer()->copyImage(CopyBackingStore, Unscaled); 341 updateExternallyAllocatedMemory(); 342 } 343 } 344 345 void HTMLCanvasElement::clearPresentationCopy() 346 { 347 m_presentedImage.clear(); 348 updateExternallyAllocatedMemory(); 349 } 350 351 void HTMLCanvasElement::setSurfaceSize(const IntSize& size) 352 { 353 m_size = size; 354 m_didFailToCreateImageBuffer = false; 355 discardImageBuffer(); 356 clearCopiedImage(); 357 if (m_context && m_context->is2d()) { 358 CanvasRenderingContext2D* context2d = toCanvasRenderingContext2D(m_context.get()); 359 if (context2d->isContextLost()) { 360 context2d->restoreContext(); 361 } 362 } 363 } 364 365 String HTMLCanvasElement::toEncodingMimeType(const String& mimeType) 366 { 367 String lowercaseMimeType = mimeType.lower(); 368 369 // FIXME: Make isSupportedImageMIMETypeForEncoding threadsafe (to allow this method to be used on a worker thread). 370 if (mimeType.isNull() || !MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(lowercaseMimeType)) 371 lowercaseMimeType = "image/png"; 372 373 return lowercaseMimeType; 374 } 375 376 const AtomicString HTMLCanvasElement::imageSourceURL() const 377 { 378 return AtomicString(toDataURLInternal("image/png", 0, true)); 379 } 380 381 String HTMLCanvasElement::toDataURLInternal(const String& mimeType, const double* quality, bool isSaving) const 382 { 383 if (m_size.isEmpty() || !buffer()) 384 return String("data:,"); 385 386 String encodingMimeType = toEncodingMimeType(mimeType); 387 388 // Try to get ImageData first, as that may avoid lossy conversions. 389 RefPtrWillBeRawPtr<ImageData> imageData = getImageData(); 390 391 if (imageData) 392 return ImageDataToDataURL(ImageDataBuffer(imageData->size(), imageData->data()), encodingMimeType, quality); 393 394 if (m_context && m_context->is3d()) { 395 toWebGLRenderingContext(m_context.get())->setSavingImage(isSaving); 396 m_context->paintRenderingResultsToCanvas(); 397 toWebGLRenderingContext(m_context.get())->setSavingImage(false); 398 } 399 400 return buffer()->toDataURL(encodingMimeType, quality); 401 } 402 403 String HTMLCanvasElement::toDataURL(const String& mimeType, const double* quality, ExceptionState& exceptionState) const 404 { 405 if (!m_originClean) { 406 exceptionState.throwSecurityError("Tainted canvases may not be exported."); 407 return String(); 408 } 409 410 return toDataURLInternal(mimeType, quality); 411 } 412 413 PassRefPtrWillBeRawPtr<ImageData> HTMLCanvasElement::getImageData() const 414 { 415 if (!m_context || !m_context->is3d()) 416 return nullptr; 417 return toWebGLRenderingContext(m_context.get())->paintRenderingResultsToImageData(); 418 } 419 420 SecurityOrigin* HTMLCanvasElement::securityOrigin() const 421 { 422 return document().securityOrigin(); 423 } 424 425 bool HTMLCanvasElement::shouldAccelerate(const IntSize& size) const 426 { 427 if (m_context && !m_context->is2d()) 428 return false; 429 430 if (m_accelerationDisabled) 431 return false; 432 433 Settings* settings = document().settings(); 434 if (!settings || !settings->accelerated2dCanvasEnabled()) 435 return false; 436 437 // Do not use acceleration for small canvas. 438 if (size.width() * size.height() < settings->minimumAccelerated2dCanvasSize()) 439 return false; 440 441 if (!blink::Platform::current()->canAccelerate2dCanvas()) 442 return false; 443 444 return true; 445 } 446 447 PassOwnPtr<ImageBufferSurface> HTMLCanvasElement::createImageBufferSurface(const IntSize& deviceSize, int* msaaSampleCount) 448 { 449 OpacityMode opacityMode = !m_context || m_context->hasAlpha() ? NonOpaque : Opaque; 450 451 *msaaSampleCount = 0; 452 if (is3D()) 453 return adoptPtr(new WebGLImageBufferSurface(size(), opacityMode)); 454 455 if (shouldAccelerate(deviceSize)) { 456 if (document().settings()) 457 *msaaSampleCount = document().settings()->accelerated2dCanvasMSAASampleCount(); 458 OwnPtr<ImageBufferSurface> surface = adoptPtr(new Canvas2DImageBufferSurface(size(), opacityMode, *msaaSampleCount)); 459 if (surface->isValid()) 460 return surface.release(); 461 } 462 463 return adoptPtr(new UnacceleratedImageBufferSurface(size(), opacityMode)); 464 } 465 466 void HTMLCanvasElement::createImageBuffer() 467 { 468 createImageBufferInternal(); 469 if (m_didFailToCreateImageBuffer && m_context && m_context->is2d()) 470 toCanvasRenderingContext2D(m_context.get())->loseContext(); 471 } 472 473 void HTMLCanvasElement::createImageBufferInternal() 474 { 475 ASSERT(!m_imageBuffer); 476 ASSERT(!m_contextStateSaver); 477 478 m_didFailToCreateImageBuffer = true; 479 m_didClearImageBuffer = true; 480 481 IntSize deviceSize = size(); 482 if (deviceSize.width() * deviceSize.height() > MaxCanvasArea) 483 return; 484 485 if (deviceSize.width() > MaxSkiaDim || deviceSize.height() > MaxSkiaDim) 486 return; 487 488 if (!deviceSize.width() || !deviceSize.height()) 489 return; 490 491 int msaaSampleCount; 492 OwnPtr<ImageBufferSurface> surface = createImageBufferSurface(deviceSize, &msaaSampleCount); 493 if (!surface->isValid()) 494 return; 495 496 m_imageBuffer = ImageBuffer::create(surface.release()); 497 m_imageBuffer->setClient(this); 498 499 m_didFailToCreateImageBuffer = false; 500 501 updateExternallyAllocatedMemory(); 502 503 if (is3D()) { 504 // Early out for WebGL canvases 505 return; 506 } 507 508 m_imageBuffer->setClient(this); 509 m_imageBuffer->context()->setShouldClampToSourceRect(false); 510 m_imageBuffer->context()->disableAntialiasingOptimizationForHairlineImages(); 511 m_imageBuffer->context()->setImageInterpolationQuality(CanvasDefaultInterpolationQuality); 512 // Enabling MSAA overrides a request to disable antialiasing. This is true regardless of whether the 513 // rendering mode is accelerated or not. For consistency, we don't want to apply AA in accelerated 514 // canvases but not in unaccelerated canvases. 515 if (!msaaSampleCount && document().settings() && !document().settings()->antialiased2dCanvasEnabled()) 516 m_imageBuffer->context()->setShouldAntialias(false); 517 // GraphicsContext's defaults don't always agree with the 2d canvas spec. 518 // See CanvasRenderingContext2D::State::State() for more information. 519 m_imageBuffer->context()->setMiterLimit(10); 520 m_imageBuffer->context()->setStrokeThickness(1); 521 #if ASSERT_ENABLED 522 m_imageBuffer->context()->disableDestructionChecks(); // 2D canvas is allowed to leave context in an unfinalized state. 523 #endif 524 m_contextStateSaver = adoptPtr(new GraphicsContextStateSaver(*m_imageBuffer->context())); 525 526 if (m_context) 527 setNeedsCompositingUpdate(); 528 } 529 530 void HTMLCanvasElement::notifySurfaceInvalid() 531 { 532 if (m_context && m_context->is2d()) { 533 CanvasRenderingContext2D* context2d = toCanvasRenderingContext2D(m_context.get()); 534 context2d->loseContext(); 535 } 536 } 537 538 void HTMLCanvasElement::trace(Visitor* visitor) 539 { 540 visitor->trace(m_observers); 541 visitor->trace(m_context); 542 DocumentVisibilityObserver::trace(visitor); 543 HTMLElement::trace(visitor); 544 } 545 546 void HTMLCanvasElement::updateExternallyAllocatedMemory() const 547 { 548 int bufferCount = 0; 549 if (m_imageBuffer) 550 bufferCount++; 551 if (is3D()) 552 bufferCount += 2; 553 if (m_copiedImage) 554 bufferCount++; 555 if (m_presentedImage) 556 bufferCount++; 557 558 Checked<intptr_t, RecordOverflow> checkedExternallyAllocatedMemory = 4 * bufferCount; 559 checkedExternallyAllocatedMemory *= width(); 560 checkedExternallyAllocatedMemory *= height(); 561 intptr_t externallyAllocatedMemory; 562 if (checkedExternallyAllocatedMemory.safeGet(externallyAllocatedMemory) == CheckedState::DidOverflow) 563 externallyAllocatedMemory = std::numeric_limits<intptr_t>::max(); 564 565 // Subtracting two intptr_t that are known to be positive will never underflow. 566 v8::Isolate::GetCurrent()->AdjustAmountOfExternalAllocatedMemory(externallyAllocatedMemory - m_externallyAllocatedMemory); 567 m_externallyAllocatedMemory = externallyAllocatedMemory; 568 } 569 570 GraphicsContext* HTMLCanvasElement::drawingContext() const 571 { 572 return buffer() ? m_imageBuffer->context() : 0; 573 } 574 575 GraphicsContext* HTMLCanvasElement::existingDrawingContext() const 576 { 577 if (!hasImageBuffer()) 578 return 0; 579 580 return drawingContext(); 581 } 582 583 ImageBuffer* HTMLCanvasElement::buffer() const 584 { 585 if (!hasImageBuffer() && !m_didFailToCreateImageBuffer) 586 const_cast<HTMLCanvasElement*>(this)->createImageBuffer(); 587 return m_imageBuffer.get(); 588 } 589 590 void HTMLCanvasElement::ensureUnacceleratedImageBuffer() 591 { 592 if ((hasImageBuffer() && !m_imageBuffer->isAccelerated()) || m_didFailToCreateImageBuffer) 593 return; 594 discardImageBuffer(); 595 OpacityMode opacityMode = !m_context || m_context->hasAlpha() ? NonOpaque : Opaque; 596 m_imageBuffer = ImageBuffer::create(size(), opacityMode); 597 m_didFailToCreateImageBuffer = !m_imageBuffer; 598 } 599 600 Image* HTMLCanvasElement::copiedImage() const 601 { 602 if (!m_copiedImage && buffer()) { 603 if (m_context) 604 m_context->paintRenderingResultsToCanvas(); 605 m_copiedImage = buffer()->copyImage(CopyBackingStore, Unscaled); 606 updateExternallyAllocatedMemory(); 607 } 608 return m_copiedImage.get(); 609 } 610 611 void HTMLCanvasElement::clearImageBuffer() 612 { 613 ASSERT(hasImageBuffer() && !m_didFailToCreateImageBuffer); 614 ASSERT(!m_didClearImageBuffer); 615 ASSERT(m_context); 616 617 m_didClearImageBuffer = true; 618 619 if (m_context->is2d()) { 620 // No need to undo transforms/clip/etc. because we are called right 621 // after the context is reset. 622 toCanvasRenderingContext2D(m_context.get())->clearRect(0, 0, width(), height()); 623 } 624 } 625 626 void HTMLCanvasElement::discardImageBuffer() 627 { 628 m_contextStateSaver.clear(); // uses context owned by m_imageBuffer 629 m_imageBuffer.clear(); 630 updateExternallyAllocatedMemory(); 631 } 632 633 bool HTMLCanvasElement::hasValidImageBuffer() const 634 { 635 return m_imageBuffer && m_imageBuffer->isSurfaceValid(); 636 } 637 638 void HTMLCanvasElement::clearCopiedImage() 639 { 640 m_copiedImage.clear(); 641 m_didClearImageBuffer = false; 642 updateExternallyAllocatedMemory(); 643 } 644 645 AffineTransform HTMLCanvasElement::baseTransform() const 646 { 647 ASSERT(hasImageBuffer() && !m_didFailToCreateImageBuffer); 648 return m_imageBuffer->baseTransform(); 649 } 650 651 void HTMLCanvasElement::didChangeVisibilityState(PageVisibilityState visibility) 652 { 653 if (hasImageBuffer()) { 654 bool hidden = visibility != PageVisibilityStateVisible; 655 if (hidden) { 656 clearCopiedImage(); 657 if (is3D()) { 658 discardImageBuffer(); 659 } 660 } 661 if (hasImageBuffer()) { 662 m_imageBuffer->setIsHidden(hidden); 663 } 664 } 665 } 666 667 void HTMLCanvasElement::didMoveToNewDocument(Document& oldDocument) 668 { 669 setObservedDocument(document()); 670 HTMLElement::didMoveToNewDocument(oldDocument); 671 } 672 673 PassRefPtr<Image> HTMLCanvasElement::getSourceImageForCanvas(SourceImageMode mode, SourceImageStatus* status) const 674 { 675 if (!width() || !height()) { 676 *status = ZeroSizeCanvasSourceImageStatus; 677 return nullptr; 678 } 679 680 if (!buffer()) { 681 *status = InvalidSourceImageStatus; 682 return nullptr; 683 } 684 685 if (mode == CopySourceImageIfVolatile) { 686 *status = NormalSourceImageStatus; 687 return copiedImage(); 688 } 689 690 if (m_context && m_context->is3d()) { 691 m_context->paintRenderingResultsToCanvas(); 692 *status = ExternalSourceImageStatus; 693 } else { 694 *status = NormalSourceImageStatus; 695 } 696 return m_imageBuffer->copyImage(DontCopyBackingStore, Unscaled); 697 } 698 699 bool HTMLCanvasElement::wouldTaintOrigin(SecurityOrigin*) const 700 { 701 return !originClean(); 702 } 703 704 FloatSize HTMLCanvasElement::sourceSize() const 705 { 706 return FloatSize(width(), height()); 707 } 708 709 } 710