本文用GDI+实现Photoshop色相/饱和度/明度功能,参照我的其它有关GDI+在 Delphi程序的应用的文章,代码也可供TBitmap使用。

有些人不喜欢,或者不太懂Delphi的BASM代码,所以本文给出纯PAS代码。须说明的是,纯PAS代码效率较低,不适合实际应用。喜欢C/C++的,可以看本人文章《C++实现Photoshop色相/饱和度/明度功能》,除了语言不同,其它都一样。

有关Photoshop饱和度调整原理可参见《GDI+ 在Delphi程序的应用 -- 图像饱和度调整》,明度调整原理可参见《GDI+ 在Delphi程序的应用 -- 仿Photoshop的明度调整》。

下面是一个完整的Delphi程序,Photoshop色相/饱和度/明度功能纯PAS代码包含在其中:

unit main;
 
interface
 
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls, StdCtrls, ComCtrls, Gdiplus;
 
type
  TForm1 = class(TForm)
    Label1: TLabel;
    Label2: TLabel;
    Label3: TLabel;
    Hbar: TTrackBar;
    SBar: TTrackBar;
    BBar: TTrackBar;
    HEdit: TEdit;
    SEdit: TEdit;
    BEdit: TEdit;
    Button1: TButton;
    PaintBox1: TPaintBox;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure PaintBox1Paint(Sender: TObject);
    procedure HBarChange(Sender: TObject);
    procedure SBarChange(Sender: TObject);
    procedure BBarChange(Sender: TObject);
    procedure HEditChange(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
    Source: TGpBitmap;
    Bitmap: TGpBitmap;
    r: TGpRect;
    Lock: Boolean;
  public
    { Public declarations }
  end;
 
var
  Form1: TForm1;
 
implementation
 
{$R *.dfm}
 
procedure SwapRGB(var a, b: Integer);
begin
  Inc(a, b);
  b := a - b;
  Dec(a, b);
end;
 
procedure CheckRGB(var Value: Integer);
begin
  if Value < 0 then Value := 0
  else if Value > 255 then Value := 255;
end;
 
procedure AssignRGB(var R, G, B: Byte; intR, intG, intB: Integer);
begin
  R := intR;
  G := intG;
  B := intB;
end;
 
procedure SetBright(var R, G, B: Byte; bValue: Integer);
var
  intR, intG, intB: Integer;
begin
  intR := R;
  intG := G;
  intB := B;
  if bValue > 0 then
  begin
    Inc(intR, (255 - intR) * bValue div 255);
    Inc(intG, (255 - intG) * bValue div 255);
    Inc(intB, (255 - intB) * bValue div 255);
  end
  else if bValue < 0 then
  begin
    Inc(intR, intR * bValue div 255);
    Inc(intG, intG * bValue div 255);
    Inc(intB, intB * bValue div 255);
  end;
  CheckRGB(intR);
  CheckRGB(intG);
  CheckRGB(intB);
  AssignRGB(R, G, B, intR, intG, intB);
end;
 
procedure SetHueAndSaturation(var R, G, B: Byte; hValue, sValue: Integer);
var
  intR, intG, intB: Integer;
  H, S, L, Lum: Integer;
  delta, entire: Integer;
  index, extra: Integer;
begin
  intR := R;
  intG := G;
  intB := B;
 
  if intR < intG then SwapRGB(intR, intG);
  if intR < intB then SwapRGB(intR, intB);
  if intB > intG then SwapRGB(intB, intG);
 
  delta := intR - intB;
  if delta = 0 then Exit;
 
  entire := intR + intB;
  L := entire shr 1;
  if L < 128 then
    S := delta * 255 div entire
  else
    S := delta * 255 div (510 - entire);
  if hValue <> 0 then
  begin
    if intR = R then
      H := (G - B) * 60 div delta
    else if intR = G then
      H := (B - R) * 60 div delta + 120
    else
      H := (R - G) * 60 div delta + 240;
    Inc(H, hValue);
    if H < 0 then
      Inc(H, 360)
    else if H > 360 then
      Dec(H, 360);
    index := H div 60;
    extra := H mod 60;
    if (index and 1) <> 0 then
      extra := 60 - extra;
    extra := (extra * 255 + 30) div 60;
    intG := extra - (extra - 128) * (255 - S) div 255;
    Lum := L - 128;
    if Lum > 0 then
      Inc(intG, (((255 - intG) * Lum + 64) div 128))
    else if Lum < 0 then
      Inc(intG, (intG * Lum div 128));
    CheckRGB(intG);
    case index of
      1: SwapRGB(intR, intG);
      2:
      begin
        SwapRGB(intR, intB);
        SwapRGB(intG, intB);
      end;
      3: SwapRGB(intR, intB);
      4:
      begin
        SwapRGB(intR, intG);
        SwapRGB(intG, intB);
      end;
      5: SwapRGB(intG, intB);
    end;
  end
  else
  begin
    intR := R;
    intG := G;
    intB := B;
  end;
  if sValue <> 0 then
  begin
    if sValue > 0 then
    begin
      if sValue + S >= 255 then sValue := S
      else sValue := 255 - sValue;
      sValue := 65025 div sValue - 255;
    end;
    Inc(intR, ((intR - L) * sValue div 255));
    Inc(intG, ((intG - L) * sValue div 255));
    Inc(intB, ((intB - L) * sValue div 255));
    CheckRGB(intR);
    CheckRGB(intG);
    CheckRGB(intB);
  end;
  AssignRGB(R, G, B, intR, intG, intB);
end;
 
procedure GdipHSBAdjustment(Bmp: TGpBitmap; hValue, sValue, bValue: Integer);
var
  Data: TBitmapData;
  x, y: Integer;
  p: PRGBQuad;
begin
  sValue := sValue * 255 div 100;
  bValue := bValue * 255 div 100;
  Data := Bmp.LockBits(GpRect(0, 0, Bmp.Width, Bmp.Height), [imRead, imWrite], pf32bppARGB);
  try
    p := Data.Scan0;
    for y := 1 to Data.Height do
    begin
      for x := 1 to Data.Width do
      begin
        if (sValue > 0) and (bValue <> 0) then
          SetBright(p^.rgbRed, p^.rgbGreen, p^.rgbBlue, bValue);
        SetHueAndSaturation(p^.rgbRed, p^.rgbGreen, p^.rgbBlue, hValue, sValue);
        if (sValue <= 0) and (bValue <> 0) then
          SetBright(p^.rgbRed, p^.rgbGreen, p^.rgbBlue, bValue);
        Inc(p);
      end;
    end;
  finally
    Bmp.UnlockBits(Data);
  end;
end;
 
procedure TForm1.FormCreate(Sender: TObject);
begin
  Source := TGpBitmap.Create('../../GdiplusDemo/media/100_0349.jpg');
  r := GpRect(0, 0, Source.Width, Source.Height);
  Bitmap := Source.Clone(r, pf32bppARGB);
  DoubleBuffered := True;
end;
 
procedure TForm1.FormDestroy(Sender: TObject);
begin
  Bitmap.Free;
  Source.Free;
end;
 
procedure TForm1.PaintBox1Paint(Sender: TObject);
var
  g: TGpGraphics;
begin
  g := TGpGraphics.Create(PaintBox1.Canvas.Handle);
  try
    g.DrawImage(Bitmap, r);
    g.TranslateTransform(0, r.Height);
    g.DrawImage(Source, r);
  finally
    g.Free;
  end;
end;
 
procedure TForm1.HBarChange(Sender: TObject);
begin
  if not Lock then
    HEdit.Text := IntToStr(HBar.Position);
end;
 
procedure TForm1.SBarChange(Sender: TObject);
begin
  if not Lock then
    SEdit.Text := IntToStr(SBar.Position);
end;
 
procedure TForm1.BBarChange(Sender: TObject);
begin
  if not Lock then
    BEdit.Text := IntToStr(BBar.Position);
end;
 
procedure TForm1.HEditChange(Sender: TObject);
begin
  Lock := True;
  if TEdit(Sender).Text = '' then
    TEdit(Sender).Text := '0';
  case TEdit(Sender).Tag of
    0: HEdit.Text := IntToStr(HBar.Position);
    1: HEdit.Text := IntToStr(HBar.Position);
    2: HEdit.Text := IntToStr(HBar.Position);
  end;
  Lock := False;
  Bitmap.Free;
  Bitmap := Source.Clone(r, pf32bppARGB);
  if (HBar.Position <> 0) or (SBar.Position <> 0) or (BBar.Position <> 0) then
    GdipHSBAdjustment(Bitmap, HBar.Position, SBar.Position, BBar.Position);
  PaintBox1.Invalidate;
end;
 
procedure TForm1.Button1Click(Sender: TObject);
begin
  HBar.Position := 0;
  SBar.Position := 0;
  BBar.Position := 0;
end;
 
end.

程序运行界面截图:

代码中所用Gdiplus单元下载地址及BUG更正见文章《GDI+ for VCL基础 -- GDI+ 与 VCL》。

建议和指导请来信:maozefa@hotmail.com

注:本文于2009.11.1整理,以前的BASM代码从本文删除,转移到《Delphi图像处理》系列文章中,特此致歉。

GDI+在Delphi程序的应用 Photoshop色相饱和度明度功能的更多相关文章

  1. 一名Delphi程序员的开发习惯

    一名Delphi程序员的开发习惯 有关开发习惯的一些想法,如鲠在喉,不吐不快.究其发贴动机,当然不排除有骗取参与分的可能,但另一方面,也希望能给同行(念Xing)者提供一些 建议,或者参考(希望不是误 ...

  2. Fastreport使用经验(转)在Delphi程序中访问报表对象

    Fastreport使用经验(转) 在Delphi程序中访问报表对象 最基本的方法就是frxReport1.FindObject. 然后把返回的对象强制转换成它的类型,当然,在报表中必须真的有这么个东 ...

  3. Delphi 程序结构

    注:该内容整理自以下链接. http://www.cnblogs.com/hackpig/archive/2010/02/15/1668513.html 概要介绍:Object Pascal语言的结构 ...

  4. delphi 程序全屏显示无标题栏,覆盖整个屏幕

    delphi 程序全屏显示无标题栏,覆盖整个屏幕,这个在做工控机或屏保时有用的,所以记下 procedure TMainFrm.FormCreate(Sender: TObject); begin w ...

  5. 远程控制篇:在DELPHI程序中拨号上网

    用MODEM拨号上网,仍是大多数个人网民选择上网的方式.如果能在我们的应用程序中启动拨号连接(如IE浏览器程序中的自动拨号功能),无疑将会方便我们的软件用户(不用再切换应用程序,运行拨号网络),提高我 ...

  6. 问题-delphi 程序在某电脑中显示???问号 乱码

    问题现象:delphi 程序在某电脑中显示???问号 乱码 问题原因:因为语言的原因.不同的国家可能显示的编码不一样. 问题处理:“控制面板”>“区域和语言选项”>“区域选项”>“标 ...

  7. 让delphi程序不受WINDOWS日期格式的影响

    http://www.cnblogs.com/hnxxcxg/archive/2013/01/30/2882672.html 如果WINDOWS系统的短日期格式为“yyyy/m/d”,执行下面的代码会 ...

  8. 让delphi程序不受WINDOWS日期格式的影响(使用SetLocaleInfo函数和Application.UpdateFormatSettings)

    如果WINDOWS系统的短日期格式为“yyyy/m/d”,执行下面的代码会报错:2013-01-29 00:00:00不是合法的日期procedure TFrmQuerySale.FormShow(S ...

  9. Delphi程序调用C#.Net编译的DLL并打开窗体(详解)

    Delphi程序调用C#.Net编译的DLL并打开窗体(详解)最近用C#.Net写了一个公用模块, 本以为仅提供给.Net程序使用, 但是领导要求把这些功能提供给旧系统使用, 天啦, 几套旧系统全是D ...

随机推荐

  1. rest framework之过滤组件

    一.普通过滤 (一)get_queryset get_queryset方法是GenericAPIView提供的一个方法,旨在返回queryset数据集,而过滤就是要在这个方法返回数据集之前对数据进行筛 ...

  2. Java高并发网络编程(五)Netty应用

    推送系统 一.系统设计 二.拆包和粘包 粘包.拆包表现形式 现在假设客户端向服务端连续发送了两个数据包,用packet1和packet2来表示,那么服务端收到的数据可以分为三种,现列举如下: 第一种情 ...

  3. Xversion for Mac优秀的SVN客户端功能特色介绍

    xversion for mac是一款专为macOS打造的svn客户端,该软件拥有一个设计精美的界面以及全面的功能,简介的用户界面可以让您做到无需浏览用户手册即可熟练使用.xversion支持从最开始 ...

  4. 关于计算机学习的书(doc,mobi,epub,pdf四种格式)

    关于计算机学习的书(doc,mobi,epub,pdf四种格式) <html> <body> <div> 21天学通C+ +2016/6/22 18:47文條 30 ...

  5. Vue学习笔记【10】——Vue指令之v-if和v-show

    Vue指令之v-if和v-show <!DOCTYPE html> <html lang="en"> ​ <head> <meta cha ...

  6. CSP-S考前各种idea题解乱堆

    快要考试了我还是这么菜. 已经没有心思维护我的博客了.开一篇博文吧.可能会记得很乱. 这也许是我OI生涯的最后一篇博文了?? 肯定很长很长. 不可能的.谁知道什么时候我心态恢复就把上面两句话删掉开始在 ...

  7. 神建模+dp——cf1236E

    首先将两个人的所有可能的操作建立成一个模型:m+2行n列的矩阵 序列A对应图上的格子(i,Ai),第0行作为起点,最后一行作为终点,每个点可以向左下,下,右下走,每种可行的情况对应图上的一条路径 推出 ...

  8. RobotFramework 切换窗口控制的用法小结

    一:滚动条控制 应用场景:通过滚动条的上下,左右移动,才能让定位的元素可见.

  9. 使用wordpress搭建的网站如何去掉域名中的wordpess

    我们搭建好的网站当以文件夹的形式把wordpress程序放在空间的根目录时,访问的时候要加上文件夹名,访问地址就是:http://www.xxx.com/wordpress,直接用域名是无法访问,解决 ...

  10. C++ 操作json文件

    一.环境搭建: 参考文章:https://blog.csdn.net/fakine/article/details/79272090 二.创建实例: #include <stdio.h> ...