LibreOffice Module vcl (master)  1
shape.cxx
Go to the documentation of this file.
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 
36 #include "shape.hxx"
37 
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]));
49  }
50 }
51 
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;
59 
60  using namespace basegfx;
61  B2DPolygon poly;
62  if (horiz || vertic) {
63  tools::Long X[2]={ orig.X(), dest.X() }, Y[2] = { orig.Y(), dest.Y() };
64  if (horiz) {
65  if (X[0] < X[1]) X[1]+=pSize.Width();
66  else X[0]+=pSize.Width();
67  Y[1] += pSize.Height();
68  }
69  else {
70  if (Y[0] < Y[1]) Y[1]+=pSize.Height();
71  else Y[0]+=pSize.Height();
72  X[1] += pSize.Width();
73  }
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]));
77  }
78  else {
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++) {
87  int wh = w%4;
88  poly.append(B2DPoint(origPt[wh][0], origPt[wh][1]));
89  }
90  for (int w = origAvoid+3; w < origAvoid+6; w++) {
91  int wh = w%4;
92  poly.append(B2DPoint(destPt[wh][0], destPt[wh][1]));
93  }
94  int wh = (origAvoid+1)%4;
95  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  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);
105  return true;
106  }
107 
108 
109  //-------------------- draws a line --------------------
110 
111  void drawLine(VirtualDevice *dev, Point const &orig, Point const &dest, Size const &pSize) {
112  if (drawLineHQ(dev,orig,dest,pSize)) return;
113 
114  tools::Long penSize=(pSize.Width()+pSize.Height())/2;
115  tools::Long decal[2] = { pSize.Width()/2, pSize.Height()/2};
116 
117  using namespace basegfx;
118  B2DPolygon poly;
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);
122  }
123 
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() };
133 
134  using namespace basegfx;
135  B2DPolygon poly;
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]));
139 
140  if (drawFrame)
141  dev->DrawPolyLine(poly, double(penSize), basegfx::B2DLineJoin::NONE);
142  else
143  dev->DrawPolygon(poly);
144  }
145 
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]));
155  if (drawFrame)
156  dev->DrawPolyLine(poly, double(penSize), basegfx::B2DLineJoin::NONE);
157  else
158  dev->DrawPolygon(poly);
159  }
160 
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;
166 
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]));
173 
174  // We must have angl1 between 0 and F_2PI
175  while (angl1 < 0.0) { angl1 += F_2PI; angl2 += F_2PI; }
176  while (angl1 >= F_2PI) { angl1 -= F_2PI; angl2 -= F_2PI; }
177 
178  // if this happen, we want a complete circle
179  // so we set angl2 slightly less than angl1
180  if (angl2 >= angl1+F_2PI) angl2 = angl1-0.001;
181 
182  // We must have angl2 between 0 and F_2PI
183  while (angl2 < 0.0) angl2 += F_2PI;
184  while (angl2 >= F_2PI) angl2 -= F_2PI;
185 
186  B2DPolygon poly = basegfx::utils::createPolygonFromEllipseSegment(center, 0.5*(X[1]-X[0]), 0.5*(Y[1]-Y[0]), angl1, angl2);
187  if (drawFrame)
188  dev->DrawPolyLine(poly, double(penSize), basegfx::B2DLineJoin::NONE);
189  else {
190  // adds circle's center
191  poly.append(center);
192  dev->DrawPolygon(poly);
193  }
194  }
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 );
207 
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);
210 
211  if (drawFrame)
212  dev->DrawPolyLine(poly, double(penSize), basegfx::B2DLineJoin::NONE);
213  else
214  dev->DrawPolygon(poly);
215  }
216 
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()};
221  if (drawFrame) {
222  decalTL[0] += penSize/2; decalTL[1] += penSize/2;
223  decalBR[0] -= (penSize+1)/2; decalBR[1] -= (penSize+1)/2;
224  }
225  // Quickdraw Drawing Reference 3-82: the pen size is only used for frame
226  else decalBR[0] = decalBR[1] = 0;
227 
228 
229  int numPt = orig.GetSize();
230  if (numPt <= 1) return;
231 
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());
237  }
238  bary[0]/=double(numPt); bary[1]/=double(numPt);
239 
240  using namespace basegfx;
241  B2DPolygon poly;
242  poly.reserve(numPt);
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));
249  }
250  if (drawFrame)
251  dev->DrawPolyLine(poly, double(penSize), basegfx::B2DLineJoin::NONE);
252  else
253  dev->DrawPolygon(poly);
254  }
255 
256 
257 }
258 
259 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
void append(const basegfx::B2DPoint &rPoint, sal_uInt32 nCount)
static bool drawLineHQ(VirtualDevice *dev, Point const &orig, Point const &dest, Size const &pSize)
Definition: shape.cxx:54
long Long
constexpr::Color COL_TRANSPARENT(ColorTransparency, 0xFF, 0xFF, 0xFF, 0xFF)
void DrawPolygon(const tools::Polygon &rPoly)
Render the given polygon.
Definition: polygon.cxx:156
void drawPolygon(VirtualDevice *dev, bool drawFrame, tools::Polygon const &orig, Size const &pSize)
draws a polygon knowing penSize
Definition: shape.cxx:218
void DrawPolyLine(const tools::Polygon &rPoly)
Render the given polygon as a line stroke.
Definition: polyline.cxx:33
void drawRectangle(VirtualDevice *dev, bool drawFrame, tools::Rectangle const &orig, Size const &pSize)
draws a rectangle knowing penSize
Definition: shape.cxx:128
void drawArc(VirtualDevice *dev, bool drawFrame, tools::Rectangle const &orig, const double &angle1, const double &angle2, Size const &pSize)
draws an arc in a b2dpolygon knowing penSize
Definition: shape.cxx:162
float x
constexpr tools::Long Width() const
tools::Long Left() const
tools::Long Bottom() const
float y
void SetLineColor()
int i
void drawLine(VirtualDevice *dev, Point const &orig, Point const &dest, Size const &pSize)
draws a line from orig to dest knowing penSize
Definition: shape.cxx:111
static tools::Rectangle contractRectangle(bool drawFrame, tools::Rectangle const &rect, Size const &pSize)
returns an inside rectangle knowing the penSize in order to obtain the ``correct'' position when we d...
Definition: shape.cxx:41
B2DPolygon createPolygonFromRect(const B2DRectangle &rRect, double fRadiusX, double fRadiusY)
#define F_2PI
Osnola: IMPORTANT NOTE: some Quickdraw lines/frames can not be "quickly" drawn exactly: for instance...
Definition: shape.cxx:38
void SetFillColor()
const Color & GetLineColor() const
Definition: outdev.hxx:634
sal_Int32 w
sal_uInt16 GetSize() const
tools::Long Top() const
#define Y
const Point & GetPoint(sal_uInt16 nPos) const
void drawEllipse(VirtualDevice *dev, bool drawFrame, tools::Rectangle const &orig, Size const &pSize)
draws an ellipse knowing penSize
Definition: shape.cxx:147
constexpr tools::Long Height() const
B2DPolygon createPolygonFromEllipseSegment(const B2DPoint &rCenter, double fRadiusX, double fRadiusY, double fStart, double fEnd)
void drawRoundRectangle(VirtualDevice *dev, bool drawFrame, tools::Rectangle const &orig, Size const &ovalSize, Size const &pSize)
draws a rounded rectangle knowing penSize
Definition: shape.cxx:196
void reserve(sal_uInt32 nCount)
B2DPolygon createPolygonFromEllipse(const B2DPoint &rCenter, double fRadiusX, double fRadiusY, sal_uInt32 nStartQuadrant=0)
tools::Long Right() const
const Color & GetFillColor() const
Definition: outdev.hxx:639