tdf#130857 qt weld: Implement QtInstanceWidget::get_text_height
[LibreOffice.git] / vcl / source / filter / ipict / shape.cxx
blob88a62cfd2ff20ca83c74281389b96a05da8417e1
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 .
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...
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)
34 #include <basegfx/polygon/b2dpolygon.hxx>
35 #include <basegfx/polygon/b2dpolygontools.hxx>
36 #include "shape.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;
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();
69 else {
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]));
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]));
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]));
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
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;
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;
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);
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;
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]));
140 if (drawFrame)
141 dev->DrawPolyLine(poly, double(penSize), basegfx::B2DLineJoin::NONE);
142 else
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]));
155 if (drawFrame)
156 dev->DrawPolyLine(poly, double(penSize), basegfx::B2DLineJoin::NONE);
157 else
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);
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);
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);
211 if (drawFrame)
212 dev->DrawPolyLine(poly, double(penSize), basegfx::B2DLineJoin::NONE);
213 else
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()};
221 if (drawFrame) {
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;
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));
250 if (drawFrame)
251 dev->DrawPolyLine(poly, double(penSize), basegfx::B2DLineJoin::NONE);
252 else
253 dev->DrawPolygon(poly);
259 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */