Click here to Skip to main content
15,891,529 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I want to compress bitmap in my own format by 2 steps. First coder, than decoder.
Das ist mein Coder. How can I do it more optimal and than decoding?



#include <iostream>
#include <stdio.h>
#include <fstream>
#include <stdlib.h>
#include <assert.h>

using namespace std;

int cnt(unsigned char *a,unsigned char b){ 

    if (*a != b)
        return 0;
    return 1+cnt(a+1, b);
}

int bytes_to_int(
    unsigned char b1, unsigned char b2,
    unsigned char b3, unsigned char b4
)
{
    return (b1 | b2<<8 | b3<<16 | b4<<24);
}

int bytes_to_int(unsigned char* b)
{
    return (b[0] | b[1]<<8 | b[2]<<16 | b[3]<<24);
}

int main()
{
    FILE *image=fopen("image.bmp","rb");
    FILE *image2=fopen("image.mRLE","wb");
    //printf("========================%d=========================\n",ftell(image));
    fseek(image,0,SEEK_END);

    const unsigned long long file_size=ftell(image);
    unsigned char* file=new unsigned char [ftell(image) + 16];

    fseek(image,0,SEEK_SET);
    fread(file, file_size, 1, image);
    fclose(image);
    //printf("\n+++++++++++++++++\n");
    const unsigned int
        width=bytes_to_int(file+18)
        ,hight=bytes_to_int(file+22)
        ,offset=bytes_to_int(file+10);
    fwrite(&width, 4, 1, image2);
    fwrite(&hight, 4, 1, image2);
    printf("width = %d; \n height = %d.\n",width,hight);

    const unsigned int length = bytes_to_int(file+2);
    fwrite(&length, 4, 1, image2);
    //fseek(image2,12,SEEK_CUR)
    fwrite(file + 54, 1024, 1, image2);
    //fseek(image,1078,SEEK_SET);

    unsigned char *a = file+1078, *aa = a + width*hight;
    //fread(a,width*hight,1,image);
    unsigned long int sizef=0;
    int i = 0;
    while(i < width*hight)
    {
        int k;
        unsigned char *c = a;
        unsigned char beg = *c;
        k = 1; ++c;
        while (c < aa && k < 255 && *c == beg)
        {
            ++k;
            ++c;
        }
        cout << k << " chars" << (int) beg << endl;
        assert(0 < k && k <= 255);
        unsigned char b1 = (unsigned char) k;
        unsigned char b2 = beg;
        a += j;
        i += j;
        fwrite(&b1, 1, 1, image2);
        fwrite(&b2, 1, 1, image2);
        //printf("===%d=========%d\n",(int)b1,(int)b2);
        // printf("%d\n",i);
    }
    /* fseek(image2,8,SEEK_SET);
    fwrite(&file_size,8,1,image2); */

    fclose(image2);
    std::cout << "Your program has finished the task!" << endl;
    // system("pause");
    return 0;
}
Posted
Comments
KarstenK 1-Mar-15 13:42pm    
Mein Rat ist: Optimal code is bug free, easy to understand and reuseable. ;-)

The cout costs performance and also the fwrite really slows down. Providing a bigger buffer and write ALL ONCE is faster.
Andreas Gieriet 1-Mar-15 19:34pm    
This is un-maintainable code.
I fully support KarstenK's statement!
1) define the algorithm on paper or other suitable means
2) describe it in some document or here in the code as comment
3) write it such that one can understand the mapping from the comment to the code
4) implement a test suite to ensure proper function
5) measure if it is performing good enough - stop here if it is
6) remove the biggest bottleneck and test against proper function
7) go to 5)
Cheers
Andi
Sergey Alexandrovich Kryukov 1-Mar-15 21:47pm    
Good advice, could be a formal answer.
—SA

1 solution

Although RLE is used (or optional) with a few well known image formats, unless you're using posterized images, there are better schemes for file storage. RLE only considers consecutive values during compression, it can easily miss opportunities and may even increase image size, e.g. a pixel level checkerboard pattern will expand under RLE while LZ77 could deflate up to 99%.

That said, it can be good to implement things like this for one's own edification.

I have some old code lying around for RLE compression, and although it was designed to work with a graphics engine, the compressor emits a byte stream, so it shouldn't be difficult to adapt. Anyway, here's some advice and some code.

  1. Decide on a format and enforce it using structures.
  2. RLE is easier to implement in terms of a scanline than an entire image.
  3. Build up complex functions from smaller less complex ones.
  4. Keep things as simple as possible.

Each scanline begins with a header specifying pitch (byte length) for that line; successive headers specify a skip/copy pair for each run, followed by copy bytes worth of data.

Snippet header,
C++
struct rlehead {
/* rle header type. */
	static const unsigned int size;

	rlehead() : pitch(0) {}
	explicit rlehead(const void* in);

	void read(const void* in);
	void write(void* out) const;

	union {
		unsigned int pitch;
		struct { unsigned short skip, copy; };
	};
};
const unsigned int rlehead::size = sizeof(rlehead);

inline rlehead::rlehead(const void* in)
{	this->read(in);
}

inline void rlehead::read(const void* in)
{	*this = *static_cast<const>(in);
}

inline void rlehead::write(void* out) const
{	*static_cast<rlehead*>(out) = *this;
}
</const>

Snippet encode,
C++
inline char* rle_advance_scanline_(char* x, int count = 1)
/* advance rle scanline along y-axis. */
{
	while ( count-- )
	 {
	   rlehead head(x);
	   x += head.pitch;
	 }
	return x;
}

void rle_encode_eval_line_(Surface* surf, int y, std::vector<rlehead>& list)
/* generate headers from pixel data. */
{
	const PixelBase& op = Pixel.op();

	Color key = surf->col_key();
	int bpp = surf->bpp();
	int x = 0;

	while ( 1 )
	 {
	   rlehead head;
	   for ( ; x < surf->w() && op.get(surf, x, y) == key; x++ )
	      head.skip += bpp;
	   for ( ; x < surf->w() && op.get(surf, x, y) != key; x++ )
	      head.copy += bpp;
	   list.push_back(head);

	   if (head.copy == 0)
	      break;
	 }
}

void rle_encode_commit_line_(Surface* surf, int y,
  std::vector<rlehead>::iterator first, char* tmpbase)
/* write encoded line data to buffer. */
{
	char* pix = surf->pixels(0, y);
	char* tmp = tmpbase + rlehead::size;

	rlehead head;
	while ( 1 )
	 {
	   head = *first++;
	   head.write(tmp);
	   tmp += head.size;
	   pix += head.skip;

	   if (head.copy == 0)
	      break;
	   fwdcopy(tmp, pix, head.copy);
	   tmp += head.copy;
	   pix += head.copy;
	 }
	head.pitch = tmp - tmpbase;
	head.write(tmpbase);
}

void rle_encode_(Surface* surf)
/* encode surface. */
{
	const int bufsize = surf->h() * surf->pitch();
	char* tmpbase = new char[bufsize*2];
	char* tmp = tmpbase;

	std::auto_ptr<char> sentinal(tmpbase);

	for ( int y = 0; y < surf->h(); y++ )
	 {
	   std::vector<rlehead> list;
	   rle_encode_eval_line_(surf, y, list);
	   rle_encode_commit_line_(surf, y, list.begin(), tmp);
	   tmp = rle_advance_scanline_(tmp);

	   if (tmp - tmpbase > bufsize)
	      return;
	 }

	fwdcopy(surf->pixels(), tmpbase, tmp - tmpbase);
	surf->set_encoding(RLE_ENCODE);
}
</rlehead></char></rlehead></rlehead>

Snippet decode,
C++
void std_encode_commit_line_(char* tmpbase, int y, Surface* surf)
/* write decoded line data to surface. */
{
	const PixelBase& op = Pixel.op();

	char* pix = surf->pixels(0, y);
	char* tmp = tmpbase + rlehead::size;

	while ( 1 )
	 {
	   rlehead head(tmp);
	   tmp += head.size;

	   op.hz_fill(pix, surf->col_key(), head.skip);
	   pix += head.skip;

	   if (head.copy == 0)
	      break;
	   fwdcopy(pix, tmp, head.copy);
	   pix += head.copy;
	   tmp += head.copy;
	 }
}

void std_encode_(Surface* surf)
/* decode rle surface. */
{
	char* pixbase = surf->pixels();
	char* pix = rle_advance_scanline_(pixbase, surf->h());

	const int bufsize = pix - pixbase;
	char* tmpbase = new char[bufsize];
	char* tmp = tmpbase;

	fwdcopy(tmpbase, surf->pixels(), bufsize);

	for ( int y = 0; y < surf->h(); y++ )
	 {
	   std_encode_commit_line_(tmp, y, surf);
	   tmp = rle_advance_scanline_(tmp);
	 }

	delete [] tmpbase;
	surf->set_encoding(STD_ENCODE);
}

Hope this helps.
 
Share this answer
 
Comments
CPallini 31-Mar-15 11:55am    
5.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900