Что нового

скриншот в бинарный код без промежуточного файла

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
Можно поставить максимальное сжатие, но сильно упадет скорость (я думаю).

_WinAPI_LZNTCompress($tBD, $tData, 1)
 

VladUs

Скриптер
Сообщения
621
Репутация
181
Yashied [?]
Можно поставить максимальное сжатие, но сильно упадет скорость (я думаю)
Правельно думаете, скорость падает существенно, а результат незначителен.
[?]
Кроме того, в GDI не предусмотренна работа с PNG и JPEG.

А какже функция _GDIPlus_ImageSaveToFileEx() , которая и спользует функцию _GDIPlus_EncodersGetCLSID(), где можно задавать тип кодировки (.jpg, .png и т.д)
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
VladUs сказал(а):
А какже функция _GDIPlus_ImageSaveToFileEx() , которая и спользует функцию _GDIPlus_EncodersGetCLSID(), где можно задавать тип кодировки (.jpg, .png и т.д).

Во-первых, это GDI+, а не GDI, а во-вторых, если вы мне скажите, как получить сжатые данные в AutoIt без создания файла (потоком), то я буду премного благодарен.
 

asdf8

Скриптер
Сообщения
564
Репутация
152
как получить сжатые данные в AutoIt без создания файла

можно попробовать этим, но там еще надо разбираться.
Мне кажется, что легче создать RAM-диск и на нем сохранять в png, кстати, если уменьшить битность png, то можно уменьшить размер до 100 - 200 кб, без потери четкости изображения.
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
asdf8 сказал(а):
Мне кажется, что легче создать RAM-диск и на нем сохранять в png.

Это все через жо**. Нужно именно через stream, но в API они реализованы через объекты... Кстати, последний вариант передает скриншот в 24-битном (3 байта на пиксел) формате, можно снизить до 16-бит (2 байта на пиксел).

Код:
Func _GetScreenshot($iX = 0, $iY = 0, $iWidth = -1, $iHeight = -1)

	Local $tBD, $tData, $tOut, $hBitmap, $hDesktop, $hSrcDC, $hDstDC, $hDstSv, $pBits, $Lenght

	If $iWidth = -1 Then
		$iWidth = @DesktopWidth
	EndIf
	If $iHeight = -1 Then
		$iHeight = @DesktopHeight
	EndIf
	$tBD = DllStructCreate($tagBITMAPINFOHEADER & 'byte[' & ($iWidth * $iHeight * 2) & ']')
	If @error Then
		Return 0
	EndIf
	DllStructSetData($tBD, 'biSize', 40)
	DllStructSetData($tBD, 'biWidth', $iWidth)
	DllStructSetData($tBD, 'biHeight', -$iHeight)
	DllStructSetData($tBD, 'biPlanes', 1)
	DllStructSetData($tBD, 'biBitCount', 16)
	DllStructSetData($tBD, 'biCompression', $BI_RGB)
	$hBitmap = _WinAPI_CreateDIBSection(0, $tBD, $DIB_RGB_COLORS, $pBits)
	$hDesktop = _WinAPI_GetDesktopWindow()
	$hSrcDC = _WinAPI_GetDC($hDesktop)
	$hDstDC = _WinAPI_CreateCompatibleDC($hSrcDC)
	$hDstSv = _WinAPI_SelectObject($hDstDC, $hBitmap)
	_WinAPI_BitBlt($hDstDC, $iX, $iY, $iWidth, $iHeight, $hSrcDC, $iX, $iY, $SRCCOPY)
	_WinAPI_SelectObject($hDstDC, $hDstSv)
	_WinAPI_ReleaseDC($hDesktop, $hSrcDC)
	_WinAPI_DeleteDC($hDstDC)
	_WinAPI_GetBitmapBits($hBitmap, $iWidth * $iHeight * 2, DllStructGetPtr($tBD) + 40)
	_WinAPI_DeleteObject($hBitmap)
	$Lenght = _WinAPI_LZNTCompress($tBD, $tData)
	$tOut = DllStructCreate('dword;dword;byte[' & ($Lenght - Mod($Lenght, 4) + 4) & ']')
	DllStructSetData($tOut, 1, DllStructGetSize($tOut))
	DllStructSetData($tOut, 2, $Lenght)
	DllStructSetData($tOut, 3, DllStructGetData($tData, 1))
	Return Binary(DllStructGetData(DllStructCreate('byte[' & DllStructGetSize($tOut) & ']', DllStructGetPtr($tOut)), 1))
EndFunc   ;==>_GetScreenshot
 

VladUs

Скриптер
Сообщения
621
Репутация
181
asdf8 [?]
Мне кажется, что легче создать RAM-диск
Буду рад если поделитись опытом в этом направлении...

Yashied Вам отдельное спасибо за участие в данной теме.
Меня, честно говоря, устраивает и Ваш последний код со сжатием...
Не знаю, есть ли это на офф. форуме, но ИМХО Ваш труд по данному вопросу заслуживает внимания и там.
 

asdf8

Скриптер
Сообщения
564
Репутация
152
Буду рад если поделитись опытом в этом направлении...

Стандартными средствами системы - пока не нашел как сделать (может плохо искал), гуглятся только сторонние приложения
 

VladUs

Скриптер
Сообщения
621
Репутация
181
asdf8 [?]
Стандартными средствами системы - пока не нашел как сделать (может плохо искал), гуглятся только сторонние приложения
Во - первых, стандартных средств нет
Во - вторых, это не такая простая как Вам кажется...
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
Отставить RAM-диски. :smile: Вот так можно получить .png без записи в файл.

Код:
#Include <GDIPlus.au3>
#Include <Memory.au3>
#Include <ScreenCapture.au3>
#Include <WinAPIEx.au3>

_GDIPlus_Startup()

$hScreen = _ScreenCapture_Capture()
$hBitmap = _GDIPlus_BitmapCreateFromHBITMAP($hScreen)
_WinAPI_DeleteObject($hScreen)

$tEncoder = _WinAPI_GUIDFromString(_GDIPlus_EncodersGetCLSID('png'))
$pEncoder = DllStructGetPtr($tEncoder)
$pStream = _WinAPI_CreateStreamOnHGlobal(0)
_GDIPlus_ImageSaveToStream($hBitmap, $pStream, $pEncoder)
_GDIPlus_BitmapDispose($hBitmap)
$hMemory = _WinAPI_GetHGlobalFromStream($pStream)
$pMemory = _MemGlobalLock($hMemory)
$iSize = _MemGlobalSize($hMemory)
$tData = DllStructCreate('byte[' & $iSize & ']', $pMemory)
$bData = DllStructGetData($tData, 1)
_MemGlobalFree($hMemory)

FileWrite(@ScriptDir & '\screenshot.png', $bData)

_GDIPlus_Shutdown()

Func _GDIPlus_ImageSaveToStream($hImage, $pStream, $pEncoder, $pParams = 0)

	Local $Ret = DllCall($ghGDIPDll, 'uint', 'GdipSaveImageToStream', 'ptr', $hImage, 'ptr', $pStream, 'ptr', $pEncoder, 'ptr', $pParams)

	If (@error) Or ($Ret[0]) Then
		Return SetError(@error, @extended, 0)
	EndIf
	Return 1
EndFunc   ;==>_GDIPlus_ImageSaveToStream
 

VladUs

Скриптер
Сообщения
621
Репутация
181
Да этот способ сжимает лучше чем _WinAPI_LZNTCompress().
Скриншот на выходе в формате .bmp весом 6076 кб сжимается с помощью _WinAPI_LZNTCompress() до 847 кб, т.е более чем в 7 раз.
А с помощью _GDIPlus_ImageSaveToStream() получается скрин весом 266 кб т.е более чем в 22 раза сжал .bmp. Поучается эффективность в 3 раза лучше чем _WinAPI_LZNTCompress()
Потом решил проверить как сожмет _WinAPI_LZNTCompress() файл полученный в результате работы _GDIPlus_ImageSaveToStream() . Эффект практически никакой.
с 266 кб сжал до 263 кб. Видимо .png и так хорошо сжат.
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
Вот вариант с PNG. Если хотите JPEG, то замените "png" на "jpg" в начале файла Client.au3, скорость будет еще больше (только не забудьте убрать задержку).

Client.au3
Код:
#Include <GDIPlus.au3>
#Include <Memory.au3>
#Include <WinAPIEx.au3>

TCPStartup()
$Socket = TCPConnect(@IPAddress1, 33891)
If @error Then
	Exit
EndIf

_GDIPlus_Startup()

Global $tEncoder = _WinAPI_GUIDFromString(_GDIPlus_EncodersGetCLSID('png'))
Global $pEncoder = DllStructGetPtr($tEncoder)

While 1
	TCPSend($Socket, _GetScreenshot(0, 0, 800, 600))
	If @error Then
		ExitLoop
	EndIf
	Sleep(1000)
WEnd

_GDIPlus_Shutdown()

TCPShutdown()

Func _GetScreenshot($iX = 0, $iY = 0, $iWidth = -1, $iHeight = -1)

	Local $hSrcDC, $hDstDC, $hDstSv, $hBitmap, $hDesktop, $hScreen, $hMemory, $pMemory, $pStream, $tOut, $pOut, $iSize

	If $iWidth = -1 Then
		$iWidth = @DesktopWidth
	EndIf
	If $iHeight = -1 Then
		$iHeight = @DesktopHeight
	EndIf
	$hDesktop = _WinAPI_GetDesktopWindow()
	$hSrcDC = _WinAPI_GetDC($hDesktop)
	$hScreen = _WinAPI_CreateCompatibleBitmap($hSrcDC, $iWidth, $iHeight)
	$hDstDC = _WinAPI_CreateCompatibleDC($hSrcDC)
	$hDstSv = _WinAPI_SelectObject($hDstDC, $hScreen)
	_WinAPI_BitBlt($hDstDC, $iX, $iY, $iWidth, $iHeight, $hSrcDC, $iX, $iY, $SRCCOPY)
	_WinAPI_SelectObject($hDstDC, $hDstSv)
	_WinAPI_ReleaseDC($hDesktop, $hSrcDC)
	_WinAPI_DeleteDC($hDstDC)
	$hBitmap = _GDIPlus_BitmapCreateFromHBITMAP($hScreen)
	_WinAPI_DeleteObject($hScreen)
	$pStream = _WinAPI_CreateStreamOnHGlobal(0)
	_GDIPlus_ImageSaveToStream($hBitmap, $pStream, $pEncoder)
	_GDIPlus_BitmapDispose($hBitmap)
	$hMemory = _WinAPI_GetHGlobalFromStream($pStream)
	$pMemory = _MemGlobalLock($hMemory)
	$iSize = _MemGlobalSize($hMemory)
	$tOut = DllStructCreate('dword;dword;byte[' & ($iSize - Mod($iSize, 4) + 4) & ']')
	$pOut = DllStructGetPtr($tOut)
	DllStructSetData($tOut, 1, DllStructGetSize($tOut))
	DllStructSetData($tOut, 2, $iSize)
	_MemMoveMemory($pMemory, $pOut + 8, $iSize)
	_MemGlobalFree($hMemory)
	Return DllStructGetData(DllStructCreate('byte[' & DllStructGetSize($tOut) & ']', DllStructGetPtr($tOut)), 1)
EndFunc   ;==>_GetScreenshot

Func _GDIPlus_ImageSaveToStream($hImage, $pStream, $pEncoder, $pParams = 0)

	Local $Ret = DllCall($ghGDIPDll, 'uint', 'GdipSaveImageToStream', 'ptr', $hImage, 'ptr', $pStream, 'ptr', $pEncoder, 'ptr', $pParams)

	If (@error) Or ($Ret[0]) Then
		Return SetError(@error, @extended, 0)
	EndIf
	Return 1
EndFunc   ;==>_GDIPlus_ImageSaveToStream


Server.au3
Код:
#Include <Constants.au3>
#Include <GDIPlus.au3>
#Include <Memory.au3>
#Include <WinAPIEx.au3>

Global Const $STM_SETIMAGE = 0x0172
Global Const $STM_GETIMAGE = 0x0173

TCPStartup()
$Main = TCPListen(@IPAddress1, 33891)
If @error Then
	Exit
EndIf

GUICreate('MyGUI', 800, 600, @DesktopWidth - 819, @DesktopHeight - 671)
$Pic = GUICtrlCreatePic('', 0, 0, 800, 600)
GUISetState()

Do
	$Socket = TCPAccept($Main)
Until $Socket <> -1

Global $tHeader = DllStructCreate('byte[4]')
Global $pHeader = DllStructGetPtr($tHeader)
Global $bData = 0, $Count = 0

_GDIPlus_Startup()

Do
	If Not IsBinary($bData) Then
		$bData = TCPRecv($Socket, 16384, 1)
		If @error Then
			ExitLoop
		EndIf
	EndIf
	If IsBinary($bData) Then
		If Not $Count Then
			DllStructSetData($tHeader, 1, $bData)
			$tData = DllStructCreate('dword', $pHeader)
			$Size = DllStructGetData($tData, 1)
			$tData = DllStructCreate('byte[' & $Size & ']')
			$pData = DllStructGetPtr($tData)
		EndIf
		$Lenght = BinaryLen($bData)
		$Sum = $Count + $Lenght
		If $Sum > $Size Then
			$dL = $Size - $Count
			$tPart = DllStructCreate('byte[' & $dL & ']', $pData + $Count)
			DllStructSetData($tPart, 1, $bData)
			$tByte = DllStructCreate('byte[' & $Lenght & ']')
			$tRecv = DllStructCreate('byte[' & ($Lenght - $dL) & ']', DllStructGetPtr($tByte) + $dL)
			DllStructSetData($tByte, 1, $bData)
			$bData = DllStructGetData($tRecv, 1)
			$Sum = $Size
		Else
			$tPart = DllStructCreate('byte[' & $Lenght & ']', $pData + $Count)
			DllStructSetData($tPart, 1, $bData)
			$Count = $Sum
			$bData = 0
		EndIf
		If $Sum = $Size Then
			_SetScreenshot($Pic, $tData)
			$Count = 0
		EndIf
	EndIf
Until GUIGetMsg() = -3

_GDIPlus_Shutdown()

TCPCloseSocket($Socket)
TCPShutdown()

Func _SetScreenshot($CtrlID, ByRef $tData, $fUpdate = 0)

	Local $hBitmap, $hPrev, $hScreen, $hMemory, $pMemory, $pStream, $pData, $iSize

	$hWnd = GUICtrlGetHandle($CtrlID)
	If Not $hWnd Then
		Return 0
	EndIf

	$pData = DllStructGetPtr($tData)
	$iSize = DllStructGetData(DllStructCreate('dword', $pData + 4), 1)
	$hMemory = _MemGlobalAlloc($iSize, $GMEM_MOVEABLE)
	$pMemory = _MemGlobalLock($hMemory)
	_MemMoveMemory($pData + 8, $pMemory, $iSize)
	_MemGlobalUnlock($hMemory)
	$pStream = _WinAPI_CreateStreamOnHGlobal($hMemory)
	$hBitmap = _GDIPlus_BitmapCreateFromStream($pStream)
	$hScreen = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hBitmap)
	_GDIPlus_BitmapDispose($hBitmap)
	_MemGlobalFree($hMemory)
	_WinAPI_DeleteObject(_SendMessage($hWnd, $STM_SETIMAGE, $IMAGE_BITMAP, $hScreen))
	$hPrev = _SendMessage($hWnd, $STM_GETIMAGE)
	If $hPrev <> $hScreen Then
		_WinAPI_DeleteObject($hScreen)
	EndIf
	If $fUpdate Then
		_WinAPI_UpdateWindow($hWnd)
	EndIf
	Return 1
EndFunc   ;==>_SetScreenshot

Func _GDIPlus_BitmapCreateFromStream($pStream)

	Local $Ret = DllCall($ghGDIPDll, 'uint', 'GdipCreateBitmapFromStream', 'ptr', $pStream, 'ptr*', 0)

	If (@error) Or ($Ret[0]) Then
		Return SetError(@error, @extended, 0)
	EndIf
	Return $Ret[2]
EndFunc   ;==>_GDIPlus_BitmapCreateFromStream
 

Arei

Скриптер
Сообщения
938
Репутация
115
Мне кажется размер картинки 30 кб, для такого способа не получиться добиться.
 

asdf8

Скриптер
Сообщения
564
Репутация
152
Мне кажется размер картинки 30 кб, для такого способа не получиться добиться.

Зато можно порезать скрин, либо просто по координатной сетке, либо по окнам и координатной сетке, и передавать только изменившиеся куски
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
Если поставить "jpg", то размер скриншота будет примерно 20-80 КБ при разрешении 800x600.
 

Arei

Скриптер
Сообщения
938
Репутация
115
А как можно установить качество связи например " с 100 как сейчас на 25 ".Можно ли так сделать?
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
Что такое "качество связи"?
 

VladUs

Скриптер
Сообщения
621
Репутация
181
В последнем варианте, скорость отображения, не уступает тому же Radmin, DameWare Mini Remote Control или VNC.
 

Arei

Скриптер
Сообщения
938
Репутация
115
Извините,я не так выразился качество картинки,конечно же.
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
Arei сказал(а):
Извините,я не так выразился качество картинки,конечно же.

Сейчас стоит "png", который сжимает картинку без потерь, т.е. качество картинки не ухудшается и будет выглядеть одинаково хорошо и на сервере, и на клиенте. Если хочешь ухудшить качество скриншота, но тем самым уменьшить размер и, соответственно, увеличить скорость передачи его по сети, то замени "png" на "jpg" в файле Client.au3. Я вроде бы уже об этом писал...
 
Верх