123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289 |
- unit GR32_Gamma;
- (* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1 or LGPL 2.1 with linking exception
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * Alternatively, the contents of this file may be used under the terms of the
- * Free Pascal modified version of the GNU Lesser General Public License
- * Version 2.1 (the "FPC modified LGPL License"), in which case the provisions
- * of this license are applicable instead of those above.
- * Please see the file LICENSE.txt for additional information concerning this
- * license.
- *
- * The Original Code is Graphics32
- *
- * The Initial Developer of the Original Code is
- * Alex A. Denisov
- *
- * Portions created by the Initial Developer are Copyright (C) 2000-2009
- * the Initial Developer. All Rights Reserved.
- *
- * ***** END LICENSE BLOCK ***** *)
- interface
- {$include GR32.inc}
- uses
- GR32;
- { Gamma bias for line/pixel antialiasing }
- type
- TGammaTable8Bit = array [Byte] of Byte;
- var
- GAMMA_IS_SRGB: boolean; // True if GAMMA_ENCODING_TABLE and GAMMA_DECODING_TABLE
- // contains sRGB <-> Linear mapping values.
- // The Set_sRGB procedure sets this value to True while
- // the SetGamma procedure sets it to False.
- GAMMA_VALUE: Double; // If GAMMA_IS_SRGB is False, GAMMA_VALUE contains the
- // gamma value upon which GAMMA_ENCODING_TABLE and
- // GAMMA_DECODING_TABLE is based.
- GAMMA_ENCODING_TABLE: TGammaTable8Bit;
- GAMMA_DECODING_TABLE: TGammaTable8Bit;
- const
- DEFAULT_GAMMA: Double = 1.6;
- // set gamma
- procedure SetGamma; overload; {$IFDEF USEINLINING} inline; {$ENDIF}
- procedure SetGamma(Gamma: Double); overload;
- procedure SetGamma(Gamma: Double; var GammaTable: TGammaTable8Bit); overload;
- procedure Set_sRGB; overload;
- procedure Set_sRGB(var GammaTable: TGammaTable8Bit); overload;
- procedure SetInv_sRGB(var GammaTable: TGammaTable8Bit);
- // apply gamma
- function ApplyGamma(Color: TColor32): TColor32; overload; {$IFDEF USEINLINING} inline; {$ENDIF}
- function ApplyInvGamma(Color: TColor32): TColor32; overload; {$IFDEF USEINLINING} inline; {$ENDIF}
- function ApplyCustomGamma(Color: TColor32; GammaTable: TGammaTable8Bit): TColor32; overload; {$IFDEF USEINLINING} inline; {$ENDIF}
- procedure ApplyGamma(Color: PColor32Array; Length: Integer); overload;
- procedure ApplyInvGamma(Color: PColor32Array; Length: Integer); overload;
- procedure ApplyCustomGamma(Color: PColor32Array; Length: Integer; GammaTable: TGammaTable8Bit); overload;
- procedure ApplyGamma(Bitmap: TBitmap32); overload;
- procedure ApplyInvGamma(Bitmap: TBitmap32); overload;
- procedure ApplyCustomGamma(Bitmap: TBitmap32; GammaTable: TGammaTable8Bit); overload;
- procedure ApplyCustomGamma(Bitmap: TBitmap32; Gamma: Double); overload;
- // Gamma change notification
- // Warning: Not thread safe
- type
- TGammaChangedProc = procedure of object;
- procedure RegisterGammaChangeNotification(Delegate: TGammaChangedProc);
- procedure UnregisterGammaChangeNotification(Delegate: TGammaChangedProc);
- implementation
- uses
- Math,
- SysUtils,
- Generics.Collections;
- var
- GammaChangedDelegates: TList<TGammaChangedProc>;
- procedure RegisterGammaChangeNotification(Delegate: TGammaChangedProc);
- begin
- if (GammaChangedDelegates = nil) then
- GammaChangedDelegates := TList<TGammaChangedProc>.Create;
- GammaChangedDelegates.Add(Delegate);
- end;
- procedure UnregisterGammaChangeNotification(Delegate: TGammaChangedProc);
- begin
- if (GammaChangedDelegates <> nil) then
- GammaChangedDelegates.Remove(Delegate);
- end;
- function ApplyGamma(Color: TColor32): TColor32;
- begin
- TColor32Entry(Result).R := GAMMA_ENCODING_TABLE[TColor32Entry(Color).R];
- TColor32Entry(Result).G := GAMMA_ENCODING_TABLE[TColor32Entry(Color).G];
- TColor32Entry(Result).B := GAMMA_ENCODING_TABLE[TColor32Entry(Color).B];
- end;
- function ApplyInvGamma(Color: TColor32): TColor32;
- begin
- TColor32Entry(Result).R := GAMMA_DECODING_TABLE[TColor32Entry(Color).R];
- TColor32Entry(Result).G := GAMMA_DECODING_TABLE[TColor32Entry(Color).G];
- TColor32Entry(Result).B := GAMMA_DECODING_TABLE[TColor32Entry(Color).B];
- end;
- function ApplyCustomGamma(Color: TColor32; GammaTable: TGammaTable8Bit): TColor32;
- begin
- TColor32Entry(Result).R := GammaTable[TColor32Entry(Color).R];
- TColor32Entry(Result).G := GammaTable[TColor32Entry(Color).G];
- TColor32Entry(Result).B := GammaTable[TColor32Entry(Color).B];
- end;
- procedure ApplyGamma(Color: PColor32Array; Length: Integer);
- var
- Index: Integer;
- begin
- for Index := 0 to Length - 1 do
- begin
- PColor32Entry(Color)^.R := GAMMA_ENCODING_TABLE[PColor32Entry(Color)^.R];
- PColor32Entry(Color)^.G := GAMMA_ENCODING_TABLE[PColor32Entry(Color)^.G];
- PColor32Entry(Color)^.B := GAMMA_ENCODING_TABLE[PColor32Entry(Color)^.B];
- Inc(Color);
- end;
- end;
- procedure ApplyInvGamma(Color: PColor32Array; Length: Integer);
- var
- Index: Integer;
- begin
- for Index := 0 to Length - 1 do
- begin
- PColor32Entry(Color)^.R := GAMMA_DECODING_TABLE[PColor32Entry(Color)^.R];
- PColor32Entry(Color)^.G := GAMMA_DECODING_TABLE[PColor32Entry(Color)^.G];
- PColor32Entry(Color)^.B := GAMMA_DECODING_TABLE[PColor32Entry(Color)^.B];
- Inc(Color);
- end;
- end;
- procedure ApplyCustomGamma(Color: PColor32Array; Length: Integer; GammaTable: TGammaTable8Bit);
- var
- Index: Integer;
- begin
- for Index := 0 to Length - 1 do
- begin
- PColor32Entry(Color)^.R := GammaTable[PColor32Entry(Color)^.R];
- PColor32Entry(Color)^.G := GammaTable[PColor32Entry(Color)^.G];
- PColor32Entry(Color)^.B := GammaTable[PColor32Entry(Color)^.B];
- Inc(Color);
- end;
- end;
- procedure ApplyGamma(Bitmap: TBitmap32);
- begin
- ApplyGamma(Bitmap.Bits, Bitmap.Width * Bitmap.Height);
- end;
- procedure ApplyInvGamma(Bitmap: TBitmap32);
- begin
- ApplyInvGamma(Bitmap.Bits, Bitmap.Width * Bitmap.Height);
- end;
- procedure ApplyCustomGamma(Bitmap: TBitmap32; GammaTable: TGammaTable8Bit);
- begin
- ApplyCustomGamma(Bitmap.Bits, Bitmap.Width * Bitmap.Height, GammaTable);
- end;
- procedure ApplyCustomGamma(Bitmap: TBitmap32; Gamma: Double);
- var
- GammaTable: TGammaTable8Bit;
- begin
- if GAMMA_VALUE = Gamma then
- ApplyGamma(Bitmap.Bits, Bitmap.Width * Bitmap.Height)
- else
- begin
- SetGamma(Gamma, GammaTable);
- ApplyCustomGamma(Bitmap.Bits, Bitmap.Width * Bitmap.Height, GammaTable);
- end;
- end;
- { Gamma / Pixel Shape Correction table }
- procedure SetGamma;
- begin
- SetGamma(DEFAULT_GAMMA);
- end;
- procedure SetGamma(Gamma: Double);
- var
- GammaChangedProc: TGammaChangedProc;
- begin
- if (IsZero(Gamma)) then
- exit;
- GAMMA_VALUE := Gamma;
- GAMMA_IS_SRGB := False;
- // calculate default gamma tables
- SetGamma(1 / Gamma, GAMMA_ENCODING_TABLE);
- SetGamma(Gamma, GAMMA_DECODING_TABLE);
- if (GammaChangedDelegates <> nil) then
- for GammaChangedProc in GammaChangedDelegates do
- GammaChangedProc;
- end;
- procedure SetGamma(Gamma: Double; var GammaTable: TGammaTable8Bit);
- var
- i: Integer;
- begin
- for i := 0 to $FF do
- GammaTable[i] := Round($FF * Power(i * COne255th, Gamma));
- end;
- procedure Set_sRGB;
- var
- GammaChangedProc: TGammaChangedProc;
- begin
- Set_sRGB(GAMMA_ENCODING_TABLE);
- SetInv_sRGB(GAMMA_DECODING_TABLE);
- GAMMA_IS_SRGB := True;
- if (GammaChangedDelegates <> nil) then
- for GammaChangedProc in GammaChangedDelegates do
- GammaChangedProc;
- end;
- procedure Set_sRGB(var GammaTable: TGammaTable8Bit);
- var
- i: Integer;
- Value: Double;
- const
- CExp = 1 / 2.4;
- begin
- for i := 0 to $FF do
- begin
- Value := i * COne255th;
- if (Value < 0.0031308) then
- GammaTable[i] := Round($FF * Value * 12.92)
- else
- GammaTable[i] := Round($FF * (1.055 * Power(Value, CExp) - 0.055));
- end;
- end;
- procedure SetInv_sRGB(var GammaTable: TGammaTable8Bit);
- var
- i: Integer;
- Value: Double;
- begin
- for i := 0 to $FF do
- begin
- Value := i * COne255th;
- if (Value < 0.004045) then
- GammaTable[i] := Round($FF * Value / 12.92)
- else
- GammaTable[i] := Round($FF * Power((Value + 0.055) / 1.055, 2.4));
- end;
- end;
- initialization
- finalization
- FreeAndNil(GammaChangedDelegates);
- end.
|