libcdio
[mplayer.git] / spudec.c
blob6b10e5ffa0a4c9c6baa8b0440746d5c4f50c8021
1 /* SPUdec.c
2 Skeleton of function spudec_process_controll() is from xine sources.
3 Further works:
4 LGB,... (yeah, try to improve it and insert your name here! ;-)
6 Kim Minh Kaplan
7 implement fragments reassembly, RLE decoding.
8 read brightness from the IFO.
10 For information on SPU format see <URL:http://sam.zoy.org/doc/dvd/subtitles/>
11 and <URL:http://members.aol.com/mpucoder/DVD/spu.html>
14 #include "config.h"
15 #include "mp_msg.h"
17 #include <errno.h>
18 #include <limits.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <unistd.h>
22 #include <string.h>
23 #include <math.h>
24 #include "libvo/video_out.h"
25 #include "spudec.h"
26 #include "postproc/swscale.h"
28 #define MIN(a, b) ((a)<(b)?(a):(b))
30 /* Valid values for spu_aamode:
31 0: none (fastest, most ugly)
32 1: approximate
33 2: full (slowest)
34 3: bilinear (similiar to vobsub, fast and not too bad)
35 4: uses swscaler gaussian (this is the only one that looks good)
38 int spu_aamode = 3;
39 int spu_alignment = -1;
40 float spu_gaussvar = 1.0;
41 extern int sub_pos;
43 typedef struct packet_t packet_t;
44 struct packet_t {
45 unsigned char *packet;
46 unsigned int palette[4];
47 unsigned int alpha[4];
48 unsigned int control_start; /* index of start of control data */
49 unsigned int current_nibble[2]; /* next data nibble (4 bits) to be
50 processed (for RLE decoding) for
51 even and odd lines */
52 int deinterlace_oddness; /* 0 or 1, index into current_nibble */
53 unsigned int start_col, end_col;
54 unsigned int start_row, end_row;
55 unsigned int width, height, stride;
56 unsigned int start_pts, end_pts;
57 packet_t *next;
60 typedef struct {
61 packet_t *queue_head;
62 packet_t *queue_tail;
63 unsigned int global_palette[16];
64 unsigned int orig_frame_width, orig_frame_height;
65 unsigned char* packet;
66 size_t packet_reserve; /* size of the memory pointed to by packet */
67 unsigned int packet_offset; /* end of the currently assembled fragment */
68 unsigned int packet_size; /* size of the packet once all fragments are assembled */
69 unsigned int packet_pts; /* PTS for this packet */
70 unsigned int palette[4];
71 unsigned int alpha[4];
72 unsigned int cuspal[4];
73 unsigned int custom;
74 unsigned int now_pts;
75 unsigned int start_pts, end_pts;
76 unsigned int start_col, end_col;
77 unsigned int start_row, end_row;
78 unsigned int width, height, stride;
79 size_t image_size; /* Size of the image buffer */
80 unsigned char *image; /* Grayscale value */
81 unsigned char *aimage; /* Alpha value */
82 unsigned int scaled_frame_width, scaled_frame_height;
83 unsigned int scaled_start_col, scaled_start_row;
84 unsigned int scaled_width, scaled_height, scaled_stride;
85 size_t scaled_image_size;
86 unsigned char *scaled_image;
87 unsigned char *scaled_aimage;
88 int auto_palette; /* 1 if we lack a palette and must use an heuristic. */
89 int font_start_level; /* Darkest value used for the computed font */
90 vo_functions_t *hw_spu;
91 int spu_changed;
92 unsigned int forced_subs_only; /* flag: 0=display all subtitle, !0 display only forced subtitles */
93 unsigned int is_forced_sub; /* true if current subtitle is a forced subtitle */
94 } spudec_handle_t;
96 static void spudec_queue_packet(spudec_handle_t *this, packet_t *packet)
98 if (this->queue_head == NULL)
99 this->queue_head = packet;
100 else
101 this->queue_tail->next = packet;
102 this->queue_tail = packet;
105 static packet_t *spudec_dequeue_packet(spudec_handle_t *this)
107 packet_t *retval = this->queue_head;
109 this->queue_head = retval->next;
110 if (this->queue_head == NULL)
111 this->queue_tail = NULL;
113 return retval;
116 static void spudec_free_packet(packet_t *packet)
118 if (packet->packet != NULL)
119 free(packet->packet);
120 free(packet);
123 static inline unsigned int get_be16(const unsigned char *p)
125 return (p[0] << 8) + p[1];
128 static inline unsigned int get_be24(const unsigned char *p)
130 return (get_be16(p) << 8) + p[2];
133 static void next_line(packet_t *packet)
135 if (packet->current_nibble[packet->deinterlace_oddness] % 2)
136 packet->current_nibble[packet->deinterlace_oddness]++;
137 packet->deinterlace_oddness = (packet->deinterlace_oddness + 1) % 2;
140 static inline unsigned char get_nibble(packet_t *packet)
142 unsigned char nib;
143 unsigned int *nibblep = packet->current_nibble + packet->deinterlace_oddness;
144 if (*nibblep / 2 >= packet->control_start) {
145 mp_msg(MSGT_SPUDEC,MSGL_WARN, "SPUdec: ERROR: get_nibble past end of packet\n");
146 return 0;
148 nib = packet->packet[*nibblep / 2];
149 if (*nibblep % 2)
150 nib &= 0xf;
151 else
152 nib >>= 4;
153 ++*nibblep;
154 return nib;
157 static inline int mkalpha(int i)
159 /* In mplayer's alpha planes, 0 is transparent, then 1 is nearly
160 opaque upto 255 which is transparent */
161 switch (i) {
162 case 0xf:
163 return 1;
164 case 0:
165 return 0;
166 default:
167 return (0xf - i) << 4;
171 /* Cut the sub to visible part */
172 static inline void spudec_cut_image(spudec_handle_t *this)
174 unsigned int fy, ly;
175 unsigned int first_y, last_y;
176 unsigned char *image;
177 unsigned char *aimage;
179 if (this->stride == 0 || this->height == 0) {
180 return;
183 for (fy = 0; fy < this->image_size && !this->aimage[fy]; fy++);
184 for (ly = this->stride * this->height-1; ly && !this->aimage[ly]; ly--);
185 first_y = fy / this->stride;
186 last_y = ly / this->stride;
187 //printf("first_y: %d, last_y: %d\n", first_y, last_y);
188 this->start_row += first_y;
190 // Some subtitles trigger this condition
191 if (last_y + 1 > first_y ) {
192 this->height = last_y - first_y +1;
193 } else {
194 this->height = 0;
195 this->image_size = 0;
196 return;
199 // printf("new h %d new start %d (sz %d st %d)---\n\n", this->height, this->start_row, this->image_size, this->stride);
201 image = malloc(2 * this->stride * this->height);
202 if(image){
203 this->image_size = this->stride * this->height;
204 aimage = image + this->image_size;
205 memcpy(image, this->image + this->stride * first_y, this->image_size);
206 memcpy(aimage, this->aimage + this->stride * first_y, this->image_size);
207 free(this->image);
208 this->image = image;
209 this->aimage = aimage;
210 } else {
211 mp_msg(MSGT_SPUDEC, MSGL_FATAL, "Fatal: update_spu: malloc requested %d bytes\n", 2 * this->stride * this->height);
215 static void spudec_process_data(spudec_handle_t *this, packet_t *packet)
217 unsigned int cmap[4], alpha[4];
218 unsigned int i, x, y;
220 this->scaled_frame_width = 0;
221 this->scaled_frame_height = 0;
222 this->start_col = packet->start_col;
223 this->end_col = packet->end_col;
224 this->start_row = packet->start_row;
225 this->end_row = packet->end_row;
226 this->height = packet->height;
227 this->width = packet->width;
228 this->stride = packet->stride;
229 for (i = 0; i < 4; ++i) {
230 alpha[i] = mkalpha(packet->alpha[i]);
231 if (alpha[i] == 0)
232 cmap[i] = 0;
233 else if (this->custom){
234 cmap[i] = ((this->cuspal[i] >> 16) & 0xff);
235 if (cmap[i] + alpha[i] > 255)
236 cmap[i] = 256 - alpha[i];
238 else {
239 cmap[i] = ((this->global_palette[packet->palette[i]] >> 16) & 0xff);
240 if (cmap[i] + alpha[i] > 255)
241 cmap[i] = 256 - alpha[i];
245 if (this->image_size < this->stride * this->height) {
246 if (this->image != NULL) {
247 free(this->image);
248 this->image_size = 0;
250 this->image = malloc(2 * this->stride * this->height);
251 if (this->image) {
252 this->image_size = this->stride * this->height;
253 this->aimage = this->image + this->image_size;
256 if (this->image == NULL)
257 return;
259 /* Kludge: draw_alpha needs width multiple of 8. */
260 if (this->width < this->stride)
261 for (y = 0; y < this->height; ++y) {
262 memset(this->aimage + y * this->stride + this->width, 0, this->stride - this->width);
263 /* FIXME: Why is this one needed? */
264 memset(this->image + y * this->stride + this->width, 0, this->stride - this->width);
267 i = packet->current_nibble[1];
268 x = 0;
269 y = 0;
270 while (packet->current_nibble[0] < i
271 && packet->current_nibble[1] / 2 < packet->control_start
272 && y < this->height) {
273 unsigned int len, color;
274 unsigned int rle = 0;
275 rle = get_nibble(packet);
276 if (rle < 0x04) {
277 rle = (rle << 4) | get_nibble(packet);
278 if (rle < 0x10) {
279 rle = (rle << 4) | get_nibble(packet);
280 if (rle < 0x040) {
281 rle = (rle << 4) | get_nibble(packet);
282 if (rle < 0x0004)
283 rle |= ((this->width - x) << 2);
287 color = 3 - (rle & 0x3);
288 len = rle >> 2;
289 if (len > this->width - x || len == 0)
290 len = this->width - x;
291 /* FIXME have to use palette and alpha map*/
292 memset(this->image + y * this->stride + x, cmap[color], len);
293 memset(this->aimage + y * this->stride + x, alpha[color], len);
294 x += len;
295 if (x >= this->width) {
296 next_line(packet);
297 x = 0;
298 ++y;
301 spudec_cut_image(this);
306 This function tries to create a usable palette.
307 It determines how many non-transparent colors are used, and assigns different
308 gray scale values to each color.
309 I tested it with four streams and even got something readable. Half of the
310 times I got black characters with white around and half the reverse.
312 static void compute_palette(spudec_handle_t *this, packet_t *packet)
314 int used[16],i,cused,start,step,color;
316 memset(used, 0, sizeof(used));
317 for (i=0; i<4; i++)
318 if (packet->alpha[i]) /* !Transparent? */
319 used[packet->palette[i]] = 1;
320 for (cused=0, i=0; i<16; i++)
321 if (used[i]) cused++;
322 if (!cused) return;
323 if (cused == 1) {
324 start = 0x80;
325 step = 0;
326 } else {
327 start = this->font_start_level;
328 step = (0xF0-this->font_start_level)/(cused-1);
330 memset(used, 0, sizeof(used));
331 for (i=0; i<4; i++) {
332 color = packet->palette[i];
333 if (packet->alpha[i] && !used[color]) { /* not assigned? */
334 used[color] = 1;
335 this->global_palette[color] = start<<16;
336 start += step;
341 static void spudec_process_control(spudec_handle_t *this, unsigned int pts100)
343 int a,b; /* Temporary vars */
344 unsigned int date, type;
345 unsigned int off;
346 unsigned int start_off = 0;
347 unsigned int next_off;
348 unsigned int start_pts;
349 unsigned int end_pts;
350 unsigned int current_nibble[2];
351 unsigned int control_start;
352 unsigned int display = 0;
353 unsigned int start_col = 0;
354 unsigned int end_col = 0;
355 unsigned int start_row = 0;
356 unsigned int end_row = 0;
357 unsigned int width = 0;
358 unsigned int height = 0;
359 unsigned int stride = 0;
361 control_start = get_be16(this->packet + 2);
362 next_off = control_start;
363 while (start_off != next_off) {
364 start_off = next_off;
365 date = get_be16(this->packet + start_off) * 1024;
366 next_off = get_be16(this->packet + start_off + 2);
367 mp_msg(MSGT_SPUDEC,MSGL_DBG2, "date=%d\n", date);
368 off = start_off + 4;
369 for (type = this->packet[off++]; type != 0xff; type = this->packet[off++]) {
370 mp_msg(MSGT_SPUDEC,MSGL_DBG2, "cmd=%d ",type);
371 switch(type) {
372 case 0x00:
373 /* Menu ID, 1 byte */
374 mp_msg(MSGT_SPUDEC,MSGL_DBG2,"Menu ID\n");
375 /* shouldn't a Menu ID type force display start? */
376 start_pts = pts100 + date;
377 end_pts = UINT_MAX;
378 display = 1;
379 this->is_forced_sub=~0; // current subtitle is forced
380 break;
381 case 0x01:
382 /* Start display */
383 mp_msg(MSGT_SPUDEC,MSGL_DBG2,"Start display!\n");
384 start_pts = pts100 + date;
385 end_pts = UINT_MAX;
386 display = 1;
387 this->is_forced_sub=0;
388 break;
389 case 0x02:
390 /* Stop display */
391 mp_msg(MSGT_SPUDEC,MSGL_DBG2,"Stop display!\n");
392 end_pts = pts100 + date;
393 break;
394 case 0x03:
395 /* Palette */
396 this->palette[0] = this->packet[off] >> 4;
397 this->palette[1] = this->packet[off] & 0xf;
398 this->palette[2] = this->packet[off + 1] >> 4;
399 this->palette[3] = this->packet[off + 1] & 0xf;
400 mp_msg(MSGT_SPUDEC,MSGL_DBG2,"Palette %d, %d, %d, %d\n",
401 this->palette[0], this->palette[1], this->palette[2], this->palette[3]);
402 off+=2;
403 break;
404 case 0x04:
405 /* Alpha */
406 this->alpha[0] = this->packet[off] >> 4;
407 this->alpha[1] = this->packet[off] & 0xf;
408 this->alpha[2] = this->packet[off + 1] >> 4;
409 this->alpha[3] = this->packet[off + 1] & 0xf;
410 mp_msg(MSGT_SPUDEC,MSGL_DBG2,"Alpha %d, %d, %d, %d\n",
411 this->alpha[0], this->alpha[1], this->alpha[2], this->alpha[3]);
412 off+=2;
413 break;
414 case 0x05:
415 /* Co-ords */
416 a = get_be24(this->packet + off);
417 b = get_be24(this->packet + off + 3);
418 start_col = a >> 12;
419 end_col = a & 0xfff;
420 width = (end_col < start_col) ? 0 : end_col - start_col + 1;
421 stride = (width + 7) & ~7; /* Kludge: draw_alpha needs width multiple of 8 */
422 start_row = b >> 12;
423 end_row = b & 0xfff;
424 height = (end_row < start_row) ? 0 : end_row - start_row /* + 1 */;
425 mp_msg(MSGT_SPUDEC,MSGL_DBG2,"Coords col: %d - %d row: %d - %d (%dx%d)\n",
426 start_col, end_col, start_row, end_row,
427 width, height);
428 off+=6;
429 break;
430 case 0x06:
431 /* Graphic lines */
432 current_nibble[0] = 2 * get_be16(this->packet + off);
433 current_nibble[1] = 2 * get_be16(this->packet + off + 2);
434 mp_msg(MSGT_SPUDEC,MSGL_DBG2,"Graphic offset 1: %d offset 2: %d\n",
435 current_nibble[0] / 2, current_nibble[1] / 2);
436 off+=4;
437 break;
438 case 0xff:
439 /* All done, bye-bye */
440 mp_msg(MSGT_SPUDEC,MSGL_DBG2,"Done!\n");
441 return;
442 // break;
443 default:
444 mp_msg(MSGT_SPUDEC,MSGL_WARN,"spudec: Error determining control type 0x%02x. Skipping %d bytes.\n",
445 type, next_off - off);
446 goto next_control;
449 next_control:
450 if (display) {
451 packet_t *packet = calloc(1, sizeof(packet_t));
452 int i;
453 packet->start_pts = start_pts;
454 if (end_pts == UINT_MAX && start_off != next_off) {
455 start_pts = pts100 + get_be16(this->packet + next_off) * 1024;
456 packet->end_pts = start_pts - 1;
457 } else packet->end_pts = end_pts;
458 packet->current_nibble[0] = current_nibble[0];
459 packet->current_nibble[1] = current_nibble[1];
460 packet->start_row = start_row;
461 packet->end_row = end_row;
462 packet->start_col = start_col;
463 packet->end_col = end_col;
464 packet->width = width;
465 packet->height = height;
466 packet->stride = stride;
467 packet->control_start = control_start;
468 for (i=0; i<4; i++) {
469 packet->alpha[i] = this->alpha[i];
470 packet->palette[i] = this->palette[i];
472 packet->packet = malloc(this->packet_size);
473 memcpy(packet->packet, this->packet, this->packet_size);
474 spudec_queue_packet(this, packet);
479 static void spudec_decode(spudec_handle_t *this, unsigned int pts100)
481 if(this->hw_spu) {
482 static vo_mpegpes_t packet = { NULL, 0, 0x20, 0 };
483 static vo_mpegpes_t *pkg=&packet;
484 packet.data = this->packet;
485 packet.size = this->packet_size;
486 packet.timestamp = pts100;
487 this->hw_spu->draw_frame((uint8_t**)&pkg);
488 } else
489 spudec_process_control(this, pts100);
492 int spudec_changed(void * this)
494 spudec_handle_t * spu = (spudec_handle_t*)this;
495 return (spu->spu_changed || spu->now_pts > spu->end_pts);
498 void spudec_assemble(void *this, unsigned char *packet, unsigned int len, unsigned int pts100)
500 spudec_handle_t *spu = (spudec_handle_t*)this;
501 // spudec_heartbeat(this, pts100);
502 if (len < 2) {
503 mp_msg(MSGT_SPUDEC,MSGL_WARN,"SPUasm: packet too short\n");
504 return;
506 #if 0
507 if ((spu->packet_pts + 10000) < pts100) {
508 // [cb] too long since last fragment: force new packet
509 spu->packet_offset = 0;
511 #endif
512 spu->packet_pts = pts100;
513 if (spu->packet_offset == 0) {
514 unsigned int len2 = get_be16(packet);
515 // Start new fragment
516 if (spu->packet_reserve < len2) {
517 if (spu->packet != NULL)
518 free(spu->packet);
519 spu->packet = malloc(len2);
520 spu->packet_reserve = spu->packet != NULL ? len2 : 0;
522 if (spu->packet != NULL) {
523 spu->packet_size = len2;
524 if (len > len2) {
525 mp_msg(MSGT_SPUDEC,MSGL_WARN,"SPUasm: invalid frag len / len2: %d / %d \n", len, len2);
526 return;
528 memcpy(spu->packet, packet, len);
529 spu->packet_offset = len;
530 spu->packet_pts = pts100;
532 } else {
533 // Continue current fragment
534 if (spu->packet_size < spu->packet_offset + len){
535 mp_msg(MSGT_SPUDEC,MSGL_WARN,"SPUasm: invalid fragment\n");
536 spu->packet_size = spu->packet_offset = 0;
537 return;
538 } else {
539 memcpy(spu->packet + spu->packet_offset, packet, len);
540 spu->packet_offset += len;
543 #if 1
544 // check if we have a complete packet (unfortunatelly packet_size is bad
545 // for some disks)
546 // [cb] packet_size is padded to be even -> may be one byte too long
547 if ((spu->packet_offset == spu->packet_size) ||
548 ((spu->packet_offset + 1) == spu->packet_size)){
549 unsigned int x=0,y;
550 while(x+4<=spu->packet_offset){
551 y=get_be16(spu->packet+x+2); // next control pointer
552 mp_msg(MSGT_SPUDEC,MSGL_DBG2,"SPUtest: x=%d y=%d off=%d size=%d\n",x,y,spu->packet_offset,spu->packet_size);
553 if(x>=4 && x==y){ // if it points to self - we're done!
554 // we got it!
555 mp_msg(MSGT_SPUDEC,MSGL_DBG2,"SPUgot: off=%d size=%d \n",spu->packet_offset,spu->packet_size);
556 spudec_decode(spu, pts100);
557 spu->packet_offset = 0;
558 break;
560 if(y<=x || y>=spu->packet_size){ // invalid?
561 mp_msg(MSGT_SPUDEC,MSGL_WARN,"SPUtest: broken packet!!!!! y=%d < x=%d\n",y,x);
562 spu->packet_size = spu->packet_offset = 0;
563 break;
565 x=y;
567 // [cb] packet is done; start new packet
568 spu->packet_offset = 0;
570 #else
571 if (spu->packet_offset == spu->packet_size) {
572 spudec_decode(spu, pts100);
573 spu->packet_offset = 0;
575 #endif
578 void spudec_reset(void *this) // called after seek
580 spudec_handle_t *spu = (spudec_handle_t*)this;
581 while (spu->queue_head)
582 spudec_free_packet(spudec_dequeue_packet(spu));
583 spu->now_pts = 0;
584 spu->end_pts = 0;
585 spu->packet_size = spu->packet_offset = 0;
588 void spudec_heartbeat(void *this, unsigned int pts100)
590 spudec_handle_t *spu = (spudec_handle_t*) this;
591 spu->now_pts = pts100;
593 while (spu->queue_head != NULL && pts100 >= spu->queue_head->start_pts) {
594 packet_t *packet = spudec_dequeue_packet(spu);
595 spu->start_pts = packet->start_pts;
596 spu->end_pts = packet->end_pts;
597 if (spu->auto_palette)
598 compute_palette(spu, packet);
599 spudec_process_data(spu, packet);
600 spudec_free_packet(packet);
601 spu->spu_changed = 1;
605 int spudec_visible(void *this){
606 spudec_handle_t *spu = (spudec_handle_t *)this;
607 int ret=(spu->start_pts <= spu->now_pts &&
608 spu->now_pts < spu->end_pts &&
609 spu->height > 0);
610 // printf("spu visible: %d \n",ret);
611 return ret;
614 void spudec_set_forced_subs_only(void * const this, const unsigned int flag)
616 if(this){
617 ((spudec_handle_t *)this)->forced_subs_only=flag;
618 mp_msg(MSGT_SPUDEC,MSGL_DBG2,"SPU: Display only forced subs now %s\n", flag ? "enabled": "disabled");
622 void spudec_draw(void *this, void (*draw_alpha)(int x0,int y0, int w,int h, unsigned char* src, unsigned char *srca, int stride))
624 spudec_handle_t *spu = (spudec_handle_t *)this;
625 if (spu->start_pts <= spu->now_pts && spu->now_pts < spu->end_pts && spu->image)
627 draw_alpha(spu->start_col, spu->start_row, spu->width, spu->height,
628 spu->image, spu->aimage, spu->stride);
629 spu->spu_changed = 0;
633 /* calc the bbox for spudec subs */
634 void spudec_calc_bbox(void *me, unsigned int dxs, unsigned int dys, unsigned int* bbox)
636 spudec_handle_t *spu;
637 spu = (spudec_handle_t *)me;
638 if (spu->orig_frame_width == 0 || spu->orig_frame_height == 0
639 || (spu->orig_frame_width == dxs && spu->orig_frame_height == dys)) {
640 bbox[0] = spu->start_col;
641 bbox[1] = spu->start_col + spu->width;
642 bbox[2] = spu->start_row;
643 bbox[3] = spu->start_row + spu->height;
645 else if (spu->scaled_frame_width != dxs || spu->scaled_frame_height != dys) {
646 unsigned int scalex = 0x100 * dxs / spu->orig_frame_width;
647 unsigned int scaley = 0x100 * dys / spu->orig_frame_height;
648 bbox[0] = spu->start_col * scalex / 0x100;
649 bbox[1] = spu->start_col * scalex / 0x100 + spu->width * scalex / 0x100;
650 switch (spu_alignment) {
651 case 0:
652 bbox[3] = dys*sub_pos/100 + spu->height * scaley / 0x100;
653 if (bbox[3] > dys) bbox[3] = dys;
654 bbox[2] = bbox[3] - spu->height * scaley / 0x100;
655 break;
656 case 1:
657 if (sub_pos < 50) {
658 bbox[2] = dys*sub_pos/100 - spu->height * scaley / 0x200;
659 if (bbox[2] < 0) bbox[2] = 0;
660 bbox[3] = bbox[2] + spu->height;
661 } else {
662 bbox[3] = dys*sub_pos/100 + spu->height * scaley / 0x200;
663 if (bbox[3] > dys) bbox[3] = dys;
664 bbox[2] = bbox[3] - spu->height * scaley / 0x100;
666 break;
667 case 2:
668 bbox[2] = dys*sub_pos/100 - spu->height * scaley / 0x100;
669 if (bbox[2] < 0) bbox[2] = 0;
670 bbox[3] = bbox[2] + spu->height;
671 break;
672 default: /* -1 */
673 bbox[2] = spu->start_row * scaley / 0x100;
674 bbox[3] = spu->start_row * scaley / 0x100 + spu->height * scaley / 0x100;
675 break;
679 /* transform mplayer's alpha value into an opacity value that is linear */
680 static inline int canon_alpha(int alpha)
682 return alpha ? 256 - alpha : 0;
685 typedef struct {
686 unsigned position;
687 unsigned left_up;
688 unsigned right_down;
689 }scale_pixel;
692 static void scale_table(unsigned int start_src, unsigned int start_tar, unsigned int end_src, unsigned int end_tar, scale_pixel * table)
694 unsigned int t;
695 unsigned int delta_src = end_src - start_src;
696 unsigned int delta_tar = end_tar - start_tar;
697 int src = 0;
698 int src_step;
699 if (delta_src == 0 || delta_tar == 0) {
700 return;
702 src_step = (delta_src << 16) / delta_tar >>1;
703 for (t = 0; t<=delta_tar; src += (src_step << 1), t++){
704 table[t].position= MIN(src >> 16, end_src - 1);
705 table[t].right_down = src & 0xffff;
706 table[t].left_up = 0x10000 - table[t].right_down;
710 /* bilinear scale, similar to vobsub's code */
711 static void scale_image(int x, int y, scale_pixel* table_x, scale_pixel* table_y, spudec_handle_t * spu)
713 int alpha[4];
714 int color[4];
715 unsigned int scale[4];
716 int base = table_y[y].position * spu->stride + table_x[x].position;
717 int scaled = y * spu->scaled_stride + x;
718 alpha[0] = canon_alpha(spu->aimage[base]);
719 alpha[1] = canon_alpha(spu->aimage[base + 1]);
720 alpha[2] = canon_alpha(spu->aimage[base + spu->stride]);
721 alpha[3] = canon_alpha(spu->aimage[base + spu->stride + 1]);
722 color[0] = spu->image[base];
723 color[1] = spu->image[base + 1];
724 color[2] = spu->image[base + spu->stride];
725 color[3] = spu->image[base + spu->stride + 1];
726 scale[0] = (table_x[x].left_up * table_y[y].left_up >> 16) * alpha[0];
727 scale[1] = (table_x[x].right_down * table_y[y].left_up >>16) * alpha[1];
728 scale[2] = (table_x[x].left_up * table_y[y].right_down >> 16) * alpha[2];
729 scale[3] = (table_x[x].right_down * table_y[y].right_down >> 16) * alpha[3];
730 spu->scaled_image[scaled] = (color[0] * scale[0] + color[1] * scale[1] + color[2] * scale[2] + color[3] * scale[3])>>24;
731 spu->scaled_aimage[scaled] = (scale[0] + scale[1] + scale[2] + scale[3]) >> 16;
732 if (spu->scaled_aimage[scaled]){
733 spu->scaled_aimage[scaled] = 256 - spu->scaled_aimage[scaled];
734 if(spu->scaled_aimage[scaled] + spu->scaled_image[scaled] > 255)
735 spu->scaled_image[scaled] = 256 - spu->scaled_aimage[scaled];
739 void sws_spu_image(unsigned char *d1, unsigned char *d2, int dw, int dh, int ds,
740 unsigned char *s1, unsigned char *s2, int sw, int sh, int ss)
742 struct SwsContext *ctx;
743 static SwsFilter filter;
744 static int firsttime = 1;
745 static float oldvar;
746 int i;
748 if (!firsttime && oldvar != spu_gaussvar) sws_freeVec(filter.lumH);
749 if (firsttime) {
750 filter.lumH = filter.lumV =
751 filter.chrH = filter.chrV = sws_getGaussianVec(spu_gaussvar, 3.0);
752 sws_normalizeVec(filter.lumH, 1.0);
753 firsttime = 0;
754 oldvar = spu_gaussvar;
757 ctx=sws_getContext(sw, sh, IMGFMT_Y800, dw, dh, IMGFMT_Y800, SWS_GAUSS, &filter, NULL, NULL);
758 sws_scale(ctx,&s1,&ss,0,sh,&d1,&ds);
759 for (i=ss*sh-1; i>=0; i--) if (!s2[i]) s2[i] = 255; //else s2[i] = 1;
760 sws_scale(ctx,&s2,&ss,0,sh,&d2,&ds);
761 for (i=ds*dh-1; i>=0; i--) if (d2[i]==0) d2[i] = 1; else if (d2[i]==255) d2[i] = 0;
762 sws_freeContext(ctx);
765 void spudec_draw_scaled(void *me, unsigned int dxs, unsigned int dys, void (*draw_alpha)(int x0,int y0, int w,int h, unsigned char* src, unsigned char *srca, int stride))
767 spudec_handle_t *spu = (spudec_handle_t *)me;
768 scale_pixel *table_x;
769 scale_pixel *table_y;
771 if (spu->start_pts <= spu->now_pts && spu->now_pts < spu->end_pts) {
773 // check if only forced subtitles are requested
774 if( (spu->forced_subs_only) && !(spu->is_forced_sub) ){
775 return;
778 if (!(spu_aamode&16) && (spu->orig_frame_width == 0 || spu->orig_frame_height == 0
779 || (spu->orig_frame_width == dxs && spu->orig_frame_height == dys))) {
780 if (spu->image)
782 draw_alpha(spu->start_col, spu->start_row, spu->width, spu->height,
783 spu->image, spu->aimage, spu->stride);
784 spu->spu_changed = 0;
787 else {
788 if (spu->scaled_frame_width != dxs || spu->scaled_frame_height != dys) { /* Resizing is needed */
789 /* scaled_x = scalex * x / 0x100
790 scaled_y = scaley * y / 0x100
791 order of operations is important because of rounding. */
792 unsigned int scalex = 0x100 * dxs / spu->orig_frame_width;
793 unsigned int scaley = 0x100 * dys / spu->orig_frame_height;
794 spu->scaled_start_col = spu->start_col * scalex / 0x100;
795 spu->scaled_start_row = spu->start_row * scaley / 0x100;
796 spu->scaled_width = spu->width * scalex / 0x100;
797 spu->scaled_height = spu->height * scaley / 0x100;
798 /* Kludge: draw_alpha needs width multiple of 8 */
799 spu->scaled_stride = (spu->scaled_width + 7) & ~7;
800 if (spu->scaled_image_size < spu->scaled_stride * spu->scaled_height) {
801 if (spu->scaled_image) {
802 free(spu->scaled_image);
803 spu->scaled_image_size = 0;
805 spu->scaled_image = malloc(2 * spu->scaled_stride * spu->scaled_height);
806 if (spu->scaled_image) {
807 spu->scaled_image_size = spu->scaled_stride * spu->scaled_height;
808 spu->scaled_aimage = spu->scaled_image + spu->scaled_image_size;
811 if (spu->scaled_image) {
812 unsigned int x, y;
813 if (spu->scaled_width <= 1 || spu->scaled_height <= 1) {
814 goto nothing_to_do;
816 switch(spu_aamode&15) {
817 case 4:
818 sws_spu_image(spu->scaled_image, spu->scaled_aimage,
819 spu->scaled_width, spu->scaled_height, spu->scaled_stride,
820 spu->image, spu->aimage, spu->width, spu->height, spu->stride);
821 break;
822 case 3:
823 table_x = calloc(spu->scaled_width, sizeof(scale_pixel));
824 table_y = calloc(spu->scaled_height, sizeof(scale_pixel));
825 if (!table_x || !table_y) {
826 mp_msg(MSGT_SPUDEC, MSGL_FATAL, "Fatal: spudec_draw_scaled: calloc failed\n");
828 scale_table(0, 0, spu->width - 1, spu->scaled_width - 1, table_x);
829 scale_table(0, 0, spu->height - 1, spu->scaled_height - 1, table_y);
830 for (y = 0; y < spu->scaled_height; y++)
831 for (x = 0; x < spu->scaled_width; x++)
832 scale_image(x, y, table_x, table_y, spu);
833 free(table_x);
834 free(table_y);
835 break;
836 case 0:
837 /* no antialiasing */
838 for (y = 0; y < spu->scaled_height; ++y) {
839 int unscaled_y = y * 0x100 / scaley;
840 int strides = spu->stride * unscaled_y;
841 int scaled_strides = spu->scaled_stride * y;
842 for (x = 0; x < spu->scaled_width; ++x) {
843 int unscaled_x = x * 0x100 / scalex;
844 spu->scaled_image[scaled_strides + x] = spu->image[strides + unscaled_x];
845 spu->scaled_aimage[scaled_strides + x] = spu->aimage[strides + unscaled_x];
848 break;
849 case 1:
851 /* Intermediate antialiasing. */
852 for (y = 0; y < spu->scaled_height; ++y) {
853 const unsigned int unscaled_top = y * spu->orig_frame_height / dys;
854 unsigned int unscaled_bottom = (y + 1) * spu->orig_frame_height / dys;
855 if (unscaled_bottom >= spu->height)
856 unscaled_bottom = spu->height - 1;
857 for (x = 0; x < spu->scaled_width; ++x) {
858 const unsigned int unscaled_left = x * spu->orig_frame_width / dxs;
859 unsigned int unscaled_right = (x + 1) * spu->orig_frame_width / dxs;
860 unsigned int color = 0;
861 unsigned int alpha = 0;
862 unsigned int walkx, walky;
863 unsigned int base, tmp;
864 if (unscaled_right >= spu->width)
865 unscaled_right = spu->width - 1;
866 for (walky = unscaled_top; walky <= unscaled_bottom; ++walky)
867 for (walkx = unscaled_left; walkx <= unscaled_right; ++walkx) {
868 base = walky * spu->stride + walkx;
869 tmp = canon_alpha(spu->aimage[base]);
870 alpha += tmp;
871 color += tmp * spu->image[base];
873 base = y * spu->scaled_stride + x;
874 spu->scaled_image[base] = alpha ? color / alpha : 0;
875 spu->scaled_aimage[base] =
876 alpha * (1 + unscaled_bottom - unscaled_top) * (1 + unscaled_right - unscaled_left);
877 /* spu->scaled_aimage[base] =
878 alpha * dxs * dys / spu->orig_frame_width / spu->orig_frame_height; */
879 if (spu->scaled_aimage[base]) {
880 spu->scaled_aimage[base] = 256 - spu->scaled_aimage[base];
881 if (spu->scaled_aimage[base] + spu->scaled_image[base] > 255)
882 spu->scaled_image[base] = 256 - spu->scaled_aimage[base];
887 break;
888 case 2:
890 /* Best antialiasing. Very slow. */
891 /* Any pixel (x, y) represents pixels from the original
892 rectangular region comprised between the columns
893 unscaled_y and unscaled_y + 0x100 / scaley and the rows
894 unscaled_x and unscaled_x + 0x100 / scalex
896 The original rectangular region that the scaled pixel
897 represents is cut in 9 rectangular areas like this:
899 +---+-----------------+---+
900 | 1 | 2 | 3 |
901 +---+-----------------+---+
902 | | | |
903 | 4 | 5 | 6 |
904 | | | |
905 +---+-----------------+---+
906 | 7 | 8 | 9 |
907 +---+-----------------+---+
909 The width of the left column is at most one pixel and
910 it is never null and its right column is at a pixel
911 boundary. The height of the top row is at most one
912 pixel it is never null and its bottom row is at a
913 pixel boundary. The width and height of region 5 are
914 integral values. The width of the right column is
915 what remains and is less than one pixel. The height
916 of the bottom row is what remains and is less than
917 one pixel.
919 The row above 1, 2, 3 is unscaled_y. The row between
920 1, 2, 3 and 4, 5, 6 is top_low_row. The row between 4,
921 5, 6 and 7, 8, 9 is (unsigned int)unscaled_y_bottom.
922 The row beneath 7, 8, 9 is unscaled_y_bottom.
924 The column left of 1, 4, 7 is unscaled_x. The column
925 between 1, 4, 7 and 2, 5, 8 is left_right_column. The
926 column between 2, 5, 8 and 3, 6, 9 is (unsigned
927 int)unscaled_x_right. The column right of 3, 6, 9 is
928 unscaled_x_right. */
929 const double inv_scalex = (double) 0x100 / scalex;
930 const double inv_scaley = (double) 0x100 / scaley;
931 for (y = 0; y < spu->scaled_height; ++y) {
932 const double unscaled_y = y * inv_scaley;
933 const double unscaled_y_bottom = unscaled_y + inv_scaley;
934 const unsigned int top_low_row = MIN(unscaled_y_bottom, unscaled_y + 1.0);
935 const double top = top_low_row - unscaled_y;
936 const unsigned int height = unscaled_y_bottom > top_low_row
937 ? (unsigned int) unscaled_y_bottom - top_low_row
938 : 0;
939 const double bottom = unscaled_y_bottom > top_low_row
940 ? unscaled_y_bottom - floor(unscaled_y_bottom)
941 : 0.0;
942 for (x = 0; x < spu->scaled_width; ++x) {
943 const double unscaled_x = x * inv_scalex;
944 const double unscaled_x_right = unscaled_x + inv_scalex;
945 const unsigned int left_right_column = MIN(unscaled_x_right, unscaled_x + 1.0);
946 const double left = left_right_column - unscaled_x;
947 const unsigned int width = unscaled_x_right > left_right_column
948 ? (unsigned int) unscaled_x_right - left_right_column
949 : 0;
950 const double right = unscaled_x_right > left_right_column
951 ? unscaled_x_right - floor(unscaled_x_right)
952 : 0.0;
953 double color = 0.0;
954 double alpha = 0.0;
955 double tmp;
956 unsigned int base;
957 /* Now use these informations to compute a good alpha,
958 and lightness. The sum is on each of the 9
959 region's surface and alpha and lightness.
961 transformed alpha = sum(surface * alpha) / sum(surface)
962 transformed color = sum(surface * alpha * color) / sum(surface * alpha)
964 /* 1: top left part */
965 base = spu->stride * (unsigned int) unscaled_y;
966 tmp = left * top * canon_alpha(spu->aimage[base + (unsigned int) unscaled_x]);
967 alpha += tmp;
968 color += tmp * spu->image[base + (unsigned int) unscaled_x];
969 /* 2: top center part */
970 if (width > 0) {
971 unsigned int walkx;
972 for (walkx = left_right_column; walkx < (unsigned int) unscaled_x_right; ++walkx) {
973 base = spu->stride * (unsigned int) unscaled_y + walkx;
974 tmp = /* 1.0 * */ top * canon_alpha(spu->aimage[base]);
975 alpha += tmp;
976 color += tmp * spu->image[base];
979 /* 3: top right part */
980 if (right > 0.0) {
981 base = spu->stride * (unsigned int) unscaled_y + (unsigned int) unscaled_x_right;
982 tmp = right * top * canon_alpha(spu->aimage[base]);
983 alpha += tmp;
984 color += tmp * spu->image[base];
986 /* 4: center left part */
987 if (height > 0) {
988 unsigned int walky;
989 for (walky = top_low_row; walky < (unsigned int) unscaled_y_bottom; ++walky) {
990 base = spu->stride * walky + (unsigned int) unscaled_x;
991 tmp = left /* * 1.0 */ * canon_alpha(spu->aimage[base]);
992 alpha += tmp;
993 color += tmp * spu->image[base];
996 /* 5: center part */
997 if (width > 0 && height > 0) {
998 unsigned int walky;
999 for (walky = top_low_row; walky < (unsigned int) unscaled_y_bottom; ++walky) {
1000 unsigned int walkx;
1001 base = spu->stride * walky;
1002 for (walkx = left_right_column; walkx < (unsigned int) unscaled_x_right; ++walkx) {
1003 tmp = /* 1.0 * 1.0 * */ canon_alpha(spu->aimage[base + walkx]);
1004 alpha += tmp;
1005 color += tmp * spu->image[base + walkx];
1009 /* 6: center right part */
1010 if (right > 0.0 && height > 0) {
1011 unsigned int walky;
1012 for (walky = top_low_row; walky < (unsigned int) unscaled_y_bottom; ++walky) {
1013 base = spu->stride * walky + (unsigned int) unscaled_x_right;
1014 tmp = right /* * 1.0 */ * canon_alpha(spu->aimage[base]);
1015 alpha += tmp;
1016 color += tmp * spu->image[base];
1019 /* 7: bottom left part */
1020 if (bottom > 0.0) {
1021 base = spu->stride * (unsigned int) unscaled_y_bottom + (unsigned int) unscaled_x;
1022 tmp = left * bottom * canon_alpha(spu->aimage[base]);
1023 alpha += tmp;
1024 color += tmp * spu->image[base];
1026 /* 8: bottom center part */
1027 if (width > 0 && bottom > 0.0) {
1028 unsigned int walkx;
1029 base = spu->stride * (unsigned int) unscaled_y_bottom;
1030 for (walkx = left_right_column; walkx < (unsigned int) unscaled_x_right; ++walkx) {
1031 tmp = /* 1.0 * */ bottom * canon_alpha(spu->aimage[base + walkx]);
1032 alpha += tmp;
1033 color += tmp * spu->image[base + walkx];
1036 /* 9: bottom right part */
1037 if (right > 0.0 && bottom > 0.0) {
1038 base = spu->stride * (unsigned int) unscaled_y_bottom + (unsigned int) unscaled_x_right;
1039 tmp = right * bottom * canon_alpha(spu->aimage[base]);
1040 alpha += tmp;
1041 color += tmp * spu->image[base];
1043 /* Finally mix these transparency and brightness information suitably */
1044 base = spu->scaled_stride * y + x;
1045 spu->scaled_image[base] = alpha > 0 ? color / alpha : 0;
1046 spu->scaled_aimage[base] = alpha * scalex * scaley / 0x10000;
1047 if (spu->scaled_aimage[base]) {
1048 spu->scaled_aimage[base] = 256 - spu->scaled_aimage[base];
1049 if (spu->scaled_aimage[base] + spu->scaled_image[base] > 255)
1050 spu->scaled_image[base] = 256 - spu->scaled_aimage[base];
1056 nothing_to_do:
1057 /* Kludge: draw_alpha needs width multiple of 8. */
1058 if (spu->scaled_width < spu->scaled_stride)
1059 for (y = 0; y < spu->scaled_height; ++y) {
1060 memset(spu->scaled_aimage + y * spu->scaled_stride + spu->scaled_width, 0,
1061 spu->scaled_stride - spu->scaled_width);
1063 spu->scaled_frame_width = dxs;
1064 spu->scaled_frame_height = dys;
1067 if (spu->scaled_image){
1068 switch (spu_alignment) {
1069 case 0:
1070 spu->scaled_start_row = dys*sub_pos/100;
1071 if (spu->scaled_start_row + spu->scaled_height > dys)
1072 spu->scaled_start_row = dys - spu->scaled_height;
1073 break;
1074 case 1:
1075 spu->scaled_start_row = dys*sub_pos/100 - spu->scaled_height/2;
1076 if (sub_pos < 50) {
1077 if (spu->scaled_start_row < 0) spu->scaled_start_row = 0;
1078 } else {
1079 if (spu->scaled_start_row + spu->scaled_height > dys)
1080 spu->scaled_start_row = dys - spu->scaled_height;
1082 break;
1083 case 2:
1084 spu->scaled_start_row = dys*sub_pos/100 - spu->scaled_height;
1085 if (spu->scaled_start_row < 0) spu->scaled_start_row = 0;
1086 break;
1088 draw_alpha(spu->scaled_start_col, spu->scaled_start_row, spu->scaled_width, spu->scaled_height,
1089 spu->scaled_image, spu->scaled_aimage, spu->scaled_stride);
1090 spu->spu_changed = 0;
1094 else
1096 mp_msg(MSGT_SPUDEC,MSGL_DBG2,"SPU not displayed: start_pts=%d end_pts=%d now_pts=%d\n",
1097 spu->start_pts, spu->end_pts, spu->now_pts);
1101 void spudec_update_palette(void * this, unsigned int *palette)
1103 spudec_handle_t *spu = (spudec_handle_t *) this;
1104 if (spu && palette) {
1105 memcpy(spu->global_palette, palette, sizeof(spu->global_palette));
1106 if(spu->hw_spu)
1107 spu->hw_spu->control(VOCTRL_SET_SPU_PALETTE,spu->global_palette);
1111 void spudec_set_font_factor(void * this, double factor)
1113 spudec_handle_t *spu = (spudec_handle_t *) this;
1114 spu->font_start_level = (int)(0xF0-(0xE0*factor));
1117 void *spudec_new_scaled(unsigned int *palette, unsigned int frame_width, unsigned int frame_height)
1119 return spudec_new_scaled_vobsub(palette, NULL, 0, frame_width, frame_height);
1122 /* get palette custom color, width, height from .idx file */
1123 void *spudec_new_scaled_vobsub(unsigned int *palette, unsigned int *cuspal, unsigned int custom, unsigned int frame_width, unsigned int frame_height)
1125 spudec_handle_t *this = calloc(1, sizeof(spudec_handle_t));
1126 if (this){
1127 //(fprintf(stderr,"VobSub Custom Palette: %d,%d,%d,%d", this->cuspal[0], this->cuspal[1], this->cuspal[2],this->cuspal[3]);
1128 this->packet = NULL;
1129 this->image = NULL;
1130 this->scaled_image = NULL;
1131 /* XXX Although the video frame is some size, the SPU frame is
1132 always maximum size i.e. 720 wide and 576 or 480 high */
1133 this->orig_frame_width = 720;
1134 this->orig_frame_height = (frame_height == 480 || frame_height == 240) ? 480 : 576;
1135 this->custom = custom;
1136 // set up palette:
1137 this->auto_palette = 1;
1138 if (palette){
1139 memcpy(this->global_palette, palette, sizeof(this->global_palette));
1140 this->auto_palette = 0;
1142 this->custom = custom;
1143 if (custom && cuspal) {
1144 memcpy(this->cuspal, cuspal, sizeof(this->cuspal));
1145 this->auto_palette = 0;
1147 // forced subtitles default: show all subtitles
1148 this->forced_subs_only=0;
1149 this->is_forced_sub=0;
1151 else
1152 mp_msg(MSGT_SPUDEC,MSGL_FATAL, "FATAL: spudec_init: calloc");
1153 return this;
1156 void *spudec_new(unsigned int *palette)
1158 return spudec_new_scaled(palette, 0, 0);
1161 void spudec_free(void *this)
1163 spudec_handle_t *spu = (spudec_handle_t*)this;
1164 if (spu) {
1165 while (spu->queue_head)
1166 spudec_free_packet(spudec_dequeue_packet(spu));
1167 if (spu->packet)
1168 free(spu->packet);
1169 if (spu->scaled_image)
1170 free(spu->scaled_image);
1171 if (spu->image)
1172 free(spu->image);
1173 free(spu);
1177 void spudec_set_hw_spu(void *this, vo_functions_t *hw_spu)
1179 spudec_handle_t *spu = (spudec_handle_t*)this;
1180 if (!spu)
1181 return;
1182 spu->hw_spu = hw_spu;
1183 hw_spu->control(VOCTRL_SET_SPU_PALETTE,spu->global_palette);