Home | History | Annotate | Download | only in minui
      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 
     38 struct GRFont {
     39     GRSurface* texture;
     40     int cwidth;
     41     int cheight;
     42 };
     43 
     44 static GRFont* gr_font = NULL;
     45 static minui_backend* gr_backend = NULL;
     46 
     47 static int overscan_percent = OVERSCAN_PERCENT;
     48 static int overscan_offset_x = 0;
     49 static int overscan_offset_y = 0;
     50 
     51 static unsigned char gr_current_r = 255;
     52 static unsigned char gr_current_g = 255;
     53 static unsigned char gr_current_b = 255;
     54 static unsigned char gr_current_a = 255;
     55 
     56 static GRSurface* gr_draw = NULL;
     57 
     58 static bool outside(int x, int y)
     59 {
     60     return x < 0 || x >= gr_draw->width || y < 0 || y >= gr_draw->height;
     61 }
     62 
     63 int gr_measure(const char *s)
     64 {
     65     return gr_font->cwidth * strlen(s);
     66 }
     67 
     68 void gr_font_size(int *x, int *y)
     69 {
     70     *x = gr_font->cwidth;
     71     *y = gr_font->cheight;
     72 }
     73 
     74 static void text_blend(unsigned char* src_p, int src_row_bytes,
     75                        unsigned char* dst_p, int dst_row_bytes,
     76                        int width, int height)
     77 {
     78     for (int j = 0; j < height; ++j) {
     79         unsigned char* sx = src_p;
     80         unsigned char* px = dst_p;
     81         for (int i = 0; i < width; ++i) {
     82             unsigned char a = *sx++;
     83             if (gr_current_a < 255) a = ((int)a * gr_current_a) / 255;
     84             if (a == 255) {
     85                 *px++ = gr_current_r;
     86                 *px++ = gr_current_g;
     87                 *px++ = gr_current_b;
     88                 px++;
     89             } else if (a > 0) {
     90                 *px = (*px * (255-a) + gr_current_r * a) / 255;
     91                 ++px;
     92                 *px = (*px * (255-a) + gr_current_g * a) / 255;
     93                 ++px;
     94                 *px = (*px * (255-a) + gr_current_b * a) / 255;
     95                 ++px;
     96                 ++px;
     97             } else {
     98                 px += 4;
     99             }
    100         }
    101         src_p += src_row_bytes;
    102         dst_p += dst_row_bytes;
    103     }
    104 }
    105 
    106 void gr_text(int x, int y, const char *s, bool bold)
    107 {
    108     GRFont* font = gr_font;
    109 
    110     if (!font->texture || gr_current_a == 0) return;
    111 
    112     bold = bold && (font->texture->height != font->cheight);
    113 
    114     x += overscan_offset_x;
    115     y += overscan_offset_y;
    116 
    117     unsigned char ch;
    118     while ((ch = *s++)) {
    119         if (outside(x, y) || outside(x+font->cwidth-1, y+font->cheight-1)) break;
    120 
    121         if (ch < ' ' || ch > '~') {
    122             ch = '?';
    123         }
    124 
    125         unsigned char* src_p = font->texture->data + ((ch - ' ') * font->cwidth) +
    126                                (bold ? font->cheight * font->texture->row_bytes : 0);
    127         unsigned char* dst_p = gr_draw->data + y*gr_draw->row_bytes + x*gr_draw->pixel_bytes;
    128 
    129         text_blend(src_p, font->texture->row_bytes,
    130                    dst_p, gr_draw->row_bytes,
    131                    font->cwidth, font->cheight);
    132 
    133         x += font->cwidth;
    134     }
    135 }
    136 
    137 void gr_texticon(int x, int y, GRSurface* icon) {
    138     if (icon == NULL) return;
    139 
    140     if (icon->pixel_bytes != 1) {
    141         printf("gr_texticon: source has wrong format\n");
    142         return;
    143     }
    144 
    145     x += overscan_offset_x;
    146     y += overscan_offset_y;
    147 
    148     if (outside(x, y) || outside(x+icon->width-1, y+icon->height-1)) return;
    149 
    150     unsigned char* src_p = icon->data;
    151     unsigned char* dst_p = gr_draw->data + y*gr_draw->row_bytes + x*gr_draw->pixel_bytes;
    152 
    153     text_blend(src_p, icon->row_bytes,
    154                dst_p, gr_draw->row_bytes,
    155                icon->width, icon->height);
    156 }
    157 
    158 void gr_color(unsigned char r, unsigned char g, unsigned char b, unsigned char a)
    159 {
    160 #if defined(RECOVERY_ABGR) || defined(RECOVERY_BGRA)
    161     gr_current_r = b;
    162     gr_current_g = g;
    163     gr_current_b = r;
    164     gr_current_a = a;
    165 #else
    166     gr_current_r = r;
    167     gr_current_g = g;
    168     gr_current_b = b;
    169     gr_current_a = a;
    170 #endif
    171 }
    172 
    173 void gr_clear()
    174 {
    175     if (gr_current_r == gr_current_g && gr_current_r == gr_current_b) {
    176         memset(gr_draw->data, gr_current_r, gr_draw->height * gr_draw->row_bytes);
    177     } else {
    178         unsigned char* px = gr_draw->data;
    179         for (int y = 0; y < gr_draw->height; ++y) {
    180             for (int x = 0; x < gr_draw->width; ++x) {
    181                 *px++ = gr_current_r;
    182                 *px++ = gr_current_g;
    183                 *px++ = gr_current_b;
    184                 px++;
    185             }
    186             px += gr_draw->row_bytes - (gr_draw->width * gr_draw->pixel_bytes);
    187         }
    188     }
    189 }
    190 
    191 void gr_fill(int x1, int y1, int x2, int y2)
    192 {
    193     x1 += overscan_offset_x;
    194     y1 += overscan_offset_y;
    195 
    196     x2 += overscan_offset_x;
    197     y2 += overscan_offset_y;
    198 
    199     if (outside(x1, y1) || outside(x2-1, y2-1)) return;
    200 
    201     unsigned char* p = gr_draw->data + y1 * gr_draw->row_bytes + x1 * gr_draw->pixel_bytes;
    202     if (gr_current_a == 255) {
    203         int x, y;
    204         for (y = y1; y < y2; ++y) {
    205             unsigned char* px = p;
    206             for (x = x1; x < x2; ++x) {
    207                 *px++ = gr_current_r;
    208                 *px++ = gr_current_g;
    209                 *px++ = gr_current_b;
    210                 px++;
    211             }
    212             p += gr_draw->row_bytes;
    213         }
    214     } else if (gr_current_a > 0) {
    215         int x, y;
    216         for (y = y1; y < y2; ++y) {
    217             unsigned char* px = p;
    218             for (x = x1; x < x2; ++x) {
    219                 *px = (*px * (255-gr_current_a) + gr_current_r * gr_current_a) / 255;
    220                 ++px;
    221                 *px = (*px * (255-gr_current_a) + gr_current_g * gr_current_a) / 255;
    222                 ++px;
    223                 *px = (*px * (255-gr_current_a) + gr_current_b * gr_current_a) / 255;
    224                 ++px;
    225                 ++px;
    226             }
    227             p += gr_draw->row_bytes;
    228         }
    229     }
    230 }
    231 
    232 void gr_blit(GRSurface* source, int sx, int sy, int w, int h, int dx, int dy) {
    233     if (source == NULL) return;
    234 
    235     if (gr_draw->pixel_bytes != source->pixel_bytes) {
    236         printf("gr_blit: source has wrong format\n");
    237         return;
    238     }
    239 
    240     dx += overscan_offset_x;
    241     dy += overscan_offset_y;
    242 
    243     if (outside(dx, dy) || outside(dx+w-1, dy+h-1)) return;
    244 
    245     unsigned char* src_p = source->data + sy*source->row_bytes + sx*source->pixel_bytes;
    246     unsigned char* dst_p = gr_draw->data + dy*gr_draw->row_bytes + dx*gr_draw->pixel_bytes;
    247 
    248     int i;
    249     for (i = 0; i < h; ++i) {
    250         memcpy(dst_p, src_p, w * source->pixel_bytes);
    251         src_p += source->row_bytes;
    252         dst_p += gr_draw->row_bytes;
    253     }
    254 }
    255 
    256 unsigned int gr_get_width(GRSurface* surface) {
    257     if (surface == NULL) {
    258         return 0;
    259     }
    260     return surface->width;
    261 }
    262 
    263 unsigned int gr_get_height(GRSurface* surface) {
    264     if (surface == NULL) {
    265         return 0;
    266     }
    267     return surface->height;
    268 }
    269 
    270 static void gr_init_font(void)
    271 {
    272     gr_font = reinterpret_cast<GRFont*>(calloc(sizeof(*gr_font), 1));
    273 
    274     int res = res_create_alpha_surface("font", &(gr_font->texture));
    275     if (res == 0) {
    276         // The font image should be a 96x2 array of character images.  The
    277         // columns are the printable ASCII characters 0x20 - 0x7f.  The
    278         // top row is regular text; the bottom row is bold.
    279         gr_font->cwidth = gr_font->texture->width / 96;
    280         gr_font->cheight = gr_font->texture->height / 2;
    281     } else {
    282         printf("failed to read font: res=%d\n", res);
    283 
    284         // fall back to the compiled-in font.
    285         gr_font->texture = reinterpret_cast<GRSurface*>(malloc(sizeof(*gr_font->texture)));
    286         gr_font->texture->width = font.width;
    287         gr_font->texture->height = font.height;
    288         gr_font->texture->row_bytes = font.width;
    289         gr_font->texture->pixel_bytes = 1;
    290 
    291         unsigned char* bits = reinterpret_cast<unsigned char*>(malloc(font.width * font.height));
    292         gr_font->texture->data = reinterpret_cast<unsigned char*>(bits);
    293 
    294         unsigned char data;
    295         unsigned char* in = font.rundata;
    296         while((data = *in++)) {
    297             memset(bits, (data & 0x80) ? 255 : 0, data & 0x7f);
    298             bits += (data & 0x7f);
    299         }
    300 
    301         gr_font->cwidth = font.cwidth;
    302         gr_font->cheight = font.cheight;
    303     }
    304 }
    305 
    306 #if 0
    307 // Exercises many of the gr_*() functions; useful for testing.
    308 static void gr_test() {
    309     GRSurface** images;
    310     int frames;
    311     int result = res_create_multi_surface("icon_installing", &frames, &images);
    312     if (result < 0) {
    313         printf("create surface %d\n", result);
    314         gr_exit();
    315         return;
    316     }
    317 
    318     time_t start = time(NULL);
    319     int x;
    320     for (x = 0; x <= 1200; ++x) {
    321         if (x < 400) {
    322             gr_color(0, 0, 0, 255);
    323         } else {
    324             gr_color(0, (x-400)%128, 0, 255);
    325         }
    326         gr_clear();
    327 
    328         gr_color(255, 0, 0, 255);
    329         GRSurface* frame = images[x%frames];
    330         gr_blit(frame, 0, 0, frame->width, frame->height, x, 0);
    331 
    332         gr_color(255, 0, 0, 128);
    333         gr_fill(400, 150, 600, 350);
    334 
    335         gr_color(255, 255, 255, 255);
    336         gr_text(500, 225, "hello, world!", 0);
    337         gr_color(255, 255, 0, 128);
    338         gr_text(300+x, 275, "pack my box with five dozen liquor jugs", 1);
    339 
    340         gr_color(0, 0, 255, 128);
    341         gr_fill(gr_draw->width - 200 - x, 300, gr_draw->width - x, 500);
    342 
    343         gr_draw = gr_backend->flip(gr_backend);
    344     }
    345     printf("getting end time\n");
    346     time_t end = time(NULL);
    347     printf("got end time\n");
    348     printf("start %ld end %ld\n", (long)start, (long)end);
    349     if (end > start) {
    350         printf("%.2f fps\n", ((double)x) / (end-start));
    351     }
    352 }
    353 #endif
    354 
    355 void gr_flip() {
    356     gr_draw = gr_backend->flip(gr_backend);
    357 }
    358 
    359 int gr_init(void)
    360 {
    361     gr_init_font();
    362 
    363     gr_backend = open_adf();
    364     if (gr_backend) {
    365         gr_draw = gr_backend->init(gr_backend);
    366         if (!gr_draw) {
    367             gr_backend->exit(gr_backend);
    368         }
    369     }
    370 
    371     if (!gr_draw) {
    372         gr_backend = open_drm();
    373         gr_draw = gr_backend->init(gr_backend);
    374     }
    375 
    376     if (!gr_draw) {
    377         gr_backend = open_fbdev();
    378         gr_draw = gr_backend->init(gr_backend);
    379         if (gr_draw == NULL) {
    380             return -1;
    381         }
    382     }
    383 
    384     overscan_offset_x = gr_draw->width * overscan_percent / 100;
    385     overscan_offset_y = gr_draw->height * overscan_percent / 100;
    386 
    387     gr_flip();
    388     gr_flip();
    389 
    390     return 0;
    391 }
    392 
    393 void gr_exit(void)
    394 {
    395     gr_backend->exit(gr_backend);
    396 }
    397 
    398 int gr_fb_width(void)
    399 {
    400     return gr_draw->width - 2*overscan_offset_x;
    401 }
    402 
    403 int gr_fb_height(void)
    404 {
    405     return gr_draw->height - 2*overscan_offset_y;
    406 }
    407 
    408 void gr_fb_blank(bool blank)
    409 {
    410     gr_backend->blank(gr_backend, blank);
    411 }
    412