diff --git a/src/js/LogoManager.js b/src/js/LogoManager.js
new file mode 100644
index 00000000..a77b4ce9
--- /dev/null
+++ b/src/js/LogoManager.js
@@ -0,0 +1,274 @@
+'use strict';
+
+var LogoManager = LogoManager || {
+ // dependencies set by init()
+ font: null,
+ logoStartIndex: null,
+ // DOM elements to cache
+ elements: {
+ preview: "#font-logo-preview",
+ uploadHint: "#font-logo-info-upload-hint",
+ },
+ // predefined values for handling the logo image
+ constants: {
+ TILES_NUM_HORIZ: 24,
+ TILES_NUM_VERT: 4,
+ MCM_COLORMAP: {
+ // background
+ '0-255-0': '01',
+ // black
+ '0-0-0': '00',
+ // white
+ '255-255-255': '10',
+ // fallback
+ 'default': '01',
+ },
+ },
+ // config for logo image selection dialog
+ acceptFileTypes: [
+ { extensions: ['png', 'bmp'] },
+ ],
+};
+
+/**
+ * Initialize Logo Manager UI with dependencies.
+ *
+ * @param {FONT} font
+ * @param {number} logoStartIndex
+ */
+LogoManager.init = function (font, logoStartIndex) {
+ // custom logo image constraints
+ this.constraints = {
+ // test for image size
+ imageSize: {
+ el: "#font-logo-info-size",
+ // calculate logo image size at runtime as it may change conditionally in the future
+ expectedWidth: font.constants.SIZES.CHAR_WIDTH
+ * this.constants.TILES_NUM_HORIZ,
+ expectedHeight: font.constants.SIZES.CHAR_HEIGHT
+ * this.constants.TILES_NUM_VERT,
+ /**
+ * @param {HTMLImageElement} img
+ */
+ test: img => {
+ var constraint = this.constraints.imageSize;
+ if (img.width != constraint.expectedWidth
+ || img.height != constraint.expectedHeight) {
+ GUI.log(i18n.getMessage("osdSetupCustomLogoImageSizeError", {
+ width: img.width,
+ height: img.height,
+ }));
+ return false;
+ }
+ return true;
+ },
+ },
+ // test for pixel colors
+ colorMap: {
+ el: "#font-logo-info-colors",
+ /**
+ * @param {HTMLImageElement} img
+ */
+ test: img => {
+ var canvas = document.createElement('canvas'),
+ ctx = canvas.getContext('2d');
+ canvas.width = img.width;
+ canvas.height = img.height;
+ ctx.drawImage(img, 0, 0);
+ for (var y = 0, Y = canvas.height; y < Y; y++) {
+ for (var x = 0, X = canvas.width; x < X; x++) {
+ var rgbPixel = ctx.getImageData(x, y, 1, 1).data.slice(0, 3),
+ colorKey = rgbPixel.join("-");
+ if (!this.constants.MCM_COLORMAP[colorKey]) {
+ GUI.log(i18n.getMessage("osdSetupCustomLogoColorMapError"));
+ return false;
+ }
+ }
+ }
+ return true;
+ },
+ },
+ };
+
+ // deps from osd.js
+ this.font = font;
+ this.logoStartIndex = logoStartIndex;
+ // inject logo size variables for dynamic translation strings
+ i18n.addResources({
+ logoWidthPx: "" + this.constraints.imageSize.expectedWidth,
+ logoHeightPx: "" + this.constraints.imageSize.expectedHeight,
+ });
+ // find/cache DOM elements
+ Object.keys(this.elements).forEach(key => {
+ this.elements["$" + key] = $(this.elements[key]);
+ });
+ Object.keys(this.constraints).forEach(key => {
+ this.constraints[key].$el = $(this.constraints[key].el);
+ });
+ // resize logo preview area to match tile size
+ this.elements.$preview
+ .width(this.constraints.imageSize.expectedWidth)
+ .height(this.constraints.imageSize.expectedHeight);
+ this.resetImageInfo();
+};
+
+LogoManager.resetImageInfo = function () {
+ this.hideUploadHint();
+ Object.values(this.constraints).forEach(constraint => {
+ var $el = constraint.$el;
+ $el.toggleClass("invalid", false);
+ $el.toggleClass("valid", false);
+ });
+};
+
+LogoManager.showConstraintNotSatisfied = constraint => {
+ constraint.$el.toggleClass("invalid", true);
+};
+
+LogoManager.showConstraintSatisfied = constraint => {
+ constraint.$el.toggleClass("valid", true);
+};
+
+LogoManager.showUploadHint = function () {
+ this.elements.$uploadHint.show();
+};
+
+LogoManager.hideUploadHint = function () {
+ this.elements.$uploadHint.hide();
+};
+
+/**
+ * Show a file open dialog and resolve to an Image object.
+ *
+ * @returns {Promise}
+ */
+LogoManager.openImage = function () {
+ return new Promise((resolve, reject) => {
+ /**
+ * Validate image using defined constraints and display results on the UI.
+ *
+ * @param {HTMLImageElement} img
+ */
+ var validateImage = img => {
+ return new Promise((resolve, reject) => {
+ this.resetImageInfo();
+ for (var key in this.constraints) {
+ if (!this.constraints.hasOwnProperty(key)) {
+ continue;
+ }
+ var constraint = this.constraints[key],
+ satisfied = constraint.test(img);
+ if (satisfied) {
+ this.showConstraintSatisfied(constraint);
+ } else {
+ this.showConstraintNotSatisfied(constraint);
+ reject();
+ return;
+ }
+ }
+ resolve();
+ });
+ };
+ var dialogOptions = {
+ type: 'openFile',
+ accepts: this.acceptFileTypes
+ };
+ chrome.fileSystem.chooseEntry(dialogOptions, fileEntry => {
+ if (chrome.runtime.lastError) {
+ console.error(chrome.runtime.lastError.message);
+ return;
+ }
+ // load and validate selected image
+ var img = new Image();
+ img.onload = () => {
+ validateImage(img)
+ .then(() => resolve(img))
+ .catch(error => reject(error));
+ };
+ img.onerror = error => reject(error);
+ fileEntry.file(file => img.src = "file://" + file.path);
+ });
+ });
+};
+
+/**
+ * Replaces the logo in the loaded font based on an image.
+ *
+ * @param {HTMLImageElement} img
+ */
+LogoManager.replaceLogoInFont = function (img) {
+ /**
+ * Takes an ImageData object and returns an MCM symbol as an array of strings.
+ *
+ * @param {ImageData} data
+ */
+ var imageToCharacter = data => {
+ var char = [],
+ line = "";
+ for (var i = 0, I = data.length; i < I; i += 4) {
+ var rgbPixel = data.slice(i, i + 3),
+ colorKey = rgbPixel.join("-");
+ line += this.constants.MCM_COLORMAP[colorKey]
+ || this.constants.MCM_COLORMAP['default'];
+ if (line.length == 8) {
+ char.push(line);
+ line = "";
+ }
+ }
+ var fieldSize = this.font.constants.SIZES.MAX_NVM_FONT_CHAR_FIELD_SIZE;
+ if (char.length < fieldSize) {
+ var pad = this.constants.MCM_COLORMAP['default'].repeat(4);
+ for (var i = 0, I = fieldSize - char.length; i < I; i++)
+ char.push(pad);
+ }
+ return char;
+ };
+ // takes an OSD symbol as an array of strings and replaces the in-memory character at charAddress with it
+ var replaceChar = (lines, charAddress) => {
+ var characterBits = [];
+ var characterBytes = [];
+ for (var n = 0, N = lines.length; n < N; n++) {
+ var line = lines[n];
+ for (var y = 0; y < 8; y = y + 2) {
+ var v = parseInt(line.slice(y, y + 2), 2);
+ characterBits.push(v);
+ }
+ characterBytes.push(parseInt(line, 2));
+ }
+ this.font.data.characters[charAddress] = characterBits;
+ this.font.data.characters_bytes[charAddress] = characterBytes;
+ this.font.data.character_image_urls[charAddress] = null;
+ this.font.draw(charAddress);
+ };
+ // loop through an image and replace font symbols
+ var canvas = document.createElement('canvas'),
+ ctx = canvas.getContext('2d'),
+ charAddr = this.logoStartIndex;
+ canvas.width = img.width;
+ canvas.height = img.height;
+ ctx.drawImage(img, 0, 0);
+ for (var y = 0; y < this.constants.TILES_NUM_VERT; y++) {
+ for (var x = 0; x < this.constants.TILES_NUM_HORIZ; x++) {
+ var imageData = ctx.getImageData(
+ x * this.font.constants.SIZES.CHAR_WIDTH,
+ y * this.font.constants.SIZES.CHAR_HEIGHT,
+ this.font.constants.SIZES.CHAR_WIDTH,
+ this.font.constants.SIZES.CHAR_HEIGHT
+ ),
+ newChar = imageToCharacter(imageData.data);
+ replaceChar(newChar, charAddr);
+ charAddr++;
+ }
+ }
+};
+
+/**
+ * Draw the logo using the loaded font data.
+ */
+LogoManager.drawPreview = function () {
+ var $el = this.elements.$preview.empty();
+ for (var i = this.logoStartIndex, I = this.font.constants.MAX_CHAR_COUNT; i < I; i++) {
+ var url = this.font.data.character_image_urls[i];
+ $el.append('');
+ }
+};
diff --git a/src/js/tabs/osd.js b/src/js/tabs/osd.js
index 73be9e78..acbbb66d 100755
--- a/src/js/tabs/osd.js
+++ b/src/js/tabs/osd.js
@@ -152,276 +152,6 @@ FONT.openFontFile = function($preview) {
});
};
-/**
- * Replaces the logo in the loaded font based on an image.
- *
- * @param {HTMLImageElement} img
- */
-FONT.replaceLogoFromImage = function(img) {
- /**
- * Takes an ImageData object and returns an MCM symbol as an array of strings.
- *
- * @param {ImageData} data
- */
- var imageToCharacter = function(data) {
- var char = [],
- line = "";
- for (var i = 0, I = data.length; i < I; i += 4) {
- var rgbPixel = data.slice(i, i + 3),
- colorKey = rgbPixel.join("-");
- line += LogoManager.constants.MCM_COLORMAP[colorKey]
- || LogoManager.constants.MCM_COLORMAP['default'];
- if (line.length == 8) {
- char.push(line);
- line = "";
- }
- }
- var fieldSize = FONT.constants.SIZES.MAX_NVM_FONT_CHAR_FIELD_SIZE;
- if (char.length < fieldSize) {
- var pad = LogoManager.constants.MCM_COLORMAP['default'].repeat(4);
- for (var i = 0, I = fieldSize - char.length; i < I; i++)
- char.push(pad);
- }
- return char;
- };
- // takes an OSD symbol as an array of strings and replaces the in-memory character at charAddress with it
- var replaceChar = function(lines, charAddress) {
- var characterBits = [];
- var characterBytes = [];
- for (var n = 0, N = lines.length; n < N; n++) {
- var line = lines[n];
- for (var y = 0; y < 8; y = y + 2) {
- var v = parseInt(line.slice(y, y + 2), 2);
- characterBits.push(v);
- }
- characterBytes.push(parseInt(line, 2));
- }
- FONT.data.characters[charAddress] = characterBits;
- FONT.data.characters_bytes[charAddress] = characterBytes;
- FONT.data.character_image_urls[charAddress] = null;
- FONT.draw(charAddress);
- };
- // loop through an image and replace font symbols
- var canvas = document.createElement('canvas'),
- ctx = canvas.getContext('2d'),
- charAddr = SYM.LOGO;
- canvas.width = img.width;
- canvas.height = img.height;
- ctx.drawImage(img, 0, 0);
- for (var y = 0; y < LogoManager.constants.TILES_NUM_VERT; y++) {
- for (var x = 0; x < LogoManager.constants.TILES_NUM_HORIZ; x++) {
- var imageData = ctx.getImageData(
- x * FONT.constants.SIZES.CHAR_WIDTH,
- y * FONT.constants.SIZES.CHAR_HEIGHT,
- FONT.constants.SIZES.CHAR_WIDTH,
- FONT.constants.SIZES.CHAR_HEIGHT
- ),
- newChar = imageToCharacter(imageData.data);
- replaceChar(newChar, charAddr);
- charAddr++;
- }
- }
-};
-
-var LogoManager = LogoManager || {
- // DOM elements to cache
- elements: {
- preview: "#font-logo-preview",
- uploadHint: "#font-logo-info-upload-hint",
- },
- constants: {
- TILES_NUM_HORIZ: 24,
- TILES_NUM_VERT: 4,
- MCM_COLORMAP: {
- // background
- '0-255-0': '01',
- // black
- '0-0-0': '00',
- // white
- '255-255-255': '10',
- // fallback
- 'default': '01',
- },
- },
- // config for logo image selection dialog
- acceptFileTypes: [
- { extensions: ['png', 'bmp'] },
- ],
-};
-
-// custom logo image constraints
-LogoManager.constraints = {
- // test for image size
- imageSize: {
- el: "#font-logo-info-size",
- // calculate logo image size at runtime as it may change conditionally in the future
- expectedWidth: FONT.constants.SIZES.CHAR_WIDTH
- * LogoManager.constants.TILES_NUM_HORIZ,
- expectedHeight: FONT.constants.SIZES.CHAR_HEIGHT
- * LogoManager.constants.TILES_NUM_VERT,
- /**
- * @param {HTMLImageElement} img
- */
- test: function(img) {
- var constraint = LogoManager.constraints.imageSize;
- if (img.width != constraint.expectedWidth
- || img.height != constraint.expectedHeight) {
- GUI.log(i18n.getMessage("osdSetupCustomLogoImageSizeError", {
- width: img.width,
- height: img.height,
- }));
- return false;
- }
- return true;
- },
- },
- // test for pixel colors
- colorMap: {
- el: "#font-logo-info-colors",
- /**
- * @param {HTMLImageElement} img
- */
- test: function(img) {
- var canvas = document.createElement('canvas'),
- ctx = canvas.getContext('2d');
- canvas.width = img.width;
- canvas.height = img.height;
- ctx.drawImage(img, 0, 0);
- for (var y = 0, Y = canvas.height; y < Y; y++) {
- for (var x = 0, X = canvas.width; x < X; x++) {
- var rgbPixel = ctx.getImageData(x, y, 1, 1).data.slice(0, 3),
- colorKey = rgbPixel.join("-");
- if (!LogoManager.constants.MCM_COLORMAP[colorKey]) {
- GUI.log(i18n.getMessage("osdSetupCustomLogoColorMapError"));
- return false;
- }
- }
- }
- return true;
- },
- },
-};
-
-/**
- * Initialize Logo Manager UI.
- */
-LogoManager.init = function() {
- // inject logo size variables for dynamic translation strings
- i18n.addResources({
- logoWidthPx: "" + LogoManager.constraints.imageSize.expectedWidth,
- logoHeightPx: "" + LogoManager.constraints.imageSize.expectedHeight,
- });
- // find/cache DOM elements
- Object.keys(LogoManager.elements).forEach(function(key) {
- LogoManager.elements["$" + key] = $(LogoManager.elements[key]);
- });
- Object.keys(LogoManager.constraints).forEach(function(key) {
- LogoManager.constraints[key].$el = $(LogoManager.constraints[key].el);
- });
- // resize logo preview area to match tile size
- LogoManager.elements.$preview
- .width(LogoManager.constraints.imageSize.expectedWidth)
- .height(LogoManager.constraints.imageSize.expectedHeight);
- LogoManager.resetImageInfo();
-};
-
-LogoManager.resetImageInfo = function() {
- LogoManager.hideUploadHint();
- Object.values(LogoManager.constraints).forEach(function(constraint) {
- var $el = constraint.$el;
- $el.toggleClass("invalid", false);
- $el.toggleClass("valid", false);
- });
-};
-
-LogoManager.showConstraintNotSatisfied = function(constraint) {
- constraint.$el.toggleClass("invalid", true);
-};
-
-LogoManager.showConstraintSatisfied = function(constraint) {
- constraint.$el.toggleClass("valid", true);
-};
-
-LogoManager.showUploadHint = function() {
- LogoManager.elements.$uploadHint.show();
-};
-
-LogoManager.hideUploadHint = function() {
- LogoManager.elements.$uploadHint.hide();
-};
-
-/**
- * Show a file open dialog and resolve to an Image object.
- *
- * @returns {Promise}
- */
-LogoManager.openImage = function() {
- return new Promise(function(resolve, reject) {
- /**
- * Validate image using defined constraints and display results on the UI.
- *
- * @param {HTMLImageElement} img
- */
- var validateImage = function(img) {
- return new Promise(function(resolve, reject) {
- LogoManager.resetImageInfo();
- for (var key in LogoManager.constraints) {
- if (!LogoManager.constraints.hasOwnProperty(key)) {
- continue;
- }
- var constraint = LogoManager.constraints[key],
- satisfied = constraint.test(img);
- if (satisfied) {
- LogoManager.showConstraintSatisfied(constraint);
- } else {
- LogoManager.showConstraintNotSatisfied(constraint);
- reject();
- return;
- }
- }
- resolve();
- });
- },
- dialogOptions = {
- type: 'openFile',
- accepts: LogoManager.acceptFileTypes
- };
-
- chrome.fileSystem.chooseEntry(dialogOptions, function(fileEntry) {
- if (chrome.runtime.lastError) {
- console.error(chrome.runtime.lastError.message);
- return;
- }
- // load and validate selected image
- var img = new Image();
- img.onload = function() {
- validateImage(img).then(function() {
- resolve(img);
- }).catch(function(error) {
- reject(error);
- });
- };
- img.onerror = function(error) {
- reject(error);
- };
- fileEntry.file(function(file) {
- img.src = "file://" + file.path;
- });
- });
- });
-};
-
-/**
- * Draw the logo using the loaded font data.
- */
-LogoManager.drawPreview = function() {
- var $el = LogoManager.elements.$preview.empty();
- for (var i = SYM.LOGO, I = FONT.constants.MAX_CHAR_COUNT; i < I; i++) {
- var url = FONT.data.character_image_urls[i];
- $el.append('');
- }
-};
-
/**
* returns a canvas image with the character on it
*/
@@ -1682,8 +1412,8 @@ TABS.osd.initialize = function (callback) {
fontbuttons.append($('