#include #include #include #include GdkPixbuf *image; GdkPixbuf *current = NULL; static void shear_horizontally (GdkPixbuf *pixbuf, gint slice_width, gint slice_height, gdouble angle) { const gint pixbuf_width = gdk_pixbuf_get_width (pixbuf); const gint pixbuf_height = gdk_pixbuf_get_height (pixbuf); const gint pixbuf_rowstride = gdk_pixbuf_get_rowstride (pixbuf); const gint n_channels = gdk_pixbuf_get_n_channels (pixbuf); const gdouble tan_a = tan (angle/2.0); guchar *pixbuf_pixels; guchar *temp_row; guchar *source_ptr, *pixbuf_ptr; gint i, j, k; gdouble shear; gdouble shear_weight; pixbuf_pixels = gdk_pixbuf_get_pixels (pixbuf); temp_row = (gchar *) g_new (guchar, n_channels*(pixbuf_width + 1)); for (i = 0; i < slice_height; i++) { memset (temp_row, 0, n_channels * pixbuf_width); if (tan_a >= 0.0) shear = (i + 0.5) * tan_a; else shear = ((i - slice_height) + 0.5) * tan_a; shear_weight = shear - floor (shear); source_ptr = pixbuf_pixels; pixbuf_ptr = temp_row + ((gint)floor (shear)) * n_channels; for (j = 0; j < slice_width; j++) { if (j + floor (shear) > pixbuf_width || j + floor (shear) < 0) break; for (k = 0; k < n_channels; k++) { *pixbuf_ptr += (*(source_ptr))*(1.0 - shear_weight); *(n_channels + (pixbuf_ptr++)) = *(n_channels + source_ptr++) * (shear_weight); } } memcpy (pixbuf_pixels, temp_row, n_channels * shear_width); pixbuf_pixels += pixbuf_rowstride; } g_free (temp_row); } static void shear_vertically (GdkPixbuf *pixbuf, gint source_width, gint slice_width, gint slice_height, gdouble angle) { const gint pixbuf_width = gdk_pixbuf_get_width (pixbuf); const gint pixbuf_height = gdk_pixbuf_get_height (pixbuf); const gint pixbuf_rowstride = gdk_pixbuf_get_rowstride (pixbuf); const gint n_channels = gdk_pixbuf_get_n_channels (pixbuf); const gdouble sin_a = sin (angle); guchar *pixbuf_pixels; guchar *temp_row; guchar *source_ptr, *pixbuf_ptr; gint i, j, k; gdouble shear; gdouble shear_weight; gint shear_floor; pixbuf_pixels = gdk_pixbuf_get_pixels (pixbuf); temp_row = (guchar *) g_new (guchar, n_channels*(pixbuf_height + 1)); if (sin_a >= 0.0) shear = (source_width - 1) * sin_a; else shear = (slice_width - source_width) * sin_a; for (i = 0; i < slice_width; i++, shear -= sin_a) { shear_floor = (gint) floor (shear); shear_weight = shear - shear_floor; memset (temp_row, 0, n_channels * pixbuf_height); if (shear_floor < 0) shear_floor = 0; source_ptr = pixbuf_pixels; pixbuf_ptr = temp_row + (shear_floor) * n_channels; for (j = 0 ; j < slice_height; j++) { if (j + shear_floor >= pixbuf_height || j + shear_floor < 0) break; for (k = 0; k < n_channels; k++) { *pixbuf_ptr += (*(source_ptr))*(1.0 - shear_weight); *(n_channels + (pixbuf_ptr++)) = *(pixbuf_rowstride + source_ptr++) * (shear_weight); } source_ptr = source_ptr + pixbuf_rowstride - n_channels; } for (j = 0; j < pixbuf_height; j++) memcpy (pixbuf_pixels + (j * pixbuf_rowstride), temp_row + j *n_channels, n_channels); pixbuf_pixels += n_channels; } g_free (temp_row); } void gdk_pixbuf_real_rotate_0 (GdkPixbuf *source, GdkPixbuf *dest) { gdk_pixbuf_copy_area (source, 0, 0, gdk_pixbuf_get_width (source), gdk_pixbuf_get_height (source), dest, 0, 0); } static GdkPixbuf * rotate_pixbuf (GdkPixbuf *pixbuf, gdouble angle) { GdkPixbuf *retval; gint width, height; gint new_width, new_height; g_return_val_if_fail (pixbuf != NULL, NULL); angle = CLAMP (angle, -G_PI/4.0, G_PI/4.0); width = gdk_pixbuf_get_width (pixbuf); height = gdk_pixbuf_get_height (pixbuf); new_width = height * ABS (sin (angle)) + width * ABS (cos (angle)); new_height = width * ABS (sin (angle)) + height * ABS (cos (angle)); retval = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, new_width, new_height); memset (gdk_pixbuf_get_pixels (retval), 0, gdk_pixbuf_get_rowstride (retval) * gdk_pixbuf_get_height (retval)); if (angle >= -G_PI/4.0 && angle <= G_PI/4.0) gdk_pixbuf_real_rotate_0 (pixbuf, retval); shear_horizontally (retval, width + (gint) (height * tan (angle/2.0)), height, angle); shear_vertically (retval, width, width + (gint) (height * tan (angle/2.0)), new_height, angle); shear_horizontally (retval, new_width, new_height, angle); return retval; } static gint expose_event (GtkWidget *widget, GdkEventExpose *event, gpointer data) { gdk_draw_rgb_image (widget->window, widget->style->white_gc, event->area.x, event->area.y, event->area.width, event->area.height, GDK_RGB_DITHER_NORMAL, gdk_pixbuf_get_pixels (current) + (event->area.y * gdk_pixbuf_get_rowstride (current)) + (event->area.x * gdk_pixbuf_get_n_channels (current)), gdk_pixbuf_get_rowstride (current)); return TRUE; } static void changed (GtkAdjustment *adjustment, gpointer data) { GdkPixbuf *temp; if (current) gdk_pixbuf_unref (current); temp = rotate_pixbuf (image, adjustment->value); current = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, gdk_pixbuf_get_width (temp), gdk_pixbuf_get_height (temp)); gdk_pixbuf_composite_color (temp, current, 0,0, gdk_pixbuf_get_width (temp), gdk_pixbuf_get_height (temp), 0, 0, 1.0, 1.0, GDK_INTERP_TILES, 255, 0, 0, 24, 0xaaaaaa, 0x000000); gdk_pixbuf_unref (temp); gtk_widget_set_usize (GTK_WIDGET (data), gdk_pixbuf_get_width (current), gdk_pixbuf_get_height (current)); } int main (int argc, char *argv[]) { GtkWidget *window; GtkWidget *vbox; GtkWidget *range; GtkWidget *drawing; GtkWidget *align; GtkAdjustment *adjustment; gtk_init (&argc, &argv); image = gdk_pixbuf_new_from_file ("/usr/share/pixmaps/apple-green.png", NULL); gdk_pixbuf_ref (image); window = gtk_window_new (GTK_WINDOW_TOPLEVEL); vbox = gtk_vbox_new (FALSE, 8); gtk_container_add (GTK_CONTAINER (window), vbox); align = gtk_alignment_new (0.5, 0.5, 0.0, 0.0); drawing = gtk_drawing_area_new (); gtk_signal_connect (GTK_OBJECT (drawing), "expose_event", GTK_SIGNAL_FUNC (expose_event), NULL); gtk_box_pack_start (GTK_BOX (vbox), align, FALSE, FALSE, 0); gtk_container_add (GTK_CONTAINER (align), drawing); adjustment = (GtkAdjustment *)gtk_adjustment_new (0.0, -(G_PI/4.0), (G_PI/4.0), 0.01, 0.01, 0.01); changed (adjustment, drawing); gtk_signal_connect (GTK_OBJECT (adjustment), "value_changed", GTK_SIGNAL_FUNC (changed), drawing); range = gtk_hscale_new (adjustment); gtk_box_pack_start (GTK_BOX (vbox), range, FALSE, FALSE, 0); gtk_widget_show_all (window); gtk_main (); return 0; }