mirror of
				https://gitlab.com/freepascal.org/lazarus/lazarus.git
				synced 2025-10-31 03:01:28 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			208 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			ObjectPascal
		
	
	
	
	
	
			
		
		
	
	
			208 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			ObjectPascal
		
	
	
	
	
	
| {
 | |
| Implements support for drawing to the LCL TCanvas
 | |
| 
 | |
| License: The same modified LGPL as the Free Pascal RTL
 | |
|          See the file COPYING.modifiedLGPL for more details
 | |
| 
 | |
| AUTHORS: Felipe Monteiro de Carvalho
 | |
| }
 | |
| unit fpvectorial2canvas;
 | |
| 
 | |
| {$ifdef fpc}
 | |
|   {$mode objfpc}{$h+}
 | |
| {$endif}
 | |
| 
 | |
| {$define USE_CANVAS_CLIP_REGION}
 | |
| {.$define DEBUG_CANVAS_CLIP_REGION}
 | |
| {$define USE_LCL_CANVAS}
 | |
| 
 | |
| {.$define FPVECTORIAL_DEBUG_DIMENSIONS}
 | |
| {.$define FPVECTORIAL_TOCANVAS_DEBUG}
 | |
| {.$define FPVECTORIAL_DEBUG_BLOCKS}
 | |
| {.$define FPVECTORIAL_AUTOFIT_DEBUG}
 | |
| {.$define FPVECTORIAL_SUPPORT_LAZARUS_1_6}
 | |
| // visual debugs
 | |
| {.$define FPVECTORIAL_TOCANVAS_ELLIPSE_VISUALDEBUG}
 | |
| {.$define FPVECTORIAL_RENDERINFO_VISUALDEBUG}
 | |
| 
 | |
| interface
 | |
| 
 | |
| uses
 | |
|   Classes, SysUtils, Math, TypInfo, contnrs, types,
 | |
|   // FCL-Image
 | |
|   fpcanvas, fpimage, fpwritebmp,
 | |
|   // lazutils
 | |
|   laz2_dom,
 | |
|   // LCL
 | |
|   lazutf8, lazregions,
 | |
|   Graphics, LCLIntf, LCLType, intfgraphics, graphtype, interfacebase,
 | |
|   // fpvectorial
 | |
|   fpvutils, fpvectorial;
 | |
| 
 | |
| type
 | |
| 
 | |
|   { TFPVCanvasRenderer }
 | |
| 
 | |
|   TFPVCanvasRenderer = class(TvRenderer)
 | |
|   public
 | |
|     procedure BeginRender(var ARenderInfo: TvRenderInfo; ADoDraw: Boolean); override;
 | |
|     procedure EndRender(var ARenderInfo: TvRenderInfo; ADoDraw: Boolean); override;
 | |
|     // TPath
 | |
|     procedure TPath_Render(var ARenderInfo: TvRenderInfo; ADoDraw: Boolean; APath: TPath); override;
 | |
|   end;
 | |
| 
 | |
| implementation
 | |
| 
 | |
| { TFPVCanvasRenderer }
 | |
| 
 | |
| procedure TFPVCanvasRenderer.BeginRender(var ARenderInfo: TvRenderInfo;
 | |
|   ADoDraw: Boolean);
 | |
| begin
 | |
| 
 | |
| end;
 | |
| 
 | |
| procedure TFPVCanvasRenderer.EndRender(var ARenderInfo: TvRenderInfo;
 | |
|   ADoDraw: Boolean);
 | |
| begin
 | |
| 
 | |
| end;
 | |
| 
 | |
| procedure TFPVCanvasRenderer.TPath_Render(var ARenderInfo: TvRenderInfo;
 | |
|   ADoDraw: Boolean; APath: TPath);
 | |
| var
 | |
|   ADest: TFPCustomCanvas absolute ARenderInfo.Canvas;
 | |
|   ADestX: Integer absolute ARenderInfo.DestX;
 | |
|   ADestY: Integer absolute ARenderInfo.DestY;
 | |
|   AMulX: Double absolute ARenderInfo.MulX;
 | |
|   AMulY: Double absolute ARenderInfo.MulY;
 | |
|   //
 | |
|   i: Integer;
 | |
|   j, n: Integer;
 | |
|   x1, y1, x2, y2: Integer;
 | |
|   pts: TPointsArray;
 | |
|   ACanvas: TCanvas absolute ARenderInfo.Canvas;
 | |
|   coordX, coordY: Integer;
 | |
|   curSegment: TPathSegment;
 | |
|   cur2DSegment: T2DSegment absolute curSegment;
 | |
|   lRect: TRect;
 | |
|   gv1, gv2: T2DPoint;
 | |
| begin
 | |
|   with APath do begin
 | |
|   ConvertPathToPolygons(APath, ADestX, ADestY, AMulX, AMulY, FPolyPoints, FPolyStarts);
 | |
|   x1 := MaxInt;
 | |
|   y1 := maxInt;
 | |
|   x2 := -MaxInt;
 | |
|   y2 := -MaxInt;
 | |
|   for i := 0 to High(FPolyPoints) do
 | |
|   begin
 | |
|     {$ifdef FPVECTORIAL_AUTOFIT_DEBUG}
 | |
|     if AutoFitDebug <> nil then AutoFitDebug.Add(Format('==[%d=%d]', [FPolyPoints[i].X, FPolyPoints[i].Y]));
 | |
|     {$endif}
 | |
|     x1 := min(x1, FPolyPoints[i].X);
 | |
|     y1 := min(y1, FPolyPoints[i].Y);
 | |
|     x2 := max(x2, FPolyPoints[i].X);
 | |
|     y2 := max(y2, FPolyPoints[i].Y);
 | |
|   end;
 | |
|   CalcEntityCanvasMinMaxXY_With2Points(ARenderInfo, x1, y1, x2, y2);
 | |
|   // Boundary rect of shape filled with a gradient
 | |
|   lRect := Rect(x1, y1, x2, y2);
 | |
| 
 | |
|   if ADoDraw then
 | |
|   begin
 | |
|     // (1) draw background only
 | |
|     ADest.Pen.Style := psClear;
 | |
|     if (Length(FPolyPoints) > 2) then
 | |
|       case Brush.Kind of
 | |
|         bkSimpleBrush:
 | |
|           if Brush.Style <> bsClear then
 | |
|           begin
 | |
|             if (Brush.Style = bsSolid) and (Length(FPolyStarts) > 1) then
 | |
|               // Non-contiguous polygon (polygon with "holes") --> use special procedure
 | |
|               // Disadvantage: it can only do solid fills!
 | |
|               APath.DrawPolygon(ARenderInfo, FPolyPoints, FPolyStarts, lRect)
 | |
|             else
 | |
|               {$IFDEF USE_LCL_CANVAS}
 | |
|               for i := 0 to High(FPolyStarts) do
 | |
|               begin
 | |
|                 j := FPolyStarts[i];
 | |
|                 if i = High(FPolyStarts) then
 | |
|                   n := Length(FPolyPoints) - j
 | |
|                 else
 | |
|                   n := FPolyStarts[i+1] - FPolyStarts[i]; // + 1;
 | |
|                 ACanvas.Polygon(@FPolyPoints[j], n, WindingRule = vcmNonZeroWindingRule);
 | |
|               end;
 | |
|               {$ELSE}
 | |
|               ADest.Polygon(FPolyPoints);
 | |
|               {$ENDIF}
 | |
|           end;
 | |
| 
 | |
|         bkHorizontalGradient,
 | |
|         bkVerticalGradient,
 | |
|         bkOtherLinearGradient:
 | |
|           begin
 | |
|             // calculate gradient vector
 | |
|             CalcGradientVector(gv1, gv2, lRect, ADestX, ADestY, AMulX, AMulY);
 | |
|             // Draw the gradient
 | |
|             DrawPolygonBrushLinearGradient(ARenderInfo, FPolyPoints, FPolyStarts, lRect, gv1, gv2);
 | |
|           end;
 | |
| 
 | |
|         bkRadialGradient:
 | |
|           DrawPolygonBrushRadialGradient(ARenderInfo, FPolyPoints, lRect);
 | |
|       end;  // case Brush.Kind of...
 | |
| 
 | |
|     // (2) draw border, take care of the segments with modified pen
 | |
|     ADest.Brush.Style := bsClear;               // We will paint no background
 | |
|     ApplyPenToCanvas(ARenderInfo, Pen);  // Restore pen
 | |
| 
 | |
|     PrepareForSequentialReading;
 | |
|     for j := 0 to Len - 1 do
 | |
|     begin
 | |
|       curSegment := TPathSegment(Next);
 | |
|       case curSegment.SegmentType of
 | |
|         stMoveTo:
 | |
|           begin
 | |
|             inc(i);
 | |
|             coordX := CoordToCanvasX(cur2DSegment.X, ADestX, AMulX);
 | |
|             coordY := CoordToCanvasY(cur2DSegment.Y, ADestY, AMulY);
 | |
|             ADest.MoveTo(coordX, coordY);
 | |
|           end;
 | |
|         st2DLineWithPen, st2DLine, st3DLine:
 | |
|           begin
 | |
|             coordX := CoordToCanvasX(cur2DSegment.X, ADestX, AMulX);
 | |
|             coordY := CoordToCanvasY(cur2DSegment.Y, ADestY, AMulY);
 | |
|             if curSegment.SegmentType = st2DLineWithPen then
 | |
|             begin
 | |
|               ADest.Pen.FPColor := AdjustColorToBackground(T2DSegmentWithPen(Cur2DSegment).Pen.Color, ARenderInfo);
 | |
|               ADest.Pen.Width := T2DSegmentWithPen(cur2DSegment).Pen.Width;
 | |
|               ADest.Pen.Style := T2DSegmentWithPen(cur2DSegment).Pen.Style;
 | |
|               ADest.LineTo(coordX, coordY);
 | |
|               ApplyPenToCanvas(ARenderInfo, Pen);
 | |
|             end else
 | |
|               ADest.LineTo(coordX, coordY);
 | |
|           end;
 | |
|         st2DBezier, st3DBezier, st2DEllipticalArc:
 | |
|           begin
 | |
|             coordX := CoordToCanvasX(T2DSegment(curSegment.Previous).X, ADestX, AMulX);
 | |
|             coordY := CoordToCanvasY(T2DSegment(curSegment.Previous).Y, ADestY, AMulY);
 | |
|             SetLength(pts, 1);
 | |
|             pts[0] := Point(coordX, coordY);
 | |
|             curSegment.AddToPoints(ADestX, ADestY, AMulX, AMulY, pts);
 | |
|             if Length(pts) > 0 then
 | |
|             begin
 | |
|               ADest.PolyLine(pts);
 | |
|               ADest.MoveTo(pts[High(pts)].X, pts[High(pts)].Y);
 | |
|             end;
 | |
|           end;
 | |
|       end;
 | |
|     end;
 | |
|   end;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| initialization
 | |
| 
 | |
| RegisterDefaultRenderer(TFPVCanvasRenderer);
 | |
| 
 | |
| end.
 | |
| 
 | 
