diff options
author | phintuka <phintuka> | 2008-12-18 14:03:59 +0000 |
---|---|---|
committer | phintuka <phintuka> | 2008-12-18 14:03:59 +0000 |
commit | b8ad8a18db8bb4b435deb1a2b46212790de39c57 (patch) | |
tree | 01dc73b11cb560bfffec55a89bab244575073d48 | |
parent | 939018cfd2124d63f91a31b4e9c0e83ac5413714 (diff) | |
download | xineliboutput-b8ad8a18db8bb4b435deb1a2b46212790de39c57.tar.gz xineliboutput-b8ad8a18db8bb4b435deb1a2b46212790de39c57.tar.bz2 |
Initial import
(OSD scaling video-out post plugin)
-rw-r--r-- | xine/vo_osdscaler.c | 435 |
1 files changed, 435 insertions, 0 deletions
diff --git a/xine/vo_osdscaler.c b/xine/vo_osdscaler.c new file mode 100644 index 00000000..03318924 --- /dev/null +++ b/xine/vo_osdscaler.c @@ -0,0 +1,435 @@ +/* + * vo_osdscaler.c: OSD scaling video-out post plugin + * + * See the main source file 'xineliboutput.c' for copyright information and + * how to reach the author. + * + * $Id: vo_osdscaler.c,v 1.1 2008-12-18 14:03:59 phintuka Exp $ + * + */ + +#include <stdlib.h> + +#include <xine/video_out.h> +#ifdef SWBLEND +# include <xine/alphablend.h> +#endif + +#define LOG_MODULENAME "[osdscaler] " +#include "../logdefs.h" + +#include "vo_props.h" +#include "vo_hook.h" + +/*#define LOGOSD(x...) LOGMSG(x)*/ +#define LOGOSD(x...) + +typedef rle_elem_t xine_rle_elem_t; + +#include "../tools/rle.h" + + +/* Make sure our properties won't overlap with xine's properties */ +#if VO_NUM_PROPERTIES > VO_PROP_OSD_SCALING +# error VO_NUM_PROPERTIES > VO_PROP_OSD_SCALING +#endif + +#undef ABS +#define ABS(x) ((x)<0?-(x):(x)) + + +/* + * osd_data_t + * + * - cache scaled OSD data + */ + +typedef struct osd_data_s osd_data_t; + +struct osd_data_s { + /* original source */ + vo_overlay_t *source; + + /* scaled overlay */ + uint8_t scaled : 1; + vo_overlay_t ovl; + + /* for what output resolution overlay was scaled */ + uint16_t output_width; + uint16_t output_height; + + /* linked list */ + osd_data_t *next; +}; + +/* + * osd_data_dispose() + * + * - free() osd_data_t and all allocated memory + */ +static void osd_data_dispose(osd_data_t *data) +{ + if (data->scaled) + free(data->ovl.rle); + free(data); +} + +/* + * osd_data_clear() + * + * - free() whole linked list + */ +static void osd_data_clear(osd_data_t *data) +{ + if (data) { + if (data->next) + osd_data_clear(data->next); + osd_data_dispose(data); + } +} + +/* + * osd_data_remove() + * + * - remove (and free) specific osd_data_t item from linked list + */ +static void osd_data_remove(osd_data_t **list, osd_data_t *data) +{ + if (!list || !*list) + return; + + /* special handling for list head */ + if (data == *list) { + *list = data->next; + free(data); + return; + } + + osd_data_t *it = *list; + while (it) { + if (data == it->next) { + it->next = data->next; + free(data); + return; + } + it = it->next; + } +} + +/* + * osd_data_init() + * + * - allocate and fill new osd_data_t + * + */ +static osd_data_t *osd_data_init(vo_overlay_t *ovl, osd_data_t *next, + uint32_t factor_x, uint32_t factor_y) +{ + osd_data_t *data = calloc(1, sizeof(osd_data_t)); + + data->source = ovl; + data->next = next; + + memcpy(&data->ovl, ovl, sizeof(vo_overlay_t)); + + int num_rle = data->ovl.num_rle; + + /* new position and size */ + int x2 = ovl->x + ovl->width + 1; + int y2 = ovl->y + ovl->height + 1; + x2 = ((x2+1) * factor_x) >> 16; + y2 = ((y2+1) * factor_y) >> 16; + data->ovl.x = (ovl->x * factor_x) >> 16; + data->ovl.y = (ovl->y * factor_y) >> 16; + data->ovl.width = x2 - data->ovl.x - 1; + data->ovl.height = y2 - data->ovl.y - 1; + + data->ovl.rle = (rle_elem_t*) + rle_scale_nearest((struct xine_rle_elem_s*)ovl->rle, &num_rle, + ovl->width, ovl->height, + data->ovl.width, data->ovl.height); + data->ovl.num_rle = num_rle; + data->scaled = 1; + + LOGOSD("I: %d,%d %dx%d", ovl->x, ovl->y, ovl->width, ovl->height); + LOGOSD("O: %d,%d %dx%d", data->ovl.x, data->ovl.y, data->ovl.width, data->ovl.height); + + return data; +} + +/* + * osdscaler_hook_t + */ +typedef struct { + vo_driver_hook_t h; + + /* configuration */ + uint8_t enable; + uint16_t osd_width; /* the size of original full-screen OSD */ + uint16_t osd_height; + + uint8_t unscaled_supported; + + /* current output */ + uint16_t output_width; + uint16_t output_height; + uint32_t factor_x; /* scaling factor << 16 */ + uint32_t factor_y; + uint16_t x_move; /* OSD displacement */ + uint16_t y_move; + + /* currently showing OSDs - pre-scaled data */ + osd_data_t *active_osds; + +} osdscaler_hook_t; + +/* + * + */ + +/* + * override overlay_begin() + */ +static void osdscaler_overlay_begin (vo_driver_t *self, vo_frame_t *frame, int changed) +{ + osdscaler_hook_t *this = (osdscaler_hook_t*)self; + + /* assume we're wired when called */ + if (!this->h.orig_driver) { + LOGMSG("osdscaler_overlay_begin: assertion this->h.orig_driver failed !"); + abort(); + } + + /* re-scale all if OSD changed */ + if (changed) { + LOGOSD("osdscaler_overlay_begin: changed = 1"); + osd_data_clear(this->active_osds); + this->active_osds = NULL; + this->unscaled_supported = (vo_def_get_capabilities(self) & VO_CAP_UNSCALED_OVERLAY); + } + + /* redirect */ + if (this->h.orig_driver->overlay_begin) + this->h.orig_driver->overlay_begin(this->h.orig_driver, frame, changed); +} + +static int check_for_scaling(osdscaler_hook_t *this, vo_frame_t *frame, vo_overlay_t *overlay) +{ + this->x_move = this->y_move = 0; + + if (!this->enable) + return 0; + + /* check for VDR OSD */ + if (overlay->hili_rgb_clut != VDR_OSD_MAGIC /* not from vdr */) { + LOGOSD("overlay: source not VDR"); + return 0; + } + + /* detect output size */ + if (overlay->unscaled && this->unscaled_supported) { + this->output_width = vo_def_get_property((vo_driver_t*)this, VO_PROP_WINDOW_WIDTH); + this->output_height = vo_def_get_property((vo_driver_t*)this, VO_PROP_WINDOW_HEIGHT); + } else { + this->output_width = frame->width; + this->output_height = frame->height; + /* check cropping */ + if (frame->crop_top > 0) this->output_height -= frame->crop_top; + if (frame->crop_bottom > 0) this->output_height -= frame->crop_bottom; + if (frame->crop_left > 0) this->output_width -= frame->crop_left; + if (frame->crop_right > 0) this->output_width -= frame->crop_right; + } + +this->output_width = this->output_width*2/3; +this->output_height = this->output_height*2/3; + + /* check if scaling should be done */ + if (ABS(this->output_width - this->osd_width) > this->osd_width /20 || + ABS(this->output_height - this->osd_height) > this->osd_height/20 ) { + LOGOSD("scaling required"); + this->factor_x = 0x10000 * this->output_width / this->osd_width; + this->factor_y = 0x10000 * this->output_height / this->osd_height; + return 1; + } + + /* if no scaling was required, we may still need to re-center OSD */ + if(this->output_width != this->osd_width) + this->x_move = (this->output_width - this->osd_width)/2; + if(this->output_height != this->osd_height) + this->y_move = (this->output_height - this->osd_height)/2; + + return 0; +} + +static vo_overlay_t *scale_overlay(osdscaler_hook_t *this, vo_frame_t *frame, vo_overlay_t *overlay) +{ + if (check_for_scaling(this, frame, overlay)) { + + /* find old scaled OSD */ + osd_data_t *scaled = this->active_osds; + while (scaled && scaled->source != overlay) + scaled = scaled->next; + + /* output size changed since last scaling (need to re-scale) ? */ + if (scaled && + (scaled->output_width != this->output_width || + scaled->output_height != this->output_height)) { + + LOGOSD("re-scaling required: output size changed %dx%d -> %dx%d", + scaled->output_width, scaled->output_height, this->output_width, this->output_height); + + osd_data_remove(&this->active_osds, scaled); + scaled = NULL; + } + + /* not scaled found ? */ + if (!scaled) { + LOGOSD("scaling OSD"); + scaled = this->active_osds = osd_data_init(overlay, this->active_osds, + this->factor_x, this->factor_y); + scaled->output_width = this->output_width; + scaled->output_height = this->output_height; + } + + /* override */ + overlay = &scaled->ovl; + } + + return overlay; +} + +/* + * interface + */ + +/* + * override overlay_blend() + */ +static void osdscaler_overlay_blend (vo_driver_t *self, vo_frame_t *frame, vo_overlay_t *overlay) +{ + osdscaler_hook_t *this = (osdscaler_hook_t*)self; + + overlay = scale_overlay(this, frame, overlay); + + /* redirect */ + if (this->h.orig_driver->overlay_blend) + this->h.orig_driver->overlay_blend(this->h.orig_driver, frame, overlay); +} + +/* + * override overlay_end() + */ +static void osdscaler_overlay_end (vo_driver_t *self, vo_frame_t *vo_img) +{ + osdscaler_hook_t *this = (osdscaler_hook_t*)self; + + /* redirect */ + if (this->h.orig_driver->overlay_end) + this->h.orig_driver->overlay_end(this->h.orig_driver, vo_img); +} + + +/* + * Management interface + */ + +/* + * override get_capabilities() + */ +static uint32_t osdscaler_get_capabilities(vo_driver_t *self) +{ + return vo_def_get_capabilities(self) | + VO_CAP_OSDSCALING; +} + +/* + * override get_property() + */ +static int osdscaler_get_property(vo_driver_t *self, int prop) +{ + osdscaler_hook_t *this = (osdscaler_hook_t*)self; + + switch (prop) { + case VO_PROP_OSD_SCALING: return this->enable; + case VO_PROP_OSD_WIDTH: return this->osd_width; + case VO_PROP_OSD_HEIGHT: return this->osd_height; + default:; + } + + return vo_def_get_property(self, prop); +} + +/* + * override set_property() + */ +static int osdscaler_set_property(vo_driver_t *self, int prop, int val) +{ + osdscaler_hook_t *this = (osdscaler_hook_t*)self; + + switch (prop) { + case VO_PROP_OSD_SCALING: + if (this->enable != val) { + LOGOSD("osdscaler_set_property: enable %d->%d", this->enable, val); + this->enable = val?1:0; + } + return this->enable; + + case VO_PROP_OSD_WIDTH: + if (this->osd_width != val) { + LOGOSD("osdscaler_set_property: width %d->%d", this->osd_width, val); + this->osd_width = val & 0xfff; + } + return this->osd_width; + + case VO_PROP_OSD_HEIGHT: + if (this->osd_height != val) { + LOGOSD("osdscaler_set_property: height %d->%d", this->osd_height, val); + this->osd_height = val & 0xfff; + } + return this->osd_height; + } + + return vo_def_set_property(self, prop, val); +} + + +/* + * dispose() + */ +static void osdscaler_dispose(vo_driver_t *self) +{ + osdscaler_hook_t *this = (osdscaler_hook_t *) self; + + osd_data_clear(this->active_osds); + + vo_def_dispose(self); +} + + +/* + * init() + */ +vo_driver_t *osdscaler_init(void) +{ + osdscaler_hook_t *this = calloc(1, sizeof(osdscaler_hook_t)); + + /* OSD interface */ + this->h.vo.overlay_begin = osdscaler_overlay_begin; + this->h.vo.overlay_blend = osdscaler_overlay_blend; + this->h.vo.overlay_end = osdscaler_overlay_end; + + /* management interface */ + this->h.vo.get_capabilities = osdscaler_get_capabilities; + this->h.vo.get_property = osdscaler_get_property; + this->h.vo.set_property = osdscaler_set_property; + + this->h.vo.dispose = osdscaler_dispose; + + /* initialize data */ + this->enable = 0; + this->osd_width = 720; + this->osd_height = 576; + + return &this->h.vo; +} + |