diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..05b1cf3 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,5 @@ +**/node_modules/* +**/vendor/* +**/*.min.js +**/coverage/* +**/build/* diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..195624a --- /dev/null +++ b/.eslintrc @@ -0,0 +1,16 @@ +{ + "rules": { + "no-console": "off", + "indent": [ "error", 2 ], + "quotes": [ "error", "single" ], + "semi": ["error", "always"], + "linebreak-style": [ "error", "unix" ] + }, + "env": { + "es6": true, + "node": true, + "mocha": true, + "jasmine": true + }, + "extends": "eslint:recommended" +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4ec7bce --- /dev/null +++ b/.gitignore @@ -0,0 +1,127 @@ +# Created by https://www.gitignore.io/api/osx,node,linux,windows + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### Node ### +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Typescript v1 declaration files +typings/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env + + +### OSX ### +*.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +ehthumbs.db +ehthumbs_vista.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# End of https://www.gitignore.io/api/osx,node,linux,windows diff --git a/assets/palette-invert-bitmap.bmp b/assets/palette-invert-bitmap.bmp new file mode 100644 index 0000000..d835124 Binary files /dev/null and b/assets/palette-invert-bitmap.bmp differ diff --git a/assets/palette-rotate-bitmap.bmp b/assets/palette-rotate-bitmap.bmp new file mode 100644 index 0000000..ad72ecd Binary files /dev/null and b/assets/palette-rotate-bitmap.bmp differ diff --git a/assets/palette-shift-bitmap.bmp b/assets/palette-shift-bitmap.bmp new file mode 100644 index 0000000..93fec77 Binary files /dev/null and b/assets/palette-shift-bitmap.bmp differ diff --git a/assets/palette-write-bitmap.bmp b/assets/palette-write-bitmap.bmp new file mode 100644 index 0000000..97ad0df Binary files /dev/null and b/assets/palette-write-bitmap.bmp differ diff --git a/index.js b/index.js new file mode 100644 index 0000000..41136e5 --- /dev/null +++ b/index.js @@ -0,0 +1,72 @@ +'use strict'; + +const Bitmap = require(`${__dirname}/model/bitmap-constructor.js`); +const ColorTransform = require(`${__dirname}/model/color-constructor.js`); +const fileHelper = require(`${__dirname}/lib/bitmap-file-helper.js`); + +module.exports = exports = {}; + +exports.invertBitmap = () => { + let onRead = (err, data) => { + if (err) throw err; + + let bitmap = new Bitmap(data); + let transform = new ColorTransform(); + let copied = new Buffer(data); + + bitmap.colorTable = transform.invertColors(bitmap); + + let startToColorTable = copied.slice(0, bitmap.headerSize + 14); + let subColorTableBuffer = new Buffer(bitmap.colorTable.join(''), 'hex'); + let colorTableToEnd = copied.slice(data.readInt32LE(10)); + let testcopy = Buffer.concat([startToColorTable, subColorTableBuffer, colorTableToEnd]); + + fileHelper.writeNew(`${__dirname}/assets/palette-invert-bitmap.bmp`, testcopy, exports.writeNew); + }; + fileHelper.initFile(`${__dirname}/assets/palette-bitmap.bmp`, onRead); +}; + +exports.colorShiftBitmap = () => { + let onRead = (err, data) => { + if (err) throw err; + + let bitmap = new Bitmap(data); + let transform = new ColorTransform(); + let copied = new Buffer(data); + + bitmap.colorTable = transform.blueShift(bitmap); + + let startToColorTable = copied.slice(0, bitmap.headerSize + 14); + let subColorTableBuffer = new Buffer(bitmap.colorTable.join(''), 'hex'); + let colorTableToEnd = copied.slice(data.readInt32LE(10)); + let testcopy = Buffer.concat([startToColorTable, subColorTableBuffer, colorTableToEnd]); + + fileHelper.writeNew(`${__dirname}/assets/palette-shift-bitmap.bmp`, testcopy, exports.writeNew); + }; + fileHelper.initFile(`${__dirname}/assets/palette-bitmap.bmp`, onRead); +}; + +exports.rotateBitmap = () => { + let onRead = (err, data) => { + fileHelper.initFile(`${__dirname}/assets/palette-bitmap.bmp`, (err, data) => { + if (err) throw err; + + let bitmap = new Bitmap(data); + let transform = new ColorTransform(); + let copied = new Buffer(data); + + bitmap.pixelTable = transform.rotateImage(bitmap); + + let startPixelTable = copied.slice(0, bitmap.pixelArrayStart); + let pixelArrBuffer = new Buffer(bitmap.pixelTable.join(''), 'hex'); + let testcopy = Buffer.concat([startPixelTable, pixelArrBuffer]); + + fileHelper.writeNew(`${__dirname}/assets/palette-rotate-bitmap.bmp`, testcopy, exports.writeNew); + }); + }; + fileHelper.initFile(`${__dirname}/assets/palette-bitmap.bmp`, onRead); +}; + +exports.colorShiftBitmap(); +exports.invertBitmap(); +exports.rotateBitmap(); diff --git a/lib/bitmap-file-helper.js b/lib/bitmap-file-helper.js new file mode 100644 index 0000000..8ad91db --- /dev/null +++ b/lib/bitmap-file-helper.js @@ -0,0 +1,26 @@ +'use strict'; + +const fs = require('fs'); + +module.exports = exports = {}; + +exports.initFile = (path, callback) => { + fs.readFile(path, (err, data) => { + if(err) return callback(err); + return callback(null, data); + }); +}; + +exports.writeNew = (path, data) => { + return fs.writeFile(path, data, (err, data) => { + if(err) return err; + return data; + }); +}; + +exports.eraseFile = (path) => { + return fs.writeFile(path, '', (err, data) => { + if(err) return err; + return data; + }); +}; diff --git a/model/bitmap-constructor.js b/model/bitmap-constructor.js new file mode 100644 index 0000000..6ff08af --- /dev/null +++ b/model/bitmap-constructor.js @@ -0,0 +1,20 @@ +'use strict'; + +module.exports = function Bitmap (data) { + this.type = data.toString('utf-8', 0, 2); + this.size = data.readInt32LE(2); + this.pixelArrayStart = data.readInt32LE(10); + this.headerSize = data.readInt32LE(14); + this.bitsPerPixel = data.readInt32LE(28); + this.width = data.readInt32LE(18); + this.height = data.readInt32LE(22); + + this.colorTableStart = this.headerSize + 14; + this.colorTable = data.toString('hex', this.colorTableStart, this.pixelArrayStart).match(/.{1,8}/g); + this.rowWidth = Math.ceil(((this.bitsPerPixel * this.width + 31) / 32) * 4); + this.pixelTable = data.toString('hex', this.pixelArrayStart, this.size).match(/.{1,2}/g); + + this.colorMap = this.pixelTable.map(function (pix) { + return parseInt(pix); + }); +}; diff --git a/model/color-constructor.js b/model/color-constructor.js new file mode 100644 index 0000000..e520f09 --- /dev/null +++ b/model/color-constructor.js @@ -0,0 +1,57 @@ +'use strict'; + +const ColorTransform = module.exports = function () {}; + +ColorTransform.prototype.invertColors = function (bitmap) { + let colorTable = bitmap.colorTable; + + return colorTable.map(function (hexColor) { + let hexArray = hexColor.match(/.{1,2}/g); + + let r = parseInt(hexArray[0], 16); + let g = parseInt(hexArray[1], 16); + let b = parseInt(hexArray[2], 16); + let invertR = (255 - r).toString(16); + let invertG = (255 - g).toString(16); + let invertB = (255 - b).toString(16); + + let invertArray = [invertR, invertG, invertB, '00']; + + invertArray.forEach(function (val, i) { + if (val.length === 1) { + invertArray[i] = '0' + invertArray[i]; + } + }); + invertArray = invertArray.join(''); + return invertArray; + }); +}; + +ColorTransform.prototype.blueShift = function (bitmap) { + let colorTable = bitmap.colorTable; + return colorTable.map(function (hexColor) { + let hexArray = hexColor.match(/.{1,2}/g); + + let r = parseInt(hexArray[0], 16); + let g = parseInt(hexArray[1], 16); + let b = parseInt(hexArray[2], 16); + let invertR = (Math.ceil(r * 0.1)).toString(16); + let invertG = (Math.ceil(g * 0.1)).toString(16); + let invertB = (b).toString(16); + + let colorShiftArray = [invertR, invertG, invertB, '00']; + colorShiftArray.forEach(function (val, i) { + if (val.length === 1) { + colorShiftArray[i] = '0' + colorShiftArray[i]; + } + }); + colorShiftArray = colorShiftArray.join(''); + return colorShiftArray; + }); +}; + +ColorTransform.prototype.rotateImage = function(bitmap) { + let pixelTable = bitmap.pixelTable; + + return pixelTable.reverse(); +}; diff --git a/package.json b/package.json new file mode 100644 index 0000000..754c89f --- /dev/null +++ b/package.json @@ -0,0 +1,29 @@ +{ + "name": "04-bitmap_transformer", + "version": "1.0.0", + "description": "this project changes a bitmap file into several variations", + "main": "index.js", + "directories": { + "test": "test" + }, + "scripts": { + "test": "mocha" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/loganabsher/04-bitmap_transformer.git" + }, + "keywords": [ + "bitmap" + ], + "author": "Logan Absher, Luis Manzo, Nick Asmussen", + "license": "MIT", + "bugs": { + "url": "https://github.com/loganabsher/04-bitmap_transformer/issues" + }, + "homepage": "https://github.com/loganabsher/04-bitmap_transformer#readme", + "devDependencies": { + "chai": "^4.1.0", + "mocha": "^3.4.2" + } +} diff --git a/test/bitmap-constructor-test.js b/test/bitmap-constructor-test.js new file mode 100644 index 0000000..f099834 --- /dev/null +++ b/test/bitmap-constructor-test.js @@ -0,0 +1,52 @@ +'use strict'; + +const expect = require('chai').expect; +const index = require(`${__dirname}/../index.js`); +const fileReader = require(`${__dirname}/../lib/bitmap-file-helper.js`); +const bitmapConstructor = require(`${__dirname}/../model/bitmap-constructor.js`); + +describe('Bitmap Constructor Module', () => { + describe('#Invert Bitmap', () => { + it('Should have a new file written', (done) => { + fileReader.eraseFile(`${__dirname}/../assets/palette-invert-bitmap.bmp`); + index.invertBitmap(); + fileReader.initFile(`${__dirname}/../assets/palette-invert-bitmap.bmp`, (err, data) => { + expect(err).to.equal(null); + expect(data).to.not.equal(null); + done(); + }); + }); + }); + describe('#Color Shift Bitmap', () => { + it('Should have a new file written', (done) => { + fileReader.eraseFile(`${__dirname}/../assets/palette-shift-bitmap.bmp`); + index.colorShiftBitmap(); + fileReader.initFile(`${__dirname}/../assets/palette-shift-bitmap.bmp`, (err, data) => { + expect(err).to.equal(null); + expect(data).to.not.equal(null); + done(); + }); + }); + }); + describe('#Rotate Bitmap', () => { + it('Should have a new file written', (done) => { + fileReader.eraseFile(`${__dirname}/../assets/palette-rotate-bitmap.bmp`); + index.rotateBitmap(); + fileReader.initFile(`${__dirname}/../assets/palette-rotate-bitmap.bmp`, (err, data) => { + expect(err).to.equal(null); + expect(data).to.not.equal(null); + done(); + }); + }); + }); + describe('#Bitmap Constructor', () => { + it('Should return an object', (done) => { + fileReader.initFile(`${__dirname}/../assets/palette-bitmap.bmp`, (err, data) => { + expect(err).to.equal(null); + let result = new bitmapConstructor(data); + expect(result).to.be.an('object'); + done(); + }); + }); + }); +}); diff --git a/test/bitmap-file-helper-test.js b/test/bitmap-file-helper-test.js new file mode 100644 index 0000000..47d6e1c --- /dev/null +++ b/test/bitmap-file-helper-test.js @@ -0,0 +1,43 @@ +'use strict'; + +const expect = require('chai').expect; +const fileReader = require(`${__dirname}/../lib/bitmap-file-helper.js`); + +describe('File Helper Module', function(){ + describe('#Init File', () => { + it('Should not error', (done) => { + fileReader.initFile(`${__dirname}/../assets/palette-bitmap.bmp`, (err, data) => { + expect(err).to.equal(null); + done(); + }); + }); + it('Should find data', (done) => { + fileReader.initFile(`${__dirname}/../assets/palette-bitmap.bmp`, (err, data) => { + expect(data).to.not.equal(null); + done(); + }); + }); + }); + describe('#Write New', () => { + it('Should write new file', (done) => { + fileReader.eraseFile(`${__dirname}/../assets/palette-write-bitmap.bmp`); + fileReader.writeNew(`${__dirname}/../assets/palette-write-bitmap.bmp`, `${__dirname}/../assets/palette-bitmap.bmp`); + fileReader.initFile(`${__dirname}/../assets/palette-write-bitmap.bmp`, (err, data) => { + expect(err).to.equal(null); + expect(data).to.not.equal(null); + done(); + }); + }); + }); + describe('#Erase file', () => { + it('Should erase palette-write-bitmap.bmp', (done) => { + fileReader.initFile(`${__dirname}/../assets/palette-bitmap.bmp`, (err, data) => { + expect(err).to.equal(null); + fileReader.writeNew(`${__dirname}/../assets/palette-write-bitmap.bmp`, data); + expect(data).to.not.equal(null); + fileReader.eraseFile(`${__dirname}/../assets/palette-write-bitmap.bmp`); + done(); + }); + }); + }); +});