#!/usr/bin/python

# Implementation of a simplified (*) version of Naor & Shamir's "visual
# cryptography"-scheme (EUROCRYPT'94)
# http://www.wisdom.weizmann.ac.il/~naor/PAPERS/vis.ps
# 
# Inspired by the almighty Krypto-Kekkonen (http://www.m-js.com:80/misc/kekkonen/)
#
# (*) Does not do 4 subpixels, only 2 subpixels in horizontal direction.
# Keeps original aspect ratio and size, with reduction in resolution.
#
# In short: Resulting shares will be twice the size of the resized (half-size) original,
# therefore resulting shares will have same size (and aspect ratio) as original input.
# 
# First, original is resized to half size. X-direction will expand two times
# bigger when using 2 subpixels in horizontal direction. To fix aspect ratio, each
# processed line is duplicated, to make shares have also height twice the size of original,
# thus giving correct aspect ratio.
#
# Author: http://slinky.imukuppi.org

import sys
import os
import Image
import random

PIXEL_BLACK = 0
PIXEL_WHITE = 255

if len(sys.argv)-1 != 1:
    print "%s [imagefile]" % sys.argv[0]
    print ""
    print "Outputs two files, share1.pdf and share2.pdf"
    print "Print the other on paper and the other on a transparency slide."
    print "Carefully align on top of each other."
    sys.exit()

im = Image.open(sys.argv[1])

im = im.resize([im.size[0]/2, im.size[1]/2], Image.ANTIALIAS).convert("1")
pixels = list(im.getdata())
shareSize = (im.size[0]*2, im.size[1]*2)

share1 = []
share2 = []

# initialize a PRNG with something from urandom
# TODO change this to cryptographically secure PRNG if you're serious
random.seed(os.urandom(10))

processed = 0
share1Row = []
share2Row = []

for i in range(len(pixels)):
    # throw dice
    dice = random.random()
  
    if pixels[i] == PIXEL_WHITE:
        # white pixel
        if dice > 0.5:
            share1Row.extend((PIXEL_BLACK, PIXEL_WHITE))
            share2Row.extend((PIXEL_BLACK, PIXEL_WHITE))
        else:
            share1Row.extend((PIXEL_WHITE, PIXEL_BLACK))
            share2Row.extend((PIXEL_WHITE, PIXEL_BLACK))
    else:
        # black pixel
        if dice > 0.5:
            share1Row.extend((PIXEL_BLACK, PIXEL_WHITE))
            share2Row.extend((PIXEL_WHITE, PIXEL_BLACK))
        else:
            share1Row.extend((PIXEL_WHITE, PIXEL_BLACK))
            share2Row.extend((PIXEL_BLACK, PIXEL_WHITE))
    
    processed += 1
    if processed >= im.size[0]:
        # row is ready, duplicate it
        share1.extend(share1Row)
        share1.extend(share1Row)
        share2.extend(share2Row)
        share2.extend(share2Row)
        share1Row = []
        share2Row = []
        processed = 0
    pass

# sanity check 
expected = shareSize[0] * shareSize[1]
if len(share1) != expected:
    print "Algorithm inconsistency. Expected length %d, got %d" % (expected,
                                                                   len(share1))
    sys.exit()

# create and save the images
imShare1 = Image.new(im.mode, shareSize)
imShare1.putdata(share1)

imShare2 = Image.new(im.mode, shareSize)
imShare2.putdata(share2)

# im.save("original.png")
imShare1.save("share1.png")
imShare2.save("share2.png")

