diff --git includes/file.inc includes/file.inc
index 80b20f7..cbcc899 100644
--- includes/file.inc
+++ includes/file.inc
@@ -77,6 +77,23 @@ define('FILE_EXISTS_ERROR', 2);
 define('FILE_STATUS_PERMANENT', 1);
 
 /**
+ * Remove a possible leading file directory path from the given path.
+ *
+ * @param $path
+ *   Path to a file that may be in Drupal's files directory.
+ * @return
+ *   String with Drupal's files directory removed from it.
+ */
+function _file_strip_files_directory($path) {
+  // Strip file_directory_path from $path. We only include relative paths in
+  // URLs.
+  if (strpos($path, file_directory_path() . '/') === 0) {
+    $path = trim(substr($path, strlen(file_directory_path())), '\\/');
+  }
+  return $path;
+}
+
+/**
  * Create the download path to a file.
  *
  * @param $path A string containing the path of the file to generate URL for.
@@ -85,9 +102,7 @@ define('FILE_STATUS_PERMANENT', 1);
 function file_create_url($path) {
   // Strip file_directory_path from $path. We only include relative paths in
   // URLs.
-  if (strpos($path, file_directory_path() . '/') === 0) {
-    $path = trim(substr($path, strlen(file_directory_path())), '\\/');
-  }
+  $path = _file_strip_files_directory($path);
   switch (variable_get('file_downloads', FILE_DOWNLOADS_PUBLIC)) {
     case FILE_DOWNLOADS_PUBLIC:
       return $GLOBALS['base_url'] . '/' . file_directory_path() . '/' . str_replace('\\', '/', $path);
diff --git modules/image/image.api.php modules/image/image.api.php
new file mode 100644
index 0000000..bec935b
--- /dev/null
+++ modules/image/image.api.php
@@ -0,0 +1,111 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * Hooks related to image styles and effects.
+ */
+
+/**
+ * @addtogroup hooks
+ * @{
+ */
+
+/**
+ * Define image effects.
+ *
+ * This hook enables modules to define image manipulation effects for use with
+ * an image style.
+ *
+ * @return
+ *   An array of image effects. Each effect has a key corresponding to the
+ *   machine-readable effect name. The effect is an associative array that may
+ *   contain the following key-value pairs:
+ *
+ *   - "name": Required. The human-readable name of the effect.
+ *   - "description": Required. A brief description of the effect.
+ *   - "function": Required. The function to call to perform this effect.
+ *   - "form": The name of a function that will return a $form array providing
+ *     a configuration form for this effect.
+ *   - "summary": The name of a theme function that will output a summary of
+ *     this effects configuration.
+ *   - "file": The name of an include file that the effect can be found in
+ *     relative to the implementing module's path.
+ *   - "file path": The directory path in which the include file resides. If
+ *     left empty this will default to the implementing module's directory.
+ */
+function hook_image_effects() {
+  $effects = array();
+
+  $effects['mymodule_resize'] = array(
+    'name' => t('Resize'),
+    'descritpion' => t('Resize an image to an exact set of dimensions, ignoring aspect ratio.'),
+    'function' => 'mymodule_resize_image',
+    'form' => 'mymodule_resize_form',
+    'summary' => 'mymodule_resize_summary',
+    'file' => 'mymodule.effects.inc',
+  );
+
+  return $effects;
+}
+
+/**
+ * Respond to style updating.
+ *
+ * This hook enables modules to update settings that might be affected by
+ * changes to an image. For example, updating a module specific variable to
+ * reflect a change in the style's name.
+ *
+ * @param $style
+ *   The style being updated.
+ * @return
+ *   None.
+ */
+function hook_image_style_save($style) {
+  // If a module defines an image style and that style is renamed by the user
+  // the module should update any references to that style.
+  if (isset($style['old_name']) && $style['old_name'] == variable_get('mymodule_image_style', '')) {
+    variable_set('mymodule_image_style', $style['name']);
+  }
+}
+
+/**
+ * Respond to image style deletion.
+ *
+ * This hook enables modules to update settings when a style is being deleted.
+ * If a style is deleted, a replacement name may be specified in $style['name']
+ * and the style being deleted will be specified in $style['old_name'].
+ *
+ * @param $style
+ *   The style being deleted.
+ * @return
+ *   None.
+ */
+function hook_image_style_delete($style) {
+  // Administrators can choose an optional replacement style when deleting.
+  // Update the modules style variable accordingly.
+  if (isset($style['old_name']) && $style['old_name'] == variable_get('mymodule_image_style', '')) {
+    variable_set('mymodule_image_style', $style['name']);
+  }
+}
+
+/**
+ * Respond to image style flushing.
+ *
+ * This hook enables modules to take effect when a style is being flushed (all
+ * images are being deleted from the server and regenerated). Any
+ * module-specific caches that contain information related to the style should
+ * be cleared using this hook. This hook is called whenever a style is updated,
+ * deleted, any effect associated with the style is update or deleted, or when
+ * the user selects the style flush option.
+ *
+ * @param $style
+ *   The image style being flushed.
+ */
+function hook_image_style_flush($style) {
+  // Empty cached data that contains information about the style.
+  cache_clear_all('*', 'cache_mymodule', TRUE);
+}
+ /**
+  * @} End of "addtogroup hooks".
+  */
diff --git modules/image/image.effects.inc modules/image/image.effects.inc
new file mode 100644
index 0000000..4cafcd2
--- /dev/null
+++ modules/image/image.effects.inc
@@ -0,0 +1,289 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * Functions needed to execute image effects provided by Image module.
+ */
+
+/**
+ * Implement hook_image_effects().
+ */
+function image_image_effects() {
+  $effects = array(
+    'image_resize' => array(
+      'name' => t('Resize'),
+      'help' => t('Resizing will make images an exact set of dimensions. This may cause images to be stretched or shrunk disproportionately.'),
+      'function' => 'image_resize_effect',
+      'form' => 'image_resize_form',
+      'summary' => 'image_resize_summary',
+    ),
+    'image_scale' => array(
+      'name' => t('Scale'),
+      'help' => t('Scaling will maintain the aspect-ratio of the original image. If only a single dimension is specified, the other dimension will be calculated.'),
+      'function' => 'image_scale_effect',
+      'form' => 'image_scale_form',
+      'summary' => 'image_scale_summary',
+    ),
+    'image_scale_and_crop' => array(
+      'name' => t('Scale and Crop'),
+      'help' => t('Scale and crop will maintain the aspect-ratio of the original image, then crop the larger dimension. This is most useful for creating perfectly square thumbnails without stretching the image.'),
+      'function' => 'image_scale_and_crop_effect',
+      'form' => 'image_resize_form',
+      'summary' => 'image_resize_summary',
+    ),
+    'image_crop' => array(
+      'name' => t('Crop'),
+      'help' => t('Cropping will remove portions of an image to make it the specified dimensions.'),
+      'function' => 'image_crop_effect',
+      'form' => 'image_crop_form',
+      'summary' => 'image_crop_summary',
+    ),
+    'image_desaturate' => array(
+      'name' => t('Desaturate'),
+      'help' => t('Desaturate converts an image to grayscale.'),
+      'function' => 'image_desaturate_effect',
+    ),
+    'image_rotate' => array(
+      'name' => t('Rotate'),
+      'help' => t('Rotating an image may cause the dimensions of an image to increase to fit the diagonal.'),
+      'function' => 'image_rotate_effect',
+      'form' => 'image_rotate_form',
+      'summary' => 'image_rotate_summary',
+    ),
+  );
+
+  return $effects;
+}
+
+/**
+ * Menu callback; Given a style and image path, generate a derivative.
+ *
+ * This menu callback is always served after checking a token to prevent
+ * generation of unnecessary images. After generating an image transfer it to
+ * the requesting agent via file_transfer().
+ */
+function image_style_generate() {
+  $args = func_get_args();
+  $style = array_shift($args);
+  $style_name = $style['name'];
+  $path = implode('/', $args);
+
+  $source = file_create_path($path);
+  $path_md5 = md5($path);
+  $destination = image_style_path($style['name'], $path);
+
+  // Check that it's a defined style and that access was granted by
+  // image_style_generate_url().
+  if (!$style || !cache_get('access:' . $style_name . ':' . $path_md5, 'cache_image')) {
+    drupal_access_denied();
+    exit();
+  }
+
+  // Don't start generating the image if it is already in progress.
+  $cid = 'generate:' . $style_name . ':' . $path_md5;
+  if (cache_get($cid, 'cache_image')) {
+    print t('Image generation in progress, please try again shortly.');
+    exit();
+  }
+
+  // If the image has already been generated then send it.
+  if ($image = image_load($destination)) {
+    file_transfer($image->source, array('Content-type: ' . $image->info['mime_type'], 'Content-length: ' . $image->info['file_size']));
+  }
+
+  // Set a cache entry designating this image as being in-process.
+  cache_set($cid, $destination, 'cache_image');
+
+  // Try to generate the image.
+  if (image_style_create_derivative($style, $source, $destination)) {
+    $image = image_load($destination);
+    cache_clear_all($cid, 'cache_image');
+    file_transfer($image->source, array('Content-type: ' . $image->info['mime_type'], 'Content-length: ' . $image->info['file_size']));
+  }
+  else {
+    cache_clear_all($cid, 'cache_image');
+    watchdog('image', 'Unable to generate the derived image located at %path.', $destination);
+    print t('Error generating image.');
+    exit();
+  }
+}
+
+/**
+ * Access callback for image/generate.
+ *
+ * Ensure this request is made by Drupal by checking for a valid site token.
+ */
+function image_style_generate_access() {
+  $args = func_get_args();
+  $style_name = array_shift($args);
+  $path = implode('/', $args);
+
+  return isset($_GET['token']) && drupal_valid_token($_GET['token'], $path);
+}
+
+/**
+ * Implements the default image resize effect.
+ *
+ * @param $image
+ *   An image object.
+ * @param $data
+ *   An array of attributes to use when performing the resize effect with the
+ *   following key value pairs.
+ *   - "width": An integer representing the desired width in pixels.
+ *   - "height": An integer representing the desired height in pixels.
+ * @return
+ *   TRUE on success. FALSE on failure to resize image.
+ * @see image_resize()
+ */
+function image_resize_effect(&$image, $data) {
+  if (!image_resize($image, $data['width'], $data['height'])) {
+    watchdog('image', 'Image resize failed. image: %image, data: %data.', array('%image' => $image, '%data' => print_r($data, TRUE)), WATCHDOG_ERROR);
+    return FALSE;
+  }
+  return TRUE;
+}
+
+/**
+ * Implements the default image scale effect.
+ *
+ * @param $image
+ *   An image object.
+ * @param $data
+ *   An array of attributes to use when performing the scale effect with the
+ *   following key value pairs.
+ *   - "width": An integer representing the desired width in pixels.
+ *   - "height": An integer representing the desired height in pixels.
+ *   - "upscale": A Boolean indicating that the image should be upscalled if
+ *     the dimensions are larger than the original image.
+ * @return
+ *   TRUE on success. FALSE on failure to scale image.
+ * @see image_scale()
+ */
+function image_scale_effect(&$image, $data) {
+  // Set impossibly large values if the width and height aren't set.
+  $data['width'] = $data['width'] ? $data['width'] : PHP_INT_MAX;
+  $data['height'] = $data['height'] ? $data['height'] : PHP_INT_MAX;
+  if (!image_scale($image, $data['width'], $data['height'], $data['upscale'])) {
+    watchdog('image', 'Image scale failed. image: %image, data: %data.', array('%image' => $image, '%data' => print_r($data, TRUE)), WATCHDOG_ERROR);
+    return FALSE;
+  }
+  return TRUE;
+}
+
+/**
+ * Implements the default image crop effect.
+ *
+ * @param $image
+ *   An image object.
+ * @param $data
+ *   An array of attributes to use when performing the crop effect with the
+ *   following key value pairs.
+ *   - "xoffset": An integer representing the desired x-axis offset in pixels.
+ *   - "yoffset": An integer representing the desired x-axis offset in pixels.
+ *   - "width": An integer representing the desired width in pixels.
+ *   - "height": An integer representing the desired height in pixels.
+ * @return
+ *   TRUE on success. FALSE on failure to crop image.
+ * @see image_crop()
+ */
+function image_crop_effect(&$image, $data) {
+  list($x, $y) = explode('-', $data['anchor']);
+  $x = image_filter_keyword($x, $image->info['width'], $data['width']);
+  $y = image_filter_keyword($y, $image->info['height'], $data['height']);
+  if (!image_crop($image, $x, $y, $data['width'], $data['height'])) {
+    watchdog('image', 'Image crop failed. image: %image, data: %data.', array('%image' => $image, '%data' => print_r($data, TRUE)), WATCHDOG_ERROR);
+    return FALSE;
+  }
+  return TRUE;
+}
+
+/**
+ * Implements the default image scale and crop effect.
+ *
+ * @param $image
+ *   An image object.
+ * @param $data
+ *   An array of attributes to use when performing the scale and crop effect
+ *   with the following key value pairs.
+ *   - "width": An integer representing the desired width in pixels.
+ *   - "height": An integer representing the desired height in pixels.
+ * @return
+ *   TRUE on success. FALSE on failure to scale and crop image.
+ * @see image_scale_and_crop()
+ */
+function image_scale_and_crop_effect(&$image, $data) {
+  if (!image_scale_and_crop($image, $data['width'], $data['height'])) {
+    watchdog('image', t('Image scale and crop failed. image: %image, data: %data.', array('%image' => $image, '%data' => print_r($data, TRUE))), WATCHDOG_ERROR);
+    return FALSE;
+  }
+  return TRUE;
+}
+
+/**
+ * Implements the default image desaturate effect.
+ *
+ * @param $image
+ *   An image object.
+ * @param $data
+ *   An array of attributes to use when performing the desaturate effect.
+ * @return
+ *   TRUE on success. FALSE on failure to desaturate image.
+ * @see image_desaturate()
+ */
+function image_desaturate_effect(&$image, $data) {
+  if (!image_desaturate($image)) {
+    watchdog('image', 'Image desaturate failed. image: %image, data: %data.', array('%image' => $image, '%data' => print_r($data, TRUE)), WATCHDOG_ERROR);
+    return FALSE;
+  }
+  return TRUE;
+}
+
+/**
+ * Implements the default image rotate effect.
+ *
+ * @param $image
+ *   An image object.
+ * @param $data
+ *   with the following key value pairs.
+ *   - "degrees": The number of (clockwise) degrees to rotate the image.
+ *   - "random": A Boolean indicating that a random rotation angle should be
+ *     used for this image. The angle specified in "degrees" is used as a
+ *     positive and negative maximum.
+ *   - "bgcolor": The background color to use for exposed areas of the image.
+ *     Use web-style hex colors (#FFFFFF for white, #000000 for black). Leave
+ *     blank for transparency on image types that support it.
+ * @return
+ *   TRUE on success. FALSE on failure to rotate image.
+ * @see image_rotate().
+ */
+function image_rotate_effect(&$image, $data) {
+  // Set sane default values.
+  $data['degrees'] = $data['degrees'] ? $data['degrees'] : 0;
+  $data['random'] = $data['random'] ? $data['random'] : FALSE;
+
+  // Convert short #FFF syntax to full #FFFFFF syntax.
+  if (strlen($data['bgcolor']) == 4) {
+    $c = $data['bgcolor'];
+    $data['bgcolor'] = $c[0] . $c[1] . $c[1] . $c[2] . $c[2] . $c[3] . $c[3];
+  }
+
+  // Convert #FFFFFF syntax to hexadecimal colors.
+  if ($data['bgcolor'] != '') {
+    $data['bgcolor'] = hexdec(str_replace('#', '0x', $data['bgcolor']));
+  }
+  else {
+    $data['bgcolor'] = NULL;
+  }
+
+  if (!empty($data['random'])) {
+    $degrees = abs((float)$data['degrees']);
+    $data['degrees'] = rand(-1 * $degrees, $degrees);
+  }
+
+  if (!image_rotate($image, $data['degrees'], $data['bgcolor'])) {
+    watchdog('image', t('Image rotate failed. image: %image, data: %data.', array('%image' => $image, '%data' => print_r($data, TRUE))), WATCHDOG_ERROR);
+    return FALSE;
+  }
+  return TRUE;
+}
diff --git modules/image/image.info modules/image/image.info
new file mode 100644
index 0000000..62dced5
--- /dev/null
+++ modules/image/image.info
@@ -0,0 +1,11 @@
+; $Id$
+
+name = Image
+description = Provides image manipulation tools.
+package = Core
+version = VERSION
+core = 7.x
+files[] = image.module
+files[] = image.effects.inc
+files[] = image.install
+files[] = image.test
diff --git modules/image/image.install modules/image/image.install
new file mode 100644
index 0000000..80f69fe
--- /dev/null
+++ modules/image/image.install
@@ -0,0 +1,137 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * Install, update and uninstall functions for the image module.
+ */
+
+/**
+ * Implement hook_install().
+ */
+function image_install() {
+  drupal_install_schema('image');
+
+  // Create the styles directory.
+  $path = file_directory_path() . '/styles';
+  file_check_directory($path, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS);
+}
+
+/**
+ * Implement hook_uninstall().
+ */
+function image_uninstall() {
+  drupal_uninstall_schema('image');
+}
+
+/**
+ * Implement hook_schema().
+ */
+function image_schema() {
+  $schema = array();
+
+  $schema['cache_image'] = array(
+    'description' => 'Cache table used to store information about image maniupaltions that are in-progress.',
+    'fields' => array(
+      'cid' => array(
+        'description' => 'Primary Key: Unique cache ID.',
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'data' => array(
+        'description' => 'A collection of data to cache.',
+        'type' => 'blob',
+        'not null' => FALSE,
+        'size' => 'big',
+      ),
+      'expire' => array(
+        'description' => 'A Unix timestamp indicating when the cache entry should expire, or 0 for never.',
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'created' => array(
+        'description' => 'A Unix timestamp indicating when the cache entry was created.',
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'headers' => array(
+        'description' => 'Any custom HTTP headers to be added to cached data.',
+        'type' => 'text',
+        'not null' => FALSE,
+      ),
+      'serialized' => array(
+        'description' => 'A flag to indicate whether content is serialized (1) or not (0).',
+        'type' => 'int',
+        'size' => 'small',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+    ),
+    'indexes' => array(
+      'expire' => array('expire'),
+    ),
+    'primary key' => array('cid'),
+  );
+
+  $schema['image_styles'] = array(
+    'fields' => array(
+      'isid' => array(
+        'description' => 'The primary identifier for an image style.',
+        'type' => 'serial',
+        'unsigned' => TRUE,
+        'not null' => TRUE),
+      'name' => array(
+        'description' => 'The style name.',
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE),
+    ),
+    'primary key' => array('isid'),
+    'indexes' => array(
+      'name' => array('name'),
+    ),
+  );
+
+  $schema['image_effects'] = array(
+    'fields' => array(
+      'ieid' => array(
+        'description' => 'The primary identifier for an image effect.',
+        'type' => 'serial',
+        'unsigned' => TRUE,
+        'not null' => TRUE),
+      'isid' => array(
+        'description' => 'The primary identifier for an image style.',
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0),
+      'weight' => array(
+        'description' => 'The weight of the effect in the style.',
+        'type' => 'int',
+        'unsigned' => FALSE,
+        'not null' => TRUE,
+        'default' => 0),
+      'effect' => array(
+        'description' => 'The unique ID of the effect to be executed.',
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE),
+      'data' => array(
+        'description' => 'The configuration data for the effect.',
+        'type' => 'text',
+        'not null' => TRUE,
+        'size' => 'big',
+        'serialize' => TRUE),
+    ),
+    'primary key' => array('ieid'),
+    'indexes' => array(
+      'isid' => array('isid'),
+    ),
+  );
+
+  return $schema;
+}
diff --git modules/image/image.module modules/image/image.module
new file mode 100644
index 0000000..f9d8134
--- /dev/null
+++ modules/image/image.module
@@ -0,0 +1,656 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * Exposes global functionality for creating image styles.
+ */
+
+/**
+ * Implement hook_menu().
+ */
+function image_menu() {
+  $items = array();
+
+  $items['image/generate/%image_style'] = array(
+    'title' => 'Generate image style',
+    'page callback' => 'image_style_generate',
+    'page arguments' => array(2),
+    'access callback' => TRUE,
+    'type' => MENU_CALLBACK,
+  );
+
+  return $items;
+}
+
+/**
+ * Implement hook_theme().
+ */
+function image_theme() {
+  return array(
+    'image_style' => array(
+      'arguments' => array('style' => NULL, 'path' => NULL, 'alt' => '', 'title' => '', 'attributes' => NULL, 'getsize' => TRUE),
+    ),
+    'image_resize_summary' => array(
+      'arguments' => array('data' => NULL),
+    ),
+    'image_scale_summary' => array(
+      'arguments' => array('data' => NULL),
+    ),
+    'image_crop_summary' => array(
+      'arguments' => array('data' => NULL),
+    ),
+    'image_rotate_summary' => array(
+      'arguments' => array('data' => NULL),
+    ),
+  );
+}
+
+/**
+ * Implement hook_flush_caches().
+ */
+function image_flush_caches() {
+  return array('cache_image');
+}
+
+/**
+ * Implement hook_file_download().
+ *
+ * Control the access to files underneath the image/generate/style directory.
+ */
+function image_file_download($filepath) {
+  if (strpos($filepath, 'styles/') === 0) {
+    $args = explode('/', $filepath);
+    array_shift($args); // Remove the "styles" item.
+    $style_name = array_shift($args);
+    $original_path = implode('/', $args);
+
+    // Check that the file exists and is an image.
+    if ($info = image_get_info(file_create_path($filepath))) {
+      // Check the permissions of the original image to grant access to this
+      // image.
+      $headers = module_invoke_all('file_download', $original_path);
+      if (!in_array(-1, $headers)) {
+        return array(
+          'Content-Type: ' . $info['mime_type'],
+          'Content-Length: ' . $info['file_size'],
+        );
+      }
+    }
+    return -1;
+  }
+}
+
+/**
+ * Implement hook_file_move().
+ */
+function image_file_move($file, $source) {
+  // Delete any image derivatives at the original image path.
+  image_path_flush($file->filepath);
+}
+
+/**
+ * Implement hook_file_delete().
+ */
+function image_file_delete($file) {
+  // Delete any image derivatives of this image.
+  image_path_flush($file->filepath);
+}
+
+/**
+ * Clear cached versions of a specific file in all styles.
+ *
+ * @param $path
+ *   The Drupal file path to the original image.
+ */
+function image_path_flush($path) {
+  $path = _file_strip_files_directory($path);
+  foreach (image_styles() as $style) {
+    if ($path = file_create_path('styles/' . $style['name'] . '/' . $path)) {
+      file_unmanaged_delete($path);
+    }
+  }
+}
+
+/**
+ * Get an array of all styles and their settings.
+ *
+ * @return
+ *   Array of styles array($isid => array('id' => integer, 'name' => string)).
+ */
+function image_styles() {
+  $styles = &drupal_static(__FUNCTION__, array());
+
+  // Grab from cache or build the array.
+  if ($cache = cache_get('image_styles', 'cache')) {
+    $styles = $cache->data;
+  }
+  else {
+    $result = db_select('image_styles', NULL, array('fetch' => PDO::FETCH_ASSOC))
+      ->fields('image_styles')
+      ->orderBy('name')
+      ->execute();
+    foreach ($result as $style) {
+      $styles[$style['name']] = $style;
+      $styles[$style['name']]['effects'] = image_style_effects($style);
+    }
+
+    cache_set('image_styles', $styles);
+  }
+
+  return $styles;
+}
+
+/**
+ * Load a style by style name or ID. May be used as a loader for menu items.
+ *
+ * @param $name
+ *   The name of the style.
+ * @param $isid
+ *   Optional. The numeric id of a style if the name is not known.
+ * @return
+ *   An image style with the format of
+ *   array('name' => array('isid' => int, 'name' => string, 'effects' => array())).
+ *   If the style name or ID is not valid, an empty array is returned.
+ */
+function image_style_load($name = NULL, $isid = NULL) {
+  $styles = image_styles();
+
+  // If retrieving by name.
+  if (isset($name) && isset($styles[$name])) {
+    return $styles[$name];
+  }
+
+  // If retrieving by image style id.
+  if (isset($isid)) {
+    foreach ($styles as $name => $style) {
+      if ($style['isid'] == $isid) {
+        return $style;
+      }
+    }
+  }
+
+  // Otherwise the style was not found.
+  return FALSE;
+}
+
+/**
+ * Save an image style.
+ *
+ * @param style
+ *   An image style array.
+ * @return
+ *   A style array. In the case of a new style, 'isid' will be populated.
+ */
+function image_style_save($style) {
+  if (isset($style['isid']) && is_numeric($style['isid'])) {
+    // Load the existing style to make sure we account for renamed styles.
+    $old_style = image_style_load(NULL, $style['isid']);
+    image_style_flush($old_style);
+    drupal_write_record('image_styles', $style, 'isid');
+    if ($old_style['name'] != $style['name']) {
+      $style['old_name'] = $old_style['name'];
+    }
+  }
+  else {
+    drupal_write_record('image_styles', $style);
+    $style['is_new'] = TRUE;
+  }
+
+  // Let other modules update as necessary on save.
+  module_invoke_all('image_style_save', $style);
+
+  // Clear all caches and flush.
+  image_style_flush($style);
+
+  return $style;
+}
+
+/**
+ * Delete an image style.
+ *
+ * @param $style
+ *   An image style array.
+ * @param $replacement_style_name
+ *   (optional) When deleting a style, specify a replacement style name so
+ *   that existing settings (if any) may be converted to a new style.
+ * @return
+ *   TRUE on success.
+ */
+function image_style_delete($style, $replacement_style_name = '') {
+  image_style_flush($style);
+
+  db_delete('image_effects')->condition('isid', $style['isid'])->execute();
+  db_delete('image_styles')->condition('isid', $style['isid'])->execute();
+
+  // Let other modules update as necessary on save.
+  $style['old_name'] = $style['name'];
+  $style['name'] = $replacement_style_name;
+  module_invoke_all('image_style_delete', $style);
+
+  return TRUE;
+}
+
+/**
+ * Load all the effects for an image style.
+ *
+ * @param $style
+ *   An image style array.
+ * @return
+ *   An array of effects associated with specified style in the format
+ *   array('isid' => array()), or an empty array if the specified style has
+ *   no effects.
+ */
+function image_style_effects($style) {
+  $effects = image_effects();
+  $style_effects = array();
+  foreach ($effects as $effect) {
+    if ($style['isid'] == $effect['isid']) {
+      $style_effects[$effect['ieid']] = $effect;
+    }
+  }
+
+  return $style_effects;
+}
+
+/**
+ * Get an array of image styles suitable for using as select list options.
+ *
+ * @param $include_empty
+ *   If TRUE a <none> option will be inserted in the options array.
+ * @return
+ *   Array of image styles both key and value are set to style name.
+ */
+function image_style_options($include_empty = TRUE) {
+  $styles = image_styles();
+  $options = array();
+  if ($include_empty && !empty($styles)) {
+    $options[''] = t('<none>');
+  }
+  $options = array_merge($options, drupal_map_assoc(array_keys($styles)));
+  if (empty($options)) {
+    $options[''] = t('No defined styles');
+  }
+  return $options;
+}
+
+/**
+ * Flush cached media for a style.
+ *
+ * @param $style
+ *   An image style array.
+ */
+function image_style_flush($style) {
+  $style_directory = realpath(file_directory_path() . '/styles/' . $style['name']);
+  if (is_dir($style_directory)) {
+    file_unmanaged_delete_recursive($style_directory);
+  }
+
+  // Let other modules update as necessary on flush.
+  module_invoke_all('image_style_flush', $style);
+
+  // Clear image style and effect caches.
+  cache_clear_all('image_styles', 'cache');
+  cache_clear_all('image_effects', 'cache');
+  drupal_static_reset('image_styles');
+  drupal_static_reset('image_effects');
+
+  // Clear page caches when flushing.
+  cache_clear_all('*', 'cache_block', TRUE);
+  cache_clear_all('*', 'cache_page', TRUE);
+}
+
+/**
+ * Return the complete URL to an image when using a style.
+ *
+ * If the image has already been created then its location will be returned. If
+ * it does not then image_style_generate_url() will be called.
+ *
+ * @param $style_name
+ *   The name of the style to be used with this image.
+ * @param $path
+ *   The path to the image.
+ * @return
+ *   The absolute URL where a style image can be downloaded, suitable for use
+ *   in an <img> tag. If the site is using the default method for generating
+ *   images, the image may not yet exist and will only be created when a
+ *   visitor's browser requests the file.
+ * @see image_style_generate_url()
+ * @see image_style_path()
+ */
+function image_style_url($style_name, $path) {
+  $style_path = image_style_path($style_name, $path);
+  if (file_exists($style_path)) {
+    return file_create_url($style_path);
+  }
+  return image_style_generate_url($style_name, $path);
+}
+
+/**
+ * Return the URL for an image derivative given a style and image path.
+ *
+ * This function is the default image generation method. It returns a URL for
+ * an image that can be used in an <img> tag. When the browser requests the
+ * image at image/generate/[style_name]/[path] the image is generated if it does
+ * not already exist and then served to the browser. This allows each image to
+ * have its own PHP instance (and memory limit) for generation of the new image.
+ *
+ * @param $style_name
+ *   The name of the style to be used with this image.
+ * @param $path
+ *   The path to the image.
+ * @return
+ *   The absolute URL where a style image can be downloaded, suitable for use
+ *   in an <img> tag. Requesting the URL will cause the image to be created.
+ * @see image_style_generate()
+ * @see image_style_url()
+ */
+function image_style_generate_url($style_name, $path) {
+  $destination = image_style_path($style_name, $path);
+
+  // If the image already exists use that rather than regenerating it.
+  if (file_exists($destination)) {
+    return image_style_url($style_name, $path);
+  }
+
+  // Disable page cache for this request. This prevents anonymous users from
+  // needlessly hitting the image generation URL when the image already exists.
+  $GLOBALS['conf']['cache'] = CACHE_DISABLED;
+
+  // Set a cache entry to grant access to this style/image path. This will be
+  // checked by image_style_generate().
+  cache_set('access:' . $style_name . ':' . md5($path), 1, 'cache_image', time() + 600);
+
+  // Generate a callback path for the image.
+  $url = url('image/generate/' . $style_name . '/' . $path, array('absolute' => TRUE));
+  return $url;
+}
+
+/**
+ * Return a relative path to an image when using a style.
+ *
+ * The path returned by this function may not exist. The default generation
+ * method only creates images when they are requested by a user's browser.
+ *
+ * @param $style_name
+ *   The name of the style to be used with this image.
+ * @param $path
+ *   The path to the image.
+ * @return
+ *   The path to an image style image relative to Drupal's root.
+ * @see image_style_url()
+ */
+function image_style_path($style_name, $path) {
+  return file_directory_path() . '/styles/' . $style_name . '/' . _file_strip_files_directory($path);
+}
+
+/**
+ * Create a new image based on an image style.
+ *
+ * @param $style
+ *   An image style array.
+ * @param $source
+ *   Path of the source file.
+ * @param $destination
+ *   Path of the destination file.
+ * @return
+ *   TRUE if an image derivative is generated, FALSE if no image derivative
+ *   is generated. NULL if the derivative is being generated.
+ */
+function image_style_create_derivative($style, $source, $destination) {
+  // Get the folder for the final location of this style.
+  $directory = dirname($destination);
+
+  // Build the destination folder tree if it doesn't already exist.
+  if (!file_check_directory($directory, FILE_CREATE_DIRECTORY) && !mkdir($directory, 0775, TRUE)) {
+    watchdog('image', 'Failed to create style directory: %directory', array('%directory' => $directory), WATCHDOG_ERROR);
+    return FALSE;
+  }
+
+  if (!$image = image_load($source)) {
+    return FALSE;
+  }
+
+  foreach ($style['effects'] as $effect) {
+    image_effect_apply($image, $effect);
+  }
+
+  if (!image_save($image, $destination)) {
+    if (file_exists($destination)) {
+      watchdog('image', 'Cached image file %destination already exists. There may be an issue with your rewrite configuration.', array('%destination' => $destination), WATCHDOG_ERROR);
+    }
+    return FALSE;
+  }
+
+  return TRUE;
+}
+
+/**
+ * Load all image effects from the database.
+ *
+ * @return
+ *   Array of image effects.
+ */
+function image_effects() {
+  $effects = &drupal_static(__FUNCTION__);
+
+  if (!isset($effects)) {
+    $effects = array();
+
+    // Add database image effects.
+    $result = db_select('image_effects', NULL, array('fetch' => PDO::FETCH_ASSOC))
+      ->fields('image_effects')
+      ->orderBy('image_effects.weight', 'ASC')
+      ->execute();
+    foreach ($result as $effect) {
+      $effect['data'] = unserialize($effect['data']);
+      $definition = image_effect_definition_load($effect['effect']);
+      // Do not load effects whose definition cannot be found.
+      if ($definition) {
+        $effect = array_merge($definition, $effect);
+        $effects[$effect['ieid']] = $effect;
+      }
+    }
+  }
+
+  return $effects;
+}
+
+/**
+ * Pull in effects exposed by other modules using hook_image_effects().
+ *
+ * @return
+ *   An array of effects to be used when transforming images.
+ */
+function image_effect_definitions() {
+  $effects = &drupal_static(__FUNCTION__);
+
+  if (!isset($effects)) {
+    if ($cache = cache_get('image_effects') && !empty($cache->data)) {
+      $effects = $cache->data;
+    }
+    else {
+      $effects = array();
+      foreach (module_implements('image_effects') as $module) {
+        foreach (module_invoke($module, 'image_effects') as $key => $effect) {
+          // Ensure the current toolkit supports the effect.
+          $effect['module'] = $module;
+          $effect['effect'] = $key;
+          $effect['data'] = isset($effect['data']) ? $effect['data'] : array();
+          $effects[$key] = $effect;
+        };
+      }
+      uasort($effects, '_image_effects_definitions_sort');
+      cache_set('image_effects', $effects);
+    }
+  }
+
+  return $effects;
+}
+
+/**
+ * Load the definition for an effect.
+ *
+ * The effect definition is a set of default values that applies to an effect
+ * regardless of user settings. This definition consists of an array containing
+ * at least the following values:
+ *  - effect: The unique name for the effect being performed. Usually prefixed
+ *    with the name of the module providing the effect.
+ *  - module: The module providing the effect.
+ *  - description: A description of the effect.
+ *
+ * @param $effect
+ *   The name of the effect definition to load.
+ * @return
+ *   An array containing at least the following values.
+ *   array(
+ *    'effect' => Unique name of the effect being performed.
+ *    'module' => Name of module providing the effect.
+ *    'description' => A description of the effect.
+ *   )
+ */
+function image_effect_definition_load($effect) {
+  $definition_cache = &drupal_static(__FUNCTION__);
+
+  if (!isset($definition_cache[$effect])) {
+    $definitions = image_effect_definitions();
+    $definition = (isset($definitions[$effect])) ? $definitions[$effect] : array();
+    $definition_cache[$effect] = $definition;
+  }
+
+  return isset($definition_cache[$effect]) ? $definition_cache[$effect] : FALSE;
+}
+
+/**
+ * Load a single image effect.
+ *
+ * @param $ieid
+ *   The image effect ID.
+ * @return
+ *   An image effect array or FALSE if the specified effect can not be found.
+ */
+function image_effect_load($ieid) {
+  $effects = image_effects();
+  return isset($effects[$ieid]) ? $effects[$ieid] : FALSE;
+}
+
+/**
+ * Save an image effect.
+ *
+ * @param $effect
+ *   An image effect array.
+ * @return
+ *   An image effect array. In the case of a new effect 'ieid' will be set.
+ */
+function image_effect_save($effect) {
+  if (!empty($effect['ieid'])) {
+    drupal_write_record('image_effects', $effect, 'ieid');
+  }
+  else {
+    drupal_write_record('image_effects', $effect);
+  }
+  $style = image_style_load(NULL, $effect['isid']);
+  image_style_flush($style);
+  return $effect;
+}
+
+/**
+ * Delete an image effect.
+ *
+ * @param $effect
+ *   An image effect array.
+ */
+function image_effect_delete($effect) {
+  db_delete('image_effects')->condition('ieid', $effect['ieid'])->execute();
+  $style = image_style_load(NULL, $effect['isid']);
+  image_style_flush($style);
+}
+
+/**
+ * Given an image object and effect, perform the effect on the file.
+ *
+ * @param $image
+ *   An image object.
+ * @param $effect
+ *   An image effect array.
+ * @return
+ *   TRUE on success. FALSE if unable to perform effect on image.
+ */
+function image_effect_apply(&$image, $effect) {
+  if (drupal_function_exists($effect['function'])) {
+    return call_user_func($effect['function'], $image, $effect['data']);
+  }
+  return FALSE;
+}
+
+/**
+ * Return a themed image using a specific image style.
+ *
+ * @param $style_name
+ *   The name of the style to be used to alter the original image.
+ * @param $path
+ *   The path of the image file relative to the Drupal files directory.
+ *   This function does not work with images outside the files directory nor
+ *   with remotely hosted images.
+ * @param $alt
+ *   The alternative text for text-based browsers.
+ * @param $title
+ *   The title text is displayed when the image is hovered in some popular
+ *   browsers.
+ * @param $attributes
+ *   Associative array of attributes to be placed in the img tag.
+ * @param $getsize
+ *   If set to TRUE, the image's dimension are fetched and added as
+ *   width/height attributes.
+ * @return
+ *   A string containing the image tag.
+ */
+function theme_image_style($style_name, $path, $alt = '', $title = '', $attributes = NULL, $getsize = TRUE) {
+  // theme_image() can only honor the $getsize parameter with local file paths.
+  // The derivative image is not created until it has been requested so the file
+  // may not yet exist, in this case we just fallback to the URL.
+  $style_path = image_style_path($style_name, $path);
+  if (!file_exists($style_path)) {
+    $style_path = image_style_url($style_name, $path);
+  }
+  return theme('image', $style_path, $alt, $title, $attributes, $getsize);
+}
+
+/**
+ * Accept a percentage and return it in pixels.
+ */
+function image_filter_percent($value, $current_pixels) {
+  if (strpos($value, '%') !== FALSE) {
+    $value = str_replace('%', '', $value) * 0.01 * $current_pixels;
+  }
+  return $value;
+}
+
+/**
+ * Accept a keyword (center, top, left, etc) and return it as a pixel offset.
+ */
+function image_filter_keyword($value, $current_pixels, $new_pixels) {
+  switch ($value) {
+    case 'top':
+    case 'left':
+      $value = 0;
+      break;
+    case 'bottom':
+    case 'right':
+      $value = $current_pixels - $new_pixels;
+      break;
+    case 'center':
+      $value = $current_pixels/2 - $new_pixels/2;
+      break;
+  }
+  return $value;
+}
+
+/**
+ * Internal function for sorting image effect definitions through uasort().
+ *
+ * @see image_effect_definitions()
+ */
+function _image_effects_definitions_sort($a, $b) {
+  return strcasecmp($a['name'], $b['name']);
+}
diff --git modules/image/image.test modules/image/image.test
new file mode 100644
index 0000000..fb53880
--- /dev/null
+++ modules/image/image.test
@@ -0,0 +1,113 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * Image module tests.
+ */
+
+class ImageStyles extends DrupalWebTestCase {
+  protected $style_name;
+  protected $image_with_generated;
+  protected $image_without_generated;
+
+  /**
+   * Implementation of getInfo().
+   */
+  function getInfo() {
+    return array(
+      'name' => t('Image styles and effects API'),
+      'description' => t('Tests creation, deletion, and editing of image styles and effects at the API level.'),
+      'group' => t('Image')
+    );
+  }
+
+  function setUp() {
+    parent::setUp();
+
+    $this->style_name = 'style_foo';
+
+    // Create the directories for the styles.
+    $status = file_check_directory($d = file_directory_path() .'/styles', FILE_CREATE_DIRECTORY);
+    $this->assertNotIdentical(FALSE, $status, t('Created the directory for the generated images.'));
+    $status = file_check_directory($d = file_directory_path() .'/styles/' . $this->style_name, FILE_CREATE_DIRECTORY);
+    $this->assertNotIdentical(FALSE, $status, t('Created the directory for the generated images for the test style.' ));
+
+    // Make two copies of the file...
+    $file = reset($this->drupalGetTestFiles('image'));
+    $this->image_without_generated = file_unmanaged_copy($file->filepath, NULL, FILE_EXISTS_RENAME);
+    $this->assertNotIdentical(FALSE, $this->image_without_generated, t('Created the without generated image file.'));
+    $this->image_with_generated = file_unmanaged_copy($file->filepath, NULL, FILE_EXISTS_RENAME);
+    $this->assertNotIdentical(FALSE, $this->image_with_generated, t('Created the with generated image file.'));
+    // and create a "generated" file for the one.
+    $status = file_unmanaged_copy($file->filepath, image_style_path($this->style_name, $this->image_with_generated), FILE_EXISTS_REPLACE);
+    $this->assertNotIdentical(FALSE, $status, t('Created a file where the generated image should be.'));
+  }
+
+  /**
+   * Test image_style_path().
+   */
+  function testImageStylePath() {
+    $actual = image_style_path($this->style_name, $this->image_without_generated);
+    $expected = file_directory_path() . '/styles/' . $this->style_name . '/' . basename($this->image_without_generated);
+    $this->assertEqual($actual, $expected, t('Got the path for a file.'));
+  }
+
+  /**
+   * Test image_style_url().
+   */
+  function testImageStyleUrl() {
+    // Test it with no generated file.
+    $actual = image_style_url($this->style_name, $this->image_without_generated);
+    $expected = url('image/generate/' . $this->style_name . '/' . $this->image_without_generated, array('absolute' => TRUE));
+    $this->assertEqual($actual, $expected, t('Got the generate URL for a non-existent file.'));
+
+    // Now test it with a generated file.
+    $actual = image_style_url($this->style_name, $this->image_with_generated);
+    $expected = file_create_url(image_style_path($this->style_name, $this->image_with_generated));
+    $this->assertEqual($actual, $expected, t('Got the download URL for an existing file.'));
+  }
+
+  /**
+   * Test image_style_generate_url().
+   */
+  function testImageStyleGenerateUrl() {
+    // Test it with no generated file.
+    $actual = image_style_generate_url($this->style_name, $this->image_without_generated);
+    $expected = url('image/generate/' . $this->style_name . '/' . $this->image_without_generated, array('absolute' => TRUE));
+    $this->assertEqual($actual, $expected, t('Got the generate URL for a non-existent file.'));
+
+    // Now test it with a generated file.
+    $actual = image_style_generate_url($this->style_name, $this->image_with_generated);
+    $expected = file_create_url(image_style_path($this->style_name, $this->image_with_generated));
+    $this->assertEqual($actual, $expected, t('Got the download URL for an existing file.'));
+  }
+
+
+
+  function testEffects() {
+    $effects = image_effects();
+    $this->assertEqual(count($effects), 6, t("Found core's effects."));
+
+    $effect_definitions = image_effect_definitions();
+    $this->assertEqual(count($effect_definitions), 6, t("Found core's effects."));
+  }
+}
+
+
+/**
+ * Tests creation, deletion, and editing of image styles and effects.
+ */
+class ImageAdminStyles extends DrupalWebTestCase {
+
+  /**
+   * Implementation of getInfo().
+   */
+  function getInfo() {
+    return array(
+      'name' => t('Image styles and effects UI configuration'),
+      'description' => t('Tests creation, deletion, and editing of image styles and effects at the UI level.'),
+      'group' => t('Image')
+    );
+  }
+}
diff --git modules/image/images/sample.jpg modules/image/images/sample.jpg
new file mode 100644
index 0000000..3e57d6a
Binary files /dev/null and modules/image/images/sample.jpg differ
diff --git modules/simpletest/tests/image.test modules/simpletest/tests/image.test
index eb41af2..bad08cf 100644
--- modules/simpletest/tests/image.test
+++ modules/simpletest/tests/image.test
@@ -18,7 +18,7 @@ class ImageToolkitTestCase extends DrupalWebTestCase {
     return array(
       'name' => t('Image toolkit tests'),
       'description' => t('Check image tookit functions.'),
-      'group' => t('Image API'),
+      'group' => t('Image'),
     );
   }
 
@@ -207,7 +207,7 @@ class ImageToolkitGdTestCase extends DrupalWebTestCase {
     return array(
       'name' => t('Image GD manipulation tests'),
       'description' => t('Check that core image manipulations work properly: scale, resize, rotate, crop, scale and crop, and desaturate.'),
-      'group' => t('Image API'),
+      'group' => t('Image'),
     );
   }
 
diff --git modules/user/user.module modules/user/user.module
index 857df97..aa37f83 100644
--- modules/user/user.module
+++ modules/user/user.module
@@ -1162,7 +1162,12 @@ function template_preprocess_user_picture(&$variables) {
     }
     if (isset($filepath)) {
       $alt = t("@user's picture", array('@user' => $account->name ? $account->name : variable_get('anonymous', t('Anonymous'))));
-      $variables['picture'] = theme('image', $filepath, $alt, $alt, '', FALSE);
+      if (module_exists('image') && $style = variable_get('user_picture_style', '')) {
+        $variables['picture'] = theme('image_style', $style, $filepath, $alt, $alt, NULL, FALSE);
+      }
+      else {
+        $variables['picture'] = theme('image', $filepath, $alt, $alt, NULL, FALSE);
+      }
       if (!empty($account->uid) && user_access('access user profiles')) {
         $attributes = array('attributes' => array('title' => t('View user profile.')), 'html' => TRUE);
         $variables['picture'] = l($variables['picture'], "user/$account->uid", $attributes);
diff --git profiles/default/default.profile profiles/default/default.profile
index 9ec0a34..740d673 100644
--- profiles/default/default.profile
+++ profiles/default/default.profile
@@ -8,7 +8,7 @@
  *   An array of modules to enable.
  */
 function default_profile_modules() {
-  return array('block', 'color', 'comment', 'help', 'menu', 'path', 'taxonomy', 'dblog', 'search', 'toolbar');
+  return array('block', 'color', 'comment', 'help', 'image', 'menu', 'path', 'taxonomy', 'dblog', 'search', 'toolbar');
 }
 
 /**
@@ -196,6 +196,27 @@ function default_profile_tasks(&$task, $url) {
   // Don't display date and author information for page nodes by default.
   variable_set('node_submitted_page', FALSE);
 
+  // Create an image style.
+  $style = array('name' => 'thumbnail_square');
+  $style = image_style_save($style);
+  $effect = array(
+    'isid' => $style['isid'],
+    'effect' => 'image_scale_and_crop',
+    'data' => array('width' => '85', 'height' => '85'),
+  );
+  image_effect_save($effect);
+
+  // Enable user picture support and set the default to a square thumbnail option.
+  variable_set('user_pictures', '1');
+  variable_set('user_picture_dimensions', '1024x1024');
+  variable_set('user_picture_file_size', '800');
+  variable_set('user_picture_style', 'thumbnail_square');
+
+  $theme_settings = theme_get_settings();
+  $theme_settings['toggle_node_user_picture'] = '1';
+  $theme_settings['toggle_comment_user_picture'] = '1';
+  variable_set('theme_settings', $theme_settings);
+
   // Create a default vocabulary named "Tags", enabled for the 'article' content type.
   $description = st('Use tags to group articles on similar topics into categories.');
   $help = st('Enter a comma-separated list of words.');
