/* -*- Mode: C; c-basic-offset: 2; indent-tabs-mode: nil -*-
 *
 * Pigment media rendering library
 *
 * Copyright © 2006, 2007, 2008 Fluendo Embedded S.L.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 *
 * Author(s): Loïc Molinari <loic@fluendo.com>
 *            Julien Moutte <julien@fluendo.com>
 */

#ifndef __PGM_IMAGE_H__
#define __PGM_IMAGE_H__

#include <gdk-pixbuf/gdk-pixbuf.h>
#include "pgmdrawable.h"

G_BEGIN_DECLS

#define PGM_TYPE_IMAGE (pgm_image_get_type ())
#define PGM_IMAGE(obj) \
  (G_TYPE_CHECK_INSTANCE_CAST((obj), PGM_TYPE_IMAGE, PgmImage))
#define PGM_IMAGE_CLASS(klass) \
  (G_TYPE_CHECK_CLASS_CAST((klass), PGM_TYPE_IMAGE, PgmImageClass))
#define PGM_IS_IMAGE(obj) \
  (G_TYPE_CHECK_INSTANCE_TYPE((obj), PGM_TYPE_IMAGE))
#define PGM_IS_IMAGE_CLASS(klass) \
  (G_TYPE_CHECK_CLASS_TYPE((klass), PGM_TYPE_IMAGE))
#define PGM_IMAGE_GET_CLASS(obj) \
  (G_TYPE_INSTANCE_GET_CLASS((obj), PGM_TYPE_IMAGE, PgmImageClass))

#define PGM_IMAGE_NB_PIXEL_FORMATS 8

typedef struct _PgmImage      PgmImage;
typedef struct _PgmImageClass PgmImageClass;

/* Data storage */
typedef struct _PgmImageFile         PgmImageFile;
typedef struct _PgmImageBuffer       PgmImageBuffer;
typedef struct _PgmImageGstBuffer    PgmImageGstBuffer;
typedef struct _PgmImageSystemBuffer PgmImageSystemBuffer;
typedef struct _PgmImagePixbuf       PgmImagePixbuf;
typedef union  _PgmImageData         PgmImageData;

/**
 * PgmImageAlignment:
 * @PGM_IMAGE_LEFT: Align to the left.
 * @PGM_IMAGE_CENTER: Align to the center (both vertically and horizontally).
 * @PGM_IMAGE_RIGHT: Align to the right.
 * @PGM_IMAGE_TOP: Align to the top.
 * @PGM_IMAGE_BOTTOM: Align to the bottom.
 * @PGM_IMAGE_TOP_LEFT: Align to the top left.
 * @PGM_IMAGE_TOP_CENTER: Align to the top center.
 * @PGM_IMAGE_TOP_RIGHT: Align to the top right.
 * @PGM_IMAGE_BOTTOM_LEFT: Align to the bottom left.
 * @PGM_IMAGE_BOTTOM_CENTER: Align to the bottom center.
 * @PGM_IMAGE_BOTTOM_RIGHT: Align to the bottom right.
 *
 * The possible alignments you can combine.
 */
typedef enum {
  PGM_IMAGE_LEFT          = (1 << 0),
  PGM_IMAGE_CENTER        = (1 << 1),
  PGM_IMAGE_RIGHT         = (1 << 2),
  PGM_IMAGE_TOP           = (1 << 3),
  PGM_IMAGE_BOTTOM        = (1 << 4),
  PGM_IMAGE_TOP_LEFT      = (PGM_IMAGE_TOP | PGM_IMAGE_LEFT),
  PGM_IMAGE_TOP_CENTER    = (PGM_IMAGE_TOP | PGM_IMAGE_CENTER),
  PGM_IMAGE_TOP_RIGHT     = (PGM_IMAGE_TOP | PGM_IMAGE_RIGHT),
  PGM_IMAGE_BOTTOM_LEFT   = (PGM_IMAGE_BOTTOM | PGM_IMAGE_LEFT),
  PGM_IMAGE_BOTTOM_CENTER = (PGM_IMAGE_BOTTOM | PGM_IMAGE_CENTER),
  PGM_IMAGE_BOTTOM_RIGHT  = (PGM_IMAGE_BOTTOM | PGM_IMAGE_RIGHT)
} PgmImageAlignment;

/**
 * PgmImageLayoutType:
 * @PGM_IMAGE_FILLED: <inlinegraphic format="PNG" fileref="filled_layout.png">
 * </inlinegraphic>Scales the stored image to the whole drawable size ignoring
 * the pixel-aspect-ratio.
 * @PGM_IMAGE_SCALED: <inlinegraphic format="PNG"
 * fileref="scaled_layout.png"></inlinegraphic>Scales the stored image to
 * adapt its size to the drawable one keeping the pixel-aspect-ratio.
 * @PGM_IMAGE_ZOOMED: <inlinegraphic format="PNG" fileref="zoomed_layout.png">
 * </inlinegraphic>Scales the stored image to the whole drawable size keeping
 * the pixel-aspect-ratio. It crops the border of the stored image.
 * @PGM_IMAGE_CENTERED: Centers the stored image in the middle of the
 * drawable keeping the pixel-aspect-ratio. It crops the image if its size is
 * bigger than the drawable size.
 * @PGM_IMAGE_TILED: Repeats the stored image along the drawable.
 *
 * The different layout types.
 */
typedef enum {
  PGM_IMAGE_FILLED   = 0,
  PGM_IMAGE_SCALED   = 1,
  PGM_IMAGE_ZOOMED   = 2,
  PGM_IMAGE_CENTERED = 3,
  PGM_IMAGE_TILED    = 4
} PgmImageLayoutType;

/**
 * PgmImageInterpType:
 * @PGM_IMAGE_NEAREST: The nearest neighbour sampling is the fastest and
 * lowest quality mode.
 * @PGM_IMAGE_BILINEAR: The bilinear sampling is the slowest and highest
 * quality mode.
 *
 * The different image interpolation types.
 */
typedef enum {
  PGM_IMAGE_NEAREST,
  PGM_IMAGE_BILINEAR
} PgmImageInterpType;

/**
 * PgmImageWrapping:
 * @PGM_IMAGE_CLAMP: The image content is displayed in the mapping range
 * [0.0, 1.0] and clamped to the last pixel outside it.
 * @PGM_IMAGE_TRANSPARENT: The image content is displayed in the mapping range
 * [0.0, 1.0] and discarded outside it. Can be used in combination with the
 * #PGM_IMAGE_BILINEAR filtering to antialias the edges of images.
 * @PGM_IMAGE_REPEAT: The image content is displayed in the mapping range
 * [0.0, 1.0] and repeated infinitely outside it.
 *
 * The different image wrapping types.
 */
typedef enum {
  PGM_IMAGE_CLAMP,
  PGM_IMAGE_TRANSPARENT,
  PGM_IMAGE_REPEAT
} PgmImageWrapping;

/**
 * PgmImagePixelFormat:
 * @PGM_IMAGE_RGB: 24 bits RGB (3 bytes, red 8 @ 16, green 8 @ 8, blue 8 @ 0).
 * @PGM_IMAGE_BGR: 24 bits BGR (3 bytes, blue 8 @ 16, green 8 @ 8, red 8 @ 0).
 * @PGM_IMAGE_RGBA: 32 bits RGBA (4 bytes, red 8 @ 24, green 8 @ 16, blue 8 @ 8,
 * alpha 8 @ 0).
 * @PGM_IMAGE_BGRA: 32 bits BGRA (4 bytes, blue 8 @ 24, green 8 @ 16, red 8 @ 8,
 * alpha 8 @ 0).
 * @PGM_IMAGE_I420: 12 bits YUV (8 bits Y plane followed by 8 bits quarter
 * size U/V planes).
 * @PGM_IMAGE_YV12: 12 bits YUV (8 bits Y plane followed by 8 bits quarter
 * size V/U planes).
 * @PGM_IMAGE_UYVY: 16 bits YUV (4 bytes / 2 pixels, macropixel contains
 * YCbYCr [31:0]).
 * @PGM_IMAGE_YUYV: 16 bits YUV (4 bytes / 2 pixels, macropixel contains
 * CbYCrY [31:0], duplicate of YUY2).
 *
 * The different image pixel formats.
 */
typedef enum {
  PGM_IMAGE_RGB  = (1 << 0),
  PGM_IMAGE_BGR  = (1 << 1),
  PGM_IMAGE_RGBA = (1 << 2),
  PGM_IMAGE_BGRA = (1 << 3),
  PGM_IMAGE_I420 = (1 << 4),
  PGM_IMAGE_YV12 = (1 << 5),
  PGM_IMAGE_UYVY = (1 << 6),
  PGM_IMAGE_YUYV = (1 << 7)
} PgmImagePixelFormat;

/**
 * PgmImageFile:
 * @pixbuf: the #GdkPixbuf object.
 * @filename: the file name.
 * @width: the cached pixbuf width.
 * @height: the cached pixbuf height.
 *
 * The #PgmImageFile structure.
 */
struct _PgmImageFile {
  GdkPixbuf *pixbuf;
  gchar     *filename;
  guint      width;
  guint      height;
};

/**
 * PgmImageBuffer:
 * @buffer: the buffer pointer.
 * @format: the buffer format.
 * @width: the buffer width.
 * @height: the buffer height.
 * @stride: the buffer stride.
 * @size: the buffer size.
 *
 * The #PgmImageBuffer structure.
 */
struct _PgmImageBuffer {
  guint8              *buffer;
  PgmImagePixelFormat  format;
  guint                width;
  guint                height;
  guint                stride;
  guint                size;
};

/**
 * PgmImageGstBuffer:
 * @gst_buffer: the #GstBuffer object.
 * @format: the #GstBuffer format.
 * @width: the #GstBuffer width.
 * @height: the #GstBuffer height.
 * @stride: the #GstBuffer stride.
 *
 * The #PgmImageGstBuffer structure.
 */
struct _PgmImageGstBuffer {
  GstBuffer           *gst_buffer;
  PgmImagePixelFormat  format;
  guint                width;
  guint                height;
  guint                stride;
};

/**
 * PgmImagePixbuf:
 * @pixbuf: the #GdkPixbuf object.
 *
 * The #PgmImagePixbuf structure.
 */
struct _PgmImagePixbuf {
  GdkPixbuf *pixbuf;
};

/**
 * PgmImageSystemBuffer:
 * @system_buffer: the system buffer pointer.
 * @format: the system buffer format.
 * @width: the system buffer width.
 * @height: the system buffer height.
 *
 * The #PgmImageSystemBuffer structure.
 */
struct _PgmImageSystemBuffer {
  gconstpointer       system_buffer;
  PgmImagePixelFormat format;
  guint               width;
  guint               height;
};

/**
 * PgmImageStorageType:
 * @PGM_IMAGE_EMPTY: no image stored.
 * @PGM_IMAGE_FILE: a #PgmImageFile is stored.
 * @PGM_IMAGE_BUFFER: a #PgmImageBuffer is stored.
 * @PGM_IMAGE_GST_BUFFER: a #PgmImageGstBuffer is stored.
 * @PGM_IMAGE_PIXBUF: a #PgmImagePixbuf is stored.
 * @PGM_IMAGE_IMAGE: a #PgmImage is stored.
 * @PGM_IMAGE_SYSTEM_BUFFER: a #PgmImageSystemBuffer is stored.
 *
 * The different storage type.
 */
typedef enum {
  PGM_IMAGE_EMPTY,
  PGM_IMAGE_FILE,
  PGM_IMAGE_BUFFER,
  PGM_IMAGE_GST_BUFFER,
  PGM_IMAGE_PIXBUF,
  PGM_IMAGE_IMAGE,
  PGM_IMAGE_SYSTEM_BUFFER
} PgmImageStorageType;

/**
 * PgmImageData:
 * @buffer: the #PgmImageFile structure.
 * @buffer: the #PgmImageBuffer structure.
 * @gst_buffer: the #PgmImageGstBuffer structure.
 * @pixbuf: the #PgmImagePixbuf structure.
 * @system_buffer: the #PgmImageSystemBuffer structure.
 *
 * The stored data depending on the storage type.
 */
union _PgmImageData {
  PgmImageFile         file;
  PgmImageBuffer       buffer;
  PgmImageGstBuffer    gst_buffer;
  PgmImagePixbuf       pixbuf;
  PgmImageSystemBuffer system_buffer;
};

/**
 * PgmImage:
 * @master: the image #PgmImage master.
 * @slaves: the image list of #PgmImage slaves.
 * @mapping_matrix: the image mapping matrix.
 * @storage_type: the currently stored image type.
 * @data: the image data depending on the type.
 * @layout: the image layout.
 * @align: the image alignment.
 * @interp: the image interpolation.
 * @wrap_s: the image s wrapping.
 * @wrap_t: the image t wrapping.
 * @par_n: the image pixel-aspect-ratio numerator.
 * @par_d: the image pixel-aspect-ratio denominator.
 * @border_width: the image border width.
 * @border_inner_r: the image border outer red color.
 * @border_inner_g: the image border outer green color.
 * @border_inner_b: the image border outer blue color.
 * @border_inner_a: the image border outer alpha color.
 * @border_outer_r: the image border outer red color.
 * @border_outer_g: the image border outer green color.
 * @border_outer_b: the image border outer blue color.
 * @border_outer_a: the image border outer alpha color.
 *
 * The #PgmImage structure.
 */
struct _PgmImage {
  /*< private >*/
  PgmDrawable parent;

  /*< public >*/ /* with LOCK */
  /* Slavery handling */
  PgmImage *master;
  GList    *slaves;

  PgmMat4x4 *mapping_matrix;

  /* Image storage */
  PgmImageStorageType storage_type;
  PgmImageData data;

  /* Image displaying properties */
  PgmImageLayoutType  layout;
  PgmImageAlignment   align;
  PgmImageInterpType  interp;
  PgmImageWrapping    wrap_s;
  PgmImageWrapping    wrap_t;
  guint               par_n, par_d;

  /* Image border */
  gfloat border_width;
  guchar border_inner_r;
  guchar border_inner_g;
  guchar border_inner_b;
  guchar border_inner_a;
  guchar border_outer_r;
  guchar border_outer_g;
  guchar border_outer_b;
  guchar border_outer_a;

  /*< private >*/
  /* Current loader id */
  guint16 loader_id;

  /* Management bit mask */
  guint8 flags;
};

struct _PgmImageClass {
  PgmDrawableClass parent_class;

  /*< private >*/
  GThreadPool *pixbuf_loader_pool;

   /* Signals */

  void (*file_loaded) (PgmImage *image);

  void (*cloned)      (PgmImage *image,
                       PgmImage *clone,
                       guint n_clones);

  void (*un_cloned)   (PgmImage *image,
                       guint n_clones);
};

/* Public functions */

GType        pgm_image_get_type               (void);

PgmDrawable *pgm_image_new                    (void);

PgmDrawable *pgm_image_new_from_file          (const gchar *filename,
                                               guint max_size);

PgmDrawable *pgm_image_new_from_pixbuf        (GdkPixbuf *pixbuf);

PgmDrawable *pgm_image_new_from_buffer        (PgmImagePixelFormat format,
                                               guint width,
                                               guint height,
                                               guint stride,
                                               guint size,
                                               gconstpointer data);

PgmDrawable *pgm_image_new_from_image         (PgmImage *src_image);

PgmError     pgm_image_set_from_file          (PgmImage *image,
                                               const gchar *filename,
                                               guint max_size);

PgmError     pgm_image_set_from_pixbuf        (PgmImage *image,
                                               GdkPixbuf *pixbuf);

PgmError     pgm_image_set_from_buffer        (PgmImage *image,
                                               PgmImagePixelFormat format,
                                               guint width,
                                               guint height,
                                               guint stride,
                                               guint size,
                                               gconstpointer data);

PgmError     pgm_image_set_from_gst_buffer    (PgmImage *image,
                                               PgmImagePixelFormat format,
                                               guint width,
                                               guint height,
                                               guint stride,
                                               GstBuffer *buffer);

PgmError     pgm_image_set_from_system_buffer (PgmImage *image,
                                               PgmImagePixelFormat format,
                                               guint width,
                                               guint height,
                                               gconstpointer system_buffer);

PgmError     pgm_image_set_from_image         (PgmImage *image,
                                               PgmImage *src_image);

PgmError     pgm_image_to_pixbuf              (PgmImage *image,
                                               GdkPixbuf **pixbuf);

PgmError     pgm_image_clear                  (PgmImage *image);

PgmError     pgm_image_get_storage_type       (PgmImage *image,
                                               PgmImageStorageType *storage);

PgmError     pgm_image_system_buffer_lock     (PgmImage *image);
PgmError     pgm_image_system_buffer_unlock   (PgmImage *image);

PgmError     pgm_image_set_mapping_matrix     (PgmImage *image,
                                               PgmMat4x4 *mapping_matrix);
PgmError     pgm_image_get_mapping_matrix     (PgmImage *image,
                                               PgmMat4x4 **mapping_matrix);

PgmError     pgm_image_set_alignment          (PgmImage *image,
                                               PgmImageAlignment align);
PgmError     pgm_image_get_alignment          (PgmImage *image,
                                               PgmImageAlignment *align);

PgmError     pgm_image_set_layout             (PgmImage *image,
                                               PgmImageLayoutType layout);
PgmError     pgm_image_get_layout             (PgmImage *image,
                                               PgmImageLayoutType *layout);

PgmError     pgm_image_set_interp             (PgmImage *image,
                                               PgmImageInterpType interp);
PgmError     pgm_image_get_interp             (PgmImage *image,
                                               PgmImageInterpType *interp);

PgmError     pgm_image_set_wrapping           (PgmImage *image,
                                               PgmImageWrapping wrap_s,
                                               PgmImageWrapping wrap_t);
PgmError     pgm_image_get_wrapping           (PgmImage *image,
                                               PgmImageWrapping *wrap_s,
                                               PgmImageWrapping *wrap_t);

PgmError     pgm_image_set_aspect_ratio       (PgmImage *image,
                                               guint numerator,
                                               guint denominator);
PgmError     pgm_image_get_aspect_ratio       (PgmImage *image,
                                               guint *numerator,
                                               guint *denominator);

PgmError     pgm_image_set_border_width       (PgmImage *image,
                                               gfloat width);
PgmError     pgm_image_get_border_width       (PgmImage *image,
                                               gfloat *width);

PgmError     pgm_image_set_border_inner_color (PgmImage *image,
                                               guchar red,
                                               guchar green,
                                               guchar blue,
                                               guchar alpha);
PgmError     pgm_image_get_border_inner_color (PgmImage *image,
                                               guchar *red,
                                               guchar *green,
                                               guchar *blue,
                                               guchar *alpha);

PgmError     pgm_image_set_border_outer_color (PgmImage *image,
                                               guchar red,
                                               guchar green,
                                               guchar blue,
                                               guchar alpha);
PgmError     pgm_image_get_border_outer_color (PgmImage *image,
                                               guchar *red,
                                               guchar *green,
                                               guchar *blue,
                                               guchar *alpha);

PgmError     pgm_image_from_drawable          (PgmImage *image,
                                               gint *x_image,
                                               gint *y_image,
                                               gfloat x_drawable,
                                               gfloat y_drawable);

PgmError     pgm_image_to_drawable            (PgmImage *image,
                                               gfloat *x_drawable,
                                               gfloat *y_drawable,
                                               gint x_image,
                                               gint y_image);

/* Protected functions */

PgmError _pgm_image_stored_from_file_free (PgmImage *image);

PgmError _pgm_image_stored_from_file_load (PgmImage *image);

G_END_DECLS

#endif /* __PGM_IMAGE_H__ */
