1<?php
2/*
3** Zabbix
4** Copyright (C) 2001-2021 Zabbix SIA
5**
6** This program is free software; you can redistribute it and/or modify
7** it under the terms of the GNU General Public License as published by
8** the Free Software Foundation; either version 2 of the License, or
9** (at your option) any later version.
10**
11** This program is distributed in the hope that it will be useful,
12** but WITHOUT ANY WARRANTY; without even the implied warranty of
13** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14** GNU General Public License for more details.
15**
16** You should have received a copy of the GNU General Public License
17** along with this program; if not, write to the Free Software
18** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19**/
20
21
22abstract class CGraphDraw {
23
24	/**
25	 * Default top padding including header label height and vertical padding.
26	 */
27	const DEFAULT_HEADER_PADDING_TOP = 36;
28	/**
29	 * Default font size for header label text.
30	 */
31	const DEFAULT_HEADER_LABEL_FONT_SIZE = 11;
32	/**
33	 * Default value for top and bottom padding.
34	 */
35	const DEFAULT_TOP_BOTTOM_PADDING = 12;
36
37	/**
38	 * Header label visibility.
39	 */
40	public $draw_header = true;
41	/**
42	 * Use top and bottom padding for graph image.
43	 */
44	public $with_vertical_padding = true;
45
46	public function __construct($type = GRAPH_TYPE_NORMAL) {
47		$this->stime = null;
48		$this->fullSizeX = null;
49		$this->fullSizeY = null;
50		$this->m_minY = null;
51		$this->m_maxY = null;
52		$this->data = [];
53		$this->items = [];
54		$this->min = null;
55		$this->max = null;
56		$this->avg = null;
57		$this->clock = null;
58		$this->count = null;
59		$this->header = null;
60		$this->from_time = null;
61		$this->to_time = null;
62		$this->colors = null;
63		$this->colorsrgb = null;
64		$this->im = null;
65		$this->period = SEC_PER_HOUR;
66		$this->sizeX = 900; // default graph size X
67		$this->sizeY = 200; // default graph size Y
68		$this->shiftXleft = 100;
69		$this->shiftXright = 50;
70		$this->shiftXCaption = 0;
71		$this->num = 0;
72		$this->type = $type; // graph type
73		$this->drawLegend = 1;
74		$this->graphtheme = getUserGraphTheme();
75		$this->shiftY = 0;
76	}
77
78	/**
79	 * Recalculate $this->shiftY property for graph according header label visibility settings and visibility of graph
80	 * top and bottom padding settings.
81	 */
82	protected function calculateTopPadding() {
83		$shift = static::DEFAULT_HEADER_PADDING_TOP;
84
85		if (!$this->draw_header) {
86			$shift -= static::DEFAULT_HEADER_LABEL_FONT_SIZE;
87		}
88
89		if (!$this->with_vertical_padding) {
90			$shift -= static::DEFAULT_TOP_BOTTOM_PADDING;
91		}
92
93		$this->shiftY = $shift;
94	}
95
96	public function initColors() {
97		// red, green, blue, alpha
98		$this->colorsrgb = [
99			'Red'				=> [255, 0, 0, 50],
100			'Dark Red'			=> [150, 0, 0, 50],
101			'Green'				=> [0, 255, 0, 50],
102			'Dark Green'		=> [0, 150, 0, 50],
103			'Blue'				=> [0, 0, 255, 50],
104			'Dark Blue'			=> [0, 0, 150, 50],
105			'Yellow'			=> [255, 255, 0, 50],
106			'Dark Yellow'		=> [150, 150, 0, 50],
107			'Cyan'				=> [0, 255, 255, 50],
108			'Dark Cyan'			=> [0, 150, 150, 50],
109			'Black'				=> [0, 0, 0, 50],
110			'Gray'				=> [150, 150, 150, 50],
111			'White'				=> [255, 255, 255],
112			'Dark Red No Alpha'	=> [150, 0, 0],
113			'Black No Alpha'	=> [0, 0, 0],
114			'HistoryMinMax'		=> [90, 150, 185, 50],
115			'HistoryMax'		=> [255, 100, 100, 50],
116			'HistoryMin'		=> [50, 255, 50, 50],
117			'HistoryAvg'		=> [50, 50, 50, 50],
118			'ValueMinMax'		=> [255, 255, 150, 50],
119			'ValueMax'			=> [255, 180, 180, 50],
120			'ValueMin'			=> [100, 255, 100, 50],
121			'Not Work Period'	=> [230, 230, 230],
122			'UnknownData'		=> [130, 130, 130, 50]
123		];
124
125		// i should rename no alpha to alpha at some point to get rid of some confusion
126		foreach ($this->colorsrgb as $name => $RGBA) {
127			if (isset($RGBA[3]) && function_exists('imagecolorexactalpha')
128					&& function_exists('imagecreatetruecolor') && @imagecreatetruecolor(1, 1)) {
129				$this->colors[$name] = imagecolorexactalpha($this->im, $RGBA[0], $RGBA[1], $RGBA[2], $RGBA[3]);
130			}
131			else {
132				$this->colors[$name] = imagecolorallocate($this->im, $RGBA[0], $RGBA[1], $RGBA[2]);
133			}
134		}
135	}
136
137	public function showLegend($type = true) {
138		$this->drawLegend = $type;
139	}
140
141	public function setPeriod($period) {
142		$this->period = $period;
143	}
144
145	public function setSTime($stime) {
146		if ($stime > 19000000000000 && $stime < 21000000000000) {
147			$this->stime = zbxDateToTime($stime);
148		}
149		else {
150			$this->stime = $stime;
151		}
152	}
153
154	public function setWidth($value = null) {
155		// avoid sizex==0, to prevent division by zero later
156		if ($value == 0) {
157			$value = null;
158		}
159		if (is_null($value)) {
160			$value = 900;
161		}
162		$this->sizeX = $value;
163	}
164
165	public function setHeight($value = null) {
166		if ($value == 0) {
167			$value = null;
168		}
169		if (is_null($value)) {
170			$value = 200;
171		}
172		$this->sizeY = $value;
173	}
174
175	public function getWidth() {
176		return $this->sizeX;
177	}
178
179	public function getHeight() {
180		return $this->sizeY;
181	}
182
183	public function getLastValue($num) {
184		$data = &$this->data[$this->items[$num]['itemid']];
185
186		if (isset($data)) {
187			for ($i = $this->sizeX - 1; $i >= 0; $i--) {
188				if (!empty($data['count'][$i])) {
189					switch ($this->items[$num]['calc_fnc']) {
190						case CALC_FNC_MIN:
191							return $data['min'][$i];
192						case CALC_FNC_MAX:
193							return $data['max'][$i];
194						case CALC_FNC_ALL:
195						case CALC_FNC_AVG:
196						default:
197							return $data['avg'][$i];
198					}
199				}
200			}
201		}
202
203		return 0;
204	}
205
206	public function drawRectangle() {
207		imagefilledrectangle($this->im, 0, 0,
208			$this->fullSizeX,
209			$this->fullSizeY,
210			$this->getColor($this->graphtheme['backgroundcolor'], 0)
211		);
212	}
213
214	public function drawHeader() {
215		if (!$this->draw_header) {
216			return;
217		}
218
219		if (!isset($this->header)) {
220			$str = $this->items[0]['hostname'].NAME_DELIMITER.$this->items[0]['name'];
221		}
222		else {
223			// TODO: graphs shouldn't resolve names themselves
224			$str = CMacrosResolverHelper::resolveGraphName($this->header, $this->items);
225		}
226
227		// calculate largest font size that can fit graph header
228		// TODO: font size must be dynamic in other parts of the graph as well, like legend, timeline, etc
229		for ($fontsize = static::DEFAULT_HEADER_LABEL_FONT_SIZE; $fontsize > 7; $fontsize--) {
230			$dims = imageTextSize($fontsize, 0, $str);
231			$x = $this->fullSizeX / 2 - ($dims['width'] / 2);
232
233			// Most important information must be displayed.
234			if ($x < 2) {
235				$x = 2;
236			}
237			if ($dims['width'] <= $this->fullSizeX) {
238				break;
239			}
240		}
241		$y_baseline = 24;
242
243		if (!$this->with_vertical_padding) {
244			$y_baseline -= static::DEFAULT_TOP_BOTTOM_PADDING;
245		}
246
247		imageText($this->im, $fontsize, 0, $x, $y_baseline, $this->getColor($this->graphtheme['textcolor'], 0), $str);
248	}
249
250	public function setHeader($header) {
251		$this->header = $header;
252	}
253
254	public function getColor($color, $alfa = 50) {
255		if (isset($this->colors[$color])) {
256			return $this->colors[$color];
257		}
258
259		return get_color($this->im, $color, $alfa);
260	}
261
262	public function getShadow($color, $alfa = 0) {
263		if (isset($this->colorsrgb[$color])) {
264			$red = $this->colorsrgb[$color][0];
265			$green = $this->colorsrgb[$color][1];
266			$blue = $this->colorsrgb[$color][2];
267		}
268		else {
269			list($red, $green, $blue) = hex2rgb($color);
270		}
271
272		if ($this->sum > 0) {
273			$red = (int)($red * 0.6);
274			$green = (int)($green * 0.6);
275			$blue = (int)($blue * 0.6);
276		}
277
278		$RGB = [$red, $green, $blue];
279
280		if (isset($alfa) && function_exists('imagecolorexactalpha') && function_exists('imagecreatetruecolor')
281				&& @imagecreatetruecolor(1, 1)) {
282			return imagecolorexactalpha($this->im, $RGB[0], $RGB[1], $RGB[2], $alfa);
283		}
284
285		return imagecolorallocate($this->im, $RGB[0], $RGB[1], $RGB[2]);
286	}
287}
288