1 /*
2 * a64 video encoder - multicolor modes
3 * Copyright (c) 2009 Tobias Bindhammer
4 *
5 * This file is part of FFmpeg.
6 *
7 * FFmpeg is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * FFmpeg is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with FFmpeg; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22 /**
23 * @file
24 * a64 video encoder - multicolor modes
25 */
26
34
36 #define CHARSET_CHARS 256
38 #define CROP_SCREENS 1
39
42
44 /* variables for multicolor modes */
57
58 /* pts of the next packet that will be output */
61
62 /* gray gradient */
63 static const int mc_colors[5]={0x0,0xb,0xc,0xf,0x1};
64
65 /* other possible gradients - to be tested */
66 //static const int mc_colors[5]={0x0,0x8,0xa,0xf,0x7};
67 //static const int mc_colors[5]={0x0,0x9,0x8,0xa,0x3};
68
70 {
71 int blockx, blocky, x,
y;
72 int luma = 0;
76
77 for (blocky = 0; blocky <
C64YRES; blocky += 8) {
78 for (blockx = 0; blockx <
C64XRES; blockx += 8) {
79 for (y = blocky; y < blocky + 8 && y <
C64YRES; y++) {
80 for (x = blockx; x < blockx + 8 && x <
C64XRES; x += 2) {
81 if(x < width && y < height) {
82 if (x + 1 < width) {
83 /* build average over 2 pixels */
84 luma = (src[(x + 0 + y * p->
linesize[0])] +
85 src[(x + 1 + y * p->
linesize[0])]) / 2;
86 } else {
87 luma = src[(x + y * p->
linesize[0])];
88 }
89 /* write blocks as linear data now so they are suitable for elbg */
90 dest[0] = luma;
91 }
92 dest++;
93 }
94 }
95 }
96 }
97 }
98
101 {
107 int lowdiff, highdiff;
112 int i;
114
115 /* generate lookup-tables for dither and index before looping */
116 i = 0;
117 for (a=0; a < 256; a++) {
118 if(i < c->mc_pal_size -1 && a == c->
mc_luma_vals[i + 1]) {
122 }
123 i++;
124 }
128 }
129
130 /* and render charset */
132 lowdiff = 0;
133 highdiff = 0;
134 for (y = 0; y < 8; y++) {
135 row1 = 0; row2 = 0;
136 for (x = 0; x < 4; x++) {
137 pix = best_cb[y * 4 + x];
138
139 /* accumulate error for brightest/darkest color */
140 if (index1[pix] >= 3)
142 if (index1[pix] < 1)
144
145 row1 <<= 2;
146
148 row2 <<= 2;
150 row1 |= 3-(index2[pix] & 3);
151 else
152 row1 |= 3-(index1[pix] & 3);
153
155 row2 |= 3-(index2[pix] & 3);
156 else
157 row2 |= 3-(index1[pix] & 3);
158 }
159 else {
161 row1 |= 3-(index2[pix] & 3);
162 else
163 row1 |= 3-(index1[pix] & 3);
164 }
165 }
166 charset[y+0x000] = row1;
168 }
169 /* do we need to adjust pixels? */
170 if (highdiff > 0 && lowdiff > 0 && c->
mc_use_5col) {
171 if (lowdiff > highdiff) {
172 for (x = 0; x < 32; x++)
174 } else {
175 for (x = 0; x < 32; x++)
177 }
178 charpos--; /* redo now adjusted char */
179 /* no adjustment needed, all fine */
180 } else {
181 /* advance pointers */
182 best_cb += 32;
183 charset += 8;
184
185 /* remember colorram value */
186 colrammap[charpos] = (highdiff > 0);
187 }
188 }
189 }
190
192 {
199 return 0;
200 }
201
203 {
207
210 } else {
212 }
213
215
219
220 /* precalc luma values for later use */
225 }
226
234 }
235
236 /* set up extradata */
240 }
244
247
249
250 return 0;
251 }
252
254 {
257 /* only needs to be done in 5col mode */
258 /* XXX could be squeezed to 0x80 bytes */
259 for (a = 0; a < 256; a++) {
260 temp = colram[charmap[a + 0x000]] << 0;
261 temp |= colram[charmap[a + 0x100]] << 1;
262 temp |= colram[charmap[a + 0x200]] << 2;
263 if (a < 0xe8) temp |= colram[charmap[a + 0x300]] << 3;
265 }
266 }
267
269 const AVFrame *p,
int *got_packet)
270 {
272
275 int b_height;
276 int b_width;
277
280
286
289 int screen_size;
290
294 screen_size = b_width * b_height;
295 } else {
298 screen_size = 0x400;
299 }
300
301 /* no data, means end encoding asap */
302 if (!p) {
303 /* all done, end encoding */
305 /* no more frames in queue, prepare to flush remaining frames */
308 }
309 /* still frames in queue so limit lifetime to remaining frames */
311 /* still new data available */
312 } else {
313 /* fill up mc_meta_charset with data until lifetime exceeds */
319 /* lifetime is not reached so wait for next frame first */
320 return 0;
321 }
322 }
323
324 /* lifetime reached so now convert X frames at once */
326 req_size = 0;
327 /* any frames to encode? */
329 int alloc_size = charset_size + c->
mc_lifetime*(screen_size + colram_size);
331 return ret;
333
334 /* calc optimal new charset + charmaps */
337 if (ret < 0)
341 if (ret < 0)
343
344 /* create colorram map and a c64 readable charset */
346
347 /* copy charset to buf */
348 memcpy(buf, charset, charset_size);
349
350 /* advance pointers */
351 buf += charset_size;
352 req_size += charset_size;
353 }
354
355 /* write x frames to buf */
357 /* copy charmap to buf. buf is uchar*, charmap is int*, so no memcpy here, sorry */
358 for (y = 0; y < b_height; y++) {
359 for (x = 0; x < b_width; x++) {
360 buf[y * b_width + x] = charmap[y * b_width + x];
361 }
362 }
363 /* advance pointers */
364 buf += screen_size;
365 req_size += screen_size;
366
367 /* compress and copy colram to buf */
370 /* advance pointers */
371 buf += colram_size;
372 req_size += colram_size;
373 }
374
375 /* advance to next charmap */
376 charmap += 1000;
377 }
378
382
383 /* reset counter */
385
388
390 pkt->
size = req_size;
392 *got_packet = !!req_size;
393 }
394 return 0;
395 }
396
397 #if CONFIG_A64MULTI_ENCODER
398 AVCodec ff_a64multi_encoder = {
409 };
410 #endif
411 #if CONFIG_A64MULTI5_ENCODER
412 AVCodec ff_a64multi5_encoder = {
414 .long_name =
NULL_IF_CONFIG_SMALL(
"Multicolor charset for Commodore 64, extended with 5th color (colram)"),
423 };
424 #endif