1 /*
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 "font_10x18.h"
35 #include "minui.h"
36 #include "graphics.h"
37 #include "common.h"
38
39 typedef struct {
40 GRSurface* texture;
41 int char_width;
42 int char_height;
43 } GRFont;
44
45 extern minui_backend* open_drm();
46
47 static GRFont* gr_font = NULL;
48 static minui_backend* gr_backend = NULL;
49
50 static int overscan_percent = 0;
51 static int overscan_offset_x = 0;
52 static int overscan_offset_y = 0;
53
54 static int gr_vt_fd = -1;
55
56 static uint32_t gr_current = ~0;
57 static uint32_t alpha_mask = 0xff000000;
58 static GRRotation rotation = ROTATION_NONE;
59
60 GRSurface* gr_draw = NULL;
61 extern minui_backend* open_drm();
62
63 //static bool outside(int x, int y)
64 //{
65 // return x < 0 || x >= gr_draw->width || y < 0 || y >= gr_draw->height;
66 //}
67
outside(int x,int y)68 static bool outside(int x, int y)
69 {
70 return x < 0 || x >= (rotation % 2 ? gr_draw->height : gr_draw->width) || y < 0 ||
71 y >= (rotation % 2 ? gr_draw->width : gr_draw->height);
72 }
73
gr_measure(const char * s)74 int gr_measure(const char *s)
75 {
76 return gr_font->char_width * strlen(s);
77 }
78
gr_font_size(int * x,int * y)79 void gr_font_size(int *x, int *y)
80 {
81 *x = gr_font->char_width;
82 *y = gr_font->char_height;
83 }
84
85 // Blends gr_current onto pix value, assumes alpha as most significant byte.
pixel_blend(uint8_t alpha,uint32_t pix)86 static inline uint32_t pixel_blend(uint8_t alpha, uint32_t pix)
87 {
88 if (alpha == 255) return gr_current;
89 if (alpha == 0) return pix;
90 uint32_t pix_r = pix & 0xff;
91 uint32_t pix_g = pix & 0xff00;
92 uint32_t pix_b = pix & 0xff0000;
93 uint32_t cur_r = gr_current & 0xff;
94 uint32_t cur_g = gr_current & 0xff00;
95 uint32_t cur_b = gr_current & 0xff0000;
96
97 uint32_t out_r = (pix_r * (255 - alpha) + cur_r * alpha) / 255;
98 uint32_t out_g = (pix_g * (255 - alpha) + cur_g * alpha) / 255;
99 uint32_t out_b = (pix_b * (255 - alpha) + cur_b * alpha) / 255;
100
101 return (out_r & 0xff) | (out_g & 0xff00) | (out_b & 0xff0000) | (gr_current & 0xff000000);
102 }
103
104 // increments pixel pointer right, with current rotation.
incr_x(uint32_t ** p,int row_pixels)105 static void incr_x(uint32_t** p, int row_pixels)
106 {
107 if (rotation % 2) {
108 *p = *p + (rotation == 1 ? 1 : -1) * row_pixels;
109 } else {
110 *p = *p + (rotation ? -1 : 1);
111 }
112 }
113
114 // increments pixel pointer down, with current rotation.
incr_y(uint32_t ** p,int row_pixels)115 static void incr_y(uint32_t** p, int row_pixels)
116 {
117 if (rotation % 2) {
118 *p = *p + (rotation == 1 ? -1 : 1);
119 } else {
120 *p = *p + (rotation ? -1 : 1) * row_pixels;
121 }
122 }
123
124 // returns pixel pointer at given coordinates with rotation adjustment.
pixel_at(GRSurface * surf,int x,int y,int row_pixels)125 static uint32_t* pixel_at(GRSurface* surf, int x, int y, int row_pixels)
126 {
127 switch (rotation) {
128 case ROTATION_NONE:
129 return (uint32_t*)(surf->data) + y * row_pixels + x;
130 case ROTATION_RIGHT:
131 return (uint32_t*)(surf->data) + x * row_pixels + (surf->width - y);
132 case ROTATION_DOWN:
133 return (uint32_t*)(surf->data) + (surf->height - 1 - y) * row_pixels +
134 (surf->width - 1 - x);
135 case ROTATION_LEFT:
136 return (uint32_t*)(surf->data) + (surf->height - 1 - x) * row_pixels + y;
137 default:
138 LOGW("invalid rotation %d", rotation);
139 }
140 return NULL;
141 }
142
143
text_blend(unsigned char * src_p,int src_row_bytes,uint32_t * dst_p,int dst_row_pixels,int width,int height)144 static void text_blend(unsigned char* src_p, int src_row_bytes,
145 uint32_t* dst_p, int dst_row_pixels,
146 int width, int height)
147 {
148 uint8_t alpha_current = (uint8_t)((alpha_mask & gr_current) >> 24);
149 for (int j = 0; j < height; ++j) {
150 uint8_t* sx = src_p;
151 uint32_t* px = dst_p;
152 for (int i = 0; i < width; ++i, incr_x(&px, dst_row_pixels)) {
153 uint8_t a = *sx++;
154 if (alpha_current < 255) a = ((uint32_t)(a) * alpha_current) / 255;
155 *px = pixel_blend(a, *px);
156 }
157 src_p += src_row_bytes;
158 incr_y(&dst_p, dst_row_pixels);
159 }
160 }
161
162
gr_text(int x,int y,const char * s)163 void gr_text(int x, int y, const char *s)
164 {
165 GRFont *font = gr_font;
166 unsigned off;
167 int bold = 0;
168
169 if (!font || !font->texture || (gr_current & alpha_mask) == 0) return;
170
171 if (font->texture->pixel_bytes != 1) {
172 LOGW("gr_text: font has wrong format\n");
173 return;
174 }
175
176 bold = bold && (font->texture->height != font->char_height);
177
178 x += overscan_offset_x;
179 y += overscan_offset_y;
180
181 unsigned char ch;
182 while ((ch = *s++)) {
183 if (outside(x, y) || outside(x + font->char_width - 1, y + font->char_height - 1)) break;
184
185 if (ch < ' ' || ch > '~') {
186 ch = '?';
187 }
188
189 if (!gr_draw) return;
190 int row_pixels = gr_draw->row_bytes / gr_draw->pixel_bytes;
191 uint8_t* src_p = font->texture->data + ((ch - ' ') * font->char_width) +
192 (bold ? font->char_height * font->texture->row_bytes : 0);
193 uint32_t* dst_p = pixel_at(gr_draw, x, y, row_pixels);
194
195 text_blend(src_p, font->texture->row_bytes, dst_p, row_pixels, font->char_width,
196 font->char_height);
197 x += font->char_width;
198 }
199 }
200
gr_texticon(int x,int y,GRSurface * icon)201 void gr_texticon(int x, int y, GRSurface* icon)
202 {
203 if (icon == NULL) return;
204
205 if (icon->pixel_bytes != 1) {
206 LOGW("gr_texticon: source has wrong format\n");
207 return;
208 }
209
210 x += overscan_offset_x;
211 y += overscan_offset_y;
212
213 if (outside(x, y) || outside(x + icon->width - 1, y + icon->height - 1)) return;
214
215 if (!gr_draw) return;
216 int row_pixels = gr_draw->row_bytes / gr_draw->pixel_bytes;
217 uint8_t* src_p = icon->data;
218 uint32_t* dst_p = pixel_at(gr_draw, x, y, row_pixels);
219
220 text_blend(src_p, icon->row_bytes, dst_p, row_pixels, icon->width, icon->height);
221 }
222
gr_color(unsigned char r,unsigned char g,unsigned char b,unsigned char a)223 void gr_color(unsigned char r, unsigned char g, unsigned char b, unsigned char a)
224 {
225 uint32_t r32 = r, g32 = g, b32 = b, a32 = a;
226 #if defined(RECOVERY_ABGR) || defined(RECOVERY_BGRA)
227 gr_current = (a32 << 24) | (r32 << 16) | (g32 << 8) | b32;
228 #else
229 gr_current = (a32 << 24) | (b32 << 16) | (g32 << 8) | r32;
230 #endif
231
232 }
233
gr_clear()234 void gr_clear()
235 {
236 if (!gr_draw) return;
237 if ((gr_current & 0xff) == ((gr_current >> 8) & 0xff) &&
238 (gr_current & 0xff) == ((gr_current >> 16) & 0xff) &&
239 (gr_current & 0xff) == ((gr_current >> 24) & 0xff) &&
240 gr_draw->row_bytes == gr_draw->width * gr_draw->pixel_bytes) {
241 memset(gr_draw->data, gr_current & 0xff, gr_draw->height * gr_draw->row_bytes);
242 } else {
243 int x, y;
244 uint32_t* px = (uint32_t*)(gr_draw->data);
245 int row_diff = gr_draw->row_bytes / gr_draw->pixel_bytes - gr_draw->width;
246 for (y = 0; y < gr_draw->height; ++y) {
247 for (x = 0; x < gr_draw->width; ++x) {
248 *px++ = gr_current;
249 }
250 px += row_diff;
251 }
252 }
253 }
254
gr_fill(int x1,int y1,int x2,int y2)255 void gr_fill(int x1, int y1, int x2, int y2)
256 {
257 if (!gr_draw)
258 return;
259
260 x1 += overscan_offset_x;
261 y1 += overscan_offset_y;
262
263 x2 += overscan_offset_x;
264 y2 += overscan_offset_y;
265
266 if (outside(x1, y1) || outside(x2 - 1, y2 - 1)) return;
267
268 int row_pixels = gr_draw->row_bytes / gr_draw->pixel_bytes;
269 uint32_t* p = pixel_at(gr_draw, x1, y1, row_pixels);
270 uint8_t alpha = (uint8_t)(((gr_current & alpha_mask) >> 24));
271 if (alpha > 0) {
272 for (int y = y1; y < y2; ++y) {
273 uint32_t* px = p;
274 for (int x = x1; x < x2 - 1; ++x) {
275 *px = pixel_blend(alpha, *px);
276 //printf("%d \n", x);
277 incr_x(&px, row_pixels);
278 //printf("%d ", x);
279 }
280 incr_y(&p, row_pixels);
281 //printf("%d \n", y);
282 }
283 }
284 }
285
gr_blit(gr_surface source,int sx,int sy,int w,int h,int dx,int dy)286 void gr_blit(gr_surface source, int sx, int sy, int w, int h, int dx, int dy)
287 {
288 if (source == NULL) return;
289
290 if (gr_draw->pixel_bytes != source->pixel_bytes) {
291 LOGW("gr_blit: source has wrong format\n");
292 return;
293 }
294
295 dx += overscan_offset_x;
296 dy += overscan_offset_y;
297
298 if (outside(dx, dy) || outside(dx + w - 1, dy + h - 1)) return;
299
300 if (rotation) {
301 int src_row_pixels = source->row_bytes / source->pixel_bytes;
302 int row_pixels = gr_draw->row_bytes / gr_draw->pixel_bytes;
303 uint32_t* src_py = (uint32_t*)(source->data) + sy * source->row_bytes / 4 + sx;
304 uint32_t* dst_py = pixel_at(gr_draw, dx, dy, row_pixels);
305
306 for (int y = 0; y < h; y += 1) {
307 uint32_t* src_px = src_py;
308 uint32_t* dst_px = dst_py;
309 for (int x = 0; x < w; x += 1) {
310 *dst_px = *src_px++;
311 incr_x(&dst_px, row_pixels);
312 }
313 src_py += src_row_pixels;
314 incr_y(&dst_py, row_pixels);
315 }
316 } else {
317 unsigned char* src_p = source->data + sy * source->row_bytes + sx * source->pixel_bytes;
318 unsigned char* dst_p = gr_draw->data + dy * gr_draw->row_bytes + dx * gr_draw->pixel_bytes;
319
320 int i;
321 for (i = 0; i < h; ++i) {
322 memcpy(dst_p, src_p, w * source->pixel_bytes);
323 src_p += source->row_bytes;
324 dst_p += gr_draw->row_bytes;
325 }
326 }
327 }
328
gr_get_width(GRSurface * surface)329 unsigned int gr_get_width(GRSurface* surface)
330 {
331 if (surface == NULL) {
332 return 0;
333 }
334 return surface->width;
335 }
336
gr_get_height(GRSurface * surface)337 unsigned int gr_get_height(GRSurface* surface)
338 {
339 if (surface == NULL) {
340 return 0;
341 }
342 return surface->height;
343 }
344
gr_init_font(void)345 static void gr_init_font(void)
346 {
347 gr_font = calloc(sizeof(*gr_font), 1);
348
349 int res = res_create_alpha_surface("font", &(gr_font->texture));
350 if (res == 0) {
351 // The font image should be a 96x2 array of character images. The
352 // columns are the printable ASCII characters 0x20 - 0x7f. The
353 // top row is regular text; the bottom row is bold.
354 gr_font->char_width = gr_font->texture->width / 96;
355 gr_font->char_height = gr_font->texture->height / 2;
356 } else {
357 LOGI("failed to read font: res=%d, fall back to the compiled-in font\n", res);
358
359 // fall back to the compiled-in font.
360 gr_font->texture = malloc(sizeof(*gr_font->texture));
361 gr_font->texture->width = font.width;
362 gr_font->texture->height = font.height;
363 gr_font->texture->row_bytes = font.width;
364 gr_font->texture->pixel_bytes = 1;
365
366 unsigned char* bits = malloc(font.width * font.height);
367 gr_font->texture->data = (void*) bits;
368
369 unsigned char data;
370 unsigned char* in = font.rundata;
371 while ((data = *in++)) {
372 memset(bits, (data & 0x80) ? 255 : 0, data & 0x7f);
373 bits += (data & 0x7f);
374 }
375
376 gr_font->char_width = font.char_width;
377 gr_font->char_height = font.char_height;
378 }
379 }
380
381 #if 0
382 // Exercises many of the gr_*() functions; useful for testing.
383 static void gr_test()
384 {
385 GRSurface** images;
386 int frames;
387 int result = res_create_multi_surface("icon_installing", &frames, &images);
388 if (result < 0) {
389 printf("create surface %d\n", result);
390 gr_exit();
391 return;
392 }
393
394 time_t start = time(NULL);
395 int x;
396 for (x = 0; x <= 1200; ++x) {
397 if (x < 400) {
398 gr_color(0, 0, 0, 255);
399 } else {
400 gr_color(0, (x - 400) % 128, 0, 255);
401 }
402 gr_clear();
403
404 gr_color(255, 0, 0, 255);
405 gr_surface frame = images[x % frames];
406 gr_blit(frame, 0, 0, frame->width, frame->height, x, 0);
407
408 gr_color(255, 0, 0, 128);
409 gr_fill(400, 150, 600, 350);
410
411 gr_color(255, 255, 255, 255);
412 gr_text(500, 225, "hello, world!", 0);
413 gr_color(255, 255, 0, 128);
414 gr_text(300 + x, 275, "pack my box with five dozen liquor jugs", 1);
415
416 gr_color(0, 0, 255, 128);
417 gr_fill(gr_draw->width - 200 - x, 300, gr_draw->width - x, 500);
418
419 gr_draw = gr_backend->flip(gr_backend);
420 }
421 printf("getting end time\n");
422 time_t end = time(NULL);
423 printf("got end time\n");
424 printf("start %ld end %ld\n", (long)start, (long)end);
425 if (end > start) {
426 printf("%.2f fps\n", ((double)x) / (end - start));
427 }
428 }
429 #endif
430
gr_flip()431 void gr_flip()
432 {
433 if (!gr_draw) return;
434 gr_draw = gr_backend->flip(gr_backend);
435 }
436
437 static int set_user_rotate = ROTATION_NONE;
gr_set_rotate(int val)438 void gr_set_rotate(int val)
439 {
440 if ((val <= ROTATION_LEFT) && (val >= ROTATION_NONE))
441 set_user_rotate = val;
442 else
443 set_user_rotate = ROTATION_NONE;
444 }
445
gr_init(void)446 int gr_init(void)
447 {
448 gr_init_font();
449
450 gr_vt_fd = open("/dev/tty0", O_RDWR | O_SYNC);
451 if (gr_vt_fd < 0) {
452 // This is non-fatal; post-Cupcake kernels don't have tty0.
453 perror("can't open /dev/tty0");
454 } else if (ioctl(gr_vt_fd, KDSETMODE, (void*) KD_GRAPHICS)) {
455 // However, if we do open tty0, we expect the ioctl to work.
456 perror("failed KDSETMODE to KD_GRAPHICS on tty0");
457 gr_exit();
458 return -1;
459 }
460
461 gr_backend = 0;//open_adf();
462 if (gr_backend) {
463 gr_draw = gr_backend->init(gr_backend);
464 if (!gr_draw) {
465 gr_backend->exit(gr_backend);
466 }
467 }
468
469 if (!gr_draw) {
470 gr_backend = open_drm();
471 gr_draw = gr_backend->init(gr_backend);
472 if (gr_draw == NULL)
473 return -1;
474 }
475 #if 0
476 if (!gr_draw) {
477 gr_backend = open_fbdev();
478 gr_draw = gr_backend->init(gr_backend);
479 if (gr_draw == NULL) {
480 return -1;
481 }
482 }
483 #endif
484
485 overscan_offset_x = gr_draw->width * overscan_percent / 100;
486 overscan_offset_y = gr_draw->height * overscan_percent / 100;
487
488 gr_flip();
489
490 gr_rotate(set_user_rotate);
491 return 0;
492 }
493
gr_exit(void)494 void gr_exit(void)
495 {
496 gr_backend->exit(gr_backend);
497
498 ioctl(gr_vt_fd, KDSETMODE, (void*) KD_TEXT);
499 close(gr_vt_fd);
500 gr_vt_fd = -1;
501 }
502
503 //int gr_fb_width(void)
504 //{
505 // return gr_draw->width - 2*overscan_offset_x;
506 //}
507 //
508 //int gr_fb_height(void)
509 //{
510 // return gr_draw->height - 2*overscan_offset_y;
511 //}
512
gr_fb_width()513 int gr_fb_width()
514 {
515 if (!gr_draw)
516 return 0;
517 return rotation % 2 ? gr_draw->height - 2 * overscan_offset_y
518 : gr_draw->width - 2 * overscan_offset_x;
519 }
520
gr_fb_height()521 int gr_fb_height()
522 {
523 if (!gr_draw)
524 return 0;
525 return rotation % 2 ? gr_draw->width - 2 * overscan_offset_x
526 : gr_draw->height - 2 * overscan_offset_y;
527 }
528
gr_fb_blank(bool blank)529 void gr_fb_blank(bool blank)
530 {
531 gr_backend->blank(gr_backend, blank);
532 }
533
gr_rotate(GRRotation rot)534 void gr_rotate(GRRotation rot)
535 {
536 rotation = rot;
537 LOGI("rotate degree: 0 - none, 1 - right, 2 - down, 3 - left.\n");
538 LOGI("current rotate degree is : %d\n", rot);
539 }
540
541