webengine/osswebengine/WebCore/platform/graphics/Path.cpp
changeset 0 dd21522fd290
equal deleted inserted replaced
-1:000000000000 0:dd21522fd290
       
     1 /*
       
     2  * Copyright (C) 2003, 2006 Apple Computer, Inc.  All rights reserved.
       
     3  *                     2006 Rob Buis <buis@kde.org>
       
     4  * Copyright (C) 2007 Eric Seidel <eric@webkit.org>
       
     5  *
       
     6  * Redistribution and use in source and binary forms, with or without
       
     7  * modification, are permitted provided that the following conditions
       
     8  * are met:
       
     9  * 1. Redistributions of source code must retain the above copyright
       
    10  *    notice, this list of conditions and the following disclaimer.
       
    11  * 2. Redistributions in binary form must reproduce the above copyright
       
    12  *    notice, this list of conditions and the following disclaimer in the
       
    13  *    documentation and/or other materials provided with the distribution.
       
    14  *
       
    15  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
       
    16  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
       
    17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
       
    18  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
       
    19  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
       
    20  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
       
    21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
       
    22  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
       
    23  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
       
    24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
       
    25  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
       
    26  */
       
    27 
       
    28 
       
    29 #include "config.h"
       
    30 #include "Path.h"
       
    31 
       
    32 #include "FloatPoint.h"
       
    33 #include "FloatRect.h"
       
    34 #include "PathTraversalState.h"
       
    35 #include <math.h>
       
    36 #include <wtf/MathExtras.h>
       
    37 
       
    38 const float QUARTER = 0.552f; // approximation of control point positions on a bezier
       
    39                               // to simulate a quarter of a circle.
       
    40 namespace WebCore {
       
    41 
       
    42 static void pathLengthApplierFunction(void* info, const PathElement* element)
       
    43 {
       
    44     PathTraversalState& traversalState = *static_cast<PathTraversalState*>(info);
       
    45     if (traversalState.m_success)
       
    46         return;
       
    47     traversalState.m_previous = traversalState.m_current;
       
    48     FloatPoint* points = element->points;
       
    49     float segmentLength = 0.0f;
       
    50     switch (element->type) {
       
    51         case PathElementMoveToPoint:
       
    52             segmentLength = traversalState.moveTo(points[0]);
       
    53             break;
       
    54         case PathElementAddLineToPoint:
       
    55             segmentLength = traversalState.lineTo(points[0]);
       
    56             break;
       
    57         case PathElementAddQuadCurveToPoint:
       
    58             segmentLength = traversalState.quadraticBezierTo(points[0], points[1]);
       
    59             break;
       
    60         case PathElementAddCurveToPoint:
       
    61             segmentLength = traversalState.cubicBezierTo(points[0], points[1], points[2]);
       
    62             break;
       
    63         case PathElementCloseSubpath:
       
    64             segmentLength = traversalState.closeSubpath();
       
    65             break;
       
    66     }
       
    67     traversalState.m_totalLength += segmentLength; 
       
    68     if ((traversalState.m_action == PathTraversalState::TraversalPointAtLength || 
       
    69          traversalState.m_action == PathTraversalState::TraversalNormalAngleAtLength) &&
       
    70         (traversalState.m_totalLength >= traversalState.m_desiredLength)) {
       
    71         FloatSize change = traversalState.m_current - traversalState.m_previous;
       
    72         float slope = atan2f(change.height(), change.width());
       
    73 
       
    74         if (traversalState.m_action == PathTraversalState::TraversalPointAtLength) {
       
    75             float offset = traversalState.m_desiredLength - traversalState.m_totalLength;
       
    76             traversalState.m_current.move(offset * cosf(slope), offset * sinf(slope));
       
    77         } else {
       
    78             static const float rad2deg = 180.0f / piFloat;
       
    79             traversalState.m_normalAngle = slope * rad2deg;
       
    80         }
       
    81 
       
    82         traversalState.m_success = true;
       
    83     }
       
    84 }
       
    85 
       
    86 float Path::length()
       
    87 {
       
    88     PathTraversalState traversalState(PathTraversalState::TraversalTotalLength);
       
    89     apply(&traversalState, pathLengthApplierFunction);
       
    90     return traversalState.m_totalLength;
       
    91 }
       
    92 
       
    93 FloatPoint Path::pointAtLength(float length, bool& ok)
       
    94 {
       
    95     PathTraversalState traversalState(PathTraversalState::TraversalPointAtLength);
       
    96     traversalState.m_desiredLength = length;
       
    97     apply(&traversalState, pathLengthApplierFunction);
       
    98     ok = traversalState.m_success;
       
    99     return traversalState.m_current;
       
   100 }
       
   101 
       
   102 float Path::normalAngleAtLength(float length, bool& ok)
       
   103 {
       
   104     PathTraversalState traversalState(PathTraversalState::TraversalNormalAngleAtLength);
       
   105     traversalState.m_desiredLength = length;
       
   106     apply(&traversalState, pathLengthApplierFunction);
       
   107     ok = traversalState.m_success;
       
   108     return traversalState.m_normalAngle;
       
   109 }
       
   110 
       
   111 Path Path::createRoundedRectangle(const FloatRect& rectangle, const FloatSize& roundingRadii)
       
   112 {
       
   113     Path path;
       
   114     float x = rectangle.x();
       
   115     float y = rectangle.y();
       
   116     float width = rectangle.width();
       
   117     float height = rectangle.height();
       
   118     float rx = roundingRadii.width();
       
   119     float ry = roundingRadii.height();
       
   120     if (width <= 0.0f || height <= 0.0f)
       
   121         return path;
       
   122 
       
   123     float dx = rx, dy = ry;
       
   124     // If rx is greater than half of the width of the rectangle
       
   125     // then set rx to half of the width (required in SVG spec)
       
   126     if (dx > width * 0.5f)
       
   127         dx = width * 0.5f;
       
   128 
       
   129     // If ry is greater than half of the height of the rectangle
       
   130     // then set ry to half of the height (required in SVG spec)
       
   131     if (dy > height * 0.5f)
       
   132         dy = height * 0.5f;
       
   133 
       
   134     path.moveTo(FloatPoint(x + dx, y));
       
   135 
       
   136     if (dx < width * 0.5f)
       
   137         path.addLineTo(FloatPoint(x + width - rx, y));
       
   138 
       
   139     path.addBezierCurveTo(FloatPoint(x + width - dx * (1 - QUARTER), y), FloatPoint(x + width, y + dy * (1 - QUARTER)), FloatPoint(x + width, y + dy));
       
   140 
       
   141     if (dy < height * 0.5)
       
   142         path.addLineTo(FloatPoint(x + width, y + height - dy));
       
   143 
       
   144     path.addBezierCurveTo(FloatPoint(x + width, y + height - dy * (1 - QUARTER)), FloatPoint(x + width - dx * (1 - QUARTER), y + height), FloatPoint(x + width - dx, y + height));
       
   145 
       
   146     if (dx < width * 0.5)
       
   147         path.addLineTo(FloatPoint(x + dx, y + height));
       
   148 
       
   149     path.addBezierCurveTo(FloatPoint(x + dx * (1 - QUARTER), y + height), FloatPoint(x, y + height - dy * (1 - QUARTER)), FloatPoint(x, y + height - dy));
       
   150 
       
   151     if (dy < height * 0.5)
       
   152         path.addLineTo(FloatPoint(x, y + dy));
       
   153 
       
   154     path.addBezierCurveTo(FloatPoint(x, y + dy * (1 - QUARTER)), FloatPoint(x + dx * (1 - QUARTER), y), FloatPoint(x + dx, y));
       
   155 
       
   156     path.closeSubpath();
       
   157 
       
   158     return path;
       
   159 }
       
   160 
       
   161 Path Path::createRoundedRectangle(const FloatRect& rectangle, const FloatSize& topLeftRadius, const FloatSize& topRightRadius, const FloatSize& bottomLeftRadius, const FloatSize& bottomRightRadius)
       
   162 {
       
   163     Path path;
       
   164 
       
   165     float width = rectangle.width();
       
   166     float height = rectangle.height();
       
   167     if (width <= 0.0 || height <= 0.0)
       
   168         return path;
       
   169 
       
   170     if (width < topLeftRadius.width() + topRightRadius.width()
       
   171             || width < bottomLeftRadius.width() + bottomRightRadius.width()
       
   172             || height < topLeftRadius.height() + bottomLeftRadius.height()
       
   173             || height < topRightRadius.height() + bottomRightRadius.height())
       
   174         // If all the radii cannot be accommodated, return a rect.
       
   175         return createRectangle(rectangle);
       
   176 
       
   177     float x = rectangle.x();
       
   178     float y = rectangle.y();
       
   179 
       
   180     path.moveTo(FloatPoint(x + topLeftRadius.width(), y));
       
   181 
       
   182     path.addLineTo(FloatPoint(x + width - topRightRadius.width(), y));
       
   183 
       
   184     path.addBezierCurveTo(FloatPoint(x + width - topRightRadius.width() * (1 - QUARTER), y), FloatPoint(x + width, y + topRightRadius.height() * (1 - QUARTER)), FloatPoint(x + width, y + topRightRadius.height()));
       
   185 
       
   186     path.addLineTo(FloatPoint(x + width, y + height - bottomRightRadius.height()));
       
   187 
       
   188     path.addBezierCurveTo(FloatPoint(x + width, y + height - bottomRightRadius.height() * (1 - QUARTER)), FloatPoint(x + width - bottomRightRadius.width() * (1 - QUARTER), y + height), FloatPoint(x + width - bottomRightRadius.width(), y + height));
       
   189 
       
   190     path.addLineTo(FloatPoint(x + bottomLeftRadius.width(), y + height));
       
   191 
       
   192     path.addBezierCurveTo(FloatPoint(x + bottomLeftRadius.width() * (1 - QUARTER), y + height), FloatPoint(x, y + height - bottomLeftRadius.height() * (1 - QUARTER)), FloatPoint(x, y + height - bottomLeftRadius.height()));
       
   193 
       
   194     path.addLineTo(FloatPoint(x, y + topLeftRadius.height()));
       
   195 
       
   196     path.addBezierCurveTo(FloatPoint(x, y + topLeftRadius.height() * (1 - QUARTER)), FloatPoint(x + topLeftRadius.width() * (1 - QUARTER), y), FloatPoint(x + topLeftRadius.width(), y));
       
   197 
       
   198     path.closeSubpath();
       
   199 
       
   200     return path;
       
   201 }
       
   202 
       
   203 Path Path::createRectangle(const FloatRect& rectangle)
       
   204 {
       
   205     Path path;
       
   206     float x = rectangle.x();
       
   207     float y = rectangle.y();
       
   208     float width = rectangle.width();
       
   209     float height = rectangle.height();
       
   210     if (width <= 0.0f || height <= 0.0f)
       
   211         return path;
       
   212     
       
   213     path.moveTo(FloatPoint(x, y));
       
   214     path.addLineTo(FloatPoint(x + width, y));
       
   215     path.addLineTo(FloatPoint(x + width, y + height));
       
   216     path.addLineTo(FloatPoint(x, y + height));
       
   217     path.closeSubpath();
       
   218 
       
   219     return path;
       
   220 }
       
   221 
       
   222 Path Path::createEllipse(const FloatPoint& center, float rx, float ry)
       
   223 {
       
   224     float cx = center.x();
       
   225     float cy = center.y();
       
   226     Path path;
       
   227     if (rx <= 0.0f || ry <= 0.0f)
       
   228         return path;
       
   229 
       
   230     float x = cx;
       
   231     float y = cy;
       
   232 
       
   233     unsigned step = 0, num = 100;
       
   234     bool running = true;
       
   235     while (running)
       
   236     {
       
   237         if (step == num)
       
   238         {
       
   239             running = false;
       
   240             break;
       
   241         }
       
   242 
       
   243         float angle = static_cast<float>(step) / static_cast<float>(num) * 2.0f * piFloat;
       
   244         x = cx + cosf(angle) * rx;
       
   245         y = cy + sinf(angle) * ry;
       
   246 
       
   247         step++;
       
   248         if (step == 1)
       
   249             path.moveTo(FloatPoint(x, y));
       
   250         else
       
   251             path.addLineTo(FloatPoint(x, y));
       
   252     }
       
   253 
       
   254     path.closeSubpath();
       
   255 
       
   256     return path;
       
   257 }
       
   258 
       
   259 Path Path::createCircle(const FloatPoint& center, float r)
       
   260 {
       
   261     return createEllipse(center, r, r);
       
   262 }
       
   263 
       
   264 Path Path::createLine(const FloatPoint& start, const FloatPoint& end)
       
   265 {
       
   266     Path path;
       
   267     if (start.x() == end.x() && start.y() == end.y())
       
   268         return path;
       
   269 
       
   270     path.moveTo(start);
       
   271     path.addLineTo(end);
       
   272 
       
   273     return path;
       
   274 }
       
   275 
       
   276 }