source: trunk/Packages/FastGraphics/UGGraphics.pas

Last change on this file was 36, checked in by chronos, 6 years ago
  • Modified: Drawing using pen with connected lines instead of individual pixels.
  • Added: Negative image function for RGB8 format.
File size: 13.0 KB
Line 
1unit UGGraphics;
2
3{$mode delphi}{$H+}
4
5interface
6
7uses
8 Classes, SysUtils, Graphics, GraphType;
9
10type
11 // Possible fixed color types:
12 // 0..1, 0..3, 0..15, Byte, Word, Cardinal, Int64, record RGB, record RGBA, class
13 // Bit level addressing -> bit addressable memory -> slowdown
14 // Color with dynamic size
15 // Color with dynamic number of channels
16 // Representing pallete
17
18 { TGBrush }
19
20 TGBrush<TGColor> = class
21 Color: TGColor;
22 Canvas: TObject; // TGCanvas<TGColor>;
23 end;
24
25 { TGPen }
26
27 TGPen<TGColor> = class
28 Position: TPoint;
29 Color: TGColor;
30 Canvas: TObject; // TGCanvas<TGColor>;
31 procedure MoveTo(Pos: TPoint);
32 procedure LineTo(Pos: TPoint);
33 end;
34
35 { TGCanvas }
36
37 TGCanvas<TGColor> = class
38 Bitmap: TObject; // TGPixmap<TGColor>;
39 Pen: TGPen<TGColor>;
40 Brush: TGBrush<TGColor>;
41 constructor Create;
42 destructor Destroy; override;
43 end;
44
45 { TGPixmap }
46
47 TGPixmap<TGColor> = class
48 public
49 type
50 TGConvertColor = function (Color: TGColor): TColor of object;
51 TGConvertFromColor = function (Color: TColor): TGColor of object;
52 TGGetColor = function (Position: TPoint): TGColor of object;
53 PGColor = ^TGColor;
54 protected
55 FData: PByte;
56 FSize: TPoint;
57 FCanvas: TGCanvas<TGColor>;
58 FBytesPerLine: Integer;
59 FBytesPerPixel: Integer;
60 function GetPixel(X, Y: Integer): TGColor; virtual;
61 function GetSize: TPoint; virtual;
62 procedure SetPixel(X, Y: Integer; AValue: TGColor); virtual;
63 procedure SetSize(AValue: TPoint); virtual;
64 private
65 procedure CheckLimits(X, Y: Integer);
66 public
67 constructor Create;
68 destructor Destroy; override;
69 procedure PaintToCanvas(Canvas: TCanvas; ColorConvertFunc: TGConvertColor); overload;
70 procedure PaintToCanvas(Canvas: TCanvas; Rect: TRect; ColorConvertFunc: TGConvertColor); overload;
71 procedure PaintToBitmap(Bitmap: TBitmap; Pos: TPoint; ColorConvertFunc: TGConvertColor); overload;
72 procedure PaintToBitmap(Bitmap: TBitmap; Rect: TRect; ColorConvertFunc: TGConvertColor); overload;
73 procedure LoadFromCanvas(Canvas: TCanvas; ColorConvertFunc: TGConvertFromColor); overload;
74 procedure LoadFromBitmap(Bitmap: TBitmap; ColorConvertFunc: TGConvertFromColor);
75 procedure Fill(Color: TGColor); overload;
76 procedure Fill(Func: TGGetColor); overload;
77 procedure Mirror;
78 procedure Flip;
79 function GetDataSize: Int64; virtual;
80 property Canvas: TGCanvas<TGColor> read FCanvas;
81 property Pixels[X, Y: Integer]: TGColor read GetPixel write SetPixel;
82 property Size: TPoint read GetSize write SetSize;
83 end;
84
85{ TGPixmapBit }
86
87 TGPixmapBit<TGColor> = class(TGPixmap<TGColor>)
88 protected
89 function GetPixel(X, Y: Integer): TGColor; override;
90 procedure SetPixel(X, Y: Integer; AValue: TGColor); override;
91 private
92 FBitsPerPixel: Integer;
93 procedure BitMove(SourceBase: PByte; SourceOffset: Integer;
94 DestBase: PByte; DestOffset: Integer; Size: Integer);
95 function GetByteSize(BitSize: Integer): Integer;
96 procedure SetBitsPerPixel(AValue: Integer);
97 public
98 property BitsPerPixel: Integer read FBitsPerPixel write SetBitsPerPixel;
99 function GetDataSize: Int64; override;
100 end;
101
102 { TPixelPointer }
103
104 TPixelPointer = record
105 Base: Pointer;
106 Pixel: Pointer;
107 Line: Pointer;
108 BytesPerPixel: Integer;
109 BytesPerLine: Integer;
110 procedure NextLine; inline; // Move pointer to start of new base line
111 procedure NextPixel; inline; // Move pointer to next pixel
112 procedure SetXY(X, Y: Integer); inline; // Set pixel position relative to base
113 procedure SetX(X: Integer); inline; // Set horizontal pixel position relative to base
114 procedure Init(Bitmap: TRasterImage; BaseX: Integer = 0; BaseY: Integer = 0); inline; overload;
115 procedure Init(Base: Pointer; BytesPerLine, BytesPerPixel: Integer; BaseX: Integer = 0; BaseY: Integer = 0); inline; overload;
116 end;
117
118
119implementation
120
121{ TPixelPointer }
122
123procedure TPixelPointer.NextLine; inline;
124begin
125 Line := Pointer(Line) + BytesPerLine;
126 Pixel := Line;
127end;
128
129procedure TPixelPointer.NextPixel; inline;
130begin
131 Pixel := Pointer(Pixel) + BytesPerPixel;
132end;
133
134procedure TPixelPointer.SetXY(X, Y: integer); inline;
135begin
136 Line := Pointer(Base) + Y * BytesPerLine;
137 SetX(X);
138end;
139
140procedure TPixelPointer.SetX(X: Integer); inline;
141begin
142 Pixel := Pointer(Line) + X * BytesPerPixel;
143end;
144
145procedure TPixelPointer.Init(Bitmap: TRasterImage; BaseX: Integer = 0;
146 BaseY: Integer = 0); inline;
147begin
148 Init(Bitmap.RawImage.Data, Bitmap.RawImage.Description.BytesPerLine, Bitmap.RawImage.Description.BitsPerPixel shr 3, BaseX, BaseY);
149end;
150
151procedure TPixelPointer.Init(Base: Pointer; BytesPerLine, BytesPerPixel: Integer; BaseX: Integer = 0; BaseY: Integer = 0); inline; overload;
152begin
153 Self.BytesPerLine := BytesPerLine;
154 Self.BytesPerPixel := BytesPerPixel;
155 Self.Base := Pointer(Base + BaseX * BytesPerPixel + BaseY * BytesPerLine);
156 SetXY(0, 0);
157end;
158
159{ TGPixmap }
160
161function TGPixmap<TGColor>.GetPixel(X, Y: Integer): TGColor;
162begin
163 CheckLimits(X, Y);
164 Move(PByte(FData + X * FBytesPerPixel + Y * FBytesPerLine)^, Result, SizeOf(TGColor));
165end;
166
167procedure TGPixmap<TGColor>.SetPixel(X, Y: Integer; AValue: TGColor);
168begin
169 CheckLimits(X, Y);
170 Move(AValue, PByte(FData + X * FBytesPerPixel + Y * FBytesPerLine)^, SizeOf(TGColor));
171end;
172
173function TGPixmap<TGColor>.GetSize: TPoint;
174begin
175 Result := FSize;
176end;
177
178procedure TGPixmap<TGColor>.SetSize(AValue: TPoint);
179begin
180 if (FSize.X <> AValue.X) and (FSize.Y <> AValue.Y) then begin
181 FSize := AValue;
182 FBytesPerPixel := SizeOf(TGColor);
183 FBytesPerLine := AValue.X * FBytesPerPixel;
184 ReAllocMem(FData, FSize.X * FBytesPerLine);
185 end;
186end;
187
188constructor TGPixmap<TGColor>.Create;
189begin
190 Size := Point(0, 0);
191 FCanvas := TGCanvas<TGColor>.Create;
192 FCanvas.Bitmap := Self;
193end;
194
195destructor TGPixmap<TGColor>.Destroy;
196begin
197 Size := Point(0, 0);
198 FreeAndNil(FCanvas);
199end;
200
201procedure TGPixmap<TGColor>.CheckLimits(X, Y: Integer);
202begin
203 if (X < 0) or (X >= FSize.X) or (Y < 0) or (Y >= FSize.Y) then
204 raise Exception.Create('Position out of range');
205end;
206
207procedure TGPixmap<TGColor>.Fill(Color: TGColor);
208var
209 X, Y: Integer;
210begin
211 for Y := 0 to FSize.Y - 1 do
212 for X := 0 to FSize.X - 1 do
213 Pixels[X, Y] := Color;
214end;
215
216procedure TGPixmap<TGColor>.Fill(Func: TGGetColor);
217var
218 X, Y: Integer;
219begin
220 for Y := 0 to FSize.Y - 1 do
221 for X := 0 to FSize.X - 1 do
222 Pixels[X, Y] := Func(Point(X, Y));
223end;
224
225procedure TGPixmap<TGColor>.PaintToCanvas(Canvas: TCanvas;
226 ColorConvertFunc: TGConvertColor);
227var
228 X, Y: Integer;
229begin
230 try
231 Canvas.Lock;
232 for Y := 0 to FSize.Y - 1 do
233 for X := 0 to FSize.X - 1 do begin
234 Canvas.Pixels[X, Y] := ColorConvertFunc(Pixels[X, Y]);
235 end;
236 finally
237 Canvas.Unlock;
238 end;
239end;
240
241procedure TGPixmap<TGColor>.PaintToCanvas(Canvas: TCanvas; Rect: TRect;
242 ColorConvertFunc: TGConvertColor);
243var
244 X, Y: Integer;
245begin
246 try
247 Canvas.Lock;
248 for Y := Rect.Top to Rect.Bottom - 1 do
249 for X := Rect.Left to Rect.Right - 1 do
250 if (X >= 0) and (X < FSize.X) and (Y >= 0) and (Y < FSize.Y) then begin
251 Canvas.Pixels[X - Rect.Left, Y - Rect.Top] := ColorConvertFunc(Pixels[X, Y]);
252 end;
253 finally
254 Canvas.Unlock;
255 end;
256end;
257
258procedure TGPixmap<TGColor>.PaintToBitmap(Bitmap: TBitmap; Pos: TPoint;
259 ColorConvertFunc: TGConvertColor);
260var
261 X, Y: Integer;
262 DstPtr: TPixelPointer;
263 SrcPtr: TPixelPointer;
264begin
265 try
266 Bitmap.BeginUpdate(False);
267 DstPtr.Init(Bitmap);
268 SrcPtr.Init(FData, FBytesPerLine, FBytesPerPixel, Pos.X, Pos.Y);
269 for Y := 0 to Bitmap.Height - 1 do begin
270 for X := 0 to Bitmap.Width - 1 do begin
271 if ((X + Pos.X) >= 0) and ((X + Pos.X) < FSize.X) and
272 ((Y + Pos.Y) >= 0) and ((Y + Pos.Y) < FSize.Y) then begin
273 PInteger(DstPtr.Pixel)^ := ColorConvertFunc(PGColor(SrcPtr.Pixel)^);
274 end;
275 DstPtr.NextPixel;
276 SrcPtr.NextPixel;
277 end;
278 DstPtr.NextLine;
279 SrcPtr.NextLine;
280 end;
281 finally
282 Bitmap.EndUpdate(False);
283 end;
284end;
285
286procedure TGPixmap<TGColor>.PaintToBitmap(Bitmap: TBitmap; Rect: TRect;
287 ColorConvertFunc: TGConvertColor);
288var
289 X, Y: Integer;
290 DstPtr: TPixelPointer;
291 ZoomX: Single;
292 ZoomY: Single;
293 SrcX: Integer;
294 SrcY: Integer;
295begin
296 try
297 Bitmap.BeginUpdate(False);
298 DstPtr.Init(Bitmap);
299 ZoomX := Bitmap.Width / (Rect.Right - Rect.Left);
300 ZoomY := Bitmap.Height / (Rect.Bottom - Rect.Top);
301 for Y := 0 to Bitmap.Height - 1 do begin
302 SrcY := Trunc(Y / ZoomY + Rect.Top);
303 for X := 0 to Bitmap.Width - 1 do begin
304 SrcX := Trunc(X / ZoomX + Rect.Left);
305 if (SrcX >= 0) and (SrcX < FSize.X) and
306 (SrcY >= 0) and (SrcY < FSize.Y) then begin
307 PInteger(DstPtr.Pixel)^ := ColorConvertFunc(Pixels[SrcX, SrcY]);
308 end;
309 DstPtr.NextPixel;
310 end;
311 DstPtr.NextLine;
312 end;
313 finally
314 Bitmap.EndUpdate(False);
315 end;
316end;
317
318procedure TGPixmap<TGColor>.LoadFromCanvas(Canvas: TCanvas; ColorConvertFunc: TGConvertFromColor);
319var
320 X, Y: Integer;
321begin
322 try
323 Canvas.Lock;
324 for Y := 0 to FSize.Y - 1 do
325 for X := 0 to FSize.X - 1 do begin
326 Pixels[X, Y] := ColorConvertFunc(Canvas.Pixels[X, Y]);
327 end;
328 finally
329 Canvas.Unlock;
330 end;
331end;
332
333procedure TGPixmap<TGColor>.LoadFromBitmap(Bitmap: TBitmap; ColorConvertFunc: TGConvertFromColor);
334var
335 X, Y: Integer;
336 SrcPtr: TPixelPointer;
337 DstPtr: TPixelPointer;
338begin
339 try
340 Bitmap.BeginUpdate(False);
341 SrcPtr.Init(Bitmap);
342 DstPtr.Init(FData, FBytesPerLine, FBytesPerPixel);
343 for Y := 0 to FSize.Y - 1 do begin
344 for X := 0 to FSize.X - 1 do begin
345 if (X >= 0) and (X < FSize.X) and (Y >= 0) and (Y < FSize.Y) then
346 PGColor(DstPtr.Pixel)^ := ColorConvertFunc(PInteger(SrcPtr.Pixel)^);
347 SrcPtr.NextPixel;
348 DstPtr.NextPixel;
349 end;
350 SrcPtr.NextLine;
351 DstPtr.NextLine;
352 end;
353 finally
354 Bitmap.EndUpdate(False);
355 end;
356end;
357
358function TGPixmap<TGColor>.GetDataSize: Int64;
359begin
360 Result := FSize.Y * FBytesPerLine;
361end;
362
363procedure TGPixmap<TGColor>.Mirror;
364var
365 X, Y: Integer;
366 Color: TGColor;
367begin
368 for Y := 0 to FSize.Y - 1 do
369 for X := 0 to FSize.X div 2 - 1 do begin
370 Color := Pixels[X, Y];
371 Pixels[X, Y] := Pixels[Size.X - 1 - X, Y];
372 Pixels[Size.X - 1 - X, Y] := Color;
373 end;
374end;
375
376procedure TGPixmap<TGColor>.Flip;
377var
378 X, Y: Integer;
379 Color: TGColor;
380begin
381 for Y := 0 to FSize.Y div 2 - 1 do
382 for X := 0 to FSize.X - 1 do begin
383 Color := Pixels[X, Y];
384 Pixels[X, Y] := Pixels[X, Size.Y - 1 - Y];
385 Pixels[X, Size.Y - 1 - Y] := Color;
386 end;
387end;
388
389{ TGPen }
390
391procedure TGPen<TGColor>.MoveTo(Pos: TPoint);
392begin
393 Position := Pos;
394end;
395
396procedure TGPen<TGColor>.LineTo(Pos: TPoint);
397var
398 I: Integer;
399 Len: Integer;
400begin
401 Len := Trunc(Sqrt(Sqr(Position.X - Pos.X) + Sqr(Position.Y - Pos.Y)));
402 for I := 0 to Len - 1 do
403 ((Canvas as TGCanvas<TGColor>).Bitmap as TGPixmap<TGColor>).Pixels[Trunc(Position.X + I * (Pos.X - Position.X) / Len),
404 Trunc(Position.Y + I * (Pos.Y - Position.Y) / Len)] := Color;
405 Position := Pos;
406end;
407
408{ TGCanvas }
409
410constructor TGCanvas<TGCanvas>.Create;
411begin
412 Pen := TGPen<TGColor>.Create;
413 Pen.Canvas := Self;
414 Brush := TGBrush<TGColor>.Create;
415 Brush.Canvas := Self;
416end;
417
418destructor TGCanvas<TGCanvas>.Destroy;
419begin
420 FreeAndNil(Pen);
421 FreeAndNil(Brush);
422 inherited Destroy;
423end;
424
425{ TGPixmapBit }
426
427procedure TGPixmapBit<TGColor>.BitMove(SourceBase: PByte; SourceOffset: Integer;
428 DestBase: PByte; DestOffset: Integer; Size: Integer);
429var
430 Value: Byte;
431 Mask: Word;
432begin
433 if ((SourceOffset + Size) > 8) or ((DestOffset + Size) > 8) then
434 raise Exception.Create('Max. supported size for bit move is 8 bits.');
435 Mask := ((1 shl Size) - 1);
436 Value := (PByte(SourceBase)^ shr SourceOffset) and Mask;
437 DestBase^ := (DestBase^ and ($ff xor (Mask shl DestOffset))) or (Value shl DestOffset);
438end;
439
440function TGPixmapBit<TGColor>.GetPixel(X, Y: Integer): TGColor;
441var
442 Offset: Integer;
443begin
444 CheckLimits(X, Y);
445 Offset := (X + Y * FSize.X) * BitsPerPixel;
446 BitMove(FData + (Offset shr 3), Offset and 7, @Result, 0, BitsPerPixel);
447end;
448
449procedure TGPixmapBit<TGColor>.SetPixel(X, Y: Integer; AValue: TGColor);
450var
451 Offset: Integer;
452begin
453 CheckLimits(X, Y);
454 Offset := (X + Y * FSize.X) * BitsPerPixel;
455 BitMove(@AValue, 0, FData + (Offset shr 3), Offset and 7, BitsPerPixel);
456end;
457
458function TGPixmapBit<TGColor>.GetDataSize: Int64;
459begin
460 Result := GetByteSize(FSize.X * FSize.Y * BitsPerPixel);
461end;
462
463function TGPixmapBit<TGColor>.GetByteSize(BitSize: Integer): Integer;
464begin
465 Result := (((BitSize - 1) or 7) + 1) shr 3;
466end;
467
468procedure TGPixmapBit<TGColor>.SetBitsPerPixel(AValue: Integer);
469begin
470 if FBitsPerPixel <> AValue then begin
471 FBitsPerPixel := AValue;
472 end;
473end;
474
475end.
476
Note: See TracBrowser for help on using the repository browser.