[
  {
    "path": ".gitignore",
    "content": "*~\nnode_modules\n"
  },
  {
    "path": ".jshint-emacs.js",
    "content": "/*jshint node: true */\nvar reporter_name = \"jhlint\";\n\nmodule.exports = {\n\treporter: function (data) {\n\t\t\"use strict\";\n\n\t\tvar str = '',\n\t\terrors = [];\n\n\t\tdata.forEach(function (data) {\n\t\t\tvar file = data.file;\n\n\t\t\tif (data.error) {\n\t\t\t\tdata.error.file = file;\n\t\t\t\terrors.push( data.error );\n\t\t\t}\n\t\t});\n\n\t\terrors.forEach(function (error) {\n\t\t\tvar file = error.file,\n\t\t\terror_id;\n\t\t\tif (error.id === '(error)') {\n\t\t\t\terror_id = 'ERROR';\n\t\t\t} else {\n\t\t\t\terror_id = 'WARNING';\n\t\t\t}\n\t\t\tstr += file + ':' + error.line + ':' + error.character + ': error ( ' + error.code + ' ) ' + error.reason + '\\n' + error.evidence + '\\n';\n\n\t\t});\n\n\t\tif (str) {\n\t\t\tprocess.stdout.write(str + \"\\n\");\n\t\t}\n\t}\n};\n"
  },
  {
    "path": ".jshintrc",
    "content": "{\n    \"bitwise\": false,\n    \"curly\": true,\n    \"eqeqeq\": true,\n    \"forin\": true,\n    \"freeze\": true,\n    \"immed\": true,\n    \"latedef\": true,\n    \"newcap\": true,\n    \"undef\": true,\n    \"unused\": \"vars\",\n    \"strict\": true,\n\n    \"browser\": true\n}\n"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2014 Commons Machinery\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "Makefile",
    "content": "\ntop := $(CURDIR)\n\nJSHINT = $(top)/node_modules/.bin/jshint\nMOCHA = $(top)/node_modules/.bin/mocha\n\nsrc = $(top)/blockhash.js\n\n# Modules should add files to be checked with jshint\njshint-files = $(src) $(mocha-files)\n\n# Modules should add files to be run as unit tests\nmocha-files = $(top)/test/tests.js\n\nREPORTER=\nifeq ($(EMACS),t)\nREPORTER=--reporter=.jshint-emacs.js\nendif\n\nall: lint\n\nclean:\n\nlint:\n\t$(JSHINT) $(REPORTER) $(jshint-files)\n\ntest:\n\t$(MOCHA) $(MOCHA_FLAGS) $(mocha-files)\n\n.PHONY: all clean lint test\n"
  },
  {
    "path": "README.md",
    "content": "blockhash-js\n============\n\nThis is a perceptual image hash calculation tool based on algorithm descibed in\nBlock Mean Value Based Image Perceptual Hashing by Bian Yang, Fan Gu and Xiamu Niu.\n\nInstallation\n-----\n\nThis module is installed via npm:\n\n```\n  $ npm install blockhash\n```\n\nUse in the browser\n-----\nTo use this library in the browser, you can build it with Browserify\nwith something like `browserify index.js --standalone blockhashjs >\nblockhash.js`\n\nInclude it and `zlib.js` on your page:\n```html\n<!DOCTYPE html>\n<html>\n  <head>\n    <title>Blockhash</title>\n  </head>\n  <body>\n    <script src=\"node_modules/png-js/zlib.js\"></script>\n    <script src=\"blockhash.js\"></script>\n    <script>\n      var blockhash = blockhashjs.blockhash;\n    </script>\n  </body>\n</html>\n```\n\nCall `blockhash(src, bits, method, callback)`, where\n`src` is an image URL, `bits` is the number of bits in a row, `method`\nis a number 1-2 (see below), and `callback` is a function with\n`(error, result)` signature.  On success, `result` will be array of\nbinary values.\n\nThe available methods are:\n\n1. Quick and crude, non-overlapping blocks\n2. Precise but slower, non-overlapping blocks\n\nMethod 2 is recommended as a good tradeoff between speed and good\nmatches on any image size.  The quick ones are only advisable when the\nimage width and height are an even multiple of the number of blocks\nused.\n\nLicense\n-------\n\nCopyright 2014 Commons Machinery http://commonsmachinery.se/\n\nDistributed under an MIT license, please see LICENSE in the top dir.\n\nContact: dev@commonsmachinery.se\n\n"
  },
  {
    "path": "index.js",
    "content": "// Perceptual image hash calculation tool based on algorithm descibed in\n// Block Mean Value Based Image Perceptual Hashing by Bian Yang, Fan Gu and Xiamu Niu\n//\n// Copyright 2014 Commons Machinery http://commonsmachinery.se/\n// Distributed under an MIT license, please see LICENSE in the top dir.\n\nvar PNG = require('png-js');\nvar jpeg = require('jpeg-js');\n\nvar one_bits = [0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4];\n\n/* Calculate the hamming distance for two hashes in hex format */\nvar hammingDistance = function(hash1, hash2) {\n    var d = 0;\n    var i;\n\n    if (hash1.length !== hash2.length) {\n        throw new Error(\"Can't compare hashes with different length\");\n    }\n\n    for (i = 0; i < hash1.length; i++) {\n        var n1 = parseInt(hash1[i], 16);\n        var n2 = parseInt(hash2[i], 16);\n        d += one_bits[n1 ^ n2];\n    }\n    return d;\n};\n\nvar median = function(data) {\n    var mdarr = data.slice(0);\n    mdarr.sort(function(a, b) { return a-b; });\n    if (mdarr.length % 2 === 0) {\n        return (mdarr[mdarr.length/2 - 1] + mdarr[mdarr.length/2]) / 2.0;\n    }\n    return mdarr[Math.floor(mdarr.length/2)];\n};\n\nvar translate_blocks_to_bits = function(blocks, pixels_per_block) {\n    var half_block_value = pixels_per_block * 256 * 3 / 2;\n    var bandsize = blocks.length / 4;\n\n    // Compare medians across four horizontal bands\n    for (var i = 0; i < 4; i++) {\n        var m = median(blocks.slice(i * bandsize, (i + 1) * bandsize));\n        for (var j = i * bandsize; j < (i + 1) * bandsize; j++) {\n            var v = blocks[j];\n\n            // Output a 1 if the block is brighter than the median.\n            // With images dominated by black or white, the median may\n            // end up being 0 or the max value, and thus having a lot\n            // of blocks of value equal to the median.  To avoid\n            // generating hashes of all zeros or ones, in that case output\n            // 0 if the median is in the lower value space, 1 otherwise\n            blocks[j] = Number(v > m || (Math.abs(v - m) < 1 && m > half_block_value));\n        }\n    }\n};\n\nvar bits_to_hexhash = function(bitsArray) {\n    var hex = [];\n    for (var i = 0; i < bitsArray.length; i += 4) {\n        var nibble = bitsArray.slice(i, i + 4);\n        hex.push(parseInt(nibble.join(''), 2).toString(16));\n    }\n\n    return hex.join('');\n};\n\nvar bmvbhash_even = function(data, bits) {\n    var blocksize_x = Math.floor(data.width / bits);\n    var blocksize_y = Math.floor(data.height / bits);\n\n    var result = [];\n\n    for (var y = 0; y < bits; y++) {\n        for (var x = 0; x < bits; x++) {\n            var total = 0;\n\n            for (var iy = 0; iy < blocksize_y; iy++) {\n                for (var ix = 0; ix < blocksize_x; ix++) {\n                    var cx = x * blocksize_x + ix;\n                    var cy = y * blocksize_y + iy;\n                    var ii = (cy * data.width + cx) * 4;\n\n                    var alpha = data.data[ii+3];\n                    if (alpha === 0) {\n                        total += 765;\n                    } else {\n                        total += data.data[ii] + data.data[ii+1] + data.data[ii+2];\n                    }\n                }\n            }\n\n            result.push(total);\n        }\n    }\n\n    translate_blocks_to_bits(result, blocksize_x * blocksize_y);\n    return bits_to_hexhash(result);\n};\n\nvar bmvbhash = function(data, bits) {\n    var result = [];\n\n    var i, j, x, y;\n    var block_width, block_height;\n    var weight_top, weight_bottom, weight_left, weight_right;\n    var block_top, block_bottom, block_left, block_right;\n    var y_mod, y_frac, y_int;\n    var x_mod, x_frac, x_int;\n    var blocks = [];\n\n    var even_x = data.width % bits === 0;\n    var even_y = data.height % bits === 0;\n\n    if (even_x && even_y) {\n        return bmvbhash_even(data, bits);\n    }\n\n    // initialize blocks array with 0s\n    for (i = 0; i < bits; i++) {\n        blocks.push([]);\n        for (j = 0; j < bits; j++) {\n            blocks[i].push(0);\n        }\n    }\n\n    block_width = data.width / bits;\n    block_height = data.height / bits;\n\n    for (y = 0; y < data.height; y++) {\n        if (even_y) {\n            // don't bother dividing y, if the size evenly divides by bits\n            block_top = block_bottom = Math.floor(y / block_height);\n            weight_top = 1;\n            weight_bottom = 0;\n        } else {\n            y_mod = (y + 1) % block_height;\n            y_frac = y_mod - Math.floor(y_mod);\n            y_int = y_mod - y_frac;\n\n            weight_top = (1 - y_frac);\n            weight_bottom = (y_frac);\n\n            // y_int will be 0 on bottom/right borders and on block boundaries\n            if (y_int > 0 || (y + 1) === data.height) {\n                block_top = block_bottom = Math.floor(y / block_height);\n            } else {\n                block_top = Math.floor(y / block_height);\n                block_bottom = Math.ceil(y / block_height);\n            }\n        }\n\n        for (x = 0; x < data.width; x++) {\n            var ii = (y * data.width + x) * 4;\n\n            var avgvalue, alpha = data.data[ii+3];\n            if (alpha === 0) {\n                avgvalue = 765;\n            } else {\n                avgvalue = data.data[ii] + data.data[ii+1] + data.data[ii+2];\n            }\n\n            if (even_x) {\n                block_left = block_right = Math.floor(x / block_width);\n                weight_left = 1;\n                weight_right = 0;\n            } else {\n                x_mod = (x + 1) % block_width;\n                x_frac = x_mod - Math.floor(x_mod);\n                x_int = x_mod - x_frac;\n\n                weight_left = (1 - x_frac);\n                weight_right = x_frac;\n\n                // x_int will be 0 on bottom/right borders and on block boundaries\n                if (x_int > 0 || (x + 1) === data.width) {\n                    block_left = block_right = Math.floor(x / block_width);\n                } else {\n                    block_left = Math.floor(x / block_width);\n                    block_right = Math.ceil(x / block_width);\n                }\n            }\n\n            // add weighted pixel value to relevant blocks\n            blocks[block_top][block_left] += avgvalue * weight_top * weight_left;\n            blocks[block_top][block_right] += avgvalue * weight_top * weight_right;\n            blocks[block_bottom][block_left] += avgvalue * weight_bottom * weight_left;\n            blocks[block_bottom][block_right] += avgvalue * weight_bottom * weight_right;\n        }\n    }\n\n    for (i = 0; i < bits; i++) {\n        for (j = 0; j < bits; j++) {\n            result.push(blocks[i][j]);\n        }\n    }\n\n    translate_blocks_to_bits(result, block_width * block_height);\n    return bits_to_hexhash(result);\n};\n\nvar blockhashData = function(imgData, bits, method) {\n    var hash;\n\n    if (method === 1) {\n        hash = bmvbhash_even(imgData, bits);\n    }\n    else if (method === 2) {\n        hash = bmvbhash(imgData, bits);\n    }\n    else {\n        throw new Error(\"Bad hashing method\");\n    }\n\n    return hash;\n};\n\nvar blockhash = function(src, bits, method, callback) {\n    var xhr;\n\n    xhr = new XMLHttpRequest();\n    xhr.open('GET', src, true);\n    xhr.responseType = \"arraybuffer\";\n\n    xhr.onload = function() {\n        var data, contentType, imgData, jpg, png, hash;\n\n        data = new Uint8Array(xhr.response || xhr.mozResponseArrayBuffer);\n        contentType = xhr.getResponseHeader('content-type');\n\n        try {\n            if (contentType === 'image/png') {\n                png = new PNG(data);\n\n                imgData = {\n                    width: png.width,\n                    height: png.height,\n                    data: new Uint8Array(png.width * png.height * 4)\n                };\n\n                png.copyToImageData(imgData, png.decodePixels());\n            }\n            else if (contentType === 'image/jpeg') {\n                imgData = jpeg.decode(data);\n            }\n\n            if (!imgData) {\n                throw new Error(\"Couldn't decode image\");\n            }\n\n            // TODO: resize if required\n\n            hash = blockhashData(imgData, bits, method);\n            callback(null, hash);\n        } catch (err) {\n            callback(err, null);\n        }\n    };\n\n    xhr.onerror = function(err) {\n        callback(err, null);\n    };\n\n    xhr.send();\n};\n\nmodule.exports = {\n  hammingDistance: hammingDistance,\n  blockhash: blockhash,\n  blockhashData: blockhashData\n};\n\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"blockhash\",\n  \"version\": \"0.3\",\n  \"homepage\": \"https://github.com/commonsmachinery/blockhash-js\",\n  \"author\": \"Commons Machinery <dev@commonsmachinery.se>\",\n  \"description\": \"Perceptual image hash calculation tool\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/commonsmachinery/blockhash-js.git\"\n  },\n  \"scripts\": {\n    \"test\": \"mocha\"\n  },\n  \"main\": \"index.js\",\n  \"browser\": {\n    \"png-js\": \"./node_modules/png-js/png.js\"\n  },\n  \"browserify-shim\": {\n    \"png-js\": \"PNG\"\n  },\n  \"browserify\": {\n    \"transform\": [\n      \"browserify-shim\"\n    ]\n  },\n  \"keywords\": [\n    \"image\",\n    \"hashing\",\n    \"perceptual\"\n  ],\n  \"devDependencies\": {\n    \"browserify-shim\": \"^3.8.0\",\n    \"expect.js\": \"*\",\n    \"glob\": \"*\",\n    \"jpeg-js\": \"0.0.4\",\n    \"jshint\": \"*\",\n    \"mocha\": \"*\",\n    \"png-js\": \"^0.1.1\"\n  },\n  \"license\": \"MIT\"\n}\n"
  },
  {
    "path": "test/.jshintrc",
    "content": "{\n    \"bitwise\": true,\n    \"curly\": true,\n    \"eqeqeq\": true,\n    \"forin\": true,\n    \"freeze\": true,\n    \"immed\": true,\n    \"latedef\": true,\n    \"newcap\": true,\n    \"undef\": true,\n    \"unused\": \"vars\",\n    \"strict\": true,\n\n    \"node\": true,\n\n    \"predef\": [ \"-Promise\" ]\n}\n"
  },
  {
    "path": "test/data/00002701_16_1.txt",
    "content": "f81bf99ffb803400e07f8c5d849f049707033a033fe33fe1bfe00e618ee30ca7  00002701.jpg\n"
  },
  {
    "path": "test/data/00002701_16_2.txt",
    "content": "f81bf99ffb803400e07f8c7d049f058706013e233fe33fe11f600e638ea30def  00002701.jpg\n"
  },
  {
    "path": "test/data/00011601_16_1.txt",
    "content": "3fff1ca004e80fe81fe00fe01fe81de8fffb8ffb00e3000000f303ff83ff4185  00011601.jpg\n"
  },
  {
    "path": "test/data/00011601_16_2.txt",
    "content": "3fff1ca004e80fe81fe00fe01fe81de8fffb0ffb00610031003b03ff80df01ff  00011601.jpg\n"
  },
  {
    "path": "test/data/00094701_16_1.txt",
    "content": "03ff007d02da33f325e366f90501379fd103c12ffd04fc787ff31e511f111702  00094701.jpg\n"
  },
  {
    "path": "test/data/00094701_16_2.txt",
    "content": "03ff007d02da33f325e366690581b79fd103f307fc0474f93ff31e117f550700  00094701.jpg\n"
  },
  {
    "path": "test/data/00106901_16_1.txt",
    "content": "ff80fe80fe00fe01ff00ff00ff00ff00fec0ff00f820fae0fcc0e7f0e7e0e0c0  00106901.jpg\n"
  },
  {
    "path": "test/data/00106901_16_2.txt",
    "content": "ff80fe80fc01fe01ff00ff00ff00ff00fec0fd20f0e0fac0ecf0e7f0e7e0e040  00106901.jpg\n"
  },
  {
    "path": "test/data/00133601_16_1.txt",
    "content": "00000fe07ff8fff81ff839d831883bcc39dc31ac300c3ffc9ef88e708041f1bf  00133601.jpg\n"
  },
  {
    "path": "test/data/00133601_16_2.txt",
    "content": "00000fe07ff8fff81ff8399831983bcc35ac303c384c3ffc8ef00660c003ffff  00133601.jpg\n"
  },
  {
    "path": "test/data/00136101_16_1.txt",
    "content": "000081f887ff8fff0fe00fe00ff80ff807fc07fc03f803f803e001e007f0ffff  00136101.jpg\n"
  },
  {
    "path": "test/data/00136101_16_2.txt",
    "content": "000083f887fe8fff0fe00fe00ff80ff807fc07f807f803f803e003e007e0ffff  00136101.jpg\n"
  },
  {
    "path": "test/data/00631701_16_1.txt",
    "content": "fffffc01bd0180018c61806fcf7181efae6d8e71893983878001800fc0ffefff  00631701.jpg\n"
  },
  {
    "path": "test/data/00631701_16_2.txt",
    "content": "fffff803bd018001886382e78f7382afceef8e71813187070001800fc0ffffff  00631701.jpg\n"
  },
  {
    "path": "test/data/00631801_16_1.txt",
    "content": "ffff8001bfb182018e8186e58dd587e58641807187df9be780018007e07fffff  00631801.jpg\n"
  },
  {
    "path": "test/data/00631801_16_2.txt",
    "content": "ffffc001bfa182018aa186e1877786e78623827385399fcf0001800fc0ffffff  00631801.jpg\n"
  },
  {
    "path": "test/data/01109801_16_1.txt",
    "content": "f80ff80ff007f007f007f183f4c3c5c3c7c3c7e3c1e3c007c003c007e087ffff  01109801.jpg\n"
  },
  {
    "path": "test/data/01109801_16_2.txt",
    "content": "f80ff80ff007f007f007f183f4c3c5c3c7c3c7e3c1e3c007c007c007e007ffff  01109801.jpg\n"
  },
  {
    "path": "test/data/06855701_16_1.txt",
    "content": "fefd0fff000f0003037f03bc00bd11ff01ff01fe01bc01ff01ff007f03fd03f8  06855701.jpg\n"
  },
  {
    "path": "test/data/06855701_16_2.txt",
    "content": "fefd0fff000f0003037f03bc01bc11ff01fe01be01bd03ff007f007f0ffd0bf0  06855701.jpg\n"
  },
  {
    "path": "test/data/24442301_16_1.txt",
    "content": "ffff8101bf0192059005860f9d8fbf8d9f818f818f998e8b8f8982e18001ffff  24442301.jpg\n"
  },
  {
    "path": "test/data/24442301_16_2.txt",
    "content": "ffff8101bf0192059005860f9d8fbfa99f818f818f999e198fc182e18001ffff  24442301.jpg\n"
  },
  {
    "path": "test/data/32499201_16_1.txt",
    "content": "0002001f1fff1fff7ff31ff303f300006f9178f0f8701ee0ffc8ce88c1cc01fc  32499201.jpg\n"
  },
  {
    "path": "test/data/32499201_16_2.txt",
    "content": "0002001f1fff1fff7ff71ff301f300007f9278f0f8700fc0ff98cc88c1cc03fc  32499201.jpg\n"
  },
  {
    "path": "test/data/Babylonian_16_1.txt",
    "content": "fe7ffe7ffe7ffc7ffc7ffc7ffc7f0000c103e187f18ff08f8003fc3ff81ff81f  Babylonian.png\n"
  },
  {
    "path": "test/data/Babylonian_16_2.txt",
    "content": "fe7ffe7ffe7ffc7ffc7ffc7ffc000000e107e187f89fe007c00ff81ff81ff83f  Babylonian.png\n"
  },
  {
    "path": "test/data/clipper_ship_16_1.txt",
    "content": "00007ff07ff07fe07fe67ff07560600077fe701e7f5e000079fd40410001ffff  clipper_ship.jpg\n"
  },
  {
    "path": "test/data/clipper_ship_16_2.txt",
    "content": "00007ff07ff07fe07fe67ff07560600077fe701e7f5e000079fd40410001ffff  clipper_ship.jpg\n"
  },
  {
    "path": "test/data/emptyBasket_16_1.txt",
    "content": "fffffe07e00380009b899f818f8f8c439f9fdc0fc807c803c003c00fe03ff1ff  emptyBasket.png\n"
  },
  {
    "path": "test/data/emptyBasket_16_2.txt",
    "content": "fffffe07e00380009f899f818f0b8c4b9f9fd00fcc07cc03c003e00fe07ff1ff  emptyBasket.png\n"
  },
  {
    "path": "test/data/puffy_white_16_1.txt",
    "content": "00000000fffffffff803f807f807f80ff90ff90fb90c9980ffffffff00000000  puffy_white.png\n"
  },
  {
    "path": "test/data/puffy_white_16_2.txt",
    "content": "00000000fffffffff803f807f807f80ff90ff90fb90c9980ffffffff00000000  puffy_white.png\n"
  },
  {
    "path": "test/data/sources.txt",
    "content": "puffy_white.jpg - http://www.nasa.gov/multimedia/imagegallery/image_feature_2466.html\nclipper_ship.jpg - http://commons.wikimedia.org/wiki/File:%27Clipper_Ship_Great_Republic%27_by_James_Butterworth,_c._1853_-_Old_State_House_Museum,_Boston,_MA_-_IMG_6709.JPG\nemptyBasket.png - https://openclipart.org/detail/194657/plastic-laundry-basket-by-gubrww2-194657\nBabylonian.png - https://openclipart.org/detail/194594/babylonian-or-summerian-religious-deity-by-lordoftheloch-194594\n\nThe remaining test images come from http://www.getty.edu/about/opencontent.html\n"
  },
  {
    "path": "test/data/stoplights_16_1.txt",
    "content": "0fe01fe007e027c037c077c00fe00fe007e047c001c003c007e007e007e003e0  stoplights.jpg\n"
  },
  {
    "path": "test/data/stoplights_16_2.txt",
    "content": "0fe01fe007e027c037c077c00fc00fe007e047c001c003c007e007e007e003c0  stoplights.jpg\n"
  },
  {
    "path": "test/data/values.txt",
    "content": "00136101_16_1 = 000001f887ff8fff0fe00fe00ff00ff807fc07f803f803f801e001e007f0ffff\n00106901_16_1 = ff80fe80fc00fe01fe00ff00ff00ff00fec0ff00f820f8e0fcc0e5f0e7e0e0c0\n00002701_16_1 = f81bf91ffb803400e07f8c5d049f049706033a033fe33fe1bfe00e618ee30ca3\n00133601_16_1 = 00000fe03ff8fff81f7839d831883bcc31dc31ac300c3ffc9ef88e608041f1bf\n00631701_16_1 = fffffc019d0180018c61806fcf7181cfae6d8e71891983878000800fc0ffefff\n00631801_16_1 = ffff8001bfb180018e8186e58dd187e586418071879f9be780018007c07fffff\n01109801_16_1 = f80ff80ff007f003f007f183f4c3c1c3c7c3c7e3c1e3c003c003c007c087ffff\n32499201_16_1 = 0002000f1fff1fff7ff31ff301f300006f9078f0f8701ee0ffc8ce88c1c401fc\n24442301_16_1 = ffff8101bf018205900586079d8fbf8d9f818f818f998e0b8f8982618001ffff\n06855701_16_1 = fe7d0fff000f0003037f03bc00bc11ff01ff01be01bc01ff01ff003f03fd03f8\nBabylonian_16_1 = fe7ffe7ffe7ffc7ffc7ffc7ffc7f0000c103e187f18ff00f8003fc3ff81ff81f\nclipper_ship_16_1 = 00007ff07fe07fe07fe67ff07520600077fe601e7f5e000079fd40410001fffe\nemptyBasket_16_1 = fffffc07e00380009b099f818f8f8c439f9fcc0fc807c803c003c00fe03ff1ff\npuffy_white_16_1 = 00000000ffbffffff803f807f807f807f90ff90fb90c9900ffff7fff00000000\n00011601_16_1 = 3fff1c2004e80fe81fe00fe00fe81de8fffb8ffb0063000000f303ff81ff4185\n00094701_16_1 = 03ff007d025a33f325e366f905013797d103c12ffd04fc687ff31e111f111702\n00002701_16_2 = f81bf91ffb803400e07f0c7d049f058706013e033fe33fe11f600e618ea30def\n00136101_16_2 = 000003f887fe8fff0fe00fe00ff00ff807fc07f807f803f003e003e007e0fffe\n00106901_16_2 = ff80fe80fc01fe00fe00ff00ff00ff00fec0fd00f0e0fac0ecf0e7f0e7e0e000\n00133601_16_2 = 00000fe03ff8fff81ff8399831883bcc35ac301c384c3ffc8ef006608003ffff\n00631701_16_2 = fffff803b9018001886382e7877382afceef8e61813187070001800f80ffffff\n00631801_16_2 = ffff8001bfa182018aa186e1873786e78623827385319fcf0000800fc0ffffff\n01109801_16_2 = f80ff80ff007f003f007f183e4c3c5c3c7c3c6e3c1e3c007c007c007e007efff\n32499201_16_2 = 0002001f0fff1fff7ff71ff300f300007f1278f0f8700fc0ff98cc88c1cc01fc\n06855701_16_2 = fefd07ff000f0003037f02bc01bc11ff01fe01be01bc03ff007f007f07fd0bf0\n24442301_16_2 = ffff8101bf0192019005860f9d8fbf899f818f818f998e198fc182618001ffff\nBabylonian_16_2 = fe7ffe7ffe7ffc7ffc7ffc7f7c000000e107e183f89fe007c00ff81ff81ff83f\nclipper_ship_16_2 = 00007ff07fe07fe07fe67ff07520600077fe601e7f5e000079fd40410001fffe\nemptyBasket_16_2 = fffffc07e00380009f099f818f0b8c4b9f9fc00fcc07cc03c003e00fe07ff1ff\npuffy_white_16_2 = 00000000ffbffffff803f807f807f807f90ff90fb90c9900ffff7fff00000000\n00011601_16_2 = 3fff1c2004e80fe81fe00fe01fe81de0fffb0ffb00210031003b03ff00df01ff\n00094701_16_2 = 03ff007d025a33f325e366690501b79fd103f3077c0474f93ff31e113f550700\n"
  },
  {
    "path": "test/tests.js",
    "content": "/* global describe, it, JpegImage */\n\n'use strict';\n\nvar expect = require('expect.js');\nvar glob = require('glob');\nvar path = require('path');\nvar fs = require('fs');\n\nvar blockhash = require('..');\n\nvar PNG = require('png-js');\nvar jpeg = require('jpeg-js');\n\nvar testFiles = glob.sync('test/data/*.jpg')\n    .concat(glob.sync('test/data/*.png'));\n\ntestFiles.forEach(function(fn) {\n    describe(fn, function(){\n        var ext = path.extname(fn);\n        var basename = path.basename(fn, ext);\n        var bits = 16;\n\n        [1, 2].forEach(function(m) {\n            it('method' + m, function(done) {\n                var data, getImgData, hash, expectedHash;\n\n                data = new Uint8Array(fs.readFileSync(fn));\n\n                switch (ext) {\n                case '.jpg':\n                    getImgData = function(next) {\n                        next(jpeg.decode(data));\n                    };\n                    break;\n\n                case '.png':\n                    getImgData = function(next) {\n                        var png = new PNG(data);\n                        var imgData = {\n                            width: png.width,\n                            height: png.height,\n                            data: new Uint8Array(png.width * png.height * 4)\n                        };\n\n                        png.decodePixels(function(pixels) {\n                            png.copyToImageData(imgData, pixels);\n                            next(imgData);\n                        });\n                    };\n                }\n\n                getImgData(function(imgData) {\n                    hash = blockhash.blockhashData(imgData, bits, m);\n\n                    expectedHash = fs.readFileSync(\"test/data/\" + basename + \"_\" + bits + \"_\" + m + \".txt\", {\n                        encoding: 'utf-8'\n                    }).split(/\\s/)[0];\n\n                    // use hamming distance to iron out little\n                    // differences between this jpeg decoder and the one in PIL\n                    var hd = blockhash.hammingDistance(expectedHash, hash);\n                    expect(hd).to.be.lessThan(3);\n\n                    done();\n                });\n            });\n        });\n    });\n});\n"
  }
]