1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
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
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...
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)
34 #include <basegfx/polygon/b2dpolygon.hxx>
35 #include <basegfx/polygon/b2dpolygontools.hxx>
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 static tools::Rectangle
contractRectangle(bool drawFrame
, tools::Rectangle
const &rect
, Size
const &pSize
) {
42 if (!drawFrame
) return rect
;
43 tools::Long penSize
=(pSize
.Width()+pSize
.Height())/2;
44 if (2*penSize
> rect
.Right()-rect
.Left()) penSize
= (rect
.Right()-rect
.Left()+1)/2;
45 if (2*penSize
> rect
.Bottom()-rect
.Top()) penSize
= (rect
.Bottom()-rect
.Top()+1)/2;
46 tools::Long
const X
[2] = { rect
.Left()+penSize
/2, rect
.Right()-(penSize
+1)/2 };
47 tools::Long
const Y
[2] = { rect
.Top()+penSize
/2, rect
.Bottom()-(penSize
+1)/2 };
48 return tools::Rectangle(Point(X
[0],Y
[0]), Point(X
[1], Y
[1]));
52 namespace PictReaderShape
{
53 //--------- draws a horizontal/vertical/small line (by creating a "rectangle/polygon") ---------
54 static bool drawLineHQ(VirtualDevice
*dev
, Point
const &orig
, Point
const &dest
, Size
const &pSize
) {
55 tools::Long dir
[2] = { dest
.X()-orig
.X(), dest
.Y()-orig
.Y() };
56 bool vertic
= dir
[0] == 0;
57 bool horiz
= dir
[1] == 0;
58 if (!horiz
&& !vertic
&& dir
[0]*dir
[0]+dir
[1]*dir
[1] > 25) return false;
60 using namespace basegfx
;
62 if (horiz
|| vertic
) {
63 tools::Long X
[2]={ orig
.X(), dest
.X() }, Y
[2] = { orig
.Y(), dest
.Y() };
65 if (X
[0] < X
[1]) X
[1]+=pSize
.Width();
66 else X
[0]+=pSize
.Width();
67 Y
[1] += pSize
.Height();
70 if (Y
[0] < Y
[1]) Y
[1]+=pSize
.Height();
71 else Y
[0]+=pSize
.Height();
72 X
[1] += pSize
.Width();
74 poly
.append(B2DPoint(X
[0], Y
[0])); poly
.append(B2DPoint(X
[1], Y
[0]));
75 poly
.append(B2DPoint(X
[1], Y
[1])); poly
.append(B2DPoint(X
[0], Y
[1]));
76 poly
.append(B2DPoint(X
[0], Y
[0]));
79 tools::Long origPt
[4][2] = { { orig
.X(), orig
.Y() }, { orig
.X()+pSize
.Width(), orig
.Y() },
80 { orig
.X()+pSize
.Width(), orig
.Y()+pSize
.Height() },
81 { orig
.X(), orig
.Y()+pSize
.Height() }};
82 int origAvoid
= dir
[0] > 0 ? (dir
[1] > 0 ? 2 : 1) : (dir
[1] > 0 ? 3 : 0);
83 tools::Long destPt
[4][2] = { { dest
.X(), dest
.Y() }, { dest
.X()+pSize
.Width(), dest
.Y() },
84 { dest
.X()+pSize
.Width(), dest
.Y()+pSize
.Height() },
85 { dest
.X(), dest
.Y()+pSize
.Height() }};
86 for (int w
= origAvoid
+1; w
< origAvoid
+4; w
++) {
88 poly
.append(B2DPoint(origPt
[wh
][0], origPt
[wh
][1]));
90 for (int w
= origAvoid
+3; w
< origAvoid
+6; w
++) {
92 poly
.append(B2DPoint(destPt
[wh
][0], destPt
[wh
][1]));
94 int wh
= (origAvoid
+1)%4;
95 poly
.append(B2DPoint(origPt
[wh
][0], origPt
[wh
][1]));
98 // HACK: here we use the line coloring when drawing the shape
99 // must be changed if other parameter are changed to draw
101 Color oldFColor
= dev
->GetFillColor(), oldLColor
= dev
->GetLineColor();
102 dev
->SetFillColor(oldLColor
); dev
->SetLineColor(COL_TRANSPARENT
);
103 dev
->DrawPolygon(poly
);
104 dev
->SetLineColor(oldLColor
); dev
->SetFillColor(oldFColor
);
109 //-------------------- draws a line --------------------
111 void drawLine(VirtualDevice
*dev
, Point
const &orig
, Point
const &dest
, Size
const &pSize
) {
112 if (drawLineHQ(dev
,orig
,dest
,pSize
)) return;
114 tools::Long penSize
=(pSize
.Width()+pSize
.Height())/2;
115 tools::Long decal
[2] = { pSize
.Width()/2, pSize
.Height()/2};
117 using namespace basegfx
;
119 poly
.append(B2DPoint(double(orig
.X()+decal
[0]), double(orig
.Y()+decal
[1])));
120 poly
.append(B2DPoint(double(dest
.X()+decal
[0]), double(dest
.Y()+decal
[1])));
121 dev
->DrawPolyLine(poly
, double(penSize
), basegfx::B2DLineJoin::NONE
);
124 //-------------------- draws a rectangle --------------------
125 /* Note(checkme): contradictally 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 void drawRectangle(VirtualDevice
*dev
, bool drawFrame
, tools::Rectangle
const &orig
, Size
const &pSize
) {
129 int penSize
=(pSize
.Width()+pSize
.Height())/2;
130 tools::Rectangle rect
= PictReaderShapePrivate::contractRectangle(drawFrame
, orig
, pSize
);
131 tools::Long
const X
[2] = { rect
.Left(), rect
.Right() };
132 tools::Long
const Y
[2] = { rect
.Top(), rect
.Bottom() };
134 using namespace basegfx
;
136 poly
.append(B2DPoint(X
[0], Y
[0])); poly
.append(B2DPoint(X
[1], Y
[0]));
137 poly
.append(B2DPoint(X
[1], Y
[1])); poly
.append(B2DPoint(X
[0], Y
[1]));
138 poly
.append(B2DPoint(X
[0], Y
[0]));
141 dev
->DrawPolyLine(poly
, double(penSize
), basegfx::B2DLineJoin::NONE
);
143 dev
->DrawPolygon(poly
);
146 //-------------------- draws an ellipse --------------------
147 void drawEllipse(VirtualDevice
*dev
, bool drawFrame
, tools::Rectangle
const &orig
, Size
const &pSize
) {
148 int penSize
=(pSize
.Width()+pSize
.Height())/2;
149 tools::Rectangle oval
= PictReaderShapePrivate::contractRectangle(drawFrame
, orig
, pSize
);
150 using namespace basegfx
;
151 tools::Long
const X
[2] = { oval
.Left(), oval
.Right() };
152 tools::Long
const Y
[2] = { oval
.Top(), oval
.Bottom() };
153 B2DPoint
center(0.5*(X
[1]+X
[0]), 0.5*(Y
[1]+Y
[0]));
154 B2DPolygon poly
= basegfx::utils::createPolygonFromEllipse(center
, 0.5*(X
[1]-X
[0]), 0.5*(Y
[1]-Y
[0]));
156 dev
->DrawPolyLine(poly
, double(penSize
), basegfx::B2DLineJoin::NONE
);
158 dev
->DrawPolygon(poly
);
161 //-------------------- draws an arc/pie --------------------
162 void drawArc(VirtualDevice
*dev
, bool drawFrame
, tools::Rectangle
const &orig
, const double& angle1
, const double& angle2
, Size
const &pSize
) {
163 int penSize
=(pSize
.Width()+pSize
.Height())/2;
164 tools::Rectangle arc
= PictReaderShapePrivate::contractRectangle(drawFrame
, orig
, pSize
);
165 using namespace basegfx
;
167 // pict angle are CW with 0 at twelve o'clock (with Y-axis inverted)...
168 double angl1
= angle1
-M_PI_2
;
169 double angl2
= angle2
-M_PI_2
;
170 tools::Long
const X
[2] = { arc
.Left(), arc
.Right() };
171 tools::Long
const Y
[2] = { arc
.Top(), arc
.Bottom() };
172 B2DPoint
center(0.5*(X
[1]+X
[0]), 0.5*(Y
[1]+Y
[0]));
174 // We must have angl1 between 0 and 2PI
175 while (angl1
< 0.0) { angl1
+= 2 * M_PI
; angl2
+= 2 * M_PI
; }
176 while (angl1
>= 2 * M_PI
) { angl1
-= 2 * M_PI
; angl2
-= 2 * M_PI
; }
178 // if this happen, we want a complete circle
179 // so we set angl2 slightly less than angl1
180 if (angl2
>= angl1
+ 2 * M_PI
) angl2
= angl1
-0.001;
182 // We must have angl2 between 0 and 2PI
183 while (angl2
< 0.0) angl2
+= 2 * M_PI
;
184 while (angl2
>= 2 * M_PI
) angl2
-= 2 * M_PI
;
186 B2DPolygon poly
= basegfx::utils::createPolygonFromEllipseSegment(center
, 0.5*(X
[1]-X
[0]), 0.5*(Y
[1]-Y
[0]), angl1
, angl2
);
188 dev
->DrawPolyLine(poly
, double(penSize
), basegfx::B2DLineJoin::NONE
);
190 // adds circle's center
192 dev
->DrawPolygon(poly
);
195 //-------------------- draws a rectangle with round corner --------------------
196 void drawRoundRectangle(VirtualDevice
*dev
, bool drawFrame
, tools::Rectangle
const &orig
, Size
const &ovalSize
, Size
const &pSize
) {
197 int penSize
=(pSize
.Width()+pSize
.Height())/2;
198 tools::Rectangle oval
= PictReaderShapePrivate::contractRectangle(drawFrame
, orig
, pSize
);
199 int ovalW
=ovalSize
.Width(), ovalH
=ovalSize
.Height();
200 using namespace basegfx
;
201 tools::Long
const X
[2] = { oval
.Left(), oval
.Right() };
202 tools::Long
const Y
[2] = { oval
.Top(), oval
.Bottom() };
203 tools::Long width
= X
[1] - X
[0];
204 tools::Long height
= Y
[1] - Y
[0];
205 if (ovalW
> width
) ovalW
= static_cast< int >( width
);
206 if (ovalH
> height
) ovalH
= static_cast< int >( height
);
208 B2DRectangle
rect(B2DPoint(X
[0],Y
[0]), B2DPoint(X
[1],Y
[1]));
209 B2DPolygon poly
= basegfx::utils::createPolygonFromRect(rect
, (width
!= 0.0) ? ovalW
/width
: 0.0, (height
!= 0.0) ? ovalH
/height
: 0.0);
212 dev
->DrawPolyLine(poly
, double(penSize
), basegfx::B2DLineJoin::NONE
);
214 dev
->DrawPolygon(poly
);
217 //-------------------- draws a polygon --------------------
218 void drawPolygon(VirtualDevice
*dev
, bool drawFrame
, tools::Polygon
const &orig
, Size
const &pSize
) {
219 int penSize
=(pSize
.Width()+pSize
.Height())/2;
220 tools::Long decalTL
[2] = {0, 0}, decalBR
[2] = { pSize
.Width(), pSize
.Height()};
222 decalTL
[0] += penSize
/2; decalTL
[1] += penSize
/2;
223 decalBR
[0] -= (penSize
+1)/2; decalBR
[1] -= (penSize
+1)/2;
225 // Quickdraw Drawing Reference 3-82: the pen size is only used for frame
226 else decalBR
[0] = decalBR
[1] = 0;
229 int numPt
= orig
.GetSize();
230 if (numPt
<= 1) return;
232 // we compute a barycenter of the point to define the extended direction of each point
233 double bary
[2] = { 0.0, 0.0 };
234 for (int i
= 0; i
< numPt
; i
++) {
235 Point
const &pt
= orig
.GetPoint(i
);
236 bary
[0] += double(pt
.X()); bary
[1] += double(pt
.Y());
238 bary
[0]/=double(numPt
); bary
[1]/=double(numPt
);
240 using namespace basegfx
;
243 // Note: a polygon can be open, so we must not close it when we draw the frame
244 for (int i
= 0; i
< numPt
; i
++) {
245 Point
const &pt
= orig
.GetPoint(i
);
246 double x
= (double(pt
.X()) < bary
[0]) ? pt
.X()+decalTL
[0] : pt
.X()+decalBR
[0];
247 double y
= (double(pt
.Y()) < bary
[1]) ? pt
.Y()+decalTL
[1] : pt
.Y()+decalBR
[1];
248 poly
.append(B2DPoint(x
, y
));
251 dev
->DrawPolyLine(poly
, double(penSize
), basegfx::B2DLineJoin::NONE
);
253 dev
->DrawPolygon(poly
);
259 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */