1 /*
2  * Copyright (C) 2000-2020 the xine project
3  *
4  * This file is part of xine, a free video player.
5  *
6  * xine is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * xine is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
19  */
20 
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24 
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <float.h>
28 
29 #define LOG_MODULE "dxr3_spu_encoder"
30 /* #define LOG_VERBOSE */
31 /* #define LOG */
32 
33 #include "video_out_dxr3.h"
34 
35 /* We use the following algorithm to reduce the given overlay palette
36  * to a spu palette with only four distinct colours:
37  *  - create a histogram on the overlay palette
38  *  - the color with the maximum histogram value becomes one spu color
39  *  - modify the histogram so that the counts for colors very near to the
40  *    chosen one are lowered; this is done by multiplying with a penalty
41  *    function 1-1/(dist/DIST_COEFF + 1) where dist is the squared spatial
42  *    distance between current color and chosen spu color
43  *  - continue with the next maximum
44  * The used histogram modification function from above looks like that:
45  *    ^
46  *  1 +              ********
47  *    |        ******
48  *    |    ****
49  *    |  **
50  *    | *
51  *  0 **--------------------> dist
52  */
53 #define DIST_COEFF 1024.0
54 
55 
56 /* spu encoder function */
57 spu_encoder_t *dxr3_spu_encoder_init(void);
58 void           dxr3_spu_encode(spu_encoder_t *this);
59 
60 /* helper functions */
61 static void    convert_palette(spu_encoder_t *this);
62 static void    create_histogram(spu_encoder_t *this);
63 static void    generate_clut(spu_encoder_t *this);
64 static void    map_colors(spu_encoder_t *this);
65 static void    convert_clut(spu_encoder_t *this);
66 static void    convert_overlay(spu_encoder_t *this);
67 static void    write_rle(spu_encoder_t *this, int *offset, int *higher_nibble, int length, int color);
68 static void    write_byte(spu_encoder_t *this, int *offset, uint8_t byte);
69 static void    write_nibble(spu_encoder_t *this, int *offset, int *higher_nibble, uint8_t nibble);
70 
71 
dxr3_spu_encoder_init(void)72 spu_encoder_t *dxr3_spu_encoder_init(void)
73 {
74   spu_encoder_t *this;
75 
76   this = (spu_encoder_t *)malloc(sizeof(spu_encoder_t));
77   this->target        = NULL;
78   this->need_reencode = 0;
79   this->malloc_size   = 0;
80   lprintf("initialized\n");
81   return this;
82 }
83 
dxr3_spu_encode(spu_encoder_t * this)84 void dxr3_spu_encode(spu_encoder_t *this)
85 {
86   if (!this->need_reencode || !this->overlay) return;
87   lprintf("overlay for encoding arrived.\n");
88   convert_palette(this);
89   create_histogram(this);
90   generate_clut(this);
91   map_colors(this);
92   convert_clut(this);
93   convert_overlay(this);
94   lprintf("overlay encoding completed\n");
95 }
96 
97 
convert_palette(spu_encoder_t * this)98 static void convert_palette(spu_encoder_t *this)
99 {
100   int i, y, cb, cr, r, g, b;
101 
102   if (!this->overlay->rgb_clut) {
103     for (i = 0; i < OVL_PALETTE_SIZE; i++) {
104       y  = (this->overlay->color[i] >> 16) & 0xff;
105       cr = (this->overlay->color[i] >>  8) & 0xff;
106       cb = (this->overlay->color[i]      ) & 0xff;
107       r  = 1.164 * y + 1.596 * (cr - 128);
108       g  = 1.164 * y - 0.813 * (cr - 128) - 0.392 * (cb - 128);
109       b  = 1.164 * y + 2.017 * (cb - 128);
110       if (r < 0) r = 0;
111       if (g < 0) g = 0;
112       if (b < 0) b = 0;
113       if (r > 0xff) r = 0xff;
114       if (g > 0xff) g = 0xff;
115       if (b > 0xff) b = 0xff;
116       this->overlay->color[i] = (r << 16) | (g << 8) | b;
117     }
118     this->overlay->rgb_clut = 1;
119   }
120   if (!this->overlay->hili_rgb_clut) {
121     for (i = 0; i < OVL_PALETTE_SIZE; i++) {
122       y  = (this->overlay->hili_color[i] >> 16) & 0xff;
123       cr = (this->overlay->hili_color[i] >>  8) & 0xff;
124       cb = (this->overlay->hili_color[i]      ) & 0xff;
125       r  = 1.164 * y + 1.596 * (cr - 128);
126       g  = 1.164 * y - 0.813 * (cr - 128) - 0.392 * (cb - 128);
127       b  = 1.164 * y + 2.017 * (cb - 128);
128       if (r < 0) r = 0;
129       if (g < 0) g = 0;
130       if (b < 0) b = 0;
131       if (r > 0xff) r = 0xff;
132       if (g > 0xff) g = 0xff;
133       if (b > 0xff) b = 0xff;
134       this->overlay->hili_color[i] = (r << 16) | (g << 8) | b;
135     }
136     this->overlay->hili_rgb_clut = 1;
137   }
138 }
139 
create_histogram(spu_encoder_t * this)140 static void create_histogram(spu_encoder_t *this)
141 {
142   rle_elem_t *rle;
143   int i, x, y, len, part;
144 
145   for (i = 0; i < OVL_PALETTE_SIZE; i++)
146     this->map[i] = this->clip_map[i] = 0;
147   x = y = 0;
148   for (i = 0, rle = this->overlay->rle; i < this->overlay->num_rle; i++, rle++) {
149     len = rle->len;
150     if (y >= this->overlay->hili_top && y < this->overlay->hili_bottom) {
151       if (x < this->overlay->hili_left) {
152         part = (this->overlay->hili_left - x < len) ? (this->overlay->hili_left - x) : len;
153         this->map[rle->color] += part;
154         len -= part;
155         x += part;
156       }
157       if (x >= this->overlay->hili_left && x < this->overlay->hili_right) {
158         part = (this->overlay->hili_right - x < len) ? (this->overlay->hili_right - x) : len;
159         this->clip_map[rle->color] += part;
160         len -= part;
161         x += part;
162       }
163     }
164     this->map[rle->color] += len;
165     x += len;
166     if (x >= this->overlay->width) {
167       x = 0;
168       y++;
169     }
170   }
171 #ifdef LOG
172   for (i = 0; i < OVL_PALETTE_SIZE; i++)
173     if (this->map[i])
174       lprintf("histogram: colour #%d 0x%.8x appears %d times\n",
175 	     i, this->overlay->color[i], this->map[i]);
176   for (i = 0; i < OVL_PALETTE_SIZE; i++)
177     if (this->clip_map[i])
178       lprintf("histogram: clip colour #%d 0x%.8x appears %d times\n",
179         i, this->overlay->hili_color[i], this->clip_map[i]);
180 #endif
181 }
182 
generate_clut(spu_encoder_t * this)183 static void generate_clut(spu_encoder_t *this)
184 {
185   int i, max, spu_color;
186   double dist, diff;
187 
188   /* find first maximum -> first spu color */
189   max = 0;
190   for (i = 1; i < OVL_PALETTE_SIZE; i++)
191     if (this->map[i] > this->map[max]) max = i;
192   this->color[0] = this->overlay->color[max];
193   this->trans[0] = this->overlay->trans[max];
194 
195   for (spu_color = 1; spu_color < 4; spu_color++) {
196     /* modify histogram and find next maximum -> next spu color */
197     max = 0;
198     for (i = 0; i < OVL_PALETTE_SIZE; i++) {
199       /* subtract a correction based on the distance to the last spu color */
200       diff  = ((this->overlay->color[i]      ) & 0xff) - ((this->color[spu_color - 1]      ) & 0xff);
201       dist  = diff * diff;
202       diff  = ((this->overlay->color[i] >>  8) & 0xff) - ((this->color[spu_color - 1] >>  8) & 0xff);
203       dist += diff * diff;
204       diff  = ((this->overlay->color[i] >> 16) & 0xff) - ((this->color[spu_color - 1] >> 16) & 0xff);
205       dist += diff * diff;
206       diff  = ((this->overlay->trans[i]      )       ) - ((this->trans[spu_color - 1]      )       );
207       dist += diff * diff;
208       this->map[i] *= 1 - 1.0 / (dist / DIST_COEFF + 1.0);
209       if (this->map[i] > this->map[max]) max = i;
210     }
211     this->color[spu_color] = this->overlay->color[max];
212     this->trans[spu_color] = this->overlay->trans[max];
213   }
214 #ifdef LOG
215   for (spu_color = 0; spu_color < 4; spu_color++)
216     lprintf("spu colour %d: 0x%.8x, trans: %d\n", spu_color,
217       this->color[spu_color], this->trans[spu_color]);
218 #endif
219 
220   /* now the same stuff again, this time for the palette of the clipping area */
221 
222   /* find first maximum -> first spu color */
223   max = 0;
224   for (i = 1; i < OVL_PALETTE_SIZE; i++)
225     if (this->clip_map[i] > this->clip_map[max]) max = i;
226   this->hili_color[0] = this->overlay->hili_color[max];
227   this->hili_trans[0] = this->overlay->hili_trans[max];
228 
229   for (spu_color = 1; spu_color < 4; spu_color++) {
230     /* modify histogram and find next maximum -> next spu color */
231     max = 0;
232     for (i = 0; i < OVL_PALETTE_SIZE; i++) {
233       /* subtract a correction based on the distance to the last spu color */
234       diff  = ((this->overlay->hili_color[i]      ) & 0xff) - ((this->hili_color[spu_color - 1]      ) & 0xff);
235       dist  = diff * diff;
236       diff  = ((this->overlay->hili_color[i] >>  8) & 0xff) - ((this->hili_color[spu_color - 1] >>  8) & 0xff);
237       dist += diff * diff;
238       diff  = ((this->overlay->hili_color[i] >> 16) & 0xff) - ((this->hili_color[spu_color - 1] >> 16) & 0xff);
239       dist += diff * diff;
240       diff  = ((this->overlay->hili_trans[i]      )       ) - ((this->hili_trans[spu_color - 1]      )       );
241       dist += diff * diff;
242       this->clip_map[i] *= 1 - 1.0 / (dist / DIST_COEFF + 1.0);
243       if (this->clip_map[i] > this->clip_map[max]) max = i;
244     }
245     this->hili_color[spu_color] = this->overlay->hili_color[max];
246     this->hili_trans[spu_color] = this->overlay->hili_trans[max];
247   }
248 #ifdef LOG
249   for (spu_color = 0; spu_color < 4; spu_color++)
250     lprintf("spu clip colour %d: 0x%.8x, trans: %d\n", spu_color,
251       this->hili_color[spu_color], this->hili_trans[spu_color]);
252 #endif
253 }
254 
map_colors(spu_encoder_t * this)255 static void map_colors(spu_encoder_t *this)
256 {
257   int i, min, spu_color;
258   double dist, diff, min_dist;
259 
260   /* for all colors in overlay palette find closest spu color */
261   for (i = 0; i < OVL_PALETTE_SIZE; i++) {
262     min = 0;
263     min_dist = DBL_MAX;
264     for (spu_color = 0; spu_color < 4; spu_color++) {
265       diff  = ((this->overlay->color[i]      ) & 0xff) - ((this->color[spu_color]      ) & 0xff);
266       dist  = diff * diff;
267       diff  = ((this->overlay->color[i] >>  8) & 0xff) - ((this->color[spu_color] >>  8) & 0xff);
268       dist += diff * diff;
269       diff  = ((this->overlay->color[i] >> 16) & 0xff) - ((this->color[spu_color] >> 16) & 0xff);
270       dist += diff * diff;
271       diff  = ((this->overlay->trans[i]      )       ) - ((this->trans[spu_color]      )       );
272       dist += diff * diff;
273       if (dist < min_dist) {
274         min_dist = dist;
275 	min = spu_color;
276       }
277     }
278     this->map[i] = min;
279   }
280 
281   /* for all colors in overlay clip palette find closest spu color */
282   for (i = 0; i < OVL_PALETTE_SIZE; i++) {
283     min = 0;
284     min_dist = DBL_MAX;
285     for (spu_color = 0; spu_color < 4; spu_color++) {
286       diff  = ((this->overlay->hili_color[i]      ) & 0xff) - ((this->hili_color[spu_color]      ) & 0xff);
287       dist  = diff * diff;
288       diff  = ((this->overlay->hili_color[i] >>  8) & 0xff) - ((this->hili_color[spu_color] >>  8) & 0xff);
289       dist += diff * diff;
290       diff  = ((this->overlay->hili_color[i] >> 16) & 0xff) - ((this->hili_color[spu_color] >> 16) & 0xff);
291       dist += diff * diff;
292       diff  = ((this->overlay->hili_trans[i]      )       ) - ((this->hili_trans[spu_color]      )       );
293       dist += diff * diff;
294       if (dist < min_dist) {
295         min_dist = dist;
296 	min = spu_color;
297       }
298     }
299     this->clip_map[i] = min;
300   }
301 }
302 
convert_clut(spu_encoder_t * this)303 static void convert_clut(spu_encoder_t *this)
304 {
305   int i, r, g, b, y, cb, cr;
306 
307   for (i = 0; i < 4; i++) {
308     r  = (this->color[i] >> 16) & 0xff;
309     g  = (this->color[i] >>  8) & 0xff;
310     b  = (this->color[i]      ) & 0xff;
311     y  =  0.257 * r + 0.504 * g + 0.098 * b;
312     cr =  0.439 * r - 0.368 * g - 0.071 * b + 128;
313     cb = -0.148 * r - 0.291 * g + 0.439 * b + 128;
314     this->color[i] = (y << 16) | (cr << 8) | cb;
315   }
316   for (i = 4; i < 16; i++)
317     this->color[i] = 0x00008080;
318 
319   for (i = 0; i < 4; i++) {
320     r  = (this->hili_color[i] >> 16) & 0xff;
321     g  = (this->hili_color[i] >>  8) & 0xff;
322     b  = (this->hili_color[i]      ) & 0xff;
323     y  =  0.257 * r + 0.504 * g + 0.098 * b;
324     cr =  0.439 * r - 0.368 * g - 0.071 * b + 128;
325     cb = -0.148 * r - 0.291 * g + 0.439 * b + 128;
326     this->hili_color[i] = (y << 16) | (cr << 8) | cb;
327   }
328   for (i = 4; i < 16; i++)
329     this->hili_color[i] = 0x00008080;
330 }
331 
convert_overlay(spu_encoder_t * this)332 static void convert_overlay(spu_encoder_t *this)
333 {
334   int offset = 0, field_start[2];
335   rle_elem_t *rle;
336   int field, i, len, part, x, y, higher_nibble = 1;
337 
338   /* size will be determined later */
339   write_byte(this, &offset, 0x00);
340   write_byte(this, &offset, 0x00);
341 
342   /* control sequence pointer will be determined later */
343   write_byte(this, &offset, 0x00);
344   write_byte(this, &offset, 0x00);
345 
346   for (field = 0; field < 2; field++) {
347     write_byte(this, &offset, 0x00);
348     write_byte(this, &offset, 0x00);
349     lprintf("encoding field %d\n", field);
350     field_start[field] = offset;
351     x = y = 0;
352     for (i = 0, rle = this->overlay->rle; i < this->overlay->num_rle; i++, rle++) {
353       len = rle->len;
354       if ((y & 1) == field) {
355         if (y >= this->overlay->hili_top && y < this->overlay->hili_bottom) {
356           if (x < this->overlay->hili_left) {
357             part = (this->overlay->hili_left - x < len) ? (this->overlay->hili_left - x) : len;
358 	    write_rle(this, &offset, &higher_nibble, part, this->map[rle->color]);
359             len -= part;
360             x += part;
361           }
362           if (x >= this->overlay->hili_left && x < this->overlay->hili_right) {
363             part = (this->overlay->hili_right - x < len) ? (this->overlay->hili_right - x) : len;
364             write_rle(this, &offset, &higher_nibble, part, this->clip_map[rle->color]);
365             len -= part;
366             x += part;
367           }
368         }
369         write_rle(this, &offset, &higher_nibble, len, this->map[rle->color]);
370       }
371       x += len;
372       if (x >= this->overlay->width) {
373         if ((y & 1) == field && !higher_nibble)
374 	  write_nibble(this, &offset, &higher_nibble, 0);
375         x = 0;
376         y++;
377       }
378     }
379   }
380 
381   /* we should be byte aligned here */
382   _x_assert(higher_nibble);
383 
384   /* control sequence starts here */
385   this->target[2] = offset >> 8;
386   this->target[3] = offset & 0xff;
387   write_byte(this, &offset, 0x00);
388   write_byte(this, &offset, 0x00);
389   /* write pointer to end sequence */
390   write_byte(this, &offset, this->target[2]);
391   write_byte(this, &offset, this->target[3]);
392   /* write control sequence */
393   write_byte(this, &offset, 0x00);
394   /* clut indices */
395   write_byte(this, &offset, 0x03);
396   write_byte(this, &offset, 0x32);
397   write_byte(this, &offset, 0x10);
398   /* alpha information */
399   write_byte(this, &offset, 0x04);
400   write_nibble(this, &offset, &higher_nibble, this->trans[3] & 0xf);
401   write_nibble(this, &offset, &higher_nibble, this->trans[2] & 0xf);
402   write_nibble(this, &offset, &higher_nibble, this->trans[1] & 0xf);
403   write_nibble(this, &offset, &higher_nibble, this->trans[0] & 0xf);
404   /* on screen position */
405   lprintf("overlay position: x %d, y %d, width %d, height %d\n",
406     this->overlay->x, this->overlay->y, this->overlay->width, this->overlay->height);
407   write_byte(this, &offset, 0x05);
408   write_byte(this, &offset, this->overlay->x >> 4);
409   write_nibble(this, &offset, &higher_nibble, this->overlay->x & 0xf);
410   write_nibble(this, &offset, &higher_nibble, (this->overlay->x + this->overlay->width - 1) >> 8);
411   write_byte(this, &offset, (this->overlay->x + this->overlay->width - 1) & 0xff);
412   write_byte(this, &offset, this->overlay->y >> 4);
413   write_nibble(this, &offset, &higher_nibble, this->overlay->y & 0xf);
414   write_nibble(this, &offset, &higher_nibble, (this->overlay->y + this->overlay->height - 1) >> 8);
415   write_byte(this, &offset, (this->overlay->y + this->overlay->height - 1) & 0xff);
416   /* field pointers */
417   write_byte(this, &offset, 0x06);
418   write_byte(this, &offset, field_start[0] >> 8);
419   write_byte(this, &offset, field_start[0] & 0xff);
420   write_byte(this, &offset, field_start[1] >> 8);
421   write_byte(this, &offset, field_start[1] & 0xff);
422   /* end marker */
423   write_byte(this, &offset, 0xff);
424   if (offset & 1)
425     write_byte(this, &offset, 0xff);
426   /* write size information */
427   this->size = offset;
428   this->target[0] = offset >> 8;
429   this->target[1] = offset & 0xff;
430 }
431 
write_rle(spu_encoder_t * this,int * offset,int * higher_nibble,int length,int color)432 static void write_rle(spu_encoder_t *this, int *offset, int *higher_nibble, int length, int color)
433 {
434   if (!length) return;
435   length <<= 2;
436   while (length > 0x03fc) {
437     write_nibble(this, offset, higher_nibble, 0x0);
438     write_nibble(this, offset, higher_nibble, 0x3);
439     write_nibble(this, offset, higher_nibble, 0xf);
440     write_nibble(this, offset, higher_nibble, 0xc | color);
441     length -= 0x03fc;
442   }
443   if ((length & ~0xc) == 0) {
444     write_nibble(this, offset, higher_nibble, length | color);
445     return;
446   }
447   if ((length & ~0x3c) == 0) {
448     write_nibble(this, offset, higher_nibble, length >> 4);
449     write_nibble(this, offset, higher_nibble, (length & 0xc) | color);
450     return;
451   }
452   if ((length & ~0xfc) == 0) {
453     write_nibble(this, offset, higher_nibble, 0x0);
454     write_nibble(this, offset, higher_nibble, length >> 4);
455     write_nibble(this, offset, higher_nibble, (length & 0xc) | color);
456     return;
457   }
458   if ((length & ~0x3fc) == 0) {
459     write_nibble(this, offset, higher_nibble, 0x0);
460     write_nibble(this, offset, higher_nibble, length >> 8);
461     write_nibble(this, offset, higher_nibble, (length >> 4) & 0xf);
462     write_nibble(this, offset, higher_nibble, (length & 0xc) | color);
463     return;
464   }
465   _x_assert(length == 0);
466 }
467 
write_byte(spu_encoder_t * this,int * offset,uint8_t byte)468 static void write_byte(spu_encoder_t *this, int *offset, uint8_t byte)
469 {
470   if (*offset >= this->malloc_size)
471     this->target = realloc(this->target, this->malloc_size += 2048);
472   this->target[(*offset)++] = byte;
473 }
474 
write_nibble(spu_encoder_t * this,int * offset,int * higher_nibble,uint8_t nibble)475 static void write_nibble(spu_encoder_t *this, int *offset, int *higher_nibble, uint8_t nibble)
476 {
477   if (*offset >= this->malloc_size)
478     this->target = realloc(this->target, this->malloc_size += 2048);
479   if (*higher_nibble) {
480     this->target[*offset] &= 0x0f;
481     this->target[*offset] |= nibble << 4;
482     *higher_nibble = 0;
483   } else {
484     this->target[*offset] &= 0xf0;
485     this->target[(*offset)++] |= nibble;
486     *higher_nibble = 1;
487   }
488 }
489