1626 lines
46 KiB
C
1626 lines
46 KiB
C
/*
|
|
* Copyright (C) 2007-2015 Apple Inc. All rights reserved.
|
|
* Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
|
|
*
|
|
* This document is the property of Apple Inc.
|
|
* It is considered confidential and proprietary.
|
|
*
|
|
* This document may not be reproduced or transmitted in any form,
|
|
* in whole or in part, without the express written permission of
|
|
* Apple Inc.
|
|
*/
|
|
#include <debug.h>
|
|
#include <platform.h>
|
|
#include <drivers/display.h>
|
|
#include <lib/lzss.h>
|
|
#include <lib/mib.h>
|
|
#include <lib/paint.h>
|
|
#include <lib/syscfg.h>
|
|
#include <string.h>
|
|
#include <sys.h>
|
|
#include <sys/types.h>
|
|
#include <arch.h>
|
|
#include <target.h>
|
|
#include <lib/libiboot.h>
|
|
|
|
#define SYSCFG_DCLR_COLOR_REPORT 1 // Report colors found in 'DClr' syscfg entry
|
|
#if DEBUG_BUILD
|
|
#define SYSCFG_DCLR_OVERRIDE 1 // Allow overridding 'DClr' syscfg entry from environment for testing
|
|
#else
|
|
#define SYSCFG_DCLR_OVERRIDE 0 // Do not honor DClr_Override on non-DEBUG builds.
|
|
#endif
|
|
#define PANIC_ON_SERIOUS_ERROR 1 // Panic if 'DClr' syscfg processing error
|
|
#define PANIC_ON_UNKNOWN_DCLR_COLOR 1 // Panic if 'DClr' syscfg version is unknown or invalid
|
|
|
|
// Nothing explicitly defines a kMIBTargetOsPictureRotate factor so use
|
|
// the value returned by kMIBTargetPictureRotate for everything.
|
|
#define kMIBTargetOsPictureRotate kMIBTargetPictureRotate
|
|
|
|
struct iboot_image;
|
|
static int paint_draw_picture(struct iboot_image *comp_image_buf, size_t comp_image_len);
|
|
static int paint_update_image_argb(void);
|
|
static int paint_update_image_wide_gammut(void);
|
|
|
|
static void paint_install_gamma_table_channel(uint32_t entries, volatile uint32_t *channel_lut, const uint8_t *bits);
|
|
static uint8_t paint_install_gamma_get_2bits(const uint8_t *bits);
|
|
|
|
static void plot4(struct plane *p, uint32_t x, uint32_t y, uint64_t color);
|
|
static void plot8(struct plane *p, uint32_t x, uint32_t y, uint64_t color);
|
|
static void plot16(struct plane *p, uint32_t x, uint32_t y, uint64_t color);
|
|
static void plot32(struct plane *p, uint32_t x, uint32_t y, uint64_t color);
|
|
static uint32_t get_pixel4(struct plane *p, uint32_t x, uint32_t y);
|
|
static uint32_t get_pixel8(struct plane *p, uint32_t x, uint32_t y);
|
|
static uint32_t get_pixel16(struct plane *p, uint32_t x, uint32_t y);
|
|
static uint32_t get_pixel32(struct plane *p, uint32_t x, uint32_t y);
|
|
static void hline4(struct plane *p, uint32_t x, uint32_t y, uint32_t len, uint64_t color);
|
|
static void hline8(struct plane *p, uint32_t x, uint32_t y, uint32_t len, uint64_t color);
|
|
static void hline16(struct plane *p, uint32_t x, uint32_t y, uint32_t len, uint64_t color);
|
|
static void hline32(struct plane *p, uint32_t x, uint32_t y, uint32_t len, uint64_t color);
|
|
static void vline4(struct plane *p, uint32_t x, uint32_t y, uint32_t len, uint64_t color);
|
|
static void vline8(struct plane *p, uint32_t x, uint32_t y, uint32_t len, uint64_t color);
|
|
static void vline16(struct plane *p, uint32_t x, uint32_t y, uint32_t len, uint64_t color);
|
|
static void vline32(struct plane *p, uint32_t x, uint32_t y, uint32_t len, uint64_t color);
|
|
static void set_plane(struct plane *p, uint32_t y, uint32_t line_x, size_t plane_size, addr_t plane_fb, addr_t plane_db, enum colorspace cs);
|
|
|
|
static void clear_flattened_images(void);
|
|
|
|
static bool paint_ready;
|
|
static struct canvas *paint_canvas;
|
|
|
|
static uint32_t window_height;
|
|
static uint32_t window_width;
|
|
static uint32_t window_stride;
|
|
static uint32_t window_depth;
|
|
static uintptr_t window_framebuffer;
|
|
|
|
static addr_t compressed_image_base;
|
|
static size_t compressed_image_size;
|
|
|
|
struct flattened_image {
|
|
bool fi_color;
|
|
uint32_t fi_width;
|
|
uint32_t fi_height;
|
|
uint32_t fi_xstart;
|
|
uint32_t fi_ystart;
|
|
uint32_t fi_xstop;
|
|
uint32_t fi_ystop;
|
|
uint8_t *fi_buffer;
|
|
uint32_t fi_src_pos;
|
|
};
|
|
|
|
#define MAX_FLATTENED_IMAGES (0x1000 / sizeof(struct flattened_image))
|
|
static uint32_t flattened_images_count;
|
|
static struct flattened_image *flattened_images;
|
|
static addr_t flattened_images_base;
|
|
static addr_t flattened_images_end;
|
|
|
|
static uint64_t background_rgb;
|
|
static bool color_map_desired;
|
|
static bool color_map_enabled;
|
|
static bool color_map_supported;
|
|
static bool color_map_invalid;
|
|
static color_policy_t color_policy;
|
|
#if DEBUG_BUILD
|
|
static struct display_window *paint_window;
|
|
#endif
|
|
static addr_t last_canvas_addr;
|
|
static size_t page_size;
|
|
|
|
int paint_init(void *window, addr_t scratch, size_t scratch_size)
|
|
{
|
|
struct display_window *w = (struct display_window *)window;
|
|
int result = -1;
|
|
|
|
paint_ready = false;
|
|
|
|
if (w == NULL) {
|
|
goto out;
|
|
}
|
|
|
|
paint_canvas = &w->c;
|
|
|
|
window_height = w->height;
|
|
window_width = w->width;
|
|
window_stride = w->stride;
|
|
window_depth = w->depth;
|
|
|
|
//The framebuffer region should be large enough to include all planes used
|
|
window_framebuffer = paint_canvas->fb_phys;
|
|
|
|
compressed_image_base = scratch;
|
|
compressed_image_size = window_height * window_stride + 0x1000;
|
|
|
|
flattened_images = 0;
|
|
flattened_images = (struct flattened_image *)(compressed_image_base + compressed_image_size);
|
|
flattened_images_base = compressed_image_base + compressed_image_size + 0x1000;
|
|
flattened_images_end = scratch + scratch_size;
|
|
flattened_images_count = 0;
|
|
|
|
if (flattened_images_base >= flattened_images_end) {
|
|
dprintf(DEBUG_INFO, "Display Size is too small\n");
|
|
goto out;
|
|
}
|
|
|
|
background_rgb = 0x00000000;
|
|
|
|
paint_ready = true;
|
|
|
|
#if DEBUG_BUILD
|
|
paint_window = w;
|
|
#endif
|
|
result = 0;
|
|
|
|
out:
|
|
return result;
|
|
}
|
|
|
|
static void clear_flattened_images(void)
|
|
{
|
|
if (!paint_ready)
|
|
return;
|
|
|
|
flattened_images_count = 0;
|
|
flattened_images_base = compressed_image_base + compressed_image_size + 0x1000;
|
|
}
|
|
|
|
int paint_set_bgcolor(uint16_t bg_red, uint16_t bg_green, uint16_t bg_blue)
|
|
{
|
|
if (!paint_ready) {
|
|
return 0;
|
|
}
|
|
|
|
if (paint_canvas->cs == CS_ARGB8101010) {
|
|
background_rgb = RGB10(bg_red, bg_green, bg_blue);
|
|
} else {
|
|
background_rgb = RGB(bg_red, bg_green, bg_blue);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* image_data is either 32 or 16 bits per sample with the b_alpha most significant. */
|
|
/* The reset of the data is the rgb or grey data having already been alpha scaled. */
|
|
/* The b_alpha value is scale with the back ground color then added to the rgb value. */
|
|
|
|
struct iboot_image {
|
|
char image_signature[8]; // 0x00 - 'iBootIm'
|
|
uint32_t image_adler32; // 0x08
|
|
uint32_t image_type; // 0x0C - 'lzss'
|
|
uint32_t image_format; // 0x10 - 'argb' or 'grey'
|
|
uint16_t image_width; // 0x14
|
|
uint16_t image_height; // 0x16
|
|
int16_t image_h_offset; // 0x18
|
|
int16_t image_v_offset; // 0x1A
|
|
uint32_t image_data_length; // 0x1C
|
|
uint32_t reserved[8]; // 0x20
|
|
uint8_t image_data[]; // 0x40
|
|
} __attribute__((packed));
|
|
typedef struct iboot_image iboot_image;
|
|
|
|
int paint_set_picture_for_tag(uint32_t image_tag)
|
|
{
|
|
struct image_info *pict_info = 0;
|
|
|
|
if (mib_get_u32(kMIBTargetApplicationProduct) == MIB_APP_PROD_IBOOT) {
|
|
pict_info = image_find(image_tag);
|
|
if (pict_info == 0) {
|
|
dprintf(DEBUG_INFO, "image tag 0x%08x not found\n", image_tag);
|
|
}
|
|
}
|
|
|
|
return paint_set_picture(pict_info);
|
|
}
|
|
|
|
int paint_set_picture_for_tag_list(uint32_t image_tag, int image_offset, int image_count)
|
|
{
|
|
struct image_info *pict_info = 0;
|
|
|
|
if (mib_get_u32(kMIBTargetApplicationProduct) == MIB_APP_PROD_IBOOT) {
|
|
pict_info = image_find(image_tag);
|
|
if (pict_info == 0) {
|
|
dprintf(DEBUG_INFO, "image tag 0x%08x not found\n", image_tag);
|
|
}
|
|
}
|
|
|
|
return paint_set_picture_list(pict_info, image_offset, image_count);
|
|
}
|
|
|
|
int paint_set_picture(struct image_info *pict_info)
|
|
{
|
|
return paint_set_picture_list(pict_info, 0, 1);
|
|
}
|
|
|
|
int paint_set_picture_list(struct image_info *pict_info, int image_offset, int image_count)
|
|
{
|
|
void *comp_image_buf;
|
|
size_t comp_image_len;
|
|
|
|
/* must be initialised */
|
|
if (!paint_ready) {
|
|
return 0;
|
|
}
|
|
|
|
/* if no image, clear the display and all images */
|
|
if (pict_info == 0) {
|
|
clear_flattened_images();
|
|
return 0;
|
|
}
|
|
|
|
/* if we're out of space, we can't do anything */
|
|
if (flattened_images_count >= MAX_FLATTENED_IMAGES) {
|
|
return 0;
|
|
}
|
|
|
|
comp_image_len = pict_info->imageAllocation;
|
|
if (comp_image_len > compressed_image_size) {
|
|
dprintf(DEBUG_INFO, "compressed image too large\n");
|
|
return -1;
|
|
}
|
|
comp_image_buf = (uint8_t *)(compressed_image_base);
|
|
|
|
/* accept any type (pass NULL) */
|
|
if (image_load(pict_info, NULL, 0, NULL, &comp_image_buf, &comp_image_len) < 0) {
|
|
dprintf(DEBUG_INFO, "cannot load image\n");
|
|
return -1;
|
|
}
|
|
|
|
// before drawing picture, consolidate environment based on image validation result
|
|
security_consolidate_environment();
|
|
|
|
for (int idx = 0; idx < image_offset + image_count; idx++) {
|
|
iboot_image *ind_comp_image = comp_image_buf;
|
|
size_t ind_comp_image_len;
|
|
|
|
if (ind_comp_image->image_data_length == 0) {
|
|
// old format
|
|
ind_comp_image_len = comp_image_len;
|
|
} else if ((comp_image_len >= sizeof(iboot_image)) && (comp_image_len >= (sizeof(iboot_image) + ind_comp_image->image_data_length))) {
|
|
ind_comp_image_len = sizeof(iboot_image) + ind_comp_image->image_data_length;
|
|
} else {
|
|
dprintf(DEBUG_INFO, "image length invalid\n");
|
|
return -1;
|
|
}
|
|
|
|
if (idx >= image_offset) {
|
|
int ret = paint_draw_picture(ind_comp_image, ind_comp_image_len);
|
|
if (ret != 0)
|
|
return ret;
|
|
}
|
|
|
|
comp_image_buf = ((uint8_t *)comp_image_buf) + ind_comp_image_len;
|
|
comp_image_len -= ind_comp_image_len;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int paint_set_picture_from_file(const char *path, uint32_t type)
|
|
{
|
|
addr_t comp_image_buf;
|
|
size_t comp_image_len;
|
|
|
|
/* must be initialised */
|
|
if (!paint_ready) {
|
|
return 0;
|
|
}
|
|
|
|
/* if we're out of space, we can't do anything */
|
|
if (flattened_images_count >= MAX_FLATTENED_IMAGES) {
|
|
return 0;
|
|
}
|
|
|
|
comp_image_buf = compressed_image_base;
|
|
comp_image_len = compressed_image_size;
|
|
if (image_load_file(path, &comp_image_buf, &comp_image_len, &type, 1, NULL, 0)) {
|
|
dprintf(DEBUG_INFO, "cannot load image\n");
|
|
return -1;
|
|
}
|
|
|
|
// before drawing picture, consolidate environment based on image validation result
|
|
security_consolidate_environment();
|
|
|
|
return paint_draw_picture((void *)compressed_image_base, comp_image_len);
|
|
}
|
|
|
|
static int paint_draw_picture(iboot_image *comp_image, size_t comp_image_len)
|
|
{
|
|
uint8_t *image_buf, *rot_buf;
|
|
uint32_t image_max_len, image_len, image_expected_len, data;
|
|
uint32_t *image_src32, *image_dst;
|
|
uint16_t *image_src16;
|
|
int32_t imagex, imagey, imagexs, imagexe, imageys, imageye;
|
|
int32_t imagex_offset, imagey_offset;
|
|
int32_t posx, posy, rposx, rposy;
|
|
int32_t picture_scale;
|
|
int32_t picture_rotate;
|
|
bool image_color;
|
|
|
|
image_max_len = flattened_images_end - flattened_images_base;
|
|
image_buf = (uint8_t *)(flattened_images_base);
|
|
|
|
if ((comp_image_len < sizeof(iboot_image)) ||
|
|
(strncmp(comp_image->image_signature, "iBootIm", 8) != 0) ||
|
|
(comp_image->image_type != 'lzss') ||
|
|
((comp_image->image_format != 'argb') && (comp_image->image_format != 'grey'))) {
|
|
dprintf(DEBUG_INFO, "image not valid\n");
|
|
return -1;
|
|
}
|
|
|
|
image_color = false;
|
|
if (comp_image->image_format == 'argb') image_color = true;
|
|
|
|
picture_scale = mib_get_u32(kMIBTargetPictureScale);
|
|
ASSERT((picture_scale == 1) || (picture_scale == 2));
|
|
picture_rotate = mib_get_s32(kMIBTargetPictureRotate);
|
|
|
|
if (picture_rotate == 0) {
|
|
imagex = comp_image->image_width * picture_scale;
|
|
imagey = comp_image->image_height * picture_scale;
|
|
imagex_offset = comp_image->image_h_offset * picture_scale;
|
|
imagey_offset = comp_image->image_v_offset * picture_scale;
|
|
} else if ((picture_rotate == 90) || (picture_rotate == -90)) {
|
|
imagex = comp_image->image_height * picture_scale;
|
|
imagey = comp_image->image_width * picture_scale;
|
|
imagex_offset = comp_image->image_v_offset * picture_scale * ((picture_rotate == 90) ? -1 : 1);
|
|
imagey_offset = comp_image->image_h_offset * picture_scale * ((picture_rotate == 90) ? 1 : -1);
|
|
} else {
|
|
panic("kMIBTargetPictureRotate value is unsupported: %d", picture_rotate);
|
|
}
|
|
|
|
imagexs = ((window_width - imagex) / 2) + imagex_offset;
|
|
imagexe = imagexs + imagex;
|
|
imageys = ((window_height - imagey) / 2) + imagey_offset;
|
|
imageye = imageys + imagey;
|
|
|
|
if ((imagexs < 0) || (imageys < 0) || (imagexe > (int32_t)window_width) || (imageye > (int32_t)window_height)) {
|
|
dprintf(DEBUG_INFO, "image does not fit on screen\n");
|
|
return -1;
|
|
}
|
|
|
|
image_expected_len = (uint32_t)(imagex / picture_scale) * (uint32_t)(imagey / picture_scale) * (image_color ? 4 : 2);
|
|
|
|
if (picture_rotate != 0) {
|
|
if ((image_expected_len * 2) > image_max_len) {
|
|
dprintf(DEBUG_INFO, "image is too large to be rotate\n");
|
|
return -1;
|
|
}
|
|
|
|
rot_buf = image_buf;
|
|
image_buf += image_expected_len;
|
|
image_max_len -= image_expected_len;
|
|
}
|
|
|
|
image_len = decompress_lzss(image_buf, image_max_len, comp_image->image_data, comp_image_len - sizeof(iboot_image));
|
|
|
|
if (image_len != image_expected_len) {
|
|
dprintf(DEBUG_INFO, "image data size does not match expected image size\n");
|
|
return -1;
|
|
}
|
|
|
|
if (picture_scale == 2) {
|
|
image_src16 = (uint16_t *)image_buf + (imagex / picture_scale) * (imagey / picture_scale) - 1;
|
|
image_src32 = (uint32_t *)image_buf + (imagex / picture_scale) * (imagey / picture_scale) - 1;
|
|
if (image_color) image_dst = (uint32_t *)image_buf + imagex * imagey - 1;
|
|
else image_dst = (uint32_t *)image_buf + (imagex / 2) * imagey - 1;
|
|
for (posy = 0; posy < (imagey / picture_scale); posy++) {
|
|
for (posx = 0; posx < (imagex / picture_scale); posx++) {
|
|
if (image_color) {
|
|
data = *(image_src32--);
|
|
*(image_dst - 0) = data;
|
|
*(image_dst - 1) = data;
|
|
*(image_dst - imagex - 0) = data;
|
|
*(image_dst - imagex - 1) = data;
|
|
image_dst -= 2;
|
|
} else {
|
|
data = *(image_src16--);
|
|
data |= data << 16;
|
|
*(image_dst) = data;
|
|
*(image_dst - imagex / 2) = data;
|
|
image_dst -= 1;
|
|
}
|
|
}
|
|
image_dst -= imagex / (image_color ? 1 : 2);
|
|
}
|
|
}
|
|
|
|
if ((picture_rotate == 90) || (picture_rotate == -90)) {
|
|
image_src16 = (uint16_t *)image_buf;
|
|
image_src32 = (uint32_t *)image_buf;
|
|
for (posx = 0; posx < imagex; posx++) {
|
|
if (picture_rotate == 90) rposx = imagex - 1 - posx;
|
|
else rposx = posx;
|
|
for (posy = 0; posy < imagey; posy++) {
|
|
if (picture_rotate == 90) rposy = posy;
|
|
else rposy = imagey - 1 - posy;
|
|
if (image_color) {
|
|
((uint32_t *)rot_buf)[rposy * imagex + rposx] = *(image_src32++);
|
|
} else {
|
|
((uint16_t *)rot_buf)[rposy * imagex + rposx] = *(image_src16++);
|
|
}
|
|
}
|
|
}
|
|
|
|
image_buf = rot_buf;
|
|
}
|
|
|
|
flattened_images[flattened_images_count].fi_color = image_color;
|
|
flattened_images[flattened_images_count].fi_width = imagex;
|
|
flattened_images[flattened_images_count].fi_height = imagey;
|
|
flattened_images[flattened_images_count].fi_xstart = imagexs;
|
|
flattened_images[flattened_images_count].fi_ystart = imageys;
|
|
flattened_images[flattened_images_count].fi_xstop = imagexs + imagex - 1;
|
|
flattened_images[flattened_images_count].fi_ystop = imageys + imagey - 1;
|
|
flattened_images[flattened_images_count].fi_buffer = image_buf;
|
|
|
|
flattened_images_base += image_len * picture_scale * picture_scale;
|
|
|
|
flattened_images_count++;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static uint32_t map_bgvalue(uint32_t bgvalue)
|
|
{
|
|
if (color_map_enabled && (paint_canvas->map_color != NULL)) {
|
|
return paint_canvas->map_color(bgvalue);
|
|
} else {
|
|
return bgvalue;
|
|
}
|
|
|
|
}
|
|
|
|
static void update_framebuffer(void)
|
|
{
|
|
uint32_t i;
|
|
size_t frame_size;
|
|
|
|
if (!paint_ready) return;
|
|
|
|
//copy final pixels from draw buffer to frame buffer
|
|
for (i = 0; i < paint_canvas->num_of_active_planes; i++) {
|
|
frame_size = paint_canvas->y * paint_canvas->planes[i].line_x * paint_canvas->planes[i].pixel_size / 8;
|
|
memcpy( paint_canvas->planes[i].fb_virt, paint_canvas->planes[i].db_virt, frame_size );
|
|
}
|
|
}
|
|
|
|
int paint_update_image(void)
|
|
{
|
|
if (!paint_ready) {
|
|
return 0;
|
|
}
|
|
|
|
if (paint_canvas->cs == CS_ARGB8101010)
|
|
return paint_update_image_wide_gammut();
|
|
return paint_update_image_argb();
|
|
}
|
|
|
|
static int paint_update_image_argb(void)
|
|
{
|
|
uint8_t b_alpha;
|
|
uint32_t cnt, x, y, rgbvalue, bgvalue, bg_mask = 0x00FFFFFF;
|
|
struct flattened_image *fi;
|
|
uint32_t images_xstart = UINT32_MAX;
|
|
uint32_t images_xstop = 0;
|
|
uint32_t images_ystart = UINT32_MAX;
|
|
uint32_t images_ystop = 0;
|
|
void (* plot_func)(struct plane *p, u_int32_t x, u_int32_t y, uint64_t color);
|
|
|
|
/* If background color is white and there is an image, make the background black */
|
|
if ((flattened_images_count != 0) && (background_rgb == 0x00FFFFFF)) {
|
|
bg_mask = 0x00000000;
|
|
}
|
|
|
|
bgvalue = background_rgb & bg_mask;
|
|
bgvalue = map_bgvalue(bgvalue);
|
|
|
|
fill_rect(paint_canvas, 0, 0, window_width, window_height, bgvalue);
|
|
|
|
for (cnt = 0; cnt < flattened_images_count; cnt++) {
|
|
flattened_images[cnt].fi_src_pos = 0;
|
|
if (flattened_images[cnt].fi_xstart < images_xstart)
|
|
images_xstart = flattened_images[cnt].fi_xstart;
|
|
if (flattened_images[cnt].fi_xstop > images_xstop)
|
|
images_xstop = flattened_images[cnt].fi_xstop;
|
|
if (flattened_images[cnt].fi_ystart < images_ystart)
|
|
images_ystart = flattened_images[cnt].fi_ystart;
|
|
if (flattened_images[cnt].fi_ystop > images_ystop)
|
|
images_ystop = flattened_images[cnt].fi_ystop;
|
|
}
|
|
|
|
// moving the plot function to the stack saves us some time
|
|
// because of -fno-strict-aliasing
|
|
plot_func = paint_canvas->planes[0].plot;
|
|
|
|
for (y = images_ystart; y <= images_ystop; y++) {
|
|
for (x = images_xstart; x <= images_xstop; x++) {
|
|
|
|
bgvalue = background_rgb & bg_mask;
|
|
|
|
for (cnt = 0; cnt < flattened_images_count; cnt++) {
|
|
fi = flattened_images + cnt;
|
|
|
|
if ((y < fi->fi_ystart) || (y > fi->fi_ystop)) continue;
|
|
if ((x < fi->fi_xstart) || (x > fi->fi_xstop)) continue;
|
|
|
|
if (fi->fi_color) {
|
|
rgbvalue = ((uint32_t *)fi->fi_buffer)[fi->fi_src_pos++];
|
|
} else {
|
|
rgbvalue = ((uint16_t *)fi->fi_buffer)[fi->fi_src_pos++];
|
|
rgbvalue = (rgbvalue << 16) | ((rgbvalue & 0x00FF) << 8) | ((rgbvalue & 0x00FF) << 0);
|
|
}
|
|
|
|
if (rgbvalue == 0xFFFFFFFF) {
|
|
rgbvalue = 0xFF000000;
|
|
}
|
|
|
|
b_alpha = rgbvalue >> 24;
|
|
|
|
if (b_alpha != 0xFF && b_alpha != 0) {
|
|
bgvalue = (((((bgvalue & 0x00FF00FF) * b_alpha) + 0x00FF00FF) >> 8) & 0x00FF00FF) |
|
|
(((((bgvalue & 0x0000FF00) * b_alpha) + 0x0000FF00) >> 8) & 0x0000FF00);
|
|
|
|
bgvalue += rgbvalue;
|
|
} else if (b_alpha == 0) {
|
|
bgvalue = rgbvalue;
|
|
}
|
|
}
|
|
|
|
bgvalue = map_bgvalue(bgvalue);
|
|
plot_func(&(paint_canvas->planes[0]), x, y, bgvalue);
|
|
}
|
|
}
|
|
|
|
update_framebuffer();
|
|
|
|
return 0;
|
|
}
|
|
|
|
static uint64_t paint_draw_plot_convert_argb_to_wide_gammut(struct flattened_image *fi, uint32_t bgvalue)
|
|
{
|
|
uint8_t b_alpha;
|
|
uint32_t rgbvalue;
|
|
uint64_t tmp = bgvalue;
|
|
|
|
if (fi->fi_color) {
|
|
rgbvalue = ((uint32_t *)fi->fi_buffer)[fi->fi_src_pos++];
|
|
} else {
|
|
rgbvalue = ((uint16_t *)fi->fi_buffer)[fi->fi_src_pos++];
|
|
rgbvalue = (rgbvalue << 16) | ((rgbvalue & 0x00FF) << 8) | ((rgbvalue & 0x00FF) << 0);
|
|
}
|
|
|
|
if (rgbvalue == 0xFFFFFFFF) {
|
|
rgbvalue = 0xFF000000;
|
|
}
|
|
|
|
b_alpha = rgbvalue >> 24;
|
|
|
|
if (b_alpha != 0xFF && b_alpha != 0) {
|
|
tmp = (((((tmp & 0x00FF00FF) * b_alpha) + 0x00FF00FF) >> 8) & 0x00FF00FF) |
|
|
(((((tmp & 0x0000FF00) * b_alpha) + 0x0000FF00) >> 8) & 0x0000FF00);
|
|
|
|
tmp += rgbvalue;
|
|
} else if (b_alpha == 0) {
|
|
tmp = rgbvalue;
|
|
}
|
|
//Till now, we have a 32 pixel, convert it to 101010
|
|
tmp = (((uint64_t) b_alpha) << 32) | RGB10(RGB_R(tmp) * 4, RGB_G(tmp) * 4, RGB_B(tmp) * 4);
|
|
|
|
return tmp;
|
|
}
|
|
|
|
static int paint_update_image_wide_gammut(void)
|
|
{
|
|
uint32_t cnt, x, y, bgvalue, bg_mask = 0x3FFFFFFF;
|
|
struct flattened_image *fi;
|
|
uint32_t images_xstart = UINT32_MAX;
|
|
uint32_t images_xstop = 0;
|
|
uint32_t images_ystart = UINT32_MAX;
|
|
uint32_t images_ystop = 0;
|
|
void (* plot_func_p0)(struct plane *p, u_int32_t x, u_int32_t y, uint64_t color);
|
|
void (* plot_func_p1)(struct plane *p, u_int32_t x, u_int32_t y, uint64_t color);
|
|
|
|
/* If background color is white and there is an image, make the background black */
|
|
if ((flattened_images_count != 0) && (background_rgb == 0x3FFFFFFF)) {
|
|
bg_mask = 0x00000000;
|
|
}
|
|
|
|
bgvalue = background_rgb & bg_mask;
|
|
bgvalue = map_bgvalue(bgvalue);
|
|
|
|
fill_rect(paint_canvas, 0, 0, window_width, window_height, bgvalue);
|
|
|
|
for (cnt = 0; cnt < flattened_images_count; cnt++) {
|
|
flattened_images[cnt].fi_src_pos = 0;
|
|
if (flattened_images[cnt].fi_xstart < images_xstart)
|
|
images_xstart = flattened_images[cnt].fi_xstart;
|
|
if (flattened_images[cnt].fi_xstop > images_xstop)
|
|
images_xstop = flattened_images[cnt].fi_xstop;
|
|
if (flattened_images[cnt].fi_ystart < images_ystart)
|
|
images_ystart = flattened_images[cnt].fi_ystart;
|
|
if (flattened_images[cnt].fi_ystop > images_ystop)
|
|
images_ystop = flattened_images[cnt].fi_ystop;
|
|
}
|
|
|
|
// moving the plot function to the stack saves us some time
|
|
// because of -fno-strict-aliasing
|
|
plot_func_p0 = paint_canvas->planes[0].plot;
|
|
plot_func_p1 = paint_canvas->planes[1].plot;
|
|
|
|
for (y = images_ystart; y <= images_ystop; y++) {
|
|
for (x = images_xstart; x <= images_xstop; x++) {
|
|
|
|
bgvalue = background_rgb & bg_mask;
|
|
|
|
for (cnt = 0; cnt < flattened_images_count; cnt++) {
|
|
fi = flattened_images + cnt;
|
|
|
|
if ((y < fi->fi_ystart) || (y > fi->fi_ystop)) continue;
|
|
if ((x < fi->fi_xstart) || (x > fi->fi_xstop)) continue;
|
|
|
|
bgvalue = paint_draw_plot_convert_argb_to_wide_gammut(fi, bgvalue);
|
|
}
|
|
|
|
bgvalue = map_bgvalue(bgvalue);
|
|
plot_func_p0(&(paint_canvas->planes[0]), x, y, bgvalue);
|
|
plot_func_p1(&(paint_canvas->planes[1]), x, y, bgvalue);
|
|
}
|
|
}
|
|
|
|
update_framebuffer();
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* after the fact get the information about the current display */
|
|
int display_get_info(struct display_info *info)
|
|
{
|
|
uint32_t rotate = ((360 + mib_get_s32(kMIBTargetOsPictureRotate)) / 90) % 4;
|
|
uint32_t os_picture_scale = mib_get_u32(kMIBTargetOsPictureScale);
|
|
uint32_t depth;
|
|
|
|
if (paint_ready) {
|
|
info->stride = window_stride;
|
|
info->width = window_width;
|
|
info->height = window_height;
|
|
depth = (paint_canvas->cs == CS_ARGB8101010) ? 30 : window_depth;
|
|
info->depth = depth | (rotate << 8) | ((os_picture_scale - 1) << 16);
|
|
info->framebuffer = window_framebuffer;
|
|
} else {
|
|
info->stride = 640 * 4;
|
|
info->width = 640;
|
|
info->height = 1136;
|
|
|
|
/*
|
|
* First setting scale as workaround for <rdar://problem/11342009>.
|
|
* This lets us use the display properly even if the iBoot display system
|
|
* is not active (e.g. fastsim support).
|
|
*/
|
|
info->depth = 32 | ((os_picture_scale - 1) << 16);
|
|
info->framebuffer = (uintptr_t)mib_get_addr(kMIBTargetDisplayBaseAddress);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void paint_install_gamma_table(uint32_t display_id,
|
|
uint32_t entries,
|
|
volatile uint32_t *red_lut,
|
|
volatile uint32_t *green_lut,
|
|
volatile uint32_t *blue_lut)
|
|
{
|
|
unsigned int cnt;
|
|
const uint8_t *gamma_data = 0;
|
|
uint32_t gamma_table_count;
|
|
const display_gamma_table *target_gamma_table_base;
|
|
const display_gamma_table *target_gamma_table = 0;
|
|
#ifndef PAINT_UNITTEST
|
|
static struct syscfg_glcl syscfg_glcl;
|
|
|
|
cnt = syscfgCopyDataForTag('GLCl', (uint8_t *)&syscfg_glcl, sizeof(syscfg_glcl));
|
|
if (cnt == sizeof(syscfg_glcl)) {
|
|
if (syscfg_glcl.major_vers != 1) {
|
|
dprintf(DEBUG_CRITICAL, "paint_install_gamma_table: Unknown version %u.%u\n",
|
|
syscfg_glcl.major_vers, syscfg_glcl.minor_vers);
|
|
} else {
|
|
dprintf(DEBUG_CRITICAL, "int_install_gamma_table: Found syscfg gamma table\n");
|
|
gamma_data = syscfg_glcl.gamma_data;
|
|
}
|
|
}
|
|
|
|
gamma_table_count = mib_get_u32(kMIBTargetDisplayGammaTableCount);
|
|
#else
|
|
gamma_table_count = 0;
|
|
#endif
|
|
if (gamma_table_count != 0) {
|
|
#ifndef PAINT_UNITTEST
|
|
target_gamma_table_base = (const display_gamma_table *)mib_get_ptr(kMIBTargetDisplayGammaTablePtr);
|
|
#else
|
|
target_gamma_table_base = 0;
|
|
#endif
|
|
for (cnt = 0; gamma_data == 0 && cnt < gamma_table_count; cnt++) {
|
|
target_gamma_table = target_gamma_table_base + cnt;
|
|
if ((display_id & target_gamma_table->display_id_mask) == target_gamma_table->display_id) {
|
|
dprintf(DEBUG_CRITICAL, "paint_install_gamma_table: Found Gamma table 0x%08x / 0x%08x\n",
|
|
target_gamma_table->display_id, target_gamma_table->display_id_mask);
|
|
gamma_data = target_gamma_table->display_gamma_data;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (gamma_data != 0) {
|
|
paint_install_gamma_table_channel(entries, red_lut, gamma_data + kGammaChannelBufferSize * 0);
|
|
paint_install_gamma_table_channel(entries, green_lut, gamma_data + kGammaChannelBufferSize * 1);
|
|
paint_install_gamma_table_channel(entries, blue_lut, gamma_data + kGammaChannelBufferSize * 2);
|
|
} else {
|
|
printf("paint_install_gamma_table: No Gamma table found for display_id: 0x%08x\n", display_id);
|
|
}
|
|
|
|
}
|
|
|
|
void paint_get_syscfg_wpcl(struct syscfg_wpcl *wpcl)
|
|
{
|
|
int size;
|
|
|
|
#ifndef PAINT_UNITTEST
|
|
size = syscfgCopyDataForTag('WpCl', (u_int8_t *)wpcl, sizeof(struct syscfg_wpcl));
|
|
#else
|
|
size = 0;
|
|
#endif
|
|
if ((size != sizeof(struct syscfg_wpcl)) || (0 == wpcl->version)) {
|
|
wpcl->version = 0;
|
|
wpcl->red = wpcl->green = wpcl->blue = (1 << WpCl_Quotation_Denominator);
|
|
}
|
|
}
|
|
|
|
#if DEBUG_BUILD
|
|
|
|
static uint32_t displaytest_colors_argb[] = {
|
|
0x00FF0000, 0x00000000, 0x0000FF00, 0x00000000, 0x000000FF, 0x00000000, 0x00FFFFFF, 0x00000000
|
|
};
|
|
#define DISPLAYTEST_COLORS_COUNT_ARGB (sizeof(displaytest_colors_argb) / sizeof(displaytest_colors_argb[0]))
|
|
|
|
static uint32_t displaytest_colors_wide_gammut[] = {
|
|
0x3FF00000, 0x00000000, 0x000FFC00, 0x00000000, 0x000003FF, 0x00000000, 0x3FFFFFFF, 0x00000000
|
|
};
|
|
#define DISPLAYTEST_COLORS_COUNT_WG (sizeof(displaytest_colors_wide_gammut) / sizeof(displaytest_colors_wide_gammut[0]))
|
|
|
|
int paint_displaytest(void)
|
|
{
|
|
uint32_t cnt, displaytes_color_count;
|
|
uint32_t *displaytest_colors;
|
|
|
|
if (!paint_ready) {
|
|
return 0;
|
|
}
|
|
if (paint_canvas->cs == CS_ARGB8101010) {
|
|
displaytest_colors = displaytest_colors_wide_gammut;
|
|
displaytes_color_count = DISPLAYTEST_COLORS_COUNT_WG;
|
|
} else {
|
|
displaytest_colors = displaytest_colors_argb;
|
|
displaytes_color_count = DISPLAYTEST_COLORS_COUNT_ARGB;
|
|
}
|
|
|
|
for (cnt = 0; cnt < displaytes_color_count; cnt++) {
|
|
fill_rect(paint_canvas, cnt, cnt, window_width - (2 * cnt), window_height - (2 * cnt), displaytest_colors[cnt]);
|
|
}
|
|
|
|
update_framebuffer();
|
|
return 0;
|
|
}
|
|
|
|
#endif
|
|
|
|
static uint32_t pig_offset;
|
|
static uint32_t pig_bitpos;
|
|
static uint8_t pig_data;
|
|
|
|
static void paint_install_gamma_table_channel(uint32_t entries, volatile uint32_t *channel_lut, const uint8_t *bits)
|
|
{
|
|
uint32_t cnt;
|
|
uint16_t lasts;
|
|
uint8_t lastc;
|
|
int8_t nibble;
|
|
|
|
nibble = 0;
|
|
lastc = 0;
|
|
lasts = 0;
|
|
|
|
channel_lut[0] = lasts;
|
|
|
|
pig_offset = 0;
|
|
pig_bitpos = 0;
|
|
pig_data = 0;
|
|
|
|
for (cnt = 1; cnt < entries; cnt++) {
|
|
switch (paint_install_gamma_get_2bits(bits)) {
|
|
case 0: nibble = 0; break;
|
|
case 1: nibble = 1; break;
|
|
case 3: nibble = -1; break;
|
|
case 2:
|
|
nibble = paint_install_gamma_get_2bits(bits) | (paint_install_gamma_get_2bits(bits) << 2);
|
|
if ((nibble & 8) == 8) nibble |= 0xF0;
|
|
break;
|
|
}
|
|
|
|
if (cnt == 1) nibble += 8;
|
|
|
|
lastc += nibble;
|
|
lasts += lastc;
|
|
channel_lut[cnt] = lasts;
|
|
}
|
|
}
|
|
|
|
static uint8_t paint_install_gamma_get_2bits(const uint8_t *bits)
|
|
{
|
|
uint8_t bits2;
|
|
|
|
if (pig_bitpos == 0) {
|
|
pig_data = bits[pig_offset++];
|
|
}
|
|
|
|
bits2 = (pig_data >> pig_bitpos) & 3;
|
|
|
|
pig_bitpos += 2;
|
|
if (pig_bitpos == 8) pig_bitpos = 0;
|
|
|
|
return bits2;
|
|
}
|
|
|
|
void fill_rect(struct canvas *c, uint32_t top_x, uint32_t top_y, uint32_t w, uint32_t h, uint64_t color)
|
|
{
|
|
uint32_t y;
|
|
|
|
if (top_x >= c->x || top_y >= c->y)
|
|
return;
|
|
if (top_x + w > c->x)
|
|
w = c->x - top_x;
|
|
if (top_y + h > c->y)
|
|
h = c->y - top_y;
|
|
|
|
for (y = top_y; y < top_y + h; y++) {
|
|
struct plane *p0 = &(c->planes[0]);
|
|
struct plane *p1 = &(c->planes[1]);
|
|
p0->hline(p0, top_x, y, w, color);
|
|
if (p1 && p1->hline) p1->hline(p1, top_x, y, w, color);
|
|
}
|
|
}
|
|
|
|
void plot(struct plane *p, uint32_t x, uint32_t y, uint64_t color)
|
|
{
|
|
p->plot(p, x, y, color);
|
|
}
|
|
|
|
uint32_t get_pixel(struct plane *p, uint32_t x, uint32_t y)
|
|
{
|
|
return p->get_pixel(p, x, y);
|
|
}
|
|
|
|
|
|
struct canvas *get_canvas(void)
|
|
{
|
|
return paint_canvas;
|
|
}
|
|
|
|
struct plane *get_plane(uint32_t plane_num)
|
|
{
|
|
if (!paint_canvas)
|
|
return NULL;
|
|
|
|
RELEASE_ASSERT(plane_num < MAX_NUM_PLANES_PER_CANVAS);
|
|
return (&(paint_canvas->planes[plane_num]));
|
|
}
|
|
|
|
|
|
#if SYSCFG_DCLR_OVERRIDE
|
|
// Testing function to permit specification of 'DClr' syscfg data in
|
|
// environment variables for devices that don't actually have 'DClr' yet.
|
|
static int dclrOverride(uint8_t *DClr, size_t length)
|
|
{
|
|
unsigned int i, j;
|
|
const char *env;
|
|
size_t envLength;
|
|
uint8_t c;
|
|
|
|
if ((DClr == NULL) || (length != sizeof(syscfg_DClr_t))) {
|
|
return -1;
|
|
}
|
|
|
|
env = env_get("DClr_override");
|
|
if (env == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
envLength = strlen(env);
|
|
if (envLength != (length * 2)) {
|
|
dprintf(DEBUG_CRITICAL, "DClr: DClr_override length is %zu should be %zu\n", envLength, length * 2);
|
|
return -1;
|
|
}
|
|
|
|
for (i = 0; i < length; ++i) {
|
|
DClr[i] = 0;
|
|
for (j = 0; j < 2; ++j) {
|
|
c = *env++;
|
|
if (c >= '0' && c <= '9')
|
|
c -= '0';
|
|
else if (c >= 'A' && c <= 'F')
|
|
c -= 'A' - 10;
|
|
else if (c >= 'a' && c <= 'f')
|
|
c -= 'a' - 10;
|
|
else {
|
|
dprintf(DEBUG_CRITICAL, "DClr: DClr_override: not a hex digit: %c\n", c);
|
|
return -1;
|
|
}
|
|
|
|
DClr[i] += c << (4 * (1 - j));
|
|
}
|
|
}
|
|
return length;
|
|
}
|
|
#endif // SYSCFG_DCLR_OVERRIDE
|
|
|
|
#if SYSCFG_DCLR_COLOR_REPORT
|
|
static void report_color(const char *title, u_int8_t r, u_int8_t g, u_int8_t b)
|
|
{
|
|
dprintf(DEBUG_INFO, "DClr: %s R/G/B = %d/%d/%d (0x%08X)\n", title, r, g, b, RGB(r, g, b));
|
|
}
|
|
#endif
|
|
|
|
int paint_color_map_get_DClr(syscfg_DClr_t *DClr, size_t size, bool report)
|
|
{
|
|
int result;
|
|
bool die = false;
|
|
|
|
// Validate parameters.
|
|
if ((DClr == NULL) || (size != sizeof(*DClr))) {
|
|
dprintf(DEBUG_CRITICAL, "DClr: paint_color_map_get_DClr: Bad parameters\n");
|
|
die = PANIC_ON_SERIOUS_ERROR;
|
|
goto fail;
|
|
}
|
|
|
|
#if SYSCFG_DCLR_OVERRIDE
|
|
result = dclrOverride((uint8_t *)DClr, size);
|
|
if (result == sizeof(*DClr)) {
|
|
dprintf(DEBUG_INFO, "DClr: Overriding syscfg data from environment\n");
|
|
} else
|
|
#endif
|
|
{
|
|
// Look up the 'DClr' tag from syscfg.
|
|
#ifndef PAINT_UNITTEST
|
|
result = syscfgCopyDataForTag('DClr', (uint8_t *)DClr, sizeof(*DClr));
|
|
#else
|
|
result = 0;
|
|
#endif
|
|
if (result < 0) {
|
|
#ifndef PAINT_UNITTEST
|
|
result = target_dclr_from_clrc((uint8_t *)DClr, sizeof(*DClr));
|
|
#else
|
|
result = 0;
|
|
#endif
|
|
if (result < 0) {
|
|
dprintf(DEBUG_INFO,
|
|
"DClr: syscfg entry not found -- color remapping disabled\n");
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (result != sizeof(*DClr)) {
|
|
dprintf(DEBUG_CRITICAL, "DClr: syscfgCopyDataForTag returned invalid length: %d\n",
|
|
result);
|
|
// Defer panic to bootprep if an attempt is made to boot an OS with this error enabled.
|
|
color_map_invalid |= PANIC_ON_SERIOUS_ERROR;
|
|
goto fail;
|
|
}
|
|
|
|
switch (DCLR_VERSION(DClr->major_version, DClr->minor_version)) {
|
|
case DCLR_VERSION(2,0):
|
|
if (report) {
|
|
#if SYSCFG_DCLR_COLOR_REPORT
|
|
report_color("Device enclosure",
|
|
DClr->device_enclosure.component.red,
|
|
DClr->device_enclosure.component.green,
|
|
DClr->device_enclosure.component.blue);
|
|
report_color("Device cover glass",
|
|
DClr->cover_glass.component.red,
|
|
DClr->cover_glass.component.green,
|
|
DClr->cover_glass.component.blue);
|
|
#endif
|
|
}
|
|
break;
|
|
|
|
default:
|
|
dprintf(DEBUG_CRITICAL, "DClr: Unknown version: %d.%d\n",
|
|
DClr->major_version, DClr->minor_version);
|
|
// Defer panic to bootprep if an attempt is made to boot an OS with this error enabled.
|
|
color_map_invalid |= mib_get_bool(kMIBTargetPanicOnUnknownDclrVersion);
|
|
goto fail;
|
|
}
|
|
|
|
return result;
|
|
|
|
fail:
|
|
if (die) {
|
|
panic("DClr: Fatal error");
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// Standard color remapping function for COLOR_MAP_POLICY_INVERT
|
|
static uint32_t invert_color_rbg888(uint64_t color)
|
|
{
|
|
color ^= 0x00FFFFFF;
|
|
return color;
|
|
}
|
|
|
|
static uint32_t invert_color_rbg101010(uint64_t color)
|
|
{
|
|
color ^= 0x3FFFFFFF;
|
|
return color;
|
|
}
|
|
|
|
void color_map_init(struct canvas *c)
|
|
{
|
|
color_policy_t *target_policy;
|
|
syscfg_DClr_t DClr;
|
|
int result;
|
|
bool die = false;
|
|
|
|
// Assume color remapping is not supported.
|
|
color_map_desired = false;
|
|
color_map_enabled = false;
|
|
color_map_supported = false;
|
|
color_map_invalid = false;
|
|
|
|
// Must have a canvas.
|
|
if (c == NULL) {
|
|
dprintf(DEBUG_CRITICAL, "Bad parameter: color_map_init(NULL)\n");
|
|
die = PANIC_ON_SERIOUS_ERROR;
|
|
goto fail;
|
|
}
|
|
|
|
// We only support the RGB888 colorspace and ARGB8101010.
|
|
if ((c->cs != CS_RGB888) && (c->cs != CS_ARGB8101010)) {
|
|
dprintf(DEBUG_CRITICAL, "DClr: Only the RBG888 colorspace is supported\n");
|
|
goto fail;
|
|
}
|
|
|
|
// Ask the target what it's color remapping policy is.
|
|
#ifndef PAINT_UNITTEST
|
|
target_policy = target_color_map_init(c->cs, &color_policy);
|
|
#else
|
|
target_policy = 0;
|
|
#endif
|
|
if (target_policy == NULL) {
|
|
goto fail;
|
|
}
|
|
|
|
switch (target_policy->policy_type) {
|
|
case COLOR_MAP_POLICY_NONE: // Standard policy: No color remapping
|
|
return;
|
|
|
|
case COLOR_MAP_POLICY_INVERT: // Standard policy: Invert colors
|
|
{
|
|
color_policy_invert_t *color_table; // Target color table
|
|
uint32_t cover_glass; // Cover glass color
|
|
uint8_t r, g, b; // Color components
|
|
unsigned int i;
|
|
|
|
// Validate what the target gave us.
|
|
if ((target_policy->color_table == NULL) || (target_policy->color_count == 0)) {
|
|
dprintf(DEBUG_CRITICAL, "target_color_map_init() returned invalid color table\n");
|
|
die = PANIC_ON_SERIOUS_ERROR;
|
|
goto fail;
|
|
}
|
|
|
|
// Look up the device's 'DClr' syscfg entry.
|
|
result = paint_color_map_get_DClr(&DClr, sizeof(DClr), true);
|
|
if (result != sizeof(DClr)) {
|
|
goto fail;
|
|
}
|
|
|
|
// Get the cover glass color.
|
|
switch (DCLR_VERSION(DClr.major_version, DClr.minor_version)) {
|
|
|
|
case DCLR_VERSION(2,0):
|
|
r = DClr.cover_glass.component.red;
|
|
g = DClr.cover_glass.component.green;
|
|
b = DClr.cover_glass.component.blue;
|
|
cover_glass = ColorRGB888(DClr.cover_glass.rgb);
|
|
break;
|
|
|
|
default:
|
|
// Should have been caught in color_map_get_DClr().
|
|
// Defer panic to bootprep if an attempt is made to
|
|
// boot an OS with this error enabled.
|
|
color_map_invalid |= PANIC_ON_SERIOUS_ERROR;
|
|
goto fail;
|
|
}
|
|
|
|
// Get the target's inversion color table.
|
|
color_table = (color_policy_invert_t *)(target_policy->color_table);
|
|
|
|
// Match the cover glass color against the expected colors for this target.
|
|
for (i = 0; i < target_policy->color_count; ++i) {
|
|
if (cover_glass == color_table[i].cover_glass) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i >= target_policy->color_count) {
|
|
dprintf(DEBUG_CRITICAL,
|
|
"DClr: Unsupported cover glass color: R/G/B = %d/%d/%d (0x%08X)\n",
|
|
r, g, b, cover_glass);
|
|
// Defer panic to bootprep if an attempt is made to boot an OS with this error enabled.
|
|
color_map_invalid |= PANIC_ON_UNKNOWN_DCLR_COLOR;
|
|
goto fail;
|
|
}
|
|
|
|
dprintf(DEBUG_INFO, "DClr: Color mapping policy is to %s colors\n",
|
|
color_table[i].invert ? "invert" : "keep");
|
|
|
|
// If the target policy is to invert the colors, then set the
|
|
// color remapping functions. Otherwise, leave the function
|
|
// pointers NULL to keep the source background/foreground colors.
|
|
if (color_table[i].invert) {
|
|
if (c->cs == CS_ARGB8101010) c->map_color = invert_color_rbg101010;
|
|
else c->map_color = invert_color_rbg888;
|
|
color_map_desired = true;
|
|
}
|
|
|
|
color_map_supported = true;
|
|
return;
|
|
}
|
|
|
|
default:
|
|
dprintf(DEBUG_CRITICAL, "target_color_map_init() returned unknown policy type: 0x%08X\n",
|
|
target_policy->policy_type);
|
|
die = PANIC_ON_SERIOUS_ERROR;
|
|
goto fail;
|
|
}
|
|
|
|
fail:
|
|
color_policy.policy_type = COLOR_MAP_POLICY_NONE;
|
|
color_map_desired = false;
|
|
color_map_enabled = false;
|
|
color_map_supported = false;
|
|
|
|
if (die) {
|
|
panic("DClr: Fatal error");
|
|
}
|
|
}
|
|
|
|
bool paint_color_map_enable(bool enable)
|
|
{
|
|
bool result = color_map_enabled;
|
|
if (!color_map_supported) {
|
|
enable = false;
|
|
}
|
|
color_map_enabled = enable;
|
|
dprintf(DEBUG_SPEW, "DClr: Color remapping %sabled\n",
|
|
color_map_enabled ? "en" : "dis");
|
|
|
|
return result;
|
|
}
|
|
|
|
bool paint_color_map_is_desired(void)
|
|
{
|
|
return color_map_desired;
|
|
}
|
|
|
|
bool paint_color_map_is_enabled(void)
|
|
{
|
|
return color_map_enabled;
|
|
}
|
|
|
|
bool paint_color_map_is_invalid(void)
|
|
{
|
|
return color_map_invalid;
|
|
}
|
|
|
|
enum color_map_policy paint_get_color_policy(void)
|
|
{
|
|
enum color_map_policy policy = color_policy.policy_type;
|
|
|
|
switch (policy) {
|
|
case COLOR_MAP_POLICY_NONE:
|
|
break;
|
|
|
|
case COLOR_MAP_POLICY_INVERT:
|
|
if (!color_map_desired) {
|
|
policy = COLOR_MAP_POLICY_NONE;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return policy;
|
|
}
|
|
|
|
static addr_t alloc_display_valid_region_memory(size_t frame_size_in_bytes)
|
|
{
|
|
RELEASE_ASSERT(page_size != 0);
|
|
RELEASE_ASSERT(last_canvas_addr != 0);
|
|
|
|
addr_t addr = last_canvas_addr;
|
|
last_canvas_addr += (frame_size_in_bytes + page_size - 1) & ~((addr_t)(page_size - 1));
|
|
|
|
return addr;
|
|
}
|
|
|
|
void display_init_alloc_mem(void * fb)
|
|
{
|
|
page_size = mib_get_size(kMIBPlatformPageSize);
|
|
last_canvas_addr = (addr_t) fb;
|
|
}
|
|
|
|
void set_canvas(struct canvas *c, void *fb, size_t region_size, uint32_t x, uint32_t y, uint32_t line_x, enum colorspace cs)
|
|
{
|
|
struct plane *p = &(c->planes[0]);
|
|
struct plane *p1 = &(c->planes[1]);
|
|
|
|
|
|
switch(cs) {
|
|
case CS_4BPP:
|
|
c->num_of_active_planes = 1;
|
|
p->pixel_size = 4;
|
|
p->pixel_size_per_channel = 8;
|
|
p->width = x;
|
|
p->height = y;
|
|
p->stride = line_x * 1; // In bytes
|
|
p->plot = &plot4;
|
|
p->get_pixel = &get_pixel4;
|
|
p->hline = &hline4;
|
|
p->vline = &vline4;
|
|
break;
|
|
case CS_8BPP:
|
|
case CS_RGB332:
|
|
c->num_of_active_planes = 1;
|
|
p->pixel_size = 8;
|
|
p->pixel_size_per_channel = 8;
|
|
p->width = x;
|
|
p->height = y;
|
|
p->stride = line_x * 4; // In bytes
|
|
p->plot = &plot8;
|
|
p->get_pixel = &get_pixel8;
|
|
p->hline = &hline8;
|
|
p->vline = &vline8;
|
|
break;
|
|
case CS_RGB565:
|
|
case CS_ARGB1555:
|
|
case CS_ARGB4444:
|
|
c->num_of_active_planes = 1;
|
|
p->pixel_size = 16;
|
|
p->pixel_size_per_channel = 8;
|
|
p->width = x;
|
|
p->height = y;
|
|
p->stride = line_x * 4; // In bytes
|
|
p->plot = &plot16;
|
|
p->get_pixel = &get_pixel16;
|
|
p->hline = &hline16;
|
|
p->vline = &vline16;
|
|
break;
|
|
case CS_RGB888:
|
|
case CS_ARGB8888:
|
|
c->num_of_active_planes = 1;
|
|
p->pixel_size = 32;
|
|
p->pixel_size_per_channel = 8;
|
|
p->width = x;
|
|
p->height = y;
|
|
p->stride = line_x * 4; // In bytes
|
|
p->plot = &plot32;
|
|
p->get_pixel = &get_pixel32;
|
|
p->hline = &hline32;
|
|
p->vline = &vline32;
|
|
break;
|
|
//10-bit RGB components are stored in packed 4 bytes (same as RGB101010) in RGB10 plane.
|
|
//8-bit Alpha is stored in packed format in A8 plane
|
|
case CS_ARGB8101010:
|
|
c->num_of_active_planes = 2;
|
|
p->pixel_size = 32;
|
|
p->pixel_size_per_channel = 10;
|
|
p->width = x;
|
|
p->height = y;
|
|
p->stride = line_x * 4; // In bytes
|
|
p->plot = &plot32;
|
|
p->get_pixel = &get_pixel32;
|
|
p->hline = &hline32;
|
|
p->vline = &vline32;
|
|
p1->pixel_size = 8;
|
|
p1->pixel_size_per_channel = 8;
|
|
p1->width = x;
|
|
p1->height = y;
|
|
p1->stride = line_x * 1; // In bytes
|
|
p1->plot = &plot8;
|
|
p1->get_pixel = &get_pixel8;
|
|
p1->hline = &hline8;
|
|
p1->vline = &vline8;
|
|
break;
|
|
}
|
|
c->fb_virt = fb;
|
|
c->fb_phys = (uintptr_t)fb;
|
|
c->x = x;
|
|
c->y = y;
|
|
c->cs = cs;
|
|
c->map_color = NULL;
|
|
|
|
//as pixel operations take time, directly drawing on canvas will generate blinking on animation
|
|
//so move the time consuming process on seperated buffer, and then copy the final result into canvas
|
|
//this process requires 2 buffers. The allocation will give the memory for HW used framebuffers, followed by the copies
|
|
display_init_alloc_mem(fb);
|
|
|
|
addr_t plane_0_fb_addr, plane_0_db_addr, plane_1_fb_addr, plane_1_db_addr;
|
|
size_t plane_0_frame_size_in_bytes , plane_1_frame_size_in_bytes;
|
|
|
|
plane_0_frame_size_in_bytes = y * line_x * c->planes[0].pixel_size/8;
|
|
if (c->num_of_active_planes > 1) plane_1_frame_size_in_bytes = y * line_x * c->planes[1].pixel_size/8;
|
|
|
|
//Allocate plane0 framebuffer
|
|
plane_0_fb_addr = alloc_display_valid_region_memory(plane_0_frame_size_in_bytes);
|
|
//if needed, allocate plane1 framebuffer
|
|
if (c->num_of_active_planes > 1) plane_1_fb_addr = alloc_display_valid_region_memory(plane_1_frame_size_in_bytes);
|
|
|
|
//Allocate plane0 buffer for drawing
|
|
plane_0_db_addr = alloc_display_valid_region_memory(plane_0_frame_size_in_bytes);
|
|
//Allocate plane1 buffer for drawing, if needed
|
|
if (c->num_of_active_planes > 1) plane_1_db_addr = alloc_display_valid_region_memory(plane_1_frame_size_in_bytes);
|
|
|
|
set_plane(&(c->planes[0]), y, line_x, plane_0_frame_size_in_bytes, plane_0_fb_addr, plane_0_db_addr, cs);
|
|
if (c->num_of_active_planes > 1) set_plane(&(c->planes[1]), y, line_x, plane_1_frame_size_in_bytes, plane_1_fb_addr, plane_1_db_addr, cs);
|
|
|
|
// Initialize color remapping if supported on this device.
|
|
color_map_init(c);
|
|
}
|
|
|
|
static void set_plane(struct plane *p, uint32_t y, uint32_t line_x, size_t plane_size, addr_t plane_fb, addr_t plane_db, enum colorspace cs)
|
|
{
|
|
p->line_x = line_x;
|
|
p->cs = cs;
|
|
p->fb_virt = (void *) plane_fb;
|
|
p->plane_size = plane_size;
|
|
p->db_virt = (void *) plane_db;
|
|
}
|
|
|
|
static void plot4(struct plane *p, uint32_t x, uint32_t y, uint64_t color)
|
|
{
|
|
panic("plot4 not implemented yet\n");
|
|
}
|
|
|
|
static void plot8(struct plane *p, uint32_t x, uint32_t y, uint64_t color)
|
|
{
|
|
if (p->cs == CS_8BPP) color = ColorIndex8BPP(color);
|
|
else color = ColorRGB332(color);
|
|
|
|
((uint8_t *)p->db_virt)[y * p->line_x + x] = color;
|
|
}
|
|
|
|
static void plot16(struct plane *p, uint32_t x, uint32_t y, uint64_t color)
|
|
{
|
|
if (p->cs == CS_ARGB1555) color = ColorRGB555(color);
|
|
else if (p->cs == CS_ARGB4444) color = ColorRGB444(color);
|
|
else color = ColorRGB565(color);
|
|
|
|
((uint16_t *)p->db_virt)[y * p->line_x + x] = color;
|
|
}
|
|
|
|
static void plot32(struct plane *p, uint32_t x, uint32_t y, uint64_t color)
|
|
{
|
|
if (p->cs == CS_ARGB8101010) color = ColorRGB101010(color);
|
|
else color = ColorRGB888(color);
|
|
|
|
((uint32_t *)p->db_virt)[y * p->line_x + x] = color;
|
|
}
|
|
|
|
static uint32_t get_pixel4(struct plane *p, uint32_t x, uint32_t y)
|
|
{
|
|
panic("get_pixel4 not implemented yet\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static uint32_t get_pixel8(struct plane *p, uint32_t x, uint32_t y)
|
|
{
|
|
panic("get_pixel8 not implemented yet\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static uint32_t get_pixel16(struct plane *p, uint32_t x, uint32_t y)
|
|
{
|
|
panic("get_pixel16 not implemented yet\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
static uint32_t get_pixel32(struct plane *p, uint32_t x, uint32_t y)
|
|
{
|
|
return ((uint32_t *)p->fb_virt)[y * p->line_x + x] & 0x00FFFFFF;
|
|
}
|
|
|
|
static void hline4(struct plane *p, uint32_t x, uint32_t y, uint32_t len, uint64_t color)
|
|
{
|
|
panic("hline4 not implemented yet\n");
|
|
}
|
|
|
|
static void hline8(struct plane *p, uint32_t x, uint32_t y, uint32_t len, uint64_t color)
|
|
{
|
|
uint8_t *ptr;
|
|
|
|
if (p->cs == CS_8BPP) color = ColorIndex8BPP(color);
|
|
else if (p->cs == CS_ARGB8101010) color = ColorA8(color);
|
|
else color = ColorRGB332(color);
|
|
|
|
ptr = &((uint8_t *)p->db_virt)[y * p->line_x + x];
|
|
while (len > 0) {
|
|
*ptr = color;
|
|
ptr++;
|
|
len--;
|
|
}
|
|
}
|
|
|
|
static void hline16(struct plane *p, uint32_t x, uint32_t y, uint32_t len, uint64_t color)
|
|
{
|
|
uint16_t *ptr;
|
|
|
|
if (p->cs == CS_ARGB1555) color = ColorRGB555(color);
|
|
else if (p->cs == CS_ARGB4444) color = ColorRGB444(color);
|
|
else color = ColorRGB565(color);
|
|
|
|
ptr = &((uint16_t *)p->db_virt)[y * p->line_x + x];
|
|
while (len > 0) {
|
|
*ptr = color;
|
|
ptr++;
|
|
len--;
|
|
}
|
|
}
|
|
|
|
static void hline32(struct plane *p, uint32_t x, uint32_t y, uint32_t len, uint64_t color)
|
|
{
|
|
uint32_t *ptr;
|
|
|
|
if (p->cs == CS_ARGB8101010) color = ColorRGB101010(color);
|
|
else color = ColorRGB888(color);
|
|
|
|
ptr = &((uint32_t *)p->db_virt)[y * p->line_x + x];
|
|
while (len > 0) {
|
|
*ptr = color;
|
|
ptr++;
|
|
len--;
|
|
}
|
|
}
|
|
|
|
static void vline4(struct plane *p, uint32_t x, uint32_t y, uint32_t len, uint64_t color)
|
|
{
|
|
panic("vline4 not implemented yet\n");
|
|
}
|
|
|
|
static void vline8(struct plane *p, uint32_t x, uint32_t y, uint32_t len, uint64_t color)
|
|
{
|
|
uint8_t *ptr;
|
|
|
|
if (p->cs == CS_8BPP) color = ColorIndex8BPP(color);
|
|
else color = ColorRGB332(color);
|
|
|
|
ptr = &((uint8_t *)p->db_virt)[y * p->line_x + x];
|
|
while (len > 0) {
|
|
*ptr = color;
|
|
ptr += p->line_x;
|
|
len--;
|
|
}
|
|
}
|
|
|
|
static void vline16(struct plane *p, uint32_t x, uint32_t y, uint32_t len, uint64_t color)
|
|
{
|
|
uint16_t *ptr;
|
|
|
|
if (p->cs == CS_ARGB1555) color = ColorRGB555(color);
|
|
else if (p->cs == CS_ARGB4444) color = ColorRGB444(color);
|
|
else color = ColorRGB565(color);
|
|
|
|
ptr = &((uint16_t *)p->db_virt)[y * p->line_x + x];
|
|
while (len > 0) {
|
|
*ptr = color;
|
|
ptr += p->line_x;
|
|
len--;
|
|
}
|
|
}
|
|
|
|
static void vline32(struct plane *p, uint32_t x, uint32_t y, uint32_t len, uint64_t color)
|
|
{
|
|
uint32_t *ptr;
|
|
|
|
if (p->cs == CS_ARGB8101010) color = ColorRGB101010(color);
|
|
else color = ColorRGB888(color);
|
|
|
|
ptr = &((uint32_t *)p->db_virt)[y * p->line_x + x];
|
|
while (len > 0) {
|
|
*ptr = color;
|
|
ptr += p->line_x;
|
|
len--;
|
|
}
|
|
}
|
|
|
|
#if DEBUG_BUILD
|
|
static char *cs_to_string(enum colorspace cs) {
|
|
|
|
switch (cs) {
|
|
case CS_4BPP: return "4BPP";
|
|
case CS_8BPP: return "8BPP";
|
|
case CS_RGB332: return "RGB332";
|
|
case CS_RGB565: return "RGB565";
|
|
case CS_ARGB1555: return "ARGB1555";
|
|
case CS_ARGB4444: return "ARGB4444";
|
|
case CS_RGB888: return "RGB888";
|
|
case CS_ARGB8888: return "ARGB8888";
|
|
case CS_ARGB8101010: return "ARGB8101010";
|
|
}
|
|
}
|
|
|
|
void dump_paint_plane(struct plane *p, uint32_t plane_num)
|
|
{
|
|
printf("\t\tPlane %d\n", plane_num);
|
|
printf("\t\t\tcs %s\n", cs_to_string(p->cs));
|
|
printf("\t\t\tfb_virt %p\n", p->fb_virt);
|
|
printf("\t\t\tdb_virt %p\n", p->db_virt);
|
|
printf("\t\t\tplane_size %u\n", p->plane_size);
|
|
printf("\t\t\tpixel_size %u\n", p->pixel_size);
|
|
printf("\t\t\tpixel_size_per_channel %u\n", p->pixel_size_per_channel);
|
|
printf("\t\t\tline_x %u\n", p->line_x);
|
|
printf("\t\t\twidth %u\n", p->width);
|
|
printf("\t\t\theight %u\n", p->height);
|
|
printf("\t\t\tstride %u\n", p->stride);
|
|
printf("\t\t\tplot %p\n", p->plot);
|
|
printf("\t\t\tget_pixel %p\n", p->get_pixel);
|
|
printf("\t\t\thline %p\n", p->hline);
|
|
printf("\t\t\tvline %p\n", p->vline);
|
|
|
|
}
|
|
|
|
void dump_paint_canvas(struct canvas *c)
|
|
{
|
|
printf("\tCanvas:\n");
|
|
printf("\t\tcs %s\n", cs_to_string(c->cs));
|
|
printf("\t\tnum_of_active_planes %u\n", c->num_of_active_planes);
|
|
printf("\t\tfb_virt %p\n", c->fb_virt);
|
|
printf("\t\tfb_phys %lu\n", c->fb_phys);
|
|
printf("\t\tx %u\n", c->x);
|
|
printf("\t\ty %u\n", c->y);
|
|
printf("\t\tmap_color %p\n", c->map_color);
|
|
for (uint32_t i = 0; i < c->num_of_active_planes; i++) {
|
|
dump_paint_plane(&(c->planes[i]), i);
|
|
}
|
|
}
|
|
|
|
void dump_paint_information(struct display_window *w)
|
|
{
|
|
printf("Window:\n");
|
|
printf("\tactive\t%d\n", w->active);
|
|
printf("\tcs\t%d\n", w->cs);
|
|
printf("\tdepth\t%d\n", w->depth);
|
|
printf("\tpos_x\t%d\n", w->pos_x);
|
|
printf("\tpos_y\t%d\n", w->pos_y);
|
|
printf("\twidth\t%d\n", w->width);
|
|
printf("\theight\t%d\n", w->height);
|
|
printf("\tstride\t%d\n", w->stride);
|
|
dump_paint_canvas(&(w->c));
|
|
}
|
|
|
|
void display_paint_plane(uint32_t plane_num)
|
|
{
|
|
struct plane *p = &(paint_canvas->planes[plane_num]);
|
|
dump_paint_plane(p, plane_num);
|
|
}
|
|
|
|
void display_paint_canvas()
|
|
{
|
|
dump_paint_canvas(paint_canvas);
|
|
}
|
|
|
|
void display_paint_information()
|
|
{
|
|
dump_paint_information(paint_window);
|
|
}
|
|
#endif // #if DEBUG_BUILD
|