blob: 35ffaebbb73d0cfaf4390851644c9ec8f20c522d [file] [log] [blame]
Ethan Yonkerfbb43532015-12-28 21:54:50 +01001/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <stdbool.h>
18#include <stdlib.h>
19#include <string.h>
20#include <unistd.h>
21
22#include <fcntl.h>
23#include <stdio.h>
24
25#include <sys/ioctl.h>
26#include <sys/mman.h>
27#include <sys/types.h>
28
29#include <linux/fb.h>
30#include <linux/kd.h>
31
32#include <time.h>
33
Aaron Kling4eccd9a2019-06-30 12:20:09 -050034#include <cutils/properties.h>
Ethan Yonkerfbb43532015-12-28 21:54:50 +010035#include <pixelflinger/pixelflinger.h>
36#include "../gui/placement.h"
37#include "minui.h"
38#include "graphics.h"
Vladimir Olteand32b7eb2018-07-03 00:04:03 +030039// For std::min and std::max
40#include <algorithm>
bigbiffd58ba182020-03-23 10:02:29 -040041#include "truetype.hpp"
Ethan Yonkerfbb43532015-12-28 21:54:50 +010042
43struct GRFont {
44 GRSurface* texture;
45 int cwidth;
46 int cheight;
47};
48
Ethan Yonkerfbb43532015-12-28 21:54:50 +010049static minui_backend* gr_backend = NULL;
50
51static int overscan_percent = OVERSCAN_PERCENT;
52static int overscan_offset_x = 0;
53static int overscan_offset_y = 0;
54
55static unsigned char gr_current_r = 255;
56static unsigned char gr_current_g = 255;
57static unsigned char gr_current_b = 255;
Ethan Yonkerfbb43532015-12-28 21:54:50 +010058
59GRSurface* gr_draw = NULL;
60
61static GGLContext *gr_context = 0;
62GGLSurface gr_mem_surface;
63static int gr_is_curr_clr_opaque = 0;
64
Aaron Kling4eccd9a2019-06-30 12:20:09 -050065unsigned int gr_rotation = 0;
66
Ethan Yonkerfbb43532015-12-28 21:54:50 +010067int gr_textEx_scaleW(int x, int y, const char *s, void* pFont, int max_width, int placement, int scale)
68{
69 GGLContext *gl = gr_context;
70 void* vfont = pFont;
71 GRFont *font = (GRFont*) pFont;
Ethan Yonker58f21322018-08-24 11:17:36 -050072 int y_scale = 0, measured_width, measured_height, new_height;
Ethan Yonkerfbb43532015-12-28 21:54:50 +010073
74 if (!s || strlen(s) == 0 || !font)
75 return 0;
76
bigbiffd58ba182020-03-23 10:02:29 -040077 measured_height = twrpTruetype::gr_ttf_getMaxFontHeight(font);
Ethan Yonkerfbb43532015-12-28 21:54:50 +010078
79 if (scale) {
bigbiffd58ba182020-03-23 10:02:29 -040080 measured_width = twrpTruetype::gr_ttf_measureEx(s, vfont);
Ethan Yonkerfbb43532015-12-28 21:54:50 +010081 if (measured_width > max_width) {
82 // Adjust font size down until the text fits
bigbiffd58ba182020-03-23 10:02:29 -040083 void *new_font = twrpTruetype::gr_ttf_scaleFont(vfont, max_width, measured_width);
Ethan Yonkerfbb43532015-12-28 21:54:50 +010084 if (!new_font) {
85 printf("gr_textEx_scaleW new_font is NULL\n");
86 return 0;
87 }
bigbiffd58ba182020-03-23 10:02:29 -040088 measured_width = twrpTruetype::gr_ttf_measureEx(s, new_font);
Ethan Yonkerfbb43532015-12-28 21:54:50 +010089 // These next 2 lines adjust the y point based on the new font's height
bigbiffd58ba182020-03-23 10:02:29 -040090 new_height = twrpTruetype::gr_ttf_getMaxFontHeight(new_font);
Ethan Yonkerfbb43532015-12-28 21:54:50 +010091 y_scale = (measured_height - new_height) / 2;
92 vfont = new_font;
93 }
94 } else
bigbiffd58ba182020-03-23 10:02:29 -040095 measured_width = twrpTruetype::gr_ttf_measureEx(s, vfont);
Ethan Yonkerfbb43532015-12-28 21:54:50 +010096
97 int x_adj = measured_width;
98 if (measured_width > max_width)
99 x_adj = max_width;
100
101 if (placement != TOP_LEFT && placement != BOTTOM_LEFT && placement != TEXT_ONLY_RIGHT) {
102 if (placement == CENTER || placement == CENTER_X_ONLY)
103 x -= (x_adj / 2);
104 else
105 x -= x_adj;
106 }
107
108 if (placement != TOP_LEFT && placement != TOP_RIGHT) {
109 if (placement == CENTER || placement == TEXT_ONLY_RIGHT)
110 y -= (measured_height / 2);
111 else if (placement == BOTTOM_LEFT || placement == BOTTOM_RIGHT)
112 y -= measured_height;
113 }
bigbiffd58ba182020-03-23 10:02:29 -0400114 return twrpTruetype::gr_ttf_textExWH(gl, x, y + y_scale, s, vfont, measured_width + x, -1, gr_draw);
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100115}
116
117void gr_clip(int x, int y, int w, int h)
118{
119 GGLContext *gl = gr_context;
Vladimir Olteand32b7eb2018-07-03 00:04:03 +0300120
Aaron Kling4eccd9a2019-06-30 12:20:09 -0500121 switch (gr_rotation) {
122 case 90:
123 gl->scissor(gl, gr_draw->width - y - h, x, h, w);
124 break;
125 case 180:
126 gl->scissor(gl, gr_draw->width - x - w, gr_draw->height - y - h, w, h);
127 break;
128 case 270:
129 gl->scissor(gl, y, gr_draw->height - x - w, h, w);
130 break;
131 default:
132 gl->scissor(gl, x, y, w, h);
133 break;
134 }
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100135 gl->enable(gl, GGL_SCISSOR_TEST);
136}
137
138void gr_noclip()
139{
140 GGLContext *gl = gr_context;
Vladimir Olteand32b7eb2018-07-03 00:04:03 +0300141 gl->scissor(gl, 0, 0,
142 gr_draw->width - 2 * overscan_offset_x,
143 gr_draw->height - 2 * overscan_offset_y);
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100144 gl->disable(gl, GGL_SCISSOR_TEST);
145}
146
147void gr_line(int x0, int y0, int x1, int y1, int width)
148{
149 GGLContext *gl = gr_context;
Vladimir Olteand32b7eb2018-07-03 00:04:03 +0300150 int x0_disp, y0_disp, x1_disp, y1_disp;
151
Aaron Kling4eccd9a2019-06-30 12:20:09 -0500152 x0_disp = ROTATION_X_DISP(x0, y0, gr_draw->width);
153 y0_disp = ROTATION_Y_DISP(x0, y0, gr_draw->height);
154 x1_disp = ROTATION_X_DISP(x1, y1, gr_draw->width);
155 y1_disp = ROTATION_Y_DISP(x1, y1, gr_draw->height);
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100156
157 if(gr_is_curr_clr_opaque)
158 gl->disable(gl, GGL_BLEND);
159
Vladimir Olteand32b7eb2018-07-03 00:04:03 +0300160 const int coords0[2] = { x0_disp << 4, y0_disp << 4 };
161 const int coords1[2] = { x1_disp << 4, y1_disp << 4 };
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100162 gl->linex(gl, coords0, coords1, width << 4);
163
164 if(gr_is_curr_clr_opaque)
165 gl->enable(gl, GGL_BLEND);
166}
167
168gr_surface gr_render_circle(int radius, unsigned char r, unsigned char g, unsigned char b, unsigned char a)
169{
170 int rx, ry;
171 GGLSurface *surface;
172 const int diameter = radius*2 + 1;
173 const int radius_check = radius*radius + radius*0.8;
174 const uint32_t px = (a << 24) | (b << 16) | (g << 8) | r;
175 uint32_t *data;
176
177 surface = (GGLSurface *)malloc(sizeof(GGLSurface));
178 memset(surface, 0, sizeof(GGLSurface));
179
180 data = (uint32_t *)malloc(diameter * diameter * 4);
181 memset(data, 0, diameter * diameter * 4);
182
183 surface->version = sizeof(surface);
184 surface->width = diameter;
185 surface->height = diameter;
186 surface->stride = diameter;
187 surface->data = (GGLubyte*)data;
notsyncing8e4e8ec2018-06-09 10:40:50 +0800188#if defined(RECOVERY_BGRA)
189 surface->format = GGL_PIXEL_FORMAT_BGRA_8888;
190#else
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100191 surface->format = GGL_PIXEL_FORMAT_RGBA_8888;
notsyncing8e4e8ec2018-06-09 10:40:50 +0800192#endif
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100193
194 for(ry = -radius; ry <= radius; ++ry)
195 for(rx = -radius; rx <= radius; ++rx)
196 if(rx*rx+ry*ry <= radius_check)
197 *(data + diameter*(radius + ry) + (radius+rx)) = px;
198
199 return (gr_surface)surface;
200}
201
202void gr_color(unsigned char r, unsigned char g, unsigned char b, unsigned char a)
203{
204 GGLContext *gl = gr_context;
205 GGLint color[4];
206#if defined(RECOVERY_ABGR) || defined(RECOVERY_BGRA)
207 color[0] = ((b << 8) | r) + 1;
208 color[1] = ((g << 8) | g) + 1;
209 color[2] = ((r << 8) | b) + 1;
210 color[3] = ((a << 8) | a) + 1;
211#else
212 color[0] = ((r << 8) | r) + 1;
213 color[1] = ((g << 8) | g) + 1;
214 color[2] = ((b << 8) | b) + 1;
215 color[3] = ((a << 8) | a) + 1;
216#endif
217 gl->color4xv(gl, color);
218
219 gr_is_curr_clr_opaque = (a == 255);
220}
221
222void gr_clear()
223{
224 if (gr_draw->pixel_bytes == 2) {
225 gr_fill(0, 0, gr_fb_width(), gr_fb_height());
226 return;
227 }
228
229 // This code only works on 32bpp devices
230 if (gr_current_r == gr_current_g && gr_current_r == gr_current_b) {
231 memset(gr_draw->data, gr_current_r, gr_draw->height * gr_draw->row_bytes);
232 } else {
233 unsigned char* px = gr_draw->data;
234 for (int y = 0; y < gr_draw->height; ++y) {
235 for (int x = 0; x < gr_draw->width; ++x) {
236 *px++ = gr_current_r;
237 *px++ = gr_current_g;
238 *px++ = gr_current_b;
239 px++;
240 }
241 px += gr_draw->row_bytes - (gr_draw->width * gr_draw->pixel_bytes);
242 }
243 }
244}
245
246void gr_fill(int x, int y, int w, int h)
247{
248 GGLContext *gl = gr_context;
Vladimir Olteand32b7eb2018-07-03 00:04:03 +0300249 int x0_disp, y0_disp, x1_disp, y1_disp;
250 int l_disp, r_disp, t_disp, b_disp;
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100251
252 if(gr_is_curr_clr_opaque)
253 gl->disable(gl, GGL_BLEND);
254
Aaron Kling4eccd9a2019-06-30 12:20:09 -0500255 x0_disp = ROTATION_X_DISP(x, y, gr_draw->width);
256 y0_disp = ROTATION_Y_DISP(x, y, gr_draw->height);
257 x1_disp = ROTATION_X_DISP(x + w, y + h, gr_draw->width);
258 y1_disp = ROTATION_Y_DISP(x + w, y + h, gr_draw->height);
Vladimir Olteand32b7eb2018-07-03 00:04:03 +0300259 l_disp = std::min(x0_disp, x1_disp);
260 r_disp = std::max(x0_disp, x1_disp);
261 t_disp = std::min(y0_disp, y1_disp);
262 b_disp = std::max(y0_disp, y1_disp);
263
264 gl->recti(gl, l_disp, t_disp, r_disp, b_disp);
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100265
266 if(gr_is_curr_clr_opaque)
267 gl->enable(gl, GGL_BLEND);
268}
269
Vladimir Olteand32b7eb2018-07-03 00:04:03 +0300270void gr_blit(gr_surface source, int sx, int sy, int w, int h, int dx, int dy)
271{
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100272 if (gr_context == NULL) {
273 return;
274 }
275
276 GGLContext *gl = gr_context;
277 GGLSurface *surface = (GGLSurface*)source;
278
279 if(surface->format == GGL_PIXEL_FORMAT_RGBX_8888)
280 gl->disable(gl, GGL_BLEND);
281
Vladimir Olteand32b7eb2018-07-03 00:04:03 +0300282 int dx0_disp, dy0_disp, dx1_disp, dy1_disp;
283 int l_disp, r_disp, t_disp, b_disp;
284
Aaron Kling4eccd9a2019-06-30 12:20:09 -0500285 // Figuring out display coordinates works for gr_rotation == 0 too,
Vladimir Olteand32b7eb2018-07-03 00:04:03 +0300286 // and isn't as expensive as allocating and rotating another surface,
287 // so we do this anyway.
Aaron Kling4eccd9a2019-06-30 12:20:09 -0500288 dx0_disp = ROTATION_X_DISP(dx, dy, gr_draw->width);
289 dy0_disp = ROTATION_Y_DISP(dx, dy, gr_draw->height);
290 dx1_disp = ROTATION_X_DISP(dx + w, dy + h, gr_draw->width);
291 dy1_disp = ROTATION_Y_DISP(dx + w, dy + h, gr_draw->height);
Vladimir Olteand32b7eb2018-07-03 00:04:03 +0300292 l_disp = std::min(dx0_disp, dx1_disp);
293 r_disp = std::max(dx0_disp, dx1_disp);
294 t_disp = std::min(dy0_disp, dy1_disp);
295 b_disp = std::max(dy0_disp, dy1_disp);
296
Vladimir Olteand32b7eb2018-07-03 00:04:03 +0300297 GGLSurface surface_rotated;
Aaron Kling4eccd9a2019-06-30 12:20:09 -0500298 if (gr_rotation != 0) {
299 // Do not perform relatively expensive operation if not needed
300 surface_rotated.version = sizeof(surface_rotated);
301 // Skip the **(gr_rotation == 0)** || (gr_rotation == 180) check
302 // because we are under a gr_rotation != 0 conditional compilation statement
303 surface_rotated.width = (gr_rotation == 180) ? surface->width : surface->height;
304 surface_rotated.height = (gr_rotation == 180) ? surface->height : surface->width;
305 surface_rotated.stride = surface_rotated.width;
306 surface_rotated.format = surface->format;
307 surface_rotated.data = (GGLubyte*) malloc(surface_rotated.stride * surface_rotated.height * 4);
308 surface_ROTATION_transform((gr_surface) &surface_rotated, (const gr_surface) surface, 4);
Vladimir Olteand32b7eb2018-07-03 00:04:03 +0300309
Aaron Kling4eccd9a2019-06-30 12:20:09 -0500310 gl->bindTexture(gl, &surface_rotated);
311 } else {
312 gl->bindTexture(gl, surface);
313 }
314
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100315 gl->texEnvi(gl, GGL_TEXTURE_ENV, GGL_TEXTURE_ENV_MODE, GGL_REPLACE);
316 gl->texGeni(gl, GGL_S, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE);
317 gl->texGeni(gl, GGL_T, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE);
318 gl->enable(gl, GGL_TEXTURE_2D);
Vladimir Olteand32b7eb2018-07-03 00:04:03 +0300319 gl->texCoord2i(gl, sx - l_disp, sy - t_disp);
320 gl->recti(gl, l_disp, t_disp, r_disp, b_disp);
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100321 gl->disable(gl, GGL_TEXTURE_2D);
322
Aaron Kling4eccd9a2019-06-30 12:20:09 -0500323 if (gr_rotation != 0)
324 free(surface_rotated.data);
Vladimir Olteand32b7eb2018-07-03 00:04:03 +0300325
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100326 if(surface->format == GGL_PIXEL_FORMAT_RGBX_8888)
327 gl->enable(gl, GGL_BLEND);
328}
329
330unsigned int gr_get_width(gr_surface surface) {
331 if (surface == NULL) {
332 return 0;
333 }
334 return ((GGLSurface*) surface)->width;
335}
336
337unsigned int gr_get_height(gr_surface surface) {
338 if (surface == NULL) {
339 return 0;
340 }
341 return ((GGLSurface*) surface)->height;
342}
343
344void gr_flip() {
345 gr_draw = gr_backend->flip(gr_backend);
346 // On double buffered back ends, when we flip, we need to tell
347 // pixel flinger to draw to the other buffer
348 gr_mem_surface.data = (GGLubyte*)gr_draw->data;
349 gr_context->colorBuffer(gr_context, &gr_mem_surface);
350}
351
352static void get_memory_surface(GGLSurface* ms) {
353 ms->version = sizeof(*ms);
354 ms->width = gr_draw->width;
355 ms->height = gr_draw->height;
356 ms->stride = gr_draw->row_bytes / gr_draw->pixel_bytes;
357 ms->data = (GGLubyte*)gr_draw->data;
358 ms->format = gr_draw->format;
359}
360
361int gr_init(void)
362{
363 gr_draw = NULL;
364
Aaron Kling4eccd9a2019-06-30 12:20:09 -0500365 char gr_rotation_string[PROPERTY_VALUE_MAX];
366 char default_rotation[4];
367 snprintf(default_rotation, 4, "%d", TW_ROTATION);
368 property_get("persist.twrp.rotation", gr_rotation_string, default_rotation);
369 gr_rotation = atoi(gr_rotation_string);
370 if (!(gr_rotation == 90 || gr_rotation == 180 || gr_rotation == 270))
371 gr_rotation = 0;
372
Ethan Yonkerb386f712017-01-20 14:30:28 -0600373#ifdef MSM_BSP
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100374 gr_backend = open_overlay();
375 if (gr_backend) {
376 gr_draw = gr_backend->init(gr_backend);
377 if (!gr_draw) {
378 gr_backend->exit(gr_backend);
379 } else
380 printf("Using overlay graphics.\n");
381 }
Ethan Yonkerb386f712017-01-20 14:30:28 -0600382#endif
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100383
384#ifdef HAS_ADF
Ethan Yonkerb386f712017-01-20 14:30:28 -0600385 if (!gr_backend || !gr_draw) {
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100386 gr_backend = open_adf();
387 if (gr_backend) {
388 gr_draw = gr_backend->init(gr_backend);
389 if (!gr_draw) {
390 gr_backend->exit(gr_backend);
391 } else
392 printf("Using adf graphics.\n");
393 }
394 }
395#else
396#ifdef MSM_BSP
397 printf("Skipping adf graphics because TW_TARGET_USES_QCOM_BSP := true\n");
398#else
399 printf("Skipping adf graphics -- not present in build tree\n");
400#endif
401#endif
402
403#ifdef HAS_DRM
Ethan Yonkerb386f712017-01-20 14:30:28 -0600404 if (!gr_backend || !gr_draw) {
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100405 gr_backend = open_drm();
406 gr_draw = gr_backend->init(gr_backend);
407 if (gr_draw)
408 printf("Using drm graphics.\n");
409 }
410#else
411 printf("Skipping drm graphics -- not present in build tree\n");
412#endif
413
Ethan Yonkerb386f712017-01-20 14:30:28 -0600414 if (!gr_backend || !gr_draw) {
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100415 gr_backend = open_fbdev();
416 gr_draw = gr_backend->init(gr_backend);
417 if (gr_draw == NULL) {
418 return -1;
419 } else
420 printf("Using fbdev graphics.\n");
421 }
422
423 overscan_offset_x = gr_draw->width * overscan_percent / 100;
424 overscan_offset_y = gr_draw->height * overscan_percent / 100;
425
426 // Set up pixelflinger
427 get_memory_surface(&gr_mem_surface);
428 gglInit(&gr_context);
429 GGLContext *gl = gr_context;
430 gl->colorBuffer(gl, &gr_mem_surface);
431
432 gl->activeTexture(gl, 0);
433 gl->enable(gl, GGL_BLEND);
434 gl->blendFunc(gl, GGL_SRC_ALPHA, GGL_ONE_MINUS_SRC_ALPHA);
435
436 gr_flip();
437 gr_flip();
438
¨Paul80bd1652016-04-26 16:49:58 +0100439#ifdef TW_SCREEN_BLANK_ON_BOOT
440 printf("TW_SCREEN_BLANK_ON_BOOT := true\n");
441 gr_fb_blank(true);
442 gr_fb_blank(false);
443#endif
444
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100445 return 0;
446}
447
448void gr_exit(void)
449{
450 gr_backend->exit(gr_backend);
451}
452
453int gr_fb_width(void)
454{
Aaron Kling4eccd9a2019-06-30 12:20:09 -0500455 return (gr_rotation == 0 || gr_rotation == 180) ?
Vladimir Olteand32b7eb2018-07-03 00:04:03 +0300456 gr_draw->width - 2 * overscan_offset_x :
457 gr_draw->height - 2 * overscan_offset_y;
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100458}
459
460int gr_fb_height(void)
461{
Aaron Kling4eccd9a2019-06-30 12:20:09 -0500462 return (gr_rotation == 0 || gr_rotation == 180) ?
Vladimir Olteand32b7eb2018-07-03 00:04:03 +0300463 gr_draw->height - 2 * overscan_offset_y :
464 gr_draw->width - 2 * overscan_offset_x;
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100465}
466
467void gr_fb_blank(bool blank)
468{
469 gr_backend->blank(gr_backend, blank);
470}
471
472int gr_get_surface(gr_surface* surface)
473{
474 GGLSurface* ms = (GGLSurface*)malloc(sizeof(GGLSurface));
475 if (!ms) return -1;
476
477 // Allocate the data
478 get_memory_surface(ms);
479 ms->data = (GGLubyte*)malloc(ms->stride * ms->height * gr_draw->pixel_bytes);
480
481 // Now, copy the data
482 memcpy(ms->data, gr_mem_surface.data, gr_draw->width * gr_draw->height * gr_draw->pixel_bytes / 8);
483
484 *surface = (gr_surface*) ms;
485 return 0;
486}
487
488int gr_free_surface(gr_surface surface)
489{
490 if (!surface)
491 return -1;
492
493 GGLSurface* ms = (GGLSurface*) surface;
494 free(ms->data);
495 free(ms);
496 return 0;
497}
498
499void gr_write_frame_to_file(int fd)
500{
501 write(fd, gr_mem_surface.data, gr_draw->width * gr_draw->height * gr_draw->pixel_bytes / 8);
502}