blob: d914eef3d2174e250ebffec080bf94beb9a4a4a4 [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"
38
39struct GRFont {
40 GRSurface* texture;
41 int cwidth;
42 int cheight;
43};
44
Ethan Yonkerfbb43532015-12-28 21:54:50 +010045static minui_backend* gr_backend = NULL;
46
47static int overscan_percent = OVERSCAN_PERCENT;
48static int overscan_offset_x = 0;
49static int overscan_offset_y = 0;
50
51static unsigned char gr_current_r = 255;
52static unsigned char gr_current_g = 255;
53static unsigned char gr_current_b = 255;
Ethan Yonkerfbb43532015-12-28 21:54:50 +010054
55GRSurface* gr_draw = NULL;
56
57static GGLContext *gr_context = 0;
58GGLSurface gr_mem_surface;
59static int gr_is_curr_clr_opaque = 0;
60
Ethan Yonkerfbb43532015-12-28 21:54:50 +010061int gr_textEx_scaleW(int x, int y, const char *s, void* pFont, int max_width, int placement, int scale)
62{
63 GGLContext *gl = gr_context;
64 void* vfont = pFont;
65 GRFont *font = (GRFont*) pFont;
Ethan Yonker58f21322018-08-24 11:17:36 -050066 int y_scale = 0, measured_width, measured_height, new_height;
Ethan Yonkerfbb43532015-12-28 21:54:50 +010067
68 if (!s || strlen(s) == 0 || !font)
69 return 0;
70
71 measured_height = gr_ttf_getMaxFontHeight(font);
72
73 if (scale) {
74 measured_width = gr_ttf_measureEx(s, vfont);
75 if (measured_width > max_width) {
76 // Adjust font size down until the text fits
77 void *new_font = gr_ttf_scaleFont(vfont, max_width, measured_width);
78 if (!new_font) {
79 printf("gr_textEx_scaleW new_font is NULL\n");
80 return 0;
81 }
82 measured_width = gr_ttf_measureEx(s, new_font);
83 // These next 2 lines adjust the y point based on the new font's height
84 new_height = gr_ttf_getMaxFontHeight(new_font);
85 y_scale = (measured_height - new_height) / 2;
86 vfont = new_font;
87 }
88 } else
89 measured_width = gr_ttf_measureEx(s, vfont);
90
91 int x_adj = measured_width;
92 if (measured_width > max_width)
93 x_adj = max_width;
94
95 if (placement != TOP_LEFT && placement != BOTTOM_LEFT && placement != TEXT_ONLY_RIGHT) {
96 if (placement == CENTER || placement == CENTER_X_ONLY)
97 x -= (x_adj / 2);
98 else
99 x -= x_adj;
100 }
101
102 if (placement != TOP_LEFT && placement != TOP_RIGHT) {
103 if (placement == CENTER || placement == TEXT_ONLY_RIGHT)
104 y -= (measured_height / 2);
105 else if (placement == BOTTOM_LEFT || placement == BOTTOM_RIGHT)
106 y -= measured_height;
107 }
108 return gr_ttf_textExWH(gl, x, y + y_scale, s, vfont, measured_width + x, -1);
109}
110
111void gr_clip(int x, int y, int w, int h)
112{
113 GGLContext *gl = gr_context;
114 gl->scissor(gl, x, y, w, h);
115 gl->enable(gl, GGL_SCISSOR_TEST);
116}
117
118void gr_noclip()
119{
120 GGLContext *gl = gr_context;
121 gl->scissor(gl, 0, 0, gr_fb_width(), gr_fb_height());
122 gl->disable(gl, GGL_SCISSOR_TEST);
123}
124
125void gr_line(int x0, int y0, int x1, int y1, int width)
126{
127 GGLContext *gl = gr_context;
128
129 if(gr_is_curr_clr_opaque)
130 gl->disable(gl, GGL_BLEND);
131
132 const int coords0[2] = { x0 << 4, y0 << 4 };
133 const int coords1[2] = { x1 << 4, y1 << 4 };
134 gl->linex(gl, coords0, coords1, width << 4);
135
136 if(gr_is_curr_clr_opaque)
137 gl->enable(gl, GGL_BLEND);
138}
139
140gr_surface gr_render_circle(int radius, unsigned char r, unsigned char g, unsigned char b, unsigned char a)
141{
142 int rx, ry;
143 GGLSurface *surface;
144 const int diameter = radius*2 + 1;
145 const int radius_check = radius*radius + radius*0.8;
146 const uint32_t px = (a << 24) | (b << 16) | (g << 8) | r;
147 uint32_t *data;
148
149 surface = (GGLSurface *)malloc(sizeof(GGLSurface));
150 memset(surface, 0, sizeof(GGLSurface));
151
152 data = (uint32_t *)malloc(diameter * diameter * 4);
153 memset(data, 0, diameter * diameter * 4);
154
155 surface->version = sizeof(surface);
156 surface->width = diameter;
157 surface->height = diameter;
158 surface->stride = diameter;
159 surface->data = (GGLubyte*)data;
notsyncing8e4e8ec2018-06-09 10:40:50 +0800160#if defined(RECOVERY_BGRA)
161 surface->format = GGL_PIXEL_FORMAT_BGRA_8888;
162#else
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100163 surface->format = GGL_PIXEL_FORMAT_RGBA_8888;
notsyncing8e4e8ec2018-06-09 10:40:50 +0800164#endif
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100165
166 for(ry = -radius; ry <= radius; ++ry)
167 for(rx = -radius; rx <= radius; ++rx)
168 if(rx*rx+ry*ry <= radius_check)
169 *(data + diameter*(radius + ry) + (radius+rx)) = px;
170
171 return (gr_surface)surface;
172}
173
174void gr_color(unsigned char r, unsigned char g, unsigned char b, unsigned char a)
175{
176 GGLContext *gl = gr_context;
177 GGLint color[4];
178#if defined(RECOVERY_ABGR) || defined(RECOVERY_BGRA)
179 color[0] = ((b << 8) | r) + 1;
180 color[1] = ((g << 8) | g) + 1;
181 color[2] = ((r << 8) | b) + 1;
182 color[3] = ((a << 8) | a) + 1;
183#else
184 color[0] = ((r << 8) | r) + 1;
185 color[1] = ((g << 8) | g) + 1;
186 color[2] = ((b << 8) | b) + 1;
187 color[3] = ((a << 8) | a) + 1;
188#endif
189 gl->color4xv(gl, color);
190
191 gr_is_curr_clr_opaque = (a == 255);
192}
193
194void gr_clear()
195{
196 if (gr_draw->pixel_bytes == 2) {
197 gr_fill(0, 0, gr_fb_width(), gr_fb_height());
198 return;
199 }
200
201 // This code only works on 32bpp devices
202 if (gr_current_r == gr_current_g && gr_current_r == gr_current_b) {
203 memset(gr_draw->data, gr_current_r, gr_draw->height * gr_draw->row_bytes);
204 } else {
205 unsigned char* px = gr_draw->data;
206 for (int y = 0; y < gr_draw->height; ++y) {
207 for (int x = 0; x < gr_draw->width; ++x) {
208 *px++ = gr_current_r;
209 *px++ = gr_current_g;
210 *px++ = gr_current_b;
211 px++;
212 }
213 px += gr_draw->row_bytes - (gr_draw->width * gr_draw->pixel_bytes);
214 }
215 }
216}
217
218void gr_fill(int x, int y, int w, int h)
219{
220 GGLContext *gl = gr_context;
221
222 if(gr_is_curr_clr_opaque)
223 gl->disable(gl, GGL_BLEND);
224
225 gl->recti(gl, x, y, x + w, y + h);
226
227 if(gr_is_curr_clr_opaque)
228 gl->enable(gl, GGL_BLEND);
229}
230
231void gr_blit(gr_surface source, int sx, int sy, int w, int h, int dx, int dy) {
232 if (gr_context == NULL) {
233 return;
234 }
235
236 GGLContext *gl = gr_context;
237 GGLSurface *surface = (GGLSurface*)source;
238
239 if(surface->format == GGL_PIXEL_FORMAT_RGBX_8888)
240 gl->disable(gl, GGL_BLEND);
241
242 gl->bindTexture(gl, surface);
243 gl->texEnvi(gl, GGL_TEXTURE_ENV, GGL_TEXTURE_ENV_MODE, GGL_REPLACE);
244 gl->texGeni(gl, GGL_S, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE);
245 gl->texGeni(gl, GGL_T, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE);
246 gl->enable(gl, GGL_TEXTURE_2D);
247 gl->texCoord2i(gl, sx - dx, sy - dy);
248 gl->recti(gl, dx, dy, dx + w, dy + h);
249 gl->disable(gl, GGL_TEXTURE_2D);
250
251 if(surface->format == GGL_PIXEL_FORMAT_RGBX_8888)
252 gl->enable(gl, GGL_BLEND);
253}
254
255unsigned int gr_get_width(gr_surface surface) {
256 if (surface == NULL) {
257 return 0;
258 }
259 return ((GGLSurface*) surface)->width;
260}
261
262unsigned int gr_get_height(gr_surface surface) {
263 if (surface == NULL) {
264 return 0;
265 }
266 return ((GGLSurface*) surface)->height;
267}
268
269void gr_flip() {
270 gr_draw = gr_backend->flip(gr_backend);
271 // On double buffered back ends, when we flip, we need to tell
272 // pixel flinger to draw to the other buffer
273 gr_mem_surface.data = (GGLubyte*)gr_draw->data;
274 gr_context->colorBuffer(gr_context, &gr_mem_surface);
275}
276
277static void get_memory_surface(GGLSurface* ms) {
278 ms->version = sizeof(*ms);
279 ms->width = gr_draw->width;
280 ms->height = gr_draw->height;
281 ms->stride = gr_draw->row_bytes / gr_draw->pixel_bytes;
282 ms->data = (GGLubyte*)gr_draw->data;
283 ms->format = gr_draw->format;
284}
285
286int gr_init(void)
287{
288 gr_draw = NULL;
289
Ethan Yonkerb386f712017-01-20 14:30:28 -0600290#ifdef MSM_BSP
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100291 gr_backend = open_overlay();
292 if (gr_backend) {
293 gr_draw = gr_backend->init(gr_backend);
294 if (!gr_draw) {
295 gr_backend->exit(gr_backend);
296 } else
297 printf("Using overlay graphics.\n");
298 }
Ethan Yonkerb386f712017-01-20 14:30:28 -0600299#endif
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100300
301#ifdef HAS_ADF
Ethan Yonkerb386f712017-01-20 14:30:28 -0600302 if (!gr_backend || !gr_draw) {
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100303 gr_backend = open_adf();
304 if (gr_backend) {
305 gr_draw = gr_backend->init(gr_backend);
306 if (!gr_draw) {
307 gr_backend->exit(gr_backend);
308 } else
309 printf("Using adf graphics.\n");
310 }
311 }
312#else
313#ifdef MSM_BSP
314 printf("Skipping adf graphics because TW_TARGET_USES_QCOM_BSP := true\n");
315#else
316 printf("Skipping adf graphics -- not present in build tree\n");
317#endif
318#endif
319
320#ifdef HAS_DRM
Ethan Yonkerb386f712017-01-20 14:30:28 -0600321 if (!gr_backend || !gr_draw) {
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100322 gr_backend = open_drm();
323 gr_draw = gr_backend->init(gr_backend);
324 if (gr_draw)
325 printf("Using drm graphics.\n");
326 }
327#else
328 printf("Skipping drm graphics -- not present in build tree\n");
329#endif
330
Ethan Yonkerb386f712017-01-20 14:30:28 -0600331 if (!gr_backend || !gr_draw) {
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100332 gr_backend = open_fbdev();
333 gr_draw = gr_backend->init(gr_backend);
334 if (gr_draw == NULL) {
335 return -1;
336 } else
337 printf("Using fbdev graphics.\n");
338 }
339
340 overscan_offset_x = gr_draw->width * overscan_percent / 100;
341 overscan_offset_y = gr_draw->height * overscan_percent / 100;
342
343 // Set up pixelflinger
344 get_memory_surface(&gr_mem_surface);
345 gglInit(&gr_context);
346 GGLContext *gl = gr_context;
347 gl->colorBuffer(gl, &gr_mem_surface);
348
349 gl->activeTexture(gl, 0);
350 gl->enable(gl, GGL_BLEND);
351 gl->blendFunc(gl, GGL_SRC_ALPHA, GGL_ONE_MINUS_SRC_ALPHA);
352
353 gr_flip();
354 gr_flip();
355
¨Paul80bd1652016-04-26 16:49:58 +0100356#ifdef TW_SCREEN_BLANK_ON_BOOT
357 printf("TW_SCREEN_BLANK_ON_BOOT := true\n");
358 gr_fb_blank(true);
359 gr_fb_blank(false);
360#endif
361
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100362 return 0;
363}
364
365void gr_exit(void)
366{
367 gr_backend->exit(gr_backend);
368}
369
370int gr_fb_width(void)
371{
372 return gr_draw->width - 2*overscan_offset_x;
373}
374
375int gr_fb_height(void)
376{
377 return gr_draw->height - 2*overscan_offset_y;
378}
379
380void gr_fb_blank(bool blank)
381{
382 gr_backend->blank(gr_backend, blank);
383}
384
385int gr_get_surface(gr_surface* surface)
386{
387 GGLSurface* ms = (GGLSurface*)malloc(sizeof(GGLSurface));
388 if (!ms) return -1;
389
390 // Allocate the data
391 get_memory_surface(ms);
392 ms->data = (GGLubyte*)malloc(ms->stride * ms->height * gr_draw->pixel_bytes);
393
394 // Now, copy the data
395 memcpy(ms->data, gr_mem_surface.data, gr_draw->width * gr_draw->height * gr_draw->pixel_bytes / 8);
396
397 *surface = (gr_surface*) ms;
398 return 0;
399}
400
401int gr_free_surface(gr_surface surface)
402{
403 if (!surface)
404 return -1;
405
406 GGLSurface* ms = (GGLSurface*) surface;
407 free(ms->data);
408 free(ms);
409 return 0;
410}
411
412void gr_write_frame_to_file(int fd)
413{
414 write(fd, gr_mem_surface.data, gr_draw->width * gr_draw->height * gr_draw->pixel_bytes / 8);
415}