00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00027 #include "a64enc.h"
00028 #include "a64colors.h"
00029 #include "a64tables.h"
00030 #include "elbg.h"
00031 #include "internal.h"
00032 #include "libavutil/intreadwrite.h"
00033
00034 #define DITHERSTEPS 8
00035 #define CHARSET_CHARS 256
00036 #define INTERLACED 1
00037 #define CROP_SCREENS 1
00038
00039
00040 static const int mc_colors[5]={0x0,0xb,0xc,0xf,0x1};
00041
00042
00043
00044
00045
00046 static void to_meta_with_crop(AVCodecContext *avctx, AVFrame *p, int *dest)
00047 {
00048 int blockx, blocky, x, y;
00049 int luma = 0;
00050 int height = FFMIN(avctx->height, C64YRES);
00051 int width = FFMIN(avctx->width , C64XRES);
00052 uint8_t *src = p->data[0];
00053
00054 for (blocky = 0; blocky < C64YRES; blocky += 8) {
00055 for (blockx = 0; blockx < C64XRES; blockx += 8) {
00056 for (y = blocky; y < blocky + 8 && y < C64YRES; y++) {
00057 for (x = blockx; x < blockx + 8 && x < C64XRES; x += 2) {
00058 if(x < width && y < height) {
00059
00060 luma = (src[(x + 0 + y * p->linesize[0])] +
00061 src[(x + 1 + y * p->linesize[0])]) / 2;
00062
00063 dest[0] = luma;
00064 }
00065 dest++;
00066 }
00067 }
00068 }
00069 }
00070 }
00071
00072 static void render_charset(AVCodecContext *avctx, uint8_t *charset,
00073 uint8_t *colrammap)
00074 {
00075 A64Context *c = avctx->priv_data;
00076 uint8_t row1, row2;
00077 int charpos, x, y;
00078 int a, b;
00079 uint8_t pix;
00080 int lowdiff, highdiff;
00081 int *best_cb = c->mc_best_cb;
00082 static uint8_t index1[256];
00083 static uint8_t index2[256];
00084 static uint8_t dither[256];
00085 int i;
00086 int distance;
00087
00088
00089 i = 0;
00090 for (a=0; a < 256; a++) {
00091 if(i < c->mc_pal_size -1 && a == c->mc_luma_vals[i + 1]) {
00092 distance = c->mc_luma_vals[i + 1] - c->mc_luma_vals[i];
00093 for(b = 0; b <= distance; b++) {
00094 dither[c->mc_luma_vals[i] + b] = b * (DITHERSTEPS - 1) / distance;
00095 }
00096 i++;
00097 }
00098 if(i >= c->mc_pal_size - 1) dither[a] = 0;
00099 index1[a] = i;
00100 index2[a] = FFMIN(i + 1, c->mc_pal_size - 1);
00101 }
00102
00103
00104 for (charpos = 0; charpos < CHARSET_CHARS; charpos++) {
00105 lowdiff = 0;
00106 highdiff = 0;
00107 for (y = 0; y < 8; y++) {
00108 row1 = 0; row2 = 0;
00109 for (x = 0; x < 4; x++) {
00110 pix = best_cb[y * 4 + x];
00111
00112
00113 if (index1[pix] >= 3)
00114 highdiff += pix - c->mc_luma_vals[3];
00115 if (index1[pix] < 1)
00116 lowdiff += c->mc_luma_vals[1] - pix;
00117
00118 row1 <<= 2;
00119
00120 if (INTERLACED) {
00121 row2 <<= 2;
00122 if (interlaced_dither_patterns[dither[pix]][(y & 3) * 2 + 0][x & 3])
00123 row1 |= 3-(index2[pix] & 3);
00124 else
00125 row1 |= 3-(index1[pix] & 3);
00126
00127 if (interlaced_dither_patterns[dither[pix]][(y & 3) * 2 + 1][x & 3])
00128 row2 |= 3-(index2[pix] & 3);
00129 else
00130 row2 |= 3-(index1[pix] & 3);
00131 }
00132 else {
00133 if (multi_dither_patterns[dither[pix]][(y & 3)][x & 3])
00134 row1 |= 3-(index2[pix] & 3);
00135 else
00136 row1 |= 3-(index1[pix] & 3);
00137 }
00138 }
00139 charset[y+0x000] = row1;
00140 if (INTERLACED) charset[y+0x800] = row2;
00141 }
00142
00143 if (highdiff > 0 && lowdiff > 0 && c->mc_use_5col) {
00144 if (lowdiff > highdiff) {
00145 for (x = 0; x < 32; x++)
00146 best_cb[x] = FFMIN(c->mc_luma_vals[3], best_cb[x]);
00147 } else {
00148 for (x = 0; x < 32; x++)
00149 best_cb[x] = FFMAX(c->mc_luma_vals[1], best_cb[x]);
00150 }
00151 charpos--;
00152
00153 } else {
00154
00155 best_cb += 32;
00156 charset += 8;
00157
00158
00159 colrammap[charpos] = (highdiff > 0);
00160 }
00161 }
00162 }
00163
00164 static av_cold int a64multi_close_encoder(AVCodecContext *avctx)
00165 {
00166 A64Context *c = avctx->priv_data;
00167 av_free(c->mc_meta_charset);
00168 av_free(c->mc_best_cb);
00169 av_free(c->mc_charset);
00170 av_free(c->mc_charmap);
00171 av_free(c->mc_colram);
00172 return 0;
00173 }
00174
00175 static av_cold int a64multi_init_encoder(AVCodecContext *avctx)
00176 {
00177 A64Context *c = avctx->priv_data;
00178 int a;
00179 av_lfg_init(&c->randctx, 1);
00180
00181 if (avctx->global_quality < 1) {
00182 c->mc_lifetime = 4;
00183 } else {
00184 c->mc_lifetime = avctx->global_quality /= FF_QP2LAMBDA;
00185 }
00186
00187 av_log(avctx, AV_LOG_INFO, "charset lifetime set to %d frame(s)\n", c->mc_lifetime);
00188
00189 c->mc_frame_counter = 0;
00190 c->mc_use_5col = avctx->codec->id == CODEC_ID_A64_MULTI5;
00191 c->mc_pal_size = 4 + c->mc_use_5col;
00192
00193
00194 for (a = 0; a < c->mc_pal_size; a++) {
00195 c->mc_luma_vals[a]=a64_palette[mc_colors[a]][0] * 0.30 +
00196 a64_palette[mc_colors[a]][1] * 0.59 +
00197 a64_palette[mc_colors[a]][2] * 0.11;
00198 }
00199
00200 if (!(c->mc_meta_charset = av_malloc(32000 * c->mc_lifetime * sizeof(int))) ||
00201 !(c->mc_best_cb = av_malloc(CHARSET_CHARS * 32 * sizeof(int))) ||
00202 !(c->mc_charmap = av_mallocz(1000 * c->mc_lifetime * sizeof(int))) ||
00203 !(c->mc_colram = av_mallocz(CHARSET_CHARS * sizeof(uint8_t))) ||
00204 !(c->mc_charset = av_malloc(0x800 * (INTERLACED+1) * sizeof(uint8_t)))) {
00205 av_log(avctx, AV_LOG_ERROR, "Failed to allocate buffer memory.\n");
00206 return AVERROR(ENOMEM);
00207 }
00208
00209
00210 if (!(avctx->extradata = av_mallocz(8 * 4 + FF_INPUT_BUFFER_PADDING_SIZE))) {
00211 av_log(avctx, AV_LOG_ERROR, "Failed to allocate memory for extradata.\n");
00212 return AVERROR(ENOMEM);
00213 }
00214 avctx->extradata_size = 8 * 4;
00215 AV_WB32(avctx->extradata, c->mc_lifetime);
00216 AV_WB32(avctx->extradata + 16, INTERLACED);
00217
00218 avcodec_get_frame_defaults(&c->picture);
00219 avctx->coded_frame = &c->picture;
00220 avctx->coded_frame->pict_type = AV_PICTURE_TYPE_I;
00221 avctx->coded_frame->key_frame = 1;
00222 if (!avctx->codec_tag)
00223 avctx->codec_tag = AV_RL32("a64m");
00224
00225 c->next_pts = AV_NOPTS_VALUE;
00226
00227 return 0;
00228 }
00229
00230 static void a64_compress_colram(unsigned char *buf, int *charmap, uint8_t *colram)
00231 {
00232 int a;
00233 uint8_t temp;
00234
00235
00236 for (a = 0; a < 256; a++) {
00237 temp = colram[charmap[a + 0x000]] << 0;
00238 temp |= colram[charmap[a + 0x100]] << 1;
00239 temp |= colram[charmap[a + 0x200]] << 2;
00240 if (a < 0xe8) temp |= colram[charmap[a + 0x300]] << 3;
00241 buf[a] = temp << 2;
00242 }
00243 }
00244
00245 static int a64multi_encode_frame(AVCodecContext *avctx, AVPacket *pkt,
00246 const AVFrame *pict, int *got_packet)
00247 {
00248 A64Context *c = avctx->priv_data;
00249 AVFrame *const p = &c->picture;
00250
00251 int frame;
00252 int x, y;
00253 int b_height;
00254 int b_width;
00255
00256 int req_size, ret;
00257 uint8_t *buf = NULL;
00258
00259 int *charmap = c->mc_charmap;
00260 uint8_t *colram = c->mc_colram;
00261 uint8_t *charset = c->mc_charset;
00262 int *meta = c->mc_meta_charset;
00263 int *best_cb = c->mc_best_cb;
00264
00265 int charset_size = 0x800 * (INTERLACED + 1);
00266 int colram_size = 0x100 * c->mc_use_5col;
00267 int screen_size;
00268
00269 if(CROP_SCREENS) {
00270 b_height = FFMIN(avctx->height,C64YRES) >> 3;
00271 b_width = FFMIN(avctx->width ,C64XRES) >> 3;
00272 screen_size = b_width * b_height;
00273 } else {
00274 b_height = C64YRES >> 3;
00275 b_width = C64XRES >> 3;
00276 screen_size = 0x400;
00277 }
00278
00279
00280 if (!pict) {
00281
00282 if (!c->mc_lifetime) return 0;
00283
00284 if (!c->mc_frame_counter) {
00285 c->mc_lifetime = 0;
00286 }
00287
00288 else c->mc_lifetime = c->mc_frame_counter;
00289
00290 } else {
00291
00292 if (c->mc_frame_counter < c->mc_lifetime) {
00293 *p = *pict;
00294 p->pict_type = AV_PICTURE_TYPE_I;
00295 p->key_frame = 1;
00296 to_meta_with_crop(avctx, p, meta + 32000 * c->mc_frame_counter);
00297 c->mc_frame_counter++;
00298 if (c->next_pts == AV_NOPTS_VALUE)
00299 c->next_pts = pict->pts;
00300
00301 return 0;
00302 }
00303 }
00304
00305
00306 if (c->mc_frame_counter == c->mc_lifetime) {
00307 req_size = 0;
00308
00309 if (c->mc_lifetime) {
00310 req_size = charset_size + c->mc_lifetime*(screen_size + colram_size);
00311 if ((ret = ff_alloc_packet2(avctx, pkt, req_size)) < 0)
00312 return ret;
00313 buf = pkt->data;
00314
00315
00316 ff_init_elbg(meta, 32, 1000 * c->mc_lifetime, best_cb, CHARSET_CHARS, 50, charmap, &c->randctx);
00317 ff_do_elbg (meta, 32, 1000 * c->mc_lifetime, best_cb, CHARSET_CHARS, 50, charmap, &c->randctx);
00318
00319
00320 render_charset(avctx, charset, colram);
00321
00322
00323 memcpy(buf, charset, charset_size);
00324
00325
00326 buf += charset_size;
00327 charset += charset_size;
00328 }
00329
00330
00331 for (frame = 0; frame < c->mc_lifetime; frame++) {
00332
00333 for (y = 0; y < b_height; y++) {
00334 for (x = 0; x < b_width; x++) {
00335 buf[y * b_width + x] = charmap[y * b_width + x];
00336 }
00337 }
00338
00339 buf += screen_size;
00340 req_size += screen_size;
00341
00342
00343 if (c->mc_use_5col) {
00344 a64_compress_colram(buf, charmap, colram);
00345
00346 buf += colram_size;
00347 req_size += colram_size;
00348 }
00349
00350
00351 charmap += 1000;
00352 }
00353
00354 AV_WB32(avctx->extradata + 4, c->mc_frame_counter);
00355 AV_WB32(avctx->extradata + 8, charset_size);
00356 AV_WB32(avctx->extradata + 12, screen_size + colram_size);
00357
00358
00359 c->mc_frame_counter = 0;
00360
00361 pkt->pts = pkt->dts = c->next_pts;
00362 c->next_pts = AV_NOPTS_VALUE;
00363
00364 pkt->size = req_size;
00365 pkt->flags |= AV_PKT_FLAG_KEY;
00366 *got_packet = !!req_size;
00367 }
00368 return 0;
00369 }
00370
00371 AVCodec ff_a64multi_encoder = {
00372 .name = "a64multi",
00373 .type = AVMEDIA_TYPE_VIDEO,
00374 .id = CODEC_ID_A64_MULTI,
00375 .priv_data_size = sizeof(A64Context),
00376 .init = a64multi_init_encoder,
00377 .encode2 = a64multi_encode_frame,
00378 .close = a64multi_close_encoder,
00379 .pix_fmts = (const enum PixelFormat[]) {PIX_FMT_GRAY8, PIX_FMT_NONE},
00380 .long_name = NULL_IF_CONFIG_SMALL("Multicolor charset for Commodore 64"),
00381 .capabilities = CODEC_CAP_DELAY,
00382 };
00383
00384 AVCodec ff_a64multi5_encoder = {
00385 .name = "a64multi5",
00386 .type = AVMEDIA_TYPE_VIDEO,
00387 .id = CODEC_ID_A64_MULTI5,
00388 .priv_data_size = sizeof(A64Context),
00389 .init = a64multi_init_encoder,
00390 .encode2 = a64multi_encode_frame,
00391 .close = a64multi_close_encoder,
00392 .pix_fmts = (const enum PixelFormat[]) {PIX_FMT_GRAY8, PIX_FMT_NONE},
00393 .long_name = NULL_IF_CONFIG_SMALL("Multicolor charset for Commodore 64, extended with 5th color (colram)"),
00394 .capabilities = CODEC_CAP_DELAY,
00395 };