Home | History | Annotate | Download | only in geometry
      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 "platform/geometry/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().toDouble(), offset.height().toDouble());
     57     else
     58         m_accumulatedTransform->translate(offset.width().toDouble(), offset.height().toDouble());
     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     }
     88     m_accumulatingTransform = accumulate == AccumulateTransform;
     89 }
     90 
     91 void TransformState::applyAccumulatedOffset()
     92 {
     93     LayoutSize offset = m_accumulatedOffset;
     94     m_accumulatedOffset = LayoutSize();
     95     if (!offset.isZero()) {
     96         if (m_accumulatedTransform) {
     97             translateTransform(offset);
     98             flatten();
     99         } else {
    100             translateMappedCoordinates(offset);
    101         }
    102     }
    103 }
    104 
    105 // FIXME: We transform AffineTransform to TransformationMatrix. This is rather inefficient.
    106 void TransformState::applyTransform(const AffineTransform& transformFromContainer, TransformAccumulation accumulate, bool* wasClamped)
    107 {
    108     applyTransform(transformFromContainer.toTransformationMatrix(), accumulate, wasClamped);
    109 }
    110 
    111 void TransformState::applyTransform(const TransformationMatrix& transformFromContainer, TransformAccumulation accumulate, bool* wasClamped)
    112 {
    113     if (wasClamped)
    114         *wasClamped = false;
    115 
    116     if (transformFromContainer.isIntegerTranslation()) {
    117         move(LayoutSize(transformFromContainer.e(), transformFromContainer.f()), accumulate);
    118         return;
    119     }
    120 
    121     applyAccumulatedOffset();
    122 
    123     // If we have an accumulated transform from last time, multiply in this transform
    124     if (m_accumulatedTransform) {
    125         if (m_direction == ApplyTransformDirection)
    126             m_accumulatedTransform = adoptPtr(new TransformationMatrix(transformFromContainer * *m_accumulatedTransform));
    127         else
    128             m_accumulatedTransform->multiply(transformFromContainer);
    129     } else if (accumulate == AccumulateTransform) {
    130         // Make one if we started to accumulate
    131         m_accumulatedTransform = adoptPtr(new TransformationMatrix(transformFromContainer));
    132     }
    133 
    134     if (accumulate == FlattenTransform) {
    135         const TransformationMatrix* finalTransform = m_accumulatedTransform ? m_accumulatedTransform.get() : &transformFromContainer;
    136         flattenWithTransform(*finalTransform, wasClamped);
    137     }
    138     m_accumulatingTransform = accumulate == AccumulateTransform;
    139 }
    140 
    141 void TransformState::flatten(bool* wasClamped)
    142 {
    143     if (wasClamped)
    144         *wasClamped = false;
    145 
    146     applyAccumulatedOffset();
    147 
    148     if (!m_accumulatedTransform) {
    149         m_accumulatingTransform = false;
    150         return;
    151     }
    152 
    153     flattenWithTransform(*m_accumulatedTransform, wasClamped);
    154 }
    155 
    156 FloatPoint TransformState::mappedPoint(bool* wasClamped) const
    157 {
    158     if (wasClamped)
    159         *wasClamped = false;
    160 
    161     FloatPoint point = m_lastPlanarPoint;
    162     point.move((m_direction == ApplyTransformDirection) ? m_accumulatedOffset : -m_accumulatedOffset);
    163     if (!m_accumulatedTransform)
    164         return point;
    165 
    166     if (m_direction == ApplyTransformDirection)
    167         return m_accumulatedTransform->mapPoint(point);
    168 
    169     return m_accumulatedTransform->inverse().projectPoint(point, wasClamped);
    170 }
    171 
    172 FloatQuad TransformState::mappedQuad(bool* wasClamped) const
    173 {
    174     if (wasClamped)
    175         *wasClamped = false;
    176 
    177     FloatQuad quad = m_lastPlanarQuad;
    178     quad.move((m_direction == ApplyTransformDirection) ? m_accumulatedOffset : -m_accumulatedOffset);
    179     if (!m_accumulatedTransform)
    180         return quad;
    181 
    182     if (m_direction == ApplyTransformDirection)
    183         return m_accumulatedTransform->mapQuad(quad);
    184 
    185     return m_accumulatedTransform->inverse().projectQuad(quad, wasClamped);
    186 }
    187 
    188 void TransformState::flattenWithTransform(const TransformationMatrix& t, bool* wasClamped)
    189 {
    190     if (m_direction == ApplyTransformDirection) {
    191         if (m_mapPoint)
    192             m_lastPlanarPoint = t.mapPoint(m_lastPlanarPoint);
    193         if (m_mapQuad)
    194             m_lastPlanarQuad = t.mapQuad(m_lastPlanarQuad);
    195     } else {
    196         TransformationMatrix inverseTransform = t.inverse();
    197         if (m_mapPoint)
    198             m_lastPlanarPoint = inverseTransform.projectPoint(m_lastPlanarPoint);
    199         if (m_mapQuad)
    200             m_lastPlanarQuad = inverseTransform.projectQuad(m_lastPlanarQuad, wasClamped);
    201     }
    202 
    203     // We could throw away m_accumulatedTransform if we wanted to here, but that
    204     // would cause thrash when traversing hierarchies with alternating
    205     // preserve-3d and flat elements.
    206     if (m_accumulatedTransform)
    207         m_accumulatedTransform->makeIdentity();
    208     m_accumulatingTransform = false;
    209 }
    210 
    211 } // namespace WebCore
    212