Two simple functions to scale or rotate images #307
Replies: 11 comments 3 replies
-
Beta Was this translation helpful? Give feedback.
-
I've just noticed a couple of unnecessary Also. I think the transparency bleed is really only apparent when there is very significant scaling |
Beta Was this translation helpful? Give feedback.
-
I think it has something to do with the "outer color"; When sampling past the edge of the bitmap the resamplers need to sample "something". In my code I use |
Beta Was this translation helpful? Give feedback.
-
In case I don't have time to push today, here's the rotate code: procedure TFormMain.PerformRotate(BitmapSource, BitmapDest: TBitmap32; Angle: Single; ResamplerClass: TCustomResamplerClass);
var
Transformation: TAffineTransformation;
Resampler: TCustomResampler;
Rasterizer: TRasterizer;
CombineInfo: TCombineInfo;
Transformer: TTransformer;
TransformedBounds: TFloatRect;
TransformedFloatWidth, TransformedFloatHeight: Single;
TransformedWidth, TransformedHeight: integer;
SourceGhost: TBitmap32;
begin
if (Abs(Frac(Angle / 360)) < 0.1/360) then
begin
BitmapDest.Assign(BitmapSource);
exit;
end;
SourceGhost := TBitmap32.Create(TGhostingBackend);
try
TGhostingBackend(SourceGhost.Backend).GhostBitmap(BitmapSource);
Transformation := TAffineTransformation.Create;
try
Transformation.Clear;
Transformation.SrcRect := FloatRect(0, 0, SourceGhost.Width, SourceGhost.Height);
// Move origin so we will be rotating around center of bitmap
Transformation.Translate(-SourceGhost.Width * 0.5, -SourceGhost.Height * 0.5);
// Rotate
Transformation.Rotate(0, 0, Angle);
TransformedBounds := Transformation.GetTransformedBounds;
// Size destination to fit transformed bitmap
TransformedFloatWidth := TransformedBounds.Right-TransformedBounds.Left;
TransformedWidth := Ceil(TransformedFloatWidth-0.00001);
TransformedFloatHeight := TransformedBounds.Bottom-TransformedBounds.Top;
TransformedHeight := Ceil(TransformedFloatHeight-0.00001);
// Center in destination bitmap
Transformation.Translate(-TransformedBounds.Left + (TransformedWidth-TransformedFloatWidth) * 0.5, -TransformedBounds.Top + (TransformedHeight-TransformedFloatHeight) * 0.5);
if (ResamplerClass = nil) then
ResamplerClass := TCustomResamplerClass(SourceGhost.Resampler.ClassType);
Resampler := ResamplerClass.Create(SourceGhost);
if (Resampler is TKernelResampler) then
begin
TKernelResampler(Resampler).KernelMode := kmTableLinear;
TKernelResampler(Resampler).TableSize := 256;
TKernelResampler(Resampler).Kernel := TCubicKernel.Create;//TLanczosKernel.Create;
// TWindowedSincKernel(TKernelResampler(Resampler).Kernel).Width := FKernelSize;
end;
Resampler.PixelAccessMode := pamTransparentEdge;
// Note: pamSafe relies on BackgroundColor
// Resampler.PixelAccessMode := pamSafe;
Transformer := TTransformer.Create(Resampler, Transformation);
try
// Rasterizer := DefaultRasterizerClass.Create;
Rasterizer := TMultithreadedRegularRasterizer.Create;
try
Rasterizer.Sampler := Transformer;
// Must use CombineInfo so BufferSource's MasterAlpha isn't used in the rasterization.
CombineInfo.SrcAlpha := 255;
CombineInfo.DrawMode := dmOpaque;
// cmMerge minimizes blend artifacts: rotate pure color rectangle on transparent background. Rotated edges does not retain original color.
CombineInfo.CombineMode := cmMerge;
CombineInfo.CombineCallBack := nil;
CombineInfo.TransparentColor := 0;
BitmapDest.SetSize(TransformedWidth, TransformedHeight);
BitmapDest.Clear(0);
Rasterizer.Rasterize(BitmapDest, BitmapDest.BoundsRect, CombineInfo);
finally
Rasterizer.Free;
end;
finally
Transformer.Free;
end;
finally
Transformation.Free;
end;
finally
SourceGhost.Free;
end;
end;
|
Beta Was this translation helpful? Give feedback.
-
...and the resize: procedure TFormMain.PerformResize(BitmapSource, BitmapDest: TBitmap32; NewWidth, NewHeight: integer; ResamplerClass: TCustomResamplerClass);
var
Resampler: TCustomResampler;
SourceGhost: TBitmap32;
begin
BitmapDest.SetSize(NewWidth, NewHeight);
if (ResamplerClass = nil) then
ResamplerClass := TCustomResamplerClass(BitmapSource.Resampler.ClassType);
SourceGhost := TBitmap32.Create(TGhostingBackend);
try
TGhostingBackend(SourceGhost.Backend).GhostBitmap(BitmapSource);
Resampler := ResamplerClass.Create(SourceGhost);
if (Resampler is TKernelResampler) then
begin
TKernelResampler(Resampler).KernelMode := kmTableLinear;
TKernelResampler(Resampler).TableSize := 256;
TKernelResampler(Resampler).Kernel := TCubicKernel.Create;//TLanczosKernel.Create;
// TWindowedSincKernel(TKernelResampler(Resampler).Kernel).Width := FKernelSize;
end;
Resampler.PixelAccessMode := pamTransparentEdge;
// Note: pamSafe relies on BackgroundColor
// Resampler.PixelAccessMode := pamSafe;
TResamplerCracker(Resampler).Resample(BitmapDest, BitmapDest.BoundsRect, BitmapDest.BoundsRect, SourceGhost, SourceGhost.BoundsRect, dmOpaque, nil);
finally
SourceGhost.Free;
end;
end;
|
Beta Was this translation helpful? Give feedback.
-
...and some support stuff type
TBitmap32Cracker = class(TBitmap32);
TResamplerCracker = class(TCustomResampler);
type
// A backend that allows us to create a bitmap that has its own properties but
// uses the memory storage from a host bitmap.
TGhostingBackend = class(TCustomBackend)
public
procedure GhostBitmap(ABitmap: TBitmap32);
end;
procedure TGhostingBackend.GhostBitmap(ABitmap: TBitmap32);
begin
FOwner.SetSizeFrom(ABitmap);
TBitmap32Cracker(ABitmap).CopyPropertiesTo(FOwner);
FBits := ABitmap.Bits;
Changed;
end;
|
Beta Was this translation helpful? Give feedback.
-
Btw, I can see that you're working on your own resampler. Have you looked at the resampler work done by @rmesch ? There's some interesting stuff there. I had hoped to get more of his ideas integrated in Graphics32 but there's just too much other stuff to do. |
Beta Was this translation helpful? Give feedback.
-
The problem is when sampling very close to edges (not just past them) that the 'outer' (ie fully transparent) color bleeds in.
No, I haven't. Apart from just tidying up existing code, I'm fully occupied with other projects (including very slowly working towards adding triangulation to my Clipper library). |
Beta Was this translation helpful? Give feedback.
-
The example I mentioned above has now been merged: 2b4857d |
Beta Was this translation helpful? Give feedback.
-
according to the first name "Renate", it should be "her" ideas :) |
Beta Was this translation helpful? Give feedback.
-
I've just edited my original post with an improved (and simplified) ScaleImage function. |
Beta Was this translation helpful? Give feedback.
-
I couldn't find an easy way to scale or rotate images, so resorted to making my own.
And since I suspect others may find these functions useful too, I'm posting these functions here.
And of course there may be a better/simpler way to do this.
And here's a very simple VCL application that tests/uses these functions ....
Note: It's unfortunate that a small amount of transparency creeps into images when they are scaled.Edit: I've amended and simplified the
ScaleImage
function so it no longer usesTAffineTransformation
, and this has removed the transparency bleed problem.Beta Was this translation helpful? Give feedback.
All reactions