rockbox/apps/plugins/mpegplayer/video_out_rockbox.c
Daniel Stenberg 2acc0ac542 Updated our source code header to explicitly mention that we are GPL v2 or
later. We still need to hunt down snippets used that are not. 1324 modified
files...
http://www.rockbox.org/mail/archive/rockbox-dev-archive-2008-06/0060.shtml


git-svn-id: svn://svn.rockbox.org/rockbox/trunk@17847 a1c6a512-1295-4272-9138-f99709370657
2008-06-28 18:10:04 +00:00

523 lines
12 KiB
C

/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* mpegplayer video output routines
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "mpeg2dec_config.h"
#include "plugin.h"
#include "mpegplayer.h"
#define VO_NON_NULL_RECT 0x1
#define VO_VISIBLE 0x2
struct vo_data
{
int image_width;
int image_height;
int image_chroma_x;
int image_chroma_y;
int display_width;
int display_height;
int output_x;
int output_y;
int output_width;
int output_height;
unsigned flags;
struct vo_rect rc_vid;
struct vo_rect rc_clip;
};
#ifdef PROC_NEEDS_CACHEALIGN
/* Cache aligned and padded to avoid clobbering other processors' cacheable
* data */
static uint8_t __vo_data[CACHEALIGN_UP(sizeof(struct vo_data))]
CACHEALIGN_ATTR;
#define vo (*((struct vo_data *)__vo_data))
#else
static struct vo_data vo;
#endif
#if NUM_CORES > 1
static struct mutex vo_mtx SHAREDBSS_ATTR;
#endif
static inline void video_lock_init(void)
{
#if NUM_CORES > 1
rb->mutex_init(&vo_mtx);
#endif
}
static inline void video_lock(void)
{
#if NUM_CORES > 1
rb->mutex_lock(&vo_mtx);
#endif
}
static inline void video_unlock(void)
{
#if NUM_CORES > 1
rb->mutex_unlock(&vo_mtx);
#endif
}
/* Draw a black rectangle if no video frame is available */
static void vo_draw_black(void)
{
int foreground;
video_lock();
foreground = lcd_(get_foreground)();
lcd_(set_foreground)(DRAW_BLACK);
lcd_(fillrect)(vo.output_x, vo.output_y, vo.output_width,
vo.output_height);
lcd_(update_rect)(vo.output_x, vo.output_y, vo.output_width,
vo.output_height);
lcd_(set_foreground)(foreground);
video_unlock();
}
static inline void yuv_blit(uint8_t * const * buf, int src_x, int src_y,
int stride, int x, int y, int width, int height)
{
video_lock();
#ifdef HAVE_LCD_COLOR
rb->lcd_blit_yuv(buf, src_x, src_y, stride, x, y , width, height);
#else
grey_ub_gray_bitmap_part(buf[0], src_x, src_y, stride, x, y, width, height);
#endif
video_unlock();
}
void vo_draw_frame(uint8_t * const * buf)
{
if (vo.flags == 0)
{
/* Frame is hidden - copout */
DEBUGF("vo hidden\n");
return;
}
else if (buf == NULL)
{
/* No frame exists - draw black */
vo_draw_black();
DEBUGF("vo no frame\n");
return;
}
yuv_blit(buf, 0, 0, vo.image_width,
vo.output_x, vo.output_y, vo.output_width,
vo.output_height);
}
static inline void vo_rect_clear_inl(struct vo_rect *rc)
{
rc->l = rc->t = rc->r = rc->b = 0;
}
static inline bool vo_rect_empty_inl(const struct vo_rect *rc)
{
return rc == NULL || rc->l >= rc->r || rc->t >= rc->b;
}
static inline bool vo_rects_intersect_inl(const struct vo_rect *rc1,
const struct vo_rect *rc2)
{
return !vo_rect_empty_inl(rc1) &&
!vo_rect_empty_inl(rc2) &&
rc1->l < rc2->r && rc1->r > rc2->l &&
rc1->t < rc2->b && rc1->b > rc2->t;
}
/* Sets all coordinates of a vo_rect to 0 */
void vo_rect_clear(struct vo_rect *rc)
{
vo_rect_clear_inl(rc);
}
/* Returns true if left >= right or top >= bottom */
bool vo_rect_empty(const struct vo_rect *rc)
{
return vo_rect_empty_inl(rc);
}
/* Initializes a vo_rect using upper-left corner and extents */
void vo_rect_set_ext(struct vo_rect *rc, int x, int y,
int width, int height)
{
rc->l = x;
rc->t = y;
rc->r = x + width;
rc->b = y + height;
}
/* Query if two rectangles intersect */
bool vo_rects_intersect(const struct vo_rect *rc1,
const struct vo_rect *rc2)
{
return vo_rects_intersect_inl(rc1, rc2);
}
/* Intersect two rectangles, placing the result in rc_dst */
bool vo_rect_intersect(struct vo_rect *rc_dst,
const struct vo_rect *rc1,
const struct vo_rect *rc2)
{
if (rc_dst != NULL)
{
if (vo_rects_intersect_inl(rc1, rc2))
{
rc_dst->l = MAX(rc1->l, rc2->l);
rc_dst->r = MIN(rc1->r, rc2->r);
rc_dst->t = MAX(rc1->t, rc2->t);
rc_dst->b = MIN(rc1->b, rc2->b);
return true;
}
vo_rect_clear_inl(rc_dst);
}
return false;
}
bool vo_rect_union(struct vo_rect *rc_dst,
const struct vo_rect *rc1,
const struct vo_rect *rc2)
{
if (rc_dst != NULL)
{
if (!vo_rect_empty_inl(rc1))
{
if (!vo_rect_empty_inl(rc2))
{
rc_dst->l = MIN(rc1->l, rc2->l);
rc_dst->t = MIN(rc1->t, rc2->t);
rc_dst->r = MAX(rc1->r, rc2->r);
rc_dst->b = MAX(rc1->b, rc2->b);
}
else
{
*rc_dst = *rc1;
}
return true;
}
else if (!vo_rect_empty_inl(rc2))
{
*rc_dst = *rc2;
return true;
}
vo_rect_clear_inl(rc_dst);
}
return false;
}
void vo_rect_offset(struct vo_rect *rc, int dx, int dy)
{
rc->l += dx;
rc->t += dy;
rc->r += dx;
rc->b += dy;
}
/* Shink or stretch each axis - rotate counter-clockwise to retain upright
* orientation on rotated displays (they rotate clockwise) */
void stretch_image_plane(const uint8_t * src, uint8_t *dst, int stride,
int src_w, int src_h, int dst_w, int dst_h)
{
uint8_t *dst_end = dst + dst_w*dst_h;
#if LCD_WIDTH >= LCD_HEIGHT
int src_w2 = src_w*2; /* 2x dimensions (for rounding before division) */
int dst_w2 = dst_w*2;
int src_h2 = src_h*2;
int dst_h2 = dst_h*2;
int qw = src_w2 / dst_w2; /* src-dst width ratio quotient */
int rw = src_w2 - qw*dst_w2; /* src-dst width ratio remainder */
int qh = src_h2 / dst_h2; /* src-dst height ratio quotient */
int rh = src_h2 - qh*dst_h2; /* src-dst height ratio remainder */
int dw = dst_w; /* Width error accumulator */
int dh = dst_h; /* Height error accumulator */
#else
int src_w2 = src_w*2;
int dst_w2 = dst_h*2;
int src_h2 = src_h*2;
int dst_h2 = dst_w*2;
int qw = src_h2 / dst_w2;
int rw = src_h2 - qw*dst_w2;
int qh = src_w2 / dst_h2;
int rh = src_w2 - qh*dst_h2;
int dw = dst_h;
int dh = dst_w;
src += src_w - 1;
#endif
while (1)
{
const uint8_t *s = src;
#if LCD_WIDTH >= LCD_HEIGHT
uint8_t * const dst_line_end = dst + dst_w;
#else
uint8_t * const dst_line_end = dst + dst_h;
#endif
while (1)
{
*dst++ = *s;
if (dst >= dst_line_end)
{
dw = dst_w;
break;
}
#if LCD_WIDTH >= LCD_HEIGHT
s += qw;
#else
s += qw*stride;
#endif
dw += rw;
if (dw >= dst_w2)
{
dw -= dst_w2;
#if LCD_WIDTH >= LCD_HEIGHT
s++;
#else
s += stride;
#endif
}
}
if (dst >= dst_end)
break;
#if LCD_WIDTH >= LCD_HEIGHT
src += qh*stride;
#else
src -= qh;
#endif
dh += rh;
if (dh >= dst_h2)
{
dh -= dst_h2;
#if LCD_WIDTH >= LCD_HEIGHT
src += stride;
#else
src--;
#endif
}
}
}
bool vo_draw_frame_thumb(uint8_t * const * buf, const struct vo_rect *rc)
{
void *mem;
size_t bufsize;
uint8_t *yuv[3];
struct vo_rect thumb_rc;
int thumb_width, thumb_height;
int thumb_uv_width, thumb_uv_height;
if (buf == NULL)
return false;
/* Obtain rectangle as clipped to the screen */
vo_rect_set_ext(&thumb_rc, 0, 0, LCD_WIDTH, LCD_HEIGHT);
if (!vo_rect_intersect(&thumb_rc, rc, &thumb_rc))
return true;
DEBUGF("thumb_rc: %d, %d, %d, %d\n", thumb_rc.l, thumb_rc.t,
thumb_rc.r, thumb_rc.b);
thumb_width = rc->r - rc->l;
thumb_height = rc->b - rc->t;
thumb_uv_width = thumb_width / 2;
thumb_uv_height = thumb_height / 2;
DEBUGF("thumb: w: %d h: %d uvw: %d uvh: %d\n", thumb_width,
thumb_height, thumb_uv_width, thumb_uv_height);
/* Use remaining mpeg2 buffer as temp space */
mem = mpeg2_get_buf(&bufsize);
if (bufsize < (size_t)(thumb_width*thumb_height)
#ifdef HAVE_LCD_COLOR
+ 2u*(thumb_uv_width * thumb_uv_height)
#endif
)
{
DEBUGF("thumb: insufficient buffer\n");
return false;
}
yuv[0] = mem;
stretch_image_plane(buf[0], yuv[0], vo.image_width,
vo.display_width, vo.display_height,
thumb_width, thumb_height);
#ifdef HAVE_LCD_COLOR
yuv[1] = yuv[0] + thumb_width*thumb_height;
yuv[2] = yuv[1] + thumb_uv_width*thumb_uv_height;
stretch_image_plane(buf[1], yuv[1], vo.image_width / 2,
vo.display_width / 2, vo.display_height / 2,
thumb_uv_width, thumb_uv_height);
stretch_image_plane(buf[2], yuv[2], vo.image_width / 2,
vo.display_width / 2, vo.display_height / 2,
thumb_uv_width, thumb_uv_height);
#endif
#if LCD_WIDTH >= LCD_HEIGHT
yuv_blit(yuv, 0, 0, thumb_width,
thumb_rc.l, thumb_rc.t,
thumb_rc.r - thumb_rc.l,
thumb_rc.b - thumb_rc.t);
#else
yuv_blit(yuv, 0, 0, thumb_height,
thumb_rc.t, thumb_rc.l,
thumb_rc.b - thumb_rc.t,
thumb_rc.r - thumb_rc.l);
#endif /* LCD_WIDTH >= LCD_HEIGHT */
return true;
}
void vo_setup(const mpeg2_sequence_t * sequence)
{
vo.image_width = sequence->width;
vo.image_height = sequence->height;
vo.display_width = sequence->display_width;
vo.display_height = sequence->display_height;
DEBUGF("vo_setup - w:%d h:%d\n", vo.display_width, vo.display_height);
vo.image_chroma_x = vo.image_width / sequence->chroma_width;
vo.image_chroma_y = vo.image_height / sequence->chroma_height;
if (sequence->display_width >= SCREEN_WIDTH)
{
vo.rc_vid.l = 0;
vo.rc_vid.r = SCREEN_WIDTH;
}
else
{
vo.rc_vid.l = (SCREEN_WIDTH - sequence->display_width) / 2;
#ifdef HAVE_LCD_COLOR
vo.rc_vid.l &= ~1;
#endif
vo.rc_vid.r = vo.rc_vid.l + sequence->display_width;
}
if (sequence->display_height >= SCREEN_HEIGHT)
{
vo.rc_vid.t = 0;
vo.rc_vid.b = SCREEN_HEIGHT;
}
else
{
vo.rc_vid.t = (SCREEN_HEIGHT - sequence->display_height) / 2;
#ifdef HAVE_LCD_COLOR
vo.rc_vid.t &= ~1;
#endif
vo.rc_vid.b = vo.rc_vid.t + sequence->display_height;
}
vo_set_clip_rect(&vo.rc_clip);
}
void vo_dimensions(struct vo_ext *sz)
{
sz->w = vo.display_width;
sz->h = vo.display_height;
}
bool vo_init(void)
{
vo.flags = 0;
vo_rect_set_ext(&vo.rc_clip, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
video_lock_init();
return true;
}
bool vo_show(bool show)
{
bool vis = vo.flags & VO_VISIBLE;
if (show)
vo.flags |= VO_VISIBLE;
else
vo.flags &= ~VO_VISIBLE;
return vis;
}
bool vo_is_visible(void)
{
return vo.flags & VO_VISIBLE;
}
void vo_cleanup(void)
{
vo.flags = 0;
}
void vo_set_clip_rect(const struct vo_rect *rc)
{
struct vo_rect rc_out;
if (rc == NULL)
vo_rect_set_ext(&vo.rc_clip, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
else
vo.rc_clip = *rc;
if (!vo_rect_intersect(&rc_out, &vo.rc_vid, &vo.rc_clip))
vo.flags &= ~VO_NON_NULL_RECT;
else
vo.flags |= VO_NON_NULL_RECT;
vo.output_x = rc_out.l;
vo.output_y = rc_out.t;
vo.output_width = rc_out.r - rc_out.l;
vo.output_height = rc_out.b - rc_out.t;
}
#if NUM_CORES > 1
void vo_lock(void)
{
video_lock();
}
void vo_unlock(void)
{
video_unlock();
}
#endif