1<?php
2// scorechart.php -- HotCRP chart generator
3// Copyright (c) 2006-2018 Eddie Kohler; see LICENSE.
4
5// Generates a PNG image of a bar chat.
6// Arguments are passed in as v; s is graph style.
7// Don't forget to change the width and height calculations in
8// ReviewField::unparse_graph if you change the width and height here.
9
10if (!isset($_GET["v"])) {
11    header("HTTP/1.0 400 Bad Request");
12    exit;
13}
14
15// fail if no GD support so the browser displays alt text
16if (!function_exists("imagecreate")) {
17    require_once("src/init.php");
18    Dbl::q("insert into Settings set name='__gd_required', value=1 on duplicate key update value=1");
19    header("HTTP/1.0 503 Service Unavailable");
20    exit;
21}
22
23// parse values
24$s = (isset($_GET["s"]) ? $_GET["s"] : 0);
25if ($s != 0 && $s != 1 && $s != 2)
26    $s = 2;
27$valMax = 1;
28$values = array();
29$maxY = $sum = 0;
30foreach (explode(",", $_GET["v"]) as $value) {
31    $value = (ctype_digit($value) && $value > 0 ? intval($value) : 0);
32    $values[$valMax++] = $value;
33    $maxY = max($value, $maxY);
34    $sum += $value;
35}
36if (isset($_GET["h"]) && is_numeric($_GET["h"]))
37    $valLight = intval($_GET["h"]);
38else
39    $valLight = 0;
40if ($valLight < 1 || $valLight >= $valMax)
41    $valLight = 0;
42$levelChar = (isset($_GET["c"]) && ord($_GET["c"]) >= 65
43              ? ord($_GET["c"]) : 0);
44
45
46// set shape constants
47if ($s == 0) {
48    list($blockHeight, $blockWidth, $blockSkip, $blockPad, $textWidth)
49        = array(8, 8, 2, 4, 12);
50} else if ($s == 1) {
51    list($blockHeight, $blockWidth, $blockSkip, $blockPad, $textWidth)
52        = array(3, 3, 2, 2, 0);
53}
54
55if ($s == 0 || $s == 1) {
56    $maxY = max($maxY, 3);
57    $picWidth = ($blockWidth + $blockPad) * ($valMax - 1)
58        + $blockPad
59        + 2 * $textWidth;
60    $picHeight = $blockHeight * $maxY + $blockSkip * ($maxY + 1);
61    $pic = @imagecreate($picWidth + 1, $picHeight + 1);
62} else if ($s == 2) {
63    $picWidth = 64;
64    $picHeight = 8;
65    $pic = @imagecreate($picWidth, $picHeight);
66}
67
68$cWhite = imagecolorallocate($pic, 255, 255, 255);
69$cBlack = imagecolorallocate($pic, 0, 0, 0);
70$cgray = imagecolorallocate($pic, 190, 190, 255);
71
72if ($s == 0) {
73    imagefilledrectangle($pic, 0, 0, $picWidth + 1, $picHeight + 1, $cBlack);
74    imagefilledrectangle($pic, 1, 1, $picWidth - 1, $picHeight - 1, $cWhite);
75} else if ($s == 1) {
76    imagecolortransparent($pic, $cWhite);
77    imagefilledrectangle($pic, 0, $picHeight, $picWidth + 1, $picHeight + 1, $cgray);
78    imagefilledrectangle($pic, 0, $picHeight - $blockHeight - $blockPad, 0, $picHeight + 1, $cgray);
79    imagefilledrectangle($pic, $picWidth, $picHeight - $blockHeight - $blockPad, $picWidth + 1, $picHeight + 1, $cgray);
80}
81
82$cv_black = array(0, 0, 0);
83$cv_bad = array(200, 128, 128);
84$cv_good = array(0, 232, 0);
85function quality_color($c1, $c2, $f) {
86    return array($c2[0] * $f + $c1[0] * (1 - $f),
87                 $c2[1] * $f + $c1[1] * (1 - $f),
88                 $c2[2] * $f + $c1[2] * (1 - $f));
89}
90
91$pos = 0;
92
93for ($value = 1; $value < $valMax; $value++) {
94    $vpos = ($levelChar ? $valMax - $value : $value);
95    $height = $values[$vpos];
96    $frac = ($vpos - 1) / ($valMax - 1);
97    $cv_cur = quality_color($cv_bad, $cv_good, $frac);
98    $cFill = imagecolorallocate($pic, $cv_cur[0], $cv_cur[1], $cv_cur[2]);
99
100    if ($s == 0 || $s == 1) {
101        $curX = $blockWidth * ($value - 1)
102            + $blockPad * $value + $textWidth;
103        $curY = $picHeight - ($blockHeight + $blockSkip) * $height + $blockHeight;
104
105        for ($h = 1; $h <= $height; $h++) {
106            if ($h == $height && $vpos == $valLight) {
107                $cv_cur = quality_color($cv_black, $cv_cur, 0.5);
108                $cFill = imagecolorallocate($pic, $cv_cur[0], $cv_cur[1], $cv_cur[2]);
109            }
110            imagefilledrectangle($pic, $curX, $curY - $blockHeight,
111                                 $curX + $blockWidth, $curY, $cFill);
112            $curY += ($blockHeight + $blockSkip);
113        }
114    } else {
115        if ($height > 0)
116            imagefilledrectangle($pic, ($picWidth + 1) * $pos / $sum, 0,
117                                 ($picWidth + 1) * ($pos + $height) / $sum - 2, $picHeight,
118                                 $cFill);
119        $pos += $height;
120    }
121}
122
123if ($s == 0) {
124    imagestringup($pic, 2, 0, 30, "Bad", $cBlack);
125    imagestringup($pic, 2, $picWidth-$textWidth, 30, "Good", $cBlack);
126 } else if ($s == 1) {
127    $lx = $textWidth + $blockPad;
128    $rx = $picWidth - $blockWidth - $textWidth - $blockPad;
129    $y = $picHeight - $blockHeight - $blockSkip - 3;
130    if (isset($_GET["c"]) && ord($_GET["c"]) >= 65) {
131        if ($values[1] == 0)
132            imagestring($pic, 1, $rx, $y, $_GET["c"], $cgray);
133        if ($values[$valMax - 1] == 0)
134            imagestring($pic, 1, $lx, $y, chr(ord($_GET["c"]) - $valMax + 2), $cgray);
135    } else {
136        if ($values[1] == 0)
137            imagestring($pic, 1, $lx, $y, 1, $cgray);
138        if ($values[$valMax - 1] == 0)
139            imagestring($pic, 1, $rx, $y, $valMax - 1, $cgray);
140    }
141}
142
143session_cache_limiter("");
144header("Cache-Control: max-age=31557600, public");
145header("Expires: " . gmdate("D, d M Y H:i:s", time() + 31557600) . " GMT");
146header("Content-Type: image/png");
147imagepng($pic);
148exit();
149