[gnome-photos/wip/rishi/buffer-decoder: 1/4] raw rawspeed



commit 8b910d2253d302caa265ed06bec1604340e864fa
Author: Debarshi Ray <debarshir gnome org>
Date:   Sun Jun 2 10:26:48 2019 +0000

    raw rawspeed

 meson.build             |   2 +-
 src/meson.build         |   6 +
 src/photos-rawspeed.cpp | 507 ++++++++++++++++++++++++++++++++++++++++++++++++
 src/photos-rawspeed.h   |  31 +++
 4 files changed, 545 insertions(+), 1 deletion(-)
---
diff --git a/meson.build b/meson.build
index d3b60622..a1e19af9 100644
--- a/meson.build
+++ b/meson.build
@@ -1,6 +1,6 @@
 project(
   'gnome-photos',
-  'c',
+  ['c', 'cpp'],
   version: '3.33.1',
   license: 'GPL3+',
   default_options: 'buildtype=debugoptimized',
diff --git a/src/meson.build b/src/meson.build
index fed2f2ea..5752133d 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -22,6 +22,7 @@ sources = files(
   'photos-pipeline.c',
   'photos-png-count.c',
   'photos-quarks.c',
+  'photos-rawspeed.cpp',
 )
 
 thumbnailer_dbus = 'photos-thumbnailer-dbus'
@@ -82,6 +83,10 @@ libgnome_photos_built_sources_resources = gnome.compile_resources(
 libgnome_photos_built_headers += libgnome_photos_built_sources_resources[1]
 sources += libgnome_photos_built_sources_resources
 
+cppflags = [
+  '-DPACKAGE_DATA_DIR="@0@"'.format(photos_pkg_datadir),
+]
+
 deps = [
   babl_dep,
   gegl_dep,
@@ -100,6 +105,7 @@ deps = [
 libgnome_photos = shared_library(
   meson.project_name(),
   sources,
+  cpp_args: cppflags,
   dependencies: deps,
   include_directories: top_inc,
   install: true,
diff --git a/src/photos-rawspeed.cpp b/src/photos-rawspeed.cpp
new file mode 100644
index 00000000..dd1c777f
--- /dev/null
+++ b/src/photos-rawspeed.cpp
@@ -0,0 +1,507 @@
+/*
+ * Photos - access, organize and share your photos on GNOME
+ * Copyright © 2019 Red Hat, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "config.h"
+
+#include <cmath>
+#include <memory>
+#include <string>
+
+#include <babl/babl.h>
+#include <glib/gi18n.h>
+#include <RawSpeed-API.h>
+
+#include "photos-error.h"
+#include "photos-rawspeed.h"
+
+
+static std::shared_ptr<rawspeed::CameraMetaData> metadata;
+
+
+static gboolean
+photos_rawspeed_ensure_metadata (GError **error)
+{
+  static GError *local_error = NULL;
+  static gsize once_init_value = 0;
+  gboolean ret_val = FALSE;
+
+  if (g_once_init_enter (&once_init_value))
+    {
+      const std::string path = PACKAGE_DATA_DIR G_DIR_SEPARATOR_S "cameras.xml";
+
+      try
+        {
+          metadata.reset (new rawspeed::CameraMetaData (path.c_str()));
+        }
+      catch (rawspeed::RawspeedException &e)
+        {
+          g_set_error (&local_error, PHOTOS_ERROR, 0, e.what());
+        }
+
+      g_once_init_leave (&once_init_value, 1);
+    }
+
+  if (local_error != NULL)
+    {
+      g_propagate_error (error, g_error_copy (local_error));
+      return ret_val;
+    }
+
+  ret_val = TRUE;
+  return ret_val;
+}
+
+
+static gint
+photos_rawspeed_get_2x2_bayer_index (gint crop_x, gint crop_y, gint x, gint y)
+{
+  const gint index = ((crop_x + x) & 1) + (((crop_y + y) & 1) << 1);
+
+  g_return_val_if_fail (0 <= index && index < 4, 0);
+  return index;
+}
+
+
+static const Babl *
+photos_rawspeed_get_format (rawspeed::RawImageType image_type, gsize components)
+{
+  g_return_val_if_fail ((components == 1
+                         && (image_type == rawspeed::TYPE_FLOAT32 || image_type == rawspeed::TYPE_USHORT16))
+                        || (components == 3 && image_type == rawspeed::TYPE_FLOAT32),
+                        NULL);
+
+  std::string format_name;
+
+  switch (components)
+    {
+    case 1:
+      format_name = "Y";
+      break;
+
+    case 3:
+      format_name = "RGB";
+      break;
+
+    default:
+      g_assert_not_reached ();
+      break;
+    }
+
+  format_name += " ";
+
+  switch (image_type)
+    {
+    case rawspeed::TYPE_FLOAT32:
+      format_name += "float";
+      break;
+
+    case rawspeed::TYPE_USHORT16:
+      format_name += "u16";
+      break;
+
+    default:
+      g_assert_not_reached ();
+      break;
+    }
+
+  const Babl *format = babl_format (format_name.c_str ());
+  return format;
+}
+
+
+static bool
+photos_rawspeed_is_normalized (rawspeed::RawImageType image_type, guint32 filters, guint32 white_point)
+{
+  bool is_normalized;
+
+  if (filters != 0 && image_type == rawspeed::TYPE_FLOAT32)
+    {
+      union
+        {
+          gfloat f;
+          guint32 u;
+        } normalized;
+
+      normalized.f = 1.0f;
+      is_normalized = white_point == normalized.u;
+    }
+  else
+    {
+      is_normalized = image_type == rawspeed::TYPE_FLOAT32;
+    }
+
+  return is_normalized;
+}
+
+
+static void
+photos_rawspeed_destroy_image (rawspeed::RawImage *image)
+{
+  delete image;
+}
+
+
+static GeglBuffer *
+photos_rawspeed_correct_black_and_white_point (rawspeed::RawImage image, gsize components, guint32 filters)
+{
+  GeglBuffer *ret_val = NULL;
+
+  rawspeed::RawImageData *image_data = image.get ();
+  const rawspeed::RawImageType image_type = image_data->getDataType ();
+
+  const guchar *data_uncropped = image_data->getDataUncropped (0, 0);
+  const rawspeed::iPoint2D dimensions_uncropped = image_data->getUncroppedDim();
+
+  if (photos_rawspeed_is_normalized (image_type, filters, static_cast<guint32> (image_data->whitePoint)))
+    {
+      const Babl *format_original = photos_rawspeed_get_format (image_type, components);
+
+      GeglRectangle bbox_original;
+      gegl_rectangle_set (&bbox_original,
+                          0,
+                          0,
+                          static_cast<guint> (dimensions_uncropped.x),
+                          static_cast<guint> (dimensions_uncropped.y));
+
+      const gint stride_original = static_cast<gint> (image_data->pitch);
+      rawspeed::RawImage *image_original = new rawspeed::RawImage (image);
+
+      g_autoptr (GeglBuffer) buffer_original
+        = gegl_buffer_linear_new_from_data ((const gpointer) data_uncropped,
+                                            format_original,
+                                            &bbox_original,
+                                            stride_original,
+                                            reinterpret_cast<GDestroyNotify> (photos_rawspeed_destroy_image),
+                                            image_original);
+
+      ret_val = static_cast<GeglBuffer *> (g_object_ref (buffer_original));
+      return ret_val;
+    }
+
+  gfloat div[4];
+  gfloat sub[4];
+  const std::size_t black_level_separate_size = image_data->blackLevelSeparate.size ();
+
+  if (filters == 0)
+    {
+      const gfloat white = static_cast<gfloat> (image_data->whitePoint) / static_cast<gfloat> (G_MAXUINT16);
+      gfloat black = 0.0f;
+
+      for (std::size_t i = 0; i < black_level_separate_size; i++)
+        black += static_cast<gfloat> (image_data->blackLevelSeparate[i]) / static_cast<gfloat> (G_MAXUINT16);
+
+      black /= static_cast<gfloat> (black_level_separate_size);
+
+      for (guint i = 0; i < G_N_ELEMENTS (div); i++)
+        {
+          sub[i] = black;
+          div[i] = white - sub[i];
+        }
+    }
+  else
+    {
+      const gfloat white = static_cast<gfloat> (image_data->whitePoint);
+
+      for (guint i = 0; i < G_N_ELEMENTS (div); i++)
+        {
+          sub[i] = static_cast<gfloat> (image_data->blackLevelSeparate[i]);
+          div[i] = white - sub[i];
+        }
+    }
+
+  const Babl *format_output = photos_rawspeed_get_format (rawspeed::TYPE_FLOAT32, components);
+  const gint bpp_output = babl_format_get_bytes_per_pixel (format_output);
+
+  const rawspeed::iPoint2D dimensions_cropped = image_data->dim;
+  const gint height_input = dimensions_uncropped.y;
+  const gint width_input = dimensions_uncropped.x;
+
+  const rawspeed::iPoint2D crop_top_left = image_data->getCropOffset ();
+  const gint crop_x = crop_top_left.x;
+  const gint crop_y = crop_top_left.y;
+
+  const rawspeed::iPoint2D crop_bottom_right = dimensions_uncropped - dimensions_cropped - crop_top_left;
+  const gint crop_height = crop_bottom_right.y;
+  const gint crop_width = crop_bottom_right.x;
+
+  const gint height_output = height_input - (crop_y + crop_height);
+  const gint width_output = width_input - (crop_x + crop_width);
+
+  GeglRectangle bbox_output;
+  gegl_rectangle_set (&bbox_output, 0, 0, static_cast<guint> (width_output), static_cast<guint> 
(height_output));
+  g_message ("rawprepare: process: x, y, width, height: %d, %d, %d, %d",
+             bbox_output.x,
+             bbox_output.y,
+             bbox_output.width,
+             bbox_output.height);
+
+  g_autoptr (GeglBuffer) buffer_output = gegl_buffer_new (&bbox_output, format_output);
+
+  GeglRectangle bbox_tile;
+  g_object_get (buffer_output, "tile-height", &bbox_tile.height, "tile-width", &bbox_tile.width, NULL);
+
+  const gint tile_stride = bpp_output * bbox_tile.width;
+  g_autofree gpointer tile_memory = g_malloc0_n (bbox_tile.height, tile_stride);
+  gfloat *const out = static_cast<gfloat *> (tile_memory);
+
+  if (filters != 0 && components == 1 && image_type == rawspeed::TYPE_USHORT16)
+    {
+      const guint16 *const in = reinterpret_cast<const guint16 *> (data_uncropped);
+
+      for (bbox_tile.y = 0; bbox_tile.y < bbox_output.height; bbox_tile.y += bbox_tile.height)
+        {
+          for (bbox_tile.x = 0; bbox_tile.x < bbox_output.width; bbox_tile.x += bbox_tile.width)
+            {
+              for (gint j = 0, y = bbox_tile.y; j < bbox_tile.height; j++, y++)
+                {
+                  for (gint i = 0, x = bbox_tile.x; i < bbox_tile.width; i++, x++)
+                    {
+                      const gsize in_offset = static_cast<gsize> (crop_y + y) * static_cast<gsize> 
(width_input)
+                                              + static_cast<gsize> (crop_x + x);
+
+                      const gsize out_offset = static_cast<gsize> (j) * static_cast<gsize> (bbox_tile.width)
+                                               + static_cast<gsize> (i);
+
+                      const gint bayer_index = photos_rawspeed_get_2x2_bayer_index (crop_x, crop_y, x, y);
+
+                      out[out_offset] = (static_cast<gfloat> (in[in_offset]) - sub[bayer_index]) / 
div[bayer_index];
+
+                      if (x % 1000 == 0 && y % 1000 == 0)
+                        g_message ("rawprepare: process: (%d, %d) %d -> %f",
+                                   x, y, (gint) in[in_offset], out[out_offset]);
+                    }
+                }
+
+              gegl_buffer_set (buffer_output,
+                               &bbox_tile,
+                               0,
+                               format_output,
+                               tile_memory,
+                               tile_stride);
+            }
+        }
+    }
+  else if (filters != 0 && components == 1 && image_type == rawspeed::TYPE_FLOAT32)
+    {
+      const gfloat *const in = reinterpret_cast<const gfloat *> (data_uncropped);
+
+      for (bbox_tile.y = 0; bbox_tile.y < bbox_output.height; bbox_tile.y += bbox_tile.height)
+        {
+          for (bbox_tile.x = 0; bbox_tile.x < bbox_output.width; bbox_tile.x += bbox_tile.width)
+            {
+              for (gint j = 0, y = bbox_tile.y; j < bbox_tile.height; j++, y++)
+                {
+                  for (gint i = 0, x = bbox_tile.x; i < bbox_tile.width; i++, x++)
+                    {
+                      const gsize in_offset = static_cast<gsize> (crop_y + y) * static_cast<gsize> 
(width_input)
+                                              + static_cast<gsize> (crop_x + x);
+
+                      const gsize out_offset = static_cast<gsize> (j) * static_cast<gsize> (bbox_tile.width)
+                                               + static_cast<gsize> (i);
+
+                      const gint bayer_index = photos_rawspeed_get_2x2_bayer_index (crop_x, crop_y, x, y);
+
+                      out[out_offset] = (in[in_offset] - sub[bayer_index]) / div[bayer_index];
+                    }
+                }
+
+              gegl_buffer_set (buffer_output,
+                               &bbox_tile,
+                               0,
+                               format_output,
+                               tile_memory,
+                               tile_stride);
+            }
+        }
+    }
+  else
+    {
+      const gfloat *const in = reinterpret_cast<const gfloat *> (data_uncropped);
+
+      for (bbox_tile.y = 0; bbox_tile.y < bbox_output.height; bbox_tile.y += bbox_tile.height)
+        {
+          for (bbox_tile.x = 0; bbox_tile.x < bbox_output.width; bbox_tile.x += bbox_tile.width)
+            {
+              for (gint j = 0, y = bbox_tile.y; j < bbox_tile.height; j++, y++)
+                {
+                  for (gint i = 0, x = bbox_tile.x; i < bbox_tile.width; i++, x++)
+                    {
+                      for (gsize c = 0; c < components; c++)
+                        {
+                          const gsize in_offset = components
+                                                  * (static_cast<gsize> (crop_y + y)
+                                                     * static_cast<gsize> (width_input)
+                                                     + static_cast<gsize> (crop_x + x))
+                                                  + c;
+
+                          const gsize out_offset = components
+                                                   * (static_cast<gsize> (j) * static_cast<gsize> 
(bbox_tile.width)
+                                                      + static_cast<gsize> (i))
+                                                   + c;
+
+                          const gint bayer_index = photos_rawspeed_get_2x2_bayer_index (crop_x, crop_y, x, 
y);
+
+                          out[out_offset] = (in[in_offset] - sub[bayer_index]) / div[bayer_index];
+                        }
+                    }
+                }
+
+              gegl_buffer_set (buffer_output,
+                               &bbox_tile,
+                               0,
+                               format_output,
+                               tile_memory,
+                               tile_stride);
+                }
+            }
+    }
+
+  ret_val = static_cast<GeglBuffer *> (g_object_ref (buffer_output));
+  return ret_val;
+}
+
+
+static rawspeed::RawImage
+photos_rawspeed_decode_bytes_to_image (GBytes *bytes, GError **error)
+{
+  rawspeed::RawImage ret_val = rawspeed::RawImage::create (rawspeed::TYPE_USHORT16);
+
+  gsize count;
+  const guchar *data = (const guchar *) g_bytes_get_data (bytes, &count);
+
+  rawspeed::Buffer rs_buffer (data, count);
+  rawspeed::RawParser parser (&rs_buffer);
+
+  std::unique_ptr<rawspeed::RawDecoder> decoder;
+
+  try
+    {
+      decoder = parser.getDecoder(metadata.get());
+    }
+  catch (rawspeed::RawspeedException &e)
+    {
+      g_set_error (error, PHOTOS_ERROR, 0, e.what());
+      return ret_val;
+    }
+
+  decoder->failOnUnknown = true;
+
+  try
+    {
+      decoder->checkSupport (metadata.get());
+    }
+  catch (rawspeed::RawspeedException &e)
+    {
+      g_set_error (error, PHOTOS_ERROR, 0, e.what());
+      return ret_val;
+    }
+
+  try
+    {
+      decoder->decodeRaw ();
+      decoder->decodeMetaData (metadata.get());
+    }
+  catch (rawspeed::RawspeedException &e)
+    {
+      g_set_error (error, PHOTOS_ERROR, 0, e.what());
+      return ret_val;
+    }
+
+  ret_val = decoder->mRaw;
+  return ret_val;
+}
+
+
+GeglBuffer *
+photos_rawspeed_decode_bytes (GBytes *bytes, GError **error)
+{
+  GeglBuffer *ret_val = NULL;
+
+  if (!photos_rawspeed_ensure_metadata (error))
+    return ret_val;
+
+  rawspeed::RawImage image = photos_rawspeed_decode_bytes_to_image (bytes, error);
+  rawspeed::RawImageData *image_data = image.get ();
+  if (!image_data->isAllocated ())
+    return ret_val;
+
+  if (image_data->blackLevelSeparate[0] == -1
+      || image_data->blackLevelSeparate[1] == -1
+      || image_data->blackLevelSeparate[2] == -1
+      || image_data->blackLevelSeparate[3] == -1)
+    {
+      image_data->calculateBlackAreas ();
+    }
+
+  const rawspeed::RawImageType image_type = image_data->getDataType ();
+  if (image_type != rawspeed::TYPE_USHORT16 && image_type != rawspeed::TYPE_FLOAT32)
+    {
+      g_set_error (error, PHOTOS_ERROR, 0, _("Unsupported image type: %d"), image_type);
+      return ret_val;
+    }
+
+  const guint32 bpp = image_data->getBpp ();
+  if (bpp != sizeof (guint16) && bpp != sizeof (gfloat))
+    {
+      g_set_error (error, PHOTOS_ERROR, 0, _("Unsupported number of bytes per pixel: %" G_GUINT16_FORMAT), 
bpp);
+      return ret_val;
+    }
+
+  if (image_type == rawspeed::TYPE_USHORT16 && bpp != sizeof (guint16))
+    {
+      g_set_error (error,
+                   PHOTOS_ERROR,
+                   0,
+                   _("Unexpected number of bytes per pixel (%" G_GUINT16_FORMAT ") for a 16-bit image"),
+                   bpp);
+      return ret_val;
+    }
+
+  if (image_type == rawspeed::TYPE_FLOAT32 && bpp != sizeof (gfloat))
+    {
+      g_set_error (error,
+                   PHOTOS_ERROR,
+                   0,
+                   _("Unexpected number of bytes per pixel (%" G_GUINT16_FORMAT ") for a floating point 
image"),
+                   bpp);
+      return ret_val;
+    }
+
+  const guint32 cpp = image_data->getCpp ();
+  if (cpp != 1)
+    {
+      g_set_error (error,
+                   PHOTOS_ERROR,
+                   0,
+                   _("Unsupported number of components per pixel: %" G_GUINT16_FORMAT),
+                   bpp);
+      return ret_val;
+    }
+
+  const rawspeed::iPoint2D crop_top_left = image_data->getCropOffset ();
+
+  guint32 filters = image_data->cfa.getDcrawFilter ();
+  if (filters != 0 && filters != 9)
+    filters = rawspeed::ColorFilterArray::shiftDcrawFilter (filters, crop_top_left.x, crop_top_left.y);
+
+  g_autoptr (GeglBuffer) buffer_corrected = photos_rawspeed_correct_black_and_white_point (image, 1, 
filters);
+
+  ret_val = static_cast<GeglBuffer *> (g_object_ref (buffer_corrected));
+  return ret_val;
+}
diff --git a/src/photos-rawspeed.h b/src/photos-rawspeed.h
new file mode 100644
index 00000000..7b2bb185
--- /dev/null
+++ b/src/photos-rawspeed.h
@@ -0,0 +1,31 @@
+/*
+ * Photos - access, organize and share your photos on GNOME
+ * Copyright © 2019 Red Hat, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PHOTOS_RAWSPEED_H
+#define PHOTOS_RAWSPEED_H
+
+#include <gegl.h>
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+GeglBuffer      *photos_rawspeed_decode_bytes             (GBytes *bytes, GError **error);
+
+G_END_DECLS
+
+#endif /* PHOTOS_RAWSPEED_H */


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