static const int defaultFontSize = 10;
static const char defaultFontFamily[] = "sans-serif";
static const char defaultFont[] = "10px sans-serif";
+static const double TryRestoreContextInterval = 0.5;
+static const unsigned MaxTryRestoreContextAttempts = 4;
+
+static bool contextLostRestoredEventsEnabled()
+{
+ return RuntimeEnabledFeatures::experimentalCanvasFeaturesEnabled();
+}
CanvasRenderingContext2D::CanvasRenderingContext2D(HTMLCanvasElement* canvas, const Canvas2DContextAttributes* attrs, bool usesCSSCompatibilityParseMode)
: CanvasRenderingContext(canvas)
, m_usesCSSCompatibilityParseMode(usesCSSCompatibilityParseMode)
, m_hasAlpha(!attrs || attrs->alpha())
+ , m_isContextLost(false)
+ , m_contextRestorable(true)
+ , m_storageMode(!attrs ? PersistentStorage : attrs->parsedStorage())
+ , m_tryRestoreContextAttemptCount(0)
+ , m_dispatchContextLostEventTimer(this, &CanvasRenderingContext2D::dispatchContextLostEvent)
+ , m_dispatchContextRestoredEventTimer(this, &CanvasRenderingContext2D::dispatchContextRestoredEvent)
+ , m_tryRestoreContextEventTimer(this, &CanvasRenderingContext2D::tryRestoreContextEvent)
{
m_stateStack.append(adoptPtrWillBeNoop(new State()));
ScriptWrappable::init(this);
CanvasRenderingContext2D::~CanvasRenderingContext2D()
{
+#if !ENABLE(OILPAN)
#if !ASSERT_DISABLED
unwindStateStack();
#endif
+#endif
}
bool CanvasRenderingContext2D::isAccelerated() const
return context && context->isAccelerated();
}
+bool CanvasRenderingContext2D::isContextLost() const
+{
+ return m_isContextLost;
+}
+
+void CanvasRenderingContext2D::loseContext()
+{
+ if (m_isContextLost)
+ return;
+ m_isContextLost = true;
+ m_dispatchContextLostEventTimer.startOneShot(0, FROM_HERE);
+}
+
+void CanvasRenderingContext2D::restoreContext()
+{
+ if (!m_contextRestorable)
+ return;
+ // This code path is for restoring from an eviction
+ // Restoring from surface failure is handled internally
+ ASSERT(m_isContextLost && !canvas()->hasImageBuffer());
+
+ if (canvas()->buffer()) {
+ if (contextLostRestoredEventsEnabled()) {
+ m_dispatchContextRestoredEventTimer.startOneShot(0, FROM_HERE);
+ } else {
+ // legacy synchronous context restoration.
+ reset();
+ m_isContextLost = false;
+ }
+ }
+}
+
+void CanvasRenderingContext2D::trace(Visitor* visitor)
+{
+#if ENABLE(OILPAN)
+ visitor->trace(m_stateStack);
+ visitor->trace(m_fetchedFonts);
+#endif
+ CanvasRenderingContext::trace(visitor);
+}
+
+void CanvasRenderingContext2D::dispatchContextLostEvent(Timer<CanvasRenderingContext2D>*)
+{
+ if (contextLostRestoredEventsEnabled()) {
+ RefPtrWillBeRawPtr<Event> event = Event::createCancelable(EventTypeNames::contextlost);
+ canvas()->dispatchEvent(event);
+ if (event->defaultPrevented()) {
+ m_contextRestorable = false;
+ }
+ }
+
+ // If an image buffer is present, it means the context was not lost due to
+ // an eviction, but rather due to a surface failure (gpu context lost?)
+ if (m_contextRestorable && canvas()->hasImageBuffer()) {
+ m_tryRestoreContextAttemptCount = 0;
+ m_tryRestoreContextEventTimer.startRepeating(TryRestoreContextInterval, FROM_HERE);
+ }
+}
+
+void CanvasRenderingContext2D::tryRestoreContextEvent(Timer<CanvasRenderingContext2D>* timer)
+{
+ if (!m_isContextLost) {
+ // Canvas was already restored (possibly thanks to a resize), so stop trying.
+ m_tryRestoreContextEventTimer.stop();
+ return;
+ }
+ if (canvas()->hasImageBuffer() && canvas()->buffer()->restoreSurface()) {
+ m_tryRestoreContextEventTimer.stop();
+ dispatchContextRestoredEvent(0);
+ }
+
+ if (++m_tryRestoreContextAttemptCount > MaxTryRestoreContextAttempts)
+ canvas()->discardImageBuffer();
+
+ if (!canvas()->hasImageBuffer()) {
+ // final attempt: allocate a brand new image buffer instead of restoring
+ timer->stop();
+ if (canvas()->buffer())
+ dispatchContextRestoredEvent(0);
+ }
+}
+
+void CanvasRenderingContext2D::dispatchContextRestoredEvent(Timer<CanvasRenderingContext2D>*)
+{
+ if (!m_isContextLost)
+ return;
+ reset();
+ m_isContextLost = false;
+ if (contextLostRestoredEventsEnabled()) {
+ RefPtrWillBeRawPtr<Event> event(Event::create(EventTypeNames::contextrestored));
+ canvas()->dispatchEvent(event);
+ }
+}
+
void CanvasRenderingContext2D::reset()
{
unwindStateStack();
c->setCompositeOperation(op, blendMode);
}
-void CanvasRenderingContext2D::setCurrentTransform(PassRefPtr<SVGMatrixTearOff> passMatrixTearOff, ExceptionState& exceptionState)
+void CanvasRenderingContext2D::setCurrentTransform(PassRefPtr<SVGMatrixTearOff> passMatrixTearOff)
{
RefPtr<SVGMatrixTearOff> matrixTearOff = passMatrixTearOff;
-
- // FIXME: bindings should do null checking and throw: http://crbug.com/321518
- if (!matrixTearOff) {
- exceptionState.throwTypeError("currentTransform only accepts a SVGMatrix.");
- return;
- }
-
const AffineTransform& transform = matrixTearOff->value();
setTransform(transform.a(), transform.b(), transform.c(), transform.d(), transform.e(), transform.f());
}
m_path.clear();
}
-PassRefPtr<Path2D> CanvasRenderingContext2D::currentPath()
-{
- return Path2D::create(m_path);
-}
-
-void CanvasRenderingContext2D::setCurrentPath(Path2D* path)
-{
- if (!path)
- return;
- m_path = path->path();
-}
-
static bool validateRectForCanvas(float& x, float& y, float& width, float& height)
{
if (!std::isfinite(x) | !std::isfinite(y) | !std::isfinite(width) | !std::isfinite(height))
return op == CompositeSourceIn || op == CompositeSourceOut || op == CompositeDestinationIn || op == CompositeDestinationAtop;
}
-static bool parseWinding(const String& windingRuleString, WindRule& windRule)
+static WindRule parseWinding(const String& windingRuleString)
{
if (windingRuleString == "nonzero")
- windRule = RULE_NONZERO;
- else if (windingRuleString == "evenodd")
- windRule = RULE_EVENODD;
- else
- return false;
+ return RULE_NONZERO;
+ if (windingRuleString == "evenodd")
+ return RULE_EVENODD;
- return true;
+ ASSERT_NOT_REACHED();
+ return RULE_EVENODD;
}
void CanvasRenderingContext2D::fillInternal(const Path& path, const String& windingRuleString)
return;
}
FloatRect clipBounds;
- if (!drawingContext()->getTransformedClipBounds(&clipBounds)) {
+ if (!c->getTransformedClipBounds(&clipBounds)) {
return;
}
}
WindRule windRule = c->fillRule();
- WindRule newWindRule = RULE_NONZERO;
- if (!parseWinding(windingRuleString, newWindRule)) {
- return;
- }
- c->setFillRule(newWindRule);
+ c->setFillRule(parseWinding(windingRuleString));
if (isFullCanvasCompositeMode(state().m_globalComposite)) {
fullCanvasCompositedFill(path);
fillInternal(m_path, windingRuleString);
}
-void CanvasRenderingContext2D::fill(Path2D* domPath, ExceptionState& exceptionState)
-{
- fill(domPath, "nonzero", exceptionState);
-}
-
-void CanvasRenderingContext2D::fill(Path2D* domPath, const String& windingRuleString, ExceptionState& exceptionState)
+void CanvasRenderingContext2D::fill(Path2D* domPath, const String& windingRuleString)
{
- if (!domPath) {
- exceptionState.throwDOMException(TypeMismatchError, ExceptionMessages::argumentNullOrIncorrectType(1, "Path"));
- return;
- }
-
fillInternal(domPath->path(), windingRuleString);
}
if (!state().m_invertibleCTM) {
return;
}
+ FloatRect clipBounds;
+ if (!c->getTransformedClipBounds(&clipBounds))
+ return;
// If gradient size is zero, then paint nothing.
Gradient* gradient = c->strokeGradient();
return;
}
- FloatRect bounds = path.boundingRect();
- inflateStrokeRect(bounds);
- FloatRect dirtyRect;
- if (computeDirtyRect(bounds, &dirtyRect)) {
+ if (isFullCanvasCompositeMode(state().m_globalComposite)) {
+ fullCanvasCompositedStroke(path);
+ didDraw(clipBounds);
+ } else if (state().m_globalComposite == CompositeCopy) {
+ clearCanvas();
c->strokePath(path);
- didDraw(dirtyRect);
+ didDraw(clipBounds);
+ } else {
+ FloatRect bounds = path.boundingRect();
+ inflateStrokeRect(bounds);
+ FloatRect dirtyRect;
+ if (computeDirtyRect(bounds, clipBounds, &dirtyRect)) {
+ c->strokePath(path);
+ didDraw(dirtyRect);
+ }
}
}
strokeInternal(m_path);
}
-void CanvasRenderingContext2D::stroke(Path2D* domPath, ExceptionState& exceptionState)
+void CanvasRenderingContext2D::stroke(Path2D* domPath)
{
- if (!domPath) {
- exceptionState.throwDOMException(TypeMismatchError, ExceptionMessages::argumentNullOrIncorrectType(1, "Path"));
- return;
- }
-
strokeInternal(domPath->path());
}
return;
}
- WindRule newWindRule = RULE_NONZERO;
- if (!parseWinding(windingRuleString, newWindRule)) {
- return;
- }
-
realizeSaves();
- c->canvasClip(path, newWindRule);
+ c->canvasClip(path, parseWinding(windingRuleString));
}
void CanvasRenderingContext2D::clip(const String& windingRuleString)
clipInternal(m_path, windingRuleString);
}
-void CanvasRenderingContext2D::clip(Path2D* domPath, ExceptionState& exceptionState)
+void CanvasRenderingContext2D::clip(Path2D* domPath, const String& windingRuleString)
{
- clip(domPath, "nonzero", exceptionState);
-}
-
-void CanvasRenderingContext2D::clip(Path2D* domPath, const String& windingRuleString, ExceptionState& exceptionState)
-{
- if (!domPath) {
- exceptionState.throwDOMException(TypeMismatchError, ExceptionMessages::argumentNullOrIncorrectType(1, "Path"));
- return;
- }
-
clipInternal(domPath->path(), windingRuleString);
}
return isPointInPathInternal(m_path, x, y, windingRuleString);
}
-bool CanvasRenderingContext2D::isPointInPath(Path2D* domPath, const float x, const float y, ExceptionState& exceptionState)
+bool CanvasRenderingContext2D::isPointInPath(Path2D* domPath, const float x, const float y, const String& windingRuleString)
{
- return isPointInPath(domPath, x, y, "nonzero", exceptionState);
-}
-
-bool CanvasRenderingContext2D::isPointInPath(Path2D* domPath, const float x, const float y, const String& windingRuleString, ExceptionState& exceptionState)
-{
- if (!domPath) {
- exceptionState.throwDOMException(TypeMismatchError, ExceptionMessages::argumentNullOrIncorrectType(1, "Path"));
- return false;
- }
-
return isPointInPathInternal(domPath->path(), x, y, windingRuleString);
}
if (!std::isfinite(transformedPoint.x()) || !std::isfinite(transformedPoint.y()))
return false;
- WindRule windRule = RULE_NONZERO;
- if (!parseWinding(windingRuleString, windRule))
- return false;
-
- return path.contains(transformedPoint, windRule);
+ return path.contains(transformedPoint, parseWinding(windingRuleString));
}
bool CanvasRenderingContext2D::isPointInStroke(const float x, const float y)
return isPointInStrokeInternal(m_path, x, y);
}
-bool CanvasRenderingContext2D::isPointInStroke(Path2D* domPath, const float x, const float y, ExceptionState& exceptionState)
+bool CanvasRenderingContext2D::isPointInStroke(Path2D* domPath, const float x, const float y)
{
- if (!domPath) {
- exceptionState.throwDOMException(TypeMismatchError, ExceptionMessages::argumentNullOrIncorrectType(1, "Path"));
- return false;
- }
-
return isPointInStrokeInternal(domPath->path(), x, y);
}
return path.strokeContains(transformedPoint, strokeData);
}
+void CanvasRenderingContext2D::scrollPathIntoView()
+{
+ scrollPathIntoViewInternal(m_path);
+}
+
+void CanvasRenderingContext2D::scrollPathIntoView(Path2D* path2d)
+{
+ scrollPathIntoViewInternal(path2d->path());
+}
+
+void CanvasRenderingContext2D::scrollPathIntoViewInternal(const Path& path)
+{
+ if (!state().m_invertibleCTM || path.isEmpty())
+ return;
+
+ canvas()->document().updateLayoutIgnorePendingStylesheets();
+
+ // Apply transformation and get the bounding rect
+ Path transformedPath = path;
+ transformedPath.transform(state().m_transform);
+ FloatRect boundingRect = transformedPath.boundingRect();
+
+ // Offset by the canvas rect (We should take border and padding into account).
+ RenderBoxModelObject* rbmo = canvas()->renderBoxModelObject();
+ IntRect canvasRect = canvas()->renderer()->absoluteBoundingBoxRect();
+ canvasRect.move(rbmo->borderLeft() + rbmo->paddingLeft(),
+ rbmo->borderTop() + rbmo->paddingTop());
+ LayoutRect pathRect = enclosingLayoutRect(boundingRect);
+ pathRect.moveBy(canvasRect.location());
+
+ if (canvas()->renderer()) {
+ canvas()->renderer()->scrollRectToVisible(
+ pathRect, ScrollAlignment::alignCenterAlways, ScrollAlignment::alignTopAlways);
+ }
+
+ // TODO: should implement "inform the user" that the caret and/or
+ // selection the specified rectangle of the canvas. See http://crbug.com/357987
+}
+
void CanvasRenderingContext2D::clearRect(float x, float y, float width, float height)
{
if (!validateRectForCanvas(x, y, width, height))
if (!state().m_invertibleCTM)
return;
FloatRect clipBounds;
- if (!drawingContext()->getTransformedClipBounds(&clipBounds))
+ if (!c->getTransformedClipBounds(&clipBounds))
return;
// from the HTML5 Canvas spec:
return;
if (!state().m_invertibleCTM)
return;
+ FloatRect clipBounds;
+ if (!c->getTransformedClipBounds(&clipBounds))
+ return;
// If gradient size is zero, then paint nothing.
Gradient* gradient = c->strokeGradient();
FloatRect rect(x, y, width, height);
- FloatRect boundingRect = rect;
- boundingRect.inflate(state().m_lineWidth / 2);
- FloatRect dirtyRect;
- if (computeDirtyRect(boundingRect, &dirtyRect)) {
- c->strokeRect(rect, state().m_lineWidth);
- didDraw(dirtyRect);
+ if (isFullCanvasCompositeMode(state().m_globalComposite)) {
+ fullCanvasCompositedStroke(rect);
+ didDraw(clipBounds);
+ } else if (state().m_globalComposite == CompositeCopy) {
+ clearCanvas();
+ c->strokeRect(rect);
+ didDraw(clipBounds);
+ } else {
+ FloatRect boundingRect = rect;
+ boundingRect.inflate(state().m_lineWidth / 2);
+ FloatRect dirtyRect;
+ if (computeDirtyRect(boundingRect, clipBounds, &dirtyRect)) {
+ c->strokeRect(rect);
+ didDraw(dirtyRect);
+ }
}
}
return alphaChannel(state().m_shadowColor) && (state().m_shadowBlur || !state().m_shadowOffset.isZero());
}
-enum ImageSizeType {
- ImageSizeAfterDevicePixelRatio,
- ImageSizeBeforeDevicePixelRatio
-};
-
static inline FloatRect normalizeRect(const FloatRect& rect)
{
return FloatRect(min(rect.x(), rect.maxX()),
dstRect->move(offset);
}
-static bool checkImageSource(CanvasImageSource* imageSource, ExceptionState& exceptionState)
-{
- if (!imageSource) {
- // FIXME: Message should mention ImageBitmap once that feature ships.
- exceptionState.throwDOMException(TypeMismatchError, ExceptionMessages::argumentNullOrIncorrectType(1, String("HTMLImageElement, HTMLCanvasElement or HTMLVideoElement")));
- return false;
- }
- return true;
-}
-
void CanvasRenderingContext2D::drawImage(CanvasImageSource* imageSource, float x, float y, ExceptionState& exceptionState)
{
- if (!checkImageSource(imageSource, exceptionState))
- return;
FloatSize destRectSize = imageSource->defaultDestinationSize();
drawImage(imageSource, x, y, destRectSize.width(), destRectSize.height(), exceptionState);
}
void CanvasRenderingContext2D::drawImage(CanvasImageSource* imageSource,
float x, float y, float width, float height, ExceptionState& exceptionState)
{
- if (!checkImageSource(imageSource, exceptionState))
- return;
FloatSize sourceRectSize = imageSource->sourceSize();
drawImage(imageSource, 0, 0, sourceRectSize.width(), sourceRectSize.height(), x, y, width, height, exceptionState);
}
float dx, float dy, float dw, float dh, ExceptionState& exceptionState,
CompositeOperator op, blink::WebBlendMode blendMode)
{
- if (!checkImageSource(imageSource, exceptionState))
- return;
-
RefPtr<Image> image;
SourceImageStatus sourceImageStatus;
if (!imageSource->isVideoElement()) {
float dx, float dy, float dw, float dh,
const String& compositeOperation)
{
+ if (!image)
+ return;
CompositeOperator op;
blink::WebBlendMode blendOp = blink::WebBlendModeNormal;
if (!parseCompositeAndBlendOperator(compositeOperation, op, blendOp) || blendOp != blink::WebBlendModeNormal)
{
ASSERT(isFullCanvasCompositeMode(op));
- drawingContext()->beginLayer(1, op);
- drawImageToContext(image, drawingContext(), dest, src, CompositeSourceOver);
- drawingContext()->endLayer();
+ GraphicsContext* c = drawingContext();
+ c->beginLayer(1, op);
+ drawImageToContext(image, c, dest, src, CompositeSourceOver);
+ c->endLayer();
}
static void fillPrimitive(const FloatRect& rect, GraphicsContext* context)
c->endLayer();
}
-PassRefPtr<CanvasGradient> CanvasRenderingContext2D::createLinearGradient(float x0, float y0, float x1, float y1, ExceptionState& exceptionState)
+static void strokePrimitive(const FloatRect& rect, GraphicsContext* context)
{
- if (!std::isfinite(x0))
- exceptionState.throwDOMException(NotSupportedError, ExceptionMessages::notAFiniteNumber(x0, "x0"));
- else if (!std::isfinite(y0))
- exceptionState.throwDOMException(NotSupportedError, ExceptionMessages::notAFiniteNumber(y0, "y0"));
- else if (!std::isfinite(x1))
- exceptionState.throwDOMException(NotSupportedError, ExceptionMessages::notAFiniteNumber(x1, "x1"));
- else if (!std::isfinite(y1))
- exceptionState.throwDOMException(NotSupportedError, ExceptionMessages::notAFiniteNumber(y1, "y1"));
+ context->strokeRect(rect);
+}
- if (exceptionState.hadException())
- return nullptr;
+static void strokePrimitive(const Path& path, GraphicsContext* context)
+{
+ context->strokePath(path);
+}
+
+template<class T> void CanvasRenderingContext2D::fullCanvasCompositedStroke(const T& area)
+{
+ ASSERT(isFullCanvasCompositeMode(state().m_globalComposite));
+
+ GraphicsContext* c = drawingContext();
+ ASSERT(c);
+ c->beginLayer(1, state().m_globalComposite);
+ CompositeOperator previousOperator = c->compositeOperation();
+ c->setCompositeOperation(CompositeSourceOver);
+ strokePrimitive(area, c);
+ c->setCompositeOperation(previousOperator);
+ c->endLayer();
+}
+PassRefPtr<CanvasGradient> CanvasRenderingContext2D::createLinearGradient(float x0, float y0, float x1, float y1)
+{
RefPtr<CanvasGradient> gradient = CanvasGradient::create(FloatPoint(x0, y0), FloatPoint(x1, y1));
return gradient.release();
}
PassRefPtr<CanvasGradient> CanvasRenderingContext2D::createRadialGradient(float x0, float y0, float r0, float x1, float y1, float r1, ExceptionState& exceptionState)
{
- if (!std::isfinite(x0))
- exceptionState.throwDOMException(NotSupportedError, ExceptionMessages::notAFiniteNumber(x0, "x0"));
- else if (!std::isfinite(y0))
- exceptionState.throwDOMException(NotSupportedError, ExceptionMessages::notAFiniteNumber(y0, "y0"));
- else if (!std::isfinite(r0))
- exceptionState.throwDOMException(NotSupportedError, ExceptionMessages::notAFiniteNumber(r0, "r0"));
- else if (!std::isfinite(x1))
- exceptionState.throwDOMException(NotSupportedError, ExceptionMessages::notAFiniteNumber(x1, "x1"));
- else if (!std::isfinite(y1))
- exceptionState.throwDOMException(NotSupportedError, ExceptionMessages::notAFiniteNumber(y1, "y1"));
- else if (!std::isfinite(r1))
- exceptionState.throwDOMException(NotSupportedError, ExceptionMessages::notAFiniteNumber(r1, "r1"));
- else if (r0 < 0 || r1 < 0)
+ if (r0 < 0 || r1 < 0) {
exceptionState.throwDOMException(IndexSizeError, String::format("The %s provided is less than 0.", r0 < 0 ? "r0" : "r1"));
-
- if (exceptionState.hadException())
return nullptr;
+ }
RefPtr<CanvasGradient> gradient = CanvasGradient::create(FloatPoint(x0, y0), r0, FloatPoint(x1, y1), r1);
return gradient.release();
PassRefPtr<CanvasPattern> CanvasRenderingContext2D::createPattern(CanvasImageSource* imageSource,
const String& repetitionType, ExceptionState& exceptionState)
{
- if (!checkImageSource(imageSource, exceptionState))
- return nullptr;
bool repeatX, repeatY;
CanvasPattern::parseRepetitionType(repetitionType, repeatX, repeatY, exceptionState);
if (exceptionState.hadException())
GraphicsContext* CanvasRenderingContext2D::drawingContext() const
{
+ if (isContextLost())
+ return 0;
return canvas()->drawingContext();
}
-static PassRefPtr<ImageData> createEmptyImageData(const IntSize& size)
+static PassRefPtrWillBeRawPtr<ImageData> createEmptyImageData(const IntSize& size)
{
- if (RefPtr<ImageData> data = ImageData::create(size)) {
+ if (RefPtrWillBeRawPtr<ImageData> data = ImageData::create(size)) {
data->data()->zeroFill();
return data.release();
}
return nullptr;
}
-PassRefPtr<ImageData> CanvasRenderingContext2D::createImageData(PassRefPtr<ImageData> imageData, ExceptionState& exceptionState) const
+PassRefPtrWillBeRawPtr<ImageData> CanvasRenderingContext2D::createImageData(PassRefPtrWillBeRawPtr<ImageData> imageData) const
{
- if (!imageData) {
- exceptionState.throwDOMException(NotSupportedError, ExceptionMessages::argumentNullOrIncorrectType(1, "ImageData"));
- return nullptr;
- }
-
return createEmptyImageData(imageData->size());
}
-PassRefPtr<ImageData> CanvasRenderingContext2D::createImageData(float sw, float sh, ExceptionState& exceptionState) const
+PassRefPtrWillBeRawPtr<ImageData> CanvasRenderingContext2D::createImageData(float sw, float sh, ExceptionState& exceptionState) const
{
- if (!sw || !sh)
+ if (!sw || !sh) {
exceptionState.throwDOMException(IndexSizeError, String::format("The source %s is 0.", sw ? "height" : "width"));
- else if (!std::isfinite(sw))
- exceptionState.throwDOMException(NotSupportedError, ExceptionMessages::notAFiniteNumber(sw, "source width"));
- else if (!std::isfinite(sh))
- exceptionState.throwDOMException(NotSupportedError, ExceptionMessages::notAFiniteNumber(sh, "source height"));
-
- if (exceptionState.hadException())
return nullptr;
+ }
FloatSize logicalSize(fabs(sw), fabs(sh));
if (!logicalSize.isExpressibleAsIntSize())
return createEmptyImageData(size);
}
-PassRefPtr<ImageData> CanvasRenderingContext2D::webkitGetImageDataHD(float sx, float sy, float sw, float sh, ExceptionState& exceptionState) const
-{
- return getImageData(sx, sy, sw, sh, exceptionState);
-}
-
-PassRefPtr<ImageData> CanvasRenderingContext2D::getImageData(float sx, float sy, float sw, float sh, ExceptionState& exceptionState) const
+PassRefPtrWillBeRawPtr<ImageData> CanvasRenderingContext2D::getImageData(float sx, float sy, float sw, float sh, ExceptionState& exceptionState) const
{
if (!canvas()->originClean())
exceptionState.throwSecurityError("The canvas has been tainted by cross-origin data.");
else if (!sw || !sh)
exceptionState.throwDOMException(IndexSizeError, String::format("The source %s is 0.", sw ? "height" : "width"));
- else if (!std::isfinite(sx))
- exceptionState.throwDOMException(NotSupportedError, ExceptionMessages::notAFiniteNumber(sx, "source X"));
- else if (!std::isfinite(sy))
- exceptionState.throwDOMException(NotSupportedError, ExceptionMessages::notAFiniteNumber(sy, "source Y"));
- else if (!std::isfinite(sw))
- exceptionState.throwDOMException(NotSupportedError, ExceptionMessages::notAFiniteNumber(sw, "source width"));
- else if (!std::isfinite(sh))
- exceptionState.throwDOMException(NotSupportedError, ExceptionMessages::notAFiniteNumber(sh, "source height"));
if (exceptionState.hadException())
return nullptr;
IntRect imageDataRect = enclosingIntRect(logicalRect);
ImageBuffer* buffer = canvas()->buffer();
- if (!buffer)
+ if (!buffer || isContextLost())
return createEmptyImageData(imageDataRect.size());
RefPtr<Uint8ClampedArray> byteArray = buffer->getUnmultipliedImageData(imageDataRect);
return ImageData::create(imageDataRect.size(), byteArray.release());
}
-void CanvasRenderingContext2D::putImageData(ImageData* data, float dx, float dy, ExceptionState& exceptionState)
+void CanvasRenderingContext2D::putImageData(ImageData* data, float dx, float dy)
{
- if (!data) {
- exceptionState.throwDOMException(TypeMismatchError, ExceptionMessages::argumentNullOrIncorrectType(1, "ImageData"));
- return;
- }
- putImageData(data, dx, dy, 0, 0, data->width(), data->height(), exceptionState);
-}
-
-void CanvasRenderingContext2D::putImageData(ImageData* data, float dx, float dy, float dirtyX, float dirtyY,
- float dirtyWidth, float dirtyHeight, ExceptionState& exceptionState)
-{
- if (!data)
- exceptionState.throwDOMException(TypeMismatchError, ExceptionMessages::argumentNullOrIncorrectType(1, "ImageData"));
- else if (!std::isfinite(dx))
- exceptionState.throwDOMException(NotSupportedError, ExceptionMessages::notAFiniteNumber(dx, "dx"));
- else if (!std::isfinite(dy))
- exceptionState.throwDOMException(NotSupportedError, ExceptionMessages::notAFiniteNumber(dy, "dy"));
- else if (!std::isfinite(dirtyX))
- exceptionState.throwDOMException(NotSupportedError, ExceptionMessages::notAFiniteNumber(dirtyX, "dirtyX"));
- else if (!std::isfinite(dirtyY))
- exceptionState.throwDOMException(NotSupportedError, ExceptionMessages::notAFiniteNumber(dirtyY, "dirtyY"));
- else if (!std::isfinite(dirtyWidth))
- exceptionState.throwDOMException(NotSupportedError, ExceptionMessages::notAFiniteNumber(dirtyWidth, "dirtyWidth"));
- else if (!std::isfinite(dirtyHeight))
- exceptionState.throwDOMException(NotSupportedError, ExceptionMessages::notAFiniteNumber(dirtyHeight, "dirtyHeight"));
-
- if (exceptionState.hadException())
- return;
+ putImageData(data, dx, dy, 0, 0, data->width(), data->height());
+}
+void CanvasRenderingContext2D::putImageData(ImageData* data, float dx, float dy, float dirtyX, float dirtyY, float dirtyWidth, float dirtyHeight)
+{
ImageBuffer* buffer = canvas()->buffer();
if (!buffer)
return;
if (!fill)
inflateStrokeRect(textRunPaintInfo.bounds);
- FloatRect dirtyRect;
- if (!computeDirtyRect(textRunPaintInfo.bounds, &dirtyRect))
- return;
-
c->setTextDrawingMode(fill ? TextModeFill : TextModeStroke);
+
+ GraphicsContextStateSaver stateSaver(*c);
if (useMaxWidth) {
- GraphicsContextStateSaver stateSaver(*c);
c->translate(location.x(), location.y());
// We draw when fontWidth is 0 so compositing operations (eg, a "copy" op) still work.
c->scale(FloatSize((fontWidth > 0 ? (width / fontWidth) : 0), 1));
- c->drawBidiText(font, textRunPaintInfo, FloatPoint(0, 0), Font::UseFallbackIfFontNotReady);
- } else
- c->drawBidiText(font, textRunPaintInfo, location, Font::UseFallbackIfFontNotReady);
+ location = FloatPoint();
+ }
- didDraw(dirtyRect);
+ FloatRect clipBounds;
+ if (!c->getTransformedClipBounds(&clipBounds)) {
+ return;
+ }
+
+ if (isFullCanvasCompositeMode(state().m_globalComposite)) {
+ c->beginLayer(1, state().m_globalComposite);
+ CompositeOperator previousOperator = c->compositeOperation();
+ c->setCompositeOperation(CompositeSourceOver);
+ c->drawBidiText(font, textRunPaintInfo, location, Font::UseFallbackIfFontNotReady);
+ c->setCompositeOperation(previousOperator);
+ c->endLayer();
+ didDraw(clipBounds);
+ } else if (state().m_globalComposite == CompositeCopy) {
+ clearCanvas();
+ c->drawBidiText(font, textRunPaintInfo, location, Font::UseFallbackIfFontNotReady);
+ didDraw(clipBounds);
+ } else {
+ FloatRect dirtyRect;
+ if (computeDirtyRect(textRunPaintInfo.bounds, clipBounds, &dirtyRect)) {
+ c->drawBidiText(font, textRunPaintInfo, location, Font::UseFallbackIfFontNotReady);
+ didDraw(dirtyRect);
+ }
+ }
}
void CanvasRenderingContext2D::inflateStrokeRect(FloatRect& rect) const
void CanvasRenderingContext2D::drawFocusIfNeeded(Element* element)
{
- if (!focusRingCallIsValid(m_path, element))
+ drawFocusIfNeededInternal(m_path, element);
+}
+
+void CanvasRenderingContext2D::drawFocusIfNeeded(Path2D* path2d, Element* element)
+{
+ drawFocusIfNeededInternal(path2d->path(), element);
+}
+
+void CanvasRenderingContext2D::drawFocusIfNeededInternal(const Path& path, Element* element)
+{
+ if (!focusRingCallIsValid(path, element))
return;
- updateFocusRingAccessibility(m_path, element);
+ updateFocusRingAccessibility(path, element);
// Note: we need to check document->focusedElement() rather than just calling
// element->focused(), because element->focused() isn't updated until after
// focus events fire.
if (element->document().focusedElement() == element)
- drawFocusRing(m_path);
+ drawFocusRing(path);
}
bool CanvasRenderingContext2D::drawCustomFocusRing(Element* element)
bool CanvasRenderingContext2D::focusRingCallIsValid(const Path& path, Element* element)
{
+ ASSERT(element);
if (!state().m_invertibleCTM)
return false;
if (path.isEmpty())
return false;
- if (!element || !element->isDescendantOf(canvas()))
+ if (!element->isDescendantOf(canvas()))
return false;
return true;
if (AXObjectCache* axObjectCache = element->document().existingAXObjectCache()) {
if (AXObject* obj = axObjectCache->getOrCreate(element)) {
// Get the bounding rect and apply transformations.
- FloatRect bounds = m_path.boundingRect();
+ FloatRect bounds = path.boundingRect();
AffineTransform ctm = state().m_transform;
FloatRect transformedBounds = ctm.mapRect(bounds);
LayoutRect elementRect = LayoutRect(transformedBounds);