summaryrefslogtreecommitdiffstats
path: root/Source/WebCore/platform/graphics/android/context/PlatformGraphicsContextRecording.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/platform/graphics/android/context/PlatformGraphicsContextRecording.cpp')
-rw-r--r--Source/WebCore/platform/graphics/android/context/PlatformGraphicsContextRecording.cpp805
1 files changed, 719 insertions, 86 deletions
diff --git a/Source/WebCore/platform/graphics/android/context/PlatformGraphicsContextRecording.cpp b/Source/WebCore/platform/graphics/android/context/PlatformGraphicsContextRecording.cpp
index d96124d..6c033b6 100644
--- a/Source/WebCore/platform/graphics/android/context/PlatformGraphicsContextRecording.cpp
+++ b/Source/WebCore/platform/graphics/android/context/PlatformGraphicsContextRecording.cpp
@@ -1,3 +1,28 @@
+/*
+ * Copyright 2012, The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
#define LOG_TAG "PlatformGraphicsContextRecording"
#define LOG_NDEBUG 1
@@ -5,71 +30,470 @@
#include "PlatformGraphicsContextRecording.h"
#include "AndroidLog.h"
+#include "FloatRect.h"
+#include "FloatQuad.h"
#include "Font.h"
#include "GraphicsContext.h"
-#include "GraphicsOperationCollection.h"
#include "GraphicsOperation.h"
+#include "LinearAllocator.h"
+#include "PlatformGraphicsContextSkia.h"
+#include "RTree.h"
+
+#include "wtf/NonCopyingSort.h"
+#include "wtf/HashSet.h"
+#include "wtf/StringHasher.h"
+
+#define NEW_OP(X) new (operationHeap()) GraphicsOperation::X
+
+#define USE_CLIPPING_PAINTER true
namespace WebCore {
+static FloatRect approximateTextBounds(size_t numGlyphs,
+ const SkPoint pos[], const SkPaint& paint)
+{
+ if (!numGlyphs || !pos) {
+ return FloatRect();
+ }
+
+ // get glyph position bounds
+ SkScalar minX = pos[0].x();
+ SkScalar maxX = minX;
+ SkScalar minY = pos[0].y();
+ SkScalar maxY = minY;
+ for (size_t i = 1; i < numGlyphs; ++i) {
+ SkScalar x = pos[i].x();
+ SkScalar y = pos[i].y();
+ minX = std::min(minX, x);
+ maxX = std::max(maxX, x);
+ minY = std::min(minY, y);
+ maxY = std::max(maxY, y);
+ }
+
+ // build final rect
+ SkPaint::FontMetrics metrics;
+ SkScalar bufY = paint.getFontMetrics(&metrics);
+ SkScalar bufX = bufY * 2;
+ SkScalar adjY = metrics.fAscent / 2;
+ minY += adjY;
+ maxY += adjY;
+ SkRect rect;
+ rect.set(minX - bufX, minY - bufY, maxX + bufX, maxY + bufY);
+ return rect;
+}
+
+class StateHash {
+public:
+ static unsigned hash(PlatformGraphicsContext::State* const& state)
+ {
+ return StringHasher::hashMemory(state, sizeof(PlatformGraphicsContext::State));
+ }
+
+ static bool equal(PlatformGraphicsContext::State* const& a,
+ PlatformGraphicsContext::State* const& b)
+ {
+ return a && b && !memcmp(a, b, sizeof(PlatformGraphicsContext::State));
+ }
+
+ static const bool safeToCompareToEmptyOrDeleted = false;
+};
+
+typedef HashSet<PlatformGraphicsContext::State*, StateHash> StateHashSet;
+
+class CanvasState {
+public:
+ CanvasState(CanvasState* parent)
+ : m_parent(parent)
+ , m_isTransparencyLayer(false)
+ {}
+
+ CanvasState(CanvasState* parent, float opacity)
+ : m_parent(parent)
+ , m_isTransparencyLayer(true)
+ , m_opacity(opacity)
+ {}
+
+ ~CanvasState() {
+ ALOGV("Delete %p", this);
+ for (size_t i = 0; i < m_operations.size(); i++)
+ delete m_operations[i];
+ m_operations.clear();
+ }
+
+ bool isParentOf(CanvasState* other) {
+ while (other->m_parent) {
+ if (other->m_parent == this)
+ return true;
+ other = other->m_parent;
+ }
+ return false;
+ }
+
+ void playback(PlatformGraphicsContext* context, size_t fromId, size_t toId) const {
+ ALOGV("playback %p from %d->%d", this, fromId, toId);
+ for (size_t i = 0; i < m_operations.size(); i++) {
+ RecordingData *data = m_operations[i];
+ if (data->m_orderBy < fromId)
+ continue;
+ if (data->m_orderBy > toId)
+ break;
+ ALOGV("Applying operation[%d] %p->%s()", i, data->m_operation,
+ data->m_operation->name());
+ data->m_operation->apply(context);
+ }
+ }
+
+ CanvasState* parent() { return m_parent; }
+
+ void enterState(PlatformGraphicsContext* context) {
+ ALOGV("enterState %p", this);
+ if (m_isTransparencyLayer)
+ context->beginTransparencyLayer(m_opacity);
+ else
+ context->save();
+ }
+
+ void exitState(PlatformGraphicsContext* context) {
+ ALOGV("exitState %p", this);
+ if (m_isTransparencyLayer)
+ context->endTransparencyLayer();
+ else
+ context->restore();
+ }
+
+ void adoptAndAppend(RecordingData* data) {
+ m_operations.append(data);
+ }
+
+ bool isTransparencyLayer() {
+ return m_isTransparencyLayer;
+ }
+
+ void* operator new(size_t size, LinearAllocator* la) {
+ return la->alloc(size);
+ }
+
+private:
+ CanvasState *m_parent;
+ bool m_isTransparencyLayer;
+ float m_opacity;
+ Vector<RecordingData*> m_operations;
+};
+
+class RecordingImpl {
+private:
+ // Careful, ordering matters here. Ordering is first constructed == last destroyed,
+ // so we have to make sure our Heap is the first thing listed so that it is
+ // the last thing destroyed.
+ LinearAllocator m_operationHeap;
+ LinearAllocator m_canvasStateHeap;
+ LinearAllocator m_stateHeap;
+public:
+ RecordingImpl()
+ : m_canvasStateHeap(sizeof(CanvasState))
+ , m_stateHeap(sizeof(PlatformGraphicsContext::State))
+ , m_nodeCount(0)
+ {
+ }
+
+ ~RecordingImpl() {
+ clearStates();
+ clearCanvasStates();
+ }
+
+ PlatformGraphicsContext::State* getState(PlatformGraphicsContext::State* inState) {
+ StateHashSet::iterator it = m_states.find(inState);
+ if (it != m_states.end())
+ return (*it);
+ void* buf = m_stateHeap.alloc(sizeof(PlatformGraphicsContext::State));
+ PlatformGraphicsContext::State* state = new (buf) PlatformGraphicsContext::State(*inState);
+ m_states.add(state);
+ return state;
+ }
+
+ void addCanvasState(CanvasState* state) {
+ m_canvasStates.append(state);
+ }
+
+ void removeCanvasState(const CanvasState* state) {
+ if (m_canvasStates.last() == state)
+ m_canvasStates.removeLast();
+ else {
+ size_t indx = m_canvasStates.find(state);
+ m_canvasStates.remove(indx);
+ }
+ }
+
+ void applyState(PlatformGraphicsContext* context,
+ CanvasState* fromState, size_t fromId,
+ CanvasState* toState, size_t toId) {
+ ALOGV("applyState(%p->%p, %d-%d)", fromState, toState, fromId, toId);
+ if (fromState != toState && fromState) {
+ if (fromState->isParentOf(toState)) {
+ // Going down the tree, playback any parent operations then save
+ // before playing back our current operations
+ applyState(context, fromState, fromId, toState->parent(), toId);
+ toState->enterState(context);
+ } else if (toState->isParentOf(fromState)) {
+ // Going up the tree, pop some states
+ while (fromState != toState) {
+ fromState->exitState(context);
+ fromState = fromState->parent();
+ }
+ } else {
+ // Siblings in the tree
+ fromState->exitState(context);
+ applyState(context, fromState->parent(), fromId, toState, toId);
+ return;
+ }
+ } else if (!fromState) {
+ if (toState->parent())
+ applyState(context, fromState, fromId, toState->parent(), toId);
+ toState->enterState(context);
+ }
+ toState->playback(context, fromId, toId);
+ }
+
+ LinearAllocator* operationHeap() { return &m_operationHeap; }
+ LinearAllocator* canvasStateHeap() { return &m_canvasStateHeap; }
+
+ RTree::RTree m_tree;
+ int m_nodeCount;
+
+private:
+
+ void clearStates() {
+ StateHashSet::iterator end = m_states.end();
+ for (StateHashSet::iterator it = m_states.begin(); it != end; ++it)
+ (*it)->~State();
+ m_states.clear();
+ }
+
+ void clearCanvasStates() {
+ for (size_t i = 0; i < m_canvasStates.size(); i++)
+ m_canvasStates[i]->~CanvasState();
+ m_canvasStates.clear();
+ }
+
+ // TODO: Use a global pool?
+ StateHashSet m_states;
+ Vector<CanvasState*> m_canvasStates;
+};
+
+Recording::~Recording()
+{
+ delete m_recording;
+}
+
+static bool CompareRecordingDataOrder(const RecordingData* a, const RecordingData* b)
+{
+ return a->m_orderBy < b->m_orderBy;
+}
+
+static IntRect enclosedIntRect(const FloatRect& rect)
+{
+ float left = ceilf(rect.x());
+ float top = ceilf(rect.y());
+ float width = floorf(rect.maxX()) - left;
+ float height = floorf(rect.maxY()) - top;
+
+ return IntRect(clampToInteger(left), clampToInteger(top),
+ clampToInteger(width), clampToInteger(height));
+}
+
+#if USE_CLIPPING_PAINTER
+class ClippingPainter {
+public:
+ ClippingPainter(RecordingImpl* recording,
+ PlatformGraphicsContextSkia& context,
+ const SkMatrix& initialMatrix,
+ Vector<RecordingData*> &nodes)
+ : m_recording(recording)
+ , m_context(context)
+ , m_initialMatrix(initialMatrix)
+ , m_nodes(nodes)
+ , m_lastOperationId(0)
+ , m_currState(0)
+ {}
+
+ void draw(const SkIRect& bounds) {
+ SkRegion coveredArea(bounds);
+
+ drawWithClipRecursive(static_cast<int>(m_nodes.size()) - 1, coveredArea);
+
+ while (m_currState) {
+ m_currState->exitState(&m_context);
+ m_currState = m_currState->parent();
+ }
+ }
+
+private:
+ void drawOperation(RecordingData* node, const SkRegion& covered)
+ {
+ GraphicsOperation::Operation* op = node->m_operation;
+ m_recording->applyState(&m_context, m_currState,
+ m_lastOperationId, op->m_canvasState, node->m_orderBy);
+ m_currState = op->m_canvasState;
+ m_lastOperationId = node->m_orderBy;
+
+ // if other opaque operations will cover the current one, clip that area out
+ // (and restore the clip immediately after drawing)
+ if (!covered.isEmpty()) {
+ m_context.save();
+ m_context.canvas()->clipRegion(covered, SkRegion::kIntersect_Op);
+ }
+ op->apply(&(m_context));
+ if (!covered.isEmpty())
+ m_context.restore();
+ }
+
+ void drawWithClipRecursive(int index, const SkRegion& covered)
+ {
+ if (index < 0)
+ return;
+ RecordingData* recordingData = m_nodes[index];
+ GraphicsOperation::Operation* op = recordingData->m_operation;
+ if (index != 0) {
+ const IntRect* opaqueRect = op->opaqueRect();
+ if (!opaqueRect || opaqueRect->isEmpty()) {
+ drawWithClipRecursive(index - 1, covered);
+ } else {
+ SkRegion newCovered = covered;
+ SkRect mappedRect = *opaqueRect;
+ m_initialMatrix.mapRect(&mappedRect);
+ newCovered.op(enclosedIntRect(mappedRect), SkRegion::kDifference_Op);
+ if (!newCovered.isEmpty())
+ drawWithClipRecursive(index - 1, newCovered);
+ }
+ }
+ drawOperation(recordingData, covered);
+ }
+
+ RecordingImpl* m_recording;
+ PlatformGraphicsContextSkia& m_context;
+ const SkMatrix& m_initialMatrix;
+ const Vector<RecordingData*>& m_nodes;
+ size_t m_lastOperationId;
+ CanvasState* m_currState;
+};
+#endif // USE_CLIPPING_PAINTER
+
+void Recording::draw(SkCanvas* canvas)
+{
+ if (!m_recording) {
+ ALOGW("No recording!");
+ return;
+ }
+ SkRect clip;
+ if (!canvas->getClipBounds(&clip)) {
+ ALOGW("Empty clip!");
+ return;
+ }
+ Vector<RecordingData*> nodes;
+
+ WebCore::IntRect iclip = enclosingIntRect(clip);
+ m_recording->m_tree.search(iclip, nodes);
+
+ size_t count = nodes.size();
+ ALOGV("Drawing %d nodes out of %d", count, m_recording->m_nodeCount);
+ if (count) {
+ int saveCount = canvas->getSaveCount();
+ nonCopyingSort(nodes.begin(), nodes.end(), CompareRecordingDataOrder);
+ PlatformGraphicsContextSkia context(canvas);
+#if USE_CLIPPING_PAINTER
+ ClippingPainter painter(recording(), context, canvas->getTotalMatrix(), nodes);
+ painter.draw(canvas->getTotalClip().getBounds());
+#else
+ CanvasState* currState = 0;
+ size_t lastOperationId = 0;
+ for (size_t i = 0; i < count; i++) {
+ GraphicsOperation::Operation* op = nodes[i]->m_operation;
+ m_recording->applyState(&context, currState, lastOperationId,
+ op->m_canvasState, nodes[i]->m_orderBy);
+ currState = op->m_canvasState;
+ lastOperationId = nodes[i]->m_orderBy;
+ ALOGV("apply: %p->%s()", op, op->name());
+ op->apply(&context);
+ }
+ while (currState) {
+ currState->exitState(&context);
+ currState = currState->parent();
+ }
+#endif
+ if (saveCount != canvas->getSaveCount()) {
+ ALOGW("Save/restore mismatch! %d vs. %d", saveCount, canvas->getSaveCount());
+ }
+ }
+}
+
+void Recording::setRecording(RecordingImpl* impl)
+{
+ if (m_recording == impl)
+ return;
+ if (m_recording)
+ delete m_recording;
+ m_recording = impl;
+}
+
//**************************************
// PlatformGraphicsContextRecording
//**************************************
-PlatformGraphicsContextRecording::PlatformGraphicsContextRecording(GraphicsOperationCollection* picture)
+PlatformGraphicsContextRecording::PlatformGraphicsContextRecording(Recording* recording)
: PlatformGraphicsContext()
- , mGraphicsOperationCollection(picture)
, mPicture(0)
+ , mRecording(recording)
+ , mOperationState(0)
+ , m_hasText(false)
+ , m_isEmpty(true)
+ , m_canvasProxy(this)
{
+ if (mRecording)
+ mRecording->setRecording(new RecordingImpl());
+ mMatrixStack.append(SkMatrix::I());
+ mCurrentMatrix = &(mMatrixStack.last());
+ pushStateOperation(new (canvasStateHeap()) CanvasState(0));
}
bool PlatformGraphicsContextRecording::isPaintingDisabled()
{
- return !mGraphicsOperationCollection;
+ return !mRecording;
}
SkCanvas* PlatformGraphicsContextRecording::recordingCanvas()
{
- SkSafeUnref(mPicture);
- mPicture = new SkPicture();
- return mPicture->beginRecording(0, 0, 0);
+ m_hasText = true;
+ return &m_canvasProxy;
}
-void PlatformGraphicsContextRecording::endRecording(int type)
-{
- if (!mPicture)
- return;
- mPicture->endRecording();
- GraphicsOperation::DrawComplexText* text = new GraphicsOperation::DrawComplexText(mPicture);
- mGraphicsOperationCollection->append(text);
- mPicture = 0;
-}
-
-
//**************************************
// State management
//**************************************
void PlatformGraphicsContextRecording::beginTransparencyLayer(float opacity)
{
- mGraphicsOperationCollection->append(new GraphicsOperation::BeginTransparencyLayer(opacity));
+ CanvasState* parent = mRecordingStateStack.last().mCanvasState;
+ pushStateOperation(new (canvasStateHeap()) CanvasState(parent, opacity));
}
void PlatformGraphicsContextRecording::endTransparencyLayer()
{
- mGraphicsOperationCollection->append(new GraphicsOperation::EndTransparencyLayer());
+ popStateOperation();
}
void PlatformGraphicsContextRecording::save()
{
PlatformGraphicsContext::save();
- mGraphicsOperationCollection->append(new GraphicsOperation::Save());
+ CanvasState* parent = mRecordingStateStack.last().mCanvasState;
+ pushStateOperation(new (canvasStateHeap()) CanvasState(parent));
+ pushMatrix();
}
void PlatformGraphicsContextRecording::restore()
{
PlatformGraphicsContext::restore();
- mGraphicsOperationCollection->append(new GraphicsOperation::Restore());
+ popMatrix();
+ popStateOperation();
}
//**************************************
@@ -79,86 +503,98 @@ void PlatformGraphicsContextRecording::restore()
void PlatformGraphicsContextRecording::setAlpha(float alpha)
{
PlatformGraphicsContext::setAlpha(alpha);
- mGraphicsOperationCollection->append(new GraphicsOperation::SetAlpha(alpha));
+ mOperationState = 0;
}
void PlatformGraphicsContextRecording::setCompositeOperation(CompositeOperator op)
{
PlatformGraphicsContext::setCompositeOperation(op);
- mGraphicsOperationCollection->append(new GraphicsOperation::SetCompositeOperation(op));
+ mOperationState = 0;
}
-void PlatformGraphicsContextRecording::setFillColor(const Color& c)
+bool PlatformGraphicsContextRecording::setFillColor(const Color& c)
{
- PlatformGraphicsContext::setFillColor(c);
- mGraphicsOperationCollection->append(new GraphicsOperation::SetFillColor(c));
+ if (PlatformGraphicsContext::setFillColor(c)) {
+ mOperationState = 0;
+ return true;
+ }
+ return false;
}
-void PlatformGraphicsContextRecording::setFillShader(SkShader* fillShader)
+bool PlatformGraphicsContextRecording::setFillShader(SkShader* fillShader)
{
- PlatformGraphicsContext::setFillShader(fillShader);
- mGraphicsOperationCollection->append(new GraphicsOperation::SetFillShader(fillShader));
+ if (PlatformGraphicsContext::setFillShader(fillShader)) {
+ mOperationState = 0;
+ return true;
+ }
+ return false;
}
void PlatformGraphicsContextRecording::setLineCap(LineCap cap)
{
PlatformGraphicsContext::setLineCap(cap);
- mGraphicsOperationCollection->append(new GraphicsOperation::SetLineCap(cap));
+ mOperationState = 0;
}
void PlatformGraphicsContextRecording::setLineDash(const DashArray& dashes, float dashOffset)
{
PlatformGraphicsContext::setLineDash(dashes, dashOffset);
- mGraphicsOperationCollection->append(new GraphicsOperation::SetLineDash(dashes, dashOffset));
+ mOperationState = 0;
}
void PlatformGraphicsContextRecording::setLineJoin(LineJoin join)
{
PlatformGraphicsContext::setLineJoin(join);
- mGraphicsOperationCollection->append(new GraphicsOperation::SetLineJoin(join));
+ mOperationState = 0;
}
void PlatformGraphicsContextRecording::setMiterLimit(float limit)
{
PlatformGraphicsContext::setMiterLimit(limit);
- mGraphicsOperationCollection->append(new GraphicsOperation::SetMiterLimit(limit));
+ mOperationState = 0;
}
void PlatformGraphicsContextRecording::setShadow(int radius, int dx, int dy, SkColor c)
{
PlatformGraphicsContext::setShadow(radius, dx, dy, c);
- mGraphicsOperationCollection->append(new GraphicsOperation::SetShadow(radius, dx, dy, c));
+ mOperationState = 0;
}
void PlatformGraphicsContextRecording::setShouldAntialias(bool useAA)
{
m_state->useAA = useAA;
PlatformGraphicsContext::setShouldAntialias(useAA);
- mGraphicsOperationCollection->append(new GraphicsOperation::SetShouldAntialias(useAA));
+ mOperationState = 0;
}
-void PlatformGraphicsContextRecording::setStrokeColor(const Color& c)
+bool PlatformGraphicsContextRecording::setStrokeColor(const Color& c)
{
- PlatformGraphicsContext::setStrokeColor(c);
- mGraphicsOperationCollection->append(new GraphicsOperation::SetStrokeColor(c));
+ if (PlatformGraphicsContext::setStrokeColor(c)) {
+ mOperationState = 0;
+ return true;
+ }
+ return false;
}
-void PlatformGraphicsContextRecording::setStrokeShader(SkShader* strokeShader)
+bool PlatformGraphicsContextRecording::setStrokeShader(SkShader* strokeShader)
{
- PlatformGraphicsContext::setStrokeShader(strokeShader);
- mGraphicsOperationCollection->append(new GraphicsOperation::SetStrokeShader(strokeShader));
+ if (PlatformGraphicsContext::setStrokeShader(strokeShader)) {
+ mOperationState = 0;
+ return true;
+ }
+ return false;
}
void PlatformGraphicsContextRecording::setStrokeStyle(StrokeStyle style)
{
PlatformGraphicsContext::setStrokeStyle(style);
- mGraphicsOperationCollection->append(new GraphicsOperation::SetStrokeStyle(style));
+ mOperationState = 0;
}
void PlatformGraphicsContextRecording::setStrokeThickness(float f)
{
PlatformGraphicsContext::setStrokeThickness(f);
- mGraphicsOperationCollection->append(new GraphicsOperation::SetStrokeThickness(f));
+ mOperationState = 0;
}
//**************************************
@@ -167,32 +603,32 @@ void PlatformGraphicsContextRecording::setStrokeThickness(float f)
void PlatformGraphicsContextRecording::concatCTM(const AffineTransform& affine)
{
- mCurrentMatrix.preConcat(affine);
- mGraphicsOperationCollection->append(new GraphicsOperation::ConcatCTM(affine));
+ mCurrentMatrix->preConcat(affine);
+ appendStateOperation(NEW_OP(ConcatCTM)(affine));
}
void PlatformGraphicsContextRecording::rotate(float angleInRadians)
{
float value = angleInRadians * (180.0f / 3.14159265f);
- mCurrentMatrix.preRotate(SkFloatToScalar(value));
- mGraphicsOperationCollection->append(new GraphicsOperation::Rotate(angleInRadians));
+ mCurrentMatrix->preRotate(SkFloatToScalar(value));
+ appendStateOperation(NEW_OP(Rotate)(angleInRadians));
}
void PlatformGraphicsContextRecording::scale(const FloatSize& size)
{
- mCurrentMatrix.preScale(SkFloatToScalar(size.width()), SkFloatToScalar(size.height()));
- mGraphicsOperationCollection->append(new GraphicsOperation::Scale(size));
+ mCurrentMatrix->preScale(SkFloatToScalar(size.width()), SkFloatToScalar(size.height()));
+ appendStateOperation(NEW_OP(Scale)(size));
}
void PlatformGraphicsContextRecording::translate(float x, float y)
{
- mCurrentMatrix.preTranslate(SkFloatToScalar(x), SkFloatToScalar(y));
- mGraphicsOperationCollection->append(new GraphicsOperation::Translate(x, y));
+ mCurrentMatrix->preTranslate(SkFloatToScalar(x), SkFloatToScalar(y));
+ appendStateOperation(NEW_OP(Translate)(x, y));
}
const SkMatrix& PlatformGraphicsContextRecording::getTotalMatrix()
{
- return mCurrentMatrix;
+ return *mCurrentMatrix;
}
//**************************************
@@ -202,50 +638,65 @@ const SkMatrix& PlatformGraphicsContextRecording::getTotalMatrix()
void PlatformGraphicsContextRecording::addInnerRoundedRectClip(const IntRect& rect,
int thickness)
{
- mGraphicsOperationCollection->append(new GraphicsOperation::InnerRoundedRectClip(rect, thickness));
+ mRecordingStateStack.last().setHasComplexClip();
+ appendStateOperation(NEW_OP(InnerRoundedRectClip)(rect, thickness));
}
void PlatformGraphicsContextRecording::canvasClip(const Path& path)
{
+ mRecordingStateStack.last().setHasComplexClip();
clip(path);
}
-void PlatformGraphicsContextRecording::clip(const FloatRect& rect)
+bool PlatformGraphicsContextRecording::clip(const FloatRect& rect)
{
- mGraphicsOperationCollection->append(new GraphicsOperation::Clip(rect));
+ clipState(rect);
+ appendStateOperation(NEW_OP(Clip)(rect));
+ return true;
}
-void PlatformGraphicsContextRecording::clip(const Path& path)
+bool PlatformGraphicsContextRecording::clip(const Path& path)
{
- mGraphicsOperationCollection->append(new GraphicsOperation::ClipPath(path));
+ mRecordingStateStack.last().setHasComplexClip();
+ clipState(path.boundingRect());
+ appendStateOperation(NEW_OP(ClipPath)(path));
+ return true;
}
-void PlatformGraphicsContextRecording::clipConvexPolygon(size_t numPoints,
+bool PlatformGraphicsContextRecording::clipConvexPolygon(size_t numPoints,
const FloatPoint*, bool antialias)
{
// TODO
+ return true;
}
-void PlatformGraphicsContextRecording::clipOut(const IntRect& r)
+bool PlatformGraphicsContextRecording::clipOut(const IntRect& r)
{
- mGraphicsOperationCollection->append(new GraphicsOperation::ClipOut(r));
+ mRecordingStateStack.last().setHasComplexClip();
+ appendStateOperation(NEW_OP(ClipOut)(r));
+ return true;
}
-void PlatformGraphicsContextRecording::clipOut(const Path& path)
+bool PlatformGraphicsContextRecording::clipOut(const Path& path)
{
- mGraphicsOperationCollection->append(new GraphicsOperation::ClipPath(path, true));
+ mRecordingStateStack.last().setHasComplexClip();
+ appendStateOperation(NEW_OP(ClipPath)(path, true));
+ return true;
}
-void PlatformGraphicsContextRecording::clipPath(const Path& pathToClip, WindRule clipRule)
+bool PlatformGraphicsContextRecording::clipPath(const Path& pathToClip, WindRule clipRule)
{
- GraphicsOperation::ClipPath* operation = new GraphicsOperation::ClipPath(pathToClip);
+ mRecordingStateStack.last().setHasComplexClip();
+ clipState(pathToClip.boundingRect());
+ GraphicsOperation::ClipPath* operation = NEW_OP(ClipPath)(pathToClip);
operation->setWindRule(clipRule);
- mGraphicsOperationCollection->append(operation);
+ appendStateOperation(operation);
+ return true;
}
void PlatformGraphicsContextRecording::clearRect(const FloatRect& rect)
{
- mGraphicsOperationCollection->append(new GraphicsOperation::ClearRect(rect));
+ appendDrawingOperation(NEW_OP(ClearRect)(rect), rect);
}
//**************************************
@@ -256,33 +707,48 @@ void PlatformGraphicsContextRecording::drawBitmapPattern(
const SkBitmap& bitmap, const SkMatrix& matrix,
CompositeOperator compositeOp, const FloatRect& destRect)
{
- mGraphicsOperationCollection->append(new GraphicsOperation::DrawBitmapPattern(bitmap, matrix, compositeOp, destRect));
+ appendDrawingOperation(
+ NEW_OP(DrawBitmapPattern)(bitmap, matrix, compositeOp, destRect),
+ destRect);
}
void PlatformGraphicsContextRecording::drawBitmapRect(const SkBitmap& bitmap,
const SkIRect* src, const SkRect& dst,
CompositeOperator op)
{
- mGraphicsOperationCollection->append(new GraphicsOperation::DrawBitmapRect(bitmap, *src, dst, op));
+ appendDrawingOperation(NEW_OP(DrawBitmapRect)(bitmap, *src, dst, op), dst);
}
void PlatformGraphicsContextRecording::drawConvexPolygon(size_t numPoints,
const FloatPoint* points,
bool shouldAntialias)
{
- // TODO
+ if (numPoints < 1) return;
+ if (numPoints != 4) {
+ // TODO: Build a path and call draw on that (webkit currently never calls this)
+ ALOGW("drawConvexPolygon with numPoints != 4 is not supported!");
+ return;
+ }
+ FloatRect bounds;
+ bounds.fitToPoints(points[0], points[1], points[2], points[3]);
+ appendDrawingOperation(NEW_OP(DrawConvexPolygonQuad)(points, shouldAntialias), bounds);
}
void PlatformGraphicsContextRecording::drawEllipse(const IntRect& rect)
{
- mGraphicsOperationCollection->append(new GraphicsOperation::DrawEllipse(rect));
+ appendDrawingOperation(NEW_OP(DrawEllipse)(rect), rect);
}
void PlatformGraphicsContextRecording::drawFocusRing(const Vector<IntRect>& rects,
- int /* width */, int /* offset */,
+ int width, int offset,
const Color& color)
{
- // TODO
+ if (!rects.size())
+ return;
+ IntRect bounds = rects[0];
+ for (size_t i = 1; i < rects.size(); i++)
+ bounds.unite(rects[i]);
+ appendDrawingOperation(NEW_OP(DrawFocusRing)(rects, width, offset, color), bounds);
}
void PlatformGraphicsContextRecording::drawHighlightForText(
@@ -307,41 +773,47 @@ void PlatformGraphicsContextRecording::drawHighlightForText(
void PlatformGraphicsContextRecording::drawLine(const IntPoint& point1,
const IntPoint& point2)
{
- mGraphicsOperationCollection->append(new GraphicsOperation::DrawLine(point1, point2));
+ FloatRect bounds = FloatQuad(point1, point1, point2, point2).boundingBox();
+ float width = m_state->strokeThickness;
+ if (!width) width = 1;
+ bounds.inflate(width);
+ appendDrawingOperation(NEW_OP(DrawLine)(point1, point2), bounds);
}
void PlatformGraphicsContextRecording::drawLineForText(const FloatPoint& pt, float width)
{
- mGraphicsOperationCollection->append(new GraphicsOperation::DrawLineForText(pt, width));
+ FloatRect bounds(pt.x(), pt.y(), width, m_state->strokeThickness);
+ appendDrawingOperation(NEW_OP(DrawLineForText)(pt, width), bounds);
}
void PlatformGraphicsContextRecording::drawLineForTextChecking(const FloatPoint& pt,
float width, GraphicsContext::TextCheckingLineStyle lineStyle)
{
- mGraphicsOperationCollection->append(new GraphicsOperation::DrawLineForTextChecking(pt, width, lineStyle));
+ FloatRect bounds(pt.x(), pt.y(), width, m_state->strokeThickness);
+ appendDrawingOperation(NEW_OP(DrawLineForTextChecking)(pt, width, lineStyle), bounds);
}
void PlatformGraphicsContextRecording::drawRect(const IntRect& rect)
{
- mGraphicsOperationCollection->append(new GraphicsOperation::DrawRect(rect));
+ appendDrawingOperation(NEW_OP(DrawRect)(rect), rect);
}
void PlatformGraphicsContextRecording::fillPath(const Path& pathToFill, WindRule fillRule)
{
- mGraphicsOperationCollection->append(new GraphicsOperation::FillPath(pathToFill, fillRule));
+ appendDrawingOperation(NEW_OP(FillPath)(pathToFill, fillRule), pathToFill.boundingRect());
}
void PlatformGraphicsContextRecording::fillRect(const FloatRect& rect)
{
- mGraphicsOperationCollection->append(new GraphicsOperation::FillRect(rect));
+ appendDrawingOperation(NEW_OP(FillRect)(rect), rect);
}
void PlatformGraphicsContextRecording::fillRect(const FloatRect& rect,
const Color& color)
{
- GraphicsOperation::FillRect* operation = new GraphicsOperation::FillRect(rect);
+ GraphicsOperation::FillRect* operation = NEW_OP(FillRect)(rect);
operation->setColor(color);
- mGraphicsOperationCollection->append(operation);
+ appendDrawingOperation(operation, rect);
}
void PlatformGraphicsContextRecording::fillRoundedRect(
@@ -349,25 +821,186 @@ void PlatformGraphicsContextRecording::fillRoundedRect(
const IntSize& bottomLeft, const IntSize& bottomRight,
const Color& color)
{
- mGraphicsOperationCollection->append(new GraphicsOperation::FillRoundedRect(rect, topLeft,
- topRight, bottomLeft, bottomRight, color));
+ appendDrawingOperation(NEW_OP(FillRoundedRect)(rect, topLeft,
+ topRight, bottomLeft, bottomRight, color), rect);
}
void PlatformGraphicsContextRecording::strokeArc(const IntRect& r, int startAngle,
int angleSpan)
{
- mGraphicsOperationCollection->append(new GraphicsOperation::StrokeArc(r, startAngle, angleSpan));
+ appendDrawingOperation(NEW_OP(StrokeArc)(r, startAngle, angleSpan), r);
}
void PlatformGraphicsContextRecording::strokePath(const Path& pathToStroke)
{
- mGraphicsOperationCollection->append(new GraphicsOperation::StrokePath(pathToStroke));
+ appendDrawingOperation(NEW_OP(StrokePath)(pathToStroke), pathToStroke.boundingRect());
}
void PlatformGraphicsContextRecording::strokeRect(const FloatRect& rect, float lineWidth)
{
- mGraphicsOperationCollection->append(new GraphicsOperation::StrokeRect(rect, lineWidth));
+ FloatRect bounds = rect;
+ bounds.inflate(lineWidth);
+ appendDrawingOperation(NEW_OP(StrokeRect)(rect, lineWidth), bounds);
+}
+
+void PlatformGraphicsContextRecording::drawPosText(const void* text, size_t byteLength,
+ const SkPoint pos[], const SkPaint& paint)
+{
+ if (paint.getTextEncoding() != SkPaint::kGlyphID_TextEncoding) {
+ ALOGE("Unsupported text encoding! %d", paint.getTextEncoding());
+ }
+ FloatRect bounds = approximateTextBounds(byteLength / sizeof(uint16_t), pos, paint);
+ appendDrawingOperation(NEW_OP(DrawPosText)(text, byteLength, pos, paint), bounds);
+}
+
+void PlatformGraphicsContextRecording::drawMediaButton(const IntRect& rect, RenderSkinMediaButton::MediaButton buttonType,
+ bool translucent, bool drawBackground,
+ const IntRect& thumb)
+{
+ appendDrawingOperation(NEW_OP(DrawMediaButton)(rect, buttonType,
+ translucent, drawBackground, thumb), rect);
+}
+
+void PlatformGraphicsContextRecording::clipState(const FloatRect& clip)
+{
+ if (mRecordingStateStack.size()) {
+ SkRect mapBounds;
+ mCurrentMatrix->mapRect(&mapBounds, clip);
+ mRecordingStateStack.last().clip(mapBounds);
+ }
+}
+
+void PlatformGraphicsContextRecording::pushStateOperation(CanvasState* canvasState)
+{
+ ALOGV("pushStateOperation: %p(isLayer=%d)", canvasState, canvasState->isTransparencyLayer());
+ mRecordingStateStack.append(canvasState);
+ mRecording->recording()->addCanvasState(canvasState);
+}
+
+void PlatformGraphicsContextRecording::popStateOperation()
+{
+ RecordingState state = mRecordingStateStack.last();
+ mRecordingStateStack.removeLast();
+ if (!state.mHasDrawing) {
+ ALOGV("popStateOperation is deleting %p(isLayer=%d)",
+ state.mCanvasState, state.mCanvasState->isTransparencyLayer());
+ mRecording->recording()->removeCanvasState(state.mCanvasState);
+ state.mCanvasState->~CanvasState();
+ canvasStateHeap()->rewindTo(state.mCanvasState);
+ } else {
+ ALOGV("popStateOperation: %p(isLayer=%d)",
+ state.mCanvasState, state.mCanvasState->isTransparencyLayer());
+ // Make sure we propagate drawing upwards so we don't delete our parent
+ mRecordingStateStack.last().mHasDrawing = true;
+ }
+}
+
+void PlatformGraphicsContextRecording::pushMatrix()
+{
+ mMatrixStack.append(mMatrixStack.last());
+ mCurrentMatrix = &(mMatrixStack.last());
+}
+
+void PlatformGraphicsContextRecording::popMatrix()
+{
+ mMatrixStack.removeLast();
+ mCurrentMatrix = &(mMatrixStack.last());
+}
+
+IntRect PlatformGraphicsContextRecording::calculateFinalBounds(FloatRect bounds)
+{
+ if (bounds.isEmpty() && mRecordingStateStack.last().mHasClip) {
+ ALOGV("Empty bounds, but has clip so using that");
+ return enclosingIntRect(mRecordingStateStack.last().mBounds);
+ }
+ if (m_gc->hasShadow()) {
+ const ShadowRec& shadow = m_state->shadow;
+ if (shadow.blur > 0)
+ bounds.inflate(ceilf(shadow.blur));
+ bounds.setWidth(bounds.width() + abs(shadow.dx));
+ bounds.setHeight(bounds.height() + abs(shadow.dy));
+ if (shadow.dx < 0)
+ bounds.move(shadow.dx, 0);
+ if (shadow.dy < 0)
+ bounds.move(0, shadow.dy);
+ // Add a bit extra to deal with rounding and blurring
+ bounds.inflate(4);
+ }
+ if (m_state->strokeStyle != NoStroke)
+ bounds.inflate(std::min(1.0f, m_state->strokeThickness));
+ SkRect translated;
+ mCurrentMatrix->mapRect(&translated, bounds);
+ FloatRect ftrect = translated;
+ if (mRecordingStateStack.last().mHasClip
+ && !translated.intersect(mRecordingStateStack.last().mBounds)) {
+ ALOGV("Operation bounds=" FLOAT_RECT_FORMAT " clipped out by clip=" FLOAT_RECT_FORMAT,
+ FLOAT_RECT_ARGS(ftrect), FLOAT_RECT_ARGS(mRecordingStateStack.last().mBounds));
+ return IntRect();
+ }
+ return enclosingIntRect(translated);
+}
+
+IntRect PlatformGraphicsContextRecording::calculateCoveredBounds(FloatRect bounds)
+{
+ SkRect translated;
+ mCurrentMatrix->mapRect(&translated, bounds);
+ FloatRect ftrect = translated;
+ if (mRecordingStateStack.last().mHasClip
+ && !translated.intersect(mRecordingStateStack.last().mBounds)) {
+ ALOGV("Operation opaque area=" FLOAT_RECT_FORMAT " clipped out by clip=" FLOAT_RECT_FORMAT,
+ FLOAT_RECT_ARGS(ftrect), FLOAT_RECT_ARGS(mRecordingStateStack.last().mBounds));
+ return IntRect();
+ }
+ return enclosedIntRect(translated);
+}
+
+void PlatformGraphicsContextRecording::appendDrawingOperation(
+ GraphicsOperation::Operation* operation, const FloatRect& untranslatedBounds)
+{
+ m_isEmpty = false;
+ RecordingState& state = mRecordingStateStack.last();
+ state.mHasDrawing = true;
+ if (!mOperationState)
+ mOperationState = mRecording->recording()->getState(m_state);
+ operation->m_state = mOperationState;
+ operation->m_canvasState = state.mCanvasState;
+
+ WebCore::IntRect ibounds = calculateFinalBounds(untranslatedBounds);
+ if (ibounds.isEmpty()) {
+ ALOGV("Operation %s() was clipped out", operation->name());
+ operation->~Operation();
+ operationHeap()->rewindTo(operation);
+ return;
+ }
+ if (operation->isOpaque()
+ && !untranslatedBounds.isEmpty()
+ && operation->m_state->alpha == 1.0f
+ && mCurrentMatrix->rectStaysRect()
+ && !state.mHasComplexClip) {
+ // if the operation maps to an opaque rect, record the area it will cover
+ operation->setOpaqueRect(calculateCoveredBounds(untranslatedBounds));
+ }
+ ALOGV("appendOperation %p->%s() bounds " INT_RECT_FORMAT, operation, operation->name(),
+ INT_RECT_ARGS(ibounds));
+ RecordingData* data = new RecordingData(operation, mRecording->recording()->m_nodeCount++);
+ mRecording->recording()->m_tree.insert(ibounds, data);
+}
+
+void PlatformGraphicsContextRecording::appendStateOperation(GraphicsOperation::Operation* operation)
+{
+ ALOGV("appendOperation %p->%s()", operation, operation->name());
+ RecordingData* data = new RecordingData(operation, mRecording->recording()->m_nodeCount++);
+ mRecordingStateStack.last().mCanvasState->adoptAndAppend(data);
}
+LinearAllocator* PlatformGraphicsContextRecording::operationHeap()
+{
+ return mRecording->recording()->operationHeap();
+}
+
+LinearAllocator* PlatformGraphicsContextRecording::canvasStateHeap()
+{
+ return mRecording->recording()->canvasStateHeap();
+}
} // WebCore