Wednesday, April 20, 2016

GPUImage 2, Redesigned in Swift

Brad Larson (tweet, comments):

The rewritten Swift version of the framework, despite doing everything the Objective-C version does*, only uses 4549 lines of non-shader code vs. the 20107 lines of code before (shaders were copied straight across between the two). That’s only 22% the size. That reduction in size is due to a radical internal reorganization which makes it far easier to build and define custom filters and other processing operations. For example, take a look at the difference between the old GPUImageSoftEleganceFilter (don’t forget the interface) and the new SoftElegance operation. They do the same thing, yet one is 62 lines long and the other 20. The setup for the new one is much easier to read, as a result.

* (OK, with just a few nonfunctional parts. See the bottom of this page.)

The Swift framework has also been made easier to work with. Clear and simple platform-independent data types (Position, Size, Color, etc.) are used to interact with the framework, and you get safe arrays of values from callbacks, rather than raw pointers. Optionals are used to enable and disable overrides, and enums make values like image orientations easy to follow.

Because of open-source Swift, it now supports Linux. On the Mac and iOS side, though, it is surprising that this sort of thing is necessary when Apple provides Core Image. The original project claims:

This framework compares favorably to Core Image when handling video, taking only 2.5 ms on an iPhone 4 to upload a frame from the camera, apply a gamma filter, and display, versus 106 ms for the same operation using Core Image. CPU-based processing takes 460 ms, making GPUImage 40X faster than Core Image for this operation on this hardware, and 184X faster than CPU-bound processing. On an iPhone 4S, GPUImage is only 4X faster than Core Image for this case, and 102X faster than CPU-bound processing. However, for more complex operations like Gaussian blurs at larger radii, Core Image currently outpaces GPUImage.

7 Comments RSS · Twitter

I'm sorry, don't you take one look at the old GPUImageSoftEleganceFilter and ask what on earth they were thinking?

Sure, the new Swift code looks much nicer, but the old Objective-C code looks like it was written for entry into an obfuscated code contest…

What kind of bullshit is this?

e.g.

#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
    UIImage *image1 = [UIImage imageNamed:@"lookup_soft_elegance_1.png"];
    UIImage *image2 = [UIImage imageNamed:@"lookup_soft_elegance_2.png"];
#else
    NSImage *image1 = [NSImage imageNamed:@"lookup_soft_elegance_1.png"];
    NSImage *image2 = [NSImage imageNamed:@"lookup_soft_elegance_2.png"];
#endif

...

lookupImageSource1 = [[GPUImagePicture alloc] initWithImage:image1];
lookupImageSource2 = [[GPUImagePicture alloc] initWithImage:image2];

Look, in the Swift version, I have created a new class or a new function and the code is now 4x smaller.

self.lookup1.lookupImage = PictureInput(imageName:"lookup_soft_elegance_1.png")
self.lookup2.lookupImage = PictureInput(imageName:"lookup_soft_elegance_2.png")

What a fucking incredible language where you can define functions or macros.

And of course, the lines below are including in the Objective-C code and the count of lines whereas they are completely useless since there's nothing after them.

#pragma mark -
#pragma mark Accessors

Swift is so fucking incredible that you can remove lines that are also useless in other languages.

Please, also enjoy comparing these 2 parts:

- (id)init;
{

vs

public override init() {

Let's just omit the fact that the ';' after init is completely useless.

Only 1 line in Swift vs 2 lines in Objective-C. What a fucking incredible language! You can put the { at the end of the line. So much more compact than Objective-C. Oh wait, it's possible to do this in Objective-C, C++, C,… ?

Let's also write the assert on 2 lines when only one is necessary:

NSAssert(image1 && image2,
             @"To use GPUImageSoftEleganceFilter you need to add lookup_soft_elegance_1.png and lookup_soft_elegance_2.png from GPUImage/framework/Resources to your application bundle.");

Comparing the number of lines of code is just plain bullshit. Would the code have been rewritten in Objective-C, it would have been smaller too.

Yes, I think much of the difference in line count is due to refactoring and the “radical internal reorganization.” I don’t think he’s really claiming that the reduction is because of Swift. Even the filter chaining could have been concise in Objective-C.

The semicolon in - (id)init; is actually useful, just a little-known ObjC syntax feature — it lets you easily copy and paste between .h and .m files. ISTR you have to turn it on explicitly in recent Clang versions however.

@Nicholas:

It's useless from the parser point of view. I've never seen some Obj-C code where the ';' was kept intentionally in the implementation.

In Xcode 7.3 (beta), you don't have to turn anything on. It just works with the ';' at the end of the line.

@somebody I don’t like that semicolon style myself, but from what I’ve seen it’s pretty common for the reason Nicholas mentioned.

Here’s Brad Larson explaining his semicolon and brace style (via Twitter).

Leave a Comment