GR32_Gamma.pas 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. unit GR32_Gamma;
  2. (* ***** BEGIN LICENSE BLOCK *****
  3. * Version: MPL 1.1 or LGPL 2.1 with linking exception
  4. *
  5. * The contents of this file are subject to the Mozilla Public License Version
  6. * 1.1 (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. * http://www.mozilla.org/MPL/
  9. *
  10. * Software distributed under the License is distributed on an "AS IS" basis,
  11. * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  12. * for the specific language governing rights and limitations under the
  13. * License.
  14. *
  15. * Alternatively, the contents of this file may be used under the terms of the
  16. * Free Pascal modified version of the GNU Lesser General Public License
  17. * Version 2.1 (the "FPC modified LGPL License"), in which case the provisions
  18. * of this license are applicable instead of those above.
  19. * Please see the file LICENSE.txt for additional information concerning this
  20. * license.
  21. *
  22. * The Original Code is Graphics32
  23. *
  24. * The Initial Developer of the Original Code is
  25. * Alex A. Denisov
  26. *
  27. * Portions created by the Initial Developer are Copyright (C) 2000-2009
  28. * the Initial Developer. All Rights Reserved.
  29. *
  30. * ***** END LICENSE BLOCK ***** *)
  31. interface
  32. {$include GR32.inc}
  33. uses
  34. GR32;
  35. { Gamma bias for line/pixel antialiasing }
  36. type
  37. TGammaTable8Bit = array [Byte] of Byte;
  38. var
  39. GAMMA_IS_SRGB: boolean; // True if GAMMA_ENCODING_TABLE and GAMMA_DECODING_TABLE
  40. // contains sRGB <-> Linear mapping values.
  41. // The Set_sRGB procedure sets this value to True while
  42. // the SetGamma procedure sets it to False.
  43. GAMMA_VALUE: Double; // If GAMMA_IS_SRGB is False, GAMMA_VALUE contains the
  44. // gamma value upon which GAMMA_ENCODING_TABLE and
  45. // GAMMA_DECODING_TABLE is based.
  46. GAMMA_ENCODING_TABLE: TGammaTable8Bit;
  47. GAMMA_DECODING_TABLE: TGammaTable8Bit;
  48. const
  49. DEFAULT_GAMMA: Double = 1.6;
  50. // set gamma
  51. procedure SetGamma; overload; {$IFDEF USEINLINING} inline; {$ENDIF}
  52. procedure SetGamma(Gamma: Double); overload;
  53. procedure SetGamma(Gamma: Double; var GammaTable: TGammaTable8Bit); overload;
  54. procedure Set_sRGB; overload;
  55. procedure Set_sRGB(var GammaTable: TGammaTable8Bit); overload;
  56. procedure SetInv_sRGB(var GammaTable: TGammaTable8Bit);
  57. // apply gamma
  58. function ApplyGamma(Color: TColor32): TColor32; overload; {$IFDEF USEINLINING} inline; {$ENDIF}
  59. function ApplyInvGamma(Color: TColor32): TColor32; overload; {$IFDEF USEINLINING} inline; {$ENDIF}
  60. function ApplyCustomGamma(Color: TColor32; GammaTable: TGammaTable8Bit): TColor32; overload; {$IFDEF USEINLINING} inline; {$ENDIF}
  61. procedure ApplyGamma(Color: PColor32Array; Length: Integer); overload;
  62. procedure ApplyInvGamma(Color: PColor32Array; Length: Integer); overload;
  63. procedure ApplyCustomGamma(Color: PColor32Array; Length: Integer; GammaTable: TGammaTable8Bit); overload;
  64. procedure ApplyGamma(Bitmap: TBitmap32); overload;
  65. procedure ApplyInvGamma(Bitmap: TBitmap32); overload;
  66. procedure ApplyCustomGamma(Bitmap: TBitmap32; GammaTable: TGammaTable8Bit); overload;
  67. procedure ApplyCustomGamma(Bitmap: TBitmap32; Gamma: Double); overload;
  68. // Gamma change notification
  69. // Warning: Not thread safe
  70. type
  71. TGammaChangedProc = procedure of object;
  72. procedure RegisterGammaChangeNotification(Delegate: TGammaChangedProc);
  73. procedure UnregisterGammaChangeNotification(Delegate: TGammaChangedProc);
  74. implementation
  75. uses
  76. Math,
  77. SysUtils,
  78. Generics.Collections;
  79. var
  80. GammaChangedDelegates: TList<TGammaChangedProc>;
  81. procedure RegisterGammaChangeNotification(Delegate: TGammaChangedProc);
  82. begin
  83. if (GammaChangedDelegates = nil) then
  84. GammaChangedDelegates := TList<TGammaChangedProc>.Create;
  85. GammaChangedDelegates.Add(Delegate);
  86. end;
  87. procedure UnregisterGammaChangeNotification(Delegate: TGammaChangedProc);
  88. begin
  89. if (GammaChangedDelegates <> nil) then
  90. GammaChangedDelegates.Remove(Delegate);
  91. end;
  92. function ApplyGamma(Color: TColor32): TColor32;
  93. begin
  94. TColor32Entry(Result).R := GAMMA_ENCODING_TABLE[TColor32Entry(Color).R];
  95. TColor32Entry(Result).G := GAMMA_ENCODING_TABLE[TColor32Entry(Color).G];
  96. TColor32Entry(Result).B := GAMMA_ENCODING_TABLE[TColor32Entry(Color).B];
  97. end;
  98. function ApplyInvGamma(Color: TColor32): TColor32;
  99. begin
  100. TColor32Entry(Result).R := GAMMA_DECODING_TABLE[TColor32Entry(Color).R];
  101. TColor32Entry(Result).G := GAMMA_DECODING_TABLE[TColor32Entry(Color).G];
  102. TColor32Entry(Result).B := GAMMA_DECODING_TABLE[TColor32Entry(Color).B];
  103. end;
  104. function ApplyCustomGamma(Color: TColor32; GammaTable: TGammaTable8Bit): TColor32;
  105. begin
  106. TColor32Entry(Result).R := GammaTable[TColor32Entry(Color).R];
  107. TColor32Entry(Result).G := GammaTable[TColor32Entry(Color).G];
  108. TColor32Entry(Result).B := GammaTable[TColor32Entry(Color).B];
  109. end;
  110. procedure ApplyGamma(Color: PColor32Array; Length: Integer);
  111. var
  112. Index: Integer;
  113. begin
  114. for Index := 0 to Length - 1 do
  115. begin
  116. PColor32Entry(Color)^.R := GAMMA_ENCODING_TABLE[PColor32Entry(Color)^.R];
  117. PColor32Entry(Color)^.G := GAMMA_ENCODING_TABLE[PColor32Entry(Color)^.G];
  118. PColor32Entry(Color)^.B := GAMMA_ENCODING_TABLE[PColor32Entry(Color)^.B];
  119. Inc(Color);
  120. end;
  121. end;
  122. procedure ApplyInvGamma(Color: PColor32Array; Length: Integer);
  123. var
  124. Index: Integer;
  125. begin
  126. for Index := 0 to Length - 1 do
  127. begin
  128. PColor32Entry(Color)^.R := GAMMA_DECODING_TABLE[PColor32Entry(Color)^.R];
  129. PColor32Entry(Color)^.G := GAMMA_DECODING_TABLE[PColor32Entry(Color)^.G];
  130. PColor32Entry(Color)^.B := GAMMA_DECODING_TABLE[PColor32Entry(Color)^.B];
  131. Inc(Color);
  132. end;
  133. end;
  134. procedure ApplyCustomGamma(Color: PColor32Array; Length: Integer; GammaTable: TGammaTable8Bit);
  135. var
  136. Index: Integer;
  137. begin
  138. for Index := 0 to Length - 1 do
  139. begin
  140. PColor32Entry(Color)^.R := GammaTable[PColor32Entry(Color)^.R];
  141. PColor32Entry(Color)^.G := GammaTable[PColor32Entry(Color)^.G];
  142. PColor32Entry(Color)^.B := GammaTable[PColor32Entry(Color)^.B];
  143. Inc(Color);
  144. end;
  145. end;
  146. procedure ApplyGamma(Bitmap: TBitmap32);
  147. begin
  148. ApplyGamma(Bitmap.Bits, Bitmap.Width * Bitmap.Height);
  149. end;
  150. procedure ApplyInvGamma(Bitmap: TBitmap32);
  151. begin
  152. ApplyInvGamma(Bitmap.Bits, Bitmap.Width * Bitmap.Height);
  153. end;
  154. procedure ApplyCustomGamma(Bitmap: TBitmap32; GammaTable: TGammaTable8Bit);
  155. begin
  156. ApplyCustomGamma(Bitmap.Bits, Bitmap.Width * Bitmap.Height, GammaTable);
  157. end;
  158. procedure ApplyCustomGamma(Bitmap: TBitmap32; Gamma: Double);
  159. var
  160. GammaTable: TGammaTable8Bit;
  161. begin
  162. if GAMMA_VALUE = Gamma then
  163. ApplyGamma(Bitmap.Bits, Bitmap.Width * Bitmap.Height)
  164. else
  165. begin
  166. SetGamma(Gamma, GammaTable);
  167. ApplyCustomGamma(Bitmap.Bits, Bitmap.Width * Bitmap.Height, GammaTable);
  168. end;
  169. end;
  170. { Gamma / Pixel Shape Correction table }
  171. procedure SetGamma;
  172. begin
  173. SetGamma(DEFAULT_GAMMA);
  174. end;
  175. procedure SetGamma(Gamma: Double);
  176. var
  177. GammaChangedProc: TGammaChangedProc;
  178. begin
  179. if (IsZero(Gamma)) then
  180. exit;
  181. GAMMA_VALUE := Gamma;
  182. GAMMA_IS_SRGB := False;
  183. // calculate default gamma tables
  184. SetGamma(1 / Gamma, GAMMA_ENCODING_TABLE);
  185. SetGamma(Gamma, GAMMA_DECODING_TABLE);
  186. if (GammaChangedDelegates <> nil) then
  187. for GammaChangedProc in GammaChangedDelegates do
  188. GammaChangedProc;
  189. end;
  190. procedure SetGamma(Gamma: Double; var GammaTable: TGammaTable8Bit);
  191. var
  192. i: Integer;
  193. begin
  194. for i := 0 to $FF do
  195. GammaTable[i] := Round($FF * Power(i * COne255th, Gamma));
  196. end;
  197. procedure Set_sRGB;
  198. var
  199. GammaChangedProc: TGammaChangedProc;
  200. begin
  201. Set_sRGB(GAMMA_ENCODING_TABLE);
  202. SetInv_sRGB(GAMMA_DECODING_TABLE);
  203. GAMMA_IS_SRGB := True;
  204. if (GammaChangedDelegates <> nil) then
  205. for GammaChangedProc in GammaChangedDelegates do
  206. GammaChangedProc;
  207. end;
  208. procedure Set_sRGB(var GammaTable: TGammaTable8Bit);
  209. var
  210. i: Integer;
  211. Value: Double;
  212. const
  213. CExp = 1 / 2.4;
  214. begin
  215. for i := 0 to $FF do
  216. begin
  217. Value := i * COne255th;
  218. if (Value < 0.0031308) then
  219. GammaTable[i] := Round($FF * Value * 12.92)
  220. else
  221. GammaTable[i] := Round($FF * (1.055 * Power(Value, CExp) - 0.055));
  222. end;
  223. end;
  224. procedure SetInv_sRGB(var GammaTable: TGammaTable8Bit);
  225. var
  226. i: Integer;
  227. Value: Double;
  228. begin
  229. for i := 0 to $FF do
  230. begin
  231. Value := i * COne255th;
  232. if (Value < 0.004045) then
  233. GammaTable[i] := Round($FF * Value / 12.92)
  234. else
  235. GammaTable[i] := Round($FF * Power((Value + 0.055) / 1.055, 2.4));
  236. end;
  237. end;
  238. initialization
  239. finalization
  240. FreeAndNil(GammaChangedDelegates);
  241. end.