I add a memory free line . After the File.close()
And the uncompressed zippack frames player is basicly done.
Download this pixiv Ugoira player demo page zip pack test.
https marcan.st /talks/2014_pixiv_ugoku_player/ Broken Link for safetyimg/cirno.zip
https marcan.st /talks/2014_pixiv_ugoku_player/ Broken Link for safetyimg/katakata.zip
Still need add get the filename of script itself , fullscreen , resize , combine Gdip.ahk lines in script and remove unused Gdip.ahk lines.
Just finished main function.
Combining Gdip.ahk that part I keep it myself. Since play Ugoria zip pack just my personal interest, read Ugoria frame delay list is not content in this post.
All other coding finished.
Still can only read "store" type zip pack, add support for none Ugoria zip pack ,which winrar 7zip created "store-noncompressed" zip file.
Now can skip file extra info data , skip folders in zip.
Compressed type not support , can't find good AHK example for doing that , and some of it need extra .dll , which I don't want to.
GDI+ drawing still not very fast while resizing picture, but DirectDraw , I search those old threads on forum , nobody can do it .
How to use it . For example Hulagirl , pack png files of Hulagirl in a zip pack , compress type choose "store".
Rename this script to Hulagirl.ahk , it will read the zip pack with the same main filename (in same folder), just like subtitle file.
Right Mouse Button Toggle Pause : while in pause , mouse wheel down , pageDown key , Xbutton2 make show next frame ,
mouse wheel up , pageUp key , Xbutton1 make show previous frame ,
Middle Mouse Button Toggle Fullscreen , it's auto fit.
Left Mouse Button drag window.
ESC key exit script.
Keeping Right Mouse Button down then click Left Mouse Button also exit script.
Sorry for my bad English discrption , I know my words had some grammar mistakes.
It have been long time since left school, English is not my native lauguage. 我说中文。
Code: Select all
#include Gdip.ahk
#NoEnv
#SingleInstance Off
#Persistent
#NoTrayIcon
#WinActivateForce
If !pToken := Gdip_Startup()
{
MsgBox, 48, Gdiplus error!, Gdiplus failed to start. Please ensure you have gdiplus on your system
ExitApp
}
OnExit, Exit
;Variables
NotFullscreen := True
onDrawing := False
onChanging := False
isPause := False
;Create Bitmaps from Zip-with-frames-Files
UgoiraZip := RegExReplace(A_ScriptName, "i).ahk$", ".zip" )
File := FileOpen( UgoiraZip , "r")
FileGetSize, fsize , %UgoiraZip%
VarSetCapacity(Buffer, fsize, 0)
;Pointer Int64, UInt, Int, UShort, Short, UChar, Char, Double, Float, UPtr or Ptr
; 64bit | 32bit | 16bit |8bitReadAsNum| 64bit| 32bit |pointer to str
;NumGet() will return any thing in decimal number
;memory address must in [ &mem ] or [ addr + 0 ] , mem is memory block object,
;addr := &mem addr store pointer address of memory block object ,but when pass it to NumGet()
;It must be written in [ addr + 0 ] <--- this [ + 0 ] must be written .
File.RawRead( &Buffer, fsize)
;Now I have a copy in memory , file reader can close.
File.Close()
NumOfAllFile := NumGet( &Buffer, fsize-14, "UShort")
listOffset := NumGet( &Buffer, fsize-6, "UInt")
next := listOffset
Loop %NumOfAllFile%
{
fileOffset%A_Index% := NumGet( &Buffer, next + 42 , "UInt")
fileLen%A_Index% := NumGet( &Buffer, next + 20 , "UInt")
nameLen%A_Index% := NumGet( &Buffer, next + 28 , "UShort")
fileExtSkip := NumGet( &Buffer, next + 30 , "UShort")
next := next + 46 + nameLen%A_Index% + fileExtSkip
}
fileWithoutDir := 0
Loop %NumOfAllFile%
{
if ( 0 = fileLen%A_Index% )
{
continue
}
else {
fileWithOutDir := fileWithOutDir + 1
;create memory for Gdiplus.dll store bitmap
hData%fileWithOutDir% := DllCall("GlobalAlloc", "Uint", 2, "Uint", fileLen%A_Index% )
;none type pointer and protect in-use memory
pData := DllCall("GlobalLock", "Uint", hData%fileWithOutDir%)
;address of image file start
pointer := &Buffer + fileOffset%A_Index% + 30 + nameLen%A_Index%
;use none type pointer copy image file content to the new empty mem for Gdiplus.dll which just create before
DllCall("RtlMoveMemory", "Uint", pData, "Uint", pointer , "Uint", fileLen%A_Index% )
;delete none type pointer and unlock
DllCall("GlobalUnlock", "Uint", hData%fileWithOutDir%)
;create Stream type pointer for Gdiplus.dll to read
DllCall("ole32\CreateStreamOnHGlobal", "Uint", hData%fileWithOutDir%, "int", True, "UintP", pStream)
;give Gdiplus.dll Stream pointer ,then get Bitmap pointer which AHK GUI can use.
;By the way, Gdiplus.dll Stream pass can handle png jpg bmp decode to Bitmap, which GUI can directly use.
DllCall("Gdiplus.dll\GdipCreateBitmapFromStream", "Uint", pStream, "UintP", pBmp)
pBitmap%fileWithOutDir% := pBmp
}
}
;All image date have been copied to Gdiplus.dll , this can be clear.
;Gdiplus.dll use GlobalAlloc created memory space (public address).
;So don't do this --> DllCall("GlobalFree", "Uint", hData)
VarSetCapacity(Buffer, 0)
;Dimensions of the images
nWidth := Gdip_GetImageWidth(pBitmap1)
nHeight := Gdip_GetImageHeight(pBitmap1)
nWidthOri := nWidth
nHeightOri := nHeight
Random, RandomIDaddTitle, 111111, 999999
myTitle := A_ScriptName RandomIDaddTitle
;Create a GUI
Gui, 1: -Caption +E0x80000 +LastFound -OwnDialogs -Owner +AlwaysOnTop
Gui, 1: Show
hwnd1 := WinExist()
WinActivate, ahk_id %hwnd1%
WinSetTitle, %myTitle%
;autofit size calculate
;screen working area ( area without taskbar )
SysGet, MonitorPrimary, MonitorPrimary
SysGet, WA, MonitorWorkArea, %MonitorPrimary%
WAWidth := WARight-WALeft
WAHeight := WABottom-WATop
aspectScr := Round( (A_ScreenWidth / A_ScreenHeight) , 4 )
aspectWA := Round( (WAWidth / WAHeight) , 4 )
aspectPic := Round( (nWidthOri / nHeightOri) , 4 )
if ( aspectPic > aspectScr )
{
Ratio := Round( (A_ScreenWidth / nWidthOri) , 4 )
}
else
{
Ratio := Round( (A_ScreenHeight / nHeightOri) , 4 )
}
;Reszie causing drawing delay
antiFullScrDelay := 30
antiResizeDelay := 0
if ( aspectPic > aspectWA )
{
if ( nWidthOri > WAWidth )
{
RatioW := Round( (WAWidth / nWidthOri) , 4 )
nWidth := Round( nWidthOri * RatioW, 0)
nHeight := Round( nHeightOri * RatioW, 0)
antiResizeDelay := 30
}
}
else
{
if ( nHeightOri > WAHeight )
{
RatioW := Round( (WAHeight / nHeightOri) , 4 )
nWidth := Round( nWidthOri * RatioW, 0)
nHeight := Round( nHeightOri * RatioW, 0)
antiResizeDelay := 30
}
}
nFWidth := Round( nWidthOri * Ratio, 0)
nFHeight := Round( nHeightOri * Ratio, 0)
fX:= (A_ScreenWidth - nFWidth)//2
fY:= (A_ScreenHeight - nFHeight)//2
X:= (WAWidth - nWidth)//2
Y:= (WAHeight - nHeight)//2
hbm := CreateDIBSection(nWidth, nHeight)
hdc := CreateCompatibleDC()
obm := SelectObject(hdc, hbm)
G := Gdip_GraphicsFromHDC(hdc)
Gdip_SetInterpolationMode(G, 7)
;Brush for drawing fullscreen black background
pBrush := Gdip_BrushCreateHatch(0xff000000, 0xff000000, Brush)
;https://autohotkey.com/board/topic/38446-getting-lparam-bits-from-wm-keydown/
OnMessage(0x101, "WM_KEYUP")
;Make left mouse button down event become drag event.
OnMessage(0x201, "WM_LBUTTONDOWN")
;Toggle pause
OnMessage(0x204, "WM_RBUTTONDOWN")
;Toggle Fullscreen
OnMessage(0x208, "WM_MBUTTONUP")
;Scroll Wheel change frame while pause
OnMessage(0x20A, "WM_MOUSEWHEEL")
;Xbutton change frame while pause
OnMessage(0x20C, "WM_XBUTTONUP")
modNum := fileWithoutDir + 1
; Do a move first else the WinGetPos() in loop will cause problem.
WinMove,%myTitle%, , X, Y
;Loop with manual control index , so the index is globally accessable.
Animate:
frame := 1
PlayLoop:
if ( frame > fileWithOutDir )
{
Goto, Animate
}
If NotFullscreen ;NotFullscreen
{
if not onChanging
{
onDrawing := True
Gdip_DrawImage(G, pBitmap%frame%, 0, 0, nWidth, nHeight, 0, 0, nWidthOri, nHeightOri)
WinGet id, ID, A
if (id = hwnd1 )
{
WinGetPos, X, Y, , , %myTitle%
}
UpdateLayeredWindow(hwnd1, hdc, X, Y, nWidth, nHeight )
Gdip_GraphicsClear(G)
if not isPause
{
frame := frame + 1
}
onDrawing := False
}
Sleep 50 - antiResizeDelay
}
Else ;Fullscreen
{
if not onChanging
{
onDrawing := True
DllCall("gdiplus\GdipFillRectangle", "uint", GF, "int", pBrush, "float", 0, "float", 0, "float", A_ScreenWidth, "float", A_ScreenHeight)
Gdip_DrawImage(GF, pBitmap%frame%, fX, fY, nFWidth, nFHeight, 0, 0, nWidthOri, nHeightOri)
UpdateLayeredWindow(hwnd1, hdcF, 0, 0, A_ScreenWidth, A_ScreenHeight )
Gdip_GraphicsClear(GF)
if not isPause
{
frame := frame + 1
}
onDrawing := False
}
Sleep 50 - antiFullScrDelay
}
Goto, PlayLoop
Return
;Exit
;for taskbar close, else it will left autohotkey.exe running in background
GuiClose:
playerEND()
return
Exit:
playerEND()
ExitApp
playerEND()
{
Global
Loop %fileWithOutDir%
{
DllCall("GlobalFree", "Uint", hData%A_Index% )
}
SelectObject(hdcF, obmF), DeleteObject(hbmF)
DeleteDC(hdcF), Gdip_DeleteGraphics(GF)
SelectObject(hdc, obm), DeleteObject(hbm)
DeleteDC(hdc), Gdip_DeleteGraphics(G)
Gdip_DeleteBrush(pBrush), Gdip_Shutdown(pToken)
ExitApp
}
frameUP()
{
Global
onChanging := True
frame := frame + 1
if frame >= modNum
{
frame := ( Mod(frame, modNum) + 1)
}
onChanging := False
}
frameDOWN()
{
Global
onChanging := True
frame := frame - 1
if frame <= 0
{
frame := Mod( (modNum + frame -1) , modNum)
}
onChanging := False
}
WM_KEYUP(wParamk, lParamk )
{
Global
if (wParamk = 27) ; ESC
{
playerEND()
}
else if isPause
{
if (wParamk = 34) ; PageDown
{
frameUP()
}
else if (wParamk = 33) ; PageUP
{
frameDOWN()
}
}
}
WM_XBUTTONUP(wParamX, lParamX)
{
Global
if isPause
{
whichK := NumGet(&wParamX, 0, "Short")
if (whichK = 49) ; Xbutton2
{
frameUP()
}
else if (whichK = 54) ; Xbutton1
{
frameDOWN()
}
}
}
;https msdn.microsoft.com /en-us/library/windows/desktop/ms644970(v=vs.85).aspx Broken Link for safety
;https://autohotkey.com/boards/viewtopic.php?t=9247 <--- I copied the wheel code part then got crash.
;https://autohotkey.com/board/topic/98112-wheel-and-drag-deincrement-edit-controls-with-the-mouse/
WM_MOUSEWHEEL(wParamS, lParamS)
{
Global
if isPause
{
; wParam length represent for scroll up or down? I don't know.
; Seen like scrolling down will use more bytes , large than 7.
; Notice I use scrolling DOWN to frameUP() , when DOING frameUP() the WHEEL IS GOING DOWN
if (StrLen(wParamS)>7)
{
frameUP()
}
else
{
frameDOWN()
}
}
}
;Toggle Pause
WM_RBUTTONDOWN()
{
Global
if isPause
{
isPause := False
}
else
{
isPause := True
}
}
; Make left mouse button down event become drag event.
; So normal click is not exist, how to have both event normal like a title bar, I don't know.
; Keep Rbutton down then click Lbutton Toggle Fullscreen
; While Fullscreen disable sending drag event make GUI unmovable.
; Don't use change window attribute Flag like that thread:
; https://autohotkey.com/board/topic/70123-disable-gui-move/
; Unless your don't want to move it again, set back to movable can't make GUI back to normal.
; Every time GUI lost then get focus again , GUI will be send back to x=0, y=0 again.
WM_LBUTTONDOWN(wParamL, lParamL)
{
Global
if NotFullscreen
{
PostMessage, 0xA1, 2
}
if ( (StrLen(wParamL)=1 ) && (NumGet(&wParamL, 0, "Short")=51) )
{
playerEND()
}
}
;Toggle Fullscreen
WM_MBUTTONUP()
{
Global
If NotFullscreen ; turn into Fullscreen
{
loop onDrawing
{
Sleep 17
}
onChanging := True
WinGetPos, X, Y, , , %myTitle%
SelectObject(hdc, obm), DeleteObject(hbm)
DeleteDC(hdc), Gdip_DeleteGraphics(G)
hbmF := CreateDIBSection(A_ScreenWidth, A_ScreenHeight)
hdcF := CreateCompatibleDC()
obmF := SelectObject(hdcF, hbmF)
GF := Gdip_GraphicsFromHDC(hdcF)
Gdip_SetInterpolationMode(GF, 7)
NotFullscreen := False
WinActivate, ahk_id %hwnd1%
onChanging := False
}
Else ; turn into NotFullscreen
{
loop onDrawing
{
Sleep 17
}
onChanging := True
SelectObject(hdcF, obmF), DeleteObject(hbmF)
DeleteDC(hdcF), Gdip_DeleteGraphics(GF)
hbm := CreateDIBSection(nWidth, nHeight)
hdc := CreateCompatibleDC()
obm := SelectObject(hdc, hbm)
G := Gdip_GraphicsFromHDC(hdc)
Gdip_SetInterpolationMode(G, 7)
;Draw a first frame then move , makes shrinking look better, this part of code can be remove .
Gdip_DrawImage(G, pBitmap%frame%, 0, 0, nWidth, nHeight, 0, 0, nWidthOri, nHeightOri)
UpdateLayeredWindow(hwnd1, hdc, X, Y, nWidth, nHeight )
Gdip_GraphicsClear(G)
onChanging := False
WinActivate, ahk_id %hwnd1%
NotFullscreen := True
WinMove, %myTitle%,, X, Y
}
}