1 /*
2  * OpenClonk, http://www.openclonk.org
3  *
4  * Copyright (c) 1998-2000, Matthes Bender
5  * Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
6  * Copyright (c) 2009-2016, The OpenClonk Team and contributors
7  *
8  * Distributed under the terms of the ISC license; see accompanying file
9  * "COPYING" for details.
10  *
11  * "Clonk" is a registered trademark of Matthes Bender, used with permission.
12  * See accompanying file "TRADEMARK" for details.
13  *
14  * To redistribute this file separately, substitute the full license texts
15  * for the above references.
16  */
17 // a wrapper class to DirectDraw surfaces
18 
19 #include "C4Include.h"
20 #include "graphics/CSurface8.h"
21 
22 #include "graphics/Bitmap256.h"
23 #include "c4group/CStdFile.h"
24 #include "lib/StdColors.h"
25 
CSurface8()26 CSurface8::CSurface8()
27 {
28 	Wdt=Hgt=Pitch=0;
29 	ClipX=ClipY=ClipX2=ClipY2=0;
30 	Bits=nullptr;
31 	pPal=nullptr;
32 }
33 
CSurface8(int iWdt,int iHgt)34 CSurface8::CSurface8(int iWdt, int iHgt)
35 {
36 	Wdt=Hgt=Pitch=0;
37 	ClipX=ClipY=ClipX2=ClipY2=0;
38 	Bits=nullptr;
39 	pPal=nullptr;
40 	Create(iWdt, iHgt);
41 }
42 
~CSurface8()43 CSurface8::~CSurface8()
44 {
45 	Clear();
46 }
47 
Clear()48 void CSurface8::Clear()
49 {
50 	// clear bitmap-copy
51 	delete [] Bits; Bits=nullptr;
52 	// clear pal
53 	delete pPal;
54 	pPal=nullptr;
55 }
56 
Box(int iX,int iY,int iX2,int iY2,int iCol)57 void CSurface8::Box(int iX, int iY, int iX2, int iY2, int iCol)
58 {
59 	for (int cy=iY; cy<=iY2; cy++) HLine(iX,iX2,cy,iCol);
60 }
61 
NoClip()62 void CSurface8::NoClip()
63 {
64 	ClipX=0; ClipY=0; ClipX2=Wdt-1; ClipY2=Hgt-1;
65 }
66 
Clip(int iX,int iY,int iX2,int iY2)67 void CSurface8::Clip(int iX, int iY, int iX2, int iY2)
68 {
69 	ClipX=Clamp(iX,0,Wdt-1); ClipY=Clamp(iY,0,Hgt-1);
70 	ClipX2=Clamp(iX2,0,Wdt-1); ClipY2=Clamp(iY2,0,Hgt-1);
71 }
72 
HLine(int iX,int iX2,int iY,int iCol)73 void CSurface8::HLine(int iX, int iX2, int iY, int iCol)
74 {
75 	for (int cx=iX; cx<=iX2; cx++) SetPix(cx,iY,iCol);
76 }
77 
Create(int iWdt,int iHgt)78 bool CSurface8::Create(int iWdt, int iHgt)
79 {
80 	Clear();
81 	// check size
82 	if (!iWdt || !iHgt) return false;
83 	Wdt=iWdt; Hgt=iHgt;
84 
85 	// create pal
86 	pPal = new CStdPalette;
87 	if (!pPal) return false;
88 	memset(pPal->Colors, 0, sizeof(pPal->Colors));
89 
90 	Bits=new BYTE[Wdt*Hgt];
91 	if (!Bits) return false;
92 	memset(Bits, 0, Wdt*Hgt);
93 	Pitch=Wdt;
94 	// update clipping
95 	NoClip();
96 	return true;
97 }
98 
Read(CStdStream & hGroup)99 bool CSurface8::Read(CStdStream &hGroup)
100 {
101 	int cnt,lcnt;
102 	C4BMP256Info BitmapInfo;
103 	// read bmpinfo-header
104 	if (!hGroup.Read(&BitmapInfo,sizeof(C4BMPInfo))) return false;
105 	// is it 8bpp?
106 	if (BitmapInfo.Info.biBitCount == 8)
107 	{
108 		if (!hGroup.Read(((BYTE *) &BitmapInfo)+sizeof(C4BMPInfo),sizeof(BitmapInfo)-sizeof(C4BMPInfo))) return false;
109 		if (!hGroup.Advance(BitmapInfo.FileBitsOffset())) return false;
110 	}
111 	else
112 	{
113 		// read 24bpp
114 		if (BitmapInfo.Info.biBitCount != 24) return false;
115 		if (!hGroup.Advance(((C4BMPInfo) BitmapInfo).FileBitsOffset())) return false;
116 	}
117 
118 	// Create and lock surface
119 	if (!Create(BitmapInfo.Info.biWidth,BitmapInfo.Info.biHeight)) return false;
120 
121 	if (BitmapInfo.Info.biBitCount == 8)
122 	{
123 		// Copy palette
124 		for (cnt=0; cnt<256; cnt++)
125 		{
126 			pPal->Colors[cnt] = C4RGB(BitmapInfo.Colors[cnt].rgbRed,
127 			                          BitmapInfo.Colors[cnt].rgbGreen,
128 			                          BitmapInfo.Colors[cnt].rgbBlue);
129 		}
130 	}
131 
132 	// create line buffer
133 	int iBufSize=DWordAligned(BitmapInfo.Info.biWidth*BitmapInfo.Info.biBitCount/8);
134 	BYTE *pBuf = new BYTE[iBufSize];
135 	// Read lines
136 	for (lcnt=Hgt-1; lcnt>=0; lcnt--)
137 	{
138 		if (!hGroup.Read(pBuf, iBufSize))
139 			{ Clear(); delete [] pBuf; return false; }
140 		BYTE *pPix=pBuf;
141 		for (int x=0; x<BitmapInfo.Info.biWidth; ++x)
142 			switch (BitmapInfo.Info.biBitCount)
143 			{
144 			case 8:
145 				SetPix(x, lcnt, *pPix++);
146 				break;
147 			case 24:
148 				return false;
149 				break;
150 			}
151 	}
152 	// free buffer again
153 	delete [] pBuf;
154 
155 	return true;
156 }
157 
Save(const char * szFilename,CStdPalette * bpPalette)158 bool CSurface8::Save(const char *szFilename, CStdPalette *bpPalette)
159 {
160 	C4BMP256Info BitmapInfo;
161 	BitmapInfo.Set(Wdt,Hgt, bpPalette ? bpPalette : pPal);
162 
163 	// Create file & write info
164 	CStdFile hFile;
165 
166 	if ( !hFile.Create(szFilename)
167 	     || !hFile.Write(&BitmapInfo,sizeof(BitmapInfo)) )
168 		{ return false; }
169 
170 	// Write lines
171 	char bpEmpty[4]; ZeroMem(bpEmpty, 4);
172 	const int iEmpty = DWordAligned(Wdt)-Wdt;
173 	for (int cnt=Hgt-1; cnt>=0; cnt--)
174 	{
175 		if (!hFile.Write(Bits+(Pitch*cnt),Wdt))
176 			{ return false; }
177 		if (iEmpty)
178 			if (!hFile.Write(bpEmpty,iEmpty))
179 				{ return false; }
180 	}
181 
182 	// Close file
183 	hFile.Close();
184 
185 	// Success
186 	return true;
187 }
188 
MapBytes(BYTE * bpMap)189 void CSurface8::MapBytes(BYTE *bpMap)
190 {
191 	if (!bpMap) return;
192 	for (int cnt=0; cnt<Wdt*Hgt; cnt++) SetPix(cnt%Wdt, cnt/Wdt, bpMap[GetPix(cnt%Wdt, cnt/Wdt)]);
193 }
194 
GetSurfaceSize(int & irX,int & irY) const195 void CSurface8::GetSurfaceSize(int &irX, int &irY) const
196 {
197 	// simply assign stored values
198 	irX=Wdt;
199 	irY=Hgt;
200 }
201 
ClearBox8Only(int iX,int iY,int iWdt,int iHgt)202 void CSurface8::ClearBox8Only(int iX, int iY, int iWdt, int iHgt)
203 {
204 	// clear rect; assume clip already
205 	for (int y=iY; y<iY+iHgt; ++y)
206 		for (int x=iX; x<iX+iWdt; ++x)
207 			Bits[y*Pitch+x] = 0;
208 	// done
209 }
210 
211 
Circle(int x,int y,int r,BYTE col)212 void CSurface8::Circle(int x, int y, int r, BYTE col)
213 {
214 	for (int ycnt=-r; ycnt<r; ycnt++)
215 	{
216 		int lwdt = (int) sqrt(float(r*r-ycnt*ycnt));
217 		for (int xcnt = 2 * lwdt - 1; xcnt >= 0; xcnt--)
218 			SetPix(x - lwdt + xcnt, y + ycnt, col);
219 	}
220 }
221 
AllowColor(BYTE iRngLo,BYTE iRngHi,bool fAllowZero)222 void CSurface8::AllowColor(BYTE iRngLo, BYTE iRngHi, bool fAllowZero)
223 {
224 	// change colors
225 	int xcnt,ycnt;
226 	if (iRngHi<iRngLo) return;
227 	for (ycnt=0; ycnt<Hgt; ycnt++)
228 	{
229 		for (xcnt=0; xcnt<Wdt; xcnt++)
230 		{
231 			BYTE px=GetPix(xcnt,ycnt);
232 			if (px || !fAllowZero)
233 				if ((px<iRngLo) || (px>iRngHi))
234 					SetPix(xcnt, ycnt, iRngLo + px % (iRngHi-iRngLo+1));
235 		}
236 	}
237 }
238 
SetBuffer(BYTE * pbyToBuf,int Wdt,int Hgt,int Pitch)239 void CSurface8::SetBuffer(BYTE *pbyToBuf, int Wdt, int Hgt, int Pitch)
240 {
241 	// release old
242 	Clear();
243 	// set new
244 	this->Wdt=Wdt;
245 	this->Hgt=Hgt;
246 	this->Pitch=Pitch;
247 	this->Bits = pbyToBuf;
248 	NoClip();
249 }
250 
ReleaseBuffer()251 void CSurface8::ReleaseBuffer()
252 {
253 	this->Bits = nullptr;
254 	Clear();
255 }
256