Sneaky bug in jgraphics_pattern_create_radial() ???


    Nov 24 2012 | 9:57 pm
    Hi folks,
    when coding UI objects I have noticed that certain types of radial gradients show significant Moire' patterns and/or aliasing. The following code will draw a pretty generic radial gradient that exhibits the above mentioned behavior. Notice that no affine transformations are performed on the gradient. I am on MacOS 10.7 (Lion) running Max 6.0.8 and using MaxSDK 6.0.4.
    Can someone confirm this ?
    #include "ext.h"
    #include "ext_common.h"
    #include "ext_obex.h"
    #include "ext_strings.h"
    #include "jpatcher_api.h"
    #include "jgraphics.h"
    
    typedef struct _mygradientbug
    {
    	t_jbox		box;
    	t_jrgba		bgcolor;
    	t_jrgba		gradientcolor;
    } t_mygradientbug;
    
    t_class *mygradientbug_class;
    
    // Class Handlers
    void *mygradientbug_new(t_symbol *sym, long argc, t_atom *argv);
    void mygradientbug_free(t_mygradientbug *x);
    
    // Typed Messages
    void mygradientbug_bang(t_mygradientbug *x);
    
    // Untyped Messages
    void mygradientbug_paint(t_mygradientbug *x, t_object *patcherview);
    void mygradientbug_assist(t_mygradientbug *x, void *box, long msg, long idx, char *str);
    
    int main(void)
    {
    	t_class *c;
    
    	common_symbols_init();
    
    	c = class_new("mygradientbug",
                      (method)mygradientbug_new, (method)mygradientbug_free,
    				  sizeof(t_mygradientbug), (method)NULL, A_GIMME, 0);
    
    	c->c_flags |= CLASS_FLAG_NEWDICTIONARY;
    	jbox_initclass(c, 0);
    
    	// Typed Messages
    	class_addmethod(c, (method)mygradientbug_bang,      "bang",     0);
    
        // Untyped Messages
        class_addmethod(c, (method)mygradientbug_paint,     "paint",    A_CANT, 0);
    	class_addmethod(c, (method)mygradientbug_assist,    "assist",   A_CANT, 0);
    
    	// Attributes
    	CLASS_ATTR_DEFAULT(c,                   "patching_rect",    ATTR_FLAGS_NONE, "0.0 0.0 300.0 300.0");
    
        CLASS_STICKY_ATTR(c,					"category",			ATTR_FLAGS_NONE, "Color");
    
    	CLASS_ATTR_RGBA(c,						"bgcolor",			ATTR_FLAGS_NONE, t_mygradientbug, bgcolor);
    	CLASS_ATTR_STYLE_LABEL(c,				"bgcolor",			ATTR_FLAGS_NONE, "rgba", "Background Color");
    	CLASS_ATTR_DEFAULTNAME_SAVE(c,          "bgcolor",			ATTR_FLAGS_NONE, "0.0 0.0 0.0 1.0");
        CLASS_ATTR_FILTER_CLIP(c,				"bgcolor",          0.0, 1.0);
    
    	CLASS_ATTR_RGBA(c,						"gradientcolor",	ATTR_FLAGS_NONE, t_mygradientbug, gradientcolor);
    	CLASS_ATTR_STYLE_LABEL(c,				"gradientcolor",	ATTR_FLAGS_NONE, "rgba", "Gradient Color");
    	CLASS_ATTR_DEFAULTNAME_SAVE(c,          "gradientcolor",	ATTR_FLAGS_NONE, "1.0 1.0 1.0 1.0");
        CLASS_ATTR_FILTER_CLIP(c,				"gradientcolor",    0.0, 1.0);
    
    	CLASS_STICKY_ATTR_CLEAR(c,				"category");
    
    	class_register(_sym_box, c);
    	mygradientbug_class = c;
    
    	return 0;
    }
    
    void *mygradientbug_new(t_symbol *sym, long argc, t_atom *argv)
    {
    	t_mygradientbug *x;
    	t_dictionary *d;
        long flags;
    
        if (!(d = object_dictionaryarg(argc, argv))) {
    		return NULL;
    	}
    	if (!(x = object_alloc(mygradientbug_class))) {
            return NULL;
        }
        flags = 0
                | JBOX_DRAWFIRSTIN
                | JBOX_NODRAWBOX
                | JBOX_DRAWINLAST
                | JBOX_GROWBOTH
                ;
    
        jbox_new(&x->box, flags, argc, argv);
        x->box.b_firstin = (t_object *)x;
        attr_dictionary_process(x, d);
        jbox_ready(&x->box);
    
    	return x;
    }
    
    void mygradientbug_free(t_mygradientbug *x)
    {
    	jbox_free(&x->box);
    }
    
    void mygradientbug_bang(t_mygradientbug *x)
    {
        jbox_redraw(&x->box);
    }
    
    void mygradientbug_paint(t_mygradientbug *x, t_object *patcherview)
    {
    	t_jgraphics *context;
        t_jpattern *pattern;
    	t_rect rect;
    
        context = (t_jgraphics *)patcherview_get_jgraphics(patcherview);
    	jbox_get_rect_for_view((t_object *)x, patcherview, ▭);
    
        // draw gradient
        pattern = jgraphics_pattern_create_radial(rect.width/2, rect.height/2, 0.0,
                                                  rect.width,   rect.height/2, 0.0);
        jgraphics_pattern_add_color_stop_rgba(pattern, 0.0,
                                              x->bgcolor.red,
                                              x->bgcolor.green,
                                              x->bgcolor.blue,
                                              x->bgcolor.alpha);
        jgraphics_pattern_add_color_stop_rgba(pattern, 0.7,
                                              x->bgcolor.red,
                                              x->bgcolor.green,
                                              x->bgcolor.blue,
                                              x->bgcolor.alpha);
        jgraphics_pattern_add_color_stop_rgba(pattern, 0.8,
                                              x->gradientcolor.red,
                                              x->gradientcolor.green,
                                              x->gradientcolor.blue,
                                              x->gradientcolor.alpha);
        jgraphics_pattern_add_color_stop_rgba(pattern, 0.9,
                                              x->bgcolor.red,
                                              x->bgcolor.green,
                                              x->bgcolor.blue,
                                              x->bgcolor.alpha);
        jgraphics_pattern_add_color_stop_rgba(pattern, 1.0,
                                              x->bgcolor.red,
                                              x->bgcolor.green,
                                              x->bgcolor.blue,
                                              x->bgcolor.alpha);
        jgraphics_set_source(context, pattern);
        jgraphics_pattern_destroy(pattern);
        jgraphics_paint(context);
    }
    
    void mygradientbug_assist(t_mygradientbug *x, void *box, long msg, long idx, char *str)
    {
    	if (msg == ASSIST_INLET) {
    		strncpy_zero(str, "Input", 256);
    	} else if (msg == ASSIST_OUTLET) {
    		strncpy_zero( str, "Output", 256);
    	}
    }

    • Nov 25 2012 | 8:07 am
      Aha! Thanks Nicolas.... that's interesting...
      You are using Max 5 and you are getting a perfect gradient with no artifacts whatsoever.
      Could it be that in Max 6 something about the way gradients are generated has changed?
      MacOS 10.7 (Lion) running Max 6.0.8 and using MaxSDK 6.0.4:
      Here's what I am getting:
      [attachment=209028,4686]
    • Nov 25 2012 | 1:16 pm
      Here is another example with a scaled gradient:
      void mygradientbug_paint(t_mygradientbug *x, t_object *patcherview)
      {
      	t_jgraphics *context;
          t_jpattern *pattern;
      	t_rect rect;
      
          context = (t_jgraphics *)patcherview_get_jgraphics(patcherview);
      	jbox_get_rect_for_view((t_object *)x, patcherview, ▭);
      
          // transform context so that center is at [0,0] and radius is 1
          jgraphics_scale(context, rect.width/2, rect.height/2);
          jgraphics_translate(context, 1, 1);
      
          // draw gradient
          jgraphics_scale(context, 0.1, 1.0);
      
          pattern = jgraphics_pattern_create_radial(0.0, 0.0, 0.0, 1.0, 0.0, 0.0);
          jgraphics_pattern_add_color_stop_rgba(pattern, 0.0,
                                                x->gradientcolor.red,
                                                x->gradientcolor.green,
                                                x->gradientcolor.blue,
                                                x->gradientcolor.alpha);
          jgraphics_pattern_add_color_stop_rgba(pattern, 1.0,
                                                x->bgcolor.red,
                                                x->bgcolor.green,
                                                x->bgcolor.blue,
                                                x->bgcolor.alpha);
          jgraphics_set_source(context, pattern);
          jgraphics_pattern_destroy(pattern);
          jgraphics_paint(context);
      }
      [attachment=209039,4687]
    • Nov 25 2012 | 2:36 pm
      Thanks for the example. I can confirm the difference.