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