Add CONFIG_LIBVORBIS #define for FFmpeg to config.h.
[mplayer/glamo.git] / spudec.c
blobedf287b2e99f894b2aca146848e40dfd51ed90e9
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 "vobsub.h"
27 #include "libavutil/avutil.h"
28 #include "libavutil/intreadwrite.h"
29 #include "libswscale/swscale.h"
31 /* Valid values for spu_aamode:
32 0: none (fastest, most ugly)
33 1: approximate
34 2: full (slowest)
35 3: bilinear (similiar to vobsub, fast and not too bad)
36 4: uses swscaler gaussian (this is the only one that looks good)
39 int spu_aamode = 3;
40 int spu_alignment = -1;
41 float spu_gaussvar = 1.0;
42 extern int sub_pos;
44 typedef struct packet_t packet_t;
45 struct packet_t {
46 unsigned char *packet;
47 unsigned int palette[4];
48 unsigned int alpha[4];
49 unsigned int control_start; /* index of start of control data */
50 unsigned int current_nibble[2]; /* next data nibble (4 bits) to be
51 processed (for RLE decoding) for
52 even and odd lines */
53 int deinterlace_oddness; /* 0 or 1, index into current_nibble */
54 unsigned int start_col, end_col;
55 unsigned int start_row, end_row;
56 unsigned int width, height, stride;
57 unsigned int start_pts, end_pts;
58 packet_t *next;
61 typedef struct {
62 packet_t *queue_head;
63 packet_t *queue_tail;
64 unsigned int global_palette[16];
65 unsigned int orig_frame_width, orig_frame_height;
66 unsigned char* packet;
67 size_t packet_reserve; /* size of the memory pointed to by packet */
68 unsigned int packet_offset; /* end of the currently assembled fragment */
69 unsigned int packet_size; /* size of the packet once all fragments are assembled */
70 int packet_pts; /* PTS for this packet */
71 unsigned int palette[4];
72 unsigned int alpha[4];
73 unsigned int cuspal[4];
74 unsigned int custom;
75 unsigned int now_pts;
76 unsigned int start_pts, end_pts;
77 unsigned int start_col, end_col;
78 unsigned int start_row, end_row;
79 unsigned int width, height, stride;
80 size_t image_size; /* Size of the image buffer */
81 unsigned char *image; /* Grayscale value */
82 unsigned char *aimage; /* Alpha value */
83 unsigned int scaled_frame_width, scaled_frame_height;
84 unsigned int scaled_start_col, scaled_start_row;
85 unsigned int scaled_width, scaled_height, scaled_stride;
86 size_t scaled_image_size;
87 unsigned char *scaled_image;
88 unsigned char *scaled_aimage;
89 int auto_palette; /* 1 if we lack a palette and must use an heuristic. */
90 int font_start_level; /* Darkest value used for the computed font */
91 const vo_functions_t *hw_spu;
92 int spu_changed;
93 unsigned int forced_subs_only; /* flag: 0=display all subtitle, !0 display only forced subtitles */
94 unsigned int is_forced_sub; /* true if current subtitle is a forced subtitle */
95 } spudec_handle_t;
97 static void spudec_queue_packet(spudec_handle_t *this, packet_t *packet)
99 if (this->queue_head == NULL)
100 this->queue_head = packet;
101 else
102 this->queue_tail->next = packet;
103 this->queue_tail = packet;
106 static packet_t *spudec_dequeue_packet(spudec_handle_t *this)
108 packet_t *retval = this->queue_head;
110 this->queue_head = retval->next;
111 if (this->queue_head == NULL)
112 this->queue_tail = NULL;
114 return retval;
117 static void spudec_free_packet(packet_t *packet)
119 if (packet->packet != NULL)
120 free(packet->packet);
121 free(packet);
124 static inline unsigned int get_be16(const unsigned char *p)
126 return (p[0] << 8) + p[1];
129 static inline unsigned int get_be24(const unsigned char *p)
131 return (get_be16(p) << 8) + p[2];
134 static void next_line(packet_t *packet)
136 if (packet->current_nibble[packet->deinterlace_oddness] % 2)
137 packet->current_nibble[packet->deinterlace_oddness]++;
138 packet->deinterlace_oddness = (packet->deinterlace_oddness + 1) % 2;
141 static inline unsigned char get_nibble(packet_t *packet)
143 unsigned char nib;
144 unsigned int *nibblep = packet->current_nibble + packet->deinterlace_oddness;
145 if (*nibblep / 2 >= packet->control_start) {
146 mp_msg(MSGT_SPUDEC,MSGL_WARN, "SPUdec: ERROR: get_nibble past end of packet\n");
147 return 0;
149 nib = packet->packet[*nibblep / 2];
150 if (*nibblep % 2)
151 nib &= 0xf;
152 else
153 nib >>= 4;
154 ++*nibblep;
155 return nib;
158 static inline int mkalpha(int i)
160 /* In mplayer's alpha planes, 0 is transparent, then 1 is nearly
161 opaque upto 255 which is transparent */
162 switch (i) {
163 case 0xf:
164 return 1;
165 case 0:
166 return 0;
167 default:
168 return (0xf - i) << 4;
172 /* Cut the sub to visible part */
173 static inline void spudec_cut_image(spudec_handle_t *this)
175 unsigned int fy, ly;
176 unsigned int first_y, last_y;
177 unsigned char *image;
178 unsigned char *aimage;
180 if (this->stride == 0 || this->height == 0) {
181 return;
184 for (fy = 0; fy < this->image_size && !this->aimage[fy]; fy++);
185 for (ly = this->stride * this->height-1; ly && !this->aimage[ly]; ly--);
186 first_y = fy / this->stride;
187 last_y = ly / this->stride;
188 //printf("first_y: %d, last_y: %d\n", first_y, last_y);
189 this->start_row += first_y;
191 // Some subtitles trigger this condition
192 if (last_y + 1 > first_y ) {
193 this->height = last_y - first_y +1;
194 } else {
195 this->height = 0;
196 this->image_size = 0;
197 return;
200 // printf("new h %d new start %d (sz %d st %d)---\n\n", this->height, this->start_row, this->image_size, this->stride);
202 image = malloc(2 * this->stride * this->height);
203 if(image){
204 this->image_size = this->stride * this->height;
205 aimage = image + this->image_size;
206 memcpy(image, this->image + this->stride * first_y, this->image_size);
207 memcpy(aimage, this->aimage + this->stride * first_y, this->image_size);
208 free(this->image);
209 this->image = image;
210 this->aimage = aimage;
211 } else {
212 mp_msg(MSGT_SPUDEC, MSGL_FATAL, "Fatal: update_spu: malloc requested %d bytes\n", 2 * this->stride * this->height);
216 static void spudec_process_data(spudec_handle_t *this, packet_t *packet)
218 unsigned int cmap[4], alpha[4];
219 unsigned int i, x, y;
221 this->scaled_frame_width = 0;
222 this->scaled_frame_height = 0;
223 this->start_col = packet->start_col;
224 this->end_col = packet->end_col;
225 this->start_row = packet->start_row;
226 this->end_row = packet->end_row;
227 this->height = packet->height;
228 this->width = packet->width;
229 this->stride = packet->stride;
230 for (i = 0; i < 4; ++i) {
231 alpha[i] = mkalpha(packet->alpha[i]);
232 if (this->custom && (this->cuspal[i] >> 31) != 0)
233 alpha[i] = 0;
234 if (alpha[i] == 0)
235 cmap[i] = 0;
236 else if (this->custom){
237 cmap[i] = ((this->cuspal[i] >> 16) & 0xff);
238 if (cmap[i] + alpha[i] > 255)
239 cmap[i] = 256 - alpha[i];
241 else {
242 cmap[i] = ((this->global_palette[packet->palette[i]] >> 16) & 0xff);
243 if (cmap[i] + alpha[i] > 255)
244 cmap[i] = 256 - alpha[i];
248 if (this->image_size < this->stride * this->height) {
249 if (this->image != NULL) {
250 free(this->image);
251 this->image_size = 0;
253 this->image = malloc(2 * this->stride * this->height);
254 if (this->image) {
255 this->image_size = this->stride * this->height;
256 this->aimage = this->image + this->image_size;
259 if (this->image == NULL)
260 return;
262 /* Kludge: draw_alpha needs width multiple of 8. */
263 if (this->width < this->stride)
264 for (y = 0; y < this->height; ++y) {
265 memset(this->aimage + y * this->stride + this->width, 0, this->stride - this->width);
266 /* FIXME: Why is this one needed? */
267 memset(this->image + y * this->stride + this->width, 0, this->stride - this->width);
270 i = packet->current_nibble[1];
271 x = 0;
272 y = 0;
273 while (packet->current_nibble[0] < i
274 && packet->current_nibble[1] / 2 < packet->control_start
275 && y < this->height) {
276 unsigned int len, color;
277 unsigned int rle = 0;
278 rle = get_nibble(packet);
279 if (rle < 0x04) {
280 rle = (rle << 4) | get_nibble(packet);
281 if (rle < 0x10) {
282 rle = (rle << 4) | get_nibble(packet);
283 if (rle < 0x040) {
284 rle = (rle << 4) | get_nibble(packet);
285 if (rle < 0x0004)
286 rle |= ((this->width - x) << 2);
290 color = 3 - (rle & 0x3);
291 len = rle >> 2;
292 if (len > this->width - x || len == 0)
293 len = this->width - x;
294 /* FIXME have to use palette and alpha map*/
295 memset(this->image + y * this->stride + x, cmap[color], len);
296 memset(this->aimage + y * this->stride + x, alpha[color], len);
297 x += len;
298 if (x >= this->width) {
299 next_line(packet);
300 x = 0;
301 ++y;
304 spudec_cut_image(this);
309 This function tries to create a usable palette.
310 It determines how many non-transparent colors are used, and assigns different
311 gray scale values to each color.
312 I tested it with four streams and even got something readable. Half of the
313 times I got black characters with white around and half the reverse.
315 static void compute_palette(spudec_handle_t *this, packet_t *packet)
317 int used[16],i,cused,start,step,color;
319 memset(used, 0, sizeof(used));
320 for (i=0; i<4; i++)
321 if (packet->alpha[i]) /* !Transparent? */
322 used[packet->palette[i]] = 1;
323 for (cused=0, i=0; i<16; i++)
324 if (used[i]) cused++;
325 if (!cused) return;
326 if (cused == 1) {
327 start = 0x80;
328 step = 0;
329 } else {
330 start = this->font_start_level;
331 step = (0xF0-this->font_start_level)/(cused-1);
333 memset(used, 0, sizeof(used));
334 for (i=0; i<4; i++) {
335 color = packet->palette[i];
336 if (packet->alpha[i] && !used[color]) { /* not assigned? */
337 used[color] = 1;
338 this->global_palette[color] = start<<16;
339 start += step;
344 static void spudec_process_control(spudec_handle_t *this, int pts100)
346 int a,b; /* Temporary vars */
347 unsigned int date, type;
348 unsigned int off;
349 unsigned int start_off = 0;
350 unsigned int next_off;
351 unsigned int start_pts = 0;
352 unsigned int end_pts = 0;
353 unsigned int current_nibble[2] = {0, 0};
354 unsigned int control_start;
355 unsigned int display = 0;
356 unsigned int start_col = 0;
357 unsigned int end_col = 0;
358 unsigned int start_row = 0;
359 unsigned int end_row = 0;
360 unsigned int width = 0;
361 unsigned int height = 0;
362 unsigned int stride = 0;
364 control_start = get_be16(this->packet + 2);
365 next_off = control_start;
366 while (start_off != next_off) {
367 start_off = next_off;
368 date = get_be16(this->packet + start_off) * 1024;
369 next_off = get_be16(this->packet + start_off + 2);
370 mp_msg(MSGT_SPUDEC,MSGL_DBG2, "date=%d\n", date);
371 off = start_off + 4;
372 for (type = this->packet[off++]; type != 0xff; type = this->packet[off++]) {
373 mp_msg(MSGT_SPUDEC,MSGL_DBG2, "cmd=%d ",type);
374 switch(type) {
375 case 0x00:
376 /* Menu ID, 1 byte */
377 mp_msg(MSGT_SPUDEC,MSGL_DBG2,"Menu ID\n");
378 /* shouldn't a Menu ID type force display start? */
379 start_pts = pts100 < 0 && -pts100 >= date ? 0 : pts100 + date;
380 end_pts = UINT_MAX;
381 display = 1;
382 this->is_forced_sub=~0; // current subtitle is forced
383 break;
384 case 0x01:
385 /* Start display */
386 mp_msg(MSGT_SPUDEC,MSGL_DBG2,"Start display!\n");
387 start_pts = pts100 < 0 && -pts100 >= date ? 0 : pts100 + date;
388 end_pts = UINT_MAX;
389 display = 1;
390 this->is_forced_sub=0;
391 break;
392 case 0x02:
393 /* Stop display */
394 mp_msg(MSGT_SPUDEC,MSGL_DBG2,"Stop display!\n");
395 end_pts = pts100 < 0 && -pts100 >= date ? 0 : pts100 + date;
396 break;
397 case 0x03:
398 /* Palette */
399 this->palette[0] = this->packet[off] >> 4;
400 this->palette[1] = this->packet[off] & 0xf;
401 this->palette[2] = this->packet[off + 1] >> 4;
402 this->palette[3] = this->packet[off + 1] & 0xf;
403 mp_msg(MSGT_SPUDEC,MSGL_DBG2,"Palette %d, %d, %d, %d\n",
404 this->palette[0], this->palette[1], this->palette[2], this->palette[3]);
405 off+=2;
406 break;
407 case 0x04:
408 /* Alpha */
409 this->alpha[0] = this->packet[off] >> 4;
410 this->alpha[1] = this->packet[off] & 0xf;
411 this->alpha[2] = this->packet[off + 1] >> 4;
412 this->alpha[3] = this->packet[off + 1] & 0xf;
413 mp_msg(MSGT_SPUDEC,MSGL_DBG2,"Alpha %d, %d, %d, %d\n",
414 this->alpha[0], this->alpha[1], this->alpha[2], this->alpha[3]);
415 off+=2;
416 break;
417 case 0x05:
418 /* Co-ords */
419 a = get_be24(this->packet + off);
420 b = get_be24(this->packet + off + 3);
421 start_col = a >> 12;
422 end_col = a & 0xfff;
423 width = (end_col < start_col) ? 0 : end_col - start_col + 1;
424 stride = (width + 7) & ~7; /* Kludge: draw_alpha needs width multiple of 8 */
425 start_row = b >> 12;
426 end_row = b & 0xfff;
427 height = (end_row < start_row) ? 0 : end_row - start_row /* + 1 */;
428 mp_msg(MSGT_SPUDEC,MSGL_DBG2,"Coords col: %d - %d row: %d - %d (%dx%d)\n",
429 start_col, end_col, start_row, end_row,
430 width, height);
431 off+=6;
432 break;
433 case 0x06:
434 /* Graphic lines */
435 current_nibble[0] = 2 * get_be16(this->packet + off);
436 current_nibble[1] = 2 * get_be16(this->packet + off + 2);
437 mp_msg(MSGT_SPUDEC,MSGL_DBG2,"Graphic offset 1: %d offset 2: %d\n",
438 current_nibble[0] / 2, current_nibble[1] / 2);
439 off+=4;
440 break;
441 case 0xff:
442 /* All done, bye-bye */
443 mp_msg(MSGT_SPUDEC,MSGL_DBG2,"Done!\n");
444 return;
445 // break;
446 default:
447 mp_msg(MSGT_SPUDEC,MSGL_WARN,"spudec: Error determining control type 0x%02x. Skipping %d bytes.\n",
448 type, next_off - off);
449 goto next_control;
452 next_control:
453 if (!display)
454 continue;
455 if (end_pts == UINT_MAX && start_off != next_off) {
456 end_pts = get_be16(this->packet + next_off) * 1024;
457 end_pts = 1 - pts100 >= end_pts ? 0 : pts100 + end_pts - 1;
459 if (end_pts > 0) {
460 packet_t *packet = calloc(1, sizeof(packet_t));
461 int i;
462 packet->start_pts = start_pts;
463 packet->end_pts = end_pts;
464 packet->current_nibble[0] = current_nibble[0];
465 packet->current_nibble[1] = current_nibble[1];
466 packet->start_row = start_row;
467 packet->end_row = end_row;
468 packet->start_col = start_col;
469 packet->end_col = end_col;
470 packet->width = width;
471 packet->height = height;
472 packet->stride = stride;
473 packet->control_start = control_start;
474 for (i=0; i<4; i++) {
475 packet->alpha[i] = this->alpha[i];
476 packet->palette[i] = this->palette[i];
478 packet->packet = malloc(this->packet_size);
479 memcpy(packet->packet, this->packet, this->packet_size);
480 spudec_queue_packet(this, packet);
485 static void spudec_decode(spudec_handle_t *this, int pts100)
487 if (!this->hw_spu)
488 spudec_process_control(this, pts100);
489 else if (pts100 >= 0) {
490 static vo_mpegpes_t packet = { NULL, 0, 0x20, 0 };
491 static vo_mpegpes_t *pkg=&packet;
492 packet.data = this->packet;
493 packet.size = this->packet_size;
494 packet.timestamp = pts100;
495 this->hw_spu->draw_frame((uint8_t**)&pkg);
499 int spudec_changed(void * this)
501 spudec_handle_t * spu = (spudec_handle_t*)this;
502 return spu->spu_changed || spu->now_pts > spu->end_pts;
505 void spudec_assemble(void *this, unsigned char *packet, unsigned int len, int pts100)
507 spudec_handle_t *spu = (spudec_handle_t*)this;
508 // spudec_heartbeat(this, pts100);
509 if (len < 2) {
510 mp_msg(MSGT_SPUDEC,MSGL_WARN,"SPUasm: packet too short\n");
511 return;
513 #if 0
514 if ((spu->packet_pts + 10000) < pts100) {
515 // [cb] too long since last fragment: force new packet
516 spu->packet_offset = 0;
518 #endif
519 spu->packet_pts = pts100;
520 if (spu->packet_offset == 0) {
521 unsigned int len2 = get_be16(packet);
522 // Start new fragment
523 if (spu->packet_reserve < len2) {
524 if (spu->packet != NULL)
525 free(spu->packet);
526 spu->packet = malloc(len2);
527 spu->packet_reserve = spu->packet != NULL ? len2 : 0;
529 if (spu->packet != NULL) {
530 spu->packet_size = len2;
531 if (len > len2) {
532 mp_msg(MSGT_SPUDEC,MSGL_WARN,"SPUasm: invalid frag len / len2: %d / %d \n", len, len2);
533 return;
535 memcpy(spu->packet, packet, len);
536 spu->packet_offset = len;
537 spu->packet_pts = pts100;
539 } else {
540 // Continue current fragment
541 if (spu->packet_size < spu->packet_offset + len){
542 mp_msg(MSGT_SPUDEC,MSGL_WARN,"SPUasm: invalid fragment\n");
543 spu->packet_size = spu->packet_offset = 0;
544 return;
545 } else {
546 memcpy(spu->packet + spu->packet_offset, packet, len);
547 spu->packet_offset += len;
550 #if 1
551 // check if we have a complete packet (unfortunatelly packet_size is bad
552 // for some disks)
553 // [cb] packet_size is padded to be even -> may be one byte too long
554 if ((spu->packet_offset == spu->packet_size) ||
555 ((spu->packet_offset + 1) == spu->packet_size)){
556 unsigned int x=0,y;
557 while(x+4<=spu->packet_offset){
558 y=get_be16(spu->packet+x+2); // next control pointer
559 mp_msg(MSGT_SPUDEC,MSGL_DBG2,"SPUtest: x=%d y=%d off=%d size=%d\n",x,y,spu->packet_offset,spu->packet_size);
560 if(x>=4 && x==y){ // if it points to self - we're done!
561 // we got it!
562 mp_msg(MSGT_SPUDEC,MSGL_DBG2,"SPUgot: off=%d size=%d \n",spu->packet_offset,spu->packet_size);
563 spudec_decode(spu, pts100);
564 spu->packet_offset = 0;
565 break;
567 if(y<=x || y>=spu->packet_size){ // invalid?
568 mp_msg(MSGT_SPUDEC,MSGL_WARN,"SPUtest: broken packet!!!!! y=%d < x=%d\n",y,x);
569 spu->packet_size = spu->packet_offset = 0;
570 break;
572 x=y;
574 // [cb] packet is done; start new packet
575 spu->packet_offset = 0;
577 #else
578 if (spu->packet_offset == spu->packet_size) {
579 spudec_decode(spu, pts100);
580 spu->packet_offset = 0;
582 #endif
585 void spudec_reset(void *this) // called after seek
587 spudec_handle_t *spu = (spudec_handle_t*)this;
588 while (spu->queue_head)
589 spudec_free_packet(spudec_dequeue_packet(spu));
590 spu->now_pts = 0;
591 spu->end_pts = 0;
592 spu->packet_size = spu->packet_offset = 0;
595 void spudec_heartbeat(void *this, unsigned int pts100)
597 spudec_handle_t *spu = (spudec_handle_t*) this;
598 spu->now_pts = pts100;
600 while (spu->queue_head != NULL && pts100 >= spu->queue_head->start_pts) {
601 packet_t *packet = spudec_dequeue_packet(spu);
602 spu->start_pts = packet->start_pts;
603 spu->end_pts = packet->end_pts;
604 if (spu->auto_palette)
605 compute_palette(spu, packet);
606 spudec_process_data(spu, packet);
607 spudec_free_packet(packet);
608 spu->spu_changed = 1;
612 int spudec_visible(void *this){
613 spudec_handle_t *spu = (spudec_handle_t *)this;
614 int ret=(spu->start_pts <= spu->now_pts &&
615 spu->now_pts < spu->end_pts &&
616 spu->height > 0);
617 // printf("spu visible: %d \n",ret);
618 return ret;
621 void spudec_set_forced_subs_only(void * const this, const unsigned int flag)
623 if(this){
624 ((spudec_handle_t *)this)->forced_subs_only=flag;
625 mp_msg(MSGT_SPUDEC,MSGL_DBG2,"SPU: Display only forced subs now %s\n", flag ? "enabled": "disabled");
629 void spudec_draw(void *this, void (*draw_alpha)(int x0,int y0, int w,int h, unsigned char* src, unsigned char *srca, int stride))
631 spudec_handle_t *spu = (spudec_handle_t *)this;
632 if (spu->start_pts <= spu->now_pts && spu->now_pts < spu->end_pts && spu->image)
634 draw_alpha(spu->start_col, spu->start_row, spu->width, spu->height,
635 spu->image, spu->aimage, spu->stride);
636 spu->spu_changed = 0;
640 /* calc the bbox for spudec subs */
641 void spudec_calc_bbox(void *me, unsigned int dxs, unsigned int dys, unsigned int* bbox)
643 spudec_handle_t *spu;
644 spu = (spudec_handle_t *)me;
645 if (spu->orig_frame_width == 0 || spu->orig_frame_height == 0
646 || (spu->orig_frame_width == dxs && spu->orig_frame_height == dys)) {
647 bbox[0] = spu->start_col;
648 bbox[1] = spu->start_col + spu->width;
649 bbox[2] = spu->start_row;
650 bbox[3] = spu->start_row + spu->height;
652 else if (spu->scaled_frame_width != dxs || spu->scaled_frame_height != dys) {
653 unsigned int scalex = 0x100 * dxs / spu->orig_frame_width;
654 unsigned int scaley = 0x100 * dys / spu->orig_frame_height;
655 bbox[0] = spu->start_col * scalex / 0x100;
656 bbox[1] = spu->start_col * scalex / 0x100 + spu->width * scalex / 0x100;
657 switch (spu_alignment) {
658 case 0:
659 bbox[3] = dys*sub_pos/100 + spu->height * scaley / 0x100;
660 if (bbox[3] > dys) bbox[3] = dys;
661 bbox[2] = bbox[3] - spu->height * scaley / 0x100;
662 break;
663 case 1:
664 if (sub_pos < 50) {
665 bbox[2] = dys*sub_pos/100 - spu->height * scaley / 0x200;
666 bbox[3] = bbox[2] + spu->height;
667 } else {
668 bbox[3] = dys*sub_pos/100 + spu->height * scaley / 0x200;
669 if (bbox[3] > dys) bbox[3] = dys;
670 bbox[2] = bbox[3] - spu->height * scaley / 0x100;
672 break;
673 case 2:
674 bbox[2] = dys*sub_pos/100 - spu->height * scaley / 0x100;
675 bbox[3] = bbox[2] + spu->height;
676 break;
677 default: /* -1 */
678 bbox[2] = spu->start_row * scaley / 0x100;
679 bbox[3] = spu->start_row * scaley / 0x100 + spu->height * scaley / 0x100;
680 break;
684 /* transform mplayer's alpha value into an opacity value that is linear */
685 static inline int canon_alpha(int alpha)
687 return alpha ? 256 - alpha : 0;
690 typedef struct {
691 unsigned position;
692 unsigned left_up;
693 unsigned right_down;
694 }scale_pixel;
697 static void scale_table(unsigned int start_src, unsigned int start_tar, unsigned int end_src, unsigned int end_tar, scale_pixel * table)
699 unsigned int t;
700 unsigned int delta_src = end_src - start_src;
701 unsigned int delta_tar = end_tar - start_tar;
702 int src = 0;
703 int src_step;
704 if (delta_src == 0 || delta_tar == 0) {
705 return;
707 src_step = (delta_src << 16) / delta_tar >>1;
708 for (t = 0; t<=delta_tar; src += (src_step << 1), t++){
709 table[t].position= FFMIN(src >> 16, end_src - 1);
710 table[t].right_down = src & 0xffff;
711 table[t].left_up = 0x10000 - table[t].right_down;
715 /* bilinear scale, similar to vobsub's code */
716 static void scale_image(int x, int y, scale_pixel* table_x, scale_pixel* table_y, spudec_handle_t * spu)
718 int alpha[4];
719 int color[4];
720 unsigned int scale[4];
721 int base = table_y[y].position * spu->stride + table_x[x].position;
722 int scaled = y * spu->scaled_stride + x;
723 alpha[0] = canon_alpha(spu->aimage[base]);
724 alpha[1] = canon_alpha(spu->aimage[base + 1]);
725 alpha[2] = canon_alpha(spu->aimage[base + spu->stride]);
726 alpha[3] = canon_alpha(spu->aimage[base + spu->stride + 1]);
727 color[0] = spu->image[base];
728 color[1] = spu->image[base + 1];
729 color[2] = spu->image[base + spu->stride];
730 color[3] = spu->image[base + spu->stride + 1];
731 scale[0] = (table_x[x].left_up * table_y[y].left_up >> 16) * alpha[0];
732 if (table_y[y].left_up == 0x10000) // necessary to avoid overflow-case
733 scale[0] = table_x[x].left_up * alpha[0];
734 scale[1] = (table_x[x].right_down * table_y[y].left_up >>16) * alpha[1];
735 scale[2] = (table_x[x].left_up * table_y[y].right_down >> 16) * alpha[2];
736 scale[3] = (table_x[x].right_down * table_y[y].right_down >> 16) * alpha[3];
737 spu->scaled_image[scaled] = (color[0] * scale[0] + color[1] * scale[1] + color[2] * scale[2] + color[3] * scale[3])>>24;
738 spu->scaled_aimage[scaled] = (scale[0] + scale[1] + scale[2] + scale[3]) >> 16;
739 if (spu->scaled_aimage[scaled]){
740 // ensure that MPlayer's simplified alpha-blending can not overflow
741 spu->scaled_image[scaled] = FFMIN(spu->scaled_image[scaled], spu->scaled_aimage[scaled]);
742 // convert to MPlayer-style alpha
743 spu->scaled_aimage[scaled] = -spu->scaled_aimage[scaled];
747 void sws_spu_image(unsigned char *d1, unsigned char *d2, int dw, int dh, int ds,
748 unsigned char *s1, unsigned char *s2, int sw, int sh, int ss)
750 struct SwsContext *ctx;
751 static SwsFilter filter;
752 static int firsttime = 1;
753 static float oldvar;
754 int i;
756 if (!firsttime && oldvar != spu_gaussvar) sws_freeVec(filter.lumH);
757 if (firsttime) {
758 filter.lumH = filter.lumV =
759 filter.chrH = filter.chrV = sws_getGaussianVec(spu_gaussvar, 3.0);
760 sws_normalizeVec(filter.lumH, 1.0);
761 firsttime = 0;
762 oldvar = spu_gaussvar;
765 ctx=sws_getContext(sw, sh, PIX_FMT_GRAY8, dw, dh, PIX_FMT_GRAY8, SWS_GAUSS, &filter, NULL, NULL);
766 sws_scale(ctx,&s1,&ss,0,sh,&d1,&ds);
767 for (i=ss*sh-1; i>=0; i--) if (!s2[i]) s2[i] = 255; //else s2[i] = 1;
768 sws_scale(ctx,&s2,&ss,0,sh,&d2,&ds);
769 for (i=ds*dh-1; i>=0; i--) if (d2[i]==0) d2[i] = 1; else if (d2[i]==255) d2[i] = 0;
770 sws_freeContext(ctx);
773 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))
775 spudec_handle_t *spu = (spudec_handle_t *)me;
776 scale_pixel *table_x;
777 scale_pixel *table_y;
779 if (spu->start_pts <= spu->now_pts && spu->now_pts < spu->end_pts) {
781 // check if only forced subtitles are requested
782 if( (spu->forced_subs_only) && !(spu->is_forced_sub) ){
783 return;
786 if (!(spu_aamode&16) && (spu->orig_frame_width == 0 || spu->orig_frame_height == 0
787 || (spu->orig_frame_width == dxs && spu->orig_frame_height == dys))) {
788 if (spu->image)
790 draw_alpha(spu->start_col, spu->start_row, spu->width, spu->height,
791 spu->image, spu->aimage, spu->stride);
792 spu->spu_changed = 0;
795 else {
796 if (spu->scaled_frame_width != dxs || spu->scaled_frame_height != dys) { /* Resizing is needed */
797 /* scaled_x = scalex * x / 0x100
798 scaled_y = scaley * y / 0x100
799 order of operations is important because of rounding. */
800 unsigned int scalex = 0x100 * dxs / spu->orig_frame_width;
801 unsigned int scaley = 0x100 * dys / spu->orig_frame_height;
802 spu->scaled_start_col = spu->start_col * scalex / 0x100;
803 spu->scaled_start_row = spu->start_row * scaley / 0x100;
804 spu->scaled_width = spu->width * scalex / 0x100;
805 spu->scaled_height = spu->height * scaley / 0x100;
806 /* Kludge: draw_alpha needs width multiple of 8 */
807 spu->scaled_stride = (spu->scaled_width + 7) & ~7;
808 if (spu->scaled_image_size < spu->scaled_stride * spu->scaled_height) {
809 if (spu->scaled_image) {
810 free(spu->scaled_image);
811 spu->scaled_image_size = 0;
813 spu->scaled_image = malloc(2 * spu->scaled_stride * spu->scaled_height);
814 if (spu->scaled_image) {
815 spu->scaled_image_size = spu->scaled_stride * spu->scaled_height;
816 spu->scaled_aimage = spu->scaled_image + spu->scaled_image_size;
819 if (spu->scaled_image) {
820 unsigned int x, y;
821 if (spu->scaled_width <= 1 || spu->scaled_height <= 1) {
822 goto nothing_to_do;
824 switch(spu_aamode&15) {
825 case 4:
826 sws_spu_image(spu->scaled_image, spu->scaled_aimage,
827 spu->scaled_width, spu->scaled_height, spu->scaled_stride,
828 spu->image, spu->aimage, spu->width, spu->height, spu->stride);
829 break;
830 case 3:
831 table_x = calloc(spu->scaled_width, sizeof(scale_pixel));
832 table_y = calloc(spu->scaled_height, sizeof(scale_pixel));
833 if (!table_x || !table_y) {
834 mp_msg(MSGT_SPUDEC, MSGL_FATAL, "Fatal: spudec_draw_scaled: calloc failed\n");
836 scale_table(0, 0, spu->width - 1, spu->scaled_width - 1, table_x);
837 scale_table(0, 0, spu->height - 1, spu->scaled_height - 1, table_y);
838 for (y = 0; y < spu->scaled_height; y++)
839 for (x = 0; x < spu->scaled_width; x++)
840 scale_image(x, y, table_x, table_y, spu);
841 free(table_x);
842 free(table_y);
843 break;
844 case 0:
845 /* no antialiasing */
846 for (y = 0; y < spu->scaled_height; ++y) {
847 int unscaled_y = y * 0x100 / scaley;
848 int strides = spu->stride * unscaled_y;
849 int scaled_strides = spu->scaled_stride * y;
850 for (x = 0; x < spu->scaled_width; ++x) {
851 int unscaled_x = x * 0x100 / scalex;
852 spu->scaled_image[scaled_strides + x] = spu->image[strides + unscaled_x];
853 spu->scaled_aimage[scaled_strides + x] = spu->aimage[strides + unscaled_x];
856 break;
857 case 1:
859 /* Intermediate antialiasing. */
860 for (y = 0; y < spu->scaled_height; ++y) {
861 const unsigned int unscaled_top = y * spu->orig_frame_height / dys;
862 unsigned int unscaled_bottom = (y + 1) * spu->orig_frame_height / dys;
863 if (unscaled_bottom >= spu->height)
864 unscaled_bottom = spu->height - 1;
865 for (x = 0; x < spu->scaled_width; ++x) {
866 const unsigned int unscaled_left = x * spu->orig_frame_width / dxs;
867 unsigned int unscaled_right = (x + 1) * spu->orig_frame_width / dxs;
868 unsigned int color = 0;
869 unsigned int alpha = 0;
870 unsigned int walkx, walky;
871 unsigned int base, tmp;
872 if (unscaled_right >= spu->width)
873 unscaled_right = spu->width - 1;
874 for (walky = unscaled_top; walky <= unscaled_bottom; ++walky)
875 for (walkx = unscaled_left; walkx <= unscaled_right; ++walkx) {
876 base = walky * spu->stride + walkx;
877 tmp = canon_alpha(spu->aimage[base]);
878 alpha += tmp;
879 color += tmp * spu->image[base];
881 base = y * spu->scaled_stride + x;
882 spu->scaled_image[base] = alpha ? color / alpha : 0;
883 spu->scaled_aimage[base] =
884 alpha * (1 + unscaled_bottom - unscaled_top) * (1 + unscaled_right - unscaled_left);
885 /* spu->scaled_aimage[base] =
886 alpha * dxs * dys / spu->orig_frame_width / spu->orig_frame_height; */
887 if (spu->scaled_aimage[base]) {
888 spu->scaled_aimage[base] = 256 - spu->scaled_aimage[base];
889 if (spu->scaled_aimage[base] + spu->scaled_image[base] > 255)
890 spu->scaled_image[base] = 256 - spu->scaled_aimage[base];
895 break;
896 case 2:
898 /* Best antialiasing. Very slow. */
899 /* Any pixel (x, y) represents pixels from the original
900 rectangular region comprised between the columns
901 unscaled_y and unscaled_y + 0x100 / scaley and the rows
902 unscaled_x and unscaled_x + 0x100 / scalex
904 The original rectangular region that the scaled pixel
905 represents is cut in 9 rectangular areas like this:
907 +---+-----------------+---+
908 | 1 | 2 | 3 |
909 +---+-----------------+---+
910 | | | |
911 | 4 | 5 | 6 |
912 | | | |
913 +---+-----------------+---+
914 | 7 | 8 | 9 |
915 +---+-----------------+---+
917 The width of the left column is at most one pixel and
918 it is never null and its right column is at a pixel
919 boundary. The height of the top row is at most one
920 pixel it is never null and its bottom row is at a
921 pixel boundary. The width and height of region 5 are
922 integral values. The width of the right column is
923 what remains and is less than one pixel. The height
924 of the bottom row is what remains and is less than
925 one pixel.
927 The row above 1, 2, 3 is unscaled_y. The row between
928 1, 2, 3 and 4, 5, 6 is top_low_row. The row between 4,
929 5, 6 and 7, 8, 9 is (unsigned int)unscaled_y_bottom.
930 The row beneath 7, 8, 9 is unscaled_y_bottom.
932 The column left of 1, 4, 7 is unscaled_x. The column
933 between 1, 4, 7 and 2, 5, 8 is left_right_column. The
934 column between 2, 5, 8 and 3, 6, 9 is (unsigned
935 int)unscaled_x_right. The column right of 3, 6, 9 is
936 unscaled_x_right. */
937 const double inv_scalex = (double) 0x100 / scalex;
938 const double inv_scaley = (double) 0x100 / scaley;
939 for (y = 0; y < spu->scaled_height; ++y) {
940 const double unscaled_y = y * inv_scaley;
941 const double unscaled_y_bottom = unscaled_y + inv_scaley;
942 const unsigned int top_low_row = FFMIN(unscaled_y_bottom, unscaled_y + 1.0);
943 const double top = top_low_row - unscaled_y;
944 const unsigned int height = unscaled_y_bottom > top_low_row
945 ? (unsigned int) unscaled_y_bottom - top_low_row
946 : 0;
947 const double bottom = unscaled_y_bottom > top_low_row
948 ? unscaled_y_bottom - floor(unscaled_y_bottom)
949 : 0.0;
950 for (x = 0; x < spu->scaled_width; ++x) {
951 const double unscaled_x = x * inv_scalex;
952 const double unscaled_x_right = unscaled_x + inv_scalex;
953 const unsigned int left_right_column = FFMIN(unscaled_x_right, unscaled_x + 1.0);
954 const double left = left_right_column - unscaled_x;
955 const unsigned int width = unscaled_x_right > left_right_column
956 ? (unsigned int) unscaled_x_right - left_right_column
957 : 0;
958 const double right = unscaled_x_right > left_right_column
959 ? unscaled_x_right - floor(unscaled_x_right)
960 : 0.0;
961 double color = 0.0;
962 double alpha = 0.0;
963 double tmp;
964 unsigned int base;
965 /* Now use these informations to compute a good alpha,
966 and lightness. The sum is on each of the 9
967 region's surface and alpha and lightness.
969 transformed alpha = sum(surface * alpha) / sum(surface)
970 transformed color = sum(surface * alpha * color) / sum(surface * alpha)
972 /* 1: top left part */
973 base = spu->stride * (unsigned int) unscaled_y;
974 tmp = left * top * canon_alpha(spu->aimage[base + (unsigned int) unscaled_x]);
975 alpha += tmp;
976 color += tmp * spu->image[base + (unsigned int) unscaled_x];
977 /* 2: top center part */
978 if (width > 0) {
979 unsigned int walkx;
980 for (walkx = left_right_column; walkx < (unsigned int) unscaled_x_right; ++walkx) {
981 base = spu->stride * (unsigned int) unscaled_y + walkx;
982 tmp = /* 1.0 * */ top * canon_alpha(spu->aimage[base]);
983 alpha += tmp;
984 color += tmp * spu->image[base];
987 /* 3: top right part */
988 if (right > 0.0) {
989 base = spu->stride * (unsigned int) unscaled_y + (unsigned int) unscaled_x_right;
990 tmp = right * top * canon_alpha(spu->aimage[base]);
991 alpha += tmp;
992 color += tmp * spu->image[base];
994 /* 4: center left part */
995 if (height > 0) {
996 unsigned int walky;
997 for (walky = top_low_row; walky < (unsigned int) unscaled_y_bottom; ++walky) {
998 base = spu->stride * walky + (unsigned int) unscaled_x;
999 tmp = left /* * 1.0 */ * canon_alpha(spu->aimage[base]);
1000 alpha += tmp;
1001 color += tmp * spu->image[base];
1004 /* 5: center part */
1005 if (width > 0 && height > 0) {
1006 unsigned int walky;
1007 for (walky = top_low_row; walky < (unsigned int) unscaled_y_bottom; ++walky) {
1008 unsigned int walkx;
1009 base = spu->stride * walky;
1010 for (walkx = left_right_column; walkx < (unsigned int) unscaled_x_right; ++walkx) {
1011 tmp = /* 1.0 * 1.0 * */ canon_alpha(spu->aimage[base + walkx]);
1012 alpha += tmp;
1013 color += tmp * spu->image[base + walkx];
1017 /* 6: center right part */
1018 if (right > 0.0 && height > 0) {
1019 unsigned int walky;
1020 for (walky = top_low_row; walky < (unsigned int) unscaled_y_bottom; ++walky) {
1021 base = spu->stride * walky + (unsigned int) unscaled_x_right;
1022 tmp = right /* * 1.0 */ * canon_alpha(spu->aimage[base]);
1023 alpha += tmp;
1024 color += tmp * spu->image[base];
1027 /* 7: bottom left part */
1028 if (bottom > 0.0) {
1029 base = spu->stride * (unsigned int) unscaled_y_bottom + (unsigned int) unscaled_x;
1030 tmp = left * bottom * canon_alpha(spu->aimage[base]);
1031 alpha += tmp;
1032 color += tmp * spu->image[base];
1034 /* 8: bottom center part */
1035 if (width > 0 && bottom > 0.0) {
1036 unsigned int walkx;
1037 base = spu->stride * (unsigned int) unscaled_y_bottom;
1038 for (walkx = left_right_column; walkx < (unsigned int) unscaled_x_right; ++walkx) {
1039 tmp = /* 1.0 * */ bottom * canon_alpha(spu->aimage[base + walkx]);
1040 alpha += tmp;
1041 color += tmp * spu->image[base + walkx];
1044 /* 9: bottom right part */
1045 if (right > 0.0 && bottom > 0.0) {
1046 base = spu->stride * (unsigned int) unscaled_y_bottom + (unsigned int) unscaled_x_right;
1047 tmp = right * bottom * canon_alpha(spu->aimage[base]);
1048 alpha += tmp;
1049 color += tmp * spu->image[base];
1051 /* Finally mix these transparency and brightness information suitably */
1052 base = spu->scaled_stride * y + x;
1053 spu->scaled_image[base] = alpha > 0 ? color / alpha : 0;
1054 spu->scaled_aimage[base] = alpha * scalex * scaley / 0x10000;
1055 if (spu->scaled_aimage[base]) {
1056 spu->scaled_aimage[base] = 256 - spu->scaled_aimage[base];
1057 if (spu->scaled_aimage[base] + spu->scaled_image[base] > 255)
1058 spu->scaled_image[base] = 256 - spu->scaled_aimage[base];
1064 nothing_to_do:
1065 /* Kludge: draw_alpha needs width multiple of 8. */
1066 if (spu->scaled_width < spu->scaled_stride)
1067 for (y = 0; y < spu->scaled_height; ++y) {
1068 memset(spu->scaled_aimage + y * spu->scaled_stride + spu->scaled_width, 0,
1069 spu->scaled_stride - spu->scaled_width);
1071 spu->scaled_frame_width = dxs;
1072 spu->scaled_frame_height = dys;
1075 if (spu->scaled_image){
1076 switch (spu_alignment) {
1077 case 0:
1078 spu->scaled_start_row = dys*sub_pos/100;
1079 if (spu->scaled_start_row + spu->scaled_height > dys)
1080 spu->scaled_start_row = dys - spu->scaled_height;
1081 break;
1082 case 1:
1083 spu->scaled_start_row = dys*sub_pos/100 - spu->scaled_height/2;
1084 if (sub_pos >= 50 && spu->scaled_start_row + spu->scaled_height > dys)
1085 spu->scaled_start_row = dys - spu->scaled_height;
1086 break;
1087 case 2:
1088 spu->scaled_start_row = dys*sub_pos/100 - spu->scaled_height;
1089 break;
1091 draw_alpha(spu->scaled_start_col, spu->scaled_start_row, spu->scaled_width, spu->scaled_height,
1092 spu->scaled_image, spu->scaled_aimage, spu->scaled_stride);
1093 spu->spu_changed = 0;
1097 else
1099 mp_msg(MSGT_SPUDEC,MSGL_DBG2,"SPU not displayed: start_pts=%d end_pts=%d now_pts=%d\n",
1100 spu->start_pts, spu->end_pts, spu->now_pts);
1104 void spudec_update_palette(void * this, unsigned int *palette)
1106 spudec_handle_t *spu = (spudec_handle_t *) this;
1107 if (spu && palette) {
1108 memcpy(spu->global_palette, palette, sizeof(spu->global_palette));
1109 if(spu->hw_spu)
1110 spu->hw_spu->control(VOCTRL_SET_SPU_PALETTE,spu->global_palette);
1114 void spudec_set_font_factor(void * this, double factor)
1116 spudec_handle_t *spu = (spudec_handle_t *) this;
1117 spu->font_start_level = (int)(0xF0-(0xE0*factor));
1120 static void spudec_parse_extradata(spudec_handle_t *this,
1121 uint8_t *extradata, int extradata_len)
1123 uint8_t *buffer, *ptr;
1124 unsigned int *pal = this->global_palette, *cuspal = this->cuspal;
1125 unsigned int tridx;
1126 int i;
1128 if (extradata_len == 16*4) {
1129 for (i=0; i<16; i++)
1130 pal[i] = AV_RB32(extradata + i*4);
1131 this->auto_palette = 0;
1132 return;
1135 if (!(ptr = buffer = malloc(extradata_len+1)))
1136 return;
1137 memcpy(buffer, extradata, extradata_len);
1138 buffer[extradata_len] = 0;
1140 do {
1141 sscanf(ptr, "size: %dx%d", &this->orig_frame_width, &this->orig_frame_height);
1142 if (sscanf(ptr, "palette: %x, %x, %x, %x, %x, %x, %x, %x,"
1143 " %x, %x, %x, %x, %x, %x, %x, %x",
1144 &pal[ 0], &pal[ 1], &pal[ 2], &pal[ 3],
1145 &pal[ 4], &pal[ 5], &pal[ 6], &pal[ 7],
1146 &pal[ 8], &pal[ 9], &pal[10], &pal[11],
1147 &pal[12], &pal[13], &pal[14], &pal[15]) == 16) {
1148 for (i=0; i<16; i++)
1149 pal[i] = vobsub_palette_to_yuv(pal[i]);
1150 this->auto_palette = 0;
1152 if (!strncasecmp(ptr, "forced subs: on", 15))
1153 this->forced_subs_only = 1;
1154 if (sscanf(ptr, "custom colors: ON, tridx: %x, colors: %x, %x, %x, %x",
1155 &tridx, cuspal+0, cuspal+1, cuspal+2, cuspal+3) == 5) {
1156 for (i=0; i<4; i++) {
1157 cuspal[i] = vobsub_rgb_to_yuv(cuspal[i]);
1158 if (tridx & (1 << (12-4*i)))
1159 cuspal[i] |= 1 << 31;
1161 this->custom = 1;
1163 } while ((ptr=strchr(ptr,'\n')) && *++ptr);
1165 free(buffer);
1168 void *spudec_new_scaled(unsigned int *palette, unsigned int frame_width, unsigned int frame_height, uint8_t *extradata, int extradata_len)
1170 spudec_handle_t *this = calloc(1, sizeof(spudec_handle_t));
1171 if (this){
1172 this->orig_frame_height = frame_height;
1173 // set up palette:
1174 if (palette)
1175 memcpy(this->global_palette, palette, sizeof(this->global_palette));
1176 else
1177 this->auto_palette = 1;
1178 if (extradata)
1179 spudec_parse_extradata(this, extradata, extradata_len);
1180 /* XXX Although the video frame is some size, the SPU frame is
1181 always maximum size i.e. 720 wide and 576 or 480 high */
1182 this->orig_frame_width = 720;
1183 if (this->orig_frame_height == 480 || this->orig_frame_height == 240)
1184 this->orig_frame_height = 480;
1185 else
1186 this->orig_frame_height = 576;
1188 else
1189 mp_msg(MSGT_SPUDEC,MSGL_FATAL, "FATAL: spudec_init: calloc");
1190 return this;
1193 void *spudec_new(unsigned int *palette)
1195 return spudec_new_scaled(palette, 0, 0, NULL, 0);
1198 void spudec_free(void *this)
1200 spudec_handle_t *spu = (spudec_handle_t*)this;
1201 if (spu) {
1202 while (spu->queue_head)
1203 spudec_free_packet(spudec_dequeue_packet(spu));
1204 if (spu->packet)
1205 free(spu->packet);
1206 if (spu->scaled_image)
1207 free(spu->scaled_image);
1208 if (spu->image)
1209 free(spu->image);
1210 free(spu);
1214 void spudec_set_hw_spu(void *this, const vo_functions_t *hw_spu)
1216 spudec_handle_t *spu = (spudec_handle_t*)this;
1217 if (!spu)
1218 return;
1219 spu->hw_spu = hw_spu;
1220 hw_spu->control(VOCTRL_SET_SPU_PALETTE,spu->global_palette);