Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

Snap! Capture of portion of the screen to a bitmap.


  • Please log in to reply
19 replies to this topic
Veovis
  • Members
  • 389 posts
  • Last active: Mar 17 2009 12:24 AM
  • Joined: 13 Feb 2006

Foreword

When i was working on including files into an uncompiled script I had this little side project and i am pretty proud of it.

It is a function that can create a bitmap from a list of colors (in BGR) and a specified width and height.


Application

WriteBMP(file     ; the file to save the bitmap to
        ,colors   ; the list of colors [color=red]IN BGR FORMAT!!![/color] for ex. "102bacf810ace01..." 
        ,width    ; width of the bitmap (default = 16)
        ,height   ; height of the bitmap (default = 16)
        ,padded)  ; if the bitmap is padded pass a 1 (if you dont know what that means, pass 0 or leave blank)

It comes in handy for taking snapshots of a portion of the screen, rather than screen printing then editing in paing. Aside from the fact that bitmaps are relatively huge in file size it is really useful.

WriteBMP(file,colors,width=16,height=16,padded=0)
{
  ;first check if we have the right number of pixels
   if padded = 0
      if strlen(colors)/6 <> height*width
         msgbox,% "Pixel Mismatch detected!`n(The Height*Width you specified does not match the number of pixels you provided, or the padding is incorrect) The BMP will be corrupted.`nWidth:" width " Height:" Height "`nPixels provided:" strlen(colors)/6 " Pixels needed: " width*height
   if padded 
      if (strlen(colors)/6 - mod(width,4)*height)<> height*width
         msgbox,% "Pixel Mismatch detected!`n(The Height*Width you specified does not match the number of pixels you provided, or the padding is incorrect) The BMP will be corrupted.`nWidth:" width " Height:" Height "`nPixels provided:" strlen(colors)/6 " Pixels needed: " width*height
  ;if the bitmap is not padded we need to pad it
   if (padded = 0) and (mod(width,4) <> 0) ;our bitmap was not provided with padding and it needs it
   {
      loop, %height%
      {
         stringmid,row,colors,% width * 6 * (A_index-1) + 1,% width*6
         colorspadded := colorspadded row removehex(padhex(0,mod(width,4)))  ;add the needed number of 00 bytes
      }
      colors := colorspadded
   }
  ;now we need to create the actual bitmap, starting with the header
   setformat,integer,H
   width := removehex(padhex(width+0,4))      ;this makes the width and height into "words" or 4 bytes long
   height := removehex(padhex(height+0,4))
   ;          B M size    reserved     offbits bitsize                    planes  bitcount (24 bit), and the rest is color table (0s) 
   BMPHex := "424d0000000000000000" . "3600000028000000" . width . height "0100" . "1800" . "000000000000000000000000000000000000000000000000" . colors

  ;Create the file
   Handle :=  DllCall("CreateFile","str",file,"Uint",0x40000000
                  ,"Uint",0,"UInt",0,"UInt",4,"Uint",0,"UInt",0)
   Loop
   { 
     if strlen(BMPHex) = 0
        break
     StringLeft, Hex, BMPHex, 2         
     StringTrimLeft, BMPHex, BMPHex, 2  
     Hex = 0x%Hex%
     DllCall("WriteFile","UInt", Handle,"UChar *", Hex
     ,"UInt",1,"UInt *",UnusedVariable,"UInt",0) 
    } 
  ;close the file
   DllCall("CloseHandle", "Uint", Handle)
   return 0
}

padhex(hexin,bytes)                                ;pads a hex number to the specified byte length
{
   if Mod(strlen(hexin),2)                         ;the string is an odd number of digits long
   {
      hexin := removehex(hexin)                    ; remove the "0x" temporarily
      hexin := "0x0" hexin   ;for ex: A into 0x0A  ; now add "0x0"
   } 
   loop, % bytes - strlen(hexin)/2 + 1             ;add zeros to the end till we get the desired byte length
      hexin := hexin "00"
   return hexin
}

removehex(hexin)                                   ;removes the 0x from hex
{
   stringleft,beg,hexin,2
   if beg = 0x
      stringtrimleft,hexin,hexin,2
   return hexin
}


Example

If you Ctrl-Drag on the screen it takes a snapshot of that section of the screen and saves it to your desktop (i hope). I created this because i was tired of having to printscreen my whole 1600*1200 desktop and then edit the file for a 16*16 picture. Please post feedback!

#singleinstance force
#noenv

file = C:\Documents and Settings\%A_username%\Desktop\ThisBMPwasmadewithAHK.bmp

~^LButton::
   colorlist = 
   CoordMode, Mouse, Screen
   CoordMode, Tooltip, Screen
   CoordMode, Pixel, Screen
   MouseGetPos, start_x, start_y
   ToolTip, ., start_x, start_y
   WinSet, Transparent, 100, ahk_class tooltips_class32
   loop
   {
      MouseGetPos, current_x, current_y
      WinMove, ahk_class tooltips_class32, , , , % current_x - start_x, % current_y - start_y
      GetKeyState, state, LButton
      if state=u
      {
         tooltip
         break
      }
   }
   mousegetpos, end_x, end_y  
   TrayTip, ,Retreiving Colors..., , 1
   width := end_x - start_x
   height := end_y - start_y 
   loop, %height%
   {
      pixely := start_y + height - A_index - 1    ;because bitmaps are written backwards
      loop, %width%
      {
         pixelx := start_x + A_index - 1
         pixelgetcolor,color,pixelx,pixely  ;get the BGR value
         color := removehex(color)
         colorlist := colorlist color
      }
   }
   TrayTip, ,Saving Bitmap..., , 1
   WriteBMP(file,colorlist,width,height,0)
   TrayTip, ,Done!, , 1
return

WriteBMP(file,colors,width=16,height=16,padded=0)
{
  ;first check if we have the right number of pixels
   if padded = 0
      if strlen(colors)/6 <> height*width
         msgbox,% "Pixel Mismatch detected!`n(The Height*Width you specified does not match the number of pixels you provided, or the padding is incorrect) The BMP will be corrupted.`nWidth:" width " Height:" Height "`nPixels provided:" strlen(colors)/6 " Pixels needed: " width*height
   if padded 
      if (strlen(colors)/6 - mod(width,4)*height)<> height*width
         msgbox,% "Pixel Mismatch detected!`n(The Height*Width you specified does not match the number of pixels you provided, or the padding is incorrect) The BMP will be corrupted.`nWidth:" width " Height:" Height "`nPixels provided:" strlen(colors)/6 " Pixels needed: " width*height
  ;if the bitmap is not padded we need to pad it
   if (padded = 0) and (mod(width,4) <> 0) ;our bitmap was not provided with padding and it needs it
   {
      loop, %height%
      {
         stringmid,row,colors,% width * 6 * (A_index-1) + 1,% width*6
         colorspadded := colorspadded row removehex(padhex(0,mod(width,4)))  ;add the needed number of 00 bytes
      }
      colors := colorspadded
   }
  ;now we need to create the actual bitmap, starting with the header
   setformat,integer,H
   width := removehex(padhex(width+0,4))      ;this makes the width and height into "words" or 4 bytes long
   height := removehex(padhex(height+0,4))
   ;          B M size    reserved     offbits bitsize                    planes  bitcount (24 bit), and the rest is color table (0s) 
   BMPHex := "424d0000000000000000" . "3600000028000000" . width . height "0100" . "1800" . "000000000000000000000000000000000000000000000000" . colors

  ;Create the file
   Handle :=  DllCall("CreateFile","str",file,"Uint",0x40000000
                  ,"Uint",0,"UInt",0,"UInt",4,"Uint",0,"UInt",0)
   Loop
   { 
     if strlen(BMPHex) = 0
        break
     StringLeft, Hex, BMPHex, 2         
     StringTrimLeft, BMPHex, BMPHex, 2  
     Hex = 0x%Hex%
     DllCall("WriteFile","UInt", Handle,"UChar *", Hex
     ,"UInt",1,"UInt *",UnusedVariable,"UInt",0) 
    } 
  ;close the file
   DllCall("CloseHandle", "Uint", Handle)
   return 0
}

padhex(hexin,bytes)                                ;pads a hex number to the specified byte length
{
   if Mod(strlen(hexin),2)                         ;the string is an odd number of digits long
   {
      hexin := removehex(hexin)                    ; remove the "0x" temporarily
      hexin := "0x0" hexin   ;for ex: A into 0x0A  ; now add "0x0"
   } 
   loop, % bytes - strlen(hexin)/2 + 1             ;add zeros to the end till we get the desired byte length
      hexin := hexin "00"
   return hexin
}

removehex(hexin)                                   ;removes the 0x from hex
{
   stringleft,beg,hexin,2
   if beg = 0x
      stringtrimleft,hexin,hexin,2
   return hexin
}


Special thanks to Goyyah for his great idea for organizing a post using qoutes
Posted Image
"Power can be given overnight, but responsibility must be taught. Long years go into its making."

d-man
  • Members
  • 290 posts
  • Last active: Jun 28 2015 09:26 AM
  • Joined: 08 Jun 2006
nice idea, but it’s a little slow.

BoBo
  • Guests
  • Last active:
  • Joined: --
Not yet tested :roll: but the concept seems to be perfect to capture a collection of images from the same screen coords (SetTimer) to be compared/used with AHK's ImageSearch ...

Thx for sharing it. 8)

Veovis
  • Members
  • 389 posts
  • Last active: Mar 17 2009 12:24 AM
  • Joined: 13 Feb 2006
@ d-man

for small captures it is reasonably fast

(times below are based on my 1.0 ghz 392 mb ram laptop)

for a 50 by 50 it takes about 4 seconds, and for a 16 by 16 it takes less than a second. both are much faster than printscreening then editing part of it out.

but for something like a 100 by 100 it can take 20+ seconds. becuase it has to retreive the color of some 10,000 pixels, then write them to a file.

It gets exponentially longer as you take a bigger snapshot, so this is more for taking small snapshots of things rather than large things, for that use Print-Screen (or Alt-Printscreen to capture the active windows)
Posted Image
"Power can be given overnight, but responsibility must be taught. Long years go into its making."

m2
  • Members
  • 4 posts
  • Last active: Jan 04 2016 01:48 PM
  • Joined: 13 Jul 2006
Nice, this makes me able to drop the need for one external tool in my script for conversion from ppm to bmp.

Also made a similar tool just the other day, but as I needed a image file in the ppm format (bmp returning feed back to the GUI, as ppm is not supported).

The ppm fomat is more simple than bmp (including not upside down), and also in asci, using th following syntax:

Format for PPM image file can be like,

P3
# example from the man page
4 4
15
 0  0  0    0  0  0    0  0  0   15  0 15
 0  0  0    0 15  7    0  0  0    0  0  0
 0  0  0    0  0  0    0 15  7    0  0  0
15  0 15    0  0  0    0  0  0    0  0  0


A early example is in the post: http://www.autohotke...er=asc&start=15

As it so slow for large grabs, locking of mouse/keyboard may also be useful if not to change the screen while its scanning.

//Regards m2

polyethene
  • Members
  • 5519 posts
  • Last active: May 17 2015 06:39 AM
  • Joined: 26 Oct 2012
WriteBMP() itself is quite fast. This is very useful, thanks!

autohotkey.com/net Site Manager

 

Contact me by email (polyethene at autohotkey.net) or message tidbit


Laszlo
  • Moderators
  • 4713 posts
  • Last active: Mar 31 2012 03:17 AM
  • Joined: 14 Feb 2005
It looks cool, but I got invalid bmp files (size 54 bytes). Do I need to change something in the script? (Also, %A_Desktop% works with other than English Windows, too.)

Laszlo
  • Moderators
  • 4713 posts
  • Last active: Mar 31 2012 03:17 AM
  • Joined: 14 Feb 2005
I think the problem was that my application reacted to Ctrl-Click strangely, and also, the mouse tracking loop consumed too much processor power (we ought to slow it down). Here is a version, which is more stable for my weird editor.
#SingleInstance force
#NoEnv
Process Priority,,High
SetBatchLines -1

file = %A_Desktop%\AHK-made.bmp
CoordMode, Mouse, Screen
CoordMode, Tooltip, Screen
CoordMode, Pixel, Screen

+^LButton::
   colorlist =
   MouseGetPos, start_x, start_y
   ToolTip, %A_Space%, start_x, start_y
   WinSet, Transparent, 150, ahk_class tooltips_class32
   SetTimer mouse, 50
Return

mouse:
   MouseGetPos, current_x, current_y
   WinMove, ahk_class tooltips_class32, , , , % current_x - start_x, % current_y - start_y
   If GetKeyState("LButton", "P")
      Return
   SetTimer mouse, OFF
   ToolTip
   MouseGetPos, end_x, end_y
   TrayTip, ,Retreiving Colors..., , 1
   width := end_x - start_x
   height := end_y - start_y
   Loop, %height%
   {
      pixely := start_y + height - A_index - 1    ;because bitmaps are written backwards
      loop, %width%
      {
         pixelx := start_x + A_index - 1
         pixelgetcolor,color,pixelx,pixely  ;get the BGR value
         color := removehex(color)
         colorlist := colorlist color
      }
   }
   TrayTip, ,Saving Bitmap..., , 1
   WriteBMP(file,colorlist,width,height,0)
   TrayTip, ,Done!, , 1
Return

;...
I also changed the hotkey to Shift-Ctrl-LButton, because it had no function I would miss.

In any case, this is a useful script, programmed nicely. It helps creating our own icons. Congratulations, and thanks for sharing it!

Fabiolus
  • Members
  • 29 posts
  • Last active: Mar 22 2007 01:10 PM
  • Joined: 20 Aug 2006
I wanted to try ur code and got an error message, wonder if it was tested or not?

Error: call to nonexistent function

color := removehex(color)
There is always something new to learn.
_________________
http://autohotkey.net/~Fabiolus/

Fabiolus
  • Members
  • 29 posts
  • Last active: Mar 22 2007 01:10 PM
  • Joined: 20 Aug 2006
Okay nevermind I got it to work but where does it save the file?
There is always something new to learn.
_________________
http://autohotkey.net/~Fabiolus/

PhiLho
  • Moderators
  • 6850 posts
  • Last active: Jan 02 2012 10:09 PM
  • Joined: 27 Dec 2005

Okay nevermind I got it to work but where does it save the file?

In the path you give in the file variable.
Posted Image vPhiLho := RegExReplace("Philippe Lhoste", "^(\w{3})\w*\s+\b(\w{3})\w*$", "$1$2")

Thalon
  • Members
  • 641 posts
  • Last active: Jan 02 2017 12:17 PM
  • Joined: 12 Jul 2005
I tried the function and got also a corrupt Bitmap with size 24065x4353 which can't be displayed...

Should I send you the file?

Thalon

Veovis
  • Members
  • 389 posts
  • Last active: Mar 17 2009 12:24 AM
  • Joined: 13 Feb 2006
lol um, that depends, how big is it? lol
and were you using my function or Laszlos?
Posted Image
"Power can be given overnight, but responsibility must be taught. Long years go into its making."

Thalon
  • Members
  • 641 posts
  • Last active: Jan 02 2017 12:17 PM
  • Joined: 12 Jul 2005
I have used both examples!
Your example:
406.074 Bytes (396kB) and 12033x48641 Pixel

Laszlos:
287.250 Bytes (280kB) and 4353x24065 Pixel

If you are interested in please send me a PM with your E-Mail-Data.

I have used the scripts to make screenshots over an area of maximum 500x800 (little part of my screen :) ).

Thalon

Laszlo
  • Moderators
  • 4713 posts
  • Last active: Mar 31 2012 03:17 AM
  • Joined: 14 Feb 2005
The problem is that the width and height of the selection has to be coded in little endian format in the bmp file. If the picture has less than 256 pixels in either direction, the scripts work. Otherwise, we have to swap the bytes in the padhex function. The simple fix below works up to 64K pixels horizontally or vertically, which should be enough for all monitors.
SetFormat Integer,H

CoordMode Mouse, Screen

CoordMode Tooltip, Screen

CoordMode Pixel, Screen

file = %A_Desktop%\AHK-made.bmp



+^LButton::                                     ; Shift-Control-Drag

   colorlist =

   MouseGetPos start_x, start_y

   ToolTip %A_Space%, start_x, start_y

   WinSet Transparent, 150, ahk_class tooltips_class32

   SetTimer mouse, 50

Return



mouse:

   MouseGetPos current_x, current_y

   WinMove ahk_class tooltips_class32,,,,% current_x - start_x, % current_y - start_y

   If GetKeyState("LButton","P")

      Return

   SetTimer mouse, OFF

   ToolTip

   MouseGetPos end_x, end_y

   TrayTip,,Retreiving Colors...,,1

   width  := end_x - start_x

   height := end_y - start_y

   Loop %height% {

      pixely := start_y + height - A_index - 1  ; bitmaps are written backwards

      Loop %width% {

         pixelx := start_x + A_index - 1

         PixelGetColor color,pixelx,pixely      ; get the BGR value

         StringTrimLeft color, color, 2         ; remove 0x

         colorlist = %colorlist%%color%

      }

   }

   TrayTip,,Saving Bitmap...,,1

   WriteBMP(file,colorlist,width,height,0)

   TrayTip,,Done!,,1

Return



WriteBMP(file,colors,width=16,height=16,padded=0) {

   If (!padded and StrLen(colors)/6 <> height*width   ; check if we have the right number of pixels

     or padded and StrLen(colors)/6 - (width&3)*height <> height*width)

      MsgBox Pixel Mismatch!`nThe Height*Width specified does not match the number of pixels provided, or the padding is incorrect

   If (!padded and (width&3)<>0) {                    ; bitmap needed but not provided with padding

      Loop %height% {

         StringMid row, colors, % width*6*A_index-5, % width*6

         colorspadded := colorspadded row padhex(0x0,width&3) ; add 00 bytes

      }

      colors = %colorspadded%

   }

; Create the actual bitmap, starting with the header

   width := padhex(width +0,4)         ; make width and height 4-byte words

   height:= padhex(height+0,4)

;           B M size   reserved offbits bitsize             planes bitcount (24 bit)...the rest is color table (0s)

   BMPHex = 424d00000000000000003600000028000000%width%%height%01001800000000000000000000000000000000000000000000000000%colors%



   Handle := DllCall("CreateFile",Str,file,Uint,0x40000000,Uint,0,UInt,0,UInt,4,Uint,0,UInt,0)

   Loop % StrLen(BMPHex)//2 {

     StringMid Hex, BMPHex, 2*A_Index-1, 2

     DllCall("WriteFile",UInt,Handle, UCharP,"0x" Hex, UInt,1,UIntP,UnusedVariable,UInt,0)

   }

   DllCall("CloseHandle", Uint,Handle)

}



padhex(hex,bytes) {        ; remove 0x, right-pad hex number to bytes length <= 4

   If hex > 255

      hex := (hex>>8) + ((hex&255)<<8) ; little endian coding

   If StrLen(hex) & 1

        StringReplace  hex, hex, x

   Else StringTrimLeft hex, hex, 2

   hex = %hex%000000

   StringLeft hex, hex, 2*bytes

   Return hex

}