6fc8adfb9ad26dc0422404a0c5ae97d9984369f3
[sdk] / ecere / src / gfx / Bitmap.ec
1 namespace gfx;
2
3 import "Display"
4
5 public class BitmapFormat
6 {
7    class_data char ** extensions;
8
9    class_property char ** extensions
10    {
11       get { return class_data(extensions); }
12       set { class_data(extensions) = value; }   }
13
14
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);
18 };
19
20 static char * typesToTry[] =
21 {
22    "gif", "jpg", "png", "bmp", "pcx", "memorybmp"
23 };
24
25 #define NUM_TYPES_TO_TRY   ((int)(sizeof(typesToTry) / sizeof(char *)))
26
27 static subclass(BitmapFormat) FindFormat(char * type)
28 {
29    subclass(BitmapFormat) format = null;
30    if(type)
31    {
32       OldLink link;
33       for(link = class(BitmapFormat).derivatives.first; link; link = link.next)
34       {
35          char ** extensions;
36          format = link.data;
37          extensions = format.extensions;
38          if(extensions)
39          {
40             int c;
41             for(c = 0; extensions[c] && extensions[c][0]; c++)
42                if(!strcmp(extensions[c], type))
43                   break;
44             if(extensions[c] && extensions[c][0])
45                break;
46          }
47       }
48       if(!link) format = null;
49    }
50    return format;
51 }
52
53 public ColorAlpha * LoadPalette(char * fileName, char * type)
54 {
55    char ext[MAX_EXTENSION];
56    subclass(BitmapFormat) format;
57    ColorAlpha * palette = null;
58    int typeToTry = -1;
59    Bitmap bitmap { };
60
61    if(!type) 
62    {
63       type = GetExtension(fileName, ext);
64       strlwr(type);
65    }
66
67    if(type)
68       format = FindFormat(type);
69
70    if(!format)
71       typeToTry = 0;
72
73    for(; typeToTry < NUM_TYPES_TO_TRY; typeToTry++)
74    {
75       if(typeToTry >= 0)
76          format = FindFormat(typesToTry[typeToTry]);
77
78       if(format)
79       {
80          palette = format.LoadPalette(fileName, type);
81          if(palette)
82             break;
83       }
84       if(!palette)
85       {
86          
87          if(bitmap.Load(fileName, type, null))
88          {
89             palette = bitmap.Quantize(0, 255);
90             bitmap.allocatePalette = false;
91             break;
92          }
93       }
94       if(typeToTry == -1) break;
95    }
96
97    delete bitmap;
98
99    if(!palette)
100       ; //eGraphics_LogErrorCode(GERR_LOAD_BITMAP_FAILED, fileName);
101    return palette;
102 }
103
104 static define GRANULARITY = 4;
105
106 static class QuantNode : struct
107 {
108    QuantNode prev, next;
109    QuantNode parent;
110    QuantNode nodes[2][2][2];
111    int index;
112    byte minR, maxR;
113    byte minG, maxG;
114    byte minB, maxB;
115    int n2;
116    float e;
117    float sr, sg, sb;
118    int level;
119
120    void AddColor(int r, int g, int b, OldList nodeList, int * nonZeroN2)
121    {
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;
127
128       if(size > GRANULARITY)
129       {
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];
134          if(!child)
135          {
136             child = nodes[ri][gi][bi] = QuantNode { };
137             nodeList.Add(child);
138
139             child.level = level + 1;
140             child.parent = this;
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);
147          }
148          child.AddColor(r, g, b, nodeList, nonZeroN2);
149       }
150       else
151       {
152          if(!n2)
153             (*nonZeroN2)++;
154          n2 ++;
155          sr += r;
156          sg += g;
157          sb += b;
158       }
159    }
160
161    void PruneNode(OldList nodeList, int * nonZeroN2)
162    {
163       int r,g,b;
164       QuantNode parent = this.parent;
165       int ri = (minR != parent.minR);
166       int gi = (minG != parent.minG);
167       int bi = (minB != parent.minB);
168
169       for(r = 0; r<2; r++)
170          for(g = 0; g<2; g++)
171             for(b = 0; b<2; b++)
172             {
173                QuantNode child = nodes[r][g][b];
174                if(child)
175                   child.PruneNode(nodeList, nonZeroN2);
176             }
177
178       this.parent.nodes[ri][gi][bi] = null;
179
180       if(n2 && parent.n2)
181          (*nonZeroN2)--;
182
183       parent.sr += sr;
184       parent.sg += sg;
185       parent.sb += sb;
186       parent.n2 += n2;
187
188       nodeList.Delete(this);
189    }
190
191    int FindColor(ColorAlpha * palette, int r, int g, int b)
192    {
193       int size = maxR - minR + 1;
194       if(size > GRANULARITY)
195       {
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];
200          if(child)
201             return child.FindColor(palette, r, g, b);
202       }
203       return index;
204    }
205
206    int Compare(const QuantNode b, void * data)
207    {
208       return (e > b.e) ? -1 : (e < b.e);
209    }
210
211    void AddNodeToColorTable(ColorAlpha * palette, int * c)
212    {
213       int r, g, b;
214
215       if(n2 > 0)
216       {
217          r = (int)(sr / n2);
218          g = (int)(sg / n2);
219          b = (int)(sb / n2);
220          r = Max(Min(r,255),0);
221          g = Max(Min(g,255),0);
222          b = Max(Min(b,255),0);
223
224          palette[*c] = { 255, Color { (byte)r, (byte)g, (byte)b } };
225          index = *c;
226          (*c)++;
227       }
228
229       for(r = 0; r<2; r++)
230          for(g = 0; g<2; g++)
231             for(b = 0; b<2; b++)
232             {
233                QuantNode child = nodes[r][g][b];
234                if(child)
235                   child.AddNodeToColorTable(palette, c);
236             }
237    }
238 };
239
240 public class Bitmap
241 {
242    class_no_expansion
243    ~Bitmap()
244    {
245       Free();
246    }
247 public:
248    int width, height;
249
250    // Data representation
251    PixelFormat pixelFormat;
252    byte * picture;
253    uint stride;
254    uint size, sizeBytes;
255    ColorAlpha * palette;
256    bool allocatePalette;
257
258    // Appearance
259    bool transparent;
260    // TODO: byte shadeShift;
261    int shadeShift;
262    byte * paletteShades;
263    bool alphaBlend;
264
265    // Hidden Data
266    DisplaySystem displaySystem;
267    subclass(DisplayDriver) driver;
268    void * driverData;
269    bool keepData;
270
271 public:
272
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; } };*/
284
285    Surface GetSurface(int x, int y, Box clip)
286    {
287       Surface result;
288       Surface surface { };
289       if(surface)
290       {
291          Box box { 0, 0, width - 1, height - 1 };
292          box.Clip(clip);
293
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...
299
300          if(surface.driver.GetBitmapSurface(displaySystem, surface, this, x, y, box))
301          {
302             surface.TextOpacity(false);
303             surface.SetForeground(white);
304             surface.SetBackground(black);
305             result = surface;
306          }
307          if(!result)
308             delete surface;
309       }
310       return result;
311    }
312
313    void SmoothEdges(int size)
314    {
315       Surface surface = GetSurface(0, 0, null);
316       if(surface)
317       {
318          int cx,cy;
319          int acrossX = width / size;
320          int acrossY = height / size;
321
322          for(cy = 0; cy < acrossY; cy++)
323             for(cx = 0; cx < acrossX; cx++)         
324             {
325                int x,y;
326                Color in1, in2, res;
327    /*
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);
333    */
334                // Corner
335                if(cx > 0 || cy > 0)
336                {
337                   int r = 0, g = 0, b = 0, num = 0;
338                   if(cx > 0)
339                   {
340                      in1 = surface.GetPixel(cx * size - 1, cy * size);
341                      in2 = surface.GetPixel(cx * size,  cy * size);
342                      r += in1.r + in2.r;
343                      g += in1.g + in2.g;
344                      b += in1.b + in2.b;
345                      num += 2;
346                   }
347                   if(cy > 0)
348                   {
349                      in1 = surface.GetPixel(cx * size, cy * size - 1);
350                      in2 = surface.GetPixel(cx * size, cy * size);
351                      r += in1.r + in2.r;
352                      g += in1.g + in2.g;
353                      b += in1.b + in2.b;
354                      num += 2;
355                   }
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);
361                }
362
363                // Left
364                if(cx>0)
365                   for(y = 1; y<size; y++)
366                   {
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);
373                   }
374
375                // Top
376                if(cy>0)
377                   for(x = 1; x<size; x++)
378                   {
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);
385                   }
386             }
387          delete surface;
388       }
389    }
390
391    // --- Bitmap conversion ---
392
393    bool Convert(DisplaySystem displaySystem, PixelFormat format, ColorAlpha * palette)
394    {
395       if(driver)
396          return driver.ConvertBitmap(displaySystem, this, format, palette);
397       return false;
398    }
399
400    bool Copy(Bitmap source)
401    {
402       bool result = false;
403       if(source)
404       {
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;
419
420          picture = new byte[sizeBytes];
421          palette = source.palette;
422          allocatePalette = false;
423
424          if(picture)
425          {
426             memcpy(picture, source.picture, sizeBytes);
427             result = true;
428          }
429          else
430             memset(picture, 0, sizeBytes);
431
432          if(!result)
433             Free();
434       }
435       return result;
436    }
437
438    bool MakeDD(DisplaySystem displaySystem)
439    {
440       bool result = false;
441       if(this && displaySystem && (!driver || driver == class(LFBDisplayDriver)))
442       {
443          if(displaySystem.driver.MakeDDBitmap(displaySystem, this, false))
444          {
445             this.displaySystem = displaySystem;
446             driver = displaySystem ? displaySystem.driver : ((subclass(DisplayDriver))class(LFBDisplayDriver));
447             result = true;
448          }
449       }
450       return result;
451    }
452
453    bool MakeMipMaps(DisplaySystem displaySystem)
454    {
455       bool result = false;
456       if(this && displaySystem && (!driver || driver == class(LFBDisplayDriver)))
457       {
458          if(displaySystem.driver.MakeDDBitmap(displaySystem, this, true))
459          {
460             this.displaySystem = displaySystem;
461             result = true;
462          }
463       }
464       return result;
465    }
466
467    // --- Bitmap loading ---
468    bool LoadFromFile(File file, char * type, DisplaySystem displaySystem)
469    {
470       bool result = false;
471       if(file)
472       {
473          subclass(BitmapFormat) format = null;
474          int typeToTry = -1;
475
476          if(type)
477             format = FindFormat(type);
478
479          if(!format)
480             typeToTry = 0;
481
482          for(; typeToTry < NUM_TYPES_TO_TRY; typeToTry++)
483          {
484             if(typeToTry >= 0)
485                format = FindFormat(typesToTry[typeToTry]);
486
487             if(format)
488             {
489                if((result = format.Load(this, file)))
490                {
491                   if(displaySystem)
492                   {
493                      if(!MakeDD(displaySystem))
494                      {
495                         Free();
496                         result = false;
497                      }
498                   }
499                   break;
500                }
501             }
502             if(typeToTry == -1) break;
503          }
504       }
505
506       if(!result)
507           ; // eGraphics_LogErrorCode(GERR_LOAD_BITMAP_FAILED, null);
508       return result;
509    }
510
511    bool Load(char * fileName, char * type, DisplaySystem displaySystem)
512    {
513       bool result = false;
514       char ext[MAX_EXTENSION];
515       subclass(BitmapFormat) format;
516       int typeToTry = -1;
517
518       if(!fileName) return false;
519       if(!type) 
520       {
521          type = GetExtension(fileName, ext);
522          strlwr(type);
523       }
524
525       if(type)
526          format = FindFormat(type);
527       if(!format)
528          typeToTry = 0;
529
530       for(; typeToTry < NUM_TYPES_TO_TRY; typeToTry++)
531       {
532          if(typeToTry >= 0)
533             format = FindFormat(typesToTry[typeToTry]);
534
535          if(format)
536          {
537             File f = FileOpen(fileName, read);
538             if(f)
539             {
540                if((result = format.Load(this, f)))
541                {
542                   if(displaySystem)
543                   {
544                      if(!MakeDD(displaySystem))
545                      {
546                         Free();
547                         result = false;
548                      }
549                   }
550                   delete f;
551                   break;
552                }
553                delete f;
554             }         
555          }
556          if(typeToTry == -1) break;
557       }
558
559       if(!result)
560           ; //eGraphics_LogErrorCode(GERR_LOAD_BITMAP_FAILED, fileName);
561       return result;
562    }
563
564    bool LoadT(char * fileName, char * type, DisplaySystem displaySystem)
565    {
566       bool result = Load(fileName, type, null);
567       if(result)
568       {
569          transparent = true;
570          if(displaySystem)
571             if(!MakeDD(displaySystem))
572             {
573                Free();
574                result = false;
575             }
576       }
577       return result;
578    }
579
580    #define TRESHOLD  384
581
582    bool LoadGrayed(char * fileName, char * type, DisplaySystem displaySystem)
583    {
584       bool result = Load(fileName, type, null);
585       if(result)
586       {
587          if(pixelFormat == pixelFormatRGBA)
588          {
589             int c;
590             int x, y;
591             Bitmap grayed { };
592
593             grayed.Allocate(null, width, height, 0, pixelFormat888, false);
594          
595             for(y = 0;  y <height - 1; y++)
596             {
597                for(x = 0; x < width - 1; x++)
598                {
599                   ColorRGBA b = ((ColorRGBA *)picture)[y * stride + x];
600                   //if(b && !(palette[b].color))
601                   if(/*b.a > 128 && */(b.r + b.g + b.b) < TRESHOLD)
602                   {
603                      // TODO: Precomp syntax error here without brackets
604                      ((ColorAlpha *)grayed.picture)[(y + 1) * grayed.stride + (x + 1)] = 
605                         ColorAlpha { b.a, white };
606                   }
607                }
608             }
609             
610             for(c = 0; c<size; c++)
611             {
612                ColorRGBA b = ((ColorRGBA *)picture)[c];
613                //if(b.a > 128)
614                {
615                   ((ColorAlpha *)grayed.picture)[c] = 
616                      (/*b.a > 128 && */b.r + b.g + b.b < TRESHOLD) ? 
617                         ColorAlpha { b.a, { 128, 128, 128 } } : ColorAlpha { b.a, { 212, 208, 200 } };
618                }
619             }
620             
621             Free();
622
623             pixelFormat = grayed.pixelFormat;
624             picture = grayed.picture;
625             grayed.picture = null;
626             transparent = true;
627             alphaBlend = true;
628             driver = grayed.driver;
629
630             delete grayed;
631
632             if(displaySystem)
633                if(!MakeDD(displaySystem))
634                {
635                   Free();
636                   result = false;
637                }
638             return result;
639          }
640          if(pixelFormat != pixelFormat8)
641          {
642             transparent = true;
643             palette = Quantize(1, 255);
644          }
645          if(pixelFormat == pixelFormat8)
646          {
647             int c;
648             int x, y;
649             Bitmap grayed { };
650
651             grayed.Allocate(null, width, height, 0, pixelFormat888, false);
652          
653             
654             for(y = 0;  y <height - 1; y++)
655             {
656                for(x = 0; x < width - 1; x++)
657                {
658                   byte b = picture[y * stride + x];
659                   //if(b && !(palette[b].color))
660                   if(b && (palette[b].color.r + palette[b].color.g + palette[b].color.b) < TRESHOLD)
661                   {
662                      // TODO: Precomp syntax error here without brackets
663                      ((ColorAlpha *)grayed.picture)[(y + 1) * grayed.stride + (x + 1)] = 
664                         ColorAlpha { 255, white };
665                   }
666                }
667             }
668             
669             for(c = 0; c<size; c++)
670             {
671                byte b = picture[c];
672                if(b)
673                {
674                   ((ColorAlpha *)grayed.picture)[c] = 
675                      //(bitmap.palette[b].color) ? Color { 212, 208, 200 } : Color { 128,128,128 };
676                      (palette[b].color.r + palette[b].color.g + palette[b].color.b < TRESHOLD) ? 
677                         ColorAlpha { 255, { 128, 128, 128 } } : ColorAlpha { 255, { 212, 208, 200 } };
678                }
679             }
680             
681             Free();
682
683             pixelFormat = grayed.pixelFormat;
684             picture = grayed.picture;
685             grayed.picture = null;
686             transparent = true;
687             driver = grayed.driver;
688
689             delete grayed;
690
691             if(displaySystem)
692                if(!MakeDD(displaySystem))
693                {
694                   Free();
695                   result = false;
696                }
697          }
698          else
699          {
700             Free();
701             result = false;
702          }
703       }
704
705       return result;
706    }
707
708    bool LoadMonochrome(char * fileName, char * type, DisplaySystem displaySystem)
709    {
710       bool result = Load(fileName, type, null);
711       if(result)
712       {
713          if(pixelFormat != pixelFormat8)
714             Convert(null, pixelFormat8, null);
715          if(pixelFormat == pixelFormat8)
716          {
717             int x, y;
718             Bitmap grayed { };
719             
720             grayed.Allocate(null, width, height, 0, pixelFormat888, false);
721          
722             for(y = 0; y<height - 1; y++)
723             {
724                for(x = 0; x<width - 1; x++)
725                {
726                   byte b = picture[y * stride + x];
727                   if(b && !palette[b].color)
728                   {
729                      ((ColorAlpha *)grayed.picture)[(y + 1) * grayed.stride + (x + 1)] = white;
730                   }
731                }
732             }
733             Free();
734
735             pixelFormat = grayed.pixelFormat;
736             stride = grayed.stride;
737             picture = grayed.picture;
738             grayed.picture = null;
739             transparent = true;
740
741             delete grayed;
742
743             if(displaySystem)
744                if(!MakeDD(displaySystem))
745                {
746                   Free();
747                   result = false;
748                }
749          }
750          else
751          {
752             Free();
753             result = false;
754          }
755       }
756       return result;
757    }
758
759    bool LoadMipMaps(char * fileName, char * type, DisplaySystem displaySystem)
760    {
761       bool result = Load(fileName, type, null);
762       if(result)
763          if(!MakeMipMaps(displaySystem))
764          {
765             Free();
766             result = false;
767          }
768       return result;
769    }
770
771    bool LoadTMipMaps(char * fileName, char * type, DisplaySystem displaySystem)
772    {
773       bool result = Load(fileName, type, null);
774       if(result)
775       {
776          transparent = true;
777          if(displaySystem)
778             if(!MakeMipMaps(displaySystem))
779                result = false;
780       }
781       return result;
782    }
783
784    bool Save(char * fileName, char * type, void * options)
785    {
786       char ext[MAX_EXTENSION];
787       subclass(BitmapFormat) format;
788
789       if(!type) 
790       {
791          type = GetExtension(fileName, ext);
792          strlwr(type);
793       }
794
795       if(type)
796          format = FindFormat(type);
797
798       if(format)
799          return format.Save(this, fileName, options);
800       return false;
801    }
802
803    // --- Memory bitmaps ---
804
805    bool AllocateDD(DisplaySystem displaySystem, int width, int height)
806    {
807       bool result = false;
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))
812          result = true;
813       else
814       {
815          paletteShades = null;
816          palette = null;
817          allocatePalette = false;
818          transparent = false;
819
820          // FillBytes(bitmap, 0, sizeof(Bitmap));
821
822          driver = ((subclass(DisplayDriver))class(LFBDisplayDriver));
823          displaySystem = null;
824          if(driver.AllocateBitmap(null, this, width, height, 0, pixelFormat, true))
825          {
826             result = true;
827          }
828       }
829       if(!result)
830          Free();
831       return result;
832    }
833
834    bool Allocate(char * driverName, int width, int height, int stride, PixelFormat format, bool allocatePalette)
835    {
836       bool result = false;
837       subclass(DisplayDriver) displayDriver = driverName ? GetDisplayDriver(driverName) : ((subclass(DisplayDriver))class(LFBDisplayDriver));
838       Free();
839       if(displayDriver)
840       {
841          driver = displayDriver;
842          if(driver.AllocateBitmap(null, this, width, height, stride, format, allocatePalette))
843             result = true;
844          if(!result)
845             Free();
846       }
847       return result;
848    }
849
850    void Free()
851    {
852       if(this && driver)
853       {
854          driver.FreeBitmap(displaySystem, this);
855       }
856       if(this && keepData)
857          delete picture;
858    }
859
860    void Grab(Bitmap src, int x, int y)
861    {
862       Surface surface = GetSurface(0, 0, null);
863       surface.blend = false;
864       surface.Blit(src, 0,0, x, y, width, height);
865       delete surface;
866    }
867
868    ColorAlpha * Quantize(uint start, uint end)
869    {
870       ColorAlpha * palette = new ColorAlpha[256];
871       if(palette)
872       {
873          int c;
874          OldList nodeList { };
875          QuantNode mainNode { maxR = 255, maxG = 255, maxB = 255 };
876          QuantNode node;
877          Color * picture;
878          int nonZeroN2 = 0;
879
880          nodeList.Add(mainNode);
881
882          Convert(null, pixelFormat888, null);
883
884          for(picture = (Color *)this.picture, c = 0; c<size; c++, picture++)
885          {
886             Color color = *picture;
887             mainNode.AddColor(color.r, color.g, color.b, nodeList, &nonZeroN2);
888          }
889
890          nodeList.Sort(QuantNode::Compare, null);
891
892          while(nonZeroN2 > end - start + 1)
893          {
894             for(node = nodeList.last; node; node = node.prev)
895             {
896                if(node.n2 > 0)
897                {
898                   ((QuantNode)nodeList.last).PruneNode(nodeList, &nonZeroN2);
899                   break;
900                }
901             }
902          }
903
904          c = 0;
905          mainNode.AddNodeToColorTable(palette + start, &c);
906          
907          {
908             Bitmap newBitmap { };
909             if(newBitmap.Allocate(null, width, height, 0, pixelFormat8, false))
910             {
911                int y, x;
912                ColorAlpha * picture = (ColorAlpha *)this.picture;
913                byte * newPicture = newBitmap.picture;
914             
915                for(y = 0; y < height; y++)
916                   for(x = 0; x < width; x++, picture++, newPicture++)
917                   {
918                      byte color;
919                      
920                      if(transparent && start > 0 && !picture->a)
921                         color = 0;
922                      else
923                         color = (byte)(mainNode.FindColor(palette + start, picture->color.r, picture->color.g, picture->color.b) + start);
924                      *newPicture = color;
925                   }
926
927                delete this.picture;
928                this.picture = newBitmap.picture;
929                this.palette = palette;
930                allocatePalette = true;
931                pixelFormat = pixelFormat8;
932
933                newBitmap.picture = null;
934             }
935             delete newBitmap;
936          }
937          nodeList.Free(null);
938       }
939       return palette;
940    }
941 };