Coverage Report

Created: 2026-07-20 07:19

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.76k
{
137
1.76k
  char
138
1.76k
    format[MagickPathExtent],
139
1.76k
    keyword[MagickPathExtent],
140
1.76k
    tag[MagickPathExtent],
141
1.76k
    value[MagickPathExtent];
142
143
1.76k
  double
144
1.76k
    gamma;
145
146
1.76k
  float
147
1.76k
    chromaticity[6],
148
1.76k
    white_point[2];
149
150
1.76k
  Image
151
1.76k
    *image;
152
153
1.76k
  int
154
1.76k
    c,
155
1.76k
    chromaticity_count = 0;
156
157
1.76k
  MagickBooleanType
158
1.76k
    status,
159
1.76k
    value_expected;
160
161
1.76k
  Quantum
162
1.76k
    *q;
163
164
1.76k
  ssize_t
165
1.76k
    i,
166
1.76k
    x;
167
168
1.76k
  ssize_t
169
1.76k
    y;
170
171
1.76k
  unsigned char
172
1.76k
    *end,
173
1.76k
    pixel[4],
174
1.76k
    *pixels;
175
176
  /*
177
    Open image file.
178
  */
179
1.76k
  assert(image_info != (const ImageInfo *) NULL);
180
1.76k
  assert(image_info->signature == MagickCoreSignature);
181
1.76k
  assert(exception != (ExceptionInfo *) NULL);
182
1.76k
  assert(exception->signature == MagickCoreSignature);
183
1.76k
  if (IsEventLogging() != MagickFalse)
184
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
185
0
      image_info->filename);
186
1.76k
  image=AcquireImage(image_info,exception);
187
1.76k
  status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
188
1.76k
  if (status == MagickFalse)
189
0
    {
190
0
      image=DestroyImageList(image);
191
0
      return((Image *) NULL);
192
0
    }
193
  /*
194
    Decode image header.
195
  */
196
1.76k
  image->columns=0;
197
1.76k
  image->rows=0;
198
1.76k
  *format='\0';
199
1.76k
  c=ReadBlobByte(image);
200
1.76k
  if (c == EOF)
201
0
    {
202
0
      image=DestroyImage(image);
203
0
      return((Image *) NULL);
204
0
    }
205
38.4k
  while (isgraph((int) ((unsigned char) c)) && (image->columns == 0) && (image->rows == 0))
206
36.7k
  {
207
36.7k
    if (c == (int) '#')
208
7.75k
      {
209
7.75k
        char
210
7.75k
          *comment;
211
212
7.75k
        char
213
7.75k
          *p;
214
215
7.75k
        size_t
216
7.75k
          length;
217
218
        /*
219
          Read comment-- any text between # and end-of-line.
220
        */
221
7.75k
        length=MagickPathExtent;
222
7.75k
        comment=AcquireString((char *) NULL);
223
56.9k
        for (p=comment; comment != (char *) NULL; p++)
224
56.9k
        {
225
56.9k
          c=ReadBlobByte(image);
226
56.9k
          if ((c == EOF) || (c == (int) '\n'))
227
7.75k
            break;
228
49.2k
          if ((size_t) (p-comment+1) >= length)
229
5
            {
230
5
              *p='\0';
231
5
              length<<=1;
232
5
              comment=(char *) ResizeQuantumMemory(comment,length+
233
5
                MagickPathExtent,sizeof(*comment));
234
5
              if (comment == (char *) NULL)
235
0
                break;
236
5
              p=comment+strlen(comment);
237
5
            }
238
49.2k
          *p=(char) c;
239
49.2k
        }
240
7.75k
        if (comment == (char *) NULL)
241
7.75k
          ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
242
7.75k
        *p='\0';
243
7.75k
        (void) SetImageProperty(image,"comment",comment,exception);
244
7.75k
        comment=DestroyString(comment);
245
7.75k
        c=ReadBlobByte(image);
246
7.75k
      }
247
28.9k
    else
248
28.9k
      if (isalnum((int) ((unsigned char) c)) == 0)
249
2.94k
        c=ReadBlobByte(image);
250
26.0k
      else
251
26.0k
        {
252
26.0k
          char
253
26.0k
            *p;
254
255
          /*
256
            Determine a keyword and its value.
257
          */
258
26.0k
          p=keyword;
259
26.0k
          do
260
153k
          {
261
153k
            if ((size_t) (p-keyword) < (MagickPathExtent-1))
262
150k
              *p++=(char) c;
263
153k
            c=ReadBlobByte(image);
264
153k
          } while (isalnum((int) ((unsigned char) c)) || (c == '_'));
265
26.0k
          *p='\0';
266
26.0k
          value_expected=MagickFalse;
267
53.4k
          while ((isspace((int) ((unsigned char) c)) != 0) || (c == '='))
268
27.4k
          {
269
27.4k
            if (c == '=')
270
21.6k
              value_expected=MagickTrue;
271
27.4k
            c=ReadBlobByte(image);
272
27.4k
          }
273
26.0k
          if (LocaleCompare(keyword,"Y") == 0)
274
3.93k
            value_expected=MagickTrue;
275
26.0k
          if (value_expected == MagickFalse)
276
2.00k
            continue;
277
24.0k
          p=value;
278
244k
          while ((c != '\n') && (c != '\0') && (c != EOF))
279
220k
          {
280
220k
            if ((size_t) (p-value) < (MagickPathExtent-1))
281
208k
              *p++=(char) c;
282
220k
            c=ReadBlobByte(image);
283
220k
          }
284
24.0k
          *p='\0';
285
          /*
286
            Assign a value to the specified keyword.
287
          */
288
24.0k
          switch (*keyword)
289
24.0k
          {
290
1.36k
            case 'F':
291
7.07k
            case 'f':
292
7.07k
            {
293
7.07k
              if (LocaleCompare(keyword,"format") == 0)
294
5.48k
                {
295
5.48k
                  (void) CopyMagickString(format,value,MagickPathExtent);
296
5.48k
                  break;
297
5.48k
                }
298
1.59k
              (void) FormatLocaleString(tag,MagickPathExtent,"hdr:%s",keyword);
299
1.59k
              (void) SetImageProperty(image,tag,value,exception);
300
1.59k
              break;
301
7.07k
            }
302
1.17k
            case 'G':
303
3.02k
            case 'g':
304
3.02k
            {
305
3.02k
              if (LocaleCompare(keyword,"gamma") == 0)
306
839
                {
307
839
                  image->gamma=StringToDouble(value,(char **) NULL);
308
839
                  break;
309
839
                }
310
2.18k
              (void) FormatLocaleString(tag,MagickPathExtent,"hdr:%s",keyword);
311
2.18k
              (void) SetImageProperty(image,tag,value,exception);
312
2.18k
              break;
313
3.02k
            }
314
1.45k
            case 'P':
315
2.04k
            case 'p':
316
2.04k
            {
317
2.04k
              if (LocaleCompare(keyword,"primaries") == 0)
318
275
                {
319
275
                  chromaticity_count=MagickSscanf(value,"%g %g %g %g %g %g %g %g",
320
275
                    &chromaticity[0],&chromaticity[1],&chromaticity[2],
321
275
                    &chromaticity[3],&chromaticity[4],&chromaticity[5],
322
275
                    &white_point[0],&white_point[1]);
323
275
                  break;
324
275
                }
325
1.76k
              (void) FormatLocaleString(tag,MagickPathExtent,"hdr:%s",keyword);
326
1.76k
              (void) SetImageProperty(image,tag,value,exception);
327
1.76k
              break;
328
2.04k
            }
329
2.46k
            case 'Y':
330
4.49k
            case 'y':
331
4.49k
            {
332
4.49k
              char
333
4.49k
                target[] = "Y";
334
335
4.49k
              if (strcmp(keyword,target) == 0)
336
1.96k
                {
337
1.96k
                  int
338
1.96k
                    height,
339
1.96k
                    width;
340
341
1.96k
                  if (MagickSscanf(value,"%d +X %d",&height,&width) == 2)
342
971
                    {
343
971
                      if ((width <= 0) || (height <= 0))
344
94
                        ThrowReaderException(CorruptImageError,
345
971
                          "ImproperImageHeader");
346
877
                      image->columns=(size_t) width;
347
877
                      image->rows=(size_t) height;
348
877
                    }
349
1.86k
                  break;
350
1.96k
                }
351
2.52k
              (void) FormatLocaleString(tag,MagickPathExtent,"hdr:%s",keyword);
352
2.52k
              (void) SetImageProperty(image,tag,value,exception);
353
2.52k
              break;
354
4.49k
            }
355
7.38k
            default:
356
7.38k
            {
357
7.38k
              (void) FormatLocaleString(tag,MagickPathExtent,"hdr:%s",keyword);
358
7.38k
              (void) SetImageProperty(image,tag,value,exception);
359
7.38k
              break;
360
4.49k
            }
361
24.0k
          }
362
24.0k
        }
363
34.6k
    if ((image->columns == 0) && (image->rows == 0))
364
60.7k
      while (isspace((int) ((unsigned char) c)) != 0)
365
26.9k
        c=ReadBlobByte(image);
366
34.6k
  }
367
1.66k
  if (LocaleCompare(format,"32-bit_rle_rgbe") == 0)
368
47
    (void) SetImageColorspace(image,RGBColorspace,exception);
369
1.61k
  else if (LocaleCompare(format,"32-bit_rle_xyze") == 0)
370
794
    (void) SetImageColorspace(image,XYZColorspace,exception);
371
825
  else
372
841
    ThrowReaderException(CorruptImageError,"ImproperImageHeader");
373
841
  if (chromaticity_count == 8)
374
1
    {
375
1
      image->chromaticity.red_primary.x=chromaticity[0];
376
1
      image->chromaticity.red_primary.y=chromaticity[1];
377
1
      image->chromaticity.green_primary.x=chromaticity[2];
378
1
      image->chromaticity.green_primary.y=chromaticity[3];
379
1
      image->chromaticity.blue_primary.x=chromaticity[4];
380
1
      image->chromaticity.blue_primary.y=chromaticity[5];
381
1
      image->chromaticity.white_point.x=white_point[0];
382
1
      image->chromaticity.white_point.y=white_point[1];
383
1
    }
384
841
  image->compression=(image->columns < 8) || (image->columns > 0x7ffff) ?
385
580
    NoCompression : RLECompression;
386
841
  if (image_info->ping != MagickFalse)
387
1
    {
388
1
      (void) CloseBlob(image);
389
1
      return(GetFirstImageInList(image));
390
1
    }
391
840
  status=SetImageExtent(image,image->columns,image->rows,exception);
392
840
  if (status == MagickFalse)
393
126
    return(DestroyImageList(image));
394
  /*
395
    Read RGBE (red+green+blue+exponent) pixels.
396
  */
397
714
  pixels=(unsigned char *) AcquireQuantumMemory(image->columns,4*
398
714
    sizeof(*pixels));
399
714
  if (pixels == (unsigned char *) NULL)
400
714
    ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
401
714
  (void) memset(pixels,0,4*image->columns*sizeof(*pixels));
402
15.5k
  for (y=0; y < (ssize_t) image->rows; y++)
403
15.0k
  {
404
15.0k
    ssize_t
405
15.0k
      count;
406
407
15.0k
    if (image->compression != RLECompression)
408
5.23k
      {
409
5.23k
        count=ReadBlob(image,4*image->columns*sizeof(*pixels),pixels);
410
5.23k
        if (count != (ssize_t) (4*image->columns*sizeof(*pixels)))
411
110
          break;
412
5.23k
      }
413
9.86k
    else
414
9.86k
      {
415
9.86k
        count=ReadBlob(image,4*sizeof(*pixel),pixel);
416
9.86k
        if (count != 4)
417
135
          break;
418
9.72k
        if ((size_t) ((((size_t) pixel[2]) << 8) | pixel[3]) != image->columns)
419
126
          {
420
126
            (void) memcpy(pixels,pixel,4*sizeof(*pixel));
421
126
            count=ReadBlob(image,4*(image->columns-1)*sizeof(*pixels),pixels+4);
422
126
            image->compression=NoCompression;
423
126
          }
424
9.60k
        else
425
9.60k
          {
426
9.60k
            unsigned char
427
9.60k
              *p;
428
429
9.60k
            p=pixels;
430
48.0k
            for (i=0; i < 4; i++)
431
38.4k
            {
432
38.4k
              end=&pixels[(i+1)*(ssize_t) image->columns];
433
44.6k
              while (p < end)
434
44.1k
              {
435
44.1k
                count=ReadBlob(image,2*sizeof(*pixel),pixel);
436
44.1k
                if (count < 1)
437
343
                  break;
438
43.8k
                if (pixel[0] > 128)
439
5.47k
                  {
440
5.47k
                    count=(ssize_t) pixel[0]-128;
441
5.47k
                    if ((count == 0) || (count > (ssize_t) (end-p)))
442
758
                      break;
443
304k
                    while (count-- > 0)
444
299k
                      *p++=pixel[1];
445
4.71k
                  }
446
38.3k
                else
447
38.3k
                  {
448
38.3k
                    count=(ssize_t) pixel[0];
449
38.3k
                    if ((count == 0) || (count > (ssize_t) (end-p)))
450
36.7k
                      break;
451
1.57k
                    *p++=pixel[1];
452
1.57k
                    if (--count > 0)
453
1.21k
                      {
454
1.21k
                        count=ReadBlob(image,(size_t) count*sizeof(*p),p);
455
1.21k
                        if (count < 1)
456
15
                          break;
457
1.19k
                        p+=(ptrdiff_t) count;
458
1.19k
                      }
459
1.57k
                  }
460
43.8k
              }
461
38.4k
            }
462
9.60k
          }
463
9.72k
      }
464
14.8k
    q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
465
14.8k
    if (q == (Quantum *) NULL)
466
0
      break;
467
14.8k
    i=0;
468
9.87M
    for (x=0; x < (ssize_t) image->columns; x++)
469
9.85M
    {
470
9.85M
      if (image->compression == RLECompression)
471
9.78M
        {
472
9.78M
          pixel[0]=pixels[x];
473
9.78M
          pixel[1]=pixels[x+(ssize_t) image->columns];
474
9.78M
          pixel[2]=pixels[x+2*(ssize_t) image->columns];
475
9.78M
          pixel[3]=pixels[x+3*(ssize_t) image->columns];
476
9.78M
        }
477
74.5k
      else
478
74.5k
        {
479
74.5k
          pixel[0]=pixels[i++];
480
74.5k
          pixel[1]=pixels[i++];
481
74.5k
          pixel[2]=pixels[i++];
482
74.5k
          pixel[3]=pixels[i++];
483
74.5k
        }
484
9.85M
      SetPixelRed(image,0,q);
485
9.85M
      SetPixelGreen(image,0,q);
486
9.85M
      SetPixelBlue(image,0,q);
487
9.85M
      if (pixel[3] != 0)
488
5.34M
        {
489
5.34M
          gamma=pow(2.0,pixel[3]-(128.0+8.0));
490
5.34M
          SetPixelRed(image,ClampToQuantum((double) QuantumRange*gamma*
491
5.34M
            (double) pixel[0]),q);
492
5.34M
          SetPixelGreen(image,ClampToQuantum((double) QuantumRange*gamma*
493
5.34M
            (double) pixel[1]),q);
494
5.34M
          SetPixelBlue(image,ClampToQuantum((double) QuantumRange*gamma*
495
5.34M
            (double) pixel[2]),q);
496
5.34M
        }
497
9.85M
      q+=(ptrdiff_t) GetPixelChannels(image);
498
9.85M
    }
499
14.8k
    if (SyncAuthenticPixels(image,exception) == MagickFalse)
500
0
      break;
501
14.8k
    status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
502
14.8k
      image->rows);
503
14.8k
    if (status == MagickFalse)
504
0
      break;
505
14.8k
  }
506
714
  pixels=(unsigned char *) RelinquishMagickMemory(pixels);
507
714
  if (EOFBlob(image) != MagickFalse)
508
341
    ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
509
714
      image->filename);
510
714
  if (CloseBlob(image) == MagickFalse)
511
0
    status=MagickFalse;
512
714
  if (status == MagickFalse)
513
0
    return(DestroyImageList(image));
514
714
  return(GetFirstImageInList(image));
515
714
}
516

517
/*
518
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
519
%                                                                             %
520
%                                                                             %
521
%                                                                             %
522
%   R e g i s t e r H D R I m a g e                                           %
523
%                                                                             %
524
%                                                                             %
525
%                                                                             %
526
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
527
%
528
%  RegisterHDRImage() adds attributes for the Radiance RGBE image format to the
529
%  list of supported formats.  The attributes include the image format tag, a
530
%  method to read and/or write the format, whether the format supports the
531
%  saving of more than one frame to the same file or blob, whether the format
532
%  supports native in-memory I/O, and a brief description of the format.
533
%
534
%  The format of the RegisterHDRImage method is:
535
%
536
%      size_t RegisterHDRImage(void)
537
%
538
*/
539
ModuleExport size_t RegisterHDRImage(void)
540
10
{
541
10
  MagickInfo
542
10
    *entry;
543
544
10
  entry=AcquireMagickInfo("HDR","HDR","Radiance RGBE image format");
545
10
  entry->decoder=(DecodeImageHandler *) ReadHDRImage;
546
10
  entry->encoder=(EncodeImageHandler *) WriteHDRImage;
547
10
  entry->magick=(IsImageFormatHandler *) IsHDR;
548
10
  (void) RegisterMagickInfo(entry);
549
10
  return(MagickImageCoderSignature);
550
10
}
551

552
/*
553
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
554
%                                                                             %
555
%                                                                             %
556
%                                                                             %
557
%   U n r e g i s t e r H D R I m a g e                                       %
558
%                                                                             %
559
%                                                                             %
560
%                                                                             %
561
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
562
%
563
%  UnregisterHDRImage() removes format registrations made by the
564
%  HDR module from the list of supported formats.
565
%
566
%  The format of the UnregisterHDRImage method is:
567
%
568
%      UnregisterHDRImage(void)
569
%
570
*/
571
ModuleExport void UnregisterHDRImage(void)
572
0
{
573
0
  (void) UnregisterMagickInfo("HDR");
574
0
}
575

576
/*
577
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
578
%                                                                             %
579
%                                                                             %
580
%                                                                             %
581
%   W r i t e H D R I m a g e                                                 %
582
%                                                                             %
583
%                                                                             %
584
%                                                                             %
585
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
586
%
587
%  WriteHDRImage() writes an image in the Radiance RGBE image format.
588
%
589
%  The format of the WriteHDRImage method is:
590
%
591
%      MagickBooleanType WriteHDRImage(const ImageInfo *image_info,
592
%        Image *image,ExceptionInfo *exception)
593
%
594
%  A description of each parameter follows.
595
%
596
%    o image_info: the image info.
597
%
598
%    o image:  The image.
599
%
600
*/
601
602
static size_t HDRWriteRunlengthPixels(Image *image,unsigned char *pixels)
603
29.5k
{
604
10.6M
#define MinimumRunlength 4
605
606
29.5k
  size_t
607
29.5k
    p,
608
29.5k
    q;
609
610
29.5k
  size_t
611
29.5k
    runlength;
612
613
29.5k
  ssize_t
614
29.5k
    count,
615
29.5k
    previous_count;
616
617
29.5k
  unsigned char
618
29.5k
    pixel[2];
619
620
946k
  for (p=0; p < image->columns; )
621
917k
  {
622
917k
    q=p;
623
917k
    runlength=0;
624
917k
    previous_count=0;
625
9.77M
    while ((runlength < MinimumRunlength) && (q < image->columns))
626
8.85M
    {
627
8.85M
      q+=(ptrdiff_t) runlength;
628
8.85M
      previous_count=(ssize_t) runlength;
629
8.85M
      runlength=1;
630
33.0M
      while ((pixels[q] == pixels[q+runlength]) &&
631
24.3M
             ((q+runlength) < image->columns) && (runlength < 127))
632
24.2M
       runlength++;
633
8.85M
    }
634
917k
    if ((previous_count > 1) && (previous_count == (ssize_t) (q-p)))
635
36.6k
      {
636
36.6k
        pixel[0]=(unsigned char) (128+previous_count);
637
36.6k
        pixel[1]=pixels[p];
638
36.6k
        if (WriteBlob(image,2*sizeof(*pixel),pixel) < 1)
639
0
          break;
640
36.6k
        p=q;
641
36.6k
      }
642
1.60M
    while (p < q)
643
690k
    {
644
690k
      count=(ssize_t) (q-p);
645
690k
      if (count > 128)
646
19.7k
        count=128;
647
690k
      pixel[0]=(unsigned char) count;
648
690k
      if (WriteBlob(image,sizeof(*pixel),pixel) < 1)
649
0
        break;
650
690k
      if (WriteBlob(image,(size_t) count*sizeof(*pixel),&pixels[p]) < 1)
651
0
        break;
652
690k
      p+=(ptrdiff_t) count;
653
690k
    }
654
917k
    if (runlength >= MinimumRunlength)
655
904k
      {
656
904k
        pixel[0]=(unsigned char) (128+runlength);
657
904k
        pixel[1]=pixels[q];
658
904k
        if (WriteBlob(image,2*sizeof(*pixel),pixel) < 1)
659
0
          break;
660
904k
        p+=(ptrdiff_t) runlength;
661
904k
      }
662
917k
  }
663
29.5k
  return(p);
664
29.5k
}
665
666
static MagickBooleanType WriteHDRImage(const ImageInfo *image_info,Image *image,
667
  ExceptionInfo *exception)
668
373
{
669
373
  char
670
373
    header[MagickPathExtent];
671
672
373
  const char
673
373
    *property;
674
675
373
  MagickBooleanType
676
373
    status;
677
678
373
  const Quantum
679
373
    *p;
680
681
373
  ssize_t
682
373
    i,
683
373
    x;
684
685
373
  size_t
686
373
    length;
687
688
373
  ssize_t
689
373
    count,
690
373
    y;
691
692
373
  unsigned char
693
373
    pixel[4],
694
373
    *pixels;
695
696
  /*
697
    Open output image file.
698
  */
699
373
  assert(image_info != (const ImageInfo *) NULL);
700
373
  assert(image_info->signature == MagickCoreSignature);
701
373
  assert(image != (Image *) NULL);
702
373
  assert(image->signature == MagickCoreSignature);
703
373
  assert(exception != (ExceptionInfo *) NULL);
704
373
  assert(exception->signature == MagickCoreSignature);
705
373
  if (IsEventLogging() != MagickFalse)
706
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
707
373
  status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
708
373
  if (status == MagickFalse)
709
0
    return(status);
710
373
  if (IsRGBColorspace(image->colorspace) == MagickFalse)
711
349
    (void) TransformImageColorspace(image,RGBColorspace,exception);
712
  /*
713
    Write header.
714
  */
715
373
  (void) memset(header,' ',MagickPathExtent);
716
373
  length=CopyMagickString(header,"#?RADIANCE\n",MagickPathExtent);
717
373
  (void) WriteBlob(image,length,(unsigned char *) header);
718
373
  property=GetImageProperty(image,"comment",exception);
719
373
  if ((property != (const char *) NULL) &&
720
64
      (strchr(property,'\n') == (char *) NULL))
721
64
    {
722
64
      count=FormatLocaleString(header,MagickPathExtent,"#%.*s\n",
723
64
        MagickPathExtent-3,property);
724
64
      (void) WriteBlob(image,(size_t) count,(unsigned char *) header);
725
64
    }
726
373
  property=GetImageProperty(image,"hdr:exposure",exception);
727
373
  if (property != (const char *) NULL)
728
2
    {
729
2
      count=FormatLocaleString(header,MagickPathExtent,"EXPOSURE=%g\n",
730
2
        strtod(property,(char **) NULL));
731
2
      (void) WriteBlob(image,(size_t) count,(unsigned char *) header);
732
2
    }
733
373
  if (image->gamma != 0.0)
734
373
    {
735
373
      count=FormatLocaleString(header,MagickPathExtent,"GAMMA=%g\n",
736
373
        image->gamma);
737
373
      (void) WriteBlob(image,(size_t) count,(unsigned char *) header);
738
373
    }
739
373
  count=FormatLocaleString(header,MagickPathExtent,
740
373
    "PRIMARIES=%g %g %g %g %g %g %g %g\n",
741
373
    image->chromaticity.red_primary.x,image->chromaticity.red_primary.y,
742
373
    image->chromaticity.green_primary.x,image->chromaticity.green_primary.y,
743
373
    image->chromaticity.blue_primary.x,image->chromaticity.blue_primary.y,
744
373
    image->chromaticity.white_point.x,image->chromaticity.white_point.y);
745
373
  (void) WriteBlob(image,(size_t) count,(unsigned char *) header);
746
373
  length=CopyMagickString(header,"FORMAT=32-bit_rle_rgbe\n\n",MagickPathExtent);
747
373
  (void) WriteBlob(image,length,(unsigned char *) header);
748
373
  count=FormatLocaleString(header,MagickPathExtent,"-Y %.17g +X %.17g\n",
749
373
    (double) image->rows,(double) image->columns);
750
373
  (void) WriteBlob(image,(size_t) count,(unsigned char *) header);
751
  /*
752
    Write HDR pixels.
753
  */
754
373
  pixels=(unsigned char *) AcquireQuantumMemory(image->columns+128,4*
755
373
    sizeof(*pixels));
756
373
  if (pixels == (unsigned char *) NULL)
757
373
    ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
758
373
  (void) memset(pixels,0,4*(image->columns+128)*sizeof(*pixels));
759
12.5k
  for (y=0; y < (ssize_t) image->rows; y++)
760
12.1k
  {
761
12.1k
    p=GetVirtualPixels(image,0,y,image->columns,1,exception);
762
12.1k
    if (p == (const Quantum *) NULL)
763
0
      break;
764
12.1k
    if ((image->columns >= 8) && (image->columns <= 0x7ffff))
765
7.39k
      {
766
7.39k
        pixel[0]=2;
767
7.39k
        pixel[1]=2;
768
7.39k
        pixel[2]=(unsigned char) (image->columns >> 8);
769
7.39k
        pixel[3]=(unsigned char) (image->columns & 0xff);
770
7.39k
        count=WriteBlob(image,4*sizeof(*pixel),pixel);
771
7.39k
        if (count != (ssize_t) (4*sizeof(*pixel)))
772
0
          break;
773
7.39k
      }
774
12.1k
    i=0;
775
8.28M
    for (x=0; x < (ssize_t) image->columns; x++)
776
8.27M
    {
777
8.27M
      double
778
8.27M
        gamma;
779
780
8.27M
      pixel[0]=0;
781
8.27M
      pixel[1]=0;
782
8.27M
      pixel[2]=0;
783
8.27M
      pixel[3]=0;
784
8.27M
      gamma=QuantumScale*(double) GetPixelRed(image,p);
785
8.27M
      if ((QuantumScale*(double) GetPixelGreen(image,p)) > gamma)
786
2.17M
        gamma=QuantumScale*(double) GetPixelGreen(image,p);
787
8.27M
      if ((QuantumScale*(double) GetPixelBlue(image,p)) > gamma)
788
191k
        gamma=QuantumScale*(double) GetPixelBlue(image,p);
789
8.27M
      if (gamma > MagickEpsilon)
790
3.74M
        {
791
3.74M
          int
792
3.74M
            exponent;
793
794
3.74M
          gamma=frexp(gamma,&exponent)*256.0/gamma;
795
3.74M
          if (GetPixelRed(image,p) > 0)
796
3.17M
            pixel[0]=(unsigned char) (gamma*QuantumScale*(double)
797
3.17M
              GetPixelRed(image,p));
798
3.74M
          if (GetPixelGreen(image,p) > 0)
799
3.23M
            pixel[1]=(unsigned char) (gamma*QuantumScale*
800
3.23M
              (double) GetPixelGreen(image,p));
801
3.74M
          if (GetPixelBlue(image,p) > 0)
802
3.45M
            pixel[2]=(unsigned char) (gamma*QuantumScale*(double)
803
3.45M
              GetPixelBlue(image,p));
804
3.74M
          pixel[3]=(unsigned char) (exponent+128);
805
3.74M
        }
806
8.27M
      if ((image->columns >= 8) && (image->columns <= 0x7ffff))
807
8.26M
        {
808
8.26M
          pixels[x]=pixel[0];
809
8.26M
          pixels[x+(ssize_t) image->columns]=pixel[1];
810
8.26M
          pixels[x+2*(ssize_t) image->columns]=pixel[2];
811
8.26M
          pixels[x+3*(ssize_t) image->columns]=pixel[3];
812
8.26M
        }
813
4.99k
      else
814
4.99k
        {
815
4.99k
          pixels[i++]=pixel[0];
816
4.99k
          pixels[i++]=pixel[1];
817
4.99k
          pixels[i++]=pixel[2];
818
4.99k
          pixels[i++]=pixel[3];
819
4.99k
        }
820
8.27M
      p+=(ptrdiff_t) GetPixelChannels(image);
821
8.27M
    }
822
12.1k
    if ((image->columns >= 8) && (image->columns <= 0x7ffff))
823
7.39k
      {
824
36.9k
        for (i=0; i < 4; i++)
825
29.5k
          length=HDRWriteRunlengthPixels(image,
826
29.5k
            &pixels[i*(ssize_t) image->columns]);
827
7.39k
      }
828
4.79k
    else
829
4.79k
      {
830
4.79k
        count=WriteBlob(image,4*image->columns*sizeof(*pixels),pixels);
831
4.79k
        if (count != (ssize_t) (4*image->columns*sizeof(*pixels)))
832
0
          break;
833
4.79k
      }
834
12.1k
    status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
835
12.1k
      image->rows);
836
12.1k
    if (status == MagickFalse)
837
0
      break;
838
12.1k
  }
839
373
  pixels=(unsigned char *) RelinquishMagickMemory(pixels);
840
373
  if (CloseBlob(image) == MagickFalse)
841
0
    status=MagickFalse;
842
373
  return(status);
843
373
}