Wycinanie okrągłego fragmentu obrazka PNG

0

Witam. Mógłby mi ktoś poradzić jak wyciąć okrąg z obrazka w formacie *.png. Robiłem to za pomocą regionów, ale jest pewien problem. A mianowicie części obrazka które powinny zniknąć po wycinaniu zostają czarne.
Przykład:

Przed:

s.png

Po:

u.png

Ja chcę aby nie było tych czarnych fragmentów.

Kod:

procedure MakeCircleImage;
var
  R1: HRGN;
  wynik, avatar: TPngImage;
begin
  avatar := TPngImage.Create;
  wynik := TPngImage.Create;
  try
    avatar.LoadFromFile('C:\Users\sauler\Desktop\s.png');

    CleanTransparentPngTo(wynik, 128, 128); // czyszczenie tla na wynikowym obrazku

    SmoothResize(avatar, 128, 128);

    R1 := CreateEllipticRgn(0, 0, 100, 100);
    try
      if SelectClipRgn(wynik.Canvas.Handle, R1) <> RGN_ERROR then
        try
          DrawPngWithAlpha(avatar, wynik, Rect(0, 0, 128, 128));
        finally
          SelectClipRgn(wynik.Canvas.Handle, 0);
        end;
    finally
      DeleteObject(R1);
    end;

    wynik.SaveToFile('C:\Users\sauler\Desktop\u.png');
  finally
    wynik.Free;
    avatar.Free;
  end;
end;
0

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;

1 użytkowników online, w tym zalogowanych: 0, gości: 1