0
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

ゲーム翻訳ツール

Last updated at Posted at 2021-12-28
    サンプルコード
GAS
function doPost(e) {
	var a = [e.postData.getDataAsString(),'en','ja']
	return ContentService.createTextOutput(LanguageApp.translate(...a))
}
MainWindow.xaml.cs
namespace Konjac{
	public partial class MainWindow : Window{
		public MainWindow(){
			InitializeComponent();
			webView.NavigationCompleted += (s, e) => {
				webView.CoreWebView2.AddHostObjectToScript("bridge", new Bridge(webView));
			};
			webView.Source = new Uri(GetPath() + "\\Konjac.html");
		}
		private static string GetPath(){
			var p = System.Diagnostics.Process.GetCurrentProcess();
			return System.IO.Path.GetDirectoryName(p.MainModule.FileName);
		}
	}
}
Bridge.cs
public class Native{
	// DPI (PerMonitorV2)
	//https://msdn.microsoft.com/en-us/library/windows/desktop/dd145062(v=vs.85).aspx
	//https://msdn.microsoft.com/en-us/library/windows/desktop/dn280510(v=vs.85).aspx
	public const int WM_DPICHANGED = 0x02E0;
	[DllImport("User32.dll")]
	public static extern IntPtr MonitorFromPoint([In] Point pt, [In] uint dwFlags);
	[DllImport("Shcore.dll")]
	public static extern IntPtr GetDpiForMonitor([In] IntPtr hmonitor, [In] DpiType dpiType, [Out] out uint dpiX, [Out] out uint dpiY);
	public enum DpiType{
		Effective = 0,
		Angular = 1,
		Raw = 2,
	}
}
public class ScreenData{
	public uint dpi { get; set; }
	public Screen screen { get; set; }
	public ScreenData(Screen s){
		var p = new Point(s.Bounds.Left + 1, s.Bounds.Top + 1);
		var m = Native.MonitorFromPoint(p, 2/*MONITOR_DEFAULTTONEAREST*/);
		uint x, y;
		Native.GetDpiForMonitor(m, Native.DpiType.Effective, out x, out y);
		screen = s;
		dpi = x;
	}
}
public class BridgeEvent{
	public string type { get; set; }
	public string value { get; set; }
	public BridgeEvent(string t, string v){
		type = t;
		value = v;
	}
	public string ToJson(){
		return JsonSerializer.Serialize(this);
	}
}

[ComVisible(true)]
public class Bridge{
	Bitmap capture;
	WebView2 view;
	public Bridge(WebView2 w){
		view = w;
	}
	public async void Scan(int x, int y, int w, int h){
		Bitmap textBmp = new Bitmap(w, h);
		Graphics g = Graphics.FromImage(textBmp);
		g.DrawImage(capture, new Point(-x, -y));
		g.Dispose();
		string file = "Konjac.png";
		string path = $"{GetPath()}\\{file}";
		textBmp.Save(path);
		string s = await OCR(file);
		Console.WriteLine(s);
		var e = new BridgeEvent("SCAN_DONE", s);

		//jsからの非同期呼び出しでnullが返る
		//https://github.com/MicrosoftEdge/WebView2Feedback/issues/822
		view.CoreWebView2.PostWebMessageAsString(e.ToJson());
	}
	public async Task<string> OCR(string file){
		//https://water2litter.net/rye/post/c_ocr_hello/

		Stream input_stream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read);
		var source = new WriteableBitmap(System.Windows.Media.Imaging.BitmapFrame.Create(input_stream));
		input_stream.Close();

		PngBitmapEncoder encoder = new PngBitmapEncoder();
		encoder.Frames.Add(System.Windows.Media.Imaging.BitmapFrame.Create(source));
		MemoryStream temp_stream = new MemoryStream();
		encoder.Save(temp_stream);

		var converted_stream = WindowsRuntimeStreamExtensions.AsRandomAccessStream(temp_stream);

		var decorder = await Windows.Graphics.Imaging.BitmapDecoder.CreateAsync(converted_stream);
		SoftwareBitmap bitmap = await decorder.GetSoftwareBitmapAsync();
		converted_stream.Dispose();
		temp_stream.Close();

		OcrEngine engine = OcrEngine.TryCreateFromLanguage(new Windows.Globalization.Language("en"));
		var result = await engine.RecognizeAsync(bitmap);
		return result.Text;
	}
	public string Post(string url, string data){
		//https://docs.microsoft.com/ja-jp/dotnet/framework/network-programming/how-to-send-data-using-the-webrequest-class

		Console.WriteLine(url);
		byte[] byteArray = Encoding.UTF8.GetBytes(data);
		var request = (HttpWebRequest)WebRequest.Create(url);
		request.Method = "POST";
		request.ContentType = "application/x-www-form-urlencoded";
		request.ContentLength = byteArray.Length;
		Stream dataStream = request.GetRequestStream();
		dataStream.Write(byteArray, 0, byteArray.Length);
		dataStream.Close();

		var response = (HttpWebResponse)request.GetResponse();
		string responseContent = string.Empty;
		using (dataStream = response.GetResponseStream())
		{
			StreamReader reader = new StreamReader(dataStream);
			string responseFromServer = reader.ReadToEnd();
			Console.WriteLine(responseFromServer);
			response.Close();
			return responseFromServer;
		}
	}
	public string CaptureScreen(int i){
		Screen s = Screen.AllScreens[i];
		capture = new Bitmap(s.Bounds.Width, s.Bounds.Height);
		Graphics g = Graphics.FromImage(capture);
		g.CopyFromScreen(new Point(s.Bounds.X, s.Bounds.Y), new Point(0, 0), capture.Size);
		g.Dispose();
		string file = $"Screen{i}.png";
		string path = $"{GetPath()}\\{file}";
		Console.WriteLine(path);
		capture.Save(path);
		return file;
	}
	private static string GetPath(){
		var p = System.Diagnostics.Process.GetCurrentProcess();
		return Path.GetDirectoryName(p.MainModule.FileName);
	}
	public string getAllScreens(){
		List<ScreenData> screens = new List<ScreenData>();
		foreach (Screen s in Screen.AllScreens){
			screens.Add(new ScreenData(s));
		}
		return JsonSerializer.Serialize(screens);
	}
}
Konjac.html
<!DOCTYPE html><html lang="ja"><head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script>

DEPLOY_ID = '***************************' //GAS

$(e=>{$('body').append(new Konjac().view)})

class Konjac{
	constructor(){
		Konjac.native = new Konjac.Bridge().ready(v=>{
			v.getScreens(r=>{
				this.item = r.map((v,i)=>new Konjac.Screen(v,i))
				this.init()
			})
		})
		this.view = _html('.container')
	}
	init(){
		this.currentItem = this.item[0]
		var screens = this.item.sort((a,b)=>{return a.x-b.x})
		screens.forEach(v=>v.menuItem.click(e=>this.select(v)))
		
		this.view.append(
			_html('.content',screens.map(v=>v.view)),
			this.overlay = _html('.overlay').hide().click(e=>this.overlay.hide()),
			_html('.header',
				_html('ul',screens.map(v=>v.menuItem)),
				_html('.button,翻訳する').click(e=>{
					this.overlay.empty().show()
					this.ocr()
				})
			),
		)
		Konjac.native.hook.SCAN_DONE = e=>{
			this.overlay.append(_html('.en',e.value))
			this.translate(e.value,ja=>{
				this.overlay.prepend(_html('.ja',ja))
			})
		}
		var g = new Konjac.Gamepad({
			GAMEPAD_A:e=>this.rescan(),
			GAMEPAD_B:e=>{this.overlay.hide()}
		})
		this.select(this.currentItem)
	}
	rescan(){
		this.currentItem.capture(r=>{
			this.overlay.empty().show()
			this.ocr()
		})
	}
	select(item){
		this.overlay.hide()
		this.currentItem = item
		this.item.forEach(v=>{
			v.menuItem.removeClass('selected')
			v.view.hide()
		})
		item.menuItem.addClass('selected')
		item.view.show()
		item.capture()
	}
	ocr(){
		var dpr = this.currentItem.dpr
		var w = $(window)
		var rect = [	
			Math.round(w.scrollLeft()*dpr),
			Math.round(w.scrollTop()*dpr),
			Math.round(w.width()*dpr),
			Math.round((w.height()-$('.header').height())*dpr),
		]
		Konjac.native.ocr(...rect)
		return this
	}
	translate(data,success){
		var url = `https://script.google.com/macros/s/${DEPLOY_ID}/exec`;
		Konjac.native.post(url,data,v=>success(v))
		return this
	}
}

Konjac.Gamepad = class KonjacGamepad{
	constructor(hook){
		var s = 'A,B,X,Y,LB,RB,LT,RT,BACK,START,LS,RS,UP,DOWN,LEFT,RIGHT,HOME'
		this.types	= s.split(',').map(s=>'GAMEPAD_'+s)
		this.hook	= hook
		this.stack	= []
		this.length	= 0
		window.addEventListener("gamepadconnected",e=>{
			console.log(e)
			this.stack[e.gamepad.index] = [e.gamepad]
			this.length++
		});
		window.addEventListener("gamepaddisconnected",e=>{
			console.log(e)
			this.stack[e.gamepad.index] = []
			this.length--
		});
		setInterval(e=>this.loop(),1000/30)
	}
	loop(){
		if(this.length==0) return
		var items = [...navigator.getGamepads()].filter(Boolean)
		if(items.length==0) return
		var a = this.stack[items[0].index]
		a.unshift(items[0])
		a[0].buttons.forEach((b,i)=>{
			if(b.pressed==true) return
			if(a[1].buttons[i].pressed==false) return
			this.dispatch(this.types[i])
		})
		this.stack[items[0].index] = a.slice(0,1)
	}
	dispatch(type){
		console.log(type)
		if(this.hook[type]) this.hook[type]()
	}
}

Konjac.Screen = class KonjacScreen{
	constructor(data,i){
		var b = data.screen.Bounds
		var dpr = data.dpi/96
		this.x	= b.Left
		this.y	= b.Top
		this.w	= b.Width
		this.h	= b.Height
		this.width	= Math.round(b.Width/dpr)
		this.height	= Math.round(b.Height/dpr)
		this.dpr 	= dpr//devicePixelRatio
		this.name	= data.screen.DeviceName.replace(/[^\w\s]/g,'')
		this.index	= i
		Object.assign(this,data)
		var s = b.Width+' x '+b.Height
		this.sizeText = Konjac.Screen.type[s]||s
		this.menuItem = _html('li',this.name,' ',_html('span',this.sizeText))
		this.view = _html('img').css({width:this.width})
	}
	capture(f){
		Konjac.native.capture(this.index,r=>{
			this.view.attr('src',r+'?'+Math.random())
			if(f) f(r)
		})
		return this
	}
}
Konjac.Screen.type = {
	'1920 x 1080':'FHD',
	'1920 x 1200':'WUXGA',
	'2560 x 1440':'WQHD',
	'3840 x 2160':'4K',
}

Konjac.Bridge = class KonjacBridge{
	constructor(){
		this.hook = {}
		this.on(()=>{}).ready(()=>{})
		this.hostObjects = {bridge:{}}
		if(chrome.webview){
			this.hostObjects = chrome.webview.hostObjects
			chrome.webview.addEventListener('message',e=>{
				var data = JSON.parse(e.data)
				var hook = this.hook[data.type]||this.hook.on
				hook(data)
			})
		}
		setTimeout(e=>this.hook.ready(this),100)
	}
	on(f){this.hook.on = f
		return this
	}
	ready(f){this.hook.ready = f
		return this
	}
	getScreens(f){
		this.hostObjects.bridge.getAllScreens().then(r=>{
			f(JSON.parse(r))
		}).catch(console.log)
		return this
	}
	capture(i,f){
		this.hostObjects.bridge.CaptureScreen(i).then(r=>f(r))
		return this
	}
	ocr(...a){this.hostObjects.bridge.Scan(...a)
		return this
	}
	async post(url,data,success) {
    	var result = await this.hostObjects.bridge.Post(url,data);
    	success(result)
	}
}

function _html(...a){
	var b = a.shift().split(',')
	var c = b.shift().split('.')
	var d = c.shift()||'div'
	return $(`<${d}>`).addClass(...c).append(...b,...a)
}

</script>
<style>
/* 略 */
</style></head></html>
0
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?