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