[dia] [transform] initial rotation support for "Standard - Image"



commit 127663095853a7fa06e245d3a686fd71db859645
Author: Hans Breuer <hans breuer org>
Date:   Sun Nov 2 14:33:03 2014 +0100

    [transform] initial rotation support for "Standard - Image"
    
    Introduce DiaRenderer::draw_rotated_image() and move some code to helper
    functions for reuse. Implement DiaCairoRenderer::draw_rotated_image() for
    testing the new functionality (mainly with transform-variations.svg).
    
    Give "Standard - Image" an angle property: copy, load, save it.
    Set Image::angle in image_transform().
    
    Unrelated: use element_update_connections_rectangle() to ensure proper
    directions initialization with less code.
    
    Open issues:
     - for now only the cairo renderer implements draw_image_rotated()
     - either the angle should be limited like for "Standard - Box" or the
       connection point directions for angles outside [-45...45deg] must be fixed

 lib/diarenderer.c                  |   21 ++++++++
 lib/diarenderer.h                  |    6 ++
 lib/element.c                      |   25 ++++++++++
 lib/element.h                      |    4 ++
 lib/geometry.c                     |   11 ++++
 lib/geometry.h                     |    1 +
 lib/libdia.def                     |    2 +
 objects/standard/image.c           |   89 +++++++++++++++++++++++------------
 plug-ins/cairo/diacairo-renderer.c |   27 +++++++++--
 9 files changed, 151 insertions(+), 35 deletions(-)
---
diff --git a/lib/diarenderer.c b/lib/diarenderer.c
index ea1ebf4..7ed882a 100644
--- a/lib/diarenderer.c
+++ b/lib/diarenderer.c
@@ -108,6 +108,11 @@ static void draw_text_line  (DiaRenderer *renderer,
                             TextLine *text_line, Point *pos, Alignment alignment, Color *color);
 static void draw_rotated_text (DiaRenderer *renderer, Text *text,
                               Point *center, real angle);
+static void draw_rotated_image (DiaRenderer *renderer,
+                               Point *point,
+                               real width, real height,
+                               real angle,
+                               DiaImage *image);
 
 static void draw_polyline (DiaRenderer *renderer,
                            Point *points, int num_points,
@@ -339,6 +344,7 @@ dia_renderer_class_init (DiaRendererClass *klass)
   renderer_class->draw_text      = draw_text;
   renderer_class->draw_text_line = draw_text_line;
   renderer_class->draw_rotated_text = draw_rotated_text;
+  renderer_class->draw_rotated_image = draw_rotated_image;
 
   /* highest level functions */
   renderer_class->draw_rounded_rect = draw_rounded_rect;
@@ -707,6 +713,21 @@ draw_rotated_text (DiaRenderer *renderer, Text *text,
   }
 }
 
+static void
+draw_rotated_image (DiaRenderer *renderer,
+                   Point *point,
+                   real width, real height,
+                   real angle,
+                   DiaImage *image)
+{
+  if (angle == 0.0) {
+    DIA_RENDERER_GET_CLASS (renderer)->draw_image (renderer, point, width, height, image);
+  } else {
+    /* XXX: implement fallback */
+    DIA_RENDERER_GET_CLASS (renderer)->draw_image (renderer, point, width, height, image);
+  }
+}
+
 /*!
  * \brief Default implementation of draw_text_line
  *
diff --git a/lib/diarenderer.h b/lib/diarenderer.h
index 4ee93a2..368beca 100644
--- a/lib/diarenderer.h
+++ b/lib/diarenderer.h
@@ -255,6 +255,12 @@ struct _DiaRendererClass
   /*! draw text rotated around center with given angle in degrees */
   void (*draw_rotated_text) (DiaRenderer *renderer, Text *text,
                             Point *center, real angle);
+  /*! draw image rotated around center with given angle in degrees */
+  void (*draw_rotated_image) (DiaRenderer *renderer,
+                             Point *point,
+                             real width, real height,
+                             real angle,
+                             DiaImage *image);
 };
 
 /*
diff --git a/lib/element.c b/lib/element.c
index 23b6ddf..8099e55 100644
--- a/lib/element.c
+++ b/lib/element.c
@@ -512,3 +512,28 @@ element_change_new (const Point *corner,
 
   return &ec->object_change;
 }
+
+void
+element_get_poly (const Element *elem, real angle, Point corners[4])
+{
+  corners[0] = elem->corner;
+  corners[1] = corners[0];
+  corners[1].x += elem->width;
+  corners[2] = corners[1];
+  corners[2].y += elem->height;
+  corners[3] = corners[2];
+  corners[3].x -= elem->width;
+
+  if (angle != 0) {
+    real cx = elem->corner.x + elem->width / 2.0;
+    real cy = elem->corner.y + elem->height / 2.0;
+    DiaMatrix m = { 1.0, 0.0, 0.0, 1.0, cx, cy };
+    DiaMatrix t = { 1.0, 0.0, 0.0, 1.0, -cx, -cy };
+    int i;
+
+    dia_matrix_set_angle_and_scales (&m, G_PI*angle/180, 1.0, 1.0);
+    dia_matrix_multiply (&m, &t, &m);
+    for (i = 0; i < 4; ++i)
+      transform_point (&corners[i], &m);
+  }
+}
diff --git a/lib/element.h b/lib/element.h
index ac72ebe..0a50799 100644
--- a/lib/element.h
+++ b/lib/element.h
@@ -70,6 +70,10 @@ void element_load(Element *elem, ObjectNode obj_node, DiaContext *ctx);
 ObjectChange *element_change_new (const Point *corner, 
                                  real width, real height,
                                  Element *elem);
+
+void element_get_poly (const Element *elem, real angle, Point corners[4]);
+
+
 /* base property stuff ... */
 #ifdef G_OS_WIN32
 /* see lib/properties.h for the reason */
diff --git a/lib/geometry.c b/lib/geometry.c
index ebbe07a..8f4810c 100644
--- a/lib/geometry.c
+++ b/lib/geometry.c
@@ -840,6 +840,17 @@ dia_matrix_is_invertible (const DiaMatrix *matrix)
   return finite(det) && det != 0.0;
 }
 
+void
+dia_matrix_set_rotate_around (DiaMatrix *result,
+                             real angle,
+                             const Point *around)
+{
+  DiaMatrix m = { 1.0, 0.0, 0.0, 1.0,  around->x,  around->y };
+  DiaMatrix t = { 1.0, 0.0, 0.0, 1.0, -around->x, -around->y };
+
+  dia_matrix_set_angle_and_scales (&m, angle, 1.0, 1.0);
+  dia_matrix_multiply (result, &t, &m);
+}
 /*!
  * \brief asin wrapped to limit to valid result range
  *
diff --git a/lib/geometry.h b/lib/geometry.h
index d016dc0..699f22d 100644
--- a/lib/geometry.h
+++ b/lib/geometry.h
@@ -141,6 +141,7 @@ void dia_matrix_set_angle_and_scales (DiaMatrix *m,
                                      real       sy);
 void dia_matrix_multiply (DiaMatrix *result, const DiaMatrix *a, const DiaMatrix *b);
 gboolean dia_matrix_is_invertible (const DiaMatrix *matrix);
+void dia_matrix_set_rotate_around (DiaMatrix *result, real angle, const Point *around);
 
 #define ROUND(x) ((int) floor((x)+0.5))
 
diff --git a/lib/libdia.def b/lib/libdia.def
index fe27357..81d19c6 100644
--- a/lib/libdia.def
+++ b/lib/libdia.def
@@ -443,6 +443,7 @@ EXPORTS
  element_update_handles
  element_update_connections_rectangle
  element_update_connections_directions
+ element_get_poly
 
  ellipse_bbox
  element_change_new
@@ -803,6 +804,7 @@ EXPORTS
  dia_matrix_is_identity
  dia_matrix_get_angle_and_scales
  dia_matrix_set_angle_and_scales
+ dia_matrix_set_rotate_around
  dia_matrix_multiply
 
  dia_option_menu_add_item
diff --git a/objects/standard/image.c b/objects/standard/image.c
index 712190c..8d7c9a8 100644
--- a/objects/standard/image.c
+++ b/objects/standard/image.c
@@ -72,6 +72,8 @@ struct _Image {
   gboolean draw_border;
   gboolean keep_aspect;
 
+  real angle;
+
   time_t mtime;
 };
 
@@ -113,6 +115,8 @@ static ObjectTypeOps image_type_ops =
   (ApplyDefaultsFunc) NULL
 };
 
+static PropNumData _image_angle_range = { -180.0, 180.0, 5.0 };
+
 static PropDescription image_props[] = {
   ELEMENT_COMMON_PROPERTIES,
   { "image_file", PROP_TYPE_FILE, PROP_FLAG_VISIBLE,
@@ -125,6 +129,8 @@ static PropDescription image_props[] = {
     N_("Draw border"), NULL, NULL},
   { "keep_aspect", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE,
     N_("Keep aspect ratio"), NULL, NULL},
+  { "angle", PROP_TYPE_REAL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
+    N_("Angle"), NULL, &_image_angle_range },
   PROP_STD_LINE_WIDTH,
   PROP_STD_LINE_COLOUR,
   PROP_STD_LINE_STYLE,
@@ -171,6 +177,7 @@ static PropOffset image_offsets[] = {
   { "pixbuf", PROP_TYPE_PIXBUF, offsetof(Image, pixbuf) },
   { "show_border", PROP_TYPE_BOOL, offsetof(Image, draw_border) },
   { "keep_aspect", PROP_TYPE_BOOL, offsetof(Image, keep_aspect) },
+  { "angle", PROP_TYPE_REAL, offsetof(Image,angle)},
   { PROP_STDNAME_LINE_WIDTH, PROP_STDTYPE_LINE_WIDTH, offsetof(Image, border_width) },
   { "line_colour", PROP_TYPE_COLOUR, offsetof(Image, border_color) },
   { "line_style", PROP_TYPE_LINESTYLE,
@@ -302,6 +309,11 @@ image_distance_from(Image *image, Point *point)
   Rectangle rect;
   real bw = image->draw_border ? image->border_width : 0;
 
+  if (image->angle != 0.0) {
+    Point corners[4];
+    element_get_poly (elem, image->angle, corners);
+    return distance_polygon_point (corners, 4, bw, point);
+  }
   rect.left = elem->corner.x - bw;
   rect.right = elem->corner.x + elem->width + bw;
   rect.top = elem->corner.y - bw;
@@ -446,20 +458,30 @@ image_draw(Image *image, DiaRenderer *renderer)
     renderer_ops->set_linewidth(renderer, image->border_width);
     renderer_ops->set_linestyle(renderer, image->line_style, image->dashlength);
     renderer_ops->set_linejoin(renderer, LINEJOIN_MITER);
-    
-    renderer_ops->draw_rect(renderer, 
-                            &ul_corner,
-                            &lr_corner,
-                            NULL,
-                            &image->border_color);
+
+    if (image->angle != 0.0) {
+      Point poly[4];
+      element_get_poly (elem, image->angle, poly);
+      /* need to grow the poly, XXX: done here by growing the border width */
+      renderer_ops->set_linewidth(renderer, image->border_width * 2);
+      renderer_ops->draw_polygon (renderer, poly, 4, NULL, &image->border_color);
+    } else {
+      renderer_ops->draw_rect (renderer,
+                              &ul_corner, &lr_corner,
+                              NULL, &image->border_color);
+    }
   }
   /* Draw the image */
   if (image->image) {
-    renderer_ops->draw_image(renderer, &elem->corner, elem->width,
-                             elem->height, image->image);
+    if (image->angle == 0.0)
+      renderer_ops->draw_image (renderer, &elem->corner, elem->width,
+                               elem->height, image->image);
+    else
+      renderer_ops->draw_rotated_image (renderer, &elem->corner, elem->width,
+                                       elem->height, image->angle, image->image);
   } else {
     DiaImage *broken = dia_image_get_broken();
-    renderer_ops->draw_image(renderer, &elem->corner, elem->width,
+    renderer_ops->draw_image (renderer, &elem->corner, elem->width,
                              elem->height, broken);
     dia_image_unref(broken);
   }
@@ -484,9 +506,9 @@ image_transform(Image *image, const DiaMatrix *m)
 
     /* rotation is invariant to the center */
     transform_point (&c, m);
-    /* XXX: implement image rotate! */
     elem->width = width;
     elem->height = height;
+    image->angle = angle;
     elem->corner.x = c.x - width / 2.0;
     elem->corner.y = c.y - height / 2.0;
   }
@@ -512,30 +534,26 @@ image_update_data(Image *image)
   }
 
   /* Update connections: */
-  image->connections[0].pos = elem->corner;
-  image->connections[1].pos.x = elem->corner.x + elem->width / 2.0;
-  image->connections[1].pos.y = elem->corner.y;
-  image->connections[2].pos.x = elem->corner.x + elem->width;
-  image->connections[2].pos.y = elem->corner.y;
-  image->connections[3].pos.x = elem->corner.x;
-  image->connections[3].pos.y = elem->corner.y + elem->height / 2.0;
-  image->connections[4].pos.x = elem->corner.x + elem->width;
-  image->connections[4].pos.y = elem->corner.y + elem->height / 2.0;
-  image->connections[5].pos.x = elem->corner.x;
-  image->connections[5].pos.y = elem->corner.y + elem->height;
-  image->connections[6].pos.x = elem->corner.x + elem->width / 2.0;
-  image->connections[6].pos.y = elem->corner.y + elem->height;
-  image->connections[7].pos.x = elem->corner.x + elem->width;
-  image->connections[7].pos.y = elem->corner.y + elem->height;
-  image->connections[8].pos.x = elem->corner.x + elem->width / 2.0;
-  image->connections[8].pos.y = elem->corner.y + elem->height / 2.0;
-  
+  element_update_connections_rectangle (elem, image->connections);
+
+  if (image->angle != 0) {
+    real cx = elem->corner.x + elem->width / 2.0;
+    real cy = elem->corner.y + elem->height / 2.0;
+    DiaMatrix m = { 1.0, 0.0, 0.0, 1.0, cx, cy };
+    DiaMatrix t = { 1.0, 0.0, 0.0, 1.0, -cx, -cy };
+    int i;
+
+    dia_matrix_set_angle_and_scales (&m, G_PI*image->angle/180, 1.0, 1.0);
+    dia_matrix_multiply (&m, &t, &m);
+    for (i = 0; i < 8; ++i)
+      transform_point (&image->connections[i].pos, &m);
+  }
+
   /* the image border is drawn vompletely outside of the image, so no /2.0 on border width */
   extra->border_trans = (image->draw_border ? image->border_width : 0.0);
   element_update_boundingbox(elem);
   
   obj->position = elem->corner;
-  image->connections[8].directions = DIR_ALL;
   element_update_handles(elem);
 }
 
@@ -633,6 +651,7 @@ image_copy(Image *image)
   newimage->border_width = image->border_width;
   newimage->border_color = image->border_color;
   newimage->line_style = image->line_style;
+  newimage->angle = image->angle;
   
   for (i=0;i<NUM_CONNECTIONS;i++) {
     newobj->connections[i] = &newimage->connections[i];
@@ -647,8 +666,8 @@ image_copy(Image *image)
     dia_image_add_ref(image->image);
   newimage->image = image->image;
 
-  /* not sure if we only want a reference, but it would be safe when 
-   * someday editing doe not work inplace, but creates new pixbufs 
+  /* not sure if we only want a reference, but it would be safe when someday
+   * editing does not work inplace anymore and instead creates new pixbufs
    * for every single undoable step */
   newimage->inline_data = image->inline_data;
   if (image->pixbuf)
@@ -720,6 +739,9 @@ image_save(Image *image, ObjectNode obj_node, DiaContext *ctx)
   data_add_boolean(new_attribute(obj_node, "draw_border"), image->draw_border, ctx);
   data_add_boolean(new_attribute(obj_node, "keep_aspect"), image->keep_aspect, ctx);
 
+  if (image->angle != 0.0)
+    data_add_real(new_attribute(obj_node, "angle"), image->angle, ctx);
+
   if (image->file != NULL) {
     if (g_path_is_absolute(image->file)) { /* Absolute pathname */
       diafile_dir = get_directory(dia_context_get_filename (ctx));
@@ -804,6 +826,11 @@ image_load(ObjectNode obj_node, int version, DiaContext *ctx)
   if (attr != NULL)
     image->keep_aspect =  data_boolean(attribute_first_data(attr), ctx);
 
+  image->angle = 0.0;
+  attr = object_find_attribute(obj_node, "angle");
+  if (attr != NULL)
+    image->angle = data_real(attribute_first_data(attr), ctx);
+
   attr = object_find_attribute(obj_node, "file");
   if (attr != NULL) {
     image->file =  data_filename(attribute_first_data(attr), ctx);
diff --git a/plug-ins/cairo/diacairo-renderer.c b/plug-ins/cairo/diacairo-renderer.c
index 2f7b4d4..047adee 100644
--- a/plug-ins/cairo/diacairo-renderer.c
+++ b/plug-ins/cairo/diacairo-renderer.c
@@ -952,10 +952,11 @@ draw_string(DiaRenderer *self,
 }
 
 static void
-draw_image(DiaRenderer *self,
-           Point *point,
-           real width, real height,
-           DiaImage *image)
+draw_rotated_image (DiaRenderer *self,
+                   Point *point,
+                   real width, real height,
+                   real angle,
+                   DiaImage *image)
 {
   DiaCairoRenderer *renderer = DIA_CAIRO_RENDERER (self);
   cairo_surface_t *surface;
@@ -1044,6 +1045,14 @@ draw_image(DiaRenderer *self,
   cairo_scale (renderer->cr, width/w, height/h);
   cairo_move_to (renderer->cr, 0.0, 0.0);
   cairo_set_source_surface (renderer->cr, surface, 0.0, 0.0);
+  if (angle != 0.0)
+    {
+      DiaMatrix rotate;
+      Point center = { w/2, h/2  };
+
+      dia_matrix_set_rotate_around (&rotate, -G_PI * angle / 180.0, &center);
+      cairo_pattern_set_matrix (cairo_get_source (renderer->cr), (cairo_matrix_t *)&rotate);
+    }
 #if 0
   /*
    * CAIRO_FILTER_FAST: aka. CAIRO_FILTER_NEAREST
@@ -1061,6 +1070,14 @@ draw_image(DiaRenderer *self,
   DIAG_STATE(renderer->cr);
 }
 
+static void
+draw_image (DiaRenderer *self,
+           Point *point,
+           real width, real height,
+           DiaImage *image)
+{
+  draw_rotated_image (self, point, width, height, 0.0, image);
+}
 static gpointer parent_class = NULL;
 
 /*!
@@ -1222,6 +1239,8 @@ cairo_renderer_class_init (DiaCairoRendererClass *klass)
   /* highest level functions */
   renderer_class->draw_rounded_rect = draw_rounded_rect;
   renderer_class->draw_rounded_polyline = draw_rounded_polyline;
+  renderer_class->draw_rotated_image = draw_rotated_image;
+
   /* other */
   renderer_class->is_capable_to = is_capable_to;
   renderer_class->set_pattern = set_pattern;


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]