graphics_overlay.cpp is based on a mix of AOSP code for graphics_fbdev.cpp along with overlay graphics code from CAF that was brought into earlier versions of TWRP and then further adapted and improved before being brought into this patch set. Also added a rule to build a minuitest binary for testing the minui engine to ensure proper operation on a device. Change-Id: I3972d3a6baa7002615319421ac07d9299c3cec69
543 lines
15 KiB
C++
543 lines
15 KiB
C++
/*
|
|
* Copyright (C) 2007 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include <stdbool.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#include <fcntl.h>
|
|
#include <stdio.h>
|
|
|
|
#include <sys/ioctl.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/types.h>
|
|
|
|
#include <linux/fb.h>
|
|
#include <linux/kd.h>
|
|
|
|
#include <time.h>
|
|
|
|
#include "font_10x18.h"
|
|
#include "minui.h"
|
|
#include "graphics.h"
|
|
|
|
struct GRFont {
|
|
GRSurface* texture;
|
|
int cwidth;
|
|
int cheight;
|
|
};
|
|
|
|
static GRFont* gr_font = NULL;
|
|
static minui_backend* gr_backend = NULL;
|
|
|
|
static int overscan_percent = OVERSCAN_PERCENT;
|
|
static int overscan_offset_x = 0;
|
|
static int overscan_offset_y = 0;
|
|
|
|
static unsigned char gr_current_r = 255;
|
|
static unsigned char gr_current_g = 255;
|
|
static unsigned char gr_current_b = 255;
|
|
static unsigned char gr_current_a = 255;
|
|
static unsigned char rgb_555[2];
|
|
static unsigned char gr_current_r5 = 31;
|
|
static unsigned char gr_current_g5 = 63;
|
|
static unsigned char gr_current_b5 = 31;
|
|
|
|
static GRSurface* gr_draw = NULL;
|
|
|
|
static bool outside(int x, int y)
|
|
{
|
|
return x < 0 || x >= gr_draw->width || y < 0 || y >= gr_draw->height;
|
|
}
|
|
|
|
int gr_measure(const char *s)
|
|
{
|
|
return gr_font->cwidth * strlen(s);
|
|
}
|
|
|
|
void gr_font_size(int *x, int *y)
|
|
{
|
|
*x = gr_font->cwidth;
|
|
*y = gr_font->cheight;
|
|
}
|
|
|
|
void blend_16bpp(unsigned char* px, unsigned r5, unsigned g5, unsigned b5, unsigned char a)
|
|
{
|
|
unsigned char orig[2];
|
|
orig[0] = px[0];
|
|
orig[1] = px[1];
|
|
|
|
/* This code is a little easier to read
|
|
unsigned oldred = (orig[1] >> 3);
|
|
unsigned oldgreen = (((orig[0] >> 5) << 3) + (orig[1] & 0x7));
|
|
unsigned oldblue = (orig[0] & 0x1F);
|
|
|
|
unsigned newred = (oldred * (255-a) + r5 * a) / 255;
|
|
unsigned newgreen = (oldgreen * (255-a) + g5 * a) / 255;
|
|
unsigned newblue = (oldblue * (255-a) + b5 * a) / 255;
|
|
*/
|
|
|
|
unsigned newred = ((orig[1] >> 3) * (255-a) + r5 * a) / 255;
|
|
unsigned newgreen = ((((orig[0] >> 5) << 3) + (orig[1] & 0x7)) * (255-a) + g5 * a) / 255;
|
|
unsigned newblue = ((orig[0] & 0x1F) * (255-a) + b5 * a) / 255;
|
|
|
|
*px++ = (newgreen << 5) + (newblue);
|
|
*px++ = (newred << 3) + (newgreen >> 3);
|
|
}
|
|
|
|
static void text_blend(unsigned char* src_p, int src_row_bytes,
|
|
unsigned char* dst_p, int dst_row_bytes,
|
|
int width, int height)
|
|
{
|
|
for (int j = 0; j < height; ++j) {
|
|
unsigned char* sx = src_p;
|
|
unsigned char* px = dst_p;
|
|
for (int i = 0; i < width; ++i) {
|
|
unsigned char a = *sx++;
|
|
if (gr_current_a < 255) a = ((int)a * gr_current_a) / 255;
|
|
if (a == 255) {
|
|
if (gr_draw->pixel_bytes == 2) {
|
|
*px++ = rgb_555[0];
|
|
*px++ = rgb_555[1];
|
|
} else {
|
|
*px++ = gr_current_r;
|
|
*px++ = gr_current_g;
|
|
*px++ = gr_current_b;
|
|
px++;
|
|
}
|
|
} else if (a > 0) {
|
|
if (gr_draw->pixel_bytes == 2) {
|
|
blend_16bpp(px, gr_current_r5, gr_current_g5, gr_current_b5, a);
|
|
px += gr_draw->pixel_bytes;
|
|
} else {
|
|
*px = (*px * (255-a) + gr_current_r * a) / 255;
|
|
++px;
|
|
*px = (*px * (255-a) + gr_current_g * a) / 255;
|
|
++px;
|
|
*px = (*px * (255-a) + gr_current_b * a) / 255;
|
|
++px;
|
|
++px;
|
|
}
|
|
} else {
|
|
px += gr_draw->pixel_bytes;
|
|
}
|
|
}
|
|
src_p += src_row_bytes;
|
|
dst_p += dst_row_bytes;
|
|
}
|
|
}
|
|
|
|
void gr_text(int x, int y, const char *s, bool bold)
|
|
{
|
|
GRFont* font = gr_font;
|
|
|
|
if (!font->texture || gr_current_a == 0) return;
|
|
|
|
bold = bold && (font->texture->height != font->cheight);
|
|
|
|
x += overscan_offset_x;
|
|
y += overscan_offset_y;
|
|
|
|
unsigned char ch;
|
|
while ((ch = *s++)) {
|
|
if (outside(x, y) || outside(x+font->cwidth-1, y+font->cheight-1)) break;
|
|
|
|
if (ch < ' ' || ch > '~') {
|
|
ch = '?';
|
|
}
|
|
|
|
unsigned char* src_p = font->texture->data + ((ch - ' ') * font->cwidth) +
|
|
(bold ? font->cheight * font->texture->row_bytes : 0);
|
|
unsigned char* dst_p = gr_draw->data + y*gr_draw->row_bytes + x*gr_draw->pixel_bytes;
|
|
|
|
text_blend(src_p, font->texture->row_bytes,
|
|
dst_p, gr_draw->row_bytes,
|
|
font->cwidth, font->cheight);
|
|
|
|
x += font->cwidth;
|
|
}
|
|
}
|
|
|
|
void gr_texticon(int x, int y, GRSurface* icon) {
|
|
if (icon == NULL) return;
|
|
|
|
if (icon->pixel_bytes != 1) {
|
|
printf("gr_texticon: source has wrong format\n");
|
|
return;
|
|
}
|
|
|
|
x += overscan_offset_x;
|
|
y += overscan_offset_y;
|
|
|
|
if (outside(x, y) || outside(x+icon->width-1, y+icon->height-1)) return;
|
|
|
|
unsigned char* src_p = icon->data;
|
|
unsigned char* dst_p = gr_draw->data + y*gr_draw->row_bytes + x*gr_draw->pixel_bytes;
|
|
|
|
text_blend(src_p, icon->row_bytes,
|
|
dst_p, gr_draw->row_bytes,
|
|
icon->width, icon->height);
|
|
}
|
|
|
|
void gr_convert_rgb_555()
|
|
{
|
|
gr_current_r5 = (((gr_current_r & 0xFF) * 0x1F) + 0x7F) / 0xFF;
|
|
gr_current_g5 = (((gr_current_g & 0xFF) * 0x3F) + 0x7F) / 0xFF;
|
|
gr_current_b5 = (((gr_current_b & 0xFF) * 0x1F) + 0x7F) / 0xFF;
|
|
|
|
rgb_555[0] = (gr_current_g5 << 5) + (gr_current_b5);
|
|
rgb_555[1] = (gr_current_r5 << 3) + (gr_current_g5 >> 3);
|
|
}
|
|
|
|
void gr_color(unsigned char r, unsigned char g, unsigned char b, unsigned char a)
|
|
{
|
|
#if defined(RECOVERY_ABGR) || defined(RECOVERY_BGRA)
|
|
gr_current_r = b;
|
|
gr_current_g = g;
|
|
gr_current_b = r;
|
|
gr_current_a = a;
|
|
#else
|
|
gr_current_r = r;
|
|
gr_current_g = g;
|
|
gr_current_b = b;
|
|
gr_current_a = a;
|
|
#endif
|
|
if (gr_draw->pixel_bytes == 2) {
|
|
gr_convert_rgb_555();
|
|
}
|
|
}
|
|
|
|
void gr_clear()
|
|
{
|
|
if (gr_draw->pixel_bytes == 2) {
|
|
gr_fill(0, 0, gr_fb_width(), gr_fb_height());
|
|
return;
|
|
}
|
|
|
|
// This code only works on 32bpp devices
|
|
if (gr_current_r == gr_current_g && gr_current_r == gr_current_b) {
|
|
memset(gr_draw->data, gr_current_r, gr_draw->height * gr_draw->row_bytes);
|
|
} else {
|
|
unsigned char* px = gr_draw->data;
|
|
for (int y = 0; y < gr_draw->height; ++y) {
|
|
for (int x = 0; x < gr_draw->width; ++x) {
|
|
*px++ = gr_current_r;
|
|
*px++ = gr_current_g;
|
|
*px++ = gr_current_b;
|
|
px++;
|
|
}
|
|
px += gr_draw->row_bytes - (gr_draw->width * gr_draw->pixel_bytes);
|
|
}
|
|
}
|
|
}
|
|
|
|
void gr_fill(int x1, int y1, int x2, int y2)
|
|
{
|
|
x1 += overscan_offset_x;
|
|
y1 += overscan_offset_y;
|
|
|
|
x2 += overscan_offset_x;
|
|
y2 += overscan_offset_y;
|
|
|
|
if (outside(x1, y1) || outside(x2-1, y2-1)) return;
|
|
|
|
unsigned char* p = gr_draw->data + y1 * gr_draw->row_bytes + x1 * gr_draw->pixel_bytes;
|
|
if (gr_current_a == 255) {
|
|
int x, y;
|
|
for (y = y1; y < y2; ++y) {
|
|
unsigned char* px = p;
|
|
for (x = x1; x < x2; ++x) {
|
|
if (gr_draw->pixel_bytes == 2) {
|
|
*px++ = rgb_555[0];
|
|
*px++ = rgb_555[1];
|
|
} else {
|
|
*px++ = gr_current_r;
|
|
*px++ = gr_current_g;
|
|
*px++ = gr_current_b;
|
|
px++;
|
|
}
|
|
}
|
|
p += gr_draw->row_bytes;
|
|
}
|
|
} else if (gr_current_a > 0) {
|
|
int x, y;
|
|
for (y = y1; y < y2; ++y) {
|
|
unsigned char* px = p;
|
|
for (x = x1; x < x2; ++x) {
|
|
if (gr_draw->pixel_bytes == 2) {
|
|
blend_16bpp(px, gr_current_r5, gr_current_g5, gr_current_b5, gr_current_a);
|
|
px += gr_draw->row_bytes;
|
|
} else {
|
|
*px = (*px * (255-gr_current_a) + gr_current_r * gr_current_a) / 255;
|
|
++px;
|
|
*px = (*px * (255-gr_current_a) + gr_current_g * gr_current_a) / 255;
|
|
++px;
|
|
*px = (*px * (255-gr_current_a) + gr_current_b * gr_current_a) / 255;
|
|
++px;
|
|
++px;
|
|
}
|
|
}
|
|
p += gr_draw->row_bytes;
|
|
}
|
|
}
|
|
}
|
|
|
|
void gr_blit_32to16(GRSurface* source, int sx, int sy, int w, int h, int dx, int dy) {
|
|
dx += overscan_offset_x;
|
|
dy += overscan_offset_y;
|
|
|
|
if (outside(dx, dy) || outside(dx+w-1, dy+h-1)) return;
|
|
|
|
unsigned char* src_p = source->data + sy*source->row_bytes + sx*source->pixel_bytes;
|
|
unsigned char* dst_p = gr_draw->data + dy*gr_draw->row_bytes + dx*gr_draw->pixel_bytes;
|
|
|
|
int i, j;
|
|
for (i = 0; i < h; ++i) {
|
|
unsigned char* spx = src_p;
|
|
unsigned char* dpx = dst_p;
|
|
|
|
for (j = 0; j < w; ++j) {
|
|
unsigned a = spx[3];
|
|
|
|
if (a == 0) {
|
|
spx += source->pixel_bytes;
|
|
dpx += gr_draw->pixel_bytes;
|
|
} else {
|
|
unsigned r5 = (((*spx++ & 0xFF) * 0x1F) + 0x7F) / 0xFF;
|
|
unsigned g5 = (((*spx++ & 0xFF) * 0x3F) + 0x7F) / 0xFF;
|
|
unsigned b5 = (((*spx++ & 0xFF) * 0x1F) + 0x7F) / 0xFF;
|
|
spx++;
|
|
if (a == 255) {
|
|
*dpx++ = (g5 << 5) + (b5);
|
|
*dpx++ = (r5 << 3) + (g5 >> 3);
|
|
} else {
|
|
blend_16bpp(dpx, r5, g5, b5, a);
|
|
spx += source->pixel_bytes;
|
|
}
|
|
}
|
|
}
|
|
src_p += source->row_bytes;
|
|
dst_p += gr_draw->row_bytes;
|
|
}
|
|
}
|
|
|
|
void gr_blit(GRSurface* source, int sx, int sy, int w, int h, int dx, int dy) {
|
|
if (source == NULL) return;
|
|
|
|
if (gr_draw->pixel_bytes != source->pixel_bytes) {
|
|
if (gr_draw->pixel_bytes == 2 && source->pixel_bytes == 4) {
|
|
gr_blit_32to16(source, sx, sy, w, h, dx, dy);
|
|
return;
|
|
} else {
|
|
printf("gr_blit: source has wrong format\n");
|
|
return;
|
|
}
|
|
}
|
|
|
|
dx += overscan_offset_x;
|
|
dy += overscan_offset_y;
|
|
|
|
if (outside(dx, dy) || outside(dx+w-1, dy+h-1)) return;
|
|
|
|
unsigned char* src_p = source->data + sy*source->row_bytes + sx*source->pixel_bytes;
|
|
unsigned char* dst_p = gr_draw->data + dy*gr_draw->row_bytes + dx*gr_draw->pixel_bytes;
|
|
|
|
int i;
|
|
for (i = 0; i < h; ++i) {
|
|
memcpy(dst_p, src_p, w * source->pixel_bytes);
|
|
src_p += source->row_bytes;
|
|
dst_p += gr_draw->row_bytes;
|
|
}
|
|
}
|
|
|
|
unsigned int gr_get_width(GRSurface* surface) {
|
|
if (surface == NULL) {
|
|
return 0;
|
|
}
|
|
return surface->width;
|
|
}
|
|
|
|
unsigned int gr_get_height(GRSurface* surface) {
|
|
if (surface == NULL) {
|
|
return 0;
|
|
}
|
|
return surface->height;
|
|
}
|
|
|
|
static void gr_init_font(void)
|
|
{
|
|
gr_font = reinterpret_cast<GRFont*>(calloc(sizeof(*gr_font), 1));
|
|
|
|
int res = res_create_alpha_surface("font", &(gr_font->texture));
|
|
if (res == 0) {
|
|
// The font image should be a 96x2 array of character images. The
|
|
// columns are the printable ASCII characters 0x20 - 0x7f. The
|
|
// top row is regular text; the bottom row is bold.
|
|
gr_font->cwidth = gr_font->texture->width / 96;
|
|
gr_font->cheight = gr_font->texture->height / 2;
|
|
} else {
|
|
printf("failed to read font: res=%d\n", res);
|
|
|
|
// fall back to the compiled-in font.
|
|
gr_font->texture = reinterpret_cast<GRSurface*>(malloc(sizeof(*gr_font->texture)));
|
|
gr_font->texture->width = font.width;
|
|
gr_font->texture->height = font.height;
|
|
gr_font->texture->row_bytes = font.width;
|
|
gr_font->texture->pixel_bytes = 1;
|
|
|
|
unsigned char* bits = reinterpret_cast<unsigned char*>(malloc(font.width * font.height));
|
|
gr_font->texture->data = reinterpret_cast<unsigned char*>(bits);
|
|
|
|
unsigned char data;
|
|
unsigned char* in = font.rundata;
|
|
while((data = *in++)) {
|
|
memset(bits, (data & 0x80) ? 255 : 0, data & 0x7f);
|
|
bits += (data & 0x7f);
|
|
}
|
|
|
|
gr_font->cwidth = font.cwidth;
|
|
gr_font->cheight = font.cheight;
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
// Exercises many of the gr_*() functions; useful for testing.
|
|
static void gr_test() {
|
|
GRSurface** images;
|
|
int frames;
|
|
int result = res_create_multi_surface("icon_installing", &frames, &images);
|
|
if (result < 0) {
|
|
printf("create surface %d\n", result);
|
|
gr_exit();
|
|
return;
|
|
}
|
|
|
|
time_t start = time(NULL);
|
|
int x;
|
|
for (x = 0; x <= 1200; ++x) {
|
|
if (x < 400) {
|
|
gr_color(0, 0, 0, 255);
|
|
} else {
|
|
gr_color(0, (x-400)%128, 0, 255);
|
|
}
|
|
gr_clear();
|
|
|
|
gr_color(255, 0, 0, 255);
|
|
GRSurface* frame = images[x%frames];
|
|
gr_blit(frame, 0, 0, frame->width, frame->height, x, 0);
|
|
|
|
gr_color(255, 0, 0, 128);
|
|
gr_fill(400, 150, 600, 350);
|
|
|
|
gr_color(255, 255, 255, 255);
|
|
gr_text(500, 225, "hello, world!", 0);
|
|
gr_color(255, 255, 0, 128);
|
|
gr_text(300+x, 275, "pack my box with five dozen liquor jugs", 1);
|
|
|
|
gr_color(0, 0, 255, 128);
|
|
gr_fill(gr_draw->width - 200 - x, 300, gr_draw->width - x, 500);
|
|
|
|
gr_draw = gr_backend->flip(gr_backend);
|
|
}
|
|
printf("getting end time\n");
|
|
time_t end = time(NULL);
|
|
printf("got end time\n");
|
|
printf("start %ld end %ld\n", (long)start, (long)end);
|
|
if (end > start) {
|
|
printf("%.2f fps\n", ((double)x) / (end-start));
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void gr_flip() {
|
|
gr_draw = gr_backend->flip(gr_backend);
|
|
}
|
|
|
|
int gr_init(void)
|
|
{
|
|
gr_init_font();
|
|
gr_draw = NULL;
|
|
|
|
gr_backend = open_overlay();
|
|
if (gr_backend) {
|
|
gr_draw = gr_backend->init(gr_backend);
|
|
if (!gr_draw) {
|
|
gr_backend->exit(gr_backend);
|
|
} else
|
|
printf("Using overlay graphics.\n");
|
|
}
|
|
|
|
#ifndef MSM_BSP
|
|
if (!gr_draw) {
|
|
gr_backend = open_adf();
|
|
if (gr_backend) {
|
|
gr_draw = gr_backend->init(gr_backend);
|
|
if (!gr_draw) {
|
|
gr_backend->exit(gr_backend);
|
|
} else
|
|
printf("Using adf graphics.\n");
|
|
}
|
|
}
|
|
#else
|
|
printf("Skipping adf graphics because TW_TARGET_USES_QCOM_BSP := true\n");
|
|
#endif
|
|
|
|
if (!gr_draw) {
|
|
gr_backend = open_drm();
|
|
gr_draw = gr_backend->init(gr_backend);
|
|
if (gr_draw)
|
|
printf("Using drm graphics.\n");
|
|
}
|
|
|
|
if (!gr_draw) {
|
|
gr_backend = open_fbdev();
|
|
gr_draw = gr_backend->init(gr_backend);
|
|
if (gr_draw == NULL) {
|
|
return -1;
|
|
} else
|
|
printf("Using fbdev graphics.\n");
|
|
}
|
|
|
|
overscan_offset_x = gr_draw->width * overscan_percent / 100;
|
|
overscan_offset_y = gr_draw->height * overscan_percent / 100;
|
|
|
|
gr_flip();
|
|
gr_flip();
|
|
|
|
return 0;
|
|
}
|
|
|
|
void gr_exit(void)
|
|
{
|
|
gr_backend->exit(gr_backend);
|
|
}
|
|
|
|
int gr_fb_width(void)
|
|
{
|
|
return gr_draw->width - 2*overscan_offset_x;
|
|
}
|
|
|
|
int gr_fb_height(void)
|
|
{
|
|
return gr_draw->height - 2*overscan_offset_y;
|
|
}
|
|
|
|
void gr_fb_blank(bool blank)
|
|
{
|
|
gr_backend->blank(gr_backend, blank);
|
|
}
|