コード例 #1
0
        /// <summary>
        /// Converts the pixel data to a bitmap image
        /// </summary>
        /// <param name="pixels"></param>
        /// <returns></returns>
        internal static unsafe System.Drawing.Bitmap ConvertDataToImage(int[] pixels, int width, int height)
        {
            if (width <= 0 || height <= 0)
            {
                return(null);
            }

            System.Drawing.Bitmap bmp  = new System.Drawing.Bitmap(width, height, PixelFormat.Format24bppRgb);
            BitmapData            bits = bmp.LockBits(new System.Drawing.Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, bmp.PixelFormat);

            // Value of 4 is the size of PixelFormat.Format32bppArgb
            // keep it synchronized with used Bitmap PixelFormat
            int bytesPerRow = width * 4;

            fixed(int *pixelsPtr = pixels)
            {
                byte *pixelsNativePtr = (byte *)pixelsPtr;

                for (int y = 0; y < height; y++)
                {
                    var rowPtr = (int *)((byte *)bits.Scan0 + (y * bits.Stride));
                    pixelsNativePtr += (y * bytesPerRow);
                    MemoryUtilities.MoveMemory(rowPtr, pixelsNativePtr, bytesPerRow);
                }
            }

            bmp.UnlockBits(bits);

            return(bmp);
        }
コード例 #2
0
        internal static void LoadTextureInternal(Texture t, System.Drawing.Bitmap bmp)
        {
            System.Drawing.Imaging.PixelFormat lock_format;
            switch (bmp.PixelFormat)
            {
            case System.Drawing.Imaging.PixelFormat.Format32bppArgb:
                lock_format = System.Drawing.Imaging.PixelFormat.Format32bppArgb;
                break;

            case System.Drawing.Imaging.PixelFormat.Format24bppRgb:
                lock_format = System.Drawing.Imaging.PixelFormat.Format32bppArgb;
                break;

            default:
                t.Failed = true;
                return;
            }


            int glTex;

            // Create the opengl texture
            GL.GenTextures(1, out glTex);

            GL.BindTexture(TextureTarget.Texture2D, glTex);
            lastTextureID = glTex;

            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);

            // Sort out our GWEN texture
            t.RendererData = glTex;
            t.Width        = bmp.Width;
            t.Height       = bmp.Height;

            var data = bmp.LockBits(new System.Drawing.Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, lock_format);

            GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, t.Width, t.Height, 0, PixelFormat.Bgra, PixelType.UnsignedByte, data.Scan0);


            bmp.UnlockBits(data);
            bmp.Dispose();
        }
コード例 #3
0
        /// <summary>
        /// Converts the pixel data to a bitmap image
        /// </summary>
        /// <param name="pixels"></param>
        /// <returns></returns>
        private unsafe System.Drawing.Bitmap ConvertDataToImage(int[] pixels)
        {
            // create a bitmap and manipulate it
            System.Drawing.Bitmap bmp  = new System.Drawing.Bitmap(Info.Width, Info.Height, PixelFormat.Format32bppArgb);
            BitmapData            bits = bmp.LockBits(new System.Drawing.Rectangle(0, 0, Info.Width, Info.Height), ImageLockMode.ReadWrite, bmp.PixelFormat);

            for (int y = 0; y < Info.Height; y++)
            {
                var row = (int *)((byte *)bits.Scan0 + (y * bits.Stride));

                for (int x = 0; x < Info.Width; x++)
                {
                    row[x] = pixels[y * Info.Width + x];
                }
            }

            bmp.UnlockBits(bits);

            return(bmp);
        }
コード例 #4
0
        /// <summary>
        /// Converts the pixel data to a bitmap image
        /// </summary>
        /// <param name="pixels"></param>
        /// <returns></returns>
        internal static unsafe System.Drawing.Bitmap ConvertDataToImage(int[] pixels, int width, int height)
        {
            if (width <= 0 || height <= 0)
            {
                return(null);
            }

            System.Drawing.Bitmap bmp  = new System.Drawing.Bitmap(width, height, PixelFormat.Format32bppArgb);
            BitmapData            bits = bmp.LockBits(new System.Drawing.Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, bmp.PixelFormat);

            for (int y = 0; y < height; y++)
            {
                var row = (int *)((byte *)bits.Scan0 + (y * bits.Stride));

                for (int x = 0; x < width; x++)
                {
                    row[x] = pixels[y * width + x];
                }
            }

            bmp.UnlockBits(bits);

            return(bmp);
        }
        public SC_SharpDX_ScreenCapture(int adapter, int numOutput, SharpDX.Direct3D11.Device device_)
        {
            //_textureByteArray[0] = 0;
            imageptrList      = new IntPtr[num_cols * num_rows];
            _frameCaptureData = new SC_SharpDX_ScreenFrame();

            arrayOfTexture2DFrac = new Texture2D[num_cols * num_rows];

            pastearray    = new int[num_cols * num_rows];
            pastearrayTwo = new int[num_cols * num_rows];

            arrayOfBytesTwo = new byte[_textureDescriptionFinal.Width * _textureDescriptionFinal.Height];

            _lastShaderResourceViewArray = new ShaderResourceView[num_cols * num_rows];
            _ShaderResourceViewArray     = new ShaderResourceView[num_cols * num_rows];


            _numAdapter = adapter;
            _numOutput  = numOutput;


            try
            {
                using (var _factory = new SharpDX.DXGI.Factory1())
                {
                    this._adapter = _factory.GetAdapter1(_numAdapter);
                }
            }
            catch (SharpDXException ex)
            {
                Console.WriteLine(ex.ToString());
                return;
            }

            try
            {
                //this._device = new Device(_adapter);
                //this._device = sccsVD4VE_LightNWithoutVr.SC_Console_DIRECTX._dxDevice.Device;

                this._device = device_;
            }
            catch (SharpDXException ex)
            {
                Console.WriteLine(ex.ToString());
                return;
            }

            try
            {
                //initializeOutput();
                using (var _output = _adapter.GetOutput(_numOutput))
                {
                    // Width/Height of desktop to capture
                    //getDesktopBoundaries();
                    _width  = ((SharpDX.Rectangle)_output.Description.DesktopBounds).Width;
                    _height = ((SharpDX.Rectangle)_output.Description.DesktopBounds).Height;
                    _frameCaptureData.width  = _width;
                    _frameCaptureData.height = _height;
                    this._output1            = _output.QueryInterface <Output1>();
                }
            }
            catch (SharpDXException ex)
            {
                Console.WriteLine(ex.ToString());
                return;
            }

            try
            {
                //duplicateOutput();
                this._outputDuplication = _output1.DuplicateOutput(_device);
            }
            catch (SharpDXException ex)
            {
                Console.WriteLine(ex.ToString());
                return;
            }

            try
            {
                //getTextureDescription();
                this._textureDescription = new Texture2DDescription
                {
                    CpuAccessFlags    = CpuAccessFlags.Read,
                    BindFlags         = BindFlags.None,//BindFlags.None, //| BindFlags.RenderTarget
                    Format            = Format.B8G8R8A8_UNorm,
                    Width             = _width,
                    Height            = _height,
                    OptionFlags       = ResourceOptionFlags.None,
                    MipLevels         = 1,
                    ArraySize         = 1,
                    SampleDescription = { Count = 1, Quality = 0 },
                    Usage             = ResourceUsage.Staging
                };

                this._textureDescriptionFinal = new Texture2DDescription
                {
                    CpuAccessFlags    = CpuAccessFlags.None,
                    BindFlags         = BindFlags.ShaderResource | BindFlags.RenderTarget,
                    Format            = Format.B8G8R8A8_UNorm,
                    Width             = _width,
                    Height            = _height,
                    OptionFlags       = ResourceOptionFlags.GenerateMipMaps,
                    MipLevels         = 1,
                    ArraySize         = 1,
                    SampleDescription = { Count = 1, Quality = 0 },
                    Usage             = ResourceUsage.Default
                };



                wid = _textureDescriptionFinal.Width / num_cols;
                hgt = _textureDescriptionFinal.Height / num_rows;

                /*this._textureDescriptionFinalFrac = new Texture2DDescription
                 * {
                 *  CpuAccessFlags = CpuAccessFlags.None,
                 *  BindFlags = BindFlags.ShaderResource | BindFlags.RenderTarget,
                 *  Format = Format.B8G8R8A8_UNorm,
                 *  Width = wid,
                 *  Height = hgt,
                 *  OptionFlags = ResourceOptionFlags.GenerateMipMaps,
                 *  MipLevels = 1,
                 *  ArraySize = 1,
                 *  SampleDescription = { Count = 1, Quality = 0 },
                 *  Usage = ResourceUsage.Default
                 * };*/

                this._textureDescriptionFinalFrac = new Texture2DDescription
                {
                    CpuAccessFlags    = CpuAccessFlags.Read,
                    BindFlags         = BindFlags.None,//BindFlags.None, //| BindFlags.RenderTarget
                    Format            = Format.B8G8R8A8_UNorm,
                    Width             = wid,
                    Height            = hgt,
                    OptionFlags       = ResourceOptionFlags.None,
                    MipLevels         = 1,
                    ArraySize         = 1,
                    SampleDescription = { Count = 1, Quality = 0 },
                    Usage             = ResourceUsage.Staging
                };



                piece     = new Bitmap(wid, hgt);
                gr        = Graphics.FromImage(piece);
                dest_rect = new System.Drawing.Rectangle(0, 0, wid, hgt);

                strider = wid * 4;

                for (int i = 0; i < arrayOfImage.Length; i++)
                {
                    arrayOfImage[i] = new int[wid * hgt * 4];
                }

                for (int i = 0; i < arrayOfBytes.Length; i++)
                {
                    arrayOfBytes[i] = new byte[wid * hgt * 4];
                }


                piece     = new System.Drawing.Bitmap(wid, hgt);
                dest_rect = new System.Drawing.Rectangle(0, 0, wid, hgt);

                //int num_rows = _textureDescriptionFinal.Height / hgt;
                //int num_cols = _textureDescriptionFinal.Width / wid;
                source_rect = new System.Drawing.Rectangle(0, 0, wid, hgt);


                for (int tex2D = 0; tex2D < 10 * 10; tex2D++)
                {
                    arrayOfTexture2DFrac[tex2D] = new Texture2D(_device, _textureDescriptionFinalFrac);
                }
            }
            catch (SharpDXException ex)
            {
                Console.WriteLine(ex.ToString());
                return;
            }

            _texture2D      = new Texture2D(_device, _textureDescription);
            _texture2DFinal = new Texture2D(_device, _textureDescriptionFinal);

            resourceViewDescription = new ShaderResourceViewDescription
            {
                Format    = _texture2DFinal.Description.Format,
                Dimension = SharpDX.Direct3D.ShaderResourceViewDimension.Texture2D,
                Texture2D = new ShaderResourceViewDescription.Texture2DResource
                {
                    MipLevels       = -1,
                    MostDetailedMip = 0
                }
            };

            _bitmap = new System.Drawing.Bitmap(_width, _height, PixelFormat.Format32bppArgb);
            var boundsRect = new System.Drawing.Rectangle(0, 0, _width, _height);
            var bmpData    = _bitmap.LockBits(boundsRect, ImageLockMode.ReadOnly, _bitmap.PixelFormat);

            _bytesTotal = Math.Abs(bmpData.Stride) * _bitmap.Height;
            _bitmap.UnlockBits(bmpData);
            _textureByteArray = new byte[_bytesTotal];



            /*try
             * {
             *
             * }
             * catch (SharpDXException ex)
             * {
             *  Console.WriteLine(ex.ToString());
             *  return;
             * }*/
        }
コード例 #6
0
        /// <summary>
        /// Gets the image for the page
        /// </summary>
        /// <returns></returns>
        public unsafe System.Drawing.Bitmap BuildImage(int subsample = 1)
        {
            lock (_loadingLock)
            {
                DateTime start = DateTime.Now;
                System.Drawing.Bitmap background = GetBackgroundImage(subsample, false);
                Console.WriteLine("Background: " + DateTime.Now.Subtract(start).TotalMilliseconds);
                start = DateTime.Now;

                using (System.Drawing.Bitmap foreground = GetForegroundImage(subsample, false))
                {
                    Console.WriteLine("Foreground: " + DateTime.Now.Subtract(start).TotalMilliseconds);
                    start = DateTime.Now;

                    using (System.Drawing.Bitmap mask = GetTextImage(subsample, false))
                    {
                        Console.WriteLine("Mask: " + DateTime.Now.Subtract(start).TotalMilliseconds);
                        start = DateTime.Now;

                        _hasLoaded = true;

                        BitmapData backgroundData =
                            background.LockBits(new System.Drawing.Rectangle(0, 0, background.Width, background.Height),
                                                ImageLockMode.ReadWrite, background.PixelFormat);
                        int backgroundPixelSize = GetPixelSize(backgroundData);

                        BitmapData foregroundData =
                            foreground.LockBits(new System.Drawing.Rectangle(0, 0, foreground.Width, foreground.Height),
                                                ImageLockMode.ReadOnly, foreground.PixelFormat);
                        int foregroundPixelSize = GetPixelSize(foregroundData);

                        BitmapData maskData = mask.LockBits(new System.Drawing.Rectangle(0, 0, mask.Width, mask.Height),
                                                            ImageLockMode.ReadOnly, mask.PixelFormat);

                        //int maskPixelSize = GetPixelSize(maskData);

                        int height = background.Height;
                        int width  = background.Width;

                        Parallel.For(
                            0,
                            height,
                            y =>
                        {
                            byte *maskRow       = (byte *)maskData.Scan0 + (y * maskData.Stride);
                            uint *backgroundRow = (uint *)(backgroundData.Scan0 + (y * backgroundData.Stride));
                            uint *foregroundRow = (uint *)(foregroundData.Scan0 + (y * foregroundData.Stride));

                            for (int x = 0; x < width; x++)
                            {
                                // Check if the mask byte is set
                                if (maskRow[x] > 0)
                                {
                                    backgroundRow[x] = _isInverted == true
                                                               ? InvertColor(foregroundRow[x])
                                                               : foregroundRow[x];
                                }
                                else if (_isInverted == true)
                                {
                                    backgroundRow[x] = InvertColor(backgroundRow[x]);
                                }
                            }
                        });

                        mask.UnlockBits(maskData);
                        foreground.UnlockBits(foregroundData);
                        background.UnlockBits(backgroundData);

                        return(background);
                    }
                }
            }
        }
コード例 #7
0
ファイル: DjvuPage.cs プロジェクト: Riksarkivet/DjvuNet
        /// <summary>
        /// Converts the pixel data to a bitmap image
        /// </summary>
        /// <param name="pixels"></param>
        /// <returns></returns>
        private unsafe System.Drawing.Bitmap ConvertDataToImage(int[] pixels)
        {
            // create a bitmap and manipulate it
            System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(Info.Width, Info.Height, PixelFormat.Format32bppArgb);
            BitmapData bits = bmp.LockBits(new System.Drawing.Rectangle(0, 0, Info.Width, Info.Height), ImageLockMode.ReadWrite, bmp.PixelFormat);

            for (int y = 0; y < Info.Height; y++)
            {
                var row = (int*)((byte*)bits.Scan0 + (y * bits.Stride));

                for (int x = 0; x < Info.Width; x++)
                {
                    row[x] = pixels[y * Info.Width + x];
                }
            }

            bmp.UnlockBits(bits);

            return bmp;
        }
コード例 #8
0
        public unsafe System.Drawing.Bitmap BuildImage(int subsample = 1)
        {
            Verify.SubsampleRange(subsample);

            lock (LoadingLock)
            {
                System.Drawing.Bitmap background = GetBackgroundImage(subsample, true);

                // TODO ETW logging goes here

                using (System.Drawing.Bitmap foreground = GetForegroundImage(subsample, true))
                {
                    using (System.Drawing.Bitmap mask = GetMaskImage(subsample, true))
                    {
                        HasLoaded = true;

                        BitmapData backgroundData =
                            background.LockBits(new System.Drawing.Rectangle(0, 0, background.Width, background.Height),
                                                ImageLockMode.ReadWrite, background.PixelFormat);
                        int backgroundPixelSize = DjvuImage.GetPixelSize(backgroundData.PixelFormat);

                        BitmapData foregroundData =
                            foreground.LockBits(new System.Drawing.Rectangle(0, 0, foreground.Width, foreground.Height),
                                                ImageLockMode.ReadOnly, foreground.PixelFormat);
                        int foregroundPixelSize = DjvuImage.GetPixelSize(foregroundData.PixelFormat);

                        BitmapData maskData = mask.LockBits(new System.Drawing.Rectangle(0, 0, mask.Width, mask.Height),
                                                            ImageLockMode.ReadOnly, mask.PixelFormat);

                        //int maskPixelSize = GetPixelSize(maskData);

                        int bgndHeight = background.Height;
                        int bgndWidth  = background.Width;

                        int fgndHeight = foreground.Height;
                        int fgndWidth  = foreground.Width;

                        int maskHeight = mask.Height;
                        int maskWidth  = mask.Width;

                        int maskbgnH = maskHeight / bgndHeight;
                        int maskfgnH = maskHeight / fgndHeight;

                        int maskbgnW = maskWidth / bgndWidth;
                        int maskfgnW = maskWidth / fgndWidth;

                        //Parallel.For(
                        //    0,
                        //    height,
                        //    y =>
                        //    {

                        for (int y = 0, yf = 0, yb = 0; y < maskHeight && yb < bgndHeight && yf < fgndHeight; ++y, yf = yb = y)
                        {
                            byte *maskRow = (byte *)maskData.Scan0 + (y * maskData.Stride);
                            DjvuNet.Graphics.Pixel *backgroundRow = (DjvuNet.Graphics.Pixel *)(backgroundData.Scan0 + (yb * backgroundData.Stride));
                            DjvuNet.Graphics.Pixel *foregroundRow = (DjvuNet.Graphics.Pixel *)(foregroundData.Scan0 + (yf * foregroundData.Stride));

                            for (int x = 0, xf = 0, xb = 0; x < bgndWidth && xb < maskWidth && xf < fgndWidth; x++)
                            {
                                // Check if the mask byte is set
                                if (maskRow[x] > 0)
                                {
                                    DjvuNet.Graphics.Pixel xF = foregroundRow[xf];

                                    if (_IsInverted)
                                    {
                                        backgroundRow[xb] = InvertColor(xF);
                                    }
                                    else
                                    {
                                        backgroundRow[xb] = xF;
                                    }
                                }
                                else if (_IsInverted)
                                {
                                    backgroundRow[xb] = InvertColor(backgroundRow[xb]);
                                }

                                if (x >= 0)
                                {
                                    if (x % maskbgnW == 0)
                                    {
                                        xb++;
                                    }

                                    if (x % maskfgnW == 0)
                                    {
                                        xf++;
                                    }
                                }
                            }

                            if (y >= 0)
                            {
                                if (y % maskbgnH == 0)
                                {
                                    yb++;
                                }

                                if (y % maskfgnH == 0)
                                {
                                    yf++;
                                }
                            }
                        }
                        //});

                        mask.UnlockBits(maskData);
                        foreground.UnlockBits(foregroundData);
                        background.UnlockBits(backgroundData);

                        return(background);
                    }
                }
            }
        }
コード例 #9
0
ファイル: DjvuPage.cs プロジェクト: fel88/CommonLibs
        /// <summary>
        /// Gets the image for the page
        /// </summary>
        /// <returns></returns>
        public System.Drawing.Bitmap BuildImage(int subsample = 1)
        {
            lock (_loadingLock)
            {
                DateTime start = DateTime.Now;
                System.Drawing.Bitmap background = GetBackgroundImage(subsample, false);
                Console.WriteLine("Background: " + DateTime.Now.Subtract(start).TotalMilliseconds);
                start = DateTime.Now;

                using (System.Drawing.Bitmap _foreground = GetForegroundImage(subsample, false))
                {
                    Console.WriteLine("Foreground: " + DateTime.Now.Subtract(start).TotalMilliseconds);
                    start = DateTime.Now;

                    using (System.Drawing.Bitmap mask = GetTextImage(subsample, false))
                    {
                        Console.WriteLine("Mask: " + DateTime.Now.Subtract(start).TotalMilliseconds);
                        start = DateTime.Now;

                        _hasLoaded = true;

                        BitmapData backgroundData =
                            background.LockBits(new System.Drawing.Rectangle(0, 0, background.Width, background.Height),
                                                ImageLockMode.ReadWrite, background.PixelFormat);
                        int backgroundPixelSize = GetPixelSize(backgroundData);


                        Bitmap foreground = null;
                        if (_foreground.Height != background.Height || _foreground.Width != background.Width)
                        {
                            foreground = new Bitmap(background.Width, background.Height);
                            System.Drawing.Graphics gr = System.Drawing.Graphics.FromImage(foreground);
                            gr.DrawImage(_foreground, new Rectangle(0, 0, background.Width, background.Height), new Rectangle(0, 0, _foreground.Width, _foreground.Height), GraphicsUnit.Pixel);
                        }
                        else
                        {
                            foreground = _foreground;
                        }


                        BitmapData foregroundData =
                            foreground.LockBits(new System.Drawing.Rectangle(0, 0, foreground.Width, foreground.Height),
                                                ImageLockMode.ReadOnly, foreground.PixelFormat);
                        int foregroundPixelSize = GetPixelSize(foregroundData);

                        BitmapData maskData = mask.LockBits(new System.Drawing.Rectangle(0, 0, mask.Width, mask.Height),
                                                            ImageLockMode.ReadOnly, mask.PixelFormat);



                        if (backgroundData.Height != foregroundData.Height)
                        {
                            throw new ArgumentException("foreground height!=background height");
                        }

                        int    size = backgroundData.Stride * backgroundData.Height;
                        byte[] data = new byte[size];



                        int    fsize = foregroundData.Stride * foregroundData.Height;
                        byte[] fdata = new byte[fsize];
                        System.Runtime.InteropServices.Marshal.Copy(foregroundData.Scan0, fdata, 0, fsize);

                        int    msize = maskData.Stride * maskData.Height;
                        byte[] mdata = new byte[msize];
                        System.Runtime.InteropServices.Marshal.Copy(maskData.Scan0, mdata, 0, msize);

                        //int maskPixelSize = GetPixelSize(maskData);

                        int height = background.Height;
                        int width  = background.Width;
                        var bpxsz  = Bitmap.GetPixelFormatSize(background.PixelFormat) / 8;
                        var frsz   = Bitmap.GetPixelFormatSize(foreground.PixelFormat) / 8;
                        var msksz  = Bitmap.GetPixelFormatSize(mask.PixelFormat) / 8;
                        for (int y = 0; y < height; y++)
                        {
                            byte maskRow = mdata[y * maskData.Stride];
                            int  mult    = y * backgroundData.Stride;
                            int  fmult   = y * foregroundData.Stride;
                            for (int x = 0; x < width; x++)
                            {
                                // Check if the mask byte is set
                                maskRow = mdata[y * maskData.Stride + x * msksz];
                                if (maskRow > 0)
                                {
                                    var flag = _isInverted == true;
                                    var b1   = BitConverter.ToUInt32(fdata, fmult + x * frsz);
                                    var b2   = InvertColor(b1);
                                    var b3   = flag ? b2 : b1;

                                    for (int i = 0; i < frsz; i++)
                                    {
                                        data[mult + x * bpxsz + i] = (byte)((b3 & (0xff << (i * 8))) >> (i * 8));
                                    }
                                    var res = BitConverter.ToUInt32(data, mult + x * bpxsz);
                                }
                                else if (_isInverted == true)
                                {
                                    var b2 = InvertColor(BitConverter.ToInt32(data, mult + x * bpxsz));
                                    for (int i = 0; i < frsz; i++)
                                    {
                                        data[mult + x * bpxsz + i] = (byte)((b2 & (0xff << (i * 8))) >> (i * 8));
                                    }
                                }
                            }
                        }

                        System.Runtime.InteropServices.Marshal.Copy(data, 0, backgroundData.Scan0, data.Length);

                        mask.UnlockBits(maskData);
                        foreground.UnlockBits(foregroundData);
                        background.UnlockBits(backgroundData);

                        return(background);
                    }
                }
            }
        }
コード例 #10
0
        /// <summary>
        /// Gets the image for the page
        /// </summary>
        /// <returns>
        /// <see cref="System.Drawing.Bitmap"/>Bitmap image.
        /// </returns>
        public unsafe System.Drawing.Bitmap BuildImage(int subsample = 1)
        {
            //
            // TODO Fix image skew
            //

            Verify.SubsampleRange(subsample);

            lock (_LoadingLock)
            {
                Stopwatch stopWatch = Stopwatch.StartNew();

                System.Drawing.Bitmap background = GetBackgroundImage(subsample, false);

                stopWatch.Stop();

                // TODO ETW logging goes here

                stopWatch.Restart();

                using (System.Drawing.Bitmap foreground = GetForegroundImage(subsample, false))
                {
                    stopWatch.Stop();
                    // TODO ETW logging goes here

                    stopWatch.Restart();

                    using (System.Drawing.Bitmap mask = GetTextImage(subsample, false))
                    {
                        stopWatch.Stop();
                        // TODO ETW logging goes here

                        stopWatch.Restart();

                        _HasLoaded = true;

                        BitmapData backgroundData =
                            background.LockBits(new System.Drawing.Rectangle(0, 0, background.Width, background.Height),
                                                ImageLockMode.ReadWrite, background.PixelFormat);
                        int backgroundPixelSize = DjvuImage.GetPixelSize(backgroundData.PixelFormat);

                        BitmapData foregroundData =
                            foreground.LockBits(new System.Drawing.Rectangle(0, 0, foreground.Width, foreground.Height),
                                                ImageLockMode.ReadOnly, foreground.PixelFormat);
                        int foregroundPixelSize = DjvuImage.GetPixelSize(foregroundData.PixelFormat);

                        BitmapData maskData = mask.LockBits(new System.Drawing.Rectangle(0, 0, mask.Width, mask.Height),
                                                            ImageLockMode.ReadOnly, mask.PixelFormat);

                        //int maskPixelSize = GetPixelSize(maskData);

                        int bgndHeight = background.Height;
                        int bgndWidth  = background.Width;

                        int fgndHeight = foreground.Height;
                        int fgndWidth  = foreground.Width;

                        int maskHeight = mask.Height;
                        int maskWidth  = mask.Width;

                        int maskbgnH = maskHeight / bgndHeight;
                        int maskfgnH = maskHeight / fgndHeight;

                        int maskbgnW = maskWidth / bgndWidth;
                        int maskfgnW = maskWidth / fgndWidth;

                        //Parallel.For(
                        //    0,
                        //    height,
                        //    y =>
                        //    {
                        ;
                        for (int y = 0, yf = 0, yb = 0; y < maskHeight && yb < bgndHeight && yf < fgndHeight; y++)
                        {
                            byte *maskRow       = (byte *)maskData.Scan0 + (y * maskData.Stride);
                            uint *backgroundRow = (uint *)(backgroundData.Scan0 + (yb * backgroundData.Stride));
                            uint *foregroundRow = (uint *)(foregroundData.Scan0 + (yf * foregroundData.Stride));

                            for (int x = 0, xf = 0, xb = 0; x < bgndWidth && xb < maskWidth && xf < fgndWidth; x++)
                            {
                                // Check if the mask byte is set
                                if (maskRow[x] > 0)
                                {
                                    uint xF = foregroundRow[xf];

                                    if (_IsInverted)
                                    {
                                        backgroundRow[xb] = InvertColor(xF);
                                    }
                                    else
                                    {
                                        backgroundRow[xb] = xF;
                                    }
                                }
                                else if (_IsInverted == true)
                                {
                                    uint xB = backgroundRow[xb];
                                    backgroundRow[xb] = InvertColor(xB);
                                }

                                if (x > 0)
                                {
                                    if (x % maskbgnW == 0)
                                    {
                                        xb++;
                                    }

                                    if (x % maskfgnW == 0)
                                    {
                                        xf++;
                                    }
                                }
                            }

                            if (y > 0)
                            {
                                if (y % maskbgnH == 0)
                                {
                                    yb++;
                                }

                                if (y % maskfgnH == 0)
                                {
                                    yf++;
                                }
                            }
                        }
                        //});

                        mask.UnlockBits(maskData);
                        foreground.UnlockBits(foregroundData);
                        background.UnlockBits(backgroundData);

                        stopWatch.Stop();
                        // TODO ETW logging goes here

                        return(background);
                    }
                }
            }
        }