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