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