diff options
Diffstat (limited to 'Source/WebCore/platform/graphics/android/context/PlatformGraphicsContextRecording.cpp')
-rw-r--r-- | Source/WebCore/platform/graphics/android/context/PlatformGraphicsContextRecording.cpp | 805 |
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 |