rnd-20090623-1-src
[rocksndiamonds.git] / src / game_sp / BitMapObject.c
1 // ----------------------------------------------------------------------------
2 // BitMapObject.c
3 // ----------------------------------------------------------------------------
4
5 #include "BitMapObject.h"
6
7 static void ConvertToVBPalette();
8 static long Get_ByteWidth();
9 static long Get_LineLength();
10 static void ReDimArrays();
11
12 // --- VERSION 1.0 CLASS
13 // --- BEGIN
14 // ---   MultiUse = -1  'True  // True
15 // ---   Persistable = 0  'NotPersistable  // NotPersistable
16 // ---   DataBindingBehavior = 0  'vbNone  // vbNone
17 // ---   DataSourceBehavior  = 0  'vbNone  // vbNone
18 // ---   MTSTransactionMode  = 0  'NotAnMTSObject  // NotAnMTSObject
19 // --- END
20
21 static char *VB_Name = "BitMapObject";
22 static boolean VB_GlobalNameSpace = False;
23 static boolean VB_Creatable = True;
24 static boolean VB_PredeclaredId = False;
25 static boolean VB_Exposed = False;
26 // --- Option Explicit
27
28 // info von http://web.usxchange.net/elmo/bmp.htm
29
30 // A BMP file consists of the four following parts:
31 //
32 //   1.BITMAPFILEHEADER
33 //   2.BITMAPINFOHEADER
34 //   3.A color table of RGBQUAD structures (1, 4 & 8 bit only)
35 //   4.An array of bytes for the actual image data
36
37 // 1.BITMAPFILEHEADER
38 //        1.bfType
39 //               Declared an unsigned integer. But, this is just to reserve space for 2 bytes.
40 //               The 2 bytes must be the 2 characters BM to indicate a BitMap file.
41 //        2.bfSize
42 //               Total size of file in bytes.
43 //        3.bfReserved1
44 //               Always zero (ignore).
45 //        4.bfReserved2
46 //               Always zero (ignore).
47 //        5.bfOffBits
48 //               Specifies the byte offset from the BITMAPFILEHEADER structure to the actual bitmap data in the file.
49 // ::: #ifndef HAS_BitmapFileHeaderType
50 // ::: typedef struct // {    /* bmfh */
51 // ::: {
52 // :::   byte bfTypeB;
53 // :::   byte bfTypeM;
54 // :::   long bfSize;
55 // :::   int bfReserved1;
56 // :::   int bfReserved2;
57 // :::   long bfOffBits;
58 // ::: } BitmapFileHeaderType;
59 // ::: #define HAS_BitmapFileHeaderType
60 // ::: #endif
61
62 //   2.BITMAPINFOHEADER
63 //        1.biSize
64 //               Size of BITMAPINFOHEADER structure (should always be 40).
65 //        2.biWidth
66 //               Width of image in pixels.
67 //        3.biHeight
68 //               Height of image in pixels.
69 //        4.biPlanes
70 //               Always one (ignore).
71 //        5.biBitCount
72 //               Specifies the number of bits per pixel. This value must be 1, 4, 8, or 24.
73 //        6.biCompression
74 //               Specifies the type of compression.
75 //                  1.BI_RGB No compression.
76 //                  2.BI_RLE8 8 bit RLE
77 //                  3.BI_RLE4 4 bit RLE
78 //        7.biSizeImage
79 //               Specifies the size, in bytes, of the image data. May be zero if the bitmap is in the BI_RGB format.
80 //        8.biXPelsPerMeter
81 //               Ignore.
82 //        9.biYPelsPerMeter
83 //               Ignore.
84 //       10.biClrUsed
85 //               Specifies the number of color indices in the color table. Zero indicaes the bitmap uses the maximum number of colors corresponding to the value of the
86 //               biBitCount member e.g. 8 bit -> 256 colors.
87 //
88 //               The maximum number of colors = 2N. Where N = biBitCount. 2N is the same as 1 << N. The same as you can get powers of 10 by shifting the decimal point
89 //               in a decimal number, only it is binary.
90 //       11.biClrImportant
91 //               Ignore.
92 // ::: #ifndef HAS_BitmapInfoHeaderType
93 // ::: typedef struct // {    /* bmih */
94 // ::: {
95 // :::   long biSize;
96 // :::   long biWidth;
97 // :::   long biHeight;
98 // :::   int biPlanes;
99 // :::   int biBitCount;
100 // :::   long biCompression;
101 // :::   long biSizeImage;
102 // :::   long biXPelsPerMeter;
103 // :::   long biYPelsPerMeter;
104 // :::   long biClrUsed;
105 // :::   long biClrImportant;
106 // ::: } BitmapInfoHeaderType;
107 // ::: #define HAS_BitmapInfoHeaderType
108 // ::: #endif
109
110 //   3.A color table of RGBQUAD structures (1, 4 & 8 bit only)
111 //          RGBQUAD structure is self explanatory.
112 // ::: #ifndef HAS_RGBQUADType
113 // ::: typedef struct // {     /* rgbq */
114 // ::: {
115 // :::   byte rgbBlue;
116 // :::   byte rgbGreen;
117 // :::   byte rgbRed;
118 // :::   byte rgbReserved;
119 // ::: } RGBQUADType;
120 // ::: #define HAS_RGBQUADType
121 // ::: #endif
122
123 RGBQUADType *ColorTable;
124 long *VBPalette;
125
126 //   4.An array of bytes for the actual image data
127 //          Bits per pixel & compression determined by biBitCount & biCompression.
128
129 byte *ImageDataBytes;
130
131 // ######################################################################################################
132
133 BitmapFileHeaderType BMFH;
134 BitmapInfoHeaderType BMIH;
135
136 static long Get_LineLength()
137 {
138   static long LineLength;
139
140   LineLength = 4 * (((Get_ByteWidth() - 1) / 4) + 1);
141
142   return LineLength;
143 }
144
145 static long Get_ByteWidth()
146 {
147   static long ByteWidth;
148
149   ByteWidth = BMIH.biWidth * BMIH.biBitCount / 8;
150
151   return ByteWidth;
152 }
153
154 void BitMapObject_CreateAtSize(long XPixels, long YPixels, long BitsPerPixel)
155 {
156   {
157     BMIH.biWidth = XPixels;
158     BMIH.biHeight = YPixels;
159     BMIH.biSize = 40;
160     BMIH.biBitCount = BitsPerPixel;
161     BMIH.biClrUsed = (1 << BMIH.biBitCount);
162     BMIH.biPlanes = 1;
163     BMIH.biClrImportant = BMIH.biClrUsed;
164     BMIH.biCompression = 0;
165   }
166   {
167     BMFH.bfTypeB = 0x42; // B'
168     BMFH.bfTypeM = 0x4D; // M'
169   }
170   ReDimArrays();
171 }
172
173 void BitMapObject_CreateFromFile(char *Path)
174 {
175   long FNum;
176
177   FNum = FreeFile();
178   if (! FileExists(Path))
179     return;
180
181   if (FileLen(Path) < (Len(BMFH) + Len(BMIH)))
182     return;
183
184   FNum = fopen(Path, "rb");
185   FILE_GET(FNum, -1, &BMFH, sizeof(BMFH));
186   FILE_GET(FNum, -1, &BMIH, sizeof(BMIH));
187   ReDimArrays();
188   {
189     if (BMIH.biCompression != 0)
190     {
191       Err.Raise(600, "BitMapObject", "Cannot read compressed BMP files");
192       fclose(FNum);
193       return;
194     }
195
196     if (BMIH.biBitCount < 9)
197     {
198       FILE_GET(FNum, -1, &ColorTable, sizeof(ColorTable));
199     }
200
201   }
202   FILE_GET(FNum, 1 + BMFH.bfOffBits, &ImageDataBytes, sizeof(ImageDataBytes));
203   fclose(FNum);
204   if (BMIH.biBitCount < 9)
205     ConvertToVBPalette();
206 }
207
208 void BitMapObject_SaveToFile(char *Path)
209 {
210   long FNum;
211
212   BMFH.bfOffBits = Len(BMFH) + Len(BMIH);
213   if (BMIH.biBitCount < 9)
214     BMFH.bfOffBits = BMFH.bfOffBits + ((1 << BMIH.biBitCount)) * Len(ColorTable[0]);
215
216   BMIH.biSizeImage = Get_LineLength() * BMIH.biHeight;
217   BMFH.bfSize = BMFH.bfOffBits + BMIH.biSizeImage;
218   FNum = FreeFile();
219   FNum = fopen(Path, "wb");
220   FILE_PUT(FNum, -1, &BMFH, sizeof(BMFH));
221   FILE_PUT(FNum, -1, &BMIH, sizeof(BMIH));
222   if (BMIH.biBitCount < 9)
223   {
224     FILE_PUT(FNum, -1, &ColorTable, sizeof(ColorTable));
225   }
226
227   FILE_PUT(FNum, -1, &ImageDataBytes, sizeof(ImageDataBytes));
228   fclose(FNum);
229 }
230
231 static void ConvertToVBPalette()
232 {
233   long ColMax, i;
234
235   ColMax = UBound(ColorTable);
236   for (i = 0; i <= ColMax; i++)
237   {
238     {
239       VBPalette[i] = RGB(ColorTable[i].rgbRed, ColorTable[i].rgbGreen, ColorTable[i].rgbBlue);
240     }
241   }
242 }
243
244 static void ReDimArrays()
245 {
246   {
247     if (BMIH.biBitCount < 9)
248     {
249       BMIH.biClrUsed = (1 << BMIH.biBitCount);
250       ColorTable = REDIM_1D(sizeof(RGBQUADType), 0, BMIH.biClrUsed - 1);
251       VBPalette = REDIM_1D(sizeof(long), 0, BMIH.biClrUsed - 1);
252     }
253
254     if (0 < Get_LineLength() && 0 < BMIH.biHeight)
255     {
256       ImageDataBytes = REDIM_2D(sizeof(byte), 0, Get_LineLength() - 1, 0, BMIH.biHeight - 1);
257     }
258
259   }
260 }
261
262 long BitMapObject_Get_Palette(long Index)
263 {
264   long Palette;
265
266   Palette = VBPalette[Index];
267
268   return Palette;
269 }
270
271 void BitMapObject_Let_Palette(long Index, long NewVal)
272 {
273   VBPalette[Index] = NewVal & 0xFFFFFF;
274   {
275     ColorTable[Index].rgbRed = (NewVal & 0xFF) / 0x1;
276     ColorTable[Index].rgbGreen = (NewVal & 0xFF00) / 0x100;
277     ColorTable[Index].rgbBlue = (NewVal & 0xFF0000) / 0x10000;
278   }
279 }
280
281 long BitMapObject_Get_ColorsUsed()
282 {
283   long ColorsUsed;
284
285   if (BMIH.biBitCount < 9)
286   {
287     ColorsUsed = (1 << BMIH.biBitCount);
288   }
289   else
290   {
291     ColorsUsed = 0;
292   }
293
294   return ColorsUsed;
295 }
296
297 long BitMapObject_Get_ColorIndex(long X, long Y)
298 {
299   long ColorIndex;
300
301   long ColIndex, NewX, BitPos, nY;
302
303   if (8 < BMIH.biBitCount)
304   {
305     Err.Raise(600, "BitmapObject", "I have ! Palette in this ColorDepthMode");
306     ColorIndex = -1;
307     return ColorIndex;
308   }
309
310   nY = BMIH.biHeight - 1 - Y;
311   switch (BMIH.biBitCount)
312   {
313     case 1:
314       ColIndex = ImageDataBytes[X / 8, nY];
315       BitPos = 7 - (X % 8);
316       NewX = (1 << BitPos);
317       if ((NewX && ColIndex) == 0)
318       {
319         ColorIndex = 0;
320       }
321       else
322       {
323         ColorIndex = 1;
324       }
325
326       break;
327
328     case 4:
329       ColIndex = ImageDataBytes[X / 2, nY];
330       if ((X % 2) == 0)
331       {
332         ColorIndex = (ColIndex & 0xF0) / 0x10;
333       }
334       else
335       {
336         ColorIndex = (ColIndex & 0xF);
337       }
338
339       break;
340
341     case 8:
342       ColorIndex = ImageDataBytes[X, nY];
343       break;
344
345     default:
346       Err.Raise(600, "BitmapObject", "Invalid bpx value");
347       break;
348   }
349
350   return ColorIndex;
351 }
352
353 void BitMapObject_Let_ColorIndex(long X, long Y, long ColorIndex)
354 {
355   long ColIndex, ByteVal, NewX, BitPos, nY;
356
357   if (8 < BMIH.biBitCount)
358   {
359     Err.Raise(600, "BitmapObject", "I have ! Palette in this ColorDepthMode");
360     return;
361   }
362
363   nY = BMIH.biHeight - 1 - Y;
364   switch (BMIH.biBitCount)
365   {
366     case 1:
367       ByteVal = ImageDataBytes[X / 8, nY];
368       BitPos = 7 - (X % 8);
369       NewX = (1 << BitPos);
370       ColIndex = ColorIndex * NewX;
371       if (ColIndex == 0)
372       {
373         ByteVal = (ByteVal & (! NewX));
374       }
375       else
376       {
377         ByteVal = (ByteVal | NewX);
378       }
379
380       ImageDataBytes[X / 8, nY] = ByteVal;
381       break;
382
383     case 4:
384       ByteVal = ImageDataBytes[X / 2, nY];
385       if ((X % 2) == 0)
386       {
387         ByteVal = (ByteVal & 0xF) + ColorIndex * 0x10;
388       }
389       else
390       {
391         ByteVal = (ByteVal & 0xF0) + ColorIndex;
392       }
393
394       ImageDataBytes[X / 2, nY] = ByteVal;
395       break;
396
397     case 8:
398       ImageDataBytes[X, nY] = ColorIndex;
399       break;
400
401     case 24:
402       Err.Raise(600, "BitmapObject", "Invalid bpx value");
403       break;
404   }
405 }
406
407 long BitMapObject_Get_Point(long X, long Y)
408 {
409   long Point;
410
411   long ColIndex, NewX, BitPos, nY;
412
413   nY = BMIH.biHeight - 1 - Y;
414   switch (BMIH.biBitCount)
415   {
416     case 1:
417       ColIndex = ImageDataBytes[X / 8, nY];
418       BitPos = 7 - (X % 8);
419       NewX = (1 << BitPos);
420       if ((NewX && ColIndex) == 0)
421       {
422         ColIndex = 0;
423       }
424       else
425       {
426         ColIndex = 1;
427       }
428
429       Point = VBPalette[ColIndex];
430       break;
431
432     case 4:
433       ColIndex = ImageDataBytes[X / 2, nY];
434       if ((X % 2) == 0)
435       {
436         ColIndex = (ColIndex & 0xF0) / 0x10;
437       }
438       else
439       {
440         ColIndex = (ColIndex & 0xF);
441       }
442
443       Point = VBPalette[ColIndex];
444       break;
445
446     case 8:
447       ColIndex = ImageDataBytes[X, nY];
448       Point = VBPalette[ColIndex];
449       break;
450
451     case 24:
452       NewX = 3 * X;
453       Point = ImageDataBytes[NewX, nY] * 0x10000;
454       Point = Point + ImageDataBytes[NewX + 1, nY] * 0x100;
455       Point = Point + ImageDataBytes[NewX + 2, nY];
456       break;
457
458     default:
459       Err.Raise(600, "BitmapObject", "Invalid bpx value");
460       break;
461   }
462
463   return Point;
464 }
465
466 void BitMapObject_Let_Point(long X, long Y, long NewColor)
467 {
468   long ColIndex, ByteVal, NewX, BitPos, nY;
469
470   nY = BMIH.biHeight - 1 - Y;
471   switch (BMIH.biBitCount)
472   {
473     case 1:
474       ColIndex = GetPaletteIndex(NewColor);
475       ByteVal = ImageDataBytes[X / 8, nY];
476       BitPos = 7 - (X % 8);
477       NewX = (1 << BitPos);
478       ColIndex = ColIndex * NewX;
479       if (ColIndex == 0)
480       {
481         ByteVal = (ByteVal & (! NewX));
482       }
483       else
484       {
485         ByteVal = (ByteVal | NewX);
486       }
487
488       ImageDataBytes[X / 8, nY] = ByteVal;
489       break;
490
491     case 4:
492       ColIndex = GetPaletteIndex(NewColor);
493       ByteVal = ImageDataBytes[X / 2, nY];
494       if ((X % 2) == 0)
495       {
496         ByteVal = (ByteVal & 0xF) + ColIndex * 0x10;
497       }
498       else
499       {
500         ByteVal = (ByteVal & 0xF0) + ColIndex;
501       }
502
503       ImageDataBytes[X / 2, nY] = ByteVal;
504       break;
505
506     case 8:
507       ImageDataBytes[X, nY] = GetPaletteIndex(NewColor);
508       break;
509
510     case 24:
511       NewX = 3 * X;
512       ImageDataBytes[NewX, nY] = (NewColor & 0xFF0000) / 0x10000;  // B
513       ImageDataBytes[NewX + 1, nY] = (NewColor & 0xFF00) / 0x100;  // G
514       ImageDataBytes[NewX + 2, nY] = (NewColor & 0xFF);  // R
515       break;
516
517     default:
518       Err.Raise(600, "BitmapObject", "Invalid bpx value");
519       break;
520   }
521 }
522
523 int BitMapObject_GetPaletteIndex(long Color)
524 {
525   int GetPaletteIndex;
526
527   long i, ColMax;
528
529   ColMax = UBound(VBPalette);
530   for (i = 0; i <= ColMax; i++)
531   {
532     if (VBPalette[i] == Color)
533       break;
534   }
535
536   if (ColMax < i) // Error - Color not in Palette!
537     i = -1;
538
539   GetPaletteIndex = i;
540
541   return GetPaletteIndex;
542 }
543
544 long BitMapObject_Get_Width()
545 {
546   long Width;
547
548   Width = BMIH.biWidth;
549
550   return Width;
551 }
552
553 long BitMapObject_Get_Height()
554 {
555   long Height;
556
557   Height = BMIH.biHeight;
558
559   return Height;
560 }
561
562 BitMapObject BitMapObject_GetStretchCopy(float StretchVal)
563 {
564   BitMapObject GetStretchCopy;
565
566   long nWidth, nHeight, iX, iY;
567
568   // GetStretchCopy = New BitMapObject; // (handle this later, if needed)
569   nWidth = StretchVal * BMIH.biWidth;
570   nHeight = StretchVal * BMIH.biHeight;
571   GetStretchCopy.CreateAtSize(nWidth, nHeight, CLng(BMIH.biBitCount));
572   nWidth = nWidth - 1;
573   nHeight = nHeight - 1;
574   for (iX = 0; iX <= BMIH.biClrUsed - 1; iX++)
575   {
576     GetStretchCopy.Let_Palette(iX, Palette(iX));
577   }
578
579   for (iY = 0; iY <= nHeight; iY++)
580   {
581     for (iX = 0; iX <= nWidth; iX++)
582     {
583       GetStretchCopy.Let_ColorIndex(iX, iY, ColorIndex(Int(iX / StretchVal), Int(iY / StretchVal)));
584     }
585   }
586
587   return GetStretchCopy;
588 }