Tales from the Evil Empire

Bertrand Le Roy's blog

News


Bertrand Le Roy

BoudinFatal's Gamercard

Tales from the Evil Empire - Blogged

Blogs I read

My other stuff

Archives

October 2011 - Posts

From ScrewTurn Wiki to Markdown

imageI'm in the process of moving the Orchard documentation site from ScrewTurn Wiki to a Mercurial + Markdown system, where revisions are managed through source control tools instead of a fully online wiki. We see quite a few advantages in doing that, but that's a story for another post.

Today, I just want to post on that quick and dirty tool that I've built to translate the ScrewTurn Wiki markup into Markdown. It's not a masterpiece in any way, it's not exhaustive, but I thought it may be useful to others, so here it is…

https://bitbucket.org/bleroy/screwturn2markdown/src

State of .NET Image Resizing: how does imageresizer do?

ThumbI've written several times before about image resizing in .NET and how the various built-in solutions (GDI, WPF and WIC) compare in terms of quality, speed and size. I'll put the links to my previous articles at the end of this post for reference.

Several readers have since pointed me to the imageresizer library, which is pure .NET and thus has no problems running in medium trust. Medium trust is an issue that has plagued existing options, preventing many people from using the best available approach. I was doubtful though that a purely managed library could come anywhere near the native Windows libraries in terms of performance. The best way to find out, of course, is to run a benchmark. Fortunately, I still had the code for my previous benchmarks so I just had to add imageresizer code to it.

I excluded IO from this test by measuring the processing time for each library working on binary streams. This also levels the playing field as some of them start processing while they are reading a file, making it harder to separate file reading overhead from image processing. By working with streams, the comparison is more meaningful. The timing code is wrapped around only the meaningful code.

The first problem that I hit with imageresizer is that it has a strong dependency on System.Web. This is really unfortunate and very probably unjustified. The library should be split between the pure image processing bits, which should have no System.Web dependency, and the request handling and caching bits, which can depend on System.Web all they want.

Since I'm mostly interested in web applications, I changed my command-line code into an ASP.NET Pages application, which probably saved me some configuration headache. The code is linked to at the end of this post. One thing I have to say is that the API feels good, natural and reasonably concise, a nice change when compared with the noisy Interop-like code we had to write before. It's nice to have something that is native .NET rather than a thin layer on top of Windows APIs that stink of C.

var settings = new ResizeSettings {
    MaxWidth = thumbnailSize,
    MaxHeight = thumbnailSize,
    Format = "jpg"
};
settings.Add("quality", quality.ToString());
ImageBuilder.Current.Build(inStream, outStream, settings);
resized = outStream.ToArray();

I really liked that I don't have to do size calculations myself, and I can just give it a square or rectangle inside of which it has to fit. You have many options to customize that. It's unfortunate that the quality setting has to be set this way instead of being promoted to a 1st class property like other settings but I can live with it. Notice how the library can work with streams, which is very important for web apps. One thing I really didn't like though was that some APIs, such as Build, take untyped object parameters and then decide internally at runtime what to do with them. Why the author didn't use strongly-typed overloads here is a mystery to me.

So how does imageresizer do then? Well, mostly as expected. In terms of size, I found it to be a little greedier than its peers, but really nothing dramatic:Size of resized images against jpg quality

The quality of the images is where imageresizer shines. See for yourself (quality 85% for those images):

imageresizer

WIC

Copenhagen_85 Copenhagen_85
IMG_2300_85 IMG_2300_85
IMG_2305_85 IMG_2305_85
IMG_2317_85 IMG_2317_85
IMG_2325_85 IMG_2325_85
IMG_2351_85 IMG_2351_85
IMG_2443_85 IMG_2443_85
IMG_2445_85 IMG_2445_85
IMG_2446_85 IMG_2446_85
IMG_2525_85 IMG_2525_85
IMG_2565_85 IMG_2565_85
IMG_2734_85 IMG_2734_85

The images look sharper (in particular the hummingbird one), and a little bit more saturated. Because of that, some moiré effects seem more visible (the shot before last makes that the most obvious. Still, the resizing is overall the best I've seen so far.

Now of course, while managed code is usually quite fast, image processing is heavy in the sort of operation where native code has a definitive advantage. That is reflected in the results I got in terms of processing speed:Time to resize against quality

Imageresizer is clearly the slowest. That was expected, but now we know by how much. It is 31% slower than GDI (I excluded the 50 quality point here), and almost 4 times slower than WIC.

Of course, unless your application is doing lots and lots of resizing operations, speed may not be that important, in particular if you are doing appropriate disk caching. Imageresizer shines in that department by making disk caching super-easy. The library, after all, is very web centric and tries hard to do everything right for the web scenarios. We should not underestimate how hard it is to get web image resizing right.

That leaves us with the unfortunate conclusion that we still don't have an image resizing silver bullet. I'll try to summarize all that we know in a table that will hopefully help you make a decision in the context of your specific application:

  Imageresizer WIC WPF GDI
Speed * **** **** **
Quality ***** **** **** ***
Medium trust Yes No No Yes
Supported By author By MS No No
.NET friendly **** * **** ***
Web centric Yes No No No

Benchmark code:
http://weblogs.asp.net/blogs/bleroy/ImageResizeBenchmark.zip

Results:
http://weblogs.asp.net/blogs/bleroy/ImageResizerBenchResults.zip

Previous articles:

The imageresizer library:
http://imageresizing.net/

So what's new in Orchard 1.3?

A reptileOrchard 1.3 was released yesterday night with some really neat features that I will outline in this post. I will come back in depth on some of those with full-length posts. Let's start with the simple but super-useful ones…

1. Draft Preview

Once you've created and saved a content item, there will now be a "Preview" link next to its summary:

The new preview link

Previously, it was impossible to view unpublished contents. This was one of the most requested missing features, and there it is.

You can now delete content types and parts2. Delete Content Types and Parts

Speaking about features high on the wish list, you can now delete the custom content types and parts that you built.

3. Markdown

The Body part can now use MarkdownNot everyone likes editing contents in Wysywyg editors, for various reasons. There has always been the possibility in Orchard of switching the flavor of the Body part, but the only available alternative to the default "html" so far has been "text", which was only giving you a plain text box. Now there is a new option, "markdown".

Markdown is a neat text syntax that has the advantage of being simple, rich and human-readable.The Markdown editor

I especially like the code sample convention of starting a line with four spaces. Very handy for writing documentation. Oh, about that, did I mention that we are planning on switching the Orchard documentation from the current wiki to Markdown?

4. Title Part

Until now, the title of Orchard content items has been kept in the Route part. That was an inconvenience when you wanted to give a title to a content type that did not have a vocation to have its own URL, for example widgets.

Title is still there in the Route part, but what we did is introduce a new interface, ITitleAspect, that the Route part implements.

public interface ITitleAspect : IContent {
    string Title { get; }
}

In all the places where we were using the old title, we are now using the interface instead. If you need to add a title, but not a URL to your own content types, you can either make one of your parts implements ITitleAspect, or you can add the new Title part, that already implements the interface in a ready to use form.

5. Rules

Rules are a truly wonderful feature that enables you to add event-driven behavior to your site from the admin UI. Let's create for example a new "Comment Notification" rule and add a "content created" event that we'll configure to be triggered on the Comment content type:image

Once this is done, we can add an action to be triggered when that event fires. We'll choose "Send e-mail", which will raise the following form, where we can specify who will receive the e-mail, and what will be the subject and body:Sending e-mail on comment submission

The subject and body of the e-mail can use tokens, which are another new feature that I'll get to in a minute.

Here is how the content creation event was wired in a handler:

OnCreated<ContentPart>(
  (context, part) =>
    rulesManager.TriggerEvent("Content", "Created",
    () => new Dictionary<string, object> { { "Content", context.ContentItem } }));

When a piece of content is created, an event is triggered on the rules manager, giving it the content item being created as the context. The event itself is implemented in a class that implements IEventProvider. I'll write more on creating your own events in a future post.

Actions are classes that implement IActionProvider. Both event and action providers can expose their own UI forms for their configuration, which brings us to the next feature…

6. Forms

So far, to expose admin UI, a module writer had to create controllers or drivers, view models and views. Starting with this release, they can use a much easier forms API. This is what rule events and actions are using to expose their UI. The forms API makes it possible to describe a form in a nice, compact and extensible form that is using, you guessed it, dynamic shapes.

Shape.Form(
    Id: "ActionEmail",
    _Type: Shape.SelectList(
        Id: "Recipient", Name: "Recipient",
        Title: T("Send to"),
        Description: T("Select who should be the recipient of this e-mail."))
        .Add(new SelectListItem { Value = "owner", Text = T("Owner").Text })
        .Add(new SelectListItem { Value = "author", Text = T("Author").Text })
        .Add(new SelectListItem { Value = "admin", Text = T("Site Admin").Text }),
    _Subject: Shape.Textbox(
        Id: "Subject", Name: "Subject",
        Title: T("Subject"),
        Description: T("The subject of the e-mail."),
        Classes: new[] { "large", "text", "tokenized" }),
    _Message: Shape.Textarea(
        Id: "Body", Name: "Body",
        Title: T("Body"),
        Description: T("The body of the e-mail."),
        Classes: new[] { "tokenized" }
        )
    );

What you see here is the form that is in the e-mail action picture above. Of course, each of the shapes in there will be rendered by a template that can be overridden (as usual). The advantage of such a description is that it can be enriched dynamically. A comments form for example could have a captcha inserted dynamically by another module, just by injecting a new shape in the form. I encourage you to dive into existing implementations to understand how you can use this new API, until we get the chance to write some good documentation.

7. Tokens

Tokens are system-wide variables whose values can vary depending on the context. They can be used in a variety of places, one of which is rules, as you can see from the example above (the subject of the e-mail that is sent when new comments are added uses a token to describe the name of the commenter). I've blogged about tokens before, so I encourage you to read these posts to know more:

http://weblogs.asp.net/bleroy/archive/2011/07/22/future-orchard-part-1-introducing-tokens.aspx
http://weblogs.asp.net/bleroy/archive/2011/07/27/future-orchard-part-2-more-tokens.aspx

8. New Content Manager Methods

Building contents by composing small parts is an extremely flexible approach, but it makes it more difficult to build efficient database queries. There is always caching, but we also needed to optimize things a bit. We did that by adding some new methods to the content manager, such as GetMany, which is taking multiple content ids in one query. We also added the Aggregate attribute that can be used to specify records that should be eagerly loaded. For example, the Tags property of the TagsPartRecord and the TagRecord property of ContentTagRecord are marked with the attribute, because when the tags are included with a content item, you don't want the act of actually getting the tags associated with the content item to be lazily executed (that is a SELECT N+1 condition). If you want the tag part, there is a very high chance that you'll want to actually access the tags themselves: the part in itself has nothing else of interest. With the attribute, the tags will be fetched at the same time and from the same query as the item itself.

9. The rest

There is a lot more in this release, such as RSS for lists (it's just there, not much to say about it), some localization improvements, the task lease API that enables better synchronization across web farms, and many, many bug fixes and performance improvements (in particular, if you use multi-tenancy, you should feel the difference). If you want to see everything that was fixed, take a look at the list on CodePlex:

http://orchard.codeplex.com/workitem/list/advanced?keyword=&status=Closed&type=All&priority=All&release=Orchard%201.3&assignedTo=All&component=All&sortField=Votes&sortDirection=Descending&page=0

What's next?

The current plan for the near future is to quickly release a 1.4 version with the focus on the new Projector feature that will enable you to query your contents in any way you want directly from the admin UI. This, I can assure you, is going to be a killer feature.

More Posts