Copy Bitmap from Windows Clipboard

Ossra

Formerly Exhydra
Veteran
Joined
Aug 21, 2013
Messages
1,076
Reaction score
854
First Language
English
Primarily Uses
RMMV
As part of a puzzle/effect within a RPGMAce project, I am attempting to use Windows API to grab a bitmap from the Windows clipboard which will then be used to create a Sprite which is displayed in-game. However, I am having issues with the process of converting the clipboard resource into a Ruby-usable resource.

I can grab text without an issue, which is great, but I really would like to snag the bitmap data, as well. I am probably missing something simple or making everything more complex than it needs to be.

Sloppy, swiped and twisted code below :

Code:
# The following script can be called from a 'Script Command' as follows :clp = Win32::Clipboard# Check For/Print Textif clp.format_available?(1)p clp.data(1)endclp = Win32::Clipboard# Check For/Use Bitmapif clp.format_available?(2)$tmpport=Viewport.new$tmpspr=Sprite.new($tmpport)$tmpspr.bitmap=clp.data(2)end
Code:
# Original Code : [URL="https://github.com/djberg96/win32-clipboard/blob/master/lib/win32/clipboard.rb#"]https://github.com/djberg96/win32-clipboard/blob/master/lib/win32/clipboard.rb#[/URL] Lock-Up Occurs At :# // get_image_data(handle)##         address  = @@globalLock.call(handle)#         buf_size = @@globalSize.call(handle)# I believe this occurs because GlobalLock is looking for a Pointer, but receives an Integer instead.# I am not sure how to solve this, as when I change 'I' to 'P' in the Win32API function, everything# explodes sooner.# The Win32 module serves as a namespace only.module Win32  # The Clipboard class encapsulates functions that relate to the MS Windows  # clipboard.  class Clipboard    @@closeClipboard             = Win32API.new('user32'   , 'CloseClipboard'              , ['V']           , 'V')    # The returned value of GetClipboardData probably needs to be a Pointer, but the    # function dies if I switch 'I' with 'P'.    @@getClipboardData           = Win32API.new('user32'   , 'GetClipboardData'            , ['I']           , 'I')    @@isClipboardFormatAvailable = Win32API.new('user32'   , 'IsClipboardFormatAvailable'  , ['I']           , 'I')    @@openClipboard              = Win32API.new('user32'   , 'OpenClipboard'               , ['L']           , 'L')    @@globalAlloc                = Win32API.new('kernel32' , 'GlobalAlloc'                 , ['L', 'L']      , 'L')    @@globalLock                 = Win32API.new('kernel32' , 'GlobalLock'                  , ['L']           , 'L')    @@globalSize                 = Win32API.new('kernel32' , 'GlobalSize'                  , ['L']           , 'L')    @@globalUnlock               = Win32API.new('kernel32' , 'GlobalUnlock'                , ['L']           , '' )    @@memcpy                     = Win32API.new('ntdll'    , 'memcpy'                      , ['P', 'P', 'L'] , 'L')        # Clipboard formats    # Text    TEXT         = 1    # Images    DIB         = 2    BITMAP      = 8        # Returns the data currently in the clipboard. If +format+ is    # specified, it will attempt to retrieve the data in that format. The    # default is Clipboard::TEXT.    #    # If there is no data in the clipboard, or data is available but the    # format doesn't match the data, then an empty string is returned.    #    # Examples:    #    #    # Get some plain text    #    Win32::Clipboard.data # => e.g. 'hello'    #    #    # Get a bitmap and write it to another file    #    File.open('bitmap_copy', 'wb'){ |fh|    #       fh.write Win32::Clipboard.data(Clipboard::DIB)    #    }    #    def self.data(format = TEXT)      begin        self.open        if @@isClipboardFormatAvailable.call(format)          handle = @@getClipboardData.call(format)          case format            when TEXT              clip_data = 0.chr * @@globalSize.call(handle)              @@memcpy.call(clip_data, handle, clip_data.size)              clip_data = clip_data[ /^[^\0]*/ ]            when DIB, BITMAP              clip_data = get_image_data(handle)            else              raise Error, 'format not supported'          end        else          clip_data = ''        end      ensure        self.close      end      clip_data    end    # Singleton aliases    #    class << self      alias :get_data :data    end    # Returns whether or not +format+ (an int) is currently available.    #    def self.format_available?(format)      @@isClipboardFormatAvailable.call(format) == 1 ? true : false    end    private    # Opens the clipboard and prevents other applications from modifying    # the clipboard content until it is closed.    #    def self.open      @@openClipboard.call(0)    end    # Close the clipboard    #    def self.close      @@closeClipboard.call    end    # Get data for bitmap files    #    def self.get_image_data(handle)      buf = nil      bmi = 0.chr * 44 # BITMAPINFO      begin        address  = @@globalLock.call(handle)        buf_size = @@globalSize.call(handle)                @@memcpy.call(bmi, address, bmi.length)        bit_count   = bmi[14,2].unpack('S').first # biBitCount        compression = bmi[16,4].unpack('L').first # biCompression        size_image  = bmi[20,4].unpack('L').first # biSizeImage        clr_used    = bmi[32,4].unpack('L').first # biClrUsed        size_image = buf_size + 16 if size_image == 0        # Calculate the header size        case bit_count          when 1            table_size = 2          when 4            table_size = 16          when 8            table_size = 256          when 16, 32            if compression == 0              table_size = clr_used            elsif compression == 3              table_size = 3            else              p "ERROR: invalid bit/compression combination"            end          when 24            table_size = clr_used          else            p "ERROR: invalid bit count"        end # case        offset = 0x36 + (table_size * 4)        buf = 0.chr * buf_size        @@memcpy.call(buf, address, buf.size)        buf = "\x42\x4D" + [size_image].pack('L') + 0.chr * 4 + [offset].pack('L') + buf      ensure        @@globalUnlock.call(handle)      end      buf    end  endend
 
Last edited by a moderator:

Zalerinian

Jack of all Errors
Veteran
Joined
Dec 17, 2012
Messages
4,696
Reaction score
935
First Language
English
Primarily Uses
N/A
I've only got experience with the bitmaps through C++, which wasn't using the clipboard, but rather editing RM bitmaps. I still think that's a bit important, though, because the bitmap in the clipboard won't be formatted for RM use, it's a bit of a different format. Colors in RM are their own system, so you'll likely need to use Cptrs to interact with the data. The colors, in C++ at least, are stored in BGRA instead of RGBA, if that helps.

I know that working with bitmaps in RM is a pain from other language, so you'd probably have to do a reverse-pain to make it RM compatible. There are 4 structs that are involved in (my code) editing RM bitmaps. Keep in mind I am still talking about C++, so I really don't know how you're going to really do that with ruby. You need to get the memory address of the bitmap, and then all the data is there, you'll just have to figure out how to get the data in an order compatible with RM. Again, I'm not sure, but you may need to use Cptrs.

I'll try to upload the old rgss3doc.tk site to rgss3doc.razelon.tk (my website) so you can see the Cptr documentation, though it isn't all that fantastic.

Edit: The site is uploaded and available for you to see at rgss3doc.razelon.tk. I haven't edited it from the original, so it should be exactly as it was. 
 
Last edited by a moderator:

Ossra

Formerly Exhydra
Veteran
Joined
Aug 21, 2013
Messages
1,076
Reaction score
854
First Language
English
Primarily Uses
RMMV
Edit: The site is uploaded and available for you to see at rgss3doc.razelon.tk. I haven't edited it from the original, so it should be exactly as it was.
Aah, coolness! I was wondering if anyone had a copy of the original site.


I'll take a look around, although my experience with programming is relatively minimal beyond Ruby. I was hoping there might be something simple I missed, but it seems like I'll need to do some more research.
 

FenixFyreX

Fire Deity
Veteran
Joined
Mar 1, 2012
Messages
434
Reaction score
310
First Language
English
Primarily Uses
Technically you could load in all of the DL lib from the main ruby installation, create a few structs resembling the original ones in C/++, then read in the bitmap via a DL CPtr or CStruct (the struct is probs better). Any way you look at it, you'll be using some form of C/++ access in your code, whether through win32API and a dll or the DL lib.

EDIT: Also, you can see within this script by Jet and Zeus81 (Zeus' part is what I'm pointing at) how bmp and png files are dumped into a file from memory. Perhaps you could 'reverse' the process with the clipboard contents, OR just use Zeus' code to dump it to a temp file and then load it..?

Here's the link: http://pastebin.com/raw.php?i=EBCFY0yH

Anyways, good luck. :)
 
Last edited by a moderator:

Users Who Are Viewing This Thread (Users: 0, Guests: 1)

Latest Threads

Latest Profile Posts

Couple hours of work. Might use in my game as a secret find or something. Not sure. Fancy though no? :D
Holy stink, where have I been? Well, I started my temporary job this week. So less time to spend on game design... :(
Cartoonier cloud cover that better fits the art style, as well as (slightly) improved blending/fading... fading clouds when there are larger patterns is still somewhat abrupt for some reason.
Do you Find Tilesetting or Looking for Tilesets/Plugins more fun? Personally I like making my tileset for my Game (Cretaceous Park TM) xD
How many parameters is 'too many'??

Forum statistics

Threads
105,867
Messages
1,017,062
Members
137,575
Latest member
akekaphol101
Top