Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 /** | 5 /** |
| 6 * @fileoverview Puts text on a braille display. | 6 * @fileoverview Puts text on a braille display. |
| 7 * | 7 * |
| 8 */ | 8 */ |
| 9 | 9 |
| 10 goog.provide('cvox.BrailleDisplayManager'); | 10 goog.provide('cvox.BrailleDisplayManager'); |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 50 */ | 50 */ |
| 51 this.commandListener_ = function() {}; | 51 this.commandListener_ = function() {}; |
| 52 /** | 52 /** |
| 53 * Current display state to show in the Virtual Braille Captions display. | 53 * Current display state to show in the Virtual Braille Captions display. |
| 54 * This is different from realDisplayState_ if the braille captions feature | 54 * This is different from realDisplayState_ if the braille captions feature |
| 55 * is enabled and there is no hardware display connected. Otherwise, it is | 55 * is enabled and there is no hardware display connected. Otherwise, it is |
| 56 * the same object as realDisplayState_. | 56 * the same object as realDisplayState_. |
| 57 * @type {!cvox.BrailleDisplayState} | 57 * @type {!cvox.BrailleDisplayState} |
| 58 * @private | 58 * @private |
| 59 */ | 59 */ |
| 60 this.displayState_ = {available: false, textRowCount: undefined, | 60 this.displayState_ = {available: false, textRowCount: 0, |
| 61 textColumnCount: undefined}; | 61 textColumnCount: 0}; |
| 62 /** | 62 /** |
| 63 * State reported from the chrome api, reflecting a real hardware | 63 * State reported from the chrome api, reflecting a real hardware |
| 64 * display. | 64 * display. |
| 65 * @type {!cvox.BrailleDisplayState} | 65 * @type {!cvox.BrailleDisplayState} |
| 66 * @private | 66 * @private |
| 67 */ | 67 */ |
| 68 this.realDisplayState_ = this.displayState_; | 68 this.realDisplayState_ = this.displayState_; |
| 69 | 69 |
| 70 translatorManager.addChangeListener(function() { | 70 translatorManager.addChangeListener(function() { |
| 71 this.translateContent_(this.content_, this.expansionType_); | 71 this.translateContent_(this.content_, this.expansionType_); |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 103 | 103 |
| 104 /** | 104 /** |
| 105 * Dots representing a cursor. | 105 * Dots representing a cursor. |
| 106 * @const | 106 * @const |
| 107 * @private | 107 * @private |
| 108 */ | 108 */ |
| 109 cvox.BrailleDisplayManager.CURSOR_DOTS_ = 1 << 6 | 1 << 7; | 109 cvox.BrailleDisplayManager.CURSOR_DOTS_ = 1 << 6 | 1 << 7; |
| 110 | 110 |
| 111 | 111 |
| 112 /** | 112 /** |
| 113 * Alpha threshold for a pixel to be possibly displayed as a raised dot when | |
| 114 * converting an image to braille, where 255 means only fully-opaque | |
| 115 * pixels can be raised (if their luminance passes the luminance threshold), | |
| 116 * and 0 means that alpha is effectively ignored and only luminance matters. | |
| 117 * @const | |
| 118 * @private | |
| 119 */ | |
| 120 cvox.BrailleDisplayManager.ALPHA_THRESHOLD_ = 255; | |
| 121 | |
| 122 | |
| 123 /** | |
| 124 * Luminance threshold for a pixel to be displayed as a raised dot when | |
| 125 * converting an image to braille, on a scale of 0 (black) to 255 (white). | |
| 126 * A pixel whose luminance is less than the given threshold will be raised. | |
| 127 * @const | |
| 128 * @private | |
| 129 */ | |
| 130 cvox.BrailleDisplayManager.LUMINANCE_THRESHOLD_ = 128; | |
| 131 | |
| 132 | |
| 133 /** | |
| 134 * Array mapping an index in an 8-dot braille cell, in column-first order, | |
| 135 * to its corresponding bit mask in the standard braille cell encoding. | |
| 136 * @const | |
| 137 * @private | |
| 138 */ | |
| 139 cvox.BrailleDisplayManager.COORDS_TO_BRAILLE_DOT_ = | |
| 140 [0x1, 0x2, 0x4, 0x40, 0x8, 0x10, 0x20, 0x80]; | |
| 141 | |
| 142 | |
| 143 /** | |
| 113 * @param {!cvox.NavBraille} content Content to send to the braille display. | 144 * @param {!cvox.NavBraille} content Content to send to the braille display. |
| 114 * @param {!cvox.ExpandingBrailleTranslator.ExpansionType} expansionType | 145 * @param {!cvox.ExpandingBrailleTranslator.ExpansionType} expansionType |
| 115 * If the text has a {@code ValueSpan}, this indicates how that part | 146 * If the text has a {@code ValueSpan}, this indicates how that part |
| 116 * of the display content is expanded when translating to braille. | 147 * of the display content is expanded when translating to braille. |
| 117 * (See {@code cvox.ExpandingBrailleTranslator}). | 148 * (See {@code cvox.ExpandingBrailleTranslator}). |
| 118 */ | 149 */ |
| 119 cvox.BrailleDisplayManager.prototype.setContent = function( | 150 cvox.BrailleDisplayManager.prototype.setContent = function( |
| 120 content, expansionType) { | 151 content, expansionType) { |
| 121 this.translateContent_(content, expansionType); | 152 this.translateContent_(content, expansionType); |
| 122 }; | 153 }; |
| 123 | 154 |
| 124 | 155 |
| 125 /** | 156 /** |
| 157 * Takes an image, in the form of a data url, and displays it in braille | |
| 158 * onto the physical braille display and the virtual braille captions display. | |
| 159 * @param {!string} imageUrl The image, in the form of a data url. | |
| 160 */ | |
| 161 cvox.BrailleDisplayManager.prototype.setImageContent = function(imageUrl) { | |
| 162 if (!this.displayState_.available) { | |
| 163 return; | |
| 164 } | |
| 165 | |
| 166 // The number of dots in a braille cell. | |
| 167 // TODO(dmazzoni): Both multi-line braille displays we're testing with | |
| 168 // are 6-dot (2 x 3), but we should have a way to detect that via brltty. | |
| 169 var cellWidth = 2; | |
| 170 var cellHeight = 3; | |
| 171 var maxCellHeight = 4; | |
| 172 | |
| 173 var rows = this.displayState_.textRowCount; | |
| 174 var columns = this.displayState_.textColumnCount; | |
| 175 var imageDataUrl = imageUrl; | |
| 176 var imgElement = document.createElement('img'); | |
| 177 imgElement.src = imageDataUrl; | |
| 178 imgElement.onload = (function() { | |
| 179 var canvas = document.createElement('canvas'); | |
| 180 var context = canvas.getContext('2d'); | |
| 181 canvas.width = columns * cellWidth; | |
| 182 canvas.height = rows * cellHeight; | |
| 183 context.drawImage(imgElement, 0, 0, canvas.width, canvas.height); | |
| 184 var imageData = context.getImageData(0, 0, canvas.width, canvas.height); | |
| 185 var data = imageData.data; | |
| 186 var outputData = []; | |
| 187 | |
| 188 // Convert image to black and white by thresholding the luminance for | |
| 189 // all opaque (non-transparent) pixels. | |
| 190 for (var i = 0; i < data.length; i += 4) { | |
| 191 var red = data[i]; | |
| 192 var green = data[i + 1]; | |
| 193 var blue = data[i + 2]; | |
| 194 var alpha = data[i + 3]; | |
| 195 var luminance = | |
| 196 0.2126 * red + | |
| 197 0.7152 * green + | |
| 198 0.0722 * blue; | |
| 199 // Show braille pin if the alpha is greater than the threshold and | |
| 200 // the luminance is less than the threshold. | |
| 201 var show = (alpha >= cvox.BrailleDisplayManager.ALPHA_THRESHOLD_ && | |
| 202 luminance < cvox.BrailleDisplayManager.LUMINANCE_THRESHOLD_); | |
| 203 outputData.push(show); | |
| 204 } | |
| 205 | |
| 206 // Convert black-and-white array to the proper encoding for Braille cells. | |
| 207 var brailleBuf = new ArrayBuffer(rows * columns); | |
| 208 var view = new Uint8Array(brailleBuf); | |
| 209 for (var i = 0; i < rows; i++) { | |
| 210 for (var j = 0; j < columns; j++) { | |
| 211 // Index in braille array | |
| 212 var brailleIndex = i * columns + j; | |
| 213 for (var x = 0; x < cellWidth; x++) { | |
|
David Tseng
2017/02/27 16:15:49
Rename x to cellColumn
dmazzoni
2017/02/28 05:08:23
Done.
| |
| 214 for (var y = 0; y < cellHeight; y++) { | |
|
David Tseng
2017/02/27 16:15:49
y is a cellRow
dmazzoni
2017/02/28 05:08:23
Done.
| |
| 215 var bitmapIndex = | |
| 216 (i * columns * cellHeight + j + y * columns) * cellWidth + x; | |
| 217 if (outputData[bitmapIndex]) { | |
| 218 view[brailleIndex] += | |
| 219 cvox.BrailleDisplayManager.COORDS_TO_BRAILLE_DOT_[ | |
| 220 x * maxCellHeight + y]; | |
| 221 } | |
| 222 } | |
| 223 } | |
| 224 } | |
| 225 } | |
| 226 | |
| 227 if (this.realDisplayState_.available) { | |
| 228 chrome.brailleDisplayPrivate.writeDots( | |
| 229 brailleBuf, | |
| 230 this.displayState_.textColumnCount, | |
| 231 this.displayState_.textRowCount); | |
| 232 } | |
| 233 if (cvox.BrailleCaptionsBackground.isEnabled()) { | |
| 234 cvox.BrailleCaptionsBackground.setImageContent( | |
| 235 brailleBuf, rows, columns); | |
| 236 } | |
| 237 }).bind(this); | |
| 238 }; | |
| 239 | |
| 240 | |
| 241 /** | |
| 126 * Sets the command listener. When a command is invoked, the listener will be | 242 * Sets the command listener. When a command is invoked, the listener will be |
| 127 * called with the BrailleKeyEvent corresponding to the command and the content | 243 * called with the BrailleKeyEvent corresponding to the command and the content |
| 128 * that was present on the display when the command was invoked. The content | 244 * that was present on the display when the command was invoked. The content |
| 129 * is guaranteed to be identical to an object previously used as the parameter | 245 * is guaranteed to be identical to an object previously used as the parameter |
| 130 * to cvox.BrailleDisplayManager.setContent, or null if no content was set. | 246 * to cvox.BrailleDisplayManager.setContent, or null if no content was set. |
| 131 * @param {function(!cvox.BrailleKeyEvent, !cvox.NavBraille)} func The listener. | 247 * @param {function(!cvox.BrailleKeyEvent, !cvox.NavBraille)} func The listener. |
| 132 */ | 248 */ |
| 133 cvox.BrailleDisplayManager.prototype.setCommandListener = function(func) { | 249 cvox.BrailleDisplayManager.prototype.setCommandListener = function(func) { |
| 134 this.commandListener_ = func; | 250 this.commandListener_ = func; |
| 135 }; | 251 }; |
| 136 | 252 |
| 137 | 253 |
| 138 /** | 254 /** |
| 139 * @param {!cvox.BrailleDisplayState} newState Display state reported | 255 * @return {!cvox.BrailleDisplayState} The current display state. |
| 140 * by the extension API. | 256 */ |
| 257 cvox.BrailleDisplayManager.prototype.getDisplayState = function() { | |
| 258 return this.displayState_; | |
| 259 }; | |
| 260 | |
| 261 | |
| 262 /** | |
| 263 * @param {{available: boolean, textRowCount: (number|undefined), | |
| 264 * textColumnCount: (number|undefined)}} newState Display state reported | |
| 265 * by the extension API. Note that the type is almost the same as | |
| 266 * cvox.BrailleDisplayState except that the extension API allows | |
| 267 * some fields to be undefined, while cvox.BrailleDisplayState does not. | |
| 141 * @private | 268 * @private |
| 142 */ | 269 */ |
| 143 cvox.BrailleDisplayManager.prototype.refreshDisplayState_ = function( | 270 cvox.BrailleDisplayManager.prototype.refreshDisplayState_ = function( |
| 144 newState) { | 271 newState) { |
| 145 var oldColumnCount = this.displayState_.textColumnCount || 0; | 272 var oldColumnCount = this.displayState_.textColumnCount || 0; |
| 146 var oldRowCount = this.displayState_.textRowCount || 0; | 273 var oldRowCount = this.displayState_.textRowCount || 0; |
| 147 var processDisplayState = function(displayState) { | 274 var processDisplayState = function(displayState) { |
| 148 this.displayState_ = displayState; | 275 this.displayState_ = displayState; |
| 149 var newColumnCount = displayState.textColumnCount || 0; | 276 var newColumnCount = displayState.textColumnCount || 0; |
| 150 var newRowCount = displayState.textRowCount || 0; | 277 var newRowCount = displayState.textRowCount || 0; |
| 151 | 278 |
| 152 if (oldColumnCount != newColumnCount || oldRowCount != newRowCount) { | 279 if (oldColumnCount != newColumnCount || oldRowCount != newRowCount) { |
| 153 this.panStrategy_.setDisplaySize(newRowCount, newColumnCount); | 280 this.panStrategy_.setDisplaySize(newRowCount, newColumnCount); |
| 154 } | 281 } |
| 155 this.refresh_(); | 282 this.refresh_(); |
| 156 }.bind(this); | 283 }.bind(this); |
| 157 this.realDisplayState_ = newState; | 284 this.realDisplayState_ = { |
| 285 available: newState.available, | |
| 286 textRowCount: newState.textRowCount || 0, | |
| 287 textColumnCount: newState.textColumnCount || 0 | |
| 288 }; | |
| 158 if (newState.available) { | 289 if (newState.available) { |
| 159 processDisplayState(newState); | 290 processDisplayState(newState); |
| 160 // Update the dimensions of the virtual braille captions display to those | 291 // Update the dimensions of the virtual braille captions display to those |
| 161 // of a real physical display when one is plugged in. | 292 // of a real physical display when one is plugged in. |
| 162 } else { | 293 } else { |
| 163 cvox.BrailleCaptionsBackground.getVirtualDisplayState(processDisplayState); | 294 cvox.BrailleCaptionsBackground.getVirtualDisplayState(processDisplayState); |
| 164 } | 295 } |
| 165 }; | 296 }; |
| 166 | 297 |
| 167 | 298 |
| (...skipping 200 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 368 }; | 499 }; |
| 369 | 500 |
| 370 /** | 501 /** |
| 371 * @param {boolean} wordWrap | 502 * @param {boolean} wordWrap |
| 372 * @private | 503 * @private |
| 373 */ | 504 */ |
| 374 cvox.BrailleDisplayManager.prototype.updatePanStrategy_ = function(wordWrap) { | 505 cvox.BrailleDisplayManager.prototype.updatePanStrategy_ = function(wordWrap) { |
| 375 this.panStrategy_.setPanStrategy(wordWrap); | 506 this.panStrategy_.setPanStrategy(wordWrap); |
| 376 this.refresh_(); | 507 this.refresh_(); |
| 377 }; | 508 }; |
| OLD | NEW |