|
|
|
@ -0,0 +1,830 @@
|
|
|
|
|
/*
|
|
|
|
|
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
|
|
|
|
*
|
|
|
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <cstdint>
|
|
|
|
|
#include <cassert>
|
|
|
|
|
#include <algorithm>
|
|
|
|
|
#include <map>
|
|
|
|
|
#include <set>
|
|
|
|
|
#include <functional>
|
|
|
|
|
#include <cstdio>
|
|
|
|
|
#include <cstring>
|
|
|
|
|
|
|
|
|
|
// add a word at the end of every row with debug information
|
|
|
|
|
// #define ADD_EOR_DEBUGGING
|
|
|
|
|
#define ENCODE_565
|
|
|
|
|
|
|
|
|
|
#define RSHIFT 0
|
|
|
|
|
#ifdef ENCODE_565
|
|
|
|
|
#define GSHIFT 6
|
|
|
|
|
#define BSHIFT 11
|
|
|
|
|
#else
|
|
|
|
|
#define GSHIFT 5
|
|
|
|
|
#define BSHIFT 10
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
inline static uint16_t to_5n5(uint32_t t) {
|
|
|
|
|
#ifndef ENCODE_565
|
|
|
|
|
return ((t & 0xf8) >> 3) | ((t & 0xf800) >> 6 | ((t & 0xf80000) >> 9));
|
|
|
|
|
#else
|
|
|
|
|
return ((t & 0xf8) >> 3) | ((t & 0xf800) >> 5 | ((t & 0xf80000) >> 8));
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// uint32_t as may be used for 2 pixels
|
|
|
|
|
inline static uint32_t to_5n5(uint32_t r5, uint32_t g5, uint32_t b5) {
|
|
|
|
|
#ifndef ENCODE_565
|
|
|
|
|
return (b5 << 10) | (g5 << 5) | (r5);
|
|
|
|
|
#else
|
|
|
|
|
return (b5 << 11) | (g5 << 6) | (r5);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t sorted_keys[32] = {
|
|
|
|
|
0x0000,
|
|
|
|
|
0x0020,
|
|
|
|
|
0x0421,
|
|
|
|
|
0x0420,
|
|
|
|
|
|
|
|
|
|
0x8420,
|
|
|
|
|
0x0400,
|
|
|
|
|
0x0021,
|
|
|
|
|
0x0441,
|
|
|
|
|
|
|
|
|
|
0x0041,
|
|
|
|
|
0x0821,
|
|
|
|
|
0x00442,
|
|
|
|
|
0x00401,
|
|
|
|
|
|
|
|
|
|
0x08020,
|
|
|
|
|
0x08440,
|
|
|
|
|
0x08400,
|
|
|
|
|
0x08820,
|
|
|
|
|
|
|
|
|
|
0x00822,
|
|
|
|
|
0x08040,
|
|
|
|
|
0x10820,
|
|
|
|
|
0x08800,
|
|
|
|
|
|
|
|
|
|
0x00040,
|
|
|
|
|
0x08041,
|
|
|
|
|
0x00422,
|
|
|
|
|
0x00801,
|
|
|
|
|
|
|
|
|
|
0x00842,
|
|
|
|
|
0x00042,
|
|
|
|
|
0x10440,
|
|
|
|
|
0x00462,
|
|
|
|
|
|
|
|
|
|
0x00062,
|
|
|
|
|
0x00463,
|
|
|
|
|
0x00440,
|
|
|
|
|
0x08021,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static uint8_t *key_lookup;
|
|
|
|
|
static uint16_t *key_dist;
|
|
|
|
|
static int8_t *key_offset;
|
|
|
|
|
|
|
|
|
|
std::vector<unsigned char> converted;
|
|
|
|
|
|
|
|
|
|
uint8_t dp[] = {
|
|
|
|
|
0, 8, 2, 10,
|
|
|
|
|
12, 4, 14, 6,
|
|
|
|
|
3, 11, 1, 9,
|
|
|
|
|
15, 7, 13, 5
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static inline void dither(uint8_t& c, uint x3, uint y3) {
|
|
|
|
|
uint x = c + dp[x3 + y3 * 4] / 2;
|
|
|
|
|
if (x > 255) x = 255;
|
|
|
|
|
c = (x & 0xf8u);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void dither(uint8_t& r, uint8_t& g, uint8_t &b, uint x3, uint y3) {
|
|
|
|
|
dither(r, x3, y3);
|
|
|
|
|
dither(g, x3, y3);
|
|
|
|
|
dither(b, x3, y3);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void dither_image(uint w, uint h, std::vector<unsigned char> &source)
|
|
|
|
|
{
|
|
|
|
|
for(uint y=0; y < h; y++) {
|
|
|
|
|
uint8_t *base = &source[y * w * 3];
|
|
|
|
|
for(uint x = 0; x < w; x+= 4) {
|
|
|
|
|
#if 1
|
|
|
|
|
dither(base[0], base[1], base[2], 0, y&3u);
|
|
|
|
|
dither(base[3], base[4], base[5], 1, y&3u);
|
|
|
|
|
dither(base[6], base[7], base[8], 2, y&3u);
|
|
|
|
|
dither(base[9], base[10], base[11], 3, y&3u);
|
|
|
|
|
#endif
|
|
|
|
|
base += 12;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int min_cost = 0x7fffffff;
|
|
|
|
|
static int max_cost = 0;
|
|
|
|
|
static int worst_frame = 0;
|
|
|
|
|
static long total_cost = 0;
|
|
|
|
|
static long total_vals = 0;
|
|
|
|
|
|
|
|
|
|
static uint rgb16(uint8_t r, uint8_t g, uint8_t b) {
|
|
|
|
|
return ((b>>3) << 11) | ((g>>3)<<6) | (r>>3);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static uint rgb15(uint8_t r, uint8_t g, uint8_t b) {
|
|
|
|
|
return ((b>>3) << 10) | ((g>>3)<<5) | (r>>3);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void write_word(uint8_t *&dest, uint v) {
|
|
|
|
|
*dest++ = v & 0xff;
|
|
|
|
|
*dest++ = v >> 8;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void write_word_be(uint8_t *&dest, uint v) {
|
|
|
|
|
*dest++ = v >> 8;
|
|
|
|
|
*dest++ = v & 0xff;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static uint32_t *row0_222;
|
|
|
|
|
static uint32_t *row1_222;
|
|
|
|
|
static uint32_t *row0_5;
|
|
|
|
|
static uint32_t *row1_5;
|
|
|
|
|
|
|
|
|
|
static inline uint get_key(uint min, uint da, uint db, uint dc, uint dd) {
|
|
|
|
|
assert(da >= min);
|
|
|
|
|
assert(db >= min);
|
|
|
|
|
assert(dc >= min);
|
|
|
|
|
assert(dd >= min);
|
|
|
|
|
return (((da - min) >> 3u) << 5u * 3u) |
|
|
|
|
|
(((db - min) >> 3u) << 5u * 2u) |
|
|
|
|
|
(((dc - min) >> 3u) << 5u * 1u) |
|
|
|
|
|
(((dd - min) >> 3u) << 5u * 0u);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifndef ENCODE_565
|
|
|
|
|
bool compress_image(const char *name, uint w, uint h, std::vector<unsigned char> &source, std::vector<unsigned char> &dest, std::vector<uint32_t> &line_offsets, uint max_rdist, uint max_gdist, uint max_bdist, uint extra_line_words = 0)
|
|
|
|
|
{
|
|
|
|
|
bool use_56bit_raw = true;
|
|
|
|
|
|
|
|
|
|
assert(!((w|h)&1u));
|
|
|
|
|
|
|
|
|
|
if (!key_lookup)
|
|
|
|
|
{
|
|
|
|
|
key_lookup = (uint8_t *)calloc(1,0x100000);
|
|
|
|
|
key_dist = (uint16_t *) calloc(2, 0x100000);
|
|
|
|
|
key_offset = (int8_t *) calloc(1, 0x100000);
|
|
|
|
|
for(int i = 0; i < 0x100000; i++)
|
|
|
|
|
{
|
|
|
|
|
key_dist[i] = 0x7fffu;
|
|
|
|
|
}
|
|
|
|
|
for(int i = 0; i < 32; i++)
|
|
|
|
|
{
|
|
|
|
|
uint8_t choice = (i < 4) ? 0x81 + i : 1 + i;
|
|
|
|
|
key_lookup[sorted_keys[i]] = choice;
|
|
|
|
|
key_dist[sorted_keys[i]] = 0;
|
|
|
|
|
for(int o = -3; o < 3; o++) { // todo really?
|
|
|
|
|
// for(int o=0;o<1;o++) {
|
|
|
|
|
uint da = (sorted_keys[i] >> (5u * 3u)) & 0x1f;
|
|
|
|
|
uint db = (sorted_keys[i] >> (5u * 2u)) & 0x1f;
|
|
|
|
|
uint dc = (sorted_keys[i] >> (5u * 1u)) & 0x1f;
|
|
|
|
|
uint dd = (sorted_keys[i] >> (5u * 0u)) & 0x1f;
|
|
|
|
|
// if (da == db && db == dc & dc == dd && (o == da || o == -da)) continue;
|
|
|
|
|
for(uint a = 0; a < 32; a++)
|
|
|
|
|
{
|
|
|
|
|
for(uint b = 0; b < 32; b++)
|
|
|
|
|
{
|
|
|
|
|
for(uint c = 0; c < 32; c++)
|
|
|
|
|
{
|
|
|
|
|
for(uint d = 0; d < 32; d++)
|
|
|
|
|
{
|
|
|
|
|
uint j = (a << (5u * 3u)) | (b << (5u * 2u)) | (c << (5u * 1u) | (d << (5u * 0u)));
|
|
|
|
|
uint score = (a + o - da) * (a + o - da);
|
|
|
|
|
score += (b + o - db) * (b + o - db);
|
|
|
|
|
score += (c + o - dc) * (c + o - dc);
|
|
|
|
|
score += (d + o - dd) * (d + o - dd);
|
|
|
|
|
if (score < key_dist[j] || (score == key_dist[j] && !o))
|
|
|
|
|
{
|
|
|
|
|
if (score == 0) {
|
|
|
|
|
// printf("pants\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
key_dist[j] = score;
|
|
|
|
|
key_lookup[j] = choice;
|
|
|
|
|
key_offset[j] = o;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for(int i = 0; i < 32; i++)
|
|
|
|
|
{
|
|
|
|
|
assert(key_dist[sorted_keys[i]] == 0);
|
|
|
|
|
}
|
|
|
|
|
uint t = 0;
|
|
|
|
|
for(int i = 0; i < 0x100000; i++) {
|
|
|
|
|
if (!key_dist[i]) {
|
|
|
|
|
// printf("%d %d %d\n", i, key_offset[i], key_dist[i]);
|
|
|
|
|
t++;
|
|
|
|
|
}
|
|
|
|
|
if (key_offset[i]) {
|
|
|
|
|
//printf("%d %d %d\n", i, key_offset[i], key_dist[i]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// assert(t == 32);
|
|
|
|
|
assert(t == 128); // todo why
|
|
|
|
|
}
|
|
|
|
|
uint32_t counts[4] = {0,0,0,0};
|
|
|
|
|
uint8_t *d = &dest[0];
|
|
|
|
|
line_offsets.clear();
|
|
|
|
|
for(uint y=0; y < h; y+=2) {
|
|
|
|
|
uint8_t *base = &source[y * w * 3];
|
|
|
|
|
uint8_t *base2 = base + w * 3;
|
|
|
|
|
line_offsets.push_back(d - &dest[0]);
|
|
|
|
|
|
|
|
|
|
for(uint x = 0; x < w; x+= 2) {
|
|
|
|
|
uint rmin = std::min({base[0], base[3], base2[0], base2[3]});
|
|
|
|
|
uint gmin = std::min({base[1], base[4], base2[1], base2[4]});
|
|
|
|
|
uint bmin = std::min({base[2], base[5], base2[2], base2[5]});
|
|
|
|
|
uint rkey = get_key(rmin, base[0], base[3], base2[0], base2[3]);
|
|
|
|
|
uint16_t rdist = key_dist[rkey];
|
|
|
|
|
uint8_t rval = key_lookup[rkey];
|
|
|
|
|
uint gkey = get_key(gmin, base[1], base[4], base2[1], base2[4]);
|
|
|
|
|
uint16_t gdist = key_dist[gkey];
|
|
|
|
|
uint8_t gval = key_lookup[gkey];
|
|
|
|
|
uint bkey = get_key(bmin, base[2], base[5], base2[2], base2[5]);
|
|
|
|
|
uint16_t bdist = key_dist[bkey];
|
|
|
|
|
uint8_t bval = key_lookup[bkey];
|
|
|
|
|
uint16_t dist = MAX(rdist, MAX(gdist, bdist));
|
|
|
|
|
uint16_t val = MIN(rval, MIN(gval, bval));
|
|
|
|
|
// if (dist) {
|
|
|
|
|
// if (rdist > 4 || gdist > 4 || bdist > 4) {
|
|
|
|
|
if (rdist > max_rdist || gdist > max_gdist || bdist > max_bdist) {
|
|
|
|
|
// if (rdist <= 4 && gdist <= 4 && bdist <= 4) {
|
|
|
|
|
// memset(base, 0, 6);
|
|
|
|
|
// if (key_offset[rkey]) base[0] |= 0xf8;
|
|
|
|
|
// if (key_offset[gkey]) base[1] |= 0xf8;
|
|
|
|
|
// if (key_offset[bkey]) base[2] |= 0xf8;
|
|
|
|
|
// //printf("oobla\n");
|
|
|
|
|
// }
|
|
|
|
|
if (use_56bit_raw)
|
|
|
|
|
{
|
|
|
|
|
// #2 : encode ABCD raw as 555, 454, 454, 454 in 7 bytes
|
|
|
|
|
// v v v v v v
|
|
|
|
|
// | Ga2 Ga1 Ga0 Ra4 : Ra3 Ra2 Ra1 Ra0
|
|
|
|
|
// | "1" Ba4 Ba3 Ba2 : Ba1 Ba0 Ga4 Ga3
|
|
|
|
|
//
|
|
|
|
|
// | Gb2 Gb1 Gb0 Rb4 : Rb3 Rb2 Rb1 Bd2
|
|
|
|
|
// | "0" Bb4 Bb3 Bb2 : Bb1 Gd4 Gb4 Gb3
|
|
|
|
|
//
|
|
|
|
|
// | Gc2 Gc1 Gc0 Rc4 : Rc3 Rc2 Rc1 Bd1
|
|
|
|
|
// | Bd3 Bc4 Bc3 Bc2 : Bc1 Gd3 Gc4 Gc3
|
|
|
|
|
//
|
|
|
|
|
// | Gd2 Gd1 Gd0 Rd4 : Rd3 Rd2 Rd1 Bd4 |
|
|
|
|
|
|
|
|
|
|
uint32_t lo = 0x8000u | rgb15(base[0], base[1], base[2]) | (rgb15(base[3], base[4], base[5]) << 16);
|
|
|
|
|
uint32_t hi = rgb15(base2[0], base2[1], base2[2]) | (rgb15(base2[3], base2[4], base2[5]) << 16);
|
|
|
|
|
// from "0" Bd4 Bd3 Bd2 : Bd1 Bd0 Gd4 Gd3
|
|
|
|
|
*d++ = lo;
|
|
|
|
|
*d++ = lo >> 8u;
|
|
|
|
|
*d++ = ((lo >> 16u) & ~1u) | ((hi & 0x10000000u)?1u:0);
|
|
|
|
|
*d++ = ((lo >> 24u) & ~4u) | ((hi & 0x02000000u)?4u:0);
|
|
|
|
|
*d++ = (hi & ~1u) | ((hi & 0x08000000u)?1u:0);
|
|
|
|
|
*d++ = ((hi >> 8u) & ~0x84u) | ((hi & 0x20000000u)?0x80u:0) | ((hi & 0x01000000u)?0x4u:0);
|
|
|
|
|
*d++ = ((hi >> 16u) & ~1u) | ((hi & 0x40000000u)?1u:0);
|
|
|
|
|
counts[3]++;
|
|
|
|
|
} else
|
|
|
|
|
{
|
|
|
|
|
// #1 : encode ABCD raw as 555, 555, 555, 555 in 8 bytes
|
|
|
|
|
//
|
|
|
|
|
// | Ga2 Ga1 Ga0 Ra4 : Ra3 Ra2 Ra1 Ra0
|
|
|
|
|
// | "1" Ba4 Ba3 Ba2 : Ba1 Ba0 Ga4 Ga3
|
|
|
|
|
|
|
|
|
|
// | Gb2 Gb1 Gb0 Rb4 : Rb3 Rb2 Rb1 Rb0
|
|
|
|
|
// | "1" Bb4 Bb3 Bb2 : Bb1 Bb0 Gb4 Gb3
|
|
|
|
|
|
|
|
|
|
// | Gc2 Gc1 Gc0 Rc4 : Rc3 Rc2 Rc1 Rc0
|
|
|
|
|
// | "0" Bc4 Bc3 Bc2 : Bc1 Bc0 Gc4 Gc3
|
|
|
|
|
|
|
|
|
|
// | Gd2 Gd1 Gd0 Rd4 : Rd3 Rd2 Rd1 Rc0
|
|
|
|
|
// | "0" Bd4 Bd3 Bd2 : Bd1 Bd0 Gd4 Gd3 |
|
|
|
|
|
//
|
|
|
|
|
write_word(d, 0x8000u | rgb15(base[0], base[1], base[2]));
|
|
|
|
|
write_word(d, 0x8000u | rgb15(base[3], base[4], base[5]));
|
|
|
|
|
write_word(d, rgb15(base2[0], base2[1], base2[2]));
|
|
|
|
|
write_word(d, rgb15(base2[3], base2[4], base2[5]));
|
|
|
|
|
counts[0]++;
|
|
|
|
|
}
|
|
|
|
|
} else if (val < 0x80) {
|
|
|
|
|
// #1 encode 555 color, then 1 of 32 2x2 add patterns for each component in 4 bytes total
|
|
|
|
|
assert(rval && gval && bval);
|
|
|
|
|
// | Ga2 Ga1 Ga0 Ra4 : Ra3 Ra2 Ra1 Ra0
|
|
|
|
|
// | "0" Ba4 Ba3 Ba2 : Ba1 Ba0 Ga4 Ga3
|
|
|
|
|
// |
|
|
|
|
|
// | "1" Bp4 Bp3 Bp2 : Bp1 Bp0 Gp4 Gp3
|
|
|
|
|
// | Gp2 Gp1 Gp0 Rp4 : Rp3 Rp2 Rp1 Rp0
|
|
|
|
|
write_word(d, rgb15(rmin-key_offset[rkey], gmin - key_offset[gkey], bmin - key_offset[bkey]));
|
|
|
|
|
write_word_be(d, 0x8000u | rgb15((rval - 1u)<<3u, (gval -1u)<<3u, (bval-1u)<<3u));
|
|
|
|
|
counts[1]++;
|
|
|
|
|
} else {
|
|
|
|
|
// #1 encode 555 color, then 1 of 4 2x2 add patterns for each component in 3 bytes total
|
|
|
|
|
// | Ga2 Ga1 Ga0 Ra4 : Ra3 Ra2 Ra1 Ra0
|
|
|
|
|
// | "0" Ba4 Ba3 Ba2 : Ba1 Ba0 Ga4 Ga3
|
|
|
|
|
// |
|
|
|
|
|
// | "0" "0" Bp1 Bp0 : Gp1 Gp0 Rp1 Rp0
|
|
|
|
|
write_word(d, rgb15(rmin-key_offset[rkey], gmin - key_offset[gkey], bmin - key_offset[bkey]));
|
|
|
|
|
*d++ = ((bval - 0x81u) << 4u) | ((gval - 0x81u) << 2u) | (rval - 0x81u);
|
|
|
|
|
counts[2]++;
|
|
|
|
|
}
|
|
|
|
|
base += 6;
|
|
|
|
|
base2 += 6;
|
|
|
|
|
}
|
|
|
|
|
while (3u & (uintptr_t)d) *d++ = 0; // need aligned rows
|
|
|
|
|
for(int i=0;i<extra_line_words;i++) {
|
|
|
|
|
*d++ = 0;
|
|
|
|
|
*d++ = 0;
|
|
|
|
|
*d++ = 0;
|
|
|
|
|
*d++ = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
line_offsets.push_back(d - &dest[0]);
|
|
|
|
|
while (d < dest.end().base()) *d++ = 0;
|
|
|
|
|
//return counts[0]*8 + counts[3] * 7 + counts[1] *3 + counts[2] * 4;
|
|
|
|
|
int cost = counts[0]*8 + counts[3] * 7 + counts[2] *3 + counts[1] * 4;
|
|
|
|
|
min_cost = MIN(cost, min_cost);
|
|
|
|
|
bool rc = cost > max_cost;
|
|
|
|
|
max_cost = MAX(cost, max_cost);
|
|
|
|
|
total_cost += cost;
|
|
|
|
|
total_vals += w*h;
|
|
|
|
|
// printf("%s %d*8 %d*7 %d*4 %d*3\n", name, counts[0], counts[3], counts[1], counts[2]);
|
|
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
bool compress_image(const char *name, uint w, uint h, std::vector<unsigned char> &source, std::vector<unsigned char> &dest, std::vector<uint32_t> &line_offsets, uint max_rdist, uint max_gdist, uint max_bdist, uint extra_line_words = 0)
|
|
|
|
|
{
|
|
|
|
|
bool use_56bit_raw = true;
|
|
|
|
|
|
|
|
|
|
assert(!((w|h)&1u));
|
|
|
|
|
|
|
|
|
|
if (!key_lookup)
|
|
|
|
|
{
|
|
|
|
|
key_lookup = (uint8_t *) calloc(1, 0x100000);
|
|
|
|
|
key_dist = (uint16_t *) calloc(2, 0x100000);
|
|
|
|
|
key_offset = (int8_t *) calloc(1, 0x100000);
|
|
|
|
|
for(int i = 0; i < 0x100000; i++)
|
|
|
|
|
{
|
|
|
|
|
key_dist[i] = 0x7fffu;
|
|
|
|
|
}
|
|
|
|
|
for(int i = 0; i < 32; i++)
|
|
|
|
|
{
|
|
|
|
|
uint8_t choice = (i < 4) ? 0x81 + i : 1 + i;
|
|
|
|
|
key_lookup[sorted_keys[i]] = choice;
|
|
|
|
|
key_dist[sorted_keys[i]] = 0;
|
|
|
|
|
for(int o = -3; o < 3; o++) { // todo really?
|
|
|
|
|
// for(int o=0;o<1;o++) {
|
|
|
|
|
uint da = (sorted_keys[i] >> (5u * 3u)) & 0x1f;
|
|
|
|
|
uint db = (sorted_keys[i] >> (5u * 2u)) & 0x1f;
|
|
|
|
|
uint dc = (sorted_keys[i] >> (5u * 1u)) & 0x1f;
|
|
|
|
|
uint dd = (sorted_keys[i] >> (5u * 0u)) & 0x1f;
|
|
|
|
|
// if (da == db && db == dc & dc == dd && (o == da || o == -da)) continue;
|
|
|
|
|
for(uint a = 0; a < 32; a++)
|
|
|
|
|
{
|
|
|
|
|
for(uint b = 0; b < 32; b++)
|
|
|
|
|
{
|
|
|
|
|
for(uint c = 0; c < 32; c++)
|
|
|
|
|
{
|
|
|
|
|
for(uint d = 0; d < 32; d++)
|
|
|
|
|
{
|
|
|
|
|
uint j = (a << (5u * 3u)) | (b << (5u * 2u)) | (c << (5u * 1u) | (d << (5u * 0u)));
|
|
|
|
|
uint score = (a + o - da) * (a + o - da);
|
|
|
|
|
score += (b + o - db) * (b + o - db);
|
|
|
|
|
score += (c + o - dc) * (c + o - dc);
|
|
|
|
|
score += (d + o - dd) * (d + o - dd);
|
|
|
|
|
// todo what is this second half
|
|
|
|
|
//if (score < key_dist[j]) // || (score == key_dist[j] && !o))
|
|
|
|
|
if (score < key_dist[j] || (score == key_dist[j] && !o))
|
|
|
|
|
{
|
|
|
|
|
if (score == 0) {
|
|
|
|
|
// printf("pants\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
key_dist[j] = score;
|
|
|
|
|
key_lookup[j] = choice;
|
|
|
|
|
key_offset[j] = o;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for(int i = 0; i < 32; i++)
|
|
|
|
|
{
|
|
|
|
|
assert(key_dist[sorted_keys[i]] == 0);
|
|
|
|
|
}
|
|
|
|
|
uint t = 0;
|
|
|
|
|
for(int i = 0; i < 0x100000; i++) {
|
|
|
|
|
if (!key_dist[i]) {
|
|
|
|
|
// printf("%d %d %d\n", i, key_offset[i], key_dist[i]);
|
|
|
|
|
t++;
|
|
|
|
|
}
|
|
|
|
|
if (key_offset[i]) {
|
|
|
|
|
//printf("%d %d %d\n", i, key_offset[i], key_dist[i]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// assert(t == 32);
|
|
|
|
|
assert(t == 128); // todo why
|
|
|
|
|
}
|
|
|
|
|
uint32_t counts[4] = {0,0,0,0};
|
|
|
|
|
uint8_t *d = &dest[0];
|
|
|
|
|
line_offsets.clear();
|
|
|
|
|
for(uint y=0; y < h; y+=2) {
|
|
|
|
|
uint8_t *base = &source[y * w * 3];
|
|
|
|
|
uint8_t *base2 = base + w * 3;
|
|
|
|
|
line_offsets.push_back(d - &dest[0]);
|
|
|
|
|
|
|
|
|
|
for(uint x = 0; x < w; x+= 2) {
|
|
|
|
|
uint rmin = std::min({base[0], base[3], base2[0], base2[3]});
|
|
|
|
|
uint gmin = std::min({base[1], base[4], base2[1], base2[4]});
|
|
|
|
|
uint bmin = std::min({base[2], base[5], base2[2], base2[5]});
|
|
|
|
|
uint rkey = get_key(rmin, base[0], base[3], base2[0], base2[3]);
|
|
|
|
|
uint16_t rdist = key_dist[rkey];
|
|
|
|
|
uint8_t rval = key_lookup[rkey];
|
|
|
|
|
uint gkey = get_key(gmin, base[1], base[4], base2[1], base2[4]);
|
|
|
|
|
uint16_t gdist = key_dist[gkey];
|
|
|
|
|
uint8_t gval = key_lookup[gkey];
|
|
|
|
|
uint bkey = get_key(bmin, base[2], base[5], base2[2], base2[5]);
|
|
|
|
|
uint16_t bdist = key_dist[bkey];
|
|
|
|
|
uint8_t bval = key_lookup[bkey];
|
|
|
|
|
uint16_t dist = std::max({rdist, gdist, bdist});
|
|
|
|
|
uint16_t val = std::min({rval, gval, bval});
|
|
|
|
|
// if (dist) {
|
|
|
|
|
// if (rdist > 4 || gdist > 4 || bdist > 4) {
|
|
|
|
|
if (rdist > max_rdist || gdist > max_gdist || bdist > max_bdist) {
|
|
|
|
|
if (use_56bit_raw)
|
|
|
|
|
{
|
|
|
|
|
// #2 : encode ABCD raw as 555, 454, 454, 454 in 7 bytes
|
|
|
|
|
//
|
|
|
|
|
// | Ga1 Ga0 "1" Ra4 : Ra3 Ra2 Ra1 Ra0
|
|
|
|
|
// | Ba4 Ba3 Ba2 Ba1 : Ba0 Ga4 Ga3 Ga2
|
|
|
|
|
//
|
|
|
|
|
// | Gb1 Gb0 "0" Rb4 : Rb3 Rb2 Rb1 Bd1
|
|
|
|
|
// | Bb4 Bb3 Bb2 Bb1 : Bd2 Gb4 Gb3 Gb2
|
|
|
|
|
//
|
|
|
|
|
// | Gc1 Gc0 Bd3 Rc4 : Rc3 Rc2 Rc1 Gd3
|
|
|
|
|
// | Bc4 Bc3 Bc2 Bc1 : Gd4 Gc4 Gc3 Gc2
|
|
|
|
|
//
|
|
|
|
|
// | Gd1 Gd0 (Gd2^Gd4) Rd4 : Rd3 Rd2 Rd1 Bd4
|
|
|
|
|
|
|
|
|
|
// | Bd4 Bd3 Bd2 Bd1 : Gd4 Gd3 Gd2
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
uint32_t lo = 0x0020u | rgb16(base[0], base[1], base[2]) | (rgb16(base[3], base[4], base[5]) << 16u);
|
|
|
|
|
uint32_t hi = rgb16(base2[0], base2[1], base2[2]) | (rgb16(base2[3], base2[4], base2[5]) << 16u);
|
|
|
|
|
// from "0" Bd4 Bd3 Bd2 : Bd1 Bd0 Gd4 Gd3
|
|
|
|
|
*d++ = lo;
|
|
|
|
|
*d++ = lo >> 8u;
|
|
|
|
|
*d++ = ((lo >> 16u) & ~1u) | ((hi & 0x10000000u)?1u:0);
|
|
|
|
|
*d++ = ((lo >> 24u) & ~8u) | ((hi & 0x20000000u)?8u:0);
|
|
|
|
|
*d++ = (hi & ~ 0x21u) | ((hi & 0x40000000u)?0x20u:0) | ((hi & 0x02000000u)?1u:0);
|
|
|
|
|
*d++ = ((hi >> 8u) & ~0x8u) | ((hi & 0x04000000u)?0x8u:0);
|
|
|
|
|
*d++ = ((hi >> 16u) & ~0x21u) | ((((hi & 0x01000000u)?1u:0)^((hi & 0x04000000u)?1u:0))?0x20u:0) | ((hi & 0x80000000u)?1u:0);
|
|
|
|
|
counts[3]++;
|
|
|
|
|
} else
|
|
|
|
|
{
|
|
|
|
|
// #1 : encode ABCD raw as 555, 555, 555, 555 in 8 bytes
|
|
|
|
|
//
|
|
|
|
|
// | Ga1 Ga0 "1" Ra4 : Ra3 Ra2 Ra1 Ra0
|
|
|
|
|
// | Ba4 Ba3 Ba2 Ba1 : Ba0 Ga4 Ga3 Ga2
|
|
|
|
|
|
|
|
|
|
// | Ga1 Ga0 "1" Ra4 : Ra3 Ra2 Ra1 Ra0
|
|
|
|
|
// | Ba4 Ba3 Ba2 Ba1 : Ba0 Ga4 Ga3 Ga2
|
|
|
|
|
|
|
|
|
|
// | Ga1 Ga0 "0" Ra4 : Ra3 Ra2 Ra1 Ra0
|
|
|
|
|
// | Ba4 Ba3 Ba2 Ba1 : Ba0 Ga4 Ga3 Ga2
|
|
|
|
|
|
|
|
|
|
// | Ga1 Ga0 "0" Ra4 : Ra3 Ra2 Ra1 Ra0
|
|
|
|
|
// | Ba4 Ba3 Ba2 Ba1 : Ba0 Ga4 Ga3 Ga2
|
|
|
|
|
//
|
|
|
|
|
write_word(d, 0x0020u | rgb16(base[0], base[1], base[2]));
|
|
|
|
|
write_word(d, 0x0020u | rgb16(base[3], base[4], base[5]));
|
|
|
|
|
write_word(d, rgb16(base2[0], base2[1], base2[2]));
|
|
|
|
|
write_word(d, rgb16(base2[3], base2[4], base2[5]));
|
|
|
|
|
counts[0]++;
|
|
|
|
|
}
|
|
|
|
|
} else if (val < 0x80) {
|
|
|
|
|
// #1 encode 555 color, then 1 of 32 2x2 add patterns for each component in 4 bytes total
|
|
|
|
|
assert(rval && gval && bval);
|
|
|
|
|
// | Ga1 Ga0 "0" Ra4 : Ra3 Ra2 Ra1 Ra0
|
|
|
|
|
// | Ba4 Ba3 Ba2 Ba1 : Ba0 Ga4 Ga3 Ga2
|
|
|
|
|
// |
|
|
|
|
|
// | Gp1 Gp0 Rp4 Rp3 : Rp2 Rp1 Rp0 "1"
|
|
|
|
|
// | Bp4 Bp3 Bp2 Bp1 : Bp0 Gp4 Gp3 Gp2
|
|
|
|
|
write_word(d, rgb16(rmin-key_offset[rkey], gmin - key_offset[gkey], bmin - key_offset[bkey]));
|
|
|
|
|
write_word(d, 1u | ((rgb15((rval - 1u)<<3u, (gval -1u)<<3u, (bval-1u)<<3u)) << 1u));
|
|
|
|
|
counts[1]++;
|
|
|
|
|
} else {
|
|
|
|
|
// #1 encode 555 color, then 1 of 4 2x2 add patterns for each component in 3 bytes total
|
|
|
|
|
// | Ga1 Ga0 "0" Ra4 : Ra3 Ra2 Ra1 Ra0
|
|
|
|
|
// | Ba4 Ba3 Ba2 Ba1 : Ba0 Ga4 Ga3 Ga2
|
|
|
|
|
// |
|
|
|
|
|
// | Bp1 Bp0 Gp1 Gp0 : Rp1 Rp0 "0" "0'
|
|
|
|
|
write_word(d, rgb16(rmin-key_offset[rkey], gmin - key_offset[gkey], bmin - key_offset[bkey]));
|
|
|
|
|
*d++ = ((bval - 0x81u) << 6u) | ((gval - 0x81u) << 4u) | ((rval - 0x81u) << 2u);
|
|
|
|
|
counts[2]++;
|
|
|
|
|
}
|
|
|
|
|
base += 6;
|
|
|
|
|
base2 += 6;
|
|
|
|
|
}
|
|
|
|
|
while (3u & (uintptr_t)d) *d++ = 0; // need aligned rows
|
|
|
|
|
for(int i=0;i<extra_line_words;i++) {
|
|
|
|
|
*d++ = 0;
|
|
|
|
|
*d++ = 0;
|
|
|
|
|
*d++ = 0;
|
|
|
|
|
*d++ = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
line_offsets.push_back(d - &dest[0]);
|
|
|
|
|
while (d < dest.end().base()) *d++ = 0;
|
|
|
|
|
//return counts[0]*8 + counts[3] * 7 + counts[1] *3 + counts[2] * 4;
|
|
|
|
|
int cost = counts[0]*8 + counts[3] * 7 + counts[2] *3 + counts[1] * 4;
|
|
|
|
|
min_cost = std::min(cost, min_cost);
|
|
|
|
|
bool rc = cost > max_cost;
|
|
|
|
|
max_cost = std::max(cost, max_cost);
|
|
|
|
|
total_cost += cost;
|
|
|
|
|
total_vals += w*h;
|
|
|
|
|
// printf("%s %d*8 %d*7 %d*4 %d*3\n", name, counts[0], counts[3], counts[1], counts[2]);
|
|
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
void write_hword(uint word, FILE *out) {
|
|
|
|
|
assert(word < 0x10000);
|
|
|
|
|
fputc(word & 0xff, out);
|
|
|
|
|
fputc(word >> 8, out);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t pad_sector(uint32_t sector_offset, FILE *out) {
|
|
|
|
|
assert(sector_offset < 512);
|
|
|
|
|
if (sector_offset) for (;sector_offset < 512; sector_offset++) fputc(0, out);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint8_t to_bcd(uint x) {
|
|
|
|
|
assert(x<100);
|
|
|
|
|
return (x/10)*16 + (x%10);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define PLAT_MAJOR 0
|
|
|
|
|
#define PLAT_MINOR 60
|
|
|
|
|
|
|
|
|
|
struct frame_header {
|
|
|
|
|
uint32_t mark0;
|
|
|
|
|
uint32_t mark1;
|
|
|
|
|
uint32_t magic;
|
|
|
|
|
uint8_t major, minior, debug, spare;
|
|
|
|
|
uint32_t sector_number; // relative to start of stream
|
|
|
|
|
uint32_t frame_number;
|
|
|
|
|
uint8_t hh, mm, ss, ff; // good old CD days (bcd)
|
|
|
|
|
uint32_t header_words;
|
|
|
|
|
uint16_t width;
|
|
|
|
|
uint16_t height;
|
|
|
|
|
uint32_t image_words;
|
|
|
|
|
uint32_t audio_words; // just to confirm really
|
|
|
|
|
uint32_t audio_freq;
|
|
|
|
|
uint8_t audio_channels; // always assume 16 bit
|
|
|
|
|
uint8_t pad[3];
|
|
|
|
|
uint32_t unused[4]; // little space for expansion
|
|
|
|
|
uint32_t total_sectors;
|
|
|
|
|
uint32_t last_sector;
|
|
|
|
|
// 1, 2, 4, 8 frame increments
|
|
|
|
|
uint32_t forward_frame_sector[4];
|
|
|
|
|
uint32_t backward_frame_sectors[4];
|
|
|
|
|
// h/2 + 1 row_offsets, last one should == image_words
|
|
|
|
|
uint16_t row_offsets[];
|
|
|
|
|
} __attribute__((packed));
|
|
|
|
|
|
|
|
|
|
int encode_movie(const char *filename, const char *audio_filename, const char *filename_out, int start_frame) {
|
|
|
|
|
worst_frame = 0;
|
|
|
|
|
total_cost = 0;
|
|
|
|
|
max_cost = 0;
|
|
|
|
|
min_cost = 0x7fffffff;
|
|
|
|
|
total_vals = 0;
|
|
|
|
|
|
|
|
|
|
#ifdef ADD_EOR_DEBUGGING
|
|
|
|
|
uint extra_line_words = 1;
|
|
|
|
|
#else
|
|
|
|
|
uint extra_line_words = 0;
|
|
|
|
|
#endif
|
|
|
|
|
unsigned error;
|
|
|
|
|
uint w = 320;
|
|
|
|
|
uint h = 240;
|
|
|
|
|
assert(!(h&1));
|
|
|
|
|
size_t size3 = w * h * 3;
|
|
|
|
|
size_t size2 = w * h * 2;
|
|
|
|
|
std::vector<uint32_t> frame_sectors;
|
|
|
|
|
std::vector<unsigned char> source;
|
|
|
|
|
std::vector<unsigned char> dest;
|
|
|
|
|
std::vector<uint32_t> line_offsets;
|
|
|
|
|
line_offsets.resize(120);
|
|
|
|
|
source.resize(size3);
|
|
|
|
|
dest.resize(size2);
|
|
|
|
|
FILE *file = fopen(filename, "rb");
|
|
|
|
|
FILE *audio_file = audio_filename ? fopen(audio_filename, "rb") : nullptr;
|
|
|
|
|
FILE *file_out = filename_out ? fopen(filename_out, "wb") : nullptr;
|
|
|
|
|
if (!file) {
|
|
|
|
|
fprintf(stderr, "Couldn't open input rgb file %s\n", filename);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
if (audio_filename && !audio_file) {
|
|
|
|
|
fprintf(stderr, "Couldn't open input pcm file %s\n", audio_filename);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
if (filename_out && !file_out) {
|
|
|
|
|
fprintf(stderr, "Couldn't open output pl2 file %s\n", filename_out);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
fseek(file, 0, SEEK_END);
|
|
|
|
|
size_t frames = ftell(file) / size3;
|
|
|
|
|
printf("Frame count %ld = %02ld:%02ld:%02ld\n", frames, (frames / (3600 * 30)) % 60, (frames / (60 * 30)) % 60, (frames / 30) % 60);
|
|
|
|
|
fseek(file, start_frame * size3, SEEK_SET);
|
|
|
|
|
// frames = 10000; // movie
|
|
|
|
|
uint32_t worst_length = 0;
|
|
|
|
|
int lcount=0;
|
|
|
|
|
int blcount=0;
|
|
|
|
|
int fbmax=0;
|
|
|
|
|
int bfcount=0;
|
|
|
|
|
uint32_t sector_offset = 0;
|
|
|
|
|
|
|
|
|
|
// todo i don't remember what the lineage of these were
|
|
|
|
|
// const int max_rdist = 8;
|
|
|
|
|
// const int max_gdist = 6;
|
|
|
|
|
// const int max_bdist = 8;
|
|
|
|
|
|
|
|
|
|
const int max_rdist = 4;
|
|
|
|
|
const int max_gdist = 4;
|
|
|
|
|
const int max_bdist = 4;
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i<frames;i++)
|
|
|
|
|
{
|
|
|
|
|
if (1 != fread(&source[0], size3, 1, file)) {
|
|
|
|
|
fprintf(stderr, "Error reading frame %d\n", i);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
dither_image(w, h, source);
|
|
|
|
|
if (compress_image(NULL, w, h, source, dest, line_offsets, max_rdist, max_gdist, max_bdist, extra_line_words))
|
|
|
|
|
{
|
|
|
|
|
worst_frame = i;
|
|
|
|
|
}
|
|
|
|
|
int fb = 0;
|
|
|
|
|
uint32_t l = 0;
|
|
|
|
|
for(uint32_t o : line_offsets)
|
|
|
|
|
{
|
|
|
|
|
int len = o - l;
|
|
|
|
|
if (len > 1024)
|
|
|
|
|
{
|
|
|
|
|
blcount++;
|
|
|
|
|
fb++;
|
|
|
|
|
}
|
|
|
|
|
lcount++;
|
|
|
|
|
if (len > worst_length)
|
|
|
|
|
{
|
|
|
|
|
worst_length = len;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
l = o;
|
|
|
|
|
}
|
|
|
|
|
#ifdef ADD_EOR_DEBUGGING
|
|
|
|
|
for(int r = 0; r < h/2; r++) {
|
|
|
|
|
int32_t o = line_offsets[r+1] - 4;
|
|
|
|
|
assert(o > 0);
|
|
|
|
|
assert(o < dest.size() - 4);
|
|
|
|
|
assert(!(o&3u));
|
|
|
|
|
// we cost 4 bytes per row, but we can use this instead of CRC to detect bad data (and also
|
|
|
|
|
// help with debugging, since we get a good idea what row the data belongs to)
|
|
|
|
|
dest[o + 0] = 0xaa;
|
|
|
|
|
dest[o + 1] = o + 1;
|
|
|
|
|
dest[o + 2] = i;
|
|
|
|
|
dest[o + 3] = r;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
fbmax = std::max(fb, fbmax);
|
|
|
|
|
if (fb) bfcount++;
|
|
|
|
|
if (file_out)
|
|
|
|
|
{
|
|
|
|
|
size_t secoff = ftell(file_out);
|
|
|
|
|
uint32_t sector_num = secoff / 512;
|
|
|
|
|
frame_sectors.push_back(sector_num);
|
|
|
|
|
struct frame_header header;
|
|
|
|
|
static_assert(sizeof(header) <= 512);
|
|
|
|
|
memset(&header, 0, sizeof(header));
|
|
|
|
|
header.mark0 = header.mark1 = 0xffffffff;
|
|
|
|
|
header.magic = ('T' << 24) | ('A' << 16) | ('L' << 8) | 'P';
|
|
|
|
|
header.major = PLAT_MAJOR;
|
|
|
|
|
header.minior = PLAT_MINOR;
|
|
|
|
|
#ifdef ADD_EOR_DEBUGGING
|
|
|
|
|
header.debug = 1;
|
|
|
|
|
#endif
|
|
|
|
|
header.sector_number = sector_num;
|
|
|
|
|
header.frame_number = i;
|
|
|
|
|
int n = i + start_frame;
|
|
|
|
|
header.hh = to_bcd(n / (60 * 60 * 30));
|
|
|
|
|
header.mm = to_bcd((n / (60 * 30)) % 60);
|
|
|
|
|
header.ss = to_bcd((n / 30) % 60);
|
|
|
|
|
header.ff = to_bcd(n % 30);
|
|
|
|
|
header.header_words = ((sizeof(header) + (h+1) + 1) + 3) / 4;
|
|
|
|
|
assert(header.header_words <= 128);
|
|
|
|
|
for(int f=0;f<4;f++) {
|
|
|
|
|
header.forward_frame_sector[f] = header.backward_frame_sectors[f] = 0xffffffff;
|
|
|
|
|
}
|
|
|
|
|
header.width = w;
|
|
|
|
|
header.height = h;
|
|
|
|
|
header.image_words = line_offsets[h / 2] >> 2;
|
|
|
|
|
if (audio_file) {
|
|
|
|
|
header.audio_freq = 44100;
|
|
|
|
|
header.audio_channels = 2;
|
|
|
|
|
header.audio_words = (header.audio_freq * 2 * header.audio_channels / 30) / 4;
|
|
|
|
|
} else {
|
|
|
|
|
header.audio_freq = header.audio_channels = header.audio_words = 0;
|
|
|
|
|
}
|
|
|
|
|
fwrite(&header, 1, sizeof(header), file_out);
|
|
|
|
|
sector_offset += sizeof(header);
|
|
|
|
|
// note there is one extra for the end
|
|
|
|
|
for(uint y = 0; y <= h / 2; y++)
|
|
|
|
|
{
|
|
|
|
|
uint off = line_offsets[y];
|
|
|
|
|
assert(!(3u & off));
|
|
|
|
|
off >>= 2;
|
|
|
|
|
write_hword(off, file_out);
|
|
|
|
|
sector_offset += 2;
|
|
|
|
|
}
|
|
|
|
|
assert(sector_offset <= 0x200);
|
|
|
|
|
sector_offset = pad_sector(sector_offset, file_out);
|
|
|
|
|
if (audio_file)
|
|
|
|
|
{
|
|
|
|
|
assert(header.audio_words * 30 * 4 ==
|
|
|
|
|
2 * header.audio_channels * header.audio_freq); // make sure we divide correctly
|
|
|
|
|
uint32_t buf[header.audio_words];
|
|
|
|
|
uint audio_size_bytes = header.audio_words * 4;
|
|
|
|
|
fseek(audio_file, audio_size_bytes * (size_t) i, SEEK_SET);
|
|
|
|
|
if (1 != fread(buf, audio_size_bytes, 1, audio_file)) {
|
|
|
|
|
fprintf(stderr, "Error reading audio for frame %d\n", i);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
fwrite(buf, audio_size_bytes, 1, file_out);
|
|
|
|
|
sector_offset = pad_sector(audio_size_bytes & 0x1ff, file_out);
|
|
|
|
|
}
|
|
|
|
|
uint32_t actual_size = line_offsets[h / 2];
|
|
|
|
|
fwrite(&dest[0], 1, actual_size, file_out);
|
|
|
|
|
sector_offset = pad_sector(actual_size & 0x1ff, file_out);
|
|
|
|
|
}
|
|
|
|
|
if (!(i%60))
|
|
|
|
|
{
|
|
|
|
|
printf("%02d:%02d:%02d\n", (i / (3600 * 30)) % 60, (i / (60 * 30)) % 60, (i / 30) % 60);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
uint32_t total_sectors = ftell(file_out) / 512;
|
|
|
|
|
|
|
|
|
|
fclose(file);
|
|
|
|
|
assert(frames == frame_sectors.size());
|
|
|
|
|
for(int i=0; i<frames; i++) {
|
|
|
|
|
fseek(file_out, 512 * ((uint64_t)frame_sectors[i]) + offsetof(frame_header, total_sectors), SEEK_SET);
|
|
|
|
|
fwrite(&total_sectors, 4, 1, file_out);
|
|
|
|
|
uint32_t v;
|
|
|
|
|
v = frame_sectors[frames-1]; fwrite(&v, 4, 1, file_out);
|
|
|
|
|
v = i < frames - 1 ? frame_sectors[i+1] : 0xffffffff; fwrite(&v, 4, 1, file_out);
|
|
|
|
|
v = i < frames - 2 ? frame_sectors[i+2] : 0xffffffff; fwrite(&v, 4, 1, file_out);
|
|
|
|
|
v = i < frames - 4 ? frame_sectors[i+4] : 0xffffffff; fwrite(&v, 4, 1, file_out);
|
|
|
|
|
v = i < frames - 8 ? frame_sectors[i+8] : 0xffffffff; fwrite(&v, 4, 1, file_out);
|
|
|
|
|
v = i >= 1 ? frame_sectors[i-1] : 0xffffffff; fwrite(&v, 4, 1, file_out);
|
|
|
|
|
v = i >= 2 ? frame_sectors[i-2] : 0xffffffff; fwrite(&v, 4, 1, file_out);
|
|
|
|
|
v = i >= 4 ? frame_sectors[i-4] : 0xffffffff; fwrite(&v, 4, 1, file_out);
|
|
|
|
|
v = i >= 8 ? frame_sectors[i-8] : 0xffffffff; fwrite(&v, 4, 1, file_out);
|
|
|
|
|
}
|
|
|
|
|
if (audio_file) fclose(audio_file);
|
|
|
|
|
if (file_out) fclose(file_out);
|
|
|
|
|
printf("last frame at %d\n", frame_sectors[frames-1]);
|
|
|
|
|
printf("Worst frame %d mic %d mac %d avg %d maxl %d\n", worst_frame, min_cost, max_cost, (int)((total_cost * w * (long)h) / total_vals), worst_length);
|
|
|
|
|
printf("%d %d %d, fbmax %d bfc %d/%ld\n", blcount, lcount, (int)(100l * blcount / lcount), fbmax, bfcount, frames);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int main(int argc, char **argv) {
|
|
|
|
|
if (argc != 4) {
|
|
|
|
|
fprintf(stderr, "usage: convert <rgb_file> <pcm_file> <output_file.pl2>\n");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
return encode_movie(argv[1], argv[2], argv[3], 0);
|
|
|
|
|
}
|