Как я могу изменить цвет изображения PNG с белого на черный в Delphi?

Я использую TPNGList из Густаво Дауда версии 1.4 в Delphi XE2

Он содержит изображения PNG 256x256, которые я использую в качестве изображений кнопок.

Однако необходимо изменить цвет фона, и контрастность изображения не является хорошей.

Так что теперь у меня есть белые изображения на темном фоне.

Мне нужно изменить их на черный для светлого фона.

Есть прозрачность и ее следует сохранить. Есть только белые пиксели. Но универсальный источник для целевой функции был бы также хорош.

РЕДАКТИРОВАТЬ: После предложения для "пойти на это" я попробовал следующее, но получить только черные или белые коробки:

procedure PNGInvertWB(Image: TPngImage; AWhite: Boolean);

  procedure WBInvertRGB(var R, G, B: Byte);
  var
    color: LongInt;
  begin
    if AWhite then
    begin
      if RGB(R, G, B) = clWhite then
      begin
        Color := ColorToRGB(clBlack);
        R := GetRValue(Color);
        G := GetGValue(Color);
        B := GetBValue(Color);
      end;
    end
    else
    begin
      if RGB(R, G, B) = clBlack then
      begin
        Color := ColorToRGB(clWhite);
        R := GetRValue(Color);
        G := GetGValue(Color);
        B := GetBValue(Color);
      end;
    end;
  end;

var
  X, Y, PalCount: Integer;
  Line: PRGBLine;
  PaletteHandle: HPalette;
  Palette: array[Byte] of TPaletteEntry;
begin
  if not (Image.Header.ColorType in [COLOR_GRAYSCALE, COLOR_GRAYSCALEALPHA]) then begin
    if Image.Header.ColorType = COLOR_PALETTE then begin
      PaletteHandle := Image.Palette;
      PalCount := GetPaletteEntries(PaletteHandle, 0, 256, Palette);
      for X := 0 to PalCount - 1 do
        WBInvertRGB(Palette[X].peRed, Palette[X].peGreen, Palette[X].peBlue);
      SetPaletteEntries(PaletteHandle, 0, PalCount, Palette);
      Image.Palette := PaletteHandle;
    end
    else begin
      for Y := 0 to Image.Height - 1 do begin
        Line := Image.Scanline[Y];
        for X := 0 to Image.Width - 1 do
          WBInvertRGB(Line[X].rgbtRed, Line[X].rgbtGreen, Line[X].rgbtBlue);
      end;
    end;
  end;
end;

Я звоню, используя этот код:

procedure TDBNavigator.UpdateColor;
var
  PNGImage: TPngImage;
  HCColor : TColor;

  procedure Invert(AImage: TImage; AWhite: boolean);
  begin
    ConvertToPNG(AImage.Picture.Graphic, PNGImage);
    PNGInvertWB(PNGImage, not AWhite);
    AImage.Picture.Graphic := PNGImage;
  end;

begin
  Color := ThemeManager.CurrentPallete.Color[FThemeColor];

  HCColor := ThemeManager.CurrentPallete.HighContrast(FThemeColor);
  if HCColor <> FCurrentColor then
  begin
    Invert(uiPrevious, HCColor = clWhite);
    Invert(uiNext,     HCColor = clWhite);
    Invert(uiInsert,   HCColor = clWhite);
    Invert(uiPost,     HCColor = clWhite);
    Invert(uiCancel,   HCColor = clWhite);
    Invert(uiDelete,   HCColor = clWhite);
    Invert(uiRefresh,  HCColor = clWhite);
    FCurrentColor := HCColor;
  end;
end;

Не уверен, какая часть не так. Это часть одного компонента, и я пытаюсь изменить изображение, назначенное во время разработки. Это было изображение PNG, которое я загрузил, 256x256 с прозрачностью.

Мне нужно использовать это изображение, я знаю, что это не кнопка. И, вероятно, есть компоненты, которые делают это. Мне нужно сделать самостоятельно, потому что конкретная библиотека, которую я использую.

Я получил идею PNGInvertWB от одной из функций Густаво в функциях PNG:

procedure MakeImageGrayscale(Image: TPngImage; Amount: Byte = 255);

Итак, у меня нет опыта работы с изображениями, что не так с этим кодом?

Вот как это выглядит на компоненте, где у меня есть изображения:

Оригинал:оригинал

После:После этой функции выше

Я использовал следующую функцию из PNGFunctions, чтобы попробовать это:

procedure MakeImageGrayscale(Image: TPngImage; Amount: Byte = 255);

  procedure GrayscaleRGB(var R, G, B: Byte);
  var
    X: Byte;
  begin
    X := Round(R * 0.30 + G * 0.59 + B * 0.11);
    R := Round(R / 256 * (256 - Amount - 1)) + Round(X / 256 * (Amount + 1));
    G := Round(G / 256 * (256 - Amount - 1)) + Round(X / 256 * (Amount + 1));
    B := Round(B / 256 * (256 - Amount - 1)) + Round(X / 256 * (Amount + 1));
  end;

var
  X, Y, PalCount: Integer;
  Line: PRGBLine;
  PaletteHandle: HPalette;
  Palette: array[Byte] of TPaletteEntry;
begin
  //Don't do anything if the image is already a grayscaled one
  if not (Image.Header.ColorType in [COLOR_GRAYSCALE, COLOR_GRAYSCALEALPHA]) then begin
    if Image.Header.ColorType = COLOR_PALETTE then begin
      //Grayscale every palette entry
      PaletteHandle := Image.Palette;
      PalCount := GetPaletteEntries(PaletteHandle, 0, 256, Palette);
      for X := 0 to PalCount - 1 do
        GrayscaleRGB(Palette[X].peRed, Palette[X].peGreen, Palette[X].peBlue);
      SetPaletteEntries(PaletteHandle, 0, PalCount, Palette);
      Image.Palette := PaletteHandle;
    end
    else begin
      //Grayscale every pixel
      for Y := 0 to Image.Height - 1 do begin
        Line := Image.Scanline[Y];
        for X := 0 to Image.Width - 1 do
          GrayscaleRGB(Line[X].rgbtRed, Line[X].rgbtGreen, Line[X].rgbtBlue);
      end;
    end;
  end;
end;

Где я изменил GrayscaleRGB, так как он получал каждый пиксель и менял его на оттенки серого, поэтому я полагал, что могу соответственно изменить цвет на черный или белый.

2 ответа

Решение

Будет хорошо увидеть, что у вас есть оригинал и что вы получите после выполнения этого кода

и вопрос таков: действительно ли вы уверены, что цвет всего фона равен черному или белому или близок к черному или почти к белому? Потому что вы сравниваете точный цвет RGB(R, G, B) = clWhite

попробуй это

procedure WBInvertRGB(var R, G, B: Byte);
  var
    color: LongInt;
  begin
    if AWhite then
    begin
      if ((R>240) and (G>240) and (B>240)) then
      begin
        Color := ColorToRGB(clBlack);
        R := GetRValue(Color);
        G := GetGValue(Color);
        B := GetBValue(Color);
      end;
    end
    else
    begin
      if ((R<15) and (G<15) and (B<15)) then
      begin
        Color := ColorToRGB(clWhite);
        R := GetRValue(Color);
        G := GetGValue(Color);
        B := GetBValue(Color);
      end;
    end;
  end;

что ты получишь после?

  1. Тогда я полагаю, что это из-за прозрачности (измените нижний левый угол на другой цвет)

я не знаю подробностей об этом компоненте png и не знаю, есть ли у него свойство пикселей, но поставлю это в конце процедуры

Line := Image.Scanline[Image.Height - 1];
Line[0].rgbtRed:= 5;
Line[0].rgbtGreen:= 5;
Line[0].rgbtBlue:= 5;

любое изменение в результате?

  1. или вы идете внутри палитры кода, чем полноцветные ставят точки останова, и вы увидите, где ваш код ломается

и добавьте комментарий здесь, если это цветовая палитра - это имеет значение, потому что вы не можете изменить цвет на белый, а другой на черный. Вам нужно три ступени, одна из которых меняется с белого на красный, например, черный на белый и красный на черный

Другие вопросы по тегам