ecere; ide; compiler: Fixed setting safety null character after (v)snprintf
[sdk] / ecere / src / gfx / Surface.ec
1 namespace gfx;
2
3 import "Display"
4
5 #include <stdarg.h>
6
7 public struct ColorKey
8 {
9    ColorAlpha color;
10    float percent;
11 };
12
13 public enum GradientDirection { vertical, horizontal };
14
15 public void PaletteGradient(ColorAlpha * palette, int numColors, ColorKey * keys, int numKeys, float smoothness)
16 {
17    ColorKey * key = keys, * nextKey = keys;
18    int keyNum = 0, nextKeyNum = 0;
19    float inc = 1.0f/(numColors-1);
20    float percent = 0;
21    int start;
22    ColorAlpha color;
23    int c;
24
25    for(c = start = 0; c<numColors; c++)
26    {
27       ColorAlpha newColor = 0;
28
29       while(nextKey && percent > nextKey->percent)
30       {
31          key = nextKey; keyNum = nextKeyNum; 
32       
33          if(keyNum < numKeys - 1) 
34          { 
35             nextKey = key + 1;
36             nextKeyNum = keyNum + 1; 
37          }
38          else
39             break;
40       }
41
42       if(nextKey && nextKey->percent != key->percent)
43       {
44          float scale = ease((percent - key->percent) / (nextKey->percent - key->percent), 
45             smoothness, smoothness);
46          int cr = key->color.color.r;
47          int cg = key->color.color.g;
48          int cb = key->color.color.b;
49          int nr = nextKey->color.color.r;
50          int ng = nextKey->color.color.g;
51          int nb = nextKey->color.color.b;
52          int r = (int)(cr + (nr - cr) * scale);
53          int g = (int)(cg + (ng - cg) * scale);
54          int b = (int)(cb + (nb - cb) * scale);
55
56          r = Max(Min(r, 255),0);
57          g = Max(Min(g, 255),0);
58          b = Max(Min(b, 255),0);
59
60          newColor = Color { (byte)r, (byte)g, (byte)b };
61       }
62       else if(key)
63          newColor = key ? key->color : 0;
64
65       if(c == 0 || newColor != color)
66       {
67          if(c != 0)
68          {
69             int i;
70             for(i = start; i<c; i++)
71                palette[i] = color;
72
73             start = c;
74          }
75          color = newColor;
76       }
77       percent += inc;
78    }
79
80    {
81       int i;
82       for(i = start; i<c; i++)
83          palette[i] = color;
84    }
85 }
86
87 float ease(float t, float a, float b) 
88 {
89    float k;
90    float s = a + b;
91
92    if (s == 0.0f) return t;
93    if (s > 1.0f) 
94    {
95       a /= s;
96       b /= s;
97    }
98    k = 1.0f/(2.0f-a-b);
99    if (t < a) return (k/a)*t*t;
100    if (t < 1.0f - b) return k*(2.0f * t - a);
101    t = 1.0f - t;
102    return 1.0f - (k/b)*t*t;
103 }
104
105 public enum AlphaWriteMode
106 {
107    dontWrite,
108    write,
109    blend
110 };
111
112 public class Surface
113 {
114    public int width, height;
115
116    subclass(DisplayDriver) driver;
117    DisplaySystem displaySystem;
118    Display display;
119
120    public Point offset;
121    public Box box, unclippedBox;
122
123    // States
124    Font font;
125    ColorAlpha foreground, background;
126    bool textOpacity;
127    public void * driverData;
128    AlphaWriteMode alphaWrite;
129    bool blend;
130    bool writeColor;
131    ColorAlpha blitTint;
132
133    blitTint = white;
134
135    blend = true;
136    writeColor = true;
137    alphaWrite = blend;
138
139    ~Surface()
140    {
141       if(driver)
142          driver.ReleaseSurface(display, this);
143    }
144
145 public:
146    property AlphaWriteMode alphaWrite
147    {
148       set { alphaWrite = value; }
149       get { return alphaWrite; }
150    }
151    property bool blend
152    {
153       set { blend = value; }
154       get { return blend; }
155    }
156    property Bitmap bitmap
157    {
158       get
159       {
160          return ((LFBSurface)driverData).bitmap;
161       }     
162    }
163
164    ColorAlpha GetPixel(int x, int y)
165    {
166       return driver.GetPixel(display, this, x,y);
167    }
168
169    void PutPixel(int x, int y)
170    {
171       driver.PutPixel(display, this, x,y);
172    }
173
174    void DrawLine(int x1, int y1, int x2, int y2)
175    {
176       driver.DrawLine(display, this, x1,y1,x2,y2);
177    }
178
179    void VLine(int y1, int y2, int x)
180    {
181       driver.DrawLine(display, this, x,y1,x,y2);
182    }
183
184    void HLine(int x1, int x2, int y)
185    {
186       driver.DrawLine(display, this, x1,y,x2,y);
187    }
188
189    void Rectangle(int x1, int y1, int x2, int y2)
190    {
191       driver.Rectangle(display, this, x1,y1,x2,y2);
192    }
193
194    void Area(int x1, int y1, int x2, int y2)
195    {
196       driver.Area(display, this, x1,y1,x2,y2);
197    }
198
199    void Clear(ClearType type)
200    {
201       driver.Clear(display, this, type);
202    }
203
204    void Blit(Bitmap src, int dx, int dy, int sx, int sy, int w, int h)
205    {
206       if(src.driver == driver)
207          driver.Blit(display, this, src, dx,dy, sx, sy,w,h);
208       else if(!src.driver || src.driver == class(LFBDisplayDriver))
209          driver.BlitDI(display, this, src, dx,dy, sx, sy,w,h);
210    }
211
212    void Stretch(Bitmap src, int dx, int dy, int sx, int sy, int w, int h, int sw, int sh)
213    {
214       if(src.driver == driver)
215          driver.Stretch(display, this, src, dx,dy, sx,sy, w,h, sw, sh);
216       else if(!src.driver || src.driver == class(LFBDisplayDriver))
217          driver.StretchDI(display, this, src, dx,dy, sx,sy, w,h, sw, sh);
218    }
219
220    void Filter(Bitmap src, int dx, int dy, int sx, int sy, int w, int h, int sw, int sh)
221    {
222       if(src.driver == driver)
223          driver.Filter(display, this, src, dx,dy, sx,sy, w,h, sw, sh);
224       else if(!src.driver || src.driver == class(LFBDisplayDriver))
225          driver.FilterDI(display, this, src, dx,dy, sx,sy, w,h, sw, sh);
226    }
227
228    void Tile(Bitmap src, int dx, int dy, int w, int h)
229    {
230       if(src && src.width && src.height)
231       {
232          int x,y,sx,sy;
233          for(x = 0,sx = dx; x < w; x += src.width, sx += src.width)
234             for(y = 0, sy = dy; y < h; y += src.height,sy += src.height)
235                Blit(src, sx, sy, 0,0, Min(src.width, w - x), Min(src.height, h - y));
236       }
237    }
238
239    void HTile(Bitmap src, int dx, int dy, int w, int h)
240    {
241       if(src && src.width && src.height)
242       {
243          int x,sx;
244          for(x = 0, sx = dx; x < w; x += src.width, sx += src.width)
245             Stretch(src, sx, dy, 0, 0, Min(src.width, w - x), h, Min(src.width, w - x), src.height);
246       }
247    }
248
249    void VTile(Bitmap src, int dx, int dy, int w, int h)
250    {
251       if(src && src.width && src.height)
252       {
253          int y,sy;
254          for(y = 0, sy = dy; y < h; y += src.height, sy += src.height)
255             Stretch(src, dx, sy, 0,0, w, Min(src.height, h - y), src.width, Min(src.height, h - y));
256       }
257    }
258
259    void FilterHTile(Bitmap src, int dx, int dy, int w, int h)
260    {
261       if(src && src.width && src.height)
262       {
263          int x,sx;
264          for(x = 0, sx = dx; x < w; x += src.width, sx += src.width)
265             Filter(src, sx, dy, 0, 0, Min(src.width, w - x), h, Min(src.width, w - x), src.height);
266       }
267    }
268
269    void FilterVTile(Bitmap src, int dx, int dy, int w, int h)
270    {
271       if(src && src.width && src.height)
272       {
273          int y,sy;
274          for(y = 0, sy = dy; y < h; y += src.height, sy += src.height)
275             Filter(src, dx, sy, 0,0, w, Min(src.height, h - y), src.width, Min(src.height, h - y));
276       }
277    }
278
279    void WriteText(int x, int y, char * text, int len)
280    {
281       if(text)
282          driver.WriteText(display, this, x,y, text, len);
283    }
284
285    void TextExtent(char * text, int len, int * width, int * height)
286    {
287       driver.TextExtent(display, this, text, len, width, height);
288    }
289
290    void WriteTextf(int x, int y, char * format, ...)
291    {
292       if(format)
293       {
294          char text[MAX_F_STRING];
295          va_list args;
296          va_start(args, format);
297          vsnprintf(text, sizeof(text), format, args);
298          text[sizeof(text)-1] = 0;
299          driver.WriteText(display, this, x,y, text, strlen(text));
300          va_end(args);
301       }
302    }
303
304    void CenterTextf(int x, int y, char * format, ...)
305    {
306       if(format)
307       {
308          char text[MAX_F_STRING];
309          va_list args;
310          int len;
311          int w, h;
312          va_start(args, format);
313          vsnprintf(text, sizeof(text), format, args);
314          text[sizeof(text)-1] = 0;
315          len = strlen(text);
316
317          driver.TextExtent(display, this, text, len, &w, &h);
318          driver.WriteText(display, this, x - w/2, y, text, len);
319          va_end(args);
320       }
321    }
322
323    void WriteTextDots(Alignment alignment, int x, int y, int width, char * text, int len)
324    {
325       int w, h;
326
327       TextExtent(text, len, &w, &h);
328       if(w < width)
329       {
330          if(alignment == right)
331             x += width - w - 1;
332          else if(alignment == center)
333             x += (width - w) / 2;
334          WriteText(x, y, text, len);
335       }
336       else
337       {
338          int c;
339          int dw, dh;
340          int current;
341          int totalW = 0;
342          int nb;
343          char ch;
344
345          TextExtent(".", 1, &dw, &dh);
346          current = 3 * dw;
347          #define UTF8_NUM_BYTES(x)  (__extension__({ byte b = x; (b & 0x80 && b & 0x40) ? ((b & 0x20) ? ((b & 0x10) ? 4 : 3) : 2) : 1; }))
348          for(c = 0; (ch = text[c]); c += nb)
349          {
350             nb = UTF8_NUM_BYTES(ch);
351             TextExtent(text+c, nb, &w, &h);
352             current += w;
353             if(current > width)
354                break;
355             totalW += w;
356          }
357          WriteText(x, y, text, c);
358          //TextExtent(text, c, &totalW, &h);
359          x += totalW;
360          WriteText(x, y, "...", 3);
361       }
362    }
363
364    void WriteTextDotsf(Alignment alignment, int x, int y, int width, char * format, ...)
365    {
366       if(format)
367       {
368          char text[MAX_F_STRING];
369          va_list args;
370          va_start(args, format);
371          vsnprintf(text, sizeof(text), format, args);
372          text[sizeof(text)-1] = 0;
373          WriteTextDots(alignment, x,y, width, text, strlen(text));
374          va_end(args);
375       }
376    }
377    
378    void Bevel(bool inner, int x, int y, int w, int h)
379    {
380       ColorAlpha foreground = this.foreground;
381
382       SetForeground(inner ? Color { 128,128,128 } : formColor);
383       HLine(x,   x+w - 2, y);
384       VLine(y+1, y+h - 2, x);
385
386       SetForeground(inner ? Color { 64,64,64 } : white);
387       HLine(x+1, x+w-3, y+1);
388       VLine(y+2, y+h-3, x+1);
389
390       SetForeground(inner ? formColor : Color { 128,128,128 } );
391       HLine(x+1, x+w-2, y + h -2);
392       VLine(y+1, y+h-3, x + w - 2);
393
394       SetForeground(inner ? white : Color { 64,64,64 });
395       HLine(x, x+w-1, y + h - 1);
396       VLine(y, y+h-2, x + w - 1);
397
398       SetForeground(foreground);
399    }
400
401    void ThinBevel(bool inner, int x, int y, int w, int h)
402    {
403       SetForeground(inner ? Color { 128,128,128 } : white);
404       HLine(x,   x+w - 2, y);
405       VLine(y+1, y+h - 2, x);
406       SetForeground(inner ? white : Color { 128,128,128 });
407       HLine(x, x+w-1, y + h - 1);
408       VLine(y, y+h-2, x + w - 1);
409    }
410
411    void Gradient(ColorKey * keys, int numKeys, float smoothness, GradientDirection direction, int x1, int y1, int x2, int y2)
412    {
413       if(x2 >= box.left && x1 <= box.right && y2 >= box.top && y1 <= box.bottom)
414       {
415          ColorKey * key = keys, * nextKey = keys;
416          int keyNum = 0, nextKeyNum = 0;
417          int height = (direction == horizontal) ? ((x2 - x1) + 1) : ((y2 - y1) + 1);
418          float inc = 1.0f/(height-1);
419          float percent = 0;
420          int start;
421          ColorAlpha color = 0;
422          int firstPixel = (direction == horizontal) ? x1 : y1;
423          int lastPixel = (direction == horizontal) ? x2 : y2;
424          int boxLeft = (direction == horizontal) ? box.left : box.top;
425          int boxRight = (direction == horizontal) ? box.right : box.bottom;
426          int c;
427
428          // Clip it
429          if(boxLeft > firstPixel)
430          {
431             percent = (boxLeft - firstPixel) * inc;
432             firstPixel = boxLeft;            
433          }
434          if(boxRight < lastPixel)
435             lastPixel = boxRight;
436
437          for(c = start = firstPixel; c<=lastPixel; c++)
438          {
439             ColorAlpha newColor;
440
441             while(nextKey && percent > nextKey->percent)
442             {
443                key = nextKey; keyNum = nextKeyNum; 
444             
445                if(keyNum < numKeys - 1) 
446                { 
447                   nextKey = key + 1;
448                   nextKeyNum = keyNum + 1; 
449                }
450                else
451                   break;
452             }
453
454             if(nextKey && nextKey->percent != key->percent)
455             {
456                float scale = ease((percent - key->percent) / (nextKey->percent - key->percent), 
457                   smoothness, smoothness);
458                int cr = key->color.color.r;
459                int cg = key->color.color.g;
460                int cb = key->color.color.b;
461                int nr = nextKey->color.color.r;
462                int ng = nextKey->color.color.g;
463                int nb = nextKey->color.color.b;
464                int r = (int)(cr + (nr - cr) * scale);
465                int g = (int)(cg + (ng - cg) * scale);
466                int b = (int)(cb + (nb - cb) * scale);
467
468                r = Max(Min(r, 255),0);
469                g = Max(Min(g, 255),0);
470                b = Max(Min(b, 255),0);
471
472                newColor = Color { (byte)r, (byte)g, (byte)b };
473             }
474             else
475                newColor = key ? key->color : 0;
476
477             if(c == firstPixel || newColor != color)
478             {
479                if(c != firstPixel)
480                {
481                   SetBackground(color);
482
483                   if(direction == horizontal)
484                      Area(start,y1,c-1,y2);
485                   else
486                      Area(x1, start,x2, c-1);
487                   start = c;
488                }
489                color = newColor;
490             }
491             percent += inc;
492          }
493
494          SetBackground(color);
495          if(direction == horizontal)
496             Area(start,y1,c-1,y2);
497          else
498             Area(x1, start,x2, c-1);
499       }
500    }
501
502    // Properties
503    property ColorAlpha foreground
504    {
505       set
506       {
507          foreground = value;
508          driver.SetForeground(display, this, value);
509       }
510       get { return foreground; }
511    }
512
513    property ColorAlpha background
514    {
515       set
516       {
517          background = value;
518          driver.SetBackground(display, this, value);
519       }
520       get { return background; }
521    }
522
523    property ColorAlpha blitTint
524    {
525       set
526       {
527          blitTint = value;
528          driver.SetBlitTint(display, this, value);
529       }
530       get { return blitTint; }
531    }
532
533    property uint lineStipple
534    {
535       set
536       {
537          driver.LineStipple(display, this, value);
538       }
539    }
540
541    property Size size
542    {
543       get { value = { width, height }; }
544    }
545
546    /*property Box box
547    {
548       get
549       {
550          value = box;
551          if(display && display.flags.text)
552          {
553             value.left *= textCellW;
554             value.top *= textCellH;
555             value.right *= textCellW;
556             value.bottom *= textCellH;
557          }
558       }
559    }*/
560
561    property Display display
562    {
563       get { return display; }
564    }
565
566    property Font font
567    {
568       set
569       {
570          if(value && font != value)
571          {
572             driver.TextFont(display, this, value);
573             font = value;
574          }
575       }
576
577       get { return font; }
578    }
579
580    property bool textOpacity
581    {
582       set
583       {
584          textOpacity = value;
585          driver.TextOpacity(display, this, value);
586       }
587
588       get { return textOpacity; }
589    }
590
591    property byte drawingChar
592    {
593       set { driver.DrawingChar(display, this, value); }
594    }
595
596    property Box clipping
597    {
598       set { driver.Clip(display, this, value); }
599    }
600
601    // TODO: Make these functions obsolete
602    void SetForeground(ColorAlpha value)
603    {
604       foreground = value;
605       driver.SetForeground(display, this, value);
606    }
607
608    void SetBackground(ColorAlpha value)
609    {
610       background = value;
611       driver.SetBackground(display, this, value);
612    }
613
614    Color GetForeground(void)
615    {
616       return foreground;
617    }
618
619    Color GetBackground(void)
620    {
621       return background;
622    }
623
624    void LineStipple(uint value)
625    {
626       driver.LineStipple(display, this, value);
627    }
628
629    void GetSize(int * w, int * h)
630    {
631       if(w) *w = width;
632       if(h) *h = height;
633    }
634
635    void GetBox(Box value)
636    {
637       value = box;
638       if(display.flags.text)
639       {
640          box.left *= textCellW;
641          box.top *= textCellH;
642          box.right *= textCellW;
643          box.bottom *= textCellH;
644       }
645    }
646
647    Display GetDisplay(void)
648    {
649       return display;
650    }
651
652    void TextFont(Font value)
653    {
654       if(value && font != value)
655       {
656          driver.TextFont(display, this, value);
657          font = value;
658       }
659    }
660
661    Font GetFont(void)
662    {
663       return font;
664    }
665
666    bool GetTextOpacity(void)
667    {
668       return textOpacity;
669    }
670
671    void TextOpacity(bool value)
672    {
673       textOpacity = value;
674       driver.TextOpacity(display, this, value);
675    }
676
677    void DrawingChar(unsigned char value)
678    {
679       driver.DrawingChar(display, this, value);
680    }
681
682    void Clip(Box box)
683    {
684       driver.Clip(display, this, box);
685    }
686 };