aboutsummaryrefslogtreecommitdiff
path: root/src/JPEG.c
blob: 5d4fdd9289d9cad2630f5ab7fa25a3d9b4ad5d79 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
#include "cctk.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h> 

#include "ioJpegGH.h"

#include "jconfig.h"
#include "jpeglib.h"

static const char *rcsid = "$Header$";

CCTK_FILEVERSION(CactusIO_IOJpeg_JPEG_c)

typedef struct jpeg_compress_struct JpgComp;

#ifndef JpgErr
typedef struct jpeg_error_mgr JpgErr;
#endif

/* prototypes of routines defined in this source file */
GLOBAL(void)
jpeg_memory_dest (j_compress_ptr cinfo, JOCTET *buffer,int bufsize);


/*
  Image data is an array of unsigned character array of
  RGB data.  The data is stored in interleaved order.
  IE, the first three elements are a byte of Red followed
  by a byte of Green and then a byte of Blue for the first
  pixel.  Data is stored in fortran order (ie. x is fastest
  moving dimension).
 */
int WriteJPEGToFileRGB(int nx, /* width of image in pixels */
		       int ny, /* height of the image in pixels */
		       void *data, /* buffer containing image data */
		       int Quality, /* Integer from 0 to 100 */
		       FILE* outfile){	/* name of file to store in */
  JpgComp cinfo;
  JpgErr jerr;  
  /*  FILE * outfile;*/
  unsigned char *dataRGB = (unsigned char *)data;
  JSAMPROW row_pointer=(JSAMPROW)dataRGB;
  
  memset (&cinfo,0,sizeof(cinfo));
  cinfo.err = jpeg_std_error(&jerr);
  jpeg_create_compress(&cinfo);
  	
  /* Setup JPEG */
  cinfo.image_width = nx ; 	/* image width and height, in pixels */
  cinfo.image_height = ny;
  cinfo.input_components = 3;	/* # of color components per pixel=3 RGB */
  cinfo.in_color_space = JCS_RGB;
  /*  if ((outfile = fopen(FileName, "wb")) == NULL) {
    printf("Cannot open file [%s]\n",FileName);
    return 0; 
  } */
  jpeg_stdio_dest(&cinfo, outfile);
  jpeg_set_defaults(&cinfo);
  jpeg_set_quality (&cinfo,Quality,TRUE);
  /* Starting compress */
  jpeg_start_compress(&cinfo, TRUE);
  /* Now compress everything one scanline at-a-time */
  while (cinfo.next_scanline < cinfo.image_height) {
    row_pointer = (JSAMPROW)(dataRGB+(cinfo.next_scanline*3*nx)); /* in bytes or words? */
    jpeg_write_scanlines(&cinfo, &row_pointer, 1);
  }
  jpeg_finish_compress(&cinfo);
  jpeg_destroy_compress(&cinfo);
  /* All done! */
  /*  fclose(outfile);*/
  return 1;
}

/*--------------
  A hack to hijack JPEG's innards to write into a memory buffer
----------------
/  this defines a new destination manager to store images in memory
/  derived by jdatadst.c */
typedef struct {
  struct jpeg_destination_mgr pub;	/* public fields */
  JOCTET *buffer;					/* start of buffer */
  int bufsize;						/* buffer size */
  int datacount;					/* finale data size */
} memory_destination_mgr;

typedef memory_destination_mgr *mem_dest_ptr;

/*----------------------------------------------------------------------------
  /  Initialize destination --- called by jpeg_start_compress before any data is actually written. */

METHODDEF(void)
init_destination (j_compress_ptr cinfo)
{
  mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest;

  dest->pub.next_output_byte = dest->buffer;
  dest->pub.free_in_buffer = dest->bufsize;
  dest->datacount=0;
}



/*----------------------------------------------------------------------------
  /  Empty the output buffer --- called whenever buffer fills up. */
METHODDEF(boolean)
empty_output_buffer (j_compress_ptr cinfo)
{
  mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest;

  dest->pub.next_output_byte = dest->buffer;
  dest->pub.free_in_buffer = dest->bufsize;

  return TRUE;
}


/*----------------------------------------------------------------------------

  /  Terminate destination --- called by jpeg_finish_compress
  /  after all data has been written.  Usually needs to flush buffer. */
METHODDEF(void)
term_destination (j_compress_ptr cinfo)
{
  /* expose the finale compressed image size */
  
  mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest;
  dest->datacount = dest->bufsize - dest->pub.free_in_buffer;
  
}

/*----------------------------------------------------------------------------
/ Prepare for output to a memory buffer. The caller must have allocate memory
/ to store the compressed image, and supply its size */
GLOBAL(void)
jpeg_memory_dest (j_compress_ptr cinfo, JOCTET *buffer,int bufsize)
{
  mem_dest_ptr dest;
  if (cinfo->dest == NULL) {	/* first time for this JPEG object? */
    cinfo->dest = (struct jpeg_destination_mgr *)
      (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
				  sizeof(memory_destination_mgr));
  }

  dest = (mem_dest_ptr) cinfo->dest;
  dest->bufsize=bufsize;
  dest->buffer=buffer;
  dest->pub.init_destination = init_destination;
  dest->pub.empty_output_buffer = empty_output_buffer;
  dest->pub.term_destination = term_destination;
}

/********************************************
Identical in nearly every way to WriteJPEGToFileRGB(), but
it writes into the memory buffer specified.  To be safe, its
good to make the memorybuffer the same size as the input image
+ 1024 bytes.  It is guaranteed that the image will be less
than this size.  In fact, if you use a typical "quality" level
of 75, you can get away with an image which is one quarter that
size.

 ******************************************** */
int WriteJPEGToMemoryRGB(int nx,int ny, void *data, int Quality, char *memorybuffer,int bufsize){	
  JpgComp cinfo;
  JpgErr jerr;  
  unsigned char *dataRGB = (unsigned char *)data;
  JSAMPROW row_pointer=(JSAMPROW)dataRGB;
  JOCTET *jpgbuff;
  mem_dest_ptr dest;
  int csize=0;

  /* zero out the compresion info structures and
     allocate a new compressor handle */
  memset (&cinfo,0,sizeof(cinfo));
  cinfo.err = jpeg_std_error(&jerr);
  jpeg_create_compress(&cinfo);
 
  /* Setup JPEG datastructures */
  cinfo.image_width = nx ; 	/* image width and height, in pixels */
  cinfo.image_height = ny;
  cinfo.input_components = 3;	/* # of color components per pixel=3 RGB */
  cinfo.in_color_space = JCS_RGB;		
  jpgbuff = (JOCTET*)memorybuffer;

  /* Setup compression and do it */
  jpeg_memory_dest(&cinfo,jpgbuff,bufsize);
  jpeg_set_defaults(&cinfo);
  jpeg_set_quality (&cinfo,Quality,TRUE);
  jpeg_start_compress(&cinfo, TRUE);
  /* compress each scanline one-at-a-time */
  while (cinfo.next_scanline < cinfo.image_height) {
    row_pointer = (JSAMPROW)(dataRGB+(cinfo.next_scanline*3*nx));
    jpeg_write_scanlines(&cinfo, &row_pointer, 1);
  }
  jpeg_finish_compress(&cinfo);
  /* Now extract the size of the compressed buffer */
  dest=(mem_dest_ptr)cinfo.dest;
  csize=dest->datacount; /* the actual compressed datasize */
  /* destroy the compressor handle */
  jpeg_destroy_compress(&cinfo);
  return csize;
}

#define ClampToByte(f) (unsigned char)(f=(f>255)?255:f)
/* Werner's nifty non-linear red-to-blue auto-colormap.
   I faked a CCTK_REAL, you can make it the right thing
   when integrated with cactus.

   Anyways, the "rdfac" concentrates the colormap when you
   use higher values.  Typical default value is 32 based
   on the current JPEG thorn.

   Next step is to embed some IDL colormaps into the
   program.  I can do that a bit later.
*/

void AutoColorDataSlice(int nx,int ny, /* size of the image x & y */
			const CCTK_REAL *datain, /* 2D slice of data input */
			unsigned char *dataout, /* RGB image data output */
			CCTK_REAL min,CCTK_REAL max, /* range of the entire 3D dataset
						  This could be ranged based 
						  on the values of the slice,
						  but then that would be a
						  bit untrustworthy.  Its
						  best to pass in the 
						  range for the entire 
						  dataset or a pre-defined
						  fixed range.  It does
						  handle clamping of the
						  range. */
			CCTK_REAL bias,
			int rdfac){
  /* Bias allows you to move the spectrum from the Red to the
     Blue.  A value of 0.5 is the median.  So 0.4 biases towards
     the Red and 0.6 would bias a little towards the blue.
     The range for this parameter would be -1.0 to +1.0. */
  /*  CCTK_REAL bias=0.4;*/
  int i,last;
  CCTK_REAL F=(CCTK_REAL)rdfac; /* cast to CCTK_REAL... don't know how the original worked at all without a cast */
  for(i=0,last=nx*ny;i<last;i++,dataout+=3){
    CCTK_REAL f;

    /* check for division by 0 */
    if (min == max)
    {
      f = bias;
    }
    else
    {
      f = bias-(*datain++ - min)/(max-min);
    }
    /* f-=(max-min); zero-center it */
    /* well it can't be less than 0 */
    if(f>0){
      f*=F;
      /* Color components biased towards blue */
      dataout[0]=ClampToByte(f);
      f*=F;
      dataout[1]=ClampToByte(f);
      f*=F;
      dataout[2]=ClampToByte(f);
    }
    else { /* f<0 */
      f=-f;
      f*=F;
      /* reverse color components to bias towards red */
      dataout[2]=ClampToByte(f);
      f*=F;
      dataout[1]=ClampToByte(f);
      f*=F;
      dataout[0]=ClampToByte(f);
    }
  }
}

#if 0
int main(int argc,char *argv[]){
  /* OK, lets create a bogus image */
  int nx=512,ny=512;
  unsigned char *datargb=(unsigned char *)malloc(3*nx*ny);
  CCTK_REAL *data= (CCTK_REAL*)malloc(nx*ny*sizeof(CCTK_REAL));
  int i,j,idx=0;
  CCTK_REAL radius=((CCTK_REAL)(nx/4));
  CCTK_REAL min=(CCTK_REAL)(nx*ny),max=-1.0;
  int bufsize = nx*ny*3+1024;
  char *memorybuffer=(char *)malloc(bufsize); /* safe size for membuf */
  int compressed_size=0;
  FILE *outfile;
  /* compute a circle by the most inefficient means possible */
  for(j=0,idx=0;j<ny;j++){
    CCTK_REAL y=((CCTK_REAL)(j-ny/2));
    y*=y;
    /* fprintf(stderr,"\n"); */
    for(i=0;i<nx;i++,idx++){
      CCTK_REAL x = ((CCTK_REAL)(i-nx/2));
      CCTK_REAL val;
      x*=x;
      data[idx]= val=sqrt(x+y);
     
      if(val>max) max=val;
      if(val<min) min=val;
    }
  }
  printf("Slice Data Min=%g Max=%g\n",min,max);
  /* Autocolor extracted from Werner's JPEG thorn */
  AutoColorDataSlice(nx,ny,data,datargb,min,max,32);
  WriteJPEGToFileRGB(nx,ny, datargb,75, "write2file.jpg");
  /* write to mem: It returns the size of the compressed image 
     cdntained in the memorybuffer */
  compressed_size = WriteJPEGToMemoryRGB(nx,ny, datargb,75, memorybuffer,bufsize);
  if ((outfile = fopen("write2mem.jpg", "wb")) == NULL) {
    printf("Cannot open file write2mem.jpg\n");
    return 0; /* failure */
  } 
  fwrite(memorybuffer,1,compressed_size,outfile);
  fclose(outfile);
  return 1;
}
#endif