Advertisement
Bisqwit

pngencoder.hh (Minimal PNG encoder)

May 3rd, 2019
245
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 4.05 KB | None | 0 0
  1. #include <vector>
  2. #include <cstdio>
  3.  
  4. // PNGencoder: A minimal PNG encoder for demonstration purposes.
  5. class PNGencoder
  6. {
  7.     std::vector<unsigned char> output;
  8. private:
  9.     static void PutWord(unsigned char* target, unsigned dword, bool msb_first)
  10.     {
  11.         for(int p=0; p<4; ++p) target[p] = dword >> (msb_first ? 24-p*8 : p*8);
  12.     }
  13.     static unsigned adler32(const unsigned char* data, unsigned size, unsigned res=1)
  14.     {
  15.         for(unsigned s1,a=0; a<size; ++a)
  16.             s1 = ((res & 0xFFFF) + (data[a] & 0xFF)) % 65521,
  17.                 res = s1 + ((((res>>16) + s1) % 65521) << 16);
  18.         return res;
  19.     }
  20.     static unsigned crc32(const unsigned char* data, unsigned size, unsigned res=0)
  21.     {
  22.         for(unsigned tmp,n,a=0; a<size; ++a, res = ~((~res>>8) ^ tmp))
  23.             for(tmp = (~res ^ data[a]) & 0xFF, n=0; n<8; ++n)
  24.                 tmp = (tmp>>1) ^ ((tmp&1) ? 0xEDB88320u : 0);
  25.         return res;
  26.     }
  27.     void Deflate(const unsigned char* source, unsigned srcsize)
  28.     {
  29.         /* A bare-bones deflate-compressor (as in gzip) */
  30.         int algo=8, windowbits=8 /* 8 to 15 allowed */, flevel=0 /* 0 to 3 */;
  31.         /* Put RFC1950 header: */
  32.         unsigned h0 = algo + (windowbits-8)*16, h1 = flevel*64 + 31-((256*h0)%31);
  33.         output.push_back(h0);
  34.         output.push_back(h1); /* checksum and compression level */
  35.         /* Compress data using a lossless algorithm (RFC1951): */
  36.         for(unsigned begin=0; ; )
  37.         {
  38.             unsigned eat = std::min(65535u, srcsize-begin);
  39.  
  40.             std::size_t o = output.size(); output.resize(o+5);
  41.             output[o+0] = (begin+eat) >= srcsize; /* bfinal bit and btype: 0=uncompressed */
  42.             PutWord(&output[o+1], eat|(~eat<<16), false);
  43.             output.insert(output.end(), source+begin, source+begin+eat);
  44.             begin += eat;
  45.             if(begin >= srcsize) break;
  46.         }
  47.         /* After compressed data, put the checksum (adler-32): */
  48.         std::size_t o = output.size(); output.resize(o+4);
  49.         PutWord(&output[o], adler32(source, srcsize), true);
  50.     }
  51. public:
  52.     void EncodeImage(unsigned width,unsigned height, const unsigned char* rgbdata)
  53.     {
  54.         std::size_t o;
  55.         #define BeginChunk(n_reserve_extra) o = output.size(); output.resize(o+8+n_reserve_extra)
  56.         #define EndChunk(type) do { \
  57.             unsigned ncopy=output.size()-(o+8); \
  58.             static const char t[4+1] = type; \
  59.             output.resize(o+8+ncopy+4); \
  60.             PutWord(&output[o+0], ncopy, true); /* chunk length */ \
  61.             std::copy(t+0, t+4, &output[o+4]);  /* chunk type */   \
  62.             /* in the chunk, put crc32 of the type and data (below) */ \
  63.             PutWord(&output[o+8+ncopy], crc32(&output[o+4], 4+ncopy), true); \
  64.         } while(0)
  65.         /* Put PNG header (always const) */
  66.         static const char header[8+1] = "\x89PNG\15\12\x1A\12";
  67.         output.insert(output.end(), header, header+8);
  68.         /* Put IHDR chunk */
  69.         BeginChunk(13);
  70.           PutWord(&output[o+8+0], width,  true); /* Put image width    */
  71.           PutWord(&output[o+8+4], height, true); /* and height in IHDR */
  72.           PutWord(&output[o+8+8], 0x08020000, true);
  73.           /* Meaning of above: 8-bit,rgb-triple,deflate,std filters,no interlacing */
  74.         EndChunk("IHDR");
  75.         /* Put IDAT chunk */
  76.         BeginChunk(0);
  77.           std::vector<unsigned char> idat;
  78.           for(unsigned y=0; y<height; ++y)
  79.           {
  80.               idat.push_back(0x00); // filter type for this scanline
  81.               idat.insert(idat.end(), rgbdata+y*width*3, rgbdata+y*width*3+width*3);
  82.           }
  83.           Deflate(&idat[0], idat.size());
  84.         EndChunk("IDAT");
  85.         /* Put IEND chunk */
  86.         BeginChunk(0);
  87.         EndChunk("IEND");
  88.         #undef BeginChunk
  89.         #undef EndChunk
  90.     }
  91.     void SaveTo(const char* fn)
  92.     {
  93.         std::FILE* fp = std::fopen(fn, "wb");
  94.         if(!fp) { std::perror(fn); return; }
  95.         std::fwrite(&output[0], 1, output.size(), fp);
  96.         std::fclose(fp);
  97.     }
  98. };
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement