Без опису
Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611
  1. using Unity.Jobs;
  2. using Unity.Burst;
  3. using UnityEngine;
  4. using Unity.Collections;
  5. using System;
  6. using Unity.Collections.LowLevel.Unsafe;
  7. namespace PaintDotNet.Data.PhotoshopFileType
  8. {
  9. #region PDNDecodeJob
  10. internal struct PDNDecoderData
  11. {
  12. public PDNWrapper.Rectangle Rect;
  13. public int SurfaceWidth;
  14. public int SurfaceByteDepth;
  15. public DecodeType DecoderType;
  16. [NativeDisableParallelForRestriction]
  17. [ReadOnly]
  18. public NativeArray<byte> ColorChannel0;
  19. [NativeDisableParallelForRestriction]
  20. [ReadOnly]
  21. public NativeArray<byte> ColorChannel1;
  22. [NativeDisableParallelForRestriction]
  23. [ReadOnly]
  24. public NativeArray<byte> ColorChannel2;
  25. [NativeDisableParallelForRestriction]
  26. [ReadOnly]
  27. public NativeArray<byte> ColorChannel3;
  28. [NativeDisableParallelForRestriction]
  29. [ReadOnly]
  30. [DeallocateOnJobCompletion]
  31. public NativeArray<byte> ColorModeData;
  32. // Outputs
  33. [NativeDisableParallelForRestriction]
  34. public NativeArray<Color32> DecodedImage;
  35. }
  36. [BurstCompile]
  37. internal struct PDNDecoderJob : IJobParallelFor
  38. {
  39. public PDNDecoderData data;
  40. public void Execute(int index)
  41. {
  42. var idx = data.Rect.Top + index;
  43. {
  44. // Calculate index into ImageData source from row and column.
  45. var idxSrcPixel = (idx - data.Rect.Top) * data.Rect.Width + (data.Rect.Left - data.Rect.Left);
  46. var idxSrcBytes = idxSrcPixel * data.SurfaceByteDepth;
  47. // Calculate pointers to destination Surface.
  48. var idxDstStart = idx * data.SurfaceWidth + data.Rect.Left;
  49. var idxDstStops = idx * data.SurfaceWidth + data.Rect.Right;
  50. // For 16-bit images, take the higher-order byte from the image data, which is now in little-endian order.
  51. if (data.SurfaceByteDepth == 2)
  52. {
  53. idxSrcBytes++;
  54. }
  55. switch (data.DecoderType)
  56. {
  57. case DecodeType.RGB32:
  58. {
  59. SetPDNRowRgb32(idxDstStart, idxDstStops, idxSrcBytes);
  60. }
  61. break;
  62. case DecodeType.Grayscale32:
  63. {
  64. SetPDNRowGrayscale32(idxDstStart, idxDstStops, idxSrcBytes);
  65. }
  66. break;
  67. case DecodeType.RGB:
  68. {
  69. SetPDNRowRgb(idxDstStart, idxDstStops, idxSrcBytes);
  70. }
  71. break;
  72. case DecodeType.CMYK:
  73. {
  74. SetPDNRowCmyk(idxDstStart, idxDstStops, idxSrcBytes);
  75. }
  76. break;
  77. case DecodeType.Bitmap:
  78. {
  79. SetPDNRowBitmap(idxDstStart, idxDstStops, idxSrcBytes);
  80. }
  81. break;
  82. case DecodeType.Grayscale:
  83. {
  84. SetPDNRowGrayscale(idxDstStart, idxDstStops, idxSrcBytes);
  85. }
  86. break;
  87. case DecodeType.Indexed:
  88. {
  89. SetPDNRowIndexed(idxDstStart, idxDstStops, idxSrcBytes);
  90. }
  91. break;
  92. case DecodeType.Lab:
  93. {
  94. SetPDNRowLab(idxDstStart, idxDstStops, idxSrcBytes);
  95. }
  96. break;
  97. }
  98. }
  99. }
  100. // Case 0:
  101. private void SetPDNRowRgb32(int dstStart, int dstStops, int idxSrc)
  102. {
  103. NativeArray<float> cR = data.ColorChannel0.Reinterpret<float>(1);
  104. NativeArray<float> cG = data.ColorChannel1.Reinterpret<float>(1);
  105. NativeArray<float> cB = data.ColorChannel2.Reinterpret<float>(1);
  106. var c = data.DecodedImage[dstStart];
  107. while (dstStart < dstStops)
  108. {
  109. c.r = ImageDecoderPdn.RGBByteFromHDRFloat(cR[idxSrc / 4]);
  110. c.g = ImageDecoderPdn.RGBByteFromHDRFloat(cG[idxSrc / 4]);
  111. c.b = ImageDecoderPdn.RGBByteFromHDRFloat(cB[idxSrc / 4]);
  112. data.DecodedImage[dstStart] = c;
  113. dstStart++;
  114. idxSrc += 4;
  115. }
  116. }
  117. // Case 1:
  118. private void SetPDNRowGrayscale32(int dstStart, int dstStops, int idxSrc)
  119. {
  120. NativeArray<float> channel = data.ColorChannel0.Reinterpret<float>(1);
  121. var c = data.DecodedImage[dstStart];
  122. while (dstStart < dstStops)
  123. {
  124. byte rgbValue = ImageDecoderPdn.RGBByteFromHDRFloat(channel[idxSrc / 4]);
  125. c.r = rgbValue;
  126. c.g = rgbValue;
  127. c.b = rgbValue;
  128. data.DecodedImage[dstStart] = c;
  129. dstStart++;
  130. idxSrc += 4;
  131. }
  132. }
  133. // Case 2:
  134. private void SetPDNRowRgb(int dstStart, int dstStops, int idxSrc)
  135. {
  136. var c = data.DecodedImage[dstStart];
  137. while (dstStart < dstStops)
  138. {
  139. c.r = data.ColorChannel0[idxSrc];
  140. c.g = data.ColorChannel1[idxSrc];
  141. c.b = data.ColorChannel2[idxSrc];
  142. data.DecodedImage[dstStart] = c;
  143. dstStart++;
  144. idxSrc += data.SurfaceByteDepth;
  145. }
  146. }
  147. // Case 3:
  148. ///////////////////////////////////////////////////////////////////////////////
  149. //
  150. // The color-conversion formulas come from the Colour Space Conversions FAQ:
  151. // http://www.poynton.com/PDFs/coloureq.pdf
  152. //
  153. // RGB --> CMYK CMYK --> RGB
  154. // --------------------------------------- --------------------------------------------
  155. // Black = minimum(1-Red,1-Green,1-Blue) Red = 1-minimum(1,Cyan*(1-Black)+Black)
  156. // Cyan = (1-Red-Black)/(1-Black) Green = 1-minimum(1,Magenta*(1-Black)+Black)
  157. // Magenta = (1-Green-Black)/(1-Black) Blue = 1-minimum(1,Yellow*(1-Black)+Black)
  158. // Yellow = (1-Blue-Black)/(1-Black)
  159. //
  160. ///////////////////////////////////////////////////////////////////////////////
  161. private void SetPDNRowCmyk(int dstStart, int dstStops, int idxSrc)
  162. {
  163. var c = data.DecodedImage[dstStart];
  164. while (dstStart < dstStops)
  165. {
  166. // CMYK values are stored as complements, presumably to allow for some
  167. // measure of compatibility with RGB-only applications.
  168. var C = 255 - data.ColorChannel0[idxSrc];
  169. var M = 255 - data.ColorChannel1[idxSrc];
  170. var Y = 255 - data.ColorChannel2[idxSrc];
  171. var K = 255 - data.ColorChannel3[idxSrc];
  172. int R = 255 - Math.Min(255, C * (255 - K) / 255 + K);
  173. int G = 255 - Math.Min(255, M * (255 - K) / 255 + K);
  174. int B = 255 - Math.Min(255, Y * (255 - K) / 255 + K);
  175. c.r = (byte)R;
  176. c.g = (byte)G;
  177. c.b = (byte)B;
  178. data.DecodedImage[dstStart] = c;
  179. dstStart++;
  180. idxSrc += data.SurfaceByteDepth;
  181. }
  182. }
  183. // Case 4:
  184. private void SetPDNRowBitmap(int dstStart, int dstStops, int idxSrc)
  185. {
  186. var c = data.DecodedImage[dstStart];
  187. while (dstStart < dstStops)
  188. {
  189. byte mask = (byte)(0x80 >> (idxSrc % 8));
  190. byte bwValue = (byte)(data.ColorChannel0[idxSrc / 8] & mask);
  191. bwValue = (bwValue == 0) ? (byte)255 : (byte)0;
  192. c.r = bwValue;
  193. c.g = bwValue;
  194. c.b = bwValue;
  195. data.DecodedImage[dstStart] = c;
  196. dstStart++;
  197. idxSrc += data.SurfaceByteDepth;
  198. }
  199. }
  200. // Case 5:
  201. private void SetPDNRowGrayscale(int dstStart, int dstStops, int idxSrc)
  202. {
  203. var c = data.DecodedImage[dstStart];
  204. while (dstStart < dstStops)
  205. {
  206. c.r = data.ColorChannel0[idxSrc];
  207. c.g = data.ColorChannel0[idxSrc];
  208. c.b = data.ColorChannel0[idxSrc];
  209. data.DecodedImage[dstStart] = c;
  210. dstStart++;
  211. idxSrc += data.SurfaceByteDepth;
  212. }
  213. }
  214. // Case 6:
  215. private void SetPDNRowIndexed(int dstStart, int dstStops, int idxSrc)
  216. {
  217. var c = data.DecodedImage[dstStart];
  218. int index = (int)data.ColorChannel0[idxSrc];
  219. while (dstStart < dstStops)
  220. {
  221. c.r = data.ColorModeData[index];
  222. c.g = data.ColorModeData[index + 256];
  223. c.b = data.ColorModeData[index + 2 * 256];
  224. data.DecodedImage[dstStart] = c;
  225. dstStart++;
  226. idxSrc += data.SurfaceByteDepth;
  227. }
  228. }
  229. // Case 7:
  230. private void SetPDNRowLab(int dstStart, int dstStops, int idxSrc)
  231. {
  232. var c = data.DecodedImage[dstStart];
  233. while (dstStart < dstStops)
  234. {
  235. double exL, exA, exB;
  236. exL = (double)data.ColorChannel0[idxSrc];
  237. exA = (double)data.ColorChannel1[idxSrc];
  238. exB = (double)data.ColorChannel2[idxSrc];
  239. int L = (int)(exL / 2.55);
  240. int a = (int)(exA - 127.5);
  241. int b = (int)(exB - 127.5);
  242. // First, convert from Lab to XYZ.
  243. // Standards used Observer = 2, Illuminant = D65
  244. const double ref_X = 95.047;
  245. const double ref_Y = 100.000;
  246. const double ref_Z = 108.883;
  247. double var_Y = ((double)L + 16.0) / 116.0;
  248. double var_X = (double)a / 500.0 + var_Y;
  249. double var_Z = var_Y - (double)b / 200.0;
  250. double var_X3 = var_X * var_X * var_X;
  251. double var_Y3 = var_Y * var_Y * var_Y;
  252. double var_Z3 = var_Z * var_Z * var_Z;
  253. if (var_Y3 > 0.008856)
  254. var_Y = var_Y3;
  255. else
  256. var_Y = (var_Y - 16 / 116) / 7.787;
  257. if (var_X3 > 0.008856)
  258. var_X = var_X3;
  259. else
  260. var_X = (var_X - 16 / 116) / 7.787;
  261. if (var_Z3 > 0.008856)
  262. var_Z = var_Z3;
  263. else
  264. var_Z = (var_Z - 16 / 116) / 7.787;
  265. double X = ref_X * var_X;
  266. double Y = ref_Y * var_Y;
  267. double Z = ref_Z * var_Z;
  268. // Then, convert from XYZ to RGB.
  269. // Standards used Observer = 2, Illuminant = D65
  270. // ref_X = 95.047, ref_Y = 100.000, ref_Z = 108.883
  271. double var_R = X * 0.032406 + Y * (-0.015372) + Z * (-0.004986);
  272. double var_G = X * (-0.009689) + Y * 0.018758 + Z * 0.000415;
  273. double var_B = X * 0.000557 + Y * (-0.002040) + Z * 0.010570;
  274. if (var_R > 0.0031308)
  275. var_R = 1.055 * (Math.Pow(var_R, 1 / 2.4)) - 0.055;
  276. else
  277. var_R = 12.92 * var_R;
  278. if (var_G > 0.0031308)
  279. var_G = 1.055 * (Math.Pow(var_G, 1 / 2.4)) - 0.055;
  280. else
  281. var_G = 12.92 * var_G;
  282. if (var_B > 0.0031308)
  283. var_B = 1.055 * (Math.Pow(var_B, 1 / 2.4)) - 0.055;
  284. else
  285. var_B = 12.92 * var_B;
  286. int nRed = (int)(var_R * 256.0);
  287. int nGreen = (int)(var_G * 256.0);
  288. int nBlue = (int)(var_B * 256.0);
  289. if (nRed < 0)
  290. nRed = 0;
  291. else if (nRed > 255)
  292. nRed = 255;
  293. if (nGreen < 0)
  294. nGreen = 0;
  295. else if (nGreen > 255)
  296. nGreen = 255;
  297. if (nBlue < 0)
  298. nBlue = 0;
  299. else if (nBlue > 255)
  300. nBlue = 255;
  301. c.r = (byte)nRed;
  302. c.g = (byte)nGreen;
  303. c.b = (byte)nBlue;
  304. data.DecodedImage[dstStart] = c;
  305. dstStart++;
  306. idxSrc += data.SurfaceByteDepth;
  307. }
  308. }
  309. }
  310. #endregion
  311. #region AlphaDecodeJob
  312. internal struct PDNAlphaMaskData
  313. {
  314. public PDNWrapper.Rectangle Rect;
  315. public int SurfaceWidth;
  316. public int SurfaceByteDepth;
  317. public int HasAlphaChannel;
  318. public int HasUserAlphaMask;
  319. public int UserMaskInvertOnBlend;
  320. public PDNWrapper.Rectangle UserMaskRect;
  321. public PDNWrapper.Rectangle UserMaskContextRect;
  322. public int HasLayerAlphaMask;
  323. public int LayerMaskInvertOnBlend;
  324. public PDNWrapper.Rectangle LayerMaskRect;
  325. public PDNWrapper.Rectangle LayerMaskContextRect;
  326. [NativeDisableParallelForRestriction]
  327. [ReadOnly]
  328. [DeallocateOnJobCompletion]
  329. public NativeArray<byte> AlphaChannel0;
  330. [NativeDisableParallelForRestriction]
  331. [ReadOnly]
  332. public NativeArray<byte> UserMask;
  333. [DeallocateOnJobCompletion]
  334. [NativeDisableParallelForRestriction]
  335. public NativeArray<byte> UserAlphaMask;
  336. [DeallocateOnJobCompletion]
  337. [NativeDisableParallelForRestriction]
  338. public NativeArray<byte> UserAlphaMaskEmpty;
  339. [NativeDisableParallelForRestriction]
  340. [ReadOnly]
  341. public NativeArray<byte> LayerMask;
  342. [DeallocateOnJobCompletion]
  343. [NativeDisableParallelForRestriction]
  344. public NativeArray<byte> LayerAlphaMask;
  345. [DeallocateOnJobCompletion]
  346. [NativeDisableParallelForRestriction]
  347. public NativeArray<byte> LayerAlphaMaskEmpty;
  348. // Outputs
  349. [NativeDisableParallelForRestriction]
  350. public NativeArray<Color32> DecodedImage;
  351. // Colors.
  352. public byte UserMaskBackgroundColor;
  353. public byte LayerMaskBackgroundColor;
  354. }
  355. [BurstCompile]
  356. internal struct PDNAlphaMaskJob : IJob
  357. {
  358. public PDNAlphaMaskData data;
  359. public void Execute()
  360. {
  361. for (var idx = data.Rect.Top; idx < data.Rect.Bottom; idx++)
  362. {
  363. // Calculate index into ImageData source from row and column.
  364. var idxSrcPixel = (idx - data.Rect.Top) * data.Rect.Width + (data.Rect.Left - data.Rect.Left);
  365. var idxSrcBytes = idxSrcPixel * data.SurfaceByteDepth;
  366. // Calculate pointers to destination Surface.
  367. var idxDstStart = idx * data.SurfaceWidth + data.Rect.Left;
  368. var idxDstStops = idx * data.SurfaceWidth + data.Rect.Right;
  369. // For 16-bit images, take the higher-order byte from the image data, which is now in little-endian order.
  370. if (data.SurfaceByteDepth == 2)
  371. {
  372. idxSrcBytes++;
  373. }
  374. SetPDNAlphaRow(idxDstStart, idxDstStops, idxSrcBytes);
  375. if (0 != data.HasLayerAlphaMask)
  376. {
  377. GetMaskAlphaRow(idx, data.LayerAlphaMask, data.LayerAlphaMaskEmpty, data.LayerMask, data.LayerMaskInvertOnBlend, data.LayerMaskBackgroundColor, data.LayerMaskContextRect, data.LayerMaskRect);
  378. }
  379. if (0 != data.HasUserAlphaMask)
  380. {
  381. GetMaskAlphaRow(idx, data.UserAlphaMask, data.UserAlphaMaskEmpty, data.UserMask, data.UserMaskInvertOnBlend, data.UserMaskBackgroundColor, data.UserMaskContextRect, data.UserMaskRect);
  382. }
  383. ApplyPDNMask(idxDstStart, idxDstStops);
  384. }
  385. }
  386. private void SetPDNAlphaRow(int dstStart, int dstStops, int idxSrc)
  387. {
  388. // Set alpha to fully-opaque if there is no alpha channel
  389. if (0 == data.HasAlphaChannel)
  390. {
  391. while (dstStart < dstStops)
  392. {
  393. var c = data.DecodedImage[dstStart];
  394. c.a = 255;
  395. data.DecodedImage[dstStart] = c;
  396. dstStart++;
  397. }
  398. }
  399. // Set the alpha channel data
  400. else
  401. {
  402. NativeArray<float> srcAlphaChannel = data.AlphaChannel0.Reinterpret<float>(1);
  403. {
  404. while (dstStart < dstStops)
  405. {
  406. var c = data.DecodedImage[dstStart];
  407. c.a = (data.SurfaceByteDepth < 4) ? data.AlphaChannel0[idxSrc] : ImageDecoderPdn.RGBByteFromHDRFloat(srcAlphaChannel[idxSrc / 4]);
  408. data.DecodedImage[dstStart] = c;
  409. dstStart++;
  410. idxSrc += data.SurfaceByteDepth;
  411. }
  412. }
  413. }
  414. }
  415. private void ApplyPDNMask(int dstStart, int dstStops)
  416. {
  417. // Do nothing if there are no masks
  418. if (0 == data.HasLayerAlphaMask && 0 == data.HasUserAlphaMask)
  419. {
  420. return;
  421. }
  422. // Apply one mask
  423. else if (0 == data.HasLayerAlphaMask || 0 == data.HasUserAlphaMask)
  424. {
  425. var maskAlpha = (0 == data.HasLayerAlphaMask) ? data.UserAlphaMask : data.LayerAlphaMask;
  426. var maskStart = 0;
  427. {
  428. while (dstStart < dstStops)
  429. {
  430. var c = data.DecodedImage[dstStart];
  431. c.a = (byte)(data.DecodedImage[dstStart].a * maskAlpha[maskStart] / 255);
  432. data.DecodedImage[dstStart] = c;
  433. dstStart++;
  434. maskStart++;
  435. }
  436. }
  437. }
  438. // Apply both masks in one pass, to minimize rounding error
  439. else
  440. {
  441. var maskStart = 0;
  442. {
  443. while (dstStart < dstStops)
  444. {
  445. var c = data.DecodedImage[dstStart];
  446. var alphaFactor = (data.LayerAlphaMask[maskStart]) * (data.UserAlphaMask[maskStart]);
  447. c.a = (byte)(data.DecodedImage[dstStart].a * alphaFactor / 65025);
  448. data.DecodedImage[dstStart] = c;
  449. dstStart++;
  450. maskStart++;
  451. }
  452. }
  453. }
  454. }
  455. private void DecodeMaskAlphaRow32(NativeArray<byte> Alpha, int dstStart, int dstStops, NativeArray<byte> Mask, int maskStart)
  456. {
  457. NativeArray<float> floatArray = Mask.Reinterpret<float>(1);
  458. while (dstStart < dstStops)
  459. {
  460. Alpha[dstStart] = ImageDecoderPdn.RGBByteFromHDRFloat(floatArray[maskStart / 4]);
  461. dstStart++;
  462. maskStart += 4;
  463. }
  464. }
  465. private void DecodeMaskAlphaRow(NativeArray<byte> Alpha, int dstStart, int dstStops, NativeArray<byte> Mask, int maskStart, int byteDepth)
  466. {
  467. while (dstStart < dstStops)
  468. {
  469. Alpha[dstStart] = Mask[maskStart];
  470. dstStart++;
  471. maskStart += byteDepth;
  472. }
  473. }
  474. private unsafe void GetMaskAlphaRow(int idxSrc, NativeArray<byte> alphaBuffer, NativeArray<byte> alphaBufferEmpty, NativeArray<byte> maskChannel, int MaskInvertOnBlend, byte MaskBackgroundColor, PDNWrapper.Rectangle MaskContextRect, PDNWrapper.Rectangle MaskRect)
  475. {
  476. //////////////////////////////////////
  477. // Transfer mask into the alpha array
  478. // Background color for areas not covered by the mask
  479. var backgroundColor = (0 != MaskInvertOnBlend) ? (byte)(255 - MaskBackgroundColor) : MaskBackgroundColor;
  480. {
  481. var alphaBufferPtr = NativeArrayUnsafeUtility.GetUnsafeBufferPointerWithoutChecks(alphaBuffer);
  482. UnsafeUtility.MemSet(alphaBufferPtr, backgroundColor, alphaBuffer.Length);
  483. }
  484. // Only process if not Empty.
  485. if (alphaBufferEmpty[idxSrc] == 0)
  486. {
  487. // Get pointers to starting positions
  488. var alphaColumn = MaskContextRect.X;
  489. // It's possible that the layer's rect is larger than the clip and it's offset.
  490. // Since we only copy out the alpha based on the MaskContext size
  491. // The copy will start from where the MaskContextRect is
  492. if(data.Rect.X > 0)
  493. alphaColumn = MaskContextRect.X - data.Rect.X;
  494. var pAlpha = alphaColumn;
  495. var pAlphaEnd = pAlpha + MaskContextRect.Width;
  496. var maskRow = idxSrc - MaskRect.Y;
  497. var maskColumn = MaskContextRect.X - MaskRect.X;
  498. var idxMaskPixel = (maskRow * MaskRect.Width) + maskColumn;
  499. var pMask = idxMaskPixel * data.SurfaceByteDepth;
  500. // Take the high-order byte if values are 16-bit (little-endian)
  501. if (data.SurfaceByteDepth == 2)
  502. {
  503. pMask++;
  504. }
  505. // Decode mask into the alpha array.
  506. if (data.SurfaceByteDepth == 4)
  507. {
  508. DecodeMaskAlphaRow32(alphaBuffer, pAlpha, pAlphaEnd, maskChannel, pMask);
  509. }
  510. else
  511. {
  512. DecodeMaskAlphaRow(alphaBuffer, pAlpha, pAlphaEnd, maskChannel, pMask, data.SurfaceByteDepth);
  513. }
  514. // Obsolete since Photoshop CS6, but retained for compatibility with older versions. Note that the background has already been inverted.
  515. if (0 != MaskInvertOnBlend)
  516. {
  517. PhotoshopFile.Util.Invert(alphaBuffer, pAlpha, pAlphaEnd);
  518. }
  519. }
  520. }
  521. }
  522. #endregion
  523. }