blob: 8abf8eb76fd04eb3adff51dfefe5d1099bebe8a1 [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
34#include <pixelflinger/pixelflinger.h>
35#include "../gui/placement.h"
36#include "minui.h"
37#include "graphics.h"
Vladimir Olteand32b7eb2018-07-03 00:04:03 +030038// For std::min and std::max
39#include <algorithm>
bigbiffd58ba182020-03-23 10:02:29 -040040#include "truetype.hpp"
Ethan Yonkerfbb43532015-12-28 21:54:50 +010041
42struct GRFont {
43 GRSurface* texture;
44 int cwidth;
45 int cheight;
46};
47
Ethan Yonkerfbb43532015-12-28 21:54:50 +010048static minui_backend* gr_backend = NULL;
49
50static int overscan_percent = OVERSCAN_PERCENT;
51static int overscan_offset_x = 0;
52static int overscan_offset_y = 0;
53
54static unsigned char gr_current_r = 255;
55static unsigned char gr_current_g = 255;
56static unsigned char gr_current_b = 255;
Ethan Yonkerfbb43532015-12-28 21:54:50 +010057
58GRSurface* gr_draw = NULL;
59
60static GGLContext *gr_context = 0;
61GGLSurface gr_mem_surface;
62static int gr_is_curr_clr_opaque = 0;
63
Ethan Yonkerfbb43532015-12-28 21:54:50 +010064int gr_textEx_scaleW(int x, int y, const char *s, void* pFont, int max_width, int placement, int scale)
65{
66 GGLContext *gl = gr_context;
67 void* vfont = pFont;
68 GRFont *font = (GRFont*) pFont;
Ethan Yonker58f21322018-08-24 11:17:36 -050069 int y_scale = 0, measured_width, measured_height, new_height;
Ethan Yonkerfbb43532015-12-28 21:54:50 +010070
71 if (!s || strlen(s) == 0 || !font)
72 return 0;
73
bigbiffd58ba182020-03-23 10:02:29 -040074 measured_height = twrpTruetype::gr_ttf_getMaxFontHeight(font);
Ethan Yonkerfbb43532015-12-28 21:54:50 +010075
76 if (scale) {
bigbiffd58ba182020-03-23 10:02:29 -040077 measured_width = twrpTruetype::gr_ttf_measureEx(s, vfont);
Ethan Yonkerfbb43532015-12-28 21:54:50 +010078 if (measured_width > max_width) {
79 // Adjust font size down until the text fits
bigbiffd58ba182020-03-23 10:02:29 -040080 void *new_font = twrpTruetype::gr_ttf_scaleFont(vfont, max_width, measured_width);
Ethan Yonkerfbb43532015-12-28 21:54:50 +010081 if (!new_font) {
82 printf("gr_textEx_scaleW new_font is NULL\n");
83 return 0;
84 }
bigbiffd58ba182020-03-23 10:02:29 -040085 measured_width = twrpTruetype::gr_ttf_measureEx(s, new_font);
Ethan Yonkerfbb43532015-12-28 21:54:50 +010086 // These next 2 lines adjust the y point based on the new font's height
bigbiffd58ba182020-03-23 10:02:29 -040087 new_height = twrpTruetype::gr_ttf_getMaxFontHeight(new_font);
Ethan Yonkerfbb43532015-12-28 21:54:50 +010088 y_scale = (measured_height - new_height) / 2;
89 vfont = new_font;
90 }
91 } else
bigbiffd58ba182020-03-23 10:02:29 -040092 measured_width = twrpTruetype::gr_ttf_measureEx(s, vfont);
Ethan Yonkerfbb43532015-12-28 21:54:50 +010093
94 int x_adj = measured_width;
95 if (measured_width > max_width)
96 x_adj = max_width;
97
98 if (placement != TOP_LEFT && placement != BOTTOM_LEFT && placement != TEXT_ONLY_RIGHT) {
99 if (placement == CENTER || placement == CENTER_X_ONLY)
100 x -= (x_adj / 2);
101 else
102 x -= x_adj;
103 }
104
105 if (placement != TOP_LEFT && placement != TOP_RIGHT) {
106 if (placement == CENTER || placement == TEXT_ONLY_RIGHT)
107 y -= (measured_height / 2);
108 else if (placement == BOTTOM_LEFT || placement == BOTTOM_RIGHT)
109 y -= measured_height;
110 }
bigbiffd58ba182020-03-23 10:02:29 -0400111 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 +0100112}
113
114void gr_clip(int x, int y, int w, int h)
115{
116 GGLContext *gl = gr_context;
Vladimir Olteand32b7eb2018-07-03 00:04:03 +0300117
Ethan Yonkere03158e2019-03-19 11:39:38 -0500118#if TW_ROTATION == 0
119 gl->scissor(gl, x, y, w, h);
120#elif TW_ROTATION == 90
121 gl->scissor(gl, gr_draw->width - y - h, x, h, w);
122#elif TW_ROTATION == 270
123 gl->scissor(gl, y, gr_draw->height - x - w, h, w);
124#else
125 int t_disp = gr_draw->height - y - h;
126 int l_disp = gr_draw->width - x - w;
127 gl->scissor(gl, l_disp, t_disp, w, h);
128#endif
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100129 gl->enable(gl, GGL_SCISSOR_TEST);
130}
131
132void gr_noclip()
133{
134 GGLContext *gl = gr_context;
Vladimir Olteand32b7eb2018-07-03 00:04:03 +0300135 gl->scissor(gl, 0, 0,
136 gr_draw->width - 2 * overscan_offset_x,
137 gr_draw->height - 2 * overscan_offset_y);
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100138 gl->disable(gl, GGL_SCISSOR_TEST);
139}
140
141void gr_line(int x0, int y0, int x1, int y1, int width)
142{
143 GGLContext *gl = gr_context;
Vladimir Olteand32b7eb2018-07-03 00:04:03 +0300144 int x0_disp, y0_disp, x1_disp, y1_disp;
145
146 x0_disp = ROTATION_X_DISP(x0, y0, gr_draw);
147 y0_disp = ROTATION_Y_DISP(x0, y0, gr_draw);
148 x1_disp = ROTATION_X_DISP(x1, y1, gr_draw);
149 y1_disp = ROTATION_Y_DISP(x1, y1, gr_draw);
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100150
151 if(gr_is_curr_clr_opaque)
152 gl->disable(gl, GGL_BLEND);
153
Vladimir Olteand32b7eb2018-07-03 00:04:03 +0300154 const int coords0[2] = { x0_disp << 4, y0_disp << 4 };
155 const int coords1[2] = { x1_disp << 4, y1_disp << 4 };
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100156 gl->linex(gl, coords0, coords1, width << 4);
157
158 if(gr_is_curr_clr_opaque)
159 gl->enable(gl, GGL_BLEND);
160}
161
162gr_surface gr_render_circle(int radius, unsigned char r, unsigned char g, unsigned char b, unsigned char a)
163{
164 int rx, ry;
165 GGLSurface *surface;
166 const int diameter = radius*2 + 1;
167 const int radius_check = radius*radius + radius*0.8;
168 const uint32_t px = (a << 24) | (b << 16) | (g << 8) | r;
169 uint32_t *data;
170
171 surface = (GGLSurface *)malloc(sizeof(GGLSurface));
172 memset(surface, 0, sizeof(GGLSurface));
173
174 data = (uint32_t *)malloc(diameter * diameter * 4);
175 memset(data, 0, diameter * diameter * 4);
176
177 surface->version = sizeof(surface);
178 surface->width = diameter;
179 surface->height = diameter;
180 surface->stride = diameter;
181 surface->data = (GGLubyte*)data;
notsyncing8e4e8ec2018-06-09 10:40:50 +0800182#if defined(RECOVERY_BGRA)
183 surface->format = GGL_PIXEL_FORMAT_BGRA_8888;
184#else
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100185 surface->format = GGL_PIXEL_FORMAT_RGBA_8888;
notsyncing8e4e8ec2018-06-09 10:40:50 +0800186#endif
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100187
188 for(ry = -radius; ry <= radius; ++ry)
189 for(rx = -radius; rx <= radius; ++rx)
190 if(rx*rx+ry*ry <= radius_check)
191 *(data + diameter*(radius + ry) + (radius+rx)) = px;
192
193 return (gr_surface)surface;
194}
195
196void gr_color(unsigned char r, unsigned char g, unsigned char b, unsigned char a)
197{
198 GGLContext *gl = gr_context;
199 GGLint color[4];
200#if defined(RECOVERY_ABGR) || defined(RECOVERY_BGRA)
201 color[0] = ((b << 8) | r) + 1;
202 color[1] = ((g << 8) | g) + 1;
203 color[2] = ((r << 8) | b) + 1;
204 color[3] = ((a << 8) | a) + 1;
205#else
206 color[0] = ((r << 8) | r) + 1;
207 color[1] = ((g << 8) | g) + 1;
208 color[2] = ((b << 8) | b) + 1;
209 color[3] = ((a << 8) | a) + 1;
210#endif
211 gl->color4xv(gl, color);
212
213 gr_is_curr_clr_opaque = (a == 255);
214}
215
216void gr_clear()
217{
218 if (gr_draw->pixel_bytes == 2) {
219 gr_fill(0, 0, gr_fb_width(), gr_fb_height());
220 return;
221 }
222
223 // This code only works on 32bpp devices
224 if (gr_current_r == gr_current_g && gr_current_r == gr_current_b) {
225 memset(gr_draw->data, gr_current_r, gr_draw->height * gr_draw->row_bytes);
226 } else {
227 unsigned char* px = gr_draw->data;
228 for (int y = 0; y < gr_draw->height; ++y) {
229 for (int x = 0; x < gr_draw->width; ++x) {
230 *px++ = gr_current_r;
231 *px++ = gr_current_g;
232 *px++ = gr_current_b;
233 px++;
234 }
235 px += gr_draw->row_bytes - (gr_draw->width * gr_draw->pixel_bytes);
236 }
237 }
238}
239
240void gr_fill(int x, int y, int w, int h)
241{
242 GGLContext *gl = gr_context;
Vladimir Olteand32b7eb2018-07-03 00:04:03 +0300243 int x0_disp, y0_disp, x1_disp, y1_disp;
244 int l_disp, r_disp, t_disp, b_disp;
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100245
246 if(gr_is_curr_clr_opaque)
247 gl->disable(gl, GGL_BLEND);
248
Vladimir Olteand32b7eb2018-07-03 00:04:03 +0300249 x0_disp = ROTATION_X_DISP(x, y, gr_draw);
250 y0_disp = ROTATION_Y_DISP(x, y, gr_draw);
251 x1_disp = ROTATION_X_DISP(x + w, y + h, gr_draw);
252 y1_disp = ROTATION_Y_DISP(x + w, y + h, gr_draw);
253 l_disp = std::min(x0_disp, x1_disp);
254 r_disp = std::max(x0_disp, x1_disp);
255 t_disp = std::min(y0_disp, y1_disp);
256 b_disp = std::max(y0_disp, y1_disp);
257
258 gl->recti(gl, l_disp, t_disp, r_disp, b_disp);
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100259
260 if(gr_is_curr_clr_opaque)
261 gl->enable(gl, GGL_BLEND);
262}
263
Vladimir Olteand32b7eb2018-07-03 00:04:03 +0300264void gr_blit(gr_surface source, int sx, int sy, int w, int h, int dx, int dy)
265{
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100266 if (gr_context == NULL) {
267 return;
268 }
269
270 GGLContext *gl = gr_context;
271 GGLSurface *surface = (GGLSurface*)source;
272
273 if(surface->format == GGL_PIXEL_FORMAT_RGBX_8888)
274 gl->disable(gl, GGL_BLEND);
275
Vladimir Olteand32b7eb2018-07-03 00:04:03 +0300276 int dx0_disp, dy0_disp, dx1_disp, dy1_disp;
277 int l_disp, r_disp, t_disp, b_disp;
278
279 // Figuring out display coordinates works for TW_ROTATION == 0 too,
280 // and isn't as expensive as allocating and rotating another surface,
281 // so we do this anyway.
282 dx0_disp = ROTATION_X_DISP(dx, dy, gr_draw);
283 dy0_disp = ROTATION_Y_DISP(dx, dy, gr_draw);
284 dx1_disp = ROTATION_X_DISP(dx + w, dy + h, gr_draw);
285 dy1_disp = ROTATION_Y_DISP(dx + w, dy + h, gr_draw);
286 l_disp = std::min(dx0_disp, dx1_disp);
287 r_disp = std::max(dx0_disp, dx1_disp);
288 t_disp = std::min(dy0_disp, dy1_disp);
289 b_disp = std::max(dy0_disp, dy1_disp);
290
291#if TW_ROTATION != 0
292 // Do not perform relatively expensive operation if not needed
293 GGLSurface surface_rotated;
294 surface_rotated.version = sizeof(surface_rotated);
295 // Skip the **(TW_ROTATION == 0)** || (TW_ROTATION == 180) check
296 // because we are under a TW_ROTATION != 0 conditional compilation statement
297 surface_rotated.width = (TW_ROTATION == 180) ? surface->width : surface->height;
298 surface_rotated.height = (TW_ROTATION == 180) ? surface->height : surface->width;
299 surface_rotated.stride = surface_rotated.width;
300 surface_rotated.format = surface->format;
301 surface_rotated.data = (GGLubyte*) malloc(surface_rotated.stride * surface_rotated.height * 4);
302 surface_ROTATION_transform((gr_surface) &surface_rotated, (const gr_surface) surface, 4);
303
304 gl->bindTexture(gl, &surface_rotated);
305#else
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100306 gl->bindTexture(gl, surface);
Vladimir Olteand32b7eb2018-07-03 00:04:03 +0300307#endif
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100308 gl->texEnvi(gl, GGL_TEXTURE_ENV, GGL_TEXTURE_ENV_MODE, GGL_REPLACE);
309 gl->texGeni(gl, GGL_S, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE);
310 gl->texGeni(gl, GGL_T, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE);
311 gl->enable(gl, GGL_TEXTURE_2D);
Vladimir Olteand32b7eb2018-07-03 00:04:03 +0300312 gl->texCoord2i(gl, sx - l_disp, sy - t_disp);
313 gl->recti(gl, l_disp, t_disp, r_disp, b_disp);
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100314 gl->disable(gl, GGL_TEXTURE_2D);
315
Vladimir Olteand32b7eb2018-07-03 00:04:03 +0300316#if TW_ROTATION != 0
317 free(surface_rotated.data);
318#endif
319
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100320 if(surface->format == GGL_PIXEL_FORMAT_RGBX_8888)
321 gl->enable(gl, GGL_BLEND);
322}
323
324unsigned int gr_get_width(gr_surface surface) {
325 if (surface == NULL) {
326 return 0;
327 }
328 return ((GGLSurface*) surface)->width;
329}
330
331unsigned int gr_get_height(gr_surface surface) {
332 if (surface == NULL) {
333 return 0;
334 }
335 return ((GGLSurface*) surface)->height;
336}
337
338void gr_flip() {
339 gr_draw = gr_backend->flip(gr_backend);
340 // On double buffered back ends, when we flip, we need to tell
341 // pixel flinger to draw to the other buffer
342 gr_mem_surface.data = (GGLubyte*)gr_draw->data;
343 gr_context->colorBuffer(gr_context, &gr_mem_surface);
344}
345
346static void get_memory_surface(GGLSurface* ms) {
347 ms->version = sizeof(*ms);
348 ms->width = gr_draw->width;
349 ms->height = gr_draw->height;
350 ms->stride = gr_draw->row_bytes / gr_draw->pixel_bytes;
351 ms->data = (GGLubyte*)gr_draw->data;
352 ms->format = gr_draw->format;
353}
354
355int gr_init(void)
356{
357 gr_draw = NULL;
358
Ethan Yonkerb386f712017-01-20 14:30:28 -0600359#ifdef MSM_BSP
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100360 gr_backend = open_overlay();
361 if (gr_backend) {
362 gr_draw = gr_backend->init(gr_backend);
363 if (!gr_draw) {
364 gr_backend->exit(gr_backend);
365 } else
366 printf("Using overlay graphics.\n");
367 }
Ethan Yonkerb386f712017-01-20 14:30:28 -0600368#endif
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100369
370#ifdef HAS_ADF
Ethan Yonkerb386f712017-01-20 14:30:28 -0600371 if (!gr_backend || !gr_draw) {
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100372 gr_backend = open_adf();
373 if (gr_backend) {
374 gr_draw = gr_backend->init(gr_backend);
375 if (!gr_draw) {
376 gr_backend->exit(gr_backend);
377 } else
378 printf("Using adf graphics.\n");
379 }
380 }
381#else
382#ifdef MSM_BSP
383 printf("Skipping adf graphics because TW_TARGET_USES_QCOM_BSP := true\n");
384#else
385 printf("Skipping adf graphics -- not present in build tree\n");
386#endif
387#endif
388
389#ifdef HAS_DRM
Ethan Yonkerb386f712017-01-20 14:30:28 -0600390 if (!gr_backend || !gr_draw) {
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100391 gr_backend = open_drm();
392 gr_draw = gr_backend->init(gr_backend);
393 if (gr_draw)
394 printf("Using drm graphics.\n");
395 }
396#else
397 printf("Skipping drm graphics -- not present in build tree\n");
398#endif
399
Ethan Yonkerb386f712017-01-20 14:30:28 -0600400 if (!gr_backend || !gr_draw) {
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100401 gr_backend = open_fbdev();
402 gr_draw = gr_backend->init(gr_backend);
403 if (gr_draw == NULL) {
404 return -1;
405 } else
406 printf("Using fbdev graphics.\n");
407 }
408
409 overscan_offset_x = gr_draw->width * overscan_percent / 100;
410 overscan_offset_y = gr_draw->height * overscan_percent / 100;
411
412 // Set up pixelflinger
413 get_memory_surface(&gr_mem_surface);
414 gglInit(&gr_context);
415 GGLContext *gl = gr_context;
416 gl->colorBuffer(gl, &gr_mem_surface);
417
418 gl->activeTexture(gl, 0);
419 gl->enable(gl, GGL_BLEND);
420 gl->blendFunc(gl, GGL_SRC_ALPHA, GGL_ONE_MINUS_SRC_ALPHA);
421
422 gr_flip();
423 gr_flip();
424
¨Paul80bd1652016-04-26 16:49:58 +0100425#ifdef TW_SCREEN_BLANK_ON_BOOT
426 printf("TW_SCREEN_BLANK_ON_BOOT := true\n");
427 gr_fb_blank(true);
428 gr_fb_blank(false);
429#endif
430
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100431 return 0;
432}
433
434void gr_exit(void)
435{
436 gr_backend->exit(gr_backend);
437}
438
439int gr_fb_width(void)
440{
Vladimir Olteand32b7eb2018-07-03 00:04:03 +0300441 return (TW_ROTATION == 0 || TW_ROTATION == 180) ?
442 gr_draw->width - 2 * overscan_offset_x :
443 gr_draw->height - 2 * overscan_offset_y;
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100444}
445
446int gr_fb_height(void)
447{
Vladimir Olteand32b7eb2018-07-03 00:04:03 +0300448 return (TW_ROTATION == 0 || TW_ROTATION == 180) ?
449 gr_draw->height - 2 * overscan_offset_y :
450 gr_draw->width - 2 * overscan_offset_x;
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100451}
452
453void gr_fb_blank(bool blank)
454{
455 gr_backend->blank(gr_backend, blank);
456}
457
458int gr_get_surface(gr_surface* surface)
459{
460 GGLSurface* ms = (GGLSurface*)malloc(sizeof(GGLSurface));
461 if (!ms) return -1;
462
463 // Allocate the data
464 get_memory_surface(ms);
465 ms->data = (GGLubyte*)malloc(ms->stride * ms->height * gr_draw->pixel_bytes);
466
467 // Now, copy the data
468 memcpy(ms->data, gr_mem_surface.data, gr_draw->width * gr_draw->height * gr_draw->pixel_bytes / 8);
469
470 *surface = (gr_surface*) ms;
471 return 0;
472}
473
474int gr_free_surface(gr_surface surface)
475{
476 if (!surface)
477 return -1;
478
479 GGLSurface* ms = (GGLSurface*) surface;
480 free(ms->data);
481 free(ms);
482 return 0;
483}
484
485void gr_write_frame_to_file(int fd)
486{
487 write(fd, gr_mem_surface.data, gr_draw->width * gr_draw->height * gr_draw->pixel_bytes / 8);
488}