Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // bpc.cpp : Defines the entry point for the console application.
- //
- #include <cstdio>
- #include <cstdint>
- #include <vector>
- #include <locale>
- #define IMSIZE (128*128)
- // BPE compression
- //The number of actual valid symbols. 16 here to encode the 16 Pico8 colours
- #define SYMBOLS 16
- //How many pairs can be defined. I chose this and ASCOFF to fit easily into plain ASCII
- #define PAIRS 73
- #define TOTALCODES (SYMBOLS+PAIRS)
- #define ASCOFF 35
- uint8_t left[PAIRS];
- uint8_t right[PAIRS];
- uint32_t counts[TOTALCODES*TOTALCODES];
- uint32_t numpairs;
- uint8_t palette[16][3] =
- {
- { 0, 0, 0 } ,
- { 29, 43, 83 } ,
- {126, 37, 83} ,
- {0, 135, 81} ,
- {171, 82, 54} ,
- {95, 87, 79 } ,
- {194, 195, 199} ,
- {255, 241, 232} ,
- {255, 0, 77} ,
- {255, 163, 0} ,
- {255, 236, 39} ,
- {0, 228, 54} ,
- {41, 173, 255} ,
- {131, 118, 156} ,
- {255, 119, 168} ,
- {255, 204, 170}
- };
- //Read one line of PPM header, throwing away comments
- void GetLine(FILE* file)
- {
- char temp[1000];
- do
- {
- fgets(temp, 1000, file);
- } while (temp[0] == '#');
- }
- //Convert to PICO8 pallete. Just aborts if it finds a colour not in there.
- uint8_t MapCol(uint8_t r, uint8_t g, uint8_t b)
- {
- for (uint8_t i = 0; i < 16; i++)
- {
- if ((r == palette[i][0]) && (g == palette[i][1]) && (b == palette[i][2]))
- {
- return i;
- }
- }
- printf("ERROR: COLOUR NOT FOUND\n");
- exit(-1);
- return 0;
- }
- //Tweak backslashes so they don't get escaped. Does [[ ]] strings avoid the need to do this?
- uint8_t NoEscape(uint8_t i )
- {
- if (i == 92) return 126;
- return i;
- }
- bool OnePass(const std::vector<uint8_t>& input, std::vector<uint8_t>& output)
- {
- if (numpairs == PAIRS)
- {
- output = input;
- return false;
- }
- // find the most common pair
- std::fill(counts, counts + TOTALCODES*TOTALCODES, 0);
- output.clear();
- for (uint32_t i = 0; i < input.size() - 1; i++)
- {
- counts[((uint32_t)input[i])*TOTALCODES + input[i + 1]]++;
- }
- uint32_t max=0;
- uint32_t maxpair=0;
- for (uint32_t i = 0; i < TOTALCODES*TOTALCODES; i++)
- {
- if (counts[i]>max)
- {
- max = counts[i];
- maxpair = i;
- }
- }
- // transform the input using this.
- uint32_t thisleft = maxpair / TOTALCODES;
- uint32_t thisright = maxpair % TOTALCODES;
- left[numpairs] = thisleft;
- right[numpairs] = thisright;
- numpairs++;
- uint32_t i = 0;
- while (i < input.size())
- {
- if (i == (input.size() - 1))
- {
- output.push_back(input[i]);
- i++;
- }
- else
- {
- if ((input[i] == thisleft) && (input[i + 1] == thisright))
- {
- output.push_back(numpairs - 1 + SYMBOLS);
- i += 2;
- }
- else
- {
- output.push_back(input[i]);
- i++;
- }
- }
- }
- return true;
- }
- void Compress(const std::vector<uint8_t>& input, std::vector<uint8_t>& output)
- {
- numpairs = 0;
- std::fill(left, left + PAIRS, 0);
- std::fill(right, right + PAIRS, 0);
- std::vector<uint8_t> temp = input;
- // do repeated passes until we can encode no more pairs
- while (OnePass(temp, output))
- {
- std::swap(temp, output);
- }
- return;
- }
- // define this to do a decompression and verify pass
- #if VERIFY
- void AddSymbol(uint8_t c, std::vector<uint8_t>& output)
- {
- if (c >= SYMBOLS)
- {
- AddSymbol(left[c - SYMBOLS], output);
- AddSymbol(right[c - SYMBOLS], output);
- }
- else
- {
- output.push_back(c);
- }
- }
- void Decompress(const std::vector<uint8_t>& input, std::vector<uint8_t>& output)
- {
- for (uint32_t i = 0; i < input.size(); i++)
- {
- AddSymbol(input[i], output);
- }
- }
- #endif
- //main entry point
- int main(int argc, char* argv[])
- {
- if (argc != 2)
- {
- printf("USAGE: bpc filename.ppm\n");
- return -1;
- }
- FILE* file = fopen(argv[1], "rb");
- std::vector<uint8_t> input, compressed, decompressed;
- input.resize(IMSIZE);
- if (file)
- {
- GetLine(file);
- GetLine(file);
- GetLine(file);
- for (int i = 0; i < IMSIZE; i++)
- {
- uint8_t r = fgetc(file);
- uint8_t g = fgetc(file);
- uint8_t b = fgetc(file);
- input[i] = MapCol(r,g,b);
- }
- Compress(input, compressed);
- #if VERIFY
- Decompress(compressed, decompressed);
- for (uint32_t i = 0; i < IMSIZE; i++)
- {
- if (input[i] != decompressed[i])
- {
- printf("MISMATCH ERROR %i %i %i\n", i, input[i], decompressed[i]);
- }
- }
- #endif
- printf("left=\"");
- for (uint32_t i = 0; i < PAIRS; i++)
- {
- putchar(NoEscape(left[i] + ASCOFF));
- }
- printf("\"\nright=\"");
- for (uint32_t i = 0; i < PAIRS; i++)
- {
- putchar(NoEscape(right[i] + ASCOFF));
- }
- printf("\"\nimstring=\"");
- for (uint32_t i = 0; i < compressed.size(); i++)
- {
- putchar(NoEscape(compressed[i] + ASCOFF));
- }
- printf("\"\n");
- printf("Compressed size %i\n", compressed.size());
- fclose(file);
- }
- else
- {
- printf("Could not open file.\n");
- }
- return 0;
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement