blob: 81e77a630f72b8fca9b4c28bf2747606e11dc0c7 [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;
160 surface->format = GGL_PIXEL_FORMAT_RGBA_8888;
161
162 for(ry = -radius; ry <= radius; ++ry)
163 for(rx = -radius; rx <= radius; ++rx)
164 if(rx*rx+ry*ry <= radius_check)
165 *(data + diameter*(radius + ry) + (radius+rx)) = px;
166
167 return (gr_surface)surface;
168}
169
170void gr_color(unsigned char r, unsigned char g, unsigned char b, unsigned char a)
171{
172 GGLContext *gl = gr_context;
173 GGLint color[4];
174#if defined(RECOVERY_ABGR) || defined(RECOVERY_BGRA)
175 color[0] = ((b << 8) | r) + 1;
176 color[1] = ((g << 8) | g) + 1;
177 color[2] = ((r << 8) | b) + 1;
178 color[3] = ((a << 8) | a) + 1;
179#else
180 color[0] = ((r << 8) | r) + 1;
181 color[1] = ((g << 8) | g) + 1;
182 color[2] = ((b << 8) | b) + 1;
183 color[3] = ((a << 8) | a) + 1;
184#endif
185 gl->color4xv(gl, color);
186
187 gr_is_curr_clr_opaque = (a == 255);
188}
189
190void gr_clear()
191{
192 if (gr_draw->pixel_bytes == 2) {
193 gr_fill(0, 0, gr_fb_width(), gr_fb_height());
194 return;
195 }
196
197 // This code only works on 32bpp devices
198 if (gr_current_r == gr_current_g && gr_current_r == gr_current_b) {
199 memset(gr_draw->data, gr_current_r, gr_draw->height * gr_draw->row_bytes);
200 } else {
201 unsigned char* px = gr_draw->data;
202 for (int y = 0; y < gr_draw->height; ++y) {
203 for (int x = 0; x < gr_draw->width; ++x) {
204 *px++ = gr_current_r;
205 *px++ = gr_current_g;
206 *px++ = gr_current_b;
207 px++;
208 }
209 px += gr_draw->row_bytes - (gr_draw->width * gr_draw->pixel_bytes);
210 }
211 }
212}
213
214void gr_fill(int x, int y, int w, int h)
215{
216 GGLContext *gl = gr_context;
217
218 if(gr_is_curr_clr_opaque)
219 gl->disable(gl, GGL_BLEND);
220
221 gl->recti(gl, x, y, x + w, y + h);
222
223 if(gr_is_curr_clr_opaque)
224 gl->enable(gl, GGL_BLEND);
225}
226
227void gr_blit(gr_surface source, int sx, int sy, int w, int h, int dx, int dy) {
228 if (gr_context == NULL) {
229 return;
230 }
231
232 GGLContext *gl = gr_context;
233 GGLSurface *surface = (GGLSurface*)source;
234
235 if(surface->format == GGL_PIXEL_FORMAT_RGBX_8888)
236 gl->disable(gl, GGL_BLEND);
237
238 gl->bindTexture(gl, surface);
239 gl->texEnvi(gl, GGL_TEXTURE_ENV, GGL_TEXTURE_ENV_MODE, GGL_REPLACE);
240 gl->texGeni(gl, GGL_S, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE);
241 gl->texGeni(gl, GGL_T, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE);
242 gl->enable(gl, GGL_TEXTURE_2D);
243 gl->texCoord2i(gl, sx - dx, sy - dy);
244 gl->recti(gl, dx, dy, dx + w, dy + h);
245 gl->disable(gl, GGL_TEXTURE_2D);
246
247 if(surface->format == GGL_PIXEL_FORMAT_RGBX_8888)
248 gl->enable(gl, GGL_BLEND);
249}
250
251unsigned int gr_get_width(gr_surface surface) {
252 if (surface == NULL) {
253 return 0;
254 }
255 return ((GGLSurface*) surface)->width;
256}
257
258unsigned int gr_get_height(gr_surface surface) {
259 if (surface == NULL) {
260 return 0;
261 }
262 return ((GGLSurface*) surface)->height;
263}
264
265void gr_flip() {
266 gr_draw = gr_backend->flip(gr_backend);
267 // On double buffered back ends, when we flip, we need to tell
268 // pixel flinger to draw to the other buffer
269 gr_mem_surface.data = (GGLubyte*)gr_draw->data;
270 gr_context->colorBuffer(gr_context, &gr_mem_surface);
271}
272
273static void get_memory_surface(GGLSurface* ms) {
274 ms->version = sizeof(*ms);
275 ms->width = gr_draw->width;
276 ms->height = gr_draw->height;
277 ms->stride = gr_draw->row_bytes / gr_draw->pixel_bytes;
278 ms->data = (GGLubyte*)gr_draw->data;
279 ms->format = gr_draw->format;
280}
281
282int gr_init(void)
283{
284 gr_draw = NULL;
285
Ethan Yonkerb386f712017-01-20 14:30:28 -0600286#ifdef MSM_BSP
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100287 gr_backend = open_overlay();
288 if (gr_backend) {
289 gr_draw = gr_backend->init(gr_backend);
290 if (!gr_draw) {
291 gr_backend->exit(gr_backend);
292 } else
293 printf("Using overlay graphics.\n");
294 }
Ethan Yonkerb386f712017-01-20 14:30:28 -0600295#endif
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100296
297#ifdef HAS_ADF
Ethan Yonkerb386f712017-01-20 14:30:28 -0600298 if (!gr_backend || !gr_draw) {
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100299 gr_backend = open_adf();
300 if (gr_backend) {
301 gr_draw = gr_backend->init(gr_backend);
302 if (!gr_draw) {
303 gr_backend->exit(gr_backend);
304 } else
305 printf("Using adf graphics.\n");
306 }
307 }
308#else
309#ifdef MSM_BSP
310 printf("Skipping adf graphics because TW_TARGET_USES_QCOM_BSP := true\n");
311#else
312 printf("Skipping adf graphics -- not present in build tree\n");
313#endif
314#endif
315
316#ifdef HAS_DRM
Ethan Yonkerb386f712017-01-20 14:30:28 -0600317 if (!gr_backend || !gr_draw) {
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100318 gr_backend = open_drm();
319 gr_draw = gr_backend->init(gr_backend);
320 if (gr_draw)
321 printf("Using drm graphics.\n");
322 }
323#else
324 printf("Skipping drm graphics -- not present in build tree\n");
325#endif
326
Ethan Yonkerb386f712017-01-20 14:30:28 -0600327 if (!gr_backend || !gr_draw) {
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100328 gr_backend = open_fbdev();
329 gr_draw = gr_backend->init(gr_backend);
330 if (gr_draw == NULL) {
331 return -1;
332 } else
333 printf("Using fbdev graphics.\n");
334 }
335
336 overscan_offset_x = gr_draw->width * overscan_percent / 100;
337 overscan_offset_y = gr_draw->height * overscan_percent / 100;
338
339 // Set up pixelflinger
340 get_memory_surface(&gr_mem_surface);
341 gglInit(&gr_context);
342 GGLContext *gl = gr_context;
343 gl->colorBuffer(gl, &gr_mem_surface);
344
345 gl->activeTexture(gl, 0);
346 gl->enable(gl, GGL_BLEND);
347 gl->blendFunc(gl, GGL_SRC_ALPHA, GGL_ONE_MINUS_SRC_ALPHA);
348
349 gr_flip();
350 gr_flip();
351
¨Paul80bd1652016-04-26 16:49:58 +0100352#ifdef TW_SCREEN_BLANK_ON_BOOT
353 printf("TW_SCREEN_BLANK_ON_BOOT := true\n");
354 gr_fb_blank(true);
355 gr_fb_blank(false);
356#endif
357
Ethan Yonkerfbb43532015-12-28 21:54:50 +0100358 return 0;
359}
360
361void gr_exit(void)
362{
363 gr_backend->exit(gr_backend);
364}
365
366int gr_fb_width(void)
367{
368 return gr_draw->width - 2*overscan_offset_x;
369}
370
371int gr_fb_height(void)
372{
373 return gr_draw->height - 2*overscan_offset_y;
374}
375
376void gr_fb_blank(bool blank)
377{
378 gr_backend->blank(gr_backend, blank);
379}
380
381int gr_get_surface(gr_surface* surface)
382{
383 GGLSurface* ms = (GGLSurface*)malloc(sizeof(GGLSurface));
384 if (!ms) return -1;
385
386 // Allocate the data
387 get_memory_surface(ms);
388 ms->data = (GGLubyte*)malloc(ms->stride * ms->height * gr_draw->pixel_bytes);
389
390 // Now, copy the data
391 memcpy(ms->data, gr_mem_surface.data, gr_draw->width * gr_draw->height * gr_draw->pixel_bytes / 8);
392
393 *surface = (gr_surface*) ms;
394 return 0;
395}
396
397int gr_free_surface(gr_surface surface)
398{
399 if (!surface)
400 return -1;
401
402 GGLSurface* ms = (GGLSurface*) surface;
403 free(ms->data);
404 free(ms);
405 return 0;
406}
407
408void gr_write_frame_to_file(int fd)
409{
410 write(fd, gr_mem_surface.data, gr_draw->width * gr_draw->height * gr_draw->pixel_bytes / 8);
411}