1 /* -*- C++ -*-
2  * File: mem_image.cpp
3  * Copyright 2008-2021 LibRaw LLC (info@libraw.org)
4  *
5  * LibRaw mem_image/mem_thumb API test. Results should be same (bitwise) to
6 dcraw [-4] [-6] [-e]
7  * Testing note: for ppm-thumbnails you should use dcraw -w -e for thumbnail
8 extraction
9 
10 LibRaw is free software; you can redistribute it and/or modify
11 it under the terms of the one of two licenses as you choose:
12 
13 1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
14    (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
15 
16 2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
17    (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
18 
19 
20  */
21 #include <stdio.h>
22 #include <string.h>
23 #include <math.h>
24 
25 #include "libraw/libraw.h"
26 
27 #ifdef USE_JPEG
28 #include "jpeglib.h"
29 #endif
30 
31 #ifdef LIBRAW_WIN32_CALLS
32 #define snprintf _snprintf
33 #include <winsock2.h>
34 #pragma comment(lib, "ws2_32.lib")
35 #else
36 #include <netinet/in.h>
37 #endif
38 
39 #ifdef USE_JPEG
write_jpeg(libraw_processed_image_t * img,const char * basename,int quality)40 void write_jpeg(libraw_processed_image_t *img, const char *basename, int quality)
41 {
42   char fn[1024];
43   if(img->colors != 1 && img->colors != 3)
44   {
45 	printf("Only BW and 3-color images supported for JPEG output\n");
46 	return;
47   }
48   snprintf(fn, 1024, "%s.jpg", basename);
49   FILE *f = fopen(fn, "wb");
50   if (!f)
51     return;
52   struct jpeg_compress_struct cinfo;
53   struct jpeg_error_mgr jerr;
54   JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */
55   int row_stride;          /* physical row width in image buffer */
56 
57   cinfo.err = jpeg_std_error(&jerr);
58   jpeg_create_compress(&cinfo);
59   jpeg_stdio_dest(&cinfo, f);
60   cinfo.image_width = img->width;      /* image width and height, in pixels */
61   cinfo.image_height = img->height;
62   cinfo.input_components = img->colors;           /* # of color components per pixel */
63   cinfo.in_color_space = img->colors==3?JCS_RGB:JCS_GRAYSCALE;       /* colorspace of input image */
64   jpeg_set_defaults(&cinfo);
65   jpeg_set_quality(&cinfo, quality, TRUE);
66   jpeg_start_compress(&cinfo, TRUE);
67   row_stride = img->width * img->colors; /* JSAMPLEs per row in image_buffer */
68   while (cinfo.next_scanline < cinfo.image_height) {
69     row_pointer[0] = &img->data[cinfo.next_scanline * row_stride];
70     (void)jpeg_write_scanlines(&cinfo, row_pointer, 1);
71   }
72   jpeg_finish_compress(&cinfo);
73   fclose(f);
74   jpeg_destroy_compress(&cinfo);
75 }
76 
77 #endif
78 
79 // no error reporting, only params check
write_ppm(libraw_processed_image_t * img,const char * basename)80 void write_ppm(libraw_processed_image_t *img, const char *basename)
81 {
82   if (!img)
83     return;
84   // type SHOULD be LIBRAW_IMAGE_BITMAP, but we'll check
85   if (img->type != LIBRAW_IMAGE_BITMAP)
86     return;
87   if (img->colors != 3 && img->colors != 1)
88   {
89     printf("Only monochrome and 3-color images supported for PPM output\n");
90     return;
91    }
92 
93   char fn[1024];
94   snprintf(fn, 1024, "%s.p%cm", basename, img->colors==1?'g':'p');
95   FILE *f = fopen(fn, "wb");
96   if (!f)
97     return;
98   fprintf(f, "P%d\n%d %d\n%d\n", img->colors/2 + 5, img->width, img->height, (1 << img->bits) - 1);
99 /*
100   NOTE:
101   data in img->data is not converted to network byte order.
102   So, we should swap values on some architectures for dcraw compatibility
103   (unfortunately, xv cannot display 16-bit PPMs with network byte order data
104 */
105 #define SWAP(a, b)                                                             \
106   {                                                                            \
107     a ^= b;                                                                    \
108     a ^= (b ^= a);                                                             \
109   }
110   if (img->bits == 16 && htons(0x55aa) != 0x55aa)
111     for (unsigned i = 0; i < img->data_size-1; i += 2)
112       SWAP(img->data[i], img->data[i + 1]);
113 #undef SWAP
114 
115   fwrite(img->data, img->data_size, 1, f);
116   fclose(f);
117 }
118 
write_thumb(libraw_processed_image_t * img,const char * basename)119 void write_thumb(libraw_processed_image_t *img, const char *basename)
120 {
121   if (!img)
122     return;
123 
124   if (img->type == LIBRAW_IMAGE_BITMAP)
125   {
126     char fnt[1024];
127     snprintf(fnt, 1024, "%s.thumb", basename);
128     write_ppm(img, fnt);
129   }
130   else if (img->type == LIBRAW_IMAGE_JPEG)
131   {
132     char fn[1024];
133     snprintf(fn, 1024, "%s.thumb.jpg", basename);
134     FILE *f = fopen(fn, "wb");
135     if (!f)
136       return;
137     fwrite(img->data, img->data_size, 1, f);
138     fclose(f);
139   }
140 }
141 
main(int ac,char * av[])142 int main(int ac, char *av[])
143 {
144   int i, ret, output_thumbs = 0;
145 #ifdef USE_JPEG
146   int output_jpeg = 0, jpgqual = 90;
147 #endif
148   // don't use fixed size buffers in real apps!
149 
150   LibRaw RawProcessor;
151 
152   if (ac < 2)
153   {
154     printf("mem_image - LibRaw sample, to illustrate work for memory buffers.\n"
155            "Emulates dcraw [-4] [-1] [-e] [-h]\n"
156 #ifdef USE_JPEG
157            "Usage: %s [-D] [-j[nn]] [-T] [-v] [-e] raw-files....\n"
158 #else
159            "Usage: %s [-D] [-T] [-v] [-e] raw-files....\n"
160 #endif
161            "\t-6 - output 16-bit PPM\n"
162            "\t-4 - linear 16-bit data\n"
163            "\t-e - extract thumbnails (same as dcraw -e in separate run)\n"
164 #ifdef USE_JPEG
165            "\t-j[qual] - output JPEG with qual quality (e.g. -j90)\n"
166 #endif
167            "\t-h - use half_size\n", av[0]);
168     return 0;
169   }
170 
171   putenv((char *)"TZ=UTC"); // dcraw compatibility, affects TIFF datestamp field
172 
173 #define P1 RawProcessor.imgdata.idata
174 #define S RawProcessor.imgdata.sizes
175 #define C RawProcessor.imgdata.color
176 #define T RawProcessor.imgdata.thumbnail
177 #define P2 RawProcessor.imgdata.other
178 #define OUT RawProcessor.imgdata.params
179 
180   for (i = 1; i < ac; i++)
181   {
182     if (av[i][0] == '-')
183     {
184       if (av[i][1] == '6' && av[i][2] == 0)
185         OUT.output_bps = 16;
186       if (av[i][1] == '4' && av[i][2] == 0)
187       {
188         OUT.output_bps = 16;
189         OUT.gamm[0] = OUT.gamm[1] = OUT.no_auto_bright = 1;
190       }
191       if (av[i][1] == 'e' && av[i][2] == 0)
192         output_thumbs++;
193       if (av[i][1] == 'h' && av[i][2] == 0)
194         OUT.half_size = 1;
195 #ifdef USE_JPEG
196       if (av[i][1] == 'j')
197       {
198         output_jpeg = 1;
199         if(av[i][2] != 0)
200         jpgqual = atoi(av[i]+2);
201       }
202 #endif
203       continue;
204     }
205 #ifdef USE_JPEG
206     if(output_jpeg && OUT.output_bps>8)
207     {
208       printf("JPEG is limited to 8 bit\n");
209       OUT.output_bps = 8;
210     }
211 #endif
212     printf("Processing %s\n", av[i]);
213     if ((ret = RawProcessor.open_file(av[i])) != LIBRAW_SUCCESS)
214     {
215       fprintf(stderr, "Cannot open %s: %s\n", av[i], libraw_strerror(ret));
216       continue; // no recycle b/c open file will recycle itself
217     }
218 
219     if ((ret = RawProcessor.unpack()) != LIBRAW_SUCCESS)
220     {
221       fprintf(stderr, "Cannot unpack %s: %s\n", av[i], libraw_strerror(ret));
222       continue;
223     }
224 
225     // we should call dcraw_process before thumbnail extraction because for
226     // some cameras (i.e. Kodak ones) white balance for thumbnal should be set
227     // from main image settings
228 
229     ret = RawProcessor.dcraw_process();
230 
231     if (LIBRAW_SUCCESS != ret)
232     {
233       fprintf(stderr, "Cannot do postpocessing on %s: %s\n", av[i],
234               libraw_strerror(ret));
235       if (LIBRAW_FATAL_ERROR(ret))
236         continue;
237     }
238     libraw_processed_image_t *image = RawProcessor.dcraw_make_mem_image(&ret);
239     if (image)
240     {
241 #ifdef USE_JPEG
242       if(output_jpeg)
243         write_jpeg(image, av[i], jpgqual);
244       else
245 #endif
246         write_ppm(image, av[i]);
247       LibRaw::dcraw_clear_mem(image);
248     }
249     else
250       fprintf(stderr, "Cannot unpack %s to memory buffer: %s\n", av[i],
251               libraw_strerror(ret));
252 
253     if (output_thumbs)
254     {
255 
256       if ((ret = RawProcessor.unpack_thumb()) != LIBRAW_SUCCESS)
257       {
258         fprintf(stderr, "Cannot unpack_thumb %s: %s\n", av[i],
259                 libraw_strerror(ret));
260         if (LIBRAW_FATAL_ERROR(ret))
261           continue; // skip to next file
262       }
263       else
264       {
265         libraw_processed_image_t *thumb =
266             RawProcessor.dcraw_make_mem_thumb(&ret);
267         if (thumb)
268         {
269           write_thumb(thumb, av[i]);
270           LibRaw::dcraw_clear_mem(thumb);
271         }
272         else
273           fprintf(stderr,
274                   "Cannot unpack thumbnail of %s to memory buffer: %s\n", av[i],
275                   libraw_strerror(ret));
276       }
277     }
278 
279     RawProcessor.recycle(); // just for show this call
280   }
281   return 0;
282 }
283