xref: /OK3568_Linux_fs/external/recovery/minui/graphics.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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