Skip to content

Instantly share code, notes, and snippets.

@danielsharvey
Last active September 15, 2022 14:32
Show Gist options
  • Save danielsharvey/929a525712a2b89ebb561226cc32f3e7 to your computer and use it in GitHub Desktop.
Save danielsharvey/929a525712a2b89ebb561226cc32f3e7 to your computer and use it in GitHub Desktop.
Converting iPhone iOS '.cpbitmap' images to PNGs
#!/usr/bin/python
from PIL import Image,ImageOps
import struct
import sys
if len(sys.argv) < 3:
print "Need two args: source_filename and result_filename\n";
sys.exit(0)
filename = sys.argv[1]
result_filename = sys.argv[2]
with open(filename) as f:
contents = f.read()
unk1, width, height, unk2, unk3, unk4 = struct.unpack('<6i', contents[-24:])
im = Image.frombytes('RGBA', (width,height), contents, 'raw', 'RGBA', 0, 1)
r,g,b,a = im.split()
im = Image.merge('RGBA', (b,g,r,a))
im.save(result_filename)
@danielsharvey
Copy link
Author

Converting iPhone iOS '.cpbitmap' images to PNGs

See https://stackoverflow.com/a/44225159/5219886

@sh00tg0a1
Copy link

It doesn't work for wallpaper in iOS 12, because every line has an offset on the basis of 16 bytes.

See the same stack overflow thread, but on the lower part - the JS solution.
https://stackoverflow.com/questions/7998324/dot-cpbitmap-images-imgaename-cpbitmap

My code works for the backup of iOS 12

from PIL import Image
import sys
import math
import struct

def r8(f):
    c = ord(f.read(1))
    return c

if len(sys.argv) <= 2:
    print("Usage: %s <input> <output>" % sys.argv[0])
else:
    f = open(sys.argv[1], "rb")
    f.seek(-78, 2)
    magic = f.read(8)
    print magic
    if magic != "bplist00":
        print("Didn't find bplist header, are you sure this is a cpbitmap file?")
        exit(1)
    f.seek(50, 1)
    dat = f.read(6)
    width, height = struct.unpack("<HxxH", dat)
    print("Size: %dx%d" % (width, height))
    img = Image.new("RGBA", (width, height))

    f.seek(0)
    imgd = img.load()

    # Take case of the line size
    line_size = int(math.ceil(width/16.0) * 16)
    print line_size
    for y in range(height):
        for x in range(width):
            b, g, r, a = r8(f), r8(f), r8(f), r8(f)
            imgd[x, y] = (r, g, b, a)
        f.seek((line_size - width)*4, 1)

    f.close()
    img.save(sys.argv[2])

@donly
Copy link

donly commented Aug 3, 2020

@sh00tg0a1 not working with this message:

import: delegate library support not built-in '' (X11) @ error/import.c/ImportImageCommand/1282.
./cpbitmap.py: line 6: syntax error near unexpected token `('
./cpbitmap.py: line 6: `def r8(f):'

@weedy
Copy link

weedy commented Sep 13, 2020

Looks like iOS 13 broke all of these.

Also needs to be converted to python3/PIL7. Thankfully I have a iOS12 backup still and gentoo gives me every python/PIL combo I could ever want.

@sh00tg0a1
Copy link

@donly @weedy
I have updated the code. It is compatible with python3/PIL now. You can find the code here
.

@danielsharvey
Copy link
Author

@sh00tg0a1 Thanks

@hthetiot
Copy link

hthetiot commented Aug 7, 2021

@ainsleyrutterford
Copy link

A browser based version I created based off of that Node.js Stack Overflow answer. It works in all modern browsers and should work with all versions of iOS so far: https://cpbitmap.github.io/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment