ecere/gfx/Surface: Fixed a Valgrind complaint about conditional jump with -O2
[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          vsprintf(text, format, args);
298          driver.WriteText(display, this, x,y, text, strlen(text));
299          va_end(args);
300       }
301    }
302
303    void CenterTextf(int x, int y, char * format, ...)
304    {
305       if(format)
306       {
307          char text[MAX_F_STRING];
308          va_list args;
309          int len;
310          int w, h;
311
312          va_start(args, format);
313          vsprintf(text, format, args);
314          len = strlen(text);
315
316          driver.TextExtent(display, this, text, len, &w, &h);
317          driver.WriteText(display, this, x - w/2, y, text, len);
318          va_end(args);
319       }
320    }
321
322    void WriteTextDots(Alignment alignment, int x, int y, int width, char * text, int len)
323    {
324       int w, h;
325
326       TextExtent(text, len, &w, &h);
327       if(w < width)
328       {
329          if(alignment == right)
330             x += width - w - 1;
331          else if(alignment == center)
332             x += (width - w) / 2;
333          WriteText(x, y, text, len);
334       }
335       else
336       {
337          int c;
338          int dw, dh;
339          int current;
340          int totalW = 0;
341          int nb;
342          char ch;
343
344          TextExtent(".", 1, &dw, &dh);
345          current = 3 * dw;
346          #define UTF8_NUM_BYTES(x)  (__extension__({ byte b = x; (b & 0x80 && b & 0x40) ? ((b & 0x20) ? ((b & 0x10) ? 4 : 3) : 2) : 1; }))
347          for(c = 0; (ch = text[c]); c += nb)
348          {
349             nb = UTF8_NUM_BYTES(ch);
350             TextExtent(text+c, nb, &w, &h);
351             current += w;
352             if(current > width)
353                break;
354             totalW += w;
355          }
356          WriteText(x, y, text, c);
357          //TextExtent(text, c, &totalW, &h);
358          x += totalW;
359          WriteText(x, y, "...", 3);
360       }
361    }
362
363    void WriteTextDotsf(Alignment alignment, int x, int y, int width, char * format, ...)
364    {
365       if(format)
366       {
367          char text[MAX_F_STRING];
368          va_list args;
369          va_start(args, format);
370          vsprintf(text, format, args);
371          WriteTextDots(alignment, x,y, width, text, strlen(text));
372          va_end(args);
373       }
374    }
375    
376    void Bevel(bool inner, int x, int y, int w, int h)
377    {
378       ColorAlpha foreground = this.foreground;
379
380       SetForeground(inner ? Color { 128,128,128 } : formColor);
381       HLine(x,   x+w - 2, y);
382       VLine(y+1, y+h - 2, x);
383
384       SetForeground(inner ? Color { 64,64,64 } : white);
385       HLine(x+1, x+w-3, y+1);
386       VLine(y+2, y+h-3, x+1);
387
388       SetForeground(inner ? formColor : Color { 128,128,128 } );
389       HLine(x+1, x+w-2, y + h -2);
390       VLine(y+1, y+h-3, x + w - 2);
391
392       SetForeground(inner ? white : Color { 64,64,64 });
393       HLine(x, x+w-1, y + h - 1);
394       VLine(y, y+h-2, x + w - 1);
395
396       SetForeground(foreground);
397    }
398
399    void ThinBevel(bool inner, int x, int y, int w, int h)
400    {
401       SetForeground(inner ? Color { 128,128,128 } : white);
402       HLine(x,   x+w - 2, y);
403       VLine(y+1, y+h - 2, x);
404       SetForeground(inner ? white : Color { 128,128,128 });
405       HLine(x, x+w-1, y + h - 1);
406       VLine(y, y+h-2, x + w - 1);
407    }
408
409    void Gradient(ColorKey * keys, int numKeys, float smoothness, GradientDirection direction, int x1, int y1, int x2, int y2)
410    {
411       if(x2 >= box.left && x1 <= box.right && y2 >= box.top && y1 <= box.bottom)
412       {
413          ColorKey * key = keys, * nextKey = keys;
414          int keyNum = 0, nextKeyNum = 0;
415          int height = (direction == horizontal) ? ((x2 - x1) + 1) : ((y2 - y1) + 1);
416          float inc = 1.0f/(height-1);
417          float percent = 0;
418          int start;
419          ColorAlpha color = 0;
420          int firstPixel = (direction == horizontal) ? x1 : y1;
421          int lastPixel = (direction == horizontal) ? x2 : y2;
422          int boxLeft = (direction == horizontal) ? box.left : box.top;
423          int boxRight = (direction == horizontal) ? box.right : box.bottom;
424          int c;
425
426          // Clip it
427          if(boxLeft > firstPixel)
428          {
429             percent = (boxLeft - firstPixel) * inc;
430             firstPixel = boxLeft;            
431          }
432          if(boxRight < lastPixel)
433             lastPixel = boxRight;
434
435          for(c = start = firstPixel; c<=lastPixel; c++)
436          {
437             ColorAlpha newColor;
438
439             while(nextKey && percent > nextKey->percent)
440             {
441                key = nextKey; keyNum = nextKeyNum; 
442             
443                if(keyNum < numKeys - 1) 
444                { 
445                   nextKey = key + 1;
446                   nextKeyNum = keyNum + 1; 
447                }
448                else
449                   break;
450             }
451
452             if(nextKey && nextKey->percent != key->percent)
453             {
454                float scale = ease((percent - key->percent) / (nextKey->percent - key->percent), 
455                   smoothness, smoothness);
456                int cr = key->color.color.r;
457                int cg = key->color.color.g;
458                int cb = key->color.color.b;
459                int nr = nextKey->color.color.r;
460                int ng = nextKey->color.color.g;
461                int nb = nextKey->color.color.b;
462                int r = (int)(cr + (nr - cr) * scale);
463                int g = (int)(cg + (ng - cg) * scale);
464                int b = (int)(cb + (nb - cb) * scale);
465
466                r = Max(Min(r, 255),0);
467                g = Max(Min(g, 255),0);
468                b = Max(Min(b, 255),0);
469
470                newColor = Color { (byte)r, (byte)g, (byte)b };
471             }
472             else
473                newColor = key ? key->color : 0;
474
475             if(c == firstPixel || newColor != color)
476             {
477                if(c != firstPixel)
478                {
479                   SetBackground(color);
480
481                   if(direction == horizontal)
482                      Area(start,y1,c-1,y2);
483                   else
484                      Area(x1, start,x2, c-1);
485                   start = c;
486                }
487                color = newColor;
488             }
489             percent += inc;
490          }
491
492          SetBackground(color);
493          if(direction == horizontal)
494             Area(start,y1,c-1,y2);
495          else
496             Area(x1, start,x2, c-1);
497       }
498    }
499
500    // Properties
501    property ColorAlpha foreground
502    {
503       set
504       {
505          foreground = value;
506          driver.SetForeground(display, this, value);
507       }
508       get { return foreground; }
509    }
510
511    property ColorAlpha background
512    {
513       set
514       {
515          background = value;
516          driver.SetBackground(display, this, value);
517       }
518       get { return background; }
519    }
520
521    property ColorAlpha blitTint
522    {
523       set
524       {
525          blitTint = value;
526          driver.SetBlitTint(display, this, value);
527       }
528       get { return blitTint; }
529    }
530
531    property uint lineStipple
532    {
533       set
534       {
535          driver.LineStipple(display, this, value);
536       }
537    }
538
539    property Size size
540    {
541       get { value = { width, height }; }
542    }
543
544    /*property Box box
545    {
546       get
547       {
548          value = box;
549          if(display && display.flags.text)
550          {
551             value.left *= textCellW;
552             value.top *= textCellH;
553             value.right *= textCellW;
554             value.bottom *= textCellH;
555          }
556       }
557    }*/
558
559    property Display display
560    {
561       get { return display; }
562    }
563
564    property Font font
565    {
566       set
567       {
568          if(value && font != value)
569          {
570             driver.TextFont(display, this, value);
571             font = value;
572          }
573       }
574
575       get { return font; }
576    }
577
578    property bool textOpacity
579    {
580       set
581       {
582          textOpacity = value;
583          driver.TextOpacity(display, this, value);
584       }
585
586       get { return textOpacity; }
587    }
588
589    property byte drawingChar
590    {
591       set { driver.DrawingChar(display, this, value); }
592    }
593
594    property Box clipping
595    {
596       set { driver.Clip(display, this, value); }
597    }
598
599    // TODO: Make these functions obsolete
600    void SetForeground(ColorAlpha value)
601    {
602       foreground = value;
603       driver.SetForeground(display, this, value);
604    }
605
606    void SetBackground(ColorAlpha value)
607    {
608       background = value;
609       driver.SetBackground(display, this, value);
610    }
611
612    Color GetForeground(void)
613    {
614       return foreground;
615    }
616
617    Color GetBackground(void)
618    {
619       return background;
620    }
621
622    void LineStipple(uint value)
623    {
624       driver.LineStipple(display, this, value);
625    }
626
627    void GetSize(int * w, int * h)
628    {
629       if(w) *w = width;
630       if(h) *h = height;
631    }
632
633    void GetBox(Box value)
634    {
635       value = box;
636       if(display.flags.text)
637       {
638          box.left *= textCellW;
639          box.top *= textCellH;
640          box.right *= textCellW;
641          box.bottom *= textCellH;
642       }
643    }
644
645    Display GetDisplay(void)
646    {
647       return display;
648    }
649
650    void TextFont(Font value)
651    {
652       if(value && font != value)
653       {
654          driver.TextFont(display, this, value);
655          font = value;
656       }
657    }
658
659    Font GetFont(void)
660    {
661       return font;
662    }
663
664    bool GetTextOpacity(void)
665    {
666       return textOpacity;
667    }
668
669    void TextOpacity(bool value)
670    {
671       textOpacity = value;
672       driver.TextOpacity(display, this, value);
673    }
674
675    void DrawingChar(unsigned char value)
676    {
677       driver.DrawingChar(display, this, value);
678    }
679
680    void Clip(Box box)
681    {
682       driver.Clip(display, this, box);
683    }
684 };