[cairo-commit] cairo-demo/path_paint ChangeLog, NONE, 1.1 Makefile, NONE, 1.1 README.txt, NONE, 1.1 path_paint.c, NONE, 1.1

OEyvind Kolaas commit at pdx.freedesktop.org
Thu May 27 12:54:28 PDT 2004


Committed by: pippin

Update of /cvs/cairo/cairo-demo/path_paint
In directory pdx:/tmp/cvs-serv16517

Added Files:
	ChangeLog Makefile README.txt path_paint.c 
Log Message:
initial import of path_paint


--- NEW FILE: ChangeLog ---
2004-05-27 OEyvind Kolaas <pippin at freedesktop.org> 

	Initial import

/* vim: set ts=4 noet : */

--- NEW FILE: Makefile ---
CFLAGS= -Wall
LDFLAGS = -lm

CFLAGS+= `pkg-config gtkcairo --cflags`
LDFLAGS+= `pkg-config gtkcairo --libs`
CC = gcc

all: path_paint

test: path_paint
	./path_paint

--- NEW FILE: README.txt ---
path_paint  |
------------'

A simple interactive paint core using cairo.

It displays the current stroke incrementally rendered as a path
over the composite of all previous strokes.

The concept itself could be used both for vector based drawing apps
and pixel based programs.

Build instructions |
-------------------'

you need gtkcairo and it's dependencies, most of this is located in the
cairo http://cairographics.org/ cvs

just running make should work fine


Not Implemented |
----------------'

Features this app would need, in approximate order of increments
to become a useful application.

    - setting color
    - load/save of png
    - setting line width
    - setting alpha
    - changing canvas size
    - path/data simplification
    - editing sample points
    - editing of bezier points of path before use
    - closing of path
    - filling path
    - stroke/fill history (for undo)
    - load/save of svg (using stroke history)
    - zoom (redraw from stroke history)

-pippin / Øyvind Kolås   pippin at freedesktop.org

--- NEW FILE: path_paint.c ---
#include <gtk/gtk.h>
#include <math.h>
#include <gtkcairo.h>

#define WIDTH  800
#define HEIGHT 600
#define STRIDE WIDTH*4

#define MAX_COORDS 1024

/* uncomment this to decrease the density of coordinate samples used for
 * the current stroke, this is not an optimal solution, a better solution
 * would be to use actuall smoothing on the coordinate data
 */
#define USE_HINT

GtkWidget *gtkcairo = NULL;

int pen_color_is_black=1;
int pen_radius=8;

double coord_x [MAX_COORDS];
double coord_y [MAX_COORDS];
int coord_count = 0;

unsigned char buffer      [WIDTH*HEIGHT*4];
unsigned char temp_buffer [WIDTH*HEIGHT*4];

cairo_surface_t *backbuffer = NULL;
cairo_t *cr_save = NULL;

int pen_is_down=0;

void
destroy ()
{
    cairo_destroy (cr_save);
}


/* utility functions, defined at bottom,
 * constructing a path from coordinate arrays
 */
void
points_to_linear_path (cairo_t *cr,
                       double   coord_x[],
                       double   coord_y[],
                       int      n_coords);
void
points_to_bezier_path (cairo_t *cr,
                       double   coord_x[],
                       double   coord_y[],
                       int      n_coords);


void
drawapp_render (cairo_t *cr,
                unsigned char *buffer)
{
    cairo_save (cr);

    cairo_rectangle (cr, 0,0, WIDTH, HEIGHT);
    cairo_set_rgb_color (cr, 1,1,1);
    cairo_fill (cr);

    cairo_move_to (cr, 0,0);
    cairo_show_surface (cr, backbuffer, WIDTH, HEIGHT);

    cairo_set_line_width (cr, pen_radius*2);
    cairo_set_alpha (cr, 0.5);

    if (pen_color_is_black) {
        cairo_set_rgb_color (cr, 0,0,0);
    } else {
        cairo_set_rgb_color (cr, 1,1,1);
    }

    cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
    cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);

    points_to_bezier_path (cr, coord_x, coord_y, coord_count);
    cairo_stroke (cr);

    cairo_restore (cr);
}

void
apply_coords (char *buffer)
{
   drawapp_render (cr_save, buffer);
   memcpy (buffer, temp_buffer, HEIGHT*STRIDE);
}

void
coords_clear (void)
{
   coord_count = 0;
}

void
coords_add (double x, double y)
{
   if (coord_count< MAX_COORDS-3) {
     coord_x [coord_count]=x;
     coord_y [coord_count]=y;
     coord_count++;
   }
}

void
redraw (GtkCairo *gtkcairo,
        cairo_t  *cr,
        gpointer data)
{
    drawapp_render (cr, buffer);
}

void
pen_motion (double x,
            double y,
            int    pen_is_down)
{
    if (pen_is_down) {
        coords_add (x,y);
        gtk_widget_queue_draw (gtkcairo);
    }
}

void
pen_down (double x,
          double y)
{
    coords_add (x,y);
    gtk_widget_queue_draw (gtkcairo);
}

void pen_up (double x,
             double y)
{
    apply_coords (buffer);
    coords_clear ();
    gtk_widget_queue_draw (gtkcairo);
}



void
init ()
{
    coords_clear ();
    cr_save = cairo_create ();
    memset (buffer, sizeof(buffer), 0);
    memset (temp_buffer, sizeof(temp_buffer), 0);

    cairo_set_target_image (
        cr_save, temp_buffer, CAIRO_FORMAT_ARGB32, WIDTH, HEIGHT, STRIDE);

    backbuffer = cairo_surface_create_for_image (
        buffer, CAIRO_FORMAT_ARGB32, WIDTH, HEIGHT, STRIDE);
}

static gboolean
event_press (GtkWidget      *widget,
             GdkEventButton *bev,
             gpointer        user_data)
{
        pen_down (bev->x, bev->y);
        pen_is_down = 1;
        return TRUE;
}

static gboolean
event_release (GtkWidget      *widget,
               GdkEventButton *bev,
             gpointer        user_data)
{
        pen_up (bev->x, bev->y);
        pen_is_down = 0;
        return TRUE;
}

static gboolean
event_motion (GtkWidget      *widget,
              GdkEventMotion *mev,
              gpointer        user_data)
{
        pen_motion (mev->x, mev->y, pen_is_down);
        gdk_window_get_pointer (widget->window, NULL, NULL, NULL);
        return TRUE;
}

int main (int    argc,
          char **argv)
{
   GtkWidget *mainwin;

   gtk_init (&argc, &argv);

   mainwin= gtk_window_new (GTK_WINDOW_TOPLEVEL);

   gtkcairo = gtk_cairo_new ();

   gtk_widget_set_events (gtkcairo,
                           GDK_EXPOSURE_MASK |
#ifdef USE_HINT
                           GDK_POINTER_MOTION_HINT_MASK |
#endif
                           GDK_BUTTON1_MOTION_MASK |
                           GDK_BUTTON_PRESS_MASK |
                           GDK_BUTTON_RELEASE_MASK);
   gtk_widget_set_size_request (gtkcairo, WIDTH, HEIGHT);

   g_signal_connect (G_OBJECT (gtkcairo), "redraw",
                     G_CALLBACK (redraw), NULL);
   g_signal_connect (G_OBJECT (gtkcairo), "motion_notify_event",
                     G_CALLBACK (event_motion), NULL);
   g_signal_connect (G_OBJECT (gtkcairo), "button_press_event",
                     G_CALLBACK (event_press), NULL);
   g_signal_connect (G_OBJECT (gtkcairo), "button_release_event",
                     G_CALLBACK (event_release), NULL);

   gtk_container_add (GTK_CONTAINER (mainwin), gtkcairo);

   init ();
   gtk_widget_show_all (mainwin);

   gtk_main ();
   return 0;
}


/* utility function that creates a smooth path from a set
 * of coordinates
 */
void
points_to_bezier_path (cairo_t *cr,
                       double   coord_x[],
                       double   coord_y[],
                       int      n_coords)
{
    int i;
    double smooth_value = 1;

    cairo_new_path (cr);
    if (!n_coords)
            return;

    cairo_move_to (cr, coord_x[0], coord_y[0]);

    for (i=1;i<n_coords;i++){
        double x2 = coord_x[i];
        double y2 = coord_y[i];

        double x0,y0,x1,y1,x3,y3;


        switch (i){
           case 1:
              x0=coord_x[i-1];
              y0=coord_y[i-1];
              x1 = coord_x[i-1];
              y1 = coord_y[i-1];
              break;
           case 2:
           default:
              x0=coord_x[i-2];
              y0=coord_y[i-2];
              x1 = coord_x[i-1];
              y1 = coord_y[i-1];
        }

        if (i<n_coords+1) {
           x3 = coord_x[i+1];
           y3 = coord_y[i+1];
        } else {
           x3 = coord_x[i];
           y3 = coord_y[i];
        }
        {

          double xc1 = (x0 + x1) / 2.0;
          double yc1 = (y0 + y1) / 2.0;
          double xc2 = (x1 + x2) / 2.0;
          double yc2 = (y1 + y2) / 2.0;
          double xc3 = (x2 + x3) / 2.0;
          double yc3 = (y2 + y3) / 2.0;

          double len1 = sqrt( (x1-x0) * (x1-x0) + (y1-y0) * (y1-y0) );
          double len2 = sqrt( (x2-x1) * (x2-x1) + (y2-y1) * (y2-y1) );
          double len3 = sqrt( (x3-x2) * (x3-x2) + (y3-y2) * (y3-y2) );

          double k1 = len1 / (len1 + len2);
          double k2 = len2 / (len2 + len3);

          double xm1 = xc1 + (xc2 - xc1) * k1;
          double ym1 = yc1 + (yc2 - yc1) * k1;

          double xm2 = xc2 + (xc3 - xc2) * k2;
          double ym2 = yc2 + (yc3 - yc2) * k2;

          double ctrl1_x = xm1 + (xc2 - xm1) * smooth_value + x1 - xm1;
          double ctrl1_y = ym1 + (yc2 - ym1) * smooth_value + y1 - ym1;

          double ctrl2_x = xm2 + (xc2 - xm2) * smooth_value + x2 - xm2;
          double ctrl2_y = ym2 + (yc2 - ym2) * smooth_value + y2 - ym2;

          cairo_curve_to (cr, ctrl1_x, ctrl1_y, ctrl2_x, ctrl2_y, x2,y2);
          }
       }
}


/* this function is included, just to be
 * able to check the improvement over the easiest method
 */
void
points_to_linear_path (cairo_t *cr,
                       double   coord_x[],
                       double   coord_y[],
                       int      n_coords)
{
    int i;
    if (!n_coords)
            return;
    cairo_move_to (cr, coord_x[0], coord_y[0]);
    for (i=1;i<n_coords;i++)
            cairo_line_to (cr, coord_x[i], coord_y[i]);
}







More information about the cairo-commit mailing list