()
| 117 | |
| 118 | |
| 119 | def draw_png() -> Image.Image: |
| 120 | image = Image.new("RGB", (W, H), "white") |
| 121 | draw = ImageDraw.Draw(image) |
| 122 | |
| 123 | for r, method in enumerate(METHODS): |
| 124 | y = TOP + r * CELL_H |
| 125 | method_w, method_h = text_size(draw, method, F_ROW) |
| 126 | draw.text( |
| 127 | (LEFT - 22 - method_w, y + (CELL_H - GAP - method_h) / 2 - 1), |
| 128 | method, |
| 129 | font=F_ROW, |
| 130 | fill=TEXT, |
| 131 | ) |
| 132 | |
| 133 | for c, (_metric, direction, values) in enumerate(METRICS): |
| 134 | x = LEFT + c * CELL_W |
| 135 | score = score_for_metric(values, r, direction) |
| 136 | draw.rectangle( |
| 137 | (x, y, x + CELL_W - GAP, y + CELL_H - GAP), |
| 138 | fill=color_for_score(score), |
| 139 | ) |
| 140 | centered_text(draw, x + CELL_W / 2, y + CELL_H / 2, fmt(values[r]), F_CELL, (0, 0, 0)) |
| 141 | |
| 142 | label_y = TOP + len(METHODS) * CELL_H + 49 |
| 143 | for c, (metric, direction, _values) in enumerate(METRICS): |
| 144 | x = LEFT + c * CELL_W + CELL_W / 2 |
| 145 | draw_rotated_text( |
| 146 | image, |
| 147 | metric + (" \u2193" if direction == "down" else " \u2191"), |
| 148 | x, |
| 149 | label_y, |
| 150 | -21, |
| 151 | F_COL, |
| 152 | TEXT, |
| 153 | ) |
| 154 | |
| 155 | bar_x = LEFT + len(METRICS) * CELL_W + 32 |
| 156 | bar_y = TOP + 4 |
| 157 | bar_h = len(METHODS) * CELL_H - GAP |
| 158 | for i in range(bar_h): |
| 159 | score = 1 - i / max(1, bar_h - 1) |
| 160 | draw.line((bar_x, bar_y + i, bar_x + 14, bar_y + i), fill=color_for_score(score)) |
| 161 | draw.text((bar_x + 23, bar_y - 6), "better", font=F_SMALL, fill=TEXT) |
| 162 | draw.text((bar_x + 23, bar_y + bar_h - 14), "worse", font=F_SMALL, fill=TEXT) |
| 163 | return image |
| 164 | |
| 165 | |
| 166 | def rgb(c: tuple[int, int, int]) -> str: |
no test coverage detected