├── Images ├── icon.png ├── favicon.png ├── RowReadOptimized.png ├── ColumnReadOptimized.png ├── RowReadOptimized_small.png ├── ColumnReadOptimized_small.png └── icon.svg ├── LICENSE ├── scripts ├── arrayUtils.js ├── stringConverter.js ├── imageConverter.js └── main.js ├── README.md ├── css └── style.css ├── index.html └── JSArrayTest.html /Images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/notisrac/FileToCArray/HEAD/Images/icon.png -------------------------------------------------------------------------------- /Images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/notisrac/FileToCArray/HEAD/Images/favicon.png -------------------------------------------------------------------------------- /Images/RowReadOptimized.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/notisrac/FileToCArray/HEAD/Images/RowReadOptimized.png -------------------------------------------------------------------------------- /Images/ColumnReadOptimized.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/notisrac/FileToCArray/HEAD/Images/ColumnReadOptimized.png -------------------------------------------------------------------------------- /Images/RowReadOptimized_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/notisrac/FileToCArray/HEAD/Images/RowReadOptimized_small.png -------------------------------------------------------------------------------- /Images/ColumnReadOptimized_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/notisrac/FileToCArray/HEAD/Images/ColumnReadOptimized_small.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 notisrac 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /scripts/arrayUtils.js: -------------------------------------------------------------------------------- 1 | var arrayUtils = { 2 | /** 3 | * Puts a part of the source array into the destination array. 4 | * This is a much faster replacement for Array.slice and UIntXArray.subArray 5 | * Note: no parameter or error checking! 6 | * It does not return anything, because in javascript every object is passed by reference. So the modifications made in the function are visible on the outside. 7 | */ 8 | subArray : function (sourceArray, destinationArray, startPos, endPos) { 9 | for (var i = startPos; i < endPos; i++) { 10 | destinationArray[i - startPos] = sourceArray[i]; 11 | } 12 | }, 13 | 14 | /** 15 | * Puts the source array at the end of the destination array. 16 | * This is a much faster replacement for Array.concat and UIntXArray.set 17 | * Note: no parameter or error checking! 18 | * It does not return anything, because in javascript every object is passed by reference. So the modifications made in the function are visible on the outside. 19 | */ 20 | concatArray : function (sourceArray, destinationArray, startPos) { 21 | for (var i = 0; i < (sourceArray.length || sourceArray.byteLength); i++) { 22 | destinationArray[startPos + i] = sourceArray[i]; 23 | } 24 | }, 25 | 26 | /** 27 | * Returns true if the object passed in as the parameter is an array 28 | * @param {*} data The object to test 29 | */ 30 | isArray : function (data) { 31 | return (Object.prototype.toString.call(data).indexOf('Array') != -1); 32 | } 33 | 34 | }; 35 | -------------------------------------------------------------------------------- /scripts/stringConverter.js: -------------------------------------------------------------------------------- 1 | var stringConverter = { 2 | convertByte: function (oneByte, bytesPerPixel, conversionType, endianness) { 3 | // console.log(oneByte); 4 | var stringByte = '???'; 5 | 6 | if (conversionType == 'HEX0' || conversionType == 'HEX_SLASH') { 7 | stringByte = oneByte.toString(16).padStart(bytesPerPixel * 2, '0'); 8 | if (endianness == 'be') { 9 | stringByte = this.changeEndianness(stringByte); 10 | } 11 | stringByte = ((conversionType == 'HEX0') ? '0x' : '\\x') + stringByte; 12 | } else if (conversionType == 'DEC') { 13 | stringByte = oneByte; 14 | } else if (conversionType == 'BIN') { 15 | stringByte = 'B' + oneByte.toString(2).padStart(bytesPerPixel * 8, '0'); 16 | } else { 17 | console.error('Unknown conversion type'); 18 | } 19 | 20 | return stringByte; 21 | }, 22 | 23 | convert: function (dataLength, bytesPerPixel, conversionType, multiLine, endianness, colNumber, data) { 24 | var resultString = ''; 25 | for (var i = 0; i < dataLength; i++) { 26 | var stringByte = ''; 27 | // need to use bigint, so we can use 32bit integers (4byte per pixel) 28 | let combinedByte = BigInt("0b00000000000000000000000000000000"); 29 | for (let j = 0; j < bytesPerPixel; j++) { 30 | let pixelByte = BigInt(data[(i * bytesPerPixel) + j]); 31 | if (j != 0) { 32 | combinedByte = combinedByte << BigInt(8); 33 | } 34 | combinedByte = combinedByte | pixelByte; 35 | } 36 | stringByte = this.convertByte(combinedByte, bytesPerPixel, conversionType, endianness) + ', '; 37 | if (multiLine && ((i + 1) % colNumber == 0)) { 38 | stringByte += '\r\n '; 39 | } 40 | 41 | resultString += stringByte; 42 | } 43 | resultString = resultString.substr(0, resultString.lastIndexOf(',')).trim(); 44 | 45 | // add the array definition 46 | // resultString = '// array size is ' + dataLength + '\r\nconst uint8_t data[] = {\r\n ' + resultString + '\r\n};'; 47 | 48 | return resultString; 49 | }, 50 | 51 | changeEndianness: function (val) { 52 | const result = []; 53 | let len = val.length - 2; 54 | while (len >= 0) { 55 | result.push(val.substr(len, 2)); 56 | len -= 2; 57 | } 58 | return result.join(''); 59 | } 60 | 61 | } -------------------------------------------------------------------------------- /Images/icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # File to C array converter 2 | Coverts any file to a C style array. 3 | Useful if you want to embed a file (binary, text, image, whatever) into your code! 4 | Use it for your Arduino or other embedded projects. 5 | 6 | Just select a file, specify the array format and click convert. It will give you the contents of the file in the specified format, that you can embed into your code. 7 | Supported formats: 8 | - Hex (0x00) 9 | - Hex with trailing slash (\x00) 10 | - Decimal (000) 11 | - Binary (B00000000) 12 | 13 | ## It can also do image color format and size coversion! 14 | If you select an image file, it will recognise it, and show you the image conversion settings panel. 15 | - Change palette: 16 | - 32 bit RGBA (4bytes/pixel) 17 | - 24bit RGB (3bytes/pixel) 18 | - 16bit RRRRRGGGGGGBBBBB (2byte/pixel) 19 | - 15bit RRRRRGGGGGBBBBBA (2byte/pixel) 20 | - 8bit RRRGGGBB (1byte/pixel) 21 | - 8bit grayscale (1byte/pixel) 22 | - 1bit line art (1bit/pixel) 23 | - Resize the image 24 | - Keep the original aspect ratio 25 | - Modify the sizes freely 26 | 27 | 28 | ## Example 29 | We take Google's favicon: 30 | 31 | ![Google's favicon](https://www.google.hu/favicon.ico "Google's favicon") 32 | 33 | Convert it down to 16x* size (keeping the aspect ratio), 8bit grayscale, with hex output (0x..), and the result is like this: 34 | ```c 35 | #define FAVICON_HEIGHT 16 36 | #define FAVICON_WIDTH 16 37 | 38 | // array size is 256 39 | static const unsigned char favicon[] PROGMEM = { 40 | 0x00, 0x00, 0x00, 0x00, 0xff, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xff, 0x00, 0x00, 0x00, 0x00, 41 | 0x00, 0x00, 0xfe, 0xfd, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xfd, 0xfe, 0x00, 0x00, 42 | 0x00, 0xfe, 0xfc, 0xff, 0xfa, 0xc1, 0x90, 0x79, 0x79, 0x8f, 0xc0, 0xfa, 0xff, 0xfd, 0xfe, 0x00, 43 | 0x00, 0xfd, 0xff, 0xee, 0x8d, 0x76, 0x76, 0x76, 0x76, 0x76, 0x76, 0x9b, 0xff, 0xff, 0xfd, 0x00, 44 | 0xff, 0xfe, 0xfa, 0x8c, 0x76, 0x7a, 0xb7, 0xdf, 0xdf, 0xb7, 0x88, 0xea, 0xff, 0xff, 0xfd, 0xf9, 45 | 0xfd, 0xff, 0xca, 0x79, 0x79, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xfd, 46 | 0xfd, 0xff, 0xa8, 0x91, 0xb5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 47 | 0xfd, 0xff, 0x96, 0x94, 0xdf, 0xff, 0xff, 0xff, 0x93, 0x93, 0x93, 0x93, 0x93, 0xb3, 0xff, 0xfd, 48 | 0xfd, 0xff, 0x96, 0x94, 0xdf, 0xff, 0xff, 0xff, 0x93, 0x93, 0x93, 0x93, 0x93, 0xaf, 0xff, 0xfd, 49 | 0xfd, 0xff, 0xa9, 0x8f, 0xb0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb8, 0x93, 0xbd, 0xff, 0xfd, 50 | 0xfd, 0xff, 0xc8, 0x69, 0x68, 0xd9, 0xff, 0xff, 0xff, 0xff, 0xe9, 0x96, 0x93, 0xdc, 0xff, 0xff, 51 | 0xff, 0xfd, 0xfa, 0x7f, 0x65, 0x69, 0xac, 0xd9, 0xdb, 0xb3, 0x7e, 0x92, 0xa8, 0xfd, 0xfd, 0xf9, 52 | 0x00, 0xfd, 0xff, 0xed, 0x80, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x82, 0xf1, 0xff, 0xfd, 0x00, 53 | 0x00, 0xfe, 0xfd, 0xff, 0xfa, 0xba, 0x84, 0x69, 0x69, 0x80, 0xb4, 0xf7, 0xff, 0xfd, 0xff, 0x00, 54 | 0x00, 0x00, 0xfe, 0xfd, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xfd, 0xff, 0x00, 0x00, 55 | 0x00, 0x00, 0x00, 0x00, 0xf9, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xff, 0xf9, 0x00, 0x00, 0x00, 0x00 56 | }; 57 | ``` 58 | 59 | 60 | ## JSArrayTest.html 61 | Tests the speed of different array manipulation techniques in javascript 62 | 63 | 64 | ## See it in acton here [https://notisrac.github.io/FileToCArray](https://notisrac.github.io/FileToCArray) 65 | 66 | 67 | -------------------------------------------------------------------------------- /css/style.css: -------------------------------------------------------------------------------- 1 | label { 2 | width: 190px; 3 | margin: 1px; 4 | display: inline-block; 5 | vertical-align: top; 6 | } 7 | 8 | fieldset { 9 | width: 600px; 10 | } 11 | 12 | img { 13 | image-rendering: optimizeSpeed; /* STOP SMOOTHING, GIVE ME SPEED */ 14 | image-rendering: -moz-crisp-edges; /* Firefox */ 15 | image-rendering: -o-crisp-edges; /* Opera */ 16 | image-rendering: -webkit-optimize-contrast; /* Chrome (and eventually Safari) */ 17 | image-rendering: pixelated; /* Chrome */ 18 | image-rendering: optimize-contrast; /* CSS3 Proposed */ 19 | -ms-interpolation-mode: nearest-neighbor; /* IE8+ */ 20 | } 21 | 22 | #imgPreview { 23 | display: inline-block; 24 | max-height: 100px; 25 | min-height: 32px; 26 | border: 1px solid #696969; 27 | } 28 | 29 | .imageConversionOption { 30 | } 31 | 32 | .shortInput { 33 | width: 50px; 34 | } 35 | 36 | /*#divResult > img { 37 | padding-top: 10px; 38 | max-width: 95vw; 39 | } 40 | 41 | #fsResult { 42 | max-width: 96vw; 43 | width: 100%; 44 | } 45 | 46 | #txtResult { 47 | width: 95vw; 48 | height: 500px; 49 | float: left; 50 | font-size: 13px; 51 | }*/ 52 | 53 | #divResult > img { 54 | padding-left: 10px; 55 | max-width: 800px; 56 | } 57 | 58 | #fsResult { 59 | max-width: 1620px; 60 | /*width: 100%;*/ 61 | } 62 | 63 | #txtResult { 64 | width: 800px; 65 | height: 530px; 66 | float: left; 67 | font-size: 13px; 68 | } 69 | 70 | .linkButtons { 71 | text-decoration: none; 72 | } 73 | 74 | .versionInfo { 75 | font-size: 10px; 76 | margin-left: 10px; 77 | font-weight: normal; 78 | } 79 | 80 | .additionalInfo { 81 | margin-top: -20px; 82 | font-weight: normal; 83 | font-size: 14px; 84 | } 85 | 86 | .signaturePreview { 87 | float: right; 88 | font-family: 'Courier New', Courier, monospace; 89 | font-size: 12px; 90 | padding-top: 4px; 91 | color: #696969; 92 | } 93 | 94 | /* Tooltip container */ 95 | .tooltip { 96 | position: relative; 97 | display: inline-block; 98 | width: 16px; 99 | height: 16px; 100 | background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAPUExURQAAAAAAAAAAAAAAAAAAAE8O540AAAAFdFJOUwAyIhcHOGe1IAAAAFdJREFUGNNlj1ECwCAIQgW5/5lHaas1vvSpqBGWkgRT0UqAFpCVu6gVVD13q0MVBqpHptog3ELGIWdrAu3bIDYgPoC3KXstymSs/R32XnwE93Pz/cGm+wN52wDgRMjGVQAAAABJRU5ErkJggg==); 101 | top: 2px; 102 | } 103 | 104 | /* Tooltip text */ 105 | .tooltip .tooltiptext { 106 | visibility: hidden; 107 | min-width: 300px; 108 | max-width: 600px; 109 | background-color: #EEE; 110 | color: #000; 111 | border: 1px solid #696969; 112 | text-align: left; 113 | padding: 5px; 114 | border-radius: 6px; 115 | 116 | /* Position the tooltip text - see examples below! */ 117 | position: absolute; 118 | z-index: 1; 119 | top: -5px; 120 | left: 150%; 121 | 122 | font-size: 12px; 123 | } 124 | 125 | /* Show the tooltip text when you mouse over the tooltip container */ 126 | .tooltip:hover .tooltiptext { 127 | visibility: visible; 128 | } 129 | 130 | .tooltip .tooltiptext::after { 131 | content: " "; 132 | position: absolute; 133 | top: 13px; 134 | right: 100%; 135 | margin-top: -5px; 136 | border-width: 5px; 137 | border-style: solid; 138 | border-color: transparent black transparent transparent; 139 | } 140 | 141 | .image1BitModeOnly { 142 | } -------------------------------------------------------------------------------- /scripts/imageConverter.js: -------------------------------------------------------------------------------- 1 | var imageConverter = { 2 | /** 3 | * Converts a pixel from 24bit (RGBA) to the specified format. 4 | * It also returns the modified pixels in 24bit format, so it can be displayed again 5 | */ 6 | convertFromPixel: function (pixelData, mode) { 7 | // the converted pixel values 8 | var retData = new Array(); 9 | var r = pixelData[0]; 10 | var g = pixelData[1]; 11 | var b = pixelData[2]; 12 | var a = pixelData[3]; 13 | var newR = 0; 14 | var newG = 0; 15 | var newB = 0; 16 | // the converted pixel in 24bit format (rgba) 17 | var newPixelData = new Array(4); 18 | switch (mode) { 19 | case '32': 20 | retData = [r, g, b, a]; 21 | newPixelData = [r, g, b, a]; 22 | break; 23 | case '32r': 24 | retData = [b, g, r, a]; 25 | newPixelData = [r, g, b, a]; 26 | break; 27 | case '24': 28 | retData = [r, g, b]; 29 | newPixelData = [r, g, b, 255]; 30 | break; 31 | case '16': 32 | // 1 2 3 4 5 6 7 8|1 2 3 4 5 6 7 8 33 | // R R R R R|G G G G G G|B B B B B 34 | // 1 2 3 4 5|1 2 3 4 5 6|1 2 3 4 5 35 | // 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 1 36 | // 0000100000100001 37 | // R, B = 32 values 38 | // G = 64 values 39 | newR = (r * 32 / 256) | 0; // Math.floor() 40 | newG = (g * 64 / 256) | 0; 41 | newB = (b * 32 / 256) | 0; 42 | var b16Pixel = newR * 2048 + newG * 32 + newB; 43 | retData = [b16Pixel >> 8, b16Pixel & 255]; // RRRRRGGG|GGGBBBBB 44 | newPixelData = [newR * 256 / 32, newG * 256 / 64, newB * 256 / 32, 255]; // scale the values back to the 0-255 range 45 | break; 46 | case '16bgr': 47 | // 1 2 3 4 5 6 7 8|1 2 3 4 5 6 7 8 48 | // B B B B B|G G G G G G|R R R R R 49 | // 1 2 3 4 5|1 2 3 4 5 6|1 2 3 4 5 50 | // 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 1 51 | // 0000100000100001 52 | // R, B = 32 values 53 | // G = 64 values 54 | newR = (r * 32 / 256) | 0; // Math.floor() 55 | newG = (g * 64 / 256) | 0; 56 | newB = (b * 32 / 256) | 0; 57 | var b16Pixel = newB * 2048 + newG * 32 + newR; 58 | retData = [b16Pixel >> 8, b16Pixel & 255]; // BBBBBGGG|GGGRRRRR 59 | newPixelData = [newB * 256 / 32, newG * 256 / 64, newR * 256 / 32, 255]; // scale the values back to the 0-255 range 60 | break; 61 | case '15': 62 | newR = (r * 32 / 256) | 0; // Math.floor() 63 | newG = (g * 32 / 256) | 0; 64 | newB = (b * 32 / 256) | 0; 65 | var newA = (a > 0) ? 1 : 0; 66 | var b15Pixel = newR * 2048 + newG * 64 + newB * 2 + newA; 67 | retData = [b15Pixel >> 8, b15Pixel & 255]; // RRRRRGGG|GGBBBBBA 68 | newPixelData = [newR * 256 / 32, newG * 256 / 32, newB * 256 / 32, (newA == 1) ? 255 : 0]; // scale the values back to the 0-255 range 69 | break; 70 | case '8': 71 | // 1 2 3 4 5 6 7 8 72 | // R R R|G G G|B B 73 | // 1 2 3|1 2 3|1 2 74 | // 0 0 1 0 0 1 0 1 75 | // R, G = 8 values 76 | // B = 4 values 77 | //retData = [Math.floor((r * 6 / 256) * 36 + (g * 6 / 256) * 6 + (b * 6 / 256))]; // http://stackoverflow.com/a/12808927 78 | //newR = (r * 6 / 256); 79 | //newG = (g * 6 / 256); 80 | //newB = (b * 6 / 256); 81 | //retData = [Math.floor(newR * 36 + newG * 6 + newB)]; 82 | newR = (r * 8 / 256) | 0; 83 | newG = (g * 8 / 256) | 0; 84 | newB = (b * 4 / 256) | 0; 85 | retData = [newR * 32 + newG * 4 + newB]; // RRRGGGBB 86 | newPixelData = [newR * 256 / 8, newG * 256 / 8, newB * 256 / 4, 255]; // scale the values back to the 0-255 range 87 | break; 88 | case '8G': 89 | // http://www.ajaxblender.com/howto-convert-image-to-grayscale-using-javascript.html 90 | newR = ((r + g + b) / 3) | 0; 91 | retData = [newR]; 92 | newPixelData = [newR, newR, newR, 255]; 93 | break; 94 | case '1': 95 | var b1Pixel = ((r * 0.3 + g * 0.59 + b * 0.11) > 127) ? 1 : 0; // http://stackoverflow.com/a/18707438 96 | retData = [b1Pixel]; 97 | newPixelData = [b1Pixel * 255, b1Pixel * 255, b1Pixel * 255, 255]; 98 | break; 99 | default: 100 | } 101 | 102 | return { convertedPixel: retData, newPixel: newPixelData }; 103 | }, 104 | 105 | getBits: function (byteValue) { 106 | var outBits = ''; 107 | for (let i = 0; i < 8; i++) { 108 | var bitMask = 1 << i; 109 | if ((byteValue & bitMask) != 0) { 110 | outBits += '1'; 111 | } else { 112 | outBits += '0'; 113 | } 114 | } 115 | //console.log(outBits); 116 | 117 | return outBits; 118 | }, 119 | 120 | getConvertedPixel: function (source, position, paletteMod) { 121 | // single pixel from the original set 122 | var pixelData = new Uint8Array(4); 123 | // get the current pixel (4 bytes) 124 | arrayUtils.subArray(source, pixelData, position, position + 4); 125 | // modify the current pixel 126 | var moddedPixelData = this.convertFromPixel(pixelData, paletteMod); 127 | 128 | return moddedPixelData; 129 | }, 130 | 131 | convert: function (imageWidth, imageHeight, bytePerPixel, paletteMod, origPixels, forColumnRead) { 132 | // the downsampled pixels 133 | var moddedPixels = new Uint8Array(imageWidth * imageHeight * bytePerPixel); // typed arrays are way faster, than the genric Array 134 | // the actual length of the modded pixels array 135 | var moddedPixelsActualLength = (paletteMod == '1') ? 0 : moddedPixels.length; 136 | // the downsampled pixels converted back to 24bit for displaying 137 | var newPixels = new Uint8ClampedArray(imageWidth * imageHeight * 4); 138 | // loop through all the pixels, and modify them one by one 139 | if (!forColumnRead || (forColumnRead && paletteMod != '1')) { 140 | for (var i = 0; i < (origPixels.byteLength/*image.height * image.width * 4*/); i += 4) { 141 | // modify the current pixel 142 | var moddedPixelData = this.getConvertedPixel(origPixels, i, paletteMod); 143 | // store the new pixel 144 | arrayUtils.concatArray(moddedPixelData.newPixel, newPixels, i); 145 | // store the modified pixel 146 | if (paletteMod == '1') { // this one is tricky: every 8 pixels make up a byte 147 | var itemNo = Math.floor(i / 4 / 8); 148 | var currentBitPos = 7 - (i / 4) % 8; 149 | moddedPixels[itemNo] = (moddedPixels[itemNo] & (~(1 << currentBitPos))) | (moddedPixelData.convertedPixel[0] << currentBitPos) 150 | 151 | if (itemNo > moddedPixelsActualLength) { 152 | moddedPixelsActualLength = itemNo + 1; 153 | } 154 | } 155 | else { // the convertFromPixel returns an array 156 | arrayUtils.concatArray(moddedPixelData.convertedPixel, moddedPixels, i / 4 * bytePerPixel); 157 | } 158 | } 159 | } else { 160 | var byteCount = 0; 161 | var yPos = 0; 162 | do { 163 | var yStop = 8; 164 | if (yPos + 8 > imageHeight) { 165 | yStop = imageHeight - yPos; 166 | } 167 | for (let x = 0; x < imageWidth; x++) { 168 | var outValue = 0; 169 | for (let y = 0; y < yStop; y++) { 170 | var pixelPos = ((yPos + y) * imageWidth * 4) + (x * 4); 171 | //console.log(x + ',' + y + '(' + yPos + '): ' + pixelPos); 172 | 173 | // modify the current pixel 174 | var moddedPixelData = this.getConvertedPixel(origPixels, pixelPos, paletteMod); 175 | // store the new pixel 176 | arrayUtils.concatArray(moddedPixelData.newPixel, newPixels, pixelPos); 177 | var pixelValue = moddedPixelData.convertedPixel[0]; 178 | // console.log('pixelPos:' + pixelPos + ' outValue:' + this.getBits(outValue) + ' pixelValue:' + JSON.stringify(pixelValue)); 179 | 180 | // add the pixels to the byte 181 | var mask = 1 << (y % 8); 182 | if (0 < pixelValue) { 183 | outValue |= mask; 184 | } 185 | else { 186 | outValue &= ~mask; 187 | } 188 | // console.log('x:' + x + ' y:' + y + ' pixelValue:' + pixelValue + ' outValue:' + this.getBits(outValue)); 189 | } 190 | // switch to a new byte 191 | arrayUtils.concatArray([outValue], moddedPixels, byteCount); 192 | // console.log(byteCount + ': ' + this.getBits(outValue)); 193 | // console.log(byteCount + ': ' + JSON.stringify(moddedPixels)); 194 | byteCount++; 195 | } 196 | yPos += yStop; 197 | } while (yPos < imageHeight); 198 | 199 | moddedPixelsActualLength = byteCount; 200 | } 201 | 202 | if (moddedPixels.length != moddedPixelsActualLength) { 203 | console.log('array length: ' + moddedPixels.length + ' -> ' + moddedPixelsActualLength); 204 | var tmp = new Uint8Array(moddedPixelsActualLength); 205 | arrayUtils.subArray(moddedPixels, tmp, 0, tmp.length); 206 | moddedPixels = tmp; 207 | } 208 | // moddedPixels: this is the actual data, that will be displayed as the array 209 | // newPixels: this will be displayed as the converted image 210 | return { moddedPixels: moddedPixels, newPixels: newPixels }; 211 | 212 | } 213 | }; 214 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | File to C style array converter 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 |

File to C style array converterv:  29 |
30 | 31 |   32 | 33 |

34 |
35 |

36 | Select a file: 37 |

38 |
39 | File info 40 |
41 | This section contains basic information on the uploaded file.
If it is an image, the size in pixel and a preview will be shown
42 |
43 |
44 |
45 | 46 | 47 |
48 |
49 | 50 | 51 |
52 |
53 | 54 | 55 |
56 |
57 | 58 | 59 |
60 |
61 | 66 | 67 |
68 |
69 |
70 | Conversion settings 71 |
72 | 83 | 89 |
90 |
91 | 96 | 97 |
98 |
99 |
100 | 105 | 116 |
117 |
118 | 123 |  x  (Fill only one to maintain aspect ratio) 124 |
125 |
126 | 131 | 132 |
133 |
134 | 139 | 140 |
141 |
142 | 147 | 148 |
149 |
150 | 151 | 155 |
156 | 161 |
162 |
163 | 164 | 165 |
166 |
167 | 168 | 169 |
170 |
171 | 172 | 173 |
174 |
175 | 176 | 192 |
193 |
194 | 195 | 196 |
197 |
198 |
199 | These control the signature generated.
Please note, not all of them make sense in all languages!
200 |
201 | ... 202 |
203 |
204 |

205 |    206 |

207 |
208 | Result 209 |   210 |   211 |   212 | 213 | 214 |
215 |
216 | 217 | 218 | -------------------------------------------------------------------------------- /scripts/main.js: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | /// 4 | /// 5 | var imgImageHolder = new Image(); 6 | var binBinaryFileHolder; 7 | var uploadedFile; 8 | var variableName; 9 | 10 | /** 11 | * Handles when a file is selected by the user. 12 | * Displays the file info. 13 | */ 14 | function handleFileSelected(e) { 15 | //console.log(e.target.files); 16 | var singleFile = e.target.files[0]; 17 | var url = URL.createObjectURL(singleFile); 18 | uploadedFile = singleFile; 19 | // figure out the variable name to use - remove the file extension, and replace any characters not allowed in a variable name 20 | variableName = uploadedFile.name.replace(/\.[^/.]+$/, "").replace(/[^a-zA-Z0-9_]+/g, '_'); 21 | // variable names can't begin with a number 22 | var rx = new RegExp(/^[0-9]*$/gm); 23 | if (rx.test(variableName)) { 24 | variableName = '_' + variableName; 25 | } 26 | // the above rules should create a variable name valid for most languages 27 | 28 | init(); 29 | 30 | imgImageHolder = null; 31 | binBinaryFileHolder = null; 32 | 33 | // display the file info 34 | $('#fiName').text(singleFile.name); 35 | $('#fiSize').text(singleFile.size + ' byte(s)'); 36 | $('#fiType').text(singleFile.type || (singleFile.name.substr(singleFile.name.lastIndexOf('.')))); 37 | $('#fiLastModifiedDate').text(new Date(singleFile.lastModified).toISOString()); 38 | // show the conversion options fieldset 39 | $('#fsConversionOptions').show(); 40 | $('#btnConvert').show(); 41 | setStatus('ready!'); 42 | 43 | if (isImage(singleFile.type)) { 44 | console.log('file is an image'); 45 | // if this is an image, show the apropriate stuff 46 | $('.fileIsImage').show(); 47 | // along with the preview image 48 | imgImageHolder = new Image(); 49 | imgImageHolder.src = url; 50 | imgImageHolder.onload = function () { 51 | //URL.revokeObjectURL(this.src); 52 | var imgPreview = imgImageHolder.cloneNode(true); 53 | imgPreview.id = 'imgPreview'; 54 | imgPreview.alt = 'Preview of ' + singleFile.name; 55 | $('#imgPreview').remove(); 56 | $('#divPreview').append(imgPreview); 57 | $('#fiSize').text($('#fiSize').text() + ' (' + imgImageHolder.width + 'x' + imgImageHolder.height + 'px)'); 58 | } 59 | } 60 | else { 61 | console.log('file is binary'); 62 | } 63 | // load the file as binary too 64 | var reader = new FileReader(); 65 | reader.onload = function (e) { 66 | binBinaryFileHolder = e.target.result; 67 | } 68 | reader.readAsArrayBuffer(singleFile); 69 | } 70 | 71 | /** 72 | * Decides based on the mime type whether the current file is an image or not 73 | */ 74 | function isImage(mimeType) { 75 | var imageType = /^image\//; 76 | return imageType.test(mimeType); 77 | } 78 | 79 | function convert() { 80 | $('#divResult').empty(); 81 | $('#fsResult').show(); 82 | $('#txtResult').val(''); 83 | var forceBinary = $('#cbForceBinary').is(':checked'); 84 | var stringData = ''; 85 | 86 | // TODO display a loading icon 87 | 88 | var colNum = 16; 89 | var byteArray = new Array(); 90 | var isImage = imgImageHolder && !forceBinary; 91 | if (isImage) { 92 | var imageWidth = imgImageHolder.width; 93 | var imageHeight = imgImageHolder.height; 94 | // handle the resize of the image 95 | var txtResizeX = $('#txtResizeX').val(); 96 | var txtResizeY = $('#txtResizeY').val(); 97 | if (txtResizeX && txtResizeY) { 98 | imageWidth = txtResizeX; 99 | imageHeight = txtResizeY; 100 | } 101 | else if (txtResizeX && !txtResizeY) { 102 | imageHeight = Math.floor(imageHeight * (txtResizeX / imageWidth)); 103 | imageWidth = txtResizeX; 104 | } 105 | else if (!txtResizeX && txtResizeY) { 106 | imageWidth = Math.floor(imageWidth * (txtResizeY / imageHeight)); 107 | imageHeight = txtResizeY; 108 | } 109 | setStatus('Converting image'); 110 | // TODO put the prepareImage into a worker thread https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers 111 | var preparedImageData = prepareImage(imgImageHolder, imageWidth, imageHeight); 112 | byteArray = preparedImageData.moddedPixels; 113 | // display the converted image 114 | // create the canvas for it 115 | var canvas = $('')[0]; 116 | canvas.id = 'cnvsResultCanvas'; 117 | canvas.width = imageWidth; 118 | canvas.height = imageHeight; 119 | // get the context from the canvas 120 | var context = canvas.getContext('2d'); 121 | //console.log('' + imgImageHolder.width + 'x' + imgImageHolder.height); 122 | // create a new imagedata and put it on the context 123 | var imgData = new ImageData(preparedImageData.newPixels, imageWidth, imageHeight); 124 | context.putImageData(imgData, 0, 0); 125 | // create a new image tag 126 | //var newImage = new Image(); 127 | //newImage.src = canvas.toDataURL(); 128 | // add it to the result div 129 | //$('#divResult').append(newImage); 130 | $('#divResult').append(canvas); 131 | colNum = imageWidth; 132 | } 133 | else if (binBinaryFileHolder) { 134 | setStatus('Preparing biary data'); 135 | byteArray = prepareBinary(binBinaryFileHolder); 136 | } 137 | setStatus('Converting data to string'); 138 | stringData += convertToString(byteArray, colNum, isImage, imageHeight, imageWidth); 139 | //console.log('Result: ' + stringData); 140 | // display the string array 141 | $('#txtResult').val(stringData); 142 | $('#txtResult').prop('scrollTop', 0); 143 | // TODO remove the loading icon 144 | setStatus('done.'); 145 | } 146 | 147 | 148 | /** 149 | * Converts the image to the specified format, and returns the modified pixels in the new format along with them in 24bit format 150 | */ 151 | function prepareImage(image, newWidth, newHeight) { 152 | var dtStart = new Date(); 153 | 154 | console.log('image size: ' + image.width + 'x' + image.height + ' => ' + newWidth + 'x' + newHeight); 155 | var paletteMod = $('#cbPaletteMod').val(); 156 | var forColumnRead = $('#cbForColumnRead').is(':checked'); 157 | var bytePerPixel = Math.ceil(parseInt(paletteMod) / 8); 158 | console.log('paletteMod: ' + paletteMod + ' (' + bytePerPixel + 'bytes/pixel)'); 159 | 160 | var imageWidth = newWidth; 161 | var imageHeight = newHeight; 162 | 163 | // create a canvas for the image 164 | var canvas = $('')[0]; 165 | canvas.width = imageWidth; 166 | canvas.height = imageHeight; 167 | var context = canvas.getContext('2d'); 168 | // set smoothing for resize 169 | if (newWidth != image.width) { 170 | context.mozImageSmoothingEnabled = true; 171 | context.imageSmoothingQuality = "medium"; 172 | context.webkitImageSmoothingEnabled = true; 173 | context.msImageSmoothingEnabled = true; 174 | context.imageSmoothingEnabled = true; 175 | } 176 | 177 | // load the image into the context 178 | context.drawImage(image, 0, 0, imageWidth, imageHeight); 179 | 180 | // get the pixels 181 | var origPixels = context.getImageData(0, 0, imageWidth, imageHeight).data; // Uint8ClampedArray 182 | 183 | //var isSingleArray = $('[type="radio"][name="cbArrayType"]:checked').val() == 'SINGLE'; 184 | 185 | // do image convert 186 | var convertResult = imageConverter.convert(imageWidth, imageHeight, bytePerPixel, paletteMod, origPixels, forColumnRead); 187 | var moddedPixels = convertResult.moddedPixels; 188 | var newPixels = convertResult.newPixels; 189 | 190 | console.log('duration: ' + ((new Date()) - dtStart) + 'ms'); 191 | console.log('moddedPixels size: ' + moddedPixels.byteLength + 'bytes') 192 | console.log('newPixels size: ' + newPixels.byteLength + 'bytes') 193 | return { moddedPixels: moddedPixels, newPixels: newPixels }; 194 | } 195 | 196 | function prepareBinary(data) { 197 | var ia = new Uint8Array(data); 198 | 199 | return ia; 200 | } 201 | 202 | function convertToString(data, colNum, isImage, imageHeight, imageWidth) { 203 | console.log('Converting data to string'); 204 | var resultString = ''; 205 | var conversionType = $('#selFormat').val(); 206 | var paletteMod = $('#cbPaletteMod').val(); 207 | var separateBytes = $('#cbSeparateBytes').is(':checked'); 208 | var endianness = $('#selEndianness').val(); 209 | var dataLength = data.byteLength; 210 | console.log('dataLength: ' + dataLength); 211 | var bytesPerPixel = 1; 212 | if (isImage) { 213 | bytesPerPixel = (dataLength / (imageWidth * imageHeight)); 214 | } 215 | if (separateBytes) { 216 | bytesPerPixel = 1; 217 | } 218 | if (bytesPerPixel < 1) { 219 | bytesPerPixel = 1; 220 | } 221 | console.log('bytesPerPixel: ' + bytesPerPixel); 222 | var actualDataLength = dataLength / bytesPerPixel; 223 | console.log('actualDataLength: ' + actualDataLength); 224 | var colNumber = (paletteMod == '1') ? Math.ceil(colNum / 8) : colNum; 225 | console.log('colNum: ' + colNum); 226 | console.log('colNumber: ' + colNumber); 227 | var multiLine = $('#cbMultiLine').is(':checked'); 228 | 229 | // do the conversion 230 | if (isImage) { 231 | resultString += '#define ' + variableName.toUpperCase() + '_HEIGHT ' + imageHeight + '\r\n'; 232 | resultString += '#define ' + variableName.toUpperCase() + '_WIDTH ' + imageWidth + '\r\n'; 233 | resultString += '\r\n'; 234 | } 235 | 236 | resultString += '// array size is ' + dataLength + '\r\n'; 237 | resultString += assebleSignature() + ' = {\r\n '; 238 | resultString += stringConverter.convert(actualDataLength, bytesPerPixel, conversionType, multiLine, endianness, colNumber, data); 239 | resultString += '\r\n};'; 240 | 241 | return resultString; 242 | } 243 | 244 | function copyToClipboard() { 245 | var txtResult = document.querySelector('#txtResult'); 246 | txtResult.select(); 247 | 248 | try { 249 | var execResult = document.execCommand('copy'); 250 | var msg = execResult ? 'successful' : 'unsuccessful'; 251 | console.log('Copying text command was ' + msg); 252 | } catch (err) { 253 | console.log('Oops, unable to copy'); 254 | } 255 | } 256 | 257 | function saveImage() { 258 | // do save image 259 | var aLink = $('#aSaveImage'); 260 | var imageUrl = document.getElementById('cnvsResultCanvas').toDataURL(uploadedFile.type); 261 | imageUrl = imageUrl.replace(uploadedFile.type, 'image/octet-stream'); 262 | aLink.attr('href', imageUrl); 263 | aLink.attr('download', uploadedFile.name); 264 | //this.download = uploadedFile.name; 265 | } 266 | 267 | function saveFile() { 268 | // do save image 269 | var aLink = $('#aSaveFile'); 270 | var fileUrl = 'data:application/octet-stream;base64,' + btoa($('#txtResult').val()); 271 | aLink.attr('href', fileUrl); 272 | aLink.attr('download', uploadedFile.name.replace(/\.[^/.]+$/, '.h')); 273 | //this.download = uploadedFile.name; 274 | } 275 | 276 | function setStatus(message) { 277 | $('#spnStatus').text(message); 278 | } 279 | 280 | function init() { 281 | $('.fileIsImage').hide(); 282 | $('#fsConversionOptions').hide(); 283 | $('#btnConvert').hide(); 284 | $('#fsResult').hide(); 285 | $('#imgPreview').remove(); 286 | $('#fiName').text(''); 287 | $('#fiSize').text(''); 288 | $('#fiType').text(''); 289 | $('#fiLastModifiedDate').text(''); 290 | setStatus(''); 291 | imgImageHolder = new Image(); 292 | binBinaryFileHolder = null; 293 | updateSignaturePreview(); 294 | } 295 | 296 | function assebleSignature() { 297 | var sig = ''; 298 | if ($('#cbStatic').is(':checked')) { 299 | sig += $('#cbStatic').val() + ' '; 300 | } 301 | if ($('#cbConst').is(':checked')) { 302 | sig += $('#cbConst').val() + ' '; 303 | } 304 | if ($('#cbUnsigned').is(':checked')) { 305 | sig += $('#cbUnsigned').val() + ' '; 306 | } 307 | sig += $('#selDataType').val() + ' ' + variableName + '[] '; 308 | if ($('#cbPROGMEM').is(':checked')) { 309 | sig += $('#cbPROGMEM').val() + ' '; 310 | } 311 | 312 | return sig; 313 | } 314 | 315 | function updateSignaturePreview() { 316 | $('#spnSignaturePreview').text(assebleSignature() + ' = { ... };'); 317 | } 318 | 319 | $('document').ready(function () { 320 | // init 321 | $('#inFileInput').on('change', handleFileSelected); 322 | $('#cbForceBinary').on('change', function (e) { 323 | $('.imageConversionOption').each(function () { 324 | $(this).prop('disabled', $('#cbForceBinary').is(':checked')); 325 | }) 326 | //$('#divForceBinary').prop('disabled', false); 327 | }); 328 | $('#cbPaletteMod').on('change', function (e) { 329 | var disabled = $('#cbPaletteMod').val() != '1'; 330 | $('.image1BitModeOnly').each(function () { 331 | $(this).prop('disabled', disabled); 332 | }) 333 | }); 334 | $('.image1BitModeOnly').each(function () { 335 | $(this).prop('disabled', true); 336 | }) 337 | $('#btnConvert').on('click', convert); 338 | $('#btnCopyToClipboard').on('click', copyToClipboard); 339 | $('#btnSaveImage').on('click', saveImage); 340 | $('#btnSaveFile').on('click', saveFile); 341 | 342 | $('#cbStatic').on('change', updateSignaturePreview); 343 | $('#cbConst').on('change', updateSignaturePreview); 344 | $('#selDataType').on('change', updateSignaturePreview); 345 | $('#cbPROGMEM').on('change', updateSignaturePreview); 346 | 347 | $.get('https://api.github.com/repos/notisrac/FileToCArray', function(data) { 348 | var updateAt = new Date(data.updated_at); 349 | console.log(updateAt); 350 | $('#versionInfo').text(updateAt.toISOString()); 351 | $('#description').text(data.description); 352 | var gitUrl = data.html_url 353 | $('#gitLink').attr('href', gitUrl); 354 | $('#gitLink').text(gitUrl); 355 | }); 356 | 357 | 358 | init(); 359 | }); 360 | -------------------------------------------------------------------------------- /JSArrayTest.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 314 | 315 | 316 |

Javascript array manipulation speed test

317 | Number of iterations to do: 318 |
319 | 320 | 321 | 322 | 323 |
324 |
325 | Open the debug console! 326 | 327 | 328 | --------------------------------------------------------------------------------