private void bWorker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e) //background worker method
        {
            numOfImages = baseImages.Count;                                                 //update number of images
            threads     = new Thread[numOfThreads];                                         //define threads
            int numOfIterations = numOfImages / poolSize;                                   //number of iterations

            failed  = 0;                                                                    //reset failed number
            counter = 0;                                                                    //reset counter

            for (int i = 0; i < numOfIterations + 1; i++)
            {
                BitmapJob bitJob;         //job var
                Bitmap    baseBit;        //base bitmap var
                Bitmap    transparentBit; //transparent bitmap var

                finishedThreads = 0;      //reset finished threads
                stopwatch2.Reset();
                stopwatch2.Start();
                for (int j = i * poolSize; j < (i + 1) * poolSize && j < numOfImages; j++)                 //read "poolsize" images
                {
                    baseBit        = new Bitmap(baseImages[j]);                                            //read base image
                    transparentBit = new Bitmap(transparentImages[rand.Next(0, transparentImages.Count)]); // read transparent image

                    bitJob = new BitmapJob(baseBit, transparentBit, false);                                //construct job
                    bitmapJobPool.Add(bitJob);                                                             //add to job pool
                }

                for (int j = 0; j < numOfThreads; j++)                     //start generating in threads
                {
                    threads[j] = new Thread(new ThreadStart(getJobsDone)); //initialise threads
                    threads[j].Start();                                    //start threads
                }

                while (finishedThreads != numOfThreads || checkJobStatus()) //if threads no finished or jobs arent done
                {
                    if (!bWorker.CancellationPending)
                    {
                        Thread.SpinWait(1);
                    }
                    else
                    {
                        break;
                    }
                    if (stopwatch2.Elapsed.TotalSeconds > 15)
                    {
                        bitmapJobPool.Clear(); //clear job pool
                        outputBitmaps.Clear(); //clear generated bitmaps
                        GC.WaitForPendingFinalizers();
                        GC.Collect();          //clear memory
                        bWorker.CancelAsync();
                    }
                }

                for (int j = 0; j < outputBitmaps.Count; j++) //for the generated number of bitmaps
                {
                    try
                    {
                        switch (selectedExtensionIndex) //switch for export type
                        {
                        case 0:                         //jpeg
                        {
                            String outpath = outPath + "\\" + fileName + lastIndex + selectedExtension.ToString();

                            outputBitmaps[j].Save(outpath, ImageFormat.Jpeg);
                            break;
                        }

                        case 1:    //png
                        {
                            String outpath = outPath + "\\" + fileName + lastIndex + selectedExtension.ToString();

                            outputBitmaps[j].Save(outpath, ImageFormat.Png);

                            break;
                        }

                        case 2:    //gif
                        {
                            String outpath = outPath + "\\" + fileName + lastIndex + selectedExtension.ToString();

                            outputBitmaps[j].Save(outpath, ImageFormat.Gif);

                            break;
                        }

                        case 3:    //bmp
                        {
                            String outpath = outPath + "\\" + fileName + lastIndex + selectedExtension.ToString();

                            outputBitmaps[j].Save(outpath, ImageFormat.Bmp);

                            break;
                        }

                        case 4:    //tiff
                        {
                            String outpath = outPath + "\\" + fileName + lastIndex + selectedExtension.ToString();

                            outputBitmaps[j].Save(outpath, ImageFormat.Tiff);

                            break;
                        }

                        default:
                            break;
                        }
                        counter++;   //increment counter
                        lastIndex++; //increment last index
                        int percent = (int)((float)(counter + failed) / (float)numOfImages * 100);
                        bWorker.ReportProgress(percent);
                    }
                    catch
                    {
                        failed++; //increment failed
                    }

                    if (bWorker.CancellationPending)
                    {
                        bitmapJobPool.Clear(); //clear job pool
                        outputBitmaps.Clear(); //clear generated bitmaps
                        GC.WaitForPendingFinalizers();
                        GC.Collect();          //clear memory
                        e.Cancel = true;
                        return;
                    }
                }
                bitmapJobPool.Clear(); //clear job pool
                outputBitmaps.Clear(); //clear generated bitmaps
                GC.WaitForPendingFinalizers();
                GC.Collect();          //clear memory
            }
        }
        void getJobsDone()                //do jobs method
        {
            while (checkJobStatus())      //if there are jobs
            {
                BitmapJob job = getJob(); //get a job
                if (job == null)          //if null exit thread
                {
                    return;
                }

                Bitmap output = new Bitmap(job.baseBitmap.Width, job.baseBitmap.Height);            //initialise output

                int tcenterx = job.transparentBitmap.Width / 2;                                     //find the center x of transparent
                int tcentery = job.transparentBitmap.Height / 2;                                    //find the center y of thransparent

                Graphics outputgraphics = Graphics.FromImage(output);                               //get output graphics

                ColorMatrix matrix = new ColorMatrix();                                             //define new color matrix
                matrix.Matrix33 = ((float)opacity) / 100f;                                          //set matrix opacity
                ImageAttributes attributes = new ImageAttributes();                                 //define new attribute to attach to bitmap
                attributes.SetColorMatrix(matrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap); //set the attribute values

                outputgraphics.CompositingMode = CompositingMode.SourceOver;                        //set composition mode for graphics

                outputgraphics.DrawImage(job.baseBitmap, 0, 0);                                     //draw image on output
                outputgraphics.DrawImage(
                    job.transparentBitmap,
                    new Rectangle(
                        rand.Next(0, job.baseBitmap.Width) - tcenterx,
                        rand.Next(0, job.baseBitmap.Height) - tcentery,
                        job.transparentBitmap.Width,
                        job.transparentBitmap.Height),
                    0,
                    0,
                    job.transparentBitmap.Width,
                    job.transparentBitmap.Height,
                    GraphicsUnit.Pixel,
                    attributes);                               //draw transparent image on output with created attribute

                StringFormat drawFormat = new StringFormat();  //define string format
                drawFormat.Alignment = StringAlignment.Center; //define alignment
                outputgraphics.DrawString(
                    wordLines[rand.Next(0, wordLines.Length)],
                    fontSelector.Font,
                    new SolidBrush(brushColor),
                    job.baseBitmap.Width / 2 - fontSelector.Font.Size / 2,
                    job.baseBitmap.Height / 2 - fontSelector.Font.Size / 2,
                    drawFormat);
                //draw string in the middle of the screen
                outputBitmaps.Add(output); //add output to generated bitmaps

                //drawFormat.Dispose();
                //outputgraphics.Dispose();
                //attributes.Dispose();
                if (bWorker.CancellationPending)
                {
                    return;
                }
            }

            finishedThreads++; //finish thread
        }