public HairTexGeneratorForm()
        {
            InitializeComponent();
            infoLabel.Text      = "";
            info2Label.Text     = "";
            this.bitmapAreaInfo = new BitmapAreaProcessor(this);
            this.uiController   = new HairGenUiController(this, editPanelMainSplitContainer.Panel2, boundsCheckerCheckBox);

            this.asyncInfoTimer.Enabled = true;
        }
 private void HairTexGeneratorForm_FormClosing(object sender, FormClosingEventArgs e)
 {
     this.bitmapAreaInfo = null;
     this.uiController   = null;
 }
        public async void GenerateTexture(bool boundsChecker, int areaId = -1) //-1 dla wszystkich
        {
            if (this.loadingInProgress)
            {
                return;
            }

            this.loadingInProgress = true;
            editPanelMainSplitContainer.Panel2.Enabled = false;

            try
            {
                BitmapAreaProcessor bitmapProc = this.bitmapAreaInfo;

                if (bitmapProc == null)
                {
                    throw new Exception($"Obiekt przetwarzania mapy bitowej jest NULL!");
                }

                List <int> areaIdsList = (areaId == -1 ? bitmapProc.GetAllAreaIds() : new List <int>()
                {
                    areaId
                });
                int bmpWidth           = this.sourceBitmap.Width;
                int bmpHeight          = this.sourceBitmap.Height;

                if (bmpWidth < 1 || bmpHeight < 1)
                {
                    throw new Exception($"odczytano błędne wymiary bitmapy źródłowej [{bmpWidth},{bmpHeight}]!");
                }

                #region Wstępne wypełnianie bitmapy rezultatu - przezroczystość
                //WSTĘPNE WYPEŁNIANIE BITMAPY REZULTATU
                UpdateInfo($"Wstępne tworzenie bitmapy rezultatu");

                PacketObjMsg transparentBmpPacket = await bitmapProc.GenerateTransparentBitmapTaskStart(bmpWidth, bmpHeight);

                this.resultBitmap = (Bitmap)transparentBmpPacket.Obj;

                if (!String.IsNullOrEmpty(transparentBmpPacket.Msg))
                {
                    MessageBox.Show(transparentBmpPacket.Msg);
                }

                #endregion

                foreach (int areaIdFromList in areaIdsList)
                {
                    UpdateInfo($"Generowanie tekstury dla ID strefy [{areaIdFromList}]");

                    #region Ograniczenia strefy
                    //POBIERANIE OGRANICZEŃ STREFY
                    PacketObjMsg boundingsPacket = await bitmapProc.GetAreaBoundingsTaskStart(areaIdFromList);

                    AreaBoundings areaBoundings = (AreaBoundings)boundingsPacket.Obj;

                    if (!String.IsNullOrEmpty(boundingsPacket.Msg))
                    {
                        MessageBox.Show(boundingsPacket.Msg);
                    }

                    int areaMinX = areaBoundings.BoundsX.X;
                    int areaMaxX = areaBoundings.BoundsX.Y;
                    int areaMinY = areaBoundings.BoundsY.X;
                    int areaMaxY = areaBoundings.BoundsY.Y;

                    //obiekt, który będzie przechowywał poszerzone ograniczenia strefy w przypadku, w którym rysowanie wyjdzie poza ograniczenia (np. losowe punkty krzywej)
                    AreaBoundings processingAreaBoundings = areaBoundings.Copy();

                    #endregion

                    #region Test ograniczeń stref
                    //TEST
                    //Brush brush = new SolidBrush(Color.FromArgb(255, 255, 0, 0));
                    //using (Graphics gr = Graphics.FromImage(this.resultBitmap))
                    //{
                    //    gr.FillRectangle(brush, areaMinX, areaMinY, (areaMaxX - areaMinX), (areaMaxY - areaMinY));
                    //}
                    #endregion

                    #region Ustawienia użytkownika
                    //USTAWIENIA UŻYTKOWNIKA DLA STREFY
                    AreaSettings userAreaSettings = this.uiController.GetAreaSettings(areaIdFromList);

                    AreaSettings.HairDrawingDirection hairDirection   = userAreaSettings.Direction;
                    AreaSettings.HairCuttingSide      hairCuttingSide = userAreaSettings.CuttingSide;

                    int     drawingSteps             = userAreaSettings.DrawingSteps;
                    int     drawingIterations        = userAreaSettings.DrawingIterations;
                    decimal iterationLossPercent     = userAreaSettings.DrawingStepIterationLossPercent;
                    decimal bezierRandomRangePercent = userAreaSettings.BezierRandomRangePercent;
                    decimal bezierMarginPercent      = userAreaSettings.BezierMarginPercent;
                    double  cuttingRangePercent      = Convert.ToDouble(userAreaSettings.CuttingRangePercent);

                    int bezierPointCount = userAreaSettings.MultiBezierPointCount;

                    if (userAreaSettings.BezierLineType == AreaSettings.BezierType.Quadratic || userAreaSettings.BezierLineType == AreaSettings.BezierType.QuadraticCp)
                    {
                        bezierPointCount = 3;
                    }
                    else
                    if (userAreaSettings.BezierLineType == AreaSettings.BezierType.Cubic)
                    {
                        bezierPointCount = 4;
                    }

                    Point3 backgroundRgbPalette = userAreaSettings.BackgroundRgbPalette;
                    Point3 foregroundRgbPalette = userAreaSettings.ForegroundRgbPalette;

                    bool hairMapDefinesOpacity    = userAreaSettings.HairMapDefinesOpacity;
                    bool hairMapDefinesBrightness = userAreaSettings.HairMapDefinesBrightness;

                    #endregion

                    #region Dane wynikowe
                    //DANE WYNIKOWE
                    int iterationLossCount    = Convert.ToInt32(drawingIterations * (iterationLossPercent / 100));
                    int currentIterationCount = drawingIterations;

                    int areaRangeX = areaMaxX - areaMinX;
                    int areaRangeY = areaMaxY - areaMinY;

                    #endregion

                    #region Dane przetwarzane
                    //DANE PRZETWARZANE W PĘTLACH
                    int           currentAxisPosition;
                    double        stepPercent;
                    double        positionPercent;
                    double        bezierPositionPercent;
                    PacketObjMsg  lineRangePacket;
                    AreaLineRange lineRange;
                    int           rangePointFrom;
                    int           rangePointTo;
                    int           resultPoint;

                    int boundRangeMin;
                    int boundRangeMax;

                    List <Point> hairBezierPoints = new List <Point>();

                    Point3 currentColorPalette = new Point3(0, 0, 0);
                    int    currentR            = 0;
                    int    currentG            = 0;
                    int    currentB            = 0;

                    bool cutOnStart = false;
                    bool cutOnEnd   = false;

                    if (hairDirection == AreaSettings.HairDrawingDirection.TopToBottom || hairDirection == AreaSettings.HairDrawingDirection.LeftToRight)
                    {
                        cutOnStart = (hairCuttingSide == AreaSettings.HairCuttingSide.Beginning || hairCuttingSide == AreaSettings.HairCuttingSide.BothSides);
                        cutOnEnd   = (hairCuttingSide == AreaSettings.HairCuttingSide.End || hairCuttingSide == AreaSettings.HairCuttingSide.BothSides);
                    }
                    else
                    if (hairDirection == AreaSettings.HairDrawingDirection.BottomToTop || hairDirection == AreaSettings.HairDrawingDirection.RightToLeft)
                    {
                        cutOnEnd   = (hairCuttingSide == AreaSettings.HairCuttingSide.Beginning || hairCuttingSide == AreaSettings.HairCuttingSide.BothSides);
                        cutOnStart = (hairCuttingSide == AreaSettings.HairCuttingSide.End || hairCuttingSide == AreaSettings.HairCuttingSide.BothSides);
                    }

                    #endregion

                    for (int drawingStep = 0; drawingStep < drawingSteps; drawingStep++) //PĘTLA TONÓW - KROKI
                    {
                        if (currentIterationCount <= 0)
                        {
                            break;
                        }

                        stepPercent = DataProcessor.CalculatePercent(Convert.ToDouble(drawingSteps - 1), Convert.ToDouble(drawingStep));

                        #region Kolor podstawowy (ton)
                        //WYODRĘBNIANIE PODSTAWOWEGO TONU KOLORU
                        currentR = Convert.ToInt32(DataProcessor.Lerp(backgroundRgbPalette.X, foregroundRgbPalette.X, (stepPercent / 100)));
                        currentG = Convert.ToInt32(DataProcessor.Lerp(backgroundRgbPalette.Y, foregroundRgbPalette.Y, (stepPercent / 100)));
                        currentB = Convert.ToInt32(DataProcessor.Lerp(backgroundRgbPalette.Z, foregroundRgbPalette.Z, (stepPercent / 100)));
                        currentR = DataProcessor.Clamp(0, 255, currentR);
                        currentG = DataProcessor.Clamp(0, 255, currentG);
                        currentB = DataProcessor.Clamp(0, 255, currentB);

                        currentColorPalette = new Point3(currentR, currentG, currentB);

                        #endregion

                        for (int drawingIteration = 0; drawingIteration < currentIterationCount; drawingIteration++) //PĘTLA ITERACJI - SZTUK WŁOSA
                        {
                            hairBezierPoints.Clear();
                            positionPercent = DataProcessor.CalculatePercent(Convert.ToDouble(currentIterationCount - 1), Convert.ToDouble(drawingIteration));

                            for (int bezierPointIteration = 0; bezierPointIteration < bezierPointCount; bezierPointIteration++)
                            {
                                bezierPositionPercent = DataProcessor.CalculatePercent(Convert.ToDouble(bezierPointCount - 1), Convert.ToDouble(bezierPointIteration));

                                if (hairDirection == AreaSettings.HairDrawingDirection.TopToBottom || hairDirection == AreaSettings.HairDrawingDirection.BottomToTop)
                                {
                                    #region Pionowo

                                    currentAxisPosition = Convert.ToInt32(DataProcessor.Lerp(areaMinY, areaMaxY, bezierPositionPercent / 100));

                                    lineRangePacket = await this.bitmapAreaInfo.GetAreaLineRangeTaskStart
                                                      (
                                        BitmapAreaProcessor.AreaAxis.Y,
                                        currentAxisPosition,
                                        areaIdFromList
                                                      );

                                    lineRange = (AreaLineRange)lineRangePacket.Obj;

                                    //POBIERANIE PUNKTÓW GRANICZNYCH DLA STREFY
                                    rangePointFrom = lineRange.RangePointFrom;
                                    rangePointTo   = lineRange.RangePointTo;

                                    //MODYFIKACJA O MARGINESY
                                    rangePointFrom = Convert.ToInt32(rangePointFrom - (rangePointTo - rangePointFrom) * (bezierMarginPercent / 100));
                                    rangePointTo   = Convert.ToInt32(rangePointTo + (rangePointTo - rangePointFrom) * (bezierMarginPercent / 100));

                                    if (rangePointFrom < 0)
                                    {
                                        rangePointFrom = 0;
                                    }

                                    if (rangePointTo > this.resultBitmap.Width - 1)
                                    {
                                        rangePointTo = this.resultBitmap.Width - 1;
                                    }


                                    //WSTĘPNE TWORZENIE PUNKTU WYNIKOWEGO
                                    resultPoint = Convert.ToInt32(DataProcessor.Lerp(rangePointFrom, rangePointTo, positionPercent / 100));

                                    //ZAKRES LOSOWANIA / LOSOWANIE PUNKTU
                                    boundRangeMin = Convert.ToInt32(resultPoint - ((rangePointTo - rangePointFrom) * (bezierRandomRangePercent / 100)));
                                    boundRangeMax = Convert.ToInt32(resultPoint + ((rangePointTo - rangePointFrom) * (bezierRandomRangePercent / 100)));

                                    resultPoint = this.random.Next(boundRangeMin, boundRangeMax + 1);
                                    resultPoint = DataProcessor.Clamp(0, this.resultBitmap.Width, resultPoint);

                                    //PRZYPISYWANIE PUNKTU WYNIKOWEGO

                                    hairBezierPoints.Add
                                    (
                                        new Point
                                        (
                                            resultPoint,
                                            currentAxisPosition
                                        )
                                    );

                                    #endregion
                                }
                                else
                                if (hairDirection == AreaSettings.HairDrawingDirection.LeftToRight || hairDirection == AreaSettings.HairDrawingDirection.RightToLeft)
                                {
                                    #region Poziomo

                                    currentAxisPosition = Convert.ToInt32(DataProcessor.Lerp(areaMinX, areaMaxX, bezierPositionPercent / 100));

                                    lineRangePacket = await this.bitmapAreaInfo.GetAreaLineRangeTaskStart
                                                      (
                                        BitmapAreaProcessor.AreaAxis.X,
                                        currentAxisPosition,
                                        areaIdFromList
                                                      );

                                    lineRange = (AreaLineRange)lineRangePacket.Obj;

                                    //POBIERANIE PUNKTÓW GRANICZNYCH DLA STREFY
                                    rangePointFrom = lineRange.RangePointFrom;
                                    rangePointTo   = lineRange.RangePointTo;

                                    //MODYFIKACJA O MARGINESY
                                    rangePointFrom = Convert.ToInt32(rangePointFrom - (rangePointTo - rangePointFrom) * (bezierMarginPercent / 100));
                                    rangePointTo   = Convert.ToInt32(rangePointTo + (rangePointTo - rangePointFrom) * (bezierMarginPercent / 100));

                                    if (rangePointFrom < 0)
                                    {
                                        rangePointFrom = 0;
                                    }

                                    if (rangePointTo > this.resultBitmap.Height - 1)
                                    {
                                        rangePointTo = this.resultBitmap.Height - 1;
                                    }

                                    //WSTĘPNE TWORZENIE PUNKTU WYNIKOWEGO
                                    resultPoint = Convert.ToInt32(DataProcessor.Lerp(rangePointFrom, rangePointTo, positionPercent / 100));

                                    //ZAKRES LOSOWANIA / LOSOWANIE PUNKTU
                                    boundRangeMin = Convert.ToInt32(resultPoint - ((rangePointTo - rangePointFrom) * (bezierRandomRangePercent / 100)));
                                    boundRangeMax = Convert.ToInt32(resultPoint + ((rangePointTo - rangePointFrom) * (bezierRandomRangePercent / 100)));

                                    resultPoint = this.random.Next(boundRangeMin, boundRangeMax + 1);
                                    resultPoint = DataProcessor.Clamp(0, this.resultBitmap.Height, resultPoint);

                                    //PRZYPISYWANIE PUNKTU WYNIKOWEGO

                                    hairBezierPoints.Add
                                    (
                                        new Point
                                        (
                                            currentAxisPosition,
                                            resultPoint
                                        )
                                    );

                                    #endregion
                                }
                                else
                                if (hairDirection == AreaSettings.HairDrawingDirection.Radial)
                                {
                                }
                                else
                                if (hairDirection == AreaSettings.HairDrawingDirection.LinearX)
                                {
                                }
                                else
                                if (hairDirection == AreaSettings.HairDrawingDirection.LinearY)
                                {
                                }
                            }

                            //RYSOWANIE

                            BitmapAreaProcessor.DrawBezierLineOnBitmap
                            (
                                hairBezierPoints,
                                this.resultBitmap,
                                (userAreaSettings.BezierLineType == AreaSettings.BezierType.QuadraticCp),
                                0.02,
                                currentColorPalette,
                                cutOnStart,
                                cutOnEnd,
                                cuttingRangePercent,
                                ref processingAreaBoundings
                            );

                            //POSZERZANIE GRANIC STREFY - DLA PRZETWARZANIA PRZEZROCZYSTOŚCI / POŁYSKU
                            //processingAreaBoundings.ExtendAreaBoundings(hairBezierPoints, this.resultBitmap.Width - 1, this.resultBitmap.Height - 1);
                        }

                        currentIterationCount -= iterationLossCount;
                    }

                    //PRZEZROCZYSTOŚĆ / POŁYSK
                    BitmapAreaProcessor.AreaAxis glossOpacAxis = BitmapAreaProcessor.AreaAxis.None;
                    if (hairDirection == AreaSettings.HairDrawingDirection.TopToBottom || hairDirection == AreaSettings.HairDrawingDirection.BottomToTop)
                    {
                        glossOpacAxis = BitmapAreaProcessor.AreaAxis.X;
                    }
                    else
                    if (hairDirection == AreaSettings.HairDrawingDirection.LeftToRight || hairDirection == AreaSettings.HairDrawingDirection.RightToLeft)
                    {
                        glossOpacAxis = BitmapAreaProcessor.AreaAxis.Y;
                    }

                    PacketObjMsg glossOpacityPacket = await bitmapProc.GenerateHairTexGlossinessAndOpacityTaskStart
                                                      (
                        processingAreaBoundings, //areaBoundings,
                        this.resultBitmap,
                        Color.FromArgb(255, backgroundRgbPalette.X, backgroundRgbPalette.Y, backgroundRgbPalette.Z),
                        hairMapDefinesBrightness,
                        hairMapDefinesOpacity,
                        //new double[] { Convert.ToDouble(bezierMarginPercent / 100), Convert.ToDouble(bezierRandomRangePercent / 100) },
                        //glossOpacAxis
                        boundsChecker
                                                      );

                    this.resultBitmap = (Bitmap)glossOpacityPacket.Obj;

                    if (!String.IsNullOrEmpty(glossOpacityPacket.Msg))
                    {
                        MessageBox.Show(glossOpacityPacket.Msg);
                    }
                }

                //WYŚWIETLANIE OBRAZU REZULTATU
                SetPreview(PreviewType.Result, this.resultBitmap);
            }
            catch (Exception exception)
            {
                MessageBox.Show($"Błąd generowania tekstury: {exception.Message}");
            }

            UpdateInfo("");
            editPanelMainSplitContainer.Panel2.Enabled = true;
            this.loadingInProgress = false;
        }