Recently i incorporated support for creating thumbnails in glgif, the backend gif animation library used in Anim8gif.
Rather than taking the easy route out and attempting to load the gif using UIImage, i decided to take advantage of glgif’s decode routines to create an accurate representation of the current state of the gif playback.
Put simply, i took the palette texture which is generated for display, and decoded it into a new UIImage.
The actual process is triggered by assigning a delegate (thumbDelegate), which will be invoked when the next frame in the gif is decoded. The delegate is then cleared.
Simple, right?
More Technical Details
The format of a palette texture in GLES is as follows:
Palette (16 or 256 entries)
Indexed Pixels (4bit or 8bit)
The palette can either be 16 or 256 entries long, depending on the format you use. Likewise the pixel data will either use 4 bits or 8 bits. To keep things simple, glgif uses an 8bit palette with 3 8bit entries for the color (GL_PALETTE8_RGB8_OES).
(Note that an 8bit palette can be insufficient as a gif can actually display more than 256 colours across multiple frames, but this is a necessary trade-off for speed)
So in order to decode it:
Read and decode the palette
Decode each pixel as an index into the palette
Using the decoded pixel data, create a new CoreGraphics bitmap context (using CGBitmapContextCreate)
Convert the bitmap context into a UIImage (using CGBitmapContextCreateImage and [UIImage imageWithCGImage:])
The resultant UIImage can then easily be used as a base to generate a sized down thumbnail of the gif, as illustrated below.
As always, the code for glgif is hosted on github, so feel free to check it out!
After almost a year of developing a and releasing a handful of iPhone apps to the App Store, i should be rollinginmoney, right?
Wrong.
In fact, i can officially reveal that with the exception of development contracts, my iPhone apps have netted me a grand total of:
£0
Yes, £0. It takes a lot of face up to facts, but it can’t be much more blunt than that. Granted i did sell a few units, but these were nowhere near the amount required to release any real payment into my bank account.
So what went wrong?
Well to be quite honest, i didn’t really market my apps at all well. I was hoping that simply being in the App Store combined with a word-of-mouth viral networking effect combined with some online ads would be sufficient. From there i could perhaps get some feedback, and introduce improvements in line with demand.
Did it work? Far from it.
Sadly since the iPhone market is pretty much saturated with thousands of apps, this is pretty much impossible to accomplish. You’d probably have more chance winning the lottery than going with that approach.
I even tried making one of my apps free and promoting one of my paid apps in the description. The actual response in sales was negligible.
It seems to me the only viable ways of attracting people to a new app are:
Getting it featured on the App Store
Pushing it out to as many review sites as you can ahead of time
Making it free
With regards to #3, not getting into in-depth analysis i will say this: if you have an app with a remotely useful purpose, people will consistently download it.
For example one of my apps, Anim8gif, gets at least 20 downloads a week – mind you, that figure isn’t really useful since none of those 20 people bother to write a review or send any useful feedback.
Right now though, i’m a bit tired of spending my budget coming up with the next big idea with elaborate marketing that’s likely to bring in tons of revenue. So here’s what i’m going to do.
I’m just giving up on making money off my own iPhone apps altogether. All my current apps: Anim8gif, VidSplit, and Overthecam are now free.
As for making future free apps, i am still undecided. Am i really getting anything out of the experience? Current signs point to no.
Recently it has come to my attention that there is no way to view hints on images on an iPhone.
Normally on any modern desktop web browser when you hover your mouse cursor over an image, a hint if provided will be displayed. Certain websites use these hints to comedic effect, and it’s a shame you can’t see them.
Fortunately i have come up with a nice and simple bookmarklet to solve this issue. Simply add it to your bookmarks in Safari, sync over to your iPhone and run the bookmarklet.
To view a hint, simply hold down two fingers over an image. The associated hint will be displayed in a pop-up alert.
Drag this link to your bookmarks to add the bookmarklet.
Those of you who happen to follow me on Twitter might have heard a while back that i tried releasing a camera app for the iPhone, which simply allowed you to overlay arbitrary images as guides over the camera.
Unfortunately it was rejected last month because of the following criteria:
“3.3.1 Applications may only use Documented APIs in the manner prescribed by Apple and must not use or call any private APIs.”
Which to me was extremely annoying. Until you get rejected for something as arbitrary as this, i don’t think one truly understands how frustrating it can be to have a weeks worth of work flushed down the toilet.
My objections to this were as follows. All the API’s i used were documented. I simply made a UIImagePickerController, a UIView and overlaid it on top of the camera view. The only potentially problematic thing i did was to check for the presence of the TPCameraPushButton in the view hierarchy (again, a public api) to see when the overlay should vanish.
This check was written in such a way that if the class name of the camera button changed in a future release, the overlay would just stay visible all the time.
But no, apparently i didn’t use the API correctly “in the manner prescribed by Apple”. Thus no camera app was released.
Now i would have just left it there and waited until Apple made a nice API for me to properly overlay images over the camera (if ever), but what do i find posted on tuaw today?
Yes, a camera app which overlays images on the camera. But not only that, it completely modifies the camera, and what’s more, it was both updated and approved well before and after my application.
(Note that i did not know about this particular camera app before i made mine. In addition it doesn’t appear to allow custom overlay images, one of the features of my app)
I guess it’s ok to make a camera application which completely modifies the camera UI. But if you just overlay images and check to see if the camera button exists then no, that doesn’t utilise the API in the manner prescribed by Apple.
So i have a challenge for you iPhone Developers out there. I have released the sourcecode to my ill-fated camera app, OverTheCam, under the GPL. Your mission, should you choose to accept it is to get it approved by Apple.
I’m going to award points based on :
How much of the app you need to change to get it approved.
How many “Camera Genius” UI enhancements you can get away with
How long it takes to get approved
The alternate name you pick
Additionally entries designed for any version of the OS later than 3.0.1 will not be counted. I’d also like to point out that you’ll also need to change the name of the application when submitting, for obvious reasons.
So what are you waiting for? Grab the code here on github and get submitting!Update:Since overthecam has finally been approved on OS 3.1, the competition has ended. The code is no longer available on github, but feel free to pop over a message if you want to check it out!
Just thought i’d let all you devoted readers know, i just released my second iPhone app on iTunes, vidsplit.
vidsplit is a simple picture puzzle game with an added twist: instead of a picture, you have to piece together a full-motion video. With a grand total of two difficulty settings and 3 puzzles, it should keep pretty much any puzzle enthusiast entertained for hours.
On the technical side of things, vidsplit is basically a really souped up fork of anim8gif. Instead of animated gifs however, i’m using far higher quality PVR compressed videos.
Why PVR? Well, it really is the only thing you can use to get smooth video playback, apart from the hardware mpeg decoding chips which sadly aren’t accessible using the SDK. All other methods and texture formats are far too slow to get any acceptable playback speed.
For the curious, you might also be interested to know the video frames are basically 256×256 pixels in size, stretched to the dimensions of the screen. Any bigger and the video files would balloon in size. Any smaller and the video would look like complete mush, so it’s pretty much an acceptable sweet spot in terms of quality.
The decoder is pretty simple. Every frame, a PVR texture is read from the video file (basically concatenated PVR textures) and uploaded as an OpenGLES texture. Ad-infimum. It really couldn’t be any simpler than that!
As for the actual graphical interface, i’m using a custom solution based on my kitten game engine.
To re-cap, i pretty much summarised the available options for playing back simple animations on the iphone in this nice table:
Method
Problem
Use UIImageView
It doesn’t scale
Re-draw a UIView every frame
Far too slow
Use GLES
Beyond the scope of the last article, but not this one!
Transform a clipped UIView each frame
We did that last time
To take things further, OpenGLES needs to be used. Specifically, you need to upload and draw a texture for every frame of your animation.
Now while this sounds like a great idea, there is a slight problem: uploading textures on the iPhone is hideously slow. Excluding the requirements of a video decoder, you only have enough time per frame to be able to playback a small stop motion video in RGB format.
All is not lost though. The iPhone supports two rather interesting texture formats: GL_COMPRESSED_RGB_PVRTC_* which is the native format, and GL_PALETTE* which is, as the name implies is a texture with a palette.
The fastest format to upload is PVR, but unfortunately there aren’t many native video codecs or animation formats about that decode to PVR format. Which leaves us with the palette format, which is just about fast enough to playback something more substantial…
Now which animation format uses a palette and is widely supported? Animated gif!
So to cut a long story short, i ended up writing a fairly elaborate library to decode and playback animated gifs on the iPhone, all in realtime.
Provided the gif is not gigantic (since the bigger the gif, the slower the decode + upload), it’s actually quite useable.
In fact, as a proof of concept i ended up writing an app using this library to playback animated gifs from any website in fullscreen (similar to the youtube app). Feel free to check it out – anim8gif.
The code
The code for this gif animation library, glgif, is located on github. As always, feel free to fork!
For those of you who have developed on the iPhone, you may have cringed at the prospect of having an animated image in your app, due to the flaky operation of the UIImageView which is the de-facto method of making a simple image animation.
For small images and animations, UIImageView works well. However, when you get to bigger and longer animations, it simply doesn’t scale. You might be fooled by the simulator, but on the real device your images will load very slowly before succumbing to the limited memory available.
Fortunately, there are other methods in which to playback and animated image. The first and perhaps the best method would be to implement everything in GLES. The second, which will be described here might be easier if you don’t want to setup GLES, considering it uses standard UIKit.
To start with
Here’s the theory: UIImageView sucks, so we need another method in which to animate images.
Method
Problem
Use UIImageView
It doesn’t scale
Re-draw a UIView every frame
Far too slow
Use GLES
Beyond the scope of this article
Transform a clipped UIView each frame
None, perfect!
As you can see, there are only two realistic methods in which to animate our image: either use GLES, or transform a view around the screen.
Since i don’t want to be messing around with GLES right now, i’ll tackle the UIView, or flip-book approach.
A flip-book can be made either by putting an image in a UIScrollView and scrolling through it, or by making a custom clipped UIView with the image inserted as a subview.
(The latter approach which i prefer as it is a bit cleaner)
The code put simply
To start off with, your master view needs to be set to clip its subviews on init. i.e.:
self.clipsToBounds = YES;
And the subview, which should be a UIView which basically needs to draw a big image containing all the frames, needs to have user interaction disabled. i.e.:
// MYAnimationViewSlide code
- (void)setImage:(UIImage*)aValue {
[image release];
image = [aValue retain];
// Resize to fit
CGSize sz = image.size;
self.frame = CGRectMake(0, 0, sz.width, sz.height);
[self setNeedsDisplay];
}
- (void)drawRect:(CGRect)rect {
// ...
// Just draw the image!
[image drawAtPoint:CGPointMake(0, 0)];
// No more need for the image
[image release];
image = nil;
// ...
}
// in init...
self.userInteractionEnabled = NO;
Then all you need to do is move your slide around in your view by setting the .transform property, i.e.:
// MYAnimationView code
// slide size == child view size
CGSize slideSz = slide.bounds.size;
// frame size == root view size
CGSize viewSz = self.bounds.size;
// Calculate x,y position for the frame
// (assuming your frames are packed in a block rather than a strip)
int framesPerRow = floor(slideSz.width / viewSz.width);
int x = currentFrame % framesPerRow;
int y = currentFrame / framesPerRow;
slide.transform = CGAffineTransformMakeTranslation(-viewSz.width*x,
-viewSz.height*y);
While tied to an NSTimer:
// MYAnimationView code
advanceTime = (1.0 / 25.0);
animTimer = [NSTimer scheduledTimerWithTimeInterval:advanceTime
target:self
selector:@selector(animateTimer:)
userInfo:nil repeats:YES];
// ...
- (void)animateTimer:(NSTimer*)timer
{
NSTimeInterval timeInterval=timer.timeInterval;
NSTimeInterval currentTime=[NSDate timeIntervalSinceReferenceDate];
if (lastTime != 0) {
double delta = (currentTime - lastTime) / timeInterval;
// advance by at least 1 frame, at most frames in delta
if (delta < 1)
self.currentFrame += 1;
else
self.currentFrame += (int)delta;
}
lastTime = currentTime;
}
It's as simple as that!
(unless your slides exceed 1024x1024, in which case you need to use more than one slide. i'll let you figure that one out)
The Code
For those of you who are interested in the code for this flip-book, i have pasted a gist on github. As always, feel free to fork!
And in case you want an example image, here's a set of frames for a 50 frame, 96x66 animation.
Recently i’ve been playing about with the iPhone SDK. I’m actually quite impressed at the great tools included to make development easier, whether you choose to develop native applications or web applications.
Since i wasn’t quite up to scratch with my Objective C, i thought i’d see how much better the in-built browser was. Using Dashcode, i was easily able to prototype some pretty neat touch-based interfaces.
Still, one of the major criticism’s i seem to find about developing web apps for the iPhone is a lack of an off-line option. If you aren’t connected to a network, you can’t make use of any web applications.
Well, that’s not strictly true. You can use the “data:/” URI hack, where you bookmark a URL containing the HTML of the application you want to use off-life. But this is not a very useful solution if you want to store persistent data your application can use after re-loading.
Which is why i was pleased to find out that the iPhone simulator in the SDK includes support for WebKit’s new Database storage API. In essence this means that you can persistently store pretty much anything you want on the client.
How is this useful on the iPhone? Well, it means we can now make very useful offline web applications with persistent data storage.
If you visit the above URL in the iPhone simulator, you should see three items which you can click on. (Note that these are all data:/ URI’s)
The first creates a database (if it does not already exist) and stores the data for an image, which is then loaded back and shown on the page.
The second inserts a second image into the database. If you click on the first item again, there will now be two images shown on the page.
The third replaces the data of the first and the second image. If you click on the first item again, you might get a little shock.
There are of course limitations to this Database API. Access to databases is restricted on a per-domain basis (although in Webkit “data:/” URL’s are treated as a single domain), and on the iPhone simulator you only get up to 5mb of storage.
Still, it works and i can see it being a very useful feature for future web applications on the iPhone.
This actually works in Safari 3.1 too, so you don’t need to download the huge iPhone SDK to try this out. It technically should work in Firefox 3 too, though as of yet i haven’t managed to get it to allow access to the database.