Coverage Report

Created: 2026-11-12 07:35

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/imagemagick/coders/bayer.c
Line
Count
Source
1
/*
2
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3
%                                                                             %
4
%                                                                             %
5
%                                                                             %
6
%                     BBBB    AAA   Y   Y  EEEEE  RRRR                        %
7
%                     B   B  A   A   Y Y   E      R   R                       %
8
%                     BBBB   AAAAA    Y    EEE    RRRR                        %
9
%                     B   B  A   A    Y    E      R  R                        %
10
%                     BBBB   A   A    Y    EEEEE  R   R                       %
11
%                                                                             %
12
%                                                                             %
13
%                     Read/Write Raw Bayer Image Format                       %
14
%                                                                             %
15
%                              Software Design                                %
16
%                            Cristy, Dirk Lemstra                             %
17
%                                 July 2022                                   %
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/artifact.h"
44
#include "MagickCore/blob.h"
45
#include "MagickCore/blob-private.h"
46
#include "MagickCore/cache.h"
47
#include "MagickCore/channel.h"
48
#include "MagickCore/colorspace.h"
49
#include "MagickCore/colorspace-private.h"
50
#include "MagickCore/constitute.h"
51
#include "MagickCore/exception.h"
52
#include "MagickCore/exception-private.h"
53
#include "MagickCore/image.h"
54
#include "MagickCore/image-private.h"
55
#include "MagickCore/list.h"
56
#include "MagickCore/magick.h"
57
#include "MagickCore/memory_.h"
58
#include "MagickCore/monitor.h"
59
#include "MagickCore/monitor-private.h"
60
#include "MagickCore/pixel-accessor.h"
61
#include "MagickCore/quantum-private.h"
62
#include "MagickCore/registry.h"
63
#include "MagickCore/static.h"
64
#include "MagickCore/statistic.h"
65
#include "MagickCore/string_.h"
66
#include "MagickCore/transform.h"
67
#include "MagickCore/module.h"
68
#include "MagickCore/utility.h"
69

70

71
/*
72
  Forward declarations.
73
*/
74
static MagickBooleanType
75
  WriteBAYERImage(const ImageInfo *,Image *,ExceptionInfo *);
76

77
/*
78
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
79
%                                                                             %
80
%                                                                             %
81
%                                                                             %
82
%   R e a d B A Y E R I m a g e                                               %
83
%                                                                             %
84
%                                                                             %
85
%                                                                             %
86
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
87
%
88
%  ReadBAYERImage() reconstructs a full color image from the incomplete color
89
%  samples output from an image sensor overlaid with a color filter array
90
%  (CFA).  It allocates the memory necessary for the new Image structure and
91
%  returns a pointer to the new image.
92
%
93
%  Reference: http://im.snibgo.com/demosaic.htm.
94
%
95
%  The format of the ReadBAYERImage method is:
96
%
97
%      Image *ReadBAYERImage(const ImageInfo *image_info,
98
%        ExceptionInfo *exception)
99
%
100
%  A description of each parameter follows:
101
%
102
%    o image_info: the image info.
103
%
104
%    o exception: return any errors or warnings in this structure.
105
%
106
*/
107
108
static Image *BayerSample(const Image *image, const char *offset,
109
  RectangleInfo geometry, ExceptionInfo *exception)
110
0
{
111
0
  Image
112
0
    *clone,
113
0
    *sample;
114
115
0
  clone=CloneImage(image,0,0,MagickTrue,exception);
116
0
  if (clone == (Image *) NULL)
117
0
    return(clone);
118
0
  (void) SetImageArtifact(clone,"sample:offset",offset);
119
0
  sample=SampleImage(clone,geometry.width,geometry.height,exception);
120
0
  clone=DestroyImage(clone);
121
0
  return(sample);
122
0
}
123
124
static Image *ReadBAYERImage(const ImageInfo *image_info,
125
  ExceptionInfo *exception)
126
43
{
127
43
  Image
128
43
    *image,
129
43
    *image_a,
130
43
    *image_b;
131
132
43
  ImageInfo
133
43
    *read_info;
134
135
43
  RectangleInfo
136
43
    geometry;
137
138
  /*
139
    Reconstruct a full color image from the incomplete camera sensor.
140
  */
141
43
  assert(image_info != (const ImageInfo *) NULL);
142
43
  assert(image_info->signature == MagickCoreSignature);
143
43
  assert(exception != (ExceptionInfo *) NULL);
144
43
  assert(exception->signature == MagickCoreSignature);
145
43
  if (IsEventLogging() != MagickFalse)
146
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
147
0
      image_info->filename);
148
43
  read_info=CloneImageInfo(image_info);
149
43
  (void) FormatLocaleString(read_info->filename,MagickPathExtent,"gray:%.1024s",
150
43
    image_info->filename);
151
43
  (void) CopyMagickString(read_info->magick,"GRAY",MagickPathExtent);
152
43
  read_info->verbose=MagickFalse;
153
43
  image=ReadImage(read_info,exception);
154
43
  read_info=DestroyImageInfo(read_info);
155
43
  if (image == (Image *) NULL)
156
43
    return((Image *) NULL);
157
0
  (void) ParseRegionGeometry(image,"50%",&geometry,exception);
158
0
  image_a=BayerSample(image,"75x25",geometry,exception);
159
0
  if (image_a == (Image *) NULL)
160
0
    return(DestroyImage(image));
161
0
  image_b=BayerSample(image,"25x75",geometry,exception);
162
0
  if (image_b == (Image *) NULL)
163
0
    {
164
0
      image_a=DestroyImage(image_a);
165
0
      return(DestroyImage(image));
166
0
    }
167
0
  AppendImageToList(&image_a,image_b);
168
0
  image_b=EvaluateImages(image_a,MeanEvaluateOperator,exception);
169
0
  image_a=DestroyImageList(image_a);
170
0
  image_a=BayerSample(image,"25",geometry,exception);
171
0
  if (image_a == (Image *) NULL)
172
0
    {
173
0
      image_b=DestroyImage(image_b);
174
0
      return(DestroyImage(image));
175
0
    }
176
0
  AppendImageToList(&image_a,image_b);
177
0
  image_b=BayerSample(image,"75",geometry,exception);
178
0
  if (image_b == (Image *) NULL)
179
0
    {
180
0
      image_a=DestroyImageList(image_a);
181
0
      return(DestroyImage(image));
182
0
    }
183
0
  AppendImageToList(&image_a,image_b);
184
0
  image_b=CombineImages(image_a,sRGBColorspace,exception);
185
0
  image_a=DestroyImageList(image_a);
186
0
  if (image_b == (Image *) NULL)
187
0
    return(DestroyImage(image));
188
0
  (void) ParseRegionGeometry(image_b,"200%",&geometry,exception);
189
0
  image_a=ResizeImage(image_b,geometry.width,geometry.height,image->filter,
190
0
    exception);
191
0
  image_b=DestroyImageList(image_b);
192
0
  if (image_a == (Image *) NULL)
193
0
    return(DestroyImage(image));
194
0
  (void) CopyMagickString(image_a->magick,image_info->magick,
195
0
    MagickPathExtent);
196
0
  (void) CopyMagickString(image_a->filename,image_info->filename,
197
0
    MagickPathExtent);
198
0
  image=DestroyImageList(image);
199
0
  return(image_a);
200
0
}
201

202
/*
203
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
204
%                                                                             %
205
%                                                                             %
206
%                                                                             %
207
%   R e g i s t e r B A Y E R I m a g e                                       %
208
%                                                                             %
209
%                                                                             %
210
%                                                                             %
211
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
212
%
213
%  RegisterBAYERImage() adds attributes for the BAYER image format to
214
%  the list of supported formats.  The attributes include the image format
215
%  tag, a method to read and/or write the format, whether the format
216
%  supports the saving of more than one frame to the same file or blob,
217
%  whether the format supports native in-memory I/O, and a brief
218
%  description of the format.
219
%
220
%  The format of the RegisterBAYERImage method is:
221
%
222
%      size_t RegisterBAYERImage(void)
223
%
224
*/
225
ModuleExport size_t RegisterBAYERImage(void)
226
8
{
227
8
  MagickInfo
228
8
    *entry;
229
230
8
  entry=AcquireMagickInfo("BAYER","BAYER","Raw mosaiced samples");
231
8
  entry->decoder=(DecodeImageHandler *) ReadBAYERImage;
232
8
  entry->encoder=(EncodeImageHandler *) WriteBAYERImage;
233
8
  entry->flags|=CoderRawSupportFlag | CoderEndianSupportFlag;
234
8
  entry->flags^=CoderBlobSupportFlag;
235
8
  (void) RegisterMagickInfo(entry);
236
8
  entry=AcquireMagickInfo("BAYER","BAYERA","Raw mosaiced and alpha samples");
237
8
  entry->decoder=(DecodeImageHandler *) ReadBAYERImage;
238
8
  entry->encoder=(EncodeImageHandler *) WriteBAYERImage;
239
8
  entry->flags|=CoderRawSupportFlag | CoderEndianSupportFlag;
240
8
  entry->flags^=CoderBlobSupportFlag;
241
8
  (void) RegisterMagickInfo(entry);
242
8
  return(MagickImageCoderSignature);
243
8
}
244

245
/*
246
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
247
%                                                                             %
248
%                                                                             %
249
%                                                                             %
250
%   U n r e g i s t e r B A Y E R I m a g e                                   %
251
%                                                                             %
252
%                                                                             %
253
%                                                                             %
254
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
255
%
256
%  UnregisterBAYERImage() removes format registrations made by the BAYER module
257
%  from the list of supported formats.
258
%
259
%  The format of the UnregisterBAYERImage method is:
260
%
261
%      UnregisterBAYERImage(void)
262
%
263
*/
264
ModuleExport void UnregisterBAYERImage(void)
265
0
{
266
0
  (void) UnregisterMagickInfo("BAYERA");
267
0
  (void) UnregisterMagickInfo("BAYER");
268
0
}
269

270
/*
271
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
272
%                                                                             %
273
%                                                                             %
274
%                                                                             %
275
%   W r i t e B A Y E R I m a g e                                             %
276
%                                                                             %
277
%                                                                             %
278
%                                                                             %
279
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
280
%
281
%  WriteBAYERImage() deconstructs a full color image into a single channel
282
%  RGGB raw image format.
283
%
284
%  Reference: http://im.snibgo.com/mosaic.htm.
285
%
286
%  The format of the WriteBAYERImage method is:
287
%
288
%      MagickBooleanType WriteBAYERImage(const ImageInfo *image_info,
289
%        Image *image,ExceptionInfo *exception)
290
%
291
%  A description of each parameter follows.
292
%
293
%    o image_info: the image info.
294
%
295
%    o image:  The image.
296
%
297
%    o exception: return any errors or warnings in this structure.
298
%
299
*/
300
static Image* BayerApplyMask(Image *images,size_t index_a,size_t index_b,
301
  size_t fill_x,size_t fill_y,ExceptionInfo *exception)
302
0
{
303
0
  DrawInfo
304
0
    *draw_info;
305
306
0
  Image
307
0
    *canvas,
308
0
    *mask_image,
309
0
    *result;
310
311
0
  PixelInfo
312
0
    pixel;
313
314
0
  Quantum
315
0
    *q;
316
317
0
  draw_info=AcquireDrawInfo();
318
0
  if (draw_info == (DrawInfo *) NULL)
319
0
    return((Image *) NULL);
320
0
  draw_info->fill_pattern=AcquireImage((ImageInfo *) NULL,exception);
321
0
  if (draw_info->fill_pattern == (Image *) NULL)
322
0
    {
323
0
      draw_info=DestroyDrawInfo(draw_info);
324
0
      return((Image *) NULL);
325
0
    }
326
0
  (void) SetImageExtent(draw_info->fill_pattern,2,2,exception);
327
0
  (void) QueryColorCompliance("#000",AllCompliance,
328
0
    &draw_info->fill_pattern->background_color,exception);
329
0
  (void) SetImageBackgroundColor(draw_info->fill_pattern,exception);
330
0
  q=GetAuthenticPixels(draw_info->fill_pattern,(ssize_t) fill_x,(ssize_t)
331
0
    fill_y,1,1,exception);
332
0
  if (q == (Quantum *) NULL)
333
0
    {
334
0
      draw_info=DestroyDrawInfo(draw_info);
335
0
      return((Image *) NULL);
336
0
    }
337
0
  (void) QueryColorCompliance("#fff",AllCompliance,&pixel,exception);
338
0
  SetPixelViaPixelInfo(draw_info->fill_pattern,&pixel,q);
339
0
  mask_image=CloneImage(GetImageFromList(images,(ssize_t) index_a),0,0,
340
0
    MagickTrue,exception);
341
0
  if (mask_image == (Image *) NULL)
342
0
    {
343
0
      draw_info=DestroyDrawInfo(draw_info);
344
0
      return((Image *) NULL);
345
0
    }
346
0
  draw_info->primitive=ConstantString("color 0,0 reset");
347
0
  (void) DrawImage(mask_image,draw_info,exception);
348
0
  (void) SetImageAlphaChannel(mask_image,OffAlphaChannel,exception);
349
0
  draw_info=DestroyDrawInfo(draw_info);
350
0
  canvas=CloneImage(GetImageFromList(images,(ssize_t) index_b),0,0,MagickTrue,
351
0
    exception);
352
0
  if (canvas == (Image *) NULL)
353
0
    {
354
0
      mask_image=DestroyImage(mask_image);
355
0
      return((Image *) NULL);
356
0
    }
357
0
  (void) CompositeImage(canvas,mask_image,CopyAlphaCompositeOp,MagickTrue,0,0,
358
0
    exception);
359
0
  mask_image=DestroyImage(mask_image);
360
0
  result=CloneImage(GetImageFromList(images,(ssize_t) index_a),0,0,MagickTrue,
361
0
    exception);
362
0
  if (result != (Image *) NULL)
363
0
    {
364
0
      (void) CompositeImage(result,canvas,OverCompositeOp,MagickTrue,0,0,
365
0
        exception);
366
0
      (void) SetImageAlphaChannel(result,OffAlphaChannel,exception);
367
0
    }
368
0
  canvas=DestroyImage(canvas);
369
0
  return(result);
370
0
}
371
372
static MagickBooleanType WriteBAYERImage(const ImageInfo *image_info,
373
  Image *image,ExceptionInfo *exception)
374
0
{
375
0
  Image
376
0
    *bayer_image=(Image *) NULL,
377
0
    *images=(Image *) NULL;
378
379
0
  MagickBooleanType
380
0
    status;
381
382
  /*
383
    Deconstruct RGB image into a single channel RGGB raw image.
384
  */
385
0
  assert(image_info != (const ImageInfo *) NULL);
386
0
  assert(image_info->signature == MagickCoreSignature);
387
0
  assert(image != (Image *) NULL);
388
0
  assert(image->signature == MagickCoreSignature);
389
0
  if (IsEventLogging() != MagickFalse)
390
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
391
0
  bayer_image=CloneImage(image,0,0,MagickTrue,exception);
392
0
  status=MagickFalse;
393
0
  if (bayer_image == (Image *) NULL)
394
0
    return(MagickFalse);
395
0
  (void) SetImageColorspace(bayer_image,sRGBColorspace,exception);
396
0
  (void) SetPixelChannelMask(bayer_image,(ChannelType) (RedChannel |
397
0
    GreenChannel | BlueChannel));
398
0
  images=SeparateImages(bayer_image,exception);
399
0
  bayer_image=DestroyImage(bayer_image);
400
0
  if (images == (Image *) NULL)
401
0
    return(MagickFalse);
402
0
  bayer_image=BayerApplyMask(images,0,1,1,0,exception);
403
0
  if (bayer_image == (Image *) NULL)
404
0
    {
405
0
      images=DestroyImageList(images);
406
0
      return(MagickFalse);
407
0
    }
408
0
  AppendImageToList(&images,bayer_image);
409
0
  bayer_image=BayerApplyMask(images,3,1,0,1,exception);
410
0
  if (bayer_image == (Image *) NULL)
411
0
    {
412
0
      images=DestroyImageList(images);
413
0
      return(MagickFalse);
414
0
    }
415
0
  AppendImageToList(&images,bayer_image);
416
0
  status=MagickFalse;
417
0
  bayer_image=BayerApplyMask(images,4,2,1,1,exception);
418
0
  if (bayer_image != (Image *) NULL)
419
0
    status=MagickTrue;
420
0
  images=DestroyImageList(images);
421
0
  if (bayer_image != (Image *) NULL)
422
0
    {
423
0
      ImageInfo
424
0
        *write_info;
425
426
0
      write_info=CloneImageInfo(image_info);
427
0
      write_info->verbose=MagickFalse;
428
0
      (void) CopyMagickString(write_info->magick,"GRAY",MagickPathExtent);
429
0
      (void) CopyMagickString(bayer_image->filename,image->filename,
430
0
        MagickPathExtent);
431
0
      status=WriteImage(write_info,bayer_image,exception);
432
0
      bayer_image=DestroyImage(bayer_image);
433
0
      write_info=DestroyImageInfo(write_info);
434
0
    }
435
0
  return(status);
436
0
}