diff --git a/core/modules/image/image.admin.inc b/core/modules/image/image.admin.inc
index 933a4ad..78cd002 100644
--- a/core/modules/image/image.admin.inc
+++ b/core/modules/image/image.admin.inc
@@ -453,6 +453,32 @@ function image_effect_scale_validate($element, &$form_state) {
 }
 
 /**
+ * Form element validation handler for a specified percent value.
+ */
+function image_effect_percent_validate($element, &$form_state) {
+  if ($element['#value'] !== '') {
+    if (!is_numeric($element['#value']) || $element['#value'] < 1 || $element['#value'] > 100) {
+      form_error($element, t('!name must be a number between 1 and 100.', array('!name' => $element['#title'])));
+    }
+  }
+}
+
+/**
+ * Form element validation handler to ensure the filter options are selected correctly.
+ */
+function image_effect_png_filter_validate($element, &$form_state) {
+  if (isset($element['#value'])) {
+    $count = count($element['#value']);
+    foreach ($element['#value'] as $value) {
+      // 'None' or 'All' can be selected on its own, never with others
+      if ($value == 8 && $count > 1 || $value == 248 && $count > 1) {
+        form_error($element, t("The options 'None' or 'All' can only be applied on its own", array('!name' => $element['#title'])));
+      }
+    }
+  }
+}
+
+/**
  * Form structure for the image resize form.
  *
  * Note that this is not a complete form, it only contains the portion of the
@@ -584,6 +610,97 @@ function image_rotate_form($data) {
 }
 
 /**
+ * Form structure for the image quality effect form.
+ *
+ * Note that this is not a complete form, it only contains the portion of the
+ * form for configuring the quality options. Therefore it does not not need to
+ * include metadata about the effect, nor a submit button.
+ *
+ * @param $data
+ *   The current configuration for this quality effect.
+ */
+function image_quality_form($data) {
+  // Obtain the option values from the serialized constant
+  $image_quality_options = _image_quality_png_settings();
+
+  $form['quality'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Quality'),
+    '#default_value' => isset($data['quality']) ? $data['quality'] : 75,
+    '#field_suffix' => t('%'),
+    '#required' => TRUE,
+    '#element_validate' => array('image_effect_percent_validate'),
+    '#size' => 10,
+    '#maxlength' => 3,
+    '#description' => t('Define the image quality for JPEG manipulations. Ranges from 1 to 100. Higher values mean better image quality but bigger files.'),
+  );
+  $form['png'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('PNG'),
+    '#description' => t('This setting will only apply for images in PNG format.'),
+    '#collapsible' => FALSE
+  );
+
+  $form['png']['filter'] = array(
+    '#type' => 'select',
+    '#title' => t('Filter'),
+    '#default_value' => isset($data['png']['filter']) ? $data['png']['filter'] : 8,
+    '#required' => TRUE,
+    '#options' => $image_quality_options['filter'],
+    '#multiple' => TRUE,
+    '#element_validate' => array('image_effect_png_filter_validate'),
+    '#description' => t("Select the filter(s) to be applied. More than one filter can be selected, but 'None' or 'All' can only be selected on its own."),
+    '#size' => 6
+  );
+
+  // To make it consistent with the JPEG quality option, this dropdown is for
+  // setting the quality rather than the compression level. The scale of
+  // 1 (lowest) - 10 (highest) is used.
+  $form['png']['compression'] = array(
+    '#type' => 'select',
+    '#title' => t('Quality'),
+    '#default_value' => isset($data['png']['compression']) ? $data['png']['compression'] : 4,
+    '#required' => TRUE,
+    '#options' => $image_quality_options['compression'],
+    '#multiple' => FALSE,
+    '#description' => t('Define the image quality for PNG manipulations. Ranges from 1 to 10. Higher values mean better image quality but bigger files.'),
+  );
+
+  return $form;
+}
+
+/**
+ * An internal function that returns configuration options for PNG filter and
+ * compression rate.
+ */
+function _image_quality_png_settings() {
+  return array(
+      'filter' => array(
+          8   => t('None'),
+          16  => t('Sub'),
+          32  => t('Up'),
+          64  => t('Average'),
+          128 => t('Perth'),
+          248 => t('All')
+      ),
+      // Compression is presented to the users as quality to make it consistent
+      // with the JPEG config
+      'compression' => array(
+          0 => 10,
+          1 => 9,
+          2 => 8,
+          3 => 7,
+          4 => 6,
+          5 => 5,
+          6 => 4,
+          7 => 3,
+          8 => 2,
+          9 => 1
+      )
+  );
+}
+
+/**
  * Returns HTML for the page containing the list of image styles.
  *
  * @param $variables
@@ -849,3 +966,39 @@ function theme_image_rotate_summary($variables) {
   $data = $variables['data'];
   return ($data['random']) ? t('random between -@degrees&deg and @degrees&deg', array('@degrees' => str_replace('-', '', $data['degrees']))) : t('@degrees&deg', array('@degrees' => $data['degrees']));
 }
+
+/**
+ * Returns HTML for a summary of an image quality effect.
+ *
+ * @param $variables
+ *   An associative array containing:
+ *   - data: The current configuration for this quality effect.
+ *
+ * @ingroup themeable
+ */
+function theme_image_quality_summary($variables) {
+  $data = $variables['data'];
+
+  // JPEG quality
+  $summary = t('JPEG: ') . $data['quality'] . '%; ';
+
+  // Get the key-value pairs for PNG quality, then display the labels
+  $image_quality_options = _image_quality_png_settings();
+
+  if (!empty($data['png']['compression'])) {
+    $summary .= t('PNG: ') . $image_quality_options['compression'][$data['png']['compression']] . '; ';
+  }
+
+  // Create a list of applied PNG filters
+  $filter_values = '';
+  foreach ($data['png']['filter'] as $filter) {
+    if (isset($image_quality_options['filter'][$filter])) {
+      $filter_values[] = $image_quality_options['filter'][$filter];
+    }
+  }
+  if (!empty($filter_values)) {
+    $summary .= format_plural(count($filter_values), 'Applied PNG filter: @filter', 'Applied PNG filters: @filter', $args = array('@filter' => implode(', ', $filter_values)));
+  }
+
+  return check_plain($summary);
+}
diff --git a/core/modules/image/image.effects.inc b/core/modules/image/image.effects.inc
index a56a429..69f5c37 100644
--- a/core/modules/image/image.effects.inc
+++ b/core/modules/image/image.effects.inc
@@ -56,6 +56,14 @@ function image_image_effect_info() {
       'form callback' => 'image_rotate_form',
       'summary theme' => 'image_rotate_summary',
     ),
+    'image_quality' => array(
+      'label' => t('Quality'),
+      'help' => t('Decreasing the quality will make image file sizes smaller.'),
+      'effect callback' => 'image_quality_effect',
+      'dimensions passthrough' => TRUE,
+      'form callback' => 'image_quality_form',
+      'summary theme' => 'image_quality_summary',
+    ),
   );
 
   return $effects;
@@ -312,3 +320,28 @@ function image_rotate_dimensions(array &$dimensions, array $data) {
     $dimensions['width'] = $dimensions['height'] = NULL;
   }
 }
+
+/**
+ * Image effect callback; Change the quality for a target image.
+ *
+ * @param $image
+ *   An image object returned by image_load().
+ * @param $data
+ *   An associative array containing the effect configuration values:
+ *   - quality (JPEG): An integer representing the desired image quality in percent.
+ *   - compression (PNG): An integer representing the desired image quality in compression rate.
+ *   - filter (PNG): An array of integer values representing the desired PNG filters to be applied.
+ *
+ * @return
+ *   Always TRUE. The quality is set as a property on the $image, so the image
+ *   toolkit's save callback may apply it when the image is written to disk.
+ *
+ * @see image_save()
+ * @see image_gd_save()
+ */
+function image_quality_effect(&$image, $data) {
+	$image->quality = $data['quality'];
+	$image->filter = $data['png']['filter'];
+	$image->png_quality = $data['png']['compression'];
+	return TRUE;
+}
diff --git a/core/modules/image/image.module b/core/modules/image/image.module
index 221e07c..3fc4897 100644
--- a/core/modules/image/image.module
+++ b/core/modules/image/image.module
@@ -216,6 +216,9 @@ function image_theme() {
     'image_rotate_summary' => array(
       'variables' => array('data' => NULL),
     ),
+    'image_quality_summary' => array(
+      'variables' => array('data' => NULL),
+    ),
 
     // Theme functions in image.field.inc.
     'image_widget' => array(
diff --git a/core/modules/system/image.gd.inc b/core/modules/system/image.gd.inc
index 7f3ac5e..fcca1e0 100644
--- a/core/modules/system/image.gd.inc
+++ b/core/modules/system/image.gd.inc
@@ -299,12 +299,19 @@ function image_gd_save(stdClass $image, $destination) {
     $success = $function($image->resource, $destination, variable_get('image_jpeg_quality', 75));
   }
   else {
-    // Always save PNG images with full transparency.
-    if ($extension == 'png') {
-      imagealphablending($image->resource, FALSE);
-      imagesavealpha($image->resource, TRUE);
-    }
-    $success = $function($image->resource, $destination);
+  	// Always save PNG images with full transparency.
+  	if ($extension == 'png') {
+  		imagealphablending($image->resource, FALSE);
+  		imagesavealpha($image->resource, TRUE);
+  	}
+  	// Set the png filter value. Convert the png filter settings into a bitmask.
+  	// Defaults to 8 (NONE)
+  	$filter = array_sum($image->filter);
+  	$png_filter = empty($filter) ? 8 : $filter;
+  	
+  	// Set the compression rate. Defaults to 4
+  	$png_quality = empty($image->png_quality) ? 4 : $image->png_quality;
+  	$success = $function($image->resource, $destination, $png_quality, $png_filter);
   }
   // Move temporary local file to remote destination.
   if (isset($permanent_destination) && $success) {
