[shotwell] Parallelize color transformations



commit d71dd2276dbfa6bfec36a275617e9b0de06a19ec
Author: Jens Georg <mail jensge org>
Date:   Sun Nov 5 19:06:36 2017 +0100

    Parallelize color transformations

 src/ColorTransformation.vala |  108 +++++++++++++++++++++++++++++-------------
 src/graphics-processor.vala  |    4 +-
 2 files changed, 78 insertions(+), 34 deletions(-)
---
diff --git a/src/ColorTransformation.vala b/src/ColorTransformation.vala
index a9f26c5..8d9ea8f 100644
--- a/src/ColorTransformation.vala
+++ b/src/ColorTransformation.vala
@@ -770,31 +770,52 @@ public class PixelTransformer {
         int dest_num_channels = dest.get_n_channels();
         int dest_rowstride = dest.get_rowstride();
         unowned uchar[] dest_pixels = dest.get_pixels();
-        
-        int cache_pixel_ticker = 0;
 
-        for (int j = 0; j < dest_height; j++) {
-            int row_start_index = j * dest_rowstride;
-            int row_end_index = row_start_index + (dest_width * dest_num_channels);
-            for (int i = row_start_index; i < row_end_index; i += dest_num_channels) {
-                RGBAnalyticPixel pixel = RGBAnalyticPixel.from_components(
-                    fp_pixel_cache[cache_pixel_ticker],
-                    fp_pixel_cache[cache_pixel_ticker + 1],
-                    fp_pixel_cache[cache_pixel_ticker + 2]);
+        var jobs = (int) GLib.get_num_processors() - 1;
+
+        uint slice_length = dest_height;
+        if (jobs > 0) {
+            slice_length = dest_height / jobs;
+        }
 
-                cache_pixel_ticker += 3;
+        var threads = new GLib.Thread<void *>[jobs];
+
+        unowned float[] cache = fp_pixel_cache;
+        for (var job = 0; job < jobs; job++) {
+            var row = job * slice_length;
+            var slice_height = (row + slice_length).clamp(0, dest_height);
+            threads[job] = new GLib.Thread<void*>("shotwell-worker", () => {
+                uint cache_pixel_ticker = row * dest_width * dest_num_channels;
+                for (uint j = row; j < slice_height; j++) {
+                    uint row_start_index = j * dest_rowstride;
+                    uint row_end_index = row_start_index + (dest_width * dest_num_channels);
+                    for (uint i = row_start_index; i < row_end_index; i += dest_num_channels) {
+                        RGBAnalyticPixel pixel = RGBAnalyticPixel.from_components(
+                                cache[cache_pixel_ticker],
+                                cache[cache_pixel_ticker + 1],
+                                cache[cache_pixel_ticker + 2]);
+
+                        cache_pixel_ticker += 3;
+
+                        pixel = apply_transformations(pixel);
+
+                        dest_pixels[i] = (uchar) (pixel.red * 255.0f);
+                        dest_pixels[i + 1] = (uchar) (pixel.green * 255.0f);
+                        dest_pixels[i + 2] = (uchar) (pixel.blue * 255.0f);
+                    }
+                }
 
-                pixel = apply_transformations(pixel);
+                return null;
+            });
+        }
 
-                dest_pixels[i] = (uchar) (pixel.red * 255.0f);
-                dest_pixels[i + 1] = (uchar) (pixel.green * 255.0f);
-                dest_pixels[i + 2] = (uchar) (pixel.blue * 255.0f);
-            }
+        foreach (var thread in threads) {
+            thread.join();
         }
     }
 
     public void transform_to_other_pixbuf(Gdk.Pixbuf source, Gdk.Pixbuf dest,
-        Cancellable? cancellable = null) {
+        Cancellable? cancellable = null, int jobs = -1) {
         if (source.width != dest.width)
             error("PixelTransformer: source and destination pixbufs must have the same width");
 
@@ -815,26 +836,47 @@ public class PixelTransformer {
         int rowbytes = n_channels * width;
         unowned uchar[] source_pixels = source.get_pixels();
         unowned uchar[] dest_pixels = dest.get_pixels();
-        for (int j = 0; j < height; j++) {
-            int row_start_index = j * rowstride;
-            int row_end_index = row_start_index + rowbytes;
-            for (int i = row_start_index; i < row_end_index; i += n_channels) {
-                RGBAnalyticPixel current_pixel = RGBAnalyticPixel.from_quantized_components(
-                    source_pixels[i], source_pixels[i + 1], source_pixels[i + 2]);
+        if (jobs == -1) {
+            jobs = (int) GLib.get_num_processors() - 1;
+        }
 
-                current_pixel = apply_transformations(current_pixel);
+        uint slice_length = height;
+        if (jobs > 0) {
+            slice_length = height / jobs;
+        }
 
-                dest_pixels[i] = (uchar) (current_pixel.red * 255.0f);
-                dest_pixels[i + 1] = (uchar) (current_pixel.green * 255.0f);
-                dest_pixels[i + 2] = (uchar) (current_pixel.blue * 255.0f);
-            }
+        var threads = new GLib.Thread<void*>[jobs];
 
-            if ((cancellable != null) && (cancellable.is_cancelled())) {
-                return;
-            }
+        for (var job = 0; job < jobs; job++) {
+            var row = job * slice_length;
+            var slice_height = (row + slice_length).clamp(0, height);
+
+            threads[job] = new GLib.Thread<void*>("shotwell-worker", () => {
+                for (var j = row; j < slice_height; j++) {
+                    this.apply_transformation(j, rowstride, rowbytes, n_channels, source_pixels,
+                            dest_pixels);
+
+                    if ((cancellable != null) && (cancellable.is_cancelled())) {
+                        break;
+                    }
+                }
+
+                return null;
+            });
+        }
+
+        foreach (var thread in threads) {
+            thread.join();
         }
     }
 
+    private extern void apply_transformation(uint row,
+                                      int rowstride,
+                                      int rowbytes,
+                                      int n_channels,
+                                      uchar[] source_pixels,
+                                      uchar[] dest_pixels);
+
 }
 
 public class RGBHistogram {
diff --git a/src/graphics-processor.vala b/src/graphics-processor.vala
index 401deb5..dc46845 100644
--- a/src/graphics-processor.vala
+++ b/src/graphics-processor.vala
@@ -3,6 +3,7 @@ static string? output_file = null;
 static string? pipeline = null;
 static bool auto_enhance = false;
 static string? format = null;
+static int jobs = -1;
 
 const GLib.OptionEntry[] options = {
     { "input", 'i', 0, GLib.OptionArg.FILENAME, ref input_file, "FILE to process", "FILE" },
@@ -10,6 +11,7 @@ const GLib.OptionEntry[] options = {
     { "pipeline", 'p', 0, GLib.OptionArg.FILENAME, ref pipeline, "graphics PIPELINE to run", "PIPELINE" },
     { "auto-enance", 'a', 0, GLib.OptionArg.NONE, ref auto_enhance, "run auto-enhance on input file", null },
     { "format", 'f', 0, GLib.OptionArg.STRING, ref format, "Save output file in specific format [png, jpeg 
(default)]", null},
+    { "jobs", 'j', 0, GLib.OptionArg.INT, ref jobs, "Number of parallel jobs to run on an image", null },
     { null, 0, 0, GLib.OptionArg.NONE, null, null, null }
 };
 
@@ -100,7 +102,7 @@ int main(string[] args) {
 
     var transformer = adjustments.generate_transformer();
     var timer = new Timer();
-    transformer.transform_to_other_pixbuf(src, output, null);
+    transformer.transform_to_other_pixbuf(src, output, null, jobs);
     var elapsed = timer.elapsed();
 
     print("Transformation took %f\n", elapsed);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]