How to make a copy of a bmap?

Posted By: Caermundh

How to make a copy of a bmap? - 04/13/11 22:13

I was wondering if its possible or if there is an easy way of creating a copy of a bmap?

What I would like to do is something like:

Code:
BMAP* my_bmap;
BMAP* my_copy;

my_bmap = bmap_create("my_file.jpg");
my_copy = bmap_copy(my_bmap);



I know there is no bmap_copy() command, but the idea is to create a copy of a given bmap - so i would have 2 seperate bmaps that are the same image. Is that possible?
Posted By: Quad

Re: How to make a copy of a bmap? - 04/13/11 22:58

what about:
my_bmap = bmap_create("my_file.jpg");
my_copy = bmap_create("my_file.jpg");
Posted By: WretchedSid

Re: How to make a copy of a bmap? - 04/13/11 23:07

This should do the trick
Code:
BMAP *bmap_copy(BMAP *source)
{
  if(!source)
    return NULL; // Oh look, invalid data...
    
  var width, height, format, bitDepth;
  BMAP *result; // The resulting bitmap
  
  
  width  = bmap_width(source); // Get the width
  height = bmap_height(source); // and height of the source bitmap
  
  format = bmap_lock(source, 0); // Lock the source bitmap
  
  // Get the bitdepth of the source
  // Only supported by the pixel functions is 16, 24 and 32 bit, so we don't care about the rest
  switch(format)
  {
    case 88:
    case 565:
      bitDepth = 16;
      break;
      
    case 888:
      bitDepth = 24;
      break;
    
    case 8888:
      bitDepth = 32;
      break;
      
    default:
      bmap_unlock(source);
      return NULL; // Well, the bitmap isn't in a format we can use
      break;
  }
    
  // Create a new, void, bitmap with the exact same size and bit depth
  result = bmap_createblack(width, height, bitDepth);
  if(result)
  {
    int x, y;
    bmap_lock(result, 0); // Lock the new bitmap so that we can write into it
    
    // Copy all pixels by iterating over each source pixel and storing it in the copy
    for(x=0; x<width; x++)
    {
      for(y=0; y<height; y++)
      {
        var pixel = pixel_for_bmap(source, x, y);
        pixel_to_bmap(result, x, y, pixel);
      }
    }
    
    bmap_unlock(result); // Unlock the previously locked copy
  }
  
  bmap_unlock(source); // Unlock the source
  return result; // Return the result, this might be NULL if the bmap_createblack() failed
}



Please remark: The code isn't tested as I don't have Gamestudio, but it should work.
Posted By: Caermundh

Re: How to make a copy of a bmap? - 04/13/11 23:13

thanks, JustSid. I was thinking along those same lines, and that should work. The thing is, if im dealing with copying more than a few BMAPs, wouldnt a function like this be kinda slow? espicially if im dealing with large BMAPs?
Posted By: WretchedSid

Re: How to make a copy of a bmap? - 04/13/11 23:15

Uh well, try it? I can't answer this for you.
Just let the function run a few thousand times with a large bmap and look how long it took.
Posted By: JibbSmart

Re: How to make a copy of a bmap? - 04/13/11 23:23

If you want it faster, try using bmap_process and a shader that just looks up the right input pixel.

Jibb
Posted By: MrGuest

Re: How to make a copy of a bmap? - 04/13/11 23:28

fastest way i can think of
Code:
BMAP *bmap_clone(BMAP* bmp){
	
	if(bmp){
		BMAP* bmpNew = malloc(sizeof(BMAP));
		memcpy(bmpNew, bmp, sizeof(BMAP));
		return(bmpNew);
	}else{
		return(NULL);
	}
}



and to prove it's working, have a bmap called "bmap_to_copy.bmp"
Code:
///////////////////////////////
#include <acknex.h>
#include <default.c>

///////////////////////////////

ENTITY* e1;

BMAP* bmp_original = "bmap_to_copy.bmp";
BMAP* bmp_clone;

PANEL* pnl_original = {
	pos_x = 0;
	flags = SHOW;
}

PANEL* pnl_clone = {
	pos_x = 64;
	flags = SHOW;
}


void bmap_cross(){
	
	BMAP* tgablitz = pnl_original.bmap;
	var format; 
	var pixel;
	format = bmap_lock(tgablitz,0);
	if (format >= 565) {
		pixel = pixel_for_vec(vector(0,0,255),100,format); // red color
		pixel_to_bmap(tgablitz,10,10,pixel);
		pixel_to_bmap(tgablitz,10,11,pixel);
		pixel_to_bmap(tgablitz,10,12,pixel);
		pixel_to_bmap(tgablitz,10,13,pixel);
		pixel_to_bmap(tgablitz,10,14,pixel);
		pixel_to_bmap(tgablitz,8,12,pixel);
		pixel_to_bmap(tgablitz,9,12,pixel);
		pixel_to_bmap(tgablitz,11,12,pixel);
		pixel_to_bmap(tgablitz,12,12,pixel);
	}
	bmap_unlock(tgablitz);
}

BMAP *bmap_clone(BMAP* bmp){
	
	BMAP* bmpNew = malloc(sizeof(BMAP));
	memcpy(bmpNew, bmp, sizeof(BMAP));
	return(bmpNew);
}

void main(){
	
	wait(1);
	
	bmp_clone = bmap_clone(bmp_original);
	
	pnl_original.bmap = bmp_original;
	pnl_clone.bmap = bmp_clone;
	
	bmap_cross();
}



Posted By: Uhrwerk

Re: How to make a copy of a bmap? - 04/14/11 01:41

On the one hand this is a bit of genius. On the other hand I think it's quite a bit dangerous code as it creates an exact copy of the bitmap. In other words there are now two engine objects with the same handle. I don't know if that is a good idea. I'd go with JustSids solution. Maybe change the for loop copying the pixel data to a single call of bmap_blit. Speed isn't that important in this case anyways as you shouldn't create hundreds of bitmap copies.
Posted By: FoxHound

Re: How to make a copy of a bmap? - 04/14/11 02:01

I'm looking at the code and they would not have the same handle just the same texture. I can test this for sure when I get home but this looks good.
Posted By: Uhrwerk

Re: How to make a copy of a bmap? - 04/14/11 02:13

They would have the same handle. Have a look at atypes.h if you don't believe me. They would even share the same pixel data and texture buffer. So if you manipulated one of them the changes would affect all copies created with mem_cpy.
Posted By: TechMuc

Re: How to make a copy of a bmap? - 04/14/11 09:37

If you need a fast way of the copy process, you have to copy the pixels directly via memcpy. Create a bitmap as stated by slin with the same format and the same size, and make a clean copy of the pixels structure element in the BMAP structure.
Do not forget to lock the file and (very important) to preload the bitmap you want to copy via bmap_preload.
Posted By: Schubido

Re: How to make a copy of a bmap? - 04/14/11 11:01

Hm, I think this should do it :

BMAP* bmSource = bmap_create("whatever.bmp");
BMAP* bmDest = bmap_createblack(bmap_width(bmSource), bmap_height(bmSource) ,bmap_format(bmSource));

bmap_blit (bmDest,bmSource,vector(0,0,0),vector(bmap_width(bmSource),bmap_height(bmSource),0));
Posted By: WretchedSid

Re: How to make a copy of a bmap? - 04/14/11 11:32

I would assume that bmap_blit creates a bit more overhead instead of copying the raw pixel data, as it needs to do some more checks about the boundaries and such stuff.
Also, 8888 is not a valid bitmap depth wink

MrGuests looks for me also a bit risky, the new bmap couldn't be purged via ptr_remove() and if the source bitmap gets removed, you are probably pretty much fucked. Also: The internal structure might be changed in the future, destroying the memcpy approach completely.
Posted By: Schubido

Re: How to make a copy of a bmap? - 04/14/11 11:49

Quote:
Also, 8888 is not a valid bitmap depth

Uuups - too much <ctrl>c <ctrl>v blush
Corrected it in my previous post.

Quote:
would assume that bmap_blit creates a bit more overhead instead of copying the raw pixel data, as it needs to do some more checks about the boundaries and such stuff.


Well - I dont have an insight into the internals of this function, but I would expect it to calculate the bounderies and then using memcpy. In this specific case I would expect that bmap_blit would do a single memcpy. But maybe it doesn't and if so I agree with the overhead argument.
Posted By: JibbSmart

Re: How to make a copy of a bmap? - 04/14/11 12:27

bmap_createblack + bmap_process. Really fast. Unless you don't have Commercial or Pro.

Jibb
Posted By: Quad

Re: How to make a copy of a bmap? - 04/14/11 12:54

what about bmap_createblack and memcpy finalbits?
Posted By: Caermundh

Re: How to make a copy of a bmap? - 04/14/11 14:13

@ Mr. Guest:

As usual, sir, you are spot on the money. I ran your code and it creates a copy of the bitmap perfectly. I think this is like the fifth or sixth question youve answered for me. I appriciate your expertise - thank you for taking the time to answer my questions.

@Uhwrek:

No, the copied bitmap in his code does not create 2 bitmaps with the same handle, if you look at the testing code he posted originally, it puts a red cross on the original bmap. When i ran the code, i had two bmaps - one with and one without a red cross on it.

@JustSid:
given that this Mr. Guests method uses malloc(), and that the source and copy bitmaps are independant of each other, i see no problems with this method. Gamestudios internal structure is irrelevant in this case - since were using malloc()

@Schubido:
I looked at bmap_blit(), ill try it out, but i am leary as the manual specifically states that it is slow. Thanks for the suggestion

@JulzMighty:
Bmap_process? I could not find that in the manual. I have Game studio pro so I should have that command? Or maybe it was only added in A8? (I am on A7.82 atm - once I start a project on one version, i finish it on that version. Saves me alot of time re-writing scripts and such when they decide to change how a given command works.)
Posted By: JibbSmart

Re: How to make a copy of a bmap? - 04/14/11 14:25

Ah. A8 only, I'm afraid.

Jibb
Posted By: TechMuc

Re: How to make a copy of a bmap? - 04/14/11 14:28

@Caermundh: I warn you to use the proposal of mr. guest. I do not want to sound rude, but the attempt is nonsense and will result in memory leaks, crashs or almost impossible to find bugs.

The code contains several errors

1) All Gamestudio objects are saved in a linked list. The method of a direct memcpy instruction will NOT attach this object to the list.

2) The BMAP structure contains pointers to values. With memcpy you only copy the pointer-values but NOT the values behind the pointers. Therefore the approach is no really copy.

3) 3D Gamestudio objects must be created via 3D-Gamestudio instructions (e.g. bmap_create, snd_create etc.), because you have no idea what the bmap_create instruction is doing besides the attachment to the linked list.

Posted By: WretchedSid

Re: How to make a copy of a bmap? - 04/14/11 14:31

@Caermundh: Not true, here is the struct copied from avtypes.h
Code:
typedef struct BMAP {
	C_LINK	link;
	long 	width,height; // original size of the bitmap
	long	bytespp;	// original bytes per pixel (1..4)
	void	*ptr;		// internal use
	byte	*pixels;	// ptr to palettized, 565, 4444, 888 or 8888 coded original image
	long	flags;      // see BMPF_... above
	void	*d3dtex;	// 	LPDIRECT3DTEXTURE9 (usually a different format than the original image)
	float	u1,v1,u2,v2; // texture pixel bounds
	long	u,v;		// cutout start size
	long	refcount;	// for implicitely created bmaps
	long	finalwidth,finalheight,finalbytespp;
	long	pitch,finalpitch;
	long	miplevels;
	long    finalformat;
	void	*finalbits;	// bitmap content when locked, otherwise NULL
} BMAP;



As you can see, a BMAP is supposed to be in an linked list (C_LINK link), you destroy this when you copy your BMAP via memcpy. Thus the new BMAP thinks it lives in the linked list and links to its neighbors, but thats not the real case. Calling _ANY_ function that might use this linked list internally will have horrible results. YOU are not the owner of the linked, so don't mess with it!

The next thing ist that there are four pointers, their content isn't copied with memcpy, so you now have two BMAPs that share to some degree their data. Calling _ANY_ function that might alter this data, will alter the data for all BMAPs.

Last but not least: The copy can't be purged from memory via ptr_remove() because it just isn't linked in the linked list. Again, you don't own it, you don't mess with it. When you now use ptr_remove() on the root source BMAP, you will invalidate all copied BMAPs as the shared data is purged and the linked list has changed, thus the copied BMAPs have a wrong state of the linked list (not to mention that this is always the case when a function alters the linked list).


Its _IS_ evil to do it the way MrGuest showed, depending on this function will result for sure in some horrible bugs!


Edit: Another snippet from the atypes.h
Code:
typedef struct C_LINK {
	long	index;		// index number of the object (handle)
	struct C_LINK *next; // pointer to next object
	char	*name;		// pointer to name of object (if any)
} C_LINK;				// object header



Do you still think that you now have two BMAPs with two different handles?
Posted By: Uhrwerk

Re: How to make a copy of a bmap? - 04/14/11 14:40

Originally Posted By: Caermundh
@Uhwrek: No, the copied bitmap in his code does not create 2 bitmaps with the same handle, if you look at the testing code he posted originally, it puts a red cross on the original bmap. When i ran the code, i had two bmaps - one with and one without a red cross on it.

I don't know what code you're talking about, I was talking about Mr. Guests code. Most obviously you're not believing me. As I already said, you better have a look at atypes.h
Code:
typedef struct BMAP {
	C_LINK	link;
	long 	width,height; // original size of the bitmap
	long	bytespp;	// original bytes per pixel (1..4)
	void	*ptr;		// internal use
	byte	*pixels;	// ptr to palettized, 565, 4444, 888 or 8888 coded original image
	long	flags;      // see BMPF_... above
	void	*d3dtex;	// 	LPDIRECT3DTEXTURE9 (usually a different format than the original image)
	float	u1,v1,u2,v2; // texture pixel bounds
	long	u,v;		// cutout start size
	long	refcount;	// for implicitely created bmaps
	long	finalwidth,finalheight,finalbytespp;
	long	pitch,finalpitch;
	long	miplevels;
	long    finalformat;
	void	*finalbits;	// bitmap content when locked, otherwise NULL
} BMAP;


As you can see here the BMAP struct contains the C_LINK struct, which contains the handle. So using the mem_cpy approach WILL copy this handle. As you can see furthermore there are different pointers to image data, namely *pixels, *d3dtex and *finalbits. All this data does not get copied by mem_cpy, just the pointers get copied. Hence the image data does not get copied and copying the bitmap is kind of senseless. If you understand that and are willing to live with the consequences, go for it. But don't cry if this will fuck things up later on.

EDIT: @JustSid: pffffffff....
Posted By: Joey

Re: How to make a copy of a bmap? - 04/14/11 14:46

if bmap_blit uses bitblt, it could be accelerated by your graphics hardware (if the graphics card is the target of the bitmap, which I don't know).
Posted By: Caermundh

Re: How to make a copy of a bmap? - 04/14/11 16:29

@Uhrwerk, Just Sid:
I see what you are saying about C_LINK link; getting copied by mem copy, so that you effectivly have 2 bmaps with the same handle, and i also see your argument that memcpy() will copy only the pointers for *pixels, *d3dtex, & *finalbits without copying the data that they point to.

Given that memcpy() will copy the pointers and not the data, if i write to one bmap, it would write to both, because each bmap is pointing to the same data. But i have run the code, and was able to write to one without affecting the other.

Here is the code:

Code:
///////////////////////////////
#include <acknex.h>
#include <default.c>

///////////////////////////////

ENTITY* e1;

BMAP* bmp_original = "bmap_to_copy.bmp";
BMAP* bmp_clone;

PANEL* pnl_original = {
	pos_x = 0;
	flags = SHOW;
}

PANEL* pnl_clone = {
	pos_x = 64;
	flags = SHOW;
}


void bmap_cross(){
	
	BMAP* tgablitz = pnl_original.bmap;
	var format; 
	var pixel;
	format = bmap_lock(tgablitz,0);
	if (format >= 565) {
		pixel = pixel_for_vec(vector(0,0,255),100,format); // red color
		pixel_to_bmap(tgablitz,10,10,pixel);
		pixel_to_bmap(tgablitz,10,11,pixel);
		pixel_to_bmap(tgablitz,10,12,pixel);
		pixel_to_bmap(tgablitz,10,13,pixel);
		pixel_to_bmap(tgablitz,10,14,pixel);
		pixel_to_bmap(tgablitz,8,12,pixel);
		pixel_to_bmap(tgablitz,9,12,pixel);
		pixel_to_bmap(tgablitz,11,12,pixel);
		pixel_to_bmap(tgablitz,12,12,pixel);
	}
	bmap_unlock(tgablitz);
}

BMAP *bmap_clone(BMAP* bmp){
	
	BMAP* bmpNew = malloc(sizeof(BMAP));
	memcpy(bmpNew, bmp, sizeof(BMAP));
	return(bmpNew);
}

void main(){
	
	wait(1);
	
	bmp_clone = bmap_clone(bmp_original);
	
	pnl_original.bmap = bmp_original;
	pnl_clone.bmap = bmp_clone;
	
	bmap_cross();
}



(this is just a re-paste of Mr. Guests earlier code.)

When I run the above code, I end up with 2 bitmaps on the screen, one with a red_cross on it and one without the red cross.

So now i am just confused. I think JustSid and Uhrwerk are both correct in their statements - memcpy() will copy the pointers without copying the data they point to, so when i modify the data - it would modify both bmaps because they are the same bmap. But if that is so, than how am i getting 2 distinct bmaps?
Posted By: WretchedSid

Re: How to make a copy of a bmap? - 04/14/11 17:19

Just a guess: Move the panels around, I guess there is a cache to avoid too many drawcalls.
Posted By: Uhrwerk

Re: How to make a copy of a bmap? - 04/14/11 17:31

Now that is exactly what I meant with "fucking things up later on".

The reason for your two "distinct" bitmaps is the order you're doing your calls in. You copy the memory area while the DirectX Texture of the bitmap has not yet been allocated. Hence the engine will find NULL when inspecting the bitmaps and will hence allocate two textures for the same image data located in the BMAP.pixel member. The pixel_to_bmap instruction will now operate on the texture rather than on the original image data. As long as both bitmaps will remain in the video memory it will look as if there are two bitmaps because there are two textures. You're lucky in this case that the DirectX Texture was not yet allocated. Still don't believe it? Place a "bmap_preload(bmp_original);" right after the wait in your main function. You'll get a very "surprising" result.

Seriously: Several experienced forum members warned you not to mess up with internal engine data and you did it anyways and got results you we're not able to understand. Does that ring a bell for you?
Posted By: MrGuest

Re: How to make a copy of a bmap? - 04/14/11 21:49

sorry, the code i posted is flawed!

if you're using commands within the engine such as prt_remove these will be invalid...

only use such commands if you know what you're doing with memory when you know what GS thinks it's doing with memory....
Posted By: Caermundh

Re: How to make a copy of a bmap? - 04/15/11 01:27

OK...so i did some testing. When I did a bmap_preload like you suggested Uhrwerk, the bitmaps both had red crosses on them. The only reason I was seeing one with and one without was because of, like you said, the way Gamestudios caches bitmaps.

Thing is, Mr. Guest got my curiosity up, so i kept at it. I knew memcpy could work. Heres what I came up with:

Code:
//////////////////////////////////////////////////////////////////////
// test.c

#include <acknex.h>
#include <default.c>
#include "headers.h"

#define PRAGMA_PATH "Graphics\Item Icons"
#define PRAGMA_PATH "Models"

FONT* standard_font = "ackfont.pcx";

BMAP* bmp_original = "jug_icon.tga";
BMAP* bmp_clone = "jug_icon.tga";

PANEL* pnl_original = {
	pos_x = 0;
	pos_y = 40;
	flags = SHOW;
}

PANEL* pnl_clone = {
	pos_x = 64;
	pos_y = 40;
	flags = SHOW;
}

PANEL* info_panel =
{   pos_x = 20;
    pos_y = 20;
    flags = SHOW;
    digits = 0,0,3.0,standard_font,1,test;
    digits = 0,10,3.0,standard_font,1,test2;
}

void bmap_cross(){
	
	BMAP* tgablitz = pnl_original.bmap;
	var format; 
	var pixel;
	format = bmap_lock(tgablitz,0);
	if (format >= 565) {
		pixel = pixel_for_vec(vector(0,0,255),100,format); // red color
		pixel_to_bmap(tgablitz,10,10,pixel);
		pixel_to_bmap(tgablitz,10,11,pixel);
		pixel_to_bmap(tgablitz,10,12,pixel);
		pixel_to_bmap(tgablitz,10,13,pixel);
		pixel_to_bmap(tgablitz,10,14,pixel);
		pixel_to_bmap(tgablitz,8,12,pixel);
		pixel_to_bmap(tgablitz,9,12,pixel);
		pixel_to_bmap(tgablitz,11,12,pixel);
		pixel_to_bmap(tgablitz,12,12,pixel);
	}
	bmap_unlock(tgablitz);
}

BMAP *bmap_copy(BMAP* bmp)
{   BMAP* bmpNew = bmap_createblack(1,1,24);
    bmpNew.width = bmp.width;
    bmpNew.height = bmp.height;
    bmpNew.bytespp = bmp.bytespp;
    bmpNew.flags = bmp.flags;
    bmpNew.u1 = bmp.u1;
    bmpNew.v1 = bmp.v1;
    bmpNew.u2 = bmp.u2;
    bmpNew.v2 = bmp.v2;
    bmpNew.u = bmp.u;
    bmpNew.v = bmp.v;
    bmpNew.refcount = bmp.refcount;
    bmpNew.finalwidth = bmp.finalwidth;
    bmpNew.finalheight = bmp.finalheight;
    bmpNew.finalbytespp = bmp.finalbytespp;
    bmpNew.pitch = bmp.pitch;
    bmpNew.finalpitch = bmp.finalpitch;
    bmpNew.miplevels = bmp.miplevels;
    bmpNew.finalformat = bmp.finalformat;
    bmpNew.finalbits = NULL;
    bmpNew.d3dtex = NULL;
    bmpNew.pixels = malloc((bmp.width*bmp.height)*bmp.bytespp);
    memcpy(bmpNew.pixels, bmp.pixels, (bmp.width*bmp.height)*bmp.bytespp);
    return(bmpNew);
}

void main()
{   wait(1);
    bmap_preload(bmp_original);
    bmp_clone = bmap_copy(bmp_original);
    pnl_original.bmap = bmp_original;
    pnl_clone.bmap = bmp_clone;
    bmap_cross();
    test = bmp_original.link.index;
    test2 = bmp_clone.link.index;
}



Instead of using memcpy to copy the BMAP, i use memcpy to copy the bmap pixels. As you can see, i did a bmap_preload so im not getting any cached bmaps. I checked bmp_original.link.index and bmp_clone.link.index and they are different numbers, so the two bmaps are unique. When I ran the code, i got one bmap with a red cross and one without.

the only problem the code has is that i get an "invalid pointer freed" error message when I exit the game.

Quote:

Seriously: Several experienced forum members warned you not to mess up with internal engine data and you did it anyways and got results you we're not able to understand. Does that ring a bell for you?


I mess with stuff im not supposed to all the time. Its how I learn. As well - when I am messing with stuff I dont fully understand - I gnerally do it in a test enviroment where I can blow things up all day long with no harm done.

In the future, I will avoid asking "why didnt that work?" style questions like I did in the later posts of this thread. You should come to the boards with "How do I...?" style questions or with answers. I apologize for violating board etiquette the way i did.
Posted By: Schubido

Re: How to make a copy of a bmap? - 04/15/11 06:39

Hi again,

memcpy for pixels sounds plausible to me - if you assume that pixels is really the bitmap without additional GS internal information.
But I would recommend not to do this:
bmpNew.width = bmp.width;
bmpNew.height = bmp.height;
bmpNew.bytespp = bmp.bytespp;
bmpNew.pixels = malloc((bmp.width*bmp.height)*bmp.bytespp);
This (.pixels = malloc...) generates a memory leak, because the previous pointer gets lost.

IMHO it would be better to let GS create the target bitmap with the proper dimensions:
BMAP* bmNew = bmap_createblack(bmp.width, bmp.height,bmap_format(bmp));
... and then do the rest of the copy stuff.
Posted By: HeelX

Re: How to make a copy of a bmap? - 04/15/11 07:02

Funny, I coded some functions for the same topic yesterday. This is what I came up with:
Code:
BMAP* bmap_clone (BMAP* src)
{
   // no source?
   if (src == NULL)
      return(NULL);
      
   var format = bmap_lock(src, 0);
   
   // invalid bitmap or unknown format?
   if (format == 0)
      return(NULL);
   else  
      bmap_unlock(src);
   
   BMAP* r = bmap_createblack(src->width, src->height, format);
   
   if (r != NULL)
      bmap_copy(src, r);
   
   return(r);
}

BMAP* bmap_copy (BMAP* src, BMAP* dest)
{
   if ((src == NULL) || (dest == NULL))
      return(NULL);
   
   bmap_blit(dest, src, NULL, NULL);
   
   return(dest);
}


Using the format retrieved by bmap_lock in bmap_createblack is actually safe. The manual says about bmap_createblack:
Quote:
format - bitmap format (uncompressed formats only; see bmap_lock) or number of bits per pixel (8, 16, 24, 32).

I'm not 100% sure, if a bmap_process approach would be faster - but from what I believe and know, it will smile. Maybe someone has the time to write a generic function for that?
Posted By: WretchedSid

Re: How to make a copy of a bmap? - 04/15/11 09:21

Originally Posted By: HeelX

Using the format retrieved by bmap_lock in bmap_createblack is actually safe. The manual says about bmap_createblack:
Quote:
format - bitmap format (uncompressed formats only; see bmap_lock) or number of bits per pixel (8, 16, 24, 32).


Ouh, I misunderstood the manual somehow. Thanks for the clarification.
Posted By: Caermundh

Re: How to make a copy of a bmap? - 04/15/11 13:53

@Schubido:

Good catch on the memory leak. I hadn't thought of that. I'll definitly tweak the code to not do that. I cant use bmap_createblack to set pixels because I've found out I need to copy a source bitmap to a target bmap without creating a new bitmap. Here's what I'm currently doing to copy the bmap:

Code:
function *bmap_copy(BMAP* target_bmp, BMAP* source_bmp)
{   target_bmp.width = source_bmp.width;
    target_bmp.height = source_bmp.height;
    target_bmp.bytespp = source_bmp.bytespp;
    target_bmp.flags = source_bmp.flags;
    target_bmp.u1 = source_bmp.u1;
    target_bmp.v1 = source_bmp.v1;
    target_bmp.u2 = source_bmp.u2;
    target_bmp.v2 = source_bmp.v2;
    target_bmp.u = source_bmp.u;
    target_bmp.v = source_bmp.v;
    target_bmp.refcount = source_bmp.refcount;
    target_bmp.finalwidth = source_bmp.finalwidth;
    target_bmp.finalheight = source_bmp.finalheight;
    target_bmp.finalbytespp = source_bmp.finalbytespp;
    target_bmp.pitch = source_bmp.pitch;
    target_bmp.finalpitch = source_bmp.finalpitch;
    target_bmp.miplevels = source_bmp.miplevels;
    target_bmp.finalformat = source_bmp.finalformat;
    target_bmp.finalbits = NULL;
    target_bmp.d3dtex = NULL;
    target_bmp.pixels = malloc((source_bmp.width*source_bmp.height)*source_bmp.bytespp);
    memcpy(target_bmp.pixels, source_bmp.pixels, (source_bmp.width*source_bmp.height)*source_bmp.bytespp);
}



Do you think free(target_bmp.pixels); would work?

@HeelX:

Schubido had actually suggested using bmap_blit() to copy the bmap earlier in the thread. I decided against using blit because of concerns over the speed of the command.

I also think i've figured out the cause of the invalid pointer freed error message when exiting the game - its because im setting d3dtex to NULL - Gamestudio is expecting something to be there. I just have to figure out the format of whats stored at d3dtex (or at least how to figure the *size* of whats stored there) and I can copy it. That should fix the error.
Posted By: Uhrwerk

Re: How to make a copy of a bmap? - 04/15/11 15:46

Originally Posted By: Caermundh
In the future, I will avoid asking "why didnt that work?" style questions like I did in the later posts of this thread.

That is not the problem. The problem is, that you seem to be advice-resistant. So here is my last comment concerning this topic. Go with HeelXs solution. It's clean, effective and fast - maybe even faster than yours. Your solution is error prone and has at least one other memory leak you haven't noticed yet. Several other problems might arise from it.
Posted By: Schubido

Re: How to make a copy of a bmap? - 04/15/11 17:19

Quote:
Do you think free(target_bmp.pixels); would work?



You could try, but I would not expect it to work properly. You don't know how memory allocation is done within the GS functions. I guess its not malloc (get memory from OS), but maybe an internal heap management. This could also be the reason why you got a "invalid pointer freed" error when exiting the game. Why do I think so? Because a message like this would be the expected behaviour if GS calls an internal (not OS!) free function for a pointer to memory which is allocated with standard malloc.
However - its just guessing.

Therefore I agree with Uhrwerk and the rest of the gang;) not to deal with GS internals. Even if you solve it, there is no guerantee that it works in all configurations or with the next update.
Have you done a performance test with bmap_blit? I think HeelX code should do the job perfectly.
If this is really to slow (how many copies do you produce??) you could try to "ask the developers" if there is a way to do it faster.
Posted By: HeelX

Re: How to make a copy of a bmap? - 04/16/11 07:24

Originally Posted By: Schubido
Have you done a performance test with bmap_blit? I think HeelX code should do the job perfectly.
I designed a stress test to discover the time consumed by both cases bmap_blit (CPU) and -process (GPU). I create the same amount of copies of bitmaps with both methods and count the consumed time with dtimer(). For each iteration I create a new bitmap to avoid engine-optimizations like not overwriting the source image pointer for the shader because the image pointer has not been changed since the last call of bmap_process.

The following test calculates the mean processing time for both methods for 100 iterations for a 512x512x24 image:

Code:
#include <acknex.h>
#include <stdio.h>

BMAP* bmap_clone (BMAP* src);
BMAP* bmap_cloneGPU (BMAP* src);

int main()
{
   wait(1);
   
   int iterations = 100;
   int width = 512, height = 512, format = 24;
   
   double t_cpu = 0;
   
      int i;
      for (i = 0; i < iterations; i++)
      {
         BMAP* b = bmap_createblack(width,height,format); // src
         
         dtimer();
         BMAP* c = bmap_clone(b);
         t_cpu += dtimer();
         
         ptr_remove(b);
         ptr_remove(c);       
      }

   t_cpu /= (double)iterations;
   
   double t_gpu = 0;
   
      int i;
      for (i = 0; i < iterations; i++)
      {
         BMAP* b = bmap_createblack(width,height,format); // src
         
         dtimer();
         BMAP* c = bmap_cloneGPU(b);
         t_gpu += dtimer();
         
         ptr_remove(b);
         ptr_remove(c);
      }
   
   t_gpu /= (double)iterations;

   char log [512];
   sprintf(log, "bitmap = %dx%dx%d\niterations = %d\nbmap_clone (CPU) ~ %f microsecs\nbmap_clone (GPU) ~ %f microsecs", width, height, format, iterations, t_cpu, t_gpu);
   error(log);
}

BMAP* bmap_clone (BMAP* src)
{
   // no source?
   if (src == NULL)
      return(NULL);
      
   var format = bmap_lock(src, 0);
   
   // invalid bitmap or unknown format?
   if (format == 0)
      return(NULL);
   else  
      bmap_unlock(src);
   
   BMAP* r = bmap_createblack(src->width, src->height, format);
   
   if (r != NULL)
      bmap_blit(r, src, NULL, NULL);
   
   return(r);
}

MATERIAL* mtlCopy =
{
   effect = "
      
      Texture TargetMap; // src
      sampler2D smpSrc = sampler_state
      {
         texture = <TargetMap>;
      };

      float4 PS (float2 Tex: TEXCOORD0): COLOR
      {
         return(tex2D(smpSrc, Tex.xy));
      }

      technique t
      {
         pass p
         {
            AlphaBlendEnable = false;
            PixelShader = compile ps_2_0 PS();
         }
      }
   ";
}

BMAP* bmap_cloneGPU (BMAP* src)
{
   // no source?
   if (src == NULL)
      return(NULL);
      
   var format = bmap_lock(src, 0);
   
   // invalid bitmap or unknown format?
   if (format == 0)
      return(NULL);
   else  
      bmap_unlock(src);
   
   BMAP* r = bmap_createblack(src->width, src->height, format);
   
   if (r != NULL)
   {
      bmap_process(r, src, mtlCopy);
   }
   
   return(r);
}



Here are my results for a 512x512x24 bitmap on my system:

iterations = 1
bmap_clone (CPU) ~ 14110.593750 microsecs
bmap_clone (GPU) ~ 19934.037109 microsecs

iterations = 5
bmap_clone (CPU) ~ 14125.698242 microsecs
bmap_clone (GPU) ~ 8372.354492 microsecs

iterations = 100
bmap_clone (CPU) ~ 14442.785156 microsecs
bmap_clone (GPU) ~ 4864.554688

iterations = 1000
bmap_clone (CPU) ~ 14664.015625 microsecs
bmap_clone (GPU) ~ 4767.796875 microsecs

You can see, that working with bmap_blit is always around 14k microsecs. Using bmap_process has obviously an initial overhead, because after using it first time, it takes even longer than with bmap_blit. The mean time consumed by taking bmap_process to copy the bitmap is being dramatically reduced after the very first iterations and reduced to a mean time of around 5k microsecs or less. Over the time, bmap_process consumes roughly 1/3 of the time which is needed for copying with bmap_blit.

There are still open questions, like how much time is consumed by bmap_lock to get the format in order to create a new bitmap in correspondence to the source image and if the mean time of bmap_process will increase if another shader is used in between (possible overhead for changing shader code).

Conclusion: If you need performance and do lots of bitmap operations which can be done in a fragment shader, go with bmap_process!
Posted By: JibbSmart

Re: How to make a copy of a bmap? - 04/16/11 14:53

Shaders aren't normally compiled until the first time they're used. I think that's likely the biggest part of the initial overhead. My test results varied a lot, although even with lower iterations GPU beat CPU (that's just because my GPU's a beast). But if I called the bmap_cloneGPU function early so that the shader would be compiled, even just one iteration took much less time.

Jibb
Posted By: HeelX

Re: How to make a copy of a bmap? - 04/16/11 16:34

Ah I forgot the compilation time needed for shaders smile thanks, Julz!
Posted By: Caermundh

Re: How to make a copy of a bmap? - 04/16/11 20:28

I went and did some tests on bmap_blit() vs bmap_copy(). I ran the following:

Code:
PANEL* info_panel =
{   pos_x = 20;
    pos_y = 20;
    flags = SHOW;
    digits = 0,0,3.0,standard_font,1,test;
    digits = 0,10,3.0,standard_font,1,test2;
}

BMAP* original_bmap = "jug_icon.tga";
BMAP* clone_bmap;

function *bmap_copy(BMAP* target_bmp, BMAP* source_bmp)
{   target_bmp.width = source_bmp.width;
    target_bmp.height = source_bmp.height;
    target_bmp.bytespp = source_bmp.bytespp;
    target_bmp.flags = source_bmp.flags;
    target_bmp.u1 = source_bmp.u1;
    target_bmp.v1 = source_bmp.v1;
    target_bmp.u2 = source_bmp.u2;
    target_bmp.v2 = source_bmp.v2;
    target_bmp.u = source_bmp.u;
    target_bmp.v = source_bmp.v;
    target_bmp.refcount = source_bmp.refcount;
    target_bmp.finalwidth = source_bmp.finalwidth;
    target_bmp.finalheight = source_bmp.finalheight;
    target_bmp.finalbytespp = source_bmp.finalbytespp;
    target_bmp.pitch = source_bmp.pitch;
    target_bmp.finalpitch = source_bmp.finalpitch;
    target_bmp.miplevels = source_bmp.miplevels;
    target_bmp.finalformat = source_bmp.finalformat;
    target_bmp.finalbits = NULL;
    target_bmp.d3dtex = NULL;
    target_bmp.pixels = malloc((source_bmp.width*source_bmp.height)*source_bmp.bytespp);
    memcpy(target_bmp.pixels, source_bmp.pixels, (source_bmp.width*source_bmp.height)*source_bmp.bytespp);
}

function main()
{   var count = 0;
    clone_bmap = bmap_createblack(32,32,24);
    timer();
    while(count<1000)
    {   bmap_copy(clone_bmap,original_bmap);
        count += 1;
    }
    test = timer();
    count = 0;
    timer();
    while(count<1000)
    {   bmap_blit(clone_bmap,original_bmap,NULL,NULL);
        count += 1;
    }
    test2 = timer();
}



Not the most accurate test in the world, i realize, but i just wanted to get a rough idea of how fast bmap_copy vs bmap_blit would be. i found that 1000 calls to bmap_copy took ~950 microseconds and 1000 calls to bmap_blit took ~947 microseconds.

So bmap_blit really is a fast (even slightly faster) than using memcpy directly. I find that more than a little surprising considering that it has to be doing more than just a simple memcpy(). I'm also not understanding why the manual says that the bmap_blit command is slow. memcpy() is about as fast as it gets, and bmap_blit is at least as fast as it - how is that a "slow" command?

Anyways, since bmap_blit *is* as fast as a memcpy() and doesnt have any of the problems that bmap_copy() has, i'm definitly using HeelX's solution. Thank you all for all the advice.
Posted By: WretchedSid

Re: How to make a copy of a bmap? - 04/16/11 21:31

bmap_blit is not implemented in Lite-C but in C++ and thus could profit from the compilers optimization.
Considering that the Lite-C compiler probably does zero to nothing optimizations with your code, its not really a surprise that bmap_blit() is faster than memcpy()

(I assume that Lite-Cs memcpy is an actual, non inlined, function)
Posted By: Caermundh

Re: How to make a copy of a bmap? - 04/16/11 22:16

memcpy() is not a lite-c function - its a C function, like malloc() and free(). If bmap_blit is implemented in C++, then its working at the same level as memcpy() so of course its going to be just as fast. That would explain it.

I wonder if all bmap_blit is doing is just a memcpy itself? I find the nearly identical run times for the two different commands to be a little suspicious.

I also still am wondering why the manual categorizes the speed of this command as "slow"? - its obviously not slow.
Posted By: Uhrwerk

Re: How to make a copy of a bmap? - 04/16/11 23:14

Originally Posted By: Caermundh
I wonder if all bmap_blit is doing is just a memcpy itself?

Yeah, sure, that's just what it does. memcpy, programmers first choice in copying and scaling bitmaps since 1872.
Posted By: HeelX

Re: How to make a copy of a bmap? - 04/17/11 07:48

Originally Posted By: Uhrwerk
mem_cpy, programmers first choice in copying and scaling bitmaps since 1872.

Gamestudio quote of the day! grin

Originally Posted By: Caermundh
I wonder if all bmap_blit is doing is just a memcpy itself? I find the nearly identical run times for the two different commands to be a little suspicious.

Bitmaps can be considered as really huge pieces of data. In most cases you are dealing with RGBA textures and this means 4bytes per pixel, which means that a 512x512 bitmap is 1 MB big. This is not much at all and this should be really fast even with bmap_blit, but if you consider scaling, offsetted copying and cropping (everything supported by bmap_blit and bmap_blitpart), you get additional overhead. Multiply that with the amount of copy-instructions and if you use even bigger bitmaps - good luck!

That is why it is not only marked with "slow", but with "slow (depends on scaling and on whether the bitmap is visible or used as a render target)". Another fact is, that mem_cpy is for sure single threaded. You can actually make your own bmap_copy function faster, if you open e.g. four threads, use a construct like a countdown latch or so for synchronizing and copy the memory data in parallel.

That is, by the way, also the reason why the processing via bmap_process takes only a fraction of the time, because fragment shader processing is ran in parallel on a GPU.

Nevertheless, I doubt that you do any good with creating copies of a bitmap while NOT hinging them into the internal C_LINK structure.
© 2024 lite-C Forums