Пример #1
0
        // Use this for initialization
        public static void Generate(Color[] Source, int width, int height, int Spread, int ScaleDownFactor)
        {
            // Setup Timer for debug purposes
            timer = new System.Diagnostics.Stopwatch();
            timer.Start();

            // Pixel Arrays which will contain the results of the distance transform of the Inner & Outer masks.
            innerMask = new Pixel[width * height];
            outerMask = new Pixel[width * height];


            // Event Handler for ThreadPool.
            _WaitHandles = new EventWaitHandle[] { new AutoResetEvent(false), new AutoResetEvent(false) };

            // Calculate Distance Transform for Inner Mask
            ThreadPool.QueueUserWorkItem(InnerMask =>
            {
                //System.TimeSpan timeStamp = timer.Elapsed;

                innerMask = Calculate3X3EDT(Source, 1, width, height, Spread);
                //Debug.Log("Inner Mask processed in [" + (timer.Elapsed - timeStamp) + "]");

                _WaitHandles[0].Set();
            });

            // Calculate Distance Transform for Outer Mask
            ThreadPool.QueueUserWorkItem(OuterMask =>
            {
                //System.TimeSpan timeStamp = timer.Elapsed;

                outerMask = Calculate3X3EDT(Source, 0, width, height, Spread);
                //Debug.Log("Outer Mask processed in [" + (timer.Elapsed - timeStamp) + "]");

                _WaitHandles[1].Set();
            });

            WaitHandle.WaitAll(_WaitHandles);   // Wait for both Threads to be completed.

            //Debug.Log("Inner & Outer Mask Processing Completed.");
            //string logfile = string.Empty;

            // The final Ouput passed back to the Font Editor Window
            Output = new Color[width / ScaleDownFactor * height / ScaleDownFactor];

            // Store Resulting process of Inner and Outer Mask in Output and apply downscaling factor
            float scale           = 1f / Spread;
            int   s1              = ScaleDownFactor / 2;
            int   ScaledDownWidth = width / ScaleDownFactor;

            //timeStamp = timer.Elapsed;
            for (int y = s1; y < height; y += ScaleDownFactor)
            {
                for (int x = s1; x < width; x += ScaleDownFactor)
                {
                    int x1 = (x - s1) / ScaleDownFactor;
                    int y1 = (y - s1) / ScaleDownFactor;

                    float inner0 = Mathf.Clamp01(innerMask[x + y * width].distance * scale);
                    float outer0 = Mathf.Clamp01(outerMask[x + y * width].distance * scale);


                    //if (ScaleDownFactor > 1)
                    //{
                    //    float inner1 = Mathf.Clamp01(innerMask[(x - 1) + y * width].distance * scale);
                    //    float inner2 = Mathf.Clamp01(innerMask[x + (y - 1) * width].distance * scale);
                    //    float inner3 = Mathf.Clamp01(innerMask[(x - 1) + (y - 1) * width].distance * scale);

                    //    float outer1 = Mathf.Clamp01(outerMask[(x - 1) + y * width].distance * scale);
                    //    float outer2 = Mathf.Clamp01(outerMask[x + (y - 1) * width].distance * scale);
                    //    float outer3 = Mathf.Clamp01(outerMask[(x - 1) + (y - 1) * width].distance * scale);

                    //    inner0 = (inner0 + inner1 + inner2 + inner3) / 4;
                    //    outer0 = (outer0 + outer1 + outer2 + outer3) / 4;
                    //}


                    //if (ScaleDownFactor > 1)
                    //{
                    //    int dx0 = x; int dy0 = y;

                    //    // Find smallest Inner distance
                    //    float inner_dst = innerMask[x + y * width].distance;

                    //    // Check dst against (x - 1, y)
                    //    if (inner_dst > innerMask[(x - 1) + y * width].distance)
                    //    {
                    //        dx0 = x - 1; dy0 = y;
                    //        inner_dst = innerMask[(x - 1) + y * width].distance;
                    //    }

                    //    // Check dst against (x - 1, y - 1)
                    //    if (inner_dst > innerMask[(x - 1) + (y - 1) * width].distance)
                    //    {
                    //        dx0 = x - 1; dy0 = y - 1;
                    //        inner_dst = innerMask[(x - 1) + (y - 1) * width].distance;
                    //    }

                    //    // Check dst against (x, y - 1)
                    //    if (inner_dst > innerMask[x + (y - 1) * width].distance)
                    //    {
                    //        dx0 = x; dy0 = y - 1;
                    //        inner_dst = innerMask[x + (y - 1) * width].distance;
                    //    }

                    //    int dX = innerMask[dx0 + dy0 * width].dX;
                    //    int dY = innerMask[dx0 + dy0 * width].dY;
                    //    float delta = ApproximateEdgeDelta(dX, dY, innerMask[(x + dX) + (y + dY) * width].alpha);

                    //    inner0 = Mathf.Sqrt((dX + 0.5f * Mathf.Sign(dX)) * (dX + 0.5f * Mathf.Sign(dX)) + (dY + 0.5f * Mathf.Sign(dY)) * (dY + 0.5f * Mathf.Sign(dY))) + delta;
                    //    inner0 = Mathf.Clamp01(inner0 * scale);

                    //    //float outer_dst = 0;
                    //    dx0 = x; dy0 = y;

                    //    // Find smallest Inner distance
                    //    float outer_dst = outerMask[x + y * width].distance;

                    //    // Check dst against (x - 1, y)
                    //    if (outer_dst > outerMask[(x - 1) + y * width].distance)
                    //    {
                    //        dx0 = x - 1; dy0 = y;
                    //        outer_dst = outerMask[(x - 1) + y * width].distance;
                    //    }

                    //    // Check dst against (x - 1, y - 1)
                    //    if (outer_dst > outerMask[(x - 1) + (y - 1) * width].distance)
                    //    {
                    //        dx0 = x - 1; dy0 = y - 1;
                    //        outer_dst = outerMask[(x - 1) + (y - 1) * width].distance;
                    //    }

                    //    // Check dst against (x, y - 1)
                    //    if (outer_dst > outerMask[x + (y - 1) * width].distance)
                    //    {
                    //        dx0 = x; dy0 = y - 1;
                    //        outer_dst = outerMask[x + (y - 1) * width].distance;
                    //    }

                    //    dX = outerMask[dx0 + dy0 * width].dX;
                    //    dY = outerMask[dx0 + dy0 * width].dY;
                    //    delta = ApproximateEdgeDelta(dX, dY, outerMask[(x + dX) + (y + dY) * width].alpha);

                    //    outer0 = Mathf.Sqrt((dX + 0.5f * Mathf.Sign(dX)) * (dX + 0.5f * Mathf.Sign(dX)) + (dY + 0.5f * Mathf.Sign(dY)) * (dY + 0.5f * Mathf.Sign(dY))) + delta;
                    //    outer0 = Mathf.Clamp01(outer0 * scale);
                    //    //Debug.Log("Min Outer Distance for (" + x + "," + y + ") at (" + dx0 + "," + dy0 + ") is " + outer0 + " with dX/dY (" + outerMask[dx0 + dy0 * width].dX + "," + outerMask[dx0 + dy0 * width].dY + ")");
                    //}



                    if (ScaleDownFactor > 1)
                    {
                        inner0 = innerMask[x + y * width].distance;
                        float inner1 = innerMask[(x - 1) + y * width].distance;
                        float inner2 = innerMask[x + (y - 1) * width].distance;
                        float inner3 = innerMask[(x - 1) + (y - 1) * width].distance;

                        outer0 = outerMask[x + y * width].distance;
                        float outer1 = outerMask[(x - 1) + y * width].distance;
                        float outer2 = outerMask[x + (y - 1) * width].distance;
                        float outer3 = outerMask[(x - 1) + (y - 1) * width].distance;

                        inner0 = Mathf.Clamp01((inner0 + inner1 + inner2 + inner3) / 4 * scale);
                        outer0 = Mathf.Clamp01((outer0 + outer1 + outer2 + outer3) / 4 * scale);
                    }

                    ///Debug.Log(inner + " " + outer);

                    float alpha = 0.5f + (inner0 - outer0) * 0.5f;


                    //Debug.Log("(" + x + "," + y + ") Dx/Dy (" + outerMask[x + y * width].dX + "," + outerMask[x + y * width].dY + ") Scale Distance: " + d);

                    Output[x1 + y1 * ScaledDownWidth] = new Color(0, 0, 0, alpha);
                    //logfile += "(" + x + "," + y + ")\t\t\tDistance: " + Mathf.Max(innerMask[x + y * width].distance, outerMask[x + y * width].distance).ToString("f6") + "\t DxDy (" + outerMask[x + y * width].dX + "," + outerMask[x + y * width].dY + ")\t\t Inner: " + inner0.ToString("f4") + "\t\t Outer: " + outer0.ToString("f4") + "\t\t Alpha: " + alpha.ToString("f4") + "\n";
                }

                //logfile += "\n";
            }
            //Debug.Log("Output processed in [" + (timer.Elapsed - timeStamp) + "]");

            // Free up allocations for pixel arrays
            innerMask = null;
            outerMask = null;


            timer.Stop();
            Debug.Log("Distance Transform process took: [" + timer.Elapsed + "].");

            TMPro_EventManager.ON_COMPUTE_DT_EVENT(null, new Compute_DT_EventArgs(Compute_DistanceTransform_EventTypes.Completed, Output));// Generate Event to notify and share results with Font Editor Window.

            //var pngData = DistanceMap.EncodeToPNG();
            //File.WriteAllText("Assets/Logs/Distance Calcs.txt", logfile);
            //File.WriteAllBytes("Assets/Textures/New Texture.png", pngData);
        }
Пример #2
0
        private static Pixel[] Calculate3X3EDT(Color[] input, int mask, int width, int height, int spread)
        {
            int inf = width * height;

            Pixel[] maskOutput = new Pixel[inf];

            Pixel p1; // Test Pixel being considered as potential closest.

            int pRx = 0, pRy = 0;

            // Forward Scan & Initialization
            for (int y = 0; y < height; y++)
            {
                if (mask == 0f)
                {
                    float pct = (float)y / (height - 1);
                    TMPro_EventManager.ON_COMPUTE_DT_EVENT(null, new Compute_DT_EventArgs(Compute_DistanceTransform_EventTypes.Processing, pct));
                    //Debug.Log("Phase I pct [" + pct + "]");
                }

                for (int x = 0; x < width; x++)
                {
                    int   index = x + y * width;
                    Pixel p     = new Pixel();
                    maskOutput[index] = p;

                    //Check which Mask is being processed. Propagation makes it possible to generate the mask in-line with Algorithm
                    p.alpha = mask == 0 ? input[index].a : 1f - input[index].a;

                    float pF;

                    if (p.alpha <= 0)
                    {
                        pF = inf;
                    }
                    else if (p.alpha < 1)
                    {
                        // Distance set to EdgeGradient for AA Pixels. No further processing needed for them.
                        ComputeEdgeGradient(p, input, mask, index, width);
                        pF         = ApproximateEdgeDelta(p.gradient.x, p.gradient.y, p.alpha);
                        p.dX       = 0;
                        p.dY       = 0;
                        p.distance = pF;
                        continue;
                    }
                    else
                    {
                        // Distance set to Zero for Alpha = 1; No further processing needed for them.
                        pF         = 0;
                        p.dX       = 0; p.dY = 0;
                        p.distance = 0;
                        continue;
                    }

                    // Scan Lower Neighbour
                    if (y > 0)
                    {
                        p1 = maskOutput[index - width];

                        if (p1.distance != inf)
                        {
                            int dX = p1.dX;
                            int dY = p1.dY - 1;

                            float delta = ApproximateEdgeDelta(dX, dY, maskOutput[index + dX + dY * width].alpha);
                            float qF    = Mathf.Sqrt(dX * dX + dY * dY) + delta;

                            if (qF < pF)
                            {
                                pF  = qF;
                                pRx = dX;
                                pRy = dY;
                            }
                        }
                    }
                    // Scan Left Neighbour
                    if (x > 0)
                    {
                        p1 = maskOutput[index - 1];

                        if (p1.distance != inf)
                        {
                            int dX = p1.dX - 1;
                            int dY = p1.dY;

                            float delta = ApproximateEdgeDelta(dX, dY, maskOutput[index + dX + dY * width].alpha);
                            float qF    = Mathf.Sqrt(dX * dX + dY * dY) + delta;

                            if (qF < pF)
                            {
                                pF  = qF;
                                pRx = dX;
                                pRy = dY;
                            }
                        }
                    }
                    // Scan Lower-Left Neighbour
                    if (x > 0 && y > 0)
                    {
                        p1 = maskOutput[index - 1 - width];

                        if (p1.distance != inf)
                        {
                            int dX = p1.dX - 1;
                            int dY = p1.dY - 1;

                            float delta = ApproximateEdgeDelta(dX, dY, maskOutput[index + dX + dY * width].alpha);
                            float qF    = Mathf.Sqrt(dX * dX + dY * dY) + delta;

                            if (qF < pF)
                            {
                                pF  = qF;
                                pRx = dX;
                                pRy = dY;
                            }
                        }
                    }
                    // Scan Lower-Right Neighbour
                    if (x < width - 1 && y > 0)
                    {
                        p1 = maskOutput[index + 1 - width];

                        if (p1.distance != inf)
                        {
                            int dX = p1.dX + 1;
                            int dY = p1.dY - 1;

                            float delta = ApproximateEdgeDelta(dX, dY, maskOutput[index + dX + dY * width].alpha);
                            float qF    = Mathf.Sqrt(dX * dX + dY * dY) + delta;

                            if (qF < pF)
                            {
                                pF  = qF;
                                pRx = dX;
                                pRy = dY;
                            }
                        }
                    }

                    p.distance = pF;
                    p.dX       = pRx;
                    p.dY       = pRy;
                }
            }

            // Backward Scan & Initialization
            for (int y = height - 1; y >= 0; y--)
            {
                if (mask == 0f)
                {
                    float pct = ((height - 1) - (float)y) / (height - 1);
                    TMPro_EventManager.ON_COMPUTE_DT_EVENT(null, new Compute_DT_EventArgs(Compute_DistanceTransform_EventTypes.Processing, pct));
                    //Debug.Log("Phase II pct [" + pct + "]");
                }

                for (int x = width - 1; x >= 0; x--)
                {
                    int   index = x + y * width;
                    Pixel p     = maskOutput[index];

                    if (p.alpha > 0 && p.alpha < 1 || p.distance == 0)
                    {
                        continue;
                    }

                    float pF = p.distance;

                    pRx = p.dX;
                    pRy = p.dY;

                    // Scan Upper Neighbour
                    if (y < height - 1)
                    {
                        p1 = maskOutput[index + width];

                        if (p1.distance != inf)
                        {
                            int dX = p1.dX;
                            int dY = p1.dY + 1;

                            float delta = ApproximateEdgeDelta(dX, dY, maskOutput[index + dX + dY * width].alpha);
                            float qF    = Mathf.Sqrt(dX * dX + dY * dY) + delta;

                            if (qF <= pF)
                            {
                                pF  = qF;
                                pRx = dX;
                                pRy = dY;
                            }
                        }
                    }
                    // Scan Right Neighbour
                    if (x < width - 1)
                    {
                        p1 = maskOutput[index + 1];

                        if (p1.distance != inf)
                        {
                            int dX = p1.dX + 1;
                            int dY = p1.dY;

                            float delta = ApproximateEdgeDelta(dX, dY, maskOutput[index + dX + dY * width].alpha);
                            float qF    = Mathf.Sqrt(dX * dX + dY * dY) + delta;

                            if (qF < pF)
                            {
                                pF  = qF;
                                pRx = dX;
                                pRy = dY;
                            }
                        }
                    }
                    // Scan Upper-Right Neighbour
                    if (x < width - 1 && y < height - 1)
                    {
                        p1 = maskOutput[index + 1 + width];

                        if (p1.distance != inf)
                        {
                            int dX = p1.dX + 1;
                            int dY = p1.dY + 1;

                            float delta = ApproximateEdgeDelta(dX, dY, maskOutput[index + dX + dY * width].alpha);
                            float qF    = Mathf.Sqrt(dX * dX + dY * dY) + delta;

                            if (qF < pF)
                            {
                                pF  = qF;
                                pRx = dX;
                                pRy = dY;
                            }
                        }
                    }
                    // Scan Upper-Left Neighboud
                    if (x > 0 && y < height - 1)
                    {
                        p1 = maskOutput[index - 1 + width];

                        if (p1.distance != inf)
                        {
                            int dX = p1.dX - 1;
                            int dY = p1.dY + 1;

                            float delta = ApproximateEdgeDelta(dX, dY, maskOutput[index + dX + dY * width].alpha);
                            float qF    = Mathf.Sqrt(dX * dX + dY * dY) + delta;

                            if (qF < pF)
                            {
                                pF  = qF;
                                pRx = dX;
                                pRy = dY;
                            }
                        }
                    }

                    p.distance = pF;
                    p.dX       = pRx;
                    p.dY       = pRy;
                }
            }

            return(maskOutput);
        }