# Copyright, Robert Donovan, LessonStudio, 2014
# If you use this then tweet what you did with it @LessonStudio.

# This file takes one argument which is a file that you would like to split into two encrypted images.
# The original image can only be viewed by overlaying the two encrypted images.
# If printed on clear plastic, It can be very finicky to align the two images if the pixel count is too high.
# For best results keep the original image below 200x200 pixels and print as large as possible onto clear plastic to
# obtain the best results.

# You can go to higher resolutions but you then really have to be precise when aligning the two images.

# The resulting images will be twice as wide and twice as tall pixelwise and there is only 1 bit colour.#Copyright, Robert Donovan, LessonStudio, 2014
# If you use this then tweet what you did with it @LessonStudio.

# This file takes one argument which is a file that you would like to split into two encrypted images.
# The original image can only be viewed by overlaying the two encrypted images.
# If printed on clear plastic, It can be very finicky to align the two images if the pixel count is too high.
# For best results keep the original image below 200x200 pixels and print as large as possible onto clear plastic to
# obtain the best results.

# You can go to higher resolutions but you then really have to be precise when aligning the two images.

# The resulting images will be twice as wide and twice as tall pixelwise and there is only 1 bit colour.

# Future features should include alignment marks to make aligning the two transparancies easier.

# Maybe I will increase the efficiency of the conversion except that I find that I spend more prep time in
# photoshop by many orders of magnitude than any time savings that could be extracted.

# The reason I built this is that I found many tools out there for doing this that didn't work for a
# variety of reasons including being built for long dead versions of Python or PIL.

# USAGE: python visual_cryptography.py file_to_encrypt.png

from PIL import Image, ImageDraw
import os
import sys
from random import SystemRandom
random = SystemRandom()
# If you want to use the more powerful PyCrypto (pip install pycrypto) then uncomment the next line and comment out the previous two lines
#from Crypto.Random import random
xrange = range

if len(sys.argv) != 2:
    print("This takes one argument; the image to be split.")
    exit()
infile = str(sys.argv[1])

if not os.path.isfile(infile):
    print("That file does not exist.")
    exit()

img = Image.open(infile)

f, e = os.path.splitext(infile)
out_filename_A = f+"_A.png"
out_filename_B = f+"_B.png"

img = img.convert('1')  # convert image to 1 bit

print("Image size: {}".format(img.size))
# Prepare two empty slider images for drawing
width = img.size[0]*2
height = img.size[1]*2
print("{} x {}".format(width, height))
out_image_A = Image.new('1', (width, height))
out_image_B = Image.new('1', (width, height))
draw_A = ImageDraw.Draw(out_image_A)
draw_B = ImageDraw.Draw(out_image_B)

# There are 6(4 choose 2) possible patterns and it is too late for me to think in binary and do these efficiently
patterns = ((1, 1, 0, 0), (1, 0, 1, 0), (1, 0, 0, 1),
            (0, 1, 1, 0), (0, 1, 0, 1), (0, 0, 1, 1))
# Cycle through pixels
for x in xrange(0, int(width/2)):
    for y in xrange(0, int(height/2)):
        pixel = img.getpixel((x, y))
        pat = random.choice(patterns)
        # A will always get the pattern
        draw_A.point((x*2, y*2), pat[0])
        draw_A.point((x*2+1, y*2), pat[1])
        draw_A.point((x*2, y*2+1), pat[2])
        draw_A.point((x*2+1, y*2+1), pat[3])
        if pixel == 0:  # Dark pixel so B gets the anti pattern
            draw_B.point((x*2, y*2), 1-pat[0])
            draw_B.point((x*2+1, y*2), 1-pat[1])
            draw_B.point((x*2, y*2+1), 1-pat[2])
            draw_B.point((x*2+1, y*2+1), 1-pat[3])
        else:
            draw_B.point((x*2, y*2), pat[0])
            draw_B.point((x*2+1, y*2), pat[1])
            draw_B.point((x*2, y*2+1), pat[2])
            draw_B.point((x*2+1, y*2+1), pat[3])

out_image_A.save(out_filename_A, 'PNG')
out_image_B.save(out_filename_B, 'PNG')
print("Done.")
