Coverage Report

Created: 2026-02-14 07:11

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/imagemagick/coders/hdr.c
Line
Count
Source
1
/*
2
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3
%                                                                             %
4
%                                                                             %
5
%                                                                             %
6
%                            H   H  DDDD   RRRR                               %
7
%                            H   H  D   D  R   R                              %
8
%                            HHHHH  D   D  RRRR                               %
9
%                            H   H  D   D  R R                                %
10
%                            H   H  DDDD   R  R                               %
11
%                                                                             %
12
%                                                                             %
13
%                   Read/Write Radiance RGBE Image Format                     %
14
%                                                                             %
15
%                              Software Design                                %
16
%                                   Cristy                                    %
17
%                                 July 1992                                   %
18
%                                                                             %
19
%                                                                             %
20
%  Copyright @ 1999 ImageMagick Studio LLC, a non-profit organization         %
21
%  dedicated to making software imaging solutions freely available.           %
22
%                                                                             %
23
%  You may not use this file except in compliance with the License.  You may  %
24
%  obtain a copy of the License at                                            %
25
%                                                                             %
26
%    https://imagemagick.org/license/                                         %
27
%                                                                             %
28
%  Unless required by applicable law or agreed to in writing, software        %
29
%  distributed under the License is distributed on an "AS IS" BASIS,          %
30
%  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31
%  See the License for the specific language governing permissions and        %
32
%  limitations under the License.                                             %
33
%                                                                             %
34
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35
%
36
%
37
*/
38

39
/*
40
  Include declarations.
41
*/
42
#include "MagickCore/studio.h"
43
#include "MagickCore/blob.h"
44
#include "MagickCore/blob-private.h"
45
#include "MagickCore/cache.h"
46
#include "MagickCore/colorspace.h"
47
#include "MagickCore/colorspace-private.h"
48
#include "MagickCore/exception.h"
49
#include "MagickCore/exception-private.h"
50
#include "MagickCore/image.h"
51
#include "MagickCore/image-private.h"
52
#include "MagickCore/list.h"
53
#include "MagickCore/magick.h"
54
#include "MagickCore/memory_.h"
55
#include "MagickCore/monitor.h"
56
#include "MagickCore/monitor-private.h"
57
#include "MagickCore/pixel-accessor.h"
58
#include "MagickCore/property.h"
59
#include "MagickCore/quantum-private.h"
60
#include "MagickCore/static.h"
61
#include "MagickCore/string_.h"
62
#include "MagickCore/string-private.h"
63
#include "MagickCore/module.h"
64

65
/*
66
  Forward declarations.
67
*/
68
static MagickBooleanType
69
  WriteHDRImage(const ImageInfo *,Image *,ExceptionInfo *);
70

71
/*
72
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
73
%                                                                             %
74
%                                                                             %
75
%                                                                             %
76
%   I s H D R                                                                 %
77
%                                                                             %
78
%                                                                             %
79
%                                                                             %
80
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
81
%
82
%  IsHDR() returns MagickTrue if the image format type, identified by the
83
%  magick string, is Radiance RGBE image format.
84
%
85
%  The format of the IsHDR method is:
86
%
87
%      MagickBooleanType IsHDR(const unsigned char *magick,
88
%        const size_t length)
89
%
90
%  A description of each parameter follows:
91
%
92
%    o magick: compare image format pattern against these bytes.
93
%
94
%    o length: Specifies the length of the magick string.
95
%
96
*/
97
static MagickBooleanType IsHDR(const unsigned char *magick,
98
  const size_t length)
99
0
{
100
0
  if (length < 10)
101
0
    return(MagickFalse);
102
0
  if (LocaleNCompare((const char *) magick,"#?RADIANCE",10) == 0)
103
0
    return(MagickTrue);
104
0
  if (LocaleNCompare((const char *) magick,"#?RGBE",6) == 0)
105
0
    return(MagickTrue);
106
0
  return(MagickFalse);
107
0
}
108

109
/*
110
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
111
%                                                                             %
112
%                                                                             %
113
%                                                                             %
114
%   R e a d H D R I m a g e                                                   %
115
%                                                                             %
116
%                                                                             %
117
%                                                                             %
118
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
119
%
120
%  ReadHDRImage() reads the Radiance RGBE image format and returns it.  It
121
%  allocates the memory necessary for the new Image structure and returns a
122
%  pointer to the new image.
123
%
124
%  The format of the ReadHDRImage method is:
125
%
126
%      Image *ReadHDRImage(const ImageInfo *image_info,ExceptionInfo *exception)
127
%
128
%  A description of each parameter follows:
129
%
130
%    o image_info: the image info.
131
%
132
%    o exception: return any errors or warnings in this structure.
133
%
134
*/
135
static Image *ReadHDRImage(const ImageInfo *image_info,ExceptionInfo *exception)
136
1.99k
{
137
1.99k
  char
138
1.99k
    format[MagickPathExtent],
139
1.99k
    keyword[MagickPathExtent],
140
1.99k
    tag[MagickPathExtent],
141
1.99k
    value[MagickPathExtent];
142
143
1.99k
  double
144
1.99k
    gamma;
145
146
1.99k
  float
147
1.99k
    chromaticity[6],
148
1.99k
    white_point[2];
149
150
1.99k
  Image
151
1.99k
    *image;
152
153
1.99k
  int
154
1.99k
    c,
155
1.99k
    chromaticity_count = 0;
156
157
1.99k
  MagickBooleanType
158
1.99k
    status,
159
1.99k
    value_expected;
160
161
1.99k
  Quantum
162
1.99k
    *q;
163
164
1.99k
  ssize_t
165
1.99k
    i,
166
1.99k
    x;
167
168
1.99k
  ssize_t
169
1.99k
    y;
170
171
1.99k
  unsigned char
172
1.99k
    *end,
173
1.99k
    pixel[4],
174
1.99k
    *pixels;
175
176
  /*
177
    Open image file.
178
  */
179
1.99k
  assert(image_info != (const ImageInfo *) NULL);
180
1.99k
  assert(image_info->signature == MagickCoreSignature);
181
1.99k
  assert(exception != (ExceptionInfo *) NULL);
182
1.99k
  assert(exception->signature == MagickCoreSignature);
183
1.99k
  if (IsEventLogging() != MagickFalse)
184
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
185
0
      image_info->filename);
186
1.99k
  image=AcquireImage(image_info,exception);
187
1.99k
  status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
188
1.99k
  if (status == MagickFalse)
189
101
    {
190
101
      image=DestroyImageList(image);
191
101
      return((Image *) NULL);
192
101
    }
193
  /*
194
    Decode image header.
195
  */
196
1.88k
  image->columns=0;
197
1.88k
  image->rows=0;
198
1.88k
  *format='\0';
199
1.88k
  c=ReadBlobByte(image);
200
1.88k
  if (c == EOF)
201
96
    {
202
96
      image=DestroyImage(image);
203
96
      return((Image *) NULL);
204
96
    }
205
27.3k
  while (isgraph((int) ((unsigned char) c)) && (image->columns == 0) && (image->rows == 0))
206
25.5k
  {
207
25.5k
    if (c == (int) '#')
208
5.39k
      {
209
5.39k
        char
210
5.39k
          *comment;
211
212
5.39k
        char
213
5.39k
          *p;
214
215
5.39k
        size_t
216
5.39k
          length;
217
218
        /*
219
          Read comment-- any text between # and end-of-line.
220
        */
221
5.39k
        length=MagickPathExtent;
222
5.39k
        comment=AcquireString((char *) NULL);
223
45.9k
        for (p=comment; comment != (char *) NULL; p++)
224
45.9k
        {
225
45.9k
          c=ReadBlobByte(image);
226
45.9k
          if ((c == EOF) || (c == (int) '\n'))
227
5.39k
            break;
228
40.5k
          if ((size_t) (p-comment+1) >= length)
229
3
            {
230
3
              *p='\0';
231
3
              length<<=1;
232
3
              comment=(char *) ResizeQuantumMemory(comment,length+
233
3
                MagickPathExtent,sizeof(*comment));
234
3
              if (comment == (char *) NULL)
235
0
                break;
236
3
              p=comment+strlen(comment);
237
3
            }
238
40.5k
          *p=(char) c;
239
40.5k
        }
240
5.39k
        if (comment == (char *) NULL)
241
5.39k
          ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
242
5.39k
        *p='\0';
243
5.39k
        (void) SetImageProperty(image,"comment",comment,exception);
244
5.39k
        comment=DestroyString(comment);
245
5.39k
        c=ReadBlobByte(image);
246
5.39k
      }
247
20.1k
    else
248
20.1k
      if (isalnum((int) ((unsigned char) c)) == 0)
249
1.31k
        c=ReadBlobByte(image);
250
18.8k
      else
251
18.8k
        {
252
18.8k
          char
253
18.8k
            *p;
254
255
          /*
256
            Determine a keyword and its value.
257
          */
258
18.8k
          p=keyword;
259
18.8k
          do
260
146k
          {
261
146k
            if ((size_t) (p-keyword) < (MagickPathExtent-1))
262
146k
              *p++=(char) c;
263
146k
            c=ReadBlobByte(image);
264
146k
          } while (isalnum((int) ((unsigned char) c)) || (c == '_'));
265
18.8k
          *p='\0';
266
18.8k
          value_expected=MagickFalse;
267
36.7k
          while ((isspace((int) ((unsigned char) c)) != 0) || (c == '='))
268
17.9k
          {
269
17.9k
            if (c == '=')
270
15.5k
              value_expected=MagickTrue;
271
17.9k
            c=ReadBlobByte(image);
272
17.9k
          }
273
18.8k
          if (LocaleCompare(keyword,"Y") == 0)
274
3.78k
            value_expected=MagickTrue;
275
18.8k
          if (value_expected == MagickFalse)
276
958
            continue;
277
17.8k
          p=value;
278
168k
          while ((c != '\n') && (c != '\0') && (c != EOF))
279
150k
          {
280
150k
            if ((size_t) (p-value) < (MagickPathExtent-1))
281
149k
              *p++=(char) c;
282
150k
            c=ReadBlobByte(image);
283
150k
          }
284
17.8k
          *p='\0';
285
          /*
286
            Assign a value to the specified keyword.
287
          */
288
17.8k
          switch (*keyword)
289
17.8k
          {
290
834
            case 'F':
291
6.81k
            case 'f':
292
6.81k
            {
293
6.81k
              if (LocaleCompare(keyword,"format") == 0)
294
5.79k
                {
295
5.79k
                  (void) CopyMagickString(format,value,MagickPathExtent);
296
5.79k
                  break;
297
5.79k
                }
298
1.01k
              (void) FormatLocaleString(tag,MagickPathExtent,"hdr:%s",keyword);
299
1.01k
              (void) SetImageProperty(image,tag,value,exception);
300
1.01k
              break;
301
6.81k
            }
302
290
            case 'G':
303
1.42k
            case 'g':
304
1.42k
            {
305
1.42k
              if (LocaleCompare(keyword,"gamma") == 0)
306
709
                {
307
709
                  image->gamma=StringToDouble(value,(char **) NULL);
308
709
                  break;
309
709
                }
310
715
              (void) FormatLocaleString(tag,MagickPathExtent,"hdr:%s",keyword);
311
715
              (void) SetImageProperty(image,tag,value,exception);
312
715
              break;
313
1.42k
            }
314
341
            case 'P':
315
795
            case 'p':
316
795
            {
317
795
              if (LocaleCompare(keyword,"primaries") == 0)
318
202
                {
319
202
                  chromaticity_count=MagickSscanf(value,"%g %g %g %g %g %g %g %g",
320
202
                    &chromaticity[0],&chromaticity[1],&chromaticity[2],
321
202
                    &chromaticity[3],&chromaticity[4],&chromaticity[5],
322
202
                    &white_point[0],&white_point[1]);
323
202
                  break;
324
202
                }
325
593
              (void) FormatLocaleString(tag,MagickPathExtent,"hdr:%s",keyword);
326
593
              (void) SetImageProperty(image,tag,value,exception);
327
593
              break;
328
795
            }
329
2.12k
            case 'Y':
330
4.07k
            case 'y':
331
4.07k
            {
332
4.07k
              char
333
4.07k
                target[] = "Y";
334
335
4.07k
              if (strcmp(keyword,target) == 0)
336
1.89k
                {
337
1.89k
                  int
338
1.89k
                    height,
339
1.89k
                    width;
340
341
1.89k
                  if (MagickSscanf(value,"%d +X %d",&height,&width) == 2)
342
1.34k
                    {
343
1.34k
                      image->columns=(size_t) width;
344
1.34k
                      image->rows=(size_t) height;
345
1.34k
                    }
346
1.89k
                  break;
347
1.89k
                }
348
2.17k
              (void) FormatLocaleString(tag,MagickPathExtent,"hdr:%s",keyword);
349
2.17k
              (void) SetImageProperty(image,tag,value,exception);
350
2.17k
              break;
351
4.07k
            }
352
4.75k
            default:
353
4.75k
            {
354
4.75k
              (void) FormatLocaleString(tag,MagickPathExtent,"hdr:%s",keyword);
355
4.75k
              (void) SetImageProperty(image,tag,value,exception);
356
4.75k
              break;
357
4.07k
            }
358
17.8k
          }
359
17.8k
        }
360
24.5k
    if ((image->columns == 0) && (image->rows == 0))
361
41.6k
      while (isspace((int) ((unsigned char) c)) != 0)
362
18.2k
        c=ReadBlobByte(image);
363
24.5k
  }
364
1.79k
  if ((image->columns == 0) || (image->rows == 0))
365
1.06k
    ThrowReaderException(CorruptImageError,"NegativeOrZeroImageSize");
366
1.06k
  if (LocaleCompare(format,"32-bit_rle_rgbe") == 0)
367
50
    (void) SetImageColorspace(image,RGBColorspace,exception);
368
1.01k
  else if (LocaleCompare(format,"32-bit_rle_xyze") == 0)
369
876
    (void) SetImageColorspace(image,XYZColorspace,exception);
370
136
  else
371
926
    ThrowReaderException(CorruptImageError,"ImproperImageHeader");
372
926
  if (chromaticity_count == 8)
373
1
    {
374
1
      image->chromaticity.red_primary.x=chromaticity[0];
375
1
      image->chromaticity.red_primary.y=chromaticity[1];
376
1
      image->chromaticity.green_primary.x=chromaticity[2];
377
1
      image->chromaticity.green_primary.y=chromaticity[3];
378
1
      image->chromaticity.blue_primary.x=chromaticity[4];
379
1
      image->chromaticity.blue_primary.y=chromaticity[5];
380
1
      image->chromaticity.white_point.x=white_point[0];
381
1
      image->chromaticity.white_point.y=white_point[1];
382
1
    }
383
926
  image->compression=(image->columns < 8) || (image->columns > 0x7ffff) ?
384
573
    NoCompression : RLECompression;
385
926
  if (image_info->ping != MagickFalse)
386
3
    {
387
3
      (void) CloseBlob(image);
388
3
      return(GetFirstImageInList(image));
389
3
    }
390
923
  status=SetImageExtent(image,image->columns,image->rows,exception);
391
923
  if (status == MagickFalse)
392
227
    return(DestroyImageList(image));
393
  /*
394
    Read RGBE (red+green+blue+exponent) pixels.
395
  */
396
696
  pixels=(unsigned char *) AcquireQuantumMemory(image->columns,4*
397
696
    sizeof(*pixels));
398
696
  if (pixels == (unsigned char *) NULL)
399
696
    ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
400
696
  (void) memset(pixels,0,4*image->columns*sizeof(*pixels));
401
15.3k
  for (y=0; y < (ssize_t) image->rows; y++)
402
14.9k
  {
403
14.9k
    ssize_t
404
14.9k
      count;
405
406
14.9k
    if (image->compression != RLECompression)
407
5.11k
      {
408
5.11k
        count=ReadBlob(image,4*image->columns*sizeof(*pixels),pixels);
409
5.11k
        if (count != (ssize_t) (4*image->columns*sizeof(*pixels)))
410
127
          break;
411
5.11k
      }
412
9.82k
    else
413
9.82k
      {
414
9.82k
        count=ReadBlob(image,4*sizeof(*pixel),pixel);
415
9.82k
        if (count != 4)
416
124
          break;
417
9.69k
        if ((size_t) ((((size_t) pixel[2]) << 8) | pixel[3]) != image->columns)
418
120
          {
419
120
            (void) memcpy(pixels,pixel,4*sizeof(*pixel));
420
120
            count=ReadBlob(image,4*(image->columns-1)*sizeof(*pixels),pixels+4);
421
120
            image->compression=NoCompression;
422
120
          }
423
9.57k
        else
424
9.57k
          {
425
9.57k
            unsigned char
426
9.57k
              *p;
427
428
9.57k
            p=pixels;
429
47.8k
            for (i=0; i < 4; i++)
430
38.3k
            {
431
38.3k
              end=&pixels[(i+1)*(ssize_t) image->columns];
432
44.4k
              while (p < end)
433
44.0k
              {
434
44.0k
                count=ReadBlob(image,2*sizeof(*pixel),pixel);
435
44.0k
                if (count < 1)
436
337
                  break;
437
43.6k
                if (pixel[0] > 128)
438
5.25k
                  {
439
5.25k
                    count=(ssize_t) pixel[0]-128;
440
5.25k
                    if ((count == 0) || (count > (ssize_t) (end-p)))
441
803
                      break;
442
314k
                    while (count-- > 0)
443
310k
                      *p++=pixel[1];
444
4.44k
                  }
445
38.4k
                else
446
38.4k
                  {
447
38.4k
                    count=(ssize_t) pixel[0];
448
38.4k
                    if ((count == 0) || (count > (ssize_t) (end-p)))
449
36.7k
                      break;
450
1.67k
                    *p++=pixel[1];
451
1.67k
                    if (--count > 0)
452
1.30k
                      {
453
1.30k
                        count=ReadBlob(image,(size_t) count*sizeof(*p),p);
454
1.30k
                        if (count < 1)
455
14
                          break;
456
1.28k
                        p+=(ptrdiff_t) count;
457
1.28k
                      }
458
1.67k
                  }
459
43.6k
              }
460
38.3k
            }
461
9.57k
          }
462
9.69k
      }
463
14.6k
    q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
464
14.6k
    if (q == (Quantum *) NULL)
465
0
      break;
466
14.6k
    i=0;
467
9.94M
    for (x=0; x < (ssize_t) image->columns; x++)
468
9.93M
    {
469
9.93M
      if (image->compression == RLECompression)
470
9.85M
        {
471
9.85M
          pixel[0]=pixels[x];
472
9.85M
          pixel[1]=pixels[x+(ssize_t) image->columns];
473
9.85M
          pixel[2]=pixels[x+2*(ssize_t) image->columns];
474
9.85M
          pixel[3]=pixels[x+3*(ssize_t) image->columns];
475
9.85M
        }
476
74.0k
      else
477
74.0k
        {
478
74.0k
          pixel[0]=pixels[i++];
479
74.0k
          pixel[1]=pixels[i++];
480
74.0k
          pixel[2]=pixels[i++];
481
74.0k
          pixel[3]=pixels[i++];
482
74.0k
        }
483
9.93M
      SetPixelRed(image,0,q);
484
9.93M
      SetPixelGreen(image,0,q);
485
9.93M
      SetPixelBlue(image,0,q);
486
9.93M
      if (pixel[3] != 0)
487
5.24M
        {
488
5.24M
          gamma=pow(2.0,pixel[3]-(128.0+8.0));
489
5.24M
          SetPixelRed(image,ClampToQuantum((double) QuantumRange*gamma*
490
5.24M
            (double) pixel[0]),q);
491
5.24M
          SetPixelGreen(image,ClampToQuantum((double) QuantumRange*gamma*
492
5.24M
            (double) pixel[1]),q);
493
5.24M
          SetPixelBlue(image,ClampToQuantum((double) QuantumRange*gamma*
494
5.24M
            (double) pixel[2]),q);
495
5.24M
        }
496
9.93M
      q+=(ptrdiff_t) GetPixelChannels(image);
497
9.93M
    }
498
14.6k
    if (SyncAuthenticPixels(image,exception) == MagickFalse)
499
0
      break;
500
14.6k
    status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
501
14.6k
      image->rows);
502
14.6k
    if (status == MagickFalse)
503
0
      break;
504
14.6k
  }
505
696
  pixels=(unsigned char *) RelinquishMagickMemory(pixels);
506
696
  if (EOFBlob(image) != MagickFalse)
507
336
    ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
508
696
      image->filename);
509
696
  if (CloseBlob(image) == MagickFalse)
510
0
    status=MagickFalse;
511
696
  if (status == MagickFalse)
512
0
    return(DestroyImageList(image));
513
696
  return(GetFirstImageInList(image));
514
696
}
515

516
/*
517
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
518
%                                                                             %
519
%                                                                             %
520
%                                                                             %
521
%   R e g i s t e r H D R I m a g e                                           %
522
%                                                                             %
523
%                                                                             %
524
%                                                                             %
525
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
526
%
527
%  RegisterHDRImage() adds attributes for the Radiance RGBE image format to the
528
%  list of supported formats.  The attributes include the image format tag, a
529
%  method to read and/or write the format, whether the format supports the
530
%  saving of more than one frame to the same file or blob, whether the format
531
%  supports native in-memory I/O, and a brief description of the format.
532
%
533
%  The format of the RegisterHDRImage method is:
534
%
535
%      size_t RegisterHDRImage(void)
536
%
537
*/
538
ModuleExport size_t RegisterHDRImage(void)
539
10
{
540
10
  MagickInfo
541
10
    *entry;
542
543
10
  entry=AcquireMagickInfo("HDR","HDR","Radiance RGBE image format");
544
10
  entry->decoder=(DecodeImageHandler *) ReadHDRImage;
545
10
  entry->encoder=(EncodeImageHandler *) WriteHDRImage;
546
10
  entry->magick=(IsImageFormatHandler *) IsHDR;
547
10
  (void) RegisterMagickInfo(entry);
548
10
  return(MagickImageCoderSignature);
549
10
}
550

551
/*
552
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
553
%                                                                             %
554
%                                                                             %
555
%                                                                             %
556
%   U n r e g i s t e r H D R I m a g e                                       %
557
%                                                                             %
558
%                                                                             %
559
%                                                                             %
560
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
561
%
562
%  UnregisterHDRImage() removes format registrations made by the
563
%  HDR module from the list of supported formats.
564
%
565
%  The format of the UnregisterHDRImage method is:
566
%
567
%      UnregisterHDRImage(void)
568
%
569
*/
570
ModuleExport void UnregisterHDRImage(void)
571
0
{
572
0
  (void) UnregisterMagickInfo("HDR");
573
0
}
574

575
/*
576
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
577
%                                                                             %
578
%                                                                             %
579
%                                                                             %
580
%   W r i t e H D R I m a g e                                                 %
581
%                                                                             %
582
%                                                                             %
583
%                                                                             %
584
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
585
%
586
%  WriteHDRImage() writes an image in the Radiance RGBE image format.
587
%
588
%  The format of the WriteHDRImage method is:
589
%
590
%      MagickBooleanType WriteHDRImage(const ImageInfo *image_info,
591
%        Image *image,ExceptionInfo *exception)
592
%
593
%  A description of each parameter follows.
594
%
595
%    o image_info: the image info.
596
%
597
%    o image:  The image.
598
%
599
*/
600
601
static size_t HDRWriteRunlengthPixels(Image *image,unsigned char *pixels)
602
29.3k
{
603
10.7M
#define MinimumRunlength 4
604
605
29.3k
  size_t
606
29.3k
    p,
607
29.3k
    q;
608
609
29.3k
  size_t
610
29.3k
    runlength;
611
612
29.3k
  ssize_t
613
29.3k
    count,
614
29.3k
    previous_count;
615
616
29.3k
  unsigned char
617
29.3k
    pixel[2];
618
619
943k
  for (p=0; p < image->columns; )
620
914k
  {
621
914k
    q=p;
622
914k
    runlength=0;
623
914k
    previous_count=0;
624
9.85M
    while ((runlength < MinimumRunlength) && (q < image->columns))
625
8.93M
    {
626
8.93M
      q+=(ptrdiff_t) runlength;
627
8.93M
      previous_count=(ssize_t) runlength;
628
8.93M
      runlength=1;
629
33.5M
      while ((pixels[q] == pixels[q+runlength]) &&
630
24.7M
             ((q+runlength) < image->columns) && (runlength < 127))
631
24.5M
       runlength++;
632
8.93M
    }
633
914k
    if ((previous_count > 1) && (previous_count == (ssize_t) (q-p)))
634
36.3k
      {
635
36.3k
        pixel[0]=(unsigned char) (128+previous_count);
636
36.3k
        pixel[1]=pixels[p];
637
36.3k
        if (WriteBlob(image,2*sizeof(*pixel),pixel) < 1)
638
0
          break;
639
36.3k
        p=q;
640
36.3k
      }
641
1.59M
    while (p < q)
642
682k
    {
643
682k
      count=(ssize_t) (q-p);
644
682k
      if (count > 128)
645
19.9k
        count=128;
646
682k
      pixel[0]=(unsigned char) count;
647
682k
      if (WriteBlob(image,sizeof(*pixel),pixel) < 1)
648
0
        break;
649
682k
      if (WriteBlob(image,(size_t) count*sizeof(*pixel),&pixels[p]) < 1)
650
0
        break;
651
682k
      p+=(ptrdiff_t) count;
652
682k
    }
653
914k
    if (runlength >= MinimumRunlength)
654
901k
      {
655
901k
        pixel[0]=(unsigned char) (128+runlength);
656
901k
        pixel[1]=pixels[q];
657
901k
        if (WriteBlob(image,2*sizeof(*pixel),pixel) < 1)
658
0
          break;
659
901k
        p+=(ptrdiff_t) runlength;
660
901k
      }
661
914k
  }
662
29.3k
  return(p);
663
29.3k
}
664
665
static MagickBooleanType WriteHDRImage(const ImageInfo *image_info,Image *image,
666
  ExceptionInfo *exception)
667
360
{
668
360
  char
669
360
    header[MagickPathExtent];
670
671
360
  const char
672
360
    *property;
673
674
360
  MagickBooleanType
675
360
    status;
676
677
360
  const Quantum
678
360
    *p;
679
680
360
  ssize_t
681
360
    i,
682
360
    x;
683
684
360
  size_t
685
360
    length;
686
687
360
  ssize_t
688
360
    count,
689
360
    y;
690
691
360
  unsigned char
692
360
    pixel[4],
693
360
    *pixels;
694
695
  /*
696
    Open output image file.
697
  */
698
360
  assert(image_info != (const ImageInfo *) NULL);
699
360
  assert(image_info->signature == MagickCoreSignature);
700
360
  assert(image != (Image *) NULL);
701
360
  assert(image->signature == MagickCoreSignature);
702
360
  assert(exception != (ExceptionInfo *) NULL);
703
360
  assert(exception->signature == MagickCoreSignature);
704
360
  if (IsEventLogging() != MagickFalse)
705
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
706
360
  status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
707
360
  if (status == MagickFalse)
708
0
    return(status);
709
360
  if (IsRGBColorspace(image->colorspace) == MagickFalse)
710
336
    (void) TransformImageColorspace(image,RGBColorspace,exception);
711
  /*
712
    Write header.
713
  */
714
360
  (void) memset(header,' ',MagickPathExtent);
715
360
  length=CopyMagickString(header,"#?RADIANCE\n",MagickPathExtent);
716
360
  (void) WriteBlob(image,length,(unsigned char *) header);
717
360
  property=GetImageProperty(image,"comment",exception);
718
360
  if ((property != (const char *) NULL) &&
719
64
      (strchr(property,'\n') == (char *) NULL))
720
64
    {
721
64
      count=FormatLocaleString(header,MagickPathExtent,"#%.*s\n",
722
64
        MagickPathExtent-3,property);
723
64
      (void) WriteBlob(image,(size_t) count,(unsigned char *) header);
724
64
    }
725
360
  property=GetImageProperty(image,"hdr:exposure",exception);
726
360
  if (property != (const char *) NULL)
727
2
    {
728
2
      count=FormatLocaleString(header,MagickPathExtent,"EXPOSURE=%g\n",
729
2
        strtod(property,(char **) NULL));
730
2
      (void) WriteBlob(image,(size_t) count,(unsigned char *) header);
731
2
    }
732
360
  if (image->gamma != 0.0)
733
360
    {
734
360
      count=FormatLocaleString(header,MagickPathExtent,"GAMMA=%g\n",
735
360
        image->gamma);
736
360
      (void) WriteBlob(image,(size_t) count,(unsigned char *) header);
737
360
    }
738
360
  count=FormatLocaleString(header,MagickPathExtent,
739
360
    "PRIMARIES=%g %g %g %g %g %g %g %g\n",
740
360
    image->chromaticity.red_primary.x,image->chromaticity.red_primary.y,
741
360
    image->chromaticity.green_primary.x,image->chromaticity.green_primary.y,
742
360
    image->chromaticity.blue_primary.x,image->chromaticity.blue_primary.y,
743
360
    image->chromaticity.white_point.x,image->chromaticity.white_point.y);
744
360
  (void) WriteBlob(image,(size_t) count,(unsigned char *) header);
745
360
  length=CopyMagickString(header,"FORMAT=32-bit_rle_rgbe\n\n",MagickPathExtent);
746
360
  (void) WriteBlob(image,length,(unsigned char *) header);
747
360
  count=FormatLocaleString(header,MagickPathExtent,"-Y %.20g +X %.20g\n",
748
360
    (double) image->rows,(double) image->columns);
749
360
  (void) WriteBlob(image,(size_t) count,(unsigned char *) header);
750
  /*
751
    Write HDR pixels.
752
  */
753
360
  pixels=(unsigned char *) AcquireQuantumMemory(image->columns+128,4*
754
360
    sizeof(*pixels));
755
360
  if (pixels == (unsigned char *) NULL)
756
360
    ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
757
360
  (void) memset(pixels,0,4*(image->columns+128)*sizeof(*pixels));
758
12.4k
  for (y=0; y < (ssize_t) image->rows; y++)
759
12.0k
  {
760
12.0k
    p=GetVirtualPixels(image,0,y,image->columns,1,exception);
761
12.0k
    if (p == (const Quantum *) NULL)
762
0
      break;
763
12.0k
    if ((image->columns >= 8) && (image->columns <= 0x7ffff))
764
7.33k
      {
765
7.33k
        pixel[0]=2;
766
7.33k
        pixel[1]=2;
767
7.33k
        pixel[2]=(unsigned char) (image->columns >> 8);
768
7.33k
        pixel[3]=(unsigned char) (image->columns & 0xff);
769
7.33k
        count=WriteBlob(image,4*sizeof(*pixel),pixel);
770
7.33k
        if (count != (ssize_t) (4*sizeof(*pixel)))
771
0
          break;
772
7.33k
      }
773
12.0k
    i=0;
774
8.39M
    for (x=0; x < (ssize_t) image->columns; x++)
775
8.37M
    {
776
8.37M
      double
777
8.37M
        gamma;
778
779
8.37M
      pixel[0]=0;
780
8.37M
      pixel[1]=0;
781
8.37M
      pixel[2]=0;
782
8.37M
      pixel[3]=0;
783
8.37M
      gamma=QuantumScale*(double) GetPixelRed(image,p);
784
8.37M
      if ((QuantumScale*(double) GetPixelGreen(image,p)) > gamma)
785
2.20M
        gamma=QuantumScale*(double) GetPixelGreen(image,p);
786
8.37M
      if ((QuantumScale*(double) GetPixelBlue(image,p)) > gamma)
787
203k
        gamma=QuantumScale*(double) GetPixelBlue(image,p);
788
8.37M
      if (gamma > MagickEpsilon)
789
3.84M
        {
790
3.84M
          int
791
3.84M
            exponent;
792
793
3.84M
          gamma=frexp(gamma,&exponent)*256.0/gamma;
794
3.84M
          if (GetPixelRed(image,p) > 0)
795
3.27M
            pixel[0]=(unsigned char) (gamma*QuantumScale*(double)
796
3.27M
              GetPixelRed(image,p));
797
3.84M
          if (GetPixelGreen(image,p) > 0)
798
3.33M
            pixel[1]=(unsigned char) (gamma*QuantumScale*
799
3.33M
              (double) GetPixelGreen(image,p));
800
3.84M
          if (GetPixelBlue(image,p) > 0)
801
3.54M
            pixel[2]=(unsigned char) (gamma*QuantumScale*(double)
802
3.54M
              GetPixelBlue(image,p));
803
3.84M
          pixel[3]=(unsigned char) (exponent+128);
804
3.84M
        }
805
8.37M
      if ((image->columns >= 8) && (image->columns <= 0x7ffff))
806
8.37M
        {
807
8.37M
          pixels[x]=pixel[0];
808
8.37M
          pixels[x+(ssize_t) image->columns]=pixel[1];
809
8.37M
          pixels[x+2*(ssize_t) image->columns]=pixel[2];
810
8.37M
          pixels[x+3*(ssize_t) image->columns]=pixel[3];
811
8.37M
        }
812
4.86k
      else
813
4.86k
        {
814
4.86k
          pixels[i++]=pixel[0];
815
4.86k
          pixels[i++]=pixel[1];
816
4.86k
          pixels[i++]=pixel[2];
817
4.86k
          pixels[i++]=pixel[3];
818
4.86k
        }
819
8.37M
      p+=(ptrdiff_t) GetPixelChannels(image);
820
8.37M
    }
821
12.0k
    if ((image->columns >= 8) && (image->columns <= 0x7ffff))
822
7.33k
      {
823
36.6k
        for (i=0; i < 4; i++)
824
29.3k
          length=HDRWriteRunlengthPixels(image,
825
29.3k
            &pixels[i*(ssize_t) image->columns]);
826
7.33k
      }
827
4.71k
    else
828
4.71k
      {
829
4.71k
        count=WriteBlob(image,4*image->columns*sizeof(*pixels),pixels);
830
4.71k
        if (count != (ssize_t) (4*image->columns*sizeof(*pixels)))
831
0
          break;
832
4.71k
      }
833
12.0k
    status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
834
12.0k
      image->rows);
835
12.0k
    if (status == MagickFalse)
836
0
      break;
837
12.0k
  }
838
360
  pixels=(unsigned char *) RelinquishMagickMemory(pixels);
839
360
  if (CloseBlob(image) == MagickFalse)
840
0
    status=MagickFalse;
841
360
  return(status);
842
360
}