SRL Archive - OSR Help

Combining 2 Bitmaps


Coh3n

2011-11-01 00:13:46

I've been working on a script that will automatically create an SPS world map, and I haven't been able to figure out how to add on to an existing bitmap, or crop two bitmaps together.

I thought about drawing them all to a canvas, but I'm not sure how to export the final canvas to a bitmap or another image format.

Anyone have any ideas?

Flight

2011-11-01 00:19:01

I've looked into this in the past, I forget why I wanted to do it, but this is what I found:
http://www.psclbrlnd.tk/merging-bmp/comment-page-1#comment-3892

I hope you find it useful.

Nava2

2011-11-01 00:36:23

Firstly, this is very tedious computing wise, so I would suggest using a plugin for this to speed it up..
Pseudo-code, I don't have time to write atm:

var totalbmp: TMufasaBitmap;

var bmpgrid: array [0..X] of array [0..Y] of TMufasaBitmap;

totalbmp.create();

for 0 to X do
for 0 to Y do
W := W + bmpgrid[X][Y].Width;
H := H + bmpgrid[X][Y].height;

totalbmp.setSize(W,H);

totalbmp.fastDrawClear(clBlack); // transparent colour

W := H := 0;

for x := 0 to X do
for y := 0 to Y do
begin
bmpgrid[x][y].drawTransparent(W, H, totalbmp);
w := W + bmpgrid.width; H := H + bmpgrid.height;
end;

done.

Coh3n

2011-11-01 01:36:01

Thanks Nava. All I was really looking for was FastDrawTransparant. I don't think the way I'm doing it will be tedious computing-wise, but we'll see. :p

Coh3n

2011-11-01 03:56:46

Okay so I'd love if someone could help here. I've been looking at this for way too long and I need some sleep. Here is what the script outputs:


Which clearly isn't what I want. It should be two different images and they should be side by side. I know somethings wrong with my looping I'm just either too stupid or too tired to figure it out. Any help is appreciated. Oh, the issue will be in the setPieces and createMap procedures.

program new;
{$i srl/srl.scar}

const
PATH_MAP = scriptPath;

NEXT_COLUMN = 8;
NEXT_ROW = 4;

OVERLAP_X = 15;
OVERLAP_Y = 81;

var
CurrentRow, CurrentColumn: integer;

bmpGrid: array[0..1] of array[0..0] of TMufasaBitmap;
bmpWorld: TMufasaBitmap;

procedure nextColumn();
var
i: integer;
begin
for i := 1 to NEXT_COLUMN do
begin
pressKey(VK_RIGHT);
wait(1000+random(500));
end;
end;

procedure nextRow();
var
i: integer;
begin
for i := 1 to NEXT_ROW do
begin
pressKey(VK_DOWN);
wait(1000+random(500));
end;
end;

procedure reset();
var
i: integer;
begin
i := 0;

mouse(680, 480, 5, 5, true);
wait(200 + random(300));
mouse(612, 312, 1, 1, true);

for i := 1 to 5 do
begin
pressKey(VK_LEFT);
wait(200 + random(300));
pressKey(VK_UP);
wait(200 + random(300));
end;

mouse(680, 480, 5, 5, true);
end;

procedure goPosition(row, column: integer);
var
i: integer;
begin
for i := 1 to column do
begin
nextColumn();
writeln('At column '+toStr(i));
end;

for i := 1 to row do
begin
nextRow();
writeln('At row '+toStr(i));
end;
end;

// returns the cropped screenshot of the map
function getPiece(): integer;
var
w, h: integer;
begin
getClientDimensions(w, h);
copyClientToBitmap(result, 7, 7, w - 26, h - 43);
end;

function getMufaPiece(): TMufasaBitmap;
var
bmp: integer;
begin
bmp := getPiece();
result := getMufasaBitmap(bmp);
end;

procedure setPieces();
var
x, y: integer;
begin
for x := 0 to high(bmpGrid) do
begin
for y := 0 to high(bmpGrid[x]) do
begin
bmpGrid[x][y] := getMufaPiece();
//bmpGrid[x][y].SaveToFile(appPath + 'sps'+toStr(x)+toStr(y)+'.bmp');
end;

if (x <> high(bmpGrid)) then
nextColumn();
end;

for x := 0 to high(bmpGrid) do
begin
for y := 0 to high(bmpGrid[x]) do
begin
bmpGrid[x][y].SaveToFile(appPath + 'sps'+toStr(x)+toStr(y)+'.bmp');
end;
end;
end;

procedure createMap();
var
x, y, w, h: integer;
begin
setPieces();

bmpWorld := TMufasaBitmap.create();

for x := 0 to high(bmpGrid) do
for y := 0 to high(bmpGrid[x]) do
begin
w := (w + bmpGrid[x][y].width);
h := (h + bmpGrid[x][y].height);
end;

bmpWorld.setSize(w, h);
bmpWorld.fastDrawClear(clBlack); // transparent colour

w := 0;
h := 0;

for x := 0 to high(bmpGrid) do
for y := 0 to high(bmpGrid[x]) do
begin
bmpGrid[x][y].fastDrawTransparent(w, h, bmpWorld);
w := (w + bmpGrid[x][y].width);
h := (h + bmpGrid[x][y].height);
end;
end;

begin
clearDebug();
activateClient();
setupSRL();

mouse(1, 1, 0, 0, true); // so keyboard arrows work
//goPosition(3, 5);

createMap();
bmpWorld.saveToFile(appPath + 'sps_temp.bmp');
end.
If you want to test, login to RS, open the world map, drag the crosshairs and hit run.

Any help is appreciated.

masterBB

2011-11-01 07:46:12

fixed:

function getPiece(): integer;
var
w, h: integer;
begin
getClientDimensions(w, h);
result := CreateBitmap(w,h);
copyClientToBitmap(result, 7, 7, w - 26, h - 43);
end;

The size of the bitmap has to be calculated like this:
for x := 0 to high(bmpGrid) do
width := (w + bmpGrid[x][0].width);

for y := 0 to high(bmpGrid[x]) do
height := (h + bmpGrid[0][y].height);

The positioning problem is obvious if you look at you code once more.

Coh3n

2011-11-01 10:27:22

From: masterBB
fixed:

function getPiece(): integer;
var
w, h: integer;
begin
getClientDimensions(w, h);
result := CreateBitmap(w,h);
copyClientToBitmap(result, 7, 7, w - 26, h - 43);
end;
Can you explain why that fixed it? I assumed copyClientToBitmap created the bitmap. I'm actually really confused here. >.< Because the bitmaps were still being created, they were just the same image. :unsure:

From: masterBB

The size of the bitmap has to be calculated like this:
for x := 0 to high(bmpGrid) do
width := (w + bmpGrid[x][0].width);

for y := 0 to high(bmpGrid[x]) do
height := (h + bmpGrid[0][y].height);

The positioning problem is obvious if you look at you code once more.
Thank you! I thought I had tried that, but it must have been a little different.

I'll have to take a look at the position when I get home from practice, but it didn't jump out at me right away.

masterBB

2011-11-01 10:57:11

No, copyClientToBitmap doesn't create a bitmap.

Maybe you already understand it, but I will explain this anyway.

Bitmaps are stored in a list by simba. The number you get back is just what the location is in this list. The first bitmap is 0, etc. You already set up srl, which includes loading a few bitmaps in to the memory, lamp, mod etc. In your function Result was 0, so when you called CopyClientToBitmap, it put it your image over the lamp bitmap. That's why you were have twice the same image. Both were pointing to bitmap 0 which was the last bitmap you've copied there. Without that srl bitmap you would receive an error. With createBitmap I created a new bitmap, I believe it's 2 and 3 in this case. So it will be stored in there.

Nava2

2011-11-01 11:49:18

Coh3n, you need to create the bitmap first.



function getPiece(): integer;
var
w, h: integer;
begin
getClientDimensions(w, h);
Result := CreateBitmap(w,h,''); // creates a black bitmap on reference
copyClientToBitmap(result, 7, 7, w - 26, h - 43);
end;

//in your drawing its because you never reset your w..

curw := 0; refwidth = CONST; // should be constant between all parts of teh grid.
curh := 0; refheight = CONST; // should be const as well

// draw column by column...
for x := 0 to high(bmpGrid) do
begin
curh := 0; curw += refwidth;
for y := 0 to high(bmpGrid[x]) do
begin
bmpGrid[x][y].fastDrawTransparent(curw, curh, bmpWorld);
curh += refheight;
end;
end;

that shoudl fix it.

Coh3n

2011-11-01 13:48:01

From: masterBB
No, copyClientToBitmap doesn't create a bitmap.

Maybe you already understand it, but I will explain this anyway.

Bitmaps are stored in a list by simba. The number you get back is just what the location is in this list. The first bitmap is 0, etc. You already set up srl, which includes loading a few bitmaps in to the memory, lamp, mod etc. In your function Result was 0, so when you called CopyClientToBitmap, it put it your image over the lamp bitmap. That's why you were have twice the same image. Both were pointing to bitmap 0 which was the last bitmap you've copied there. Without that srl bitmap you would receive an error. With createBitmap I created a new bitmap, I believe it's 2 and 3 in this case. So it will be stored in there.
Yeah that's great, thanks a lot.

And Nava, yeah I thought copyClientToBitmap created it. My mistake. Also, you know you're using wrong syntax in your examples, right?

Nava2

2011-11-01 17:38:46

From: Coh3n
Yeah that's great, thanks a lot.

And Nava, yeah I thought copyClientToBitmap created it. My mistake. Also, you know you're using wrong syntax in your examples, right?


you know I'm just writing psuedocode, right? :-D

I haven't compiled PS code in forever lol.

Coh3n

2011-11-01 18:27:46

Yes. :p

I got it all figured out, thanks. Here's an example output of the script. Also, here's the script in case I may have missed something.

(**

This is a program that will automatically create the entire Runescape from
in game screenshots. The final image will be saved as a .bmp and saved to
the PATH_MAP constant file path.

NOTES:
~~~~~~

* I suggest using SMART or running it overnight as it takes a VERY long time
* If you're using SMART, start with the map CLOSED
* If you're not using SMART, you can start with it open OR closed
* ALWAYS start logged in; it won't login for you
* Reccommend running on a bad account as it's quite tedious

*)

program new;
//{$DEFINE SMART}
{$i srl/srl.scar}

const
PATH_MAP = appPath + 'sps_temp.bmp';

NEXT_COLUMN = 8;
NEXT_ROW = 4;

OVERLAP_X = 15;
OVERLAP_Y = 80;

COLUMNS = 2;
ROWS = 2;

var
bmpGrid: array[0..(COLUMNS - 1)] of array[0..(ROWS - 1)] of TMufasaBitmap;
bmpWorld: TMufasaBitmap;


function mapOpen(): boolean;
begin
result := (getColor(749, 467) = 657930);
end;

function openMap(): boolean;
var
x, y: integer;
begin
if (findColorTolerance(x, y, 14802090, 510, 120, 565, 165, 5)) then
mouse(x, y, 5, 5, true);

result := waitFunc(@mapOpen, 500, 15000);

if (result) then
while (countColorTolerance(0, 10, 10, 100, 100, 10) > 500) do
wait(100);
end;

procedure set100Percent();
var
x, y: integer;
begin
if (findText(x, y, '100', statChars, 700, 460, 760, 495)) then
exit;

mouse(750, 475, 2, 2, true);
end;

procedure nextColumn();
var
i: integer;
begin
for i := 1 to NEXT_COLUMN do
begin
typeByte(VK_RIGHT);
wait(1000+random(500));
end;
end;

procedure nextRow();
var
i: integer;
begin
for i := 1 to NEXT_ROW do
begin
typeByte(VK_DOWN);
wait(1000+random(500));
end;
end;

procedure reset();
var
i: integer;
begin
i := 0;

mouse(680, 480, 5, 5, true); // opens the overview map
wait(200 + random(300));

mouse(660, 380, 0, 0, true); // random spot on the map (used for testing)

{ commented out for testing

mouse(612, 312, 1, 1, true);

for i := 1 to 5 do
begin
typeByte(VK_LEFT);
wait(200 + random(300));
typeByte(VK_UP);
wait(200 + random(300));
end;
}
mouse(680, 480, 5, 5, true); // removes the overview map
mmouse(600, 400, 30, 30); // to get rid of the "overview" uptext
end;

procedure goPosition(row, column: integer);
var
i: integer;
begin
reset();

for i := 1 to column do
begin
nextColumn();
writeln('At column '+toStr(i));
end;

for i := 1 to row do
begin
nextRow();
writeln('At row '+toStr(i));
end;
end;

// returns the cropped screenshot of the map
function getPiece(): integer;
var
w, h: integer;
begin
getClientDimensions(w, h);
result := createBitmap(w, h);
copyClientToBitmap(result, 7, 7, w - 26, h - 43);
end;

function getMufaPiece(): TMufasaBitmap;
var
bmp: integer;
begin
bmp := getPiece();
result := getMufasaBitmap(bmp);
end;

procedure setPieces();
var
x, y, h, hh: integer;
begin
h := high(bmpGrid);

for x := 0 to h do
begin
goPosition(0, x);

hh := high(bmpGrid[x]);

for y := 0 to hh do
begin
bmpGrid[x][y] := getMufaPiece();

if (y <> hh) then
nextRow();
end;
end;
end;

procedure createMap();
var
x, y, w, h: integer;
begin
setPieces();

bmpWorld := TMufasaBitmap.create();

for x := 0 to high(bmpGrid) do
w := (w + bmpGrid[x][0].width);

for y := 0 to high(bmpGrid[0]) do
h := (h + bmpGrid[0][y].height - OVERLAP_Y);

bmpWorld.setSize(w, h);
bmpWorld.fastDrawClear(clBlack); // transparent colour

w := 0;
h := 0;

for x := 0 to high(bmpGrid) do
begin
h := 0;
w := bmpGrid[x][0].width * x;

for y := 0 to high(bmpGrid[x]) do
begin
bmpGrid[x][y].fastDrawTransparent(w, h, bmpWorld);
h := (h + bmpGrid[x][y].height - OVERLAP_Y);
end;
end;
end;

procedure free();
var
i, j: integer;
begin
for i := 0 to high(bmpGrid) do
for j := 0 to high(bmpGrid[i]) do
bmpGrid[i][j].free();
end;

var
t: integer;
begin
{$IFDEF SMART}
SMART_Server := 152;
SMART_Members := False;
SMART_Signed := True;
SMART_SuperDetail := False;
{$ENDIF}

clearDebug();
activateClient();
setupSRL();

t := getSystemTime();

if (not mapOpen()) then
openMap();

set100Percent();

createMap();
bmpWorld.saveToFile(PATH_MAP);
free();

writeln('Took '+msToTime(getSystemTime - t, TIME_SHORT)+' to complete.');
end.

Gu3st...

2011-11-01 19:05:22

Coh3n, that looks freakin' fantastic! How is this image compared to SPS's image?

Coh3n

2011-11-01 19:15:38

From: Zyt3x
Coh3n, that looks freakin' fantastic! How is this image compared to SPS's image?
The one I posted is quite different. I did that one in SMART and for whatever reason that map is much much darker than the minimap. I loaded RS up in my browser and the map + minimap are almost identical so it's creating the entire map now. We'll see how it goes.

masterBB

2011-11-01 19:18:12

Do you position yourself in a dungeon while you do this? Or how else do you get the you are here out of the image?

Coh3n

2011-11-01 19:21:54

From: masterBB
Do you position yourself in a dungeon while you do this? Or how else do you get the you are here out of the image?
When you click the question mark on the map there's options to disable it, along with a few other options.

E: Well that's rattling. It got all the map pieces and went to create the entire map and BAM, out of memory. :(

masterBB

2011-11-01 20:00:42

Uses a lot of memory less:

program new;
//{$DEFINE SMART}
{$i srl/srl.scar}

const
PATH_MAP = appPath + 'sps_temp.bmp';

NEXT_COLUMN = 8;
NEXT_ROW = 4;

OVERLAP_X = 15;
OVERLAP_Y = 80;

COLUMNS = 2;
ROWS = 2;

var
bmpGrid: array[0..(COLUMNS - 1)] of array[0..(ROWS - 1)] of TMufasaBitmap;
bmpWorld: TMufasaBitmap;


function mapOpen(): boolean;
begin
result := (getColor(749, 467) = 657930);
end;

function openMap(): boolean;
var
x, y: integer;
begin
if (findColorTolerance(x, y, 14802090, 510, 120, 565, 165, 5)) then
mouse(x, y, 5, 5, true);

result := waitFunc(@mapOpen, 500, 15000);

if (result) then
while (countColorTolerance(0, 10, 10, 100, 100, 10) > 500) do
wait(100);
end;

procedure set100Percent();
var
x, y: integer;
begin
if (findText(x, y, '100', statChars, 700, 460, 760, 495)) then
exit;

mouse(750, 475, 2, 2, true);
end;

procedure nextColumn();
var
i: integer;
begin
for i := 1 to NEXT_COLUMN do
begin
typeByte(VK_RIGHT);
wait(800+random(400));
end;
end;

procedure nextRow();
var
i: integer;
begin
for i := 1 to NEXT_ROW do
begin
typeByte(VK_DOWN);
wait(800+random(400));
end;
end;

procedure reset();
var
i: integer;
begin
i := 0;

mouse(680, 480, 5, 5, true); // opens the overview map
wait(200 + random(300));

mouse(660, 380, 0, 0, true); // random spot on the map (used for testing)

{ commented out for testing

mouse(612, 312, 1, 1, true);

for i := 1 to 5 do
begin
typeByte(VK_LEFT);
wait(200 + random(300));
typeByte(VK_UP);
wait(200 + random(300));
end;
}
mouse(680, 480, 5, 5, true); // removes the overview map
mmouse(600, 400, 30, 30); // to get rid of the "overview" uptext
end;

procedure goPosition(row, column: integer);
var
i: integer;
begin
reset();

for i := 1 to column do
begin
nextColumn();
writeln('At column '+toStr(i));
end;

for i := 1 to row do
begin
nextRow();
writeln('At row '+toStr(i));
end;
end;

// returns the cropped screenshot of the map
function getPiece(): integer;
var
w, h: integer;
begin
getClientDimensions(w, h);
result := createBitmap(w, h);
copyClientToBitmap(result, 7, 7, w - 26, h - 43);
end;

function getMufaPiece(): TMufasaBitmap;
var
bmp: integer;
begin
bmp := getPiece();
result := getMufasaBitmap(bmp);
end;

procedure setPieces();
var
bmp:TMufasaBitmap;
x, y, w, h: integer;
begin
getClientDimensions(w, h);

w := w - 33;
h := h - 50;

for x := 0 to COLUMNS-1 do
begin
goPosition(0, x);

for y := 0 to ROWS-1 do
begin
bmp := getMufaPiece();
bmp.fastDrawTransparent(x*w, y*(h-OVERLAP_Y), bmpWorld);
bmp.Free;
if (y < ROWS-1) then
nextRow();
end;
end;
end;

procedure createMap();
var
x, y, w, h: integer;
begin

bmpWorld := TMufasaBitmap.create();
getClientDimensions(w, h);

w := w - 33;
h := h - 50;

bmpWorld.setSize(w*COLUMNS, h*ROWS);
bmpWorld.fastDrawClear(clBlack); // transparent colour

setPieces();
end;

procedure free();
var
i, j: integer;
begin
bmpWorld.Free;
end;

var
t: integer;
begin
{$IFDEF SMART}
SMART_Server := 152;
SMART_Members := False;
SMART_Signed := True;
SMART_SuperDetail := False;
{$ENDIF}

clearDebug();
activateClient();
setupSRL();

t := getSystemTime();

if (not mapOpen()) then
openMap();

set100Percent();

createMap();
bmpWorld.saveToFile(PATH_MAP);
free();

writeln('Took '+msToTime(getSystemTime - t, TIME_SHORT)+' to complete.');
end.

Can be improved by removing the repeated code where the size of the pieces are calculated.

Coh3n

2011-11-02 02:24:14

Thanks you are a genius! :D

Here's an example output if you're interested, I'll run the full map soon and see how it goes. :)

E: Actually got the same error (Error: Exception: Out of memory at line 174):

bmpWorld.setSize(w * COLUMNS, h * ROWS);
Think the bitmap is actually too large?

E2: Rewrote how it's all done, and it finishes about 100 times faster than the previous version.

Took 01m 52s to complete.
Here's the map it generated.

Thanks for all your help guys. :)

grats

2011-11-02 02:33:35

amazing work.. that will sure save lots of time in the future.

Coh3n

2011-11-02 02:34:35

That's the idea. I just need to reload the client until I can find a map that matches the minimap. >.<

Nava2

2011-11-02 11:20:53

is the area splitter still separate? why not make these includes (using defines maybe) and have one catch all for doing areas and the whole map. also, i said before a plugin would make this faster.. it wont anymore. :)

masterBB

2011-11-02 12:23:58

From: Coh3n
Thanks you are a genius! :D

Here's an example output if you're interested, I'll run the full map soon and see how it goes. :)

E: Actually got the same error (Error: Exception: Out of memory at line 174):

bmpWorld.setSize(w * COLUMNS, h * ROWS);
Think the bitmap is actually too large?

E2: Rewrote how it's all done, and it finishes about 100 times faster than the previous version.

Took 01m 52s to complete.
Here's the map it generated.

Thanks for all your help guys. :)


Thanks, can I see your new code? I'm interested in the changes.

Nava2

2011-11-02 13:37:45

From: masterBB
Thanks, can I see your new code? I'm interested in the changes.


Check his SPS repo on github, sorry I don't have the link atm.

Coh3n

2011-11-02 20:42:56

From: Nava2
is the area splitter still separate? why not make these includes (using defines maybe) and have one catch all for doing areas and the whole map. also, i said before a plugin would make this faster.. it wont anymore. :)
Yeah I plan on doing that actually. Also, SPS doesn't actually use the world map for anything so I may remove it from the repository. It's an extra 6MB that's not really needed. Will be nice for when I get around to making an updater.

From: masterBB
Thanks, can I see your new code? I'm interested in the changes.
If you haven't found it, it's here.