file-dicom.c 56.9 KB
Newer Older
1
/* GIMP - The GNU Image Manipulation Program
2
3
4
 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
 * PNM reading and writing code Copyright (C) 1996 Erik Nygren
 *
5
 * This program is free software: you can redistribute it and/or modify
6
 * it under the terms of the GNU General Public License as published by
7
 * the Free Software Foundation; either version 3 of the License, or
8
9
10
11
12
13
14
15
 * (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
16
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
17
18
19
20
 */

/*
 * The dicom reading and writing code was written from scratch
21
 * by Dov Grobgeld.  (dov.grobgeld@gmail.com).
22
23
24
25
 */

#include "config.h"

26
#include <errno.h>
27
#include <string.h>
Sven Neumann's avatar
Sven Neumann committed
28
#include <time.h>
29

30
31
#include <glib/gstdio.h>

32
33
34
35
36
37
#include <libgimp/gimp.h>
#include <libgimp/gimpui.h>

#include "libgimp/stdplugins-intl.h"


38
39
#define LOAD_PROC      "file-dicom-load"
#define SAVE_PROC      "file-dicom-save"
40
#define PLUG_IN_BINARY "file-dicom"
41
#define PLUG_IN_ROLE   "gimp-file-dicom"
42
43


44
45
46
47
48
49
50
51
/* A lot of Dicom images are wrongly encoded. By guessing the endian
 * we can get around this problem.
 */
#define GUESS_ENDIAN 1

/* Declare local data types */
typedef struct _DicomInfo
{
David Odin's avatar
David Odin committed
52
53
54
  guint      width, height;      /* The size of the image                  */
  gint       maxval;             /* For 16 and 24 bit image files, the max
                                    value which we need to normalize to    */
55
56
  gint       samples_per_pixel;  /* Number of image planes (0 for pbm)     */
  gint       bpp;
57
58
59
  gint       bits_stored;
  gint       high_bit;
  gboolean   is_signed;
60
  gboolean   planar;
61
62
} DicomInfo;

63
64
65
66
67

typedef struct _Dicom      Dicom;
typedef struct _DicomClass DicomClass;

struct _Dicom
68
{
69
  GimpPlugIn      parent_instance;
70
71
};

72
struct _DicomClass
73
{
74
75
  GimpPlugInClass parent_class;
};
76
77


78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
#define DICOM_TYPE  (dicom_get_type ())
#define DICOM (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), DICOM_TYPE, Dicom))

GType                   dicom_get_type         (void) G_GNUC_CONST;

static GList          * dicom_query_procedures (GimpPlugIn           *plug_in);
static GimpProcedure  * dicom_create_procedure (GimpPlugIn           *plug_in,
                                                const gchar          *name);

static GimpValueArray * dicom_load             (GimpProcedure        *procedure,
                                                GimpRunMode           run_mode,
                                                GFile                *file,
                                                const GimpValueArray *args,
                                                gpointer              run_data);
static GimpValueArray * dicom_save             (GimpProcedure        *procedure,
                                                GimpRunMode           run_mode,
                                                GimpImage            *image,
95
96
                                                gint                  n_drawables,
                                                GimpDrawable        **drawables,
97
98
99
100
                                                GFile                *file,
                                                const GimpValueArray *args,
                                                gpointer              run_data);

101
static GimpImage      * load_image             (GFile                *file,
102
                                                GError              **error);
103
static gboolean         save_image             (GFile                *file,
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
                                                GimpImage            *image,
                                                GimpDrawable         *drawable,
                                                GError              **error);
static void             dicom_loader           (guint8               *pix_buf,
                                                DicomInfo            *info,
                                                GeglBuffer           *buffer);
static void             guess_and_set_endian2  (guint16              *buf16,
                                                gint                  length);
static void             toggle_endian2         (guint16              *buf16,
                                                gint                  length);
static void             add_tag_pointer        (GByteArray           *group_stream,
                                                gint                  group,
                                                gint                  element,
                                                const gchar          *value_rep,
                                                const guint8         *data,
                                                gint                  length);
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);


G_DEFINE_TYPE (Dicom, dicom, GIMP_TYPE_PLUG_IN)

GIMP_MAIN (DICOM_TYPE)

132
133

static void
134
dicom_class_init (DicomClass *klass)
135
{
136
  GimpPlugInClass *plug_in_class = GIMP_PLUG_IN_CLASS (klass);
137

138
139
140
  plug_in_class->query_procedures = dicom_query_procedures;
  plug_in_class->create_procedure = dicom_create_procedure;
}
141

142
143
144
145
static void
dicom_init (Dicom *dicom)
{
}
146

147
148
149
150
static GList *
dicom_query_procedures (GimpPlugIn *plug_in)
{
  GList *list = NULL;
151

152
153
  list = g_list_append (list, g_strdup (LOAD_PROC));
  list = g_list_append (list, g_strdup (SAVE_PROC));
154

155
156
  return list;
}
157

158
159
160
161
162
static GimpProcedure *
dicom_create_procedure (GimpPlugIn  *plug_in,
                        const gchar *name)
{
  GimpProcedure *procedure = NULL;
163

164
165
  if (! strcmp (name, LOAD_PROC))
    {
166
167
      procedure = gimp_load_procedure_new (plug_in, name,
                                           GIMP_PDB_PROC_TYPE_PLUGIN,
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
                                           dicom_load, NULL, NULL);

      gimp_procedure_set_menu_label (procedure, N_("DICOM image"));

      gimp_procedure_set_documentation (procedure,
                                        "Loads files of the dicom file format",
                                        "Load a file in the DICOM standard "
                                        "format. The standard is defined at "
                                        "http://medical.nema.org/. The plug-in "
                                        "currently only supports reading "
                                        "images with uncompressed pixel "
                                        "sections.",
                                        name);
      gimp_procedure_set_attribution (procedure,
                                      "Dov Grobgeld",
                                      "Dov Grobgeld <dov@imagic.weizmann.ac.il>",
                                      "2003");

      gimp_file_procedure_set_mime_types (GIMP_FILE_PROCEDURE (procedure),
                                          "image/x-dcm");
      gimp_file_procedure_set_extensions (GIMP_FILE_PROCEDURE (procedure),
                                          "dcm,dicom");
      gimp_file_procedure_set_magics (GIMP_FILE_PROCEDURE (procedure),
                                      "128,string,DICM");
192
    }
193
  else if (! strcmp (name, SAVE_PROC))
194
    {
195
196
      procedure = gimp_save_procedure_new (plug_in, name,
                                           GIMP_PDB_PROC_TYPE_PLUGIN,
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
                                           dicom_save, NULL, NULL);

      gimp_procedure_set_image_types (procedure, "RGB, GRAY");

      gimp_procedure_set_menu_label (procedure,
                                     N_("Digital Imaging and Communications in "
                                        "Medicine image"));

      gimp_procedure_set_documentation (procedure,
                                        "Save file in the DICOM file format",
                                        "Save an image in the medical "
                                        "standard DICOM image formats. "
                                        "The standard is defined at "
                                        "http://medical.nema.org/. The file "
                                        "format is defined in section 10 of "
                                        "the standard. The files are saved "
                                        "uncompressed and the compulsory DICOM "
                                        "tags are filled with default dummy "
                                        "values.",
                                        name);
      gimp_procedure_set_attribution (procedure,
                                      "Dov Grobgeld",
                                      "Dov Grobgeld <dov@imagic.weizmann.ac.il>",
                                      "2003");

      gimp_file_procedure_set_mime_types (GIMP_FILE_PROCEDURE (procedure),
                                          "image/x-dcm");
      gimp_file_procedure_set_extensions (GIMP_FILE_PROCEDURE (procedure),
                                          "dcm,dicom");
    }
227

228
229
  return procedure;
}
230

231
232
233
234
235
236
237
238
239
240
static GimpValueArray *
dicom_load (GimpProcedure        *procedure,
            GimpRunMode           run_mode,
            GFile                *file,
            const GimpValueArray *args,
            gpointer              run_data)
{
  GimpValueArray *return_vals;
  GimpImage      *image;
  GError         *error = NULL;
241

242
243
  INIT_I18N ();
  gegl_init (NULL, NULL);
244

245
  image = load_image (file, &error);
246

247
248
249
250
  if (! image)
    return gimp_procedure_new_return_values (procedure,
                                             GIMP_PDB_EXECUTION_ERROR,
                                             error);
251

252
253
254
  return_vals = gimp_procedure_new_return_values (procedure,
                                                  GIMP_PDB_SUCCESS,
                                                  NULL);
255

256
  GIMP_VALUES_SET_IMAGE (return_vals, 1, image);
257

258
259
  return return_vals;
}
260

261
262
263
264
static GimpValueArray *
dicom_save (GimpProcedure        *procedure,
            GimpRunMode           run_mode,
            GimpImage            *image,
265
266
            gint                  n_drawables,
            GimpDrawable        **drawables,
267
268
269
270
271
272
273
            GFile                *file,
            const GimpValueArray *args,
            gpointer              run_data)
{
  GimpPDBStatusType  status = GIMP_PDB_SUCCESS;
  GimpExportReturn   export = GIMP_EXPORT_CANCEL;
  GError            *error = NULL;
274

275
276
277
278
279
280
281
  INIT_I18N ();
  gegl_init (NULL, NULL);

  switch (run_mode)
    {
    case GIMP_RUN_INTERACTIVE:
    case GIMP_RUN_WITH_LAST_VALS:
282
      gimp_ui_init (PLUG_IN_BINARY);
283
      export = gimp_export_image (&image, &n_drawables, &drawables, "DICOM",
284
285
286
287
288
289
290
291
292
293
294
                                  GIMP_EXPORT_CAN_HANDLE_RGB |
                                  GIMP_EXPORT_CAN_HANDLE_GRAY);

      if (export == GIMP_EXPORT_CANCEL)
        return gimp_procedure_new_return_values (procedure,
                                                 GIMP_PDB_CANCEL,
                                                 NULL);
      break;

    default:
      break;
295
    }
296

297
298
299
300
301
302
303
304
305
306
  if (n_drawables != 1)
    {
      g_set_error (&error, G_FILE_ERROR, 0,
                   _("Dicom format does not support multiple layers."));

      return gimp_procedure_new_return_values (procedure,
                                               GIMP_PDB_CALLING_ERROR,
                                               error);
    }

307
  if (status == GIMP_PDB_SUCCESS)
308
    {
309
      if (! save_image (file, image, drawables[0],
310
311
312
313
                        &error))
        {
          status = GIMP_PDB_EXECUTION_ERROR;
        }
314
315
    }

316
  if (export == GIMP_EXPORT_EXPORT)
317
318
319
320
    {
      gimp_image_delete (image);
      g_free (drawables);
    }
321
322

  return gimp_procedure_new_return_values (procedure, status, error);
323
324
}

325
326
/**
 * add_parasites_to_image:
327
328
329
330
 * @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.
331
332
333
334
335
336
337
 *
 * Attaches parasite to image and also frees that parasite
**/
static void
add_parasites_to_image (gpointer data,
                        gpointer user_data)
{
338
339
  GimpParasite *parasite = data;
  GimpImage    *image    = user_data;
340

341
  gimp_image_attach_parasite (image, parasite);
342
343
344
  gimp_parasite_free (parasite);
}

345
static GimpImage *
346
347
load_image (GFile   *file,
            GError **error)
348
{
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
  GimpImage  *image = NULL;
  GimpLayer  *layer;
  GeglBuffer *buffer;
  GSList     *elements          = NULL;
  FILE       *dicom;
  gchar       buf[500];    /* buffer for random things like scanning */
  DicomInfo  *dicominfo;
  guint       width             = 0;
  guint       height            = 0;
  gint        samples_per_pixel = 0;
  gint        bpp               = 0;
  gint        bits_stored       = 0;
  gint        high_bit          = 0;
  guint8     *pix_buf           = NULL;
  gboolean    is_signed         = FALSE;
  guint8      in_sequence       = 0;
365
366
  gboolean    implicit_encoding = FALSE;
  gboolean    big_endian        = FALSE;
367

368
  gimp_progress_init_printf (_("Opening '%s'"),
369
                             gimp_file_get_utf8_name (file));
370

371
  dicom = g_fopen (g_file_peek_path (file), "rb");
372

373
  if (! dicom)
374
    {
375
376
      g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
                   _("Could not open '%s' for reading: %s"),
377
                   gimp_file_get_utf8_name (file), g_strerror (errno));
378
      return NULL;
379
380
381
    }

  /* allocate the necessary structures */
382
  dicominfo = g_new0 (DicomInfo, 1);
383
384

  /* Parse the file */
385
  fread (buf, 1, 128, dicom); /* skip past buffer */
386
387
388
389
390

  /* Check for unsupported formats */
  if (g_ascii_strncasecmp (buf, "PAPYRUS", 7) == 0)
    {
      g_message ("'%s' is a PAPYRUS DICOM file.\n"
391
                 "This plug-in does not support this type yet.",
392
                 gimp_file_get_utf8_name (file));
393
      g_free (dicominfo);
394
395
      fclose (dicom);
      return NULL;
396
397
    }

398
  fread (buf, 1, 4, dicom); /* This should be dicom */
399
400
  if (g_ascii_strncasecmp (buf,"DICM",4) != 0)
    {
401
402
      g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
                   _("'%s' is not a DICOM file."),
403
                   gimp_file_get_utf8_name (file));
404
      g_free (dicominfo);
405
406
      fclose (dicom);
      return NULL;
407
408
    }

409
  while (!feof (dicom))
410
411
412
413
414
415
416
    {
      guint16  group_word;
      guint16  element_word;
      gchar    value_rep[3];
      guint32  element_length;
      guint16  ctx_us;
      guint8  *value;
417
      guint32  tag;
418

419
      if (fread (&group_word, 1, 2, dicom) == 0)
David Odin's avatar
David Odin committed
420
        break;
421
422
      group_word = g_ntohs (GUINT16_SWAP_LE_BE (group_word));

423
      fread (&element_word, 1, 2, dicom);
424
425
      element_word = g_ntohs (GUINT16_SWAP_LE_BE (element_word));

426
427
428
429
430
431
      if (group_word != 0x0002 && big_endian)
        {
          group_word   = GUINT16_SWAP_LE_BE (group_word);
          element_word = GUINT16_SWAP_LE_BE (element_word);
        }

432
      tag = (group_word << 16) | element_word;
433
      fread(value_rep, 2, 1, dicom);
434
435
      value_rep[2] = 0;

436
437
438
439
      /* Check if the value rep looks valid. There probably is a
         better way of checking this...
       */
      if ((/* Always need lookup for implicit encoding */
David Odin's avatar
David Odin committed
440
441
442
443
444
445
446
447
448
449
           tag > 0x0002ffff && implicit_encoding)
          /* This heuristics isn't used if we are doing implicit
             encoding according to the value representation... */
          || ((value_rep[0] < 'A' || value_rep[0] > 'Z'
          || value_rep[1] < 'A' || value_rep[1] > 'Z')

          /* I found this in one of Ednas images. It seems like a
             bug...
          */
              && !(value_rep[0] == ' ' && value_rep[1]))
450
          )
451
        {
452
          /* Look up type from the dictionary. At the time we don't
David Odin's avatar
David Odin committed
453
             support this option... */
454
455
456
457
458
459
          gchar element_length_chars[4];

          /* Store the bytes that were read */
          element_length_chars[0] = value_rep[0];
          element_length_chars[1] = value_rep[1];

David Odin's avatar
David Odin committed
460
461
          /* Unknown value rep. It is not used right now anyhow */
          strcpy (value_rep, "??");
462
463
464

          /* For implicit value_values the length is always four bytes,
             so we need to read another two. */
465
          fread (&element_length_chars[2], 1, 2, dicom);
466
467

          /* Now cast to integer and insert into element_length */
468
469
470
471
472
473
          if (big_endian && group_word != 0x0002)
            element_length =
              g_ntohl (*((gint *) element_length_chars));
          else
            element_length =
              g_ntohl (GUINT32_SWAP_LE_BE (*((gint *) element_length_chars)));
474
475
476
      }
      /* Binary value reps are OB, OW, SQ or UN */
      else if (strncmp (value_rep, "OB", 2) == 0
David Odin's avatar
David Odin committed
477
478
479
480
          || strncmp (value_rep, "OW", 2) == 0
          || strncmp (value_rep, "SQ", 2) == 0
          || strncmp (value_rep, "UN", 2) == 0)
        {
481
482
          fread (&element_length, 1, 2, dicom); /* skip two bytes */
          fread (&element_length, 1, 4, dicom);
483
484
485
486
          if (big_endian && group_word != 0x0002)
            element_length = g_ntohl (element_length);
          else
            element_length = g_ntohl (GUINT32_SWAP_LE_BE (element_length));
David Odin's avatar
David Odin committed
487
        }
488
      /* Short length */
489
      else
David Odin's avatar
David Odin committed
490
491
        {
          guint16 el16;
492

493
          fread (&el16, 1, 2, dicom);
494
495
496
497
          if (big_endian && group_word != 0x0002)
            element_length = g_ntohs (el16);
          else
            element_length = g_ntohs (GUINT16_SWAP_LE_BE (el16));
David Odin's avatar
David Odin committed
498
        }
499

500
501
      /* Sequence of items - just ignore the delimiters... */
      if (element_length == 0xffffffff)
502
503
504
505
506
507
508
509
510
511
        {
          in_sequence = 1;
          continue;
        }
      /* End of Sequence tag */
      if (tag == 0xFFFEE0DD)
        {
          in_sequence = 0;
          continue;
        }
512
513
514

      /* Sequence of items item tag... Ignore as well */
      if (tag == 0xFFFEE000)
David Odin's avatar
David Odin committed
515
        continue;
516

517
518
519
520
521
      /* Even for pixel data, we don't handle very large element
         lengths */

      if (element_length >= (G_MAXUINT - 6))
        {
522
          g_message ("'%s' seems to have an incorrect value field length.",
523
                     gimp_file_get_utf8_name (file));
524
          gimp_quit ();
525
526
        }

527
528
529
      /* Read contents. Allocate a bit more to make room for casts to int
       below. */
      value = g_new0 (guint8, element_length + 4);
530
      fread (value, 1, element_length, dicom);
531

532
533
534
535
536
537
      /* ignore everything inside of a sequence */
      if (in_sequence)
        {
          g_free (value);
          continue;
        }
538
539
      /* Some special casts that are used below */
      ctx_us = *(guint16 *) value;
540
541
      if (big_endian && group_word != 0x0002)
        ctx_us = GUINT16_SWAP_LE_BE (ctx_us);
542

543
544
545
      g_debug ("group: %04x, element: %04x, length: %d",
               group_word, element_word, element_length);
      g_debug ("Value: %s", (char*)value);
546
      /* Recognize some critical tags */
547
548
549
550
551
552
553
554
      if (group_word == 0x0002)
        {
          switch (element_word)
            {
            case 0x0010:   /* transfer syntax id */
              if (strcmp("1.2.840.10008.1.2", (char*)value) == 0)
                {
                  implicit_encoding = TRUE;
555
                  g_debug ("Transfer syntax: Implicit VR Endian: Default Transfer Syntax for DICOM.");
556
557
                }
              else if (strcmp("1.2.840.10008.1.2.1", (char*)value) == 0)
558
559
560
561
562
563
564
                {
                  g_debug ("Transfer syntax: Explicit VR Little Endian.");
                }
              else if (strcmp("1.2.840.10008.1.2.1.99", (char*)value) == 0)
                {
                  g_debug ("Transfer syntax: Deflated Explicit VR Little Endian.");
                }
565
              else if (strcmp("1.2.840.10008.1.2.2", (char*)value) == 0)
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
                {
                  /* This Transfer Syntax was retired in 2006. For the most recent description of it, see PS3.5 2016b */
                  big_endian = TRUE;
                  g_debug ("Transfer syntax: Deprecated Explicit VR Big Endian.");
                }
              else
                {
                  g_debug ("Transfer syntax %s is not supported by GIMP.", (gchar *) value);
                  g_set_error (error, GIMP_PLUG_IN_ERROR, 0,
                              _("Transfer syntax %s is not supported by GIMP."),
                              (gchar *) value);
                  g_free (dicominfo);
                  fclose (dicom);
                  return NULL;
                }
581
582
583
584
              break;
            }
        }
      else if (group_word == 0x0028)
David Odin's avatar
David Odin committed
585
586
587
588
589
        {
          switch (element_word)
            {
            case 0x0002:  /* samples per pixel */
              samples_per_pixel = ctx_us;
590
591
592
593
594
595
596
597
598
599
600
              g_debug ("spp: %d", samples_per_pixel);
              break;
            case 0x0004:  /* photometric interpretation */
              g_debug ("photometric interpretation: %s", (char*) value);
              break;
            case 0x0006:  /* planar configuration */
              g_debug ("planar configuration: %u", ctx_us);
              dicominfo->planar = (ctx_us == 1);
              break;
            case 0x0008:  /* number of frames */
              g_debug ("number of frames: %d", ctx_us);
David Odin's avatar
David Odin committed
601
602
603
              break;
            case 0x0010:  /* rows */
              height = ctx_us;
604
              g_debug ("height: %d", height);
David Odin's avatar
David Odin committed
605
606
607
              break;
            case 0x0011:  /* columns */
              width = ctx_us;
608
              g_debug ("width: %d", width);
David Odin's avatar
David Odin committed
609
610
611
              break;
            case 0x0100:  /* bits allocated */
              bpp = ctx_us;
612
              g_debug ("bpp: %d", bpp);
David Odin's avatar
David Odin committed
613
614
615
              break;
            case 0x0101:  /* bits stored */
              bits_stored = ctx_us;
616
              g_debug ("bits stored: %d", bits_stored);
David Odin's avatar
David Odin committed
617
618
619
              break;
            case 0x0102:  /* high bit */
              high_bit = ctx_us;
620
              g_debug ("high bit: %d", high_bit);
David Odin's avatar
David Odin committed
621
622
623
              break;
            case 0x0103:  /* is pixel representation signed? */
              is_signed = (ctx_us == 0) ? FALSE : TRUE;
624
              g_debug ("is signed: %d", ctx_us);
David Odin's avatar
David Odin committed
625
626
627
              break;
            }
        }
628
629
630

      /* Pixel data */
      if (group_word == 0x7fe0 && element_word == 0x0010)
David Odin's avatar
David Odin committed
631
632
633
        {
          pix_buf = value;
        }
634
635
      else
        {
636
637
          /* save this element to a parasite for later writing */
          GimpParasite *parasite;
638
639
          gchar         pname[255];

640
          /* all elements are retrievable using gimp_get_parasite_list() */
641
642
          g_snprintf (pname, sizeof (pname),
                      "dcm/%04x-%04x-%s", group_word, element_word, value_rep);
643
          if ((parasite = gimp_parasite_new (pname,
644
645
                                             GIMP_PARASITE_PERSISTENT,
                                             element_length, value)))
646
647
            {
              /*
648
               * at this point, the image has not yet been created, so
649
650
               * image is not valid.  keep the parasite around until
               * we're able to attach it.
651
652
653
654
               */

              /* add to our list of parasites to be added (prepending
               * for speed. we'll reverse it later)
655
               */
656
              elements = g_slist_prepend (elements, parasite);
657
658
            }

659
660
661
662
          g_free (value);
        }
    }

663
664
665
  if ((bpp != 8) && (bpp != 16))
    {
      g_message ("'%s' has a bpp of %d which GIMP cannot handle.",
666
                 gimp_file_get_utf8_name (file), bpp);
667
668
669
      gimp_quit ();
    }

670
671
  if ((width > GIMP_MAX_IMAGE_SIZE) || (height > GIMP_MAX_IMAGE_SIZE))
    {
672
      g_message ("'%s' has a larger image size (%d x %d) than GIMP can handle.",
673
                 gimp_file_get_utf8_name (file), width, height);
674
675
676
677
678
679
      gimp_quit ();
    }

  if (samples_per_pixel > 3)
    {
      g_message ("'%s' has samples per pixel of %d which GIMP cannot handle.",
680
                 gimp_file_get_utf8_name (file), samples_per_pixel);
681
      gimp_quit ();
682
683
    }

684
685
686
  dicominfo->width  = width;
  dicominfo->height = height;
  dicominfo->bpp    = bpp;
687
688
689
690

  dicominfo->bits_stored = bits_stored;
  dicominfo->high_bit = high_bit;
  dicominfo->is_signed = is_signed;
691
692
693
694
695
  dicominfo->samples_per_pixel = samples_per_pixel;
  dicominfo->maxval = -1;   /* External normalization factor - not used yet */

  /* Create a new image of the proper size and associate the filename with it.
   */
696
697
698
  image = gimp_image_new (dicominfo->width, dicominfo->height,
                          (dicominfo->samples_per_pixel >= 3 ?
                           GIMP_RGB : GIMP_GRAY));
699
  gimp_image_set_file (image, file);
700

701
702
703
704
705
706
707
  layer = gimp_layer_new (image, _("Background"),
                          dicominfo->width, dicominfo->height,
                          (dicominfo->samples_per_pixel >= 3 ?
                           GIMP_RGB_IMAGE : GIMP_GRAY_IMAGE),
                          100,
                          gimp_image_get_default_new_layer_mode (image));
  gimp_image_insert_layer (image, layer, NULL, 0);
708

709
  buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (layer));
710
711
712
713
714
715

#if GUESS_ENDIAN
  if (bpp == 16)
    guess_and_set_endian2 ((guint16 *) pix_buf, width * height);
#endif

716
  dicom_loader (pix_buf, dicominfo, buffer);
717

718
719
  if (elements)
    {
720
721
722
      /* flip the parasites back around into the order they were
       * created (read from the file)
       */
723
724
      elements = g_slist_reverse (elements);
      /* and add each one to the image */
725
      g_slist_foreach (elements, add_parasites_to_image, image);
726
727
      g_slist_free (elements);
    }
728

729
730
731
  g_free (pix_buf);
  g_free (dicominfo);

732
  fclose (dicom);
733

734
  g_object_unref (buffer);
735

736
  return image;
737
738
739
}

static void
740
741
742
dicom_loader (guint8     *pix_buffer,
              DicomInfo  *info,
              GeglBuffer *buffer)
743
{
Sven Neumann's avatar
Sven Neumann committed
744
745
  guchar  *data;
  gint     row_idx;
746
747
748
749
750
751
752
  gint     width             = info->width;
  gint     height            = info->height;
  gint     samples_per_pixel = info->samples_per_pixel;
  guint16 *buf16             = (guint16 *) pix_buffer;

  if (info->bpp == 16)
    {
Sven Neumann's avatar
Sven Neumann committed
753
754
755
      gulong pix_idx;
      guint  shift = info->high_bit + 1 - info->bits_stored;

756
757
758
759
      /* Reorder the buffer; also shift the data so that the LSB
       * of the pixel data is at the LSB of the 16-bit array entries
       * (i.e., compensate for high_bit and bits_stored).
       */
760
      for (pix_idx = 0; pix_idx < width * height * samples_per_pixel; pix_idx++)
Sven Neumann's avatar
Sven Neumann committed
761
        buf16[pix_idx] = g_htons (buf16[pix_idx]) >> shift;
762
763
764
765
766
767
    }

  data = g_malloc (gimp_tile_height () * width * samples_per_pixel);

  for (row_idx = 0; row_idx < height; )
    {
Sven Neumann's avatar
Sven Neumann committed
768
769
770
771
772
773
      guchar *d = data;
      gint    start;
      gint    end;
      gint    scanlines;
      gint    i;

774
      start = row_idx;
Sven Neumann's avatar
Sven Neumann committed
775
776
777
      end   = row_idx + gimp_tile_height ();
      end   = MIN (end, height);

778
779
780
      scanlines = end - start;

      for (i = 0; i < scanlines; i++)
David Odin's avatar
David Odin committed
781
782
783
784
785
        {
          if (info->bpp == 16)
            {
              guint16 *row_start;
              gint     col_idx;
786
787
788

              row_start = buf16 + (row_idx + i) * width * samples_per_pixel;

David Odin's avatar
David Odin committed
789
              for (col_idx = 0; col_idx < width * samples_per_pixel; col_idx++)
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
                {
                  /* Shift it by 8 bits, or less in case bits_stored
                   * is less than bpp.
                   */
                  d[col_idx] = (guint8) (row_start[col_idx] >>
                                         (info->bits_stored - 8));
                  if (info->is_signed)
                    {
                      /* If the data is negative, make it 0. Otherwise,
                       * multiply the positive value by 2, so that the
                       * positive values span between 0 and 254.
                       */
                      if (d[col_idx] > 127)
                        d[col_idx] = 0;
                      else
                        d[col_idx] <<= 1;
                    }
                }
David Odin's avatar
David Odin committed
808
809
810
            }
          else if (info->bpp == 8)
            {
811
812
813
814
              if (! info->planar)
                {
                  guint8 *row_start;
                  gint    col_idx;
Sven Neumann's avatar
Sven Neumann committed
815

816
817
                  row_start = (pix_buffer +
                              (row_idx + i) * width * samples_per_pixel);
818

819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
                  for (col_idx = 0; col_idx < width * samples_per_pixel; col_idx++)
                    {
                      /* Shift it by 0 bits, or more in case bits_stored is
                       * less than bpp.
                       */
                      d[col_idx] = row_start[col_idx] << (8 - info->bits_stored);

                      if (info->is_signed)
                        {
                          /* If the data is negative, make it 0. Otherwise,
                           * multiply the positive value by 2, so that the
                           * positive values span between 0 and 254.
                           */
                          if (d[col_idx] > 127)
                            d[col_idx] = 0;
                          else
                            d[col_idx] <<= 1;
                        }
                    }
                }
              else
840
                {
841
842
843
844
                  /* planar organization of color data */
                  guint8 *row_start;
                  gint    col_idx;
                  gint    plane_size = width * height;
845

846
847
848
                  row_start = (pix_buffer + (row_idx + i) * width);

                  for (col_idx = 0; col_idx < width; col_idx++)
849
                    {
850
851
                      /* Shift it by 0 bits, or more in case bits_stored is
                       * less than bpp.
852
                       */
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
                      gint  pix_idx;
                      gint  src_offset = col_idx;

                      for (pix_idx = 0; pix_idx < samples_per_pixel; pix_idx++)
                        {
                          gint  dest_idx = col_idx * samples_per_pixel + pix_idx;

                          d[dest_idx] = row_start[src_offset] << (8 - info->bits_stored);
                          if (info->is_signed)
                            {
                              /* If the data is negative, make it 0. Otherwise,
                               * multiply the positive value by 2, so that the
                               * positive values span between 0 and 254.
                               */
                              if (d[dest_idx] > 127)
                                d[dest_idx] = 0;
                              else
                                d[dest_idx] <<= 1;
                            }
                          src_offset += plane_size;
                        }
874
875
                    }
                }
David Odin's avatar
David Odin committed
876
            }
877

David Odin's avatar
David Odin committed
878
879
          d += width * samples_per_pixel;
        }
880

881
882
883
      gegl_buffer_set (buffer, GEGL_RECTANGLE (0, row_idx, width, scanlines), 0,
                       NULL, data, GEGL_AUTO_ROWSTRIDE);

884
      row_idx += scanlines;
885
886

      gimp_progress_update ((gdouble) row_idx / (gdouble) height);
887
888
889
    }

  g_free (data);
890
891

  gimp_progress_update (1.0);
892
893
894
895
896
897
898
899
900
901
902
}


/* Guess and set endian. Guesses the endian of a buffer by
 * checking the maximum value of the first and the last byte
 * in the words of the buffer. It assumes that the least
 * significant byte has a larger maximum than the most
 * significant byte.
 */
static void
guess_and_set_endian2 (guint16 *buf16,
David Odin's avatar
David Odin committed
903
                       int length)
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
{
  guint16 *p          = buf16;
  gint     max_first  = -1;
  gint     max_second = -1;

  while (p<buf16+length)
    {
      if (*(guint8*)p > max_first)
        max_first = *(guint8*)p;
      if (((guint8*)p)[1] > max_second)
        max_second = ((guint8*)p)[1];
      p++;
    }

  if (   ((max_second > max_first) && (G_BYTE_ORDER == G_LITTLE_ENDIAN))
         || ((max_second < max_first) && (G_BYTE_ORDER == G_BIG_ENDIAN)))
    toggle_endian2 (buf16, length);
}

/* toggle_endian2 toggles the endian for a 16 bit entity.  */
static void
toggle_endian2 (guint16 *buf16,
David Odin's avatar
David Odin committed
926
                gint     length)
927
928
929
930
931
932
933
934
935
936
{
  guint16 *p = buf16;

  while (p < buf16 + length)
    {
      *p = ((*p & 0xff) << 8) | (*p >> 8);
      p++;
    }
}

937
938
939
940
941
942
943
944
typedef struct
{
  guint16   group_word;
  guint16   element_word;
  gchar     value_rep[3];
  guint32   element_length;
  guint8   *value;
  gboolean  free;
945
946
947
948
} DICOMELEMENT;

/**
 * dicom_add_element:
949
950
951
952
953
954
955
956
 * @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.
957
958
959
 *
 * Creates a DICOMELEMENT object and inserts it into @elements.
 *
960
 * Returns: the new head of @elements
961
962
**/
static GSList *
963
964
965
966
967
968
dicom_add_element (GSList      *elements,
                   guint16      group_word,
                   guint16      element_word,
                   const gchar *value_rep,
                   guint32      element_length,
                   guint8      *value)
969
{
970
971
972
973
  DICOMELEMENT *element = g_slice_new0 (DICOMELEMENT);

  element->group_word     = group_word;
  element->element_word   = element_word;
974
  g_strlcpy (element->value_rep, value_rep, sizeof (element->value_rep));
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
  element->element_length = element_length;
  element->value          = value;

  return g_slist_prepend (elements, element);
}

static GSList *
dicom_add_element_copy (GSList       *elements,
                        guint16       group_word,
                        guint16       element_word,
                        gchar        *value_rep,
                        guint32       element_length,
                        const guint8 *value)
{
  elements = dicom_add_element (elements,
                                group_word, element_word, value_rep,
                                element_length,
992
                                g_memdup2 (value, element_length));
993
994
995

  ((DICOMELEMENT *) elements->data)->free = TRUE;

996
997
998
999
1000
  return elements;
}

/**
 * dicom_add_element_int:
1001
1002
1003
1004
1005
1006
1007
1008
1009
 * @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.
1010
 *
1011
1012
1013
1014
 * 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
1015
 *
1016
 * Returns: the new head of @elements
1017
1018
 */
static GSList *
1019
1020
1021
1022
1023
dicom_add_element_int (GSList  *elements,
                       guint16  group_word,
                       guint16  element_word,
                       gchar   *value_rep,
                       guint8  *value)
1024
1025
1026
1027
1028
1029
1030
1031
{
  guint32 len;

  if (strcmp (value_rep, "US") == 0)
    len = 2;
  else
    len = 4;

1032
1033
1034
  return dicom_add_element (elements,
                            group_word, element_word, value_rep,
                            len, value);
1035
1036
1037
1038
1039
1040
1041
1042
1043
}

/**
 * dicom_element_done:
 * @data: pointer to a DICOMELEMENT structure which is to be destroyed.
 *
 * Destroys the DICOMELEMENT passed as @data
**/
static void
1044
dicom_element_done (gpointer data)
1045
1046
1047
{
  if (data)
    {
1048
1049
      DICOMELEMENT *e = data;

1050
      if (e->free)
1051
1052
1053
        g_free (e->value);

      g_slice_free (DICOMELEMENT, data);
1054
    }
1055
}
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066

/**
 * 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)
1067
    g_slist_free_full (elements, dicom_element_done);
1068
1069
1070
1071
1072
1073
1074
1075
1076
}

/**
 * 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
 *
1077
 * Returns: the new head of @elements
1078
1079
**/
static GSList *
1080
dicom_destroy_element (GSList       *elements,
1081
1082
1083
1084
                       DICOMELEMENT *ele)
{
  if (ele)
    {
1085
1086
      elements = g_slist_remove_all (elements, ele);

1087
      if (ele->free)
1088
1089
1090
        g_free (ele->value);

      g_slice_free (DICOMELEMENT, ele);
1091
1092
1093
1094
1095
1096
1097
1098
1099
    }

  return elements;
}

/**
 * dicom_elements_compare:
 * @a: pointer to a DICOMELEMENT structure.
 * @b: pointer to a DICOMELEMENT structure.
1100
 *
1101
1102
 * Determines the equality of @a and @b as strcmp
 *
1103
 * Returns: an integer indicating the equality of @a and @b.
1104
**/
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
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;
    }
1131

1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
  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.
 *
1143
 * Returns: a DICOMELEMENT matching the specified group,element,
1144
 *               or NULL if the specified element was not found.
1145
1146
**/
static DICOMELEMENT *
1147
1148
1149
dicom_element_find_by_num (GSList  *head,
                           guint16  group_word,
                           guint16  element_word)
1150
{
1151
  DICOMELEMENT data = { group_word,element_word, "", 0, NULL};
1152
  GSList *ele = g_slist_find_custom (head,&data,dicom_elements_compare);
1153
  return (ele ? ele->data : NULL);
1154
1155
1156
1157
}

/**
 * dicom_get_elements_list:
1158
1159
 * @image: the image from which to read parasites in order to
 *         retrieve the dicom elements
1160
1161
1162
 *
 * Reads all DICOMELEMENTs from the specified image's parasites.
 *
1163
 * Returns: a GSList of all known dicom elements
1164
1165
**/
static GSList *
1166
dicom_get_elements_list (GimpImage *image)
1167
{
1168
1169
  GSList        *elements = NULL;
  GimpParasite  *parasite;
1170
1171
1172
  gchar        **parasites = NULL;
  gint           count = 0;

1173
  parasites = gimp_image_get_parasite_list (image, &count);
1174

1175
1176
1177
  if (parasites && count > 0)
    {
      gint i;
1178

1179
1180
      for (i = 0; i < count; i++)
        {
1181
          if (strncmp (parasites[i], "dcm", 3) == 0)
1182
            {
1183
              parasite = gimp_image_get_parasite (image, parasites[i]);
1184

1185
1186
1187
1188
1189
              if (parasite)
                {
                  gchar buf[1024];
                  gchar *ptr1;
                  gchar *ptr2;
1190
1191
1192
                  gchar value_rep[3]   = "";
                  guint16 group_word   = 0;
                  guint16 element_word = 0;
1193
1194

                  /* sacrificial buffer */
1195
                  g_strlcpy (buf, parasites[i], sizeof (buf));
1196

1197
1198
1199
1200
                  /* 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
1201
1202
1203
                   *
                   * start off by jumping over the dcm/ to the first Hex blob
                   */
1204
1205
                  ptr1 = strchr (buf, '/');

1206
1207
1208
                  if (ptr1)
                    {
                      gchar t[15];
1209

1210
1211
                      ptr1++;
                      ptr2 = strchr (ptr1,'-');
1212

1213
                      if (ptr2)
1214
1215
1216
1217
1218
                        *ptr2 = '\0';

                      g_snprintf (t, sizeof (t), "0x%s", ptr1);
                      group_word = (guint16) g_ascii_strtoull (t, NULL, 16);
                      ptr1 = ptr2 + 1;
1219
                    }
1220

1221
1222
1223
1224
                  /* now get the second Hex blob */
                  if (ptr1)
                    {
                      gchar t[15];