Filling a custom-shaped Clutter Actor with a Cairo-drawn canvas

扶醉桌前 提交于 2019-12-08 02:15:39

问题


Clutter 1.12
Cogl 1.10
Vala or C or Python.

I might have a fundamental misunderstanding here —

I think of "Actors" as 3D polygon things. I think of their colours as either vertex colors or as texture-mapping. In this light, I have been trying to draw a custom Actor and fill it with stuff drawn via Cairo. I'm not getting anywhere.

Code is included below (in Vala). Can anyone set me right about Clutter's basics (the docs just don't cut it) or, if I'm close, help me get that code working.

I expect to see a rounded rectangle with a smiley face within. What I'm seeing instead is the Cogl path fill covering* the face. I think the paint() is being done after the drawme()

*If you set the Clutter.Color to "#0001" in method paint(), you'll see this.

/*

Clutter Actor with custom paint() and filled with a Cairo-drawn texture.
(NOT yet working.)

Compile with:

    valac \
  --pkg clutter-1.0 \
  --pkg cogl-1.0 \
    somename.vala

*/


public class SmileyRect : Clutter.Actor {

  //private vars
  private Clutter.Canvas _canvas;
  private bool _flip = false;
  private int _w = 300;
  private int _h = 300;

  //Constructor
  construct {
    _canvas = new Clutter.Canvas();
    _canvas.set_size( this._w, this._h );

    this.set_size( this._w, this._h );
    this.set_content( _canvas );

    //Connect to the draw signal - this is as-per Clutter docs.
    _canvas.draw.connect( drawme );

    //Make it reactive and connect to the button-press-event
    this.set_reactive( true );
    this.button_press_event.connect( button_press );
  }

  /*
    Button press signal handler.
    Changes the colour of what will be painted on the canvas.
  */
  private bool button_press ( Clutter.ButtonEvent evt ) {
    this._flip = !this._flip; //Jiggle a value.
    this.redraw(); // Forces re-run of the drawme() method.
    return true; //all done with this signal.
  }

  //Common function to draw Cogl stuff - used in paint and pick.
  private void draw_rr( Clutter.Color? color ) {
    if (color != null ) { Cogl.set_source_color4ub(color.red,color.green,color.blue,color.alpha); }
    Cogl.Path.round_rectangle( 0, 0, this._w, this._h, 15, 0.3f );
    Cogl.Path.close();

    // When from paint(): 
    // Is there some way to fill this Cogl path with the contents
    // of this._canvas? Or some other paradigm?
    if (color != null ) { Cogl.Path.fill(); }
  }

  /* Some kind of freaky, this magic paint() thing.
     Took me ages to get it running.
     I want to draw a rounded rectangle as the basic shape
     of this actor. 
  */
  public override void paint() {
    stdout.printf("paint runs.\n");
    // I did try a transparent color #0000 - just to see. No go.
    // #000f - draws a black rounded rect *OVER* the Cairo canvas. It covers
    // the smiley face.
    this.draw_rr( Clutter.Color.from_string("#0000") );
  }


  /* I followed paint() example, but the argument was tricky.
     I eventually found it from some PyClutter source code. 
  */
  public override void pick(Clutter.Color color) {
    stdout.printf("pick runs.\n");
    this.draw_rr( color );
  }

  /*
    Draws the Cairo art to the canvas.
    I want this art to be the bitmap/pixmap that *fills* the
    basic rounded rectangle shape of this actor.
    i.e I want the smile face cairo rectangle to be *within* the
    polygon that is draw via Cogl in paint() method.

    Does this even make sense?
  */
  private bool drawme( Cairo.Context ctx, int width, int height) {
    //Draw a rectangle
    double[] col;
    if (this._flip) {
      col = {1f,0f,0f};
      } 
    else {
      col = {0f,1f,0f};
    }

    ctx.set_source_rgb( col[0], col[1], col[2] );
    ctx.rectangle( 0, 0, width, height );
    ctx.fill();

    //Draw a smiley face.

    // Aside: Been trying to avoid all the weird casting of the floats/doubles used below
    // (see the generated c source.) Not at all sure what's going on there.
    // Also not sure about that 'f' on the numbers. Where/when do I have to use it?

    double pi = 3.14f;
    double w = (double) width;
    double h = (double) height;

    ctx.set_line_width( 6f );

    ctx.set_source_rgb( 0f, 0f, 0.8f );

    //eyes
    ctx.move_to( w/3f, h/3f );
    ctx.rel_line_to( 0f, h/6f );
    ctx.move_to(  2*(w/3f), h/3f );
    ctx.rel_line_to( 0f, h/6f );
    ctx.stroke();

    ctx.set_source_rgb( 1f, 1f, 0f );

    double rad = (w > h) ? h : w;
    //face
    ctx.arc( w/2f, h/2f, (rad/2f) - 20f,0f,2f * pi );
    ctx.stroke();
    //smile
    ctx.arc( w/2f, h/2f, (rad/3f) -10f, pi/3f, 2f * (pi/3f) );
    ctx.stroke();

    return true;
  }

  //Redraw - forces invalidate which trips the draw event
  public void redraw() {
    this._canvas.invalidate();
  }
} //end SmileyRect class



void main( string[] args ) {
  Clutter.init(ref args);

  var stage = new Clutter.Stage();
  stage.set_size(400,400);

  var rs = new SmileyRect();
  stage.add_child(rs);
  rs.redraw();

  stage.destroy.connect(Clutter.main_quit);
  stage.show();
  Clutter.main();

}

来源:https://stackoverflow.com/questions/16170085/filling-a-custom-shaped-clutter-actor-with-a-cairo-drawn-canvas

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!