Home | History | Annotate | Download | only in transforms
      1 /*
      2  * Copyright (C) 2011 Apple Inc.  All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions
      6  * are met:
      7  * 1. Redistributions of source code must retain the above copyright
      8  *    notice, this list of conditions and the following disclaimer.
      9  * 2. Redistributions in binary form must reproduce the above copyright
     10  *    notice, this list of conditions and the following disclaimer in the
     11  *    documentation and/or other materials provided with the distribution.
     12  *
     13  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
     14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
     17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
     21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     24  */
     25 
     26 #include "config.h"
     27 #include "core/platform/graphics/transforms/TransformState.h"
     28 
     29 #include "wtf/PassOwnPtr.h"
     30 
     31 namespace WebCore {
     32 
     33 TransformState& TransformState::operator=(const TransformState& other)
     34 {
     35     m_accumulatedOffset = other.m_accumulatedOffset;
     36     m_mapPoint = other.m_mapPoint;
     37     m_mapQuad = other.m_mapQuad;
     38     if (m_mapPoint)
     39         m_lastPlanarPoint = other.m_lastPlanarPoint;
     40     if (m_mapQuad)
     41         m_lastPlanarQuad = other.m_lastPlanarQuad;
     42     m_accumulatingTransform = other.m_accumulatingTransform;
     43     m_direction = other.m_direction;
     44 
     45     m_accumulatedTransform.clear();
     46 
     47     if (other.m_accumulatedTransform)
     48         m_accumulatedTransform = adoptPtr(new TransformationMatrix(*other.m_accumulatedTransform));
     49 
     50     return *this;
     51 }
     52 
     53 void TransformState::translateTransform(const LayoutSize& offset)
     54 {
     55     if (m_direction == ApplyTransformDirection)
     56         m_accumulatedTransform->translateRight(offset.width(), offset.height());
     57     else
     58         m_accumulatedTransform->translate(offset.width(), offset.height());
     59 }
     60 
     61 void TransformState::translateMappedCoordinates(const LayoutSize& offset)
     62 {
     63     LayoutSize adjustedOffset = (m_direction == ApplyTransformDirection) ? offset : -offset;
     64     if (m_mapPoint)
     65         m_lastPlanarPoint.move(adjustedOffset);
     66     if (m_mapQuad)
     67         m_lastPlanarQuad.move(adjustedOffset);
     68 }
     69 
     70 void TransformState::move(const LayoutSize& offset, TransformAccumulation accumulate)
     71 {
     72     if (accumulate == FlattenTransform || !m_accumulatedTransform)
     73         m_accumulatedOffset += offset;
     74     else {
     75         applyAccumulatedOffset();
     76         if (m_accumulatingTransform && m_accumulatedTransform) {
     77             // If we're accumulating into an existing transform, apply the translation.
     78             translateTransform(offset);
     79 
     80             // Then flatten if necessary.
     81             if (accumulate == FlattenTransform)
     82                 flatten();
     83         } else
     84             // Just move the point and/or quad.
     85             translateMappedCoordinates(offset);
     86     }
     87     m_accumulatingTransform = accumulate == AccumulateTransform;
     88 }
     89 
     90 void TransformState::applyAccumulatedOffset()
     91 {
     92     LayoutSize offset = m_accumulatedOffset;
     93     m_accumulatedOffset = LayoutSize();
     94     if (!offset.isZero()) {
     95         if (m_accumulatedTransform) {
     96             translateTransform(offset);
     97             flatten();
     98         } else
     99             translateMappedCoordinates(offset);
    100     }
    101 }
    102 
    103 // FIXME: We transform AffineTransform to TransformationMatrix. This is rather inefficient.
    104 void TransformState::applyTransform(const AffineTransform& transformFromContainer, TransformAccumulation accumulate, bool* wasClamped)
    105 {
    106     applyTransform(transformFromContainer.toTransformationMatrix(), accumulate, wasClamped);
    107 }
    108 
    109 void TransformState::applyTransform(const TransformationMatrix& transformFromContainer, TransformAccumulation accumulate, bool* wasClamped)
    110 {
    111     if (wasClamped)
    112         *wasClamped = false;
    113 
    114     if (transformFromContainer.isIntegerTranslation()) {
    115         move(LayoutSize(transformFromContainer.e(), transformFromContainer.f()), accumulate);
    116         return;
    117     }
    118 
    119     applyAccumulatedOffset();
    120 
    121     // If we have an accumulated transform from last time, multiply in this transform
    122     if (m_accumulatedTransform) {
    123         if (m_direction == ApplyTransformDirection)
    124             m_accumulatedTransform = adoptPtr(new TransformationMatrix(transformFromContainer * *m_accumulatedTransform));
    125         else
    126             m_accumulatedTransform->multiply(transformFromContainer);
    127     } else if (accumulate == AccumulateTransform) {
    128         // Make one if we started to accumulate
    129         m_accumulatedTransform = adoptPtr(new TransformationMatrix(transformFromContainer));
    130     }
    131 
    132     if (accumulate == FlattenTransform) {
    133         const TransformationMatrix* finalTransform = m_accumulatedTransform ? m_accumulatedTransform.get() : &transformFromContainer;
    134         flattenWithTransform(*finalTransform, wasClamped);
    135     }
    136     m_accumulatingTransform = accumulate == AccumulateTransform;
    137 }
    138 
    139 void TransformState::flatten(bool* wasClamped)
    140 {
    141     if (wasClamped)
    142         *wasClamped = false;
    143 
    144     applyAccumulatedOffset();
    145 
    146     if (!m_accumulatedTransform) {
    147         m_accumulatingTransform = false;
    148         return;
    149     }
    150 
    151     flattenWithTransform(*m_accumulatedTransform, wasClamped);
    152 }
    153 
    154 FloatPoint TransformState::mappedPoint(bool* wasClamped) const
    155 {
    156     if (wasClamped)
    157         *wasClamped = false;
    158 
    159     FloatPoint point = m_lastPlanarPoint;
    160     point.move((m_direction == ApplyTransformDirection) ? m_accumulatedOffset : -m_accumulatedOffset);
    161     if (!m_accumulatedTransform)
    162         return point;
    163 
    164     if (m_direction == ApplyTransformDirection)
    165         return m_accumulatedTransform->mapPoint(point);
    166 
    167     return m_accumulatedTransform->inverse().projectPoint(point, wasClamped);
    168 }
    169 
    170 FloatQuad TransformState::mappedQuad(bool* wasClamped) const
    171 {
    172     if (wasClamped)
    173         *wasClamped = false;
    174 
    175     FloatQuad quad = m_lastPlanarQuad;
    176     quad.move((m_direction == ApplyTransformDirection) ? m_accumulatedOffset : -m_accumulatedOffset);
    177     if (!m_accumulatedTransform)
    178         return quad;
    179 
    180     if (m_direction == ApplyTransformDirection)
    181         return m_accumulatedTransform->mapQuad(quad);
    182 
    183     return m_accumulatedTransform->inverse().projectQuad(quad, wasClamped);
    184 }
    185 
    186 void TransformState::flattenWithTransform(const TransformationMatrix& t, bool* wasClamped)
    187 {
    188     if (m_direction == ApplyTransformDirection) {
    189         if (m_mapPoint)
    190             m_lastPlanarPoint = t.mapPoint(m_lastPlanarPoint);
    191         if (m_mapQuad)
    192             m_lastPlanarQuad = t.mapQuad(m_lastPlanarQuad);
    193     } else {
    194         TransformationMatrix inverseTransform = t.inverse();
    195         if (m_mapPoint)
    196             m_lastPlanarPoint = inverseTransform.projectPoint(m_lastPlanarPoint);
    197         if (m_mapQuad)
    198             m_lastPlanarQuad = inverseTransform.projectQuad(m_lastPlanarQuad, wasClamped);
    199     }
    200 
    201     // We could throw away m_accumulatedTransform if we wanted to here, but that
    202     // would cause thrash when traversing hierarchies with alternating
    203     // preserve-3d and flat elements.
    204     if (m_accumulatedTransform)
    205         m_accumulatedTransform->makeIdentity();
    206     m_accumulatingTransform = false;
    207 }
    208 
    209 } // namespace WebCore
    210