📄 jclgraphics.pas
字号:
I: Integer;
Run: PBGRA;
begin
Run := Line;
for I := 0 to N - 1 do
begin
CurrentLineR[I] := Run.R;
CurrentLineG[I] := Run.G;
CurrentLineB[I] := Run.B;
Inc(PByte(Run), Delta);
end;
end;
function ApplyContributors(N: Integer; Contributors: TContributors): TBGRA;
var
J: Integer;
RGB: TRGBInt;
Total,
Weight: Integer;
Pixel: Cardinal;
Contr: PContributor;
begin
RGB.R := 0;
RGB.G := 0;
RGB.B := 0;
Total := 0;
Contr := @Contributors[0];
for J := 0 to N - 1 do
begin
Weight := Contr.Weight;
Inc(Total, Weight);
Pixel := Contr.Pixel;
Inc(RGB.R, CurrentLineR[Pixel] * Weight);
Inc(RGB.G, CurrentLineG[Pixel] * Weight);
Inc(RGB.B, CurrentLineB[Pixel] * Weight);
Inc(Contr);
end;
if Total = 0 then
begin
Result.R := IntToByte(RGB.R shr 8);
Result.G := IntToByte(RGB.G shr 8);
Result.B := IntToByte(RGB.B shr 8);
end
else
begin
Result.R := IntToByte(RGB.R div Total);
Result.G := IntToByte(RGB.G div Total);
Result.B := IntToByte(RGB.B div Total);
end;
end;
// This is the actual scaling routine. Target must be allocated already with
// sufficient size. Source must contain valid data, Radius must not be 0 and
// Filter must not be nil.
procedure DoStretch(Filter: TBitmapFilterFunction; Radius: Single; Source, Target: TBitmap);
var
ScaleX, ScaleY: Single; // Zoom scale factors
I, J, K, N: Integer; // Loop variables
Center: Single; // Filter calculation variables
Width: Single;
Weight: Integer; // Filter calculation variables
Left, Right: Integer; // Filter calculation variables
Work: TBitmap;
ContributorList: TContributorList;
SourceLine, DestLine: PPixelArray;
DestPixel: PBGRA;
Delta, DestDelta: Integer;
SourceHeight, SourceWidth: Integer;
TargetHeight, TargetWidth: Integer;
begin
// shortcut variables
SourceHeight := Source.Height;
SourceWidth := Source.Width;
TargetHeight := Target.Height;
TargetWidth := Target.Width;
// create intermediate image to hold horizontal zoom
Work := TBitmap.Create;
try
Work.PixelFormat := pf32bit;
Work.Height := SourceHeight;
Work.Width := TargetWidth;
if SourceWidth = 1 then
ScaleX := TargetWidth / SourceWidth
else
ScaleX := (TargetWidth - 1) / (SourceWidth - 1);
if SourceHeight = 1 then
ScaleY := TargetHeight / SourceHeight
else
ScaleY := (TargetHeight - 1) / (SourceHeight - 1);
// pre-calculate filter contributions for a row
SetLength(ContributorList, TargetWidth);
// horizontal sub-sampling
if ScaleX < 1 then
begin
// scales from bigger to smaller Width
Width := Radius / ScaleX;
for I := 0 to TargetWidth - 1 do
begin
ContributorList[I].N := 0;
Center := I / ScaleX;
Left := Math.Floor(Center - Width);
Right := Math.Ceil(Center + Width);
SetLength(ContributorList[I].Contributors, Right - Left + 1);
for J := Left to Right do
begin
Weight := Round(Filter((Center - J) * ScaleX) * ScaleX * 256);
if Weight <> 0 then
begin
if J < 0 then
N := -J
else
if J >= SourceWidth then
N := SourceWidth - J + SourceWidth - 1
else
N := J;
K := ContributorList[I].N;
Inc(ContributorList[I].N);
ContributorList[I].Contributors[K].Pixel := N;
ContributorList[I].Contributors[K].Weight := Weight;
end;
end;
end;
end
else
begin
// horizontal super-sampling
// scales from smaller to bigger Width
for I := 0 to TargetWidth - 1 do
begin
ContributorList[I].N := 0;
Center := I / ScaleX;
Left := Math.Floor(Center - Radius);
Right := Math.Ceil(Center + Radius);
SetLength(ContributorList[I].Contributors, Right - Left + 1);
for J := Left to Right do
begin
Weight := Round(Filter(Center - J) * 256);
if Weight <> 0 then
begin
if J < 0 then
N := -J
else
if J >= SourceWidth then
N := SourceWidth - J + SourceWidth - 1
else
N := J;
K := ContributorList[I].N;
Inc(ContributorList[I].N);
ContributorList[I].Contributors[K].Pixel := N;
ContributorList[I].Contributors[K].Weight := Weight;
end;
end;
end;
end;
// now apply filter to sample horizontally from Src to Work
SetLength(CurrentLineR, SourceWidth);
SetLength(CurrentLineG, SourceWidth);
SetLength(CurrentLineB, SourceWidth);
for K := 0 to SourceHeight - 1 do
begin
SourceLine := Source.ScanLine[K];
FillLineCache(SourceWidth, SizeOf(TBGRA), SourceLine);
DestPixel := Work.ScanLine[K];
for I := 0 to TargetWidth - 1 do
with ContributorList[I] do
begin
DestPixel^ := ApplyContributors(N, ContributorList[I].Contributors);
// move on to next column
Inc(DestPixel);
end;
end;
// free the memory allocated for horizontal filter weights, since we need
// the structure again
for I := 0 to TargetWidth - 1 do
ContributorList[I].Contributors := nil;
ContributorList := nil;
// pre-calculate filter contributions for a column
SetLength(ContributorList, TargetHeight);
// vertical sub-sampling
if ScaleY < 1 then
begin
// scales from bigger to smaller height
Width := Radius / ScaleY;
for I := 0 to TargetHeight - 1 do
begin
ContributorList[I].N := 0;
Center := I / ScaleY;
Left := Math.Floor(Center - Width);
Right := Math.Ceil(Center + Width);
SetLength(ContributorList[I].Contributors, Right - Left + 1);
for J := Left to Right do
begin
Weight := Round(Filter((Center - J) * ScaleY) * ScaleY * 256);
if Weight <> 0 then
begin
if J < 0 then
N := -J
else
if J >= SourceHeight then
N := SourceHeight - J + SourceHeight - 1
else
N := J;
K := ContributorList[I].N;
Inc(ContributorList[I].N);
ContributorList[I].Contributors[K].Pixel := N;
ContributorList[I].Contributors[K].Weight := Weight;
end;
end;
end;
end
else
begin
// vertical super-sampling
// scales from smaller to bigger height
for I := 0 to TargetHeight - 1 do
begin
ContributorList[I].N := 0;
Center := I / ScaleY;
Left := Math.Floor(Center - Radius);
Right := Math.Ceil(Center + Radius);
SetLength(ContributorList[I].Contributors, Right - Left + 1);
for J := Left to Right do
begin
Weight := Round(Filter(Center - J) * 256);
if Weight <> 0 then
begin
if J < 0 then
N := -J
else
if J >= SourceHeight then
N := SourceHeight - J + SourceHeight - 1
else
N := J;
K := ContributorList[I].N;
Inc(ContributorList[I].N);
ContributorList[I].Contributors[K].Pixel := N;
ContributorList[I].Contributors[K].Weight := Weight;
end;
end;
end;
end;
// apply filter to sample vertically from Work to Target
SetLength(CurrentLineR, SourceHeight);
SetLength(CurrentLineG, SourceHeight);
SetLength(CurrentLineB, SourceHeight);
SourceLine := Work.ScanLine[0];
Delta := Integer(Work.ScanLine[1]) - Integer(SourceLine);
DestLine := Target.ScanLine[0];
DestDelta := Integer(Target.ScanLine[1]) - Integer(DestLine);
for K := 0 to TargetWidth - 1 do
begin
DestPixel := Pointer(DestLine);
FillLineCache(SourceHeight, Delta, SourceLine);
for I := 0 to TargetHeight - 1 do
with ContributorList[I] do
begin
DestPixel^ := ApplyContributors(N, ContributorList[I].Contributors);
Inc(Integer(DestPixel), DestDelta);
end;
Inc(SourceLine);
Inc(DestLine);
end;
// free the memory allocated for vertical filter weights
for I := 0 to TargetHeight - 1 do
ContributorList[I].Contributors := nil;
// this one is done automatically on exit, but is here for completeness
ContributorList := nil;
finally
Work.Free;
CurrentLineR := nil;
CurrentLineG := nil;
CurrentLineB := nil;
Target.Modified := True;
end;
end;
// Filter functions for TJclBitmap32
type
TPointRec = record
Pos: Integer;
Weight: Integer;
end;
TCluster = array of TPointRec;
TMappingTable = array of TCluster;
TFilterFunc = function(Value: Extended): Extended;
function NearestFilter(Value: Extended): Extended;
begin
if (Value > -0.5) and (Value <= 0.5) then
Result := 1
else
Result := 0;
end;
function LinearFilter(Value: Extended): Extended;
begin
if Value < -1 then
Result := 0
else
if Value < 0 then
Result := 1 + Value
else
if Value < 1 then
Result := 1 - Value
else
Result := 0;
end;
function SplineFilter(Value: Extended): Extended;
var
tt: Extended;
begin
Value := Abs(Value);
if Value < 1 then
begin
tt := Sqr(Value);
Result := 0.5 * tt * Value - tt + 2 / 3;
end
else
if Value < 2 then
begin
Value := 2 - Value;
Result := 1 / 6 * Sqr(Value) * Value;
end
else
Result := 0;
end;
function BuildMappingTable(DstWidth, SrcFrom, SrcWidth: Integer;
StretchFilter: TStretchFilter): TMappingTable;
const
FILTERS: array [TStretchFilter] of TFilterFunc =
(NearestFilter, LinearFilter, SplineFilter);
var
Filter: TFilterFunc;
FilterWidth: Extended;
Scale, OldScale: Extended;
Center: Extended;
Bias: Extended;
Left, Right: Integer;
I, J, K: Integer;
Weight: Integer;
begin
if SrcWidth = 0 then
begin
Result := nil;
Exit;
end;
Filter := FILTERS[StretchFilter];
if StretchFilter in [sfNearest, sfLinear] then
FilterWidth := 1
else
FilterWidth := 1.5;
SetLength(Result, DstWidth);
Scale := (DstWidth - 1) / (SrcWidth - 1);
if Scale < 1 then
begin
OldScale := Scale;
Scale := 1 / Scale;
FilterWidth := FilterWidth * Scale;
for I := 0 to DstWidth - 1 do
begin
Center := I * Scale;
Left := Floor(Center - FilterWidth);
Right := Ceil(Center + FilterWidth);
Bias := 0;
for J := Left to Right do
begin
Weight := Round(255 * Filter((Center - J) * OldScale) * OldScale);
if Weight <> 0 then
begin
Bias := Bias + Weight / 255;
K := Length(Result[I]);
SetLength(Result[I], K + 1);
Result[I][K].Pos := Constrain(J + SrcFrom, 0, SrcWidth - 1);
Result[I][K].Weight := Weight;
end;
end;
if (Bias > 0) and (Bias <> 1) then
begin
Bias := 1 / Bias;
for K := 0 to High(Result[I]) do
Result[I][K].Weight := Round(Result[I][K].Weight * Bias);
end;
end;
end
else
begin
FilterWidth := 1 / FilterWidth;
Scale := 1 / Scale;
for I := 0 to DstWidth - 1 do
begin
Center := I * Scale;
Left := Floor(Center - FilterWidth);
Right := Ceil(Center + FilterWidth);
for J := Left to Right do
begin
Weight := Round(255 * Filter(Center - J));
if Weight <> 0 then
begin
K := Length(Result[I]);
SetLength(Result[I], K + 1);
Result[I][K].Pos := Constrain(J + SrcFrom, 0, SrcWidth - 1);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -