How to save image in clipboard.

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
malcev
Posts: 1769
Joined: 12 Aug 2014, 12:37

Re: How to save image in clipboard.

09 Feb 2022, 19:17

@teadrinker, found bug in this post:
viewtopic.php?p=418993#p418993

Code: Select all

pDIB := DllCall("GlobalLock", "Ptr", hDIB)
Should be

Code: Select all

pDIB := DllCall("GlobalLock", "Ptr", hDIB, "ptr")
GEOVAN
Posts: 174
Joined: 03 Mar 2022, 11:12

Re: How to save image in clipboard.

25 Apr 2024, 14:45

teadrinker wrote:
04 Sep 2021, 06:30

Code: Select all

destPngFilePath := A_ScriptDir . "\ClipboardImage.png"
hBitmap := GetBitmapFromClipboard()
HBitmapToPng(hBitmap, destPngFilePath)
DllCall("DeleteObject", "Ptr", hBitmap)
Return

GetBitmapFromClipboard() {
   static CF_BITMAP := 2, CF_DIB := 8, SRCCOPY := 0x00CC0020
   if !DllCall("IsClipboardFormatAvailable", "UInt", CF_BITMAP)
      throw "There is no image in the Clipboard"
   if !DllCall("OpenClipboard", "Ptr", 0)
      throw "OpenClipboard failed"
   hDIB := DllCall("GetClipboardData", "UInt", CF_DIB, "Ptr")
   hBM  := DllCall("GetClipboardData", "UInt", CF_BITMAP, "Ptr")
   DllCall("CloseClipboard")
   if !hDIB
      throw "GetClipboardData failed"
   pDIB := DllCall("GlobalLock", "Ptr", hDIB, "Ptr")
   width  := NumGet(pDIB +  4, "UInt")
   height := NumGet(pDIB +  8, "UInt")
   bpp    := NumGet(pDIB + 14, "UShort")
   DllCall("GlobalUnlock", "Ptr", pDIB)
   
   hDC := DllCall("CreateCompatibleDC", "Ptr", 0, "Ptr")
   oBM := DllCall("SelectObject", "Ptr", hDC, "Ptr", hBM, "Ptr")
   
   hMDC := DllCall("CreateCompatibleDC", "Ptr", 0, "Ptr")
   hNewBM := CreateDIBSection(width, -height,, bpp)
   oPrevBM := DllCall("SelectObject", "Ptr", hMDC, "Ptr", hNewBM, "Ptr")
   DllCall("BitBlt", "Ptr", hMDC, "Int", 0, "Int", 0, "Int", width, "Int", height
                   , "Ptr", hDC , "Int", 0, "Int", 0, "UInt", SRCCOPY)
   DllCall("SelectObject", "Ptr", hDC, "Ptr", oBM, "Ptr")
   DllCall("DeleteDC", "Ptr", hDC), DllCall("DeleteObject", "Ptr", hBM)
   DllCall("SelectObject", "Ptr", hMDC, "Ptr", oPrevBM, "Ptr")
   DllCall("DeleteDC", "Ptr", hMDC)
   Return hNewBM
}

CreateDIBSection(w, h, ByRef ppvBits := 0, bpp := 32) {
   hDC := DllCall("GetDC", "Ptr", 0, "Ptr")
   VarSetCapacity(BITMAPINFO, 40, 0)
   NumPut(40 , BITMAPINFO,  0)
   NumPut( w , BITMAPINFO,  4)
   NumPut( h , BITMAPINFO,  8)
   NumPut( 1 , BITMAPINFO, 12)
   NumPut(bpp, BITMAPINFO, 14)
   hBM := DllCall("CreateDIBSection", "Ptr", hDC, "Ptr", &BITMAPINFO, "UInt", 0
                                    , "PtrP", ppvBits, "Ptr", 0, "UInt", 0, "Ptr")
   DllCall("ReleaseDC", "Ptr", 0, "Ptr", hDC)
   return hBM
}

HBitmapToPng(hBitmap, destPngFilePath) {
   static CLSID_WICImagingFactory  := "{CACAF262-9370-4615-A13B-9F5539DA4C0A}"
         , IID_IWICImagingFactory  := "{EC5EC8A9-C395-4314-9C77-54D7A935FF70}"
         , GUID_ContainerFormatPng := "{1B7CFAF4-713F-473C-BBCD-6137425FAEAF}"
         , WICBitmapUseAlpha := 0x00000000, GENERIC_WRITE := 0x40000000
         , WICBitmapEncoderNoCache := 0x00000002
         
   VarSetCapacity(GUID, 16, 0)
   DllCall("Ole32\CLSIDFromString", "WStr", GUID_ContainerFormatPng, "Ptr", &GUID)
   IWICImagingFactory := ComObjCreate(CLSID_WICImagingFactory, IID_IWICImagingFactory)
   Vtable( IWICImagingFactory    , CreateBitmapFromHBITMAP := 21 ).Call("Ptr", hBitmap, "Ptr", 0, "UInt", WICBitmapUseAlpha, "PtrP", IWICBitmap)
   Vtable( IWICImagingFactory    , CreateStream            := 14 ).Call("PtrP", IWICStream)
   Vtable( IWICStream            , InitializeFromFilename  := 15 ).Call("WStr", destPngFilePath, "UInt", GENERIC_WRITE)
   Vtable( IWICImagingFactory    , CreateEncoder           :=  8 ).Call("Ptr", &GUID, "Ptr", 0, "PtrP", IWICBitmapEncoder)
   Vtable( IWICBitmapEncoder     , Initialize              :=  3 ).Call("Ptr", IWICStream, "UInt", WICBitmapEncoderNoCache)
   Vtable( IWICBitmapEncoder     , CreateNewFrame          := 10 ).Call("PtrP", IWICBitmapFrameEncode, "Ptr", 0)
   Vtable( IWICBitmapFrameEncode , Initialize              :=  3 ).Call("Ptr", 0)
   Vtable( IWICBitmapFrameEncode , WriteSource             := 11 ).Call("Ptr", IWICBitmap, "Ptr", 0)
   Vtable( IWICBitmapFrameEncode , Commit                  := 12 ).Call()
   Vtable( IWICBitmapEncoder     , Commit                  := 11 ).Call()
   for k, v in [IWICBitmapFrameEncode, IWICBitmapEncoder, IWICStream, IWICBitmap, IWICImagingFactory]
      ObjRelease(v)
}

Vtable(ptr, n) {
   return Func("DllCall").Bind(NumGet(NumGet(ptr+0), A_PtrSize*n), "Ptr", ptr)
}

Hello,
Thank you for this wonderful code, please note that:
It works in Win10. But unfortunately is not working in Windows XP. (May be due to errgedit values). Please is there a way to make it also work under Windows XP ?
teadrinker
Posts: 4354
Joined: 29 Mar 2015, 09:41
Contact:

Re: How to save image in clipboard.

25 Apr 2024, 16:10

Perhaps this will work on Windows XP:

Code: Select all

destPngFilePath := A_ScriptDir . "\ClipboardImage.png"
hBitmap := GetBitmapFromClipboard()
HBitmapToPng(hBitmap, destPngFilePath)
DllCall("DeleteObject", "Ptr", hBitmap)
Return

GetBitmapFromClipboard() {
   static CF_BITMAP := 2, CF_DIB := 8, SRCCOPY := 0x00CC0020
   if !DllCall("IsClipboardFormatAvailable", "UInt", CF_BITMAP)
      throw "There is no image in the Clipboard"
   if !DllCall("OpenClipboard", "Ptr", 0)
      throw "OpenClipboard failed"
   hDIB := DllCall("GetClipboardData", "UInt", CF_DIB, "Ptr")
   hBM  := DllCall("GetClipboardData", "UInt", CF_BITMAP, "Ptr")
   DllCall("CloseClipboard")
   if !hDIB
      throw "GetClipboardData failed"
   pDIB := DllCall("GlobalLock", "Ptr", hDIB, "Ptr")
   width  := NumGet(pDIB +  4, "UInt")
   height := NumGet(pDIB +  8, "UInt")
   bpp    := NumGet(pDIB + 14, "UShort")
   DllCall("GlobalUnlock", "Ptr", pDIB)
   
   hDC := DllCall("CreateCompatibleDC", "Ptr", 0, "Ptr")
   oBM := DllCall("SelectObject", "Ptr", hDC, "Ptr", hBM, "Ptr")
   
   hMDC := DllCall("CreateCompatibleDC", "Ptr", 0, "Ptr")
   hNewBM := CreateDIBSection(width, -height,, bpp)
   oPrevBM := DllCall("SelectObject", "Ptr", hMDC, "Ptr", hNewBM, "Ptr")
   DllCall("BitBlt", "Ptr", hMDC, "Int", 0, "Int", 0, "Int", width, "Int", height
                   , "Ptr", hDC , "Int", 0, "Int", 0, "UInt", SRCCOPY)
   DllCall("SelectObject", "Ptr", hDC, "Ptr", oBM, "Ptr")
   DllCall("DeleteDC", "Ptr", hDC), DllCall("DeleteObject", "Ptr", hBM)
   DllCall("SelectObject", "Ptr", hMDC, "Ptr", oPrevBM, "Ptr")
   DllCall("DeleteDC", "Ptr", hMDC)
   Return hNewBM
}

CreateDIBSection(w, h, ByRef ppvBits := 0, bpp := 32) {
   hDC := DllCall("GetDC", "Ptr", 0, "Ptr")
   VarSetCapacity(BITMAPINFO, 40, 0)
   NumPut(40 , BITMAPINFO,  0)
   NumPut( w , BITMAPINFO,  4)
   NumPut( h , BITMAPINFO,  8)
   NumPut( 1 , BITMAPINFO, 12)
   NumPut(bpp, BITMAPINFO, 14)
   hBM := DllCall("CreateDIBSection", "Ptr", hDC, "Ptr", &BITMAPINFO, "UInt", 0
                                    , "PtrP", ppvBits, "Ptr", 0, "UInt", 0, "Ptr")
   DllCall("ReleaseDC", "Ptr", 0, "Ptr", hDC)
   return hBM
}

HBitmapToPng(hBitmap, destPngFilePath) {
   gdip := new GDIplus()
   pBitmap := gdip.CreateBitmapFromHBITMAP(hBitmap)
   gdip.SaveBitmap(pBitmap, destPngFilePath)
   gdip.DisposeImage(pBitmap)
}

class GDIplus  {
   __New() {
      this.hLib := DllCall("LoadLibrary", "Str", "gdiplus", "Ptr")
      VarSetCapacity(si, 8 + A_PtrSize*2, 0), si := Chr(1)
      DllCall("gdiplus\GdiplusStartup", "PtrP", pToken, "Ptr", &si, "Ptr", 0)
      this.token := pToken
   }
   
   __Delete() {
      DllCall("gdiplus\GdiplusShutdown", "Ptr", this.token)
      DllCall("FreeLibrary", "Ptr", this.hLib)
   }

   CreateBitmapFromHBITMAP(hBitmap, Palette=0)  {
      DllCall("gdiplus\GdipCreateBitmapFromHBITMAP", "Ptr", hBitmap, "Ptr", Palette, "PtrP", pBitmap)
      return pBitmap
   }

   SaveBitmap(pBitmap, ByRef info, Quality := 75, tobuff := "")
   {
      ; info — if copying to buffer, then file extension, if copying to file, then file path
      if tobuff
         Extension := info
      else
         SplitPath, info,,, Extension
      if Extension not in BMP,DIB,RLE,JPG,JPEG,JPE,JFIF,GIF,TIF,TIFF,PNG
         return -1

      DllCall("gdiplus\GdipGetImageEncodersSize", "UintP", nCount, "UintP", nSize)
      VarSetCapacity(ci, nSize)
      DllCall("gdiplus\GdipGetImageEncoders", "UInt", nCount, "UInt", nSize, "Ptr", &ci)
      if !(nCount && nSize)
         return -2
      
      Loop, % nCount  {
         sString := StrGet(NumGet(ci, (idx := (48+7*A_PtrSize)*(A_Index-1))+32+3*A_PtrSize), "UTF-16")
         if !InStr(sString, "*." Extension)
            continue
         
         pCodec := &ci+idx
         break
      }
      
      if !pCodec
         return -3
      
      if RegExMatch(Extension, "i)^J(PG|PEG|PE|FIF)$") && Quality != 75  {
         DllCall("gdiplus\GdipGetEncoderParameterListSize", "Ptr", pBitmap, "Ptr", pCodec, "UintP", nSize)
         VarSetCapacity(EncoderParameters, nSize, 0)
         DllCall("gdiplus\GdipGetEncoderParameterList", "Ptr", pBitmap, "Ptr", pCodec, "UInt", nSize, "Ptr", &EncoderParameters)
         Loop, % NumGet(EncoderParameters, "UInt")
         {
            elem := (24+A_PtrSize)*(A_Index-1) + 4 + (pad := A_PtrSize = 8 ? 4 : 0)
            if (NumGet(EncoderParameters, elem+16, "UInt") = 1) && (NumGet(EncoderParameters, elem+20, "UInt") = 6)
            {
               p := elem+&EncoderParameters-pad-4
               NumPut(Quality, NumGet(NumPut(4, NumPut(1, p+0)+20, "UInt")), "UInt")
               break
            }
         }      
      }
      if !tobuff
         E := DllCall("gdiplus\GdipSaveImageToFile", "Ptr", pBitmap, "WStr", info, "Ptr", pCodec, "UInt", p ? p : 0)
      else  {
         DllCall( "ole32\CreateStreamOnHGlobal", "UInt", 0, "Int", 1, "PtrP", pStream )
         if !E := DllCall( "gdiplus\GdipSaveImageToStream", "Ptr", pBitmap, "Ptr", pStream, "Ptr", pCodec, "UInt", p ? p : 0 )  {
            DllCall( "ole32\GetHGlobalFromStream", "Ptr", pStream, "PtrP", hData )
            pData := DllCall( "GlobalLock", "Ptr", hData, "Ptr" )
            nSize := DllCall( "GlobalSize", "Ptr", hData, "Ptr" )
            VarSetCapacity( info, 0), VarSetCapacity( info, nSize, 0 )
            DllCall( "RtlMoveMemory", "Ptr", &info, "Ptr", pData, "UInt", nSize )
            DllCall( "GlobalUnlock", "Ptr", hData )
            DllCall( "GlobalFree", "Ptr", hData )
         }
         ObjRelease(pStream)
      }
      return E ? -4 : tobuff ? nSize : 0
   }
   
   DisposeImage(pBitmap)  {
      return DllCall("gdiplus\GdipDisposeImage", "Ptr", pBitmap)
   }
}
GEOVAN
Posts: 174
Joined: 03 Mar 2022, 11:12

Re: How to save image in clipboard.

25 Apr 2024, 22:57

teadrinker wrote:
25 Apr 2024, 16:10
Perhaps this will work on Windows XP:

Code: Select all

destPngFilePath := A_ScriptDir . "\ClipboardImage.png"
hBitmap := GetBitmapFromClipboard()
HBitmapToPng(hBitmap, destPngFilePath)
DllCall("DeleteObject", "Ptr", hBitmap)
Return

GetBitmapFromClipboard() {
   static CF_BITMAP := 2, CF_DIB := 8, SRCCOPY := 0x00CC0020
   if !DllCall("IsClipboardFormatAvailable", "UInt", CF_BITMAP)
      throw "There is no image in the Clipboard"
   if !DllCall("OpenClipboard", "Ptr", 0)
      throw "OpenClipboard failed"
   hDIB := DllCall("GetClipboardData", "UInt", CF_DIB, "Ptr")
   hBM  := DllCall("GetClipboardData", "UInt", CF_BITMAP, "Ptr")
   DllCall("CloseClipboard")
   if !hDIB
      throw "GetClipboardData failed"
   pDIB := DllCall("GlobalLock", "Ptr", hDIB, "Ptr")
   width  := NumGet(pDIB +  4, "UInt")
   height := NumGet(pDIB +  8, "UInt")
   bpp    := NumGet(pDIB + 14, "UShort")
   DllCall("GlobalUnlock", "Ptr", pDIB)
   
   hDC := DllCall("CreateCompatibleDC", "Ptr", 0, "Ptr")
   oBM := DllCall("SelectObject", "Ptr", hDC, "Ptr", hBM, "Ptr")
   
   hMDC := DllCall("CreateCompatibleDC", "Ptr", 0, "Ptr")
   hNewBM := CreateDIBSection(width, -height,, bpp)
   oPrevBM := DllCall("SelectObject", "Ptr", hMDC, "Ptr", hNewBM, "Ptr")
   DllCall("BitBlt", "Ptr", hMDC, "Int", 0, "Int", 0, "Int", width, "Int", height
                   , "Ptr", hDC , "Int", 0, "Int", 0, "UInt", SRCCOPY)
   DllCall("SelectObject", "Ptr", hDC, "Ptr", oBM, "Ptr")
   DllCall("DeleteDC", "Ptr", hDC), DllCall("DeleteObject", "Ptr", hBM)
   DllCall("SelectObject", "Ptr", hMDC, "Ptr", oPrevBM, "Ptr")
   DllCall("DeleteDC", "Ptr", hMDC)
   Return hNewBM
}

CreateDIBSection(w, h, ByRef ppvBits := 0, bpp := 32) {
   hDC := DllCall("GetDC", "Ptr", 0, "Ptr")
   VarSetCapacity(BITMAPINFO, 40, 0)
   NumPut(40 , BITMAPINFO,  0)
   NumPut( w , BITMAPINFO,  4)
   NumPut( h , BITMAPINFO,  8)
   NumPut( 1 , BITMAPINFO, 12)
   NumPut(bpp, BITMAPINFO, 14)
   hBM := DllCall("CreateDIBSection", "Ptr", hDC, "Ptr", &BITMAPINFO, "UInt", 0
                                    , "PtrP", ppvBits, "Ptr", 0, "UInt", 0, "Ptr")
   DllCall("ReleaseDC", "Ptr", 0, "Ptr", hDC)
   return hBM
}

HBitmapToPng(hBitmap, destPngFilePath) {
   gdip := new GDIplus()
   pBitmap := gdip.CreateBitmapFromHBITMAP(hBitmap)
   gdip.SaveBitmap(pBitmap, destPngFilePath)
   gdip.DisposeImage(pBitmap)
}

class GDIplus  {
   __New() {
      this.hLib := DllCall("LoadLibrary", "Str", "gdiplus", "Ptr")
      VarSetCapacity(si, 8 + A_PtrSize*2, 0), si := Chr(1)
      DllCall("gdiplus\GdiplusStartup", "PtrP", pToken, "Ptr", &si, "Ptr", 0)
      this.token := pToken
   }
   
   __Delete() {
      DllCall("gdiplus\GdiplusShutdown", "Ptr", this.token)
      DllCall("FreeLibrary", "Ptr", this.hLib)
   }

   CreateBitmapFromHBITMAP(hBitmap, Palette=0)  {
      DllCall("gdiplus\GdipCreateBitmapFromHBITMAP", "Ptr", hBitmap, "Ptr", Palette, "PtrP", pBitmap)
      return pBitmap
   }

   SaveBitmap(pBitmap, ByRef info, Quality := 75, tobuff := "")
   {
      ; info — if copying to buffer, then file extension, if copying to file, then file path
      if tobuff
         Extension := info
      else
         SplitPath, info,,, Extension
      if Extension not in BMP,DIB,RLE,JPG,JPEG,JPE,JFIF,GIF,TIF,TIFF,PNG
         return -1

      DllCall("gdiplus\GdipGetImageEncodersSize", "UintP", nCount, "UintP", nSize)
      VarSetCapacity(ci, nSize)
      DllCall("gdiplus\GdipGetImageEncoders", "UInt", nCount, "UInt", nSize, "Ptr", &ci)
      if !(nCount && nSize)
         return -2
      
      Loop, % nCount  {
         sString := StrGet(NumGet(ci, (idx := (48+7*A_PtrSize)*(A_Index-1))+32+3*A_PtrSize), "UTF-16")
         if !InStr(sString, "*." Extension)
            continue
         
         pCodec := &ci+idx
         break
      }
      
      if !pCodec
         return -3
      
      if RegExMatch(Extension, "i)^J(PG|PEG|PE|FIF)$") && Quality != 75  {
         DllCall("gdiplus\GdipGetEncoderParameterListSize", "Ptr", pBitmap, "Ptr", pCodec, "UintP", nSize)
         VarSetCapacity(EncoderParameters, nSize, 0)
         DllCall("gdiplus\GdipGetEncoderParameterList", "Ptr", pBitmap, "Ptr", pCodec, "UInt", nSize, "Ptr", &EncoderParameters)
         Loop, % NumGet(EncoderParameters, "UInt")
         {
            elem := (24+A_PtrSize)*(A_Index-1) + 4 + (pad := A_PtrSize = 8 ? 4 : 0)
            if (NumGet(EncoderParameters, elem+16, "UInt") = 1) && (NumGet(EncoderParameters, elem+20, "UInt") = 6)
            {
               p := elem+&EncoderParameters-pad-4
               NumPut(Quality, NumGet(NumPut(4, NumPut(1, p+0)+20, "UInt")), "UInt")
               break
            }
         }      
      }
      if !tobuff
         E := DllCall("gdiplus\GdipSaveImageToFile", "Ptr", pBitmap, "WStr", info, "Ptr", pCodec, "UInt", p ? p : 0)
      else  {
         DllCall( "ole32\CreateStreamOnHGlobal", "UInt", 0, "Int", 1, "PtrP", pStream )
         if !E := DllCall( "gdiplus\GdipSaveImageToStream", "Ptr", pBitmap, "Ptr", pStream, "Ptr", pCodec, "UInt", p ? p : 0 )  {
            DllCall( "ole32\GetHGlobalFromStream", "Ptr", pStream, "PtrP", hData )
            pData := DllCall( "GlobalLock", "Ptr", hData, "Ptr" )
            nSize := DllCall( "GlobalSize", "Ptr", hData, "Ptr" )
            VarSetCapacity( info, 0), VarSetCapacity( info, nSize, 0 )
            DllCall( "RtlMoveMemory", "Ptr", &info, "Ptr", pData, "UInt", nSize )
            DllCall( "GlobalUnlock", "Ptr", hData )
            DllCall( "GlobalFree", "Ptr", hData )
         }
         ObjRelease(pStream)
      }
      return E ? -4 : tobuff ? nSize : 0
   }
   
   DisposeImage(pBitmap)  {
      return DllCall("gdiplus\GdipDisposeImage", "Ptr", pBitmap)
   }
}


Wonderful!!! Thank you very much, this is working in all Windows Versions from XP to Win10.
Please let me ask:
If we want to save as JPG with 100% quality, we must change in the above code the PNG with JPG and the 75 with 100.
Is there a way to add the quality change in the beggining of the code - something like the code you wrote in:
viewtopic.php?t=112783

Code: Select all

jpegFilePath := A_Desktop . "\MyImageFile.jpeg" ; specify the file path you prefer
quality := 1 ; specify quality from 0 to 1, where 1 is 100%

hBitmap := GetBitmapFromClipboard()
SaveBitmapToJpeg(hBitmap, jpegFilePath, quality)
DllCall("DeleteObject", "Ptr", hBitmap)

GetBitmapFromClipboard() {
   static CF_BITMAP := 2, CF_DIB := 8, SRCCOPY := 0x00CC0020
   if !DllCall("IsClipboardFormatAvailable", "UInt", CF_BITMAP)
      throw "There is no image in the Clipboard"
   if !DllCall("OpenClipboard", "Ptr", 0)
      throw "OpenClipboard failed"
   hDIB := DllCall("GetClipboardData", "UInt", CF_DIB, "Ptr")
   hBM := DllCall("GetClipboardData", "UInt", CF_BITMAP, "Ptr")
   DllCall("CloseClipboard")
   if !hDIB
      throw "GetClipboardData failed"
   pDIB := DllCall("GlobalLock", "Ptr", hDIB, "Ptr")
   width := NumGet(pDIB + 4, "UInt")
   height := NumGet(pDIB + 8, "UInt")
   bpp := NumGet(pDIB + 14, "UShort")
   DllCall("GlobalUnlock", "Ptr", pDIB)

   hDC := DllCall("CreateCompatibleDC", "Ptr", 0, "Ptr")
   oBM := DllCall("SelectObject", "Ptr", hDC, "Ptr", hBM, "Ptr")

   hMDC := DllCall("CreateCompatibleDC", "Ptr", 0, "Ptr")
   hNewBM := CreateDIBSection(width, -height,, bpp)
   oPrevBM := DllCall("SelectObject", "Ptr", hMDC, "Ptr", hNewBM, "Ptr")
   DllCall("BitBlt", "Ptr", hMDC, "Int", 0, "Int", 0, "Int", width, "Int", height
      , "Ptr", hDC , "Int", 0, "Int", 0, "UInt", SRCCOPY)
   DllCall("SelectObject", "Ptr", hDC, "Ptr", oBM, "Ptr")
   DllCall("DeleteDC", "Ptr", hDC), DllCall("DeleteObject", "Ptr", hBM)
   DllCall("SelectObject", "Ptr", hMDC, "Ptr", oPrevBM, "Ptr")
   DllCall("DeleteDC", "Ptr", hMDC)
   Return hNewBM
}

CreateDIBSection(w, h, ByRef ppvBits := 0, bpp := 32) {
   hDC := DllCall("GetDC", "Ptr", 0, "Ptr")
   VarSetCapacity(BITMAPINFO, 40, 0)
   NumPut(40 , BITMAPINFO, 0)
   NumPut( w , BITMAPINFO, 4)
   NumPut( h , BITMAPINFO, 8)
   NumPut( 1 , BITMAPINFO, 12)
   NumPut(bpp, BITMAPINFO, 14)
   hBM := DllCall("CreateDIBSection", "Ptr", hDC, "Ptr", &BITMAPINFO, "UInt", 0
      , "PtrP", ppvBits, "Ptr", 0, "UInt", 0, "Ptr")
   DllCall("ReleaseDC", "Ptr", 0, "Ptr", hDC)
   return hBM
}

SaveBitmapToJpeg(hBitmap, destJpegFilePath, quality := 0.75) {
    static CLSID_WICImagingFactory  := "{CACAF262-9370-4615-A13B-9F5539DA4C0A}"
         , IID_IWICImagingFactory   := "{EC5EC8A9-C395-4314-9C77-54D7A935FF70}"
         , GUID_ContainerFormatJpeg := "{19E4A5AA-5662-4FC5-A0C0-1758028E1057}"
         , WICBitmapIgnoreAlpha := 0x2, GENERIC_WRITE := 0x40000000, VT_R4 := 0x00000004
         , WICBitmapEncoderNoCache := 0x00000002, szPROPBAG2 := 24 + A_PtrSize*2

   VarSetCapacity(GUID, 16, 0)
   DllCall("Ole32\CLSIDFromString", "WStr", GUID_ContainerFormatJpeg, "Ptr", &GUID)
   IWICImagingFactory := ComObjCreate(CLSID_WICImagingFactory, IID_IWICImagingFactory)
   Vtable( IWICImagingFactory    , CreateBitmapFromHBITMAP := 21 ).Call("Ptr", hBitmap, "Ptr", 0, "UInt", WICBitmapIgnoreAlpha, "PtrP", IWICBitmap)
   Vtable( IWICImagingFactory    , CreateStream            := 14 ).Call("PtrP", IWICStream)
   Vtable( IWICStream            , InitializeFromFilename  := 15 ).Call("WStr", destJpegFilePath, "UInt", GENERIC_WRITE)
   Vtable( IWICImagingFactory    , CreateEncoder           :=  8 ).Call("Ptr", &GUID, "Ptr", 0, "PtrP", IWICBitmapEncoder)
   Vtable( IWICBitmapEncoder     , Initialize              :=  3 ).Call("Ptr", IWICStream, "UInt", WICBitmapEncoderNoCache)
   Vtable( IWICBitmapEncoder     , CreateNewFrame          := 10 ).Call("PtrP", IWICBitmapFrameEncode, "PtrP", IPropertyBag2)

   Vtable( IPropertyBag2         , CountProperties         :=  5 ).Call("UIntP", count)
   VarSetCapacity(arrPROPBAG2    , szPROPBAG2*count, 0)
   Vtable( IPropertyBag2         , GetPropertyInfo         :=  6 ).Call("UInt", 0, "UInt", count, "Ptr", &arrPROPBAG2, "UIntP", read)
   Loop % read
      addr := &arrPROPBAG2 + szPROPBAG2*(A_Index - 1)
   until StrGet(NumGet(addr + 8 + A_PtrSize)) = "ImageQuality" && found := true
   if found {
      VarSetCapacity(variant, 24, 0)
      NumPut(VT_R4, variant)
      NumPut(quality, variant, 8, "Float")
      Vtable( IPropertyBag2, Write := 4 ).Call("UInt", 1, "Ptr", addr, "Ptr", &variant)
   }
   Vtable( IWICBitmapFrameEncode , Initialize              :=  3 ).Call("Ptr", IPropertyBag2)
   Vtable( IWICBitmapFrameEncode , WriteSource             := 11 ).Call("Ptr", IWICBitmap, "Ptr", 0)
   Vtable( IWICBitmapFrameEncode , Commit                  := 12 ).Call()
   Vtable( IWICBitmapEncoder     , Commit                  := 11 ).Call()
   for k, v in [IWICBitmapFrameEncode, IWICBitmapEncoder, IPropertyBag2, IWICStream, IWICBitmap, IWICImagingFactory]
      ObjRelease(v)
}

Vtable(ptr, n) {
   return Func("DllCall").Bind(NumGet(NumGet(ptr+0), A_PtrSize*n), "Ptr", ptr)
}
[/quote]
teadrinker
Posts: 4354
Joined: 29 Mar 2015, 09:41
Contact:

Re: How to save image in clipboard.

26 Apr 2024, 13:53

Sure.

Code: Select all

SaveBitmapFromClipboard(A_ScriptDir . "\ClipboardImage.jpg", 100)

SaveBitmapFromClipboard(destImageFilePath, jpegQuality := 75) {
   hBitmap := GetBitmapFromClipboard()
   HBitmapToImageFile(hBitmap, destImageFilePath, 100)
   DllCall("DeleteObject", "Ptr", hBitmap)
}

GetBitmapFromClipboard() {
   static CF_BITMAP := 2, CF_DIB := 8, SRCCOPY := 0x00CC0020
   if !DllCall("IsClipboardFormatAvailable", "UInt", CF_BITMAP)
      throw "There is no image in the Clipboard"
   if !DllCall("OpenClipboard", "Ptr", 0)
      throw "OpenClipboard failed"
   hDIB := DllCall("GetClipboardData", "UInt", CF_DIB, "Ptr")
   hBM  := DllCall("GetClipboardData", "UInt", CF_BITMAP, "Ptr")
   DllCall("CloseClipboard")
   if !hDIB
      throw "GetClipboardData failed"
   pDIB := DllCall("GlobalLock", "Ptr", hDIB, "Ptr")
   width  := NumGet(pDIB +  4, "UInt")
   height := NumGet(pDIB +  8, "UInt")
   bpp    := NumGet(pDIB + 14, "UShort")
   DllCall("GlobalUnlock", "Ptr", pDIB)
   
   hDC := DllCall("CreateCompatibleDC", "Ptr", 0, "Ptr")
   oBM := DllCall("SelectObject", "Ptr", hDC, "Ptr", hBM, "Ptr")
   
   hMDC := DllCall("CreateCompatibleDC", "Ptr", 0, "Ptr")
   hNewBM := CreateDIBSection(width, -height,, bpp)
   oPrevBM := DllCall("SelectObject", "Ptr", hMDC, "Ptr", hNewBM, "Ptr")
   DllCall("BitBlt", "Ptr", hMDC, "Int", 0, "Int", 0, "Int", width, "Int", height
                   , "Ptr", hDC , "Int", 0, "Int", 0, "UInt", SRCCOPY)
   DllCall("SelectObject", "Ptr", hDC, "Ptr", oBM, "Ptr")
   DllCall("DeleteDC", "Ptr", hDC), DllCall("DeleteObject", "Ptr", hBM)
   DllCall("SelectObject", "Ptr", hMDC, "Ptr", oPrevBM, "Ptr")
   DllCall("DeleteDC", "Ptr", hMDC)
   Return hNewBM
}

CreateDIBSection(w, h, ByRef ppvBits := 0, bpp := 32) {
   hDC := DllCall("GetDC", "Ptr", 0, "Ptr")
   VarSetCapacity(BITMAPINFO, 40, 0)
   NumPut(40 , BITMAPINFO,  0)
   NumPut( w , BITMAPINFO,  4)
   NumPut( h , BITMAPINFO,  8)
   NumPut( 1 , BITMAPINFO, 12)
   NumPut(bpp, BITMAPINFO, 14)
   hBM := DllCall("CreateDIBSection", "Ptr", hDC, "Ptr", &BITMAPINFO, "UInt", 0
                                    , "PtrP", ppvBits, "Ptr", 0, "UInt", 0, "Ptr")
   DllCall("ReleaseDC", "Ptr", 0, "Ptr", hDC)
   return hBM
}

HBitmapToImageFile(hBitmap, destImageFilePath, jpegQuality := 75) {
   gdip := new GDIplus()
   pBitmap := gdip.CreateBitmapFromHBITMAP(hBitmap)
   gdip.SaveBitmap(pBitmap, destImageFilePath, jpegQuality)
   gdip.DisposeImage(pBitmap)
}

class GDIplus  {
   __New() {
      this.hLib := DllCall("LoadLibrary", "Str", "gdiplus", "Ptr")
      VarSetCapacity(si, 8 + A_PtrSize*2, 0), si := Chr(1)
      DllCall("gdiplus\GdiplusStartup", "PtrP", pToken, "Ptr", &si, "Ptr", 0)
      this.token := pToken
   }
   
   __Delete() {
      DllCall("gdiplus\GdiplusShutdown", "Ptr", this.token)
      DllCall("FreeLibrary", "Ptr", this.hLib)
   }

   CreateBitmapFromHBITMAP(hBitmap, Palette=0)  {
      DllCall("gdiplus\GdipCreateBitmapFromHBITMAP", "Ptr", hBitmap, "Ptr", Palette, "PtrP", pBitmap)
      return pBitmap
   }

   SaveBitmap(pBitmap, ByRef info, Quality := 75, tobuff := "")
   {
      ; info — if copying to buffer, then file extension, if copying to file, then file path
      if tobuff
         Extension := info
      else
         SplitPath, info,,, Extension
      if Extension not in BMP,DIB,RLE,JPG,JPEG,JPE,JFIF,GIF,TIF,TIFF,PNG
         return -1

      DllCall("gdiplus\GdipGetImageEncodersSize", "UintP", nCount, "UintP", nSize)
      VarSetCapacity(ci, nSize)
      DllCall("gdiplus\GdipGetImageEncoders", "UInt", nCount, "UInt", nSize, "Ptr", &ci)
      if !(nCount && nSize)
         return -2
      
      Loop, % nCount  {
         sString := StrGet(NumGet(ci, (idx := (48+7*A_PtrSize)*(A_Index-1))+32+3*A_PtrSize), "UTF-16")
         if !InStr(sString, "*." Extension)
            continue
         
         pCodec := &ci+idx
         break
      }
      
      if !pCodec
         return -3
      
      if RegExMatch(Extension, "i)^J(PG|PEG|PE|FIF)$") && Quality != 75  {
         DllCall("gdiplus\GdipGetEncoderParameterListSize", "Ptr", pBitmap, "Ptr", pCodec, "UintP", nSize)
         VarSetCapacity(EncoderParameters, nSize, 0)
         DllCall("gdiplus\GdipGetEncoderParameterList", "Ptr", pBitmap, "Ptr", pCodec, "UInt", nSize, "Ptr", &EncoderParameters)
         Loop, % NumGet(EncoderParameters, "UInt")
         {
            elem := (24+A_PtrSize)*(A_Index-1) + 4 + (pad := A_PtrSize = 8 ? 4 : 0)
            if (NumGet(EncoderParameters, elem+16, "UInt") = 1) && (NumGet(EncoderParameters, elem+20, "UInt") = 6)
            {
               p := elem+&EncoderParameters-pad-4
               NumPut(Quality, NumGet(NumPut(4, NumPut(1, p+0)+20, "UInt")), "UInt")
               break
            }
         }      
      }
      if !tobuff
         E := DllCall("gdiplus\GdipSaveImageToFile", "Ptr", pBitmap, "WStr", info, "Ptr", pCodec, "UInt", p ? p : 0)
      else  {
         DllCall( "ole32\CreateStreamOnHGlobal", "UInt", 0, "Int", 1, "PtrP", pStream )
         if !E := DllCall( "gdiplus\GdipSaveImageToStream", "Ptr", pBitmap, "Ptr", pStream, "Ptr", pCodec, "UInt", p ? p : 0 )  {
            DllCall( "ole32\GetHGlobalFromStream", "Ptr", pStream, "PtrP", hData )
            pData := DllCall( "GlobalLock", "Ptr", hData, "Ptr" )
            nSize := DllCall( "GlobalSize", "Ptr", hData, "Ptr" )
            VarSetCapacity( info, 0), VarSetCapacity( info, nSize, 0 )
            DllCall( "RtlMoveMemory", "Ptr", &info, "Ptr", pData, "UInt", nSize )
            DllCall( "GlobalUnlock", "Ptr", hData )
            DllCall( "GlobalFree", "Ptr", hData )
         }
         ObjRelease(pStream)
      }
      return E ? -4 : tobuff ? nSize : 0
   }
   
   DisposeImage(pBitmap)  {
      return DllCall("gdiplus\GdipDisposeImage", "Ptr", pBitmap)
   }
}
GEOVAN
Posts: 174
Joined: 03 Mar 2022, 11:12

Re: How to save image in clipboard.

27 Apr 2024, 04:14

Perfect, Thank you very very much!!!
GEOVAN
Posts: 174
Joined: 03 Mar 2022, 11:12

Re: How to save image in clipboard.

27 Apr 2024, 09:49

Hello,
Please i find the following code, (from - viewtopic.php?f=6&t=67716&p=569569#p569569 ), which saves a screenshot at JPG at 75% quality.

Here is the code:

Code: Select all

hBM := CB_hBMP_Get() 
       sFile := A_ScriptDir "\\" A_Now ".jpg"              ; you can also save as jpg but quality = 75
       GDIP("Startup")
       SavePicture(hBM, sFile) 
       GDIP("Shutdown")
       DllCall( "DeleteObject", "Ptr",hBM )
       

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

CB_hBMP_Get() {                                                      ; By SKAN on D297 @ bit.ly/2L81pmP
Local OK := [0,0,0,0]
	OK.1 := DllCall( "OpenClipboard", "Ptr",0 )
  OK.2 := OK.1 ? DllCall( "IsClipboardFormatAvailable", "UInt",8 ) : 0  ; CF_BITMAP
  OK.3 := OK.2 ? DllCall( "GetClipboardData", "UInt", 2, "Ptr" )   : 0
  OK.4 := OK.1 ? DllCall( "CloseClipboard" ) : 0  
Return OK.3 ? DllCall( "CopyImage", "Ptr",OK.3, "Int",0, "Int",0, "Int",0, "UInt",0x200C, "Ptr" )
          + ( ErrorLevel := 0 ) : ( ErrorLevel := !OK.2 ? 1 : 2 ) >> 2          
}

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

SavePicture(hBM, sFile) {                                            ; By SKAN on D293 @ bit.ly/2L81pmP 
Local V,  pBM := VarSetCapacity(V,16,0)>>8,  Ext := LTrim(SubStr(sFile,-3),"."),  E := [0,0,0,0]
Local Enc := 0x557CF400 | Round({"bmp":0, "jpg":1,"jpeg":1,"gif":2,"tif":5,"tiff":5,"png":6}[Ext])
  E[1] := DllCall("gdi32\GetObjectType", "Ptr",hBM ) <> 7
  E[2] := E[1] ? 0 : DllCall("gdiplus\GdipCreateBitmapFromHBITMAP", "Ptr",hBM, "UInt",0, "PtrP",pBM)
  NumPut(0x2EF31EF8,NumPut(0x0000739A,NumPut(0x11D31A04,NumPut(Enc+0,V,"UInt"),"UInt"),"UInt"),"UInt")
  E[3] := pBM ? DllCall("gdiplus\GdipSaveImageToFile", "Ptr",pBM, "WStr",sFile, "Ptr",&V, "UInt",0) : 1
  E[4] := pBM ? DllCall("gdiplus\GdipDisposeImage", "Ptr",pBM) : 1
Return E[1] ? 0 : E[2] ? -1 : E[3] ? -2 : E[4] ? -3 : 1  
} 

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

GDIP(C:="Startup") {                                                 ; By SKAN on D293 @ bit.ly/2L81pmP
  Static SI:=Chr(!(VarSetCapacity(Si,24,0)>>16)), pToken:=0, hMod:=0, Res:=0, AOK:=0
  If (AOK := (C="Startup" and pToken=0) Or (C<>"Startup" and pToken<>0))  {
      If (C="Startup") {
               hMod := DllCall("LoadLibrary", "Str","gdiplus.dll", "Ptr")
               Res  := DllCall("gdiplus\GdiplusStartup", "PtrP",pToken, "Ptr",&SI, "UInt",0)
      } Else { 
               Res  := DllCall("gdiplus\GdiplusShutdown", "Ptr",pToken )
               DllCall("FreeLibrary", "Ptr",hMod),   hMod:=0,   pToken:=0
   }}  
Return (AOK ? !Res : Res:=0)    
}

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -


Please let me ask:
How can i edit the above code to get JPG at 100% quality, please?
teadrinker
Posts: 4354
Joined: 29 Mar 2015, 09:41
Contact:

Re: How to save image in clipboard.

27 Apr 2024, 12:54

I wouldn't really want to get into the details of someone else's code on v1, I only use v2 now. Why not just use my code, is it any worse?
GEOVAN
Posts: 174
Joined: 03 Mar 2022, 11:12

Re: How to save image in clipboard.

27 Apr 2024, 23:54

Hello,
Thank you dear @teadrinker , yes i will use your code because works perfect without any errors.

The reason i was wonder regarding the code from SKAN, is that it saves JPG to 75%, and i did not find any value inside the code to change it from 75 to 100 in order to get jpg at 100%.
Other formats, for example PNG are saved 100% qaulity.
Also i see that SKAN code is "less lines code" than your code, but ok this actually does not matter.

I am sure that there is a "line" in the code that can modify the code to always save JPG as 100% qaulity.
Just for educational purposes, i will appreciate if you can point me what is the part of the code that changes the 75 to 100%.

Once again i want to thank you dear @teadrinker, and also all the AHK Forum.
teadrinker
Posts: 4354
Joined: 29 Mar 2015, 09:41
Contact:

Re: How to save image in clipboard.

28 Apr 2024, 10:07

teadrinker wrote: I am sure that there is a "line" in the code that can modify the code to always save JPG as 100% qaulity.
Just for educational purposes, i will appreciate if you can point me what is the part of the code that changes the 75 to 100%.
Understood. In fact, saving a JPEG at 75% quality doesn't require any special steps since it's the default setting. But if you need to change the quality, additional actions are required. In my code the following lines are responsible for this:

Code: Select all

      if RegExMatch(Extension, "i)^J(PG|PEG|PE|FIF)$") && Quality != 75  {
         DllCall("gdiplus\GdipGetEncoderParameterListSize", "Ptr", pBitmap, "Ptr", pCodec, "UintP", nSize)
         VarSetCapacity(EncoderParameters, nSize, 0)
         DllCall("gdiplus\GdipGetEncoderParameterList", "Ptr", pBitmap, "Ptr", pCodec, "UInt", nSize, "Ptr", &EncoderParameters)
         Loop, % NumGet(EncoderParameters, "UInt")
         {
            elem := (24+A_PtrSize)*(A_Index-1) + 4 + (pad := A_PtrSize = 8 ? 4 : 0)
            if (NumGet(EncoderParameters, elem+16, "UInt") = 1) && (NumGet(EncoderParameters, elem+20, "UInt") = 6)
            {
               p := elem+&EncoderParameters-pad-4
               NumPut(Quality, NumGet(NumPut(4, NumPut(1, p+0)+20, "UInt")), "UInt")
               break
            }
         }      
      }
GEOVAN
Posts: 174
Joined: 03 Mar 2022, 11:12

Re: How to save image in clipboard.

28 Apr 2024, 11:40

Thank you very much, now i understand.
GEOVAN
Posts: 174
Joined: 03 Mar 2022, 11:12

Re: How to save image in clipboard.

08 May 2024, 15:20

Dear teadrinker, please let me further ask:

If i want to add ---> DllCall("DeleteObject", "Ptr", hBM) ; Clean-up

I modify "little" your code as follow:

Code: Select all

F2::  ; press F2 to save screenshot

hBM := ""
hBM := GDI_CaptureScreen( 0, 0, A_ScreenWidth, A_ScreenHeight )

SaveToImageFile(hBM, A_ScriptDir . "\ClipboardImage.jpg", 100)

DllCall("DeleteObject", "Ptr", hBM)  ; Clean-up

Return




;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

GDI_CaptureScreen( X, Y, W, H, ByRef Checksum="" )  {
 tDC := DllCall( "CreateCompatibleDC", UInt,0 )
 hBM := DllCall( "CopyImage", UInt,DllCall( "CreateBitmap", Int,W, Int,H, UInt,1, UInt,24
                            , UInt,0 ), UInt,0, Int,0, Int,0, UInt,0x2008, UInt )
 oBM := DllCall( "SelectObject", UInt,tDC, UInt,hBM ), hDC := DllCall( "GetDC", UInt,0 )
 DllCall( "BitBlt"
     , UInt,tDC, UInt,0, UInt,0, Int,W, Int,H, UInt,hDC, UInt,X, UInt,Y, UInt,0x00CC0020 )
 DllCall( "ReleaseDC", UInt,0, UInt,hDC ),   DllCall( "SelectObject", UInt,tDC, UInt,oBM )
 If ( Checksum <> "" )
   VarSetCapacity( BM,24,0 ), DllCall( "GetObject", UInt,hBM, UInt,24, UInt,&BM )
 , DllCall( "shlwapi\HashData", UInt,NumGet(BM,20), UInt,NumGet( BM,12 )*NumGet( BM,8 )
                              , Int64P,Checksum, UInt,7 )
 Return hBM, DllCall( "DeleteDC", UInt,tDC )
}


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

SaveToImageFile(hBM, destImageFilePath, jpegQuality := 75) {
   gdip := new GDIplus()
   pBM := gdip.CreateBitmapFromHBITMAP(hBM)
   gdip.SaveBitmap(pBM, destImageFilePath, jpegQuality)
   gdip.DisposeImage(pBM)
}

class GDIplus  {
   __New() {
      this.hLib := DllCall("LoadLibrary", "Str", "gdiplus", "Ptr")
      VarSetCapacity(si, 8 + A_PtrSize*2, 0), si := Chr(1)
      DllCall("gdiplus\GdiplusStartup", "PtrP", pToken, "Ptr", &si, "Ptr", 0)
      this.token := pToken
   }
   
   __Delete() {
      DllCall("gdiplus\GdiplusShutdown", "Ptr", this.token)
      DllCall("FreeLibrary", "Ptr", this.hLib)
   }

   CreateBitmapFromHBITMAP(hBM, Palette=0)  {
      DllCall("gdiplus\GdipCreateBitmapFromHBITMAP", "Ptr", hBM, "Ptr", Palette, "PtrP", pBM)
      return pBM
   }

   SaveBitmap(pBM, ByRef info, Quality := 75, tobuff := "")
   {
      ; info — if copying to buffer, then file extension, if copying to file, then file path
      if tobuff
         Extension := info
      else
         SplitPath, info,,, Extension
      if Extension not in BMP,DIB,RLE,JPG,JPEG,JPE,JFIF,GIF,TIF,TIFF,PNG
         return -1

      DllCall("gdiplus\GdipGetImageEncodersSize", "UintP", nCount, "UintP", nSize)
      VarSetCapacity(ci, nSize)
      DllCall("gdiplus\GdipGetImageEncoders", "UInt", nCount, "UInt", nSize, "Ptr", &ci)
      if !(nCount && nSize)
         return -2
      
      Loop, % nCount  {
         sString := StrGet(NumGet(ci, (idx := (48+7*A_PtrSize)*(A_Index-1))+32+3*A_PtrSize), "UTF-16")
         if !InStr(sString, "*." Extension)
            continue
         
         pCodec := &ci+idx
         break
      }
      
      if !pCodec
         return -3
      
      if RegExMatch(Extension, "i)^J(PG|PEG|PE|FIF)$") && Quality != 75  {
         DllCall("gdiplus\GdipGetEncoderParameterListSize", "Ptr", pBM, "Ptr", pCodec, "UintP", nSize)
         VarSetCapacity(EncoderParameters, nSize, 0)
         DllCall("gdiplus\GdipGetEncoderParameterList", "Ptr", pBM, "Ptr", pCodec, "UInt", nSize, "Ptr", &EncoderParameters)
         Loop, % NumGet(EncoderParameters, "UInt")
         {
            elem := (24+A_PtrSize)*(A_Index-1) + 4 + (pad := A_PtrSize = 8 ? 4 : 0)
            if (NumGet(EncoderParameters, elem+16, "UInt") = 1) && (NumGet(EncoderParameters, elem+20, "UInt") = 6)
            {
               p := elem+&EncoderParameters-pad-4
               NumPut(Quality, NumGet(NumPut(4, NumPut(1, p+0)+20, "UInt")), "UInt")
               break
            }
         }      
      }
      if !tobuff
         E := DllCall("gdiplus\GdipSaveImageToFile", "Ptr", pBM, "WStr", info, "Ptr", pCodec, "UInt", p ? p : 0)
      else  {
         DllCall( "ole32\CreateStreamOnHGlobal", "UInt", 0, "Int", 1, "PtrP", pStream )
         if !E := DllCall( "gdiplus\GdipSaveImageToStream", "Ptr", pBM, "Ptr", pStream, "Ptr", pCodec, "UInt", p ? p : 0 )  {
            DllCall( "ole32\GetHGlobalFromStream", "Ptr", pStream, "PtrP", hData )
            pData := DllCall( "GlobalLock", "Ptr", hData, "Ptr" )
            nSize := DllCall( "GlobalSize", "Ptr", hData, "Ptr" )
            VarSetCapacity( info, 0), VarSetCapacity( info, nSize, 0 )
            DllCall( "RtlMoveMemory", "Ptr", &info, "Ptr", pData, "UInt", nSize )
            DllCall( "GlobalUnlock", "Ptr", hData )
            DllCall( "GlobalFree", "Ptr", hData )
         }
         ObjRelease(pStream)
      }
      return E ? -4 : tobuff ? nSize : 0
   }
   
   DisposeImage(pBM)  {
      return DllCall("gdiplus\GdipDisposeImage", "Ptr", pBM)
   }
}

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Please let me know:
Am i correct regarding the above code?
The location of DllCall("DeleteObject", "Ptr", hBM) ; Clean-up is it correct?
Do i also need to add ---> DllCall("DeleteObject", "Ptr", pBM) ; Clean-up ?

I am looking forward for your help, thanks!
teadrinker
Posts: 4354
Joined: 29 Mar 2015, 09:41
Contact:

Re: How to save image in clipboard.

08 May 2024, 21:19

GEOVAN wrote: The location of DllCall("DeleteObject", "Ptr", hBM) ; Clean-up is it correct?
Yes, it's ok.
GEOVAN wrote: Do i also need to add ---> DllCall("DeleteObject", "Ptr", pBM) ; Clean-up ?
No, the DeleteObject() function is not intended for this data type. Resource freeing has already been done in the gdip.DisposeImage(pBM) line.
Your function GDI_CaptureScreen() has incorrect data types, should be

Code: Select all

GDI_CaptureScreen(X, Y, W, H) {
   static flags := (SRCCOPY := 0x00CC0020) | (CAPTUREBLT := 0x40000000)
   hDC := DllCall("GetDC", "Ptr", 0, "Ptr")
   hBM := DllCall("CreateCompatibleBitmap", "Ptr", hDC, "Int", W, "Int", H, "Ptr")
   hMDC := DllCall("CreateCompatibleDC", "Ptr", hDC, "Ptr")
   hObj := DllCall("SelectObject", "Ptr", hMDC, "Ptr", hBM, "Ptr")
   DllCall("BitBlt", "Ptr", hMDC, "Int", 0, "Int", 0, "Int", W, "Int", H
                   , "Ptr", hDC, "Int", X, "Int", Y, "UInt", flags)
   DllCall("SelectObject", "Ptr", hMDC, "Ptr", hObj, "Ptr")
   DllCall("DeleteDC", "Ptr", hMDC)
   DllCall("ReleaseDC", "Ptr", 0, "Ptr", hDC)
   Return hBM
}
GEOVAN
Posts: 174
Joined: 03 Mar 2022, 11:12

Re: How to save image in clipboard.

08 May 2024, 22:57

Thank you very very much!!!
I appreciate your help.
With your help, and from a topic at viewtopic.php?t=129501
i ended up with the following code:

Code: Select all

F2::  ; press F2 to save screenshot

hBM := ""
hBM := GDI_CaptureScreen( 0, 0, A_ScreenWidth, A_ScreenHeight )
SaveToImageFile(hBM, A_ScriptDir . "\ScreenshotImage.jpg", 100)
DllCall("DeleteObject", "Ptr", hBM)  ; Clean-up
Return



F3::   ; press F3 to load screenshot to clipboard

Clipboard := 	; Clear the clipboard
GDI_CaptureScreenToClipboard(0, 0, A_ScreenWidth, A_ScreenHeight)
DllCall("DeleteObject", "Ptr", hBM)  ; Clean-up
Return


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

GDI_CaptureScreen(X, Y, W, H) {
   static flags := (SRCCOPY := 0x00CC0020) | (CAPTUREBLT := 0x40000000)
   hDC := DllCall("GetDC", "Ptr", 0, "Ptr")
   hBM := DllCall("CreateCompatibleBitmap", "Ptr", hDC, "Int", W, "Int", H, "Ptr")
   hMDC := DllCall("CreateCompatibleDC", "Ptr", hDC, "Ptr")
   hObj := DllCall("SelectObject", "Ptr", hMDC, "Ptr", hBM, "Ptr")
   DllCall("BitBlt", "Ptr", hMDC, "Int", 0, "Int", 0, "Int", W, "Int", H
                   , "Ptr", hDC, "Int", X, "Int", Y, "UInt", flags)
   DllCall("SelectObject", "Ptr", hMDC, "Ptr", hObj, "Ptr")
   DllCall("DeleteDC", "Ptr", hMDC)
   DllCall("ReleaseDC", "Ptr", 0, "Ptr", hDC)
   Return hBM
}


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

SaveToImageFile(hBM, destImageFilePath, jpegQuality := 75) {
   gdip := new GDIplus()
   pBM := gdip.CreateBitmapFromHBITMAP(hBM)
   gdip.SaveBitmap(pBM, destImageFilePath, jpegQuality)
   gdip.DisposeImage(pBM)
}

class GDIplus  {
   __New() {
      this.hLib := DllCall("LoadLibrary", "Str", "gdiplus", "Ptr")
      VarSetCapacity(si, 8 + A_PtrSize*2, 0), si := Chr(1)
      DllCall("gdiplus\GdiplusStartup", "PtrP", pToken, "Ptr", &si, "Ptr", 0)
      this.token := pToken
   }
   
   __Delete() {
      DllCall("gdiplus\GdiplusShutdown", "Ptr", this.token)
      DllCall("FreeLibrary", "Ptr", this.hLib)
   }

   CreateBitmapFromHBITMAP(hBM, Palette=0)  {
      DllCall("gdiplus\GdipCreateBitmapFromHBITMAP", "Ptr", hBM, "Ptr", Palette, "PtrP", pBM)
      return pBM
   }

   SaveBitmap(pBM, ByRef info, Quality := 75, tobuff := "")
   {
      ; info — if copying to buffer, then file extension, if copying to file, then file path
      if tobuff
         Extension := info
      else
         SplitPath, info,,, Extension
      if Extension not in BMP,DIB,RLE,JPG,JPEG,JPE,JFIF,GIF,TIF,TIFF,PNG
         return -1

      DllCall("gdiplus\GdipGetImageEncodersSize", "UintP", nCount, "UintP", nSize)
      VarSetCapacity(ci, nSize)
      DllCall("gdiplus\GdipGetImageEncoders", "UInt", nCount, "UInt", nSize, "Ptr", &ci)
      if !(nCount && nSize)
         return -2
      
      Loop, % nCount  {
         sString := StrGet(NumGet(ci, (idx := (48+7*A_PtrSize)*(A_Index-1))+32+3*A_PtrSize), "UTF-16")
         if !InStr(sString, "*." Extension)
            continue
         
         pCodec := &ci+idx
         break
      }
      
      if !pCodec
         return -3
      
      if RegExMatch(Extension, "i)^J(PG|PEG|PE|FIF)$") && Quality != 75  {
         DllCall("gdiplus\GdipGetEncoderParameterListSize", "Ptr", pBM, "Ptr", pCodec, "UintP", nSize)
         VarSetCapacity(EncoderParameters, nSize, 0)
         DllCall("gdiplus\GdipGetEncoderParameterList", "Ptr", pBM, "Ptr", pCodec, "UInt", nSize, "Ptr", &EncoderParameters)
         Loop, % NumGet(EncoderParameters, "UInt")
         {
            elem := (24+A_PtrSize)*(A_Index-1) + 4 + (pad := A_PtrSize = 8 ? 4 : 0)
            if (NumGet(EncoderParameters, elem+16, "UInt") = 1) && (NumGet(EncoderParameters, elem+20, "UInt") = 6)
            {
               p := elem+&EncoderParameters-pad-4
               NumPut(Quality, NumGet(NumPut(4, NumPut(1, p+0)+20, "UInt")), "UInt")
               break
            }
         }      
      }
      if !tobuff
         E := DllCall("gdiplus\GdipSaveImageToFile", "Ptr", pBM, "WStr", info, "Ptr", pCodec, "UInt", p ? p : 0)
      else  {
         DllCall( "ole32\CreateStreamOnHGlobal", "UInt", 0, "Int", 1, "PtrP", pStream )
         if !E := DllCall( "gdiplus\GdipSaveImageToStream", "Ptr", pBM, "Ptr", pStream, "Ptr", pCodec, "UInt", p ? p : 0 )  {
            DllCall( "ole32\GetHGlobalFromStream", "Ptr", pStream, "PtrP", hData )
            pData := DllCall( "GlobalLock", "Ptr", hData, "Ptr" )
            nSize := DllCall( "GlobalSize", "Ptr", hData, "Ptr" )
            VarSetCapacity( info, 0), VarSetCapacity( info, nSize, 0 )
            DllCall( "RtlMoveMemory", "Ptr", &info, "Ptr", pData, "UInt", nSize )
            DllCall( "GlobalUnlock", "Ptr", hData )
            DllCall( "GlobalFree", "Ptr", hData )
         }
         ObjRelease(pStream)
      }
      return E ? -4 : tobuff ? nSize : 0
   }
   
   DisposeImage(pBM)  {
      return DllCall("gdiplus\GdipDisposeImage", "Ptr", pBM)
   }
}

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

GDI_CaptureScreenToClipboard( X, Y, W, H) {
   Static SizeOfDIB := A_PtrSize = 8 ? 104 : 84 ; DIBSECTION
   Static SizeOfBM  := A_PtrSize = 8 ?  32 : 24 ; BITMAP
   Static SizeOfBIH := 40                       ; BITMAPINFOHEADER
   Static OffBits   := A_PtrSize = 8 ?  24 : 20 ; BITMAP -> bmBits
   Static OffSize   := A_PtrSize = 8 ?  52 : 44 ; BITMAPINFOHEADER -> biSizeImage
   Local
   hDC := DllCall("GetDC", "Ptr", 0, "UPtr")
   cDC := DllCall("CreateCompatibleDC", "Ptr", hDC, "UPtr")
   hBM := DllCall("CreateCompatibleBitmap", "Ptr", hDC, "Int", W, "Int", H, "UPtr")
   hBM := DllCall("CopyImage", "Ptr", hBM, "UInt", 0, "Int", 0, "Int", 0, "UInt", 0x2008, "UPtr")
   oBM := DllCall("SelectObject", "Ptr", cDC, "Ptr", hBM, "UPtr")
   DllCall("BitBlt", "Ptr", cDC, "Int", 0, "Int", 0, "Int", W, "Int", H
                   , "Ptr", hDC, "Int", X, "Int", Y, "UInt", 0x40CC0020)
   DllCall("SelectObject", "Ptr", cDC, "Ptr", oBM)
   DllCall("DeleteDC", "Ptr", tDC)
   DllCall("ReleaseDC", "Ptr", 0, "Ptr", hDC)
   VarSetCapacity(DIB, SizeOfDIB, 0) ; DIBSECTION
   DllCall("GetObject", "Ptr", hBM, "Int", SizeOfDIB, "Ptr", &DIB)
   Size := NumGet(DIB, OffSize, "UInt")
   Bits := NumGet(DIB, OffBits, "UPtr")
   hDIB := DllCall("GlobalAlloc", "UInt", 2, "UInt", SizeOfBIH + Size, "UPtr")
   pDIB := DllCall("GlobalLock", "Ptr", hDIB, "UPtr")
   DllCall("RtlMoveMemory", "Ptr", pDIB, "Ptr", &DIB + SizeOfBM, "UInt", SizeOfBIH)
   DllCall("RtlMoveMemory", "Ptr", pDIB + SizeOfBIH, "Ptr", Bits, "UInt", Size)
   DllCall("GlobalUnlock", "Ptr", hDIB)
   DllCall("DeleteObject", "Ptr", hBM)
   DllCall("OpenClipboard", "Ptr", 0)
   DllCall("EmptyClipboard")
   DllCall("SetClipboardData", "UInt", 8, "Ptr", hDIB)
   DllCall("CloseClipboard")
}

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


In the above code, by pressing the F2 -> it saves screenshot to file 100% jpg qaulity
and by pressing F3 -> it loads screenshot to clipboard


My final question regarding the code above, is:
Is there any corrections needed to be done, or is good and ready to use?
(Note that i try it and it is working perfect, but i want to see by your knowladge if i need some code corrections, please)
teadrinker
Posts: 4354
Joined: 29 Mar 2015, 09:41
Contact:

Re: How to save image in clipboard.

09 May 2024, 18:26

GEOVAN wrote:

Code: Select all

F3::   ; press F3 to load screenshot to clipboard

Clipboard := 	; Clear the clipboard
GDI_CaptureScreenToClipboard(0, 0, A_ScreenWidth, A_ScreenHeight)
DllCall("DeleteObject", "Ptr", hBM)  ; Clean-up
Return
These lines

Code: Select all

Clipboard := 	; Clear the clipboard
DllCall("DeleteObject", "Ptr", hBM)  ; Clean-up
are unnecessary.
GEOVAN
Posts: 174
Joined: 03 Mar 2022, 11:12

Re: How to save image in clipboard.

Yesterday, 07:11

Thank you very very much!

Generally speaking, i want to ask for educational purposes the following question:
I observe that some functions use the following:

Code: Select all

DllCall("DeleteObject", "Ptr", hBM)  ; Clean-up
Is it necessary to use it, or we can leave and without it, please?
If we decide to not use it, does it will cause any problems to anywhere?

Return to “Ask for Help (v1)”

Who is online

Users browsing this forum: No registered users and 143 guests