Index: src/ColorTransformation.vala
===================================================================
--- src/ColorTransformation.vala	(revision 1983)
+++ src/ColorTransformation.vala	(working copy)
@@ -227,7 +227,8 @@
     TEMPERATURE,
     TINT,
     SATURATION,
-    EXPOSURE
+    EXPOSURE,
+    CONTRAST
 }
 
 public class PixelTransformationBundle {
@@ -248,6 +249,7 @@
         set(new TintTransformation(0.0f));
         set(new SaturationTransformation(0.0f));
         set(new ExposureTransformation(0.0f));
+        set(new ContrastTransformation(0.0f));
     }
     
     public void load(KeyValueMap store) {
@@ -262,6 +264,7 @@
         set(new TintTransformation(store.get_float("tint", 0.0f)));
         set(new SaturationTransformation(store.get_float("saturation", 0.0f)));
         set(new ExposureTransformation(store.get_float("exposure", 0.0f)));
+        set(new ContrastTransformation(store.get_float("contrast", 0.0f)));
     }
     
     public KeyValueMap save(string group) {
@@ -297,6 +300,11 @@
         assert(new_exposure_trans != null);
         store.set_float("exposure", new_exposure_trans.get_parameter());
         
+        ContrastTransformation? new_contrast_trans =
+            (ContrastTransformation) get_transformation(PixelTransformationType.CONTRAST);
+        assert(new_contrast_trans != null);
+        store.set_float("contrast", new_contrast_trans.get_parameter());
+        
         return store;
     }
     
@@ -707,6 +715,42 @@
     }
 }
 
+public class ContrastTransformation : RGBTransformation {
+    public const float MIN_PARAMETER = -16.0f;
+    public const float MAX_PARAMETER = 16.0f;
+
+    const float MAX_CONTRAST_ADJUSTMENT = 0.5f;  // must be less than 1.0
+
+    float parameter;
+
+    public ContrastTransformation(float client_parameter) {
+        base(PixelTransformationType.CONTRAST);
+        
+        parameter = client_parameter.clamp(MIN_PARAMETER, MAX_PARAMETER);
+
+        if (parameter != 0.0f) {
+
+            float contrast_adjustment = (parameter / 16.0f) * MAX_CONTRAST_ADJUSTMENT;
+            float component_coefficient = 1.0f + contrast_adjustment;
+            float component_offset = contrast_adjustment / -2.0f;
+
+            matrix_entries[0] = component_coefficient;
+            matrix_entries[5] = component_coefficient;
+            matrix_entries[10] = component_coefficient;
+            
+            matrix_entries[3] = component_offset;
+            matrix_entries[7] = component_offset;
+            matrix_entries[11] = component_offset;
+
+            identity = false;
+        }
+    }
+
+    public float get_parameter() {
+        return parameter;
+    }
+}
+
 public class PixelTransformer {
     private Gee.ArrayList<PixelTransformation> transformations =
         new Gee.ArrayList<PixelTransformation>();
@@ -1439,6 +1483,7 @@
     adjustments.set(new TemperatureTransformation(0.0f));
     adjustments.set(new TintTransformation(0.0f));
     adjustments.set(new ExposureTransformation(0.0f));
+    adjustments.set(new ContrastTransformation(0.0f));
     adjustments.set(new SaturationTransformation(0.0f));
     
     return adjustments;
Index: src/EditingTools.vala
===================================================================
--- src/EditingTools.vala	(revision 1983)
+++ src/EditingTools.vala	(working copy)
@@ -2003,6 +2003,9 @@
         public Gtk.HScale exposure_slider = new Gtk.HScale.with_range(
             ExposureTransformation.MIN_PARAMETER, ExposureTransformation.MAX_PARAMETER,
             1.0);
+        public Gtk.HScale contrast_slider = new Gtk.HScale.with_range(
+            ContrastTransformation.MIN_PARAMETER, ContrastTransformation.MAX_PARAMETER,
+            1.0);
         public Gtk.HScale saturation_slider = new Gtk.HScale.with_range(
             SaturationTransformation.MIN_PARAMETER, SaturationTransformation.MAX_PARAMETER,
             1.0);
@@ -2034,18 +2037,26 @@
             exposure_slider.set_draw_value(false);
             exposure_slider.set_update_policy(Gtk.UpdateType.DELAYED);
 
+            Gtk.Label contrast_label = new Gtk.Label.with_mnemonic(_("Contrast:"));
+            contrast_label.set_alignment(0.0f, 0.5f);
+            slider_organizer.attach_defaults(contrast_label, 0, 1, 1, 2);
+            slider_organizer.attach_defaults(contrast_slider, 1, 2, 1, 2);
+            contrast_slider.set_size_request(SLIDER_WIDTH, -1);
+            contrast_slider.set_draw_value(false);
+            contrast_slider.set_update_policy(Gtk.UpdateType.DELAYED);
+
             Gtk.Label saturation_label = new Gtk.Label.with_mnemonic(_("Saturation:"));
             saturation_label.set_alignment(0.0f, 0.5f);
-            slider_organizer.attach_defaults(saturation_label, 0, 1, 1, 2);
-            slider_organizer.attach_defaults(saturation_slider, 1, 2, 1, 2);
+            slider_organizer.attach_defaults(saturation_label, 0, 1, 2, 3);
+            slider_organizer.attach_defaults(saturation_slider, 1, 2, 2, 3);
             saturation_slider.set_size_request(SLIDER_WIDTH, -1);
             saturation_slider.set_draw_value(false);
             saturation_slider.set_update_policy(Gtk.UpdateType.DELAYED);
 
             Gtk.Label tint_label = new Gtk.Label.with_mnemonic(_("Tint:"));
             tint_label.set_alignment(0.0f, 0.5f);
-            slider_organizer.attach_defaults(tint_label, 0, 1, 2, 3);
-            slider_organizer.attach_defaults(tint_slider, 1, 2, 2, 3);
+            slider_organizer.attach_defaults(tint_label, 0, 1, 3, 4);
+            slider_organizer.attach_defaults(tint_slider, 1, 2, 3, 4);
             tint_slider.set_size_request(SLIDER_WIDTH, -1);
             tint_slider.set_draw_value(false);
             tint_slider.set_update_policy(Gtk.UpdateType.DELAYED);
@@ -2053,16 +2064,16 @@
             Gtk.Label temperature_label =
                 new Gtk.Label.with_mnemonic(_("Temperature:"));
             temperature_label.set_alignment(0.0f, 0.5f);
-            slider_organizer.attach_defaults(temperature_label, 0, 1, 3, 4);
-            slider_organizer.attach_defaults(temperature_slider, 1, 2, 3, 4);
+            slider_organizer.attach_defaults(temperature_label, 0, 1, 4, 5);
+            slider_organizer.attach_defaults(temperature_slider, 1, 2, 4, 5);
             temperature_slider.set_size_request(SLIDER_WIDTH, -1);
             temperature_slider.set_draw_value(false);
             temperature_slider.set_update_policy(Gtk.UpdateType.DELAYED);
 
             Gtk.Label shadows_label = new Gtk.Label.with_mnemonic(_("Shadows:"));
             shadows_label.set_alignment(0.0f, 0.5f);
-            slider_organizer.attach_defaults(shadows_label, 0, 1, 4, 5);
-            slider_organizer.attach_defaults(shadows_slider, 1, 2, 4, 5);
+            slider_organizer.attach_defaults(shadows_label, 0, 1, 5, 6);
+            slider_organizer.attach_defaults(shadows_slider, 1, 2, 5, 6);
             shadows_slider.set_size_request(SLIDER_WIDTH, -1);
             shadows_slider.set_draw_value(false);
             shadows_slider.set_update_policy(Gtk.UpdateType.DELAYED);
@@ -2320,6 +2331,12 @@
         histogram_transformer.attach_transformation(exposure_trans);
         adjust_tool_window.exposure_slider.set_value(exposure_trans.get_parameter());
 
+        /* set up contrast */
+        ContrastTransformation contrast_trans = (ContrastTransformation)
+            transformations.get_transformation(PixelTransformationType.CONTRAST);
+        histogram_transformer.attach_transformation(contrast_trans);
+        adjust_tool_window.contrast_slider.set_value(contrast_trans.get_parameter());
+
         bind_canvas_handlers(canvas);
         bind_window_handlers();
 
@@ -2458,6 +2475,12 @@
         slider_updated(new_exp_trans, _("Exposure"));
     }
     
+    private void on_contrast_adjustment() {
+        ContrastTransformation new_exp_trans = new ContrastTransformation(
+            (float) adjust_tool_window.contrast_slider.get_value());
+        slider_updated(new_exp_trans, _("Contrast"));
+    }
+    
     private void on_shadows_adjustment() {
         ShadowDetailTransformation new_shadows_trans = new ShadowDetailTransformation(
             (float) adjust_tool_window.shadows_slider.get_value());
@@ -2506,6 +2529,7 @@
         adjust_tool_window.reset_button.clicked.connect(on_reset);
         adjust_tool_window.cancel_button.clicked.connect(notify_cancel);
         adjust_tool_window.exposure_slider.value_changed.connect(on_exposure_adjustment);
+        adjust_tool_window.contrast_slider.value_changed.connect(on_contrast_adjustment);
         adjust_tool_window.saturation_slider.value_changed.connect(on_saturation_adjustment);
         adjust_tool_window.tint_slider.value_changed.connect(on_tint_adjustment);
         adjust_tool_window.temperature_slider.value_changed.connect(on_temperature_adjustment);
@@ -2514,6 +2538,7 @@
     
         adjust_tool_window.saturation_slider.button_press_event.connect(on_hscale_reset);
         adjust_tool_window.exposure_slider.button_press_event.connect(on_hscale_reset);
+        adjust_tool_window.contrast_slider.button_press_event.connect(on_hscale_reset);
         adjust_tool_window.tint_slider.button_press_event.connect(on_hscale_reset);
         adjust_tool_window.temperature_slider.button_press_event.connect(on_hscale_reset);
         adjust_tool_window.shadows_slider.button_press_event.connect(on_hscale_reset);
@@ -2524,6 +2549,7 @@
         adjust_tool_window.reset_button.clicked.disconnect(on_reset);
         adjust_tool_window.cancel_button.clicked.disconnect(notify_cancel);
         adjust_tool_window.exposure_slider.value_changed.disconnect(on_exposure_adjustment);
+        adjust_tool_window.contrast_slider.value_changed.disconnect(on_contrast_adjustment);
         adjust_tool_window.saturation_slider.value_changed.disconnect(on_saturation_adjustment);
         adjust_tool_window.tint_slider.value_changed.disconnect(on_tint_adjustment);
         adjust_tool_window.temperature_slider.value_changed.disconnect(on_temperature_adjustment);
@@ -2532,6 +2558,7 @@
             
         adjust_tool_window.saturation_slider.button_press_event.disconnect(on_hscale_reset);
         adjust_tool_window.exposure_slider.button_press_event.disconnect(on_hscale_reset);
+        adjust_tool_window.contrast_slider.button_press_event.disconnect(on_hscale_reset);
         adjust_tool_window.tint_slider.button_press_event.disconnect(on_hscale_reset);
         adjust_tool_window.temperature_slider.button_press_event.disconnect(on_hscale_reset);
         adjust_tool_window.shadows_slider.button_press_event.disconnect(on_hscale_reset);
@@ -2586,6 +2613,11 @@
                     ((ExposureTransformation) transformation).get_parameter());
             break;
             
+            case PixelTransformationType.CONTRAST:
+                adjust_tool_window.contrast_slider.set_value(
+                    ((ContrastTransformation) transformation).get_parameter());
+            break;
+            
             case PixelTransformationType.SATURATION:
                 adjust_tool_window.saturation_slider.set_value(
                     ((SaturationTransformation) transformation).get_parameter());
