mirror of
https://gitlab.gnome.org/GNOME/eog
synced 2024-10-19 14:34:42 +00:00
bdaa44a847
2005-03-14 Jens Finke <jens@triq.net> * Merged the experimental-job-mgr branch back to head.
291 lines
7 KiB
C
291 lines
7 KiB
C
#include <time.h>
|
|
#include <stdlib.h>
|
|
#include <math.h>
|
|
#include <gtk/gtkmain.h>
|
|
#include <libgnome/gnome-macros.h>
|
|
#include <libart_lgpl/art_affine.h>
|
|
#include "eog-transform.h"
|
|
#include "libeog-marshal.h"
|
|
|
|
#define PROFILE 1
|
|
|
|
struct _EogTransformPrivate {
|
|
double affine[6];
|
|
};
|
|
|
|
static void
|
|
eog_transform_finalize (GObject *object)
|
|
{
|
|
EogTransform *trans = EOG_TRANSFORM (object);
|
|
|
|
g_free (trans->priv);
|
|
}
|
|
|
|
static void
|
|
eog_transform_instance_init (EogTransform *trans)
|
|
{
|
|
EogTransformPrivate *priv;
|
|
|
|
priv = g_new0 (EogTransformPrivate, 1);
|
|
|
|
trans->priv = priv;
|
|
}
|
|
|
|
static void
|
|
eog_transform_class_init (EogTransformClass *klass)
|
|
{
|
|
GObjectClass *object_class = (GObjectClass*) klass;
|
|
|
|
object_class->finalize = eog_transform_finalize;
|
|
}
|
|
|
|
|
|
GNOME_CLASS_BOILERPLATE (EogTransform,
|
|
eog_transform,
|
|
GObject,
|
|
G_TYPE_OBJECT);
|
|
|
|
|
|
GdkPixbuf*
|
|
eog_transform_apply (EogTransform *trans, GdkPixbuf *pixbuf, EogJob *job)
|
|
{
|
|
ArtPoint dest_top_left;
|
|
ArtPoint dest_bottom_right;
|
|
ArtPoint vertices[4] = { {0, 0}, {1, 0}, {1, 1}, {0, 1} };
|
|
double r_det;
|
|
int inverted [6];
|
|
ArtPoint dest;
|
|
ArtPoint src;
|
|
|
|
int src_width;
|
|
int src_height;
|
|
int src_rowstride;
|
|
int src_n_channels;
|
|
guchar *src_buffer;
|
|
|
|
GdkPixbuf *dest_pixbuf;
|
|
int dest_width;
|
|
int dest_height;
|
|
int dest_rowstride;
|
|
int dest_n_channels;
|
|
guchar *dest_buffer;
|
|
|
|
guchar *src_pos;
|
|
guchar *dest_pos;
|
|
int dx, dy, sx, sy;
|
|
int i, x, y;
|
|
float progress = 0.0;
|
|
|
|
g_return_val_if_fail (pixbuf != NULL, NULL);
|
|
|
|
g_object_ref (pixbuf);
|
|
|
|
src_width = gdk_pixbuf_get_width (pixbuf);
|
|
src_height = gdk_pixbuf_get_height (pixbuf);
|
|
src_rowstride = gdk_pixbuf_get_rowstride (pixbuf);
|
|
src_n_channels = gdk_pixbuf_get_n_channels (pixbuf);
|
|
src_buffer = gdk_pixbuf_get_pixels (pixbuf);
|
|
|
|
/* find out the dimension of the destination pixbuf */
|
|
dest_top_left.x = 100000;
|
|
dest_top_left.y = 100000;
|
|
dest_bottom_right.x = -100000;
|
|
dest_bottom_right.y = -100000;
|
|
|
|
for (i = 0; i < 4; i++) {
|
|
src.x = vertices[i].x * (src_width - 1);
|
|
src.y = vertices[i].y * (src_height -1);
|
|
|
|
art_affine_point (&dest, &src, trans->priv->affine);
|
|
|
|
dest_top_left.x = MIN (dest_top_left.x, dest.x);
|
|
dest_top_left.y = MIN (dest_top_left.y, dest.y);
|
|
|
|
dest_bottom_right.x = MAX (dest_bottom_right.x, dest.x);
|
|
dest_bottom_right.y = MAX (dest_bottom_right.y, dest.y);
|
|
}
|
|
|
|
/* create the resulting pixbuf */
|
|
dest_width = abs (dest_bottom_right.x - dest_top_left.x + 1);
|
|
dest_height = abs (dest_bottom_right.y - dest_top_left.y + 1);
|
|
|
|
dest_pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
|
|
gdk_pixbuf_get_has_alpha (pixbuf),
|
|
gdk_pixbuf_get_bits_per_sample (pixbuf),
|
|
dest_width,
|
|
dest_height);
|
|
dest_rowstride = gdk_pixbuf_get_rowstride (dest_pixbuf);
|
|
dest_n_channels = gdk_pixbuf_get_n_channels (dest_pixbuf);
|
|
dest_buffer = gdk_pixbuf_get_pixels (dest_pixbuf);
|
|
|
|
/* invert the matrix so that we can compute the source pixel
|
|
from the target pixel and convert the values to integer
|
|
ones (faster!) FIXME: Maybe we can do some more
|
|
improvements by using special mmx/3dnow features if
|
|
available.
|
|
*/
|
|
r_det = 1.0 / (trans->priv->affine[0] * trans->priv->affine[3] - trans->priv->affine[1] * trans->priv->affine[2]);
|
|
inverted[0] = trans->priv->affine[3] * r_det;
|
|
inverted[1] = -trans->priv->affine[1] * r_det;
|
|
inverted[2] = -trans->priv->affine[2] * r_det;
|
|
inverted[3] = trans->priv->affine[0] * r_det;
|
|
inverted[4] = -trans->priv->affine[4] * inverted[0] - trans->priv->affine[5] * inverted[2];
|
|
inverted[5] = -trans->priv->affine[4] * inverted[1] - trans->priv->affine[5] * inverted[3];
|
|
|
|
/* for every destination pixel (dx,dy) compute the source pixel (sx, sy) and copy the
|
|
color values */
|
|
for (y = 0; y < dest_height; y++) {
|
|
for (x = 0; x < dest_width; x++) {
|
|
|
|
dx = dest_top_left.x + x;
|
|
dy = dest_top_left.y + y;
|
|
|
|
sx = dx * inverted[0] + dy * inverted[2] + inverted[4];
|
|
sy = dx * inverted[1] + dy * inverted[3] + inverted[5];
|
|
|
|
if (sx >= 0 && sx < src_width && sy >= 0 && sy < src_height) {
|
|
src_pos = src_buffer + sy * src_rowstride + sx * src_n_channels;
|
|
dest_pos = dest_buffer + y * dest_rowstride + x * dest_n_channels;
|
|
|
|
for (i = 0; i < src_n_channels; i++) {
|
|
dest_pos[i] = src_pos[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
if (job != NULL) {
|
|
progress = (float) (y + 1.0) / (float) dest_height;
|
|
eog_job_set_progress (job, progress);
|
|
}
|
|
}
|
|
|
|
g_object_unref (pixbuf);
|
|
|
|
return dest_pixbuf;
|
|
}
|
|
|
|
EogTransform*
|
|
eog_transform_reverse (EogTransform *trans)
|
|
{
|
|
EogTransform *reverse;
|
|
|
|
g_return_val_if_fail (EOG_IS_TRANSFORM (trans), NULL);
|
|
|
|
reverse = EOG_TRANSFORM (g_object_new (EOG_TYPE_TRANSFORM, 0));
|
|
|
|
art_affine_invert (reverse->priv->affine, trans->priv->affine);
|
|
|
|
return reverse;
|
|
}
|
|
|
|
EogTransform*
|
|
eog_transform_compose (EogTransform *trans, EogTransform *compose)
|
|
{
|
|
EogTransform *composition;
|
|
|
|
g_return_val_if_fail (EOG_IS_TRANSFORM (trans), NULL);
|
|
g_return_val_if_fail (EOG_IS_TRANSFORM (compose), NULL);
|
|
|
|
composition = EOG_TRANSFORM (g_object_new (EOG_TYPE_TRANSFORM, 0));
|
|
|
|
art_affine_multiply (composition->priv->affine,
|
|
trans->priv->affine,
|
|
compose->priv->affine);
|
|
|
|
return composition;
|
|
}
|
|
|
|
gboolean
|
|
eog_transform_is_identity (EogTransform *trans)
|
|
{
|
|
double identity[6] = { 1, 0, 0, 1, 0, 0 };
|
|
|
|
g_return_val_if_fail (EOG_IS_TRANSFORM (trans), FALSE);
|
|
|
|
return art_affine_equal (identity, trans->priv->affine);
|
|
}
|
|
|
|
EogTransform*
|
|
eog_transform_rotate_new (int degree)
|
|
{
|
|
EogTransform *trans;
|
|
|
|
trans = EOG_TRANSFORM (g_object_new (EOG_TYPE_TRANSFORM, 0));
|
|
|
|
art_affine_rotate (trans->priv->affine, degree);
|
|
|
|
return trans;
|
|
}
|
|
|
|
EogTransform*
|
|
eog_transform_flip_new (EogTransformType type)
|
|
{
|
|
EogTransform *trans;
|
|
gboolean horiz, vert;
|
|
|
|
trans = EOG_TRANSFORM (g_object_new (EOG_TYPE_TRANSFORM, 0));
|
|
|
|
art_affine_identity (trans->priv->affine);
|
|
|
|
horiz = (type == EOG_TRANSFORM_FLIP_HORIZONTAL);
|
|
vert = (type == EOG_TRANSFORM_FLIP_VERTICAL);
|
|
|
|
art_affine_flip (trans->priv->affine,
|
|
trans->priv->affine,
|
|
horiz, vert);
|
|
|
|
return trans;
|
|
}
|
|
|
|
EogTransform*
|
|
eog_transform_scale_new (double sx, double sy)
|
|
{
|
|
EogTransform *trans;
|
|
|
|
trans = EOG_TRANSFORM (g_object_new (EOG_TYPE_TRANSFORM, 0));
|
|
|
|
art_affine_scale (trans->priv->affine, sx, sy);
|
|
|
|
return trans;
|
|
}
|
|
|
|
EogTransformType
|
|
eog_transform_get_transform_type (EogTransform *trans)
|
|
{
|
|
double affine[6];
|
|
EogTransformPrivate *priv;
|
|
|
|
g_return_val_if_fail (EOG_IS_TRANSFORM (trans), EOG_TRANSFORM_NONE);
|
|
|
|
priv = trans->priv;
|
|
|
|
art_affine_rotate (affine, 90);
|
|
if (art_affine_equal (affine, priv->affine)) {
|
|
return EOG_TRANSFORM_ROT_90;
|
|
}
|
|
|
|
art_affine_rotate (affine, 180);
|
|
if (art_affine_equal (affine, priv->affine)) {
|
|
return EOG_TRANSFORM_ROT_180;
|
|
}
|
|
|
|
art_affine_rotate (affine, 270);
|
|
if (art_affine_equal (affine, priv->affine)) {
|
|
return EOG_TRANSFORM_ROT_270;
|
|
}
|
|
|
|
art_affine_identity (affine);
|
|
art_affine_flip (affine, affine, TRUE, FALSE);
|
|
if (art_affine_equal (affine, priv->affine)) {
|
|
return EOG_TRANSFORM_FLIP_HORIZONTAL;
|
|
}
|
|
|
|
art_affine_identity (affine);
|
|
art_affine_flip (affine, affine, FALSE, TRUE);
|
|
if (art_affine_equal (affine, priv->affine)) {
|
|
return EOG_TRANSFORM_FLIP_VERTICAL;
|
|
}
|
|
|
|
return EOG_TRANSFORM_NONE;
|
|
}
|