LoginSignup
0
3

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