diff --git a/pipi/Makefile.am b/pipi/Makefile.am
index adc3279..d29d426 100644
--- a/pipi/Makefile.am
+++ b/pipi/Makefile.am
@@ -21,6 +21,7 @@ libpipi_la_SOURCES = \
 	pipi_template.h \
 	context.c \
 	pixels.c \
+	tiles.c \
 	codec.c \
 	stock.c \
 	colorstring.c \
diff --git a/pipi/pipi.h b/pipi/pipi.h
index f2b218f..69d4356 100644
--- a/pipi/pipi.h
+++ b/pipi/pipi.h
@@ -101,6 +101,9 @@ typedef struct
 }
 pipi_pixels_t;
 
+/* pipi_tile_t: the internal tile type */
+typedef struct pipi_tile pipi_tile_t;
+
 /* pipi_image_t: the main image type */
 typedef struct pipi_image pipi_image_t;
 
@@ -127,6 +130,11 @@ __extern void pipi_destroy_context(pipi_context_t *);
 __extern pipi_command_t const *pipi_get_command_list(void);
 __extern int pipi_command(pipi_context_t *, char const *, ...);
 
+__extern pipi_tile_t *pipi_get_tile(pipi_image_t *, int, int, int,
+                                    pipi_format_t, int);
+__extern void pipi_release_tile(pipi_image_t *, pipi_tile_t *);
+__extern pipi_tile_t *pipi_create_tile(pipi_format_t, int);
+
 __extern pipi_image_t *pipi_load(char const *);
 __extern pipi_image_t *pipi_load_stock(char const *);
 __extern pipi_image_t *pipi_new(int, int);
diff --git a/pipi/pipi_internals.h b/pipi/pipi_internals.h
index 3bbb76a..ab7d47c 100644
--- a/pipi/pipi_internals.h
+++ b/pipi/pipi_internals.h
@@ -35,6 +35,22 @@ struct pipi_histogram
     unsigned int y[256];
 };
 
+#ifdef USE_TILES
+#define TILE_SIZE 128
+
+struct pipi_tile
+{
+    int x, y;
+    int zoom;
+
+    int refcount;
+
+    pipi_format_t fmt;
+    int plane;
+    union { uint8_t *u8; float *f; double *d; } data;
+    union { uint8_t u8[1]; float f[1]; double d[1]; } align;
+};
+#endif /* USE_TILES */
 
 /* pipi_image_t: the image structure. This is probably going to be the most
  * complex structure in the library, but right now it only has fairly normal
@@ -43,6 +59,11 @@ struct pipi_image
 {
     int w, h, pitch;
 
+#ifdef USE_TILES
+    pipi_tile_t **tiles;
+    int ntiles;
+#endif /* USE_TILES */
+
     /* A list of internal image flags.
      *  wrap: should filters wrap around at edges?
      *  u8: are the image samples still 8-bit per channel? */
diff --git a/pipi/tiles.c b/pipi/tiles.c
new file mode 100644
index 0000000..315add7
--- /dev/null
+++ b/pipi/tiles.c
@@ -0,0 +1,112 @@
+/*
+ *  libpipi       Pathetic image processing interface library
+ *  Copyright (c) 2004-2008 Sam Hocevar <sam@zoy.org>
+ *                All Rights Reserved
+ *
+ *  $Id$
+ *
+ *  This library is free software. It comes without any warranty, to
+ *  the extent permitted by applicable law. You can redistribute it
+ *  and/or modify it under the terms of the Do What The Fuck You Want
+ *  To Public License, Version 2, as published by Sam Hocevar. See
+ *  http://sam.zoy.org/wtfpl/COPYING for more details.
+ */
+
+/*
+ * tiles.c: the tiles system
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "pipi.h"
+#include "pipi_internals.h"
+
+#ifdef USE_TILES
+pipi_tile_t *pipi_get_tile(pipi_image_t *img, int x, int y, int zoom,
+                           pipi_format_t fmt, int plane)
+{
+    pipi_tile_t * ret;
+    int i;
+
+    /* If the tile already exists, return it. */
+    for(i = 0; i < img->ntiles; i++)
+    {
+        if(img->tiles[i]->x == x && img->tiles[i]->y == y
+            && img->tiles[i]->fmt == fmt
+            && img->tiles[i]->zoom == zoom
+            && img->tiles[i]->plane == plane)
+        {
+            img->tiles[i]->refcount++;
+            return img->tiles[i];
+        }
+    }
+
+    /* Create a tile. When the image provides a tile creation function,
+     * we should use it. */
+    ret = pipi_create_tile(fmt, plane);
+    ret->x = x;
+    ret->y = y;
+    ret->refcount = 1;
+
+    /* Insert tile and return it. */
+    img->ntiles++;
+    img->tiles = realloc(img->tiles, img->ntiles * sizeof(pipi_tile_t *));
+    img->tiles[img->ntiles - 1] = ret;
+
+    return ret;
+}
+
+void pipi_release_tile(pipi_image_t *img, pipi_tile_t *tile)
+{
+    int i;
+
+    for(i = 0; i < img->ntiles; i++)
+    {
+        if(img->tiles[i] == tile)
+        {
+            img->tiles[i]->refcount--;
+            if(img->tiles[i]->refcount <= 0)
+            {
+                free(img->tiles[i]);
+                img->tiles[i] = img->tiles[img->ntiles - 1];
+                img->ntiles--;
+            }
+            return;
+        }
+    }
+}
+
+pipi_tile_t *pipi_create_tile(pipi_format_t fmt, int plane)
+{
+    pipi_tile_t * ret;
+    size_t bytes;
+
+    switch(fmt)
+    {
+        case PIPI_PIXELS_RGBA_C:
+        case PIPI_PIXELS_BGR_C:
+            bytes = sizeof(uint8_t) * TILE_SIZE * TILE_SIZE;
+            break;
+        case PIPI_PIXELS_RGBA_F:
+        case PIPI_PIXELS_Y_F:
+            bytes = sizeof(float) * TILE_SIZE * TILE_SIZE;
+            break;
+        default:
+            bytes = 0;
+            break;
+    }
+
+    ret = malloc(sizeof(pipi_tile_t) + bytes);
+    ret->fmt = fmt;
+    ret->plane = plane;
+    ret->data.u8 = ret->align.u8;
+
+    return ret;
+}
+
+#endif /* USE_TILES */
+