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;
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;
270 /*property byte * picture { get { return picture; } set { picture = value; } };
271 property int width { get { return width; } set { width = value; } };
272 property int height { get { return height; } set { height = value; } };
273 property int stride { get { return stride; } set { stride = value; } };
274 property int size { get { return size; } };
275 property ColorAlpha * palette { set { palette = value; } get { return palette; } };
276 property bool transparent { set { transparent = value; } get { return transparent; } };
277 property bool alphaBlend { set { alphaBlend = value; } get { return alphaBlend; } };
278 property byte * paletteShades { set { paletteShades = value; } get { return paletteShades; } };
279 property PixelFormat pixelFormat { get { return pixelFormat; } set { pixelFormat = value; } };
280 property bool keepData { set { keepData = value; } get { return keepData; } };*/
282 Surface GetSurface(int x, int y, Box clip)
284 Surface result = null;
288 Box box { 0, 0, width - 1, height - 1 };
291 surface.width = (clip != null) ? (clip.right - clip.left + 1) : width;
292 surface.height = (clip != null) ? (clip.bottom - clip.top + 1) : height;
293 surface.driver = (driver != null) ? driver : ((subclass(DisplayDriver))class(LFBDisplayDriver));
294 surface.displaySystem = displaySystem;
295 surface.display = null; // For now... with render to textures, the texture should have a display...
297 if(surface.driver.GetBitmapSurface(displaySystem, surface, this, x, y, box))
299 surface.TextOpacity(false);
300 surface.SetForeground(white);
301 surface.SetBackground(black);
310 void SmoothEdges(int size)
312 Surface surface = GetSurface(0, 0, null);
316 int acrossX = width / size;
317 int acrossY = height / size;
319 for(cy = 0; cy < acrossY; cy++)
320 for(cx = 0; cx < acrossX; cx++)
325 Color color { GetRandom(0,255), GetRandom(0,255), GetRandom(0,255)) };
326 surface.SetForeground(color);
327 for(y = 0; y<size; y++)
328 for(x = 0; x<size; x++)
329 surface.PutPixel(cx * size + x, cy * size + y);
334 int r = 0, g = 0, b = 0, num = 0;
337 in1 = surface.GetPixel(cx * size - 1, cy * size);
338 in2 = surface.GetPixel(cx * size, cy * size);
346 in1 = surface.GetPixel(cx * size, cy * size - 1);
347 in2 = surface.GetPixel(cx * size, cy * size);
353 surface.SetForeground(Color { (byte)(r/num), (byte)(g/num), (byte)(b/num) });
354 surface.PutPixel(cx * size, cy * size);
355 if(cx > 0) surface.PutPixel(cx * size - 1, cy * size);
356 if(cy > 0) surface.PutPixel(cx * size, cy * size - 1);
357 if(cx > 0 && cy > 0) surface.PutPixel(cx * size - 1, cy * size - 1);
362 for(y = 1; y<size; y++)
364 in1 = surface.GetPixel(cx * size - 1, cy * size + y);
365 in2 = surface.GetPixel(cx * size, cy * size + y);
366 res = Color { (in1.r + in2.r) / 2, (in1.g + in2.g) / 2, (in1.b + in2.b) / 2 };
367 surface.SetForeground(res);
368 surface.PutPixel(cx * size - 1, cy * size + y);
369 surface.PutPixel(cx * size, cy * size + y);
374 for(x = 1; x<size; x++)
376 in1 = surface.GetPixel(cx * size + x, cy * size - 1);
377 in2 = surface.GetPixel(cx * size + x, cy * size);
378 res = Color { (in1.r + in2.r) / 2, (in1.g + in2.g) / 2, (in1.b + in2.b) / 2 };
379 surface.SetForeground(res);
380 surface.PutPixel(cx * size + x, cy * size - 1);
381 surface.PutPixel(cx * size + x, cy * size);
388 // --- Bitmap conversion ---
390 bool Convert(DisplaySystem displaySystem, PixelFormat format, ColorAlpha * palette)
393 return driver.ConvertBitmap(displaySystem, this, format, palette);
397 bool Copy(Bitmap source)
402 // TODO: Watch out for inst stuff
403 width = source.width;
404 height = source.height;
405 pixelFormat = source.pixelFormat;
406 picture = source.picture;
407 stride = source.stride;
408 size = source.stride;
409 transparent = source.transparent;
410 shadeShift = source.shadeShift;
411 paletteShades = source.paletteShades;
412 sizeBytes = source.sizeBytes;
413 displaySystem = source.displaySystem;
414 driver = source.driver;
415 driverData = source.driverData;
417 picture = new byte[sizeBytes];
418 palette = source.palette;
419 allocatePalette = false;
423 memcpy(picture, source.picture, sizeBytes);
427 memset(picture, 0, sizeBytes);
435 bool MakeDD(DisplaySystem displaySystem)
438 if(this && displaySystem && (!driver || driver == class(LFBDisplayDriver)))
440 if(displaySystem.driver.MakeDDBitmap(displaySystem, this, false))
442 this.displaySystem = displaySystem;
443 driver = displaySystem ? displaySystem.driver : ((subclass(DisplayDriver))class(LFBDisplayDriver));
450 bool MakeMipMaps(DisplaySystem displaySystem)
453 if(this && displaySystem && (!driver || driver == class(LFBDisplayDriver)))
455 if(displaySystem.driver.MakeDDBitmap(displaySystem, this, true))
457 this.displaySystem = displaySystem;
464 // --- Bitmap loading ---
465 bool LoadFromFile(File file, const char * type, DisplaySystem displaySystem)
470 subclass(BitmapFormat) format = null;
472 uintsize pos = file.Tell();
475 format = FindFormat(type);
480 for(; typeToTry < NUM_TYPES_TO_TRY; typeToTry++)
482 file.Seek(pos, start);
484 format = FindFormat(typesToTry[typeToTry]);
488 if((result = format.Load(this, file)))
492 if(!MakeDD(displaySystem))
501 if(typeToTry == -1) break;
506 ; // eGraphics_LogErrorCode(GERR_LOAD_BITMAP_FAILED, null);
510 bool Load(const char * fileName, const char * type, DisplaySystem displaySystem)
513 char ext[MAX_EXTENSION];
514 subclass(BitmapFormat) format;
516 const char * guessedType = type;
518 if(!fileName) return false;
520 guessedType = strlwr(GetExtension(fileName, ext));
523 format = FindFormat(guessedType);
527 for(; typeToTry < NUM_TYPES_TO_TRY; typeToTry++)
530 format = FindFormat(typesToTry[typeToTry]);
534 File f = FileOpen(fileName, read);
537 if((result = format.Load(this, f)))
541 if(!MakeDD(displaySystem))
561 ; //eGraphics_LogErrorCode(GERR_LOAD_BITMAP_FAILED, fileName);
565 bool LoadT(const char * fileName, const char * type, DisplaySystem displaySystem)
567 bool result = Load(fileName, type, null);
572 if(!MakeDD(displaySystem))
583 bool LoadGrayed(const char * fileName, const char * type, DisplaySystem displaySystem)
585 bool result = Load(fileName, type, null);
588 if(pixelFormat == pixelFormatRGBA)
594 grayed.Allocate(null, width, height, 0, pixelFormat888, false);
596 for(y = 0; y <height - 1; y++)
598 for(x = 0; x < width - 1; x++)
600 ColorRGBA b = ((ColorRGBA *)picture)[y * stride + x];
601 //if(b && !(palette[b].color))
602 if(/*b.a > 128 && */(b.r + b.g + b.b) < TRESHOLD)
604 // TODO: Precomp syntax error here without brackets
605 ((ColorAlpha *)grayed.picture)[(y + 1) * grayed.stride + (x + 1)] =
606 ColorAlpha { b.a, white };
611 for(c = 0; c<size; c++)
613 ColorRGBA b = ((ColorRGBA *)picture)[c];
616 ((ColorAlpha *)grayed.picture)[c] =
617 (/*b.a > 128 && */b.r + b.g + b.b < TRESHOLD) ?
618 ColorAlpha { b.a, { 128, 128, 128 } } : ColorAlpha { b.a, { 212, 208, 200 } };
624 pixelFormat = grayed.pixelFormat;
625 picture = grayed.picture;
626 grayed.picture = null;
629 driver = grayed.driver;
634 if(!MakeDD(displaySystem))
641 if(pixelFormat != pixelFormat8)
644 palette = Quantize(1, 255);
646 if(pixelFormat == pixelFormat8)
652 grayed.Allocate(null, width, height, 0, pixelFormat888, false);
655 for(y = 0; y <height - 1; y++)
657 for(x = 0; x < width - 1; x++)
659 byte b = picture[y * stride + x];
660 //if(b && !(palette[b].color))
661 if(b && (palette[b].color.r + palette[b].color.g + palette[b].color.b) < TRESHOLD)
663 // TODO: Precomp syntax error here without brackets
664 ((ColorAlpha *)grayed.picture)[(y + 1) * grayed.stride + (x + 1)] =
665 ColorAlpha { 255, white };
670 for(c = 0; c<size; c++)
675 ((ColorAlpha *)grayed.picture)[c] =
676 //(bitmap.palette[b].color) ? Color { 212, 208, 200 } : Color { 128,128,128 };
677 (palette[b].color.r + palette[b].color.g + palette[b].color.b < TRESHOLD) ?
678 ColorAlpha { 255, { 128, 128, 128 } } : ColorAlpha { 255, { 212, 208, 200 } };
684 pixelFormat = grayed.pixelFormat;
685 picture = grayed.picture;
686 grayed.picture = null;
688 driver = grayed.driver;
693 if(!MakeDD(displaySystem))
709 bool LoadMonochrome(const char * fileName, const char * type, DisplaySystem displaySystem)
711 bool result = Load(fileName, type, null);
714 if(pixelFormat != pixelFormat8)
715 Convert(null, pixelFormat8, null);
716 if(pixelFormat == pixelFormat8)
721 grayed.Allocate(null, width, height, 0, pixelFormat888, false);
723 for(y = 0; y<height - 1; y++)
725 for(x = 0; x<width - 1; x++)
727 byte b = picture[y * stride + x];
728 if(b && !palette[b].color)
730 ((ColorAlpha *)grayed.picture)[(y + 1) * grayed.stride + (x + 1)] = white;
736 pixelFormat = grayed.pixelFormat;
738 sizeBytes = grayed.sizeBytes;
739 stride = grayed.stride;
740 picture = grayed.picture;
741 grayed.picture = null;
747 if(!MakeDD(displaySystem))
762 bool LoadMipMaps(const char * fileName, const char * type, DisplaySystem displaySystem)
764 bool result = Load(fileName, type, null);
766 if(!MakeMipMaps(displaySystem))
774 bool LoadTMipMaps(const char * fileName, const char * type, DisplaySystem displaySystem)
776 bool result = Load(fileName, type, null);
781 if(!MakeMipMaps(displaySystem))
787 bool Save(const char * fileName, const char * type, void * options)
789 char ext[MAX_EXTENSION];
790 subclass(BitmapFormat) format;
793 type = strlwr(GetExtension(fileName, ext));
796 format = FindFormat(type);
799 return format.Save(this, fileName, options);
803 // --- Memory bitmaps ---
805 bool AllocateDD(DisplaySystem displaySystem, int width, int height)
808 PixelFormat pixelFormat = displaySystem ? displaySystem.pixelFormat : pixelFormat888;
809 driver = displaySystem ? displaySystem.driver : ((subclass(DisplayDriver))class(LFBDisplayDriver));
810 this.displaySystem = displaySystem;
811 if(driver.AllocateBitmap(displaySystem, this, width, height, 0, pixelFormat, true))
815 paletteShades = null;
817 allocatePalette = false;
820 // FillBytes(bitmap, 0, sizeof(Bitmap));
822 driver = ((subclass(DisplayDriver))class(LFBDisplayDriver));
823 displaySystem = null;
824 if(driver.AllocateBitmap(null, this, width, height, 0, pixelFormat, true))
834 bool Allocate(const char * driverName, int width, int height, int stride, PixelFormat format, bool allocatePalette)
837 subclass(DisplayDriver) displayDriver = driverName ? GetDisplayDriver(driverName) : ((subclass(DisplayDriver))class(LFBDisplayDriver));
841 driver = displayDriver;
842 if(driver.AllocateBitmap(null, this, width, height, stride, format, allocatePalette))
854 driver.FreeBitmap(displaySystem, this);
861 void Grab(Bitmap src, int x, int y)
863 Surface surface = GetSurface(0, 0, null);
864 surface.blend = false;
865 surface.Blit(src, 0,0, x, y, width, height);
869 ColorAlpha * Quantize(uint start, uint end)
871 ColorAlpha * palette = new ColorAlpha[256];
875 OldList nodeList { };
876 QuantNode mainNode { maxR = 255, maxG = 255, maxB = 255 };
881 nodeList.Add(mainNode);
883 Convert(null, pixelFormat888, null);
885 for(picture = (Color *)this.picture, c = 0; c<size; c++, picture++)
887 Color color = *picture;
888 mainNode.AddColor(color.r, color.g, color.b, nodeList, &nonZeroN2);
891 nodeList.Sort(QuantNode::Compare, null);
893 while(nonZeroN2 > end - start + 1)
895 for(node = nodeList.last; node; node = node.prev)
899 ((QuantNode)nodeList.last).PruneNode(nodeList, &nonZeroN2);
906 mainNode.AddNodeToColorTable(palette + start, &c);
909 Bitmap newBitmap { };
910 if(newBitmap.Allocate(null, width, height, 0, pixelFormat8, false))
913 ColorAlpha * picture = (ColorAlpha *)this.picture;
914 byte * newPicture = newBitmap.picture;
916 for(y = 0; y < height; y++)
917 for(x = 0; x < width; x++, picture++, newPicture++)
921 if(transparent && start > 0 && !picture->a)
924 color = (byte)(mainNode.FindColor(palette + start, picture->color.r, picture->color.g, picture->color.b) + start);
929 this.picture = newBitmap.picture;
930 this.palette = palette;
931 allocatePalette = true;
932 pixelFormat = pixelFormat8;
934 newBitmap.picture = null;