123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008 |
- MODULE WMGraphics; (** AUTHOR "TF"; PURPOSE "Generic Graphic Support"; *)
- IMPORT
- Kernel, Rectangles := WMRectangles, Raster, KernelLog, UTF8Strings, Strings, RasterScale := WMRasterScale,
- Codecs, Files, Streams;
- CONST
- (** Copy Modes *)
- ModeCopy* = RasterScale.ModeCopy; ModeSrcOverDst* = RasterScale.ModeSrcOverDst;
- (** Scale Modes *)
- ScaleBox* = RasterScale.ScaleBox; ScaleBilinear* = RasterScale.ScaleBilinear;
- (** Clip Modes *)
- ClipNone* = 0; ClipRect* = 1; (*ClipStencil* = 2;*)
- (** FontStyles *)
- FontBold* = 0; FontItalic* = 1;
- Black* = 0FFH; White* = LONGINT(0FFFFFFFFH); Transparent*=0H;
- Gray*=LONGINT(0777777FFH);
- Red* = LONGINT(0FF0000FFH);
- DarkRed* = LONGINT(08C0000FFH);
- Green* = 000FF00FFH; Blue* = 0FFFFH;
- Yellow* = LONGINT(0FFFF00FFH);
- Magenta* = LONGINT(0FF00FFFFH);
- Cyan* = 00FFFFFFH;
- Gold* = LONGINT(0FFD700FFH);
- TYPE
- Real* = REAL;
- Char32 = LONGINT;
- Point2d* = RECORD x*, y* : LONGINT END;
- Image* = OBJECT(Raster.Image)
- VAR
- key* : POINTER TO ARRAY OF CHAR;
- END Image;
- Rectangle* = Rectangles.Rectangle;
- Color* = LONGINT;
- GlyphSpacings* = RECORD
- bearing* : Rectangle;
- width*, height*, ascent*, descent* : LONGINT;
- dx*, dy* : LONGINT; (** Delta position where the bitmap returned by GetGlyphMap has to be placed relatively to
- x, y on the base line *)
- END;
- (* Bearings are the blank spaces left an right of a character.
- bearing.l is the left, bearing.r is the right, bearing.t top and bearing.b the bottom side - bearing of the character
- hadvance = bearing.l + width + bearing.r --> the distance to the next character on the line without --> kerning
- vadvance = bearing.t + height + bearing.b --> the baseline to baseline distance of two lines of this font
- When rendering a character at the position (x, y), y refers to the y position of the baseline, x refers to .
- --> Kerning pairs
- *)
- (* ascent is the height of the font above the base line in units of the destination canvas *)
- (* descent is the height of the font below the base line in units of the destination canvas *)
- (* basetobasedist is the suggested distance between two lines of this font *)
- Font* = OBJECT
- VAR
- ascent*, descent* : LONGINT;
- name* : ARRAY 256 OF CHAR;
- size* : LONGINT;
- style* : SET;
- PROCEDURE &Init*;
- END Init;
- PROCEDURE GetHeight*():LONGINT;
- BEGIN
- RETURN ascent + descent
- END GetHeight;
- PROCEDURE GetAscent*():LONGINT;
- BEGIN
- RETURN ascent
- END GetAscent;
- PROCEDURE GetDescent*():LONGINT;
- BEGIN
- RETURN descent
- END GetDescent;
- (* return TRUE if the font can render the character *)
- PROCEDURE HasChar*(char : Char32) : BOOLEAN;
- BEGIN
- RETURN FALSE
- END HasChar;
- (** Render an UTF8 string to a canvas *)
- PROCEDURE RenderString*(canvas : Canvas ; x, y : Real; CONST text : ARRAY OF CHAR);
- VAR i, len, code : LONGINT; g : GlyphSpacings;
- BEGIN
- len := LEN(text); i := 0;
- WHILE (i < len) & (text[i] # 0X) DO
- IF UTF8Strings.DecodeChar(text, i, code) THEN
- IF HasChar(code) THEN
- GetGlyphSpacings(code, g);
- RenderChar(canvas, x, y, code)
- ELSE
- FBGetGlyphSpacings(code, g);
- FBRenderChar(canvas, x, y, code)
- END;
- x := x + g.bearing.l + g.width + g.bearing.r
- ELSE INC(i) (* avoid endless loop *)
- END
- END
- END RenderString;
- (** Render an UTF8 string to a canvas *)
- PROCEDURE GetStringSize*(CONST text : ARRAY OF CHAR; VAR dx, dy : LONGINT);
- VAR i, len, code : LONGINT; g : GlyphSpacings;
- BEGIN
- len := LEN(text); i := 0; dx := 0; dy := GetHeight();
- WHILE (i < len) & (text[i] # 0X) DO
- IF UTF8Strings.DecodeChar(text, i, code) THEN
- IF HasChar(code) THEN GetGlyphSpacings(code, g);
- ELSE FBGetGlyphSpacings(code, g)
- END;
- dy := MAX(dy, g.height);
- dx := dx + g.bearing.l + g.width + g.bearing.r
- ELSE INC(i) (* avoid endless loop *)
- END
- END
- END GetStringSize;
- (** Render character char to canvas at x, y (baseline) *)
- PROCEDURE RenderChar*(canvas : Canvas ; x, y : Real; char : Char32);
- VAR g : GlyphSpacings; img : Image;
- BEGIN
- GetGlyphSpacings(char, g);
- GetGlyphMap(char, img);
- canvas.DrawImage(ENTIER(x + g.bearing.l) + g.dx, ENTIER(y - ascent) + g.dy, img, ModeSrcOverDst)
- END RenderChar;
- (** return a bitmap of character code *)
- PROCEDURE GetGlyphMap*(code : LONGINT; VAR map : Image);
- END GetGlyphMap;
- (** return spacing of character code *)
- PROCEDURE GetGlyphSpacings*(code : LONGINT; VAR glyphSpacings : GlyphSpacings);
- END GetGlyphSpacings;
- END Font;
- FontManager* = OBJECT
- PROCEDURE GetFont*(CONST name : ARRAY OF CHAR; size : LONGINT; style : SET) : Font;
- BEGIN
- RETURN NIL
- END GetFont;
- END FontManager;
- CanvasState* = RECORD
- clipMode : SET;
- clipRect : Rectangle;
- limits : Rectangle;
- dx, dy : LONGINT;
- font : Font;
- color : Color;
- END;
- Canvas* = OBJECT
- VAR
- limits*, (* The limits to which the clip Rect can be set *)
- clipRect* : Rectangle; (* The current clip rectangle *)
- dx*, dy* : LONGINT;
- color* : Color;
- clipMode* : SET;
- generator*: Strings.String;
- font- : Font;
- (** IF cs is NIL a new canvas state object is created for this canvas, otherwise cs is reused *)
- PROCEDURE SaveState*(VAR cs : CanvasState);
- BEGIN
- cs.clipMode := clipMode;
- cs.limits := limits;
- cs.dx := dx; cs.dy := dy;
- cs.font := font; cs.color := color;
- GetClipRect(cs.clipRect)
- END SaveState;
- (** Restore a previously saved canvas state *)
- PROCEDURE RestoreState*(CONST cs : CanvasState);
- BEGIN
- clipMode := cs.clipMode;
- limits := cs.limits;
- dx := cs.dx; dy := cs.dy;
- font := cs.font; color := cs.color;
- SetClipRect(cs.clipRect)
- END RestoreState;
- (** set the current clipping rectangle as the limit for new SetClipRect operations.
- ddx and ddy specify a coordinate shift. *)
- PROCEDURE ClipRectAsNewLimits*(ddx, ddy : LONGINT);
- BEGIN
- limits := clipRect;
- SetDelta(dx + ddx, dy + ddy)
- END ClipRectAsNewLimits;
- (** in user coordinates *)
- PROCEDURE SetClipRect*(rect : Rectangle);
- BEGIN
- INCL(clipMode, ClipRect);
- rect.r := MAX(rect.r, rect.l); rect.b := MAX(rect.b, rect.t);
- Rectangles.MoveRel(rect, dx, dy);
- Rectangles.ClipRect(rect, limits);
- clipRect := rect
- END SetClipRect;
- (** return the current Clipping rectangle in user coordinates; Clients may use this to avoid drawing that is
- clipped away for sure *)
- PROCEDURE GetClipRect*(VAR rect : Rectangle);
- BEGIN
- rect := clipRect;
- Rectangles.MoveRel(rect, -dx, -dy)
- END GetClipRect;
- (** *)
- PROCEDURE SetClipMode*(mode : SET);
- BEGIN
- clipMode := mode
- END SetClipMode;
- (** Set color for fonts *)
- PROCEDURE SetColor*(x : Color);
- BEGIN
- color := x
- END SetColor;
- PROCEDURE GetColor*() : Color;
- BEGIN
- RETURN color;
- END GetColor;
- (** Set the current font. IF f is NIL, GetFont will search for the system default font. *)
- PROCEDURE SetFont*(f: Font);
- BEGIN
- font := f
- END SetFont;
- (** Return the font currently set for this canvas. If no font is set, return the system default font. If no
- system default font is set, block until a default font is set *)
- PROCEDURE GetFont*():Font;
- BEGIN
- IF font = NIL THEN font := GetDefaultFont() END;
- RETURN font
- END GetFont;
- (** Draw an UTF8 String at position x, y (base line)
- The currently set font and color is used
- *)
- PROCEDURE DrawString*(x, y: LONGINT; CONST text : ARRAY OF CHAR);
- BEGIN
- IF font # NIL THEN
- font.RenderString(SELF, x, y, text)
- END
- END DrawString;
- PROCEDURE SetLineWidth*(w : Real);
- BEGIN
- (* Dummy. But is implemented in WMGraphicsGfx *)
- END SetLineWidth;
- (** draw a line within the current clipping rectangle *)
- (** Override for improved speed *)
- PROCEDURE Line*(x0, y0, x1, y1 : LONGINT; color : Color; mode : LONGINT);
- VAR t, xi, mi, xf, mf, dt2, ds2 : LONGINT; r: Rectangles.Rectangle;
- (* clipped bresenham algorithm according to
- Bresenham's Line Generation Algorithm with Built-in Clipping, Yevgeny P. Kuzmin, 1995
- *)
- PROCEDURE ClippedLine(x0,y0,x1,y1: LONGINT; r: Rectangle; color : Color; mode : LONGINT);
- VAR
- dsx,dsy,stx,sty,xd,yd,dx2,dy2,rem,term,e: LONGINT;
- tmp: HUGEINT;
- rev,setx: BOOLEAN;
- BEGIN
- (* standardization && trivial reject *)
- IF( x1 - x0 > 0 ) THEN
- IF ( x0 > r.r) OR (x1 < r.l ) THEN RETURN END;
- stx := 1;
- ELSE
- IF ( x1 > r.r) OR ( x0 < r.l ) THEN RETURN END;
- stx := -1;
- x0 :=-x0; x1 :=-x1;
- r.l :=-r.l; r.r :=-r.r;
- Swap(r.l,r.r);
- END;
- IF ( y1 - y0 > 0 ) THEN
- IF ( y0 > r.b) OR (y1 < r.t ) THEN RETURN END;
- sty:=1;
- ELSE
- IF ( y1 > r.b) OR (y0 < r.t ) THEN RETURN END;
- sty :=-1;
- y0 :=-y0; y1 :=-y1;
- r.t :=-r.t; r.b :=-r.b;
- Swap(r.t,r.b);
- END;
- dsx := x1-x0; dsy := y1-y0;
- IF ( dsx < dsy ) THEN
- rev := TRUE;
- Swap(x0,y0); Swap(x1,y1); Swap(dsx,dsy);
- Swap(r.l,r.t); Swap(r.r,r.b); Swap(stx,sty);
- ELSE
- rev := FALSE;
- END;
- (* Bresenham's set up *)
- dx2 := 2*dsx; dy2 := 2*dsy;
- xd := x0; yd :=y0;
- e := 2*dsy-dsx; term := x1;
- setx := TRUE;
- IF (y0 < r.t) THEN
- (* window horizontal entry *)
- tmp := HUGEINT(dx2) *(r.t-y0)-dsx;
- INC(xd,LONGINT(tmp DIV dy2));
- rem := LONGINT(tmp MOD dy2);
- IF ( xd>r.r ) THEN RETURN END;
- IF ( xd+1>=r.l ) THEN
- yd := r.t; DEC(e,rem+dsx);
- IF (rem>0 ) THEN INC(xd); INC(e,dy2) END;
- setx := FALSE;
- END;
- END;
- IF setx & ( x0 < r.l ) THEN
- (* window vertical entry *)
- tmp := HUGEINT(dy2) * (r.l-x0);
- INC(yd, LONGINT(tmp DIV dx2));
- rem := LONGINT(tmp MOD dx2);
- IF ( yd>r.b) OR (yd=r.b) & (rem>=dsx) THEN RETURN END;
- xd :=r.l; INC(e,rem);
- IF( rem>=dsx ) THEN INC(yd); DEC(e,dx2) END;
- END;
- IF ( y1 > r.b ) THEN
- (* window exit *)
- tmp := HUGEINT(dx2)*(r.b-y0)+dsx;
- term := x0+LONGINT(tmp DIV dy2);
- rem := LONGINT(tmp MOD dy2);
- IF ( rem=0 ) THEN DEC(term) END;
- END;
- IF ( term>r.r) THEN term := r.r; END;
- INC(term);
- IF ( sty =-1 ) THEN yd := -yd END;
- (* reverse transformation *)
- IF ( stx =-1 ) THEN xd := -xd; term := -term; END;
- DEC(dx2,dy2);
- (* Bresenham's line drawing *)
- IF rev THEN
- WHILE ( xd # term ) DO
- SetPixel(yd, xd, color, mode);
- IF ( e >= 0 ) THEN INC(xd, stx); INC(yd, sty); DEC(e,dx2)
- ELSE INC(xd, stx); INC(e, dy2);
- END;
- END;
- ELSE
- WHILE ( xd # term ) DO (* Bresenham's line drawing *)
- SetPixel(xd,yd, color, mode);
- IF ( e >= 0 ) THEN INC(xd, stx); INC(yd, sty); DEC(e,dx2)
- ELSE INC(xd, stx); INC(e, dy2);
- END;
- END;
- END;
- END ClippedLine;
- BEGIN
- IF y0 = y1 THEN (* horizontal case *)
- IF x0 > x1 THEN t := x0; x0 := x1; x1 := t END;
- Fill(Rectangles.MakeRect(x0, y0, x1 + 1, y0 + 1), color, mode)
- ELSIF x0 = x1 THEN (* vertical case *)
- IF y0 > y1 THEN t := y0; y0 := y1; y1 := t END;
- Fill(Rectangles.MakeRect(x0, y0, x0 + 1, y1 + 1), color, mode)
- ELSE (* general case *)
- GetClipRect(r);
- ClippedLine(x0,y0,x1,y1,r,color,mode);
- (*
- IF ABS(y1 - y0) > ABS(x1 - x0) THEN
- IF y0 > y1 THEN t := y0; y0 := y1; y1 := t; t := x0; x0 := x1; x1 := t END;
- xi := x0; xf := y0 - y1; mi := (x1 - x0) DIV (y1 - y0); mf := 2 * ( (x1 - x0) MOD (y1 - y0)); dt2 := 2 * (y1 - y0);
- GetClipRect(r);
- IF y0 < r.t THEN
- (* this loop should be replaced by a closed expression *)
- FOR t := y0 TO r.t-1 DO
- INC(xi, mi); INC(xf, mf);
- IF xf > 0 THEN INC(xi); DEC(xf, dt2) END
- END;
- y0 := r.t;
- END;
- IF y1 > r.b THEN
- y1 := r.b;
- END;
- FOR t := y0 TO y1 DO
- SetPixel(xi, t, color, mode);
- INC(xi, mi); INC(xf, mf);
- IF xf > 0 THEN INC(xi); DEC(xf, dt2) END
- END
- ELSE
- IF x0 > x1 THEN t := y0; y0 := y1; y1 := t; t := x0; x0 := x1; x1 := t END;
- xi := y0; xf := x0 - x1; mi := (y1 - y0) DIV (x1 - x0); mf := 2 * ( (y1 - y0) MOD (x1 - x0)); dt2 := 2 * (x1 - x0);
- GetClipRect(r);
- IF x0 < r.l THEN
- (* this loop should be replaced by a closed expression *)
- FOR t := x0 TO r.l-1 DO
- INC(xi, mi); INC(xf, mf);
- IF xf > 0 THEN INC(xi); DEC(xf, dt2) END
- END;
- x0 := r.l;
- END;
- IF x1 > r.r THEN
- x1 := r.r;
- END;
- FOR t := x0 TO x1 DO
- SetPixel(t, xi, color, mode);
- INC(xi, mi); INC(xf, mf);
- IF xf > 0 THEN INC(xi); DEC(xf, dt2) END
- END
- END
- *)
- END
- END Line;
- PROCEDURE LineReal*(x0, y0, x1, y1 : Real; color : Color; mode : LONGINT);
- BEGIN
- Line(ENTIER(x0),ENTIER(y0),ENTIER(x1),ENTIER(y1),color,mode);
- END LineReal;
- (** set a pixel within the current clipping rectangle *)
- PROCEDURE SetPixel*(x, y : LONGINT; color : Color; mode : LONGINT);
- BEGIN
- Fill(Rectangles.MakeRect(x, y, x + 1, y + 1), color, mode)
- END SetPixel;
- (** fill a rectangle within the current clipping rectangle *)
- PROCEDURE Fill*(rect : Rectangle; color : Color; mode : LONGINT);
- END Fill;
- (** fill a polygon given by points *)
- PROCEDURE FillPolygonFlat*(CONST points : ARRAY OF Point2d; nofPoints : LONGINT; color : Color; mode : LONGINT);
- END FillPolygonFlat;
- PROCEDURE FillPolygonCB*(CONST points : ARRAY OF Point2d; nofPoints : LONGINT; callBack : FillLineCallBack);
- END FillPolygonCB;
- PROCEDURE PolyLine*(CONST points : ARRAY OF Point2d; nofPoints : LONGINT; closed : BOOLEAN; color : Color; mode : LONGINT);
- VAR i : LONGINT;
- BEGIN
- FOR i := 1 TO nofPoints - 1 DO
- Line(points[i-1].x, points[i-1].y, points[i].x, points[i].y, color, mode)
- END;
- IF closed THEN
- Line(points[nofPoints-1].x, points[nofPoints-1].y, points[0].x, points[0].y, color, mode)
- END
- END PolyLine;
- (** draw an image within the current clipping rectangle *)
- PROCEDURE DrawImage*(x, y: LONGINT; image: Raster.Image; mode : LONGINT);
- END DrawImage;
- PROCEDURE ScaleImage*(src : Raster.Image; sr, dr : Rectangle; copyMode, scaleMode : LONGINT);
- END ScaleImage;
- (** Set coordinate shift *)
- PROCEDURE SetDelta*(dx, dy: LONGINT);
- BEGIN
- SELF.dx := dx; SELF.dy := dy
- END SetDelta;
- (** Set the available range in the super drawing space *)
- PROCEDURE SetLimits*(r : Rectangle);
- BEGIN
- limits := r
- END SetLimits;
- (** Get the avalilable range in the super drawing space, like the range set but clipped *)
- PROCEDURE GetLimits*(): Rectangle;
- BEGIN
- RETURN limits
- END GetLimits;
- END Canvas;
- TYPE
- FillPosEntry = RECORD pos, next : LONGINT END;
- FillHeap = POINTER TO ARRAY OF FillPosEntry;
- FillLineCallBack* = PROCEDURE {DELEGATE} (canvas : Canvas; y, x0, x1 : LONGINT);
- CanvasGenerator* = PROCEDURE(img:Raster.Image):BufferCanvas;
- TYPE
- BufferCanvas* = OBJECT(Canvas)
- VAR img- : Raster.Image;
- bounds : Rectangle; (* real limiting img bounds *)
- (* filling *)
- fillHeap : FillHeap;
- heapSize, topHeap : LONGINT;
- height : LONGINT;
- edges : POINTER TO ARRAY OF LONGINT;
- PROCEDURE &New*(img : Raster.Image);
- BEGIN
- SELF.img := img;
- bounds := Rectangles.MakeRect(0, 0, img.width, img.height);
- SetLimits(bounds);
- clipRect := bounds;
- clipMode := { ClipRect };
- (* filling *)
- height := img.height; NEW(edges, height);
- SetFont(GetDefaultFont());
- generator:=Strings.NewString("WMGraphics.GenCanvas");
- END New;
- (* Not thread-safe!!! *)
- PROCEDURE GetImage*() : Raster.Image;
- BEGIN
- RETURN img;
- END GetImage;
- PROCEDURE SetLimits*(r : Rectangle);
- BEGIN
- r.r := MAX(r.r, r.l); r.b := MAX(r.t, r.b);
- Rectangles.ClipRect(r, bounds); SetLimits^(r)
- END SetLimits;
- (* PROCEDURE Line*(x0, y0, x1, y1 : LONGINT; color : Color; mode : LONGINT);
- BEGIN
- END Line; *)
- PROCEDURE Fill*(rect : Rectangle; color : Color; mode : LONGINT);
- VAR rm : Raster.Mode; pix : Raster.Pixel;
- BEGIN
- (* convert to super coordinates *)
- Rectangles.MoveRel(rect, dx, dy);
- IF ClipRect IN clipMode THEN Rectangles.ClipRect(rect, clipRect) END;
- Rectangles.ClipRect(rect, limits);
- IF ~Rectangles.RectEmpty(rect) THEN
- Raster.SetRGBA(pix, ((color DIV 65536) DIV 256) MOD 256, (color DIV 65536) MOD 256,
- (color DIV 256) MOD 256, color MOD 256);
- IF mode = ModeCopy THEN Raster.InitMode(rm, Raster.srcCopy) ELSE Raster.InitMode(rm, Raster.srcOverDst) END;
- Raster.Fill(SELF.img, rect.l, rect.t, rect.r, rect.b, pix, rm);
- END
- END Fill;
- (* Polygon filling *)
- (** fill a polygon given by points *)
- PROCEDURE FillPolygonFlat*(CONST points : ARRAY OF Point2d; nofPoints : LONGINT; color : Color; mode : LONGINT);
- VAR i : LONGINT;
- BEGIN
- IF nofPoints < 3 THEN RETURN END;
- ASSERT(nofPoints <= LEN(points));
- ClearHeap;
- FOR i := 1 TO nofPoints - 1 DO AddLine(points[i - 1].x, points[i - 1].y, points[i].x, points[i].y) END;
- AddLine(points[nofPoints - 1].x, points[nofPoints - 1].y, points[0].x, points[0].y);
- FillFlat(color, mode)
- END FillPolygonFlat;
- (** fill a polygon given by points *)
- PROCEDURE FillPolygonCB*(CONST points : ARRAY OF Point2d; nofPoints : LONGINT; callBack : FillLineCallBack);
- VAR i : LONGINT;
- BEGIN
- IF nofPoints < 3 THEN RETURN END;
- ASSERT(nofPoints <= LEN(points));
- ClearHeap;
- FOR i := 1 TO nofPoints - 1 DO AddLine(points[i - 1].x, points[i - 1].y, points[i].x, points[i].y) END;
- AddLine(points[nofPoints - 1].x, points[nofPoints - 1].y, points[0].x, points[0].y);
- FillCB(callBack)
- END FillPolygonCB;
- PROCEDURE ClearHeap;
- VAR i : LONGINT;
- BEGIN
- topHeap := 0;
- FOR i := 0 TO height - 1 DO edges[i] := 0 END;
- IF fillHeap = NIL THEN NEW(fillHeap, 1024); heapSize := 1024 END
- END ClearHeap;
- PROCEDURE NewFillPos(pos : LONGINT) : LONGINT;
- VAR newHeap : FillHeap;
- i : LONGINT;
- BEGIN
- INC(topHeap);
- IF topHeap >= heapSize THEN (* grow heap *)
- NEW(newHeap, heapSize * 2);
- FOR i := 0 TO heapSize - 1 DO newHeap[i] := fillHeap[i] END;
- heapSize := heapSize * 2;
- fillHeap := newHeap
- END;
- fillHeap[topHeap].pos := pos;
- fillHeap[topHeap].next := 0;
- RETURN topHeap
- END NewFillPos;
- PROCEDURE AddIntersection(y, pos : LONGINT);
- VAR new, cur : LONGINT;
- BEGIN
- IF (y < 0) OR (y >= height) THEN RETURN END;
- new := NewFillPos(pos);
- IF edges[y] = 0 THEN edges[y] := new
- ELSE
- cur := edges[y];
- IF fillHeap[cur].pos > pos THEN
- fillHeap[new].next := cur;
- edges[y] := new
- ELSE
- WHILE (fillHeap[cur].next # 0) & (fillHeap[fillHeap[cur].next].pos < pos) DO cur := fillHeap[cur].next END;
- fillHeap[new].next := fillHeap[cur].next;
- fillHeap[cur].next := new
- END;
- END;
- END AddIntersection;
- PROCEDURE AddLine(x0, y0, x1, y1 : LONGINT);
- VAR t, xi, xf, mi, mf, dt2 : LONGINT ;
- BEGIN
- IF (y0 = y1) THEN RETURN END;
- IF y0 > y1 THEN t := y0; y0 := y1; y1 := t; t := x0; x0 := x1; x1 := t END;
- xi := x0; xf := y0 - y1; mi := (x1 - x0) DIV (y1 - y0); mf := 2 * ( (x1 - x0) MOD (y1 - y0)); dt2 := 2 * (y1 - y0);
- FOR t := y0 TO y1 - 1 DO
- AddIntersection(t, xi);
- INC(xi, mi); INC(xf, mf);
- IF xf > 0 THEN INC(xi); DEC(xf, dt2) END
- END
- END AddLine;
- PROCEDURE FillFlat(color : Color; mode : LONGINT);
- VAR i, sp, cur : LONGINT;
- in : BOOLEAN;
- BEGIN
- FOR i := 0 TO height - 1 DO
- cur := edges[i];
- in := FALSE;
- WHILE cur # 0 DO
- in := ~in;
- IF in THEN sp := fillHeap[cur].pos
- ELSE Fill(Rectangles.MakeRect(sp, i, fillHeap[cur].pos, i + 1), color, mode)
- END;
- cur := fillHeap[cur].next
- END
- END
- END FillFlat;
- PROCEDURE FillCB(cb : FillLineCallBack);
- VAR i, sp, cur : LONGINT;
- in : BOOLEAN;
- BEGIN
- FOR i := 0 TO height - 1 DO
- cur := edges[i];
- in := FALSE;
- WHILE cur # 0 DO
- in := ~in;
- IF in THEN sp := fillHeap[cur].pos
- ELSE cb(SELF, i, sp, fillHeap[cur].pos)
- END;
- cur := fillHeap[cur].next
- END
- END
- END FillCB;
- PROCEDURE DrawImage*(x, y: LONGINT; img: Raster.Image; mode : LONGINT);
- VAR imgBounds : Rectangle;
- rm : Raster.Mode;
- dx, dy: LONGINT;
- BEGIN
- IF (img = NIL) OR (img.adr = NIL) THEN RETURN END;
- dx := SELF.dx; dy := SELF.dy; (* avoid race below *)
- imgBounds := Rectangles.MakeRect(0, 0, img.width, img.height);
- (* to super coordinates *)
- Rectangles.MoveRel(imgBounds, x + dx, y + dy);
- IF ClipRect IN clipMode THEN Rectangles.ClipRect(imgBounds, clipRect) END;
- Rectangles.ClipRect(imgBounds, limits);
- IF ~Rectangles.RectEmpty(imgBounds) THEN
- IF mode = ModeCopy THEN Raster.InitMode(rm, Raster.srcCopy) ELSE Raster.InitMode(rm, Raster.srcOverDst) END;
- Raster.SetRGBA(rm.col, (color DIV 1000000H) MOD 100H, (color DIV 10000H) MOD 100H,
- (color DIV 100H) MOD 100H, color MOD 100H);
- IF imgBounds.l - (x + dx) < 0 THEN
- KernelLog.String("Error...");
- KernelLog.String("x + dx = "); KernelLog.Int(x + dx, 4); KernelLog.Ln;
- KernelLog.String("x = "); KernelLog.Int(x, 4); KernelLog.Ln;
- KernelLog.String("dx = "); KernelLog.Int(dx, 4); KernelLog.Ln;
- KernelLog.String("clip = "); KernelLog.Int(clipRect.l, 4); KernelLog.Int(clipRect.t, 4);
- KernelLog.Int(clipRect.r, 4); KernelLog.Int(clipRect.b, 4);KernelLog.Ln;
- KernelLog.String("imgBounds = ");
- KernelLog.Int(imgBounds.l, 4); KernelLog.Int(imgBounds.t, 4); KernelLog.Int(imgBounds.r, 4); KernelLog.Int(imgBounds.b, 4);KernelLog.Ln;
- KernelLog.String("limits = "); KernelLog.Int(limits.l, 4); KernelLog.Int(limits.t, 4);
- KernelLog.Int(limits.r, 4); KernelLog.Int(limits.b, 4);KernelLog.Ln;
- RETURN
- END;
- Raster.Copy(img, SELF.img, imgBounds.l - (x + dx), imgBounds.t - (y + dy),
- imgBounds.r - imgBounds.l + (imgBounds.l - (x + dx)), imgBounds.b - imgBounds.t + (imgBounds.t - (y + dy)),
- imgBounds.l, imgBounds.t, rm);
- END;
- END DrawImage;
- PROCEDURE ScaleImage*(src : Raster.Image; sr , dr : Rectangle; copyMode, scaleMode : LONGINT);
- BEGIN
- Rectangles.MoveRel(dr, dx, dy);
- RasterScale.Scale(src, sr, img, dr, clipRect, copyMode, scaleMode);
- END ScaleImage;
- END BufferCanvas;
- VAR imgCache : Kernel.FinalizedCollection;
- searchName : ARRAY 128 OF CHAR;
- foundImg : Image;
- defaultFont : Font;
- fontManager : FontManager;
- fallbackFonts* : ARRAY 5 OF Font;
- nofFallbackFonts : LONGINT;
- CONST
- AlignLeft* = 0; AlignCenter* = 1; AlignRight* = 2;
- AlignTop* = 0; AlignBottom* = 2;
- PROCEDURE Swap(VAR a,b: LONGINT);
- VAR t: LONGINT;
- BEGIN
- t := a; a := b; b := t;
- END Swap;
- (* Tool Functions *)
- PROCEDURE MakeRectangle*(l, t, r, b: LONGINT):Rectangle;
- VAR result : Rectangle;
- BEGIN
- result.l := l; result.t := t; result.r := r; result.b := b; RETURN result
- END MakeRectangle;
- PROCEDURE ColorToRGBA*(color : Color; VAR r, g, b, a : LONGINT);
- BEGIN
- r := (color DIV 1000000H) MOD 100H;
- g := (color DIV 10000H) MOD 100H;
- b := (color DIV 100H) MOD 100H;
- a := color MOD 100H
- END ColorToRGBA;
- PROCEDURE RGBAToColor*(r, g, b, a: LONGINT): Color;
- BEGIN
- RETURN r * 1000000H + g * 10000H + b * 100H + a
- END RGBAToColor;
- PROCEDURE Dark*(color:Color):Color;
- VAR c:Color;
- BEGIN
- c := MAX(0, (color DIV 1000000H) MOD 100H-40H);
- c := 100H * c + MAX(0, (color DIV 10000H) MOD 100H - 40H);
- c := 100H * c +MAX(0, (color DIV 100H) MOD 100H-40H);
- c := 100H * c + color MOD 100H;
- RETURN LONGINT(c);
- END Dark;
- PROCEDURE Light*(color:Color):Color;
- VAR c:Color;
- BEGIN
- c := MIN(0FFH, (color DIV 1000000H) MOD 100H + 40H);
- c := 100H * c + MIN(0FFH, (color DIV 10000H) MOD 100H + 40H);
- c := 100H * c +MIN(0FFH, (color DIV 100H) MOD 100H + 40H);
- c := 100H * c + color MOD 100H;
- RETURN LONGINT(c);
- END Light;
- PROCEDURE CheckImage(obj: ANY; VAR cont: BOOLEAN);
- BEGIN
- IF obj IS Image THEN
- IF obj(Image).key # NIL THEN
- IF obj(Image).key^ = searchName THEN
- foundImg := obj(Image);
- cont := FALSE
- END
- END
- END
- END CheckImage;
- PROCEDURE GetExtension (CONST name : ARRAY OF CHAR;VAR ext: ARRAY OF CHAR);
- VAR i, j: LONGINT; ch: CHAR;
- BEGIN
- i := 0; j := 0;
- WHILE name[i] # 0X DO
- IF name[i] = "." THEN j := i+1 END;
- INC(i)
- END;
- i := 0;
- REPEAT
- ch := name[j]; ext[i] := ch; INC(i); INC(j)
- UNTIL (ch = 0X) OR (i = LEN(ext));
- ext[i-1] := 0X
- END GetExtension;
- (** loads an image and returns a BGRA8888 bitmap if successful, NIL otherwise.
- If shared is TRUE, the image will not be reloaded if it is already in memory.
- *)
- PROCEDURE LoadImage*(CONST name : ARRAY OF CHAR; shared : BOOLEAN): Image;
- VAR img : Image;
- res: WORD; w, h, x : LONGINT;
- decoder : Codecs.ImageDecoder;
- in : Streams.Reader;
- ext : ARRAY 16 OF CHAR;
- BEGIN
- IF name = "" THEN RETURN NIL END;
- BEGIN {EXCLUSIVE}
- IF shared THEN
- foundImg := NIL; COPY(name, searchName);
- imgCache.Enumerate(CheckImage);
- IF foundImg # NIL THEN RETURN foundImg END
- END;
- END;
- GetExtension(name, ext);
- Strings.UpperCase(ext);
- decoder := Codecs.GetImageDecoder(ext);
- IF decoder = NIL THEN
- KernelLog.String("No decoder found for "); KernelLog.String(ext); KernelLog.Ln;
- RETURN NIL
- END;
- in := Codecs.OpenInputStream(name);
- IF in # NIL THEN
- decoder.Open(in, res);
- IF res = 0 THEN
- decoder.GetImageInfo(w, h, x, x);
- NEW(img);
- Raster.Create(img, w, h, Raster.BGRA8888);
- decoder.Render(img);
- NEW(img.key, LEN(name)); COPY(name, img.key^);
- IF shared THEN imgCache.Add(img, NIL) END
- END
- END;
- RETURN img
- END LoadImage;
- PROCEDURE StoreImage*(img : Raster.Image; CONST name : ARRAY OF CHAR; VAR res : WORD);
- VAR encoder : Codecs.ImageEncoder;
- f : Files.File;
- w : Files.Writer;
- ext : ARRAY 16 OF CHAR;
- BEGIN
- res := -1;
- GetExtension(name, ext);
- Strings.UpperCase(ext);
- encoder := Codecs.GetImageEncoder(ext);
- IF encoder = NIL THEN
- KernelLog.String("No encoder found for "); KernelLog.String(ext); KernelLog.Ln;
- RETURN
- END;
- f := Files.New(name);
- IF f # NIL THEN
- Files.OpenWriter(w, f, 0);
- END;
- IF w # NIL THEN
- encoder.Open(w);
- encoder.WriteImage(img, res);
- Files.Register(f);
- END
- END StoreImage;
- (** Draw an UTF8 String in a rectangle *)
- PROCEDURE DrawStringInRect*(canvas : Canvas; rect : Rectangle; wrap : BOOLEAN; hAlign, vAlign : LONGINT;
- CONST text : ARRAY OF CHAR);
- VAR tw, th, xPos, yPos : LONGINT;
- font : Font;
- BEGIN
- font := canvas.GetFont();
- IF font # NIL THEN
- font.GetStringSize(text, tw, th);
- END;
- xPos := rect.l; yPos := rect.t + font.GetAscent();
- IF ~wrap THEN
- IF hAlign = AlignCenter THEN xPos := ((rect.l + rect.r) - tw) DIV 2
- ELSIF hAlign = AlignRight THEN xPos := rect.r - tw
- END;
- IF vAlign = AlignCenter THEN yPos := (rect.t + rect.b - font.GetDescent() - font.GetAscent() ) DIV 2 + font.GetAscent() ;
- ELSIF vAlign = AlignBottom THEN yPos := rect.b - font.GetDescent();
- END;
- canvas.DrawString(xPos, yPos, text);
- ELSE
- (* not implemented *)
- END
- END DrawStringInRect;
- PROCEDURE GenCanvas*(img:Raster.Image):BufferCanvas;
- VAR c:BufferCanvas;
- BEGIN
- NEW(c,img); RETURN c
- END GenCanvas;
- PROCEDURE InstallDefaultFont*(f : Font);
- BEGIN { EXCLUSIVE }
- defaultFont := f;
- fallbackFonts[0] := defaultFont
- END InstallDefaultFont;
- PROCEDURE GetDefaultFont*() : Font;
- BEGIN { EXCLUSIVE }
- AWAIT(defaultFont # NIL);
- RETURN defaultFont
- END GetDefaultFont;
- PROCEDURE InstallFontManager*(fm : FontManager);
- BEGIN { EXCLUSIVE }
- fontManager := fm;
- IF fontManager # NIL THEN
- fallbackFonts[1] := fontManager.GetFont("Single", 20, {});
- END
- END InstallFontManager;
- (*Workaround:
- If the font with a weight other than Bold is required, use a full name of the file (for ttf-fonts) with empty style.
- For example: GetFont( "IBMPlexSans-SemiBold", 24, {} );
- *)
- PROCEDURE GetFont*(CONST name : ARRAY OF CHAR; size : LONGINT; style : SET) : Font;
- VAR f : Font;
- BEGIN { EXCLUSIVE }
- f := NIL;
- IF fontManager # NIL THEN f := fontManager.GetFont(name, size, style) END;
- IF f = NIL THEN AWAIT(defaultFont # NIL); f := defaultFont END;
- RETURN f
- END GetFont;
- (** Render the fallback case of the character char to canvas at x, y (baseline) *)
- PROCEDURE FBRenderChar*(canvas : Canvas ; x, y : Real; char : Char32);
- VAR i, w, h : LONGINT; f : Font; found : BOOLEAN; str : ARRAY 16 OF CHAR; r: Rectangles.Rectangle;
- BEGIN
- i := 0; found := FALSE;
- WHILE ~found & (i < nofFallbackFonts) DO
- f := fallbackFonts[i];
- IF (f # NIL) & f.HasChar(char) THEN found := TRUE END;
- INC(i)
- END;
- IF f # NIL THEN f.RenderChar(canvas, x, y, char)
- ELSE
- f := GetDefaultFont();
- Strings.IntToStr(char,str); Strings.Concat("U", str, str);
- f.GetStringSize(str, w, h);
- r := Rectangles.MakeRect(ENTIER(x), ENTIER(y) - f.ascent, ENTIER(x) + w, ENTIER(y) + f.descent);
- canvas.Fill(r, LONGINT(0CCCC00FFH), ModeCopy);
- f.RenderString(canvas, x, y, str)
- END
- END FBRenderChar;
- (** return the fallback spacing of character code *)
- PROCEDURE FBGetGlyphSpacings*(code : LONGINT; VAR glyphSpacings : GlyphSpacings);
- VAR i : LONGINT; f : Font; found : BOOLEAN; str : ARRAY 16 OF CHAR;
- BEGIN
- i := 0; found := FALSE;
- WHILE ~found & (i < nofFallbackFonts) DO
- f := fallbackFonts[i];
- IF (f # NIL) & f.HasChar(code) THEN found := TRUE END;
- INC(i)
- END;
- IF f # NIL THEN f.GetGlyphSpacings(code, glyphSpacings)
- ELSE
- f := GetDefaultFont();
- Strings.IntToStr(code, str); Strings.Concat("U", str, str);
- glyphSpacings.bearing := Rectangles.MakeRect(0, 0, 0, 0);
- f.GetStringSize(str, glyphSpacings.width, glyphSpacings.height);
- glyphSpacings.ascent := f.ascent; glyphSpacings.descent := f.descent;
- glyphSpacings.dx := 0; glyphSpacings.dy := 0
- END
- END FBGetGlyphSpacings;
- (** Tools *)
- (* Return true if the alpha value at pos x, y in img is >= threshold. Returns false if x, y are out of image *)
- PROCEDURE IsBitmapHit*(x, y, threshold: LONGINT; img: Raster.Image) : BOOLEAN;
- VAR pix : Raster.Pixel;
- mode : Raster.Mode;
- BEGIN
- IF (img # NIL) & (x >= 0) & (y >= 0) & (x < img.width) & (y < img.height) THEN
- Raster.InitMode(mode, Raster.srcCopy);
- Raster.Get(img, x, y, pix, mode);
- RETURN (ORD(pix[Raster.a]) >= threshold)
- ELSE RETURN FALSE
- END
- END IsBitmapHit;
- PROCEDURE IsScaledBitmapHit*(x,y,w,h,threshold: LONGINT; img: Raster.Image): BOOLEAN;
- BEGIN
- RETURN IsBitmapHit(x*img.width DIV w, y*img.height DIV h, threshold,img);
- END IsScaledBitmapHit;
- PROCEDURE ClearCache*;
- BEGIN
- imgCache.Clear;
- END ClearCache;
- BEGIN
- nofFallbackFonts := 3;
- NEW(imgCache)
- END WMGraphics.
|