blob: c3d6ca5af4b113188117ca0eb0a4482687190259 [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>
Ethan Yonkerfbb43532015-12-28 21:54:50 +010040
41struct GRFont {
42 GRSurface* texture;
43 int cwidth;
44 int cheight;
45};
46
Ethan Yonkerfbb43532015-12-28 21:54:50 +010047static minui_backend* gr_backend = NULL;
48
49static int overscan_percent = OVERSCAN_PERCENT;
50static int overscan_offset_x = 0;
51static int overscan_offset_y = 0;
52
53static unsigned char gr_current_r = 255;
54static unsigned char gr_current_g = 255;
55static unsigned char gr_current_b = 255;
Ethan Yonkerfbb43532015-12-28 21:54:50 +010056
57GRSurface* gr_draw = NULL;
58
59static GGLContext *gr_context = 0;
60GGLSurface gr_mem_surface;
61static int gr_is_curr_clr_opaque = 0;
62
Ethan Yonkerfbb43532015-12-28 21:54:50 +010063int gr_textEx_scaleW(int x, int y, const char *s, void* pFont, int max_width, int placement, int scale)
64{
65 GGLContext *gl = gr_context;
66 void* vfont = pFont;
67 GRFont *font = (GRFont*) pFont;
Ethan Yonker58f21322018-08-24 11:17:36 -050068 int y_scale = 0, measured_width, measured_height, new_height;
Ethan Yonkerfbb43532015-12-28 21:54:50 +010069
70 if (!s || strlen(s) == 0 || !font)
71 return 0;
72
73 measured_height = gr_ttf_getMaxFontHeight(font);
74
75 if (scale) {
76 measured_width = gr_ttf_measureEx(s, vfont);
77 if (measured_width > max_width) {
78 // Adjust font size down until the text fits
79 void *new_font = gr_ttf_scaleFont(vfont, max_width, measured_width);
80 if (!new_font) {
81 printf("gr_textEx_scaleW new_font is NULL\n");
82 return 0;
83 }
84 measured_width = gr_ttf_measureEx(s, new_font);
85 // These next 2 lines adjust the y point based on the new font's height
86 new_height = gr_ttf_getMaxFontHeight(new_font);
87 y_scale = (measured_height - new_height) / 2;
88 vfont = new_font;
89 }
90 } else
91 measured_width = gr_ttf_measureEx(s, vfont);
92
93 int x_adj = measured_width;
94 if (measured_width > max_width)
95 x_adj = max_width;
96
97 if (placement != TOP_LEFT && placement != BOTTOM_LEFT && placement != TEXT_ONLY_RIGHT) {
98 if (placement == CENTER || placement == CENTER_X_ONLY)
99 x -= (x_adj / 2);
100 else
101 x -= x_adj;
102 }
103
104 if (placement != TOP_LEFT && placement != TOP_RIGHT) {
105 if (placement == CENTER || placement == TEXT_ONLY_RIGHT)
106 y -= (measured_height / 2);
107 else if (placement == BOTTOM_LEFT || placement == BOTTOM_RIGHT)
108 y -= measured_height;
109 }
Vladimir Olteand32b7eb2018-07-03 00:04:03 +0300110 return gr_ttf_textExWH(gl, x, y + y_scale, s, vfont, measured_width + x, -1, gr_draw);
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100111}
112
113void gr_clip(int x, int y, int w, int h)
114{
115 GGLContext *gl = gr_context;
Vladimir Olteand32b7eb2018-07-03 00:04:03 +0300116
Ethan Yonkere03158e2019-03-19 11:39:38 -0500117#if TW_ROTATION == 0
118 gl->scissor(gl, x, y, w, h);
119#elif TW_ROTATION == 90
120 gl->scissor(gl, gr_draw->width - y - h, x, h, w);
121#elif TW_ROTATION == 270
122 gl->scissor(gl, y, gr_draw->height - x - w, h, w);
123#else
124 int t_disp = gr_draw->height - y - h;
125 int l_disp = gr_draw->width - x - w;
126 gl->scissor(gl, l_disp, t_disp, w, h);
127#endif
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100128 gl->enable(gl, GGL_SCISSOR_TEST);
129}
130
131void gr_noclip()
132{
133 GGLContext *gl = gr_context;
Vladimir Olteand32b7eb2018-07-03 00:04:03 +0300134 gl->scissor(gl, 0, 0,
135 gr_draw->width - 2 * overscan_offset_x,
136 gr_draw->height - 2 * overscan_offset_y);
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100137 gl->disable(gl, GGL_SCISSOR_TEST);
138}
139
140void gr_line(int x0, int y0, int x1, int y1, int width)
141{
142 GGLContext *gl = gr_context;
Vladimir Olteand32b7eb2018-07-03 00:04:03 +0300143 int x0_disp, y0_disp, x1_disp, y1_disp;
144
145 x0_disp = ROTATION_X_DISP(x0, y0, gr_draw);
146 y0_disp = ROTATION_Y_DISP(x0, y0, gr_draw);
147 x1_disp = ROTATION_X_DISP(x1, y1, gr_draw);
148 y1_disp = ROTATION_Y_DISP(x1, y1, gr_draw);
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100149
150 if(gr_is_curr_clr_opaque)
151 gl->disable(gl, GGL_BLEND);
152
Vladimir Olteand32b7eb2018-07-03 00:04:03 +0300153 const int coords0[2] = { x0_disp << 4, y0_disp << 4 };
154 const int coords1[2] = { x1_disp << 4, y1_disp << 4 };
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100155 gl->linex(gl, coords0, coords1, width << 4);
156
157 if(gr_is_curr_clr_opaque)
158 gl->enable(gl, GGL_BLEND);
159}
160
161gr_surface gr_render_circle(int radius, unsigned char r, unsigned char g, unsigned char b, unsigned char a)
162{
163 int rx, ry;
164 GGLSurface *surface;
165 const int diameter = radius*2 + 1;
166 const int radius_check = radius*radius + radius*0.8;
167 const uint32_t px = (a << 24) | (b << 16) | (g << 8) | r;
168 uint32_t *data;
169
170 surface = (GGLSurface *)malloc(sizeof(GGLSurface));
171 memset(surface, 0, sizeof(GGLSurface));
172
173 data = (uint32_t *)malloc(diameter * diameter * 4);
174 memset(data, 0, diameter * diameter * 4);
175
176 surface->version = sizeof(surface);
177 surface->width = diameter;
178 surface->height = diameter;
179 surface->stride = diameter;
180 surface->data = (GGLubyte*)data;
notsyncing8e4e8ec2018-06-09 10:40:50 +0800181#if defined(RECOVERY_BGRA)
182 surface->format = GGL_PIXEL_FORMAT_BGRA_8888;
183#else
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100184 surface->format = GGL_PIXEL_FORMAT_RGBA_8888;
notsyncing8e4e8ec2018-06-09 10:40:50 +0800185#endif
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100186
187 for(ry = -radius; ry <= radius; ++ry)
188 for(rx = -radius; rx <= radius; ++rx)
189 if(rx*rx+ry*ry <= radius_check)
190 *(data + diameter*(radius + ry) + (radius+rx)) = px;
191
192 return (gr_surface)surface;
193}
194
195void gr_color(unsigned char r, unsigned char g, unsigned char b, unsigned char a)
196{
197 GGLContext *gl = gr_context;
198 GGLint color[4];
199#if defined(RECOVERY_ABGR) || defined(RECOVERY_BGRA)
200 color[0] = ((b << 8) | r) + 1;
201 color[1] = ((g << 8) | g) + 1;
202 color[2] = ((r << 8) | b) + 1;
203 color[3] = ((a << 8) | a) + 1;
204#else
205 color[0] = ((r << 8) | r) + 1;
206 color[1] = ((g << 8) | g) + 1;
207 color[2] = ((b << 8) | b) + 1;
208 color[3] = ((a << 8) | a) + 1;
209#endif
210 gl->color4xv(gl, color);
211
212 gr_is_curr_clr_opaque = (a == 255);
213}
214
215void gr_clear()
216{
217 if (gr_draw->pixel_bytes == 2) {
218 gr_fill(0, 0, gr_fb_width(), gr_fb_height());
219 return;
220 }
221
222 // This code only works on 32bpp devices
223 if (gr_current_r == gr_current_g && gr_current_r == gr_current_b) {
224 memset(gr_draw->data, gr_current_r, gr_draw->height * gr_draw->row_bytes);
225 } else {
226 unsigned char* px = gr_draw->data;
227 for (int y = 0; y < gr_draw->height; ++y) {
228 for (int x = 0; x < gr_draw->width; ++x) {
229 *px++ = gr_current_r;
230 *px++ = gr_current_g;
231 *px++ = gr_current_b;
232 px++;
233 }
234 px += gr_draw->row_bytes - (gr_draw->width * gr_draw->pixel_bytes);
235 }
236 }
237}
238
239void gr_fill(int x, int y, int w, int h)
240{
241 GGLContext *gl = gr_context;
Vladimir Olteand32b7eb2018-07-03 00:04:03 +0300242 int x0_disp, y0_disp, x1_disp, y1_disp;
243 int l_disp, r_disp, t_disp, b_disp;
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100244
245 if(gr_is_curr_clr_opaque)
246 gl->disable(gl, GGL_BLEND);
247
Vladimir Olteand32b7eb2018-07-03 00:04:03 +0300248 x0_disp = ROTATION_X_DISP(x, y, gr_draw);
249 y0_disp = ROTATION_Y_DISP(x, y, gr_draw);
250 x1_disp = ROTATION_X_DISP(x + w, y + h, gr_draw);
251 y1_disp = ROTATION_Y_DISP(x + w, y + h, gr_draw);
252 l_disp = std::min(x0_disp, x1_disp);
253 r_disp = std::max(x0_disp, x1_disp);
254 t_disp = std::min(y0_disp, y1_disp);
255 b_disp = std::max(y0_disp, y1_disp);
256
257 gl->recti(gl, l_disp, t_disp, r_disp, b_disp);
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100258
259 if(gr_is_curr_clr_opaque)
260 gl->enable(gl, GGL_BLEND);
261}
262
Vladimir Olteand32b7eb2018-07-03 00:04:03 +0300263void gr_blit(gr_surface source, int sx, int sy, int w, int h, int dx, int dy)
264{
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100265 if (gr_context == NULL) {
266 return;
267 }
268
269 GGLContext *gl = gr_context;
270 GGLSurface *surface = (GGLSurface*)source;
271
272 if(surface->format == GGL_PIXEL_FORMAT_RGBX_8888)
273 gl->disable(gl, GGL_BLEND);
274
Vladimir Olteand32b7eb2018-07-03 00:04:03 +0300275 int dx0_disp, dy0_disp, dx1_disp, dy1_disp;
276 int l_disp, r_disp, t_disp, b_disp;
277
278 // Figuring out display coordinates works for TW_ROTATION == 0 too,
279 // and isn't as expensive as allocating and rotating another surface,
280 // so we do this anyway.
281 dx0_disp = ROTATION_X_DISP(dx, dy, gr_draw);
282 dy0_disp = ROTATION_Y_DISP(dx, dy, gr_draw);
283 dx1_disp = ROTATION_X_DISP(dx + w, dy + h, gr_draw);
284 dy1_disp = ROTATION_Y_DISP(dx + w, dy + h, gr_draw);
285 l_disp = std::min(dx0_disp, dx1_disp);
286 r_disp = std::max(dx0_disp, dx1_disp);
287 t_disp = std::min(dy0_disp, dy1_disp);
288 b_disp = std::max(dy0_disp, dy1_disp);
289
290#if TW_ROTATION != 0
291 // Do not perform relatively expensive operation if not needed
292 GGLSurface surface_rotated;
293 surface_rotated.version = sizeof(surface_rotated);
294 // Skip the **(TW_ROTATION == 0)** || (TW_ROTATION == 180) check
295 // because we are under a TW_ROTATION != 0 conditional compilation statement
296 surface_rotated.width = (TW_ROTATION == 180) ? surface->width : surface->height;
297 surface_rotated.height = (TW_ROTATION == 180) ? surface->height : surface->width;
298 surface_rotated.stride = surface_rotated.width;
299 surface_rotated.format = surface->format;
300 surface_rotated.data = (GGLubyte*) malloc(surface_rotated.stride * surface_rotated.height * 4);
301 surface_ROTATION_transform((gr_surface) &surface_rotated, (const gr_surface) surface, 4);
302
303 gl->bindTexture(gl, &surface_rotated);
304#else
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100305 gl->bindTexture(gl, surface);
Vladimir Olteand32b7eb2018-07-03 00:04:03 +0300306#endif
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100307 gl->texEnvi(gl, GGL_TEXTURE_ENV, GGL_TEXTURE_ENV_MODE, GGL_REPLACE);
308 gl->texGeni(gl, GGL_S, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE);
309 gl->texGeni(gl, GGL_T, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE);
310 gl->enable(gl, GGL_TEXTURE_2D);
Vladimir Olteand32b7eb2018-07-03 00:04:03 +0300311 gl->texCoord2i(gl, sx - l_disp, sy - t_disp);
312 gl->recti(gl, l_disp, t_disp, r_disp, b_disp);
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100313 gl->disable(gl, GGL_TEXTURE_2D);
314
Vladimir Olteand32b7eb2018-07-03 00:04:03 +0300315#if TW_ROTATION != 0
316 free(surface_rotated.data);
317#endif
318
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100319 if(surface->format == GGL_PIXEL_FORMAT_RGBX_8888)
320 gl->enable(gl, GGL_BLEND);
321}
322
323unsigned int gr_get_width(gr_surface surface) {
324 if (surface == NULL) {
325 return 0;
326 }
327 return ((GGLSurface*) surface)->width;
328}
329
330unsigned int gr_get_height(gr_surface surface) {
331 if (surface == NULL) {
332 return 0;
333 }
334 return ((GGLSurface*) surface)->height;
335}
336
337void gr_flip() {
338 gr_draw = gr_backend->flip(gr_backend);
339 // On double buffered back ends, when we flip, we need to tell
340 // pixel flinger to draw to the other buffer
341 gr_mem_surface.data = (GGLubyte*)gr_draw->data;
342 gr_context->colorBuffer(gr_context, &gr_mem_surface);
343}
344
345static void get_memory_surface(GGLSurface* ms) {
346 ms->version = sizeof(*ms);
347 ms->width = gr_draw->width;
348 ms->height = gr_draw->height;
349 ms->stride = gr_draw->row_bytes / gr_draw->pixel_bytes;
350 ms->data = (GGLubyte*)gr_draw->data;
351 ms->format = gr_draw->format;
352}
353
354int gr_init(void)
355{
356 gr_draw = NULL;
357
Ethan Yonkerb386f712017-01-20 14:30:28 -0600358#ifdef MSM_BSP
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100359 gr_backend = open_overlay();
360 if (gr_backend) {
361 gr_draw = gr_backend->init(gr_backend);
362 if (!gr_draw) {
363 gr_backend->exit(gr_backend);
364 } else
365 printf("Using overlay graphics.\n");
366 }
Ethan Yonkerb386f712017-01-20 14:30:28 -0600367#endif
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100368
369#ifdef HAS_ADF
Ethan Yonkerb386f712017-01-20 14:30:28 -0600370 if (!gr_backend || !gr_draw) {
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100371 gr_backend = open_adf();
372 if (gr_backend) {
373 gr_draw = gr_backend->init(gr_backend);
374 if (!gr_draw) {
375 gr_backend->exit(gr_backend);
376 } else
377 printf("Using adf graphics.\n");
378 }
379 }
380#else
381#ifdef MSM_BSP
382 printf("Skipping adf graphics because TW_TARGET_USES_QCOM_BSP := true\n");
383#else
384 printf("Skipping adf graphics -- not present in build tree\n");
385#endif
386#endif
387
388#ifdef HAS_DRM
Ethan Yonkerb386f712017-01-20 14:30:28 -0600389 if (!gr_backend || !gr_draw) {
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100390 gr_backend = open_drm();
391 gr_draw = gr_backend->init(gr_backend);
392 if (gr_draw)
393 printf("Using drm graphics.\n");
394 }
395#else
396 printf("Skipping drm graphics -- not present in build tree\n");
397#endif
398
Ethan Yonkerb386f712017-01-20 14:30:28 -0600399 if (!gr_backend || !gr_draw) {
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100400 gr_backend = open_fbdev();
401 gr_draw = gr_backend->init(gr_backend);
402 if (gr_draw == NULL) {
403 return -1;
404 } else
405 printf("Using fbdev graphics.\n");
406 }
407
408 overscan_offset_x = gr_draw->width * overscan_percent / 100;
409 overscan_offset_y = gr_draw->height * overscan_percent / 100;
410
411 // Set up pixelflinger
412 get_memory_surface(&gr_mem_surface);
413 gglInit(&gr_context);
414 GGLContext *gl = gr_context;
415 gl->colorBuffer(gl, &gr_mem_surface);
416
417 gl->activeTexture(gl, 0);
418 gl->enable(gl, GGL_BLEND);
419 gl->blendFunc(gl, GGL_SRC_ALPHA, GGL_ONE_MINUS_SRC_ALPHA);
420
421 gr_flip();
422 gr_flip();
423
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100424 return 0;
425}
426
427void gr_exit(void)
428{
429 gr_backend->exit(gr_backend);
430}
431
432int gr_fb_width(void)
433{
Vladimir Olteand32b7eb2018-07-03 00:04:03 +0300434 return (TW_ROTATION == 0 || TW_ROTATION == 180) ?
435 gr_draw->width - 2 * overscan_offset_x :
436 gr_draw->height - 2 * overscan_offset_y;
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100437}
438
439int gr_fb_height(void)
440{
Vladimir Olteand32b7eb2018-07-03 00:04:03 +0300441 return (TW_ROTATION == 0 || TW_ROTATION == 180) ?
442 gr_draw->height - 2 * overscan_offset_y :
443 gr_draw->width - 2 * overscan_offset_x;
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100444}
445
446void gr_fb_blank(bool blank)
447{
448 gr_backend->blank(gr_backend, blank);
449}
450
451int gr_get_surface(gr_surface* surface)
452{
453 GGLSurface* ms = (GGLSurface*)malloc(sizeof(GGLSurface));
454 if (!ms) return -1;
455
456 // Allocate the data
457 get_memory_surface(ms);
458 ms->data = (GGLubyte*)malloc(ms->stride * ms->height * gr_draw->pixel_bytes);
459
460 // Now, copy the data
461 memcpy(ms->data, gr_mem_surface.data, gr_draw->width * gr_draw->height * gr_draw->pixel_bytes / 8);
462
463 *surface = (gr_surface*) ms;
464 return 0;
465}
466
467int gr_free_surface(gr_surface surface)
468{
469 if (!surface)
470 return -1;
471
472 GGLSurface* ms = (GGLSurface*) surface;
473 free(ms->data);
474 free(ms);
475 return 0;
476}
477
478void gr_write_frame_to_file(int fd)
479{
480 write(fd, gr_mem_surface.data, gr_draw->width * gr_draw->height * gr_draw->pixel_bytes / 8);
481}