Forum

Copy/Paste Texture to Clipboard

Discuss programming topics for the various GPL'd game engine sources.

Moderator: InsideQC Admins

Copy/Paste Texture to Clipboard

Postby Baker » Mon Mar 21, 2011 8:21 pm

FitzQuake 0.85 has a dumptextures command that downloads the textures from the video card and writes them as TGA:

Code: Select all
/*
===============
TexMgr_Imagedump_f -- dump all current textures to TGA files
===============
*/
void TexMgr_Imagedump_f (void)
{
.
.
.
         buffer = malloc(glt->width*glt->height*4);
         glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
         Image_WriteTGA (tganame, buffer, glt->width, glt->height, 32, true);
.
.
.
   Con_Printf ("dumped %i textures to %s\n", numgltextures, dirname);
}


I plan on making it so a single texture can be copied or pasted to the clipboard Winodws, but looks like I'll have to do this incrementally.

Accessing the clipboard is easy enough ...

Code: Select all
char *Sys_GetClipboardData (void)
{
   HANDLE      th;
   char      *clipText, *s, *t;
   static char   clipboard[SYS_CLIPBOARD_SIZE];

   if (!OpenClipboard(NULL))
      return NULL;

   if (!(th = GetClipboardData(CF_TEXT)))
   {
      CloseClipboard ();
      return NULL;
   }

   if (!(clipText = GlobalLock(th)))
   {
      CloseClipboard ();
      return NULL;
   }

   s = clipText;
   t = clipboard;

   /*
   \e   Write an <escape> character.
   \a   Write a <bell> character.
   \b   Write a <backspace> character.
   \f   Write a <form-feed> character.
   \n   Write a <new-line> character.
   \r   Write a <carriage return> character.
   \t   Write a <tab> character.
   \v   Write a <vertical tab> character.
   \'   Write a <single quote> character.
   \\   Write a backslash character.
   */

   // Filter out newlines, carriage return and backspace characters
   while (*s && t - clipboard < SYS_CLIPBOARD_SIZE - 1 && *s != '\n' && *s != '\r' && *s != '\b')
      *t++ = *s++;
   *t = 0;

   GlobalUnlock (th);
   CloseClipboard ();

   return clipboard;
}

// copies given text to clipboard
void Sys_CopyToClipboard(const char *text)
{
   char *clipText;
   HGLOBAL hglbCopy;

   if (!OpenClipboard(NULL))
      return;

   if (!EmptyClipboard())
   {
      CloseClipboard();
      return;
   }

   if (!(hglbCopy = GlobalAlloc(GMEM_DDESHARE, strlen(text) + 1)))
   {
      CloseClipboard();
      return;
   }

   if (!(clipText = GlobalLock(hglbCopy)))
   {
      CloseClipboard();
      return;
   }

   strcpy((char *) clipText, text);
   GlobalUnlock(hglbCopy);
   SetClipboardData(CF_TEXT, hglbCopy);

   CloseClipboard();
}


But looks like I'll have to dig into some Windows API stuffs to use CF_DIB. I downloaded some source codes like GIMP and such, but most of them use GTK obfuscating getting into the Windows procedure to allocatie a DIB and fill in the data.
The night is young. How else can I annoy the world before sunsrise? 8) Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
User avatar
Baker
 
Posts: 3666
Joined: Tue Mar 14, 2006 5:15 am

Postby Baker » Mon Mar 21, 2011 8:29 pm

Puzzle pieces ...

Code: Select all
HGLOBAL hData = data.GetGlobalData(CF_DIB);
BITMAPINFO* pData = (BITMAPINFO*) GlobalLock(hData);
if (pData)
{
// use it!
GlobalFree(hData);
}



Code: Select all
typedef struct tagBITMAPINFOHEADER {
  DWORD biSize;
  LONG  biWidth;
  LONG  biHeight;
  WORD  biPlanes;
  WORD  biBitCount;
  DWORD biCompression;
  DWORD biSizeImage;
  LONG  biXPelsPerMeter;
  LONG  biYPelsPerMeter;
  DWORD biClrUsed;
  DWORD biClrImportant;
} BITMAPINFOHEADER, *PBITMAPINFOHEADER;
The night is young. How else can I annoy the world before sunsrise? 8) Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
User avatar
Baker
 
Posts: 3666
Joined: Tue Mar 14, 2006 5:15 am

Postby mh » Tue Mar 22, 2011 12:02 am

Hmmm; I've done this in .NET just using Clipboard.SetImage which seems to use CF_BITMAP behind the scenes. Might be useful info or might not! 8)
We had the power, we had the space, we had a sense of time and place
We knew the words, we knew the score, we knew what we were fighting for
User avatar
mh
 
Posts: 2292
Joined: Sat Jan 12, 2008 1:38 am

Postby Baker » Tue Mar 22, 2011 2:50 am

mh wrote:Hmmm; I've done this in .NET just using Clipboard.SetImage which seems to use CF_BITMAP behind the scenes. Might be useful info or might not! 8)


I'm thinking this is gonna be a bit more "raw" than something insulated like .NET but maybe I'll be wrong. I've been rather stunned so far as to how few open source image editors are solely native Windows coded in C or C++.

Still, I'm just not gonna be deterred. Stubborness ... the heart of engine coding :D I'll just keep pounding away and playing around until it happens.
The night is young. How else can I annoy the world before sunsrise? 8) Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
User avatar
Baker
 
Posts: 3666
Joined: Tue Mar 14, 2006 5:15 am

Postby mh » Tue Mar 22, 2011 11:43 am

Don't worry about the DirectQ-specific bits in here, this works:
Code: Select all
      byte *bmbits = (byte *) MainHunk->Alloc (width * height * 4);

      D3D_Transfer8BitTexture ((byte *) data, (unsigned *) bmbits, width * height, D3D_GetTexturePalette (d3d_QuakePalette.standard, 0));
      HBITMAP hBitmap = CreateBitmap (width, height, 1, 32, bmbits);

      OpenClipboard (NULL);

      if ((SetClipboardData (CF_BITMAP, hBitmap)) == NULL)
         Sys_Error ("SetClipboardData failed");

      CloseClipboard ();
We had the power, we had the space, we had a sense of time and place
We knew the words, we knew the score, we knew what we were fighting for
User avatar
mh
 
Posts: 2292
Joined: Sat Jan 12, 2008 1:38 am

Postby Baker » Tue Mar 22, 2011 12:53 pm

mh wrote:Don't worry about the DirectQ-specific bits in here, this works:
Code: Select all
      byte *bmbits = (byte *) MainHunk->Alloc (width * height * 4);

      D3D_Transfer8BitTexture ((byte *) data, (unsigned *) bmbits, width * height, D3D_GetTexturePalette (d3d_QuakePalette.standard, 0));
      HBITMAP hBitmap = CreateBitmap (width, height, 1, 32, bmbits);

      OpenClipboard (NULL);

      if ((SetClipboardData (CF_BITMAP, hBitmap)) == NULL)
         Sys_Error ("SetClipboardData failed");

      CloseClipboard ();


I was doing this a bit less efficiently than your example (filling in the bitmap header manually because I didn't know better).

And it would seem that GetBitmapBits does the reverse.

Thanks, MH ;)
The night is young. How else can I annoy the world before sunsrise? 8) Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
User avatar
Baker
 
Posts: 3666
Joined: Tue Mar 14, 2006 5:15 am

Postby Baker » Tue Mar 22, 2011 4:47 pm

Upside down fix ....

glReadPixels (...)
FlipRows (...)
ApplyGamma (...) // Optional
hBitmap= CreateBitmap (...)

Code: Select all
static qbool FlipRows (int columns, int rows, int colordepth, byte *buffer)   
{     
    byte  *tb1, *tb2;   
    int      offset1, offset2;
   int      i,bufsize;   
   
    bufsize = columns * colordepth;
   
    tb1= Q_malloc(bufsize);   
    tb2= Q_malloc(bufsize);
   
    for (i=0;i<(rows+1)/2;i++)   
    {   
        offset1= i * bufsize;   
        offset2=((rows-1)-i) * bufsize;     
           
        memcpy(tb1,            buffer+offset1,   bufsize);   
        memcpy(tb2,            buffer+offset2, bufsize);   
        memcpy(buffer+offset1,   tb2,         bufsize);   
        memcpy(buffer+offset2,   tb1,         bufsize);   
    }     
   
   free (tb1);
   free (tb2); 
    return true;   
}
The night is young. How else can I annoy the world before sunsrise? 8) Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
User avatar
Baker
 
Posts: 3666
Joined: Tue Mar 14, 2006 5:15 am

Postby mh » Tue Mar 22, 2011 4:56 pm

Also look at glGetTexImage: http://www.opengl.org/sdk/docs/man/xhtm ... xImage.xml

Hmm, I could do this in DirectQ using it's tab autocompletion on texturenames. "copytexture <tab>", etc. And also do "dumptexture" for a single texture. Also "pastetexture" to update a texture image from the clipboard - that might be really useful for experimenting with external textures (or even just different textures) without needing to rebuild or reload a map. In fact I imagine that mappers would find such a feature to be really good. :D
We had the power, we had the space, we had a sense of time and place
We knew the words, we knew the score, we knew what we were fighting for
User avatar
mh
 
Posts: 2292
Joined: Sat Jan 12, 2008 1:38 am

Postby Baker » Tue Mar 22, 2011 5:43 pm

mh wrote:Also look at glGetTexImage: http://www.opengl.org/sdk/docs/man/xhtm ... xImage.xml


Actually, I was testing it with the screenshot command just to fine tune things. glReadPixels in the above post was a mistake.


Hmm, I could do this in DirectQ using it's tab autocompletion on texturenames. "copytexture <tab>", etc. And also do "dumptexture" for a single texture. Also "pastetexture" to update a texture image from the clipboard - that might be really useful for experimenting with external textures (or even just different textures) without needing to rebuild or reload a map. In fact I imagine that mappers would find such a feature to be really good. :D


Or alter the texture of a single surface that the crosshair is pointing to :D

Or, in fact, you could test model skins in real-time by copy/paste. Sure the texture would have to go through the whole padding, power of 2 routine.
The night is young. How else can I annoy the world before sunsrise? 8) Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
User avatar
Baker
 
Posts: 3666
Joined: Tue Mar 14, 2006 5:15 am

Postby Baker » Tue Mar 22, 2011 7:58 pm

Rather than start a new thread ...

I'm rewriting my engine a bit so commands can output to ...

1. The console
2. To file
3. Text to clipboard

Why? Well the edicts command is virtually useless since it dumps probably about 2MB of entity information to the console, which vastly exceeds the console buffer and any size you would ever sensibly make it.

Just take Con_Printf and modify it to have output options.

enum {PRINT_CONSOLE, PRINT_FILE, PRINT_CLIPBOARD}
The night is young. How else can I annoy the world before sunsrise? 8) Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
User avatar
Baker
 
Posts: 3666
Joined: Tue Mar 14, 2006 5:15 am

Re: Copy/Paste Texture to Clipboard

Postby Baker » Thu Aug 16, 2012 3:18 am

Not wanting to start a new thread ...

I wrote a function to get an image off the clipboard and convert it to RGBA. (I use RGBA instead of BGRA as MH strongly advocates because I'd have to adjust a lot of image functions I've written and I don't want to do that right now. image loaders, resample functions, color manipulation functions, etc.)

To get BGRA instead, just turn #if 1 into #if 0 in the code below.

Code: Select all
byte* Platform_Clipboard_To_RGBA_Alloc (int* outwidth, int* outheight)
{
   byte* ptr = NULL;
   
   if (OpenClipboard(NULL))
   {
      HBITMAP hBitmap = GetClipboardData (CF_BITMAP);
      BITMAP csBitmap;
      if (hBitmap && GetObject(hBitmap, sizeof(csBitmap), &csBitmap) && csBitmap.bmBitsPixel == 32)
      {
         // allocate buffer
         int i, bufsize = csBitmap.bmWidth * csBitmap.bmHeight * (csBitmap.bmBitsPixel / 8);   

         csBitmap.bmBits = ptr = Memory_malloc (bufsize, "bmbits buffer");
         GetBitmapBits((HBITMAP)hBitmap, bufsize, csBitmap.bmBits );
      
         // Convert BGRA --> RGBA, set alpha full since clipboard loses it somehow
         for (i = 0; i < bufsize; i += 4)
         {
#if 1 // Wants RGBA.  Set to 0 for BGRA
            byte temp = ptr[i + 0];
            ptr[i + 0] = ptr[i + 2];
            ptr[i + 2] = temp;
#endif
            ptr[i + 3] = 255; // Full alpha
         }
         *outwidth = csBitmap.bmWidth;
         *outheight = csBitmap.bmHeight;
      }
      CloseClipboard ();
   }
   return ptr;
}
The night is young. How else can I annoy the world before sunsrise? 8) Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
User avatar
Baker
 
Posts: 3666
Joined: Tue Mar 14, 2006 5:15 am

Re: Copy/Paste Texture to Clipboard

Postby mh » Thu Aug 16, 2012 4:47 pm

RGBA is OK for this kinda thing; it's not as performance-critical as lightmaps. :D
We had the power, we had the space, we had a sense of time and place
We knew the words, we knew the score, we knew what we were fighting for
User avatar
mh
 
Posts: 2292
Joined: Sat Jan 12, 2008 1:38 am


Return to Engine Programming

Who is online

Users browsing this forum: No registered users and 1 guest