5 public class BitmapFormat
7 class_data char ** extensions;
9 class_property 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, char * fileName, void * options);
17 virtual ColorAlpha * ::LoadPalette(char * fileName, char * type);
20 static 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(char * type)
29 subclass(BitmapFormat) format = null;
33 for(link = class(BitmapFormat).derivatives.first; link; link = link.next)
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(char * fileName, char * type)
55 char ext[MAX_EXTENSION];
56 subclass(BitmapFormat) format;
57 ColorAlpha * palette = null;
63 type = GetExtension(fileName, ext);
68 format = FindFormat(type);
73 for(; typeToTry < NUM_TYPES_TO_TRY; typeToTry++)
76 format = FindFormat(typesToTry[typeToTry]);
80 palette = format.LoadPalette(fileName, type);
87 if(bitmap.Load(fileName, type, null))
89 palette = bitmap.Quantize(0, 255);
90 bitmap.allocatePalette = false;
94 if(typeToTry == -1) break;
100 ; //eGraphics_LogErrorCode(GERR_LOAD_BITMAP_FAILED, fileName);
104 static define GRANULARITY = 4;
106 static class QuantNode : struct
108 QuantNode prev, next;
110 QuantNode nodes[2][2][2];
120 void AddColor(int r, int g, int b, OldList nodeList, int * nonZeroN2)
122 int size = maxR - minR + 1;
123 int mr = (maxR - minR) / 2;
124 int mg = (maxG - minG) / 2;
125 int mb = (maxB - minB) / 2;
126 e += ((r-mr) * (r-mr) + (g-mg) * (g-mg) + (b-mb) * (b-mb)) >> level;
128 if(size > GRANULARITY)
130 int ri = (r - minR) / (size >> 1);
131 int gi = (g - minG) / (size >> 1);
132 int bi = (b - minB) / (size >> 1);
133 QuantNode child = nodes[ri][gi][bi];
136 child = nodes[ri][gi][bi] = QuantNode { };
139 child.level = level + 1;
141 child.minR = (byte)(minR + ri * (size >> 1));
142 child.maxR = (byte)(child.minR + (size >> 1) - 1);
143 child.minG = (byte)(minG + gi * (size >> 1));
144 child.maxG = (byte)(child.minG + (size >> 1) - 1);
145 child.minB = (byte)(minB + bi * (size >> 1));
146 child.maxB = (byte)(child.minB + (size >> 1) - 1);
148 child.AddColor(r, g, b, nodeList, nonZeroN2);
161 void PruneNode(OldList nodeList, int * nonZeroN2)
164 QuantNode parent = this.parent;
165 int ri = (minR != parent.minR);
166 int gi = (minG != parent.minG);
167 int bi = (minB != parent.minB);
173 QuantNode child = nodes[r][g][b];
175 child.PruneNode(nodeList, nonZeroN2);
178 this.parent.nodes[ri][gi][bi] = null;
188 nodeList.Delete(this);
191 int FindColor(ColorAlpha * palette, int r, int g, int b)
193 int size = maxR - minR + 1;
194 if(size > GRANULARITY)
196 int ri = (r - minR) / (size >> 1);
197 int gi = (g - minG) / (size >> 1);
198 int bi = (b - minB) / (size >> 1);
199 QuantNode child = nodes[ri][gi][bi];
201 return child.FindColor(palette, r, g, b);
206 int Compare(const QuantNode b, void * data)
208 return (e > b.e) ? -1 : (e < b.e);
211 void AddNodeToColorTable(ColorAlpha * palette, int * c)
220 r = Max(Min(r,255),0);
221 g = Max(Min(g,255),0);
222 b = Max(Min(b,255),0);
224 palette[*c] = { 255, Color { (byte)r, (byte)g, (byte)b } };
233 QuantNode child = nodes[r][g][b];
235 child.AddNodeToColorTable(palette, c);
250 // Data representation
251 PixelFormat pixelFormat;
254 uint size, sizeBytes;
255 ColorAlpha * palette;
256 bool allocatePalette;
260 // TODO: byte shadeShift;
262 byte * paletteShades;
266 DisplaySystem displaySystem;
267 subclass(DisplayDriver) driver;
273 /*property byte * picture { get { return picture; } set { picture = value; } };
274 property int width { get { return width; } set { width = value; } };
275 property int height { get { return height; } set { height = value; } };
276 property int stride { get { return stride; } set { stride = value; } };
277 property int size { get { return size; } };
278 property ColorAlpha * palette { set { palette = value; } get { return palette; } };
279 property bool transparent { set { transparent = value; } get { return transparent; } };
280 property bool alphaBlend { set { alphaBlend = value; } get { return alphaBlend; } };
281 property byte * paletteShades { set { paletteShades = value; } get { return paletteShades; } };
282 property PixelFormat pixelFormat { get { return pixelFormat; } set { pixelFormat = value; } };
283 property bool keepData { set { keepData = value; } get { return keepData; } };*/
285 Surface GetSurface(int x, int y, Box clip)
291 Box box { 0, 0, width - 1, height - 1 };
294 surface.width = (clip != null) ? (clip.right - clip.left + 1) : width;
295 surface.height = (clip != null) ? (clip.bottom - clip.top + 1) : height;
296 surface.driver = (driver != null) ? driver : ((subclass(DisplayDriver))class(LFBDisplayDriver));
297 surface.displaySystem = displaySystem;
298 surface.display = null; // For now... with render to textures, the texture should have a display...
300 if(surface.driver.GetBitmapSurface(displaySystem, surface, this, x, y, box))
302 surface.TextOpacity(false);
303 surface.SetForeground(white);
304 surface.SetBackground(black);
313 void SmoothEdges(int size)
315 Surface surface = GetSurface(0, 0, null);
319 int acrossX = width / size;
320 int acrossY = height / size;
322 for(cy = 0; cy < acrossY; cy++)
323 for(cx = 0; cx < acrossX; cx++)
328 Color color { GetRandom(0,255), GetRandom(0,255), GetRandom(0,255)) };
329 surface.SetForeground(color);
330 for(y = 0; y<size; y++)
331 for(x = 0; x<size; x++)
332 surface.PutPixel(cx * size + x, cy * size + y);
337 int r = 0, g = 0, b = 0, num = 0;
340 in1 = surface.GetPixel(cx * size - 1, cy * size);
341 in2 = surface.GetPixel(cx * size, cy * size);
349 in1 = surface.GetPixel(cx * size, cy * size - 1);
350 in2 = surface.GetPixel(cx * size, cy * size);
356 surface.SetForeground(Color { (byte)(r/num), (byte)(g/num), (byte)(b/num) });
357 surface.PutPixel(cx * size, cy * size);
358 if(cx > 0) surface.PutPixel(cx * size - 1, cy * size);
359 if(cy > 0) surface.PutPixel(cx * size, cy * size - 1);
360 if(cx > 0 && cy > 0) surface.PutPixel(cx * size - 1, cy * size - 1);
365 for(y = 1; y<size; y++)
367 in1 = surface.GetPixel(cx * size - 1, cy * size + y);
368 in2 = surface.GetPixel(cx * size, cy * size + y);
369 res = Color { (in1.r + in2.r) / 2, (in1.g + in2.g) / 2, (in1.b + in2.b) / 2 };
370 surface.SetForeground(res);
371 surface.PutPixel(cx * size - 1, cy * size + y);
372 surface.PutPixel(cx * size, cy * size + y);
377 for(x = 1; x<size; x++)
379 in1 = surface.GetPixel(cx * size + x, cy * size - 1);
380 in2 = surface.GetPixel(cx * size + x, cy * size);
381 res = Color { (in1.r + in2.r) / 2, (in1.g + in2.g) / 2, (in1.b + in2.b) / 2 };
382 surface.SetForeground(res);
383 surface.PutPixel(cx * size + x, cy * size - 1);
384 surface.PutPixel(cx * size + x, cy * size);
391 // --- Bitmap conversion ---
393 bool Convert(DisplaySystem displaySystem, PixelFormat format, ColorAlpha * palette)
396 return driver.ConvertBitmap(displaySystem, this, format, palette);
400 bool Copy(Bitmap source)
405 // TODO: Watch out for inst stuff
406 width = source.width;
407 height = source.height;
408 pixelFormat = source.pixelFormat;
409 picture = source.picture;
410 stride = source.stride;
411 size = source.stride;
412 transparent = source.transparent;
413 shadeShift = source.shadeShift;
414 paletteShades = source.paletteShades;
415 sizeBytes = source.sizeBytes;
416 displaySystem = source.displaySystem;
417 driver = source.driver;
418 driverData = source.driverData;
420 picture = new byte[sizeBytes];
421 palette = source.palette;
422 allocatePalette = false;
426 memcpy(picture, source.picture, sizeBytes);
430 memset(picture, 0, sizeBytes);
438 bool MakeDD(DisplaySystem displaySystem)
441 if(this && displaySystem && (!driver || driver == class(LFBDisplayDriver)))
443 if(displaySystem.driver.MakeDDBitmap(displaySystem, this, false))
445 this.displaySystem = displaySystem;
446 driver = displaySystem ? displaySystem.driver : ((subclass(DisplayDriver))class(LFBDisplayDriver));
453 bool MakeMipMaps(DisplaySystem displaySystem)
456 if(this && displaySystem && (!driver || driver == class(LFBDisplayDriver)))
458 if(displaySystem.driver.MakeDDBitmap(displaySystem, this, true))
460 this.displaySystem = displaySystem;
467 // --- Bitmap loading ---
468 bool LoadFromFile(File file, 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(char * fileName, char * type, DisplaySystem displaySystem)
516 char ext[MAX_EXTENSION];
517 subclass(BitmapFormat) format;
520 if(!fileName) return false;
523 type = GetExtension(fileName, ext);
528 format = FindFormat(type);
532 for(; typeToTry < NUM_TYPES_TO_TRY; typeToTry++)
535 format = FindFormat(typesToTry[typeToTry]);
539 File f = FileOpen(fileName, read);
542 if((result = format.Load(this, f)))
546 if(!MakeDD(displaySystem))
558 if(typeToTry == -1) break;
562 ; //eGraphics_LogErrorCode(GERR_LOAD_BITMAP_FAILED, fileName);
566 bool LoadT(char * fileName, char * type, DisplaySystem displaySystem)
568 bool result = Load(fileName, type, null);
573 if(!MakeDD(displaySystem))
584 bool LoadGrayed(char * fileName, char * type, DisplaySystem displaySystem)
586 bool result = Load(fileName, type, null);
589 if(pixelFormat == pixelFormatRGBA)
595 grayed.Allocate(null, width, height, 0, pixelFormat888, false);
597 for(y = 0; y <height - 1; y++)
599 for(x = 0; x < width - 1; x++)
601 ColorRGBA b = ((ColorRGBA *)picture)[y * stride + x];
602 //if(b && !(palette[b].color))
603 if(/*b.a > 128 && */(b.r + b.g + b.b) < TRESHOLD)
605 // TODO: Precomp syntax error here without brackets
606 ((ColorAlpha *)grayed.picture)[(y + 1) * grayed.stride + (x + 1)] =
607 ColorAlpha { b.a, white };
612 for(c = 0; c<size; c++)
614 ColorRGBA b = ((ColorRGBA *)picture)[c];
617 ((ColorAlpha *)grayed.picture)[c] =
618 (/*b.a > 128 && */b.r + b.g + b.b < TRESHOLD) ?
619 ColorAlpha { b.a, { 128, 128, 128 } } : ColorAlpha { b.a, { 212, 208, 200 } };
625 pixelFormat = grayed.pixelFormat;
626 picture = grayed.picture;
627 grayed.picture = null;
630 driver = grayed.driver;
635 if(!MakeDD(displaySystem))
642 if(pixelFormat != pixelFormat8)
645 palette = Quantize(1, 255);
647 if(pixelFormat == pixelFormat8)
653 grayed.Allocate(null, width, height, 0, pixelFormat888, false);
656 for(y = 0; y <height - 1; y++)
658 for(x = 0; x < width - 1; x++)
660 byte b = picture[y * stride + x];
661 //if(b && !(palette[b].color))
662 if(b && (palette[b].color.r + palette[b].color.g + palette[b].color.b) < TRESHOLD)
664 // TODO: Precomp syntax error here without brackets
665 ((ColorAlpha *)grayed.picture)[(y + 1) * grayed.stride + (x + 1)] =
666 ColorAlpha { 255, white };
671 for(c = 0; c<size; c++)
676 ((ColorAlpha *)grayed.picture)[c] =
677 //(bitmap.palette[b].color) ? Color { 212, 208, 200 } : Color { 128,128,128 };
678 (palette[b].color.r + palette[b].color.g + palette[b].color.b < TRESHOLD) ?
679 ColorAlpha { 255, { 128, 128, 128 } } : ColorAlpha { 255, { 212, 208, 200 } };
685 pixelFormat = grayed.pixelFormat;
686 picture = grayed.picture;
687 grayed.picture = null;
689 driver = grayed.driver;
694 if(!MakeDD(displaySystem))
710 bool LoadMonochrome(char * fileName, char * type, DisplaySystem displaySystem)
712 bool result = Load(fileName, type, null);
715 if(pixelFormat != pixelFormat8)
716 Convert(null, pixelFormat8, null);
717 if(pixelFormat == pixelFormat8)
722 grayed.Allocate(null, width, height, 0, pixelFormat888, false);
724 for(y = 0; y<height - 1; y++)
726 for(x = 0; x<width - 1; x++)
728 byte b = picture[y * stride + x];
729 if(b && !palette[b].color)
731 ((ColorAlpha *)grayed.picture)[(y + 1) * grayed.stride + (x + 1)] = white;
737 pixelFormat = grayed.pixelFormat;
738 stride = grayed.stride;
739 picture = grayed.picture;
740 grayed.picture = null;
746 if(!MakeDD(displaySystem))
761 bool LoadMipMaps(char * fileName, char * type, DisplaySystem displaySystem)
763 bool result = Load(fileName, type, null);
765 if(!MakeMipMaps(displaySystem))
773 bool LoadTMipMaps(char * fileName, char * type, DisplaySystem displaySystem)
775 bool result = Load(fileName, type, null);
780 if(!MakeMipMaps(displaySystem))
786 bool Save(char * fileName, char * type, void * options)
788 char ext[MAX_EXTENSION];
789 subclass(BitmapFormat) format;
793 type = GetExtension(fileName, ext);
798 format = FindFormat(type);
801 return format.Save(this, fileName, options);
805 // --- Memory bitmaps ---
807 bool AllocateDD(DisplaySystem displaySystem, int width, int height)
810 PixelFormat pixelFormat = displaySystem ? displaySystem.pixelFormat : pixelFormat888;
811 driver = displaySystem ? displaySystem.driver : ((subclass(DisplayDriver))class(LFBDisplayDriver));
812 this.displaySystem = displaySystem;
813 if(driver.AllocateBitmap(displaySystem, this, width, height, 0, pixelFormat, true))
817 paletteShades = null;
819 allocatePalette = false;
822 // FillBytes(bitmap, 0, sizeof(Bitmap));
824 driver = ((subclass(DisplayDriver))class(LFBDisplayDriver));
825 displaySystem = null;
826 if(driver.AllocateBitmap(null, this, width, height, 0, pixelFormat, true))
836 bool Allocate(char * driverName, int width, int height, int stride, PixelFormat format, bool allocatePalette)
839 subclass(DisplayDriver) displayDriver = driverName ? GetDisplayDriver(driverName) : ((subclass(DisplayDriver))class(LFBDisplayDriver));
843 driver = displayDriver;
844 if(driver.AllocateBitmap(null, this, width, height, stride, format, allocatePalette))
856 driver.FreeBitmap(displaySystem, this);
862 void Grab(Bitmap src, int x, int y)
864 Surface surface = GetSurface(0, 0, null);
865 surface.blend = false;
866 surface.Blit(src, 0,0, x, y, width, height);
870 ColorAlpha * Quantize(uint start, uint end)
872 ColorAlpha * palette = new ColorAlpha[256];
876 OldList nodeList { };
877 QuantNode mainNode { maxR = 255, maxG = 255, maxB = 255 };
882 nodeList.Add(mainNode);
884 Convert(null, pixelFormat888, null);
886 for(picture = (Color *)this.picture, c = 0; c<size; c++, picture++)
888 Color color = *picture;
889 mainNode.AddColor(color.r, color.g, color.b, nodeList, &nonZeroN2);
892 nodeList.Sort(QuantNode::Compare, null);
894 while(nonZeroN2 > end - start + 1)
896 for(node = nodeList.last; node; node = node.prev)
900 ((QuantNode)nodeList.last).PruneNode(nodeList, &nonZeroN2);
907 mainNode.AddNodeToColorTable(palette + start, &c);
910 Bitmap newBitmap { };
911 if(newBitmap.Allocate(null, width, height, 0, pixelFormat8, false))
914 ColorAlpha * picture = (ColorAlpha *)this.picture;
915 byte * newPicture = newBitmap.picture;
917 for(y = 0; y < height; y++)
918 for(x = 0; x < width; x++, picture++, newPicture++)
922 if(transparent && start > 0 && !picture->a)
925 color = (byte)(mainNode.FindColor(palette + start, picture->color.r, picture->color.g, picture->color.b) + start);
930 this.picture = newBitmap.picture;
931 this.palette = palette;
932 allocatePalette = true;
933 pixelFormat = pixelFormat8;
935 newBitmap.picture = null;