Oto rozwiązanie, którego użyłem. Niestety nie wspiera ono przeźroczystości, ale bez tego mogę się obejść. Wydaje mi się też, że strasznie przekombinowałem ;)
Skalowanie PNG do określonych rozmiarów:
procedure ResizePng(apng: TPngImage; NuWidth, NuHeight: Integer);
var
xscale, yscale: Single;
sfrom_y, sfrom_x: Single;
ifrom_y, ifrom_x: Integer;
to_y, to_x: Integer;
weight_x, weight_y: array [0 .. 1] of Single;
weight: Single;
new_red, new_green: Integer;
new_blue, new_alpha: Integer;
new_colortype: Integer;
total_red, total_green: Single;
total_blue, total_alpha: Single;
IsAlpha: Boolean;
ix, iy: Integer;
bTmp: TPngImage;
sli, slo: pRGBLine;
ali, alo: PngImage.pbytearray;
begin
if not(apng.Header.ColorType in [COLOR_RGBALPHA, COLOR_RGB]) then
raise Exception.Create('Only COLOR_RGBALPHA and COLOR_RGB formats' +
' are supported');
IsAlpha := apng.Header.ColorType in [COLOR_RGBALPHA];
if IsAlpha then
new_colortype := COLOR_RGBALPHA
else
new_colortype := COLOR_RGB;
bTmp := tpngobject.CreateBlank(new_colortype, 8, NuWidth, NuHeight);
xscale := bTmp.Width / (apng.Width - 1);
yscale := bTmp.Height / (apng.Height - 1);
for to_y := 0 to bTmp.Height - 1 do
begin
sfrom_y := to_y / yscale;
ifrom_y := Trunc(sfrom_y);
weight_y[1] := sfrom_y - ifrom_y;
weight_y[0] := 1 - weight_y[1];
for to_x := 0 to bTmp.Width - 1 do
begin
sfrom_x := to_x / xscale;
ifrom_x := Trunc(sfrom_x);
weight_x[1] := sfrom_x - ifrom_x;
weight_x[0] := 1 - weight_x[1];
total_red := 0.0;
total_green := 0.0;
total_blue := 0.0;
total_alpha := 0.0;
for ix := 0 to 1 do
begin
for iy := 0 to 1 do
begin
sli := apng.Scanline[ifrom_y + iy];
if IsAlpha then
ali := apng.AlphaScanline[ifrom_y + iy];
new_red := sli[ifrom_x + ix].rgbtRed;
new_green := sli[ifrom_x + ix].rgbtGreen;
new_blue := sli[ifrom_x + ix].rgbtBlue;
if IsAlpha then
new_alpha := ali[ifrom_x + ix];
weight := weight_x[ix] * weight_y[iy];
total_red := total_red + new_red * weight;
total_green := total_green + new_green * weight;
total_blue := total_blue + new_blue * weight;
if IsAlpha then
total_alpha := total_alpha + new_alpha * weight;
end;
end;
slo := bTmp.Scanline[to_y];
if IsAlpha then
alo := bTmp.AlphaScanline[to_y];
slo[to_x].rgbtRed := Round(total_red);
slo[to_x].rgbtGreen := Round(total_green);
slo[to_x].rgbtBlue := Round(total_blue);
if IsAlpha then
alo[to_x] := Round(total_alpha);
end;
end;
apng.Assign(bTmp);
bTmp.Free;
end;
Konwersja PNG do BMP:
procedure ConvertPngToBmp(Src: TPngImage; Dest: TBitmap);
begin
Dest.Height := Src.Height;
Dest.Width := Src.Width;
Dest.PixelFormat := pf32bit;
Dest.AlphaFormat := afDefined;
Dest.TransparentMode := tmFixed;
Dest.Transparent := true;
Dest.Canvas.Draw(0, 0, Src);
end;
Maskowanie:
//avatar - plik PNG, który chcemy zamaskować
//mask - biało-czarna maska w formacie PNG. Czarny niewidocznym, biały widoczny
//save_path - gdzie zapisać
procedure LoadPNGAndApplyMask(avatar, mask, save_path: string);
var
av, ma, wy: FMX.Graphics.TBitmap;
tmp: TPngImage;
tmp1: TBitmap;
s: TMemoryStream;
begin
av := FMX.Graphics.TBitmap.Create;
try
ma := FMX.Graphics.TBitmap.Create;
try
wy := FMX.Graphics.TBitmap.Create;
try
s := TMemoryStream.Create;
try
tmp := TPngImage.Create;
try
tmp1 := TBitmap.Create;
try
tmp.LoadFromFile(avatar);
ResizePng(tmp, 128, 128);
ConvertPngToBmp(tmp, tmp1);
tmp1.PixelFormat := pf32bit;
tmp1.SaveToStream(s);
av.LoadFromStream(s);
ma.LoadFromFile(mask);
wy.CreateFromBitmapAndMask(av, ma);
wy.SaveToFile(save_path);
finally
tmp1.Free;
end;
finally
tmp.Free;
end;
finally
s.Free;
end;
finally
wy.Free;
end;
finally
ma.Free;
end;
finally
av.Free;
end;
end;