1 // Copyright (C) 2002-2012 Nikolaus Gebhardt
2 // This file is part of the "Irrlicht Engine".
3 // For conditions of distribution and use, see copyright notice in irrlicht.h
4 
5 #include "CImageWriterBMP.h"
6 
7 #ifdef _IRR_COMPILE_WITH_BMP_WRITER_
8 
9 #include "CImageLoaderBMP.h"
10 #include "IWriteFile.h"
11 #include "CColorConverter.h"
12 #include "irrString.h"
13 
14 namespace irr
15 {
16 namespace video
17 {
18 
createImageWriterBMP()19 IImageWriter* createImageWriterBMP()
20 {
21 	return new CImageWriterBMP;
22 }
23 
CImageWriterBMP()24 CImageWriterBMP::CImageWriterBMP()
25 {
26 #ifdef _DEBUG
27 	setDebugName("CImageWriterBMP");
28 #endif
29 }
30 
isAWriteableFileExtension(const io::path & filename) const31 bool CImageWriterBMP::isAWriteableFileExtension(const io::path& filename) const
32 {
33 	return core::hasFileExtension ( filename, "bmp" );
34 }
35 
writeImage(io::IWriteFile * file,IImage * image,u32 param) const36 bool CImageWriterBMP::writeImage(io::IWriteFile* file, IImage* image, u32 param) const
37 {
38 	// we always write 24-bit color because nothing really reads 32-bit
39 
40 	SBMPHeader imageHeader;
41 	imageHeader.Id = 0x4d42;
42 	imageHeader.Reserved = 0;
43 	imageHeader.BitmapDataOffset = sizeof(imageHeader);
44 	imageHeader.BitmapHeaderSize = 0x28;
45 	imageHeader.Width = image->getDimension().Width;
46 	imageHeader.Height = image->getDimension().Height;
47 	imageHeader.Planes = 1;
48 	imageHeader.BPP = 24;
49 	imageHeader.Compression = 0;
50 	imageHeader.PixelPerMeterX = 0;
51 	imageHeader.PixelPerMeterY = 0;
52 	imageHeader.Colors = 0;
53 	imageHeader.ImportantColors = 0;
54 
55 	// data size is rounded up to next larger 4 bytes boundary
56 	imageHeader.BitmapDataSize = imageHeader.Width * imageHeader.BPP / 8;
57 	imageHeader.BitmapDataSize = (imageHeader.BitmapDataSize + 3) & ~3;
58 	imageHeader.BitmapDataSize *= imageHeader.Height;
59 
60 	// file size is data size plus offset to data
61 	imageHeader.FileSize = imageHeader.BitmapDataOffset + imageHeader.BitmapDataSize;
62 
63 	// bitmaps are stored upside down and padded so we always do this
64 	void (*CColorConverter_convertFORMATtoFORMAT)(const void*, s32, void*) = 0;
65 	switch(image->getColorFormat())
66 	{
67 	case ECF_R8G8B8:
68 		CColorConverter_convertFORMATtoFORMAT
69 			= CColorConverter::convert_R8G8B8toR8G8B8;
70 		break;
71 	case ECF_A8R8G8B8:
72 		CColorConverter_convertFORMATtoFORMAT
73 			= CColorConverter::convert_A8R8G8B8toB8G8R8;
74 		break;
75 	case ECF_A1R5G5B5:
76 		CColorConverter_convertFORMATtoFORMAT
77 			= CColorConverter::convert_A1R5G5B5toR8G8B8;
78 		break;
79 	case ECF_R5G6B5:
80 		CColorConverter_convertFORMATtoFORMAT
81 			= CColorConverter::convert_R5G6B5toR8G8B8;
82 		break;
83 #ifndef _DEBUG
84 	default:
85 		break;
86 #endif
87 	}
88 
89 	// couldn't find a color converter
90 	if (!CColorConverter_convertFORMATtoFORMAT)
91 		return false;
92 
93 	// write the bitmap header
94 	if (file->write(&imageHeader, sizeof(imageHeader)) != sizeof(imageHeader))
95 		return false;
96 
97 	u8* scan_lines = (u8*)image->lock();
98 	if (!scan_lines)
99 		return false;
100 
101 	// size of one pixel in bytes
102 	u32 pixel_size = image->getBytesPerPixel();
103 
104 	// length of one row of the source image in bytes
105 	u32 row_stride = (pixel_size * imageHeader.Width);
106 
107 	// length of one row in bytes, rounded up to nearest 4-byte boundary
108 	s32 row_size = ((3 * imageHeader.Width) + 3) & ~3;
109 
110 	// allocate and clear memory for our scan line
111 	u8* row_pointer = new u8[row_size];
112 	memset(row_pointer, 0, row_size);
113 
114 	// convert the image to 24-bit BGR and flip it over
115 	s32 y;
116 	for (y = imageHeader.Height - 1; 0 <= y; --y)
117 	{
118 		if (image->getColorFormat()==ECF_R8G8B8)
119 			CColorConverter::convert24BitTo24Bit(&scan_lines[y * row_stride], row_pointer, imageHeader.Width, 1, 0, false, true);
120 		else
121 			// source, length [pixels], destination
122 			CColorConverter_convertFORMATtoFORMAT(&scan_lines[y * row_stride], imageHeader.Width, row_pointer);
123 		if (file->write(row_pointer, row_size) < row_size)
124 			break;
125 	}
126 
127 	// clean up our scratch area
128 	delete [] row_pointer;
129 
130 	// give back image handle
131 	image->unlock();
132 
133 	return y < 0;
134 }
135 
136 } // namespace video
137 } // namespace irr
138 
139 #endif
140 
141