| #include "config.h" |
| #include "LayerAndroid.h" |
| |
| #if USE(ACCELERATED_COMPOSITING) |
| |
| #include "AndroidAnimation.h" |
| #include "DrawExtra.h" |
| #include "SkCanvas.h" |
| #include "SkDrawFilter.h" |
| #include "SkPaint.h" |
| #include "SkPicture.h" |
| #include <wtf/CurrentTime.h> |
| |
| #define LAYER_DEBUG // Add diagonals for debugging |
| #undef LAYER_DEBUG |
| |
| namespace WebCore { |
| |
| static int gDebugLayerAndroidInstances; |
| static int gUniqueId; |
| |
| inline int LayerAndroid::instancesCount() |
| { |
| return gDebugLayerAndroidInstances; |
| } |
| |
| class OpacityDrawFilter : public SkDrawFilter { |
| public: |
| OpacityDrawFilter(int opacity) : m_opacity(opacity) { } |
| virtual bool filter(SkCanvas* canvas, SkPaint* paint, Type) |
| { |
| m_previousOpacity = paint->getAlpha(); |
| paint->setAlpha(m_opacity); |
| return true; |
| } |
| virtual void restore(SkCanvas* canvas, SkPaint* paint, Type) |
| { |
| paint->setAlpha(m_previousOpacity); |
| } |
| private: |
| int m_opacity; |
| int m_previousOpacity; |
| }; |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| LayerAndroid::LayerAndroid(bool isRootLayer) : SkLayer(), |
| m_isRootLayer(isRootLayer), |
| m_haveContents(false), |
| m_drawsContent(true), |
| m_haveImage(false), |
| m_haveClip(false), |
| m_doRotation(false), |
| m_isFixed(false), |
| m_recordingPicture(0), |
| m_extra(0), |
| m_uniqueId(++gUniqueId) |
| { |
| m_angleTransform = 0; |
| m_translation.set(0, 0); |
| m_scale.set(1, 1); |
| m_backgroundColor = 0; |
| |
| gDebugLayerAndroidInstances++; |
| } |
| |
| LayerAndroid::LayerAndroid(const LayerAndroid& layer) : SkLayer(layer), |
| m_isRootLayer(layer.m_isRootLayer), |
| m_haveContents(layer.m_haveContents), |
| m_drawsContent(layer.m_drawsContent), |
| m_haveImage(layer.m_haveImage), |
| m_haveClip(layer.m_haveClip), |
| m_extra(0), // deliberately not copied |
| m_uniqueId(layer.m_uniqueId) |
| { |
| m_doRotation = layer.m_doRotation; |
| m_isFixed = layer.m_isFixed; |
| |
| m_angleTransform = layer.m_angleTransform; |
| m_translation = layer.m_translation; |
| m_scale = layer.m_scale; |
| m_backgroundColor = layer.m_backgroundColor; |
| |
| m_fixedLeft = layer.m_fixedLeft; |
| m_fixedTop = layer.m_fixedTop; |
| m_fixedRight = layer.m_fixedRight; |
| m_fixedBottom = layer.m_fixedBottom; |
| |
| m_recordingPicture = layer.m_recordingPicture; |
| SkSafeRef(m_recordingPicture); |
| |
| for (int i = 0; i < layer.countChildren(); i++) |
| addChild(new LayerAndroid(*layer.getChild(i)))->unref(); |
| |
| KeyframesMap::const_iterator end = layer.m_animations.end(); |
| for (KeyframesMap::const_iterator it = layer.m_animations.begin(); it != end; ++it) |
| m_animations.add((it->second)->name(), (it->second)->copy()); |
| |
| gDebugLayerAndroidInstances++; |
| } |
| |
| LayerAndroid::LayerAndroid(SkPicture* picture) : SkLayer(), |
| m_isRootLayer(true), |
| m_haveContents(false), |
| m_drawsContent(true), |
| m_haveImage(false), |
| m_haveClip(false), |
| m_doRotation(false), |
| m_isFixed(false), |
| m_recordingPicture(picture), |
| m_extra(0), |
| m_uniqueId(-1) |
| { |
| m_angleTransform = 0; |
| m_translation.set(0, 0); |
| m_scale.set(1, 1); |
| m_backgroundColor = 0; |
| SkSafeRef(m_recordingPicture); |
| gDebugLayerAndroidInstances++; |
| } |
| |
| LayerAndroid::~LayerAndroid() |
| { |
| removeChildren(); |
| m_recordingPicture->safeUnref(); |
| m_animations.clear(); |
| gDebugLayerAndroidInstances--; |
| } |
| |
| static int gDebugNbAnims = 0; |
| |
| bool LayerAndroid::evaluateAnimations() const |
| { |
| double time = WTF::currentTime(); |
| gDebugNbAnims = 0; |
| return evaluateAnimations(time); |
| } |
| |
| bool LayerAndroid::hasAnimations() const |
| { |
| for (int i = 0; i < countChildren(); i++) { |
| if (getChild(i)->hasAnimations()) |
| return true; |
| } |
| return !!m_animations.size(); |
| } |
| |
| bool LayerAndroid::evaluateAnimations(double time) const |
| { |
| bool hasRunningAnimations = false; |
| for (int i = 0; i < countChildren(); i++) { |
| if (getChild(i)->evaluateAnimations(time)) |
| hasRunningAnimations = true; |
| } |
| KeyframesMap::const_iterator end = m_animations.end(); |
| for (KeyframesMap::const_iterator it = m_animations.begin(); it != end; ++it) { |
| gDebugNbAnims++; |
| LayerAndroid* currentLayer = const_cast<LayerAndroid*>(this); |
| if ((it->second)->evaluate(currentLayer, time)) { |
| hasRunningAnimations = true; |
| } |
| } |
| |
| return hasRunningAnimations; |
| } |
| |
| void LayerAndroid::addAnimation(PassRefPtr<AndroidAnimation> anim) |
| { |
| m_animations.add(anim->name(), anim); |
| } |
| |
| void LayerAndroid::removeAnimation(const String& name) |
| { |
| m_animations.remove(name); |
| } |
| |
| void LayerAndroid::setDrawsContent(bool drawsContent) |
| { |
| m_drawsContent = drawsContent; |
| for (int i = 0; i < countChildren(); i++) |
| getChild(i)->setDrawsContent(drawsContent); |
| } |
| |
| // We only use the bounding rect of the layer as mask... |
| // TODO: use a real mask? |
| void LayerAndroid::setMaskLayer(LayerAndroid* layer) |
| { |
| if (layer) |
| m_haveClip = true; |
| } |
| |
| void LayerAndroid::setMasksToBounds(bool masksToBounds) |
| { |
| m_haveClip = masksToBounds; |
| } |
| |
| void LayerAndroid::setBackgroundColor(SkColor color) |
| { |
| m_backgroundColor = color; |
| setHaveContents(true); |
| setDrawsContent(true); |
| } |
| |
| static int gDebugChildLevel; |
| |
| void LayerAndroid::bounds(SkRect* rect) const |
| { |
| const SkPoint& pos = this->getPosition(); |
| const SkSize& size = this->getSize(); |
| rect->fLeft = pos.fX + m_translation.fX; |
| rect->fTop = pos.fY + m_translation.fY; |
| rect->fRight = rect->fLeft + size.width(); |
| rect->fBottom = rect->fTop + size.height(); |
| } |
| |
| bool LayerAndroid::boundsIsUnique(SkTDArray<SkRect>* region, |
| const SkRect& local) const |
| { |
| for (int i = 0; i < region->count(); i++) { |
| if ((*region)[i].contains(local)) |
| return false; |
| } |
| return true; |
| } |
| |
| void LayerAndroid::clipArea(SkTDArray<SkRect>* region) const |
| { |
| SkRect local; |
| local.set(0, 0, std::numeric_limits<float>::max(), |
| std::numeric_limits<float>::max()); |
| clipInner(region, local); |
| } |
| |
| void LayerAndroid::clipInner(SkTDArray<SkRect>* region, |
| const SkRect& local) const |
| { |
| SkRect localBounds; |
| bounds(&localBounds); |
| localBounds.intersect(local); |
| if (localBounds.isEmpty()) |
| return; |
| if (m_recordingPicture && boundsIsUnique(region, localBounds)) |
| *region->append() = localBounds; |
| for (int i = 0; i < countChildren(); i++) |
| getChild(i)->clipInner(region, m_haveClip ? localBounds : local); |
| } |
| |
| const LayerAndroid* LayerAndroid::find(int x, int y) const |
| { |
| for (int i = 0; i < countChildren(); i++) { |
| const LayerAndroid* found = getChild(i)->find(x, y); |
| if (found) |
| return found; |
| } |
| SkRect localBounds; |
| bounds(&localBounds); |
| if (localBounds.contains(x, y)) |
| return this; |
| return 0; |
| } |
| |
| void LayerAndroid::setClip(SkCanvas* canvas) |
| { |
| SkRect clip; |
| bounds(&clip); |
| canvas->clipRect(clip); |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| void LayerAndroid::updatePositions(const SkRect& viewport) { |
| // apply the viewport to us |
| SkMatrix matrix; |
| if (m_isFixed) { |
| float x = 0; |
| float y = 0; |
| float w = viewport.width(); |
| float h = viewport.height(); |
| float dx = viewport.fLeft; |
| float dy = viewport.fTop; |
| |
| if (m_fixedLeft.defined()) |
| x = dx + m_fixedLeft.calcFloatValue(w); |
| else if (m_fixedRight.defined()) |
| x = dx + w - m_fixedRight.calcFloatValue(w) - getSize().width(); |
| |
| if (m_fixedTop.defined()) |
| y = dy + m_fixedTop.calcFloatValue(h); |
| else if (m_fixedBottom.defined()) |
| y = dy + h - m_fixedBottom.calcFloatValue(h) - getSize().height(); |
| |
| this->setPosition(x, y); |
| matrix.reset(); |
| } else { |
| // turn our fields into a matrix. |
| // |
| // TODO: this should happen in the caller, and we should remove these |
| // fields from our subclass |
| matrix.setTranslate(m_translation.fX, m_translation.fY); |
| if (m_doRotation) { |
| matrix.preRotate(m_angleTransform); |
| } |
| matrix.preScale(m_scale.fX, m_scale.fY); |
| } |
| this->setMatrix(matrix); |
| |
| // now apply it to our children |
| int count = this->countChildren(); |
| if (count > 0) { |
| SkRect tmp = viewport; |
| // adjust the viewport by our (the parent) position |
| tmp.offset(-this->getPosition()); |
| for (int i = 0; i < count; i++) { |
| this->getChild(i)->updatePositions(tmp); |
| } |
| } |
| } |
| |
| void LayerAndroid::onDraw(SkCanvas* canvas, SkScalar opacity) { |
| if (m_haveClip) { |
| SkRect r; |
| r.set(0, 0, getSize().width(), getSize().height()); |
| canvas->clipRect(r); |
| } |
| |
| if (!m_haveImage && !m_drawsContent && !m_isRootLayer) |
| return; |
| |
| if (!prepareContext()) |
| return; |
| |
| // we just have this save/restore for opacity... |
| SkAutoCanvasRestore restore(canvas, true); |
| |
| int canvasOpacity = SkScalarRound(opacity * 255); |
| if (canvasOpacity < 255) |
| canvas->setDrawFilter(new OpacityDrawFilter(canvasOpacity)); |
| |
| m_recordingPicture->draw(canvas); |
| if (m_extra) |
| m_extra->draw(canvas, this); |
| |
| #ifdef LAYER_DEBUG |
| float w = getSize().width(); |
| float h = getSize().height(); |
| SkPaint paint; |
| paint.setARGB(128, 255, 0, 0); |
| canvas->drawLine(0, 0, w, h, paint); |
| canvas->drawLine(0, h, w, 0, paint); |
| paint.setARGB(128, 0, 255, 0); |
| canvas->drawLine(0, 0, 0, h, paint); |
| canvas->drawLine(0, h, w, h, paint); |
| canvas->drawLine(w, h, w, 0, paint); |
| canvas->drawLine(w, 0, 0, 0, paint); |
| #endif |
| } |
| |
| SkPicture* LayerAndroid::recordContext() |
| { |
| if (prepareContext(true)) |
| return m_recordingPicture; |
| return 0; |
| } |
| |
| bool LayerAndroid::prepareContext(bool force) |
| { |
| if (!m_haveContents) |
| return false; |
| |
| if (!m_isRootLayer) { |
| if (force || !m_recordingPicture |
| || (m_recordingPicture |
| && ((m_recordingPicture->width() != (int) getSize().width()) |
| || (m_recordingPicture->height() != (int) getSize().height())))) { |
| m_recordingPicture->safeUnref(); |
| m_recordingPicture = new SkPicture(); |
| } |
| } else if (m_recordingPicture) { |
| m_recordingPicture->safeUnref(); |
| m_recordingPicture = 0; |
| } |
| |
| return m_recordingPicture; |
| } |
| |
| // Debug tools : dump the layers tree in a file. |
| // The format is simple: |
| // properties have the form: key = value; |
| // all statements are finished with a semi-colon. |
| // value can be: |
| // - int |
| // - float |
| // - array of elements |
| // - composed type |
| // a composed type enclose properties in { and } |
| // an array enclose composed types in { }, separated with a comma. |
| // exemple: |
| // { |
| // x = 3; |
| // y = 4; |
| // value = { |
| // x = 3; |
| // y = 4; |
| // }; |
| // anarray = [ |
| // { x = 3; }, |
| // { y = 4; } |
| // ]; |
| // } |
| |
| void lwrite(FILE* file, const char* str) |
| { |
| fwrite(str, sizeof(char), strlen(str), file); |
| } |
| |
| void writeIndent(FILE* file, int indentLevel) |
| { |
| if (indentLevel) |
| fprintf(file, "%*s", indentLevel*2, " "); |
| } |
| |
| void writeln(FILE* file, int indentLevel, const char* str) |
| { |
| writeIndent(file, indentLevel); |
| lwrite(file, str); |
| lwrite(file, "\n"); |
| } |
| |
| void writeIntVal(FILE* file, int indentLevel, const char* str, int value) |
| { |
| writeIndent(file, indentLevel); |
| fprintf(file, "%s = %d;\n", str, value); |
| } |
| |
| void writeHexVal(FILE* file, int indentLevel, const char* str, int value) |
| { |
| writeIndent(file, indentLevel); |
| fprintf(file, "%s = %x;\n", str, value); |
| } |
| |
| void writeFloatVal(FILE* file, int indentLevel, const char* str, float value) |
| { |
| writeIndent(file, indentLevel); |
| fprintf(file, "%s = %.3f;\n", str, value); |
| } |
| |
| void writePoint(FILE* file, int indentLevel, const char* str, SkPoint point) |
| { |
| writeIndent(file, indentLevel); |
| fprintf(file, "%s = { x = %.3f; y = %.3f; };\n", str, point.fX, point.fY); |
| } |
| |
| void writeSize(FILE* file, int indentLevel, const char* str, SkSize size) |
| { |
| writeIndent(file, indentLevel); |
| fprintf(file, "%s = { w = %.3f; h = %.3f; };\n", str, size.width(), size.height()); |
| } |
| |
| void writeLength(FILE* file, int indentLevel, const char* str, SkLength length) |
| { |
| if (!length.defined()) return; |
| writeIndent(file, indentLevel); |
| fprintf(file, "%s = { type = %d; value = %.2f; };\n", str, length.type, length.value); |
| } |
| |
| void LayerAndroid::dumpLayers(FILE* file, int indentLevel) const |
| { |
| writeln(file, indentLevel, "{"); |
| |
| writeHexVal(file, indentLevel + 1, "layer", (int)this); |
| writeIntVal(file, indentLevel + 1, "haveContents", m_haveContents); |
| writeIntVal(file, indentLevel + 1, "drawsContent", m_drawsContent); |
| writeIntVal(file, indentLevel + 1, "haveImage", m_haveImage); |
| writeIntVal(file, indentLevel + 1, "clipRect", m_haveClip); |
| |
| writeFloatVal(file, indentLevel + 1, "opacity", getOpacity()); |
| writeSize(file, indentLevel + 1, "size", getSize()); |
| writePoint(file, indentLevel + 1, "position", getPosition()); |
| writePoint(file, indentLevel + 1, "translation", m_translation); |
| writePoint(file, indentLevel + 1, "anchor", getAnchorPoint()); |
| writePoint(file, indentLevel + 1, "scale", m_scale); |
| |
| if (m_doRotation) |
| writeFloatVal(file, indentLevel + 1, "angle", m_angleTransform); |
| |
| writeLength(file, indentLevel + 1, "fixedLeft", m_fixedLeft); |
| writeLength(file, indentLevel + 1, "fixedTop", m_fixedTop); |
| writeLength(file, indentLevel + 1, "fixedRight", m_fixedRight); |
| writeLength(file, indentLevel + 1, "fixedBottom", m_fixedBottom); |
| |
| if (countChildren()) { |
| writeln(file, indentLevel + 1, "children = ["); |
| for (int i = 0; i < countChildren(); i++) { |
| if (i > 0) |
| writeln(file, indentLevel + 1, ", "); |
| getChild(i)->dumpLayers(file, indentLevel + 1); |
| } |
| writeln(file, indentLevel + 1, "];"); |
| } |
| writeln(file, indentLevel, "}"); |
| } |
| |
| const LayerAndroid* LayerAndroid::findById(int match) const |
| { |
| if (m_uniqueId == match) |
| return this; |
| for (int i = 0; i < countChildren(); i++) { |
| const LayerAndroid* result = getChild(i)->findById(match); |
| if (result) |
| return result; |
| } |
| return 0; |
| } |
| |
| void LayerAndroid::setExtra(DrawExtra* extra) |
| { |
| m_extra = extra; |
| for (int i = 0; i < countChildren(); i++) |
| getChild(i)->setExtra(extra); |
| } |
| |
| } // namespace WebCore |
| |
| #endif // USE(ACCELERATED_COMPOSITING) |