Create a button that accepts .PNG images as Glyph

前端 未结 3 664
盖世英雄少女心
盖世英雄少女心 2020-12-18 09:04

I\'m trying to understand how the SpeedButton Glyph property work, I find that the field declared as:

FGlyph: TObject;

<
3条回答
  •  清酒与你
    2020-12-18 09:57

    The first part is about how the Glyph property of TSpeedButton works, as you seem to be asking that as a part of your problem.

    While TSpeedButton's FGlyph field is declared as an TObject, you will find that in code it actually contains an instance of TButtonGlyph. In the TSpeedButton constructor you will find the line FGlyph := TButtonGlyph.Create; and the setter and getter for the Glyph property of TSpeedButton look like this:

    function TSpeedButton.GetGlyph: TBitmap;
    begin
      Result := TButtonGlyph(FGlyph).Glyph;
    end;
    
    procedure TSpeedButton.SetGlyph(Value: TBitmap);
    begin
      TButtonGlyph(FGlyph).Glyph := Value;
      Invalidate;
    end;
    

    So TSpeedButton's Glyph property actually accesses the Glyph property of the TButtonGlyph class, an internal class defined in Vcl.Buttons, which encapsulates - among other things - the actual TBitMap with following property

    property Glyph: TBitmap read FOriginal write SetGlyph;
    

    So the TButtonGlyph has an TBitMap field FOriginal and the setter is implemented like this:

    procedure TButtonGlyph.SetGlyph(Value: TBitmap);
    var
      Glyphs: Integer;
    begin
      Invalidate;
      FOriginal.Assign(Value);
      if (Value <> nil) and (Value.Height > 0) then
      begin
        FTransparentColor := Value.TransparentColor;
        if Value.Width mod Value.Height = 0 then
        begin
          Glyphs := Value.Width div Value.Height;
          if Glyphs > 4 then Glyphs := 1;
          SetNumGlyphs(Glyphs);
        end;
      end;
    end;
    

    At this point it is important how accepts .PNG is defined:

    • Being able to use the PNG image, with some trade-offs.
    • Fully supports PNG images

    For the latter I believe the answer of Remy Lebeau is the best advice. The internal class TButtonGylph makes OOP approaches like inheritance with png capable class impossible as far as I see. Or even go further and do as Remy suggests in a comment: third-party component.

    If trade-offs are acceptable however:

    Note the FOriginal.Assign(Value); which can already help in using PNGs, as TPNGImage's AssignTo procedure knows how to assign itself to a TBitMap. With the above known about the Glyph property, we can simply assign a PNG with the following code:

    var
      APNG: TPngImage;
    begin
      APNG := TPngImage.Create;
      try
        APNG.LoadFromFile('C:\Binoculars.png');
        SpeedButton1.Glyph.Assign(APNG);
      finally
        APNG.Free;
      end;
    

    Due to differences between bitmap and PNG this might however ignore alpha channel of the PNG, but based on an answer from Andreas Rejbrand there is a partial solution for that:

    var
      APNG: TPngImage;
      ABMP: TBitmap;
    begin
      APNG := TPngImage.Create;
      ABMP := TBitmap.Create;
      try
        APNG.LoadFromFile('C:\Binoculars.png');
    
        ABMP.SetSize(APNG.Width, APNG.Height);
        ABMP.Canvas.Brush.Color := Self.Color;
        ABMP.Canvas.FillRect(Rect(0, 0, ABMP.Width, ABMP.Height));
        ABMP.Canvas.Draw(0, 0, APNG);
    
        SpeedButton1.Glyph.Assign(APNG);
      finally
        APNG.Free;
        ABMP.Free;
      end;
    end;
    

提交回复
热议问题