1 /************************************************************** 2 * 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * 20 *************************************************************/ 21 22 23 24 /** Osnola: 25 IMPORTANT NOTE: some Quickdraw lines/frames can not be "quickly" drawn exactly: 26 for instance, when PenSize=(1,1), the line from (0,0) to (8,0) 27 corresponds to the rectangle (0,0)(0,1)(9,1)(9,0), which can only be drawn 28 by drawing a rectangle. Drawing a non horizontal/vertical will imply to draw 29 a polygon, ... 30 Similarly, drawing the frame of a rectangle (0,0)(0,1)(9,1)(9,0) when PenSize=(1,1), 31 will imply to draw a rectangle (0.5,0.5)(0.5,8.5)(8.5,8.5)(8.5,0.5) with linewidth=1... 32 33 Here, we choose: 34 - for horizontal/vertical lines and line with length less than five to draw the real line, 35 - in the other case, we keep the same shape (even if this means some "bad" coordinates) 36 */ 37 #include "precompiled_filter.hxx" 38 39 #include <basegfx/polygon/b2dpolygon.hxx> 40 #include <basegfx/polygon/b2dpolygontools.hxx> 41 #include "shape.hxx" 42 43 // a local helper to initialize double[2] arrays with long values 44 #define L2I_ARY(a,b) { static_cast<int>(a), static_cast<int>(b) } 45 #define L2D_ARY(a,b) { static_cast<double>(a), static_cast<double>(b) } 46 47 namespace PictReaderShapePrivate { 48 /** returns an inside rectangle knowing the penSize in order to obtain the ``correct'' position 49 when we draw a frame in wide length*/ contractRectangle(bool drawFrame,Rectangle const & rect,Size const & pSize)50 Rectangle contractRectangle(bool drawFrame, Rectangle const &rect, Size const &pSize) { 51 if (!drawFrame) return rect; 52 int penSize=(pSize.Width()+pSize.Height())/2; 53 if (2*penSize > rect.Right()-rect.Left()) penSize = (rect.Right()-rect.Left()+1)/2; 54 if (2*penSize > rect.Bottom()-rect.Top()) penSize = (rect.Bottom()-rect.Top()+1)/2; 55 int const X[2] = L2I_ARY( rect.Left()+penSize/2, rect.Right()-(penSize+1)/2); 56 int const Y[2] = L2I_ARY( rect.Top()+penSize/2, rect.Bottom()-(penSize+1)/2); 57 return Rectangle(Point(X[0],Y[0]), Point(X[1], Y[1])); 58 } 59 } 60 61 namespace PictReaderShape { 62 //--------- draws a horizontal/vertical/small line (by creating a "rectangle/polygon") --------- drawLineHQ(VirtualDevice * dev,Point const & orig,Point const & dest,Size const & pSize)63 bool drawLineHQ(VirtualDevice *dev, Point const &orig, Point const &dest, Size const &pSize) { 64 int dir[2] = L2I_ARY( dest.X()-orig.X(), dest.Y()-orig.Y() ); 65 bool vertic = dir[0] == 0; 66 bool horiz = dir[1] == 0; 67 if (!horiz && !vertic && dir[0]*dir[0]+dir[1]*dir[1] > 25) return false; 68 69 int X[2] = L2I_ARY( orig.X(), dest.X()); 70 int Y[2] = L2I_ARY( orig.Y(), dest.Y()); 71 using namespace basegfx; 72 B2DPolygon poly; 73 if (horiz || vertic) { 74 if (horiz) { 75 if (X[0] < X[1]) X[1]+=pSize.Width(); 76 else X[0]+=pSize.Width(); 77 Y[1] += pSize.Height(); 78 } 79 else { 80 if (Y[0] < Y[1]) Y[1]+=pSize.Height(); 81 else Y[0]+=pSize.Height(); 82 X[1] += pSize.Width(); 83 } 84 poly.append(B2DPoint(X[0], Y[0])); poly.append(B2DPoint(X[1], Y[0])); 85 poly.append(B2DPoint(X[1], Y[1])); poly.append(B2DPoint(X[0], Y[1])); 86 poly.append(B2DPoint(X[0], Y[0])); 87 } 88 else { 89 int origPt[4][2] = { L2I_ARY( orig.X(), orig.Y()), 90 L2I_ARY( orig.X()+pSize.Width(), orig.Y()), 91 L2I_ARY( orig.X()+pSize.Width(), orig.Y()+pSize.Height() ), 92 L2I_ARY( orig.X(), orig.Y()+pSize.Height() )}; 93 int origAvoid = dir[0] > 0 ? (dir[1] > 0 ? 2 : 1) : (dir[1] > 0 ? 3 : 0); 94 int destPt[4][2] = { L2I_ARY( dest.X(), dest.Y()), 95 L2I_ARY( dest.X()+pSize.Width(), dest.Y()), 96 L2I_ARY( dest.X()+pSize.Width(), dest.Y()+pSize.Height()), 97 L2I_ARY( dest.X(), dest.Y()+pSize.Height())}; 98 for (int w = origAvoid+1; w < origAvoid+4; w++) { 99 int wh = (w%4); 100 poly.append(B2DPoint(origPt[wh][0], origPt[wh][1])); 101 } 102 for (int w = origAvoid+3; w < origAvoid+6; w++) { 103 int wh = (w%4); 104 poly.append(B2DPoint(destPt[wh][0], destPt[wh][1])); 105 } 106 int wh = (origAvoid+1)%4; 107 poly.append(B2DPoint(origPt[wh][0], origPt[wh][1])); 108 } 109 110 // HACK: here we use the line coloring when drawing the shape 111 // must be changed if other parameter are changed to draw 112 // a line/fill shape 113 Color oldFColor = dev->GetFillColor(), oldLColor = dev->GetLineColor(); 114 dev->SetFillColor(oldLColor); dev->SetLineColor(Color(COL_TRANSPARENT)); 115 dev->DrawPolygon(poly); 116 dev->SetLineColor(oldLColor); dev->SetFillColor(oldFColor); 117 return true; 118 } 119 120 // 121 //-------------------- draws a line -------------------- 122 // drawLine(VirtualDevice * dev,Point const & orig,Point const & dest,Size const & pSize)123 void drawLine(VirtualDevice *dev, Point const &orig, Point const &dest, Size const &pSize) { 124 if (drawLineHQ(dev,orig,dest,pSize)) return; 125 126 int penSize=(pSize.Width()+pSize.Height())/2; 127 int decal[2] = L2I_ARY( pSize.Width()/2, pSize.Height()/2); 128 129 using namespace basegfx; 130 B2DPolygon poly; 131 poly.append(B2DPoint(double(orig.X()+decal[0]), double(orig.Y()+decal[1]))); 132 poly.append(B2DPoint(double(dest.X()+decal[0]), double(dest.Y()+decal[1]))); 133 dev->DrawPolyLine(poly, double(penSize), basegfx::B2DLINEJOIN_NONE); 134 } 135 136 //-------------------- draws a rectangle -------------------- 137 /* Note(checkme): contradically with the QuickDraw's reference 3-23, it seems better to consider 138 that the frame/content of a rectangle appears inside the given rectangle. Does a conversion 139 appear between the pascal functions and the data stored in the file ? */ drawRectangle(VirtualDevice * dev,bool drawFrame,Rectangle const & orig,Size const & pSize)140 void drawRectangle(VirtualDevice *dev, bool drawFrame, Rectangle const &orig, Size const &pSize) { 141 int penSize=(pSize.Width()+pSize.Height())/2; 142 Rectangle rect = PictReaderShapePrivate::contractRectangle(drawFrame, orig, pSize); 143 double const X[2] = L2D_ARY( rect.Left(), rect.Right() ); 144 double const Y[2] = L2D_ARY( rect.Top(), rect.Bottom() ); 145 146 using namespace basegfx; 147 B2DPolygon poly; 148 poly.append(B2DPoint(X[0], Y[0])); poly.append(B2DPoint(X[1], Y[0])); 149 poly.append(B2DPoint(X[1], Y[1])); poly.append(B2DPoint(X[0], Y[1])); 150 poly.append(B2DPoint(X[0], Y[0])); 151 152 if (drawFrame) 153 dev->DrawPolyLine(poly, double(penSize), basegfx::B2DLINEJOIN_NONE); 154 else 155 dev->DrawPolygon(poly); 156 } 157 158 //-------------------- draws an ellipse -------------------- drawEllipse(VirtualDevice * dev,bool drawFrame,Rectangle const & orig,Size const & pSize)159 void drawEllipse(VirtualDevice *dev, bool drawFrame, Rectangle const &orig, Size const &pSize) { 160 int penSize=(pSize.Width()+pSize.Height())/2; 161 Rectangle oval = PictReaderShapePrivate::contractRectangle(drawFrame, orig, pSize); 162 using namespace basegfx; 163 double const X[2] = L2D_ARY( oval.Left(), oval.Right() ); 164 double const Y[2] = L2D_ARY( oval.Top(), oval.Bottom() ); 165 B2DPoint center(0.5*(X[1]+X[0]), 0.5*(Y[1]+Y[0])); 166 B2DPolygon poly = tools::createPolygonFromEllipse(center, 0.5*(X[1]-X[0]), 0.5*(Y[1]-Y[0])); 167 if (drawFrame) 168 dev->DrawPolyLine(poly, double(penSize), basegfx::B2DLINEJOIN_NONE); 169 else 170 dev->DrawPolygon(poly); 171 } 172 173 //-------------------- draws an arc/pie -------------------- drawArc(VirtualDevice * dev,bool drawFrame,Rectangle const & orig,const double & angle1,const double & angle2,Size const & pSize)174 void drawArc(VirtualDevice *dev, bool drawFrame, Rectangle const &orig, const double& angle1, const double& angle2, Size const &pSize) { 175 int penSize=(pSize.Width()+pSize.Height())/2; 176 Rectangle arc = PictReaderShapePrivate::contractRectangle(drawFrame, orig, pSize); 177 using namespace basegfx; 178 179 double const PI2 = M_PI/2.0; 180 // pict angle are CW with 0 at twelve oclock ( with Y-axis inverted)... 181 double angl1 = angle1-PI2; 182 double angl2 = angle2-PI2; 183 double const X[2] = L2D_ARY( arc.Left(), arc.Right() ); 184 double const Y[2] = L2D_ARY( arc.Top(), arc.Bottom() ); 185 B2DPoint center(0.5*(X[1]+X[0]), 0.5*(Y[1]+Y[0])); 186 187 // We must have angl1 between 0 and F_2PI 188 while (angl1 < 0.0) { angl1 += F_2PI; angl2 += F_2PI; } 189 while (angl1 >= F_2PI) { angl1 -= F_2PI; angl2 -= F_2PI; } 190 191 // if this happen, we want a complete circle 192 // so we set angl2 slightly less than angl1 193 if (angl2 >= angl1+F_2PI) angl2 = angl1-0.001; 194 195 // We must have angl2 between 0 and F_2PI 196 while (angl2 < 0.0) angl2 += F_2PI; 197 while (angl2 >= F_2PI) angl2 -= F_2PI; 198 199 B2DPolygon poly = tools::createPolygonFromEllipseSegment(center, 0.5*(X[1]-X[0]), 0.5*(Y[1]-Y[0]), angl1, angl2); 200 if (drawFrame) 201 dev->DrawPolyLine(poly, double(penSize), basegfx::B2DLINEJOIN_NONE); 202 else { 203 // adds circle's center 204 poly.append(center); 205 dev->DrawPolygon(poly); 206 } 207 } 208 //-------------------- draws a rectangle with round corner -------------------- drawRoundRectangle(VirtualDevice * dev,bool drawFrame,Rectangle const & orig,Size const & ovalSize,Size const & pSize)209 void drawRoundRectangle(VirtualDevice *dev, bool drawFrame, Rectangle const &orig, Size const &ovalSize, Size const &pSize) { 210 int penSize=(pSize.Width()+pSize.Height())/2; 211 Rectangle oval = PictReaderShapePrivate::contractRectangle(drawFrame, orig, pSize); 212 int ovalW=ovalSize.Width(), ovalH=ovalSize.Height(); 213 using namespace basegfx; 214 double const X[2] = L2D_ARY( oval.Left(), oval.Right() ); 215 double const Y[2] = L2D_ARY( oval.Top(), oval.Bottom() ); 216 double width = X[1] - X[0]; 217 double height = Y[1] - Y[0]; 218 if (ovalW > width) ovalW = static_cast< int >( width ); 219 if (ovalH > height) ovalH = static_cast< int >( height ); 220 221 B2DRectangle rect(B2DPoint(X[0],Y[0]), B2DPoint(X[1],Y[1])); 222 B2DPolygon poly = tools::createPolygonFromRect(rect, (width != 0.0) ? ovalW/width : 0.0, (height != 0.0) ? ovalH/height : 0.0); 223 224 if (drawFrame) 225 dev->DrawPolyLine(poly, double(penSize), basegfx::B2DLINEJOIN_NONE); 226 else 227 dev->DrawPolygon(poly); 228 } 229 230 //-------------------- draws a polygon -------------------- drawPolygon(VirtualDevice * dev,bool drawFrame,Polygon const & orig,Size const & pSize)231 void drawPolygon(VirtualDevice *dev, bool drawFrame, Polygon const &orig, Size const &pSize) { 232 int penSize=(pSize.Width()+pSize.Height())/2; 233 int decalTL[2] = L2I_ARY( 0, 0); 234 int decalBR[2] = L2I_ARY( pSize.Width(), pSize.Height()); 235 if (drawFrame) { 236 decalTL[0] += penSize/2; decalTL[1] += penSize/2; 237 decalBR[0] -= (penSize+1)/2; decalBR[1] -= (penSize+1)/2; 238 } 239 // Quickdraw Drawing Reference 3-82: the pen size is only used for frame 240 else decalBR[0] = decalBR[1] = 0; 241 242 int numPt = orig.GetSize(); 243 if (numPt <= 1) return; 244 245 // we compute a barycenter of the point to define the extended direction of each point 246 double bary[2] = { 0.0, 0.0 }; 247 for (int i = 0; i < numPt; i++) { 248 Point const &pt = orig.GetPoint(i); 249 bary[0] += double(pt.X()); bary[1] += double(pt.Y()); 250 } 251 bary[0]/=double(numPt); bary[1]/=double(numPt); 252 253 using namespace basegfx; 254 B2DPolygon poly; 255 // Note: a polygon can be open, so we must not close it when we draw the frame 256 for (int i = 0; i < numPt; i++) { 257 Point const &pt = orig.GetPoint(i); 258 double x = (double(pt.X()) < bary[0]) ? pt.X()+decalTL[0] : pt.X()+decalBR[0]; 259 double y = (double(pt.Y()) < bary[1]) ? pt.Y()+decalTL[1] : pt.Y()+decalBR[1]; 260 poly.append(B2DPoint(x, y)); 261 } 262 if (drawFrame) 263 dev->DrawPolyLine(poly, double(penSize), basegfx::B2DLINEJOIN_NONE); 264 else 265 dev->DrawPolygon(poly); 266 } 267 268 } 269 270