You are currently browsing the category archive for the ‘Tips’ category.

I’ve come up with a trick that can be used in some very specific scenarios in order to avoid extra array copying when calling into native code from managed code (e.g. C#). This won’t usually work for regular P/Invokes into all your favorite Win32 APIs, but I’m hopeful it’ll be useful for someone somewhere. It’s not even evil! No hacks required.

Many native methods require the caller to allocate the array and specify its length, and then the callee fills it in or returns an error code indicating that the buffer is too small. The technique described in this post is not necessary for those, as they can already be used optimally without any copying.

Instead, let’s talk about the general problem if you’re calling a native method which does the array allocation and then returns it. You can’t use it as a “managed array” unless you copy it into a brand new managed array (don’t forget to free the native array). In other words, native { T* pArray; size_t length; } cannot be used as a simple managed T[] as-is (or even with modification!). The managed runtime didn’t allocate it, won’t recognize it, and there’s nothing you can do about it. Very few managed methods will accept a pointer and a length, and will require a managed array. This is particularly irksome when you want to use System.IO.Stream.Read() or Write() with bytes from a native-side buffer.

Paint.NET uses a library written in classic C called General Polygon Clipper (GPC), from The University of Manchester, to perform polygon clipping. This is used for, among other things, when you draw a selection with a mode such as add (union), subtract (exclude), intersect, and invert (“xor”). I blogged about this 4 years ago when version 3.35 was about to be released: using GPC made these operations immensely faster, and I saved a lot of time and headache by purchasing a commercial use license for the library and then integrating it into the Paint.NET code base. tl;dr: The algorithms for doing this are nontrivial and rife with special corner cases, and I’d been struggling to find enough sequential time to implement and debug it on my own.

Anyway, the data going into and coming out of GPC is an array of polygons. Each polygon is an array of points, each of which is just a struct containing X and Y as double-precision floating point values. To put it simply, it’s just a System.Windows.Point[][] (I actually use my own geometry primitives nowadays, but that’s another story, and it’s the same exact thing).

Getting this data into GPC from the managed side is easy. You pin every array, and then hand off the pinned pointers to GPC. Since you can’t use the “fixed” expression with a dynamic number of elements, I use GCHandle directly and stuff them all into GCHandle[] arrays for the duration of the native call. This is great because on the managed side I can work with regular ol’ managed arrays, and then send them off to GPC as “native arrays” by pinning them and using the pointers obtained from GCHandle.AddrOfPinnedObject().

Now, here’s the heart breaking part. GPC allocates the output polygon using good ol’ malloc*. So when I get the result back on the managed side, I must copy every single last one so that I can use it as a Point[] (a managed array). This ends up burning a lot of CPU time, and can cause virtual address space claustrophobia on 32-bit/x86 systems when working with complex selections (e.g. Magic Wand), as you must have enough memory for 2 copies of the result while you’re doing the copying. (Or you could free each native array after you copy it into a managed array, but that’s an optimization for another day, and isn’t as straightforward as you’d think because freeing the native memory requires another P/Invoke, and those add up, and so it might not actually be an optimization.)

But wait, there’s another way! Since the code for GPC is part of my build, I can modify it. So I added an extra parameter called gpc_vertex_calloc:

    typedef (gpc_vertex *)(__stdcall * gpc_vertex_calloc_fn)(int count);

    GPC_DLL_EXPORT(
    void, gpc_polygon_clip)(
        gpc_op                set_operation,
        gpc_polygon          *subject_polygon,
        gpc_polygon          *clip_polygon,
        // result_polygon holds the arrays of gpc_vertex, aka System.Windows.Point)
        gpc_polygon          *result_polygon, 
        gpc_vertex_calloc_fn  gpc_vertex_calloc);

("gpc_vertex” is GPC’s struct that has the same layout as System.Windows.Point: X and Y, defined as a double.)

In short, I’ve changed GPC so that is uses an external allocator by passing in a function pointer it should use instead of malloc. And now if I want I can have it use malloc, HeapAlloc, VirtualAlloc, or even the secret sauce detailed below.

On the managed side, the interop delegate definition for gpc_vertex_calloc_fn gets defined as such:

    [UnmanagedFunctionPointer(CallingConvention.StdCall)]
    public delegate IntPtr gpc_vertex_calloc_fn(int count);

And gpc_polygon_clip’s interop defintion is like so:

    [DllImport(“PaintDotNet.SystemLayer.Native.x86.dll”, CallingConvention = CallingConvention.StdCall)]
    public static extern void gpc_polygon_clip(
        [In] NativeConstants.gpc_op set_operation,
        [In] ref NativeStructs.gpc_polygon subject_polygon,
        [In] ref NativeStructs.gpc_polygon clip_polygon,
        [In, Out] ref NativeStructs.gpc_polygon result_polygon,
        [In] [MarshalAs(UnmanagedType.FunctionPtr)] NativeDelegates.gpc_vertex_calloc_fn gpc_vertex_calloc);

So, we’re half way there, and now we need to implement the allocator on the managed side.

    internal unsafe sealed class PinnedManagedArrayAllocator<T>
        : Disposable
          where T : struct
    {
        private Dictionary<IntPtr, T[]> pbArrayToArray;
        private Dictionary<IntPtr, GCHandle> pbArrayToGCHandle;

        public PinnedManagedArrayAllocator()
        {
            this.pbArrayToArray = new Dictionary<IntPtr, T[]>();
            this.pbArrayToGCHandle = new Dictionary<IntPtr, GCHandle>();
        }
 
        // (Finalizer is already implemented by the base class (Disposable))

        protected override void Dispose(bool disposing)
        {
            if (this.pbArrayToGCHandle != null)
            {
                foreach (GCHandle gcHandle in this.pbArrayToGCHandle.Values)
                {
                    gcHandle.Free();
                }

                this.pbArrayToGCHandle = null;
            }

            this.pbArrayToArray = null;

            base.Dispose(disposing);
        }

        // Pass a delegate to this method for “gpc_vertex_calloc_fn”. Don’t forget to use GC.KeepAlive() on the delegate!
        public IntPtr AllocateArray(int count)
        {
            T[] array = new T[count];
            GCHandle gcHandle = GCHandle.Alloc(array, GCHandleType.Pinned);
            IntPtr pbArray = gcHandle.AddrOfPinnedObject();
            this.pbArrayToArray.Add(pbArray, array);
            this.pbArrayToGCHandle.Add(pbArray, gcHandle);
            return pbArray;
        }

        // This is what you would use instead of, e.g. Marshal.Copy()
        public T[] GetManagedArray(IntPtr pbArray)
        {
            return this.pbArrayToArray[pbArray];
        }
    }

(“Disposable” is a base class which implements, you guessed it, IDisposable, while also ensuring that Dispose(bool) never gets called more than once. This is important for other places where thread safety is very important. This class is specifically not thread safe, but it should be reasonably easy to make it so.)

And that’s it! Well, almost. I’m omitting the guts of the interop code but nothing that should inhibit comprehension of this part of it. Also, the above code is not hardened for error cases, and should not be used as-is for anything running on a server or in a shared process. Oh, and I just noticed that my Dispose() method has an incorrect implementation, whereby it shouldn’t be using this.pbArrayToGCHandle, specifically it shouldn’t be foreach-ing on it, and should instead wrap that in its own IDisposable-implementing class … exercise for the reader? Or I can post a fix later if someone wants it.

After I’ve called gpc_polygon_clip, instead of copying all the arrays using something like System.Runtime.InteropServices.Marshal.Copy(), I just use GetManagedArray() and pass in the pointer that GPC retrieved from its gpc_vertex_calloc_fn, aka AllocateArray(). When I’m done, I dispose the PinnedManagedArrayAllocator and it unpins all the managed arrays. And this is much faster than making copies of everything.

Now, this isn’t the exact code I’m using. I’ve un-generalized it in the real code so I can allocate all of the arrays at once instead of incurring potentially hundreds of managed <—> native transitions for each allocation. The above implementation also doesn’t have a “FreeArray” method; I had one, but I ended up not needing it, so I removed it.

So the next time you find yourself calling into native code which either 1) allows you to specify an external allocator, or 2) is part of your build, and that 3) involves lots of data and thus lots of copying and wasted CPU time, you might just consider using the tactic above. Your users will thank you.

Lastly, I apologize for my blog’s poor code formatting.

Legal: I hereby place the code in this blog post into the public domain for anyone to do whatever they want with. Attribution is not required, but I certainly appreciate if you send me an e-mail or post a comment and let me know it was useful for you.

* Actually in Paint.NET 3.5, I changed it to use HeapAlloc(). This way I can get an exception raised when it runs out of memory, instead of corrupt results. This does happen on 32-bit/x86, especially when using the Magic Wand on large images.

If the software you work on ever ends up with more than a few dialogs or a non-trivial navigation flow, then you should create a document that I call the “UI Map”. This document is quite simple to create, and the work you put in to it will pay off many, many times over. A UI Map is basically just a Word file (or pick your favorite editor, or wiki, etc.) that documents every single dialog and major state that your application can be in.

For the version 3.0 release of Paint.NET I finally created one of these at the behest of the translation team I’ve been working with, and I’m glad I did! The primary goal of this for them is to document every localizable piece of text in Paint.NET. For the new versions that I put out, it’s important to keep this document up-to-date and to spell out what is new or changed, as well as what text can be localized versus what text is supplied by the operating system (or framework, or user, etc.). Sometimes there are differences between how the UI appears in XP versus Vista (or versus 2000, 95, 98, Lunix, take your pick), and if those affect localization (or whatever your audience is interested in) then it should be noted in this document as well.

It’s very convenient for translators to be able to test that their translation does not cause layout or other visual errors, such as clipped text or worse. (And to point the finger in the right direction, it is not actually the translation that causes the error. It merely helps to expose the bug that was already there! :)) This will give them a visual tool to work with, and also makes it really easy for them to connect the text they’ve translated with where it shows up in the UI! That last part is very important, because usually a translator just sees this:

<data
name=BlockedPluginException.PluginIsNowBuiltIn>

<value>This plugin is now built-in to Paint.NET, so the old plugin DLL has been blocked from loading. You may safely delete this plugin DLL.</value>

</data>

If I hadn’t written the code myself, then I’d have no idea where to find this text in the UI and clueless when it came to validate that it showed up properly (no clipped text, scrunched layout, etc.) It won’t kill you to spend a few minutes to tell them exactly how to reach that text in the UI.

Maintaining this document with every new version also forces me to touch and re-touch every part of the UI, which is important to increase confidence in a release. I’m not always the most diligent when it comes to testing things (shh, don’t tell anyone!) and so this is a very good exercise to require myself to do. In the new Paint.NET v3.20 release I have completely reorganized the Effects menu and revamped most of the effects’ user interfaces using the new IndirectUI system. I even found a bug while I was updating the UI map!*

Notice the scrollbar and the clipped text? Yeah, oops is right. No doubt there will be more bugs like this once I do a test pass with the other languages.

Having a UI map also makes sure that you don’t forget about any part of your UI. Remember that out-of-the-way error dialog you added three days before you shipped? Yeah, that one! On a fully professional project with a large team you might think this is taken care of by the design and documentation process, and you’d be completely wrong. The UI map can also help new developers on your project to get a quick visual tour of the application. Or new managers. Or higher-up managers or marketing folks. A document like this can serve as a valuable quick-summary for all of them, or even as a reference book. There is just so much value in having a UI map.

To get started, I use the outlining feature in Word. This way I can use the indentation level as an implicit navigation guide within the application. Here’s an abbreviated excerpt from the Paint.NET UI Map that shows how I’ve documented part of the File menu (up through “New” and “Open”), with screenshots resized in order to fit more appropriately on the blog. I start out by explaining the specific purpose of the document as well as certain constraints that are important. For example, it is implied from the 2nd paragraph that if text is cut off at 800×600, but looks fine at 1024×768, then that is not something of concern.

(Also, as another tip, I highly recommend using Kenny Kerr’s
WindowClippings program for making the screenshots look super professional. It captures the glass and drop shadow effects in Windows Vista perfectly. It’s what I use. And no, this is not a paid endorsement :))

Paint.NET v3.20 User Interface Map

This document exhaustively details the Paint.NET v3.0 user interface. It is meant primarily for translators to be able to find every location in the user interface for translation-related testing and tuning. Each part of the UI will be presented with a screenshot. The outline hierarchy implicitly shows how one can navigate to a particular part of the UI. This document is not meant to provide sufficient documentation on how to use Paint.NET and its various features. It is also not meant to be instructional on how to prepare a language pack for Paint.NET. Most error dialogs will not be detailed.

NOTE: Paint.NET’s system requirements list a screen resolution of 1024 x 768. It is important that UI elements work comfortably with a maximized window at this resolution with the Windows task bar set to one (1) unit of height. The main concern here is the help and context texts for the tools, which should not be clipped at this resolution with a maximized window. Paint.NET is not designed or optimized for smaller resolutions or window sizes (e.g. 800×600, or 1024×768 with a smaller window).

1. Application
This is the interface the user sees when they start Paint.NET. By default there are four docked floating toolform windows (aka floating windows, floating tool windows).
(screenshot removed for brevity)

1.1. through 1.4 – (removed for brevity)

1.5. Menu bar

1.5.1. File
(new for v3.20: View Plugin Load Errors…)

1.5.1.1 New
When the user creates a new image, it will be called “Untitled”. This name is localizable.

1.5.1.2 Open
This is a common Windows dialog. Most of the text on it is provided by Windows.

1.5.1.2.1. The file type drop down has some strings that are localizable:
(New for v3.10: “DirectDraw Surface (DDS)” is now a supported file type)

1.5.1.2.2 In Windows Vista, the dialog will look like this instead:

1.5.1.2.3
If the user is transferring files from a location that is not mapped to a file system path (such as from an http:// source, or from a digital camera that is not mapped to a drive letter), then they will see the following UI:

If the user cancels the transfer, they will see this while the transfer is being canceled:

If there is an error while transferring (server error, camera failure / disconnect), then this dialog will be shown. Note that the error message (“A device attached…”) is provided by the OS and/or the .NET Framework, and is not localizable. It will generally be supplied in the language that the OS and/or Framework is configured or installed for.

* To be fair, a member of the forum, “I Like Pi,” actually pointed this out first. I would have discovered this much sooner however, if I had updated my UI map two weeks ago instead of waiting until tonight!

Back in September I posted a tip stating that freeware authors should release frequent updates to their products. I claimed to be practicing this tip myself, and that it was working. A constant stream of updates creates a constant stream of press and blog coverage. Someone who sees two bits of news about Paint.NET is more likely to try it out than if they’d seen only one bit of news.* I personally don’t recommend going the uTorrent route and updating two new builds every day though – that seems a bit too much. Why should I even bother updating when a new release is just hours away? However, I’ll bet their ad revenue is higher than mine, so I’ll keep my mouth shut beyond that :)

For the last few weeks I have been aggressively pushing out pre-release updates to Paint.NET v3.20 (one update per week!), and so I have some new data. Today (or rather yesterday), November 20th, 2007, was my 4th highest traffic day ever as reported by Google AdSense. The #1 and #2 days were right after the huge v3.0 release at the end of January. The #3 day was at the end of March after v3.05 was released. And because of all the new ways I’ve optimized my AdSense, today’s earnings were significantly more than double the average of those 3 days’ earnings: more clicks, much higher CTR, and significantly higher eCPM. Just like John Chow says, there’s really only one secret formula: get traffic, optimize ads. It’s true, and everyone with a monetized website knows it.

As far as data I can actually share**, Google Analytics states that today there were 17,412 unique visitors to http://www.getpaint.net :

Including the help content and other areas I track separately, there were over 24,000 unique visitors. This is obviously chump change to anyone who runs big websites for a living (imagine how much traffic I’d get if I really, really tried). I’ve always wondered how much money one could make if they were able to sneak their AdSense code on to the front page of Google.com or Microsoft.com … even if just for one hour!

My prediction is that with the next beta release, which I should have out by end of next week, I’ll break this record. And we’ll be that much closer to another stable, quality Paint.NET release …

* Just another theory of mine. I could be wrong, but probably not.

** AdSense’s Terms of Service are pretty strict about not sharing most of your statistics other than gross earnings.

Prior to Paint.NET v2.6, I was releasing at a pace that covered several months. 2.0 to 2.1 was about 5 months, and forward to 2.5 was another 6 months. V2.6 was done in 3 months, and 3.0 took about 11 months. However, between 2.6 and 3.0 I release 2.61, 2.62, 2.63, 2.64, 2.70, and 2.72 – at a pace of roughly one every 4-6 weeks. Once 3.0 came out, I released 3.01, 3.05, 3.07, and 3.08 at a similarly fast pace, followed by v3.10 just shy of 3 months later.

There were several reasons for all the small “.01″ releases from the 2.6 codebase. One was that I needed to put out some important bug fixes to decrease support costs (“costs” meaning “my time” and “user happiness”). If you can just fix common bugs, or work around common user mistakes, then they are no longer bugs or mistakes: it just works! Less e-mails for you, more working software for the masses. It’s a win-win.

The second reason was that I wanted to see what would happen if I released more often. I knew that 3.0 was going to take awhile to develop, and I wanted to keep online attention devoted towards Paint.NET. People forget about things quickly, and I wanted to avoid seeing any comments like, “Whatever happened to Paint.NET?”

So I forked the code and continued to release off the 2.6 tree. My theory was that if I didn’t release every so often, the media and online community would slowly forget about Paint.NET. I don’t really do any marketing or advertising to fill that niche (it wouldn’t be profitable). My theory continued such that if I did release often (4-6 weeks is about right), even with just a +.01 bugfix-only release, that the media and online community would be consistently reminded about Paint.NET. Users would hear more about it, which breeds familiarity, which coaxes more and more people to download and try it out. This means more users sticking with the program, more people donating, more traffic to my website, and generally just more awesomeness all around. Oh, and quick releases means users get bug fixes and features quicker too ;)

Here’s the great thing: this theory is proving itself to be correct. Here are some facts that back it up:

  • Nearly 80% of the 3-year old forum‘s activity is from this calendar year. Part of this I believe can also be attributed to having placed a link to the forum in the application’s Help menu. (Comparing # of posts from December 31st, 2006 to today’s count.)
  • Almost 60% of the all Paint.NET downloads have occurred during this calendar year. (Comparing download counts for December 31st, 2006 versus today at BetaNews, historically the primary download mirror.)
  • Revenue via donations is about 2x what it was before 3.0 was released. (This is comparing February 2007 through August 2007, with August 2006 through December 2006).
  • Revenue via AdSense is about 10x what it used to be. (This is comparing May 2007 through September 2007, with August 2006 through December 2006, but excluding November 2006 because there was some glitch that nuked my earnings for a week or so).

After the v3.0 release things really started picking up in the donations and AdSense departments. This is of course partly due to the fact that v3.0 simply rocked compared to previous versions and reached a critical mass of features and press coverage, and I started getting more traffic.

The 3 months gap between 3.08 and 3.10 confirmed my theory that frequent releases create a positive feedback cycle for earnings and avoids media forgetfulness. Before the 3.10 release, it was quite apparent that donations and AdSense were losing their steam from the 3.07 and 3.08 releases. Check out this graph showing my relative daily AdSense earnings:

The amount of traffic coming to the site is mostly constant, but it appears like AdSense was just, I don’t know, getting bored with the site or something. Now that 3.10 is out the door, AdSense is continuing on a slow upward trend, even a full month after the release. Maybe AdSense has a bias against stale sites? I even saw a very happy spike on Sunday which set an all-time 1-day record. AdSense is definitely a strange, strange beast.

The graph for donations is very different and always shows huge spikes for about 10 days following a release. I do not have a graph prepared because of the difference in how PayPal provides its data: I have to do some crazy Excel programming to get it to work L

The base theory is that every time I release, I get a short-term spike in traffic after which it settles down to a level that is slightly higher than what the average was before.

After writing the first two articles in my “Making Money With Freeware” series, I’ve come to the conclusion that there are several cross-cutting topics that deserve separate treatment. Because, as you might expect and as an example, the first tip one should heed towards making money with freeware is to strive to have popular freeware application. Having a popular freeware project quite often also depends on it being a high-quality freeware project. And having a popular or high-quality freeware program does not by itself make you money, and I don’t want to talk about popularity or quality only while under the justification of monetization.

To that end, I think I’ll rename the series to “Successful Freeware Project Tips”, and then at the beginning of each article I’ll notarize which bucket(s) the tips go into.

In conclusion, you should be looking forward to the new Tohuvabohu album by KMFDM, set to be released later this month. They’ve got a few preview tracks on their MySpace page.

(Actually, that’s completely unrelated. I’m just a big fan of KMFDM. QED.)

With Paint.NET v3.07, there was a subtle change in the release notes:

  • Changed: The help file / documentation is now hosted online. This has reduced the download size by more than 3 MB, and will also allow us to provide translations without ballooning the size of the download (each language would have added between 2 and 4MB).

This was firstly possible because the Paint.NET help content is just a collection of HTML pages that load in your browser. So I guess my first recommendation is to do the same: write your help content with a copy of your favorite web page editor. I use FrontPage 2003. Yeah, I know, I’m lame J But heck, I also use CVS, which means I’m “stupid and ugly” according to Linus Torvalds! (I’m not defending CVS. I just happen to use it.)

This change was made for many reasons, not all of which were necessarily listed in the change log, but all of which I see as advantageous:

  • Smaller Download Size. It dropped from 4.5 MB down to 1.5 MB. That’s an enormous savings.
  • Easy to Correct. If there is a typo or error, I can fix it and just upload the new version of the page.
  • Easy to Amend. If I want to add a new topic, I can now just write it up and click “upload”.
  • Localization. This ties in to the three previous ones, and I’ll discuss it below.
  • Statistics. I can now get usage data on the help content itself.
  • Search. Since it’s just a web site, it’s now easy to add a little search box on there.
  • Advertising. Now I can place Google AdSense or other things on the pages.

There is only one negative that I could think of:

  • Offline Access. It isn’t easy to access the help content anymore if you aren’t connected to the Internet. But you can mitigate this by offering an optional download for the small number of people who care about this. And also for anyone volunteering to do localization.

Almost all of these can affect your ability to make money from your freeware project.

Smaller Download Size

I’m going to publish a completely separate article on this because what I was typing got really long and I want to keep this post focused. Stay tuned!

Easy to Correct, Easy to Amend

Sometimes I get an e-mail from someone telling me about a typo on the website or in the help content. It used to be I’d have to file a bug in my Bugzilla database to make sure it got fixed before release. Now I just fix it, upload it, and I’m done. Sometimes I also get the itch to update or freshen pages (like I did with the Features page on the main website recently). This has less to do with making money and is more about simply being agile with your product.

Localization

Before, if I had wanted to publish the help file in all of the other 7 languages that Paint.NET is released in, it would have ballooned the download size to about 22 MB (at an estimated 2.5 MB per additional language). Most of this increase comes from screenshots which have text and must be done separately for each language. Most users only care about 1 of those languages so this ends up being an incredible waste of bandwidth. And on that note, most users don’t use the help content at all and so even downloading 1 language’s worth of it is a waste for them.

I can now also publish extra languages on a schedule independent of the main Paint.NET release, which relieves a lot of potential scheduling hassle. This is also directly related to having a smaller download size which, again, I will talk about later. And if you put advertising on your help pages then a user is more likely to see an ad in their preferred languages and thus much more likely to click on it.

Statistics

I now know how many people are reading the help file. I know which pages are popular. I know how many pages are read during each visit. Having good statistics is never a bad thing. Just sign up for Google Analytics and put the code at the bottom of your pages, although make sure it is tracked separately from your main website. Right now the Paint.NET help content is getting about 1,700 visitors per day, which is something I didn’t know before!

Search

There are a few ways to add search to your help content if it’s offline. One way is to use the CHM format, but the tools and utilities I had for managing this were excruciatingly difficult and painful to use. I absolutely hated it, so for version 2.5 I switched to normal HTML. Also, for cross-platform enthusiasts/purists, CHM either restricts you to Windows or forces you to find a different solution for your other target platforms. It just adds to the amount of time you have to spend managing your help content, and reduces time available for other things like drinking beer. Bleh to that.

Another way to get search is to write your own help system complete with original UI, indexing, and searching code (and that’s in addition to writing the content itself!). It’s definitely a challenging and fun direction to go in and you’ll learn a lot, but it’s also a liability in traditional software development terms. More code means more churn means more bugs, and which means less time to focus on the core of your project. Are you in the business of developing indexing and search algorithms? I doubt it. I prefer to let the
big
guns handle that stuff, and focus on what I do and enjoy best.

It’s easy to use something like Google Custom Search to add a search box if your help content is just a collection of HTML files that are hosted online. I only recently added this to the Paint.NET help file and now I really wish I had done it sooner. It isn’t a big earner in absolute numbers, but it’s always good to have another passive income stream – and those nickels and dimes really add up. (Small tip: Don’t look at your daily revenue numbers for inspiration. Multiply them by 365 and consider how they have affected your yearly income! J)

Advertising

Since putting the help content online, I have placed Google AdSense on the pages. I’m also considering using something like Kontera for in-text advertising links. You might think that the added advertising makes the help file ugly or less attractive, and you’d be right. But you need to ask yourself what the best release model is for yourself and your users:

  • Freeware. In this choice you give the software away for free. No ifs, ands, or buts. This is how Paint.NET was for its first 2 years. You won’t make any money at all doing it this way. I sure didn’t. All hosting costs come out of your pocket.
  • Bundling / Spyware / Adware. There’s also a lot of money in bundling stuff that isn’t related to your program. Irfanview, an otherwise highly rated program, optionally installs the Google Toolbar. Lots of other free programs try to install toolbars or set your browser’s homepage. I personally feel that is a dishonest way for the “bundlers” to get their software on people’s machines. This is actually a lot I could talk about on this subject in the areas of business, morals, ethics, etc. and I am planning on dedicating a blog post to it.
  • Shareware. Publishing your software this way is good and legitimate business, and might even be the right thing for your software project (it works for Patrick’s
    Bingo Card Software). You can make a lot of money with this strategy if your download and conversion numbers are high enough. But, of course people have to pay for the software which means you’ll have fewer users. Going in this direction also means you have to manage a business. You’ll have to worry about things like customer service (“I want a refund!”), buying advertising with AdWords or AdCenter, maybe hiring employees, etc. You also lose some potential for ubuiqity, which is important for some types of software (web browsers and image editors, for sure). I currently estimate that Paint.NET has 1 million active users. I regularly have people that work down the hall from me say things like, “Oh, you wrote Paint.NET? I had no idea, cool!” Do you think it would have such ubiquity if it were paid software? Not with my budget (time or money), that’s for sure.
  • Freeware, but with advertising outside of the program (website, online help file). NOW we’re talking! I personally believe that this is the absolute best user experience possible. Users get software for free and you get money so you can afford to manage its continued development. You don’t have to manage a business. If you want, you can take a vacation from the project but still be making money on it. People are used to seeing ads on the Internet. You won’t get any hate mail — I sure haven’t. You should also include a few smartly placed “Please Donate” buttons in the software itself.

I’ll even tell you how successful AdSense has been on the help file, even though I earlier said I wouldn’t publish any comprehensive data. Since Paint.NET v3.07 was released, I’ve made over $1,000 just from the help file. It’s been almost three months since I made this move, and I trust that you can do the math on that.

Conclusion

Putting the help content for your freeware project has almost no negatives. It’s a no brainer. Your download size will decrease, your download counts will go up, and you’ll have extra money in your pocket. Almost every software project is going to be different, so please don’t take this information as canon. This is what works for me, taking into account the direction I want to take Paint.NET and the way I want to spend both my personal and professional time.

Oh, and to keep the bandwidth use in check for your online help content, I recommend using PNGOUT. I’ve seen PNG’s drop anywhere from 5% to 50% in size using this thing! You can purchase a good front-end for it, PNGOUTWin, from Ardfry Imaging (they own PNGOUT). I personally use PNGGauntlet because it’s free, but it doesn’t run jobs in parallel so it runs a lot slower on my Core 2 Quad. Alternately you can use JPEG, but then all your screenshots look like crap.

I don’t really have an outline for this series so I’m really hoping that the “#1″ up there in the topic doesn’t stand by itself for too long. Anyway, like I said earlier when I decided against disclosing revenue figures for now (either because I’m humble or because I’m a chicken, take your pick!), I will instead offer up bits of information that I believe have helped Paint.NET to become strongly profitable.

The first tip is simple, and is a play on statistics that is very easy to understand:

Have a Donate Button.

Studies have confirmed* that a website with a PayPal button on it makes as much or more as the same site that does not have the PayPal button.

But really, think about it. If you don’t have a way for your audience to send you money then they probably won’t send you any. You will make $0. However, if you give them an easy way to send you money then you will make $0 … or you will make more than $0. I know, I know, it’s a silly logic pun from a nerdy Computer Science graduate, but it’s also strongly motivational. Hidden in that little phrase is a corollary: “the worst that could happen is that you make money.”

John Chow made $219 last month just by having a silly link at the end of every blog post that says, “buy me a beer!” If he didn’t have that link there then I promise you he would have $219 less in his pocket right now. It would not be made up for from any of his other income streams.

While I was in college I wrote a little freeware app called ListXP and it had a donate link in it too. I maybe made a grand total of $200 or $300 over the course of 2 years. But guess what? Without that PayPal button I would have made a grand total of zero dollars. Yup, that’s right: $0.

I look at these things from a purely statistical standpoint, which is really how (many?) businesses operate. Just like free-trial-to-paid conversion rates, freeware with a PayPal button can have a quantifiable download-to-donation conversion rate. I will say that the conversion rate for Paint.NET is at least an order of magnitude lower than what Patrick has for his Bingo card software (his is about 2.5%) (Edit 2:11pm PST: Sorry, had the wrong URL for that link!). But guess what? My audience is many, many orders of magnitude larger. I don’t any advertising, and I don’t have to ship CD’s or worry about refunds, so my profit margin is also higher.

Patrick’s software was downloaded about 1,000 times last month, which is pretty phenomenal for niche software. By my best estimates, Paint.NET was downloaded about 400,000 times last month. And every time I push out an update, the user is reminded about their ability to donate: in the installer, for about 30 seconds while it does the little “Optimizing for your system” dance, it shows a little banner that invites them to donate. I get a big spike in donations for the 2 weeks following an update … coincidence? So here’s the second part of my tip:

Remind your audience about donating.

Just don’t be in their face about it or be annoying. Because, well, that’s just annoying. Paint.NET has Donate buttons on the website, in the Help menu, in the installer, and in the Save Configuration dialog down in the bottom left corner. These are the areas that I believe bring the least amount of annoyance balanced by the highest amount of conversion. I’m not sure if I’m right yet though because I have yet to do a thorough analysis and comparison of how many clicks they are all getting (each one goes to a different redirect page, which I can then track with standard web stats).

Anyway, it’s important to remind people about donating to your freeware because donations are something that don’t come immediately, unlike a purchase of shareware. A person probably won’t donate unless they’ve already exercised good value from what you’ve given them (as opposed to buying a candy bar where I pay for something I’m about to get value from). In the case of Paint.NET, the donation reminder during upgrade serves as a good place to politely prod the user towards thinking, “Hmm yes, I have received good value from this free software. I like it. I think I’ll go ahead and donate!”

And back to the statistics angle, with regard to conversion rates. Patrick and I both intuitively know that if our download numbers go up that our conversion rate will stay about the same. This means that all we have to do to earn more money from this is to find ways to ramp up the download count, and our revenue will increase linearly. Patrick has experimented with various advertising and fulfillment channels, and is getting a grasp on what works for his product and audience. For Paint.NET, I need to make sure that I quickly say “yes” whenever someone e-mails me asking permission to include Paint.NET on a magazine CD. I also experiment with the website content to see what brings in the most search engine traffic. Et cetera. Lather, rinse, repeat.

I’ve used this basic strategy in a few other places, and it works great. It’s also fun. One time when I was still in college I was getting coffee and I asked the barista girl if I could have it for free. She said “Sure.” So I saved $4 or something. Yesterday the guy who is 4 ranks above me in management (he’s my manager’s manager’s manager’s manager?) gave me a $4 “coffee-or-snack” cafeteria coupon because I said, “You know what you could do with that? You could give it to me.” I was half joking and mostly just in a good/confident mood, but he shrugged his shoulders and said, “Ok.” And then I thanked him, because that’s what you do when people give you things for free.

You won’t get something for free if you don’t ask for it, and the worst that can happen is that you won’t get it. When you buy a car, make sure that you don’t pay the sticker price without a fight. And when you put up a website, the absolute worst thing you can do is to not monetize it.

* Not really. I just made that up.

Follow

Get every new post delivered to your Inbox.

Join 221 other followers