1 /* GStreamer DVD Sub-Picture Unit
2 * Copyright (C) 2007 Fluendo S.A. <info@fluendo.com>
3 * Copyright (C) 2009 Jan Schmidt <thaytan@noraisin.net>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 */
20 #ifdef HAVE_CONFIG_H
21 # include <config.h>
22 #endif
23
24 #include <string.h>
25
26 #include <gst/gst.h>
27
28 #include "gstdvdspu.h"
29
30 GST_DEBUG_CATEGORY_EXTERN (dvdspu_debug);
31 #define GST_CAT_DEFAULT dvdspu_debug
32
33 static void
gstspu_vobsub_recalc_palette(GstDVDSpu * dvdspu,SpuColour * dest,guint8 * idx,guint8 * alpha)34 gstspu_vobsub_recalc_palette (GstDVDSpu * dvdspu,
35 SpuColour * dest, guint8 * idx, guint8 * alpha)
36 {
37 SpuState *state = &dvdspu->spu_state;
38 gint i;
39
40 if (state->vobsub.current_clut[idx[0]] != 0) {
41 for (i = 0; i < 4; i++, dest++) {
42 guint32 col = state->vobsub.current_clut[idx[i]];
43
44 /* Convert incoming 4-bit alpha to 8 bit for blending */
45 dest->A = (alpha[i] << 4) | alpha[i];
46 dest->Y = ((guint16) ((col >> 16) & 0xff)) * dest->A;
47 /* U/V are stored as V/U in the clut words, so switch them */
48 dest->V = ((guint16) ((col >> 8) & 0xff)) * dest->A;
49 dest->U = ((guint16) (col & 0xff)) * dest->A;
50 }
51 } else {
52 int y = 240;
53
54 /* The CLUT presumably hasn't been set, so we'll just guess some
55 * values for the non-transparent colors (white, grey, black) */
56 for (i = 0; i < 4; i++, dest++) {
57 dest->A = (alpha[i] << 4) | alpha[i];
58 if (alpha[i] != 0) {
59 dest[0].Y = y * dest[0].A;
60 y -= 112;
61 if (y < 0)
62 y = 0;
63 }
64 dest[0].U = 128 * dest[0].A;
65 dest[0].V = 128 * dest[0].A;
66 }
67 }
68 }
69
70 /* Recalculate the main, HL & ChgCol palettes */
71 static void
gstspu_vobsub_update_palettes(GstDVDSpu * dvdspu,SpuState * state)72 gstspu_vobsub_update_palettes (GstDVDSpu * dvdspu, SpuState * state)
73 {
74 guint8 index[4]; /* Indices for the palette */
75 guint8 alpha[4]; /* Alpha values the palette */
76
77 if (state->vobsub.main_pal_dirty) {
78 gstspu_vobsub_recalc_palette (dvdspu, state->vobsub.main_pal,
79 state->vobsub.main_idx, state->vobsub.main_alpha);
80
81 /* Need to refresh the hl_ctrl info copies of the main palette too */
82 memcpy (state->vobsub.hl_ctrl_i.pix_ctrl_i[0].pal_cache,
83 state->vobsub.main_pal, 4 * sizeof (SpuColour));
84 memcpy (state->vobsub.hl_ctrl_i.pix_ctrl_i[2].pal_cache,
85 state->vobsub.main_pal, 4 * sizeof (SpuColour));
86
87 state->vobsub.main_pal_dirty = FALSE;
88 }
89
90 if (state->vobsub.hl_pal_dirty) {
91 gstspu_vobsub_recalc_palette (dvdspu,
92 state->vobsub.hl_ctrl_i.pix_ctrl_i[1].pal_cache, state->vobsub.hl_idx,
93 state->vobsub.hl_alpha);
94 state->vobsub.hl_pal_dirty = FALSE;
95 }
96
97 /* Update the offset positions for the highlight region */
98 if (state->vobsub.hl_rect.top != -1) {
99 state->vobsub.hl_ctrl_i.top = state->vobsub.hl_rect.top;
100 state->vobsub.hl_ctrl_i.bottom = state->vobsub.hl_rect.bottom;
101 state->vobsub.hl_ctrl_i.n_changes = 3;
102 state->vobsub.hl_ctrl_i.pix_ctrl_i[0].left = 0;
103 state->vobsub.hl_ctrl_i.pix_ctrl_i[1].left = state->vobsub.hl_rect.left;
104 state->vobsub.hl_ctrl_i.pix_ctrl_i[2].left =
105 state->vobsub.hl_rect.right + 1;
106 }
107
108 if (state->vobsub.line_ctrl_i_pal_dirty) {
109 gint16 l, c;
110 GST_LOG_OBJECT (dvdspu, "Updating chg-col-con palettes");
111 for (l = 0; l < state->vobsub.n_line_ctrl_i; l++) {
112 SpuVobsubLineCtrlI *cur_line_ctrl = state->vobsub.line_ctrl_i + l;
113
114 for (c = 0; c < cur_line_ctrl->n_changes; c++) {
115 SpuVobsubPixCtrlI *cur = cur_line_ctrl->pix_ctrl_i + c;
116
117 index[3] = (cur->palette >> 28) & 0x0f;
118 index[2] = (cur->palette >> 24) & 0x0f;
119 index[1] = (cur->palette >> 20) & 0x0f;
120 index[0] = (cur->palette >> 16) & 0x0f;
121
122 alpha[3] = (cur->palette >> 12) & 0x0f;
123 alpha[2] = (cur->palette >> 8) & 0x0f;
124 alpha[1] = (cur->palette >> 4) & 0x0f;
125 alpha[0] = (cur->palette) & 0x0f;
126 gstspu_vobsub_recalc_palette (dvdspu, cur->pal_cache, index, alpha);
127 }
128 }
129 state->vobsub.line_ctrl_i_pal_dirty = FALSE;
130 }
131 }
132
133 static inline guint8
gstspu_vobsub_get_nibble(SpuState * state,guint16 * rle_offset)134 gstspu_vobsub_get_nibble (SpuState * state, guint16 * rle_offset)
135 {
136 guint8 ret;
137
138 if (G_UNLIKELY (*rle_offset >= state->vobsub.max_offset))
139 return 0; /* Overran the buffer */
140
141 ret = state->vobsub.pix_buf_map.data[(*rle_offset) / 2];
142
143 /* If the offset is even, we shift the answer down 4 bits, otherwise not */
144 if (*rle_offset & 0x01)
145 ret &= 0x0f;
146 else
147 ret = ret >> 4;
148
149 (*rle_offset)++;
150 return ret;
151 }
152
153 static guint16
gstspu_vobsub_get_rle_code(SpuState * state,guint16 * rle_offset)154 gstspu_vobsub_get_rle_code (SpuState * state, guint16 * rle_offset)
155 {
156 guint16 code;
157
158 code = gstspu_vobsub_get_nibble (state, rle_offset);
159 if (code < 0x4) { /* 4 .. f */
160 code = (code << 4) | gstspu_vobsub_get_nibble (state, rle_offset);
161 if (code < 0x10) { /* 1x .. 3x */
162 code = (code << 4) | gstspu_vobsub_get_nibble (state, rle_offset);
163 if (code < 0x40) { /* 04x .. 0fx */
164 code = (code << 4) | gstspu_vobsub_get_nibble (state, rle_offset);
165 }
166 }
167 }
168 return code;
169 }
170
171 static inline gboolean
gstspu_vobsub_draw_rle_run(SpuState * state,gint16 x,gint16 end,SpuColour * colour)172 gstspu_vobsub_draw_rle_run (SpuState * state, gint16 x, gint16 end,
173 SpuColour * colour)
174 {
175 #if 0
176 GST_LOG ("Y: %d x: %d end %d col %d %d %d %d",
177 state->vobsub.cur_Y, x, end, colour->Y, colour->U, colour->V, colour->A);
178 #endif
179
180 if (colour->A != 0) {
181 guint32 inv_A = 0xff - colour->A;
182
183 /* FIXME: This could be more efficient */
184 while (x < end) {
185 state->vobsub.out_Y[x] =
186 (inv_A * state->vobsub.out_Y[x] + colour->Y) / 0xff;
187 state->vobsub.out_U[x / 2] += colour->U;
188 state->vobsub.out_V[x / 2] += colour->V;
189 state->vobsub.out_A[x / 2] += colour->A;
190 x++;
191 }
192 /* Update the compositing buffer so we know how much to blend later */
193 *(state->vobsub.comp_last_x_ptr) = end - 1; /* end is the start of the *next* run */
194
195 return TRUE;
196 }
197 return FALSE;
198 }
199
200 static inline gint16
rle_end_x(guint16 rle_code,gint16 x,gint16 end)201 rle_end_x (guint16 rle_code, gint16 x, gint16 end)
202 {
203 /* run length = rle_code >> 2 */
204 if (G_UNLIKELY (((rle_code >> 2) == 0)))
205 return end;
206 else
207 return MIN (end, x + (rle_code >> 2));
208 }
209
210 static gboolean gstspu_vobsub_render_line_with_chgcol (SpuState * state,
211 guint8 * planes[3], guint16 * rle_offset);
212 static gboolean gstspu_vobsub_update_chgcol (SpuState * state);
213
214 static gboolean
gstspu_vobsub_render_line(SpuState * state,guint8 * planes[3],guint16 * rle_offset)215 gstspu_vobsub_render_line (SpuState * state, guint8 * planes[3],
216 guint16 * rle_offset)
217 {
218 gint16 x, next_x, end, rle_code, next_draw_x;
219 SpuColour *colour;
220 gboolean visible = FALSE;
221
222 /* Check for special case of chg_col info to use (either highlight or
223 * ChgCol command */
224 if (state->vobsub.cur_chg_col != NULL) {
225 if (gstspu_vobsub_update_chgcol (state)) {
226 /* Check the top & bottom, because we might not be within the region yet */
227 if (state->vobsub.cur_Y >= state->vobsub.cur_chg_col->top &&
228 state->vobsub.cur_Y <= state->vobsub.cur_chg_col->bottom) {
229 return gstspu_vobsub_render_line_with_chgcol (state, planes,
230 rle_offset);
231 }
232 }
233 }
234
235 /* No special case. Render as normal */
236
237 /* Set up our output pointers */
238 state->vobsub.out_Y = planes[0];
239 state->vobsub.out_U = state->comp_bufs[0];
240 state->vobsub.out_V = state->comp_bufs[1];
241 state->vobsub.out_A = state->comp_bufs[2];
242 /* We always need to start our RLE decoding byte_aligned */
243 *rle_offset = GST_ROUND_UP_2 (*rle_offset);
244
245 x = state->vobsub.disp_rect.left;
246 end = state->vobsub.disp_rect.right + 1;
247 while (x < end) {
248 rle_code = gstspu_vobsub_get_rle_code (state, rle_offset);
249 colour = &state->vobsub.main_pal[rle_code & 3];
250 next_x = rle_end_x (rle_code, x, end);
251 next_draw_x = next_x;
252 if (next_draw_x > state->vobsub.clip_rect.right)
253 next_draw_x = state->vobsub.clip_rect.right; /* ensure no overflow */
254 /* Now draw the run between [x,next_x) */
255 if (state->vobsub.cur_Y >= state->vobsub.clip_rect.top &&
256 state->vobsub.cur_Y <= state->vobsub.clip_rect.bottom)
257 visible |= gstspu_vobsub_draw_rle_run (state, x, next_draw_x, colour);
258 x = next_x;
259 }
260
261 return visible;
262 }
263
264 static gboolean
gstspu_vobsub_update_chgcol(SpuState * state)265 gstspu_vobsub_update_chgcol (SpuState * state)
266 {
267 if (state->vobsub.cur_chg_col == NULL)
268 return FALSE;
269
270 if (state->vobsub.cur_Y <= state->vobsub.cur_chg_col->bottom)
271 return TRUE;
272
273 while (state->vobsub.cur_chg_col < state->vobsub.cur_chg_col_end) {
274 if (state->vobsub.cur_Y >= state->vobsub.cur_chg_col->top &&
275 state->vobsub.cur_Y <= state->vobsub.cur_chg_col->bottom) {
276 #if 0
277 g_print ("Stopped @ entry %d with top %d bottom %d, cur_y %d",
278 (gint16) (state->vobsub.cur_chg_col - state->vobsub.line_ctrl_i),
279 state->vobsub.cur_chg_col->top, state->vobsub.cur_chg_col->bottom, y);
280 #endif
281 return TRUE;
282 }
283 state->vobsub.cur_chg_col++;
284 }
285
286 /* Finished all our cur_chg_col entries. Use the main palette from here on */
287 state->vobsub.cur_chg_col = NULL;
288 return FALSE;
289 }
290
291 static gboolean
gstspu_vobsub_render_line_with_chgcol(SpuState * state,guint8 * planes[3],guint16 * rle_offset)292 gstspu_vobsub_render_line_with_chgcol (SpuState * state, guint8 * planes[3],
293 guint16 * rle_offset)
294 {
295 SpuVobsubLineCtrlI *chg_col = state->vobsub.cur_chg_col;
296
297 gint16 x, next_x, disp_end, rle_code, run_end, run_draw_end;
298 SpuColour *colour;
299 SpuVobsubPixCtrlI *cur_pix_ctrl;
300 SpuVobsubPixCtrlI *next_pix_ctrl;
301 SpuVobsubPixCtrlI *end_pix_ctrl;
302 SpuVobsubPixCtrlI dummy_pix_ctrl;
303 gboolean visible = FALSE;
304 gint16 cur_reg_end;
305 gint i;
306
307 state->vobsub.out_Y = planes[0];
308 state->vobsub.out_U = state->comp_bufs[0];
309 state->vobsub.out_V = state->comp_bufs[1];
310 state->vobsub.out_A = state->comp_bufs[2];
311
312 /* We always need to start our RLE decoding byte_aligned */
313 *rle_offset = GST_ROUND_UP_2 (*rle_offset);
314
315 /* Our run will cover the display rect */
316 x = state->vobsub.disp_rect.left;
317 disp_end = state->vobsub.disp_rect.right + 1;
318
319 /* Work out the first pixel control info, which may point to the dummy entry if
320 * the global palette/alpha need using initially */
321 cur_pix_ctrl = chg_col->pix_ctrl_i;
322 end_pix_ctrl = chg_col->pix_ctrl_i + chg_col->n_changes;
323
324 if (cur_pix_ctrl->left != 0) {
325 next_pix_ctrl = cur_pix_ctrl;
326 cur_pix_ctrl = &dummy_pix_ctrl;
327 for (i = 0; i < 4; i++) /* Copy the main palette to our dummy entry */
328 dummy_pix_ctrl.pal_cache[i] = state->vobsub.main_pal[i];
329 } else {
330 next_pix_ctrl = cur_pix_ctrl + 1;
331 }
332 if (next_pix_ctrl < end_pix_ctrl)
333 cur_reg_end = next_pix_ctrl->left;
334 else
335 cur_reg_end = disp_end;
336
337 /* Render stuff */
338 while (x < disp_end) {
339 rle_code = gstspu_vobsub_get_rle_code (state, rle_offset);
340 next_x = rle_end_x (rle_code, x, disp_end);
341
342 /* Now draw the run between [x,next_x), crossing palette regions as needed */
343 while (x < next_x) {
344 run_end = MIN (next_x, cur_reg_end);
345
346 run_draw_end = run_end;
347 if (run_draw_end > state->vobsub.clip_rect.right)
348 run_draw_end = state->vobsub.clip_rect.right; /* ensure no overflow */
349
350 if (G_LIKELY (x < run_end)) {
351 colour = &cur_pix_ctrl->pal_cache[rle_code & 3];
352 visible |= gstspu_vobsub_draw_rle_run (state, x, run_draw_end, colour);
353 x = run_end;
354 }
355
356 if (x >= cur_reg_end) {
357 /* Advance to next region */
358 cur_pix_ctrl = next_pix_ctrl;
359 next_pix_ctrl++;
360
361 if (next_pix_ctrl < end_pix_ctrl)
362 cur_reg_end = next_pix_ctrl->left;
363 else
364 cur_reg_end = disp_end;
365 }
366 }
367 }
368
369 return visible;
370 }
371
372 static void
gstspu_vobsub_blend_comp_buffers(SpuState * state,guint8 * planes[3])373 gstspu_vobsub_blend_comp_buffers (SpuState * state, guint8 * planes[3])
374 {
375 state->comp_left = state->vobsub.disp_rect.left;
376 state->comp_right =
377 MAX (state->vobsub.comp_last_x[0], state->vobsub.comp_last_x[1]);
378
379 state->comp_left = MAX (state->comp_left, state->vobsub.clip_rect.left);
380 state->comp_right = MIN (state->comp_right, state->vobsub.clip_rect.right);
381
382 gstspu_blend_comp_buffers (state, planes);
383 }
384
385 static void
gstspu_vobsub_clear_comp_buffers(SpuState * state)386 gstspu_vobsub_clear_comp_buffers (SpuState * state)
387 {
388 state->comp_left = state->vobsub.clip_rect.left;
389 state->comp_right = state->vobsub.clip_rect.right;
390
391 gstspu_clear_comp_buffers (state);
392
393 state->vobsub.comp_last_x[0] = -1;
394 state->vobsub.comp_last_x[1] = -1;
395 }
396
397 static void
gstspu_vobsub_draw_highlight(SpuState * state,GstVideoFrame * frame,SpuRect * rect)398 gstspu_vobsub_draw_highlight (SpuState * state,
399 GstVideoFrame * frame, SpuRect * rect)
400 {
401 guint8 *cur;
402 gint16 pos;
403 gint ystride;
404
405 ystride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0);
406
407 cur = GST_VIDEO_FRAME_COMP_DATA (frame, 0) + ystride * rect->top;
408 for (pos = rect->left + 1; pos < rect->right; pos++)
409 cur[pos] = (cur[pos] / 2) + 0x8;
410 cur = GST_VIDEO_FRAME_COMP_DATA (frame, 0) + ystride * rect->bottom;
411 for (pos = rect->left + 1; pos < rect->right; pos++)
412 cur[pos] = (cur[pos] / 2) + 0x8;
413 cur = GST_VIDEO_FRAME_COMP_DATA (frame, 0) + ystride * rect->top;
414 for (pos = rect->top; pos <= rect->bottom; pos++) {
415 cur[rect->left] = (cur[rect->left] / 2) + 0x8;
416 cur[rect->right] = (cur[rect->right] / 2) + 0x8;
417 cur += ystride;
418 }
419 }
420
421 void
gstspu_vobsub_render(GstDVDSpu * dvdspu,GstVideoFrame * frame)422 gstspu_vobsub_render (GstDVDSpu * dvdspu, GstVideoFrame * frame)
423 {
424 SpuState *state = &dvdspu->spu_state;
425 guint8 *planes[3]; /* YUV frame pointers */
426 gint y, last_y;
427 gint width, height;
428 gint strides[3];
429 gint offset_index = 0;
430
431 /* Set up our initial state */
432 if (G_UNLIKELY (state->vobsub.pix_buf == NULL))
433 return;
434
435 if (!gst_buffer_map (state->vobsub.pix_buf, &state->vobsub.pix_buf_map,
436 GST_MAP_READ))
437 return;
438
439 /* Store the start of each plane */
440 planes[0] = GST_VIDEO_FRAME_COMP_DATA (frame, 0);
441 planes[1] = GST_VIDEO_FRAME_COMP_DATA (frame, 1);
442 planes[2] = GST_VIDEO_FRAME_COMP_DATA (frame, 2);
443
444 strides[0] = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0);
445 strides[1] = GST_VIDEO_FRAME_COMP_STRIDE (frame, 1);
446 strides[2] = GST_VIDEO_FRAME_COMP_STRIDE (frame, 2);
447
448 width = GST_VIDEO_FRAME_WIDTH (frame);
449 height = GST_VIDEO_FRAME_HEIGHT (frame);
450
451 GST_DEBUG_OBJECT (dvdspu,
452 "Rendering SPU. disp_rect %d,%d to %d,%d. hl_rect %d,%d to %d,%d",
453 state->vobsub.disp_rect.left, state->vobsub.disp_rect.top,
454 state->vobsub.disp_rect.right, state->vobsub.disp_rect.bottom,
455 state->vobsub.hl_rect.left, state->vobsub.hl_rect.top,
456 state->vobsub.hl_rect.right, state->vobsub.hl_rect.bottom);
457
458 GST_DEBUG_OBJECT (dvdspu, "video size %d,%d", width, height);
459
460 /* When reading RLE data, we track the offset in nibbles... */
461 state->vobsub.cur_offsets[0] = state->vobsub.pix_data[0] * 2;
462 state->vobsub.cur_offsets[1] = state->vobsub.pix_data[1] * 2;
463 state->vobsub.max_offset = state->vobsub.pix_buf_map.size * 2;
464
465 /* Update all the palette caches */
466 gstspu_vobsub_update_palettes (dvdspu, state);
467
468 /* Set up HL or Change Color & Contrast rect tracking */
469 if (state->vobsub.hl_rect.top != -1) {
470 state->vobsub.cur_chg_col = &state->vobsub.hl_ctrl_i;
471 state->vobsub.cur_chg_col_end = state->vobsub.cur_chg_col + 1;
472 } else if (state->vobsub.n_line_ctrl_i > 0) {
473 state->vobsub.cur_chg_col = state->vobsub.line_ctrl_i;
474 state->vobsub.cur_chg_col_end =
475 state->vobsub.cur_chg_col + state->vobsub.n_line_ctrl_i;
476 } else
477 state->vobsub.cur_chg_col = NULL;
478
479 state->vobsub.clip_rect.left = state->vobsub.disp_rect.left;
480 state->vobsub.clip_rect.right = state->vobsub.disp_rect.right;
481
482 /* center the image when display rectangle exceeds the video width */
483 if (width <= state->vobsub.disp_rect.right) {
484 gint left, disp_width;
485
486 disp_width = state->vobsub.disp_rect.right - state->vobsub.disp_rect.left
487 + 1;
488 left = (width - disp_width) / 2;
489 state->vobsub.disp_rect.left = left;
490 state->vobsub.disp_rect.right = left + disp_width - 1;
491
492 /* if it clips to the right, shift it left, but only till zero */
493 if (state->vobsub.disp_rect.right >= width) {
494 gint shift = state->vobsub.disp_rect.right - width - 1;
495 if (shift > state->vobsub.disp_rect.left)
496 shift = state->vobsub.disp_rect.left;
497 state->vobsub.disp_rect.left -= shift;
498 state->vobsub.disp_rect.right -= shift;
499 }
500
501 /* init clip to disp */
502 state->vobsub.clip_rect.left = state->vobsub.disp_rect.left;
503 state->vobsub.clip_rect.right = state->vobsub.disp_rect.right;
504
505 /* clip right after the shift */
506 if (state->vobsub.clip_rect.right >= width)
507 state->vobsub.clip_rect.right = width - 1;
508
509 GST_DEBUG_OBJECT (dvdspu,
510 "clipping width to %d,%d", state->vobsub.clip_rect.left,
511 state->vobsub.clip_rect.right);
512 }
513
514 /* for the height, bring it up till it fits as well as it can. We
515 * assume the picture is in the lower part. We should better check where it
516 * is and do something more clever. */
517 state->vobsub.clip_rect.top = state->vobsub.disp_rect.top;
518 state->vobsub.clip_rect.bottom = state->vobsub.disp_rect.bottom;
519 if (height <= state->vobsub.disp_rect.bottom) {
520
521 /* shift it up, but only till zero */
522 gint shift = state->vobsub.disp_rect.bottom - height - 1;
523 if (shift > state->vobsub.disp_rect.top)
524 shift = state->vobsub.disp_rect.top;
525 state->vobsub.disp_rect.top -= shift;
526 state->vobsub.disp_rect.bottom -= shift;
527
528 /* start on even line */
529 if (state->vobsub.disp_rect.top & 1) {
530 state->vobsub.disp_rect.top--;
531 state->vobsub.disp_rect.bottom--;
532 }
533
534 /* init clip to disp */
535 state->vobsub.clip_rect.top = state->vobsub.disp_rect.top;
536 state->vobsub.clip_rect.bottom = state->vobsub.disp_rect.bottom;
537
538 /* clip bottom after the shift */
539 if (state->vobsub.clip_rect.bottom >= height)
540 state->vobsub.clip_rect.bottom = height - 1;
541
542 GST_DEBUG_OBJECT (dvdspu,
543 "clipping height to %d,%d", state->vobsub.clip_rect.top,
544 state->vobsub.clip_rect.bottom);
545 }
546
547 /* We start rendering from the first line of the display rect */
548 y = state->vobsub.disp_rect.top;
549 /* We render most lines in pairs starting from an even y,
550 * accumulating 2 lines of chroma then blending it. We might need to render a
551 * single line at the start and end if the display rect starts on an odd line
552 * or ends on an even one */
553 if (y > state->vobsub.disp_rect.bottom)
554 return; /* Empty clip rect, nothing to do */
555
556 /* Update our plane references to the first line of the disp_rect */
557 planes[0] += strides[0] * y;
558 planes[1] += strides[1] * (y / 2);
559 planes[2] += strides[2] * (y / 2);
560
561 /* If the render rect starts on an odd line, render that only to start */
562 state->vobsub.cur_Y = y;
563 if (state->vobsub.cur_Y & 0x1) {
564 gboolean clip, visible = FALSE;
565
566 clip = (state->vobsub.cur_Y < state->vobsub.clip_rect.top
567 || state->vobsub.cur_Y > state->vobsub.clip_rect.bottom);
568
569 if (!clip) {
570 /* Render a first odd line. */
571 gstspu_vobsub_clear_comp_buffers (state);
572 state->vobsub.comp_last_x_ptr = state->vobsub.comp_last_x + 1;
573 visible |=
574 gstspu_vobsub_render_line (state, planes,
575 &state->vobsub.cur_offsets[offset_index]);
576 if (visible)
577 gstspu_vobsub_blend_comp_buffers (state, planes);
578 }
579
580 /* Update all the output pointers */
581 state->vobsub.cur_Y++;
582 planes[0] += strides[0];
583 planes[1] += strides[1];
584 planes[2] += strides[2];
585 /* Switch the offset index 0 <=> 1 */
586 offset_index ^= 0x1;
587 }
588
589 last_y = (state->vobsub.disp_rect.bottom - 1) & ~(0x01);
590 for (; state->vobsub.cur_Y <= last_y; state->vobsub.cur_Y++) {
591 gboolean clip, visible = FALSE;
592
593 clip = (state->vobsub.cur_Y < state->vobsub.clip_rect.top
594 || state->vobsub.cur_Y > state->vobsub.clip_rect.bottom);
595
596 /* Reset the compositing buffer */
597 gstspu_vobsub_clear_comp_buffers (state);
598 /* Render even line */
599 state->vobsub.comp_last_x_ptr = state->vobsub.comp_last_x;
600 gstspu_vobsub_render_line (state, planes,
601 &state->vobsub.cur_offsets[offset_index]);
602
603 /* Advance the luminance output pointer */
604 planes[0] += strides[0];
605 /* Switch the offset index 0 <=> 1 */
606 offset_index ^= 0x1;
607
608 state->vobsub.cur_Y++;
609
610 /* Render odd line */
611 state->vobsub.comp_last_x_ptr = state->vobsub.comp_last_x + 1;
612 visible |=
613 gstspu_vobsub_render_line (state, planes,
614 &state->vobsub.cur_offsets[offset_index]);
615
616 if (visible && !clip) {
617 /* Blend the accumulated UV compositing buffers onto the output */
618 gstspu_vobsub_blend_comp_buffers (state, planes);
619 }
620
621 /* Update all the output pointers */
622 planes[0] += strides[0];
623 planes[1] += strides[1];
624 planes[2] += strides[2];
625 /* Switch the offset index 0 <=> 1 */
626 offset_index ^= 0x1;
627 }
628
629 if (state->vobsub.cur_Y == state->vobsub.disp_rect.bottom) {
630 gboolean clip, visible = FALSE;
631
632 clip = (state->vobsub.cur_Y < state->vobsub.clip_rect.top
633 || state->vobsub.cur_Y > state->vobsub.clip_rect.bottom);
634
635 g_return_if_fail ((state->vobsub.disp_rect.bottom & 0x01) == 0);
636
637 if (!clip) {
638 /* Render a remaining lone last even line. y already has the correct value
639 * after the above loop exited. */
640 gstspu_vobsub_clear_comp_buffers (state);
641 state->vobsub.comp_last_x_ptr = state->vobsub.comp_last_x;
642 visible |=
643 gstspu_vobsub_render_line (state, planes,
644 &state->vobsub.cur_offsets[offset_index]);
645 if (visible)
646 gstspu_vobsub_blend_comp_buffers (state, planes);
647 }
648 }
649
650 /* for debugging purposes, draw a faint rectangle at the edges of the disp_rect */
651 if ((dvdspu_debug_flags & GST_DVD_SPU_DEBUG_RENDER_RECTANGLE) != 0) {
652 gstspu_vobsub_draw_highlight (state, frame, &state->vobsub.disp_rect);
653 }
654 /* For debugging purposes, draw a faint rectangle around the highlight rect */
655 if ((dvdspu_debug_flags & GST_DVD_SPU_DEBUG_HIGHLIGHT_RECTANGLE) != 0
656 && state->vobsub.hl_rect.top != -1) {
657 gstspu_vobsub_draw_highlight (state, frame, &state->vobsub.hl_rect);
658 }
659
660 gst_buffer_unmap (state->vobsub.pix_buf, &state->vobsub.pix_buf_map);
661 }
662