123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611 |
- using Unity.Jobs;
- using Unity.Burst;
- using UnityEngine;
- using Unity.Collections;
- using System;
- using Unity.Collections.LowLevel.Unsafe;
-
- namespace PaintDotNet.Data.PhotoshopFileType
- {
- #region PDNDecodeJob
-
- internal struct PDNDecoderData
- {
- public PDNWrapper.Rectangle Rect;
- public int SurfaceWidth;
- public int SurfaceByteDepth;
- public DecodeType DecoderType;
-
- [NativeDisableParallelForRestriction]
- [ReadOnly]
- public NativeArray<byte> ColorChannel0;
-
- [NativeDisableParallelForRestriction]
- [ReadOnly]
- public NativeArray<byte> ColorChannel1;
-
- [NativeDisableParallelForRestriction]
- [ReadOnly]
- public NativeArray<byte> ColorChannel2;
-
- [NativeDisableParallelForRestriction]
- [ReadOnly]
- public NativeArray<byte> ColorChannel3;
-
- [NativeDisableParallelForRestriction]
- [ReadOnly]
- [DeallocateOnJobCompletion]
- public NativeArray<byte> ColorModeData;
-
- // Outputs
- [NativeDisableParallelForRestriction]
- public NativeArray<Color32> DecodedImage;
-
- }
-
- [BurstCompile]
- internal struct PDNDecoderJob : IJobParallelFor
- {
- public PDNDecoderData data;
-
- public void Execute(int index)
- {
- var idx = data.Rect.Top + index;
- {
- // Calculate index into ImageData source from row and column.
- var idxSrcPixel = (idx - data.Rect.Top) * data.Rect.Width + (data.Rect.Left - data.Rect.Left);
- var idxSrcBytes = idxSrcPixel * data.SurfaceByteDepth;
-
- // Calculate pointers to destination Surface.
- var idxDstStart = idx * data.SurfaceWidth + data.Rect.Left;
- var idxDstStops = idx * data.SurfaceWidth + data.Rect.Right;
-
- // For 16-bit images, take the higher-order byte from the image data, which is now in little-endian order.
- if (data.SurfaceByteDepth == 2)
- {
- idxSrcBytes++;
- }
-
- switch (data.DecoderType)
- {
- case DecodeType.RGB32:
- {
- SetPDNRowRgb32(idxDstStart, idxDstStops, idxSrcBytes);
- }
- break;
- case DecodeType.Grayscale32:
- {
- SetPDNRowGrayscale32(idxDstStart, idxDstStops, idxSrcBytes);
- }
- break;
- case DecodeType.RGB:
- {
- SetPDNRowRgb(idxDstStart, idxDstStops, idxSrcBytes);
- }
- break;
- case DecodeType.CMYK:
- {
- SetPDNRowCmyk(idxDstStart, idxDstStops, idxSrcBytes);
- }
- break;
- case DecodeType.Bitmap:
- {
- SetPDNRowBitmap(idxDstStart, idxDstStops, idxSrcBytes);
- }
- break;
- case DecodeType.Grayscale:
- {
- SetPDNRowGrayscale(idxDstStart, idxDstStops, idxSrcBytes);
- }
- break;
- case DecodeType.Indexed:
- {
- SetPDNRowIndexed(idxDstStart, idxDstStops, idxSrcBytes);
- }
- break;
- case DecodeType.Lab:
- {
- SetPDNRowLab(idxDstStart, idxDstStops, idxSrcBytes);
- }
- break;
- }
- }
- }
-
- // Case 0:
- private void SetPDNRowRgb32(int dstStart, int dstStops, int idxSrc)
- {
- NativeArray<float> cR = data.ColorChannel0.Reinterpret<float>(1);
- NativeArray<float> cG = data.ColorChannel1.Reinterpret<float>(1);
- NativeArray<float> cB = data.ColorChannel2.Reinterpret<float>(1);
- var c = data.DecodedImage[dstStart];
- while (dstStart < dstStops)
- {
- c.r = ImageDecoderPdn.RGBByteFromHDRFloat(cR[idxSrc / 4]);
- c.g = ImageDecoderPdn.RGBByteFromHDRFloat(cG[idxSrc / 4]);
- c.b = ImageDecoderPdn.RGBByteFromHDRFloat(cB[idxSrc / 4]);
- data.DecodedImage[dstStart] = c;
-
- dstStart++;
- idxSrc += 4;
- }
- }
-
- // Case 1:
- private void SetPDNRowGrayscale32(int dstStart, int dstStops, int idxSrc)
- {
- NativeArray<float> channel = data.ColorChannel0.Reinterpret<float>(1);
- var c = data.DecodedImage[dstStart];
- while (dstStart < dstStops)
- {
- byte rgbValue = ImageDecoderPdn.RGBByteFromHDRFloat(channel[idxSrc / 4]);
- c.r = rgbValue;
- c.g = rgbValue;
- c.b = rgbValue;
- data.DecodedImage[dstStart] = c;
-
- dstStart++;
- idxSrc += 4;
- }
- }
-
- // Case 2:
- private void SetPDNRowRgb(int dstStart, int dstStops, int idxSrc)
- {
- var c = data.DecodedImage[dstStart];
- while (dstStart < dstStops)
- {
- c.r = data.ColorChannel0[idxSrc];
- c.g = data.ColorChannel1[idxSrc];
- c.b = data.ColorChannel2[idxSrc];
- data.DecodedImage[dstStart] = c;
-
- dstStart++;
- idxSrc += data.SurfaceByteDepth;
- }
- }
-
- // Case 3:
- ///////////////////////////////////////////////////////////////////////////////
- //
- // The color-conversion formulas come from the Colour Space Conversions FAQ:
- // http://www.poynton.com/PDFs/coloureq.pdf
- //
- // RGB --> CMYK CMYK --> RGB
- // --------------------------------------- --------------------------------------------
- // Black = minimum(1-Red,1-Green,1-Blue) Red = 1-minimum(1,Cyan*(1-Black)+Black)
- // Cyan = (1-Red-Black)/(1-Black) Green = 1-minimum(1,Magenta*(1-Black)+Black)
- // Magenta = (1-Green-Black)/(1-Black) Blue = 1-minimum(1,Yellow*(1-Black)+Black)
- // Yellow = (1-Blue-Black)/(1-Black)
- //
- ///////////////////////////////////////////////////////////////////////////////
- private void SetPDNRowCmyk(int dstStart, int dstStops, int idxSrc)
- {
- var c = data.DecodedImage[dstStart];
- while (dstStart < dstStops)
- {
- // CMYK values are stored as complements, presumably to allow for some
- // measure of compatibility with RGB-only applications.
- var C = 255 - data.ColorChannel0[idxSrc];
- var M = 255 - data.ColorChannel1[idxSrc];
- var Y = 255 - data.ColorChannel2[idxSrc];
- var K = 255 - data.ColorChannel3[idxSrc];
-
- int R = 255 - Math.Min(255, C * (255 - K) / 255 + K);
- int G = 255 - Math.Min(255, M * (255 - K) / 255 + K);
- int B = 255 - Math.Min(255, Y * (255 - K) / 255 + K);
-
- c.r = (byte)R;
- c.g = (byte)G;
- c.b = (byte)B;
- data.DecodedImage[dstStart] = c;
-
- dstStart++;
- idxSrc += data.SurfaceByteDepth;
- }
- }
-
- // Case 4:
- private void SetPDNRowBitmap(int dstStart, int dstStops, int idxSrc)
- {
- var c = data.DecodedImage[dstStart];
- while (dstStart < dstStops)
- {
- byte mask = (byte)(0x80 >> (idxSrc % 8));
- byte bwValue = (byte)(data.ColorChannel0[idxSrc / 8] & mask);
- bwValue = (bwValue == 0) ? (byte)255 : (byte)0;
-
- c.r = bwValue;
- c.g = bwValue;
- c.b = bwValue;
- data.DecodedImage[dstStart] = c;
-
- dstStart++;
- idxSrc += data.SurfaceByteDepth;
- }
- }
-
- // Case 5:
- private void SetPDNRowGrayscale(int dstStart, int dstStops, int idxSrc)
- {
- var c = data.DecodedImage[dstStart];
- while (dstStart < dstStops)
- {
-
- c.r = data.ColorChannel0[idxSrc];
- c.g = data.ColorChannel0[idxSrc];
- c.b = data.ColorChannel0[idxSrc];
- data.DecodedImage[dstStart] = c;
-
- dstStart++;
- idxSrc += data.SurfaceByteDepth;
- }
- }
-
- // Case 6:
- private void SetPDNRowIndexed(int dstStart, int dstStops, int idxSrc)
- {
- var c = data.DecodedImage[dstStart];
- int index = (int)data.ColorChannel0[idxSrc];
- while (dstStart < dstStops)
- {
- c.r = data.ColorModeData[index];
- c.g = data.ColorModeData[index + 256];
- c.b = data.ColorModeData[index + 2 * 256];
- data.DecodedImage[dstStart] = c;
-
- dstStart++;
- idxSrc += data.SurfaceByteDepth;
- }
- }
-
- // Case 7:
- private void SetPDNRowLab(int dstStart, int dstStops, int idxSrc)
- {
- var c = data.DecodedImage[dstStart];
- while (dstStart < dstStops)
- {
- double exL, exA, exB;
- exL = (double)data.ColorChannel0[idxSrc];
- exA = (double)data.ColorChannel1[idxSrc];
- exB = (double)data.ColorChannel2[idxSrc];
-
- int L = (int)(exL / 2.55);
- int a = (int)(exA - 127.5);
- int b = (int)(exB - 127.5);
-
- // First, convert from Lab to XYZ.
- // Standards used Observer = 2, Illuminant = D65
-
- const double ref_X = 95.047;
- const double ref_Y = 100.000;
- const double ref_Z = 108.883;
-
- double var_Y = ((double)L + 16.0) / 116.0;
- double var_X = (double)a / 500.0 + var_Y;
- double var_Z = var_Y - (double)b / 200.0;
-
- double var_X3 = var_X * var_X * var_X;
- double var_Y3 = var_Y * var_Y * var_Y;
- double var_Z3 = var_Z * var_Z * var_Z;
-
- if (var_Y3 > 0.008856)
- var_Y = var_Y3;
- else
- var_Y = (var_Y - 16 / 116) / 7.787;
-
- if (var_X3 > 0.008856)
- var_X = var_X3;
- else
- var_X = (var_X - 16 / 116) / 7.787;
-
- if (var_Z3 > 0.008856)
- var_Z = var_Z3;
- else
- var_Z = (var_Z - 16 / 116) / 7.787;
-
- double X = ref_X * var_X;
- double Y = ref_Y * var_Y;
- double Z = ref_Z * var_Z;
-
- // Then, convert from XYZ to RGB.
- // Standards used Observer = 2, Illuminant = D65
- // ref_X = 95.047, ref_Y = 100.000, ref_Z = 108.883
-
- double var_R = X * 0.032406 + Y * (-0.015372) + Z * (-0.004986);
- double var_G = X * (-0.009689) + Y * 0.018758 + Z * 0.000415;
- double var_B = X * 0.000557 + Y * (-0.002040) + Z * 0.010570;
-
- if (var_R > 0.0031308)
- var_R = 1.055 * (Math.Pow(var_R, 1 / 2.4)) - 0.055;
- else
- var_R = 12.92 * var_R;
-
- if (var_G > 0.0031308)
- var_G = 1.055 * (Math.Pow(var_G, 1 / 2.4)) - 0.055;
- else
- var_G = 12.92 * var_G;
-
- if (var_B > 0.0031308)
- var_B = 1.055 * (Math.Pow(var_B, 1 / 2.4)) - 0.055;
- else
- var_B = 12.92 * var_B;
-
- int nRed = (int)(var_R * 256.0);
- int nGreen = (int)(var_G * 256.0);
- int nBlue = (int)(var_B * 256.0);
-
- if (nRed < 0)
- nRed = 0;
- else if (nRed > 255)
- nRed = 255;
- if (nGreen < 0)
- nGreen = 0;
- else if (nGreen > 255)
- nGreen = 255;
- if (nBlue < 0)
- nBlue = 0;
- else if (nBlue > 255)
- nBlue = 255;
-
- c.r = (byte)nRed;
- c.g = (byte)nGreen;
- c.b = (byte)nBlue;
- data.DecodedImage[dstStart] = c;
-
- dstStart++;
- idxSrc += data.SurfaceByteDepth;
- }
- }
- }
-
- #endregion
-
- #region AlphaDecodeJob
-
- internal struct PDNAlphaMaskData
- {
- public PDNWrapper.Rectangle Rect;
- public int SurfaceWidth;
- public int SurfaceByteDepth;
-
- public int HasAlphaChannel;
- public int HasUserAlphaMask;
- public int UserMaskInvertOnBlend;
- public PDNWrapper.Rectangle UserMaskRect;
- public PDNWrapper.Rectangle UserMaskContextRect;
- public int HasLayerAlphaMask;
- public int LayerMaskInvertOnBlend;
- public PDNWrapper.Rectangle LayerMaskRect;
- public PDNWrapper.Rectangle LayerMaskContextRect;
-
- [NativeDisableParallelForRestriction]
- [ReadOnly]
- [DeallocateOnJobCompletion]
- public NativeArray<byte> AlphaChannel0;
-
- [NativeDisableParallelForRestriction]
- [ReadOnly]
- public NativeArray<byte> UserMask;
-
- [DeallocateOnJobCompletion]
- [NativeDisableParallelForRestriction]
- public NativeArray<byte> UserAlphaMask;
-
- [DeallocateOnJobCompletion]
- [NativeDisableParallelForRestriction]
- public NativeArray<byte> UserAlphaMaskEmpty;
-
- [NativeDisableParallelForRestriction]
- [ReadOnly]
- public NativeArray<byte> LayerMask;
-
- [DeallocateOnJobCompletion]
- [NativeDisableParallelForRestriction]
- public NativeArray<byte> LayerAlphaMask;
-
- [DeallocateOnJobCompletion]
- [NativeDisableParallelForRestriction]
- public NativeArray<byte> LayerAlphaMaskEmpty;
-
- // Outputs
- [NativeDisableParallelForRestriction]
- public NativeArray<Color32> DecodedImage;
-
- // Colors.
- public byte UserMaskBackgroundColor;
- public byte LayerMaskBackgroundColor;
- }
-
- [BurstCompile]
- internal struct PDNAlphaMaskJob : IJob
- {
- public PDNAlphaMaskData data;
-
- public void Execute()
- {
- for (var idx = data.Rect.Top; idx < data.Rect.Bottom; idx++)
- {
- // Calculate index into ImageData source from row and column.
- var idxSrcPixel = (idx - data.Rect.Top) * data.Rect.Width + (data.Rect.Left - data.Rect.Left);
- var idxSrcBytes = idxSrcPixel * data.SurfaceByteDepth;
-
- // Calculate pointers to destination Surface.
- var idxDstStart = idx * data.SurfaceWidth + data.Rect.Left;
- var idxDstStops = idx * data.SurfaceWidth + data.Rect.Right;
-
- // For 16-bit images, take the higher-order byte from the image data, which is now in little-endian order.
- if (data.SurfaceByteDepth == 2)
- {
- idxSrcBytes++;
- }
-
- SetPDNAlphaRow(idxDstStart, idxDstStops, idxSrcBytes);
-
- if (0 != data.HasLayerAlphaMask)
- {
- GetMaskAlphaRow(idx, data.LayerAlphaMask, data.LayerAlphaMaskEmpty, data.LayerMask, data.LayerMaskInvertOnBlend, data.LayerMaskBackgroundColor, data.LayerMaskContextRect, data.LayerMaskRect);
- }
- if (0 != data.HasUserAlphaMask)
- {
- GetMaskAlphaRow(idx, data.UserAlphaMask, data.UserAlphaMaskEmpty, data.UserMask, data.UserMaskInvertOnBlend, data.UserMaskBackgroundColor, data.UserMaskContextRect, data.UserMaskRect);
- }
-
- ApplyPDNMask(idxDstStart, idxDstStops);
- }
- }
-
- private void SetPDNAlphaRow(int dstStart, int dstStops, int idxSrc)
- {
- // Set alpha to fully-opaque if there is no alpha channel
- if (0 == data.HasAlphaChannel)
- {
- while (dstStart < dstStops)
- {
- var c = data.DecodedImage[dstStart];
- c.a = 255;
- data.DecodedImage[dstStart] = c;
- dstStart++;
- }
- }
- // Set the alpha channel data
- else
- {
- NativeArray<float> srcAlphaChannel = data.AlphaChannel0.Reinterpret<float>(1);
- {
- while (dstStart < dstStops)
- {
- var c = data.DecodedImage[dstStart];
- c.a = (data.SurfaceByteDepth < 4) ? data.AlphaChannel0[idxSrc] : ImageDecoderPdn.RGBByteFromHDRFloat(srcAlphaChannel[idxSrc / 4]);
-
- data.DecodedImage[dstStart] = c;
- dstStart++;
- idxSrc += data.SurfaceByteDepth;
- }
- }
- }
- }
-
- private void ApplyPDNMask(int dstStart, int dstStops)
- {
- // Do nothing if there are no masks
- if (0 == data.HasLayerAlphaMask && 0 == data.HasUserAlphaMask)
- {
- return;
- }
-
- // Apply one mask
- else if (0 == data.HasLayerAlphaMask || 0 == data.HasUserAlphaMask)
- {
- var maskAlpha = (0 == data.HasLayerAlphaMask) ? data.UserAlphaMask : data.LayerAlphaMask;
- var maskStart = 0;
- {
- while (dstStart < dstStops)
- {
- var c = data.DecodedImage[dstStart];
- c.a = (byte)(data.DecodedImage[dstStart].a * maskAlpha[maskStart] / 255);
- data.DecodedImage[dstStart] = c;
-
- dstStart++;
- maskStart++;
- }
- }
- }
- // Apply both masks in one pass, to minimize rounding error
- else
- {
- var maskStart = 0;
- {
- while (dstStart < dstStops)
- {
- var c = data.DecodedImage[dstStart];
- var alphaFactor = (data.LayerAlphaMask[maskStart]) * (data.UserAlphaMask[maskStart]);
- c.a = (byte)(data.DecodedImage[dstStart].a * alphaFactor / 65025);
- data.DecodedImage[dstStart] = c;
-
- dstStart++;
- maskStart++;
- }
- }
- }
- }
-
- private void DecodeMaskAlphaRow32(NativeArray<byte> Alpha, int dstStart, int dstStops, NativeArray<byte> Mask, int maskStart)
- {
- NativeArray<float> floatArray = Mask.Reinterpret<float>(1);
-
- while (dstStart < dstStops)
- {
- Alpha[dstStart] = ImageDecoderPdn.RGBByteFromHDRFloat(floatArray[maskStart / 4]);
-
- dstStart++;
- maskStart += 4;
- }
- }
-
- private void DecodeMaskAlphaRow(NativeArray<byte> Alpha, int dstStart, int dstStops, NativeArray<byte> Mask, int maskStart, int byteDepth)
- {
- while (dstStart < dstStops)
- {
- Alpha[dstStart] = Mask[maskStart];
-
- dstStart++;
- maskStart += byteDepth;
- }
- }
-
- 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)
- {
- //////////////////////////////////////
- // Transfer mask into the alpha array
- // Background color for areas not covered by the mask
- var backgroundColor = (0 != MaskInvertOnBlend) ? (byte)(255 - MaskBackgroundColor) : MaskBackgroundColor;
- {
- var alphaBufferPtr = NativeArrayUnsafeUtility.GetUnsafeBufferPointerWithoutChecks(alphaBuffer);
- UnsafeUtility.MemSet(alphaBufferPtr, backgroundColor, alphaBuffer.Length);
- }
- // Only process if not Empty.
- if (alphaBufferEmpty[idxSrc] == 0)
- {
- // Get pointers to starting positions
- var alphaColumn = MaskContextRect.X;
- // It's possible that the layer's rect is larger than the clip and it's offset.
- // Since we only copy out the alpha based on the MaskContext size
- // The copy will start from where the MaskContextRect is
- if(data.Rect.X > 0)
- alphaColumn = MaskContextRect.X - data.Rect.X;
- var pAlpha = alphaColumn;
- var pAlphaEnd = pAlpha + MaskContextRect.Width;
-
- var maskRow = idxSrc - MaskRect.Y;
- var maskColumn = MaskContextRect.X - MaskRect.X;
- var idxMaskPixel = (maskRow * MaskRect.Width) + maskColumn;
- var pMask = idxMaskPixel * data.SurfaceByteDepth;
-
- // Take the high-order byte if values are 16-bit (little-endian)
- if (data.SurfaceByteDepth == 2)
- {
- pMask++;
- }
-
- // Decode mask into the alpha array.
- if (data.SurfaceByteDepth == 4)
- {
- DecodeMaskAlphaRow32(alphaBuffer, pAlpha, pAlphaEnd, maskChannel, pMask);
- }
- else
- {
- DecodeMaskAlphaRow(alphaBuffer, pAlpha, pAlphaEnd, maskChannel, pMask, data.SurfaceByteDepth);
- }
-
- // Obsolete since Photoshop CS6, but retained for compatibility with older versions. Note that the background has already been inverted.
- if (0 != MaskInvertOnBlend)
- {
- PhotoshopFile.Util.Invert(alphaBuffer, pAlpha, pAlphaEnd);
- }
- }
- }
- }
-
- #endregion
- }
|