add some docu about when and how to call geometry
[mplayer/glamo.git] / spudec.c
blob8c86149ed30080e5450ab1aae59c047b8ed7e22a
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 } spudec_handle_t;
94 static void spudec_queue_packet(spudec_handle_t *this, packet_t *packet)
96 if (this->queue_head == NULL)
97 this->queue_head = packet;
98 else
99 this->queue_tail->next = packet;
100 this->queue_tail = packet;
103 static packet_t *spudec_dequeue_packet(spudec_handle_t *this)
105 packet_t *retval = this->queue_head;
107 this->queue_head = retval->next;
108 if (this->queue_head == NULL)
109 this->queue_tail = NULL;
111 return retval;
114 static void spudec_free_packet(packet_t *packet)
116 if (packet->packet != NULL)
117 free(packet->packet);
118 free(packet);
121 static inline unsigned int get_be16(const unsigned char *p)
123 return (p[0] << 8) + p[1];
126 static inline unsigned int get_be24(const unsigned char *p)
128 return (get_be16(p) << 8) + p[2];
131 static void next_line(packet_t *packet)
133 if (packet->current_nibble[packet->deinterlace_oddness] % 2)
134 packet->current_nibble[packet->deinterlace_oddness]++;
135 packet->deinterlace_oddness = (packet->deinterlace_oddness + 1) % 2;
138 static inline unsigned char get_nibble(packet_t *packet)
140 unsigned char nib;
141 unsigned int *nibblep = packet->current_nibble + packet->deinterlace_oddness;
142 if (*nibblep / 2 >= packet->control_start) {
143 mp_msg(MSGT_SPUDEC,MSGL_WARN, "SPUdec: ERROR: get_nibble past end of packet\n");
144 return 0;
146 nib = packet->packet[*nibblep / 2];
147 if (*nibblep % 2)
148 nib &= 0xf;
149 else
150 nib >>= 4;
151 ++*nibblep;
152 return nib;
155 static inline int mkalpha(int i)
157 /* In mplayer's alpha planes, 0 is transparent, then 1 is nearly
158 opaque upto 255 which is transparent */
159 switch (i) {
160 case 0xf:
161 return 1;
162 case 0:
163 return 0;
164 default:
165 return (0xf - i) << 4;
169 /* Cut the sub to visible part */
170 static inline void spudec_cut_image(spudec_handle_t *this)
172 unsigned int fy, ly;
173 unsigned int first_y, last_y;
174 unsigned char *image;
175 unsigned char *aimage;
177 if (this->stride == 0 || this->height == 0) {
178 return;
181 for (fy = 0; fy < this->image_size && !this->aimage[fy]; fy++);
182 for (ly = this->stride * this->height-1; ly && !this->aimage[ly]; ly--);
183 first_y = fy / this->stride;
184 last_y = ly / this->stride;
185 //printf("first_y: %d, last_y: %d\n", first_y, last_y);
186 this->start_row += first_y;
188 // Some subtitles trigger this condition
189 if (last_y + 1 > first_y ) {
190 this->height = last_y - first_y +1;
191 } else {
192 this->height = 0;
193 this->image_size = 0;
194 return;
197 // printf("new h %d new start %d (sz %d st %d)---\n\n", this->height, this->start_row, this->image_size, this->stride);
199 image = malloc(2 * this->stride * this->height);
200 if(image){
201 this->image_size = this->stride * this->height;
202 aimage = image + this->image_size;
203 memcpy(image, this->image + this->stride * first_y, this->image_size);
204 memcpy(aimage, this->aimage + this->stride * first_y, this->image_size);
205 free(this->image);
206 this->image = image;
207 this->aimage = aimage;
208 } else {
209 mp_msg(MSGT_SPUDEC, MSGL_FATAL, "Fatal: update_spu: malloc requested %d bytes\n", 2 * this->stride * this->height);
213 static void spudec_process_data(spudec_handle_t *this, packet_t *packet)
215 unsigned int cmap[4], alpha[4];
216 unsigned int i, x, y;
218 this->scaled_frame_width = 0;
219 this->scaled_frame_height = 0;
220 this->start_col = packet->start_col;
221 this->end_col = packet->end_col;
222 this->start_row = packet->start_row;
223 this->end_row = packet->end_row;
224 this->height = packet->height;
225 this->width = packet->width;
226 this->stride = packet->stride;
227 for (i = 0; i < 4; ++i) {
228 alpha[i] = mkalpha(packet->alpha[i]);
229 if (alpha[i] == 0)
230 cmap[i] = 0;
231 else if (this->custom){
232 cmap[i] = ((this->cuspal[i] >> 16) & 0xff);
233 if (cmap[i] + alpha[i] > 255)
234 cmap[i] = 256 - alpha[i];
236 else {
237 cmap[i] = ((this->global_palette[packet->palette[i]] >> 16) & 0xff);
238 if (cmap[i] + alpha[i] > 255)
239 cmap[i] = 256 - alpha[i];
243 if (this->image_size < this->stride * this->height) {
244 if (this->image != NULL) {
245 free(this->image);
246 this->image_size = 0;
248 this->image = malloc(2 * this->stride * this->height);
249 if (this->image) {
250 this->image_size = this->stride * this->height;
251 this->aimage = this->image + this->image_size;
254 if (this->image == NULL)
255 return;
257 /* Kludge: draw_alpha needs width multiple of 8. */
258 if (this->width < this->stride)
259 for (y = 0; y < this->height; ++y) {
260 memset(this->aimage + y * this->stride + this->width, 0, this->stride - this->width);
261 /* FIXME: Why is this one needed? */
262 memset(this->image + y * this->stride + this->width, 0, this->stride - this->width);
265 i = packet->current_nibble[1];
266 x = 0;
267 y = 0;
268 while (packet->current_nibble[0] < i
269 && packet->current_nibble[1] / 2 < packet->control_start
270 && y < this->height) {
271 unsigned int len, color;
272 unsigned int rle = 0;
273 rle = get_nibble(packet);
274 if (rle < 0x04) {
275 rle = (rle << 4) | get_nibble(packet);
276 if (rle < 0x10) {
277 rle = (rle << 4) | get_nibble(packet);
278 if (rle < 0x040) {
279 rle = (rle << 4) | get_nibble(packet);
280 if (rle < 0x0004)
281 rle |= ((this->width - x) << 2);
285 color = 3 - (rle & 0x3);
286 len = rle >> 2;
287 if (len > this->width - x || len == 0)
288 len = this->width - x;
289 /* FIXME have to use palette and alpha map*/
290 memset(this->image + y * this->stride + x, cmap[color], len);
291 memset(this->aimage + y * this->stride + x, alpha[color], len);
292 x += len;
293 if (x >= this->width) {
294 next_line(packet);
295 x = 0;
296 ++y;
299 spudec_cut_image(this);
304 This function tries to create a usable palette.
305 Is searchs how many non-transparent colors are used and assigns different
306 gray scale values to each color.
307 I tested it with four streams and even got something readable. Half of the
308 times I got black characters with white around and half the reverse.
310 static void compute_palette(spudec_handle_t *this, packet_t *packet)
312 int used[16],i,cused,start,step,color;
314 memset(used, 0, sizeof(used));
315 for (i=0; i<4; i++)
316 if (packet->alpha[i]) /* !Transparent? */
317 used[packet->palette[i]] = 1;
318 for (cused=0, i=0; i<16; i++)
319 if (used[i]) cused++;
320 if (!cused) return;
321 if (cused == 1) {
322 start = 0x80;
323 step = 0;
324 } else {
325 start = this->font_start_level;
326 step = (0xF0-this->font_start_level)/(cused-1);
328 memset(used, 0, sizeof(used));
329 for (i=0; i<4; i++) {
330 color = packet->palette[i];
331 if (packet->alpha[i] && !used[color]) { /* not assigned? */
332 used[color] = 1;
333 this->global_palette[color] = start<<16;
334 start += step;
339 static void spudec_process_control(spudec_handle_t *this, unsigned int pts100)
341 int a,b; /* Temporary vars */
342 unsigned int date, type;
343 unsigned int off;
344 unsigned int start_off = 0;
345 unsigned int next_off;
346 unsigned int start_pts;
347 unsigned int end_pts;
348 unsigned int current_nibble[2];
349 unsigned int control_start;
350 unsigned int display = 0;
351 unsigned int start_col = 0;
352 unsigned int end_col = 0;
353 unsigned int start_row = 0;
354 unsigned int end_row = 0;
355 unsigned int width = 0;
356 unsigned int height = 0;
357 unsigned int stride = 0;
359 control_start = get_be16(this->packet + 2);
360 next_off = control_start;
361 while (start_off != next_off) {
362 start_off = next_off;
363 date = get_be16(this->packet + start_off) * 1024;
364 next_off = get_be16(this->packet + start_off + 2);
365 mp_msg(MSGT_SPUDEC,MSGL_DBG2, "date=%d\n", date);
366 off = start_off + 4;
367 for (type = this->packet[off++]; type != 0xff; type = this->packet[off++]) {
368 mp_msg(MSGT_SPUDEC,MSGL_DBG2, "cmd=%d ",type);
369 switch(type) {
370 case 0x00:
371 /* Menu ID, 1 byte */
372 mp_msg(MSGT_SPUDEC,MSGL_DBG2,"Menu ID\n");
373 /* shouldn't a Menu ID type force display start? */
374 //this->start_pts = pts100 + date;
375 //this->end_pts = UINT_MAX;
376 break;
377 case 0x01:
378 /* Start display */
379 mp_msg(MSGT_SPUDEC,MSGL_DBG2,"Start display!\n");
380 start_pts = pts100 + date;
381 end_pts = UINT_MAX;
382 display = 1;
383 break;
384 case 0x02:
385 /* Stop display */
386 mp_msg(MSGT_SPUDEC,MSGL_DBG2,"Stop display!\n");
387 end_pts = pts100 + date;
388 break;
389 case 0x03:
390 /* Palette */
391 this->palette[0] = this->packet[off] >> 4;
392 this->palette[1] = this->packet[off] & 0xf;
393 this->palette[2] = this->packet[off + 1] >> 4;
394 this->palette[3] = this->packet[off + 1] & 0xf;
395 mp_msg(MSGT_SPUDEC,MSGL_DBG2,"Palette %d, %d, %d, %d\n",
396 this->palette[0], this->palette[1], this->palette[2], this->palette[3]);
397 off+=2;
398 break;
399 case 0x04:
400 /* Alpha */
401 this->alpha[0] = this->packet[off] >> 4;
402 this->alpha[1] = this->packet[off] & 0xf;
403 this->alpha[2] = this->packet[off + 1] >> 4;
404 this->alpha[3] = this->packet[off + 1] & 0xf;
405 mp_msg(MSGT_SPUDEC,MSGL_DBG2,"Alpha %d, %d, %d, %d\n",
406 this->alpha[0], this->alpha[1], this->alpha[2], this->alpha[3]);
407 off+=2;
408 break;
409 case 0x05:
410 /* Co-ords */
411 a = get_be24(this->packet + off);
412 b = get_be24(this->packet + off + 3);
413 start_col = a >> 12;
414 end_col = a & 0xfff;
415 width = (end_col < start_col) ? 0 : end_col - start_col + 1;
416 stride = (width + 7) & ~7; /* Kludge: draw_alpha needs width multiple of 8 */
417 start_row = b >> 12;
418 end_row = b & 0xfff;
419 height = (end_row < start_row) ? 0 : end_row - start_row /* + 1 */;
420 mp_msg(MSGT_SPUDEC,MSGL_DBG2,"Coords col: %d - %d row: %d - %d (%dx%d)\n",
421 start_col, end_col, start_row, end_row,
422 width, height);
423 off+=6;
424 break;
425 case 0x06:
426 /* Graphic lines */
427 current_nibble[0] = 2 * get_be16(this->packet + off);
428 current_nibble[1] = 2 * get_be16(this->packet + off + 2);
429 mp_msg(MSGT_SPUDEC,MSGL_DBG2,"Graphic offset 1: %d offset 2: %d\n",
430 current_nibble[0] / 2, current_nibble[1] / 2);
431 off+=4;
432 break;
433 case 0xff:
434 /* All done, bye-bye */
435 mp_msg(MSGT_SPUDEC,MSGL_DBG2,"Done!\n");
436 return;
437 // break;
438 default:
439 mp_msg(MSGT_SPUDEC,MSGL_WARN,"spudec: Error determining control type 0x%02x. Skipping %d bytes.\n",
440 type, next_off - off);
441 goto next_control;
444 next_control:
445 if (display) {
446 packet_t *packet = calloc(1, sizeof(packet_t));
447 int i;
448 packet->start_pts = start_pts;
449 if (end_pts == UINT_MAX && start_off != next_off) {
450 start_pts = pts100 + get_be16(this->packet + next_off) * 1024;
451 packet->end_pts = start_pts - 1;
452 } else packet->end_pts = end_pts;
453 packet->current_nibble[0] = current_nibble[0];
454 packet->current_nibble[1] = current_nibble[1];
455 packet->start_row = start_row;
456 packet->end_row = end_row;
457 packet->start_col = start_col;
458 packet->end_col = end_col;
459 packet->width = width;
460 packet->height = height;
461 packet->stride = stride;
462 packet->control_start = control_start;
463 for (i=0; i<4; i++) {
464 packet->alpha[i] = this->alpha[i];
465 packet->palette[i] = this->palette[i];
467 packet->packet = malloc(this->packet_size);
468 memcpy(packet->packet, this->packet, this->packet_size);
469 spudec_queue_packet(this, packet);
474 static void spudec_decode(spudec_handle_t *this, unsigned int pts100)
476 if(this->hw_spu) {
477 static vo_mpegpes_t packet = { NULL, 0, 0x20, 0 };
478 static vo_mpegpes_t *pkg=&packet;
479 packet.data = this->packet;
480 packet.size = this->packet_size;
481 packet.timestamp = pts100;
482 this->hw_spu->draw_frame((uint8_t**)&pkg);
483 } else
484 spudec_process_control(this, pts100);
487 int spudec_changed(void * this)
489 spudec_handle_t * spu = (spudec_handle_t*)this;
490 return (spu->spu_changed || spu->now_pts > spu->end_pts);
493 void spudec_assemble(void *this, unsigned char *packet, unsigned int len, unsigned int pts100)
495 spudec_handle_t *spu = (spudec_handle_t*)this;
496 // spudec_heartbeat(this, pts100);
497 if (len < 2) {
498 mp_msg(MSGT_SPUDEC,MSGL_WARN,"SPUasm: packet too short\n");
499 return;
501 if ((spu->packet_pts + 10000) < pts100) {
502 // [cb] too long since last fragment: force new packet
503 spu->packet_offset = 0;
505 spu->packet_pts = pts100;
506 if (spu->packet_offset == 0) {
507 unsigned int len2 = get_be16(packet);
508 // Start new fragment
509 if (spu->packet_reserve < len2) {
510 if (spu->packet != NULL)
511 free(spu->packet);
512 spu->packet = malloc(len2);
513 spu->packet_reserve = spu->packet != NULL ? len2 : 0;
515 if (spu->packet != NULL) {
516 spu->packet_size = len2;
517 if (len > len2) {
518 mp_msg(MSGT_SPUDEC,MSGL_WARN,"SPUasm: invalid frag len / len2: %d / %d \n", len, len2);
519 return;
521 memcpy(spu->packet, packet, len);
522 spu->packet_offset = len;
523 spu->packet_pts = pts100;
525 } else {
526 // Continue current fragment
527 if (spu->packet_size < spu->packet_offset + len){
528 mp_msg(MSGT_SPUDEC,MSGL_WARN,"SPUasm: invalid fragment\n");
529 spu->packet_size = spu->packet_offset = 0;
530 return;
531 } else {
532 memcpy(spu->packet + spu->packet_offset, packet, len);
533 spu->packet_offset += len;
536 #if 1
537 // check if we have a complete packet (unfortunatelly packet_size is bad
538 // for some disks)
539 // [cb] packet_size is padded to be even -> may be one byte too long
540 if ((spu->packet_offset == spu->packet_size) ||
541 ((spu->packet_offset + 1) == spu->packet_size)){
542 unsigned int x=0,y;
543 while(x+4<=spu->packet_offset){
544 y=get_be16(spu->packet+x+2); // next control pointer
545 mp_msg(MSGT_SPUDEC,MSGL_DBG2,"SPUtest: x=%d y=%d off=%d size=%d\n",x,y,spu->packet_offset,spu->packet_size);
546 if(x>=4 && x==y){ // if it points to self - we're done!
547 // we got it!
548 mp_msg(MSGT_SPUDEC,MSGL_DBG2,"SPUgot: off=%d size=%d \n",spu->packet_offset,spu->packet_size);
549 spudec_decode(spu, pts100);
550 spu->packet_offset = 0;
551 break;
553 if(y<=x || y>=spu->packet_size){ // invalid?
554 mp_msg(MSGT_SPUDEC,MSGL_WARN,"SPUtest: broken packet!!!!! y=%d < x=%d\n",y,x);
555 spu->packet_size = spu->packet_offset = 0;
556 break;
558 x=y;
560 // [cb] packet is done; start new packet
561 spu->packet_offset = 0;
563 #else
564 if (spu->packet_offset == spu->packet_size) {
565 spudec_decode(spu, pts100);
566 spu->packet_offset = 0;
568 #endif
571 void spudec_reset(void *this) // called after seek
573 spudec_handle_t *spu = (spudec_handle_t*)this;
574 while (spu->queue_head)
575 spudec_free_packet(spudec_dequeue_packet(spu));
576 spu->now_pts = 0;
577 spu->end_pts = 0;
578 spu->packet_size = spu->packet_offset = 0;
581 void spudec_heartbeat(void *this, unsigned int pts100)
583 spudec_handle_t *spu = (spudec_handle_t*) this;
584 spu->now_pts = pts100;
586 while (spu->queue_head != NULL && pts100 >= spu->queue_head->start_pts) {
587 packet_t *packet = spudec_dequeue_packet(spu);
588 spu->start_pts = packet->start_pts;
589 spu->end_pts = packet->end_pts;
590 if (spu->auto_palette)
591 compute_palette(spu, packet);
592 spudec_process_data(spu, packet);
593 spudec_free_packet(packet);
594 spu->spu_changed = 1;
598 int spudec_visible(void *this){
599 spudec_handle_t *spu = (spudec_handle_t *)this;
600 int ret=(spu->start_pts <= spu->now_pts &&
601 spu->now_pts < spu->end_pts &&
602 spu->height > 0);
603 // printf("spu visible: %d \n",ret);
604 return ret;
607 void spudec_draw(void *this, void (*draw_alpha)(int x0,int y0, int w,int h, unsigned char* src, unsigned char *srca, int stride))
609 spudec_handle_t *spu = (spudec_handle_t *)this;
610 if (spu->start_pts <= spu->now_pts && spu->now_pts < spu->end_pts && spu->image)
612 draw_alpha(spu->start_col, spu->start_row, spu->width, spu->height,
613 spu->image, spu->aimage, spu->stride);
614 spu->spu_changed = 0;
618 /* calc the bbox for spudec subs */
619 void spudec_calc_bbox(void *me, unsigned int dxs, unsigned int dys, unsigned int* bbox)
621 spudec_handle_t *spu;
622 spu = (spudec_handle_t *)me;
623 if (spu->orig_frame_width == 0 || spu->orig_frame_height == 0
624 || (spu->orig_frame_width == dxs && spu->orig_frame_height == dys)) {
625 bbox[0] = spu->start_col;
626 bbox[1] = spu->start_col + spu->width;
627 bbox[2] = spu->start_row;
628 bbox[3] = spu->start_row + spu->height;
630 else if (spu->scaled_frame_width != dxs || spu->scaled_frame_height != dys) {
631 unsigned int scalex = 0x100 * dxs / spu->orig_frame_width;
632 unsigned int scaley = 0x100 * dys / spu->orig_frame_height;
633 bbox[0] = spu->start_col * scalex / 0x100;
634 bbox[1] = spu->start_col * scalex / 0x100 + spu->width * scalex / 0x100;
635 switch (spu_alignment) {
636 case 0:
637 bbox[3] = dys*sub_pos/100 + spu->height * scaley / 0x100;
638 if (bbox[3] > dys) bbox[3] = dys;
639 bbox[2] = bbox[3] - spu->height * scaley / 0x100;
640 break;
641 case 1:
642 if (sub_pos < 50) {
643 bbox[2] = dys*sub_pos/100 - spu->height * scaley / 0x200;
644 if (bbox[2] < 0) bbox[2] = 0;
645 bbox[3] = bbox[2] + spu->height;
646 } else {
647 bbox[3] = dys*sub_pos/100 + spu->height * scaley / 0x200;
648 if (bbox[3] > dys) bbox[3] = dys;
649 bbox[2] = bbox[3] - spu->height * scaley / 0x100;
651 break;
652 case 2:
653 bbox[2] = dys*sub_pos/100 - spu->height * scaley / 0x100;
654 if (bbox[2] < 0) bbox[2] = 0;
655 bbox[3] = bbox[2] + spu->height;
656 break;
657 default: /* -1 */
658 bbox[2] = spu->start_row * scaley / 0x100;
659 bbox[3] = spu->start_row * scaley / 0x100 + spu->height * scaley / 0x100;
660 break;
664 /* transform mplayer's alpha value into an opacity value that is linear */
665 static inline int canon_alpha(int alpha)
667 return alpha ? 256 - alpha : 0;
670 typedef struct {
671 unsigned position;
672 unsigned left_up;
673 unsigned right_down;
674 }scale_pixel;
677 static void scale_table(unsigned int start_src, unsigned int start_tar, unsigned int end_src, unsigned int end_tar, scale_pixel * table)
679 unsigned int t;
680 unsigned int delta_src = end_src - start_src;
681 unsigned int delta_tar = end_tar - start_tar;
682 int src = 0;
683 int src_step;
684 if (delta_src == 0 || delta_tar == 0) {
685 return;
687 src_step = (delta_src << 16) / delta_tar >>1;
688 for (t = 0; t<=delta_tar; src += (src_step << 1), t++){
689 table[t].position= MIN(src >> 16, end_src - 1);
690 table[t].right_down = src & 0xffff;
691 table[t].left_up = 0x10000 - table[t].right_down;
695 /* bilinear scale, similar to vobsub's code */
696 static void scale_image(int x, int y, scale_pixel* table_x, scale_pixel* table_y, spudec_handle_t * spu)
698 int alpha[4];
699 int color[4];
700 unsigned int scale[4];
701 int base = table_y[y].position * spu->stride + table_x[x].position;
702 int scaled = y * spu->scaled_stride + x;
703 alpha[0] = canon_alpha(spu->aimage[base]);
704 alpha[1] = canon_alpha(spu->aimage[base + 1]);
705 alpha[2] = canon_alpha(spu->aimage[base + spu->stride]);
706 alpha[3] = canon_alpha(spu->aimage[base + spu->stride + 1]);
707 color[0] = spu->image[base];
708 color[1] = spu->image[base + 1];
709 color[2] = spu->image[base + spu->stride];
710 color[3] = spu->image[base + spu->stride + 1];
711 scale[0] = (table_x[x].left_up * table_y[y].left_up >> 16) * alpha[0];
712 scale[1] = (table_x[x].right_down * table_y[y].left_up >>16) * alpha[1];
713 scale[2] = (table_x[x].left_up * table_y[y].right_down >> 16) * alpha[2];
714 scale[3] = (table_x[x].right_down * table_y[y].right_down >> 16) * alpha[3];
715 spu->scaled_image[scaled] = (color[0] * scale[0] + color[1] * scale[1] + color[2] * scale[2] + color[3] * scale[3])>>24;
716 spu->scaled_aimage[scaled] = (scale[0] + scale[1] + scale[2] + scale[3]) >> 16;
717 if (spu->scaled_aimage[scaled]){
718 spu->scaled_aimage[scaled] = 256 - spu->scaled_aimage[scaled];
719 if(spu->scaled_aimage[scaled] + spu->scaled_image[scaled] > 255)
720 spu->scaled_image[scaled] = 256 - spu->scaled_aimage[scaled];
724 void sws_spu_image(unsigned char *d1, unsigned char *d2, int dw, int dh, int ds,
725 unsigned char *s1, unsigned char *s2, int sw, int sh, int ss)
727 struct SwsContext *ctx;
728 static SwsFilter filter;
729 static int firsttime = 1;
730 static float oldvar;
731 int i;
733 if (!firsttime && oldvar != spu_gaussvar) sws_freeVec(filter.lumH);
734 if (firsttime) {
735 filter.lumH = filter.lumV =
736 filter.chrH = filter.chrV = sws_getGaussianVec(spu_gaussvar, 3.0);
737 sws_normalizeVec(filter.lumH, 1.0);
738 firsttime = 0;
739 oldvar = spu_gaussvar;
742 ctx=sws_getContext(sw, sh, IMGFMT_Y800, dw, dh, IMGFMT_Y800, SWS_GAUSS, &filter, NULL);
743 sws_scale(ctx,&s1,&ss,0,sh,&d1,&ds);
744 for (i=ss*sh-1; i>=0; i--) if (!s2[i]) s2[i] = 255; //else s2[i] = 1;
745 sws_scale(ctx,&s2,&ss,0,sh,&d2,&ds);
746 for (i=ds*dh-1; i>=0; i--) if (d2[i]==0) d2[i] = 1; else if (d2[i]==255) d2[i] = 0;
747 sws_freeContext(ctx);
750 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))
752 spudec_handle_t *spu = (spudec_handle_t *)me;
753 scale_pixel *table_x;
754 scale_pixel *table_y;
755 if (spu->start_pts <= spu->now_pts && spu->now_pts < spu->end_pts) {
756 if (!(spu_aamode&16) && (spu->orig_frame_width == 0 || spu->orig_frame_height == 0
757 || (spu->orig_frame_width == dxs && spu->orig_frame_height == dys))) {
758 if (spu->image)
760 draw_alpha(spu->start_col, spu->start_row, spu->width, spu->height,
761 spu->image, spu->aimage, spu->stride);
762 spu->spu_changed = 0;
765 else {
766 if (spu->scaled_frame_width != dxs || spu->scaled_frame_height != dys) { /* Resizing is needed */
767 /* scaled_x = scalex * x / 0x100
768 scaled_y = scaley * y / 0x100
769 order of operations is important because of rounding. */
770 unsigned int scalex = 0x100 * dxs / spu->orig_frame_width;
771 unsigned int scaley = 0x100 * dys / spu->orig_frame_height;
772 spu->scaled_start_col = spu->start_col * scalex / 0x100;
773 spu->scaled_start_row = spu->start_row * scaley / 0x100;
774 spu->scaled_width = spu->width * scalex / 0x100;
775 spu->scaled_height = spu->height * scaley / 0x100;
776 /* Kludge: draw_alpha needs width multiple of 8 */
777 spu->scaled_stride = (spu->scaled_width + 7) & ~7;
778 if (spu->scaled_image_size < spu->scaled_stride * spu->scaled_height) {
779 if (spu->scaled_image) {
780 free(spu->scaled_image);
781 spu->scaled_image_size = 0;
783 spu->scaled_image = malloc(2 * spu->scaled_stride * spu->scaled_height);
784 if (spu->scaled_image) {
785 spu->scaled_image_size = spu->scaled_stride * spu->scaled_height;
786 spu->scaled_aimage = spu->scaled_image + spu->scaled_image_size;
789 if (spu->scaled_image) {
790 unsigned int x, y;
791 /* Kludge: draw_alpha needs width multiple of 8. */
792 if (spu->scaled_width < spu->scaled_stride)
793 for (y = 0; y < spu->scaled_height; ++y) {
794 memset(spu->scaled_aimage + y * spu->scaled_stride + spu->scaled_width, 0,
795 spu->scaled_stride - spu->scaled_width);
796 /* FIXME: Why is this one needed? */
797 memset(spu->scaled_image + y * spu->scaled_stride + spu->scaled_width, 0,
798 spu->scaled_stride - spu->scaled_width);
800 if (spu->scaled_width <= 1 || spu->scaled_height <= 1) {
801 goto nothing_to_do;
803 switch(spu_aamode&15) {
804 case 4:
805 sws_spu_image(spu->scaled_image, spu->scaled_aimage,
806 spu->scaled_width, spu->scaled_height, spu->scaled_stride,
807 spu->image, spu->aimage, spu->width, spu->height, spu->stride);
808 break;
809 case 3:
810 table_x = calloc(spu->scaled_width, sizeof(scale_pixel));
811 table_y = calloc(spu->scaled_height, sizeof(scale_pixel));
812 if (!table_x || !table_y) {
813 mp_msg(MSGT_SPUDEC, MSGL_FATAL, "Fatal: spudec_draw_scaled: calloc failed\n");
815 scale_table(0, 0, spu->width - 1, spu->scaled_width - 1, table_x);
816 scale_table(0, 0, spu->height - 1, spu->scaled_height - 1, table_y);
817 for (y = 0; y < spu->scaled_height; y++)
818 for (x = 0; x < spu->scaled_width; x++)
819 scale_image(x, y, table_x, table_y, spu);
820 free(table_x);
821 free(table_y);
822 break;
823 case 0:
824 /* no antialiasing */
825 for (y = 0; y < spu->scaled_height; ++y) {
826 int unscaled_y = y * 0x100 / scaley;
827 int strides = spu->stride * unscaled_y;
828 int scaled_strides = spu->scaled_stride * y;
829 for (x = 0; x < spu->scaled_width; ++x) {
830 int unscaled_x = x * 0x100 / scalex;
831 spu->scaled_image[scaled_strides + x] = spu->image[strides + unscaled_x];
832 spu->scaled_aimage[scaled_strides + x] = spu->aimage[strides + unscaled_x];
835 break;
836 case 1:
838 /* Intermediate antialiasing. */
839 for (y = 0; y < spu->scaled_height; ++y) {
840 const unsigned int unscaled_top = y * spu->orig_frame_height / dys;
841 unsigned int unscaled_bottom = (y + 1) * spu->orig_frame_height / dys;
842 if (unscaled_bottom >= spu->height)
843 unscaled_bottom = spu->height - 1;
844 for (x = 0; x < spu->scaled_width; ++x) {
845 const unsigned int unscaled_left = x * spu->orig_frame_width / dxs;
846 unsigned int unscaled_right = (x + 1) * spu->orig_frame_width / dxs;
847 unsigned int color = 0;
848 unsigned int alpha = 0;
849 unsigned int walkx, walky;
850 unsigned int base, tmp;
851 if (unscaled_right >= spu->width)
852 unscaled_right = spu->width - 1;
853 for (walky = unscaled_top; walky <= unscaled_bottom; ++walky)
854 for (walkx = unscaled_left; walkx <= unscaled_right; ++walkx) {
855 base = walky * spu->stride + walkx;
856 tmp = canon_alpha(spu->aimage[base]);
857 alpha += tmp;
858 color += tmp * spu->image[base];
860 base = y * spu->scaled_stride + x;
861 spu->scaled_image[base] = alpha ? color / alpha : 0;
862 spu->scaled_aimage[base] =
863 alpha * (1 + unscaled_bottom - unscaled_top) * (1 + unscaled_right - unscaled_left);
864 /* spu->scaled_aimage[base] =
865 alpha * dxs * dys / spu->orig_frame_width / spu->orig_frame_height; */
866 if (spu->scaled_aimage[base]) {
867 spu->scaled_aimage[base] = 256 - spu->scaled_aimage[base];
868 if (spu->scaled_aimage[base] + spu->scaled_image[base] > 255)
869 spu->scaled_image[base] = 256 - spu->scaled_aimage[base];
874 break;
875 case 2:
877 /* Best antialiasing. Very slow. */
878 /* Any pixel (x, y) represents pixels from the original
879 rectangular region comprised between the columns
880 unscaled_y and unscaled_y + 0x100 / scaley and the rows
881 unscaled_x and unscaled_x + 0x100 / scalex
883 The original rectangular region that the scaled pixel
884 represents is cut in 9 rectangular areas like this:
886 +---+-----------------+---+
887 | 1 | 2 | 3 |
888 +---+-----------------+---+
889 | | | |
890 | 4 | 5 | 6 |
891 | | | |
892 +---+-----------------+---+
893 | 7 | 8 | 9 |
894 +---+-----------------+---+
896 The width of the left column is at most one pixel and
897 it is never null and its right column is at a pixel
898 boundary. The height of the top row is at most one
899 pixel it is never null and its bottom row is at a
900 pixel boundary. The width and height of region 5 are
901 integral values. The width of the right column is
902 what remains and is less than one pixel. The height
903 of the bottom row is what remains and is less than
904 one pixel.
906 The row above 1, 2, 3 is unscaled_y. The row between
907 1, 2, 3 and 4, 5, 6 is top_low_row. The row between 4,
908 5, 6 and 7, 8, 9 is (unsigned int)unscaled_y_bottom.
909 The row beneath 7, 8, 9 is unscaled_y_bottom.
911 The column left of 1, 4, 7 is unscaled_x. The column
912 between 1, 4, 7 and 2, 5, 8 is left_right_column. The
913 column between 2, 5, 8 and 3, 6, 9 is (unsigned
914 int)unscaled_x_right. The column right of 3, 6, 9 is
915 unscaled_x_right. */
916 const double inv_scalex = (double) 0x100 / scalex;
917 const double inv_scaley = (double) 0x100 / scaley;
918 for (y = 0; y < spu->scaled_height; ++y) {
919 const double unscaled_y = y * inv_scaley;
920 const double unscaled_y_bottom = unscaled_y + inv_scaley;
921 const unsigned int top_low_row = MIN(unscaled_y_bottom, unscaled_y + 1.0);
922 const double top = top_low_row - unscaled_y;
923 const unsigned int height = unscaled_y_bottom > top_low_row
924 ? (unsigned int) unscaled_y_bottom - top_low_row
925 : 0;
926 const double bottom = unscaled_y_bottom > top_low_row
927 ? unscaled_y_bottom - floor(unscaled_y_bottom)
928 : 0.0;
929 for (x = 0; x < spu->scaled_width; ++x) {
930 const double unscaled_x = x * inv_scalex;
931 const double unscaled_x_right = unscaled_x + inv_scalex;
932 const unsigned int left_right_column = MIN(unscaled_x_right, unscaled_x + 1.0);
933 const double left = left_right_column - unscaled_x;
934 const unsigned int width = unscaled_x_right > left_right_column
935 ? (unsigned int) unscaled_x_right - left_right_column
936 : 0;
937 const double right = unscaled_x_right > left_right_column
938 ? unscaled_x_right - floor(unscaled_x_right)
939 : 0.0;
940 double color = 0.0;
941 double alpha = 0.0;
942 double tmp;
943 unsigned int base;
944 /* Now use these informations to compute a good alpha,
945 and lightness. The sum is on each of the 9
946 region's surface and alpha and lightness.
948 transformed alpha = sum(surface * alpha) / sum(surface)
949 transformed color = sum(surface * alpha * color) / sum(surface * alpha)
951 /* 1: top left part */
952 base = spu->stride * (unsigned int) unscaled_y;
953 tmp = left * top * canon_alpha(spu->aimage[base + (unsigned int) unscaled_x]);
954 alpha += tmp;
955 color += tmp * spu->image[base + (unsigned int) unscaled_x];
956 /* 2: top center part */
957 if (width > 0) {
958 unsigned int walkx;
959 for (walkx = left_right_column; walkx < (unsigned int) unscaled_x_right; ++walkx) {
960 base = spu->stride * (unsigned int) unscaled_y + walkx;
961 tmp = /* 1.0 * */ top * canon_alpha(spu->aimage[base]);
962 alpha += tmp;
963 color += tmp * spu->image[base];
966 /* 3: top right part */
967 if (right > 0.0) {
968 base = spu->stride * (unsigned int) unscaled_y + (unsigned int) unscaled_x_right;
969 tmp = right * top * canon_alpha(spu->aimage[base]);
970 alpha += tmp;
971 color += tmp * spu->image[base];
973 /* 4: center left part */
974 if (height > 0) {
975 unsigned int walky;
976 for (walky = top_low_row; walky < (unsigned int) unscaled_y_bottom; ++walky) {
977 base = spu->stride * walky + (unsigned int) unscaled_x;
978 tmp = left /* * 1.0 */ * canon_alpha(spu->aimage[base]);
979 alpha += tmp;
980 color += tmp * spu->image[base];
983 /* 5: center part */
984 if (width > 0 && height > 0) {
985 unsigned int walky;
986 for (walky = top_low_row; walky < (unsigned int) unscaled_y_bottom; ++walky) {
987 unsigned int walkx;
988 base = spu->stride * walky;
989 for (walkx = left_right_column; walkx < (unsigned int) unscaled_x_right; ++walkx) {
990 tmp = /* 1.0 * 1.0 * */ canon_alpha(spu->aimage[base + walkx]);
991 alpha += tmp;
992 color += tmp * spu->image[base + walkx];
996 /* 6: center right part */
997 if (right > 0.0 && height > 0) {
998 unsigned int walky;
999 for (walky = top_low_row; walky < (unsigned int) unscaled_y_bottom; ++walky) {
1000 base = spu->stride * walky + (unsigned int) unscaled_x_right;
1001 tmp = right /* * 1.0 */ * canon_alpha(spu->aimage[base]);
1002 alpha += tmp;
1003 color += tmp * spu->image[base];
1006 /* 7: bottom left part */
1007 if (bottom > 0.0) {
1008 base = spu->stride * (unsigned int) unscaled_y_bottom + (unsigned int) unscaled_x;
1009 tmp = left * bottom * canon_alpha(spu->aimage[base]);
1010 alpha += tmp;
1011 color += tmp * spu->image[base];
1013 /* 8: bottom center part */
1014 if (width > 0 && bottom > 0.0) {
1015 unsigned int walkx;
1016 base = spu->stride * (unsigned int) unscaled_y_bottom;
1017 for (walkx = left_right_column; walkx < (unsigned int) unscaled_x_right; ++walkx) {
1018 tmp = /* 1.0 * */ bottom * canon_alpha(spu->aimage[base + walkx]);
1019 alpha += tmp;
1020 color += tmp * spu->image[base + walkx];
1023 /* 9: bottom right part */
1024 if (right > 0.0 && bottom > 0.0) {
1025 base = spu->stride * (unsigned int) unscaled_y_bottom + (unsigned int) unscaled_x_right;
1026 tmp = right * bottom * canon_alpha(spu->aimage[base]);
1027 alpha += tmp;
1028 color += tmp * spu->image[base];
1030 /* Finally mix these transparency and brightness information suitably */
1031 base = spu->scaled_stride * y + x;
1032 spu->scaled_image[base] = alpha > 0 ? color / alpha : 0;
1033 spu->scaled_aimage[base] = alpha * scalex * scaley / 0x10000;
1034 if (spu->scaled_aimage[base]) {
1035 spu->scaled_aimage[base] = 256 - spu->scaled_aimage[base];
1036 if (spu->scaled_aimage[base] + spu->scaled_image[base] > 255)
1037 spu->scaled_image[base] = 256 - spu->scaled_aimage[base];
1043 nothing_to_do:
1044 spu->scaled_frame_width = dxs;
1045 spu->scaled_frame_height = dys;
1048 if (spu->scaled_image){
1049 switch (spu_alignment) {
1050 case 0:
1051 spu->scaled_start_row = dys*sub_pos/100;
1052 if (spu->scaled_start_row + spu->scaled_height > dys)
1053 spu->scaled_start_row = dys - spu->scaled_height;
1054 break;
1055 case 1:
1056 spu->scaled_start_row = dys*sub_pos/100 - spu->scaled_height/2;
1057 if (sub_pos < 50) {
1058 if (spu->scaled_start_row < 0) spu->scaled_start_row = 0;
1059 } else {
1060 if (spu->scaled_start_row + spu->scaled_height > dys)
1061 spu->scaled_start_row = dys - spu->scaled_height;
1063 break;
1064 case 2:
1065 spu->scaled_start_row = dys*sub_pos/100 - spu->scaled_height;
1066 if (spu->scaled_start_row < 0) spu->scaled_start_row = 0;
1067 break;
1069 draw_alpha(spu->scaled_start_col, spu->scaled_start_row, spu->scaled_width, spu->scaled_height,
1070 spu->scaled_image, spu->scaled_aimage, spu->scaled_stride);
1071 spu->spu_changed = 0;
1075 else
1077 mp_msg(MSGT_SPUDEC,MSGL_DBG2,"SPU not displayed: start_pts=%d end_pts=%d now_pts=%d\n",
1078 spu->start_pts, spu->end_pts, spu->now_pts);
1082 void spudec_update_palette(void * this, unsigned int *palette)
1084 spudec_handle_t *spu = (spudec_handle_t *) this;
1085 if (spu && palette) {
1086 memcpy(spu->global_palette, palette, sizeof(spu->global_palette));
1087 if(spu->hw_spu)
1088 spu->hw_spu->control(VOCTRL_SET_SPU_PALETTE,spu->global_palette);
1092 void spudec_set_font_factor(void * this, double factor)
1094 spudec_handle_t *spu = (spudec_handle_t *) this;
1095 spu->font_start_level = (int)(0xF0-(0xE0*factor));
1098 void *spudec_new_scaled(unsigned int *palette, unsigned int frame_width, unsigned int frame_height)
1100 return spudec_new_scaled_vobsub(palette, NULL, 0, frame_width, frame_height);
1103 /* get palette custom color, width, height from .idx file */
1104 void *spudec_new_scaled_vobsub(unsigned int *palette, unsigned int *cuspal, unsigned int custom, unsigned int frame_width, unsigned int frame_height)
1106 spudec_handle_t *this = calloc(1, sizeof(spudec_handle_t));
1107 if (this){
1108 //(fprintf(stderr,"VobSub Custom Palette: %d,%d,%d,%d", this->cuspal[0], this->cuspal[1], this->cuspal[2],this->cuspal[3]);
1109 this->packet = NULL;
1110 this->image = NULL;
1111 this->scaled_image = NULL;
1112 /* XXX Although the video frame is some size, the SPU frame is
1113 always maximum size i.e. 720 wide and 576 or 480 high */
1114 this->orig_frame_width = 720;
1115 this->orig_frame_height = (frame_height == 480 || frame_height == 240) ? 480 : 576;
1116 this->custom = custom;
1117 // set up palette:
1118 this->auto_palette = 1;
1119 if (palette){
1120 memcpy(this->global_palette, palette, sizeof(this->global_palette));
1121 this->auto_palette = 0;
1123 this->custom = custom;
1124 if (custom && cuspal) {
1125 memcpy(this->cuspal, cuspal, sizeof(this->cuspal));
1126 this->auto_palette = 0;
1129 else
1130 mp_msg(MSGT_SPUDEC,MSGL_FATAL, "FATAL: spudec_init: calloc");
1131 return this;
1134 void *spudec_new(unsigned int *palette)
1136 return spudec_new_scaled(palette, 0, 0);
1139 void spudec_free(void *this)
1141 spudec_handle_t *spu = (spudec_handle_t*)this;
1142 if (spu) {
1143 while (spu->queue_head)
1144 spudec_free_packet(spudec_dequeue_packet(spu));
1145 if (spu->packet)
1146 free(spu->packet);
1147 if (spu->scaled_image)
1148 free(spu->scaled_image);
1149 if (spu->image)
1150 free(spu->image);
1151 free(spu);
1155 void spudec_set_hw_spu(void *this, vo_functions_t *hw_spu)
1157 spudec_handle_t *spu = (spudec_handle_t*)this;
1158 if (!spu)
1159 return;
1160 spu->hw_spu = hw_spu;
1161 hw_spu->control(VOCTRL_SET_SPU_PALETTE,spu->global_palette);