diff options
Diffstat (limited to 'media/libvpx/vp8/encoder/bitstream.c')
-rw-r--r-- | media/libvpx/vp8/encoder/bitstream.c | 1737 |
1 files changed, 1737 insertions, 0 deletions
diff --git a/media/libvpx/vp8/encoder/bitstream.c b/media/libvpx/vp8/encoder/bitstream.c new file mode 100644 index 000000000..ea279b321 --- /dev/null +++ b/media/libvpx/vp8/encoder/bitstream.c @@ -0,0 +1,1737 @@ +/* + * Copyright (c) 2010 The WebM project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + + +#include "vp8/common/header.h" +#include "encodemv.h" +#include "vp8/common/entropymode.h" +#include "vp8/common/findnearmv.h" +#include "mcomp.h" +#include "vp8/common/systemdependent.h" +#include <assert.h> +#include <stdio.h> +#include <limits.h> +#include "vpx/vpx_encoder.h" +#include "vpx_mem/vpx_mem.h" +#include "bitstream.h" + +#include "defaultcoefcounts.h" +#include "vp8/common/common.h" + +const int vp8cx_base_skip_false_prob[128] = +{ + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 251, 248, 244, 240, 236, 232, 229, 225, + 221, 217, 213, 208, 204, 199, 194, 190, + 187, 183, 179, 175, 172, 168, 164, 160, + 157, 153, 149, 145, 142, 138, 134, 130, + 127, 124, 120, 117, 114, 110, 107, 104, + 101, 98, 95, 92, 89, 86, 83, 80, + 77, 74, 71, 68, 65, 62, 59, 56, + 53, 50, 47, 44, 41, 38, 35, 32, + 30, 28, 26, 24, 22, 20, 18, 16, +}; + +#if defined(SECTIONBITS_OUTPUT) +unsigned __int64 Sectionbits[500]; +#endif + +#ifdef VP8_ENTROPY_STATS +int intra_mode_stats[10][10][10]; +static unsigned int tree_update_hist [BLOCK_TYPES] [COEF_BANDS] [PREV_COEF_CONTEXTS] [ENTROPY_NODES] [2]; +extern unsigned int active_section; +#endif + +#ifdef MODE_STATS +int count_mb_seg[4] = { 0, 0, 0, 0 }; +#endif + + +static void update_mode( + vp8_writer *const w, + int n, + vp8_token tok [/* n */], + vp8_tree tree, + vp8_prob Pnew [/* n-1 */], + vp8_prob Pcur [/* n-1 */], + unsigned int bct [/* n-1 */] [2], + const unsigned int num_events[/* n */] +) +{ + unsigned int new_b = 0, old_b = 0; + int i = 0; + + vp8_tree_probs_from_distribution( + n--, tok, tree, + Pnew, bct, num_events, + 256, 1 + ); + + do + { + new_b += vp8_cost_branch(bct[i], Pnew[i]); + old_b += vp8_cost_branch(bct[i], Pcur[i]); + } + while (++i < n); + + if (new_b + (n << 8) < old_b) + { + int j = 0; + + vp8_write_bit(w, 1); + + do + { + const vp8_prob p = Pnew[j]; + + vp8_write_literal(w, Pcur[j] = p ? p : 1, 8); + } + while (++j < n); + } + else + vp8_write_bit(w, 0); +} + +static void update_mbintra_mode_probs(VP8_COMP *cpi) +{ + VP8_COMMON *const x = & cpi->common; + + vp8_writer *const w = cpi->bc; + + { + vp8_prob Pnew [VP8_YMODES-1]; + unsigned int bct [VP8_YMODES-1] [2]; + + update_mode( + w, VP8_YMODES, vp8_ymode_encodings, vp8_ymode_tree, + Pnew, x->fc.ymode_prob, bct, (unsigned int *)cpi->mb.ymode_count + ); + } + { + vp8_prob Pnew [VP8_UV_MODES-1]; + unsigned int bct [VP8_UV_MODES-1] [2]; + + update_mode( + w, VP8_UV_MODES, vp8_uv_mode_encodings, vp8_uv_mode_tree, + Pnew, x->fc.uv_mode_prob, bct, (unsigned int *)cpi->mb.uv_mode_count + ); + } +} + +static void write_ymode(vp8_writer *bc, int m, const vp8_prob *p) +{ + vp8_write_token(bc, vp8_ymode_tree, p, vp8_ymode_encodings + m); +} + +static void kfwrite_ymode(vp8_writer *bc, int m, const vp8_prob *p) +{ + vp8_write_token(bc, vp8_kf_ymode_tree, p, vp8_kf_ymode_encodings + m); +} + +static void write_uv_mode(vp8_writer *bc, int m, const vp8_prob *p) +{ + vp8_write_token(bc, vp8_uv_mode_tree, p, vp8_uv_mode_encodings + m); +} + + +static void write_bmode(vp8_writer *bc, int m, const vp8_prob *p) +{ + vp8_write_token(bc, vp8_bmode_tree, p, vp8_bmode_encodings + m); +} + +static void write_split(vp8_writer *bc, int x) +{ + vp8_write_token( + bc, vp8_mbsplit_tree, vp8_mbsplit_probs, vp8_mbsplit_encodings + x + ); +} + +void vp8_pack_tokens(vp8_writer *w, const TOKENEXTRA *p, int xcount) +{ + const TOKENEXTRA *stop = p + xcount; + unsigned int split; + unsigned int shift; + int count = w->count; + unsigned int range = w->range; + unsigned int lowvalue = w->lowvalue; + + while (p < stop) + { + const int t = p->Token; + vp8_token *a = vp8_coef_encodings + t; + const vp8_extra_bit_struct *b = vp8_extra_bits + t; + int i = 0; + const unsigned char *pp = p->context_tree; + int v = a->value; + int n = a->Len; + + if (p->skip_eob_node) + { + n--; + i = 2; + } + + do + { + const int bb = (v >> --n) & 1; + split = 1 + (((range - 1) * pp[i>>1]) >> 8); + i = vp8_coef_tree[i+bb]; + + if (bb) + { + lowvalue += split; + range = range - split; + } + else + { + range = split; + } + + shift = vp8_norm[range]; + range <<= shift; + count += shift; + + if (count >= 0) + { + int offset = shift - count; + + if ((lowvalue << (offset - 1)) & 0x80000000) + { + int x = w->pos - 1; + + while (x >= 0 && w->buffer[x] == 0xff) + { + w->buffer[x] = (unsigned char)0; + x--; + } + + w->buffer[x] += 1; + } + + validate_buffer(w->buffer + w->pos, + 1, + w->buffer_end, + w->error); + + w->buffer[w->pos++] = (lowvalue >> (24 - offset)); + lowvalue <<= offset; + shift = count; + lowvalue &= 0xffffff; + count -= 8 ; + } + + lowvalue <<= shift; + } + while (n); + + + if (b->base_val) + { + const int e = p->Extra, L = b->Len; + + if (L) + { + const unsigned char *proba = b->prob; + const int v2 = e >> 1; + int n2 = L; /* number of bits in v2, assumed nonzero */ + i = 0; + + do + { + const int bb = (v2 >> --n2) & 1; + split = 1 + (((range - 1) * proba[i>>1]) >> 8); + i = b->tree[i+bb]; + + if (bb) + { + lowvalue += split; + range = range - split; + } + else + { + range = split; + } + + shift = vp8_norm[range]; + range <<= shift; + count += shift; + + if (count >= 0) + { + int offset = shift - count; + + if ((lowvalue << (offset - 1)) & 0x80000000) + { + int x = w->pos - 1; + + while (x >= 0 && w->buffer[x] == 0xff) + { + w->buffer[x] = (unsigned char)0; + x--; + } + + w->buffer[x] += 1; + } + + validate_buffer(w->buffer + w->pos, + 1, + w->buffer_end, + w->error); + + w->buffer[w->pos++] = (lowvalue >> (24 - offset)); + lowvalue <<= offset; + shift = count; + lowvalue &= 0xffffff; + count -= 8 ; + } + + lowvalue <<= shift; + } + while (n2); + } + + + { + + split = (range + 1) >> 1; + + if (e & 1) + { + lowvalue += split; + range = range - split; + } + else + { + range = split; + } + + range <<= 1; + + if ((lowvalue & 0x80000000)) + { + int x = w->pos - 1; + + while (x >= 0 && w->buffer[x] == 0xff) + { + w->buffer[x] = (unsigned char)0; + x--; + } + + w->buffer[x] += 1; + + } + + lowvalue <<= 1; + + if (!++count) + { + count = -8; + + validate_buffer(w->buffer + w->pos, + 1, + w->buffer_end, + w->error); + + w->buffer[w->pos++] = (lowvalue >> 24); + lowvalue &= 0xffffff; + } + } + + } + + ++p; + } + + w->count = count; + w->lowvalue = lowvalue; + w->range = range; + +} + +static void write_partition_size(unsigned char *cx_data, int size) +{ + signed char csize; + + csize = size & 0xff; + *cx_data = csize; + csize = (size >> 8) & 0xff; + *(cx_data + 1) = csize; + csize = (size >> 16) & 0xff; + *(cx_data + 2) = csize; + +} + +static void pack_tokens_into_partitions(VP8_COMP *cpi, unsigned char *cx_data, + unsigned char * cx_data_end, + int num_part) +{ + + int i; + unsigned char *ptr = cx_data; + unsigned char *ptr_end = cx_data_end; + vp8_writer * w; + + for (i = 0; i < num_part; i++) + { + int mb_row; + + w = cpi->bc + i + 1; + + vp8_start_encode(w, ptr, ptr_end); + + for (mb_row = i; mb_row < cpi->common.mb_rows; mb_row += num_part) + { + const TOKENEXTRA *p = cpi->tplist[mb_row].start; + const TOKENEXTRA *stop = cpi->tplist[mb_row].stop; + int tokens = (int)(stop - p); + + vp8_pack_tokens(w, p, tokens); + } + + vp8_stop_encode(w); + ptr += w->pos; + } +} + + +static void pack_mb_row_tokens(VP8_COMP *cpi, vp8_writer *w) +{ + int mb_row; + + for (mb_row = 0; mb_row < cpi->common.mb_rows; mb_row++) + { + const TOKENEXTRA *p = cpi->tplist[mb_row].start; + const TOKENEXTRA *stop = cpi->tplist[mb_row].stop; + int tokens = (int)(stop - p); + + vp8_pack_tokens(w, p, tokens); + } + +} + +static void write_mv_ref +( + vp8_writer *w, MB_PREDICTION_MODE m, const vp8_prob *p +) +{ +#if CONFIG_DEBUG + assert(NEARESTMV <= m && m <= SPLITMV); +#endif + vp8_write_token(w, vp8_mv_ref_tree, p, + vp8_mv_ref_encoding_array + (m - NEARESTMV)); +} + +static void write_sub_mv_ref +( + vp8_writer *w, B_PREDICTION_MODE m, const vp8_prob *p +) +{ +#if CONFIG_DEBUG + assert(LEFT4X4 <= m && m <= NEW4X4); +#endif + vp8_write_token(w, vp8_sub_mv_ref_tree, p, + vp8_sub_mv_ref_encoding_array + (m - LEFT4X4)); +} + +static void write_mv +( + vp8_writer *w, const MV *mv, const int_mv *ref, const MV_CONTEXT *mvc +) +{ + MV e; + e.row = mv->row - ref->as_mv.row; + e.col = mv->col - ref->as_mv.col; + + vp8_encode_motion_vector(w, &e, mvc); +} + +static void write_mb_features(vp8_writer *w, const MB_MODE_INFO *mi, const MACROBLOCKD *x) +{ + /* Encode the MB segment id. */ + if (x->segmentation_enabled && x->update_mb_segmentation_map) + { + switch (mi->segment_id) + { + case 0: + vp8_write(w, 0, x->mb_segment_tree_probs[0]); + vp8_write(w, 0, x->mb_segment_tree_probs[1]); + break; + case 1: + vp8_write(w, 0, x->mb_segment_tree_probs[0]); + vp8_write(w, 1, x->mb_segment_tree_probs[1]); + break; + case 2: + vp8_write(w, 1, x->mb_segment_tree_probs[0]); + vp8_write(w, 0, x->mb_segment_tree_probs[2]); + break; + case 3: + vp8_write(w, 1, x->mb_segment_tree_probs[0]); + vp8_write(w, 1, x->mb_segment_tree_probs[2]); + break; + + /* TRAP.. This should not happen */ + default: + vp8_write(w, 0, x->mb_segment_tree_probs[0]); + vp8_write(w, 0, x->mb_segment_tree_probs[1]); + break; + } + } +} +void vp8_convert_rfct_to_prob(VP8_COMP *const cpi) +{ + const int *const rfct = cpi->mb.count_mb_ref_frame_usage; + const int rf_intra = rfct[INTRA_FRAME]; + const int rf_inter = rfct[LAST_FRAME] + rfct[GOLDEN_FRAME] + rfct[ALTREF_FRAME]; + + /* Calculate the probabilities used to code the ref frame based on usage */ + if (!(cpi->prob_intra_coded = rf_intra * 255 / (rf_intra + rf_inter))) + cpi->prob_intra_coded = 1; + + cpi->prob_last_coded = rf_inter ? (rfct[LAST_FRAME] * 255) / rf_inter : 128; + + if (!cpi->prob_last_coded) + cpi->prob_last_coded = 1; + + cpi->prob_gf_coded = (rfct[GOLDEN_FRAME] + rfct[ALTREF_FRAME]) + ? (rfct[GOLDEN_FRAME] * 255) / (rfct[GOLDEN_FRAME] + rfct[ALTREF_FRAME]) : 128; + + if (!cpi->prob_gf_coded) + cpi->prob_gf_coded = 1; + +} + +static void pack_inter_mode_mvs(VP8_COMP *const cpi) +{ + VP8_COMMON *const pc = & cpi->common; + vp8_writer *const w = cpi->bc; + const MV_CONTEXT *mvc = pc->fc.mvc; + + + MODE_INFO *m = pc->mi; + const int mis = pc->mode_info_stride; + int mb_row = -1; + + int prob_skip_false = 0; + + cpi->mb.partition_info = cpi->mb.pi; + + vp8_convert_rfct_to_prob(cpi); + +#ifdef VP8_ENTROPY_STATS + active_section = 1; +#endif + + if (pc->mb_no_coeff_skip) + { + int total_mbs = pc->mb_rows * pc->mb_cols; + + prob_skip_false = (total_mbs - cpi->mb.skip_true_count ) * 256 / total_mbs; + + if (prob_skip_false <= 1) + prob_skip_false = 1; + + if (prob_skip_false > 255) + prob_skip_false = 255; + + cpi->prob_skip_false = prob_skip_false; + vp8_write_literal(w, prob_skip_false, 8); + } + + vp8_write_literal(w, cpi->prob_intra_coded, 8); + vp8_write_literal(w, cpi->prob_last_coded, 8); + vp8_write_literal(w, cpi->prob_gf_coded, 8); + + update_mbintra_mode_probs(cpi); + + vp8_write_mvprobs(cpi); + + while (++mb_row < pc->mb_rows) + { + int mb_col = -1; + + while (++mb_col < pc->mb_cols) + { + const MB_MODE_INFO *const mi = & m->mbmi; + const MV_REFERENCE_FRAME rf = mi->ref_frame; + const MB_PREDICTION_MODE mode = mi->mode; + + MACROBLOCKD *xd = &cpi->mb.e_mbd; + + /* Distance of Mb to the various image edges. + * These specified to 8th pel as they are always compared to MV + * values that are in 1/8th pel units + */ + xd->mb_to_left_edge = -((mb_col * 16) << 3); + xd->mb_to_right_edge = ((pc->mb_cols - 1 - mb_col) * 16) << 3; + xd->mb_to_top_edge = -((mb_row * 16) << 3); + xd->mb_to_bottom_edge = ((pc->mb_rows - 1 - mb_row) * 16) << 3; + +#ifdef VP8_ENTROPY_STATS + active_section = 9; +#endif + + if (cpi->mb.e_mbd.update_mb_segmentation_map) + write_mb_features(w, mi, &cpi->mb.e_mbd); + + if (pc->mb_no_coeff_skip) + vp8_encode_bool(w, m->mbmi.mb_skip_coeff, prob_skip_false); + + if (rf == INTRA_FRAME) + { + vp8_write(w, 0, cpi->prob_intra_coded); +#ifdef VP8_ENTROPY_STATS + active_section = 6; +#endif + write_ymode(w, mode, pc->fc.ymode_prob); + + if (mode == B_PRED) + { + int j = 0; + + do + write_bmode(w, m->bmi[j].as_mode, pc->fc.bmode_prob); + while (++j < 16); + } + + write_uv_mode(w, mi->uv_mode, pc->fc.uv_mode_prob); + } + else /* inter coded */ + { + int_mv best_mv; + vp8_prob mv_ref_p [VP8_MVREFS-1]; + + vp8_write(w, 1, cpi->prob_intra_coded); + + if (rf == LAST_FRAME) + vp8_write(w, 0, cpi->prob_last_coded); + else + { + vp8_write(w, 1, cpi->prob_last_coded); + vp8_write(w, (rf == GOLDEN_FRAME) ? 0 : 1, cpi->prob_gf_coded); + } + + { + int_mv n1, n2; + int ct[4]; + + vp8_find_near_mvs(xd, m, &n1, &n2, &best_mv, ct, rf, cpi->common.ref_frame_sign_bias); + vp8_clamp_mv2(&best_mv, xd); + + vp8_mv_ref_probs(mv_ref_p, ct); + +#ifdef VP8_ENTROPY_STATS + accum_mv_refs(mode, ct); +#endif + + } + +#ifdef VP8_ENTROPY_STATS + active_section = 3; +#endif + + write_mv_ref(w, mode, mv_ref_p); + + switch (mode) /* new, split require MVs */ + { + case NEWMV: + +#ifdef VP8_ENTROPY_STATS + active_section = 5; +#endif + + write_mv(w, &mi->mv.as_mv, &best_mv, mvc); + break; + + case SPLITMV: + { + int j = 0; + +#ifdef MODE_STATS + ++count_mb_seg [mi->partitioning]; +#endif + + write_split(w, mi->partitioning); + + do + { + B_PREDICTION_MODE blockmode; + int_mv blockmv; + const int *const L = vp8_mbsplits [mi->partitioning]; + int k = -1; /* first block in subset j */ + int mv_contz; + int_mv leftmv, abovemv; + + blockmode = cpi->mb.partition_info->bmi[j].mode; + blockmv = cpi->mb.partition_info->bmi[j].mv; +#if CONFIG_DEBUG + while (j != L[++k]) + if (k >= 16) + assert(0); +#else + while (j != L[++k]); +#endif + leftmv.as_int = left_block_mv(m, k); + abovemv.as_int = above_block_mv(m, k, mis); + mv_contz = vp8_mv_cont(&leftmv, &abovemv); + + write_sub_mv_ref(w, blockmode, vp8_sub_mv_ref_prob2 [mv_contz]); + + if (blockmode == NEW4X4) + { +#ifdef VP8_ENTROPY_STATS + active_section = 11; +#endif + write_mv(w, &blockmv.as_mv, &best_mv, (const MV_CONTEXT *) mvc); + } + } + while (++j < cpi->mb.partition_info->count); + } + break; + default: + break; + } + } + + ++m; + cpi->mb.partition_info++; + } + + ++m; /* skip L prediction border */ + cpi->mb.partition_info++; + } +} + + +static void write_kfmodes(VP8_COMP *cpi) +{ + vp8_writer *const bc = cpi->bc; + const VP8_COMMON *const c = & cpi->common; + /* const */ + MODE_INFO *m = c->mi; + + int mb_row = -1; + int prob_skip_false = 0; + + if (c->mb_no_coeff_skip) + { + int total_mbs = c->mb_rows * c->mb_cols; + + prob_skip_false = (total_mbs - cpi->mb.skip_true_count ) * 256 / total_mbs; + + if (prob_skip_false <= 1) + prob_skip_false = 1; + + if (prob_skip_false >= 255) + prob_skip_false = 255; + + cpi->prob_skip_false = prob_skip_false; + vp8_write_literal(bc, prob_skip_false, 8); + } + + while (++mb_row < c->mb_rows) + { + int mb_col = -1; + + while (++mb_col < c->mb_cols) + { + const int ym = m->mbmi.mode; + + if (cpi->mb.e_mbd.update_mb_segmentation_map) + write_mb_features(bc, &m->mbmi, &cpi->mb.e_mbd); + + if (c->mb_no_coeff_skip) + vp8_encode_bool(bc, m->mbmi.mb_skip_coeff, prob_skip_false); + + kfwrite_ymode(bc, ym, vp8_kf_ymode_prob); + + if (ym == B_PRED) + { + const int mis = c->mode_info_stride; + int i = 0; + + do + { + const B_PREDICTION_MODE A = above_block_mode(m, i, mis); + const B_PREDICTION_MODE L = left_block_mode(m, i); + const int bm = m->bmi[i].as_mode; + +#ifdef VP8_ENTROPY_STATS + ++intra_mode_stats [A] [L] [bm]; +#endif + + write_bmode(bc, bm, vp8_kf_bmode_prob [A] [L]); + } + while (++i < 16); + } + + write_uv_mode(bc, (m++)->mbmi.uv_mode, vp8_kf_uv_mode_prob); + } + + m++; /* skip L prediction border */ + } +} + +#if 0 +/* This function is used for debugging probability trees. */ +static void print_prob_tree(vp8_prob + coef_probs[BLOCK_TYPES][COEF_BANDS][PREV_COEF_CONTEXTS][ENTROPY_NODES]) +{ + /* print coef probability tree */ + int i,j,k,l; + FILE* f = fopen("enc_tree_probs.txt", "a"); + fprintf(f, "{\n"); + for (i = 0; i < BLOCK_TYPES; i++) + { + fprintf(f, " {\n"); + for (j = 0; j < COEF_BANDS; j++) + { + fprintf(f, " {\n"); + for (k = 0; k < PREV_COEF_CONTEXTS; k++) + { + fprintf(f, " {"); + for (l = 0; l < ENTROPY_NODES; l++) + { + fprintf(f, "%3u, ", + (unsigned int)(coef_probs [i][j][k][l])); + } + fprintf(f, " }\n"); + } + fprintf(f, " }\n"); + } + fprintf(f, " }\n"); + } + fprintf(f, "}\n"); + fclose(f); +} +#endif + +static void sum_probs_over_prev_coef_context( + const unsigned int probs[PREV_COEF_CONTEXTS][MAX_ENTROPY_TOKENS], + unsigned int* out) +{ + int i, j; + for (i=0; i < MAX_ENTROPY_TOKENS; ++i) + { + for (j=0; j < PREV_COEF_CONTEXTS; ++j) + { + const unsigned int tmp = out[i]; + out[i] += probs[j][i]; + /* check for wrap */ + if (out[i] < tmp) + out[i] = UINT_MAX; + } + } +} + +static int prob_update_savings(const unsigned int *ct, + const vp8_prob oldp, const vp8_prob newp, + const vp8_prob upd) +{ + const int old_b = vp8_cost_branch(ct, oldp); + const int new_b = vp8_cost_branch(ct, newp); + const int update_b = 8 + + ((vp8_cost_one(upd) - vp8_cost_zero(upd)) >> 8); + + return old_b - new_b - update_b; +} + +static int independent_coef_context_savings(VP8_COMP *cpi) +{ + MACROBLOCK *const x = & cpi->mb; + int savings = 0; + int i = 0; + do + { + int j = 0; + do + { + int k = 0; + unsigned int prev_coef_count_sum[MAX_ENTROPY_TOKENS] = {0}; + int prev_coef_savings[MAX_ENTROPY_TOKENS] = {0}; + const unsigned int (*probs)[MAX_ENTROPY_TOKENS]; + /* Calculate new probabilities given the constraint that + * they must be equal over the prev coef contexts + */ + + probs = (const unsigned int (*)[MAX_ENTROPY_TOKENS]) + x->coef_counts[i][j]; + + /* Reset to default probabilities at key frames */ + if (cpi->common.frame_type == KEY_FRAME) + probs = default_coef_counts[i][j]; + + sum_probs_over_prev_coef_context(probs, prev_coef_count_sum); + + do + { + /* at every context */ + + /* calc probs and branch cts for this frame only */ + int t = 0; /* token/prob index */ + + vp8_tree_probs_from_distribution( + MAX_ENTROPY_TOKENS, vp8_coef_encodings, vp8_coef_tree, + cpi->frame_coef_probs[i][j][k], + cpi->frame_branch_ct [i][j][k], + prev_coef_count_sum, + 256, 1); + + do + { + const unsigned int *ct = cpi->frame_branch_ct [i][j][k][t]; + const vp8_prob newp = cpi->frame_coef_probs [i][j][k][t]; + const vp8_prob oldp = cpi->common.fc.coef_probs [i][j][k][t]; + const vp8_prob upd = vp8_coef_update_probs [i][j][k][t]; + const int s = prob_update_savings(ct, oldp, newp, upd); + + if (cpi->common.frame_type != KEY_FRAME || + (cpi->common.frame_type == KEY_FRAME && newp != oldp)) + prev_coef_savings[t] += s; + } + while (++t < ENTROPY_NODES); + } + while (++k < PREV_COEF_CONTEXTS); + k = 0; + do + { + /* We only update probabilities if we can save bits, except + * for key frames where we have to update all probabilities + * to get the equal probabilities across the prev coef + * contexts. + */ + if (prev_coef_savings[k] > 0 || + cpi->common.frame_type == KEY_FRAME) + savings += prev_coef_savings[k]; + } + while (++k < ENTROPY_NODES); + } + while (++j < COEF_BANDS); + } + while (++i < BLOCK_TYPES); + return savings; +} + +static int default_coef_context_savings(VP8_COMP *cpi) +{ + MACROBLOCK *const x = & cpi->mb; + int savings = 0; + int i = 0; + do + { + int j = 0; + do + { + int k = 0; + do + { + /* at every context */ + + /* calc probs and branch cts for this frame only */ + int t = 0; /* token/prob index */ + + vp8_tree_probs_from_distribution( + MAX_ENTROPY_TOKENS, vp8_coef_encodings, vp8_coef_tree, + cpi->frame_coef_probs [i][j][k], + cpi->frame_branch_ct [i][j][k], + x->coef_counts [i][j][k], + 256, 1 + ); + + do + { + const unsigned int *ct = cpi->frame_branch_ct [i][j][k][t]; + const vp8_prob newp = cpi->frame_coef_probs [i][j][k][t]; + const vp8_prob oldp = cpi->common.fc.coef_probs [i][j][k][t]; + const vp8_prob upd = vp8_coef_update_probs [i][j][k][t]; + const int s = prob_update_savings(ct, oldp, newp, upd); + + if (s > 0) + { + savings += s; + } + } + while (++t < ENTROPY_NODES); + } + while (++k < PREV_COEF_CONTEXTS); + } + while (++j < COEF_BANDS); + } + while (++i < BLOCK_TYPES); + return savings; +} + +void vp8_calc_ref_frame_costs(int *ref_frame_cost, + int prob_intra, + int prob_last, + int prob_garf + ) +{ + assert(prob_intra >= 0); + assert(prob_intra <= 255); + assert(prob_last >= 0); + assert(prob_last <= 255); + assert(prob_garf >= 0); + assert(prob_garf <= 255); + ref_frame_cost[INTRA_FRAME] = vp8_cost_zero(prob_intra); + ref_frame_cost[LAST_FRAME] = vp8_cost_one(prob_intra) + + vp8_cost_zero(prob_last); + ref_frame_cost[GOLDEN_FRAME] = vp8_cost_one(prob_intra) + + vp8_cost_one(prob_last) + + vp8_cost_zero(prob_garf); + ref_frame_cost[ALTREF_FRAME] = vp8_cost_one(prob_intra) + + vp8_cost_one(prob_last) + + vp8_cost_one(prob_garf); + +} + +int vp8_estimate_entropy_savings(VP8_COMP *cpi) +{ + int savings = 0; + + const int *const rfct = cpi->mb.count_mb_ref_frame_usage; + const int rf_intra = rfct[INTRA_FRAME]; + const int rf_inter = rfct[LAST_FRAME] + rfct[GOLDEN_FRAME] + rfct[ALTREF_FRAME]; + int new_intra, new_last, new_garf, oldtotal, newtotal; + int ref_frame_cost[MAX_REF_FRAMES]; + + vp8_clear_system_state(); + + if (cpi->common.frame_type != KEY_FRAME) + { + if (!(new_intra = rf_intra * 255 / (rf_intra + rf_inter))) + new_intra = 1; + + new_last = rf_inter ? (rfct[LAST_FRAME] * 255) / rf_inter : 128; + + new_garf = (rfct[GOLDEN_FRAME] + rfct[ALTREF_FRAME]) + ? (rfct[GOLDEN_FRAME] * 255) / (rfct[GOLDEN_FRAME] + rfct[ALTREF_FRAME]) : 128; + + + vp8_calc_ref_frame_costs(ref_frame_cost,new_intra,new_last,new_garf); + + newtotal = + rfct[INTRA_FRAME] * ref_frame_cost[INTRA_FRAME] + + rfct[LAST_FRAME] * ref_frame_cost[LAST_FRAME] + + rfct[GOLDEN_FRAME] * ref_frame_cost[GOLDEN_FRAME] + + rfct[ALTREF_FRAME] * ref_frame_cost[ALTREF_FRAME]; + + + /* old costs */ + vp8_calc_ref_frame_costs(ref_frame_cost,cpi->prob_intra_coded, + cpi->prob_last_coded,cpi->prob_gf_coded); + + oldtotal = + rfct[INTRA_FRAME] * ref_frame_cost[INTRA_FRAME] + + rfct[LAST_FRAME] * ref_frame_cost[LAST_FRAME] + + rfct[GOLDEN_FRAME] * ref_frame_cost[GOLDEN_FRAME] + + rfct[ALTREF_FRAME] * ref_frame_cost[ALTREF_FRAME]; + + savings += (oldtotal - newtotal) / 256; + } + + + if (cpi->oxcf.error_resilient_mode & VPX_ERROR_RESILIENT_PARTITIONS) + savings += independent_coef_context_savings(cpi); + else + savings += default_coef_context_savings(cpi); + + + return savings; +} + +#if CONFIG_REALTIME_ONLY & CONFIG_ONTHEFLY_BITPACKING +int vp8_update_coef_context(VP8_COMP *cpi) +{ + int savings = 0; + + + if (cpi->common.frame_type == KEY_FRAME) + { + /* Reset to default counts/probabilities at key frames */ + vp8_copy(cpi->mb.coef_counts, default_coef_counts); + } + + if (cpi->oxcf.error_resilient_mode & VPX_ERROR_RESILIENT_PARTITIONS) + savings += independent_coef_context_savings(cpi); + else + savings += default_coef_context_savings(cpi); + + return savings; +} +#endif + +void vp8_update_coef_probs(VP8_COMP *cpi) +{ + int i = 0; +#if !(CONFIG_REALTIME_ONLY & CONFIG_ONTHEFLY_BITPACKING) + vp8_writer *const w = cpi->bc; +#endif + int savings = 0; + + vp8_clear_system_state(); + + do + { + int j = 0; + + do + { + int k = 0; + int prev_coef_savings[ENTROPY_NODES] = {0}; + if (cpi->oxcf.error_resilient_mode & VPX_ERROR_RESILIENT_PARTITIONS) + { + for (k = 0; k < PREV_COEF_CONTEXTS; ++k) + { + int t; /* token/prob index */ + for (t = 0; t < ENTROPY_NODES; ++t) + { + const unsigned int *ct = cpi->frame_branch_ct [i][j] + [k][t]; + const vp8_prob newp = cpi->frame_coef_probs[i][j][k][t]; + const vp8_prob oldp = cpi->common.fc.coef_probs[i][j] + [k][t]; + const vp8_prob upd = vp8_coef_update_probs[i][j][k][t]; + + prev_coef_savings[t] += + prob_update_savings(ct, oldp, newp, upd); + } + } + k = 0; + } + do + { + /* note: use result from vp8_estimate_entropy_savings, so no + * need to call vp8_tree_probs_from_distribution here. + */ + + /* at every context */ + + /* calc probs and branch cts for this frame only */ + int t = 0; /* token/prob index */ + + do + { + const vp8_prob newp = cpi->frame_coef_probs [i][j][k][t]; + + vp8_prob *Pold = cpi->common.fc.coef_probs [i][j][k] + t; + const vp8_prob upd = vp8_coef_update_probs [i][j][k][t]; + + int s = prev_coef_savings[t]; + int u = 0; + + if (!(cpi->oxcf.error_resilient_mode & + VPX_ERROR_RESILIENT_PARTITIONS)) + { + s = prob_update_savings( + cpi->frame_branch_ct [i][j][k][t], + *Pold, newp, upd); + } + + if (s > 0) + u = 1; + + /* Force updates on key frames if the new is different, + * so that we can be sure we end up with equal probabilities + * over the prev coef contexts. + */ + if ((cpi->oxcf.error_resilient_mode & + VPX_ERROR_RESILIENT_PARTITIONS) && + cpi->common.frame_type == KEY_FRAME && newp != *Pold) + u = 1; + +#if CONFIG_REALTIME_ONLY & CONFIG_ONTHEFLY_BITPACKING + cpi->update_probs[i][j][k][t] = u; +#else + vp8_write(w, u, upd); +#endif + + +#ifdef VP8_ENTROPY_STATS + ++ tree_update_hist [i][j][k][t] [u]; +#endif + + if (u) + { + /* send/use new probability */ + + *Pold = newp; +#if !(CONFIG_REALTIME_ONLY & CONFIG_ONTHEFLY_BITPACKING) + vp8_write_literal(w, newp, 8); +#endif + + savings += s; + + } + + } + while (++t < ENTROPY_NODES); + + /* Accum token counts for generation of default statistics */ +#ifdef VP8_ENTROPY_STATS + t = 0; + + do + { + context_counters [i][j][k][t] += cpi->coef_counts [i][j][k][t]; + } + while (++t < MAX_ENTROPY_TOKENS); + +#endif + + } + while (++k < PREV_COEF_CONTEXTS); + } + while (++j < COEF_BANDS); + } + while (++i < BLOCK_TYPES); + +} + +#if CONFIG_REALTIME_ONLY & CONFIG_ONTHEFLY_BITPACKING +static void pack_coef_probs(VP8_COMP *cpi) +{ + int i = 0; + vp8_writer *const w = cpi->bc; + + do + { + int j = 0; + + do + { + int k = 0; + + do + { + int t = 0; /* token/prob index */ + + do + { + const vp8_prob newp = cpi->common.fc.coef_probs [i][j][k][t]; + const vp8_prob upd = vp8_coef_update_probs [i][j][k][t]; + + const char u = cpi->update_probs[i][j][k][t] ; + + vp8_write(w, u, upd); + + if (u) + { + /* send/use new probability */ + vp8_write_literal(w, newp, 8); + } + } + while (++t < ENTROPY_NODES); + } + while (++k < PREV_COEF_CONTEXTS); + } + while (++j < COEF_BANDS); + } + while (++i < BLOCK_TYPES); +} +#endif + +#ifdef PACKET_TESTING +FILE *vpxlogc = 0; +#endif + +static void put_delta_q(vp8_writer *bc, int delta_q) +{ + if (delta_q != 0) + { + vp8_write_bit(bc, 1); + vp8_write_literal(bc, abs(delta_q), 4); + + if (delta_q < 0) + vp8_write_bit(bc, 1); + else + vp8_write_bit(bc, 0); + } + else + vp8_write_bit(bc, 0); +} + +void vp8_pack_bitstream(VP8_COMP *cpi, unsigned char *dest, unsigned char * dest_end, unsigned long *size) +{ + int i, j; + VP8_HEADER oh; + VP8_COMMON *const pc = & cpi->common; + vp8_writer *const bc = cpi->bc; + MACROBLOCKD *const xd = & cpi->mb.e_mbd; + int extra_bytes_packed = 0; + + unsigned char *cx_data = dest; + unsigned char *cx_data_end = dest_end; + const int *mb_feature_data_bits; + + oh.show_frame = (int) pc->show_frame; + oh.type = (int)pc->frame_type; + oh.version = pc->version; + oh.first_partition_length_in_bytes = 0; + + mb_feature_data_bits = vp8_mb_feature_data_bits; + + bc[0].error = &pc->error; + + validate_buffer(cx_data, 3, cx_data_end, &cpi->common.error); + cx_data += 3; + +#if defined(SECTIONBITS_OUTPUT) + Sectionbits[active_section = 1] += sizeof(VP8_HEADER) * 8 * 256; +#endif + + /* every keyframe send startcode, width, height, scale factor, clamp + * and color type + */ + if (oh.type == KEY_FRAME) + { + int v; + + validate_buffer(cx_data, 7, cx_data_end, &cpi->common.error); + + /* Start / synch code */ + cx_data[0] = 0x9D; + cx_data[1] = 0x01; + cx_data[2] = 0x2a; + + v = (pc->horiz_scale << 14) | pc->Width; + cx_data[3] = v; + cx_data[4] = v >> 8; + + v = (pc->vert_scale << 14) | pc->Height; + cx_data[5] = v; + cx_data[6] = v >> 8; + + + extra_bytes_packed = 7; + cx_data += extra_bytes_packed ; + + vp8_start_encode(bc, cx_data, cx_data_end); + + /* signal clr type */ + vp8_write_bit(bc, 0); + vp8_write_bit(bc, pc->clamp_type); + + } + else + vp8_start_encode(bc, cx_data, cx_data_end); + + + /* Signal whether or not Segmentation is enabled */ + vp8_write_bit(bc, xd->segmentation_enabled); + + /* Indicate which features are enabled */ + if (xd->segmentation_enabled) + { + /* Signal whether or not the segmentation map is being updated. */ + vp8_write_bit(bc, xd->update_mb_segmentation_map); + vp8_write_bit(bc, xd->update_mb_segmentation_data); + + if (xd->update_mb_segmentation_data) + { + signed char Data; + + vp8_write_bit(bc, xd->mb_segement_abs_delta); + + /* For each segmentation feature (Quant and loop filter level) */ + for (i = 0; i < MB_LVL_MAX; i++) + { + /* For each of the segments */ + for (j = 0; j < MAX_MB_SEGMENTS; j++) + { + Data = xd->segment_feature_data[i][j]; + + /* Frame level data */ + if (Data) + { + vp8_write_bit(bc, 1); + + if (Data < 0) + { + Data = - Data; + vp8_write_literal(bc, Data, mb_feature_data_bits[i]); + vp8_write_bit(bc, 1); + } + else + { + vp8_write_literal(bc, Data, mb_feature_data_bits[i]); + vp8_write_bit(bc, 0); + } + } + else + vp8_write_bit(bc, 0); + } + } + } + + if (xd->update_mb_segmentation_map) + { + /* Write the probs used to decode the segment id for each mb */ + for (i = 0; i < MB_FEATURE_TREE_PROBS; i++) + { + int Data = xd->mb_segment_tree_probs[i]; + + if (Data != 255) + { + vp8_write_bit(bc, 1); + vp8_write_literal(bc, Data, 8); + } + else + vp8_write_bit(bc, 0); + } + } + } + + vp8_write_bit(bc, pc->filter_type); + vp8_write_literal(bc, pc->filter_level, 6); + vp8_write_literal(bc, pc->sharpness_level, 3); + + /* Write out loop filter deltas applied at the MB level based on mode + * or ref frame (if they are enabled). + */ + vp8_write_bit(bc, xd->mode_ref_lf_delta_enabled); + + if (xd->mode_ref_lf_delta_enabled) + { + /* Do the deltas need to be updated */ + int send_update = xd->mode_ref_lf_delta_update + || cpi->oxcf.error_resilient_mode; + + vp8_write_bit(bc, send_update); + if (send_update) + { + int Data; + + /* Send update */ + for (i = 0; i < MAX_REF_LF_DELTAS; i++) + { + Data = xd->ref_lf_deltas[i]; + + /* Frame level data */ + if (xd->ref_lf_deltas[i] != xd->last_ref_lf_deltas[i] + || cpi->oxcf.error_resilient_mode) + { + xd->last_ref_lf_deltas[i] = xd->ref_lf_deltas[i]; + vp8_write_bit(bc, 1); + + if (Data > 0) + { + vp8_write_literal(bc, (Data & 0x3F), 6); + vp8_write_bit(bc, 0); /* sign */ + } + else + { + Data = -Data; + vp8_write_literal(bc, (Data & 0x3F), 6); + vp8_write_bit(bc, 1); /* sign */ + } + } + else + vp8_write_bit(bc, 0); + } + + /* Send update */ + for (i = 0; i < MAX_MODE_LF_DELTAS; i++) + { + Data = xd->mode_lf_deltas[i]; + + if (xd->mode_lf_deltas[i] != xd->last_mode_lf_deltas[i] + || cpi->oxcf.error_resilient_mode) + { + xd->last_mode_lf_deltas[i] = xd->mode_lf_deltas[i]; + vp8_write_bit(bc, 1); + + if (Data > 0) + { + vp8_write_literal(bc, (Data & 0x3F), 6); + vp8_write_bit(bc, 0); /* sign */ + } + else + { + Data = -Data; + vp8_write_literal(bc, (Data & 0x3F), 6); + vp8_write_bit(bc, 1); /* sign */ + } + } + else + vp8_write_bit(bc, 0); + } + } + } + + /* signal here is multi token partition is enabled */ + vp8_write_literal(bc, pc->multi_token_partition, 2); + + /* Frame Qbaseline quantizer index */ + vp8_write_literal(bc, pc->base_qindex, 7); + + /* Transmit Dc, Second order and Uv quantizer delta information */ + put_delta_q(bc, pc->y1dc_delta_q); + put_delta_q(bc, pc->y2dc_delta_q); + put_delta_q(bc, pc->y2ac_delta_q); + put_delta_q(bc, pc->uvdc_delta_q); + put_delta_q(bc, pc->uvac_delta_q); + + /* When there is a key frame all reference buffers are updated using + * the new key frame + */ + if (pc->frame_type != KEY_FRAME) + { + /* Should the GF or ARF be updated using the transmitted frame + * or buffer + */ + vp8_write_bit(bc, pc->refresh_golden_frame); + vp8_write_bit(bc, pc->refresh_alt_ref_frame); + + /* If not being updated from current frame should either GF or ARF + * be updated from another buffer + */ + if (!pc->refresh_golden_frame) + vp8_write_literal(bc, pc->copy_buffer_to_gf, 2); + + if (!pc->refresh_alt_ref_frame) + vp8_write_literal(bc, pc->copy_buffer_to_arf, 2); + + /* Indicate reference frame sign bias for Golden and ARF frames + * (always 0 for last frame buffer) + */ + vp8_write_bit(bc, pc->ref_frame_sign_bias[GOLDEN_FRAME]); + vp8_write_bit(bc, pc->ref_frame_sign_bias[ALTREF_FRAME]); + } + +#if !(CONFIG_REALTIME_ONLY & CONFIG_ONTHEFLY_BITPACKING) + if (cpi->oxcf.error_resilient_mode & VPX_ERROR_RESILIENT_PARTITIONS) + { + if (pc->frame_type == KEY_FRAME) + pc->refresh_entropy_probs = 1; + else + pc->refresh_entropy_probs = 0; + } +#endif + + vp8_write_bit(bc, pc->refresh_entropy_probs); + + if (pc->frame_type != KEY_FRAME) + vp8_write_bit(bc, pc->refresh_last_frame); + +#ifdef VP8_ENTROPY_STATS + + if (pc->frame_type == INTER_FRAME) + active_section = 0; + else + active_section = 7; + +#endif + + vp8_clear_system_state(); + +#if CONFIG_REALTIME_ONLY & CONFIG_ONTHEFLY_BITPACKING + pack_coef_probs(cpi); +#else + if (pc->refresh_entropy_probs == 0) + { + /* save a copy for later refresh */ + memcpy(&cpi->common.lfc, &cpi->common.fc, sizeof(cpi->common.fc)); + } + + vp8_update_coef_probs(cpi); +#endif + +#ifdef VP8_ENTROPY_STATS + active_section = 2; +#endif + + /* Write out the mb_no_coeff_skip flag */ + vp8_write_bit(bc, pc->mb_no_coeff_skip); + + if (pc->frame_type == KEY_FRAME) + { + write_kfmodes(cpi); + +#ifdef VP8_ENTROPY_STATS + active_section = 8; +#endif + } + else + { + pack_inter_mode_mvs(cpi); + +#ifdef VP8_ENTROPY_STATS + active_section = 1; +#endif + } + + vp8_stop_encode(bc); + + cx_data += bc->pos; + + oh.first_partition_length_in_bytes = cpi->bc->pos; + + /* update frame tag */ + { + int v = (oh.first_partition_length_in_bytes << 5) | + (oh.show_frame << 4) | + (oh.version << 1) | + oh.type; + + dest[0] = v; + dest[1] = v >> 8; + dest[2] = v >> 16; + } + + *size = VP8_HEADER_SIZE + extra_bytes_packed + cpi->bc->pos; + + cpi->partition_sz[0] = *size; + +#if CONFIG_REALTIME_ONLY & CONFIG_ONTHEFLY_BITPACKING + { + const int num_part = (1 << pc->multi_token_partition); + unsigned char * dp = cpi->partition_d[0] + cpi->partition_sz[0]; + + if (num_part > 1) + { + /* write token part sizes (all but last) if more than 1 */ + validate_buffer(dp, 3 * (num_part - 1), cpi->partition_d_end[0], + &pc->error); + + cpi->partition_sz[0] += 3*(num_part-1); + + for(i = 1; i < num_part; i++) + { + write_partition_size(dp, cpi->partition_sz[i]); + dp += 3; + } + } + + if (!cpi->output_partition) + { + /* concatenate partition buffers */ + for(i = 0; i < num_part; i++) + { + memmove(dp, cpi->partition_d[i+1], cpi->partition_sz[i+1]); + cpi->partition_d[i+1] = dp; + dp += cpi->partition_sz[i+1]; + } + } + + /* update total size */ + *size = 0; + for(i = 0; i < num_part+1; i++) + { + *size += cpi->partition_sz[i]; + } + } +#else + if (pc->multi_token_partition != ONE_PARTITION) + { + int num_part = 1 << pc->multi_token_partition; + + /* partition size table at the end of first partition */ + cpi->partition_sz[0] += 3 * (num_part - 1); + *size += 3 * (num_part - 1); + + validate_buffer(cx_data, 3 * (num_part - 1), cx_data_end, + &pc->error); + + for(i = 1; i < num_part + 1; i++) + { + cpi->bc[i].error = &pc->error; + } + + pack_tokens_into_partitions(cpi, cx_data + 3 * (num_part - 1), + cx_data_end, num_part); + + for(i = 1; i < num_part; i++) + { + cpi->partition_sz[i] = cpi->bc[i].pos; + write_partition_size(cx_data, cpi->partition_sz[i]); + cx_data += 3; + *size += cpi->partition_sz[i]; /* add to total */ + } + + /* add last partition to total size */ + cpi->partition_sz[i] = cpi->bc[i].pos; + *size += cpi->partition_sz[i]; + } + else + { + bc[1].error = &pc->error; + + vp8_start_encode(&cpi->bc[1], cx_data, cx_data_end); + +#if CONFIG_MULTITHREAD + if (cpi->b_multi_threaded) + pack_mb_row_tokens(cpi, &cpi->bc[1]); + else +#endif + vp8_pack_tokens(&cpi->bc[1], cpi->tok, cpi->tok_count); + + vp8_stop_encode(&cpi->bc[1]); + + *size += cpi->bc[1].pos; + cpi->partition_sz[1] = cpi->bc[1].pos; + } +#endif +} + +#ifdef VP8_ENTROPY_STATS +void print_tree_update_probs() +{ + int i, j, k, l; + FILE *f = fopen("context.c", "a"); + int Sum; + fprintf(f, "\n/* Update probabilities for token entropy tree. */\n\n"); + fprintf(f, "const vp8_prob tree_update_probs[BLOCK_TYPES] [COEF_BANDS] [PREV_COEF_CONTEXTS] [ENTROPY_NODES] = {\n"); + + for (i = 0; i < BLOCK_TYPES; i++) + { + fprintf(f, " { \n"); + + for (j = 0; j < COEF_BANDS; j++) + { + fprintf(f, " {\n"); + + for (k = 0; k < PREV_COEF_CONTEXTS; k++) + { + fprintf(f, " {"); + + for (l = 0; l < ENTROPY_NODES; l++) + { + Sum = tree_update_hist[i][j][k][l][0] + tree_update_hist[i][j][k][l][1]; + + if (Sum > 0) + { + if (((tree_update_hist[i][j][k][l][0] * 255) / Sum) > 0) + fprintf(f, "%3ld, ", (tree_update_hist[i][j][k][l][0] * 255) / Sum); + else + fprintf(f, "%3ld, ", 1); + } + else + fprintf(f, "%3ld, ", 128); + } + + fprintf(f, "},\n"); + } + + fprintf(f, " },\n"); + } + + fprintf(f, " },\n"); + } + + fprintf(f, "};\n"); + fclose(f); +} +#endif |