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