OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2004, 2006, 2007 Apple Inc. All rights reserved. | 2 * Copyright (C) 2004, 2006, 2007 Apple Inc. All rights reserved. |
3 * Copyright (C) 2007 Alp Toker <alp@atoker.com> | 3 * Copyright (C) 2007 Alp Toker <alp@atoker.com> |
4 * Copyright (C) 2010 Torch Mobile (Beijing) Co. Ltd. All rights reserved. | 4 * Copyright (C) 2010 Torch Mobile (Beijing) Co. Ltd. All rights reserved. |
5 * | 5 * |
6 * Redistribution and use in source and binary forms, with or without | 6 * Redistribution and use in source and binary forms, with or without |
7 * modification, are permitted provided that the following conditions | 7 * modification, are permitted provided that the following conditions |
8 * are met: | 8 * are met: |
9 * 1. Redistributions of source code must retain the above copyright | 9 * 1. Redistributions of source code must retain the above copyright |
10 * notice, this list of conditions and the following disclaimer. | 10 * notice, this list of conditions and the following disclaimer. |
(...skipping 28 matching lines...) Expand all Loading... |
39 #include "core/frame/Settings.h" | 39 #include "core/frame/Settings.h" |
40 #include "core/html/ImageData.h" | 40 #include "core/html/ImageData.h" |
41 #include "core/html/canvas/CanvasContextCreationAttributes.h" | 41 #include "core/html/canvas/CanvasContextCreationAttributes.h" |
42 #include "core/html/canvas/CanvasFontCache.h" | 42 #include "core/html/canvas/CanvasFontCache.h" |
43 #include "core/html/canvas/CanvasRenderingContext.h" | 43 #include "core/html/canvas/CanvasRenderingContext.h" |
44 #include "core/html/canvas/CanvasRenderingContextFactory.h" | 44 #include "core/html/canvas/CanvasRenderingContextFactory.h" |
45 #include "core/layout/LayoutHTMLCanvas.h" | 45 #include "core/layout/LayoutHTMLCanvas.h" |
46 #include "core/paint/DeprecatedPaintLayer.h" | 46 #include "core/paint/DeprecatedPaintLayer.h" |
47 #include "platform/MIMETypeRegistry.h" | 47 #include "platform/MIMETypeRegistry.h" |
48 #include "platform/RuntimeEnabledFeatures.h" | 48 #include "platform/RuntimeEnabledFeatures.h" |
| 49 #include "platform/Task.h" |
| 50 #include "platform/ThreadSafeFunctional.h" |
49 #include "platform/graphics/Canvas2DImageBufferSurface.h" | 51 #include "platform/graphics/Canvas2DImageBufferSurface.h" |
50 #include "platform/graphics/ExpensiveCanvasHeuristicParameters.h" | 52 #include "platform/graphics/ExpensiveCanvasHeuristicParameters.h" |
51 #include "platform/graphics/ImageBuffer.h" | 53 #include "platform/graphics/ImageBuffer.h" |
52 #include "platform/graphics/RecordingImageBufferSurface.h" | 54 #include "platform/graphics/RecordingImageBufferSurface.h" |
53 #include "platform/graphics/StaticBitmapImage.h" | 55 #include "platform/graphics/StaticBitmapImage.h" |
54 #include "platform/graphics/UnacceleratedImageBufferSurface.h" | 56 #include "platform/graphics/UnacceleratedImageBufferSurface.h" |
55 #include "platform/graphics/gpu/AcceleratedImageBufferSurface.h" | 57 #include "platform/graphics/gpu/AcceleratedImageBufferSurface.h" |
56 #include "platform/transforms/AffineTransform.h" | 58 #include "platform/transforms/AffineTransform.h" |
57 #include "public/platform/Platform.h" | 59 #include "public/platform/Platform.h" |
58 #include "public/platform/WebTraceLocation.h" | 60 #include "public/platform/WebTraceLocation.h" |
59 #include "wtf/Functional.h" | |
60 #include <math.h> | 61 #include <math.h> |
61 #include <v8.h> | 62 #include <v8.h> |
62 | 63 |
63 namespace blink { | 64 namespace blink { |
64 | 65 |
65 using namespace HTMLNames; | 66 using namespace HTMLNames; |
66 | 67 |
67 namespace { | 68 namespace { |
68 | 69 |
69 // These values come from the WhatWG spec. | 70 // These values come from the WhatWG spec. |
70 const int DefaultWidth = 300; | 71 const int DefaultWidth = 300; |
71 const int DefaultHeight = 150; | 72 const int DefaultHeight = 150; |
72 | 73 |
73 // Firefox limits width/height to 32767 pixels, but slows down dramatically befo
re it | 74 // Firefox limits width/height to 32767 pixels, but slows down dramatically befo
re it |
74 // reaches that limit. We limit by area instead, giving us larger maximum dimens
ions, | 75 // reaches that limit. We limit by area instead, giving us larger maximum dimens
ions, |
75 // in exchange for a smaller maximum canvas size. | 76 // in exchange for a smaller maximum canvas size. |
76 const int MaxCanvasArea = 32768 * 8192; // Maximum canvas area in CSS pixels | 77 const int MaxCanvasArea = 32768 * 8192; // Maximum canvas area in CSS pixels |
77 | 78 |
78 // In Skia, we will also limit width/height to 32767. | 79 // In Skia, we will also limit width/height to 32767. |
79 const int MaxSkiaDim = 32767; // Maximum width/height in CSS pixels. | 80 const int MaxSkiaDim = 32767; // Maximum width/height in CSS pixels. |
80 | 81 |
| 82 // A default value of quality argument for toDataURL and toBlob |
| 83 // It is in an invalid range (outside 0.0 - 1.0) so that it will not be misinter
preted as a user-input value |
| 84 const int UndefinedQualityValue = -1.0; |
| 85 |
81 bool canCreateImageBuffer(const IntSize& size) | 86 bool canCreateImageBuffer(const IntSize& size) |
82 { | 87 { |
83 if (size.isEmpty()) | 88 if (size.isEmpty()) |
84 return false; | 89 return false; |
85 if (size.width() * size.height() > MaxCanvasArea) | 90 if (size.width() * size.height() > MaxCanvasArea) |
86 return false; | 91 return false; |
87 if (size.width() > MaxSkiaDim || size.height() > MaxSkiaDim) | 92 if (size.width() > MaxSkiaDim || size.height() > MaxSkiaDim) |
88 return false; | 93 return false; |
89 return true; | 94 return true; |
90 } | 95 } |
91 | 96 |
92 PassRefPtr<Image> createTransparentImage(const IntSize& size) | 97 PassRefPtr<Image> createTransparentImage(const IntSize& size) |
93 { | 98 { |
94 ASSERT(canCreateImageBuffer(size)); | 99 ASSERT(canCreateImageBuffer(size)); |
95 RefPtr<SkSurface> surface = adoptRef(SkSurface::NewRasterN32Premul(size.widt
h(), size.height())); | 100 RefPtr<SkSurface> surface = adoptRef(SkSurface::NewRasterN32Premul(size.widt
h(), size.height())); |
96 surface->getCanvas()->clear(SK_ColorTRANSPARENT); | 101 surface->getCanvas()->clear(SK_ColorTRANSPARENT); |
97 return StaticBitmapImage::create(adoptRef(surface->newImageSnapshot())); | 102 return StaticBitmapImage::create(adoptRef(surface->newImageSnapshot())); |
98 } | 103 } |
99 | 104 |
100 } // namespace | 105 } // namespace |
101 | 106 |
| 107 int HTMLCanvasElement::s_numCanvasInstances = 0; |
| 108 |
102 DEFINE_EMPTY_DESTRUCTOR_WILL_BE_REMOVED(CanvasObserver); | 109 DEFINE_EMPTY_DESTRUCTOR_WILL_BE_REMOVED(CanvasObserver); |
103 | 110 |
104 inline HTMLCanvasElement::HTMLCanvasElement(Document& document) | 111 inline HTMLCanvasElement::HTMLCanvasElement(Document& document) |
105 : HTMLElement(canvasTag, document) | 112 : HTMLElement(canvasTag, document) |
106 , DocumentVisibilityObserver(document) | 113 , DocumentVisibilityObserver(document) |
107 , m_size(DefaultWidth, DefaultHeight) | 114 , m_size(DefaultWidth, DefaultHeight) |
108 , m_ignoreReset(false) | 115 , m_ignoreReset(false) |
109 , m_accelerationDisabled(false) | 116 , m_accelerationDisabled(false) |
110 , m_externallyAllocatedMemory(0) | 117 , m_externallyAllocatedMemory(0) |
111 , m_originClean(true) | 118 , m_originClean(true) |
112 , m_didFailToCreateImageBuffer(false) | 119 , m_didFailToCreateImageBuffer(false) |
113 , m_imageBufferIsClear(false) | 120 , m_imageBufferIsClear(false) |
114 { | 121 { |
115 setHasCustomStyleCallbacks(); | 122 setHasCustomStyleCallbacks(); |
| 123 s_numCanvasInstances++; |
116 } | 124 } |
117 | 125 |
118 DEFINE_NODE_FACTORY(HTMLCanvasElement) | 126 DEFINE_NODE_FACTORY(HTMLCanvasElement) |
119 | 127 |
120 HTMLCanvasElement::~HTMLCanvasElement() | 128 HTMLCanvasElement::~HTMLCanvasElement() |
121 { | 129 { |
122 v8::Isolate::GetCurrent()->AdjustAmountOfExternalAllocatedMemory(-m_external
lyAllocatedMemory); | 130 v8::Isolate::GetCurrent()->AdjustAmountOfExternalAllocatedMemory(-m_external
lyAllocatedMemory); |
123 #if !ENABLE(OILPAN) | 131 s_numCanvasInstances--; |
| 132 if (s_numCanvasInstances == 0 && !!getToBlobThreadInstance()) |
| 133 { |
| 134 OwnPtr<WebThread> toBlobThread = getToBlobThreadInstance(); |
| 135 if (!toBlobThread) |
| 136 toBlobThread.clear(); |
| 137 } |
| 138 #if !ENABLE(OILPAN) |
124 for (CanvasObserver* canvasObserver : m_observers) | 139 for (CanvasObserver* canvasObserver : m_observers) |
125 canvasObserver->canvasDestroyed(this); | 140 canvasObserver->canvasDestroyed(this); |
126 // Ensure these go away before the ImageBuffer. | 141 // Ensure these go away before the ImageBuffer. |
127 m_context.clear(); | 142 m_context.clear(); |
128 #endif | 143 #endif |
129 } | 144 } |
130 | 145 |
| 146 PassOwnPtr<WebThread> HTMLCanvasElement::getToBlobThreadInstance() |
| 147 { |
| 148 DEFINE_STATIC_LOCAL(OwnPtr<WebThread>, s_toBlobThread, ()); |
| 149 if (!s_toBlobThread) { |
| 150 s_toBlobThread = adoptPtr(Platform::current()->createThread("Async toBlo
b")); |
| 151 } |
| 152 return s_toBlobThread.release(); |
| 153 } |
| 154 |
131 void HTMLCanvasElement::parseAttribute(const QualifiedName& name, const AtomicSt
ring& value) | 155 void HTMLCanvasElement::parseAttribute(const QualifiedName& name, const AtomicSt
ring& value) |
132 { | 156 { |
133 if (name == widthAttr || name == heightAttr) | 157 if (name == widthAttr || name == heightAttr) |
134 reset(); | 158 reset(); |
135 HTMLElement::parseAttribute(name, value); | 159 HTMLElement::parseAttribute(name, value); |
136 } | 160 } |
137 | 161 |
138 LayoutObject* HTMLCanvasElement::createLayoutObject(const ComputedStyle& style) | 162 LayoutObject* HTMLCanvasElement::createLayoutObject(const ComputedStyle& style) |
139 { | 163 { |
140 LocalFrame* frame = document().frame(); | 164 LocalFrame* frame = document().frame(); |
(...skipping 362 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
503 ASSERT(m_context->is2d()); | 527 ASSERT(m_context->is2d()); |
504 RefPtr<SkImage> snapshot = buffer()->newSkImageSnapshot(); | 528 RefPtr<SkImage> snapshot = buffer()->newSkImageSnapshot(); |
505 if (snapshot) { | 529 if (snapshot) { |
506 SkImageInfo imageInfo = SkImageInfo::Make(width(), height(), kRGBA_8888_
SkColorType, kUnpremul_SkAlphaType); | 530 SkImageInfo imageInfo = SkImageInfo::Make(width(), height(), kRGBA_8888_
SkColorType, kUnpremul_SkAlphaType); |
507 snapshot->readPixels(imageInfo, imageData->data()->data(), imageInfo.min
RowBytes(), 0, 0); | 531 snapshot->readPixels(imageInfo, imageData->data()->data(), imageInfo.min
RowBytes(), 0, 0); |
508 } | 532 } |
509 | 533 |
510 return imageData; | 534 return imageData; |
511 } | 535 } |
512 | 536 |
513 String HTMLCanvasElement::toDataURLInternal(const String& mimeType, const double
* quality, SourceDrawingBuffer sourceBuffer) const | 537 String HTMLCanvasElement::toDataURLInternal(const String& mimeType, const double
& quality, SourceDrawingBuffer sourceBuffer) const |
514 { | 538 { |
515 if (!isPaintable()) | 539 if (!isPaintable()) |
516 return String("data:,"); | 540 return String("data:,"); |
517 | 541 |
518 String encodingMimeType = toEncodingMimeType(mimeType); | 542 String encodingMimeType = toEncodingMimeType(mimeType); |
519 | 543 |
520 ImageData* imageData = toImageData(sourceBuffer); | 544 ImageData* imageData = toImageData(sourceBuffer); |
521 ScopedDisposal<ImageData> disposer(imageData); | 545 ScopedDisposal<ImageData> disposer(imageData); |
522 | 546 |
523 return ImageDataBuffer(imageData->size(), imageData->data()->data()).toDataU
RL(encodingMimeType, quality); | 547 return ImageDataBuffer(imageData->size(), imageData->data()->data()).toDataU
RL(encodingMimeType, quality); |
524 } | 548 } |
525 | 549 |
526 String HTMLCanvasElement::toDataURL(const String& mimeType, const ScriptValue& q
ualityArgument, ExceptionState& exceptionState) const | 550 String HTMLCanvasElement::toDataURL(const String& mimeType, const ScriptValue& q
ualityArgument, ExceptionState& exceptionState) const |
527 { | 551 { |
528 if (!originClean()) { | 552 if (!originClean()) { |
529 exceptionState.throwSecurityError("Tainted canvases may not be exported.
"); | 553 exceptionState.throwSecurityError("Tainted canvases may not be exported.
"); |
530 return String(); | 554 return String(); |
531 } | 555 } |
532 double quality; | 556 double quality = UndefinedQualityValue; |
533 double* qualityPtr = nullptr; | |
534 if (!qualityArgument.isEmpty()) { | 557 if (!qualityArgument.isEmpty()) { |
535 v8::Local<v8::Value> v8Value = qualityArgument.v8Value(); | 558 v8::Local<v8::Value> v8Value = qualityArgument.v8Value(); |
536 if (v8Value->IsNumber()) { | 559 if (v8Value->IsNumber()) { |
537 quality = v8Value.As<v8::Number>()->Value(); | 560 quality = v8Value.As<v8::Number>()->Value(); |
538 qualityPtr = &quality; | |
539 } | 561 } |
540 } | 562 } |
541 return toDataURLInternal(mimeType, qualityPtr, BackBuffer); | 563 return toDataURLInternal(mimeType, quality, BackBuffer); |
542 } | 564 } |
543 | 565 |
544 void HTMLCanvasElement::toBlob(FileCallback* callback, const String& mimeType, c
onst ScriptValue& qualityArgument, ExceptionState& exceptionState) const | 566 void HTMLCanvasElement::encodeImageAsync(DOMUint8ClampedArray* imageData, IntSiz
e imageSize, FileCallback* callback, const String& mimeType, double quality) |
| 567 { |
| 568 OwnPtr<Vector<char>> encodedImage(adoptPtr(new Vector<char>())); |
| 569 |
| 570 if (!ImageDataBuffer(imageSize, imageData->data()).encodeImage(mimeType, qua
lity, encodedImage.get())) { |
| 571 Platform::current()->mainThread()->taskRunner()->postTask(FROM_HERE, bin
d(&FileCallback::handleEvent, callback, nullptr)); |
| 572 } else { |
| 573 Platform::current()->mainThread()->taskRunner()->postTask(FROM_HERE, thr
eadSafeBind(&HTMLCanvasElement::createBlobAndCall, AllowCrossThreadAccess(this),
encodedImage.release(), mimeType, AllowCrossThreadAccess(callback))); |
| 574 } |
| 575 } |
| 576 |
| 577 void HTMLCanvasElement::createBlobAndCall(PassOwnPtr<Vector<char>> encodedImage,
const String& mimeType, FileCallback* callback) |
| 578 { |
| 579 // The main thread takes ownership of encoded image vector |
| 580 OwnPtr<Vector<char>> enc(encodedImage); |
| 581 |
| 582 File* resultBlob = File::create(enc->data(), enc->size(), mimeType); |
| 583 Platform::current()->mainThread()->taskRunner()->postTask(FROM_HERE, bind(&F
ileCallback::handleEvent, callback, resultBlob)); |
| 584 } |
| 585 |
| 586 void HTMLCanvasElement::toBlob(FileCallback* callback, const String& mimeType, c
onst ScriptValue& qualityArgument, ExceptionState& exceptionState) |
545 { | 587 { |
546 if (!originClean()) { | 588 if (!originClean()) { |
547 exceptionState.throwSecurityError("Tainted canvases may not be exported.
"); | 589 exceptionState.throwSecurityError("Tainted canvases may not be exported.
"); |
548 return; | 590 return; |
549 } | 591 } |
550 | 592 |
551 File* resultBlob = nullptr; | |
552 if (!isPaintable()) { | 593 if (!isPaintable()) { |
553 // If the canvas element's bitmap has no pixels | 594 // If the canvas element's bitmap has no pixels |
| 595 Platform::current()->mainThread()->taskRunner()->postTask(FROM_HERE, bin
d(&FileCallback::handleEvent, callback, nullptr)); |
554 return; | 596 return; |
555 } | 597 } |
556 | 598 |
557 double quality; | 599 double quality = UndefinedQualityValue; |
558 double* qualityPtr = nullptr; | |
559 if (!qualityArgument.isEmpty()) { | 600 if (!qualityArgument.isEmpty()) { |
560 v8::Local<v8::Value> v8Value = qualityArgument.v8Value(); | 601 v8::Local<v8::Value> v8Value = qualityArgument.v8Value(); |
561 if (v8Value->IsNumber()) { | 602 if (v8Value->IsNumber()) { |
562 quality = v8Value.As<v8::Number>()->Value(); | 603 quality = v8Value.As<v8::Number>()->Value(); |
563 qualityPtr = &quality; | |
564 } | 604 } |
565 } | 605 } |
566 | 606 |
567 String encodingMimeType = toEncodingMimeType(mimeType); | 607 String encodingMimeType = toEncodingMimeType(mimeType); |
568 | 608 |
569 ImageData* imageData = toImageData(BackBuffer); | 609 ImageData* imageData = toImageData(BackBuffer); |
| 610 // imageData unref its data, which we still keep alive for the async toBlob
thread |
570 ScopedDisposal<ImageData> disposer(imageData); | 611 ScopedDisposal<ImageData> disposer(imageData); |
571 | 612 |
572 // Perform image encoding | 613 // Add a ref to keep image data alive until completion of encoding |
573 Vector<char> encodedImage; | 614 RefPtr<DOMUint8ClampedArray> imageDataRef(imageData->data()); |
574 ImageDataBuffer(imageData->size(), imageData->data()->data()).encodeImage(en
codingMimeType, qualityPtr, &encodedImage); | |
575 resultBlob = File::create(encodedImage.data(), encodedImage.size(), encoding
MimeType); | |
576 | 615 |
577 Platform::current()->mainThread()->taskRunner()->postTask(FROM_HERE, bind(&F
ileCallback::handleEvent, callback, resultBlob)); | 616 // TODO: We cannot guarantee that 'this' will stay alive long enough. |
| 617 getToBlobThreadInstance()->taskRunner()->postTask(FROM_HERE, new Task(thread
SafeBind(&HTMLCanvasElement::encodeImageAsync, AllowCrossThreadAccess(this), All
owCrossThreadAccess(imageDataRef.release().leakRef()), imageData->size(), AllowC
rossThreadAccess(callback), encodingMimeType, quality))); |
578 } | 618 } |
579 | 619 |
580 SecurityOrigin* HTMLCanvasElement::securityOrigin() const | 620 SecurityOrigin* HTMLCanvasElement::securityOrigin() const |
581 { | 621 { |
582 return document().securityOrigin(); | 622 return document().securityOrigin(); |
583 } | 623 } |
584 | 624 |
585 bool HTMLCanvasElement::originClean() const | 625 bool HTMLCanvasElement::originClean() const |
586 { | 626 { |
587 if (document().settings() && document().settings()->disableReadingFromCanvas
()) | 627 if (document().settings() && document().settings()->disableReadingFromCanvas
()) |
(...skipping 355 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
943 { | 983 { |
944 return FloatSize(width(), height()); | 984 return FloatSize(width(), height()); |
945 } | 985 } |
946 | 986 |
947 bool HTMLCanvasElement::isOpaque() const | 987 bool HTMLCanvasElement::isOpaque() const |
948 { | 988 { |
949 return m_context && !m_context->hasAlpha(); | 989 return m_context && !m_context->hasAlpha(); |
950 } | 990 } |
951 | 991 |
952 } // blink | 992 } // blink |
OLD | NEW |