| /* GStreamer DVD Sub-Picture Unit |
| * Copyright (C) 2007 Fluendo S.A. <info@fluendo.com> |
| * Copyright (C) 2009 Jan Schmidt <thaytan@noraisin.net> |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Library General Public |
| * License as published by the Free Software Foundation; either |
| * version 2 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Library General Public License for more details. |
| * |
| * You should have received a copy of the GNU Library General Public |
| * License along with this library; if not, write to the |
| * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, |
| * Boston, MA 02110-1301, USA. |
| */ |
| #ifdef HAVE_CONFIG_H |
| # include <config.h> |
| #endif |
| |
| #include <string.h> |
| |
| #include <gst/gst.h> |
| |
| #include "gstdvdspu.h" |
| |
| GST_DEBUG_CATEGORY_EXTERN (dvdspu_debug); |
| #define GST_CAT_DEFAULT dvdspu_debug |
| |
| static void |
| gstspu_vobsub_recalc_palette (GstDVDSpu * dvdspu, |
| SpuColour * dest, guint8 * idx, guint8 * alpha) |
| { |
| SpuState *state = &dvdspu->spu_state; |
| gint i; |
| |
| if (state->vobsub.current_clut[idx[0]] != 0) { |
| for (i = 0; i < 4; i++, dest++) { |
| guint32 col = state->vobsub.current_clut[idx[i]]; |
| |
| /* Convert incoming 4-bit alpha to 8 bit for blending */ |
| dest->A = (alpha[i] << 4) | alpha[i]; |
| dest->Y = ((guint16) ((col >> 16) & 0xff)) * dest->A; |
| /* U/V are stored as V/U in the clut words, so switch them */ |
| dest->V = ((guint16) ((col >> 8) & 0xff)) * dest->A; |
| dest->U = ((guint16) (col & 0xff)) * dest->A; |
| } |
| } else { |
| int y = 240; |
| |
| /* The CLUT presumably hasn't been set, so we'll just guess some |
| * values for the non-transparent colors (white, grey, black) */ |
| for (i = 0; i < 4; i++, dest++) { |
| dest->A = (alpha[i] << 4) | alpha[i]; |
| if (alpha[i] != 0) { |
| dest[0].Y = y * dest[0].A; |
| y -= 112; |
| if (y < 0) |
| y = 0; |
| } |
| dest[0].U = 128 * dest[0].A; |
| dest[0].V = 128 * dest[0].A; |
| } |
| } |
| } |
| |
| /* Recalculate the main, HL & ChgCol palettes */ |
| static void |
| gstspu_vobsub_update_palettes (GstDVDSpu * dvdspu, SpuState * state) |
| { |
| guint8 index[4]; /* Indices for the palette */ |
| guint8 alpha[4]; /* Alpha values the palette */ |
| |
| if (state->vobsub.main_pal_dirty) { |
| gstspu_vobsub_recalc_palette (dvdspu, state->vobsub.main_pal, |
| state->vobsub.main_idx, state->vobsub.main_alpha); |
| |
| /* Need to refresh the hl_ctrl info copies of the main palette too */ |
| memcpy (state->vobsub.hl_ctrl_i.pix_ctrl_i[0].pal_cache, |
| state->vobsub.main_pal, 4 * sizeof (SpuColour)); |
| memcpy (state->vobsub.hl_ctrl_i.pix_ctrl_i[2].pal_cache, |
| state->vobsub.main_pal, 4 * sizeof (SpuColour)); |
| |
| state->vobsub.main_pal_dirty = FALSE; |
| } |
| |
| if (state->vobsub.hl_pal_dirty) { |
| gstspu_vobsub_recalc_palette (dvdspu, |
| state->vobsub.hl_ctrl_i.pix_ctrl_i[1].pal_cache, state->vobsub.hl_idx, |
| state->vobsub.hl_alpha); |
| state->vobsub.hl_pal_dirty = FALSE; |
| } |
| |
| /* Update the offset positions for the highlight region */ |
| if (state->vobsub.hl_rect.top != -1) { |
| state->vobsub.hl_ctrl_i.top = state->vobsub.hl_rect.top; |
| state->vobsub.hl_ctrl_i.bottom = state->vobsub.hl_rect.bottom; |
| state->vobsub.hl_ctrl_i.n_changes = 3; |
| state->vobsub.hl_ctrl_i.pix_ctrl_i[0].left = 0; |
| state->vobsub.hl_ctrl_i.pix_ctrl_i[1].left = state->vobsub.hl_rect.left; |
| state->vobsub.hl_ctrl_i.pix_ctrl_i[2].left = |
| state->vobsub.hl_rect.right + 1; |
| } |
| |
| if (state->vobsub.line_ctrl_i_pal_dirty) { |
| gint16 l, c; |
| GST_LOG_OBJECT (dvdspu, "Updating chg-col-con palettes"); |
| for (l = 0; l < state->vobsub.n_line_ctrl_i; l++) { |
| SpuVobsubLineCtrlI *cur_line_ctrl = state->vobsub.line_ctrl_i + l; |
| |
| for (c = 0; c < cur_line_ctrl->n_changes; c++) { |
| SpuVobsubPixCtrlI *cur = cur_line_ctrl->pix_ctrl_i + c; |
| |
| index[3] = (cur->palette >> 28) & 0x0f; |
| index[2] = (cur->palette >> 24) & 0x0f; |
| index[1] = (cur->palette >> 20) & 0x0f; |
| index[0] = (cur->palette >> 16) & 0x0f; |
| |
| alpha[3] = (cur->palette >> 12) & 0x0f; |
| alpha[2] = (cur->palette >> 8) & 0x0f; |
| alpha[1] = (cur->palette >> 4) & 0x0f; |
| alpha[0] = (cur->palette) & 0x0f; |
| gstspu_vobsub_recalc_palette (dvdspu, cur->pal_cache, index, alpha); |
| } |
| } |
| state->vobsub.line_ctrl_i_pal_dirty = FALSE; |
| } |
| } |
| |
| static inline guint8 |
| gstspu_vobsub_get_nibble (SpuState * state, guint16 * rle_offset) |
| { |
| guint8 ret; |
| |
| if (G_UNLIKELY (*rle_offset >= state->vobsub.max_offset)) |
| return 0; /* Overran the buffer */ |
| |
| ret = state->vobsub.pix_buf_map.data[(*rle_offset) / 2]; |
| |
| /* If the offset is even, we shift the answer down 4 bits, otherwise not */ |
| if (*rle_offset & 0x01) |
| ret &= 0x0f; |
| else |
| ret = ret >> 4; |
| |
| (*rle_offset)++; |
| return ret; |
| } |
| |
| static guint16 |
| gstspu_vobsub_get_rle_code (SpuState * state, guint16 * rle_offset) |
| { |
| guint16 code; |
| |
| code = gstspu_vobsub_get_nibble (state, rle_offset); |
| if (code < 0x4) { /* 4 .. f */ |
| code = (code << 4) | gstspu_vobsub_get_nibble (state, rle_offset); |
| if (code < 0x10) { /* 1x .. 3x */ |
| code = (code << 4) | gstspu_vobsub_get_nibble (state, rle_offset); |
| if (code < 0x40) { /* 04x .. 0fx */ |
| code = (code << 4) | gstspu_vobsub_get_nibble (state, rle_offset); |
| } |
| } |
| } |
| return code; |
| } |
| |
| static inline gboolean |
| gstspu_vobsub_draw_rle_run (SpuState * state, gint16 x, gint16 end, |
| SpuColour * colour) |
| { |
| #if 0 |
| GST_LOG ("Y: %d x: %d end %d col %d %d %d %d", |
| state->vobsub.cur_Y, x, end, colour->Y, colour->U, colour->V, colour->A); |
| #endif |
| |
| if (colour->A != 0) { |
| guint32 inv_A = 0xff - colour->A; |
| |
| /* FIXME: This could be more efficient */ |
| while (x < end) { |
| state->vobsub.out_Y[x] = |
| (inv_A * state->vobsub.out_Y[x] + colour->Y) / 0xff; |
| state->vobsub.out_U[x / 2] += colour->U; |
| state->vobsub.out_V[x / 2] += colour->V; |
| state->vobsub.out_A[x / 2] += colour->A; |
| x++; |
| } |
| /* Update the compositing buffer so we know how much to blend later */ |
| *(state->vobsub.comp_last_x_ptr) = end - 1; /* end is the start of the *next* run */ |
| |
| return TRUE; |
| } |
| return FALSE; |
| } |
| |
| static inline gint16 |
| rle_end_x (guint16 rle_code, gint16 x, gint16 end) |
| { |
| /* run length = rle_code >> 2 */ |
| if (G_UNLIKELY (((rle_code >> 2) == 0))) |
| return end; |
| else |
| return MIN (end, x + (rle_code >> 2)); |
| } |
| |
| static gboolean gstspu_vobsub_render_line_with_chgcol (SpuState * state, |
| guint8 * planes[3], guint16 * rle_offset); |
| static gboolean gstspu_vobsub_update_chgcol (SpuState * state); |
| |
| static gboolean |
| gstspu_vobsub_render_line (SpuState * state, guint8 * planes[3], |
| guint16 * rle_offset) |
| { |
| gint16 x, next_x, end, rle_code, next_draw_x; |
| SpuColour *colour; |
| gboolean visible = FALSE; |
| |
| /* Check for special case of chg_col info to use (either highlight or |
| * ChgCol command */ |
| if (state->vobsub.cur_chg_col != NULL) { |
| if (gstspu_vobsub_update_chgcol (state)) { |
| /* Check the top & bottom, because we might not be within the region yet */ |
| if (state->vobsub.cur_Y >= state->vobsub.cur_chg_col->top && |
| state->vobsub.cur_Y <= state->vobsub.cur_chg_col->bottom) { |
| return gstspu_vobsub_render_line_with_chgcol (state, planes, |
| rle_offset); |
| } |
| } |
| } |
| |
| /* No special case. Render as normal */ |
| |
| /* Set up our output pointers */ |
| state->vobsub.out_Y = planes[0]; |
| state->vobsub.out_U = state->comp_bufs[0]; |
| state->vobsub.out_V = state->comp_bufs[1]; |
| state->vobsub.out_A = state->comp_bufs[2]; |
| /* We always need to start our RLE decoding byte_aligned */ |
| *rle_offset = GST_ROUND_UP_2 (*rle_offset); |
| |
| x = state->vobsub.disp_rect.left; |
| end = state->vobsub.disp_rect.right + 1; |
| while (x < end) { |
| rle_code = gstspu_vobsub_get_rle_code (state, rle_offset); |
| colour = &state->vobsub.main_pal[rle_code & 3]; |
| next_x = rle_end_x (rle_code, x, end); |
| next_draw_x = next_x; |
| if (next_draw_x > state->vobsub.clip_rect.right) |
| next_draw_x = state->vobsub.clip_rect.right; /* ensure no overflow */ |
| /* Now draw the run between [x,next_x) */ |
| if (state->vobsub.cur_Y >= state->vobsub.clip_rect.top && |
| state->vobsub.cur_Y <= state->vobsub.clip_rect.bottom) |
| visible |= gstspu_vobsub_draw_rle_run (state, x, next_draw_x, colour); |
| x = next_x; |
| } |
| |
| return visible; |
| } |
| |
| static gboolean |
| gstspu_vobsub_update_chgcol (SpuState * state) |
| { |
| if (state->vobsub.cur_chg_col == NULL) |
| return FALSE; |
| |
| if (state->vobsub.cur_Y <= state->vobsub.cur_chg_col->bottom) |
| return TRUE; |
| |
| while (state->vobsub.cur_chg_col < state->vobsub.cur_chg_col_end) { |
| if (state->vobsub.cur_Y >= state->vobsub.cur_chg_col->top && |
| state->vobsub.cur_Y <= state->vobsub.cur_chg_col->bottom) { |
| #if 0 |
| g_print ("Stopped @ entry %d with top %d bottom %d, cur_y %d", |
| (gint16) (state->vobsub.cur_chg_col - state->vobsub.line_ctrl_i), |
| state->vobsub.cur_chg_col->top, state->vobsub.cur_chg_col->bottom, y); |
| #endif |
| return TRUE; |
| } |
| state->vobsub.cur_chg_col++; |
| } |
| |
| /* Finished all our cur_chg_col entries. Use the main palette from here on */ |
| state->vobsub.cur_chg_col = NULL; |
| return FALSE; |
| } |
| |
| static gboolean |
| gstspu_vobsub_render_line_with_chgcol (SpuState * state, guint8 * planes[3], |
| guint16 * rle_offset) |
| { |
| SpuVobsubLineCtrlI *chg_col = state->vobsub.cur_chg_col; |
| |
| gint16 x, next_x, disp_end, rle_code, run_end, run_draw_end; |
| SpuColour *colour; |
| SpuVobsubPixCtrlI *cur_pix_ctrl; |
| SpuVobsubPixCtrlI *next_pix_ctrl; |
| SpuVobsubPixCtrlI *end_pix_ctrl; |
| SpuVobsubPixCtrlI dummy_pix_ctrl; |
| gboolean visible = FALSE; |
| gint16 cur_reg_end; |
| gint i; |
| |
| state->vobsub.out_Y = planes[0]; |
| state->vobsub.out_U = state->comp_bufs[0]; |
| state->vobsub.out_V = state->comp_bufs[1]; |
| state->vobsub.out_A = state->comp_bufs[2]; |
| |
| /* We always need to start our RLE decoding byte_aligned */ |
| *rle_offset = GST_ROUND_UP_2 (*rle_offset); |
| |
| /* Our run will cover the display rect */ |
| x = state->vobsub.disp_rect.left; |
| disp_end = state->vobsub.disp_rect.right + 1; |
| |
| /* Work out the first pixel control info, which may point to the dummy entry if |
| * the global palette/alpha need using initally */ |
| cur_pix_ctrl = chg_col->pix_ctrl_i; |
| end_pix_ctrl = chg_col->pix_ctrl_i + chg_col->n_changes; |
| |
| if (cur_pix_ctrl->left != 0) { |
| next_pix_ctrl = cur_pix_ctrl; |
| cur_pix_ctrl = &dummy_pix_ctrl; |
| for (i = 0; i < 4; i++) /* Copy the main palette to our dummy entry */ |
| dummy_pix_ctrl.pal_cache[i] = state->vobsub.main_pal[i]; |
| } else { |
| next_pix_ctrl = cur_pix_ctrl + 1; |
| } |
| if (next_pix_ctrl < end_pix_ctrl) |
| cur_reg_end = next_pix_ctrl->left; |
| else |
| cur_reg_end = disp_end; |
| |
| /* Render stuff */ |
| while (x < disp_end) { |
| rle_code = gstspu_vobsub_get_rle_code (state, rle_offset); |
| next_x = rle_end_x (rle_code, x, disp_end); |
| |
| /* Now draw the run between [x,next_x), crossing palette regions as needed */ |
| while (x < next_x) { |
| run_end = MIN (next_x, cur_reg_end); |
| |
| run_draw_end = run_end; |
| if (run_draw_end > state->vobsub.clip_rect.right) |
| run_draw_end = state->vobsub.clip_rect.right; /* ensure no overflow */ |
| |
| if (G_LIKELY (x < run_end)) { |
| colour = &cur_pix_ctrl->pal_cache[rle_code & 3]; |
| visible |= gstspu_vobsub_draw_rle_run (state, x, run_draw_end, colour); |
| x = run_end; |
| } |
| |
| if (x >= cur_reg_end) { |
| /* Advance to next region */ |
| cur_pix_ctrl = next_pix_ctrl; |
| next_pix_ctrl++; |
| |
| if (next_pix_ctrl < end_pix_ctrl) |
| cur_reg_end = next_pix_ctrl->left; |
| else |
| cur_reg_end = disp_end; |
| } |
| } |
| } |
| |
| return visible; |
| } |
| |
| static void |
| gstspu_vobsub_blend_comp_buffers (SpuState * state, guint8 * planes[3]) |
| { |
| state->comp_left = state->vobsub.disp_rect.left; |
| state->comp_right = |
| MAX (state->vobsub.comp_last_x[0], state->vobsub.comp_last_x[1]); |
| |
| state->comp_left = MAX (state->comp_left, state->vobsub.clip_rect.left); |
| state->comp_right = MIN (state->comp_right, state->vobsub.clip_rect.right); |
| |
| gstspu_blend_comp_buffers (state, planes); |
| } |
| |
| static void |
| gstspu_vobsub_clear_comp_buffers (SpuState * state) |
| { |
| state->comp_left = state->vobsub.clip_rect.left; |
| state->comp_right = state->vobsub.clip_rect.right; |
| |
| gstspu_clear_comp_buffers (state); |
| |
| state->vobsub.comp_last_x[0] = -1; |
| state->vobsub.comp_last_x[1] = -1; |
| } |
| |
| static void |
| gstspu_vobsub_draw_highlight (SpuState * state, |
| GstVideoFrame * frame, SpuRect * rect) |
| { |
| guint8 *cur; |
| gint16 pos; |
| gint ystride; |
| |
| ystride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0); |
| |
| cur = GST_VIDEO_FRAME_COMP_DATA (frame, 0) + ystride * rect->top; |
| for (pos = rect->left + 1; pos < rect->right; pos++) |
| cur[pos] = (cur[pos] / 2) + 0x8; |
| cur = GST_VIDEO_FRAME_COMP_DATA (frame, 0) + ystride * rect->bottom; |
| for (pos = rect->left + 1; pos < rect->right; pos++) |
| cur[pos] = (cur[pos] / 2) + 0x8; |
| cur = GST_VIDEO_FRAME_COMP_DATA (frame, 0) + ystride * rect->top; |
| for (pos = rect->top; pos <= rect->bottom; pos++) { |
| cur[rect->left] = (cur[rect->left] / 2) + 0x8; |
| cur[rect->right] = (cur[rect->right] / 2) + 0x8; |
| cur += ystride; |
| } |
| } |
| |
| void |
| gstspu_vobsub_render (GstDVDSpu * dvdspu, GstVideoFrame * frame) |
| { |
| SpuState *state = &dvdspu->spu_state; |
| guint8 *planes[3]; /* YUV frame pointers */ |
| gint y, last_y; |
| gint width, height; |
| gint strides[3]; |
| gint offset_index = 0; |
| |
| /* Set up our initial state */ |
| if (G_UNLIKELY (state->vobsub.pix_buf == NULL)) |
| return; |
| |
| if (!gst_buffer_map (state->vobsub.pix_buf, &state->vobsub.pix_buf_map, |
| GST_MAP_READ)) |
| return; |
| |
| /* Store the start of each plane */ |
| planes[0] = GST_VIDEO_FRAME_COMP_DATA (frame, 0); |
| planes[1] = GST_VIDEO_FRAME_COMP_DATA (frame, 1); |
| planes[2] = GST_VIDEO_FRAME_COMP_DATA (frame, 2); |
| |
| strides[0] = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0); |
| strides[1] = GST_VIDEO_FRAME_COMP_STRIDE (frame, 1); |
| strides[2] = GST_VIDEO_FRAME_COMP_STRIDE (frame, 2); |
| |
| width = GST_VIDEO_FRAME_WIDTH (frame); |
| height = GST_VIDEO_FRAME_HEIGHT (frame); |
| |
| GST_DEBUG_OBJECT (dvdspu, |
| "Rendering SPU. disp_rect %d,%d to %d,%d. hl_rect %d,%d to %d,%d", |
| state->vobsub.disp_rect.left, state->vobsub.disp_rect.top, |
| state->vobsub.disp_rect.right, state->vobsub.disp_rect.bottom, |
| state->vobsub.hl_rect.left, state->vobsub.hl_rect.top, |
| state->vobsub.hl_rect.right, state->vobsub.hl_rect.bottom); |
| |
| GST_DEBUG_OBJECT (dvdspu, "video size %d,%d", width, height); |
| |
| /* When reading RLE data, we track the offset in nibbles... */ |
| state->vobsub.cur_offsets[0] = state->vobsub.pix_data[0] * 2; |
| state->vobsub.cur_offsets[1] = state->vobsub.pix_data[1] * 2; |
| state->vobsub.max_offset = state->vobsub.pix_buf_map.size * 2; |
| |
| /* Update all the palette caches */ |
| gstspu_vobsub_update_palettes (dvdspu, state); |
| |
| /* Set up HL or Change Color & Contrast rect tracking */ |
| if (state->vobsub.hl_rect.top != -1) { |
| state->vobsub.cur_chg_col = &state->vobsub.hl_ctrl_i; |
| state->vobsub.cur_chg_col_end = state->vobsub.cur_chg_col + 1; |
| } else if (state->vobsub.n_line_ctrl_i > 0) { |
| state->vobsub.cur_chg_col = state->vobsub.line_ctrl_i; |
| state->vobsub.cur_chg_col_end = |
| state->vobsub.cur_chg_col + state->vobsub.n_line_ctrl_i; |
| } else |
| state->vobsub.cur_chg_col = NULL; |
| |
| state->vobsub.clip_rect.left = state->vobsub.disp_rect.left; |
| state->vobsub.clip_rect.right = state->vobsub.disp_rect.right; |
| |
| /* center the image when display rectangle exceeds the video width */ |
| if (width <= state->vobsub.disp_rect.right) { |
| gint left, disp_width; |
| |
| disp_width = state->vobsub.disp_rect.right - state->vobsub.disp_rect.left |
| + 1; |
| left = (width - disp_width) / 2; |
| state->vobsub.disp_rect.left = left; |
| state->vobsub.disp_rect.right = left + disp_width - 1; |
| |
| /* if it clips to the right, shift it left, but only till zero */ |
| if (state->vobsub.disp_rect.right >= width) { |
| gint shift = state->vobsub.disp_rect.right - width - 1; |
| if (shift > state->vobsub.disp_rect.left) |
| shift = state->vobsub.disp_rect.left; |
| state->vobsub.disp_rect.left -= shift; |
| state->vobsub.disp_rect.right -= shift; |
| } |
| |
| /* init clip to disp */ |
| state->vobsub.clip_rect.left = state->vobsub.disp_rect.left; |
| state->vobsub.clip_rect.right = state->vobsub.disp_rect.right; |
| |
| /* clip right after the shift */ |
| if (state->vobsub.clip_rect.right >= width) |
| state->vobsub.clip_rect.right = width - 1; |
| |
| GST_DEBUG_OBJECT (dvdspu, |
| "clipping width to %d,%d", state->vobsub.clip_rect.left, |
| state->vobsub.clip_rect.right); |
| } |
| |
| /* for the height, bring it up till it fits as well as it can. We |
| * assume the picture is in the lower part. We should better check where it |
| * is and do something more clever. */ |
| state->vobsub.clip_rect.top = state->vobsub.disp_rect.top; |
| state->vobsub.clip_rect.bottom = state->vobsub.disp_rect.bottom; |
| if (height <= state->vobsub.disp_rect.bottom) { |
| |
| /* shift it up, but only till zero */ |
| gint shift = state->vobsub.disp_rect.bottom - height - 1; |
| if (shift > state->vobsub.disp_rect.top) |
| shift = state->vobsub.disp_rect.top; |
| state->vobsub.disp_rect.top -= shift; |
| state->vobsub.disp_rect.bottom -= shift; |
| |
| /* start on even line */ |
| if (state->vobsub.disp_rect.top & 1) { |
| state->vobsub.disp_rect.top--; |
| state->vobsub.disp_rect.bottom--; |
| } |
| |
| /* init clip to disp */ |
| state->vobsub.clip_rect.top = state->vobsub.disp_rect.top; |
| state->vobsub.clip_rect.bottom = state->vobsub.disp_rect.bottom; |
| |
| /* clip bottom after the shift */ |
| if (state->vobsub.clip_rect.bottom >= height) |
| state->vobsub.clip_rect.bottom = height - 1; |
| |
| GST_DEBUG_OBJECT (dvdspu, |
| "clipping height to %d,%d", state->vobsub.clip_rect.top, |
| state->vobsub.clip_rect.bottom); |
| } |
| |
| /* We start rendering from the first line of the display rect */ |
| y = state->vobsub.disp_rect.top; |
| /* We render most lines in pairs starting from an even y, |
| * accumulating 2 lines of chroma then blending it. We might need to render a |
| * single line at the start and end if the display rect starts on an odd line |
| * or ends on an even one */ |
| if (y > state->vobsub.disp_rect.bottom) |
| return; /* Empty clip rect, nothing to do */ |
| |
| /* Update our plane references to the first line of the disp_rect */ |
| planes[0] += strides[0] * y; |
| planes[1] += strides[1] * (y / 2); |
| planes[2] += strides[2] * (y / 2); |
| |
| /* If the render rect starts on an odd line, render that only to start */ |
| state->vobsub.cur_Y = y; |
| if (state->vobsub.cur_Y & 0x1) { |
| gboolean clip, visible = FALSE; |
| |
| clip = (state->vobsub.cur_Y < state->vobsub.clip_rect.top |
| || state->vobsub.cur_Y > state->vobsub.clip_rect.bottom); |
| |
| if (!clip) { |
| /* Render a first odd line. */ |
| gstspu_vobsub_clear_comp_buffers (state); |
| state->vobsub.comp_last_x_ptr = state->vobsub.comp_last_x + 1; |
| visible |= |
| gstspu_vobsub_render_line (state, planes, |
| &state->vobsub.cur_offsets[offset_index]); |
| if (visible) |
| gstspu_vobsub_blend_comp_buffers (state, planes); |
| } |
| |
| /* Update all the output pointers */ |
| state->vobsub.cur_Y++; |
| planes[0] += strides[0]; |
| planes[1] += strides[1]; |
| planes[2] += strides[2]; |
| /* Switch the offset index 0 <=> 1 */ |
| offset_index ^= 0x1; |
| } |
| |
| last_y = (state->vobsub.disp_rect.bottom - 1) & ~(0x01); |
| for (; state->vobsub.cur_Y <= last_y; state->vobsub.cur_Y++) { |
| gboolean clip, visible = FALSE; |
| |
| clip = (state->vobsub.cur_Y < state->vobsub.clip_rect.top |
| || state->vobsub.cur_Y > state->vobsub.clip_rect.bottom); |
| |
| /* Reset the compositing buffer */ |
| gstspu_vobsub_clear_comp_buffers (state); |
| /* Render even line */ |
| state->vobsub.comp_last_x_ptr = state->vobsub.comp_last_x; |
| gstspu_vobsub_render_line (state, planes, |
| &state->vobsub.cur_offsets[offset_index]); |
| |
| /* Advance the luminance output pointer */ |
| planes[0] += strides[0]; |
| /* Switch the offset index 0 <=> 1 */ |
| offset_index ^= 0x1; |
| |
| state->vobsub.cur_Y++; |
| |
| /* Render odd line */ |
| state->vobsub.comp_last_x_ptr = state->vobsub.comp_last_x + 1; |
| visible |= |
| gstspu_vobsub_render_line (state, planes, |
| &state->vobsub.cur_offsets[offset_index]); |
| |
| if (visible && !clip) { |
| /* Blend the accumulated UV compositing buffers onto the output */ |
| gstspu_vobsub_blend_comp_buffers (state, planes); |
| } |
| |
| /* Update all the output pointers */ |
| planes[0] += strides[0]; |
| planes[1] += strides[1]; |
| planes[2] += strides[2]; |
| /* Switch the offset index 0 <=> 1 */ |
| offset_index ^= 0x1; |
| } |
| |
| if (state->vobsub.cur_Y == state->vobsub.disp_rect.bottom) { |
| gboolean clip, visible = FALSE; |
| |
| clip = (state->vobsub.cur_Y < state->vobsub.clip_rect.top |
| || state->vobsub.cur_Y > state->vobsub.clip_rect.bottom); |
| |
| g_return_if_fail ((state->vobsub.disp_rect.bottom & 0x01) == 0); |
| |
| if (!clip) { |
| /* Render a remaining lone last even line. y already has the correct value |
| * after the above loop exited. */ |
| gstspu_vobsub_clear_comp_buffers (state); |
| state->vobsub.comp_last_x_ptr = state->vobsub.comp_last_x; |
| visible |= |
| gstspu_vobsub_render_line (state, planes, |
| &state->vobsub.cur_offsets[offset_index]); |
| if (visible) |
| gstspu_vobsub_blend_comp_buffers (state, planes); |
| } |
| } |
| |
| /* for debugging purposes, draw a faint rectangle at the edges of the disp_rect */ |
| if ((dvdspu_debug_flags & GST_DVD_SPU_DEBUG_RENDER_RECTANGLE) != 0) { |
| gstspu_vobsub_draw_highlight (state, frame, &state->vobsub.disp_rect); |
| } |
| /* For debugging purposes, draw a faint rectangle around the highlight rect */ |
| if ((dvdspu_debug_flags & GST_DVD_SPU_DEBUG_HIGHLIGHT_RECTANGLE) != 0 |
| && state->vobsub.hl_rect.top != -1) { |
| gstspu_vobsub_draw_highlight (state, frame, &state->vobsub.hl_rect); |
| } |
| |
| gst_buffer_unmap (state->vobsub.pix_buf, &state->vobsub.pix_buf_map); |
| } |