[.net] Maximizing GDI+ Speed

Started by
19 comments, last by Nasenbaer 12 years, 4 months ago
Hello everyone, I've recently been working on a 2D RTS in C#, using nothing but GDI+ for rendering. This worked fairly well for me, but after upgrading monitors I've noticed that my game's performance has sunk incredibly low. After a quick trip to google I found out that GDI+ doesn't use any hardware acceleration - so as my resolution increased, my computer just couldn't put up with the pixel throughput I needed. So I went to work researching techniques to speed up GDI+. The following is both a collection of tips I've found on the web, and the results of my own ideas. I hope you find them useful. Tip #1 - PixelFormat.Format32bppPArgb When creating Bitmap objects, use PixelFormat.Format32bppPArgb. This provides considerable speed increases - something to do with the internal format used by the Bitmap object. This is extremely simple to implement. Tip #2 - Split large Bitmaps into multiple, smaller bitmaps Instead of having a massive bitmap with a resolution of, say, 1024x1024, break it into smaller chunks of something like 64x64. This will give you a minor increase in speed, due to the way DrawImageUnscaled() scales with size (I have benchmarks I'll post further on). I find that the easiest way to do this is to simply break images up into scan lines, and put them in an array. For the example I gave, I'd have an array of Bitmaps with 1024 entries, each 1024x1 pixels. You can split the images nicely using Bitmap.LockBits() and System.Runtime.InteropServices.Marshal. If there's a demand for them, I'll post examples. It's not too difficult to implement/use if you encapsulate it in its own class. How to draw Bitmaps This is the big one that I have been researching. As far as I know, I'm the only one who's investigated these different techniques and posted an analysis on the internet. If you find any other articles about this on the web, please let me know. Note: The numbers posted here are for copying an image 100 times. Basically, they'll give you an indication of relative speed, but don't use those numbers to do any calculations. If you really want to, you'll have to extrapolate the speed for one iteration. I did this so that numbers wouldn't get ridiculously huge, and so that I wouldn't have output like "Infinity pixels/ms" :P DrawImageUnscaled The traditional method of drawing an image onto a backbuffer is to use DrawImageUnscaled() This works pretty well, and scales excellently. The sweet spot for this technique is images about 384 pixels in width and height, which gives 915 pixels/ms. Unfortunately, it's still ridiculously slow. If your backbuffer and image use the same pixel format, this doesn't make a lot of sense over blitting... Blit the entire image to the backbuffer using System.Marshal The process here is simple. LockBits() on both the backbuffer and the image, create an array of bytes to act as a buffer, copy the image to that byte array in its entirety using Marshal.Copy(), then copy the byte array to the back buffer, once again using Marshal.Copy(). This is somewhere around twice as fast as DrawImageUnscaled, but it scales horribly, probably due to the memory usage skyrocketing as images get bigger. Interestingly, Marshal doesn't allow you to copy from IntPtr to IntPtr (that I've noticed...). Using unsafe code to copy byte-by-byte directly from the image to the backbuffer is far too slow, so I'm content to leave this method as it is. The sweet spot for this method is about 256x256 pixels, which gives 3855 pixels/ms. Wait, it gets better... Blit the image to the backbuffer in small chunks using System.Marshal Allocating and filling chunks of memory several megabytes in size definitely can't be helping speed, so this technique simply uses a smaller buffer size and fills it chunk-by-chunk. In my test case, the buffer size was BitmapData.Stride bytes for each resolution. Basically I copied each scan line from the Bitmap into my array, and then copied that array to the back buffer. The speed is amazing, and the scaling seems to be a little better than blitting the entire image at once (haven't checked this with real maths though). The sweet spot for this technique is about 576x576 pixels, and gives a throughput of 9479 pixels/ms. With proper tweaking, you could probably get a little more out of this method, but I can't think of any ways to increase speed a great deal more than this method does. In order to get more speed, the complexity of copying the image increases. As long as everything has the same format, however, you should be able to write a simple function to do your blitting for you. For the cases where the formats are different, you'll lose a bit of speed, but it is probably still worthwhile to blit. Conclusion I hope someone finds this useful. If you have any ideas on how to speed things up further, please let me know! The final technique is more than fast enough for my needs at the moment. Also, if anyone wants source code for different methods, just let me know. I can also post the code I used for my benchmark program, if there's a demand for it. Benchmark Results These should help you optimize things, and give you a feel for the speed of the different methods. I've yet to do a detailed analysis of them - when/if I do, I'll post it here as well.
Quote: Using 100 repetitions per size Rendering using DrawImageUnscaled() Size: 128 18 ms = 910.222222222222 pixels/ms Size: 192 37 ms = 996.324324324324 pixels/ms Size: 256 66 ms = 992.969696969697 pixels/ms Size: 320 109 ms = 939.449541284404 pixels/ms Size: 384 154 ms = 957.506493506493 pixels/ms Size: 448 206 ms = 974.291262135922 pixels/ms Size: 512 302 ms = 868.026490066225 pixels/ms Size: 576 366 ms = 906.491803278689 pixels/ms Size: 640 446 ms = 918.385650224215 pixels/ms Size: 704 555 ms = 893.001801801802 pixels/ms Size: 768 651 ms = 906.027649769585 pixels/ms Size: 832 765 ms = 904.867973856209 pixels/ms Size: 896 896 ms = 896 pixels/ms Size: 960 1048 ms = 879.389312977099 pixels/ms Size: 1024 1194 ms = 878.204355108878 pixels/ms Size: 1088 1364 ms = 867.847507331378 pixels/ms Size: 1152 1510 ms = 878.876821192053 pixels/ms Size: 1216 1687 ms = 876.500296384114 pixels/ms Size: 1280 1852 ms = 884.665226781857 pixels/ms Size: 1344 2049 ms = 881.569546120059 pixels/ms Size: 1408 2249 ms = 881.486883059137 pixels/ms Size: 1472 2396 ms = 904.333889816361 pixels/ms Size: 1536 2686 ms = 878.367833209233 pixels/ms Size: 1600 2851 ms = 897.930550683971 pixels/ms Size: 1664 3161 ms = 875.955710218285 pixels/ms Rendering using blitting Size: 128 8 ms = 2048 pixels/ms Size: 192 22 ms = 1675.63636363636 pixels/ms Size: 256 28 ms = 2340.57142857143 pixels/ms Size: 320 31 ms = 3303.22580645161 pixels/ms Size: 384 53 ms = 2782.18867924528 pixels/ms Size: 448 69 ms = 2908.75362318841 pixels/ms Size: 512 126 ms = 2080.50793650794 pixels/ms Size: 576 169 ms = 1963.17159763314 pixels/ms Size: 640 232 ms = 1765.51724137931 pixels/ms Size: 704 298 ms = 1663.14093959732 pixels/ms Size: 768 457 ms = 1290.64332603939 pixels/ms Size: 832 425 ms = 1628.76235294118 pixels/ms Size: 896 509 ms = 1577.2416502947 pixels/ms Size: 960 590 ms = 1562.03389830508 pixels/ms Size: 1024 657 ms = 1596.00608828006 pixels/ms Size: 1088 687 ms = 1723.06259097525 pixels/ms Size: 1152 784 ms = 1692.73469387755 pixels/ms Size: 1216 911 ms = 1623.11306256861 pixels/ms Size: 1280 1026 ms = 1596.88109161793 pixels/ms Size: 1344 1120 ms = 1612.8 pixels/ms Size: 1408 1222 ms = 1622.31096563011 pixels/ms Size: 1472 1332 ms = 1626.71471471471 pixels/ms Size: 1536 1482 ms = 1591.96761133603 pixels/ms Size: 1600 1590 ms = 1610.06289308176 pixels/ms Size: 1664 1730 ms = 1600.51791907514 pixels/ms Rendering using blitting - one line at a time Size: 128 3 ms = 5461.33333333333 pixels/ms Size: 192 6 ms = 6144 pixels/ms Size: 256 9 ms = 7281.77777777778 pixels/ms Size: 320 12 ms = 8533.33333333333 pixels/ms Size: 384 17 ms = 8673.88235294118 pixels/ms Size: 448 22 ms = 9122.90909090909 pixels/ms Size: 512 29 ms = 9039.44827586207 pixels/ms Size: 576 34 ms = 9758.11764705882 pixels/ms Size: 640 44 ms = 9309.09090909091 pixels/ms Size: 704 61 ms = 8124.85245901639 pixels/ms Size: 768 93 ms = 6342.1935483871 pixels/ms Size: 832 122 ms = 5673.96721311475 pixels/ms Size: 896 146 ms = 5498.7397260274 pixels/ms Size: 960 174 ms = 5296.55172413793 pixels/ms Size: 1024 229 ms = 4578.93449781659 pixels/ms Size: 1088 240 ms = 4932.26666666667 pixels/ms Size: 1152 277 ms = 4790.98916967509 pixels/ms Size: 1216 314 ms = 4709.09554140127 pixels/ms Size: 1280 350 ms = 4681.14285714286 pixels/ms Size: 1344 388 ms = 4655.50515463918 pixels/ms Size: 1408 428 ms = 4631.92523364486 pixels/ms Size: 1472 466 ms = 4649.75107296137 pixels/ms Size: 1536 522 ms = 4519.72413793103 pixels/ms Size: 1600 545 ms = 4697.24770642202 pixels/ms Size: 1664 599 ms = 4622.53088480801 pixels/ms
[Edited by - shiz98 on October 9, 2007 3:15:46 PM]
Advertisement
Benchmark Analysis
After plugging the data into Excel, what I've determined is that contrary to my initial thoughts, all methods are linear with respect to pixels. The third method is fastest both in terms of rate and in terms of constant size. The second method comes next for both rate and constant, and DrawImageUnscaled() comes in last. The only advantage I'd say DrawImageUnscaled() has is that it is the most consistent, with the lowest R^2 of the three. Here's the pertinent info:
Quote:
DrawImageUnscaled():
y = 0.0011x -6.7591
R^2 = 0.9996

Blitting:
y = 0.0006x - 14.9366
R^2 = 0.9973

Blitting in chunks:
y = 0.0002x - 23.6602
R^2 = 0.9952

Obviously it'd be best if I got some data for lower sizes, but the benefits don't warrant re-running the benchmark and inputting the data again.

[Edited by - shiz98 on October 9, 2007 3:00:53 PM]
You can indeed speed things up further by adjusting graphics quality. These can effect the speed of rendering considerably. Especially using NearestNeighbor interpolation when you use DrawImage to stretch an image.

Check out these:

Graphics.InterpolationMode
Graphics.SmoothingMode
Graphics.PixelOffsetMode
Graphics.CompositingQuality
Graphics.TextRenderingHint

You will need to set these before you call DrawImage.

Fast rendering:

g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.Low; // or NearestNeighbourg.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None;g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.None;g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighSpeed;g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.SingleBitPerPixel;


Slow rendering:

g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.High;g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
Thanks for the Tips.

Can you post the code for that benchmark?
Here's the code. It's nothing pretty - just a quick and dirty benchmark.

using System;using System.Collections.Generic;using System.Text;using System.Drawing;using System.Drawing.Imaging;namespace RenderingBenchmark{    class Program    {        static void Main(string[] args)        {            int origSize = 64;            int size = origSize;            int runs = 1;            int reps = 25;            bool exponential = false;            bool outputTxt = false;                        System.IO.StreamWriter writer = new System.IO.StreamWriter("");            if (outputTxt)            {                writer = new System.IO.StreamWriter("output.txt");                Console.SetOut(writer);            }            Console.WriteLine("Benchmarking blitting times");            Console.WriteLine("Using " + runs.ToString() + " repetitions per size");            Console.WriteLine("Rendering using DrawImageUnscaled()\n");            DateTime start;            DateTime finish;            TimeSpan elapsed;                        for (int i = 0; i < reps; i++)            {                if (exponential)                    size = size * 2;                else                    size += origSize;                Console.Write("Size: " + size.ToString() + " ");                Bitmap orig = new Bitmap(size, size);                Bitmap from = new Bitmap(size, size);                start = DateTime.Now;                for (int j = 0; j < runs; j++)                {                    Graphics.FromImage(orig).DrawImageUnscaled(from, new Point(0, 0));                }                finish = DateTime.Now;                elapsed = finish - start;                double ppms = (size * size) / elapsed.TotalMilliseconds;                Console.WriteLine(elapsed.TotalMilliseconds.ToString() + " ms = " + ppms.ToString() + " pixels/ms");            }            Console.WriteLine("\nRendering using blitting\n");            size = origSize;            for (int i = 0; i < reps; i++)            {                if (exponential)                    size = size * 2;                else                    size += origSize;                Console.Write("Size: " + size.ToString() + " ");                Bitmap orig = new Bitmap(size, size, PixelFormat.Format32bppPArgb);                Bitmap from = new Bitmap(size, size, PixelFormat.Format32bppPArgb);                start = DateTime.Now;                for (int j = 0; j < runs; j++)                {                    Rectangle lockRect = new Rectangle(0, 0, size, size);                    BitmapData origData = orig.LockBits(lockRect, ImageLockMode.WriteOnly, PixelFormat.Format32bppPArgb);                    BitmapData fromData = from.LockBits(lockRect, ImageLockMode.ReadOnly, PixelFormat.Format32bppPArgb);                                       //see if this works - technique one                    //origData.Scan0 = fromData.Scan0;                    //manually copy data over                                        byte[] data = new byte[origData.Stride * origData.Height]; //copy the whole thing at a time                    System.Runtime.InteropServices.Marshal.Copy(fromData.Scan0, data, 0, data.Length);                    System.Runtime.InteropServices.Marshal.Copy(data, 0, origData.Scan0, data.Length);                    orig.UnlockBits(origData);                    from.UnlockBits(fromData);                }                finish = DateTime.Now;                elapsed = finish - start;                double ppms = (size * size) / elapsed.TotalMilliseconds;                Console.WriteLine(elapsed.TotalMilliseconds.ToString() + " ms = " + ppms.ToString() + " pixels/ms");            }            Console.WriteLine("\nRendering using blitting - one line at a time\n");            size = origSize;            for (int i = 0; i < reps; i++)            {                if (exponential)                    size = size * 2;                else                    size += origSize;                Console.Write("Size: " + size.ToString() + " ");                Bitmap orig = new Bitmap(size, size, PixelFormat.Format32bppPArgb);                Bitmap from = new Bitmap(size, size, PixelFormat.Format32bppPArgb);                start = DateTime.Now;                for (int j = 0; j < runs; j++)                {                    Rectangle lockRect = new Rectangle(0, 0, size, size);                    BitmapData origData = orig.LockBits(lockRect, ImageLockMode.WriteOnly, PixelFormat.Format32bppPArgb);                    BitmapData fromData = from.LockBits(lockRect, ImageLockMode.ReadOnly, PixelFormat.Format32bppPArgb);                    //see if this works - technique one                    //origData.Scan0 = fromData.Scan0;                    //manually copy data over                    byte[] data = new byte[origData.Stride];                    for (int row = 0; row < origData.Height; row++)                    {                        IntPtr fromPtr = (IntPtr)(fromData.Scan0.ToInt64() + row * origData.Stride);                        IntPtr toPtr = (IntPtr)(origData.Scan0.ToInt64() + row * origData.Stride);                        System.Runtime.InteropServices.Marshal.Copy(fromPtr, data, 0, data.Length);                        System.Runtime.InteropServices.Marshal.Copy(data, 0, toPtr, data.Length);                    }                    orig.UnlockBits(origData);                    from.UnlockBits(fromData);                }                finish = DateTime.Now;                elapsed = finish - start;                double ppms = (size * size) / elapsed.TotalMilliseconds;                Console.WriteLine(elapsed.TotalMilliseconds.ToString() + " ms = " + ppms.ToString() + " pixels/ms");            }                        Console.WriteLine("\nPress any key to continue...");            Console.ReadKey(true);            if (outputTxt || writer != null)                writer.Close();        }    }}
Great post, first off, I was about to give up on GDI+ altogether. Now my question is how do you do screen rendering?

I know there are different ways to do double buffering. There's the .Net-handles-everything way of "Form.DoubleBuffered = true". Then there is the BufferedGraphics class (which I havent looked into extensively). Last, one could just manage their own buffer through a bitmap. I don't have any preference for which method to use, but I can't see any of them helping me escape calling DrawImage. For example, if I drew everything to a bitmap first (which would be fast using the info in your post), I would still then have to copy the end result to the screen via Graphics.DrawImage (which would be slow).

So I guess what I'm asking is what is the "fast" way of getting data to the screen itself? Thanks!
Well, I've been unable to find a way to double buffer my forms by rendering from a bitmap to a bitmap; this doesn't make sense from a GDI perspective anyway. Unfortunately for us, .NET's built-in double buffering is pretty terrible (at least from what I've noticed). The only options left are to use BufferedGraphics, or to do our own double buffering.

I've been digging around the assemblies today using Lutz Roeder's .Net Reflector (fantastic program for this sort of stuff) to see what .Net is doing internally for its drawing methods. What I've discovered is that it does its work using DllImports to the GDI+ libraries. While it would be conceivably possible to write a new Graphics implementation using calls to these libraries (or possibly plain old GDI libraries), it wouldn't work on other platforms (aka Mono on Linux).

BufferedGraphics, BufferedGraphicsContext, and BufferedGraphicsManager, however, are a different story. I haven't used these, as I figured they would have no advantage over my manual double buffering. However, after a bit of snooping around in these classes to figure out what they're doing internally, I found that they are more memory efficient (no bitmap backbuffer like in the manual approach), and that they are probably much faster. While manual double buffering forces you to copy your data using DrawImage(), BufferedGraphics uses our old friend BitBlt straight from GDI. This should bring the speed on par with GDI, while still allowing applications to be truly cross-platform (assuming your target platform has implemented those three classes, that is).

I have yet to test how much faster BufferedGraphics is, but I plan to write up another benchmark within the next few days.
After reading what you said, I looked at BufferedGraphics in the reflector. Like you said, if uses faster methods to render to screen, but it still (ugh) uses a graphics object (and by extension DrawImage) as the only way to write to it. Maybe I've missed something, but that seems like it wouldn't be any faster, though, as you said, maybe more memory efficient.
A bit offtopic:

I've been following this thread and I am curious on the advantages of GDI+ have over a hardware accelerated approach through OpenGL. Personally I use GDI+ for fonts and OpenGL for everything else (in C#), but I can't imagine any reason to do all rendering through GDI+!

On topic:

Have you tried looking at the source of the Mono project? It might give some more clues on the available fast paths.

[OpenTK: C# OpenGL 4.4, OpenGL ES 3.0 and OpenAL 1.1. Now with Linux/KMS support!]

hardware acceleration is simply not everywhere as performant, and not something one can rely on if not targeting gamers. so for casuals (notebook users, ordinary "walk-in-a-shop-and-buy-that-nice-shiny-pc" users) or even business work, using gdi+ is more save, depending on situation.


anyways, played around a bit with the code, unsure about all the numbers, but i've implemented a BitmapHelper.Copy function wich outperforms all the others shown here.. I _guess_ the size of the perfect buffer is cache-dependent (for me, the size of 8 * 1024 bytes hits the optimum.. dualcore 1.2ghz ulv). after leaving caches, the performance of all (except first) are constant and about equal.

as I've rewritten some parts of the test (made it slower, to stabilize more).. here the full code, ready to copypaste:


using System;using System.Collections.Generic;using System.Text;using System.Drawing;using System.Drawing.Imaging;namespace RenderingBenchmark{    class Program    {        static void Main(string[] args)        {            int origSize = 32;            int size = origSize;            int runs = 200;            int reps = 16;            bool exponential = false;            Console.WriteLine("Benchmarking blitting times");            Console.WriteLine("Using " + runs.ToString() + " repetitions per size");            Console.WriteLine("Rendering using DrawImageUnscaled()\n");            DateTime start;            DateTime finish;            TimeSpan elapsed;            for (int i = 0; i < reps; i++)            {                if (exponential)                    size = size * 2;                else                    size += origSize;                Console.Write("Size: " + (size * size * 4 / 1024).ToString() + "KB ");                using (Bitmap orig = new Bitmap(size, size))                using (Bitmap from = new Bitmap(size, size))                {                    start = DateTime.Now;                    for (int j = 0; j < runs; j++)                    {                        using (Graphics g = Graphics.FromImage(orig))                        {                            g.DrawImageUnscaled(from, new Point(0, 0));                        }                    }                    finish = DateTime.Now;                }                elapsed = finish - start;                double ppms = (size * size) / elapsed.TotalMilliseconds * (double)runs;                Console.WriteLine(elapsed.TotalMilliseconds.ToString() + " ms = " + (ppms / 1000).ToString() + " Mpixels/s");            }            Console.WriteLine("\nRendering using blitting\n");            size = origSize;            for (int i = 0; i < reps; i++)            {                if (exponential)                    size = size * 2;                else                    size += origSize;                Console.Write("Size: " + (size * size * 4 / 1024).ToString() + "KB ");                using (Bitmap orig = new Bitmap(size, size, PixelFormat.Format32bppPArgb))                using (Bitmap from = new Bitmap(size, size, PixelFormat.Format32bppPArgb))                {                    start = DateTime.Now;                    for (int j = 0; j < runs; j++)                    {                        Rectangle lockRect = new Rectangle(0, 0, size, size);                        BitmapData origData = orig.LockBits(lockRect, ImageLockMode.WriteOnly, PixelFormat.Format32bppPArgb);                        BitmapData fromData = from.LockBits(lockRect, ImageLockMode.ReadOnly, PixelFormat.Format32bppPArgb);                        //see if this works - technique one                        //origData.Scan0 = fromData.Scan0;                        //manually copy data over                        byte[] data = new byte[origData.Stride * origData.Height]; //copy the whole thing at a time                        System.Runtime.InteropServices.Marshal.Copy(fromData.Scan0, data, 0, data.Length);                        System.Runtime.InteropServices.Marshal.Copy(data, 0, origData.Scan0, data.Length);                        orig.UnlockBits(origData);                        from.UnlockBits(fromData);                    }                    finish = DateTime.Now;                }                elapsed = finish - start;                double ppms = (size * size) / elapsed.TotalMilliseconds * (double)runs;                Console.WriteLine(elapsed.TotalMilliseconds.ToString() + " ms = " + (ppms / 1000).ToString() + " Mpixels/s");            }            Console.WriteLine("\nRendering using blitting - one line at a time\n");            size = origSize;            for (int i = 0; i < reps; i++)            {                if (exponential)                    size = size * 2;                else                    size += origSize;                Console.Write("Size: " + (size * size * 4 / 1024).ToString() + "KB ");                using (Bitmap orig = new Bitmap(size, size, PixelFormat.Format32bppPArgb))                using (Bitmap from = new Bitmap(size, size, PixelFormat.Format32bppPArgb))                {                    start = DateTime.Now;                    for (int j = 0; j < runs; j++)                    {                        Rectangle lockRect = new Rectangle(0, 0, size, size);                        BitmapData origData = orig.LockBits(lockRect, ImageLockMode.WriteOnly, PixelFormat.Format32bppPArgb);                        BitmapData fromData = from.LockBits(lockRect, ImageLockMode.ReadOnly, PixelFormat.Format32bppPArgb);                        //see if this works - technique one                        //origData.Scan0 = fromData.Scan0;                        //manually copy data over                        byte[] data = new byte[origData.Stride];                        for (int row = 0; row < origData.Height; row++)                        {                            IntPtr fromPtr = (IntPtr)(fromData.Scan0.ToInt64() + row * origData.Stride);                            IntPtr toPtr = (IntPtr)(origData.Scan0.ToInt64() + row * origData.Stride);                            System.Runtime.InteropServices.Marshal.Copy(fromPtr, data, 0, data.Length);                            System.Runtime.InteropServices.Marshal.Copy(data, 0, toPtr, data.Length);                        }                        orig.UnlockBits(origData);                        from.UnlockBits(fromData);                    }                    finish = DateTime.Now;                }                elapsed = finish - start;                double ppms = (size * size) / elapsed.TotalMilliseconds * (double)runs;                Console.WriteLine(elapsed.TotalMilliseconds.ToString() + " ms = " + (ppms / 1000).ToString() + " Mpixels/s");            }            Console.WriteLine("\nRendering using BitmapHelper.Copy\n");            size = origSize;            for (int i = 0; i < reps; i++)            {                if (exponential)                    size = size * 2;                else                    size += origSize;                Console.Write("Size: " + (size * size * 4 / 1024).ToString() + "KB ");                using (Bitmap orig = new Bitmap(size, size, PixelFormat.Format32bppPArgb))                using (Bitmap from = new Bitmap(size, size, PixelFormat.Format32bppPArgb))                {                    start = DateTime.Now;                    for (int j = 0; j < runs; j++)                    {                        BitmapHelper.Copy(from, orig);                    }                    finish = DateTime.Now;                }                elapsed = finish - start;                double ppms = (size * size) / elapsed.TotalMilliseconds * (double)runs;                Console.WriteLine(elapsed.TotalMilliseconds.ToString() + " ms = " + (ppms / 1000).ToString() + " Mpixels/s");            }            Console.WriteLine("\nPress any key to continue...");            Console.ReadKey(true);        }    }    static class BitmapHelper    {        public static void Copy(Bitmap from, Bitmap to)        {            if(from.Size != to.Size) throw new FormatException("Pictures are not Equal in Size");            if (from.PixelFormat != PixelFormat.Format32bppPArgb) throw new FormatException("Source Picture has wrong PixelFormat");            if (to.PixelFormat != PixelFormat.Format32bppPArgb) throw new FormatException("Target Picture has wrong PixelFormat");            Rectangle lockRect = new Rectangle(0, 0, to.Width, to.Height);            BitmapData toData = to.LockBits(lockRect, ImageLockMode.WriteOnly, PixelFormat.Format32bppPArgb);            BitmapData fromData = from.LockBits(lockRect, ImageLockMode.ReadOnly, PixelFormat.Format32bppPArgb);            byte[] data = new byte[8 * 1024];            int i = 0;            for (; i < toData.Stride * toData.Height / data.Length; i++)            {                IntPtr fromPtr = (IntPtr)(fromData.Scan0.ToInt32() + i * data.Length);                IntPtr toPtr = (IntPtr)(toData.Scan0.ToInt32() + i * data.Length);                System.Runtime.InteropServices.Marshal.Copy(fromPtr, data, 0, data.Length);                System.Runtime.InteropServices.Marshal.Copy(data, 0, toPtr, data.Length);            }            if ((toData.Stride * toData.Height) % data.Length != 0)            {                int Rest = (toData.Stride * toData.Height) % data.Length;                IntPtr fromPtr = (IntPtr)(fromData.Scan0.ToInt32() + i * data.Length);                IntPtr toPtr = (IntPtr)(toData.Scan0.ToInt32() + i * data.Length);                System.Runtime.InteropServices.Marshal.Copy(fromPtr, data, 0, Rest);                System.Runtime.InteropServices.Marshal.Copy(data, 0, toPtr, Rest);            }            to.UnlockBits(toData);            from.UnlockBits(fromData);        }    }}
If that's not the help you're after then you're going to have to explain the problem better than what you have. - joanusdmentia

My Page davepermen.net | My Music on Bandcamp and on Soundcloud

This topic is closed to new replies.

Advertisement