public void DoWork(EMode mode = EMode.Default, int scannerFlags = 0)
        {
            Stopwatch timerTotal = new Stopwatch();
            Stopwatch timerStep  = new Stopwatch();

            timerTotal.Start();

            bool debugMode = (mode & EMode.Debug) != EMode.None;

            activeScanner = null;
            currentMode   = mode;

            // load input
            bool hasInputImage = LoadInputImage(mode, timerStep, scanClipBounds);

            if (hasInputImage)
            {
                // convert to HSL representation
                timerStep.Restart();
                cachedFastBitmap = ImageUtils.ConvertToFastBitmap(screenReader.cachedScreenshot);
                timerStep.Stop();
                if (debugMode)
                {
                    Logger.WriteLine("Screenshot convert: " + timerStep.ElapsedMilliseconds + "ms");
                }
                if (Logger.IsSuperVerbose())
                {
                    Logger.WriteLine("Screenshot: {0}x{1}", screenReader.cachedScreenshot.Width, screenReader.cachedScreenshot.Height);
                }

                // reset scanner's intermediate data
                bool canResetCache = (mode & EMode.NeverResetCache) == EMode.None;
                if (canResetCache)
                {
                    bool forceResetCache = (mode & EMode.AlwaysResetCache) != EMode.None;
                    if (forceResetCache)
                    {
                        unknownHashes.Clear();
                        currentHashMatches.Clear();
                    }

                    // invalidate scanner's cache if input image size has changed
                    if (forceResetCache || cachedBitmapSize.Width <= 0 || cachedBitmapSize.Width != cachedFastBitmap.Width || cachedBitmapSize.Height != cachedFastBitmap.Height)
                    {
                        cachedBitmapSize = new Size(cachedFastBitmap.Width, cachedFastBitmap.Height);
                        foreach (var kvp in mapScanners)
                        {
                            kvp.Value.InvalidateCache();
                        }
                    }
                }

                currentState = EState.NoScannerMatch;

                // pass 1: check if cache is still valid for requested scanner
                foreach (var kvp in mapScanners)
                {
                    if ((kvp.Key & mode) != EMode.None && kvp.Value.HasValidCache(cachedFastBitmap, scannerFlags))
                    {
                        bool scanned = false;
                        try
                        {
                            scanned = kvp.Value.DoWork(cachedFastBitmap, scannerFlags, timerStep, debugMode);
                        }
                        catch (Exception ex)
                        {
                            Logger.WriteLine("Failed to scan [1] image! {0}", ex);
                            scanned = false;
                        }

                        if (scanned)
                        {
                            activeScanner = kvp.Value;
                            currentState  = (currentState == EState.NoScannerMatch) ? EState.NoErrors : currentState;
                            if (debugMode)
                            {
                                Logger.WriteLine("Scan [1] successful, type:{0}, state:{1}", kvp.Key, currentState);
                            }
                        }
                        else
                        {
                            currentState = EState.ScannerErrors;
                        }

                        break;
                    }
                }

                // pass 2: all requested
                if (activeScanner == null)
                {
                    foreach (var kvp in mapScanners)
                    {
                        if ((kvp.Key & mode) != EMode.None)
                        {
                            bool scanned = false;
                            try
                            {
                                scanned = kvp.Value.DoWork(cachedFastBitmap, scannerFlags, timerStep, debugMode);
                            }
                            catch (Exception ex)
                            {
                                Logger.WriteLine("Failed to scan [2] image! {0}", ex);
                                scanned = false;
                            }

                            if (scanned)
                            {
                                activeScanner = kvp.Value;
                                currentState  = (currentState == EState.NoScannerMatch) ? EState.NoErrors : currentState;
                                if (debugMode)
                                {
                                    Logger.WriteLine("Scan [2] successful, type:{0}, state:{1}", kvp.Key, currentState);
                                }
                                break;
                            }
                        }
                    }
                }

                // save debug markup if needed
                if ((activeScanner != null) && ((mode & EMode.DebugSaveMarkup) != EMode.None))
                {
                    List <Rectangle> debugBounds = new List <Rectangle>();
                    List <ImageUtils.HashPreview> debugHashes = new List <ImageUtils.HashPreview>();
                    activeScanner.AppendDebugShapes(debugBounds, debugHashes);

                    if (currentScanArea.Width > 0)
                    {
                        debugBounds.Add(currentScanArea);
                    }

                    timerStep.Restart();

                    string imagePath = GetDefaultScreenshotPath() + "screenshot-markup.png";
                    if (File.Exists(imagePath))
                    {
                        File.Delete(imagePath);
                    }

                    using (Bitmap markupBitmap = ImageUtils.ConvertToBitmap(cachedFastBitmap))
                    {
                        ImageUtils.DrawDebugShapes(markupBitmap, debugBounds);
                        ImageUtils.DrawDebugHashes(markupBitmap, debugHashes);

                        markupBitmap.Save(imagePath, ImageFormat.Png);
                    }

                    timerStep.Stop();
                    Logger.WriteLine("Screenshot save: " + timerStep.ElapsedMilliseconds + "ms");
                }
            }
            else
            {
                currentState = EState.NoInputImage;
            }

            timerTotal.Stop();
            if (debugMode)
            {
                Logger.WriteLine("Screenshot TOTAL: " + timerTotal.ElapsedMilliseconds + "ms");
            }
        }