Skip to content

Commit

Permalink
1.3-night.rev2
Browse files Browse the repository at this point in the history
* Lands OCR support so closes #22
  • Loading branch information
Noitidart committed Jan 7, 2016
1 parent b4d56dd commit 1bf4c0d
Show file tree
Hide file tree
Showing 7 changed files with 6,226 additions and 2 deletions.
2 changes: 2 additions & 0 deletions amo-reviewer.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ocrad.js - https://github.com/antimatter15/ocrad.js/blob/5b0af624ebfd70cf45ddf55c58eaf25718133ba3/ocrad.js
gocr.js - https://github.com/antimatter15/gocr.js/tree/d820e0651cf819e9649a837d83125724a2c1cc37
247 changes: 245 additions & 2 deletions bootstrap.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
const {classes: Cc, interfaces: Ci, manager: Cm, results: Cr, utils: Cu, Constructor: CC} = Components;
Cm.QueryInterface(Ci.nsIComponentRegistrar);

const { BasePromiseWorker } = Cu.import('resource://gre/modules/PromiseWorker.jsm', {});
const PromiseWorker = Cu.import('resource://gre/modules/PromiseWorker.jsm').BasePromiseWorker;
Cu.import('resource:///modules/CustomizableUI.jsm');

Cu.import('resource://gre/modules/ctypes.jsm');
Expand Down Expand Up @@ -269,6 +269,13 @@ function get_gEMenuDomJson() {
['xul:menuitem', {label:myServices.sb.GetStringFromName('editor-menu_google-images'), oncommand:function(e){ gEditor.reverseSearch(e, 1) }}]
]
],
['xul:menu', {label:myServices.sb.GetStringFromName('editor-menu_ocr')},
['xul:menupopup', {},
['xul:menuitem', {label:myServices.sb.GetStringFromName('editor-menu_gocr'), oncommand:function(e){ gEditor.ocr(e, 'gocr') }}],
['xul:menuitem', {label:myServices.sb.GetStringFromName('editor-menu_ocrad'), oncommand:function(e){ gEditor.ocr(e, 'ocrad') }}],
['xul:menuitem', {label:myServices.sb.GetStringFromName('editor-menu_ocr-all'), oncommand:function(e){ gEditor.ocr(e, 'all') }}]
]
],
['xul:menuseparator', {}],
['xul:menuitem', {label:myServices.sb.GetStringFromName('editor-menu_select-clear'), oncommand:function(e){ gEditor.clearSelection(e) }}],
['xul:menu', {label:myServices.sb.GetStringFromName('editor-menu_select-fullscreen')},
Expand Down Expand Up @@ -1739,9 +1746,101 @@ var gEditor = {
appendToHistoryLog(serviceTypeStr, {
d: new Date().getTime()
});
},
ocr: function(e, serviceTypeStr) {
// serviceTypeStr valid values
// gocr
// ocrad
// all



this.compositeSelection();

var cDOMWindow = gEditor.gBrowserDOMWindow;
var cWidth = gEditor.canComp.width;
var cHeight = gEditor.canComp.height;

var serviceTypeFunc = {
gocr: function(aByteArr, dontTransfer) {
if (!bootstrap.GOCRWorker) {
bootstrap.GOCRWorker = new PromiseWorker(core.addon.path.content + 'modules/gocr/GOCRWorker.js');
}

return GOCRWorker.post('readByteArr', [aByteArr, cWidth, cHeight], null, dontTransfer ? undefined : [aByteArr]);
},
ocrad: function(aByteArr, dontTransfer) {
if (!bootstrap.OCRADWorker) {
bootstrap.OCRADWorker = new PromiseWorker(core.addon.path.content + 'modules/ocrad/OCRADWorker.js');
}

return OCRADWorker.post('readByteArr', [aByteArr, cWidth, cHeight], null, dontTransfer ? undefined : [aByteArr]);
}
};

var cImgData = this.ctxComp.getImageData(0, 0, this.canComp.width, this.canComp.height);
console.log('cImgData:', cImgData);
gEditor.closeOutEditor(e);

var promiseAllArr_ocr = [];
var allArr_serviceTypeStr = [];
if (serviceTypeStr == 'all') {
for (var p in serviceTypeFunc) {
promiseAllArr_ocr.push(serviceTypeFunc[p](cImgData.data.buffer, true));
allArr_serviceTypeStr.push(p.toUpperCase());
}
} else {
promiseAllArr_ocr.push(serviceTypeFunc[serviceTypeStr](cImgData.data.buffer));
allArr_serviceTypeStr.push(serviceTypeStr);
}

var promiseAll_ocr = Promise.all(promiseAllArr_ocr);
promiseAll_ocr.then(
function(aTxtArr) {
console.log('Fullfilled - promiseAll_ocr - ', aTxtArr);
cImgData = undefined; // when do all, we dont transfer, so it doesnt get neutered, so lets just do this, it might help it gc
var alertStrArr = [];
for (var i=0; i<allArr_serviceTypeStr.length; i++) {
if (allArr_serviceTypeStr.length > 1) {
alertStrArr.push(allArr_serviceTypeStr[i] + ':');
alertStrArr.push();
alertStrArr.push();
}
alertStrArr.push(aTxtArr[i]);
}
var alertStr = alertStrArr.join('\n');
Services.prompt.alert(cDOMWindow, 'NativeShot - OCR Results', alertStr); // :todo: hook this up to the notif-bar system once i rework it
if (allArr_serviceTypeStr.length == 1) {
copyTextToClip(alertStr, cDOMWindow);
} else {
var choices = ['All'];
for (var i=0; i<allArr_serviceTypeStr.length; i++) {
choices.push(allArr_serviceTypeStr[i]);
}
var selectedChoiceIndex = {};
var rez_select = Services.prompt.select(cDOMWindow, 'NativeShot - Copy to Clipboard', 'Select which result to copy to clipboard:', choices.length, choices, selectedChoiceIndex)
if (rez_select) {
console.log('selectedChoiceIndex:', selectedChoiceIndex); // this is ```Object { value: 2 }```
if (selectedChoiceIndex.value == 0) {
copyTextToClip(alertStr, cDOMWindow);
console.log('copied all to clip:', alertStr);
} else {
copyTextToClip(aTxtArr[selectedChoiceIndex.value - 1], cDOMWindow);
console.log('copied just ', selectedChoiceIndex.value, ' to clip:', aTxtArr[selectedChoiceIndex.value - 1]);
}
}
}
},
genericReject.bind(null, 'promiseAll_ocr', 0)
).catch(genericCatch.bind(null, 'promiseAll_ocr', 0));

// :todo: appendToHistoryLog
}
};

var OCRADWorkerFuncs = {};
var GOCRWorkerFuncs = {};

function gEMouseMove(e) {
var iMon; //var iMon = gIMonMouseDownedIn; //parseInt(e.view.location.search.substr('?iMon='.length)); // cant do this as on mouse move, user may not be over iMon they started in, so have to calc it
var screenPoint = new Rect(e.screenX, e.screenY, 1, 1);
Expand Down Expand Up @@ -2948,6 +3047,13 @@ function shutdown(aData, aReason) {
// destroy worker
MainWorker._worker.terminate();

if (bootstrap.GOCRWorker) {
GOCRWorker._worker.terminate();
}

if (bootstrap.OCRADWorker) {
OCRADWorker._worker.terminate();
}
}

function getPrefNoSetStuff(aPrefName) {
Expand Down Expand Up @@ -3302,7 +3408,7 @@ function SIPWorker(workerScopeName, aPath, aCore=core) {
var deferredMain_SIPWorker = new Deferred();

if (!(workerScopeName in bootstrap)) {
bootstrap[workerScopeName] = new BasePromiseWorker(aPath);
bootstrap[workerScopeName] = new PromiseWorker(aPath);

if ('addon' in aCore && 'aData' in aCore.addon) {
delete aCore.addon.aData; // we delete this because it has nsIFile and other crap it, but maybe in future if I need this I can try JSON.stringify'ing it
Expand Down Expand Up @@ -3336,6 +3442,123 @@ function SIPWorker(workerScopeName, aPath, aCore=core) {
return deferredMain_SIPWorker.promise;

}

// SICWorker - rev8 - https://gist.github.com/Noitidart/6a9da3589e88cc3df7e7
const SIC_CB_PREFIX = '_a_gen_cb_';
const SIC_TRANS_WORD = '_a_gen_trans_';
var sic_last_cb_id = -1;
function SICWorker(workerScopeName, aPath, aFuncExecScope=bootstrap, aCore=core) {
// creates a global variable in bootstrap named workerScopeName which will hold worker, do not set up a global for it like var Blah; as then this will think something exists there
// aScope is the scope in which the functions are to be executed
// ChromeWorker must listen to a message of 'init' and on success of it, it should sendMessage back saying aMsgEvent.data == {aTopic:'init', aReturn:true}
// "Start and Initialize ChromeWorker" // based on SIPWorker
// returns promise
// resolve value: jsBool true
// aCore is what you want aCore to be populated with
// aPath is something like `core.addon.path.content + 'modules/workers/blah-blah.js'`
var deferredMain_SICWorker = new Deferred();

if (!(workerScopeName in bootstrap)) {
bootstrap[workerScopeName] = new ChromeWorker(aPath);

if ('addon' in aCore && 'aData' in aCore.addon) {
delete aCore.addon.aData; // we delete this because it has nsIFile and other crap it, but maybe in future if I need this I can try JSON.stringify'ing it
}

var afterInitListener = function(aMsgEvent) {
// note:all msgs from bootstrap must be postMessage([nameOfFuncInWorker, arg1, ...])
var aMsgEventData = aMsgEvent.data;
console.log('mainthread receiving message:', aMsgEventData);

// postMessageWithCallback from worker to mt. so worker can setup callbacks after having mt do some work
var callbackPendingId;
if (typeof aMsgEventData[aMsgEventData.length-1] == 'string' && aMsgEventData[aMsgEventData.length-1].indexOf(SIC_CB_PREFIX) == 0) {
callbackPendingId = aMsgEventData.pop();
}

var funcName = aMsgEventData.shift();

if (funcName in aFuncExecScope) {
var rez_mainthread_call = aFuncExecScope[funcName].apply(null, aMsgEventData);

if (callbackPendingId) {
if (rez_mainthread_call.constructor.name == 'Promise') {
rez_mainthread_call.then(
function(aVal) {
if (aVal.length >= 2 && aVal[aVal.length-1] == SIC_TRANS_WORD && Array.isArray(aVal[aVal.length-2])) {
// to transfer in callback, set last element in arr to SIC_TRANS_WORD and 2nd to last element an array of the transferables // cannot transfer on promise reject, well can, but i didnt set it up as probably makes sense not to
console.error('doing transferrrrr');
aVal.pop();
bootstrap[workerScopeName].postMessage([callbackPendingId, aVal], aVal.pop());
} else {
bootstrap[workerScopeName].postMessage([callbackPendingId, aVal]);
}
},
function(aReason) {
console.error('aReject:', aReason);
bootstrap[workerScopeName].postMessage([callbackPendingId, ['promise_rejected', aReason]]);
}
).catch(
function(aCatch) {
console.error('aCatch:', aCatch);
bootstrap[workerScopeName].postMessage([callbackPendingId, ['promise_rejected', aCatch]]);
}
);
} else {
// assume array
if (rez_mainthread_call.length > 2 && rez_mainthread_call[rez_mainthread_call.length-1] == SIC_TRANS_WORD && Array.isArray(rez_mainthread_call[rez_mainthread_call.length-2])) {
// to transfer in callback, set last element in arr to SIC_TRANS_WORD and 2nd to last element an array of the transferables // cannot transfer on promise reject, well can, but i didnt set it up as probably makes sense not to
rez_mainthread_call.pop();
bootstrap[workerScopeName].postMessage([callbackPendingId, rez_mainthread_call], rez_mainthread_call.pop());
} else {
bootstrap[workerScopeName].postMessage([callbackPendingId, rez_mainthread_call]);
}
}
}
}
else { console.warn('funcName', funcName, 'not in scope of aFuncExecScope') } // else is intentionally on same line with console. so on finde replace all console. lines on release it will take this out

};

var beforeInitListener = function(aMsgEvent) {
// note:all msgs from bootstrap must be postMessage([nameOfFuncInWorker, arg1, ...])
var aMsgEventData = aMsgEvent.data;
if (aMsgEventData[0] == 'init') {
bootstrap[workerScopeName].removeEventListener('message', beforeInitListener);
bootstrap[workerScopeName].addEventListener('message', afterInitListener);
deferredMain_SICWorker.resolve(true);
if ('init' in aFuncExecScope) {
aFuncExecScope[aMsgEventData.shift()].apply(null, aMsgEventData);
}
}
};

// var lastCallbackId = -1; // dont do this, in case multi SICWorker's are sharing the same aFuncExecScope so now using new Date().getTime() in its place // link8888881
bootstrap[workerScopeName].postMessageWithCallback = function(aPostMessageArr, aCB, aPostMessageTransferList) {
// lastCallbackId++; // link8888881
sic_last_cb_id++;
var thisCallbackId = SIC_CB_PREFIX + sic_last_cb_id; // + lastCallbackId; // link8888881
aFuncExecScope[thisCallbackId] = function() {
delete aFuncExecScope[thisCallbackId];
// console.log('in mainthread callback trigger wrap, will apply aCB with these arguments:', arguments, 'turned into array:', Array.prototype.slice.call(arguments));
aCB.apply(null, arguments[0]);
};
aPostMessageArr.push(thisCallbackId);
// console.log('aPostMessageArr:', aPostMessageArr);
bootstrap[workerScopeName].postMessage(aPostMessageArr, aPostMessageTransferList);
};

bootstrap[workerScopeName].addEventListener('message', beforeInitListener);
bootstrap[workerScopeName].postMessage(['init', aCore]);

} else {
deferredMain_SICWorker.reject('Something is loaded into bootstrap[workerScopeName] already');
}

return deferredMain_SICWorker.promise;

}

function jsonToDOM(json, doc, nodes) {

var namespaces = {
Expand Down Expand Up @@ -3870,4 +4093,24 @@ function encodeFormData(data, charset, forArrBuf_nameDotExt, forArrBuf_mimeType)

return postStream;
}
function genericReject(aPromiseName, aPromiseToReject, aReason) {
var rejObj = {
name: aPromiseName,
aReason: aReason
};
console.error('Rejected - ' + aPromiseName + ' - ', rejObj);
if (aPromiseToReject) {
aPromiseToReject.reject(rejObj);
}
}
function genericCatch(aPromiseName, aPromiseToReject, aCaught) {
var rejObj = {
name: aPromiseName,
aCaught: aCaught
};
console.error('Caught - ' + aPromiseName + ' - ', rejObj);
if (aPromiseToReject) {
aPromiseToReject.reject(rejObj);
}
}
// end - common helper functions
5 changes: 5 additions & 0 deletions locale/en-US/bootstrap.properties
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ editor-menu_search-reverse=Reverse Image Search
editor-menu_tineye=Tineye
editor-menu_google-images=Google Images

editor-menu_ocr=Text Recognition (OCR)
editor-menu_ocrad=OCRAD
editor-menu_gocr=GOCR
editor-menu_ocr-all=All Methods

screenshot=Screenshot
failed-upload=Failed Upload
filepicker-title-save-screenshot=Save Screenshot to File As
Expand Down
35 changes: 35 additions & 0 deletions modules/gocr/GOCRWorker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
importScripts('resource://gre/modules/workers/require.js');
importScripts('chrome://nativeshot/content/modules/gocr/gocr.js');

// Setup PromiseWorker
var PromiseWorker = require('resource://gre/modules/workers/PromiseWorker.js');

// Instantiate AbstractWorker (see below).
var worker = new PromiseWorker.AbstractWorker()

worker.dispatch = function(method, args = []) {
// Dispatch a call to method `method` with args `args`
return self[method](...args);
};
worker.postMessage = function(...args) {
// Post a message to the main thread
self.postMessage(...args);
};
worker.close = function() {
// Close the worker
self.close();
};
worker.log = function(...args) {
// Log (or discard) messages (optional)
dump('Worker: ' + args.join(' ') + '\n');
};

// Connect it to message port.
self.addEventListener('message', msg => worker.handleMessage(msg));

function readByteArr(aImgBuf, aWidth, aHeight) {
console.info('GOCR aImgBuf:', aImgBuf, 'aWidth:', aWidth, 'aHeight:', aHeight, '');
var cImgData = new ImageData(new Uint8ClampedArray(aImgBuf), aWidth, aHeight);
var txt = GOCR(cImgData);
return txt;
}
Loading

0 comments on commit 1bf4c0d

Please sign in to comment.