added my nick
[mplayer/glamo.git] / spudec.c
blob10ab1f69fbd54661986da1f965506bb01cbda8e7
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 ((spu->packet_pts + 10000) < pts100) {
507 // [cb] too long since last fragment: force new packet
508 spu->packet_offset = 0;
510 spu->packet_pts = pts100;
511 if (spu->packet_offset == 0) {
512 unsigned int len2 = get_be16(packet);
513 // Start new fragment
514 if (spu->packet_reserve < len2) {
515 if (spu->packet != NULL)
516 free(spu->packet);
517 spu->packet = malloc(len2);
518 spu->packet_reserve = spu->packet != NULL ? len2 : 0;
520 if (spu->packet != NULL) {
521 spu->packet_size = len2;
522 if (len > len2) {
523 mp_msg(MSGT_SPUDEC,MSGL_WARN,"SPUasm: invalid frag len / len2: %d / %d \n", len, len2);
524 return;
526 memcpy(spu->packet, packet, len);
527 spu->packet_offset = len;
528 spu->packet_pts = pts100;
530 } else {
531 // Continue current fragment
532 if (spu->packet_size < spu->packet_offset + len){
533 mp_msg(MSGT_SPUDEC,MSGL_WARN,"SPUasm: invalid fragment\n");
534 spu->packet_size = spu->packet_offset = 0;
535 return;
536 } else {
537 memcpy(spu->packet + spu->packet_offset, packet, len);
538 spu->packet_offset += len;
541 #if 1
542 // check if we have a complete packet (unfortunatelly packet_size is bad
543 // for some disks)
544 // [cb] packet_size is padded to be even -> may be one byte too long
545 if ((spu->packet_offset == spu->packet_size) ||
546 ((spu->packet_offset + 1) == spu->packet_size)){
547 unsigned int x=0,y;
548 while(x+4<=spu->packet_offset){
549 y=get_be16(spu->packet+x+2); // next control pointer
550 mp_msg(MSGT_SPUDEC,MSGL_DBG2,"SPUtest: x=%d y=%d off=%d size=%d\n",x,y,spu->packet_offset,spu->packet_size);
551 if(x>=4 && x==y){ // if it points to self - we're done!
552 // we got it!
553 mp_msg(MSGT_SPUDEC,MSGL_DBG2,"SPUgot: off=%d size=%d \n",spu->packet_offset,spu->packet_size);
554 spudec_decode(spu, pts100);
555 spu->packet_offset = 0;
556 break;
558 if(y<=x || y>=spu->packet_size){ // invalid?
559 mp_msg(MSGT_SPUDEC,MSGL_WARN,"SPUtest: broken packet!!!!! y=%d < x=%d\n",y,x);
560 spu->packet_size = spu->packet_offset = 0;
561 break;
563 x=y;
565 // [cb] packet is done; start new packet
566 spu->packet_offset = 0;
568 #else
569 if (spu->packet_offset == spu->packet_size) {
570 spudec_decode(spu, pts100);
571 spu->packet_offset = 0;
573 #endif
576 void spudec_reset(void *this) // called after seek
578 spudec_handle_t *spu = (spudec_handle_t*)this;
579 while (spu->queue_head)
580 spudec_free_packet(spudec_dequeue_packet(spu));
581 spu->now_pts = 0;
582 spu->end_pts = 0;
583 spu->packet_size = spu->packet_offset = 0;
586 void spudec_heartbeat(void *this, unsigned int pts100)
588 spudec_handle_t *spu = (spudec_handle_t*) this;
589 spu->now_pts = pts100;
591 while (spu->queue_head != NULL && pts100 >= spu->queue_head->start_pts) {
592 packet_t *packet = spudec_dequeue_packet(spu);
593 spu->start_pts = packet->start_pts;
594 spu->end_pts = packet->end_pts;
595 if (spu->auto_palette)
596 compute_palette(spu, packet);
597 spudec_process_data(spu, packet);
598 spudec_free_packet(packet);
599 spu->spu_changed = 1;
603 int spudec_visible(void *this){
604 spudec_handle_t *spu = (spudec_handle_t *)this;
605 int ret=(spu->start_pts <= spu->now_pts &&
606 spu->now_pts < spu->end_pts &&
607 spu->height > 0);
608 // printf("spu visible: %d \n",ret);
609 return ret;
612 void spudec_set_forced_subs_only(void * const this, const unsigned int flag)
614 if(this){
615 ((spudec_handle_t *)this)->forced_subs_only=flag;
616 mp_msg(MSGT_SPUDEC,MSGL_DBG2,"SPU: Display only forced subs now %s\n", flag ? "enabled": "disabled");
620 void spudec_draw(void *this, void (*draw_alpha)(int x0,int y0, int w,int h, unsigned char* src, unsigned char *srca, int stride))
622 spudec_handle_t *spu = (spudec_handle_t *)this;
623 if (spu->start_pts <= spu->now_pts && spu->now_pts < spu->end_pts && spu->image)
625 draw_alpha(spu->start_col, spu->start_row, spu->width, spu->height,
626 spu->image, spu->aimage, spu->stride);
627 spu->spu_changed = 0;
631 /* calc the bbox for spudec subs */
632 void spudec_calc_bbox(void *me, unsigned int dxs, unsigned int dys, unsigned int* bbox)
634 spudec_handle_t *spu;
635 spu = (spudec_handle_t *)me;
636 if (spu->orig_frame_width == 0 || spu->orig_frame_height == 0
637 || (spu->orig_frame_width == dxs && spu->orig_frame_height == dys)) {
638 bbox[0] = spu->start_col;
639 bbox[1] = spu->start_col + spu->width;
640 bbox[2] = spu->start_row;
641 bbox[3] = spu->start_row + spu->height;
643 else if (spu->scaled_frame_width != dxs || spu->scaled_frame_height != dys) {
644 unsigned int scalex = 0x100 * dxs / spu->orig_frame_width;
645 unsigned int scaley = 0x100 * dys / spu->orig_frame_height;
646 bbox[0] = spu->start_col * scalex / 0x100;
647 bbox[1] = spu->start_col * scalex / 0x100 + spu->width * scalex / 0x100;
648 switch (spu_alignment) {
649 case 0:
650 bbox[3] = dys*sub_pos/100 + spu->height * scaley / 0x100;
651 if (bbox[3] > dys) bbox[3] = dys;
652 bbox[2] = bbox[3] - spu->height * scaley / 0x100;
653 break;
654 case 1:
655 if (sub_pos < 50) {
656 bbox[2] = dys*sub_pos/100 - spu->height * scaley / 0x200;
657 if (bbox[2] < 0) bbox[2] = 0;
658 bbox[3] = bbox[2] + spu->height;
659 } else {
660 bbox[3] = dys*sub_pos/100 + spu->height * scaley / 0x200;
661 if (bbox[3] > dys) bbox[3] = dys;
662 bbox[2] = bbox[3] - spu->height * scaley / 0x100;
664 break;
665 case 2:
666 bbox[2] = dys*sub_pos/100 - spu->height * scaley / 0x100;
667 if (bbox[2] < 0) bbox[2] = 0;
668 bbox[3] = bbox[2] + spu->height;
669 break;
670 default: /* -1 */
671 bbox[2] = spu->start_row * scaley / 0x100;
672 bbox[3] = spu->start_row * scaley / 0x100 + spu->height * scaley / 0x100;
673 break;
677 /* transform mplayer's alpha value into an opacity value that is linear */
678 static inline int canon_alpha(int alpha)
680 return alpha ? 256 - alpha : 0;
683 typedef struct {
684 unsigned position;
685 unsigned left_up;
686 unsigned right_down;
687 }scale_pixel;
690 static void scale_table(unsigned int start_src, unsigned int start_tar, unsigned int end_src, unsigned int end_tar, scale_pixel * table)
692 unsigned int t;
693 unsigned int delta_src = end_src - start_src;
694 unsigned int delta_tar = end_tar - start_tar;
695 int src = 0;
696 int src_step;
697 if (delta_src == 0 || delta_tar == 0) {
698 return;
700 src_step = (delta_src << 16) / delta_tar >>1;
701 for (t = 0; t<=delta_tar; src += (src_step << 1), t++){
702 table[t].position= MIN(src >> 16, end_src - 1);
703 table[t].right_down = src & 0xffff;
704 table[t].left_up = 0x10000 - table[t].right_down;
708 /* bilinear scale, similar to vobsub's code */
709 static void scale_image(int x, int y, scale_pixel* table_x, scale_pixel* table_y, spudec_handle_t * spu)
711 int alpha[4];
712 int color[4];
713 unsigned int scale[4];
714 int base = table_y[y].position * spu->stride + table_x[x].position;
715 int scaled = y * spu->scaled_stride + x;
716 alpha[0] = canon_alpha(spu->aimage[base]);
717 alpha[1] = canon_alpha(spu->aimage[base + 1]);
718 alpha[2] = canon_alpha(spu->aimage[base + spu->stride]);
719 alpha[3] = canon_alpha(spu->aimage[base + spu->stride + 1]);
720 color[0] = spu->image[base];
721 color[1] = spu->image[base + 1];
722 color[2] = spu->image[base + spu->stride];
723 color[3] = spu->image[base + spu->stride + 1];
724 scale[0] = (table_x[x].left_up * table_y[y].left_up >> 16) * alpha[0];
725 scale[1] = (table_x[x].right_down * table_y[y].left_up >>16) * alpha[1];
726 scale[2] = (table_x[x].left_up * table_y[y].right_down >> 16) * alpha[2];
727 scale[3] = (table_x[x].right_down * table_y[y].right_down >> 16) * alpha[3];
728 spu->scaled_image[scaled] = (color[0] * scale[0] + color[1] * scale[1] + color[2] * scale[2] + color[3] * scale[3])>>24;
729 spu->scaled_aimage[scaled] = (scale[0] + scale[1] + scale[2] + scale[3]) >> 16;
730 if (spu->scaled_aimage[scaled]){
731 spu->scaled_aimage[scaled] = 256 - spu->scaled_aimage[scaled];
732 if(spu->scaled_aimage[scaled] + spu->scaled_image[scaled] > 255)
733 spu->scaled_image[scaled] = 256 - spu->scaled_aimage[scaled];
737 void sws_spu_image(unsigned char *d1, unsigned char *d2, int dw, int dh, int ds,
738 unsigned char *s1, unsigned char *s2, int sw, int sh, int ss)
740 struct SwsContext *ctx;
741 static SwsFilter filter;
742 static int firsttime = 1;
743 static float oldvar;
744 int i;
746 if (!firsttime && oldvar != spu_gaussvar) sws_freeVec(filter.lumH);
747 if (firsttime) {
748 filter.lumH = filter.lumV =
749 filter.chrH = filter.chrV = sws_getGaussianVec(spu_gaussvar, 3.0);
750 sws_normalizeVec(filter.lumH, 1.0);
751 firsttime = 0;
752 oldvar = spu_gaussvar;
755 ctx=sws_getContext(sw, sh, IMGFMT_Y800, dw, dh, IMGFMT_Y800, SWS_GAUSS, &filter, NULL);
756 sws_scale(ctx,&s1,&ss,0,sh,&d1,&ds);
757 for (i=ss*sh-1; i>=0; i--) if (!s2[i]) s2[i] = 255; //else s2[i] = 1;
758 sws_scale(ctx,&s2,&ss,0,sh,&d2,&ds);
759 for (i=ds*dh-1; i>=0; i--) if (d2[i]==0) d2[i] = 1; else if (d2[i]==255) d2[i] = 0;
760 sws_freeContext(ctx);
763 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))
765 spudec_handle_t *spu = (spudec_handle_t *)me;
766 scale_pixel *table_x;
767 scale_pixel *table_y;
769 if (spu->start_pts <= spu->now_pts && spu->now_pts < spu->end_pts) {
771 // check if only forced subtitles are requested
772 if( (spu->forced_subs_only) && !(spu->is_forced_sub) ){
773 return;
776 if (!(spu_aamode&16) && (spu->orig_frame_width == 0 || spu->orig_frame_height == 0
777 || (spu->orig_frame_width == dxs && spu->orig_frame_height == dys))) {
778 if (spu->image)
780 draw_alpha(spu->start_col, spu->start_row, spu->width, spu->height,
781 spu->image, spu->aimage, spu->stride);
782 spu->spu_changed = 0;
785 else {
786 if (spu->scaled_frame_width != dxs || spu->scaled_frame_height != dys) { /* Resizing is needed */
787 /* scaled_x = scalex * x / 0x100
788 scaled_y = scaley * y / 0x100
789 order of operations is important because of rounding. */
790 unsigned int scalex = 0x100 * dxs / spu->orig_frame_width;
791 unsigned int scaley = 0x100 * dys / spu->orig_frame_height;
792 spu->scaled_start_col = spu->start_col * scalex / 0x100;
793 spu->scaled_start_row = spu->start_row * scaley / 0x100;
794 spu->scaled_width = spu->width * scalex / 0x100;
795 spu->scaled_height = spu->height * scaley / 0x100;
796 /* Kludge: draw_alpha needs width multiple of 8 */
797 spu->scaled_stride = (spu->scaled_width + 7) & ~7;
798 if (spu->scaled_image_size < spu->scaled_stride * spu->scaled_height) {
799 if (spu->scaled_image) {
800 free(spu->scaled_image);
801 spu->scaled_image_size = 0;
803 spu->scaled_image = malloc(2 * spu->scaled_stride * spu->scaled_height);
804 if (spu->scaled_image) {
805 spu->scaled_image_size = spu->scaled_stride * spu->scaled_height;
806 spu->scaled_aimage = spu->scaled_image + spu->scaled_image_size;
809 if (spu->scaled_image) {
810 unsigned int x, y;
811 /* Kludge: draw_alpha needs width multiple of 8. */
812 if (spu->scaled_width < spu->scaled_stride)
813 for (y = 0; y < spu->scaled_height; ++y) {
814 memset(spu->scaled_aimage + y * spu->scaled_stride + spu->scaled_width, 0,
815 spu->scaled_stride - spu->scaled_width);
816 /* FIXME: Why is this one needed? */
817 memset(spu->scaled_image + y * spu->scaled_stride + spu->scaled_width, 0,
818 spu->scaled_stride - spu->scaled_width);
820 if (spu->scaled_width <= 1 || spu->scaled_height <= 1) {
821 goto nothing_to_do;
823 switch(spu_aamode&15) {
824 case 4:
825 sws_spu_image(spu->scaled_image, spu->scaled_aimage,
826 spu->scaled_width, spu->scaled_height, spu->scaled_stride,
827 spu->image, spu->aimage, spu->width, spu->height, spu->stride);
828 break;
829 case 3:
830 table_x = calloc(spu->scaled_width, sizeof(scale_pixel));
831 table_y = calloc(spu->scaled_height, sizeof(scale_pixel));
832 if (!table_x || !table_y) {
833 mp_msg(MSGT_SPUDEC, MSGL_FATAL, "Fatal: spudec_draw_scaled: calloc failed\n");
835 scale_table(0, 0, spu->width - 1, spu->scaled_width - 1, table_x);
836 scale_table(0, 0, spu->height - 1, spu->scaled_height - 1, table_y);
837 for (y = 0; y < spu->scaled_height; y++)
838 for (x = 0; x < spu->scaled_width; x++)
839 scale_image(x, y, table_x, table_y, spu);
840 free(table_x);
841 free(table_y);
842 break;
843 case 0:
844 /* no antialiasing */
845 for (y = 0; y < spu->scaled_height; ++y) {
846 int unscaled_y = y * 0x100 / scaley;
847 int strides = spu->stride * unscaled_y;
848 int scaled_strides = spu->scaled_stride * y;
849 for (x = 0; x < spu->scaled_width; ++x) {
850 int unscaled_x = x * 0x100 / scalex;
851 spu->scaled_image[scaled_strides + x] = spu->image[strides + unscaled_x];
852 spu->scaled_aimage[scaled_strides + x] = spu->aimage[strides + unscaled_x];
855 break;
856 case 1:
858 /* Intermediate antialiasing. */
859 for (y = 0; y < spu->scaled_height; ++y) {
860 const unsigned int unscaled_top = y * spu->orig_frame_height / dys;
861 unsigned int unscaled_bottom = (y + 1) * spu->orig_frame_height / dys;
862 if (unscaled_bottom >= spu->height)
863 unscaled_bottom = spu->height - 1;
864 for (x = 0; x < spu->scaled_width; ++x) {
865 const unsigned int unscaled_left = x * spu->orig_frame_width / dxs;
866 unsigned int unscaled_right = (x + 1) * spu->orig_frame_width / dxs;
867 unsigned int color = 0;
868 unsigned int alpha = 0;
869 unsigned int walkx, walky;
870 unsigned int base, tmp;
871 if (unscaled_right >= spu->width)
872 unscaled_right = spu->width - 1;
873 for (walky = unscaled_top; walky <= unscaled_bottom; ++walky)
874 for (walkx = unscaled_left; walkx <= unscaled_right; ++walkx) {
875 base = walky * spu->stride + walkx;
876 tmp = canon_alpha(spu->aimage[base]);
877 alpha += tmp;
878 color += tmp * spu->image[base];
880 base = y * spu->scaled_stride + x;
881 spu->scaled_image[base] = alpha ? color / alpha : 0;
882 spu->scaled_aimage[base] =
883 alpha * (1 + unscaled_bottom - unscaled_top) * (1 + unscaled_right - unscaled_left);
884 /* spu->scaled_aimage[base] =
885 alpha * dxs * dys / spu->orig_frame_width / spu->orig_frame_height; */
886 if (spu->scaled_aimage[base]) {
887 spu->scaled_aimage[base] = 256 - spu->scaled_aimage[base];
888 if (spu->scaled_aimage[base] + spu->scaled_image[base] > 255)
889 spu->scaled_image[base] = 256 - spu->scaled_aimage[base];
894 break;
895 case 2:
897 /* Best antialiasing. Very slow. */
898 /* Any pixel (x, y) represents pixels from the original
899 rectangular region comprised between the columns
900 unscaled_y and unscaled_y + 0x100 / scaley and the rows
901 unscaled_x and unscaled_x + 0x100 / scalex
903 The original rectangular region that the scaled pixel
904 represents is cut in 9 rectangular areas like this:
906 +---+-----------------+---+
907 | 1 | 2 | 3 |
908 +---+-----------------+---+
909 | | | |
910 | 4 | 5 | 6 |
911 | | | |
912 +---+-----------------+---+
913 | 7 | 8 | 9 |
914 +---+-----------------+---+
916 The width of the left column is at most one pixel and
917 it is never null and its right column is at a pixel
918 boundary. The height of the top row is at most one
919 pixel it is never null and its bottom row is at a
920 pixel boundary. The width and height of region 5 are
921 integral values. The width of the right column is
922 what remains and is less than one pixel. The height
923 of the bottom row is what remains and is less than
924 one pixel.
926 The row above 1, 2, 3 is unscaled_y. The row between
927 1, 2, 3 and 4, 5, 6 is top_low_row. The row between 4,
928 5, 6 and 7, 8, 9 is (unsigned int)unscaled_y_bottom.
929 The row beneath 7, 8, 9 is unscaled_y_bottom.
931 The column left of 1, 4, 7 is unscaled_x. The column
932 between 1, 4, 7 and 2, 5, 8 is left_right_column. The
933 column between 2, 5, 8 and 3, 6, 9 is (unsigned
934 int)unscaled_x_right. The column right of 3, 6, 9 is
935 unscaled_x_right. */
936 const double inv_scalex = (double) 0x100 / scalex;
937 const double inv_scaley = (double) 0x100 / scaley;
938 for (y = 0; y < spu->scaled_height; ++y) {
939 const double unscaled_y = y * inv_scaley;
940 const double unscaled_y_bottom = unscaled_y + inv_scaley;
941 const unsigned int top_low_row = MIN(unscaled_y_bottom, unscaled_y + 1.0);
942 const double top = top_low_row - unscaled_y;
943 const unsigned int height = unscaled_y_bottom > top_low_row
944 ? (unsigned int) unscaled_y_bottom - top_low_row
945 : 0;
946 const double bottom = unscaled_y_bottom > top_low_row
947 ? unscaled_y_bottom - floor(unscaled_y_bottom)
948 : 0.0;
949 for (x = 0; x < spu->scaled_width; ++x) {
950 const double unscaled_x = x * inv_scalex;
951 const double unscaled_x_right = unscaled_x + inv_scalex;
952 const unsigned int left_right_column = MIN(unscaled_x_right, unscaled_x + 1.0);
953 const double left = left_right_column - unscaled_x;
954 const unsigned int width = unscaled_x_right > left_right_column
955 ? (unsigned int) unscaled_x_right - left_right_column
956 : 0;
957 const double right = unscaled_x_right > left_right_column
958 ? unscaled_x_right - floor(unscaled_x_right)
959 : 0.0;
960 double color = 0.0;
961 double alpha = 0.0;
962 double tmp;
963 unsigned int base;
964 /* Now use these informations to compute a good alpha,
965 and lightness. The sum is on each of the 9
966 region's surface and alpha and lightness.
968 transformed alpha = sum(surface * alpha) / sum(surface)
969 transformed color = sum(surface * alpha * color) / sum(surface * alpha)
971 /* 1: top left part */
972 base = spu->stride * (unsigned int) unscaled_y;
973 tmp = left * top * canon_alpha(spu->aimage[base + (unsigned int) unscaled_x]);
974 alpha += tmp;
975 color += tmp * spu->image[base + (unsigned int) unscaled_x];
976 /* 2: top center part */
977 if (width > 0) {
978 unsigned int walkx;
979 for (walkx = left_right_column; walkx < (unsigned int) unscaled_x_right; ++walkx) {
980 base = spu->stride * (unsigned int) unscaled_y + walkx;
981 tmp = /* 1.0 * */ top * canon_alpha(spu->aimage[base]);
982 alpha += tmp;
983 color += tmp * spu->image[base];
986 /* 3: top right part */
987 if (right > 0.0) {
988 base = spu->stride * (unsigned int) unscaled_y + (unsigned int) unscaled_x_right;
989 tmp = right * top * canon_alpha(spu->aimage[base]);
990 alpha += tmp;
991 color += tmp * spu->image[base];
993 /* 4: center left part */
994 if (height > 0) {
995 unsigned int walky;
996 for (walky = top_low_row; walky < (unsigned int) unscaled_y_bottom; ++walky) {
997 base = spu->stride * walky + (unsigned int) unscaled_x;
998 tmp = left /* * 1.0 */ * canon_alpha(spu->aimage[base]);
999 alpha += tmp;
1000 color += tmp * spu->image[base];
1003 /* 5: center part */
1004 if (width > 0 && height > 0) {
1005 unsigned int walky;
1006 for (walky = top_low_row; walky < (unsigned int) unscaled_y_bottom; ++walky) {
1007 unsigned int walkx;
1008 base = spu->stride * walky;
1009 for (walkx = left_right_column; walkx < (unsigned int) unscaled_x_right; ++walkx) {
1010 tmp = /* 1.0 * 1.0 * */ canon_alpha(spu->aimage[base + walkx]);
1011 alpha += tmp;
1012 color += tmp * spu->image[base + walkx];
1016 /* 6: center right part */
1017 if (right > 0.0 && height > 0) {
1018 unsigned int walky;
1019 for (walky = top_low_row; walky < (unsigned int) unscaled_y_bottom; ++walky) {
1020 base = spu->stride * walky + (unsigned int) unscaled_x_right;
1021 tmp = right /* * 1.0 */ * canon_alpha(spu->aimage[base]);
1022 alpha += tmp;
1023 color += tmp * spu->image[base];
1026 /* 7: bottom left part */
1027 if (bottom > 0.0) {
1028 base = spu->stride * (unsigned int) unscaled_y_bottom + (unsigned int) unscaled_x;
1029 tmp = left * bottom * canon_alpha(spu->aimage[base]);
1030 alpha += tmp;
1031 color += tmp * spu->image[base];
1033 /* 8: bottom center part */
1034 if (width > 0 && bottom > 0.0) {
1035 unsigned int walkx;
1036 base = spu->stride * (unsigned int) unscaled_y_bottom;
1037 for (walkx = left_right_column; walkx < (unsigned int) unscaled_x_right; ++walkx) {
1038 tmp = /* 1.0 * */ bottom * canon_alpha(spu->aimage[base + walkx]);
1039 alpha += tmp;
1040 color += tmp * spu->image[base + walkx];
1043 /* 9: bottom right part */
1044 if (right > 0.0 && bottom > 0.0) {
1045 base = spu->stride * (unsigned int) unscaled_y_bottom + (unsigned int) unscaled_x_right;
1046 tmp = right * bottom * canon_alpha(spu->aimage[base]);
1047 alpha += tmp;
1048 color += tmp * spu->image[base];
1050 /* Finally mix these transparency and brightness information suitably */
1051 base = spu->scaled_stride * y + x;
1052 spu->scaled_image[base] = alpha > 0 ? color / alpha : 0;
1053 spu->scaled_aimage[base] = alpha * scalex * scaley / 0x10000;
1054 if (spu->scaled_aimage[base]) {
1055 spu->scaled_aimage[base] = 256 - spu->scaled_aimage[base];
1056 if (spu->scaled_aimage[base] + spu->scaled_image[base] > 255)
1057 spu->scaled_image[base] = 256 - spu->scaled_aimage[base];
1063 nothing_to_do:
1064 spu->scaled_frame_width = dxs;
1065 spu->scaled_frame_height = dys;
1068 if (spu->scaled_image){
1069 switch (spu_alignment) {
1070 case 0:
1071 spu->scaled_start_row = dys*sub_pos/100;
1072 if (spu->scaled_start_row + spu->scaled_height > dys)
1073 spu->scaled_start_row = dys - spu->scaled_height;
1074 break;
1075 case 1:
1076 spu->scaled_start_row = dys*sub_pos/100 - spu->scaled_height/2;
1077 if (sub_pos < 50) {
1078 if (spu->scaled_start_row < 0) spu->scaled_start_row = 0;
1079 } else {
1080 if (spu->scaled_start_row + spu->scaled_height > dys)
1081 spu->scaled_start_row = dys - spu->scaled_height;
1083 break;
1084 case 2:
1085 spu->scaled_start_row = dys*sub_pos/100 - spu->scaled_height;
1086 if (spu->scaled_start_row < 0) spu->scaled_start_row = 0;
1087 break;
1089 draw_alpha(spu->scaled_start_col, spu->scaled_start_row, spu->scaled_width, spu->scaled_height,
1090 spu->scaled_image, spu->scaled_aimage, spu->scaled_stride);
1091 spu->spu_changed = 0;
1095 else
1097 mp_msg(MSGT_SPUDEC,MSGL_DBG2,"SPU not displayed: start_pts=%d end_pts=%d now_pts=%d\n",
1098 spu->start_pts, spu->end_pts, spu->now_pts);
1102 void spudec_update_palette(void * this, unsigned int *palette)
1104 spudec_handle_t *spu = (spudec_handle_t *) this;
1105 if (spu && palette) {
1106 memcpy(spu->global_palette, palette, sizeof(spu->global_palette));
1107 if(spu->hw_spu)
1108 spu->hw_spu->control(VOCTRL_SET_SPU_PALETTE,spu->global_palette);
1112 void spudec_set_font_factor(void * this, double factor)
1114 spudec_handle_t *spu = (spudec_handle_t *) this;
1115 spu->font_start_level = (int)(0xF0-(0xE0*factor));
1118 void *spudec_new_scaled(unsigned int *palette, unsigned int frame_width, unsigned int frame_height)
1120 return spudec_new_scaled_vobsub(palette, NULL, 0, frame_width, frame_height);
1123 /* get palette custom color, width, height from .idx file */
1124 void *spudec_new_scaled_vobsub(unsigned int *palette, unsigned int *cuspal, unsigned int custom, unsigned int frame_width, unsigned int frame_height)
1126 spudec_handle_t *this = calloc(1, sizeof(spudec_handle_t));
1127 if (this){
1128 //(fprintf(stderr,"VobSub Custom Palette: %d,%d,%d,%d", this->cuspal[0], this->cuspal[1], this->cuspal[2],this->cuspal[3]);
1129 this->packet = NULL;
1130 this->image = NULL;
1131 this->scaled_image = NULL;
1132 /* XXX Although the video frame is some size, the SPU frame is
1133 always maximum size i.e. 720 wide and 576 or 480 high */
1134 this->orig_frame_width = 720;
1135 this->orig_frame_height = (frame_height == 480 || frame_height == 240) ? 480 : 576;
1136 this->custom = custom;
1137 // set up palette:
1138 this->auto_palette = 1;
1139 if (palette){
1140 memcpy(this->global_palette, palette, sizeof(this->global_palette));
1141 this->auto_palette = 0;
1143 this->custom = custom;
1144 if (custom && cuspal) {
1145 memcpy(this->cuspal, cuspal, sizeof(this->cuspal));
1146 this->auto_palette = 0;
1148 // forced subtitles default: show all subtitles
1149 this->forced_subs_only=0;
1150 this->is_forced_sub=0;
1152 else
1153 mp_msg(MSGT_SPUDEC,MSGL_FATAL, "FATAL: spudec_init: calloc");
1154 return this;
1157 void *spudec_new(unsigned int *palette)
1159 return spudec_new_scaled(palette, 0, 0);
1162 void spudec_free(void *this)
1164 spudec_handle_t *spu = (spudec_handle_t*)this;
1165 if (spu) {
1166 while (spu->queue_head)
1167 spudec_free_packet(spudec_dequeue_packet(spu));
1168 if (spu->packet)
1169 free(spu->packet);
1170 if (spu->scaled_image)
1171 free(spu->scaled_image);
1172 if (spu->image)
1173 free(spu->image);
1174 free(spu);
1178 void spudec_set_hw_spu(void *this, vo_functions_t *hw_spu)
1180 spudec_handle_t *spu = (spudec_handle_t*)this;
1181 if (!spu)
1182 return;
1183 spu->hw_spu = hw_spu;
1184 hw_spu->control(VOCTRL_SET_SPU_PALETTE,spu->global_palette);