This commit is contained in:
恍兮惚兮 2024-10-01 22:58:41 +08:00
parent 7f6518bad8
commit 707644cdfa
35 changed files with 230 additions and 132 deletions

View File

@ -12,6 +12,7 @@ from myutils.config import (
extradatas,
globalconfig,
)
from myutils.hwnd import clipboard_set_image
from myutils.utils import str2rgba, get_time_stamp, loopbackrecorder
from myutils.audioplayer import playonce
from gui.inputdialog import autoinitdialog
@ -468,7 +469,10 @@ class viewpixmap_x(QWidget):
self.pathview.resize(e.size().width(), self.pathview.height())
self.infoview.resize(e.size().width(), self.infoview.height())
self.leftclick.setGeometry(
0, e.size().height() // 10, e.size().width() // 5, 7 * e.size().height() // 10
0,
e.size().height() // 10,
e.size().width() // 5,
7 * e.size().height() // 10,
)
self.bottombtn.setGeometry(
e.size().width() // 5,
@ -581,12 +585,14 @@ class pixwrapper(QWidget):
else:
seticon = LAction(("设为图标"))
deleteimage = LAction(("删除图片"))
copyimage = LAction(("复制图片"))
deleteimage_x = LAction(("删除图片文件"))
hualang = LAction(("画廊"))
pos = LAction(("位置"))
menu.addAction(setimage)
menu.addAction(seticon)
menu.addAction(copyimage)
menu.addAction(deleteimage)
menu.addAction(deleteimage_x)
menu.addAction(hualang)
@ -596,6 +602,8 @@ class pixwrapper(QWidget):
action = menu.exec(QCursor.pos())
if action == deleteimage:
self.removecurrent(False)
elif copyimage == action:
clipboard_set_image(curr)
elif action == deleteimage_x:
self.removecurrent(True)
elif action == pos:

View File

@ -626,7 +626,7 @@ class TranslatorWindow(resizableframeless):
),
("searchwordW", lambda: gobject.baseobject.searchwordW.showsignal.emit()),
("fullscreen", self._fullsgame, lambda: self.isletgamefullscreened, None),
("grabwindow", grabwindow),
("grabwindow", grabwindow, None, None, lambda: grabwindow(tocliponly=True)),
(
"muteprocess",
self.muteprocessfuntion,

View File

@ -5,12 +5,25 @@ import gobject
import os, subprocess, functools
import time, winrtutils, winsharedutils, hashlib
from myutils.config import savehook_new_data, globalconfig
from myutils.wrapper import threader
from myutils.wrapper import threader, tryprint
from myutils.utils import qimage2binary
def clipboard_set_image(p: QImage):
if isinstance(p, str):
qimg = QImage()
qimg.load(p)
p = qimg
if p.isNull():
return
winsharedutils.clipboard_set_image(qimage2binary(p))
@threader
def grabwindow(app="PNG", callback_origin=None):
if callback_origin:
def grabwindow(app="PNG", callback_origin=None, tocliponly=False):
if tocliponly:
pass
elif callback_origin or tocliponly:
fname = gobject.gettempdir(time.strftime("%Y-%m-%d-%H-%M-%S", time.localtime()))
uid = None
else:
@ -42,50 +55,54 @@ def grabwindow(app="PNG", callback_origin=None):
else:
fname = fnamef()
def callback_1(callback, uid, fn):
if not os.path.exists(fn):
def callback_1(callback_origin, uid, tocliponly, p: QPixmap, fn):
if p.isNull():
return
if callback:
callback(os.path.abspath(fn))
if tocliponly:
clipboard_set_image(p)
return
p.save(fn)
if callback_origin:
callback_origin(os.path.abspath(fn))
if uid:
savehook_new_data[uid]["imagepath_all"].append(fn)
callback = functools.partial(callback_1, callback_origin, uid)
hwnd = windows.FindWindow(
"Window_Magpie_967EB565-6F73-4E94-AE53-00CC42592A22", None
)
if hwnd:
@threader
def _():
winrtutils._winrt_capture_window(fname + "_winrt_magpie." + app, hwnd)
callback(fname + "_winrt_magpie." + app)
_()
callback = functools.partial(callback_1, callback_origin, uid, tocliponly)
hwnd = gobject.baseobject.hwnd
if not hwnd:
hwnd = windows.GetForegroundWindow()
hwnd = windows.GetAncestor(hwnd)
_ = windows.GetClientRect(hwnd)
p = screenshot(0, 0, _[2], _[3], hwnd).toImage()
if not p.allGray():
p.save(fname + "_gdi." + app)
callback(fname + "_gdi." + app)
p = gdi_screenshot(0, 0, _[2], _[3], hwnd)
if not callback_origin:
callback(p, fname + "_gdi." + app)
isshit = (not callback_origin) and (not tocliponly)
if (not p.isNull()) or isshit:
@threader
def _():
p = winrt_capture_window(hwnd)
callback(p, fname + "_winrt." + app)
_()
if isshit:
gobject.baseobject.translation_ui.displaystatus.emit(
"saved to " + fname, False, True
)
@threader
def _():
winrtutils._winrt_capture_window(fname + "_winrt." + app, hwnd)
callback(fname + "_winrt." + app)
hwnd = windows.FindWindow(
"Window_Magpie_967EB565-6F73-4E94-AE53-00CC42592A22", None
)
if hwnd:
if p.allGray() or (not callback_origin):
_()
@threader
def _():
p = winrt_capture_window(hwnd)
callback(p, fname + "_winrt_magpie." + app)
_()
def getpidexe(pid):
@ -238,13 +255,27 @@ def mouseselectwindow(callback):
threading.Thread(target=_loop).start()
def screenshot(x1, y1, x2, y2, hwnd=None):
def safepixmap(bs):
if not bs:
return QPixmap()
pixmap = QPixmap()
pixmap.loadFromData(bs)
if pixmap.isNull():
return QPixmap()
if pixmap.toImage().allGray():
return QPixmap()
return pixmap
def gdi_screenshot(x1, y1, x2, y2, hwnd=None):
if hwnd:
_r = QApplication.instance().devicePixelRatio()
_dpi = windows.GetDpiForWindow(hwnd)
x1, y1, x2, y2 = (int(_ * _dpi / 96 / _r) for _ in (x1, y1, x2, y2))
bs = winsharedutils.gdi_screenshot(x1, y1, x2, y2, hwnd)
pixmap = QPixmap()
if bs:
pixmap.loadFromData(bs)
return pixmap
return safepixmap(bs)
def winrt_capture_window(hwnd):
bs = winrtutils.winrt_capture_window(hwnd)
return safepixmap(bs)

View File

@ -3,21 +3,12 @@ import os, importlib
from myutils.config import globalconfig, _TR
from qtsymbols import *
from myutils.commonbase import ArgsEmptyExc
from myutils.hwnd import screenshot
from myutils.utils import stringfyerror
from myutils.hwnd import gdi_screenshot
from myutils.utils import stringfyerror, qimage2binary
from traceback import print_exc
import threading, gobject
def qimage2binary(qimage: QImage, fmt="BMP"):
byte_array = QByteArray()
buffer = QBuffer(byte_array)
buffer.open(QBuffer.WriteOnly)
qimage.save(buffer, fmt)
buffer.close()
image_data = byte_array.data()
return image_data
def binary2qimage(binary):
image = QImage()
@ -44,14 +35,14 @@ def imageCut(hwnd, x1, y1, x2, y2) -> QImage:
QRect(_x1, _y1, _x2 - _x1, _y2 - _y1)
):
continue
pix = screenshot(_x1, _y1, _x2, _y2, hwnd)
if pix.toImage().allGray():
pix = gdi_screenshot(_x1, _y1, _x2, _y2, hwnd)
if pix.isNull():
continue
break
except:
print_exc()
else:
pix = screenshot(x1, y1, x2, y2)
pix = gdi_screenshot(x1, y1, x2, y2)
image = pix.toImage()
gobject.baseobject.maybesetimage(image)

View File

@ -23,6 +23,16 @@ import re, heapq, winsharedutils
from myutils.wrapper import tryprint, threader
def qimage2binary(qimage: QImage, fmt="BMP"):
byte_array = QByteArray()
buffer = QBuffer(byte_array)
buffer.open(QBuffer.OpenModeFlag.WriteOnly)
qimage.save(buffer, fmt)
buffer.close()
image_data = byte_array.data()
return image_data
def checkisusingwine():
iswine = True
try:

View File

@ -6,6 +6,9 @@ from ctypes import (
c_size_t,
CFUNCTYPE,
c_void_p,
cast,
POINTER,
c_char,
)
import platform, gobject
@ -50,4 +53,15 @@ if winrtutilsdll:
return ret
_winrt_capture_window = winrtutilsdll.winrt_capture_window
_winrt_capture_window.argtypes = c_wchar_p, c_void_p
_winrt_capture_window.argtypes = c_void_p, c_void_p
def winrt_capture_window(hwnd):
ret = []
def cb(ptr, size):
ret.append(cast(ptr, POINTER(c_char))[:size])
_winrt_capture_window(hwnd, CFUNCTYPE(None, c_void_p, c_size_t)(cb))
if len(ret):
return ret[0]
return None

View File

@ -61,10 +61,10 @@ _clipboard_get = utilsdll.clipboard_get
_clipboard_get.argtypes = (c_void_p,)
_clipboard_get.restype = c_bool
_clipboard_set = utilsdll.clipboard_set
_clipboard_set.argtypes = (
c_void_p,
c_wchar_p,
)
_clipboard_set.argtypes = (HWND, c_wchar_p)
_clipboard_set_image = utilsdll.clipboard_set_image
_clipboard_set_image.argtypes = (HWND, c_void_p, c_size_t)
_clipboard_set_image.restype = c_bool
def SAPI_List(v):
@ -86,9 +86,8 @@ def SAPI_Speak(content, v, voiceid, rate, volume):
return ret[0]
def distance(
s1, s2
): # 词典更适合用编辑距离,因为就一两个字符,相似度会很小,预翻译适合用相似度
def distance(s1, s2):
# 词典更适合用编辑距离,因为就一两个字符,相似度会很小,预翻译适合用相似度
return _levenshtein_distance(len(s1), s1, len(s2), s2)
@ -101,10 +100,14 @@ clphwnd = windll.user32.CreateWindowExW(0, "STATIC", 0, 0, 0, 0, 0, 0, 0, 0, 0,
def clipboard_set(text):
global clphwnd
# _set_clip_board_queue.put(text)
return _clipboard_set(clphwnd, text)
def clipboard_set_image(bytes_):
global clphwnd
return _clipboard_set_image(clphwnd, bytes_, len(bytes_))
def clipboard_get():
ret = []
if not _clipboard_get(CFUNCTYPE(None, c_wchar_p)(ret.append)):

View File

@ -56,6 +56,7 @@
var htmltabbuttons = ''
var htmlcontents = ''
var scriptElementss = []
var scriptElementsssrc = []
for (var iiii = 0; iiii < dictionaryInfo.length; iiii++) {
htmltabbuttons += '<button type="button" onclick="onclickbtn(\'' + dictionaryInfo[iiii]['dict'] + '\')" id="luna_dict_btn_' + dictionaryInfo[iiii]['dict'] + '" class="tab-button">' + dictionaryInfo[iiii]['name'] + '</button>'
@ -72,12 +73,18 @@
for (var jjjj = 0; jjjj < scriptElements.length; jjjj++) {
scriptElementss.push(scriptElements[jjjj].textContent)
scriptElementsssrc.push(scriptElements[jjjj].src)
}
}
document.getElementById('tab_buttons').innerHTML = htmltabbuttons
document.getElementById('tab_contents').innerHTML = htmlcontents
for (var iiii = 0; iiii < scriptElementss.length; iiii++) {
eval(scriptElementss[iiii])
let newScript = document.createElement('script')
if (scriptElementsssrc[iiii]) {
newScript.src = scriptElementsssrc[iiii];
document.head.appendChild(newScript);
}
}
if (dictionaryInfo.length > 0) {
onclickbtn(dictionaryInfo[0]['dict'])

View File

@ -825,5 +825,6 @@
"按字数过滤或截断": "تصفية أو اقتطاع حسب عدد الكلمات",
"按行数过滤或截断": "تصفية أو اقتطاع حسب عدد الصفوف",
"超过最大字数时截断而非过滤": "اقتطاع عند تجاوز الحد الأقصى لعدد الكلمات بدلا من تصفية",
"超过最大行数时截断而非过滤": "البتر عند تجاوز الحد الأقصى لعدد الصفوف بدلا من الترشيح"
"超过最大行数时截断而非过滤": "البتر عند تجاوز الحد الأقصى لعدد الصفوف بدلا من الترشيح",
"复制图片": "نسخ الصور"
}

View File

@ -825,5 +825,6 @@
"按字数过滤或截断": "按字數過濾或截斷",
"按行数过滤或截断": "按行數過濾或截斷",
"超过最大字数时截断而非过滤": "超過最大字數時截斷而非過濾",
"超过最大行数时截断而非过滤": "超過最大行數時截斷而非過濾"
"超过最大行数时截断而非过滤": "超過最大行數時截斷而非過濾",
"复制图片": "複製圖片"
}

View File

@ -825,5 +825,6 @@
"按字数过滤或截断": "Filtrovat nebo zkrátit podle počtu slov",
"按行数过滤或截断": "Filtrovat nebo zkrátit podle počtu řádků",
"超过最大字数时截断而非过滤": "Při překročení maximálního počtu slov zkrátit místo filtrování",
"超过最大行数时截断而非过滤": "Při překročení maximálního počtu řádků zkrátit namísto filtrování"
"超过最大行数时截断而非过滤": "Při překročení maximálního počtu řádků zkrátit namísto filtrování",
"复制图片": "Kopírovat obrázek"
}

View File

@ -825,5 +825,6 @@
"按字数过滤或截断": "Nach Wortanzahl filtern oder kürzen",
"按行数过滤或截断": "Nach Zeilenanzahl filtern oder kürzen",
"超过最大字数时截断而非过滤": "Kürzen statt Filtern, wenn die maximale Wortanzahl überschritten wird",
"超过最大行数时截断而非过滤": "Kürzen statt Filtern, wenn die maximale Anzahl von Zeilen überschritten wird"
"超过最大行数时截断而非过滤": "Kürzen statt Filtern, wenn die maximale Anzahl von Zeilen überschritten wird",
"复制图片": "Bild kopieren"
}

View File

@ -825,5 +825,6 @@
"按字数过滤或截断": "Filter or truncate by word count",
"按行数过滤或截断": "Filter or truncate by row count",
"超过最大字数时截断而非过滤": "Truncate instead of filtering when exceeding the maximum word count",
"超过最大行数时截断而非过滤": "Truncate instead of filtering when the maximum number of rows is exceeded"
"超过最大行数时截断而非过滤": "Truncate instead of filtering when the maximum number of rows is exceeded",
"复制图片": "Copy image"
}

View File

@ -825,5 +825,6 @@
"按字数过滤或截断": "Filtrar o truncar por número de palabras",
"按行数过滤或截断": "Filtrar o truncar por número de líneas",
"超过最大字数时截断而非过滤": "Cortar en lugar de filtrar cuando se supera el número máximo de palabras",
"超过最大行数时截断而非过滤": "Cortar en lugar de filtrar cuando se supera el número máximo de líneas"
"超过最大行数时截断而非过滤": "Cortar en lugar de filtrar cuando se supera el número máximo de líneas",
"复制图片": "Copiar imagen"
}

View File

@ -825,5 +825,6 @@
"按字数过滤或截断": "Filtrer ou tronquer par nombre de mots",
"按行数过滤或截断": "Filtrer ou tronquer par nombre de lignes",
"超过最大字数时截断而非过滤": "Tronquer au lieu de filtrer lorsque le nombre maximal de mots est dépassé",
"超过最大行数时截断而非过滤": "Tronquer au lieu de filtrer lorsque le nombre maximal de lignes est dépassé"
"超过最大行数时截断而非过滤": "Tronquer au lieu de filtrer lorsque le nombre maximal de lignes est dépassé",
"复制图片": "Copier l'image"
}

View File

@ -825,5 +825,6 @@
"按字数过滤或截断": "Filtra o tronca per numero di parole",
"按行数过滤或截断": "Filtra o tronca per numero di righe",
"超过最大字数时截断而非过滤": "Truncare invece di filtrare quando si supera il numero massimo di parole",
"超过最大行数时截断而非过滤": "Truncare invece di filtrare quando viene superato il numero massimo di righe"
"超过最大行数时截断而非过滤": "Truncare invece di filtrare quando viene superato il numero massimo di righe",
"复制图片": "Copia immagine"
}

View File

@ -825,5 +825,6 @@
"按字数过滤或截断": "文字数によるフィルタまたは切り捨て",
"按行数过滤或截断": "行数によるフィルタまたは切り捨て",
"超过最大字数时截断而非过滤": "最大文字数を超えるとフィルタではなく切り捨て",
"超过最大行数时截断而非过滤": "最大行数を超えるとフィルタではなくトランケートされます"
"超过最大行数时截断而非过滤": "最大行数を超えるとフィルタではなくトランケートされます",
"复制图片": "画像をコピー"
}

View File

@ -825,5 +825,6 @@
"按字数过滤或截断": "단어 수로 필터링 또는 자르기",
"按行数过滤或截断": "행 수로 필터링 또는 자르기",
"超过最大字数时截断而非过滤": "최대 단어 수 초과 시 필터링 대신 절단",
"超过最大行数时截断而非过滤": "최대 행 수를 초과할 경우 필터링 대신 잘림"
"超过最大行数时截断而非过滤": "최대 행 수를 초과할 경우 필터링 대신 잘림",
"复制图片": "사진 복사"
}

View File

@ -825,5 +825,6 @@
"按字数过滤或截断": "Filteren of inkorten op woordaantal",
"按行数过滤或截断": "Filteren of inkorten op rijaantal",
"超过最大字数时截断而非过滤": "Afsnijden in plaats van filteren bij overschrijding van het maximale aantal woorden",
"超过最大行数时截断而非过滤": "Afsnijden in plaats van filteren wanneer het maximale aantal rijen wordt overschreden"
"超过最大行数时截断而非过滤": "Afsnijden in plaats van filteren wanneer het maximale aantal rijen wordt overschreden",
"复制图片": "Afbeelding kopiëren"
}

View File

@ -825,5 +825,6 @@
"按字数过滤或截断": "Filtruj lub skróć według liczby słów",
"按行数过滤或截断": "Filtruj lub skróć według liczby wierszy",
"超过最大字数时截断而非过滤": "Przycięcie zamiast filtrowania przy przekroczeniu maksymalnej liczby słów",
"超过最大行数时截断而非过滤": "Przycięcie zamiast filtrowania przy przekroczeniu maksymalnej liczby wierszy"
"超过最大行数时截断而非过滤": "Przycięcie zamiast filtrowania przy przekroczeniu maksymalnej liczby wierszy",
"复制图片": "Kopiuj obrazek"
}

View File

@ -825,5 +825,6 @@
"按字数过滤或截断": "Filtrar ou truncar por contagem de palavras",
"按行数过滤或截断": "Filtrar ou truncar por contagem de linhas",
"超过最大字数时截断而非过滤": "Truncar em vez de filtrar quando exceder a contagem máxima de palavras",
"超过最大行数时截断而非过滤": "Truncar em vez de filtrar quando o número máximo de linhas for excedido"
"超过最大行数时截断而非过滤": "Truncar em vez de filtrar quando o número máximo de linhas for excedido",
"复制图片": "Copiar a imagem"
}

View File

@ -825,5 +825,6 @@
"按字数过滤或截断": "Фильтрация или отсечение по количеству слов",
"按行数过滤或截断": "Фильтрация или отсечение по числу строк",
"超过最大字数时截断而非过滤": "Преодоление максимального количества слов вместо фильтрации",
"超过最大行数时截断而非过滤": "Преодоление максимального числа строк вместо фильтрации"
"超过最大行数时截断而非过滤": "Преодоление максимального числа строк вместо фильтрации",
"复制图片": "Копировать изображение"
}

View File

@ -825,5 +825,6 @@
"按字数过滤或截断": "Filtrera eller förkorta efter ordräkning",
"按行数过滤或截断": "Filtrera eller förkorta efter radantal",
"超过最大字数时截断而非过滤": "Trunkera istället för filtrering när det maximala ordantalet överskrids",
"超过最大行数时截断而非过滤": "Trunkera istället för filtrering när det maximala antalet rader överskrids"
"超过最大行数时截断而非过滤": "Trunkera istället för filtrering när det maximala antalet rader överskrids",
"复制图片": "Kopiera bild"
}

View File

@ -825,5 +825,6 @@
"按字数过滤或截断": "กรองหรือตัดตามจำนวนคำ",
"按行数过滤或截断": "กรองหรือตัดตามจำนวนแถว",
"超过最大字数时截断而非过滤": "ตัดแทนการกรองเมื่อเกินจำนวนคำสูงสุด",
"超过最大行数时截断而非过滤": "ตัดแทนการกรองเมื่อเกินจำนวนแถวสูงสุด"
"超过最大行数时截断而非过滤": "ตัดแทนการกรองเมื่อเกินจำนวนแถวสูงสุด",
"复制图片": "คัดลอกรูปภาพ"
}

View File

@ -825,5 +825,6 @@
"按字数过滤或截断": "Kelime sayıyla sil ya da küçük",
"按行数过滤或截断": "Sırada sayıyla sil ya da küçük",
"超过最大字数时截断而非过滤": "Maksimum kelime sayısından fazladığında filtreleme yerine küçük",
"超过最大行数时截断而非过滤": "Azamik satır sayısını aştığında filtrelemek yerine küçük"
"超过最大行数时截断而非过滤": "Azamik satır sayısını aştığında filtrelemek yerine küçük",
"复制图片": "Görüntü kopyala"
}

View File

@ -825,5 +825,6 @@
"按字数过滤或截断": "Фільтрувати або скоротити за кількістю слів",
"按行数过滤或截断": "Фільтрувати або вирізати за кількістю рядків",
"超过最大字数时截断而非过滤": "Вирізати замість фільтрування, якщо перевищує максимальну кількість слів",
"超过最大行数时截断而非过滤": "Вирізати замість фільтрування, якщо перевищено максимальну кількість рядків"
"超过最大行数时截断而非过滤": "Вирізати замість фільтрування, якщо перевищено максимальну кількість рядків",
"复制图片": "Копіювати зображення"
}

View File

@ -825,5 +825,6 @@
"按字数过滤或截断": "Lọc hoặc cắt ngắn theo số lượng từ",
"按行数过滤或截断": "Lọc hoặc cắt ngắn theo số dòng",
"超过最大字数时截断而非过滤": "Cắt ngắn thay vì lọc khi vượt quá số từ tối đa",
"超过最大行数时截断而非过滤": "Cắt ngắn thay vì lọc khi vượt quá số dòng tối đa"
"超过最大行数时截断而非过滤": "Cắt ngắn thay vì lọc khi vượt quá số dòng tối đa",
"复制图片": "Sao chép ảnh"
}

View File

@ -825,5 +825,9 @@
"按字数过滤或截断": "",
"按行数过滤或截断": "",
"超过最大字数时截断而非过滤": "",
"超过最大行数时截断而非过滤": ""
"超过最大行数时截断而非过滤": "",
"复制图片": "",
"旭光のマリアージュ": "",
"俺たちに翼はない ―――under the innocent sky.": "",
"卡片模板「modelofluna」中的「内容模板 1」存在错误。<br>内容模板正面应有字段替换。": ""
}

View File

@ -88,7 +88,8 @@ Some buttons have two icons to indicate two different states. Some buttons only
You can scale the game window (HOOK linked game/clipboard, OCR bound window) with one click (default uses the built-in Magpie, or you can set it to use your own downloaded Magpie, etc.).<br>
1. #### <i class="fa fa-camera"></i> <i class="fa fa-icon fa-rotate-right"></i> Window Screenshot
You can take a screenshot of the bound window (it will take two screenshots by default, GDI and Winrt, both of which have a certain probability of failure). The best part is that if you are currently using Magpie for scaling, it will also take a screenshot of the enlarged window.<br>
See [Useful Features](/zh/usefulsmalltools.md?id=Window Screenshot & Gallery & Recording, Capture Every Wonderful Moment)
When left clicked, the screenshot will be saved to a file, and when right clicked, the screenshot will be saved to the clipboard.
1. #### <i class="fa fa-volume-off"></i> <i class="btnstatus2 fa fa-volume-up"></i> Game Mute
After binding the game window (not just in HOOK mode, OCR or clipboard mode can also, as long as the game window is bound), you can mute the game with one click, saving the trouble of muting the game in the system volume mixer.
1. #### <i class="fa fa-eye"></i> <i class="btnstatus2 fa fa-eye-slash"></i> Show/Hide Original Text

View File

@ -99,7 +99,7 @@
1. #### <i class="fa fa-camera"></i> <i class="fa fa-icon fa-rotate-right"></i> Снимок окна
Можно сделать снимок привязанного окна (по умолчанию делает два снимка, GDI и Winrt, оба имеют некоторую вероятность сбоя). Лучшее место, если в настоящее время используется Magpie для масштабирования, также будет делать снимок увеличенного окна.<br>
Подробнее в разделе [Полезные функции](/ru/usefulsmalltools.md?id=Снимок_окна&amp;галерея&amp;запись_звука,_захват_каждогоркогоомента)
Снимок экрана будет сохранен в файле при щелчке левой кнопкой мыши, а снимок экрана будет сохранен в буфере обмена при щелчке правой кнопкой мыши.
1. #### <i class="fa fa-volume-off"></i> <i class="btnstatus2 fa fa-volume-up"></i> Выключение звука игры
После привязки окна игры (не только в режиме HOOK, но и в режиме OCR или буфера обмена, если только окно игры привязано), можно одним нажатием выключить звук игры, избавившись от необходимости отключать звук игры в системном микшере.

View File

@ -88,7 +88,7 @@
可以一键对游戏窗口(HOOK链接游戏/剪贴板、OCR绑定窗口)进行缩放默认使用内置的Magpie也可以设置使用自己下载的Magpie等<br>
1. #### <i class="fa fa-camera"></i> <i class="fa fa-icon fa-rotate-right"></i> 窗口截图
可以对绑定的窗口进行截图默认会截两张图GDI和Winrt两者均有一定概率会失败。最好的地方是如果当前正在使用Magpie进行缩放还会对放大的窗口进行截图。<br>
详见[实用功能](/zh/usefulsmalltools.md?id=窗口截图amp画廊amp录音捕捉每个精彩瞬间)
左键点击时会把截图保存到文件,右键点击时截图会保存到剪贴板。
1. #### <i class="fa fa-volume-off"></i> <i class="btnstatus2 fa fa-volume-up"></i> 游戏静音
当绑定游戏窗口后不只是hook模式ocr或剪贴板模式都可以只要绑定了游戏窗口可以一键对游戏进行静音省去了在系统音量合成器进行游戏静音的麻烦。
1. #### <i class="fa fa-eye"></i> <i class="btnstatus2 fa fa-eye-slash"></i> 显示/隐藏原文

View File

@ -28,8 +28,8 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}/version)
include(generate_product_version)
set(VERSION_MAJOR 5)
set(VERSION_MINOR 40)
set(VERSION_PATCH 3)
set(VERSION_MINOR 41)
set(VERSION_PATCH 0)
add_library(pch pch.cpp)
target_precompile_headers(pch PUBLIC pch.h)

View File

@ -1,7 +1,7 @@
#pragma once
extern "C"
{
__declspec(dllexport) void winrt_capture_window(wchar_t *savepath, HWND hwnd);
__declspec(dllexport) void winrt_capture_window(HWND hwnd, void (*cb)(byte *, size_t));
__declspec(dllexport) bool check_language_valid(wchar_t *);
__declspec(dllexport) void getlanguagelist(void (*cb)(LPCWSTR));

View File

@ -56,9 +56,11 @@ int GetEncoderClsid(const WCHAR *format, CLSID *pClsid)
free(pImageCodecInfo);
return -1; // Failure
}
void capture_window(HWND window_handle, const std::wstring &output_file_path)
void capture_window(HWND window_handle, void (*cb)(byte *, size_t))
{
HMODULE hModule = LoadLibrary(TEXT("d3d11.dll"));
HMODULE hModule = GetModuleHandle(TEXT("d3d11.dll"));
if (!hModule)
hModule = LoadLibrary(TEXT("d3d11.dll"));
HRESULT typedef(_stdcall * CreateDirect3D11DeviceFromDXGIDevice_t)(
_In_ IDXGIDevice * dxgiDevice,
_COM_Outptr_ IInspectable * *graphicsDevice);
@ -88,7 +90,7 @@ void capture_window(HWND window_handle, const std::wstring &output_file_path)
winrt::check_hresult(idxgi_device2->GetParent(winrt::guid_of<IDXGIAdapter>(), adapter.put_void()));
winrt::com_ptr<IDXGIFactory2> factory;
winrt::check_hresult(adapter->GetParent(winrt::guid_of<IDXGIFactory2>(), factory.put_void()));
winrt::com_ptr<ID3D11DeviceContext> d3d_context;
d3d_device->GetImmediateContext(d3d_context.put());
@ -132,7 +134,6 @@ void capture_window(HWND window_handle, const std::wstring &output_file_path)
access->GetInterface(winrt::guid_of<ID3D11Texture2D>(), texture.put_void());
is_frame_arrived = true;
return; });
session.StartCapture();
// Message pump
@ -146,7 +147,6 @@ void capture_window(HWND window_handle, const std::wstring &output_file_path)
}
session.Close();
//??
D3D11_TEXTURE2D_DESC captured_texture_desc;
texture->GetDesc(&captured_texture_desc);
@ -160,7 +160,6 @@ void capture_window(HWND window_handle, const std::wstring &output_file_path)
winrt::check_hresult(d3d_device->CreateTexture2D(&captured_texture_desc, nullptr, user_texture.put()));
d3d_context->CopyResource(user_texture.get(), texture.get());
D3D11_MAPPED_SUBRESOURCE resource;
winrt::check_hresult(d3d_context->Map(user_texture.get(), NULL, D3D11_MAP_READ, 0, &resource));
@ -189,45 +188,23 @@ void capture_window(HWND window_handle, const std::wstring &output_file_path)
sptr += resource.RowPitch;
dptr -= l_bmp_row_pitch;
}
d3d_context->Unmap(user_texture.get(), NULL);
BITMAPFILEHEADER bmfh;
memset(&bmfh, 0, sizeof(BITMAPFILEHEADER));
bmfh.bfType = 0x4D42; // 'BM'
bmfh.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + l_bmp_info.bmiHeader.biSizeImage;
bmfh.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
// FILE* lfile = nullptr;
// if (auto lerr = _wfopen_s(&lfile, output_file_path.c_str(), L"wb"); lerr != 0)
//{
// return;
// }
// if (lfile != nullptr)
//{
// BITMAPFILEHEADER bmp_file_header;
// bmp_file_header.bfReserved1 = 0;
// bmp_file_header.bfReserved2 = 0;
// bmp_file_header.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + l_bmp_info.bmiHeader.biSizeImage;
// bmp_file_header.bfType = 'MB';
// bmp_file_header.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
// fwrite(&bmp_file_header, sizeof(BITMAPFILEHEADER), 1, lfile);
// fwrite(&l_bmp_info.bmiHeader, sizeof(BITMAPINFOHEADER), 1, lfile);
// fwrite(p_buf.get(), l_bmp_info.bmiHeader.biSizeImage, 1, lfile);
// fclose(lfile);
// //convert_image_encoding(output_file_path, L"png");
//}
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
Gdiplus::Bitmap *image = Gdiplus::Bitmap::FromBITMAPINFO(&l_bmp_info, p_buf.get());
CLSID encoderClsid;
GetEncoderClsid(L"image/png", &encoderClsid);
image->Save(output_file_path.c_str(), &encoderClsid, NULL);
delete image;
Gdiplus::GdiplusShutdown(gdiplusToken);
auto p_buf2 = std::make_unique<BYTE[]>(bmfh.bfSize);
auto ptr = p_buf2.get();
memcpy(ptr, &bmfh, sizeof(BITMAPFILEHEADER));
ptr += sizeof(BITMAPFILEHEADER);
memcpy(ptr, (char *)&l_bmp_info.bmiHeader, sizeof(BITMAPINFOHEADER));
ptr += sizeof(BITMAPINFOHEADER);
memcpy(ptr, p_buf.get(), l_bmp_info.bmiHeader.biSizeImage);
cb(p_buf2.get(), bmfh.bfSize);
}
void winrt_capture_window(wchar_t *savepath, HWND hwnd)
void winrt_capture_window(HWND hwnd, void (*cb)(byte *, size_t))
{
// auto hwnd = GetForegroundWindow();// FindWindow(L"Window_Magpie_967EB565-6F73-4E94-AE53-00CC42592A22", 0);
auto style_ex = GetWindowLong(hwnd, GWL_EXSTYLE);
@ -241,7 +218,7 @@ void winrt_capture_window(wchar_t *savepath, HWND hwnd)
SetWindowLong(hwnd, GWL_EXSTYLE, style_ex);
}
capture_window(hwnd, savepath);
capture_window(hwnd, cb);
if (needset)
SetWindowLong(hwnd, GWL_EXSTYLE, style_ex_save);
}

View File

@ -63,11 +63,18 @@ bool clipboard_set(HWND hwnd, wchar_t *text)
break;
auto pchData = (wchar_t *)GlobalLock(hClipboardData);
if (pchData == 0)
{
GlobalFree(hClipboardData);
break;
}
wcscpy_s(pchData, len, text);
GlobalUnlock(hClipboardData);
SetClipboardData(CF_UNICODETEXT, hClipboardData);
success = true;
if (SetClipboardData(CF_UNICODETEXT, hClipboardData))
success = true;
else
{
GlobalFree(hClipboardData);
}
} while (false);
CloseClipboard();
@ -137,3 +144,29 @@ DECLARE void clipboard_callback_stop(HWND hwnd)
RemoveClipboardFormatListener(hwnd);
DestroyWindow(hwnd);
}
DECLARE bool clipboard_set_image(HWND hwnd, void *ptr, size_t size)
{
size -= sizeof(BITMAPFILEHEADER);
HGLOBAL hDib = GlobalAlloc(GMEM_MOVEABLE, size);
if (!hDib)
return false;
void *pDib = GlobalLock(hDib);
if (!pDib)
{
GlobalFree(hDib);
return false;
}
memcpy((char *)pDib, (char *)ptr + sizeof(BITMAPFILEHEADER), size);
if (tryopenclipboard(hwnd) == false)
return false;
EmptyClipboard();
if (!SetClipboardData(CF_DIB, hDib))
{
GlobalFree(hDib);
CloseClipboard();
return false;
}
CloseClipboard();
return true;
}