Current version

v1.10.4 (stable)

Navigation

Main page
Archived news
Downloads
Documentation
   Capture
   Compiling
   Processing
   Crashes
Features
Filters
Plugin SDK
Knowledge base
Contact info
 
Other projects
   Altirra

Archives

Blog Archive

If you receive a pitch/stride, you can't memcpy() across the image

It's fairly common for images to be represented as a structure that includes width, height, and a field called pitch or stride. The pitch of an image is the distance in bytes between the start of one scanline to the next. It allows an image to be specified such that the scanlines aren't adjacent in memory, which is useful for achieving a given alignment. Software tends to run slightly faster if 8 or 16 byte alignment can be achieved; hardware sometimes has more draconian alignment requirements, like 128 bytes.

A fairly common mistake I see when handling an image with pitch is code like this:

memcpy(image.Data, srcImage, image.Pitch * image.Height);

This is a great way to cause image corruption or even a crash.

You can't just write across the entire memory block, because the area of memory outside of the valid scanlines is usually both unspecified and reserved. DirectDraw, for instance, interleaves surfaces and thus writing in this manner may trash other surfaces in video memory. This also generally assumes a specific pitch, which defeats the purpose of having it specifiable in the first place. Some people have gotten used to this because Direct3D always uses a specific alignment for system memory and managed surfaces that tends to give adjacent scanlines; switch to video memory resources and suddenly the code gives corrupted images.

Oh, and by the way, pitches can be negative in some frameworks, in which case the code above will nicely crash. I got burned by this issue when I was upgrading the filter pipeline to support YCbCr formats in 1.8.0, during which I discovered that a not so insignificant number of third-party filters crashed hard when given bitmaps with negative pitches. That's why in 1.8.0, if you manage to get an RGB image in the filter chain that has a top-down orientation, the pipeline will force a blit to flip it and you'll see a [C] conversion marker next to the filter entry. This is skipped if the filter advertises support for image format negotation, so if you're planning on upgrading a video filter to support YCbCr, you're gonna have to fix your code or it'll blow up.

When reading from a strided surfaces you can more often get away with such sloppy code, but there is still a gotcha: technically, the description of such a surface doesn't guarantee that there is readable padding beyond the end of the last scanline, in which case reading pitch bytes can fault. In the end, it's not worth the trouble -- just write a MemcpyRect() routine once that copies scanlines individually, and use it everywhere. You can even optimize it for the case where the scanlines do turn out to be contiguous and in ascending order.

There is one case where it's generally OK to step outside of the bounds of a scanline, and it's when you're trying to optimize away unaligned access. To be more specific, you can assume (at least on x86) that an unaligned word can safely be accessed by reading the two aligned words that hold it. You may drive analysis tools like Purify nuts, but you can gain a lot of performance in the unaligned case this way. The resampler in VirtualDub 1.8.0 optimizes for the planar 8-bit case by taking advantage of this: it precomputes filter kernels that have been appropriately padded and shifted by the misalignment amount. This results in a small amount of extra computation in some cases, but completely avoids misalignment penalties.

Comments

This blog was originally open for comments when this entry was first posted, but was later closed and then removed due to spam and after a migration away from the original blog software. Unfortunately, it would have been a lot of work to reformat the comments to republish them. The author thanks everyone who posted comments and added to the discussion.