5 public class BitmapFormat
7 class_data const char ** extensions;
9 class_property const char ** extensions
11 get { return class_data(extensions); }
12 set { class_data(extensions) = value; } }
15 virtual bool ::Load(Bitmap bitmap, File f);
16 virtual bool ::Save(Bitmap bitmap, const char * fileName, void * options);
17 virtual ColorAlpha * ::LoadPalette(const char * fileName, const char * type);
20 static const char * typesToTry[] =
22 "gif", "jpg", "png", "bmp", "pcx", "memorybmp"
25 #define NUM_TYPES_TO_TRY ((int)(sizeof(typesToTry) / sizeof(char *)))
27 static subclass(BitmapFormat) FindFormat(const char * type)
29 subclass(BitmapFormat) format = null;
33 for(link = class(BitmapFormat).derivatives.first; link; link = link.next)
35 const char ** extensions;
37 extensions = format.extensions;
41 for(c = 0; extensions[c] && extensions[c][0]; c++)
42 if(!strcmp(extensions[c], type))
44 if(extensions[c] && extensions[c][0])
48 if(!link) format = null;
53 public ColorAlpha * LoadPalette(const char * fileName, const char * type)
55 char ext[MAX_EXTENSION];
56 subclass(BitmapFormat) format = null;
57 ColorAlpha * palette = null;
62 type = strlwr(GetExtension(fileName, ext));
65 format = FindFormat(type);
70 for(; typeToTry < NUM_TYPES_TO_TRY; typeToTry++)
73 format = FindFormat(typesToTry[typeToTry]);
77 palette = format.LoadPalette(fileName, type);
84 if(bitmap.Load(fileName, type, null))
86 palette = bitmap.Quantize(0, 255);
87 bitmap.allocatePalette = false;
91 if(typeToTry == -1) break;
97 ; //eGraphics_LogErrorCode(GERR_LOAD_BITMAP_FAILED, fileName);
101 static define GRANULARITY = 4;
103 static class QuantNode : struct
105 QuantNode prev, next;
107 QuantNode nodes[2][2][2];
117 void AddColor(int r, int g, int b, OldList nodeList, int * nonZeroN2)
119 int size = maxR - minR + 1;
120 int mr = (maxR - minR) / 2;
121 int mg = (maxG - minG) / 2;
122 int mb = (maxB - minB) / 2;
123 e += ((r-mr) * (r-mr) + (g-mg) * (g-mg) + (b-mb) * (b-mb)) >> level;
125 if(size > GRANULARITY)
127 int ri = (r - minR) / (size >> 1);
128 int gi = (g - minG) / (size >> 1);
129 int bi = (b - minB) / (size >> 1);
130 QuantNode child = nodes[ri][gi][bi];
133 child = nodes[ri][gi][bi] = QuantNode { };
136 child.level = level + 1;
138 child.minR = (byte)(minR + ri * (size >> 1));
139 child.maxR = (byte)(child.minR + (size >> 1) - 1);
140 child.minG = (byte)(minG + gi * (size >> 1));
141 child.maxG = (byte)(child.minG + (size >> 1) - 1);
142 child.minB = (byte)(minB + bi * (size >> 1));
143 child.maxB = (byte)(child.minB + (size >> 1) - 1);
145 child.AddColor(r, g, b, nodeList, nonZeroN2);
158 void PruneNode(OldList nodeList, int * nonZeroN2)
161 QuantNode parent = this.parent;
162 int ri = (minR != parent.minR);
163 int gi = (minG != parent.minG);
164 int bi = (minB != parent.minB);
170 QuantNode child = nodes[r][g][b];
172 child.PruneNode(nodeList, nonZeroN2);
175 this.parent.nodes[ri][gi][bi] = null;
185 nodeList.Delete(this);
188 int FindColor(ColorAlpha * palette, int r, int g, int b)
190 int size = maxR - minR + 1;
191 if(size > GRANULARITY)
193 int ri = (r - minR) / (size >> 1);
194 int gi = (g - minG) / (size >> 1);
195 int bi = (b - minB) / (size >> 1);
196 QuantNode child = nodes[ri][gi][bi];
198 return child.FindColor(palette, r, g, b);
203 int Compare(const QuantNode b, void * data)
205 return (e > b.e) ? -1 : (e < b.e);
208 void AddNodeToColorTable(ColorAlpha * palette, int * c)
217 r = Max(Min(r,255),0);
218 g = Max(Min(g,255),0);
219 b = Max(Min(b,255),0);
221 palette[*c] = { 255, Color { (byte)r, (byte)g, (byte)b } };
230 QuantNode child = nodes[r][g][b];
232 child.AddNodeToColorTable(palette, c);
247 // Data representation
248 PixelFormat pixelFormat;
251 uint size, sizeBytes;
252 ColorAlpha * palette;
253 bool allocatePalette;
257 // TODO: byte shadeShift;
259 byte * paletteShades;
263 DisplaySystem displaySystem;
264 subclass(DisplayDriver) driver;
271 /*property byte * picture { get { return picture; } set { picture = value; } };
272 property int width { get { return width; } set { width = value; } };
273 property int height { get { return height; } set { height = value; } };
274 property int stride { get { return stride; } set { stride = value; } };
275 property int size { get { return size; } };
276 property ColorAlpha * palette { set { palette = value; } get { return palette; } };
277 property bool transparent { set { transparent = value; } get { return transparent; } };
278 property bool alphaBlend { set { alphaBlend = value; } get { return alphaBlend; } };
279 property byte * paletteShades { set { paletteShades = value; } get { return paletteShades; } };
280 property PixelFormat pixelFormat { get { return pixelFormat; } set { pixelFormat = value; } };
281 property bool keepData { set { keepData = value; } get { return keepData; } };*/
283 Surface GetSurface(int x, int y, Box clip)
285 Surface result = null;
289 Box box { 0, 0, width - 1, height - 1 };
292 surface.width = (clip != null) ? (clip.right - clip.left + 1) : width;
293 surface.height = (clip != null) ? (clip.bottom - clip.top + 1) : height;
294 surface.driver = (driver != null) ? driver : ((subclass(DisplayDriver))class(LFBDisplayDriver));
295 surface.displaySystem = displaySystem;
296 surface.display = null; // For now... with render to textures, the texture should have a display...
297 //surface.alphaWrite = write;
299 if(surface.driver.GetBitmapSurface(displaySystem, surface, this, x, y, box))
301 surface.TextOpacity(false);
302 surface.SetForeground(white);
303 surface.SetBackground(black);
312 void SmoothEdges(int size)
314 Surface surface = GetSurface(0, 0, null);
318 int acrossX = width / size;
319 int acrossY = height / size;
321 for(cy = 0; cy < acrossY; cy++)
322 for(cx = 0; cx < acrossX; cx++)
327 Color color { GetRandom(0,255), GetRandom(0,255), GetRandom(0,255)) };
328 surface.SetForeground(color);
329 for(y = 0; y<size; y++)
330 for(x = 0; x<size; x++)
331 surface.PutPixel(cx * size + x, cy * size + y);
336 int r = 0, g = 0, b = 0, num = 0;
339 in1 = surface.GetPixel(cx * size - 1, cy * size);
340 in2 = surface.GetPixel(cx * size, cy * size);
348 in1 = surface.GetPixel(cx * size, cy * size - 1);
349 in2 = surface.GetPixel(cx * size, cy * size);
355 surface.SetForeground(Color { (byte)(r/num), (byte)(g/num), (byte)(b/num) });
356 surface.PutPixel(cx * size, cy * size);
357 if(cx > 0) surface.PutPixel(cx * size - 1, cy * size);
358 if(cy > 0) surface.PutPixel(cx * size, cy * size - 1);
359 if(cx > 0 && cy > 0) surface.PutPixel(cx * size - 1, cy * size - 1);
364 for(y = 1; y<size; y++)
366 in1 = surface.GetPixel(cx * size - 1, cy * size + y);
367 in2 = surface.GetPixel(cx * size, cy * size + y);
368 res = Color { (in1.r + in2.r) / 2, (in1.g + in2.g) / 2, (in1.b + in2.b) / 2 };
369 surface.SetForeground(res);
370 surface.PutPixel(cx * size - 1, cy * size + y);
371 surface.PutPixel(cx * size, cy * size + y);
376 for(x = 1; x<size; x++)
378 in1 = surface.GetPixel(cx * size + x, cy * size - 1);
379 in2 = surface.GetPixel(cx * size + x, cy * size);
380 res = Color { (in1.r + in2.r) / 2, (in1.g + in2.g) / 2, (in1.b + in2.b) / 2 };
381 surface.SetForeground(res);
382 surface.PutPixel(cx * size + x, cy * size - 1);
383 surface.PutPixel(cx * size + x, cy * size);
390 // --- Bitmap conversion ---
392 bool Convert(DisplaySystem displaySystem, PixelFormat format, ColorAlpha * palette)
395 return driver.ConvertBitmap(displaySystem, this, format, palette);
396 return pixelFormat == format;
399 bool Copy(Bitmap source)
404 // TODO: Watch out for inst stuff
405 width = source.width;
406 height = source.height;
407 pixelFormat = source.pixelFormat;
408 picture = source.picture;
409 stride = source.stride;
410 size = source.stride;
411 transparent = source.transparent;
412 shadeShift = source.shadeShift;
413 paletteShades = source.paletteShades;
414 sizeBytes = source.sizeBytes;
415 displaySystem = source.displaySystem;
416 driver = source.driver;
417 driverData = source.driverData;
419 picture = new byte[sizeBytes];
420 palette = source.palette;
421 allocatePalette = false;
425 memcpy(picture, source.picture, sizeBytes);
429 memset(picture, 0, sizeBytes);
437 bool MakeDD(DisplaySystem displaySystem)
440 if(this && displaySystem && (!driver || driver == class(LFBDisplayDriver)))
442 if(displaySystem.driver.MakeDDBitmap(displaySystem, this, false, 0))
444 this.displaySystem = displaySystem;
445 driver = displaySystem ? displaySystem.driver : ((subclass(DisplayDriver))class(LFBDisplayDriver));
452 bool MakeMipMaps(DisplaySystem displaySystem)
455 if(this && displaySystem && (!driver || driver == class(LFBDisplayDriver)))
457 if(displaySystem.driver.MakeDDBitmap(displaySystem, this, true, 0))
460 this.displaySystem = displaySystem;
467 // --- Bitmap loading ---
468 bool LoadFromFile(File file, const char * type, DisplaySystem displaySystem)
473 subclass(BitmapFormat) format = null;
475 uintsize pos = file.Tell();
478 format = FindFormat(type);
483 for(; typeToTry < NUM_TYPES_TO_TRY; typeToTry++)
485 file.Seek(pos, start);
487 format = FindFormat(typesToTry[typeToTry]);
491 if((result = format.Load(this, file)))
495 if(!MakeDD(displaySystem))
504 if(typeToTry == -1) break;
509 ; // eGraphics_LogErrorCode(GERR_LOAD_BITMAP_FAILED, null);
513 bool Load(const char * fileName, const char * type, DisplaySystem displaySystem)
516 char ext[MAX_EXTENSION];
517 subclass(BitmapFormat) format = null;
519 const char * guessedType = type;
521 if(!fileName) return false;
523 guessedType = strlwr(GetExtension(fileName, ext));
526 format = FindFormat(guessedType);
530 for(; typeToTry < NUM_TYPES_TO_TRY; typeToTry++)
533 format = FindFormat(typesToTry[typeToTry]);
537 File f = FileOpen(fileName, read);
540 if((result = format.Load(this, f)))
544 if(!MakeDD(displaySystem))
564 ; //eGraphics_LogErrorCode(GERR_LOAD_BITMAP_FAILED, fileName);
568 bool LoadT(const char * fileName, const char * type, DisplaySystem displaySystem)
570 bool result = Load(fileName, type, null);
575 if(!MakeDD(displaySystem))
586 bool LoadGrayed(const char * fileName, const char * type, DisplaySystem displaySystem)
588 bool result = Load(fileName, type, null);
591 if(pixelFormat == pixelFormatRGBA)
597 grayed.Allocate(null, width, height, 0, pixelFormat888, false);
599 for(y = 0; y <height - 1; y++)
601 for(x = 0; x < width - 1; x++)
603 ColorRGBA b = ((ColorRGBA *)picture)[y * stride + x];
604 //if(b && !(palette[b].color))
605 if(/*b.a > 128 && */(b.r + b.g + b.b) < TRESHOLD)
607 // TODO: Precomp syntax error here without brackets
608 ((ColorAlpha *)grayed.picture)[(y + 1) * grayed.stride + (x + 1)] =
609 ColorAlpha { b.a, white };
614 for(c = 0; c<size; c++)
616 ColorRGBA b = ((ColorRGBA *)picture)[c];
619 ((ColorAlpha *)grayed.picture)[c] =
620 (/*b.a > 128 && */b.r + b.g + b.b < TRESHOLD) ?
621 ColorAlpha { b.a, { 128, 128, 128 } } : ColorAlpha { b.a, { 212, 208, 200 } };
627 pixelFormat = grayed.pixelFormat;
628 picture = grayed.picture;
629 grayed.picture = null;
632 driver = grayed.driver;
637 if(!MakeDD(displaySystem))
644 if(pixelFormat != pixelFormat8)
647 palette = Quantize(1, 255);
649 if(pixelFormat == pixelFormat8)
655 grayed.Allocate(null, width, height, 0, pixelFormat888, false);
658 for(y = 0; y <height - 1; y++)
660 for(x = 0; x < width - 1; x++)
662 byte b = picture[y * stride + x];
663 //if(b && !(palette[b].color))
664 if(b && (palette[b].color.r + palette[b].color.g + palette[b].color.b) < TRESHOLD)
666 // TODO: Precomp syntax error here without brackets
667 ((ColorAlpha *)grayed.picture)[(y + 1) * grayed.stride + (x + 1)] =
668 ColorAlpha { 255, white };
673 for(c = 0; c<size; c++)
678 ((ColorAlpha *)grayed.picture)[c] =
679 //(bitmap.palette[b].color) ? Color { 212, 208, 200 } : Color { 128,128,128 };
680 (palette[b].color.r + palette[b].color.g + palette[b].color.b < TRESHOLD) ?
681 ColorAlpha { 255, { 128, 128, 128 } } : ColorAlpha { 255, { 212, 208, 200 } };
687 pixelFormat = grayed.pixelFormat;
688 picture = grayed.picture;
689 grayed.picture = null;
691 driver = grayed.driver;
696 if(!MakeDD(displaySystem))
712 bool LoadMonochrome(const char * fileName, const char * type, DisplaySystem displaySystem)
714 bool result = Load(fileName, type, null);
717 if(pixelFormat != pixelFormat8)
718 Convert(null, pixelFormat8, null);
719 if(pixelFormat == pixelFormat8)
724 grayed.Allocate(null, width, height, 0, pixelFormat888, false);
726 for(y = 0; y<height - 1; y++)
728 for(x = 0; x<width - 1; x++)
730 byte b = picture[y * stride + x];
731 if(b && !palette[b].color)
733 ((ColorAlpha *)grayed.picture)[(y + 1) * grayed.stride + (x + 1)] = white;
739 pixelFormat = grayed.pixelFormat;
741 sizeBytes = grayed.sizeBytes;
742 stride = grayed.stride;
743 picture = grayed.picture;
744 grayed.picture = null;
750 if(!MakeDD(displaySystem))
765 bool LoadMipMaps(const char * fileName, const char * type, DisplaySystem displaySystem)
767 bool result = Load(fileName, type, null);
769 if(!MakeMipMaps(displaySystem))
777 bool LoadTMipMaps(const char * fileName, const char * type, DisplaySystem displaySystem)
779 bool result = Load(fileName, type, null);
784 if(!MakeMipMaps(displaySystem))
790 bool Save(const char * fileName, const char * type, void * options)
792 char ext[MAX_EXTENSION];
793 subclass(BitmapFormat) format = null;
796 type = strlwr(GetExtension(fileName, ext));
799 format = FindFormat(type);
802 return format.Save(this, fileName, options);
806 // --- Memory bitmaps ---
808 bool AllocateDD(DisplaySystem displaySystem, int width, int height)
811 PixelFormat pixelFormat = displaySystem ? displaySystem.pixelFormat : pixelFormat888;
812 driver = displaySystem ? displaySystem.driver : ((subclass(DisplayDriver))class(LFBDisplayDriver));
813 this.displaySystem = displaySystem;
814 if(driver.AllocateBitmap(displaySystem, this, width, height, 0, pixelFormat, true))
818 paletteShades = null;
820 allocatePalette = false;
823 // FillBytes(bitmap, 0, sizeof(Bitmap));
825 driver = ((subclass(DisplayDriver))class(LFBDisplayDriver));
826 displaySystem = null;
827 if(driver.AllocateBitmap(null, this, width, height, 0, pixelFormat, true))
837 bool Allocate(const char * driverName, int width, int height, int stride, PixelFormat format, bool allocatePalette)
840 subclass(DisplayDriver) displayDriver = driverName ? GetDisplayDriver(driverName) : ((subclass(DisplayDriver))class(LFBDisplayDriver));
844 driver = displayDriver;
845 if(driver.AllocateBitmap(null, this, width, height, stride, format, allocatePalette))
857 driver.FreeBitmap(displaySystem, this);
859 driver = class(LFBDisplayDriver);
865 void Grab(Bitmap src, int x, int y)
867 Surface surface = GetSurface(0, 0, null);
868 surface.blend = false;
869 surface.Blit(src, 0,0, x, y, width, height);
873 ColorAlpha * Quantize(uint start, uint end)
875 ColorAlpha * palette = new ColorAlpha[256];
879 OldList nodeList { };
880 QuantNode mainNode { maxR = 255, maxG = 255, maxB = 255 };
885 nodeList.Add(mainNode);
887 Convert(null, pixelFormat888, null);
889 for(picture = (Color *)this.picture, c = 0; c<size; c++, picture++)
891 Color color = *picture;
892 mainNode.AddColor(color.r, color.g, color.b, nodeList, &nonZeroN2);
895 nodeList.Sort(QuantNode::Compare, null);
897 while(nonZeroN2 > end - start + 1)
899 for(node = nodeList.last; node; node = node.prev)
903 ((QuantNode)nodeList.last).PruneNode(nodeList, &nonZeroN2);
910 mainNode.AddNodeToColorTable(palette + start, &c);
913 Bitmap newBitmap { };
914 if(newBitmap.Allocate(null, width, height, 0, pixelFormat8, false))
917 ColorAlpha * picture = (ColorAlpha *)this.picture;
918 byte * newPicture = newBitmap.picture;
920 for(y = 0; y < height; y++)
921 for(x = 0; x < width; x++, picture++, newPicture++)
925 if(transparent && start > 0 && !picture->a)
928 color = (byte)(mainNode.FindColor(palette + start, picture->color.r, picture->color.g, picture->color.b) + start);
933 this.picture = newBitmap.picture;
934 this.palette = palette;
935 allocatePalette = true;
936 pixelFormat = pixelFormat8;
937 sizeBytes = stride * height;
939 newBitmap.picture = null;
949 public class CubeMap : Bitmap
952 void Load(DisplaySystem displaySystem, const String * names, const String extension, bool oldStyle)
955 for(i = 0; i < 6; i++)
957 char location[MAX_LOCATION];
958 Bitmap face = i > 0 ? { } : this;
959 strcpy(location, names[i]);
961 ChangeExtension(location, extension, location);
962 if(face.Load(location, null, null))
964 face.driverData = driverData;
965 displaySystem.driver.MakeDDBitmap(displaySystem, face, true, (i + 1) | (oldStyle << 3));