Offscreen patcher capture

Feb 19, 2009 at 3:36pm

Offscreen patcher capture

Hi,

I wonder if there’s a way with the new API to export a patcher or a portion of it as an image file (a la jit.desktop but without having the patcher as the frontmost window).

Not sure it’s very clear, my goal is to export easily & quickly the waveform drawn by the waveform~ object, but more generally, this could be useful (without having to use jit.desktop or render a buffer~ in a matrix).

The best way would be to have an exportimage method in the waveform object.

Has anyone any idea where to start ?
I see jsurface has an “jgraphics_image_surface_writepng” method but how can I obtain the t_jsurface in question ?
Can I for example attach my object to a waveform~ and obtain a t_jsurface ?

Thanks

Leo

#42398
Feb 20, 2009 at 1:46pm

Here is how you can do it. Be warned though that this is undocumented and is not guaranteed to keep working in future versions of max. Below is some loosely sketched code that could work.

The patcherview object has an A_CANT method called “getimage” with the following prototype:

t_jsurface* patcherview_getimage(t_patcherview *pv, t_rect *rect);

So, you could use the jpatcher_get_firstview() function to get a patcherview object from the patcher. Then, do:

t_rect rect;
// fill in the rect with the coords you want
// You could call jbox_get_rect_for_view to
// get the rect for a given box in a given view.
// This would take into account the difference between
// patching and presentation modes.
surface = object_method(patcherview, gensym(“getimage”), &rect);

Then you could use jgraphics_image_surface_writepng() to write the surface to a file. Or you could use the surface to draw to a jgraphics context later in a UI object’s paint method. I’m sure you could have lots of fun with that.

When you are done make sure to call jgraphics_surface_destroy() to free the surface.

Rob

#151768
Feb 22, 2009 at 9:51pm

Thanks a lot,

I’ll give it a try tomorrow and let you know

Thanks again

Leo

#151769
Feb 23, 2009 at 10:09am

Hi Rob,

I’m trying to implement what you said, but there’s a problem with the jgraphics_image_surface_writepng function in the link :

Undefined symbols:
“_jgraphics_image_surface_writepng”, referenced from:
_capture_bang in capture.o
ld: symbol(s) not found
collect2: ld returned 1 exit status

Can you reproduce this ?

See code below :

#include "ext.h"
#include "ext_obex.h"
#include "jpatcher_api.h"
#include "jgraphics.h"
#include "ext_path.h"

////////////////////////// object struct
typedef struct _capture
{
	t_object a_ob;
} t_capture;

///////////////////////// function prototypes
//// standard set
void *capture_new(t_symbol *s, long argc, t_atom *argv);
void capture_free(t_capture *x);
void capture_assist(t_capture *x, void *b, long m, long a, char *s);
void capture_bang(t_capture *x); // incoming bang message
void capture_export(t_capture *x, t_symbol *s, long argc, t_atom *argv);
void capture_version(t_capture *x);

//////////////////////// global class pointer variable
void *capture_class;

int main(void)
{
	t_class *c;

	c = class_new("capture", (method)capture_new, (method)capture_free, (long)sizeof(t_capture), 0L, A_GIMME, 0);

    class_addmethod(c, (method)capture_bang, "bang", 0);
    class_addmethod(c, (method)capture_assist,	"assist", A_CANT, 0);
	class_addmethod(c, (method)capture_export, "export", A_GIMME, 0);
	class_addmethod(c, (method)capture_version, "version",	0);

	class_register(CLASS_BOX, c);
	capture_class = c;

	post("Copyright © 2009 capture Leopold Frey");
	post("tBuild LF - %s %s",__DATE__,__TIME__);

	return 0;
}

void capture_assist(t_capture *x, void *b, long m, long a, char *s)
{
	if (m == ASSIST_INLET) { //inlet
		sprintf(s, "I am inlet %ld", a);
	}
	else {	// outlet
		sprintf(s, "I am outlet %ld", a);
	}
}

void capture_export(t_capture *x, t_symbol *s, long argc, t_atom *argv)
{

}

void capture_bang(t_capture *x)
{
	t_object *jp;
	t_object *pv;
	t_rect rect;
	t_jsurface *surface;
	char *filename;
	short path_id;
	long dpi;

	t_max_err err;

	// get the object's parent patcher
	err = object_obex_lookup(x, gensym("#P"), (t_object **)&jp);
	if (err != MAX_ERR_NONE)
		return;

	pv = jpatcher_get_firstview(gensym("#P")->s_thing);

	filename = "test";
	path_id = path_getdefault(); // todo : use path_frompathname
	dpi = 72;

	if(pv) {
		rect.x = 0;
		rect.y = 0;
		rect.width = 100;
		rect.height = 100;
		surface = object_method(pv, gensym("getimage"), &rect); // =? patcherview_getimage(pv, &rect);
		if(surface)
		{
			err = jgraphics_image_surface_writepng(surface, filename, path_id, dpi);
			if (err != MAX_ERR_NONE)
				object_post((t_object *)x, "Error while writing image");
			jgraphics_surface_destroy(surface);
		}
	}
}

void capture_free(t_capture *x)
{
}

void capture_version(t_capture *x)
{
	object_post((t_object *)x,"Copyright © 2009 capture Leopold Frey");
	object_post((t_object *)x,"tBuild LF - %s %s",__DATE__,__TIME__);
}

void *capture_new(t_symbol *s, long argc, t_atom *argv)
{
	t_capture *x = NULL;

	if (x = (t_capture *)object_alloc(capture_class)) {
	}
	return (x);
}
#151770
Feb 24, 2009 at 3:12pm

Hi there,

I’ve found a workaround to this link problem.
I’m now using jgraphics_write_image_surface_to_filedata instead of jgraphics_image_surface_writepng (still don’t know where this problem comes from).

Now, jgraphics_write_image_surface_to_filedata should, according to the sdk (jgraphics.h line 344), be able to write done JPEG files as well.

But it crashes the app systematically on a mac, and doesn’t do anything on a pc while The PNG export is fine (and fun) on both platforms.

Attached crash report and a simple example.

I’m also working on using the surface in an UI object’s paint method as Rob suggested but I’m also having trouble there (based on the scripto example in the sdk). I’ll provide a simple example ASAP.

Thanks

Leo

#151771
Feb 25, 2009 at 6:25am

Hi,

Just in case anyone is (or will be) interested in this thread, I’ve made a simple example of painting a patcherview inside another ui object.

See example attached.

I’ve also made an external which combines both methods (rendering a patcherview or a portion of it in a file or in an ui object). Rendering a waveform to an image has never been easier ! I’ll release it soon.

Questions : in the attached example, I’m building a patcher window and placing a nobox ui object in it (like the scripto example of the sdk).
Is it possible to prevent this patcher from being edited or switched to presentation mode (like the file browser for example) ? I assume there are just a few attributes to add to the following code, but which of them ? Is this part of a private API ?

sprintf(parsebuf,"@defrect 0 0 100 100 @title capture @enablehscroll 0 @enablevscroll 0 @presentation 0 @toolbarid """);
[...]
patcher = (t_object *)object_new_typed(CLASS_NOBOX,gensym("jpatcher"),1, &a);

What about JPEG rendering (see previous post) ?

By the way, this new API is great, and porting to windows is easy : there’s nothing to do !

Thanks Cycling Folks.

Leo

#151772
Feb 25, 2009 at 7:29pm

Hi Leo,

As you found, jgraphics_image_surface_writepng is not exported. Perhaps we can export that in max/msp 5.0.7.

Meanwhile I’m glad jgraphics_write_image_surface_to_filedata is mostly working for you. We’ll have to look into the JPEG writing crash when we get a chance. It is quite possible that that has never been tested.

Rob

#151773

You must be logged in to reply to this topic.