Legacy GM [Horrible] draw_sprite_ext and surfaces problem in android

W

Wraithious

Guest
Hello, I have a project that creates pages for a book and shows them, it works perfectly fine on windows.

This is the problem:
On android the scale is wayyyy off on the images for the pages in the book, but somehow the size of the page's sprites are correct. Here's pics to illustrate what should happen and what is happening:
this is an original image used for the test:
original2.png
This is the correct result from running the game on windows:
Clipboard03.png
And this is the incorrect result when running the game on android:
Screenshot_20171028-174238.png
I made a small gmz project that demonstrates the issue, actually I'll post the troublesome code in a spoiler too, anyways if anyone could please download it and first try it on windows and see it is working perfect, then try on android and you'll immediately see what is happening, the only 2 places I can see there might be an error is in the script droidSave, or the second action in the controller step event titled MAKE PAGES. (there is only 1 object and 1 script in the example project)
to use the example
press/left click new page and when the button pops back up the page is ready to view, there are 5 backgrounds you can save.
press/left click view page to show or hide the book.
press/left click up or down to turn the pages.
press backspace (android) or escape (windows) to quit

Here is the GMZ.

droidSave script:
Code:
var wth=surface_get_width(application_surface);
var hgt=surface_get_height(application_surface);

surf = application_surface;
surface_set_target(surf);
spr3 = sprite_create_from_surface(surf,0,0,wth,hgt,false,false,0,0);
surface_free(surf);
surface_reset_target();

sprite_assign(spr_temp,spr3);
sprite_assign(spr_temp2,spr3);
sprite_delete(spr3);
e=400/sprite_get_width(spr_temp);
f=225/sprite_get_height(spr_temp);

global.snapshot=1; //this activates the step event MAKE PAGES
MAKE PAGE code, in step event:
Code:
///MAKE PAGES
if(global.snapshot=1)
{global.maxpage+=1;
sur=surface_create(400*(global.maxpage+1),225);surface_set_target(sur);draw_clear_alpha(-1,0);//spr_content
for(var i=0;i<global.maxpage+1;i+=1;){if i<global.maxpage draw_sprite(spr_content,i,400*i,0);
if(i=global.maxpage){draw_sprite_ext(spr_temp,0,400*i,0,e,f,0,-1,1);
draw_set_color(c_blue);draw_text(((200+(400*i))-13),209,"("+string((i+1))+")");}
}
surface_reset_target();
spr=sprite_create_from_surface(sur,0,0,400*(global.maxpage+1),225,0,0,0,0);
sprite_save_strip(spr, "spr_content.png");surface_free(sur);

sur=surface_create(400*(global.maxpage+1),225);surface_set_target(sur);draw_clear_alpha(-1,0);//invertYes
repit=0;repeat(global.maxpage+1)
{if(repit<global.maxpage){draw_sprite(invertYes,repit+repit,(400*repit),0);draw_sprite(invertYes,repit+repit+1,(400*repit)+200,0);}

if(repit=global.maxpage)
{draw_sprite_ext(spr_temp2,0,(400*repit)+200,0,e*-1,f,0,-1,1);
draw_sprite_ext(spr_temp2,0,(400*repit)+600,0,e*-1,f,0,-1,1);
draw_sprite(invertYes,(2*repit)-1,(400*repit)-200,0);}

if repit<global.maxpage repit+=1;}
surface_reset_target();
spr2=sprite_create_from_surface(sur,0,0,400*(global.maxpage+1),225,0,0,0,0);
sprite_save_strip(spr2,"invertYes.png");surface_free(sur);

alarm[2]=70;
global.snapshot=0;
}
alarm2 code:
Code:
///Load saved pages
if(file_exists("spr_content.png"))
{sprite_replace(spr_content,"spr_content.png",global.maxpage+1,0,1,0,0);
sprite_replace(invertNo,"spr_content.png",(global.maxpage+global.maxpage)+2,0,1,0,0);
}
if(file_exists("invertYes.png"))
{sprite_replace(invertYes,"invertYes.png",(global.maxpage+global.maxpage)+2,0,1,0,0);
}
if sprite_exists(spr)sprite_flush(spr);
if sprite_exists(spr2)sprite_flush(spr2);
working=0;
//change to next background
bkg+=1;if bkg>4 bkg=0;
if bkg=0 background_index[0]=background0;
if bkg=1 background_index[0]=background1;
if bkg=2 background_index[0]=background2;
if bkg=3 background_index[0]=background3;
if bkg=4 background_index[0]=background4;
clickedBtn=0;
So when I comment out some stuff in the droidSave script and change it to save the surface and use alarm 1 exactly how the windows version does it still has the same exact error:
changed script from above spoiler to:
Code:
surf = application_surface;
surface_set_target(surf);
surface_reset_target();

surface_save(surf,"savescreen.png");
alarm[1]=70;
surface_free(surf);
which then calls alarm1 just like the windows version:
Code:
///get screenshot, start make pages
if(file_exists("savescreen.png"))
{sprite_replace(spr_temp,"savescreen.png",0,0,0,0,0);
sprite_replace(spr_temp2,"savescreen.png",0,0,0,0,0);
e=400/sprite_get_width(spr_temp);f=225/sprite_get_height(spr_temp);
global.snapshot=1;// activate the MAKE PAGE code in step event
}
[/CODE]
next the step event MAKE PAGE code is called:
Code:
///MAKE PAGES
if(global.snapshot=1)
{global.maxpage+=1;
sur=surface_create(400*(global.maxpage+1),225);surface_set_target(sur);draw_clear_alpha(-1,0);//spr_content
for(var i=0;i<global.maxpage+1;i+=1;){if i<global.maxpage draw_sprite(spr_content,i,400*i,0);
if(i=global.maxpage){draw_sprite_ext(spr_temp,0,400*i,0,e,f,0,-1,1);
draw_set_color(c_blue);draw_text(((200+(400*i))-13),209,"("+string((i+1))+")");}
}
surface_reset_target();
spr=sprite_create_from_surface(sur,0,0,400*(global.maxpage+1),225,0,0,0,0);
sprite_save_strip(spr, "spr_content.png");surface_free(sur);

sur=surface_create(400*(global.maxpage+1),225);surface_set_target(sur);draw_clear_alpha(-1,0);//invertYes
repit=0;repeat(global.maxpage+1)
{if(repit<global.maxpage){draw_sprite(invertYes,repit+repit,(400*repit),0);draw_sprite(invertYes,repit+repit+1,(400*repit)+200,0);}

if(repit=global.maxpage)
{draw_sprite_ext(spr_temp2,0,(400*repit)+200,0,e*-1,f,0,-1,1);
draw_sprite_ext(spr_temp2,0,(400*repit)+600,0,e*-1,f,0,-1,1);
draw_sprite(invertYes,(2*repit)-1,(400*repit)-200,0);}

if repit<global.maxpage repit+=1;}
surface_reset_target();
spr2=sprite_create_from_surface(sur,0,0,400*(global.maxpage+1),225,0,0,0,0);
sprite_save_strip(spr2,"invertYes.png");surface_free(sur);

alarm[2]=70;
global.snapshot=0;
}
which then calls alarm2 code:
Code:
///Load saved pages
if(file_exists("spr_content.png"))
{sprite_replace(spr_content,"spr_content.png",global.maxpage+1,0,1,0,0);
sprite_replace(invertNo,"spr_content.png",(global.maxpage+global.maxpage)+2,0,1,0,0);
}
if(file_exists("invertYes.png"))
{sprite_replace(invertYes,"invertYes.png",(global.maxpage+global.maxpage)+2,0,1,0,0);
}
if sprite_exists(spr)sprite_flush(spr);
if sprite_exists(spr2)sprite_flush(spr2);
working=0;
//change to next background
bkg+=1;if bkg>4 bkg=0;
if bkg=0 background_index[0]=background0;
if bkg=1 background_index[0]=background1;
if bkg=2 background_index[0]=background2;
if bkg=3 background_index[0]=background3;
if bkg=4 background_index[0]=background4;
clickedBtn=0;
And here is the image that is saved, note that it is all there like it should be, but it's not getting scaled down to fit the 400x225 sprite in the MAKE PAGE code action that runs after the surface is saved, even tho it does for windows!
savescreen.png

The only hint I have as to what's happening is that each time you save a page, the resulting image displayed on every page gets more and more magnified every time, what could be causing this??
 
Last edited by a moderator:
W

Wraithious

Guest
I just want to add some notes here that might help someone solve this,
Note 1. spr_temp and spr_temp2 and the draw_sprite_ext function, I believe, are the only things that are involved with the problem, also the only difference between the 2 sprites is one has an x origin of 0 and the other has an x origin of 400 (so the inverted sprite when scaled and flipped will work correctly when scaled down to 400x225)
Note 2. currently when I create the surface, it is targeted to the application surface and it is saved on the device. The application surface is normally 736x414,
in room creation event of 1st room:
Code:
var base_w = 736;//room width OR view size
var base_h = 414;//room height OR view size
var aspect = base_w / base_h ; // get the GAME aspect ratio
if (global.dw < global.dh)
    {
    //portrait
    var ww = min(base_w, global.dw);
    var hh = ww / aspect;
    if(global.dw & 1)
    global.dw++;
    if(global.dh & 1)
    global.dh++;
    global.sx=ww/base_w;//Sets up scale ratio for width
    global.sy=hh/base_h;//Sets up scale ratio for height
    }
else
    {
    //landscape
    var hh = min(base_h, global.dh);
    var ww = hh * aspect;
    if(global.dw & 1)
    global.dw++;
    if(global.dh & 1)
    global.dh++;
    global.sx=ww/base_w;//Sets up scale ratio for width
    global.sy=hh/base_h;//Sets up scale ratio for height
    }
surface_resize(application_surface, ww, hh);//resize the game
display_set_gui_size(ww,hh);//resize the drawing canvas
window_set_size(ww,hh);
global.idlwth=ww;global.idlht=hh;
if global.osys=1 room_speed=60;
which is the size of my room, and would only be different if the room creation code finds that the device's resolution is smaller than 736x414. I have checked the size of the resulting file saved and each time it is 736x414 as expected, well actually no because I figured the saved file would be the size of my devices display which is 2560x1440, BUT IN EITHER CASE that is supposed to be taken care of in the last 2 lines of these 4 lines of code in alarm 1:
Code:
sprite_replace(spr_temp,"savescreen.png",0,0,0,0,0);
sprite_replace(spr_temp2,"savescreen.png",0,0,0,0,0);// I tried setting the x origin here to 400 for the invertYes sprite to use but that makes things worse
e=400/sprite_get_width(spr_temp);
f=225/sprite_get_height(spr_temp);
Note 3. which leads to these all important lines of code which I believe, in android but not windows, is the actual problem, but whyyyy
step event code MAKE PAGE:
Code:
/*line 6*/ draw_sprite_ext(spr_temp,0,400*i,0,e,f,0,-1,1);
/*line 18*/ draw_sprite_ext(spr_temp2,0,(400*repit)+200,0,e*-1,f,0,-1,1);
/*line 19*/ draw_sprite_ext(spr_temp2,0,(400*repit)+600,0,e*-1,f,0,-1,1);
line 6 draws the screenshot/saved application_surface scaled down to 400x225 to add 2 sub images to the invertNo sprite
lines 18 and 19 draws the screenshot/saved application_surface FLIPPED horizontally and scaled down to 400x225, and because the x origin of 400 when flipped allows me to stack the images to add 2 sub images to the invertYes sprite with each sub image in the right order.
But it isn't respecting my scale arguments in the draw_sprite_ext function in android, why?
 
Last edited by a moderator:
W

Wraithious

Guest
Wow so I found out what is happening here and you aren't going to like it, no, not one bit! because I for sure really really hate it, ALOT! any image you make with a surface, and save it to a file, and then change a sprite's image from a file, ... wait for it...

has to be a power of 2!! this is totally rediculous and I had to test it 10 times in different ways to confirm this, but it's true for android.
don't believe me? try it yourself, make a sprite32x32, then save it from a surface like this:
Code:
surf=surface_create(64,64);
surface_set_target(surf);
draw_sprite_ext(sprite0,0,0,0,2,2,0,-1,1);
surface_reset_target();
surface_save(surf,"monster.png");
surface_free(surf);
now in some monster's event change to the sprite:
Code:
spr3=sprite_add("monster.png",0,0,0,0,0);
sprite_assign(sprite0,spr3);
sprite_delete(spr3);
Hey it works great doesn't it? ok now try this, 32x32 sprite, save it from a surface this time like this:
Code:
surf=surface_create(48,48);
surface_set_target(surf);
draw_sprite_ext(sprite0,0,0,0,1.5,1.5,0,-1,1);
surface_reset_target();
surface_save(surf,"monster.png");
surface_free(surf);
and again, in some monster's event change to the sprite:
Code:
spr3=sprite_add("monster.png",0,0,0,0,0);
sprite_assign(sprite0,spr3);
sprite_delete(spr3);
BAM! BROKEN!!!
this is horrible, not only do I have to change all my sprites and code involved with my project but because of the power of 2 clause in android I hve to resize my backgrounds and room, and room creation code, and all my code that has anything to do with relying on the room size, AND guess what that will do on android??? all that hard work I spent on aspect ratios and images to fit the screen for android, BAM!! :glassBreak: right out the window! what am I supposed to do otherwise? resize my application background, make multiple surfaces to resize and save my images then reload them to yet another surface to then process them as a proper power of 2 to it every single time and then set it all back? Horrible. and that's not all, this used to work fine in previous versions of game maker, which means I'm unable to EVER update my Mergery graphics program without re writing the entire thing from scratch I AM NOT HAPPY.
 

RangerX

Member
I know that in the Windows target, when you create a sprite from surface, that sprite is being catch scaled up. This means its therefore drawn scaled up and ending up double size if you don't scale it down at drawing.
So that's probably what's happening here?
 
W

Wraithious

Guest
I know that in the Windows target, when you create a sprite from surface, that sprite is being catch scaled up. This means its therefore drawn scaled up and ending up double size if you don't scale it down at drawing.
So that's probably what's happening here?
the weird thing is it works fine on windows after you scale it, but on mobile if it isn't a power of 2 the bottom and right sides get cut off and what's left gets scaled bigger and bigger each time you perform the surface/draw scaled image/save to file/ load file image into sprite operations.
 
W

Wraithious

Guest
I know that in the Windows target, when you create a sprite from surface, that sprite is being catch scaled up.
Yes, when you screen_save in windows it grabs the image at whatever size it's showing on your monitor, so what I did to get the correct sized image after saving it is just re scale afterwards using this formula and entering e and f as the x and yscale arguments:
Code:
sprite_replace(spr_temp,"savescreen.png",1,0,0,0,0);
e=400/sprite_get_width(spr_temp);f=225/sprite_get_height(spr_temp);
replacing the 400 and 225 with whatever values you want the image to be of course, but none of that works in Android unless the surface the image is drawn on AND the image saved is a power of 2
 
Last edited by a moderator:
M

Molinware

Guest
I'm using the "Pixel Perfect" example, it works fine, except for collision events like "mouse_left_pressed". I think it's because the surface is not being drawn where it really is. Any work arounds?
 
Top