This week only. Pastebin PRO Accounts Christmas Special! Don't miss out!Want more features on Pastebin? Sign Up, it's FREE!
Guest

Boojum

By: a guest on May 30th, 2009  |  syntax: C++  |  size: 22.83 KB  |  views: 3,892  |  expires: Never
download  |  raw  |  embed  |  report abuse  |  print
Text below is selected. Please press Ctrl+C to copy to your clipboard. (⌘+C on Mac)
  1. #include <Magick++.h>
  2. #include <gmpxx.h>
  3. #include <string.h>
  4. #include <iostream>
  5. #include <fstream>
  6. #include <algorithm>
  7. #include <vector>
  8.  
  9. using namespace std;
  10. using namespace Magick;
  11.  
  12. // Tuning parameters.  These control the quality and size of the compression.  Increase these to
  13. // enhance image quality at the expense of a larger output string.  These numbers should produce 140
  14. // character strings with the full assigned Unicode set described below.
  15. static const int steps_in_x = 23;
  16. static const int steps_in_y = 23;
  17. static const int steps_in_red = 4;
  18. static const int steps_in_green = 4;
  19. static const int steps_in_blue = 4;
  20. static const int blocks_in_x = 11;
  21. static const int blocks_in_y = 11;
  22. static const int maximum_width = 1000;
  23. static const int maximum_height = 1000;
  24.  
  25. // Other tuning parameters that don't affect the string size.  The first two are weights for color
  26. // blending.  Higher in b makes things more "fractally" looking.  Higher in a makes things more
  27. // blocky.  The iterations is just how many steps to go through when decoding before stopping.  The
  28. // output image usually converges well before 15 iterations.
  29. static const int color_weight_a = 1;
  30. static const int color_weight_b = 2;
  31. static const int iterations = 15;
  32.  
  33. // Images will be broken into a fixed set of blocks, with a small amount of data for each block.  We
  34. // also need to store the image size.  Filling this in is really the core function of this program.
  35. struct block
  36. {
  37.     int orientation;
  38.     int x, y;
  39.     int red, green, blue;
  40.     long long int error;
  41. } blocks[ blocks_in_y ][ blocks_in_x ];
  42. int image_width, image_height;
  43.  
  44. // Full assigned Unicode, excluding <, >, &, control, combining, surrogate and private characters.
  45. // The data for this run-length encoded into pairs of numbers in the table.  The first number is how
  46. // many invalid character codes to skip over.  The second number is the how many of the next
  47. // character codes are valid for use.  The data terminates with two empty runs.
  48. static const int number_assigned = 100385;
  49. static const int codes[] =
  50. {
  51.  
  52.     32, 6, 1, 21, 1, 1, 1, 705, 112, 8, 2, 5, 5, 7, 1, 1, 1, 20, 1, 224, 7, 154, 13, 38, 2, 7, 1, 39, 1, 2, 6, 55, 8, 27, 5,
  53.     5, 11, 4, 2, 22, 2, 2, 1, 62, 1, 174, 1, 60, 2, 101, 14, 43, 9, 7, 262, 57, 2, 18, 2, 5, 3, 27, 8, 5, 1, 3, 1, 8, 2,
  54.     2, 2, 22, 1, 7, 1, 1, 3, 4, 2, 9, 2, 2, 2, 4, 8, 1, 4, 2, 1, 5, 2, 21, 6, 3, 1, 6, 4, 2, 2, 22, 1, 7, 1, 2, 1, 2, 1,
  55.     2, 2, 1, 1, 5, 4, 2, 2, 3, 3, 1, 7, 4, 1, 1, 7, 16, 11, 3, 1, 9, 1, 3, 1, 22, 1, 7, 1, 2, 1, 5, 2, 10, 1, 3, 1, 3,
  56.     2, 1, 15, 4, 2, 10, 1, 1, 15, 3, 1, 8, 2, 2, 2, 22, 1, 7, 1, 2, 1, 5, 2, 9, 2, 2, 2, 3, 8, 2, 4, 2, 1, 5, 2, 12, 16,
  57.     2, 1, 6, 3, 3, 1, 4, 3, 2, 1, 1, 1, 2, 3, 2, 3, 3, 3, 12, 4, 5, 3, 3, 1, 4, 2, 1, 6, 1, 14, 21, 6, 3, 1, 8, 1, 3, 1,
  58.     23, 1, 10, 1, 5, 3, 8, 1, 3, 1, 4, 7, 2, 1, 2, 6, 4, 2, 10, 8, 8, 2, 2, 1, 8, 1, 3, 1, 23, 1, 10, 1, 5, 2, 9, 1, 3,
  59.     1, 4, 7, 2, 7, 1, 1, 4, 2, 10, 1, 2, 15, 2, 1, 8, 1, 3, 1, 23, 1, 16, 3, 8, 1, 3, 1, 4, 9, 1, 8, 4, 2, 16, 3, 7, 2,
  60.     2, 1, 18, 3, 24, 1, 9, 1, 1, 2, 7, 3, 1, 4, 6, 1, 1, 1, 8, 18, 3, 12, 58, 4, 29, 37, 2, 1, 1, 2, 2, 1, 1, 2, 1, 6,
  61.     4, 1, 7, 1, 3, 1, 1, 1, 1, 2, 2, 1, 13, 1, 3, 2, 5, 1, 1, 1, 6, 2, 10, 2, 2, 34, 72, 1, 36, 4, 27, 4, 8, 1, 36, 1,
  62.     15, 1, 7, 43, 154, 4, 40, 10, 45, 3, 90, 5, 68, 5, 82, 6, 73, 1, 4, 2, 7, 1, 1, 1, 4, 2, 41, 1, 4, 2, 33, 1, 4, 2,
  63.     7, 1, 1, 1, 4, 2, 15, 1, 57, 1, 4, 2, 67, 5, 29, 3, 26, 6, 85, 12, 630, 9, 29, 3, 81, 15, 13, 1, 7, 11, 23, 9, 20,
  64.     12, 13, 1, 3, 1, 2, 12, 94, 2, 10, 6, 10, 6, 15, 1, 10, 6, 88, 8, 43, 85, 29, 3, 12, 4, 12, 4, 1, 3, 42, 2, 5, 11,
  65.     42, 6, 26, 6, 10, 4, 62, 2, 2, 224, 76, 4, 27, 9, 9, 3, 43, 3, 12, 70, 56, 3, 15, 3, 51, 128, 192, 64, 278, 2, 6, 2,
  66.     38, 2, 6, 2, 8, 1, 1, 1, 1, 1, 1, 1, 31, 2, 53, 1, 15, 1, 14, 2, 6, 1, 19, 2, 3, 1, 9, 1, 101, 5, 8, 2, 27, 1, 5,
  67.     11, 22, 74, 80, 3, 54, 7, 600, 24, 39, 25, 11, 21, 574, 2, 29, 3, 4, 61, 4, 1, 4, 2, 28, 1, 35, 1, 1, 1, 4, 3, 1, 1,
  68.     7, 2, 52, 3, 24, 1, 14, 1, 11, 1, 1, 3, 893, 3, 5, 171, 47, 1, 47, 1, 16, 1, 13, 2, 107, 14, 45, 10, 54, 9, 1, 16,
  69.     23, 9, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 33, 49, 79, 26, 1, 89, 12, 214, 26, 12, 4, 64, 1, 86, 4, 101, 5,
  70.     41, 3, 94, 1, 40, 8, 36, 12, 47, 1, 36, 12, 175, 1, 6838, 10, 20996, 60, 1165, 3, 55, 57, 300, 20, 32, 2, 13, 4, 1,
  71.     10, 26, 104, 141, 110, 49, 20, 56, 8, 69, 9, 12, 38, 84, 11, 1, 160, 55, 9, 14, 2, 10, 2, 4, 416, 11172, 8540, 302,
  72.     2, 59, 5, 106, 38, 7, 12, 5, 5, 26, 1, 5, 1, 1, 1, 2, 1, 2, 1, 108, 33, 365, 16, 64, 2, 54, 40, 14, 2, 26, 22, 35,
  73.     1, 19, 1, 4, 4, 5, 1, 135, 2, 1, 1, 190, 3, 6, 2, 6, 2, 6, 2, 3, 3, 7, 1, 7, 10, 5, 2, 12, 1, 26, 1, 19, 1, 2, 1,
  74.     15, 2, 14, 34, 123, 5, 3, 4, 45, 3, 84, 5, 12, 52, 45, 131, 29, 3, 49, 47, 31, 1, 4, 12, 27, 53, 30, 1, 37, 4, 14,
  75.     42, 158, 2, 10, 854, 6, 2, 1, 1, 44, 1, 2, 3, 1, 2, 1, 192, 26, 5, 27, 5, 1, 192, 4, 1, 2, 5, 8, 1, 3, 1, 27, 4, 3,
  76.     4, 9, 8, 9, 5543, 879, 145, 99, 13, 4, 43916, 246, 10, 39, 2, 60, 5, 3, 6, 8, 8, 2, 7, 30, 4, 48, 34, 66, 3, 1, 186,
  77.     87, 9, 18, 142, 85, 1, 71, 1, 2, 2, 1, 2, 2, 2, 4, 1, 12, 1, 1, 1, 7, 1, 65, 1, 4, 2, 8, 1, 7, 1, 28, 1, 4, 1, 5, 1,
  78.     1, 3, 7, 1, 340, 2, 292, 2, 50, 6144, 44, 4, 100, 3948, 42711, 20777, 542, 722403, 1, 30, 96, 128, 240, 0, 0
  79.  
  80. };
  81.  
  82. // Printable 7-bit ASCII, excluding <, and >, and &
  83. // static const int number_assigned = 92;
  84. // static const int codes[] =
  85. // {
  86. //     32, 6, 1, 21, 1, 1, 1, 64, 0, 0
  87. // };
  88.  
  89. // CJK Unified
  90. // static const int number_assigned = 20944;
  91. // static const int codes[] =
  92. // {
  93. //     19968, 20944, 0, 0
  94. // };
  95.  
  96. void compress(
  97.     char const *filename )
  98. {
  99.     // Initialize the blocks' error to a ridiculously high number.
  100.     for ( int y = 0; y < blocks_in_y; ++y )
  101.         for ( int x = 0; x < blocks_in_x; ++x )
  102.             blocks[ y ][ x ].error = 0x7fffffffffffffffLL;
  103.  
  104.     // Grab the source (range) image.
  105.     Image range( filename );
  106.     range.type( TrueColorType );
  107.     range.matte( true );
  108.     range.backgroundColor( Color( 0, 0, 0, QuantumRange ) );
  109.     Geometry range_size = range.size();
  110.     Pixels range_view( range );
  111.  
  112.     // Save the image size data for encoding later.
  113.     image_width = range_size.width();
  114.     image_height = range_size.height();
  115.  
  116.     // Try 16 different orientations for the downsampled image: four different 45 degree angles,
  117.     // either flipped horizontally or not, and flipped vertically or not.  These are the domains.
  118.     // We'll search these for matches to the blocks in the range.  (The 45 degree angle versions are
  119.     // unorthodox, but help a *lot* for this image size and are only two more bits per block.)
  120.     for ( int orientation = 0; orientation < 16; ++orientation )
  121.     {
  122.         Image domain( range );
  123.         domain.zoom( "50%" );
  124.         if ( orientation & 1 )
  125.             domain.flip();
  126.         if ( orientation & 2 )
  127.             domain.flop();
  128.         domain.rotate( orientation / 4 * 45 );
  129.         Geometry domain_size = domain.size();
  130.         Pixels domain_view( domain );
  131.  
  132.         // For each of the range blocks, we'll look for good matches in the current domain image.
  133.         for ( int range_y = 0; range_y < blocks_in_y; ++range_y )
  134.         {
  135.             cout << ( orientation * blocks_in_y + range_y ) << "/" << ( 16 * blocks_in_y ) << "\r" << flush;
  136.             int range_top = range_size.height() * range_y / blocks_in_y;
  137.             int block_height = range_size.height() * ( range_y + 1) / blocks_in_y - range_top;
  138.             for ( int range_x = 0; range_x < blocks_in_x; ++range_x )
  139.             {
  140.                 int range_left = range_size.width() * range_x / blocks_in_x;
  141.                 int block_width = range_size.width() * ( range_x + 1) / blocks_in_x - range_left;
  142.                 int area = block_width * block_height;
  143.  
  144.                 // Make note of the average color in this range block.
  145.                 const PixelPacket *range_pixels = range_view.getConst( range_left, range_top, block_width, block_height );
  146.                 int range_red = 0;
  147.                 int range_green = 0;
  148.                 int range_blue = 0;
  149.                 for ( int index = 0; index < area; ++index, ++range_pixels )
  150.                 {
  151.                     range_red += range_pixels->red;
  152.                     range_green += range_pixels->green;
  153.                     range_blue += range_pixels->blue;
  154.                 }
  155.  
  156.                 // Now we'll search for pieces of the domain that look like good matches for the
  157.                 // current range block.
  158.                 for ( int domain_y = 0; domain_y < steps_in_y; ++domain_y )
  159.                 {
  160.                     int domain_top = ( domain_size.height() - block_height ) * domain_y / steps_in_y;
  161.                     for ( int domain_x = 0; domain_x < steps_in_x; ++domain_x )
  162.                     {
  163.                         int domain_left = ( domain_size.width() - block_width ) * domain_x / steps_in_x;
  164.  
  165.                         // Compute the average color in this domain block.  If we have transparent
  166.                         // pixels, we've hit the transparent corners of the image generated by
  167.                         // rotations at 45 degree angles.  We'll reject these since they produce
  168.                         // weird solid colors in the output.
  169.                         const PixelPacket *domain_pixels = domain_view.getConst( domain_left, domain_top, block_width, block_height );
  170.                         int domain_red = 0;
  171.                         int domain_green = 0;
  172.                         int domain_blue = 0;
  173.                         bool corner = false;
  174.                         for ( int index = 0; index < area; ++index, ++domain_pixels )
  175.                         {
  176.                             if ( domain_pixels->opacity > QuantumRange / 2 )
  177.                             {
  178.                                 corner = true;
  179.                                 break;
  180.                             }
  181.                             domain_red += domain_pixels->red;
  182.                             domain_green += domain_pixels->green;
  183.                             domain_blue += domain_pixels->blue;
  184.                         }
  185.                         if ( corner )
  186.                             continue;
  187.  
  188.                         // Storing a contrast and brightness adjustment for each each color
  189.                         // component takes too much space.  Instead, we find a constant color, that
  190.                         // when blended with the domain block comes as close as possible to the
  191.                         // range block's color.
  192.                         int red_shift = ( ( color_weight_a + color_weight_b ) * range_red - color_weight_b * domain_red ) / ( area * color_weight_a );
  193.                         int green_shift = ( ( color_weight_a + color_weight_b ) * range_green - color_weight_b * domain_green ) / ( area * color_weight_a );
  194.                         int blue_shift = ( ( color_weight_a + color_weight_b ) * range_blue - color_weight_b * domain_blue ) / ( area * color_weight_a );
  195.  
  196.                         // Then we heavily quantize that color down to a few steps.  It's an
  197.                         // extremely limited palette but when blended with the domain blocks the
  198.                         // color fidelity can be surprising good.
  199.                         int red_bits = ( red_shift * ( steps_in_red - 1 ) + QuantumRange / 2 ) / QuantumRange;
  200.                         int green_bits = ( green_shift * ( steps_in_green - 1 ) + QuantumRange / 2 ) / QuantumRange;
  201.                         int blue_bits = ( blue_shift * ( steps_in_blue - 1 ) + QuantumRange / 2 ) / QuantumRange;
  202.                         red_bits = min( steps_in_red - 1, max( 0, red_bits ) );
  203.                         green_bits = min( steps_in_green - 1, max( 0, green_bits ) );
  204.                         blue_bits = min( steps_in_blue - 1, max( 0, blue_bits ) );
  205.  
  206.                         // Now, compute the total RMS error between all of the pixels in the range
  207.                         // block and the color-blended domain block.
  208.                         int quantized_red = red_bits * QuantumRange / ( steps_in_red - 1 );
  209.                         int quantized_green = green_bits * QuantumRange / ( steps_in_green - 1 );
  210.                         int quantized_blue = blue_bits * QuantumRange / ( steps_in_blue - 1 );
  211.                         range_pixels = range_view.getConst( range_left, range_top, block_width, block_height );
  212.                         domain_pixels = domain_view.getConst( domain_left, domain_top, block_width, block_height );
  213.                         long long int error = 0;
  214.                         for ( int index = 0; index < area; ++index, ++range_pixels, ++domain_pixels )
  215.                         {
  216.                             long long int red_error = range_pixels->red - ( quantized_red * color_weight_a + domain_pixels->red * color_weight_b ) / ( color_weight_a + color_weight_b );
  217.                             long long int green_error = range_pixels->green - ( quantized_green * color_weight_a + domain_pixels->green * color_weight_b ) / ( color_weight_a + color_weight_b );
  218.                             long long int blue_error = range_pixels->blue - ( quantized_blue * color_weight_a + domain_pixels->blue * color_weight_b ) / ( color_weight_a + color_weight_b );
  219.                             error += red_error * red_error + green_error * green_error + blue_error * blue_error;
  220.                         }
  221.  
  222.                         // Is it out best match yet?
  223.                         if ( error < blocks[ range_y ][ range_x ].error )
  224.                         {
  225.                             blocks[ range_y ][ range_x ].orientation = orientation;
  226.                             blocks[ range_y ][ range_x ].x = domain_x;
  227.                             blocks[ range_y ][ range_x ].y = domain_y;
  228.                             blocks[ range_y ][ range_x ].red = red_bits;
  229.                             blocks[ range_y ][ range_x ].green = green_bits;
  230.                             blocks[ range_y ][ range_x ].blue = blue_bits;
  231.                             blocks[ range_y ][ range_x ].error = error;
  232.                         }
  233.  
  234.                     }
  235.                 }
  236.             }
  237.         }
  238.     }
  239. }
  240.  
  241. void decompress(
  242.     char const *filename )
  243. {
  244.     // Start out with a black range image.
  245.     Image range( Geometry( image_width, image_height ), Color( "black" ) );
  246.     range.backgroundColor( Color( "black" ) );
  247.  
  248.     // Then go for a fixed number of iterations.  Saving the image after each iteration produces a
  249.     // fun progressive reveal of the decompressed image.
  250.     for ( int iteration = 0; iteration < iterations; ++iteration )
  251.     {
  252.         cout << iteration << "/" << iterations << "\r" << flush;
  253.  
  254.         // Precompute the downsampling and all of the different orientations of the range image.
  255.         Image orientations[ 16 ];
  256.         orientations[ 0 ] = range;
  257.         orientations[ 0 ].zoom( "50%" );
  258.         for ( int orientation = 1; orientation < 16; ++orientation )
  259.         {
  260.             orientations[ orientation ] = orientations[ 0 ];
  261.             if ( orientation & 1 )
  262.                 orientations[ orientation ].flip();
  263.             if ( orientation & 2 )
  264.                 orientations[ orientation ].flop();
  265.             orientations[ orientation ].rotate( orientation / 4 * 45 );
  266.         }
  267.  
  268.         // Then loop over all of the range blocks, and replace each with a copy of the domain block
  269.         // that our compression data tells us to.  We also do the color blending as part of this.
  270.         for ( int range_y = 0; range_y < blocks_in_y; ++range_y )
  271.         {
  272.             int range_top = image_height * range_y / blocks_in_y;
  273.             int block_height = image_height * ( range_y + 1) / blocks_in_y - range_top;
  274.             for ( int range_x = 0; range_x < blocks_in_x; ++range_x )
  275.             {
  276.                 int range_left = image_width * range_x / blocks_in_x;
  277.                 int block_width = image_width * ( range_x + 1) / blocks_in_x - range_left;
  278.  
  279.                 block &current = blocks[ range_y ][ range_x ];
  280.  
  281.                 Image domain = orientations[ current.orientation ];
  282.                 Geometry domain_size = domain.size();
  283.  
  284.                 int domain_top = ( domain_size.height() - block_height ) * current.y / steps_in_y;
  285.                 int domain_left = ( domain_size.width() - block_width ) * current.x / steps_in_x;
  286.                 domain.crop( Geometry( block_width, block_height, domain_left, domain_top ) );
  287.  
  288.                 int quantized_red = current.red * QuantumRange / ( steps_in_red - 1 );
  289.                 int quantized_green = current.green * QuantumRange / ( steps_in_green - 1 );
  290.                 int quantized_blue = current.blue * QuantumRange / ( steps_in_blue - 1 );
  291.                 domain.colorize( 100 * color_weight_a / ( color_weight_a + color_weight_b ),
  292.                                  Color( quantized_red, quantized_green, quantized_blue ) );
  293.  
  294.                 range.draw( DrawableCompositeImage( range_left, range_top, domain ) );
  295.             }
  296.         }
  297.  
  298.     }
  299.  
  300.     range.write( filename );
  301. }
  302.  
  303. void encode(
  304.     char const *filename )
  305. {
  306.     // For encoding, we just take all the information stored in the blocks structure, along with the
  307.     // image size, and turn it into a huge number.  This is like using a variable base.
  308.     mpz_class number = 0;
  309.     for ( int y = 0; y < blocks_in_y; ++y )
  310.         for ( int x = 0; x < blocks_in_x; ++x )
  311.         {
  312.             block &current = blocks[ y ][ x ];
  313.             int part = current.orientation;
  314.             part = part * steps_in_x + current.x;
  315.             part = part * steps_in_y + current.y;
  316.             part = part * steps_in_red + current.red;
  317.             part = part * steps_in_green + current.green;
  318.             part = part * steps_in_blue + current.blue;
  319.             number *= 16 * steps_in_x * steps_in_y * steps_in_red * steps_in_green * steps_in_blue;
  320.             number += part;
  321.         }
  322.     number = number * maximum_width + image_width;
  323.     number = number * maximum_height + image_height;
  324.  
  325.     // Next, we convert that to characters.  We essentially just convert the giant number into the
  326.     // base of how ever many characters we have available in our encoding.
  327.     int count = 0;
  328.     ofstream out( filename, ios::out | ios::binary );
  329.     while ( number != 0 )
  330.     {
  331.         int value = static_cast< mpz_class >( number % number_assigned ).get_si();
  332.         number /= number_assigned;
  333.  
  334.         // Lookup the character that this value maps to.
  335.         int character = 0;
  336.         for ( int index = 0; ; index += 2 )
  337.         {
  338.             character += codes[ index ] + min( value, codes[ index + 1 ] );
  339.             if ( value == codes[ index + 1 ] )
  340.                 character += codes[ index + 2 ];
  341.             value -= min( value, codes[ index + 1 ] );
  342.             if ( !value )
  343.                 break;
  344.         }
  345.  
  346.         // And UTF-8 encode that and output it.
  347.         if ( character < 0x80 )
  348.             out << static_cast< char >( character );
  349.         else if ( character < 0x800 )
  350.             out << static_cast< char >( 0xc0 | character >>  6 & 0x1f )
  351.                 << static_cast< char >( 0x80 | character >>  0 & 0x3f );
  352.         else if ( character < 0x10000 )
  353.             out << static_cast< char >( 0xe0 | character >> 12 & 0x0f )
  354.                 << static_cast< char >( 0x80 | character >>  6 & 0x3f )
  355.                 << static_cast< char >( 0x80 | character >>  0 & 0x3f );
  356.         else if ( character < 0x200000 )
  357.             out << static_cast< char >( 0xf0 | character >> 18 & 0x07 )
  358.                 << static_cast< char >( 0x80 | character >> 12 & 0x3f )
  359.                 << static_cast< char >( 0x80 | character >>  6 & 0x3f )
  360.                 << static_cast< char >( 0x80 | character >>  0 & 0x3f );
  361.         ++count;
  362.     }
  363.     cout << "Characters written: " << count << endl;
  364. }
  365.  
  366. void decode(
  367.     char const *filename )
  368. {
  369.     // Here we build back that giant number.  Again, it's deriving a number in the base of however
  370.     // many characters there are in the character set we're using.
  371.     ifstream in( filename, ios::out | ios::binary );
  372.     mpz_class number = 0;
  373.     mpz_class place = 1;
  374.     for ( ; ; )
  375.     {
  376.         // Read in a UTF-8 character
  377.         int character = in.get();
  378.         if ( character == EOF )
  379.             break;
  380.         if ( ( character & 0xe0 ) == 0xc0 )
  381.             character = ( ( character & 0x1f ) <<  6 |
  382.                           in.get()    & 0x3f );
  383.         else if ( ( character & 0xf0 ) == 0xe0 )
  384.             character = ( ( character & 0x0f ) << 12 |
  385.                           ( in.get()  & 0x3f ) <<  6 |
  386.                           in.get()    & 0x3f );
  387.         else if ( ( character & 0xf8 ) == 0xf0 )
  388.             character = ( ( character & 0x07 ) << 18 |
  389.                           ( in.get()  & 0x3f )  << 12 |
  390.                           ( in.get()  & 0x3f )  <<  6 |
  391.                           in.get()    & 0x3f );
  392.  
  393.         // And convert it back to a value.
  394.         int value = 0;
  395.         for ( int index = 0; character; index += 2 )
  396.         {
  397.             character -= codes[ index ];
  398.             value += min( character, codes[ index + 1 ] );
  399.             character -= min( character, codes[ index + 1 ] );
  400.         }
  401.  
  402.         // We're reading back in order from least to most significant.
  403.         number += value * place;
  404.         place *= number_assigned;
  405.     }
  406.  
  407.     // Now, we do the conversion back from that giant number to fill the image size and the block
  408.     // data.  This is all just the reverse order of the encoding of this into the giant number.
  409.     image_height = static_cast< mpz_class >( number % maximum_height ).get_si();
  410.     number /= maximum_height;
  411.     image_width = static_cast< mpz_class >( number % maximum_width ).get_si();
  412.     number /= maximum_width;
  413.     for ( int y = blocks_in_y - 1; y >= 0; --y )
  414.         for ( int x = blocks_in_x - 1; x >= 0; --x )
  415.         {
  416.             block &current = blocks[ y ][ x ];
  417.             current.blue = static_cast< mpz_class >( number % steps_in_blue ).get_si();
  418.             number /= steps_in_blue;
  419.             current.green = static_cast< mpz_class >( number % steps_in_green ).get_si();
  420.             number /= steps_in_green;
  421.             current.red = static_cast< mpz_class >( number % steps_in_red ).get_si();
  422.             number /= steps_in_red;
  423.             current.y = static_cast< mpz_class >( number % steps_in_y ).get_si();
  424.             number /= steps_in_y;
  425.             current.x = static_cast< mpz_class >( number % steps_in_x ).get_si();
  426.             number /= steps_in_x;
  427.             current.orientation = static_cast< mpz_class >( number % 16 ).get_si();
  428.             number /= 16;
  429.         }
  430. }
  431.  
  432. int main(
  433.     int argc,
  434.     char **argv )
  435. {
  436.     if ( !strcmp( argv[ 1 ], "encode" ) )
  437.     {
  438.         compress( argv[ 2 ] );
  439.         encode( argv[ 3 ] );
  440.     }
  441.     else
  442.     {
  443.         decode( argv[ 2 ] );
  444.         decompress( argv[ 3 ] );
  445.     }
  446.     return 0;
  447. }
clone this paste RAW Paste Data