SVGAnimateTransformElement.cpp [plain text]
#include "config.h"
#if SVG_SUPPORT
#include "Attr.h"
#include <kcanvas/RenderPath.h>
#include <kcanvas/KCanvasMatrix.h>
#include "SVGAngle.h"
#include "SVGMatrix.h"
#include "SVGTransform.h"
#include "SVGStyledTransformableElement.h"
#include "SVGTransformList.h"
#include "SVGAnimatedTransformList.h"
#include "SVGAnimateTransformElement.h"
#include "KSVGTimeScheduler.h"
#include "Document.h"
#include "SVGDocumentExtensions.h"
#include "SVGSVGElement.h"
#include <math.h>
using namespace std;
namespace WebCore {
SVGAnimateTransformElement::SVGAnimateTransformElement(const QualifiedName& tagName, Document *doc)
: SVGAnimationElement(tagName, doc)
, m_currentItem(-1)
, m_type(SVG_TRANSFORM_UNKNOWN)
, m_rotateSpecialCase(false)
, m_toRotateSpecialCase(false)
, m_fromRotateSpecialCase(false)
{
}
SVGAnimateTransformElement::~SVGAnimateTransformElement()
{
}
void SVGAnimateTransformElement::parseMappedAttribute(MappedAttribute *attr)
{
if (attr->name() == SVGNames::typeAttr) {
const String& value = attr->value();
if (value == "translate")
m_type = SVG_TRANSFORM_TRANSLATE;
else if (value == "scale")
m_type = SVG_TRANSFORM_SCALE;
else if (value == "rotate")
m_type = SVG_TRANSFORM_ROTATE;
else if (value == "skewX")
m_type = SVG_TRANSFORM_SKEWX;
else if (value == "skewY")
m_type = SVG_TRANSFORM_SKEWY;
} else
SVGAnimationElement::parseMappedAttribute(attr);
}
void SVGAnimateTransformElement::handleTimerEvent(double timePercentage)
{
if (!m_connected) {
m_initialTransform = 0;
if (targetElement()->isStyledTransformable()) {
SVGStyledTransformableElement *transform = static_cast<SVGStyledTransformableElement *>(targetElement());
RefPtr<SVGTransformList> transformList = transform->transform()->baseVal();
if (transformList) {
for (unsigned long i = 0; i < transformList->numberOfItems(); i++) {
SVGTransform *value = transformList->getItem(i);
if (!value)
continue;
if (value->type() == m_type) {
m_initialTransform = value;
break;
}
}
}
}
switch(detectAnimationMode())
{
case TO_ANIMATION:
case FROM_TO_ANIMATION:
{
m_toTransform = parseTransformValue(m_to);
m_toRotateSpecialCase = m_rotateSpecialCase;
if (!m_from.isEmpty()) {
m_fromTransform = parseTransformValue(m_from);
m_fromRotateSpecialCase = m_rotateSpecialCase;
}
else {
m_fromTransform = m_initialTransform;
m_fromRotateSpecialCase = false;
}
if (!m_fromTransform)
m_fromTransform = new SVGTransform();
break;
}
case BY_ANIMATION:
case FROM_BY_ANIMATION:
{
m_toTransform = parseTransformValue(m_by);
m_toRotateSpecialCase = m_rotateSpecialCase;
if (!m_from.isEmpty()) {
m_fromTransform = parseTransformValue(m_from);
m_fromRotateSpecialCase = m_rotateSpecialCase;
}
else {
m_fromTransform = m_initialTransform;
m_fromRotateSpecialCase = false;
}
if (!m_fromTransform)
m_fromTransform = new SVGTransform();
SVGMatrix *byMatrix = m_toTransform->matrix();
SVGMatrix *fromMatrix = m_fromTransform->matrix();
byMatrix->multiply(fromMatrix);
break;
}
case VALUES_ANIMATION:
break;
default:
{
return;
}
}
ownerSVGElement()->timeScheduler()->connectIntervalTimer(this);
m_connected = true;
return;
}
if (timePercentage >= 1.0)
timePercentage = 1.0;
AffineTransform qToMatrix, qFromMatrix;
double useTimePercentage = timePercentage;
if (m_values) {
int itemByPercentage = calculateCurrentValueItem(timePercentage);
if (itemByPercentage == -1)
return;
if (m_currentItem != itemByPercentage) {
String value1 = String(m_values->getItem(itemByPercentage));
String value2 = String(m_values->getItem(itemByPercentage + 1));
if (!value1.isEmpty() && !value2.isEmpty()) {
bool apply = false;
if (m_toTransform && m_fromTransform) {
qToMatrix = m_toTransform->matrix()->matrix();
qFromMatrix = m_fromTransform->matrix()->matrix();
apply = true;
useTimePercentage = 1.0;
}
m_toTransform = parseTransformValue(value2.deprecatedString());
m_toRotateSpecialCase = m_rotateSpecialCase;
m_fromTransform = parseTransformValue(value1.deprecatedString());
m_fromRotateSpecialCase = m_rotateSpecialCase;
m_currentItem = itemByPercentage;
if (!apply)
return;
}
}
else if (m_toTransform && m_fromTransform)
useTimePercentage = calculateRelativeTimePercentage(timePercentage, m_currentItem);
}
if (m_toTransform && m_toTransform->matrix() && qToMatrix.isIdentity())
qToMatrix = m_toTransform->matrix()->matrix();
if (m_fromTransform && m_fromTransform->matrix() && qFromMatrix.isIdentity())
qFromMatrix = m_fromTransform->matrix()->matrix();
if (!m_transformMatrix)
m_transformMatrix = new SVGMatrix();
else {
m_transformMatrix->reset();
if (isAccumulated() && repeations() != 0.0 && m_lastMatrix)
m_transformMatrix->multiply(m_lastMatrix.get());
}
switch(m_type)
{
case SVG_TRANSFORM_TRANSLATE:
{
double dx = ((qToMatrix.dx() - qFromMatrix.dx()) * useTimePercentage) + qFromMatrix.dx();
double dy = ((qToMatrix.dy() - qFromMatrix.dy()) * useTimePercentage) + qFromMatrix.dy();
m_transformMatrix->translate(dx, dy);
break;
}
case SVG_TRANSFORM_SCALE:
{
double sx = ((qToMatrix.m11() - qFromMatrix.m11()) * useTimePercentage) + qFromMatrix.m11();
double sy = ((qToMatrix.m22() - qFromMatrix.m22()) * useTimePercentage) + qFromMatrix.m22();
m_transformMatrix->scaleNonUniform(sx, sy);
break;
}
case SVG_TRANSFORM_ROTATE:
{
double toAngle, toCx, toCy, fromAngle, fromCx, fromCy;
calculateRotationFromMatrix(qToMatrix, toAngle, toCx, toCy);
calculateRotationFromMatrix(qFromMatrix, fromAngle, fromCx, fromCy);
if (m_toRotateSpecialCase) {
if (lround(toAngle) == 1)
toAngle = 0.0;
else
toAngle = 360.0;
}
if (m_fromRotateSpecialCase) {
if (lround(fromAngle) == 1)
fromAngle = 0.0;
else
fromAngle = 360.0;
}
double angle = ((toAngle - fromAngle) * useTimePercentage) + fromAngle;
double cx = (toCx - fromCx) * useTimePercentage + fromCx;
double cy = (toCy - fromCy) * useTimePercentage + fromCy;
m_transformMatrix->translate(cx, cy);
m_transformMatrix->rotate(angle);
m_transformMatrix->translate(-cx, -cy);
break;
}
case SVG_TRANSFORM_SKEWX:
{
double sx = (SVGAngle::todeg(atan(qToMatrix.m21()) - atan(qFromMatrix.m21())) *
useTimePercentage) + SVGAngle::todeg(atan(qFromMatrix.m21()));
m_transformMatrix->skewX(sx);
break;
}
case SVG_TRANSFORM_SKEWY:
{
double sy = (SVGAngle::todeg(atan(qToMatrix.m12()) - atan(qFromMatrix.m12())) *
useTimePercentage) + SVGAngle::todeg(atan(qFromMatrix.m12()));
m_transformMatrix->skewY(sy);
break;
}
default:
break;
}
if (timePercentage == 1.0)
{
if ((m_repeatCount > 0 && m_repeations < m_repeatCount - 1) || isIndefinite(m_repeatCount))
{
m_lastMatrix = new SVGMatrix();
if (m_transformMatrix)
m_lastMatrix->copy(m_transformMatrix.get());
m_repeations++;
return;
}
ownerSVGElement()->timeScheduler()->disconnectIntervalTimer(this);
m_connected = false;
m_currentItem = -1;
m_toTransform = 0;
m_fromTransform = 0;
m_initialTransform = 0;
if (!isFrozen()) {
SVGMatrix *initial = initialMatrix();
if (initial)
m_transformMatrix = initial;
else
m_transformMatrix = new SVGMatrix();
}
}
}
RefPtr<SVGTransform> SVGAnimateTransformElement::parseTransformValue(const DeprecatedString &data) const
{
DeprecatedString parse = data.stripWhiteSpace();
if (parse.isEmpty())
return 0;
int commaPos = parse.find(',');
RefPtr<SVGTransform> parsedTransform = new SVGTransform();
switch(m_type)
{
case SVG_TRANSFORM_TRANSLATE:
{
double tx = 0.0, ty = 0.0;
if (commaPos != - 1) {
tx = parse.mid(0, commaPos).toDouble();
ty = parse.mid(commaPos + 1).toDouble();
} else
tx = parse.toDouble();
parsedTransform->setTranslate(tx, ty);
break;
}
case SVG_TRANSFORM_SCALE:
{
double sx = 1.0, sy = 1.0;
if (commaPos != - 1) {
sx = parse.mid(0, commaPos).toDouble();
sy = parse.mid(commaPos + 1).toDouble();
} else {
sx = parse.toDouble();
sy = sx;
}
parsedTransform->setScale(sx, sy);
break;
}
case SVG_TRANSFORM_ROTATE:
{
double angle = 0, cx = 0, cy = 0;
if (commaPos != - 1) {
angle = parse.mid(0, commaPos).toDouble();
int commaPosTwo = parse.find(',', commaPos + 1); if (commaPosTwo != -1) {
cx = parse.mid(commaPos + 1, commaPosTwo - commaPos - 1).toDouble();
cy = parse.mid(commaPosTwo + 1).toDouble();
}
}
else
angle = parse.toDouble();
m_rotateSpecialCase = false;
if (angle == 0.0) {
angle = angle + 1.0;
m_rotateSpecialCase = true;
} else if (angle == 360.0) {
angle = angle - 1.0;
m_rotateSpecialCase = true;
}
parsedTransform->setRotate(angle, cx, cy);
break;
}
case SVG_TRANSFORM_SKEWX:
case SVG_TRANSFORM_SKEWY:
{
double angle = parse.toDouble();
if (m_type == SVG_TRANSFORM_SKEWX)
parsedTransform->setSkewX(angle);
else
parsedTransform->setSkewY(angle);
break;
}
default:
return 0;
}
return parsedTransform;
}
void SVGAnimateTransformElement::calculateRotationFromMatrix(const AffineTransform &matrix, double &angle, double &cx, double &cy) const
{
double cosa = matrix.m11();
double sina = -matrix.m21();
if (cosa != 1.0)
{
double temp = SVGAngle::todeg(asin(sina));
angle = SVGAngle::todeg(acos(cosa));
if (temp < 0)
angle = 360.0 - angle;
double res = (1 - cosa) + ((sina * sina) / (1 - cosa));
cx = (matrix.dx() - ((sina * matrix.dy()) / (1 - cosa))) / res;
cy = (matrix.dy() + ((sina * matrix.dx()) / (1 - cosa))) / res;
return;
}
cx = 0.0;
cy = 0.0;
angle = 0.0;
}
SVGMatrix *SVGAnimateTransformElement::initialMatrix() const
{
if (!targetElement()->isStyledTransformable())
return 0;
SVGStyledTransformableElement *transform = static_cast<SVGStyledTransformableElement *>(targetElement());
SVGTransformList *transformList = (transform ? transform->transform()->baseVal() : 0);
if (!transformList)
return 0;
RefPtr<SVGTransform> result = transformList->concatenate();
if (!result)
return 0;
SVGMatrix *ret = result->matrix();
ret->ref();
return ret;
}
SVGMatrix *SVGAnimateTransformElement::transformMatrix() const
{
return m_transformMatrix.get();
}
}
#endif // SVG_SUPPORT