cleaned all trailing white space from source files.
[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          uintsize pos = file.Tell();
476
477          if(type)
478             format = FindFormat(type);
479
480          if(!format)
481             typeToTry = 0;
482
483          for(; typeToTry < NUM_TYPES_TO_TRY; typeToTry++)
484          {
485             file.Seek(pos, start);
486             if(typeToTry >= 0)
487                format = FindFormat(typesToTry[typeToTry]);
488
489             if(format)
490             {
491                if((result = format.Load(this, file)))
492                {
493                   if(displaySystem)
494                   {
495                      if(!MakeDD(displaySystem))
496                      {
497                         Free();
498                         result = false;
499                      }
500                   }
501                   break;
502                }
503             }
504             if(typeToTry == -1) break;
505          }
506       }
507
508       if(!result)
509           ; // eGraphics_LogErrorCode(GERR_LOAD_BITMAP_FAILED, null);
510       return result;
511    }
512
513    bool Load(char * fileName, char * type, DisplaySystem displaySystem)
514    {
515       bool result = false;
516       char ext[MAX_EXTENSION];
517       subclass(BitmapFormat) format;
518       int typeToTry = -1;
519
520       if(!fileName) return false;
521       if(!type)
522       {
523          type = GetExtension(fileName, ext);
524          strlwr(type);
525       }
526
527       if(type)
528          format = FindFormat(type);
529       if(!format)
530          typeToTry = 0;
531
532       for(; typeToTry < NUM_TYPES_TO_TRY; typeToTry++)
533       {
534          if(typeToTry >= 0)
535             format = FindFormat(typesToTry[typeToTry]);
536
537          if(format)
538          {
539             File f = FileOpen(fileName, read);
540             if(f)
541             {
542                if((result = format.Load(this, f)))
543                {
544                   if(displaySystem)
545                   {
546                      if(!MakeDD(displaySystem))
547                      {
548                         Free();
549                         result = false;
550                      }
551                   }
552                   delete f;
553                   break;
554                }
555                delete f;
556             }
557          }
558          if(typeToTry == -1) break;
559       }
560
561       if(!result)
562           ; //eGraphics_LogErrorCode(GERR_LOAD_BITMAP_FAILED, fileName);
563       return result;
564    }
565
566    bool LoadT(char * fileName, char * type, DisplaySystem displaySystem)
567    {
568       bool result = Load(fileName, type, null);
569       if(result)
570       {
571          transparent = true;
572          if(displaySystem)
573             if(!MakeDD(displaySystem))
574             {
575                Free();
576                result = false;
577             }
578       }
579       return result;
580    }
581
582    #define TRESHOLD  384
583
584    bool LoadGrayed(char * fileName, char * type, DisplaySystem displaySystem)
585    {
586       bool result = Load(fileName, type, null);
587       if(result)
588       {
589          if(pixelFormat == pixelFormatRGBA)
590          {
591             int c;
592             int x, y;
593             Bitmap grayed { };
594
595             grayed.Allocate(null, width, height, 0, pixelFormat888, false);
596
597             for(y = 0;  y <height - 1; y++)
598             {
599                for(x = 0; x < width - 1; x++)
600                {
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)
604                   {
605                      // TODO: Precomp syntax error here without brackets
606                      ((ColorAlpha *)grayed.picture)[(y + 1) * grayed.stride + (x + 1)] =
607                         ColorAlpha { b.a, white };
608                   }
609                }
610             }
611
612             for(c = 0; c<size; c++)
613             {
614                ColorRGBA b = ((ColorRGBA *)picture)[c];
615                //if(b.a > 128)
616                {
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 } };
620                }
621             }
622
623             Free();
624
625             pixelFormat = grayed.pixelFormat;
626             picture = grayed.picture;
627             grayed.picture = null;
628             transparent = true;
629             alphaBlend = true;
630             driver = grayed.driver;
631
632             delete grayed;
633
634             if(displaySystem)
635                if(!MakeDD(displaySystem))
636                {
637                   Free();
638                   result = false;
639                }
640             return result;
641          }
642          if(pixelFormat != pixelFormat8)
643          {
644             transparent = true;
645             palette = Quantize(1, 255);
646          }
647          if(pixelFormat == pixelFormat8)
648          {
649             int c;
650             int x, y;
651             Bitmap grayed { };
652
653             grayed.Allocate(null, width, height, 0, pixelFormat888, false);
654
655
656             for(y = 0;  y <height - 1; y++)
657             {
658                for(x = 0; x < width - 1; x++)
659                {
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)
663                   {
664                      // TODO: Precomp syntax error here without brackets
665                      ((ColorAlpha *)grayed.picture)[(y + 1) * grayed.stride + (x + 1)] =
666                         ColorAlpha { 255, white };
667                   }
668                }
669             }
670
671             for(c = 0; c<size; c++)
672             {
673                byte b = picture[c];
674                if(b)
675                {
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 } };
680                }
681             }
682
683             Free();
684
685             pixelFormat = grayed.pixelFormat;
686             picture = grayed.picture;
687             grayed.picture = null;
688             transparent = true;
689             driver = grayed.driver;
690
691             delete grayed;
692
693             if(displaySystem)
694                if(!MakeDD(displaySystem))
695                {
696                   Free();
697                   result = false;
698                }
699          }
700          else
701          {
702             Free();
703             result = false;
704          }
705       }
706
707       return result;
708    }
709
710    bool LoadMonochrome(char * fileName, char * type, DisplaySystem displaySystem)
711    {
712       bool result = Load(fileName, type, null);
713       if(result)
714       {
715          if(pixelFormat != pixelFormat8)
716             Convert(null, pixelFormat8, null);
717          if(pixelFormat == pixelFormat8)
718          {
719             int x, y;
720             Bitmap grayed { };
721
722             grayed.Allocate(null, width, height, 0, pixelFormat888, false);
723
724             for(y = 0; y<height - 1; y++)
725             {
726                for(x = 0; x<width - 1; x++)
727                {
728                   byte b = picture[y * stride + x];
729                   if(b && !palette[b].color)
730                   {
731                      ((ColorAlpha *)grayed.picture)[(y + 1) * grayed.stride + (x + 1)] = white;
732                   }
733                }
734             }
735             Free();
736
737             pixelFormat = grayed.pixelFormat;
738             stride = grayed.stride;
739             picture = grayed.picture;
740             grayed.picture = null;
741             transparent = true;
742
743             delete grayed;
744
745             if(displaySystem)
746                if(!MakeDD(displaySystem))
747                {
748                   Free();
749                   result = false;
750                }
751          }
752          else
753          {
754             Free();
755             result = false;
756          }
757       }
758       return result;
759    }
760
761    bool LoadMipMaps(char * fileName, char * type, DisplaySystem displaySystem)
762    {
763       bool result = Load(fileName, type, null);
764       if(result)
765          if(!MakeMipMaps(displaySystem))
766          {
767             Free();
768             result = false;
769          }
770       return result;
771    }
772
773    bool LoadTMipMaps(char * fileName, char * type, DisplaySystem displaySystem)
774    {
775       bool result = Load(fileName, type, null);
776       if(result)
777       {
778          transparent = true;
779          if(displaySystem)
780             if(!MakeMipMaps(displaySystem))
781                result = false;
782       }
783       return result;
784    }
785
786    bool Save(char * fileName, char * type, void * options)
787    {
788       char ext[MAX_EXTENSION];
789       subclass(BitmapFormat) format;
790
791       if(!type)
792       {
793          type = GetExtension(fileName, ext);
794          strlwr(type);
795       }
796
797       if(type)
798          format = FindFormat(type);
799
800       if(format)
801          return format.Save(this, fileName, options);
802       return false;
803    }
804
805    // --- Memory bitmaps ---
806
807    bool AllocateDD(DisplaySystem displaySystem, int width, int height)
808    {
809       bool result = false;
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))
814          result = true;
815       else
816       {
817          paletteShades = null;
818          palette = null;
819          allocatePalette = false;
820          transparent = false;
821
822          // FillBytes(bitmap, 0, sizeof(Bitmap));
823
824          driver = ((subclass(DisplayDriver))class(LFBDisplayDriver));
825          displaySystem = null;
826          if(driver.AllocateBitmap(null, this, width, height, 0, pixelFormat, true))
827          {
828             result = true;
829          }
830       }
831       if(!result)
832          Free();
833       return result;
834    }
835
836    bool Allocate(char * driverName, int width, int height, int stride, PixelFormat format, bool allocatePalette)
837    {
838       bool result = false;
839       subclass(DisplayDriver) displayDriver = driverName ? GetDisplayDriver(driverName) : ((subclass(DisplayDriver))class(LFBDisplayDriver));
840       Free();
841       if(displayDriver)
842       {
843          driver = displayDriver;
844          if(driver.AllocateBitmap(null, this, width, height, stride, format, allocatePalette))
845             result = true;
846          if(!result)
847             Free();
848       }
849       return result;
850    }
851
852    void Free()
853    {
854       if(this && driver)
855       {
856          driver.FreeBitmap(displaySystem, this);
857       }
858       if(this && keepData)
859          delete picture;
860    }
861
862    void Grab(Bitmap src, int x, int y)
863    {
864       Surface surface = GetSurface(0, 0, null);
865       surface.blend = false;
866       surface.Blit(src, 0,0, x, y, width, height);
867       delete surface;
868    }
869
870    ColorAlpha * Quantize(uint start, uint end)
871    {
872       ColorAlpha * palette = new ColorAlpha[256];
873       if(palette)
874       {
875          int c;
876          OldList nodeList { };
877          QuantNode mainNode { maxR = 255, maxG = 255, maxB = 255 };
878          QuantNode node;
879          Color * picture;
880          int nonZeroN2 = 0;
881
882          nodeList.Add(mainNode);
883
884          Convert(null, pixelFormat888, null);
885
886          for(picture = (Color *)this.picture, c = 0; c<size; c++, picture++)
887          {
888             Color color = *picture;
889             mainNode.AddColor(color.r, color.g, color.b, nodeList, &nonZeroN2);
890          }
891
892          nodeList.Sort(QuantNode::Compare, null);
893
894          while(nonZeroN2 > end - start + 1)
895          {
896             for(node = nodeList.last; node; node = node.prev)
897             {
898                if(node.n2 > 0)
899                {
900                   ((QuantNode)nodeList.last).PruneNode(nodeList, &nonZeroN2);
901                   break;
902                }
903             }
904          }
905
906          c = 0;
907          mainNode.AddNodeToColorTable(palette + start, &c);
908
909          {
910             Bitmap newBitmap { };
911             if(newBitmap.Allocate(null, width, height, 0, pixelFormat8, false))
912             {
913                int y, x;
914                ColorAlpha * picture = (ColorAlpha *)this.picture;
915                byte * newPicture = newBitmap.picture;
916
917                for(y = 0; y < height; y++)
918                   for(x = 0; x < width; x++, picture++, newPicture++)
919                   {
920                      byte color;
921
922                      if(transparent && start > 0 && !picture->a)
923                         color = 0;
924                      else
925                         color = (byte)(mainNode.FindColor(palette + start, picture->color.r, picture->color.g, picture->color.b) + start);
926                      *newPicture = color;
927                   }
928
929                delete this.picture;
930                this.picture = newBitmap.picture;
931                this.palette = palette;
932                allocatePalette = true;
933                pixelFormat = pixelFormat8;
934
935                newBitmap.picture = null;
936             }
937             delete newBitmap;
938          }
939          nodeList.Free(null);
940       }
941       return palette;
942    }
943 };