| /* |
| * Copyright 2017 The Chromium OS Authors. All rights reserved. |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "bs_drm.h" |
| |
| struct draw_format_component { |
| float rgba_coeffs[4]; |
| float value_offset; |
| uint32_t horizontal_subsample_rate; |
| uint32_t vertical_subsample_rate; |
| uint32_t byte_skip; |
| uint32_t plane_index; |
| uint32_t plane_offset; |
| }; |
| |
| #define MAX_COMPONENTS 4 |
| struct bs_draw_format { |
| uint32_t pixel_format; |
| const char* name; |
| size_t component_count; |
| struct draw_format_component components[MAX_COMPONENTS]; |
| }; |
| |
| struct draw_data { |
| uint32_t x; |
| uint32_t y; |
| uint32_t w; |
| uint32_t h; |
| float progress; |
| uint8_t out_color[MAX_COMPONENTS]; |
| }; |
| |
| struct draw_data_lines { |
| struct draw_data base; |
| bool color_olive; |
| uint32_t interval; |
| uint32_t stop; |
| }; |
| |
| typedef void (*compute_color_t)(struct draw_data* data); |
| |
| // clang-format off |
| #define PIXEL_FORMAT_AND_NAME(x) GBM_FORMAT_##x, #x |
| static const struct bs_draw_format bs_draw_formats[] = { |
| { |
| PIXEL_FORMAT_AND_NAME(ABGR8888), |
| 4, |
| { |
| { { 1.0f, 0.0f, 0.0f, 0.0f }, 0.0f, 1, 1, 4, 0, 0 }, |
| { { 0.0f, 1.0f, 0.0f, 0.0f }, 0.0f, 1, 1, 4, 0, 1 }, |
| { { 0.0f, 0.0f, 1.0f, 0.0f }, 0.0f, 1, 1, 4, 0, 2 }, |
| { { 0.0f, 0.0f, 0.0f, 1.0f }, 0.0f, 1, 1, 4, 0, 3 }, |
| }, |
| }, |
| { |
| PIXEL_FORMAT_AND_NAME(ARGB8888), |
| 4, |
| { |
| { { 0.0f, 0.0f, 1.0f, 0.0f }, 0.0f, 1, 1, 4, 0, 0 }, |
| { { 0.0f, 1.0f, 0.0f, 0.0f }, 0.0f, 1, 1, 4, 0, 1 }, |
| { { 1.0f, 0.0f, 0.0f, 0.0f }, 0.0f, 1, 1, 4, 0, 2 }, |
| { { 0.0f, 0.0f, 0.0f, 1.0f }, 0.0f, 1, 1, 4, 0, 3 }, |
| }, |
| }, |
| { |
| PIXEL_FORMAT_AND_NAME(ABGR2101010), |
| 4, |
| { |
| { { 1.0f, 0.0f, 0.0f, 0.0f }, 0.0f, 1, 1, 4, 0, 0 }, |
| { { 0.0f, 1.0f, 0.0f, 0.0f }, 0.0f, 1, 1, 4, 0, 1 }, |
| { { 0.0f, 0.0f, 1.0f, 0.0f }, 0.0f, 1, 1, 4, 0, 2 }, |
| { { 0.0f, 0.0f, 0.0f, 1.0f }, 0.0f, 1, 1, 4, 0, 3 }, |
| }, |
| }, |
| { |
| PIXEL_FORMAT_AND_NAME(ARGB2101010), |
| 4, |
| { |
| { { 0.0f, 0.0f, 1.0f, 0.0f }, 0.0f, 1, 1, 4, 0, 0 }, |
| { { 0.0f, 1.0f, 0.0f, 0.0f }, 0.0f, 1, 1, 4, 0, 1 }, |
| { { 1.0f, 0.0f, 0.0f, 0.0f }, 0.0f, 1, 1, 4, 0, 2 }, |
| { { 0.0f, 0.0f, 0.0f, 1.0f }, 0.0f, 1, 1, 4, 0, 3 }, |
| }, |
| }, |
| { PIXEL_FORMAT_AND_NAME(BGR888), |
| 3, |
| { |
| { { 1.0f, 0.0f, 0.0f }, 0.0f, 1, 1, 3, 0, 0 }, |
| { { 0.0f, 1.0f, 0.0f }, 0.0f, 1, 1, 3, 0, 1 }, |
| { { 0.0f, 0.0f, 1.0f }, 0.0f, 1, 1, 3, 0, 2 }, |
| }, |
| }, |
| { |
| PIXEL_FORMAT_AND_NAME(NV12), |
| 3, |
| { |
| { { 0.2567890625f, 0.50412890625f, 0.09790625f }, 16.0f, 1, 1, 1, 0, 0 }, |
| { { -0.14822265625f, -0.2909921875f, 0.43921484375f }, 128.0f, 2, 2, 2, 1, 0 }, |
| { { 0.43921484375f, -0.3677890625f, -0.07142578125f }, 128.0f, 2, 2, 2, 1, 1 }, |
| }, |
| }, |
| { |
| PIXEL_FORMAT_AND_NAME(NV21), |
| 3, |
| { |
| { { 0.2567890625f, 0.50412890625f, 0.09790625f }, 16.0f, 1, 1, 1, 0, 0 }, |
| { { 0.43921484375f, -0.3677890625f, -0.07142578125f }, 128.0f, 2, 2, 2, 1, 0 }, |
| { { -0.14822265625f, -0.2909921875f, 0.43921484375f }, 128.0f, 2, 2, 2, 1, 1 }, |
| }, |
| }, |
| { |
| PIXEL_FORMAT_AND_NAME(RGB888), |
| 3, |
| { |
| { { 0.0f, 0.0f, 1.0f }, 0.0f, 1, 1, 3, 0, 0 }, |
| { { 0.0f, 1.0f, 0.0f }, 0.0f, 1, 1, 3, 0, 1 }, |
| { { 1.0f, 0.0f, 0.0f }, 0.0f, 1, 1, 3, 0, 2 }, |
| }, |
| }, |
| { |
| PIXEL_FORMAT_AND_NAME(XBGR8888), |
| 3, |
| { |
| { { 1.0f, 0.0f, 0.0f }, 0.0f, 1, 1, 4, 0, 0 }, |
| { { 0.0f, 1.0f, 0.0f }, 0.0f, 1, 1, 4, 0, 1 }, |
| { { 0.0f, 0.0f, 1.0f }, 0.0f, 1, 1, 4, 0, 2 }, |
| }, |
| }, |
| { |
| PIXEL_FORMAT_AND_NAME(XRGB8888), |
| 3, |
| { |
| { { 0.0f, 0.0f, 1.0f }, 0.0f, 1, 1, 4, 0, 0 }, |
| { { 0.0f, 1.0f, 0.0f }, 0.0f, 1, 1, 4, 0, 1 }, |
| { { 1.0f, 0.0f, 0.0f }, 0.0f, 1, 1, 4, 0, 2 }, |
| }, |
| }, |
| { |
| PIXEL_FORMAT_AND_NAME(XBGR2101010), |
| 3, |
| { |
| { { 1.0f, 0.0f, 0.0f }, 0.0f, 1, 1, 4, 0, 0 }, |
| { { 0.0f, 1.0f, 0.0f }, 0.0f, 1, 1, 4, 0, 1 }, |
| { { 0.0f, 0.0f, 1.0f }, 0.0f, 1, 1, 4, 0, 2 }, |
| }, |
| }, |
| { |
| PIXEL_FORMAT_AND_NAME(XRGB2101010), |
| 3, |
| { |
| { { 0.0f, 0.0f, 1.0f }, 0.0f, 1, 1, 4, 0, 0 }, |
| { { 0.0f, 1.0f, 0.0f }, 0.0f, 1, 1, 4, 0, 1 }, |
| { { 1.0f, 0.0f, 0.0f }, 0.0f, 1, 1, 4, 0, 2 }, |
| }, |
| }, |
| { PIXEL_FORMAT_AND_NAME(UYVY), |
| 3, |
| { |
| { { -0.14822265625f, -0.2909921875f, 0.43921484375f }, 128.0f, 2, 1, 4, 0, 0 }, |
| { { 0.2567890625f, 0.50412890625f, 0.09790625f }, 16.0f, 1, 1, 2, 0, 1 }, |
| { { 0.43921484375f, -0.3677890625f, -0.07142578125f }, 128.0f, 2, 1, 4, 0, 2 }, |
| }, |
| }, |
| { |
| PIXEL_FORMAT_AND_NAME(YUYV), |
| 3, |
| { |
| { { 0.2567890625f, 0.50412890625f, 0.09790625f }, 16.0f, 1, 1, 2, 0, 0 }, |
| { { -0.14822265625f, -0.2909921875f, 0.43921484375f }, 128.0f, 2, 1, 4, 0, 1 }, |
| { { 0.43921484375f, -0.3677890625f, -0.07142578125f }, 128.0f, 2, 1, 4, 0, 3 }, |
| }, |
| }, |
| { |
| PIXEL_FORMAT_AND_NAME(YVU420), |
| 3, |
| { |
| { { 0.2567890625f, 0.50412890625f, 0.09790625f }, 16.0f, 1, 1, 1, 0, 0 }, |
| { { 0.43921484375f, -0.3677890625f, -0.07142578125f }, 128.0f, 2, 2, 1, 1, 0 }, |
| { { -0.14822265625f, -0.2909921875f, 0.43921484375f }, 128.0f, 2, 2, 1, 2, 0 }, |
| }, |
| }, |
| { |
| PIXEL_FORMAT_AND_NAME(P010), |
| 3, |
| { |
| { { 0.2567890625f, 0.50412890625f, 0.09790625f }, 16.0f, 1, 1, 2, 0, 0 }, |
| { { -0.14822265625f, -0.2909921875f, 0.43921484375f }, 128.0f, 2, 2, 4, 1, 0 }, |
| { { 0.43921484375f, -0.3677890625f, -0.07142578125f }, 128.0f, 2, 2, 4, 1, 2 }, |
| }, |
| }, |
| }; |
| // clang-format on |
| |
| struct draw_plane { |
| uint32_t row_stride; |
| uint8_t* ptr; |
| void* map_data; |
| }; |
| |
| static uint8_t clampbyte(float f) { |
| if (f >= 255.0f) |
| return 255; |
| if (f <= 0.0f) |
| return 0; |
| return (uint8_t)f; |
| } |
| |
| uint8_t static convert_color(const struct draw_format_component* comp, |
| uint8_t r, |
| uint8_t g, |
| uint8_t b, |
| uint8_t a) { |
| return clampbyte(comp->value_offset + r * comp->rgba_coeffs[0] + |
| g * comp->rgba_coeffs[1] + b * comp->rgba_coeffs[2] + |
| a * comp->rgba_coeffs[3]); |
| } |
| |
| static void unmmap_planes(struct bs_mapper* mapper, |
| struct gbm_bo* bo, |
| size_t num_planes, |
| struct draw_plane* planes) { |
| for (uint32_t plane_index = 0; plane_index < num_planes; plane_index++) |
| bs_mapper_unmap(mapper, bo, planes[plane_index].map_data); |
| } |
| |
| static size_t mmap_planes(struct bs_mapper* mapper, |
| struct gbm_bo* bo, |
| struct draw_plane planes[GBM_MAX_PLANES]) { |
| size_t num_planes = gbm_bo_get_plane_count(bo); |
| for (size_t plane_index = 0; plane_index < num_planes; plane_index++) { |
| struct draw_plane* plane = &planes[plane_index]; |
| plane->ptr = bs_mapper_map(mapper, bo, plane_index, &plane->map_data, |
| &plane->row_stride); |
| if (plane->ptr == MAP_FAILED) { |
| bs_debug_error("failed to mmap plane %zu of buffer object", plane_index); |
| unmmap_planes(mapper, bo, plane_index, planes); |
| return 0; |
| } |
| } |
| |
| return num_planes; |
| } |
| |
| static bool draw_color(struct bs_mapper* mapper, |
| struct gbm_bo* bo, |
| const struct bs_draw_format* format, |
| struct draw_data* data, |
| compute_color_t compute_color_fn) { |
| uint8_t *ptr, *converted_colors[MAX_COMPONENTS]; |
| struct draw_plane planes[GBM_MAX_PLANES]; |
| const uint32_t height = data->h = gbm_bo_get_height(bo); |
| const uint32_t width = data->w = gbm_bo_get_width(bo); |
| const uint32_t bytes_per_pixel = gbm_bo_get_bpp(bo); |
| |
| size_t num_planes = mmap_planes(mapper, bo, planes); |
| if (num_planes == 0) { |
| bs_debug_error("failed to prepare to draw pattern to buffer object"); |
| return false; |
| } |
| |
| for (size_t comp_index = 0; comp_index < format->component_count; |
| comp_index++) { |
| converted_colors[comp_index] = calloc(width * height, sizeof(uint8_t)); |
| assert(converted_colors[comp_index]); |
| } |
| |
| for (uint32_t y = 0; y < height; y++) { |
| data->y = y; |
| for (uint32_t x = 0; x < width; x++) { |
| data->x = x; |
| compute_color_fn(data); |
| for (size_t comp_index = 0; comp_index < format->component_count; |
| comp_index++) { |
| const struct draw_format_component* comp = |
| &format->components[comp_index]; |
| ptr = converted_colors[comp_index] + width * y + x; |
| *ptr = convert_color(comp, data->out_color[2], data->out_color[1], |
| data->out_color[0], data->out_color[3]); |
| } |
| } |
| } |
| |
| // High bit depth formats (XR30 etc) have to be written word by word. |
| if (format->pixel_format == GBM_FORMAT_ABGR2101010 || |
| format->pixel_format == GBM_FORMAT_ARGB2101010 || |
| format->pixel_format == GBM_FORMAT_XRGB2101010 || |
| format->pixel_format == GBM_FORMAT_XBGR2101010) { |
| assert(format->components[0].vertical_subsample_rate == 1); |
| assert(format->components[0].horizontal_subsample_rate == 1); |
| assert(format->component_count == 4 || format->component_count == 3); |
| assert(num_planes == 1); |
| |
| for (uint32_t row = 0; row < height; ++row) { |
| for (uint32_t column = 0; column < width; ++column) { |
| uint32_t* const word_address = |
| (uint32_t*)(planes[0].ptr + row * planes[0].row_stride + |
| 4 * column); |
| const uint32_t converted_colors_index = row * width + column; |
| // Leftmost bits are alpha, then follow the colour channels. |
| *word_address = |
| (0x3 << 30) | |
| (((converted_colors[2][converted_colors_index]) & 0xFF) << 22) | |
| (((converted_colors[1][converted_colors_index]) & 0xFF) << 12) | |
| (((converted_colors[0][converted_colors_index]) & 0xFF) << 2); |
| } |
| } |
| } else { |
| uint32_t color, samples, offset; |
| uint8_t* rows[MAX_COMPONENTS] = {0}; |
| for (size_t comp_index = 0; comp_index < format->component_count; |
| comp_index++) { |
| const struct draw_format_component* comp = |
| &format->components[comp_index]; |
| struct draw_plane* plane = &planes[comp->plane_index]; |
| |
| for (uint32_t y = 0; y < height / comp->vertical_subsample_rate; y++) { |
| rows[comp_index] = |
| plane->ptr + comp->plane_offset + plane->row_stride * y; |
| |
| for (uint32_t x = 0; x < width / comp->horizontal_subsample_rate; x++) { |
| offset = color = samples = 0; |
| |
| for (uint32_t j = 0; j < comp->vertical_subsample_rate; j++) { |
| offset = (y * comp->vertical_subsample_rate + j) * width + |
| x * comp->horizontal_subsample_rate; |
| for (uint32_t i = 0; i < comp->horizontal_subsample_rate; i++) { |
| color += converted_colors[comp_index][offset]; |
| samples++; |
| offset++; |
| } |
| } |
| uint8_t* const byte_address = rows[comp_index] + x * comp->byte_skip; |
| if (bytes_per_pixel != 2u) |
| *byte_address = color / samples; |
| else // This is for P010 and similar high-bit depth YUV formats. |
| *(uint16_t*)byte_address = (color / samples) << 8; |
| } |
| } |
| } |
| } |
| |
| unmmap_planes(mapper, bo, num_planes, planes); |
| for (size_t comp_index = 0; comp_index < format->component_count; |
| comp_index++) { |
| free(converted_colors[comp_index]); |
| } |
| |
| return true; |
| } |
| |
| static void compute_stripe(struct draw_data* data) { |
| const uint32_t striph = data->h / 4; |
| const uint32_t s = data->y / striph; |
| uint8_t r = 0, g = 0, b = 0; |
| switch (s) { |
| case 0: |
| r = g = b = 1; |
| break; |
| case 1: |
| r = 1; |
| break; |
| case 2: |
| g = 1; |
| break; |
| case 3: |
| b = 1; |
| break; |
| default: |
| r = g = b = 0; |
| break; |
| } |
| |
| const float i = (float)data->x / (float)data->w * 256.0f; |
| data->out_color[0] = b * i; |
| data->out_color[1] = g * i; |
| data->out_color[2] = r * i; |
| data->out_color[3] = 255; |
| } |
| |
| static void compute_transparent_hole(struct draw_data* data) { |
| // Write solid stripe pattern. |
| compute_stripe(data); |
| // Poke a round hole in the center of the screen. |
| float delta_x = (int)data->x - (int)data->w / 2; |
| float delta_y = (int)data->y - (int)data->h / 2; |
| float dist2 = delta_x * delta_x + delta_y * delta_y; |
| float alpha = 1.0f; |
| float half_min_wh = (data->w < data->h ? data->w : data->h) >> 1; |
| if (dist2 < half_min_wh * half_min_wh) { |
| alpha = 1 - 4 * (half_min_wh * half_min_wh - dist2) / |
| ((half_min_wh * half_min_wh)); |
| alpha = alpha < 0.0f ? 0.0f : alpha; |
| } |
| for (int i = 0; i < 3; ++i) |
| data->out_color[i] = data->out_color[i] * alpha; |
| data->out_color[3] = alpha * 255; |
| } |
| |
| static void compute_ellipse(struct draw_data* data) { |
| float xratio = ((int)data->x - (int)data->w / 2) / ((float)(data->w / 2)); |
| float yratio = ((int)data->y - (int)data->h / 2) / ((float)(data->h / 2)); |
| |
| // If a point is on or inside an ellipse, num <= 1. |
| float num = xratio * xratio + yratio * yratio; |
| uint32_t g = 255 * num; |
| |
| if (g < 256) { |
| memset(data->out_color, 0, 4); |
| data->out_color[2] = 0xFF; |
| data->out_color[1] = g; |
| } else { |
| memset(data->out_color, (uint8_t)(data->progress * 255), 4); |
| } |
| data->out_color[3] = 0xFF; |
| } |
| |
| static void compute_cursor(struct draw_data* data) { |
| // A white triangle pointing right |
| if (data->y > data->x / 2 && data->y < (data->w - data->x / 2)) |
| memset(data->out_color, 0xFF, 4); |
| else |
| memset(data->out_color, 0, 4); |
| data->out_color[3] = 0xFF; |
| } |
| |
| static void compute_lines(struct draw_data* data) { |
| struct draw_data_lines* line_data = (struct draw_data_lines*)data; |
| // horizontal stripes on first vertical half, vertical stripes on next half |
| if (line_data->base.y < (line_data->base.h / 2)) { |
| if (line_data->base.x == 0) { |
| line_data->color_olive = false; |
| line_data->interval = 5; |
| line_data->stop = line_data->base.x + line_data->interval; |
| } else if (line_data->base.x >= line_data->stop) { |
| if (line_data->color_olive) |
| line_data->interval += 5; |
| |
| line_data->stop += line_data->interval; |
| line_data->color_olive = !line_data->color_olive; |
| } |
| } else { |
| if (line_data->base.y == (line_data->base.h / 2)) { |
| line_data->color_olive = false; |
| line_data->interval = 10; |
| line_data->stop = line_data->base.y + line_data->interval; |
| } else if (line_data->base.y >= line_data->stop) { |
| if (line_data->color_olive) |
| line_data->interval += 5; |
| |
| line_data->stop += line_data->interval; |
| line_data->color_olive = !line_data->color_olive; |
| } |
| } |
| if (line_data->color_olive) { |
| // yellowish green color |
| line_data->base.out_color[0] = 0; |
| line_data->base.out_color[1] = 128; |
| line_data->base.out_color[2] = 128; |
| line_data->base.out_color[3] = 255; |
| } else { |
| // fuchsia |
| line_data->base.out_color[0] = 255; |
| line_data->base.out_color[1] = 0; |
| line_data->base.out_color[2] = 255; |
| line_data->base.out_color[3] = 255; |
| } |
| } |
| |
| bool bs_draw_stripe(struct bs_mapper* mapper, |
| struct gbm_bo* bo, |
| const struct bs_draw_format* format) { |
| struct draw_data data = {0}; |
| return draw_color(mapper, bo, format, &data, compute_stripe); |
| } |
| |
| bool bs_draw_transparent_hole(struct bs_mapper* mapper, |
| struct gbm_bo* bo, |
| const struct bs_draw_format* format) { |
| struct draw_data data = {0}; |
| return draw_color(mapper, bo, format, &data, compute_transparent_hole); |
| } |
| |
| bool bs_draw_ellipse(struct bs_mapper* mapper, |
| struct gbm_bo* bo, |
| const struct bs_draw_format* format, |
| float progress) { |
| struct draw_data data = {0}; |
| data.progress = progress; |
| return draw_color(mapper, bo, format, &data, compute_ellipse); |
| } |
| |
| bool bs_draw_cursor(struct bs_mapper* mapper, |
| struct gbm_bo* bo, |
| const struct bs_draw_format* format) { |
| struct draw_data data = {0}; |
| return draw_color(mapper, bo, format, &data, compute_cursor); |
| } |
| |
| bool bs_draw_lines(struct bs_mapper* mapper, |
| struct gbm_bo* bo, |
| const struct bs_draw_format* format) { |
| struct draw_data_lines line_data = {{0}}; |
| return draw_color(mapper, bo, format, &line_data.base, compute_lines); |
| } |
| |
| const struct bs_draw_format* bs_get_draw_format(uint32_t pixel_format) { |
| for (size_t format_index = 0; format_index < BS_ARRAY_LEN(bs_draw_formats); |
| format_index++) { |
| const struct bs_draw_format* format = &bs_draw_formats[format_index]; |
| if (format->pixel_format == pixel_format) |
| return format; |
| } |
| |
| return NULL; |
| } |
| |
| const struct bs_draw_format* bs_get_draw_format_from_name(const char* str) { |
| for (size_t format_index = 0; format_index < BS_ARRAY_LEN(bs_draw_formats); |
| format_index++) { |
| const struct bs_draw_format* format = &bs_draw_formats[format_index]; |
| if (!strcmp(str, format->name)) |
| return format; |
| } |
| |
| return NULL; |
| } |
| |
| uint32_t bs_get_pixel_format(const struct bs_draw_format* format) { |
| assert(format); |
| return format->pixel_format; |
| } |
| |
| const char* bs_get_format_name(const struct bs_draw_format* format) { |
| assert(format); |
| return format->name; |
| } |
| |
| bool bs_parse_draw_format(const char* str, |
| const struct bs_draw_format** format) { |
| if (strlen(str) == 4) { |
| const struct bs_draw_format* bs_draw_format = |
| bs_get_draw_format(*(uint32_t*)str); |
| if (bs_draw_format) { |
| *format = bs_draw_format; |
| return true; |
| } |
| } else { |
| const struct bs_draw_format* bs_draw_format = |
| bs_get_draw_format_from_name(str); |
| if (bs_draw_format) { |
| *format = bs_draw_format; |
| return true; |
| } |
| } |
| |
| bs_debug_error("format %s is not recognized\n", str); |
| return false; |
| } |