[gimp] Bug 144454 - Loading and storing DICOM again looses important information



commit 55bc1fa84fd1dabfc286d7aee7c53e8fb0105bf6
Author: Sven Neumann <sven gimp org>
Date:   Mon May 10 20:43:12 2010 +0200

    Bug 144454 - Loading and storing DICOM again looses important information
    
    Retain dicom tags using parasites. Patch by Paul Epperson.

 devel-docs/parasites.txt     |    8 +
 plug-ins/common/file-dicom.c |  725 +++++++++++++++++++++++++++++++++++-------
 2 files changed, 619 insertions(+), 114 deletions(-)
---
diff --git a/devel-docs/parasites.txt b/devel-docs/parasites.txt
index 93e2eb5..40c2c1b 100644
--- a/devel-docs/parasites.txt
+++ b/devel-docs/parasites.txt
@@ -28,6 +28,7 @@ Global data follows no strict rules.
 "tiff"    : The standard GIMP TIFF plugin
 "jpeg"    : The standard GIMP JPEG plugin
 "png"     : The standard GIMP PNG plugin
+"dcm"     : The standard GIMP DICOM plugin
 "gimp"    : For common and standard parasites
 
 ------------------------------------------------------------------
@@ -215,6 +216,13 @@ Global data follows no strict rules.
         from GtkPageSetup. The content is identical to what is stored in
 	~/.gimp-2.x/print-page-setup.
 
+"dcm/XXXX-XXXX-AA" (IMAGE, PERSISTANT)
+        These parasites are stored by the Dicom plug-in and hold the DICOM
+        element information for that image. The format is raw binary data
+        as read from the original image.
+        where: XXXX is a 4-digit ascii encoded hexadecimal number
+               AA is a two character ascii value representing the Dicom
+                 element's Value Represenation (VR)
 
 ------------------------------------------------------------------
 *** KNOWN LAYER/DRAWABLE PARASITES:
diff --git a/plug-ins/common/file-dicom.c b/plug-ins/common/file-dicom.c
index 0cede18..58c857c 100644
--- a/plug-ins/common/file-dicom.c
+++ b/plug-ins/common/file-dicom.c
@@ -84,16 +84,9 @@ static void      add_tag_pointer       (GByteArray       *group_stream,
                                         const gchar      *value_rep,
                                         const guint8     *data,
                                         gint              length);
-static void      add_tag_string        (GByteArray       *group_stream,
-                                        gint              group,
-                                        gint              element,
-                                        const gchar      *value_rep,
-                                        const gchar      *s);
-static void      add_tag_int           (GByteArray       *group_stream,
-                                        gint              group,
-                                        gint              element,
-                                        const gchar      *value_rep,
-                                        gint              value);
+static GSList *  dicom_add_tags        (FILE *DICOM,
+                                        GByteArray *group_stream,
+                                        GSList *elements);
 static gboolean  write_group_to_file   (FILE             *DICOM,
                                         gint              group,
                                         GByteArray       *group_stream);
@@ -295,6 +288,23 @@ run (const gchar      *name,
   values[0].data.d_status = status;
 }
 
+/**
+ * add_parasites_to_image:
+ * @data: pointer to a GimpParasite to be attached to the image specified by @user_data.
+ * @user_data: pointer to the image_ID to which parasite @data should be added.
+ *
+ * Attaches parasite to image and also frees that parasite
+**/
+static void
+add_parasites_to_image (gpointer data,
+                        gpointer user_data)
+{
+  GimpParasite *parasite = (GimpParasite *)data;
+  gint32 *image_ID = (gint32 *)user_data;
+  gimp_image_parasite_attach (*image_ID, parasite);
+  gimp_parasite_free (parasite);
+}
+
 static gint32
 load_image (const gchar  *filename,
             GError      **error)
@@ -303,6 +313,7 @@ load_image (const gchar  *filename,
   gint32 volatile image_ID          = -1;
   gint32          layer_ID;
   GimpDrawable   *drawable;
+  GSList         *elements          = NULL;
   FILE           *DICOM;
   gchar           buf[500];    /* buffer for random things like scanning */
   DicomInfo      *dicominfo;
@@ -314,6 +325,7 @@ load_image (const gchar  *filename,
   gint            high_bit          = 0;
   guint8         *pix_buf           = NULL;
   gboolean        is_signed         = FALSE;
+  guint8          in_sequence       = 0;
 
   /* open the file */
   DICOM = g_fopen (filename, "rb");
@@ -433,7 +445,16 @@ load_image (const gchar  *filename,
 
       /* Sequence of items - just ignore the delimiters... */
       if (element_length == 0xffffffff)
-        continue;
+        {
+          in_sequence = 1;
+          continue;
+        }
+      /* End of Sequence tag */
+      if (tag == 0xFFFEE0DD)
+        {
+          in_sequence = 0;
+          continue;
+        }
 
       /* Sequence of items item tag... Ignore as well */
       if (tag == 0xFFFEE000)
@@ -454,6 +475,12 @@ load_image (const gchar  *filename,
       value = g_new0 (guint8, element_length + 4);
       fread (value, 1, element_length, DICOM);
 
+      /* ignore everything inside of a sequence */
+      if (in_sequence)
+        {
+          g_free (value);
+          continue;
+        }
       /* Some special casts that are used below */
       ctx_ul = *(guint32 *) value;
       ctx_us = *(guint16 *) value;
@@ -511,6 +538,25 @@ load_image (const gchar  *filename,
         }
       else
         {
+          /* save this element to a parasite for later writing */
+          GimpParasite *parasite;
+          gchar pname[255];
+          /* all elements are retrievable using gimp_parasite_list() */
+          g_snprintf(pname,sizeof(pname),"dcm/%04x-%04x-%s",group_word,element_word,value_rep);
+          if ((parasite = gimp_parasite_new (pname,
+                                        GIMP_PARASITE_PERSISTENT,
+                                        element_length, value)))
+            {
+              /*
+               * at this point, the image has not yet been created, so image_ID is not valid.
+               * keep the parasite around until we're able to attach it.
+               */
+              /* gimp_image_parasite_attach (image_ID, parasite);*/
+              /* gimp_parasite_free (parasite); */
+              /* add to our list of parasites to be added (prepending for speed. we'll reverse it later) */
+              elements = g_slist_prepend (elements,parasite);
+            }
+
           g_free (value);
         }
     }
@@ -571,6 +617,14 @@ load_image (const gchar  *filename,
 
   dicom_loader (pix_buf, dicominfo, &pixel_rgn);
 
+  if (elements)
+    {
+      /* flip the parasites back around into the order they were created (read from the file) */
+      elements = g_slist_reverse (elements);
+      /* and add each one to the image */
+      g_slist_foreach (elements,add_parasites_to_image,(gpointer)&image_ID);
+      g_slist_free (elements);
+    }
   /* free the structures */
   g_free (pix_buf);
   g_free (dicominfo);
@@ -578,8 +632,9 @@ load_image (const gchar  *filename,
   /* close the file */
   fclose (DICOM);
 
-  /* Tell GIMP to display the image. */
-  gimp_drawable_flush (drawable);
+  /* Tell GIMP to display the image and detach. */
+  /* gimp_drawable_flush (drawable); -- implicitely done via gimp_drawable_detach() */
+  gimp_drawable_detach (drawable);
 
   return image_ID;
 }
@@ -737,6 +792,446 @@ toggle_endian2 (guint16 *buf16,
     }
 }
 
+typedef struct _DicomElement {
+    guint16  group_word;
+    guint16  element_word;
+    gchar    value_rep[3];
+    guint32  element_length;
+    guint8   *value;
+    gint     free;
+} DICOMELEMENT;
+
+/**
+ * dicom_add_element:
+ * @elements: head of a GSList containing DICOMELEMENT structures.
+ * @group_word: Dicom Element group number for the tag to be added to @elements.
+ * @element_word: Dicom Element element number for the tag to be added to @elements.
+ * @value_rep: a string representing the Dicom VR for the new element.
+ * @value: a pointer to an integer containing the value for the element to be created.
+ *
+ * Creates a DICOMELEMENT object and inserts it into @elements.
+ *
+ * Return value: the new head of @elements
+**/
+static GSList *
+dicom_add_element (GSList *elements,
+                   guint16 group_word,
+                   guint16 element_word,
+                   gchar *value_rep,
+                   guint32 element_length,
+                   guint8 *value,
+                   gint copy)
+{
+  DICOMELEMENT *element = g_new0 (DICOMELEMENT, 1);
+  if (element)
+    {
+      element->free = 0;
+      if (copy)
+        {
+          guint8 *v = g_new (guint8,element_length);
+          if (v)
+            {
+              memcpy (v,value,element_length);
+              value = v;
+              element->free = 1;
+            }
+        }
+      element->group_word = group_word;
+      element->element_word = element_word;
+      strncpy (element->value_rep,value_rep,sizeof (element->value_rep));
+      element->element_length = element_length;
+      element->value = value;
+      elements = g_slist_prepend (elements,element);
+    }
+  return elements;
+}
+
+/**
+ * dicom_add_element_int:
+ * @elements: head of a GSList containing DICOMELEMENT structures.
+ * @group_word: Dicom Element group number for the tag to be added to @elements.
+ * @element_word: Dicom Element element number for the tag to be added to @elements.
+ * @value_rep: a string representing the Dicom VR for the new element.
+ * @value: a pointer to an integer containing the value for the element to be created.
+ *
+ * Creates a DICOMELEMENT object from the passed integer pointer and adds it to @elements.
+ * Note: value should be the address of a guint16 for @value_rep==%US or guint32 for other values of @value_rep
+ *
+ * Return value: the new head of @elements
+ */
+static GSList *
+dicom_add_element_int (GSList *elements,
+                       guint16 group_word,
+                       guint16 element_word,
+                       gchar *value_rep,
+                       guint8 *value)
+{
+  guint32 len;
+
+  if (strcmp (value_rep, "US") == 0)
+    len = 2;
+  else
+    len = 4;
+
+  return dicom_add_element (elements,group_word,element_word,value_rep,len,value,0);
+}
+
+/**
+ * dicom_element_done:
+ * @data: pointer to a DICOMELEMENT structure which is to be destroyed.
+ * @user_data: unused.
+ *
+ * Destroys the DICOMELEMENT passed as @data
+**/
+static void
+dicom_element_done (gpointer data,
+                    gpointer user_data)
+{
+  if (data)
+    {
+      DICOMELEMENT *e = (DICOMELEMENT *)data;
+      if (e->free)
+        {
+          g_free (e->value);
+        }
+      g_free (data);
+    }
+} 
+
+/**
+ * dicom_elements_destroy:
+ * @elements: head of a GSList containing DICOMELEMENT structures.
+ *
+ * Destroys the list of DICOMELEMENTs
+**/
+static void
+dicom_elements_destroy (GSList *elements)
+{
+  if (elements)
+    {
+      g_slist_foreach (elements,dicom_element_done,NULL);
+      g_slist_free (elements);
+    }
+}
+
+/**
+ * dicom_destroy_element:
+ * @elements: head of a GSList containing DICOMELEMENT structures.
+ * @ele: a DICOMELEMENT structure to be removed from @elements
+ *
+ * Removes the specified DICOMELEMENT from @elements and Destroys it
+ *
+ * Return value: the new head of @elements
+**/
+static GSList *
+dicom_destroy_element (GSList *elements,
+                       DICOMELEMENT *ele)
+{
+  if (ele)
+    {
+      elements = g_slist_remove_all (elements,ele);
+      if (ele->free)
+        {
+          g_free (ele->value);
+        }
+      g_free (ele);
+    }
+
+  return elements;
+}
+
+/**
+ * dicom_elements_compare:
+ * @a: pointer to a DICOMELEMENT structure.
+ * @b: pointer to a DICOMELEMENT structure.
+ * 
+ * Determines the equality of @a and @b as strcmp
+ *
+ * Return value: an integer indicating the equality of @a and @b.
+**/ 
+static gint
+dicom_elements_compare (gconstpointer a,
+                        gconstpointer b)
+{
+  DICOMELEMENT *e1 = (DICOMELEMENT *)a;
+  DICOMELEMENT *e2 = (DICOMELEMENT *)b;
+
+  if (e1->group_word == e2->group_word)
+    {
+      if (e1->element_word == e2->element_word)
+        {
+          return 0;
+        }
+      else if (e1->element_word > e2->element_word)
+        {
+          return 1;
+        }
+      else
+        {
+          return -1;
+        }
+    }
+  else if (e1->group_word < e2->group_word)
+    {
+      return -1;
+    }
+  return 1;
+}
+
+/**
+ * dicom_element_find_by_num:
+ * @head: head of a GSList containing DICOMELEMENT structures.
+ * @group_word: Dicom Element group number for the tag to be found.
+ * @element_word: Dicom Element element number for the tag to be found.
+ *
+ * Retrieves the specified DICOMELEMENT from @head, if available.
+ *
+ * Return value: a DICOMELEMENT matching the specified group,element, or NULL if the specified element was not found.
+**/
+static DICOMELEMENT *
+dicom_element_find_by_num (GSList *head,
+                           guint16 group_word,
+                           guint16 element_word)
+{
+  DICOMELEMENT data = {group_word,element_word,"",0,NULL};
+  GSList *ele = g_slist_find_custom (head,&data,dicom_elements_compare);
+  return ele?ele->data:NULL;
+}
+
+/**
+ * dicom_get_elements_list:
+ * @image_ID: the image_ID from which to read parasites in order to retrieve the dicom elements
+ *
+ * Reads all DICOMELEMENTs from the specified image's parasites.
+ *
+ * Return value: a GSList of all known dicom elements
+**/
+static GSList *
+dicom_get_elements_list (gint32 image_ID)
+{
+  GSList *elements = NULL;
+  GimpParasite *parasite;
+  gchar        **parasites = NULL;
+  gint           count = 0;
+
+  gimp_image_parasite_list (image_ID,&count,&parasites);
+  if (parasites && count > 0)
+    {
+      gint i;
+      for (i = 0; i < count; i++)
+        {
+          if (strncmp (parasites[i],"dcm",3) == 0)
+            {
+              parasite = gimp_image_parasite_find (image_ID,parasites[i]);
+              if (parasite)
+                {
+                  gchar buf[1024];
+                  gchar *ptr1;
+                  gchar *ptr2;
+                  gchar value_rep[3]="";
+                  guint16 group_word=0;
+                  guint16 element_word=0;
+
+                  /* sacrificial buffer */
+                  strncpy (buf,parasites[i],sizeof (buf));
+
+                  /* buf should now hold a string of the form dcm/XXXX-XXXX-AA
+                   * where XXXX are Hex values for group and element respectively
+                   * AA is the Value Representation of the element
+                   *
+                   * start off by jumping over the dcm/ to the first Hex blob
+                   */
+                  ptr1 = strchr (buf,'/');
+                  if (ptr1)
+                    {
+                      gchar t[15];
+                      ptr1++;
+                      ptr2 = strchr (ptr1,'-');
+                      if (ptr2)
+                        {
+                          *ptr2 = '\0';
+                        }
+                      g_snprintf (t,sizeof (t),"0x%s",ptr1);
+                      group_word = (guint16)g_ascii_strtoull (t,NULL,16);
+                      ptr1 = ptr2+1;
+                    }
+                  /* now get the second Hex blob */
+                  if (ptr1)
+                    {
+                      gchar t[15];
+                      ptr2 = strchr (ptr1,'-');
+                      if (ptr2)
+                        {
+                          *ptr2 = '\0';
+                        }
+                      g_snprintf (t,sizeof (t),"0x%s",ptr1);
+                      element_word = (guint16)g_ascii_strtoull (t,NULL,16);
+                      ptr1 = ptr2+1;
+                    }
+                  /* and lastly, the VR */
+                  if (ptr1)
+                    {
+                      strncpy (value_rep,ptr1,sizeof (value_rep));
+                    }
+                  /*
+                   * If all went according to plan, we should be able to add this element
+                   */
+                  if (group_word > 0 && element_word > 0)
+                    {
+                      /* create a copy of the parasite's data so we can drop the const qualifier */
+                      guint32 len = gimp_parasite_data_size (parasite);
+                      if (len > 0)
+                        {
+                          guint8 *val = g_new0 (guint8,len);
+                          if (val)
+                            {
+                              memcpy (val,gimp_parasite_data (parasite),len);
+                              /* and add the dicom element, asking to have it's value copied for later garbage collection */
+                              elements = dicom_add_element (elements,group_word,element_word,value_rep,len,val,1);
+                              g_free (val);
+                            }
+                        }
+                    }
+                  gimp_parasite_free (parasite);
+                }
+            }
+          /* make sure we free each individual parasite name, in addition to the array of names */
+          g_free (parasites[i]);
+        }
+    }
+  /* cleanup the array of names */
+  if (parasites)
+    {
+      g_free (parasites);
+    }
+  return elements;
+}
+
+/**
+ * dicom_remove_gimp_specified_elements:
+ * @elements:  GSList to remove elements from
+ * @samples_per_pixel: samples per pixel of the image to be written.
+ *                     if set to %3 the planar configuration for color images
+ *                     will also be removed from @elements
+ * 
+ * Removes certain DICOMELEMENTs from the elements list which are specific to the output of this plugin.
+ *
+ * Return value: the new head of @elements
+**/
+static GSList *
+dicom_remove_gimp_specified_elements (GSList *elements,
+                                      gint samples_per_pixel)
+{
+  DICOMELEMENT remove[] = {
+    /* Image presentation group */
+    /* Samples per pixel */
+    {0x0028, 0x0002, "", 0, NULL},
+    /* Photometric interpretation */
+    {0x0028, 0x0004, "", 0, NULL},
+    /* rows */
+    {0x0028, 0x0010, "", 0, NULL},
+    /* columns */
+    {0x0028, 0x0011, "", 0, NULL},
+    /* Bits allocated */
+    {0x0028, 0x0100, "", 0, NULL},
+    /* Bits Stored */
+    {0x0028, 0x0101, "", 0, NULL},
+    /* High bit */
+    {0x0028, 0x0102, "", 0, NULL},
+    /* Pixel representation */
+    {0x0028, 0x0103, "", 0, NULL},
+
+    {0,0,"",0,NULL}
+  };
+  DICOMELEMENT *ele;
+  gint i;
+
+  /*
+   * Remove all Dicom elements which will be set as part of the writing of the new file
+   */
+  for (i=0; remove[i].group_word > 0;i++)
+    {
+      if ((ele = dicom_element_find_by_num (elements,remove[i].group_word,remove[i].element_word)))
+        {
+          elements = dicom_destroy_element (elements,ele);
+        }
+    }
+  /* special case - allow this to be overwritten if necessary */
+  if (samples_per_pixel == 3)
+    {
+      /* Planar configuration for color images */
+      if ((ele = dicom_element_find_by_num (elements,0x0028,0x0006)))
+        {
+          elements = dicom_destroy_element (elements,ele);
+        }
+    }
+  return elements;
+}
+
+/**
+ * dicom_ensure_required_elements_present:
+ * @elements:  GSList to remove elements from
+ * @today_string: string containing today's date in DICOM format. This is used to default any required Dicom elements of date type to today's date.
+ *
+ * Defaults DICOMELEMENTs to the values set by previous version of this plugin, but only if they do not already exist.
+ *
+ * Return value: the new head of @elements
+**/
+static GSList *
+dicom_ensure_required_elements_present (GSList *elements,
+                                        gchar *today_string)
+{
+  DICOMELEMENT defaults[] = {
+              /* Meta element group */
+              /* 0002,0001 - File Meta Information Version */
+              {0x0002,0x0001,"OB",2,(guint8 *)"\0\1"},
+              /* 0002,0010 - Transfer syntax uid */
+              {0x0002,0x0001,"UI",strlen ("1.2.840.10008.1.2.1"),(guint8 *)"1.2.840.10008.1.2.1"},
+              /* 0002,0013 - Implementation version name */
+              {0x0002,0x0013,"SH",strlen ("Gimp Dicom Plugin 1.0"),(guint8 *)"Gimp Dicom Plugin 1.0"},
+              /* Identifying group */
+              /* Study date */
+              {0x0008,0x0020,"DA",strlen (today_string),(guint8 *)today_string},
+              /* Series date */
+              {0x0008,0x0021,"DA",strlen (today_string),(guint8 *)today_string},
+              /* Acquisition date */
+              {0x0008,0x0022,"DA",strlen (today_string),(guint8 *)today_string},
+              /* Content Date */
+              {0x0008,0x0023,"DA",strlen (today_string),(guint8 *)today_string},
+              /* Modality - I have to add something.. */
+              {0x0008,0x0060,"CS",strlen ("MR"),(guint8 *)"MR"},
+              /* Patient group */
+              /* Patient name */
+              {0x0010, 0x0010, "PN", strlen ("DOE^WILBER"), (guint8 *)"DOE^WILBER"},
+              /* Patient ID */
+              {0x0010, 0x0020, "CS", strlen ("314159265"), (guint8 *)"314159265"},
+              /* Patient Birth date */
+              {0x0010, 0x0030, "DA", strlen (today_string), (guint8 *)today_string},
+              /* Patient sex */
+              {0x0010, 0x0040, "CS", strlen (""), (guint8 *)"" /* unknown */},
+              /* Relationship group */
+              /* Instance number */
+              {0x0020, 0x0013, "IS", strlen ("1"), (guint8 *)"1"},
+
+              {0,0,"",0,NULL}
+  };
+  gint i;
+
+  /*
+   * Make sure that all of the default elements have a value
+   */
+  for (i=0; defaults[i].group_word > 0;i++)
+    {
+      if (dicom_element_find_by_num (elements,defaults[i].group_word,defaults[i].element_word) == NULL)
+        {
+          elements = dicom_add_element (elements,defaults[i].group_word,defaults[i].element_word,
+                                        defaults[i].value_rep,defaults[i].element_length,defaults[i].value,0);
+        }
+    }
+
+  return elements;
+}
+
 /* save_image() saves an image in the dicom format. The DICOM format
  * requires a lot of tags to be set. Some of them have real uses, others
  * must just be filled with dummy values.
@@ -752,12 +1247,17 @@ save_image (const gchar  *filename,
   GimpDrawable  *drawable;
   GimpPixelRgn   pixel_rgn;
   GByteArray    *group_stream;
+  GSList        *elements = NULL;
   gint           group;
   GDate         *date;
   gchar          today_string[16];
   gchar         *photometric_interp;
   gint           samples_per_pixel;
   gboolean       retval = TRUE;
+  guint16        zero = 0;
+  guint16        seven = 7;
+  guint16        eight = 8;
+  guchar        *src = NULL;
 
   drawable_type = gimp_drawable_type (drawable_ID);
   drawable = gimp_drawable_get (drawable_ID);
@@ -814,101 +1314,129 @@ save_image (const gchar  *filename,
 
   group_stream = g_byte_array_new ();
 
-  /* Meta element group */
-  group = 0x0002;
-  /* 0002,0001 - File Meta Information Version */
-  add_tag_pointer (group_stream, group, 0x0001, "OB",
-                   (const guint8 *) "\0\1", 2);
-  /* 0002,0010 - Transfer syntax uid */
-  add_tag_string (group_stream, group, 0x0010, "UI", "1.2.840.10008.1.2.1");
-  /* 0002,0013 - Implementation version name */
-  add_tag_string (group_stream, group, 0x0013, "SH", "Gimp Dicom Plugin 1.0");
-  write_group_to_file (DICOM, group, group_stream);
-
-  /* Identifying group */
-  group = 0x0008;
-  /* Study date */
-  add_tag_string (group_stream, group, 0x0020, "DA", today_string);
-  /* Series date */
-  add_tag_string (group_stream, group, 0x0021, "DA", today_string);
-  /* Acquisition date */
-  add_tag_string (group_stream, group, 0x0022, "DA", today_string);
-  /* Content Date */
-  add_tag_string (group_stream, group, 0x0023, "DA", today_string);
-  /* Modality - I have to add something.. */
-  add_tag_string (group_stream, group, 0x0060, "CS", "MR");
-  write_group_to_file (DICOM, group, group_stream);
-
-  /* Patient group */
-  group = 0x0010;
-  /* Patient name */
-  add_tag_string (group_stream, group, 0x0010, "PN", "Wilber Doe");
-  /* Patient ID */
-  add_tag_string (group_stream, group, 0x0020, "CS", "314159265");
-  /* Patient Birth date */
-  add_tag_string (group_stream, group, 0x0030, "DA", today_string);
-  /* Patient sex */
-  add_tag_string (group_stream, group, 0x0040, "CS", "" /* unknown */);
-  write_group_to_file (DICOM, group, group_stream);
-
-  /* Relationship group */
-  group = 0x0020;
-  /* Instance number */
-  add_tag_string (group_stream, group, 0x0013, "IS", "1");
-  write_group_to_file (DICOM, group, group_stream);
+  elements = dicom_get_elements_list (image_ID);
+  if (0/*replaceElementsList*/)
+    {
+      /* to do */
+    }
+  else if (1/*insist_on_basic_elements*/)
+    {
+      elements = dicom_ensure_required_elements_present (elements,today_string);
+    }
+
+  /*
+   * Set value of custom elements
+   */
+  elements = dicom_remove_gimp_specified_elements (elements,samples_per_pixel);
 
   /* Image presentation group */
   group = 0x0028;
   /* Samples per pixel */
-  add_tag_int   (group_stream, group, 0x0002, "US", samples_per_pixel);
+  elements = dicom_add_element_int (elements,group,0x0002,"US",(guint8 *)&samples_per_pixel);
   /* Photometric interpretation */
-  add_tag_string (group_stream, group, 0x0004, "CS", photometric_interp);
+  elements = dicom_add_element (elements,group,0x0004,"CS",strlen (photometric_interp),(guint8 *)photometric_interp,0);
   /* Planar configuration for color images */
   if (samples_per_pixel == 3)
-    add_tag_int (group_stream, group, 0x0006, "US", 0);
+    elements = dicom_add_element_int (elements,group,0x0006,"US",(guint8 *)&zero);
   /* rows */
-  add_tag_int   (group_stream, group, 0x0010, "US", drawable->height);
+  elements = dicom_add_element_int (elements, group, 0x0010, "US", (guint8 *)&(drawable->height));
   /* columns */
-  add_tag_int   (group_stream, group, 0x0011, "US", drawable->width);
+  elements = dicom_add_element_int (elements, group, 0x0011, "US", (guint8 *)&(drawable->width));
   /* Bits allocated */
-  add_tag_int   (group_stream, group, 0x0100, "US", 8);
+  elements = dicom_add_element_int (elements, group, 0x0100, "US", (guint8 *)&eight);
   /* Bits Stored */
-  add_tag_int   (group_stream, group, 0x0101, "US", 8);
+  elements = dicom_add_element_int (elements, group, 0x0101, "US", (guint8 *)&eight);
   /* High bit */
-  add_tag_int   (group_stream, group, 0x0102, "US", 7);
+  elements = dicom_add_element_int (elements, group, 0x0102, "US", (guint8 *)&seven);
   /* Pixel representation */
-  add_tag_int   (group_stream, group, 0x0103, "US", 0);
-  write_group_to_file (DICOM, group, group_stream);
+  elements = dicom_add_element_int (elements, group, 0x0103, "US", (guint8 *)&zero);
 
   /* Pixel data */
   group = 0x7fe0;
-  {
-    guchar *src;
-
-    src = g_new (guchar,
-                 drawable->height * drawable->width * samples_per_pixel);
-
-    gimp_pixel_rgn_init (&pixel_rgn, drawable,
-                         0, 0, drawable->width, drawable->height,
-                         FALSE, FALSE);
-    gimp_pixel_rgn_get_rect (&pixel_rgn,
-                             src, 0, 0, drawable->width, drawable->height);
-    add_tag_pointer (group_stream, group, 0x0010, "OW", (guint8 *) src,
-                     drawable->width * drawable->height * samples_per_pixel);
-
-    g_free (src);
-  }
-
-  retval = write_group_to_file (DICOM, group, group_stream);
+  src = g_new (guchar,
+               drawable->height * drawable->width * samples_per_pixel);
+  if (src)
+    {
+      gimp_pixel_rgn_init (&pixel_rgn, drawable,
+                           0, 0, drawable->width, drawable->height,
+                           FALSE, FALSE);
+      gimp_pixel_rgn_get_rect (&pixel_rgn,
+                               src, 0, 0, drawable->width, drawable->height);
+      elements = dicom_add_element (elements,group,0x0010,"OW",
+                                    drawable->width * drawable->height * samples_per_pixel,
+                                    (guint8 *)src,0);
+
+      elements = dicom_add_tags (DICOM,group_stream,elements);
+
+      g_free (src);
+    }
+  else
+    {
+      retval = FALSE;
+    }
 
   fclose (DICOM);
 
+  dicom_elements_destroy (elements);
   g_byte_array_free (group_stream, TRUE);
   gimp_drawable_detach (drawable);
 
   return retval;
 }
 
+/**
+ * dicom_print_tags:
+ * @data: pointer to a DICOMELEMENT structure which is to be written to file
+ * @user_data: structure containing state information and output parameters
+ * 
+ * Writes the specified DICOMELEMENT to @user_data's group_stream member.
+ * Between groups, flushes the group_stream to @user_data's DICOM member.
+ */
+static void
+dicom_print_tags(gpointer data,
+                 gpointer user_data)
+{
+  struct {
+    FILE *DICOM;
+    GByteArray *group_stream;
+    gint last_group;
+  } *d = user_data;
+  DICOMELEMENT *e = (DICOMELEMENT *)data;
+  if (d->last_group >= 0 && e->group_word != d->last_group)
+    {
+      write_group_to_file (d->DICOM, d->last_group, d->group_stream);
+    }
+
+  add_tag_pointer (d->group_stream,e->group_word,e->element_word,e->value_rep,e->value,e->element_length);
+  d->last_group = e->group_word;
+}
+
+/**
+ * dicom_add_tags:
+ * @DICOM: File pointer to which @elements should be written.
+ * @group_stream: byte array used for staging Dicom Element groups before flushing them to disk.
+ * @elements:  GSList container the Dicom Element elements from
+ * 
+ * Writes all Dicom tags in @elements to the file @DICOM
+ * 
+ * Return value: the new head of @elements
+**/
+static GSList *
+dicom_add_tags(FILE *DICOM,
+              GByteArray *group_stream,
+              GSList *elements)
+{
+  struct {
+    FILE *DICOM;
+    GByteArray *group_stream;
+    gint last_group;
+  } data = {DICOM,group_stream,-1};
+  elements = g_slist_sort (elements,dicom_elements_compare);
+  g_slist_foreach (elements,dicom_print_tags,&data);
+  /* make sure that the final group is written to the file */
+  write_group_to_file (data.DICOM, data.last_group, data.group_stream);
+  return elements;
+}
 /* add_tag_pointer () adds to the group_stream one single value with its
  * corresponding value_rep. Note that we use "explicit VR".
  */
@@ -986,37 +1514,6 @@ add_tag_pointer (GByteArray   *group_stream,
     }
 }
 
-/* Convenience function for adding a string to the dicom stream */
-static void
-add_tag_string (GByteArray  *group_stream,
-                gint         group,
-                gint         element,
-                const gchar *value_rep,
-                const gchar *s)
-{
-  add_tag_pointer (group_stream,
-                   group, element, value_rep, (const guint8 *) s, strlen (s));
-}
-
-/* Convenience function for adding an integer to the dicom stream */
-static void
-add_tag_int (GByteArray  *group_stream,
-             gint         group,
-             gint         element,
-             const gchar *value_rep,
-             gint         value)
-{
-  gint len;
-
-  if (strcmp (value_rep, "US") == 0)
-    len = 2;
-  else
-    len = 4;
-
-  add_tag_pointer (group_stream,
-                   group, element, value_rep, (const guint8 *) &value, len);
-}
-
 /* Once a group has been built it has to be wrapped with a meta-group
  * tag before it is written to the DICOM file. This is done by
  * write_group_to_file.



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