ecere/gfx/3D: Updates to Direct3D driver to not crash on bump maps / cube maps
[sdk] / ecere / src / gfx / Bitmap.ec
1 namespace gfx;
2
3 import "Display"
4
5 public class BitmapFormat
6 {
7    class_data const char ** extensions;
8
9    class_property const 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, const char * fileName, void * options);
17    virtual ColorAlpha * ::LoadPalette(const char * fileName, const char * type);
18 };
19
20 static const 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(const 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          const 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(const char * fileName, const char * type)
54 {
55    char ext[MAX_EXTENSION];
56    subclass(BitmapFormat) format = null;
57    ColorAlpha * palette = null;
58    int typeToTry = -1;
59    Bitmap bitmap { };
60
61    if(!type)
62       type = strlwr(GetExtension(fileName, ext));
63
64    if(type)
65       format = FindFormat(type);
66
67    if(!format)
68       typeToTry = 0;
69
70    for(; typeToTry < NUM_TYPES_TO_TRY; typeToTry++)
71    {
72       if(typeToTry >= 0)
73          format = FindFormat(typesToTry[typeToTry]);
74
75       if(format)
76       {
77          palette = format.LoadPalette(fileName, type);
78          if(palette)
79             break;
80       }
81       if(!palette)
82       {
83
84          if(bitmap.Load(fileName, type, null))
85          {
86             palette = bitmap.Quantize(0, 255);
87             bitmap.allocatePalette = false;
88             break;
89          }
90       }
91       if(typeToTry == -1) break;
92    }
93
94    delete bitmap;
95
96    if(!palette)
97       ; //eGraphics_LogErrorCode(GERR_LOAD_BITMAP_FAILED, fileName);
98    return palette;
99 }
100
101 static define GRANULARITY = 4;
102
103 static class QuantNode : struct
104 {
105    QuantNode prev, next;
106    QuantNode parent;
107    QuantNode nodes[2][2][2];
108    int index;
109    byte minR, maxR;
110    byte minG, maxG;
111    byte minB, maxB;
112    int n2;
113    float e;
114    float sr, sg, sb;
115    int level;
116
117    void AddColor(int r, int g, int b, OldList nodeList, int * nonZeroN2)
118    {
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;
124
125       if(size > GRANULARITY)
126       {
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];
131          if(!child)
132          {
133             child = nodes[ri][gi][bi] = QuantNode { };
134             nodeList.Add(child);
135
136             child.level = level + 1;
137             child.parent = this;
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);
144          }
145          child.AddColor(r, g, b, nodeList, nonZeroN2);
146       }
147       else
148       {
149          if(!n2)
150             (*nonZeroN2)++;
151          n2 ++;
152          sr += r;
153          sg += g;
154          sb += b;
155       }
156    }
157
158    void PruneNode(OldList nodeList, int * nonZeroN2)
159    {
160       int r,g,b;
161       QuantNode parent = this.parent;
162       int ri = (minR != parent.minR);
163       int gi = (minG != parent.minG);
164       int bi = (minB != parent.minB);
165
166       for(r = 0; r<2; r++)
167          for(g = 0; g<2; g++)
168             for(b = 0; b<2; b++)
169             {
170                QuantNode child = nodes[r][g][b];
171                if(child)
172                   child.PruneNode(nodeList, nonZeroN2);
173             }
174
175       this.parent.nodes[ri][gi][bi] = null;
176
177       if(n2 && parent.n2)
178          (*nonZeroN2)--;
179
180       parent.sr += sr;
181       parent.sg += sg;
182       parent.sb += sb;
183       parent.n2 += n2;
184
185       nodeList.Delete(this);
186    }
187
188    int FindColor(ColorAlpha * palette, int r, int g, int b)
189    {
190       int size = maxR - minR + 1;
191       if(size > GRANULARITY)
192       {
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];
197          if(child)
198             return child.FindColor(palette, r, g, b);
199       }
200       return index;
201    }
202
203    int Compare(const QuantNode b, void * data)
204    {
205       return (e > b.e) ? -1 : (e < b.e);
206    }
207
208    void AddNodeToColorTable(ColorAlpha * palette, int * c)
209    {
210       int r, g, b;
211
212       if(n2 > 0)
213       {
214          r = (int)(sr / n2);
215          g = (int)(sg / n2);
216          b = (int)(sb / n2);
217          r = Max(Min(r,255),0);
218          g = Max(Min(g,255),0);
219          b = Max(Min(b,255),0);
220
221          palette[*c] = { 255, Color { (byte)r, (byte)g, (byte)b } };
222          index = *c;
223          (*c)++;
224       }
225
226       for(r = 0; r<2; r++)
227          for(g = 0; g<2; g++)
228             for(b = 0; b<2; b++)
229             {
230                QuantNode child = nodes[r][g][b];
231                if(child)
232                   child.AddNodeToColorTable(palette, c);
233             }
234    }
235 };
236
237 public class Bitmap
238 {
239    class_no_expansion
240    ~Bitmap()
241    {
242       Free();
243    }
244 public:
245    int width, height;
246
247    // Data representation
248    PixelFormat pixelFormat;
249    byte * picture;
250    uint stride;
251    uint size, sizeBytes;
252    ColorAlpha * palette;
253    bool allocatePalette;
254
255    // Appearance
256    bool transparent;
257    // TODO: byte shadeShift;
258    int shadeShift;
259    byte * paletteShades;
260    bool alphaBlend;
261
262    // Hidden Data
263    DisplaySystem displaySystem;
264    subclass(DisplayDriver) driver;
265    void * driverData;
266    bool keepData;
267    bool mipMaps;
268
269 public:
270
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; } };*/
282
283    Surface GetSurface(int x, int y, Box clip)
284    {
285       Surface result = null;
286       Surface surface { };
287       if(surface)
288       {
289          Box box { 0, 0, width - 1, height - 1 };
290          box.Clip(clip);
291
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;
298
299          if(surface.driver.GetBitmapSurface(displaySystem, surface, this, x, y, box))
300          {
301             surface.TextOpacity(false);
302             surface.SetForeground(white);
303             surface.SetBackground(black);
304             result = surface;
305          }
306          if(!result)
307             delete surface;
308       }
309       return result;
310    }
311
312    void SmoothEdges(int size)
313    {
314       Surface surface = GetSurface(0, 0, null);
315       if(surface)
316       {
317          int cx,cy;
318          int acrossX = width / size;
319          int acrossY = height / size;
320
321          for(cy = 0; cy < acrossY; cy++)
322             for(cx = 0; cx < acrossX; cx++)
323             {
324                int x,y;
325                Color in1, in2, res;
326    /*
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);
332    */
333                // Corner
334                if(cx > 0 || cy > 0)
335                {
336                   int r = 0, g = 0, b = 0, num = 0;
337                   if(cx > 0)
338                   {
339                      in1 = surface.GetPixel(cx * size - 1, cy * size);
340                      in2 = surface.GetPixel(cx * size,  cy * size);
341                      r += in1.r + in2.r;
342                      g += in1.g + in2.g;
343                      b += in1.b + in2.b;
344                      num += 2;
345                   }
346                   if(cy > 0)
347                   {
348                      in1 = surface.GetPixel(cx * size, cy * size - 1);
349                      in2 = surface.GetPixel(cx * size, cy * size);
350                      r += in1.r + in2.r;
351                      g += in1.g + in2.g;
352                      b += in1.b + in2.b;
353                      num += 2;
354                   }
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);
360                }
361
362                // Left
363                if(cx>0)
364                   for(y = 1; y<size; y++)
365                   {
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);
372                   }
373
374                // Top
375                if(cy>0)
376                   for(x = 1; x<size; x++)
377                   {
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);
384                   }
385             }
386          delete surface;
387       }
388    }
389
390    // --- Bitmap conversion ---
391
392    bool Convert(DisplaySystem displaySystem, PixelFormat format, ColorAlpha * palette)
393    {
394       if(driver)
395          return driver.ConvertBitmap(displaySystem, this, format, palette);
396       return pixelFormat == format;
397    }
398
399    bool Copy(Bitmap source)
400    {
401       bool result = false;
402       if(source)
403       {
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;
418
419          picture = new byte[sizeBytes];
420          palette = source.palette;
421          allocatePalette = false;
422
423          if(picture)
424          {
425             memcpy(picture, source.picture, sizeBytes);
426             result = true;
427          }
428          else
429             memset(picture, 0, sizeBytes);
430
431          if(!result)
432             Free();
433       }
434       return result;
435    }
436
437    bool MakeDD(DisplaySystem displaySystem)
438    {
439       bool result = false;
440       if(this && displaySystem && (!driver || driver == class(LFBDisplayDriver)))
441       {
442          if(displaySystem.driver.MakeDDBitmap(displaySystem, this, false, 0))
443          {
444             this.displaySystem = displaySystem;
445             driver = displaySystem ? displaySystem.driver : ((subclass(DisplayDriver))class(LFBDisplayDriver));
446             result = true;
447          }
448       }
449       return result;
450    }
451
452    bool MakeMipMaps(DisplaySystem displaySystem)
453    {
454       bool result = false;
455       if(this && displaySystem && (!driver || driver == class(LFBDisplayDriver)))
456       {
457          if(displaySystem.driver.MakeDDBitmap(displaySystem, this, true, 0))
458          {
459             this.mipMaps = true;
460             this.displaySystem = displaySystem;
461             result = true;
462          }
463       }
464       return result;
465    }
466
467    // --- Bitmap loading ---
468    bool LoadFromFile(File file, const 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(const char * fileName, const char * type, DisplaySystem displaySystem)
514    {
515       bool result = false;
516       char ext[MAX_EXTENSION];
517       subclass(BitmapFormat) format = null;
518       int typeToTry = -1;
519       const char * guessedType = type;
520
521       if(!fileName) return false;
522       if(!guessedType)
523          guessedType = strlwr(GetExtension(fileName, ext));
524
525       if(guessedType)
526          format = FindFormat(guessedType);
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)
557          {
558             if(type) break;
559             typeToTry = 0;
560          }
561       }
562
563       if(!result)
564           ; //eGraphics_LogErrorCode(GERR_LOAD_BITMAP_FAILED, fileName);
565       return result;
566    }
567
568    bool LoadT(const char * fileName, const char * type, DisplaySystem displaySystem)
569    {
570       bool result = Load(fileName, type, null);
571       if(result)
572       {
573          transparent = true;
574          if(displaySystem)
575             if(!MakeDD(displaySystem))
576             {
577                Free();
578                result = false;
579             }
580       }
581       return result;
582    }
583
584    #define TRESHOLD  384
585
586    bool LoadGrayed(const char * fileName, const char * type, DisplaySystem displaySystem)
587    {
588       bool result = Load(fileName, type, null);
589       if(result)
590       {
591          if(pixelFormat == pixelFormatRGBA)
592          {
593             int c;
594             int x, y;
595             Bitmap grayed { };
596
597             grayed.Allocate(null, width, height, 0, pixelFormat888, false);
598
599             for(y = 0;  y <height - 1; y++)
600             {
601                for(x = 0; x < width - 1; x++)
602                {
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)
606                   {
607                      // TODO: Precomp syntax error here without brackets
608                      ((ColorAlpha *)grayed.picture)[(y + 1) * grayed.stride + (x + 1)] =
609                         ColorAlpha { b.a, white };
610                   }
611                }
612             }
613
614             for(c = 0; c<size; c++)
615             {
616                ColorRGBA b = ((ColorRGBA *)picture)[c];
617                //if(b.a > 128)
618                {
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 } };
622                }
623             }
624
625             Free();
626
627             pixelFormat = grayed.pixelFormat;
628             picture = grayed.picture;
629             grayed.picture = null;
630             transparent = true;
631             alphaBlend = true;
632             driver = grayed.driver;
633
634             delete grayed;
635
636             if(displaySystem)
637                if(!MakeDD(displaySystem))
638                {
639                   Free();
640                   result = false;
641                }
642             return result;
643          }
644          if(pixelFormat != pixelFormat8)
645          {
646             transparent = true;
647             palette = Quantize(1, 255);
648          }
649          if(pixelFormat == pixelFormat8)
650          {
651             int c;
652             int x, y;
653             Bitmap grayed { };
654
655             grayed.Allocate(null, width, height, 0, pixelFormat888, false);
656
657
658             for(y = 0;  y <height - 1; y++)
659             {
660                for(x = 0; x < width - 1; x++)
661                {
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)
665                   {
666                      // TODO: Precomp syntax error here without brackets
667                      ((ColorAlpha *)grayed.picture)[(y + 1) * grayed.stride + (x + 1)] =
668                         ColorAlpha { 255, white };
669                   }
670                }
671             }
672
673             for(c = 0; c<size; c++)
674             {
675                byte b = picture[c];
676                if(b)
677                {
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 } };
682                }
683             }
684
685             Free();
686
687             pixelFormat = grayed.pixelFormat;
688             picture = grayed.picture;
689             grayed.picture = null;
690             transparent = true;
691             driver = grayed.driver;
692
693             delete grayed;
694
695             if(displaySystem)
696                if(!MakeDD(displaySystem))
697                {
698                   Free();
699                   result = false;
700                }
701          }
702          else
703          {
704             Free();
705             result = false;
706          }
707       }
708
709       return result;
710    }
711
712    bool LoadMonochrome(const char * fileName, const char * type, DisplaySystem displaySystem)
713    {
714       bool result = Load(fileName, type, null);
715       if(result)
716       {
717          if(pixelFormat != pixelFormat8)
718             Convert(null, pixelFormat8, null);
719          if(pixelFormat == pixelFormat8)
720          {
721             int x, y;
722             Bitmap grayed { };
723
724             grayed.Allocate(null, width, height, 0, pixelFormat888, false);
725
726             for(y = 0; y<height - 1; y++)
727             {
728                for(x = 0; x<width - 1; x++)
729                {
730                   byte b = picture[y * stride + x];
731                   if(b && !palette[b].color)
732                   {
733                      ((ColorAlpha *)grayed.picture)[(y + 1) * grayed.stride + (x + 1)] = white;
734                   }
735                }
736             }
737             Free();
738
739             pixelFormat = grayed.pixelFormat;
740             size = grayed.size;
741             sizeBytes = grayed.sizeBytes;
742             stride = grayed.stride;
743             picture = grayed.picture;
744             grayed.picture = null;
745             transparent = true;
746
747             delete grayed;
748
749             if(displaySystem)
750                if(!MakeDD(displaySystem))
751                {
752                   Free();
753                   result = false;
754                }
755          }
756          else
757          {
758             Free();
759             result = false;
760          }
761       }
762       return result;
763    }
764
765    bool LoadMipMaps(const char * fileName, const char * type, DisplaySystem displaySystem)
766    {
767       bool result = Load(fileName, type, null);
768       if(result)
769          if(!MakeMipMaps(displaySystem))
770          {
771             Free();
772             result = false;
773          }
774       return result;
775    }
776
777    bool LoadTMipMaps(const char * fileName, const char * type, DisplaySystem displaySystem)
778    {
779       bool result = Load(fileName, type, null);
780       if(result)
781       {
782          transparent = true;
783          if(displaySystem)
784             if(!MakeMipMaps(displaySystem))
785                result = false;
786       }
787       return result;
788    }
789
790    bool Save(const char * fileName, const char * type, void * options)
791    {
792       char ext[MAX_EXTENSION];
793       subclass(BitmapFormat) format = null;
794
795       if(!type)
796          type = strlwr(GetExtension(fileName, ext));
797
798       if(type)
799          format = FindFormat(type);
800
801       if(format)
802          return format.Save(this, fileName, options);
803       return false;
804    }
805
806    // --- Memory bitmaps ---
807
808    bool AllocateDD(DisplaySystem displaySystem, int width, int height)
809    {
810       bool result = false;
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))
815          result = true;
816       else
817       {
818          paletteShades = null;
819          palette = null;
820          allocatePalette = false;
821          transparent = false;
822
823          // FillBytes(bitmap, 0, sizeof(Bitmap));
824
825          driver = ((subclass(DisplayDriver))class(LFBDisplayDriver));
826          displaySystem = null;
827          if(driver.AllocateBitmap(null, this, width, height, 0, pixelFormat, true))
828          {
829             result = true;
830          }
831       }
832       if(!result)
833          Free();
834       return result;
835    }
836
837    bool Allocate(const char * driverName, int width, int height, int stride, PixelFormat format, bool allocatePalette)
838    {
839       bool result = false;
840       subclass(DisplayDriver) displayDriver = driverName ? GetDisplayDriver(driverName) : ((subclass(DisplayDriver))class(LFBDisplayDriver));
841       Free();
842       if(displayDriver)
843       {
844          driver = displayDriver;
845          if(driver.AllocateBitmap(null, this, width, height, stride, format, allocatePalette))
846             result = true;
847          if(!result)
848             Free();
849       }
850       return result;
851    }
852
853    void Free()
854    {
855       if(this && driver)
856       {
857          driver.FreeBitmap(displaySystem, this);
858          driverData = null;
859          driver = class(LFBDisplayDriver);
860       }
861       if(this && keepData)
862          delete picture;
863    }
864
865    void Grab(Bitmap src, int x, int y)
866    {
867       Surface surface = GetSurface(0, 0, null);
868       surface.blend = false;
869       surface.Blit(src, 0,0, x, y, width, height);
870       delete surface;
871    }
872
873    ColorAlpha * Quantize(uint start, uint end)
874    {
875       ColorAlpha * palette = new ColorAlpha[256];
876       if(palette)
877       {
878          int c;
879          OldList nodeList { };
880          QuantNode mainNode { maxR = 255, maxG = 255, maxB = 255 };
881          QuantNode node;
882          Color * picture;
883          int nonZeroN2 = 0;
884
885          nodeList.Add(mainNode);
886
887          Convert(null, pixelFormat888, null);
888
889          for(picture = (Color *)this.picture, c = 0; c<size; c++, picture++)
890          {
891             Color color = *picture;
892             mainNode.AddColor(color.r, color.g, color.b, nodeList, &nonZeroN2);
893          }
894
895          nodeList.Sort(QuantNode::Compare, null);
896
897          while(nonZeroN2 > end - start + 1)
898          {
899             for(node = nodeList.last; node; node = node.prev)
900             {
901                if(node.n2 > 0)
902                {
903                   ((QuantNode)nodeList.last).PruneNode(nodeList, &nonZeroN2);
904                   break;
905                }
906             }
907          }
908
909          c = 0;
910          mainNode.AddNodeToColorTable(palette + start, &c);
911
912          {
913             Bitmap newBitmap { };
914             if(newBitmap.Allocate(null, width, height, 0, pixelFormat8, false))
915             {
916                int y, x;
917                ColorAlpha * picture = (ColorAlpha *)this.picture;
918                byte * newPicture = newBitmap.picture;
919
920                for(y = 0; y < height; y++)
921                   for(x = 0; x < width; x++, picture++, newPicture++)
922                   {
923                      byte color;
924
925                      if(transparent && start > 0 && !picture->a)
926                         color = 0;
927                      else
928                         color = (byte)(mainNode.FindColor(palette + start, picture->color.r, picture->color.g, picture->color.b) + start);
929                      *newPicture = color;
930                   }
931
932                delete this.picture;
933                this.picture = newBitmap.picture;
934                this.palette = palette;
935                allocatePalette = true;
936                pixelFormat = pixelFormat8;
937                sizeBytes = stride * height;
938
939                newBitmap.picture = null;
940             }
941             delete newBitmap;
942          }
943          nodeList.Free(null);
944       }
945       return palette;
946    }
947 };
948
949 public class CubeMap : Bitmap
950 {
951 public:
952    bool Load(DisplaySystem displaySystem, const String * names, const String extension, bool oldStyle)
953    {
954       int i;
955       bool result = true;
956       for(i = 0; result && i < 6; i++)
957       {
958          char location[MAX_LOCATION];
959          Bitmap face = i > 0 ? { } : this;
960          strcpy(location, names[i]);
961          if(extension)
962             ChangeExtension(location, extension, location);
963          if(face.Load(location, null, null))
964          {
965             face.driverData = driverData;
966             result = displaySystem.driver.MakeDDBitmap(displaySystem, face, true, (i + 1) | (oldStyle << 3));
967          }
968          else
969             result = false;
970          if(i > 0)
971          {
972             face.driverData = 0;
973             delete face;
974          }
975       }
976       return result;
977    }
978 };