From fc8abf144379397449ff2fd08368a9bbc2653643 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Reimar=20D=C3=B6ffinger?= Date: Sat, 16 Nov 2013 23:39:49 +0100 Subject: [PATCH 1/2] Add hackish support for brightness, contrast and saturation. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Support for hue is there in principle, but would need tweaking, Due to the very limited range supported by the driver I didn't manage to do that. The hardware actually has full support for arbitrary CSC matrices, but since it is not exposed some hack like this is the best I could think of. Patch also fixes vdp_generate_csc_matrix to write the data to the correct location, previously it would actually write outside the matrix. Signed-off-by: Reimar Döffinger --- presentation_queue.c | 19 ++++++++++++ surface_output.c | 2 ++ vdpau_private.h | 10 +++++++ video_mixer.c | 70 +++++++++++++++++++++++++++++++++++--------- 4 files changed, 87 insertions(+), 14 deletions(-) diff --git a/presentation_queue.c b/presentation_queue.c index 017bc26..cdeb7c1 100644 --- a/presentation_queue.c +++ b/presentation_queue.c @@ -287,6 +287,25 @@ VdpStatus vdp_presentation_queue_display(VdpPresentationQueue presentation_queue ioctl(q->target->fd, DISP_CMD_LAYER_BOTTOM, args); ioctl(q->target->fd, DISP_CMD_LAYER_OPEN, args); + // Note: might be more reliable (but slower and problematic when there + // are driver issues and the GET functions return wrong values) to query the + // old values instead of relying on our internal csc_change. + // Since the driver calculates a matrix out of these values after each + // set doing this unconditionally is costly. + if (os->csc_change) { + ioctl(q->target->fd, DISP_CMD_LAYER_ENHANCE_OFF, args); + args[2] = 0xff * os->brightness + 0x20; + ioctl(q->target->fd, DISP_CMD_LAYER_SET_BRIGHT, args); + args[2] = 0x20 * os->contrast; + ioctl(q->target->fd, DISP_CMD_LAYER_SET_CONTRAST, args); + args[2] = 0x20 * os->saturation; + ioctl(q->target->fd, DISP_CMD_LAYER_SET_SATURATION, args); + // hue scale is randomly chosen, no idea how it maps exactly + args[2] = (32 / 3.14) * os->hue + 0x20; + ioctl(q->target->fd, DISP_CMD_LAYER_SET_HUE, args); + ioctl(q->target->fd, DISP_CMD_LAYER_ENHANCE_ON, args); + os->csc_change = 0; + } return VDP_STATUS_OK; } diff --git a/surface_output.c b/surface_output.c index 1453af2..82ff38f 100644 --- a/surface_output.c +++ b/surface_output.c @@ -38,6 +38,8 @@ VdpStatus vdp_output_surface_create(VdpDevice device, VdpRGBAFormat rgba_format, out->width = width; out->height = height; out->rgba_format = rgba_format; + out->contrast = 1.0; + out->saturation = 1.0; out->device = dev; int handle = handle_create(out); diff --git a/vdpau_private.h b/vdpau_private.h index b119bd4..fb06562 100644 --- a/vdpau_private.h +++ b/vdpau_private.h @@ -78,6 +78,11 @@ typedef struct typedef struct { device_ctx_t *device; + int csc_change; + float brightness; + float contrast; + float saturation; + float hue; } mixer_ctx_t; typedef struct @@ -87,6 +92,11 @@ typedef struct uint32_t width, height; video_surface_ctx_t *vs; VdpRect video_src_rect, video_dst_rect; + int csc_change; + float brightness; + float contrast; + float saturation; + float hue; } output_surface_ctx_t; #ifndef ARRAY_SIZE diff --git a/video_mixer.c b/video_mixer.c index 43db92b..8390e4a 100644 --- a/video_mixer.c +++ b/video_mixer.c @@ -17,6 +17,7 @@ * */ +#include #include "vdpau_private.h" VdpStatus vdp_video_mixer_create(VdpDevice device, uint32_t feature_count, VdpVideoMixerFeature const *features, uint32_t parameter_count, VdpVideoMixerParameter const *parameters, void const *const *parameter_values, VdpVideoMixer *mixer) @@ -30,6 +31,8 @@ VdpStatus vdp_video_mixer_create(VdpDevice device, uint32_t feature_count, VdpVi return VDP_STATUS_RESOURCES; mix->device = dev; + mix->contrast = 1.0; + mix->saturation = 1.0; int handle = handle_create(mix); if (handle == -1) @@ -92,6 +95,12 @@ VdpStatus vdp_video_mixer_render(VdpVideoMixer mixer, VdpOutputSurface backgroun os->video_src_rect.y1 = os->vs->height; } } + os->csc_change = mix->csc_change; + os->brightness = mix->brightness; + os->contrast = mix->contrast; + os->saturation = mix->saturation; + os->hue = mix->hue; + mix->csc_change = 0; if (layer_count != 0) VDPAU_DBG_ONCE("Requested unimplemented additional layers"); @@ -145,6 +154,26 @@ VdpStatus vdp_video_mixer_get_feature_enables(VdpVideoMixer mixer, uint32_t feat return VDP_STATUS_ERROR; } +static void set_csc_matrix(mixer_ctx_t *mix, const VdpCSCMatrix *matrix) +{ + mix->csc_change = 1; + // default contrast for full-range has 1.0 as luma coefficients + mix->contrast = ((*matrix)[0][0] + (*matrix)[1][0] + (*matrix)[2][0]) / 3; + // the way brightness and contrast work with this driver, brightness + // is the brightness of a "black" pixel + mix->brightness = ((*matrix)[0][1] + (*matrix)[1][1] + (*matrix)[2][1]) / 2 + + ((*matrix)[0][2] + (*matrix)[1][2] + (*matrix)[2][2]) / 2 + + (*matrix)[0][3] + (*matrix)[1][3] + (*matrix)[2][3]; + mix->brightness /= 3; + + float sin = (*matrix)[0][1] + (*matrix)[2][2]; + float cos = (*matrix)[0][2] + (*matrix)[2][1]; + float e = 0.001; + if (-e < cos && cos < e) mix->hue = M_PI; + else mix->hue = atanf(sin/cos); + mix->saturation = sqrtf(sin * sin + cos * cos) / (1.403 + 1.773); +} + VdpStatus vdp_video_mixer_set_attribute_values(VdpVideoMixer mixer, uint32_t attribute_count, VdpVideoMixerAttribute const *attributes, void const *const *attribute_values) { if (!attributes || !attribute_values) @@ -154,6 +183,10 @@ VdpStatus vdp_video_mixer_set_attribute_values(VdpVideoMixer mixer, uint32_t att if (!mix) return VDP_STATUS_INVALID_HANDLE; + uint32_t i; + for (i = 0; i < attribute_count; i++) + if (attributes[i] == VDP_VIDEO_MIXER_ATTRIBUTE_CSC_MATRIX) + set_csc_matrix(mix, (const VdpCSCMatrix *)attribute_values[i]); return VDP_STATUS_OK; } @@ -305,20 +338,29 @@ VdpStatus vdp_generate_csc_matrix(VdpProcamp *procamp, VdpColorStandard standard if (procamp->struct_version > VDP_PROCAMP_VERSION) return VDP_STATUS_INVALID_STRUCT_VERSION; - (*csc_matrix)[0][0] = 1.164000; - (*csc_matrix)[0][1] = 0.000000; - (*csc_matrix)[0][2] = 1.596000; - (*csc_matrix)[0][3] = -0.874165; - - (*csc_matrix)[1][0] = 1.164000; - (*csc_matrix)[1][1] = -0.391000; - (*csc_matrix)[1][2] = -0.813000; - (*csc_matrix)[1][3] = 0.531326; - - (*csc_matrix)[2][0] = 1.164000; - (*csc_matrix)[2][1] = 2.018000; - (*csc_matrix)[2][2] = 0.000000; - (*csc_matrix)[2][3] = -1.085992; + // BT.601 table + (*csc_matrix)[0][1] = 0.000; + (*csc_matrix)[0][2] = 1.403; + + (*csc_matrix)[1][1] = -0.344; + (*csc_matrix)[1][2] = -0.714; + + (*csc_matrix)[2][1] = 1.773; + (*csc_matrix)[2][2] = 0.000; + + float uvcos = procamp->saturation * cosf(procamp->hue); + float uvsin = procamp->saturation * sinf(procamp->hue); + int i; + for (i = 0; i < 3; i++) { + (*csc_matrix)[i][0] = procamp->contrast; + float u = (*csc_matrix)[i][1] * uvcos + (*csc_matrix)[i][2] * uvsin; + float v = (*csc_matrix)[i][1] * uvsin + (*csc_matrix)[i][2] * uvcos; + (*csc_matrix)[i][1] = u; + (*csc_matrix)[i][2] = v; + (*csc_matrix)[i][3] = - (u + v) / 2; + (*csc_matrix)[i][3] += 0.5 - procamp->contrast / 2; + (*csc_matrix)[i][3] += procamp->brightness; + } return VDP_STATUS_OK; } From e38c26059cb297a3694b5da0067932b07b49d9c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Reimar=20D=C3=B6ffinger?= Date: Thu, 21 Nov 2013 00:14:59 +0100 Subject: [PATCH 2/2] Proof of concept OSD code. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It works for displaying a usable OSD in MPlayer at full screen resolution and even if using hardware decode, but is far too slow for fullscreen use for example. Signed-off-by: Reimar Döffinger --- presentation_queue.c | 20 ++++++++++ surface_output.c | 95 ++++++++++++++++++++++++++++++++++++++++++-- vdpau_private.h | 2 + video_mixer.c | 2 + 4 files changed, 116 insertions(+), 3 deletions(-) diff --git a/presentation_queue.c b/presentation_queue.c index cdeb7c1..71cc1da 100644 --- a/presentation_queue.c +++ b/presentation_queue.c @@ -226,6 +226,26 @@ VdpStatus vdp_presentation_queue_display(VdpPresentationQueue presentation_queue int x,y; XTranslateCoordinates(q->device->display, q->target->drawable, RootWindow(q->device->display, q->device->screen), 0, 0, &x, &y, &c); XClearWindow(q->device->display, q->target->drawable); + if (os->data && !os->data_clear) + { + GC gc = XCreateGC(q->device->display, q->target->drawable, 0, 0); + const uint32_t *src = os->data; + uint32_t x, y; + for (y = 0; y < os->height; y++) + { + for (x = 0; x < os->width; x++) + { + uint32_t v = *src++; + if ((v >> 24) > 0x80) + { + XSetForeground(q->device->display, gc, v); + XDrawPoint(q->device->display, q->target->drawable, gc, x, y); + } + } + } + XFreeGC(q->device->display, gc); + XFlush(q->device->display); + } __disp_layer_info_t layer_info; memset(&layer_info, 0, sizeof(layer_info)); diff --git a/surface_output.c b/surface_output.c index 82ff38f..2fbd89c 100644 --- a/surface_output.c +++ b/surface_output.c @@ -17,6 +17,7 @@ * */ +#include #include "vdpau_private.h" VdpStatus vdp_output_surface_create(VdpDevice device, VdpRGBAFormat rgba_format, uint32_t width, uint32_t height, VdpOutputSurface *surface) @@ -24,7 +25,7 @@ VdpStatus vdp_output_surface_create(VdpDevice device, VdpRGBAFormat rgba_format, if (!surface) return VDP_STATUS_INVALID_POINTER; - if (!width || !height) + if (!width || !height || width >= 16384 || height >= 16384) return VDP_STATUS_INVALID_SIZE; device_ctx_t *dev = handle_get(device); @@ -41,6 +42,8 @@ VdpStatus vdp_output_surface_create(VdpDevice device, VdpRGBAFormat rgba_format, out->contrast = 1.0; out->saturation = 1.0; out->device = dev; + // Do not allocate data yet, we might not need to + out->data = NULL; int handle = handle_create(out); if (handle == -1) @@ -108,16 +111,58 @@ VdpStatus vdp_output_surface_put_bits_native(VdpOutputSurface surface, void cons return VDP_STATUS_OK; } +static int valid_rect(const VdpRect *rect, const output_surface_ctx_t *out) +{ + return rect->x0 <= rect->x1 && + rect->y0 <= rect->y1 && + rect->x1 < out->width && + rect->y1 < out->height; +} + VdpStatus vdp_output_surface_put_bits_indexed(VdpOutputSurface surface, VdpIndexedFormat source_indexed_format, void const *const *source_data, uint32_t const *source_pitch, VdpRect const *destination_rect, VdpColorTableFormat color_table_format, void const *color_table) { output_surface_ctx_t *out = handle_get(surface); if (!out) return VDP_STATUS_INVALID_HANDLE; - VDPAU_DBG_ONCE("%s called but unimplemented!", __func__); + if (!valid_rect(destination_rect, out)) + return VDP_STATUS_INVALID_SIZE; + if (out->rgba_format != VDP_RGBA_FORMAT_B8G8R8A8 || + source_indexed_format != VDP_INDEXED_FORMAT_I8A8 || + color_table_format != VDP_COLOR_TABLE_FORMAT_B8G8R8X8) + { + VDPAU_DBG_ONCE("%s called but unimplemented!", __func__); + return VDP_STATUS_OK; + } + if (!out->data) + out->data = calloc(out->width * 4, out->height); + if (!out->data) + return VDP_STATUS_RESOURCES; + const uint32_t *palette = color_table; + uint32_t w = destination_rect->x1 - destination_rect->x0; + uint32_t h = destination_rect->y1 - destination_rect->y0; + uint32_t *dst = out->data; + dst += destination_rect->y0 * out->width + destination_rect->x0; + uint32_t dst_step = out->width - w; + const uint8_t *src = source_data[0]; + if (source_pitch[0] < 2 * w) + return VDP_STATUS_INVALID_SIZE; + uint32_t src_step = source_pitch[0] - 2*w; + uint32_t x, y; + for (y = 0; y < h; y++) + { + for (x = 0; x < w; x++) + { + *dst = palette[*src++] & 0x00ffffffu; + *dst++ |= *src++ << 24; + } + src += src_step; + dst += dst_step; + } + out->data_clear = 0; return VDP_STATUS_OK; } @@ -140,14 +185,58 @@ VdpStatus vdp_output_surface_render_output_surface(VdpOutputSurface destination_ if (!out) return VDP_STATUS_INVALID_HANDLE; + if (source_surface == VDP_INVALID_HANDLE) + { + // for now assume destination_rect == NULL and + // always clear the whole surface + if (out->data) + { + free(out->data); + out->data = NULL; + } + return VDP_STATUS_OK; + } + output_surface_ctx_t *in = handle_get(source_surface); if (!in) return VDP_STATUS_INVALID_HANDLE; - VDPAU_DBG_ONCE("%s called but unimplemented!", __func__); + if (!valid_rect(source_rect, in) || + !valid_rect(destination_rect, out)) + return VDP_STATUS_INVALID_SIZE; + if (out->rgba_format != VDP_RGBA_FORMAT_B8G8R8A8 || + !in->data) + { + VDPAU_DBG_ONCE("%s called but unimplemented!", __func__); + return VDP_STATUS_OK; + } + if (out->data && out->data_clear) + memset(out->data, 0, out->width * 4 * out->height); + if (!out->data) + out->data = calloc(out->width * 4, out->height); + if (!out->data) + return VDP_STATUS_RESOURCES; + uint32_t w = destination_rect->x1 - destination_rect->x0; + uint32_t ws = source_rect->x1 - source_rect->x0; + if (ws < w) w = ws; + uint32_t h = destination_rect->y1 - destination_rect->y0; + uint32_t hs = source_rect->y1 - source_rect->y0; + if (hs < h) h = hs; + uint32_t *dst = out->data; + dst += destination_rect->y0 * out->width + destination_rect->x0; + const uint32_t *src = in->data; + src += source_rect->y0 * in->width + source_rect->x0; + uint32_t y; + for (y = 0; y < h; y++) + { + memcpy(dst, src, w * 4); + src += in->width; + dst += out->width; + } + out->data_clear = 0; return VDP_STATUS_OK; } diff --git a/vdpau_private.h b/vdpau_private.h index fb06562..b2a1773 100644 --- a/vdpau_private.h +++ b/vdpau_private.h @@ -90,6 +90,8 @@ typedef struct device_ctx_t *device; VdpRGBAFormat rgba_format; uint32_t width, height; + int data_clear; // data should be treated as clear + uint32_t *data; video_surface_ctx_t *vs; VdpRect video_src_rect, video_dst_rect; int csc_change; diff --git a/video_mixer.c b/video_mixer.c index 8390e4a..9947162 100644 --- a/video_mixer.c +++ b/video_mixer.c @@ -79,6 +79,8 @@ VdpStatus vdp_video_mixer_render(VdpVideoMixer mixer, VdpOutputSurface backgroun if (!os) return VDP_STATUS_INVALID_HANDLE; + os->data_clear = 1; + os->vs = handle_get(video_surface_current); if (!(os->vs)) return VDP_STATUS_INVALID_HANDLE;