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 "graphics.h" 18 19 #include <stdint.h> 20 #include <stdio.h> 21 #include <stdlib.h> 22 #include <string.h> 23 24 #include <memory> 25 26 #include <android-base/properties.h> 27 28 #include "graphics_drm.h" 29 #include "graphics_fbdev.h" 30 #include "minui/minui.h" 31 32 static GRFont* gr_font = nullptr; 33 static MinuiBackend* gr_backend = nullptr; 34 35 static int overscan_offset_x = 0; 36 static int overscan_offset_y = 0; 37 38 static uint32_t gr_current = ~0; 39 40 // gr_draw is owned by backends. 41 static GRSurface* gr_draw = nullptr; 42 static GRRotation rotation = GRRotation::NONE; 43 static PixelFormat pixel_format = PixelFormat::UNKNOWN; 44 // The graphics backend list that provides fallback options for the default backend selection. 45 // For example, it will fist try DRM, then try FBDEV if DRM is unavailable. 46 constexpr auto default_backends = { GraphicsBackend::DRM, GraphicsBackend::FBDEV }; 47 outside(int x,int y)48 static bool outside(int x, int y) { 49 auto swapped = (rotation == GRRotation::LEFT || rotation == GRRotation::RIGHT); 50 return x < 0 || x >= (swapped ? gr_draw->height : gr_draw->width) || y < 0 || 51 y >= (swapped ? gr_draw->width : gr_draw->height); 52 } 53 gr_sys_font()54 const GRFont* gr_sys_font() { 55 return gr_font; 56 } 57 gr_pixel_format()58 PixelFormat gr_pixel_format() { 59 return pixel_format; 60 } 61 gr_measure(const GRFont * font,const char * s)62 int gr_measure(const GRFont* font, const char* s) { 63 if (font == nullptr) { 64 return -1; 65 } 66 67 return font->char_width * strlen(s); 68 } 69 gr_font_size(const GRFont * font,int * x,int * y)70 int gr_font_size(const GRFont* font, int* x, int* y) { 71 if (font == nullptr) { 72 return -1; 73 } 74 75 *x = font->char_width; 76 *y = font->char_height; 77 return 0; 78 } 79 80 // Blends gr_current onto pix value, assumes alpha as most significant byte. pixel_blend_argb(uint8_t alpha,uint32_t pix)81 static inline uint32_t pixel_blend_argb(uint8_t alpha, uint32_t pix) { 82 if (alpha == 255) return gr_current; 83 if (alpha == 0) return pix; 84 uint32_t pix_r = pix & 0xff; 85 uint32_t pix_g = pix & 0xff00; 86 uint32_t pix_b = pix & 0xff0000; 87 uint32_t cur_r = gr_current & 0xff; 88 uint32_t cur_g = gr_current & 0xff00; 89 uint32_t cur_b = gr_current & 0xff0000; 90 91 uint32_t out_r = (pix_r * (255 - alpha) + cur_r * alpha) / 255; 92 uint32_t out_g = (pix_g * (255 - alpha) + cur_g * alpha) / 255; 93 uint32_t out_b = (pix_b * (255 - alpha) + cur_b * alpha) / 255; 94 95 return (out_r & 0xff) | (out_g & 0xff00) | (out_b & 0xff0000) | (gr_current & 0xff000000); 96 } 97 pixel_blend_rgba(uint8_t alpha,uint32_t pix)98 static inline uint32_t pixel_blend_rgba(uint8_t alpha, uint32_t pix) { 99 if (alpha == 255) return gr_current; 100 if (alpha == 0) return pix; 101 uint32_t pix_r = pix & 0xff00; 102 uint32_t pix_g = pix & 0xff0000; 103 uint32_t pix_b = pix & 0xff000000; 104 uint32_t cur_r = gr_current & 0xff00; 105 uint32_t cur_g = gr_current & 0xff0000; 106 uint32_t cur_b = gr_current & 0xff000000; 107 108 uint32_t out_r = (pix_r * (255 - alpha) + cur_r * alpha) / 255; 109 uint32_t out_g = (pix_g * (255 - alpha) + cur_g * alpha) / 255; 110 uint32_t out_b = (pix_b * (255 - alpha) + cur_b * alpha) / 255; 111 112 return (gr_current & 0xff) | (out_r & 0xff00) | (out_g & 0xff0000) | (out_b & 0xff000000); 113 } 114 pixel_blend(uint8_t alpha,uint32_t pix)115 static inline uint32_t pixel_blend(uint8_t alpha, uint32_t pix) { 116 if (pixel_format == PixelFormat::RGBA) { 117 return pixel_blend_rgba(alpha, pix); 118 } 119 return pixel_blend_argb(alpha, pix); 120 } 121 get_alphamask()122 static inline uint32_t get_alphamask() { 123 if (pixel_format == PixelFormat::RGBA) { 124 return 0x000000ff; 125 } 126 return 0xff000000; 127 } 128 get_alpha_shift()129 static inline uint8_t get_alpha_shift() { 130 if (pixel_format == PixelFormat::RGBA) { 131 return 0; 132 } 133 return 24; 134 } 135 get_alpha(uint32_t pix)136 static inline uint8_t get_alpha(uint32_t pix) { 137 return static_cast<uint8_t>((pix & (gr_current & get_alphamask())) >> get_alpha_shift()); 138 } 139 140 // Increments pixel pointer right, with current rotation. incr_x(uint32_t ** p,int row_pixels)141 static void incr_x(uint32_t** p, int row_pixels) { 142 if (rotation == GRRotation::LEFT) { 143 *p = *p - row_pixels; 144 } else if (rotation == GRRotation::RIGHT) { 145 *p = *p + row_pixels; 146 } else if (rotation == GRRotation::DOWN) { 147 *p = *p - 1; 148 } else { // GRRotation::NONE 149 *p = *p + 1; 150 } 151 } 152 153 // Increments pixel pointer down, with current rotation. incr_y(uint32_t ** p,int row_pixels)154 static void incr_y(uint32_t** p, int row_pixels) { 155 if (rotation == GRRotation::LEFT) { 156 *p = *p + 1; 157 } else if (rotation == GRRotation::RIGHT) { 158 *p = *p - 1; 159 } else if (rotation == GRRotation::DOWN) { 160 *p = *p - row_pixels; 161 } else { // GRRotation::NONE 162 *p = *p + row_pixels; 163 } 164 } 165 166 // Returns pixel pointer at given coordinates with rotation adjustment. PixelAt(GRSurface * surface,int x,int y,int row_pixels)167 static uint32_t* PixelAt(GRSurface* surface, int x, int y, int row_pixels) { 168 switch (rotation) { 169 case GRRotation::NONE: 170 return reinterpret_cast<uint32_t*>(surface->data()) + y * row_pixels + x; 171 case GRRotation::RIGHT: 172 return reinterpret_cast<uint32_t*>(surface->data()) + x * row_pixels + (surface->width - y); 173 case GRRotation::DOWN: 174 return reinterpret_cast<uint32_t*>(surface->data()) + (surface->height - 1 - y) * row_pixels + 175 (surface->width - 1 - x); 176 case GRRotation::LEFT: 177 return reinterpret_cast<uint32_t*>(surface->data()) + (surface->height - 1 - x) * row_pixels + 178 y; 179 default: 180 printf("invalid rotation %d", static_cast<int>(rotation)); 181 } 182 return nullptr; 183 } 184 TextBlend(const uint8_t * src_p,int src_row_bytes,uint32_t * dst_p,int dst_row_pixels,int width,int height)185 static void TextBlend(const uint8_t* src_p, int src_row_bytes, uint32_t* dst_p, int dst_row_pixels, 186 int width, int height) { 187 uint8_t alpha_current = get_alpha(gr_current); 188 for (int j = 0; j < height; ++j) { 189 const uint8_t* sx = src_p; 190 uint32_t* px = dst_p; 191 for (int i = 0; i < width; ++i, incr_x(&px, dst_row_pixels)) { 192 uint8_t a = *sx++; 193 if (alpha_current < 255) a = (static_cast<uint32_t>(a) * alpha_current) / 255; 194 *px = pixel_blend(a, *px); 195 } 196 src_p += src_row_bytes; 197 incr_y(&dst_p, dst_row_pixels); 198 } 199 } 200 gr_text(const GRFont * font,int x,int y,const char * s,bool bold)201 void gr_text(const GRFont* font, int x, int y, const char* s, bool bold) { 202 if (!font || !font->texture || (gr_current & get_alphamask()) == 0) return; 203 204 if (font->texture->pixel_bytes != 1) { 205 printf("gr_text: font has wrong format\n"); 206 return; 207 } 208 209 bold = bold && (font->texture->height != font->char_height); 210 211 x += overscan_offset_x; 212 y += overscan_offset_y; 213 214 unsigned char ch; 215 while ((ch = *s++)) { 216 if (outside(x, y) || outside(x + font->char_width - 1, y + font->char_height - 1)) break; 217 218 if (ch < ' ' || ch > '~') { 219 ch = '?'; 220 } 221 222 int row_pixels = gr_draw->row_bytes / gr_draw->pixel_bytes; 223 const uint8_t* src_p = font->texture->data() + ((ch - ' ') * font->char_width) + 224 (bold ? font->char_height * font->texture->row_bytes : 0); 225 uint32_t* dst_p = PixelAt(gr_draw, x, y, row_pixels); 226 227 TextBlend(src_p, font->texture->row_bytes, dst_p, row_pixels, font->char_width, 228 font->char_height); 229 230 x += font->char_width; 231 } 232 } 233 gr_texticon(int x,int y,const GRSurface * icon)234 void gr_texticon(int x, int y, const GRSurface* icon) { 235 if (icon == nullptr) return; 236 237 if (icon->pixel_bytes != 1) { 238 printf("gr_texticon: source has wrong format\n"); 239 return; 240 } 241 242 x += overscan_offset_x; 243 y += overscan_offset_y; 244 245 if (outside(x, y) || outside(x + icon->width - 1, y + icon->height - 1)) return; 246 247 int row_pixels = gr_draw->row_bytes / gr_draw->pixel_bytes; 248 const uint8_t* src_p = icon->data(); 249 uint32_t* dst_p = PixelAt(gr_draw, x, y, row_pixels); 250 TextBlend(src_p, icon->row_bytes, dst_p, row_pixels, icon->width, icon->height); 251 } 252 gr_color(unsigned char r,unsigned char g,unsigned char b,unsigned char a)253 void gr_color(unsigned char r, unsigned char g, unsigned char b, unsigned char a) { 254 uint32_t r32 = r, g32 = g, b32 = b, a32 = a; 255 if (pixel_format == PixelFormat::ARGB || pixel_format == PixelFormat::BGRA) { 256 gr_current = (a32 << 24) | (r32 << 16) | (g32 << 8) | b32; 257 } else if (pixel_format == PixelFormat::RGBA) { 258 gr_current = (b32 << 24) | (g32 << 16) | (r32 << 8) | a32; 259 } else { 260 gr_current = (a32 << 24) | (b32 << 16) | (g32 << 8) | r32; 261 } 262 } 263 gr_clear()264 void gr_clear() { 265 if ((gr_current & 0xff) == ((gr_current >> 8) & 0xff) && 266 (gr_current & 0xff) == ((gr_current >> 16) & 0xff) && 267 (gr_current & 0xff) == ((gr_current >> 24) & 0xff) && 268 gr_draw->row_bytes == gr_draw->width * gr_draw->pixel_bytes) { 269 memset(gr_draw->data(), gr_current & 0xff, gr_draw->height * gr_draw->row_bytes); 270 } else { 271 uint32_t* px = reinterpret_cast<uint32_t*>(gr_draw->data()); 272 int row_diff = gr_draw->row_bytes / gr_draw->pixel_bytes - gr_draw->width; 273 for (int y = 0; y < gr_draw->height; ++y) { 274 for (int x = 0; x < gr_draw->width; ++x) { 275 *px++ = gr_current; 276 } 277 px += row_diff; 278 } 279 } 280 } 281 gr_fill(int x1,int y1,int x2,int y2)282 void gr_fill(int x1, int y1, int x2, int y2) { 283 x1 += overscan_offset_x; 284 y1 += overscan_offset_y; 285 286 x2 += overscan_offset_x; 287 y2 += overscan_offset_y; 288 289 if (outside(x1, y1) || outside(x2 - 1, y2 - 1)) return; 290 291 int row_pixels = gr_draw->row_bytes / gr_draw->pixel_bytes; 292 uint32_t* p = PixelAt(gr_draw, x1, y1, row_pixels); 293 uint8_t alpha = get_alpha(gr_current); 294 if (alpha > 0) { 295 for (int y = y1; y < y2; ++y) { 296 uint32_t* px = p; 297 for (int x = x1; x < x2; ++x) { 298 *px = pixel_blend(alpha, *px); 299 incr_x(&px, row_pixels); 300 } 301 incr_y(&p, row_pixels); 302 } 303 } 304 } 305 gr_blit(const GRSurface * source,int sx,int sy,int w,int h,int dx,int dy)306 void gr_blit(const GRSurface* source, int sx, int sy, int w, int h, int dx, int dy) { 307 if (source == nullptr) return; 308 309 if (gr_draw->pixel_bytes != source->pixel_bytes) { 310 printf("gr_blit: source has wrong format\n"); 311 return; 312 } 313 314 dx += overscan_offset_x; 315 dy += overscan_offset_y; 316 317 if (outside(dx, dy) || outside(dx + w - 1, dy + h - 1)) return; 318 319 if (rotation != GRRotation::NONE) { 320 int src_row_pixels = source->row_bytes / source->pixel_bytes; 321 int row_pixels = gr_draw->row_bytes / gr_draw->pixel_bytes; 322 const uint32_t* src_py = 323 reinterpret_cast<const uint32_t*>(source->data()) + sy * source->row_bytes / 4 + sx; 324 uint32_t* dst_py = PixelAt(gr_draw, dx, dy, row_pixels); 325 326 for (int y = 0; y < h; y += 1) { 327 const uint32_t* src_px = src_py; 328 uint32_t* dst_px = dst_py; 329 for (int x = 0; x < w; x += 1) { 330 *dst_px = *src_px++; 331 incr_x(&dst_px, row_pixels); 332 } 333 src_py += src_row_pixels; 334 incr_y(&dst_py, row_pixels); 335 } 336 } else { 337 const uint8_t* src_p = source->data() + sy * source->row_bytes + sx * source->pixel_bytes; 338 uint8_t* dst_p = gr_draw->data() + dy * gr_draw->row_bytes + dx * gr_draw->pixel_bytes; 339 340 for (int i = 0; i < h; ++i) { 341 memcpy(dst_p, src_p, w * source->pixel_bytes); 342 src_p += source->row_bytes; 343 dst_p += gr_draw->row_bytes; 344 } 345 } 346 } 347 gr_get_width(const GRSurface * surface)348 unsigned int gr_get_width(const GRSurface* surface) { 349 if (surface == nullptr) { 350 return 0; 351 } 352 return surface->width; 353 } 354 gr_get_height(const GRSurface * surface)355 unsigned int gr_get_height(const GRSurface* surface) { 356 if (surface == nullptr) { 357 return 0; 358 } 359 return surface->height; 360 } 361 gr_init_font(const char * name,GRFont ** dest)362 int gr_init_font(const char* name, GRFont** dest) { 363 GRFont* font = static_cast<GRFont*>(calloc(1, sizeof(*gr_font))); 364 if (font == nullptr) { 365 return -1; 366 } 367 368 int res = res_create_alpha_surface(name, &(font->texture)); 369 if (res < 0) { 370 free(font); 371 return res; 372 } 373 374 // The font image should be a 96x2 array of character images. The 375 // columns are the printable ASCII characters 0x20 - 0x7f. The 376 // top row is regular text; the bottom row is bold. 377 font->char_width = font->texture->width / 96; 378 font->char_height = font->texture->height / 2; 379 380 *dest = font; 381 382 return 0; 383 } 384 gr_flip()385 void gr_flip() { 386 gr_draw = gr_backend->Flip(); 387 } 388 create_backend(GraphicsBackend backend)389 std::unique_ptr<MinuiBackend> create_backend(GraphicsBackend backend) { 390 switch (backend) { 391 case GraphicsBackend::DRM: 392 return std::make_unique<MinuiBackendDrm>(); 393 case GraphicsBackend::FBDEV: 394 return std::make_unique<MinuiBackendFbdev>(); 395 default: 396 return nullptr; 397 } 398 } 399 gr_init()400 int gr_init() { 401 return gr_init(default_backends); 402 } 403 gr_init(std::initializer_list<GraphicsBackend> backends)404 int gr_init(std::initializer_list<GraphicsBackend> backends) { 405 // pixel_format needs to be set before loading any resources or initializing backends. 406 std::string format = android::base::GetProperty("ro.minui.pixel_format", ""); 407 if (format == "ABGR_8888") { 408 pixel_format = PixelFormat::ABGR; 409 } else if (format == "RGBX_8888") { 410 pixel_format = PixelFormat::RGBX; 411 } else if (format == "ARGB_8888") { 412 pixel_format = PixelFormat::ARGB; 413 } else if (format == "BGRA_8888") { 414 pixel_format = PixelFormat::BGRA; 415 } else if (format == "RGBA_8888") { 416 pixel_format = PixelFormat::RGBA; 417 } else { 418 pixel_format = PixelFormat::UNKNOWN; 419 } 420 421 int ret = gr_init_font("font", &gr_font); 422 if (ret != 0) { 423 printf("Failed to init font: %d, continuing graphic backend initialization without font file\n", 424 ret); 425 } 426 427 std::unique_ptr<MinuiBackend> minui_backend; 428 for (GraphicsBackend backend : backends) { 429 minui_backend = create_backend(backend); 430 if (!minui_backend) { 431 printf("gr_init: minui_backend %d is a nullptr\n", backend); 432 continue; 433 } 434 gr_draw = minui_backend->Init(); 435 if (gr_draw) break; 436 } 437 438 if (!gr_draw) { 439 return -1; 440 } 441 442 gr_backend = minui_backend.release(); 443 444 int overscan_percent = android::base::GetIntProperty("ro.minui.overscan_percent", 0); 445 overscan_offset_x = gr_draw->width * overscan_percent / 100; 446 overscan_offset_y = gr_draw->height * overscan_percent / 100; 447 448 gr_flip(); 449 gr_flip(); 450 if (!gr_draw) { 451 printf("gr_init: gr_draw becomes nullptr after gr_flip\n"); 452 return -1; 453 } 454 455 std::string rotation_str = 456 android::base::GetProperty("ro.minui.default_rotation", "ROTATION_NONE"); 457 if (rotation_str == "ROTATION_RIGHT") { 458 gr_rotate(GRRotation::RIGHT); 459 } else if (rotation_str == "ROTATION_DOWN") { 460 gr_rotate(GRRotation::DOWN); 461 } else if (rotation_str == "ROTATION_LEFT") { 462 gr_rotate(GRRotation::LEFT); 463 } else { // "ROTATION_NONE" or unknown string 464 gr_rotate(GRRotation::NONE); 465 } 466 467 if (gr_draw->pixel_bytes != 4) { 468 printf("gr_init: Only 4-byte pixel formats supported\n"); 469 } 470 471 return 0; 472 } 473 gr_exit()474 void gr_exit() { 475 delete gr_backend; 476 gr_backend = nullptr; 477 478 delete gr_font; 479 gr_font = nullptr; 480 } 481 gr_fb_width()482 int gr_fb_width() { 483 return (rotation == GRRotation::LEFT || rotation == GRRotation::RIGHT) 484 ? gr_draw->height - 2 * overscan_offset_y 485 : gr_draw->width - 2 * overscan_offset_x; 486 } 487 gr_fb_height()488 int gr_fb_height() { 489 return (rotation == GRRotation::LEFT || rotation == GRRotation::RIGHT) 490 ? gr_draw->width - 2 * overscan_offset_x 491 : gr_draw->height - 2 * overscan_offset_y; 492 } 493 gr_fb_blank(bool blank)494 void gr_fb_blank(bool blank) { 495 gr_backend->Blank(blank); 496 } 497 gr_fb_blank(bool blank,int index)498 void gr_fb_blank(bool blank, int index) { 499 gr_backend->Blank(blank, static_cast<MinuiBackend::DrmConnector>(index)); 500 } 501 gr_rotate(GRRotation rot)502 void gr_rotate(GRRotation rot) { 503 rotation = rot; 504 } 505 gr_get_rotation()506 GRRotation gr_get_rotation() { 507 return rotation; 508 } 509 gr_has_multiple_connectors()510 bool gr_has_multiple_connectors() { 511 return gr_backend->HasMultipleConnectors(); 512 } 513