1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * mpegplayer video output routines
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
21 #include "mpeg2dec_config.h"
24 #include "mpegplayer.h"
26 #define VO_NON_NULL_RECT 0x1
27 #define VO_VISIBLE 0x2
42 struct vo_rect rc_vid
;
43 struct vo_rect rc_clip
;
44 void (*post_draw_callback
)(void);
48 /* Cache aligned and padded to avoid clobbering other processors' cacheable
50 static uint8_t __vo_data
[CACHEALIGN_UP(sizeof(struct vo_data
))]
52 #define vo (*((struct vo_data *)__vo_data))
54 static struct vo_data vo
;
58 static struct mutex vo_mtx SHAREDBSS_ATTR
;
61 static inline void video_lock_init(void)
64 rb
->mutex_init(&vo_mtx
);
68 static inline void video_lock(void)
71 rb
->mutex_lock(&vo_mtx
);
75 static inline void video_unlock(void)
78 rb
->mutex_unlock(&vo_mtx
);
83 /* Draw a black rectangle if no video frame is available */
84 static void vo_draw_black(struct vo_rect
*rc
)
91 foreground
= mylcd_get_foreground();
93 mylcd_set_foreground(MYLCD_BLACK
);
104 #if LCD_WIDTH >= LCD_HEIGHT
108 h
= vo
.output_height
;
110 x
= LCD_WIDTH
- vo
.output_height
- vo
.output_y
;
112 w
= vo
.output_height
;
117 mylcd_fillrect(x
, y
, w
, h
);
118 mylcd_update_rect(x
, y
, w
, h
);
120 mylcd_set_foreground(foreground
);
125 static inline void yuv_blit(uint8_t * const * buf
, int src_x
, int src_y
,
126 int stride
, int x
, int y
, int width
, int height
)
130 #ifdef HAVE_LCD_COLOR
131 rb
->lcd_blit_yuv(buf
, src_x
, src_y
, stride
, x
, y
, width
, height
);
133 grey_ub_gray_bitmap_part(buf
[0], src_x
, src_y
, stride
, x
, y
, width
, height
);
139 void vo_draw_frame(uint8_t * const * buf
)
141 if ((vo
.flags
& (VO_NON_NULL_RECT
| VO_VISIBLE
)) !=
142 (VO_NON_NULL_RECT
| VO_VISIBLE
))
144 /* Frame is hidden - either by being set invisible or is clipped
146 DEBUGF("vo hidden\n");
148 else if (buf
== NULL
)
150 /* No frame exists - draw black */
152 DEBUGF("vo no frame\n");
156 yuv_blit(buf
, 0, 0, vo
.image_width
,
157 vo
.output_x
, vo
.output_y
, vo
.output_width
,
161 if (vo
.post_draw_callback
)
162 vo
.post_draw_callback();
165 static inline void vo_rect_clear_inl(struct vo_rect
*rc
)
167 rc
->l
= rc
->t
= rc
->r
= rc
->b
= 0;
170 static inline bool vo_rect_empty_inl(const struct vo_rect
*rc
)
172 return rc
== NULL
|| rc
->l
>= rc
->r
|| rc
->t
>= rc
->b
;
175 static inline bool vo_rects_intersect_inl(const struct vo_rect
*rc1
,
176 const struct vo_rect
*rc2
)
178 return !vo_rect_empty_inl(rc1
) &&
179 !vo_rect_empty_inl(rc2
) &&
180 rc1
->l
< rc2
->r
&& rc1
->r
> rc2
->l
&&
181 rc1
->t
< rc2
->b
&& rc1
->b
> rc2
->t
;
184 /* Sets all coordinates of a vo_rect to 0 */
185 void vo_rect_clear(struct vo_rect
*rc
)
187 vo_rect_clear_inl(rc
);
190 /* Returns true if left >= right or top >= bottom */
191 bool vo_rect_empty(const struct vo_rect
*rc
)
193 return vo_rect_empty_inl(rc
);
196 /* Initializes a vo_rect using upper-left corner and extents */
197 void vo_rect_set_ext(struct vo_rect
*rc
, int x
, int y
,
198 int width
, int height
)
206 /* Query if two rectangles intersect */
207 bool vo_rects_intersect(const struct vo_rect
*rc1
,
208 const struct vo_rect
*rc2
)
210 return vo_rects_intersect_inl(rc1
, rc2
);
213 /* Intersect two rectangles, placing the result in rc_dst */
214 bool vo_rect_intersect(struct vo_rect
*rc_dst
,
215 const struct vo_rect
*rc1
,
216 const struct vo_rect
*rc2
)
220 if (vo_rects_intersect_inl(rc1
, rc2
))
222 rc_dst
->l
= MAX(rc1
->l
, rc2
->l
);
223 rc_dst
->r
= MIN(rc1
->r
, rc2
->r
);
224 rc_dst
->t
= MAX(rc1
->t
, rc2
->t
);
225 rc_dst
->b
= MIN(rc1
->b
, rc2
->b
);
229 vo_rect_clear_inl(rc_dst
);
235 bool vo_rect_union(struct vo_rect
*rc_dst
,
236 const struct vo_rect
*rc1
,
237 const struct vo_rect
*rc2
)
241 if (!vo_rect_empty_inl(rc1
))
243 if (!vo_rect_empty_inl(rc2
))
245 rc_dst
->l
= MIN(rc1
->l
, rc2
->l
);
246 rc_dst
->t
= MIN(rc1
->t
, rc2
->t
);
247 rc_dst
->r
= MAX(rc1
->r
, rc2
->r
);
248 rc_dst
->b
= MAX(rc1
->b
, rc2
->b
);
257 else if (!vo_rect_empty_inl(rc2
))
263 vo_rect_clear_inl(rc_dst
);
269 void vo_rect_offset(struct vo_rect
*rc
, int dx
, int dy
)
277 /* Shink or stretch each axis - rotate counter-clockwise to retain upright
278 * orientation on rotated displays (they rotate clockwise) */
279 void stretch_image_plane(const uint8_t * src
, uint8_t *dst
, int stride
,
280 int src_w
, int src_h
, int dst_w
, int dst_h
)
282 uint8_t *dst_end
= dst
+ dst_w
*dst_h
;
284 #if LCD_WIDTH >= LCD_HEIGHT
285 int src_w2
= src_w
*2; /* 2x dimensions (for rounding before division) */
286 int dst_w2
= dst_w
*2;
287 int src_h2
= src_h
*2;
288 int dst_h2
= dst_h
*2;
289 int qw
= src_w2
/ dst_w2
; /* src-dst width ratio quotient */
290 int rw
= src_w2
- qw
*dst_w2
; /* src-dst width ratio remainder */
291 int qh
= src_h2
/ dst_h2
; /* src-dst height ratio quotient */
292 int rh
= src_h2
- qh
*dst_h2
; /* src-dst height ratio remainder */
293 int dw
= dst_w
; /* Width error accumulator */
294 int dh
= dst_h
; /* Height error accumulator */
296 int src_w2
= src_w
*2;
297 int dst_w2
= dst_h
*2;
298 int src_h2
= src_h
*2;
299 int dst_h2
= dst_w
*2;
300 int qw
= src_h2
/ dst_w2
;
301 int rw
= src_h2
- qw
*dst_w2
;
302 int qh
= src_w2
/ dst_h2
;
303 int rh
= src_w2
- qh
*dst_h2
;
312 const uint8_t *s
= src
;
313 #if LCD_WIDTH >= LCD_HEIGHT
314 uint8_t * const dst_line_end
= dst
+ dst_w
;
316 uint8_t * const dst_line_end
= dst
+ dst_h
;
322 if (dst
>= dst_line_end
)
328 #if LCD_WIDTH >= LCD_HEIGHT
338 #if LCD_WIDTH >= LCD_HEIGHT
348 #if LCD_WIDTH >= LCD_HEIGHT
358 #if LCD_WIDTH >= LCD_HEIGHT
367 bool vo_draw_frame_thumb(uint8_t * const * buf
, const struct vo_rect
*rc
)
372 struct vo_rect thumb_rc
;
373 int thumb_width
, thumb_height
;
374 int thumb_uv_width
, thumb_uv_height
;
376 /* Obtain rectangle as clipped to the screen */
377 vo_rect_set_ext(&thumb_rc
, 0, 0, LCD_WIDTH
, LCD_HEIGHT
);
378 if (!vo_rect_intersect(&thumb_rc
, rc
, &thumb_rc
))
384 DEBUGF("thumb_rc: %d, %d, %d, %d\n", thumb_rc
.l
, thumb_rc
.t
,
385 thumb_rc
.r
, thumb_rc
.b
);
387 thumb_width
= rc
->r
- rc
->l
;
388 thumb_height
= rc
->b
- rc
->t
;
389 thumb_uv_width
= thumb_width
/ 2;
390 thumb_uv_height
= thumb_height
/ 2;
392 DEBUGF("thumb: w: %d h: %d uvw: %d uvh: %d\n", thumb_width
,
393 thumb_height
, thumb_uv_width
, thumb_uv_height
);
395 /* Use remaining mpeg2 buffer as temp space */
396 mem
= mpeg2_get_buf(&bufsize
);
398 if (bufsize
< (size_t)(thumb_width
*thumb_height
)
399 #ifdef HAVE_LCD_COLOR
400 + 2u*(thumb_uv_width
* thumb_uv_height
)
404 DEBUGF("thumb: insufficient buffer\n");
409 stretch_image_plane(buf
[0], yuv
[0], vo
.image_width
,
410 vo
.display_width
, vo
.display_height
,
411 thumb_width
, thumb_height
);
413 #ifdef HAVE_LCD_COLOR
414 yuv
[1] = yuv
[0] + thumb_width
*thumb_height
;
415 yuv
[2] = yuv
[1] + thumb_uv_width
*thumb_uv_height
;
417 stretch_image_plane(buf
[1], yuv
[1], vo
.image_width
/ 2,
418 vo
.display_width
/ 2, vo
.display_height
/ 2,
419 thumb_uv_width
, thumb_uv_height
);
421 stretch_image_plane(buf
[2], yuv
[2], vo
.image_width
/ 2,
422 vo
.display_width
/ 2, vo
.display_height
/ 2,
423 thumb_uv_width
, thumb_uv_height
);
426 #if LCD_WIDTH >= LCD_HEIGHT
427 yuv_blit(yuv
, 0, 0, thumb_width
,
428 thumb_rc
.l
, thumb_rc
.t
,
429 thumb_rc
.r
- thumb_rc
.l
,
430 thumb_rc
.b
- thumb_rc
.t
);
432 yuv_blit(yuv
, 0, 0, thumb_height
,
433 thumb_rc
.t
, thumb_rc
.l
,
434 thumb_rc
.b
- thumb_rc
.t
,
435 thumb_rc
.r
- thumb_rc
.l
);
436 #endif /* LCD_WIDTH >= LCD_HEIGHT */
441 vo_draw_black(&thumb_rc
);
445 void vo_setup(const mpeg2_sequence_t
* sequence
)
447 vo
.image_width
= sequence
->width
;
448 vo
.image_height
= sequence
->height
;
449 vo
.display_width
= sequence
->display_width
;
450 vo
.display_height
= sequence
->display_height
;
452 DEBUGF("vo_setup - w:%d h:%d\n", vo
.display_width
, vo
.display_height
);
454 vo
.image_chroma_x
= vo
.image_width
/ sequence
->chroma_width
;
455 vo
.image_chroma_y
= vo
.image_height
/ sequence
->chroma_height
;
457 if (sequence
->display_width
>= SCREEN_WIDTH
)
460 vo
.rc_vid
.r
= SCREEN_WIDTH
;
464 vo
.rc_vid
.l
= (SCREEN_WIDTH
- sequence
->display_width
) / 2;
465 #ifdef HAVE_LCD_COLOR
468 vo
.rc_vid
.r
= vo
.rc_vid
.l
+ sequence
->display_width
;
471 if (sequence
->display_height
>= SCREEN_HEIGHT
)
474 vo
.rc_vid
.b
= SCREEN_HEIGHT
;
478 vo
.rc_vid
.t
= (SCREEN_HEIGHT
- sequence
->display_height
) / 2;
479 #ifdef HAVE_LCD_COLOR
482 vo
.rc_vid
.b
= vo
.rc_vid
.t
+ sequence
->display_height
;
485 vo_set_clip_rect(&vo
.rc_clip
);
488 void vo_dimensions(struct vo_ext
*sz
)
490 sz
->w
= vo
.display_width
;
491 sz
->h
= vo
.display_height
;
497 vo_rect_set_ext(&vo
.rc_clip
, 0, 0, SCREEN_WIDTH
, SCREEN_HEIGHT
);
502 bool vo_show(bool show
)
504 bool vis
= vo
.flags
& VO_VISIBLE
;
507 vo
.flags
|= VO_VISIBLE
;
509 vo
.flags
&= ~VO_VISIBLE
;
514 bool vo_is_visible(void)
516 return vo
.flags
& VO_VISIBLE
;
519 void vo_cleanup(void)
524 void vo_set_clip_rect(const struct vo_rect
*rc
)
526 struct vo_rect rc_out
;
529 vo_rect_set_ext(&vo
.rc_clip
, 0, 0, SCREEN_WIDTH
, SCREEN_HEIGHT
);
533 if (!vo_rect_intersect(&rc_out
, &vo
.rc_vid
, &vo
.rc_clip
))
534 vo
.flags
&= ~VO_NON_NULL_RECT
;
536 vo
.flags
|= VO_NON_NULL_RECT
;
538 vo
.output_x
= rc_out
.l
;
539 vo
.output_y
= rc_out
.t
;
540 vo
.output_width
= rc_out
.r
- rc_out
.l
;
541 vo
.output_height
= rc_out
.b
- rc_out
.t
;
544 bool vo_get_clip_rect(struct vo_rect
*rc
)
548 rc
->r
= rc
->l
+ vo
.output_width
;
549 rc
->b
= rc
->t
+ vo
.output_height
;
550 return (vo
.flags
& VO_NON_NULL_RECT
) != 0;
553 void vo_set_post_draw_callback(void (*cb
)(void))
555 vo
.post_draw_callback
= cb
;