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