diff --git a/devel-docs/parasites.txt b/devel-docs/parasites.txt index 93e2eb5f51..40c2c1b695 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 0cede181fa..58c857ce7f 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,¶sites); + 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); + 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); + } - /* 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); + /* + * 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); + 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); - src = g_new (guchar, - drawable->height * drawable->width * samples_per_pixel); + elements = dicom_add_tags (DICOM,group_stream,elements); - 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); + 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.