PDNDecodeJob.cs 23 KB

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