Hi guys—sorry to be late to the party (it'd be really great, @
Venomator, if there was a way to get an e-mail alert when tagged...).
It's not surprising that a simple load/save will change the file when you look at what is typically happening to it (although there is a way around this for app developers).
In theory, a JPEG image should not need to be recompressed if it hasn't been changed (and even, in some circumstances, if it has, in a way—such as an orientation change—that only affects the metadata and not the image data.
In
practice, what generally happens is this (at a technical level, it's different with loading an image from Camera Roll, and loading it from an app's private space or via Open In..., but the end result is the same):
- The image data is loaded into memory as one of Apple's internal image formats (typically CGImage or UIImage). This involves a conversion of the image data from compressed (and encoded and...) Y'CbCr data to uncompressed RGBA.
- Even if the image is unchanged, when it is saved to a new location it's the CGImage or UIImage that is saved as a JPEG, which involves the re-conversion of the image data from uncompressed RGBA to compressed (and encoded and...) Y'CbCr
So there are two key changes to the image data. Its seems most likely to me that the algorithm for either the compression or the expansion of the Y'CbCr data has a flaw, which is causing the shift towards purple. The banding, too, seem likely to be the result of flawed handling of the 8x8 pixel blocks in which JPEG data is held. If I had to go for one or the other I'd guess the problem was at the point where the JPEG is converted to RGB rather than at the other end—just a hunch.
The way around this for developers, at least with unaltered images, is to flag whether the image has been edited and, if not, to ignore the CGImage or UIImage they've created and get the data directly from the source file with, say,
Code:
NSDatadataWithContentsOfURL:
and write it straight out to the destination (using
Code:
writeImageDataToSavedPhotosAlbum:metadata:completionBlock:
for Camera Roll). For simple orientation changes or other metadata changes the
metadata alone can be extracted, tweaked and saved with the
unchanged image data using this technique.
With images that have been altered, it's trickier.
The only real answer is to use non-lossy formats until final output has been created—which is what imaging pros normally do anyway on the desktop. JPEG is an excellent format for final output—it's a lousy workflow tool if you value image integrity and you
will get quality problems surfacing after repeated edits, even without (apparent) OS-level bugs.
Of the formats natively supported by iOS, both PNG and TIFF are lossless. I much prefer TIFF for photographs because it has excellent metadata support—PNG doesn't—and writing TIFFs (even compressed ones) is much faster than writing PNGs. (And compressed TIFFs are generally smaller, because PNG's compression algorithm wasn't really designed for handling continuous tone efficiently.)
- If an app saves and re-loads temporary files during the editing process I think it's extremely important that it uses a lossless format—there could be dozens of saves that a user isn't aware have taken place!
As far as format support is concerned, it may be helpful to know that image editors can open any image that's been saved to Camera Roll, whatever the format (the conversion to uncompressed RGB is handled by iOS) but each supported
output format must be manually added by the developer—JPEG is a doodle, JPEG with configurable compression is pretty simple, PNG is a bit harder and TIFF is a bit harder still (but still pretty straightforward). Equally, each format the the app supports with Open In... has to be manually specified by the developer. This is why there are so many apparent disconnects between different formats an app may seem to support in different places.