This section of the archives stores flipcode's complete Developer Toolbox collection, featuring a variety of mini-articles and source code contributions from our readers.

| Image Factory Submitted by

Hi, there hasn't been an update recently, so I thought I could contribute with my image-factory. It's based on the generic-factory method described in the expert's corner at cuj.com by Herb Sutter and Jim Hyslop. This is basically two (and a half) things:

> - A generic abstract factory, to be used with all kinds of things.
> - An image abstract baseclass with two implementations: bmp_image and pcx_image.
> - (My reference counter class)

The features are:

> - loading and saving of 24-bit bmp and pcx images.
> - portability, loading/saving works for both little and big-endian cpus.
> - plugability, when you've written a new image-class you only need to compile that class to plug it into the factory.
> - one function for all types BUT separate implementations.

Here's an example:

    // this will load a bmp-image, load_image will create a bmp_image object.
    image_hnd my_image = load_image("my_image.bmp");
    if(my_image.is_null()) // failure
      exit(1);
    //
    paint_bitmap(my_image-get_bits(), my_image-get_width(),
                 my_image-get_height());
    // this will save a pcx-image, save_image will create a pcx_image object
    // if needed...
    if(!save_image("my_image.pcx", my_image)) // fail to save
      exit(1);

Anyway for a more complete example look at the file win_main.cpp, which is a simple program that loads a bmp or pcx image and draws it in a window. I'm sorry if I don't make much sense, but it 2:30 in the morning and I'm a bit tired. But post here or mail me if you have any questions, I'll try to be clearer when I'm awake!
/Andreas Magnusson

Currently browsing [imagefactory.zip] (9,523 bytes) - [win_main.cpp] - (2,829 bytes)

    /********************************************************************************
    *
    *	File: win_main.cpp
    *	Copyright(c) Andreas Magnusson 2002
    *
    *	Just very basic stuff...a testbed for my image-factory!
    *
    *	DISCLAIMER AND STUFF:
    *	This source-code is supplied AS IS. I cannot be held responsible for any damage caused
    *	by use or misuse of this source-code. So use the code as you please, but give credit
    *	where they are due.
    *
    ********************************************************************************/
    #include <windows.h>
    // disable the annoying warnings that comes up when you use map...
    #pragma warning( disable : 4786)
    #include "image.h"

    const char *CLASSNAME = "IMAGEVIEW", *WINNAME = "Image View";

    LRESULT CALLBACK WndProc(HWND hWnd, unsigned int iMessage, WPARAM wParam, LPARAM lParam);

    image_hnd img_array[5];

    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int nCmdShow)
    {
    	HWND hWnd;
    	MSG Message;
    	WNDCLASS WndClass;
    	ZeroMemory(&WndClass, sizeof(WNDCLASS));
    	WndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    	WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
    	WndClass.hIcon = LoadIcon(hInstance, NULL);
    	WndClass.hInstance = hInstance;
    	WndClass.lpfnWndProc = WndProc;
    	WndClass.lpszClassName = CLASSNAME;
    	WndClass.style = CS_HREDRAW | CS_VREDRAW;
    	if(!RegisterClass(&WndClass))
    		return 0;

    	hWnd = CreateWindow(CLASSNAME, WINNAME, WS_OVERLAPPEDWINDOW,
    											CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
    											NULL, NULL, hInstance, NULL);

    	img_array[0] = load_image("forest.bmp");
    	save_image("forest.pcx", img_array[0]);

    	ShowWindow(hWnd, nCmdShow);

    	while(GetMessage(&Message, hWnd, 0, 0))
    	{
    		TranslateMessage(&Message);
    		DispatchMessage(&Message);
    	}
    	return Message.wParam;
    }

    COLORREF make_color(const unsigned char *buf)
    {
    	COLORREF cl = buf[0] | (buf[1] << 8) | (buf[2] << 16);
    	return cl;
    }

    void draw_image(HDC hdc, int ystart, image_hnd &img)
    {
    	if(img.is_null())
    		return;
    	for(int y = 0; y < img->get_height(); y++)
    	{
    		for(int x = 0; x < img->get_width(); x++)
    		{
    			COLORREF cl = make_color(&img->get_bits()[y * 3 * img->get_width() + x * 3]);
    			SetPixel(hdc, x, y + ystart, cl);
    		}
    	}
    }

    LRESULT CALLBACK WndProc(HWND hWnd, unsigned int iMessage, WPARAM wParam, LPARAM lParam)
    {
    	PAINTSTRUCT ps;
    	int ystart, i;
    	switch(iMessage)
    	{
    	case WM_PAINT:
    		BeginPaint(hWnd, &ps);
    		ystart = 0;
    		for(i = 0; i < 5; i++)
    		{
    			if(!img_array[i].is_null())
    			{
    				draw_image(ps.hdc, ystart, img_array[i]);
    				ystart += img_array[i]->get_height() + 1;
    			}
    		}
    		EndPaint(hWnd, &ps);
    		break;
    	case WM_CLOSE:
    		PostQuitMessage(0);
    		break;
    	default:
    		return DefWindowProc(hWnd, iMessage, wParam, lParam);
    	}
    	return 0;
    }

Currently browsing [imagefactory.zip] (9,523 bytes) - [generic_factory.h] - (1,854 bytes)

    /**************************************************************************
    *
    *	File: generic_factory.h
    *
    *	This file is basically what Herb Sutter and Jim Hyslop show in an article
    *	on http://www.cuj.com/ so I can't really take any credit for it.
    *
    *
    *	DISCLAIMER AND STUFF:
    *	This source-code is supplied AS IS. I cannot be held responsible for any damage caused
    *	by use or misuse of this source-code nor any derivatives of it.
    *	So use the code as you please, but give credit where they are due.
    *
    **************************************************************************/
    #ifndef __GENERIC_FACTORY_H__
    #define __GENERIC_FACTORY_H__

    #include <map>
    #include <string>
    #include "handle.h"

    template <class prod_type, typename class_id = std::string>
    class generic_factory
    {
    	typedef handle<prod_type> (*base_type_fn)();
    	typedef std::map<class_id, base_type_fn> registry_t;
    	registry_t registry;
    	generic_factory() {}
    	generic_factory(const generic_factory &); // not implemented
    	generic_factory &operator=(const generic_factory &); // not implemented
    public:
    	static generic_factory &instance()
    	{
    		static generic_factory gf;
    		return gf;
    	}
    	void register_type(const class_id &key, base_type_fn fn)
    	{
    		registry[key] = fn;
    	}
    	handle<prod_type> create(const class_id &key) const
    	{
    		handle<prod_type> obj;
    		registry_t::const_iterator entry = registry.find(key);
    		if(entry != registry.end())
    			obj = entry->second();
    		return obj;
    	}
    };

    template <class parent_t, class self_t, typename classid_t = std::string>
    class register_in_factory
    {
    public:
    	static handle<parent_t> create_instance()
    	{
    		return handle<parent_t>(new self_t);
    	}
    	register_in_factory(const classid_t &key)
    	{
    		generic_factory<parent_t>::instance().register_type(key, create_instance);
    	}
    };

    #endif

Currently browsing [imagefactory.zip] (9,523 bytes) - [handle.h] - (1,799 bytes)

    /****************************************************************
    *
    *	File: handle.h
    *	Copyright(c) Andreas Magnusson 2002
    *
    *	This is my classes for reference counted objects.
    *
    *	DISCLAIMER AND STUFF:
    *	This source-code is supplied AS IS. I cannot be held responsible for any damage caused
    *	by use or misuse of this source-code nor any derivatives of it.
    *	So use the code as you please, but give credit where they are due.
    *
    ****************************************************************/
    #ifndef __HANDLE_H__
    #define __HANDLE_H__

    class ref_counted
    {
    	int _ref_count;
    public:
    	ref_counted() : _ref_count(0) {}
    	virtual ~ref_counted() {}
    	int add_ref() {return ++_ref_count;}
    	int release() {return --_ref_count;}
    };

    template<class T> class handle
    {
    	T *object;
    	void release()
    	{
    		if(object)
    		{
    			if(object->release() == 0)
    				delete object;
    		}
    	}
    	void add_ref()
    	{
    		if(object)
    			object->add_ref();
    	}
    	void assign(T *ptr)
    	{
    		if(ptr == object)
    			return;
    		release();
    		object = ptr;
    		add_ref();
    	}
    public:
    	handle() : object(0) {}
    	handle(T *pNew)
    	{
    		object = pNew;
    		add_ref();
    	}
    	handle(const handle &hnd)
    	{
    		object = hnd.object;
    		add_ref();
    	}
    	~handle()
    	{
    		release();
    	}
    	const handle &operator =(const handle &hnd)
    	{
    		assign(hnd.object);
    		return *this;
    	}
    	const handle &operator =(T *pNew)
    	{
    		assign(pNew);
    		return *this;
    	}
    	T *operator ->()
    	{
    		return object;
    	}
    	operator T *()
    	{
    		return object;
    	}
    	T &operator *()
    	{
    		return *object;
    	}
    	bool operator ==(const handle &hnd)
    	{
    		return object == hnd.object;
    	}
    	bool operator !=(const handle &hnd)
    	{
    		return object != hnd.object;
    	}
    	bool is_null()
    	{
    		return object == NULL;
    	}
    };

    #endif

Currently browsing [imagefactory.zip] (9,523 bytes) - [image.cpp] - (2,203 bytes)

    /*************************************************************************
    *
    *	File: image.cpp
    *	Copyright(c) Andreas Magnusson 2002
    *
    *	This is the image implementation...
    *
    *	DISCLAIMER AND STUFF:
    *	This source-code is supplied AS IS. I cannot be held responsible for any damage caused
    *	by use or misuse of this source-code nor any derivatives of it.
    *	So use the code as you please, but give credit where they are due.
    *
    *************************************************************************/

    // disable the annoying warnings that comes up when you use map...
    #pragma warning( disable : 4786)
    #include "image.h"
    #include <stdio.h>

    image::image()
    :width(0), height(0), buffer(0), color_depth(0)
    {
    }

    image::image(const image &img)
    :width(img.width), height(img.height), color_depth(img.color_depth)
    {
    	int size = height * width * (color_depth >> 3);
    	buffer = new unsigned char[size];
    	memcpy(buffer, img.buffer, size);
    }

    image::~image()
    {
    	if(buffer)
    		delete[] buffer;
    }

    bool image::copy(const image &img)
    {
    	width = img.get_width();
    	height = img.get_height();
    	color_depth = img.get_color_depth();
    	long size = height * width * (color_depth >> 3);
    	buffer = new unsigned char[size];
    	if(!buffer)
    		return false;
    	memcpy(buffer, img.get_bits(), size);
    	return true;
    }

    ///////////////////////////////

    image_hnd load_image(const std::string &file_name)
    {
    	int p = file_name.find_last_of('.');
    	std::string suffix;
    	if(p != std::string::npos)
    		suffix = file_name.substr(p + 1);

    	image_hnd img = image_factory::instance().create(suffix);
    	if(img.is_null())
    		return img;

    	if(img->load(file_name))
    		return img;

    	return handle<image>(0);
    }

    bool save_image(const std::string &file_name, image_hnd &src)
    {
    	int p = file_name.find_last_of('.');
    	std::string suffix;
    	if(p != std::string::npos)
    		suffix = file_name.substr(p + 1);

    	if(_stricmp(suffix.c_str(), src->get_type()) != 0)
    	{
    		image_hnd img = image_factory::instance().create(suffix);
    		if(img.is_null())
    			return false;

    		if(!img->copy(*src))
    			return false;

    		return img->save(file_name);
    	}
    	else
    		return src->save(file_name);
    }

Currently browsing [imagefactory.zip] (9,523 bytes) - [image.h] - (1,612 bytes)

    /*********************************************************************************
    *
    *	File: image.h
    * Copyright(c) Andreas Magnusson 2002
    *
    *
    *	DISCLAIMER AND STUFF:
    *	This source-code is supplied AS IS. I cannot be held responsible for any damage caused
    *	by use or misuse of this source-code nor any derivatives of it.
    *	So use the code as you please, but give credit where they are due.
    *
    *********************************************************************************/
    #ifndef __IMAGE_H__
    #define __IMAGE_H__

    #include "generic_factory.h"
    #include "handle.h"

    enum IMAGE_COLOR_DEPTH {IMAGE_2_BITS = 2, IMAGE_4_BITS = 4, IMAGE_8_BITS = 8,
    											IMAGE_16_BITS = 16, IMAGE_24_BITS = 24, IMAGE_32_BITS = 32};

    class image : public ref_counted
    {
    	void operator =(const image &img); // not implemented on purpose!
    protected:
    	int width, height, color_depth;
    	unsigned char *buffer;
    public:
    	image();
    	image(const image &img);
    	virtual ~image();
    	//
    	virtual unsigned char *get_bits() const {return buffer;}
    	virtual int get_width() const {return width;}
    	virtual int get_height() const {return height;}
    	virtual int get_color_depth() const {return color_depth;}
    	virtual bool copy(const image &img);
    	//
    	virtual bool load(const std::string &file_name) = 0;
    	virtual bool save(const std::string &file_name) = 0;
    	virtual const char *get_type() = 0;
    };

    typedef handle<image> image_hnd;
    typedef generic_factory<image> image_factory;
    image_hnd load_image(const std::string &file_name);
    bool save_image(const std::string &file_name, image_hnd &img);

    #endif

Currently browsing [imagefactory.zip] (9,523 bytes) - [pcx.cpp] - (8,606 bytes)

    /*************************************************************************
    *
    *	File: pcx.cpp
    *	Copyright(c) Andreas Magnusson 2002
    *
    *	Should work on both big-endian (eg. Sun Sparc) and little-endian (eg. Intel x86)
    *	machines.
    *
    *	DISCLAIMER AND STUFF:
    *	This source-code is supplied AS IS. I cannot be held responsible for any damage caused
    *	by use or misuse of this source-code nor any derivatives of it.
    *	So use the code as you please, but give credit where they are due.
    *
    *************************************************************************/

    // disable the annoying warnings that comes up when you use map...
    #pragma warning( disable : 4786)
    #include <stdio.h>
    #include "image.h"

    struct pcx_header
    {
      unsigned char manufacturer;
      unsigned char version;
      unsigned char encoding;
      unsigned char bits_per_pixel;
      unsigned short x, y;
      unsigned short width, height;
      unsigned short horz_res;
      unsigned short vert_res;
      unsigned char ega_palette[48];
      unsigned char reserved;
      unsigned char num_color_planes;
      unsigned short bytes_per_line;
      unsigned short palette_type;
      unsigned char padding[58];
    	//
    	unsigned short make_word(unsigned char *&buf)
    	{
    		unsigned short rv = *buf++;
    		rv += (*buf++ << 8);
    		return rv;
    	}
    	void word2byte(unsigned short v, unsigned char *&buf)
    	{
    		*buf++ = v & 0xff;
    		*buf++ = (v >> 8) & 0xff;
    	}
    	//
    	pcx_header(unsigned char *temphead)
    	{
    	  // this procedure is needed for compatibility between
    		// little-endian machines (eg Intel-based) and big-endian
    		// machines (eg Sun Sparc)
    	  manufacturer = *temphead++;
    		version = *temphead++;
    	  encoding = *temphead++;
    		bits_per_pixel = *temphead++;
    	  x = make_word(temphead);
    		y = make_word(temphead);
    	  width = make_word(temphead);
    		height = make_word(temphead);

    	  horz_res = make_word(temphead);
    		vert_res = make_word(temphead);

    	  // I doubt I will ever use EGA-palettes so I don't copy their
    		// contents to the header.
    	  //  char ega_palette[48];
    		temphead += sizeof(ega_palette);

    		reserved = *temphead++;
    	  num_color_planes = *temphead++;
    		bytes_per_line = make_word(temphead);
    		palette_type = make_word(temphead);
    		//
    	}
    	pcx_header()
    	{
    	}
    	void copy_to_buffer(unsigned char *tempbuf)
    	{
    		//memcpy(tempbuf, this, sizeof(pcx_header));
    		memset(tempbuf, 0, sizeof(pcx_header));
    		*tempbuf++ = manufacturer;
    		*tempbuf++ = version;
    		*tempbuf++ = encoding;
    		*tempbuf++ = bits_per_pixel;
    		word2byte(x, tempbuf);
    		word2byte(y, tempbuf);
    		word2byte(width, tempbuf);
    		word2byte(height, tempbuf);
    		word2byte(horz_res, tempbuf);
    		word2byte(vert_res, tempbuf);
    		tempbuf += sizeof(ega_palette);
    		*tempbuf++ = reserved;
    		*tempbuf++ = num_color_planes;
    		word2byte(bytes_per_line, tempbuf);
    		word2byte(palette_type, tempbuf);
    	}
    };

    ///////////////////////////////////////////////////////////////////////////////////////

    class pcx_image : public image
    {
    	unsigned char *read_line24(unsigned char *buf, unsigned long line, unsigned long sv);
    	unsigned char *write_line24(unsigned char *buf, unsigned long line, unsigned long sv);
    public:
    	pcx_image();
    	virtual bool load(const std::string &file_name);
    	virtual bool save(const std::string &file_name);
    	virtual const char *get_type() {return "pcx";}
    };

    // register pcx_image
    namespace
    {
    	register_in_factory<image, pcx_image> register_me("pcx");
    };

    pcx_image::pcx_image()
    {
    }

    // this function currently only reads 24-bit pcx-files
    bool pcx_image::load(const std::string &file_name)
    {
    	FILE *file;

    	file = fopen(file_name.c_str(), "rb");
    	if(!file)
    		return false;

    	fseek(file, 0, SEEK_END);
    	long file_size = ftell(file) - sizeof(pcx_header);
    	rewind(file);

    	unsigned char *temphead = new unsigned char[sizeof(pcx_header)];
    	fread(temphead, 1, sizeof(pcx_header), file);
    	pcx_header header(temphead);
    	delete[] temphead;

    	width = header.width + 1;
    	height = header.height + 1;
    	color_depth = header.num_color_planes << 3;

    	long size = header.bytes_per_line * get_height() * header.num_color_planes;
    	if(buffer)
    		delete[] buffer;

    	unsigned char *tmpbuf = new unsigned char[file_size];
    	unsigned char *svtmpbuf = tmpbuf; // to delete tmpbuf later...

    	buffer = new unsigned char[size * 2];
    	if(buffer == NULL || tmpbuf == NULL)
    	{
    		fclose(file);
    		return false;
    	}

    	memset(buffer, 0, size);
    	fread(tmpbuf, 1, file_size, file);
    	fclose(file);

    	// the data is stored as bitplanes in the pcx-image, thus first a row of red, then a row
    	// of green and finally a row of blue. Then comes next row of red...theoretically this
    	// could be used to save alpha-values in pcx-images, but ASFAIK no paint-program supports
    	// that...
    	for(int i = 0; i < get_height(); i++)
    	{
    		for(int j = 0; j < (get_color_depth() >> 3); j++)
    			tmpbuf = read_line24(tmpbuf, i * width, j);
    	}

    	delete[] svtmpbuf;
    	return true;
    }

    bool pcx_image::save(const std::string &file_name)
    {
    	if(!buffer)
    		return false;

    	long size = get_width() * get_height() * (get_color_depth() >> 3);
    	unsigned char *pack = NULL, *tmppack;

    	// hopefully the amount of bytes packed will never
    	// be bigger than the original size, but if it does
    	// try making it bigger. OBSERVE that it will if your
    	// image consists of a lot of vertical lines and the
    	// lines are drawn with a color above 191
    	// so to be on the safe side we allocate twice that...
    	// we should of course process the image and calculate
    	// the number of bytes required...
    	pack = new unsigned char[size * 2];
    	if(!pack)
    		return false;
    	tmppack = pack;

    	FILE *file;

    	file = fopen(file_name.c_str(), "wb");
    	if(!file)
    	{
    		delete[] pack;
    		return false;
    	}

    	// setup pcxheader to standard values
      unsigned char *temphead = new unsigned char[sizeof(pcx_header)];
    	pcx_header header;
    	header.manufacturer = 10;
    	header.version = 5;
    	header.encoding = 1;
    	header.bits_per_pixel = 8;
    	header.x = 0;
    	header.y = 0;
    	header.height = get_height() - 1;
    	header.width = get_width() - 1;

    	header.horz_res = 71; // ???
    	header.vert_res = 71; // ???
    	header.bytes_per_line = get_width();
    	header.reserved = 0;
    	header.num_color_planes = get_color_depth() >> 3;
    	header.palette_type = 1;
    	header.copy_to_buffer(temphead);

    	fwrite(temphead, 1, sizeof(pcx_header), file);

    	for(int i = 0; i < get_height(); i++)
    	{
    		for(int j = 0; j < (get_color_depth() >> 3); j++)
    			tmppack = write_line24(tmppack, i * get_width(), j);
    	}

    	fwrite(pack, 1, (long)tmppack - (long)pack, file);

    	fclose(file);
    	delete[] temphead;
    	delete[] pack;
    	return true;
    }

    // this writes an 24-bit PCX-encoded line used by save() (above)
    unsigned char *pcx_image::write_line24(unsigned char *buf, unsigned long line, unsigned long in_pos)
    {
    	const unsigned long color_planes = get_color_depth() >> 3;
    	unsigned char *read_buf = &buffer[line * color_planes];
    	unsigned char last = read_buf[in_pos];
    	int bpl = 0;
    	int read_pos = in_pos + color_planes;
    	short runlen = 1;
    	for(int i = 1; i < get_width(); i++)
    	{
    		if(read_buf[read_pos] == last && runlen < 63)
    		{
    			runlen++;
    			if(runlen == 63)
    			{
    				*buf++ = (unsigned char)((runlen | 0xc0) & 0xff);
    				*buf++ = last;
    				runlen = 0;
    				bpl += 2;
    			}
    		}
    		else
    		{
    			if(runlen > 1 || 0xc0 == (last & 0xc0))
    			{
    				*buf++ = (unsigned char)((runlen | 0xc0) & 0xff);
    				bpl++;
    			}
    			*buf++ = last;
    			bpl++;
    			last = read_buf[read_pos];
    			runlen = 1;
    		}
    		read_pos += color_planes;
    	}
    	// handle the last pixel too
    	if(runlen)
    	{
    		if(runlen > 1 || 0xc0 == (last & 0xc0))
    		{
    			*buf++ = (unsigned char)((runlen | 0xc0) & 0xff);
    			bpl++;
    		}
    		*buf++ = last;
    		bpl++;
    	}
    	return buf;
    }

    unsigned char *pcx_image::read_line24(unsigned char *buf, unsigned long line, unsigned long sv)
    {
    	const unsigned long color_planes = get_color_depth() >> 3;
    	unsigned long byte_count = 0, bytes_read = 0, write_pos = sv;
      unsigned char data;
      unsigned char *buffer24 = (unsigned char *)&buffer[line * color_planes];
      while(bytes_read < get_width())
      {
        data = *buf++;
        if((data >= 192) && (data <= 255))
        {
          byte_count = data - 192;
          data  = *buf++;
          while(byte_count-- > 0)
          {
    				buffer24[write_pos] = data;
    				write_pos += color_planes;
    				bytes_read++;
          }
        }
        else
        {
          buffer24[write_pos] = data;
          write_pos += color_planes;
          bytes_read++;
        }
      }
      return buf;
    }

Currently browsing [imagefactory.zip] (9,523 bytes) - [bmp.cpp] - (6,673 bytes)

    /*************************************************************************
    *
    *	File: bmp.cpp
    *	Copyright(c) Andreas Magnusson 2002
    *
    *	This is my file for reading and writing bmp-files.
    *
    *	Should work on both big-endian (eg. Sun Sparc) and little-endian (eg. Intel x86)
    *	machines.
    *
    *	DISCLAIMER AND STUFF:
    *	This source-code is supplied AS IS. I cannot be held responsible for any damage caused
    *	by use or misuse of this source-code nor any derivatives of it.
    *	So use the code as you please, but give credit where they are due.
    *
    *************************************************************************/

    // disable the annoying warnings that comes up when you use map...
    #pragma warning( disable : 4786)
    #include "image.h"
    #include <stdio.h>

    //////////////////////////////////

    struct bmp_header
    {
    	char bm[2];
    	unsigned long file_size;
    	unsigned long reserved1;
    	unsigned long data_offset;
    	unsigned long bmp_header_size;
    	unsigned long width;
    	unsigned long height;
    	unsigned short planes;
    	unsigned short bpp;
    	unsigned long compr;
    	unsigned long bmp_data_size;
    	unsigned long hres;
    	unsigned long vres;
    	unsigned long colors;
    	unsigned long imp_colors;
    	//
    	unsigned long make_long(unsigned char *&buf)
    	{
    		unsigned long rv = *buf++;
    		rv += (*buf++ << 8);
    		rv += (*buf++ << 16);
    		rv += (*buf++ << 24);
    		return rv;
    	}
    	unsigned short make_word(unsigned char *&buf)
    	{
    		unsigned short rv = *buf++;
    		rv += (*buf++ << 8);
    		return rv;
    	}
    	void long2byte(unsigned long v, unsigned char *&buf)
    	{
    		*buf++ = v & 0xff;
    		*buf++ = (v >> 8) & 0xff;
    		*buf++ = (v >> 16) & 0xff;
    		*buf++ = (v >> 24) & 0xff;
    	}
    	void word2byte(unsigned short v, unsigned char *&buf)
    	{
    		*buf++ = v & 0xff;
    		*buf++ = (v >> 8) & 0xff;
    	}
    	bmp_header(unsigned char *temphead)
    	{
    	  // this procedure is needed for compatibility between
    		// little-endian machines (eg Intel-based) and big-endian
    		// machines (eg Sun Sparc)
    		bm[0] = *temphead++;
    		bm[1] = *temphead++;
    		file_size = make_long(temphead);
    		reserved1 = make_long(temphead);
    		data_offset = make_long(temphead);
    		bmp_header_size = make_long(temphead);
    		width = make_long(temphead);
    		height = make_long(temphead);
    		planes = make_word(temphead);
    		bpp = make_word(temphead);
    		compr = make_long(temphead);
    		bmp_data_size = make_long(temphead);
    		hres = make_long(temphead);
    		vres = make_long(temphead);
    		colors = make_long(temphead);
    		imp_colors = make_long(temphead);
    		//
    	}
    	bmp_header()
    	{
    	}
    	void copy_to_buffer(unsigned char *tempbuf)
    	{
    		*tempbuf++ = 'B'; //bm[0];
    		*tempbuf++ = 'M'; //bm[1];
    		long2byte(file_size, tempbuf);
    		long2byte(reserved1, tempbuf);
    		long2byte(data_offset, tempbuf);
    		long2byte(bmp_header_size, tempbuf);
    		long2byte(width, tempbuf);
    		long2byte(height, tempbuf);
    		word2byte(planes, tempbuf);
    		word2byte(bpp, tempbuf);
    		long2byte(compr, tempbuf);
    		long2byte(bmp_data_size, tempbuf);
    		long2byte(hres, tempbuf);
    		long2byte(vres, tempbuf);
    		long2byte(colors, tempbuf);
    		long2byte(imp_colors, tempbuf);
    	}
    };

    ///////////////////////////////////////////////////////////////////////////////////

    class bmp_image : public image
    {
    	void bgr2rgb();
    	void rgb2bgr();
    public:
    	bmp_image();
    	virtual bool load(const std::string &file_name);
    	virtual bool save(const std::string &file_name);
    	virtual const char *get_type() {return "bmp";}
    };

    // register bmp_image to image factory
    namespace
    {
    	register_in_factory<image, bmp_image> register_me("bmp");
    };

    bmp_image::bmp_image()
    {
    }

    bool bmp_image::load(const std::string &file_name)
    {
    	FILE *file;

      file = fopen(file_name.c_str(), "rb");
    	if(!file)
        return false;

    	unsigned char *temphead = new unsigned char[sizeof(bmp_header)];
    	fread(temphead, 1, sizeof(bmp_header), file);
    	bmp_header header(temphead);
    	delete[] temphead;

    	if(strncmp(header.bm, "BM", 2) != 0)
    	{
    		fclose(file);
    		return false;
    	}

    	// currently only handles 24 and 32 bit images
    	if(header.bpp < 24)
    	{
    		fclose(file);
    		return false;
    	}

    	width = header.width;
    	height = header.height;

    	color_depth = header.bpp;

    	int ch_count = get_color_depth() >> 3;

    	long size = width * height * ch_count;

    	if(buffer)
    		delete[] buffer;

    	buffer = new unsigned char[size];
    	if(buffer == NULL)
    	{
    		fclose(file);
    		return false;
    	}

    	if(ftell(file) != header.data_offset)
    		fseek(file, header.data_offset, SEEK_SET);

    	// the bmp-image is read into the buffer backwards, since it seems to be stored
    	// upside down...
    	int oneline = get_width() * ch_count;
    	int offset = size;
    	for(int i = 0; i < size; i += oneline)
    	{
    		fread(&buffer[offset - oneline], 1, oneline, file);
    		offset -= oneline;
    	}
    	fclose(file);

    	// convert to rgb
    	bgr2rgb();

    	return true;
    }

    bool bmp_image::save(const std::string &file_name)
    {
    	bmp_header header;
    	header.bpp = get_color_depth();
    	header.colors = 0;
    	header.imp_colors = 0;
    	header.compr = 0;
    	header.height = get_height();
    	header.width = get_width();
    	header.planes = 1;
    	header.bmp_data_size = get_width() * get_height() * (get_color_depth() >> 3);
    	header.bmp_header_size = 0x28;
    	header.data_offset = sizeof(bmp_header);
    	header.file_size = header.bmp_data_size + header.bmp_header_size;
    	header.hres = 2834;
    	header.vres = 2834;
    	header.reserved1 = 0;
    	//
    	unsigned char *temphead = new unsigned char[sizeof(bmp_header)];
    	if(!temphead)
    		return false;

    	FILE *fp = fopen(file_name.c_str(), "wb");
    	if(!fp)
    	{
    		delete[] temphead;
    		return false;
    	}

    	header.copy_to_buffer(temphead);
    	fwrite(temphead, 1, sizeof(bmp_header), fp);

    	// convert from rgb to bgr (since bmp seems to be that way)
    	bgr2rgb();

    	// write the image backwards, since that seems to be right...
    	int oneline = get_width() * (get_color_depth() >> 3);
    	unsigned long size = oneline * get_height();
    	int offset = size;
    	for(int i = 0; i < size; i += oneline)
    	{
    		fwrite(&buffer[offset - oneline], 1, oneline, fp);
    		offset -= oneline;
    	}
    	fclose(fp);
    	delete[] temphead;

    	// convert the buffer back to rgb, since that is standard in my library.
    	bgr2rgb();
    	return true;
    }

    ///
    // this routine swaps the blue and red components since bmp seems to be in BGR format
    // instead of rgb.
    void bmp_image::bgr2rgb()
    {
    	const int color_planes = get_color_depth() >> 3;
    	int size = get_width() * get_height() * color_planes;
    	unsigned char tmp;
    	for(int i = 0; i < size; i += color_planes)
    	{
    		tmp = buffer[i];
    		buffer[i] = buffer[i + 2];
    		buffer[i + 2] = tmp;
    	}
    }

The zip file viewer built into the Developer Toolbox made use of the zlib library, as well as the zlibdll source additions.

Copyright 1999-2008 (C) FLIPCODE.COM and/or the original content author(s). All rights reserved.
Please read our Terms, Conditions, and Privacy information.