/* Spectroscopic Toolkit version 1.76 by Pieter Suurmond, september 24, 2004. See headerfile gt.h. 'gt' is a minimal graphic toolkit in C by Pieter Suurmond. */ #define JPEG_INTERNALS /* Hard includes instead of using the JPEG6-LIBRARY: */ #include "jversion.h" /* Sources from cjpeg-example provided with library code */ #include "jinclude.h" /* of the Independent JPEG Group (vers.6b, 27-Mar-1998). */ #include "jpeglib.h" /* File "jconfig.h" is included sometimes! */ #include "jerror.c" /* Includes header jerror.h. */ #include "jcapimin.c" #include "jcapistd.c" #include "jcparam.c" #include "jdatadst.c" #include "jcinit.c" #include "jcmaster.c" #include "jcmarker.c" #include "jcmainct.c" #include "jcprepct.c" #include "jcsample.c" #include "jutils.c" #include "jmemsys.h" /* Import the system-dependent declarations */ #include "jmemmgr.c" #include "jmemnobs.c" #include "jccoefct.c" #include "jccolor.c" #include "jcomapi.c" #include "jchuff.c" #include "jfdctint.c" /* Only slow integer (no fast integer, no float). */ #include "jcdctmgr.c" #include "gt.h" #include "gtFontSmall.c" /* My only font thus far. */ /*------------------------------------------------------------------------------------*/ struct RGBPIC /* Already declared (typedef) in headerfile gt.h. */ { /* Data fields NOT accesible directly to the gt-user. */ unsigned int width; unsigned int height; unsigned long pixels; /* Should always be the product of the 2 above. */ unsigned char* mem; /* Pixel-depth always 24 bit, thus 3 bytes per pixel. */ }; /* This struct holds all pixels in memory. */ /*------------------------------------------------------------------------------------*/ int gtCreateMemRGB(RGBPIC* handle, int width, int height, unsigned char R, unsigned char G, unsigned char B) { RGBPIC ptr = malloc(sizeof(struct RGBPIC)); *handle = ptr; /* Always initialise! */ if (!ptr) return 1; /* Not enough memory. */ if ((width < 1) || (height < 1)) { gtDestroyMemRGB(handle); /* Also clears reference! */ return 2; /* Invalid dimension(s). */ } ptr->width = width; ptr->height = height; ptr->pixels = (unsigned long)width * (unsigned long)height; ptr->mem = (unsigned char*)malloc(ptr->pixels * 3L); if (!ptr->mem) /* Always 24 bits per pixel. */ { gtDestroyMemRGB(handle); /* Also clears reference. */ return 3; /* Not enough memory. */ } gtErase(ptr, R, G, B); /* Give it background-color. */ return 0; } /*-----------------------------------------------------------------------------------*/ void gtDestroyMemRGB(RGBPIC* handle) /* Don't forget to call after gtCreateMemRGB(). */ { if (*handle) { if ((*handle)->mem) free((*handle)->mem); free(*handle); *handle = (RGBPIC)NULL; } } /*----------------------------------------------------------------------------------*/ void gtErase(RGBPIC pic, unsigned char R, unsigned char G, unsigned char B) { unsigned char* c = pic->mem; unsigned long a = pic->pixels; while (a--) /* Give it background-color. */ { *c++ = R; *c++ = G; *c++ = B; } } /*----------------------------------------------------------------------------------*/ short gtSaveMemRGB(RGBPIC pic, const char* filename, unsigned int Q) { struct jpeg_compress_struct cinfo; struct jpeg_error_mgr jerr; FILE* output_file; JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */ int row_stride; /* physical row width in image buffer */ /* Initialize the JPEG compression object with default error handling. */ cinfo.err = jpeg_std_error(&jerr); jpeg_create_compress(&cinfo); /* CAN THIS FAIL ?????? */ cinfo.image_width = pic->width; /* image width and height, in pixels */ cinfo.image_height = pic->height; cinfo.input_components = 3; /* # of color components per pixel */ cinfo.in_color_space = JCS_RGB; /* colorspace of input image */ jpeg_set_defaults(&cinfo); /* Initialize JPEG parameters */ jpeg_set_quality(&cinfo, Q, TRUE); /* limit to baseline-JPEG values */ if (!(output_file = fopen(filename, "wb"))) { jpeg_destroy_compress(&cinfo); /* But Release MEM first!! */ return 1; } jpeg_stdio_dest(&cinfo, output_file); /* Specify data-dest for compr. */ jpeg_start_compress(&cinfo, TRUE); /* Start compressor */ row_stride = cinfo.image_width*cinfo.input_components; /* JSAMPLEs per row in pic->mem */ while (cinfo.next_scanline < cinfo.image_height) /* Process data */ { row_pointer[0] = & pic->mem[cinfo.next_scanline * row_stride]; (void) jpeg_write_scanlines(&cinfo, row_pointer, 1); } jpeg_finish_compress(&cinfo); /* Finish compression and release memory */ jpeg_destroy_compress(&cinfo); fclose(output_file); return 0; } /*--------------------------------------------------------------------------------------------*/ int gtSetPixel(RGBPIC pic, int x, int y, unsigned char R, unsigned char G, unsigned char B) { unsigned char* c; if ((x >= 0) && (x < pic->width) && (y >= 0) && (y < pic->height)) { c = & pic->mem[3 * (x + (y * pic->width))]; *c++ = R; *c++ = G; *c = B; return 0; } return 1; } /*--------------------------------------------------------------------------------------------*/ int gtGetPixel(RGBPIC pic, int x, int y, unsigned char* R, unsigned char* G, unsigned char* B) { unsigned char* c; if ((x >= 0) && (x < pic->width) && (y >= 0) && (y < pic->height)) { c = & pic->mem[3 * (x + (y * pic->width))]; *R = *c++; *G = *c++; *B = *c; return 0; } return 1; } /*--------------------------------------------------------------------------------------------*/ void gtDrawLine(RGBPIC pic, int x1, int y1, int x2, int y2, unsigned char R, unsigned char G, unsigned char B) { /* No anti-aliasing yet. */ int dx = x1 - x2; /* Calculate differences in x and y. */ int dy = y1 - y2; int ux, uy; /* Absolute (unsigned) differences. */ int x, y; float a, b; /* Slope and offset of linear function. */ /* Not as optimized as possible but it works. */ if (!dx) /* Vertical line. */ { if (dy > 0) { y = y1; y1 = y2; y2 = y; } /* Swap y1 y2. */ for (y = y1; y <= y2; y++) gtSetPixel(pic, x1, y, R, G, B); } else if (!dy) /* Horizontal line. */ { if (dx > 0) { x = x1; x1 = x2; x2 = x; } /* Swap x1 x2. */ for (x = x1; x <= x2; x++) gtSetPixel(pic, x, y1, R, G, B); } else /* Some "diagonal". Do not skip any pixels in between but draw */ { /* with the highest "density" as possible (dependant on angle). */ ux = dx; if (ux < 0) ux = -ux; uy = dy; if (uy < 0) uy = -uy; if (uy < ux) /* Difference in y is smaller so use y as function of x. */ { a = (float)dy / (float)dx; /* Sure dx is not 0. */ b = (float)y1 - (a * (float)x1); if (dx > 0) { x = x1; x1 = x2; x2 = x; } /* Swap x1 x2. */ for (x = x1; x <= x2; x++) { y = (int)(0.5 + (a * (float)x) + b); /* Rounding assumes y>=0. */ gtSetPixel(pic, x, y, R, G, B); } } else /* Difference in x is smaller so use x as function of y. */ { a = (float)dx / (float)dy; /* Sure dy is not 0. */ b = (float)x1 - (a * (float)y1); if (dy > 0) { y = y1; y1 = y2; y2 = y; } /* Swap y1 y2. */ for (y = y1; y <= y2; y++) { x = (int)(0.5 + (a * (float)y) + b); /* Rounding assumes x>=0. */ gtSetPixel(pic, x, y, R, G, B); } } } } /*--------------------------------------------------------------------------------------*/ void gtDrawString(RGBPIC pic, int x, int y, unsigned char R, unsigned char G, unsigned char B, short T, const char* s) { /* pix denotes left upper corner (6x12 font). */ char c; short row, l, col; int xx, yy, Xstart = x; while ((c = *s++) != (char)0) /* (Don't cause 'possible assignment' warnings during compilation.) */ { if (c < 32) c = 0; /* Take space for ASCII-codes less than 32. */ else c -= 32; row = c * 12; /* Start of this char in our font-array. */ yy = y; for (l=12; l--;) { c = gtFontSmall[row++]; /* Get bit-pattern in c. */ xx = Xstart; for (col=6; col--;) /* Transparency-code 0 only plots the font-pixels, */ { /* code 1 plots them on a WHITE background. */ if (c & 32) /* Look at the 6th bit from the left. */ gtSetPixel(pic, xx, yy, R, G, B); else if (T) gtSetPixel(pic, xx, yy, 255, 255, 255); c <<= 1; /* Shift to next bit in bit-pattern. */ xx++; /* Advance to next pixel. */ } yy++; /* Advance to next row of pixels. */ } Xstart += 6; } } /*--------------------------------------------------------------------------------------------*/ static float gtHertzToRtable[1024]; /* Frequency, 53.0 */ static float gtHertzToGtable[1024]; /* Hue and 56.5 51.5 +13 */ static float gtHertzToBtable[1024]; /* RGB-value: ------------- * 10 Hertz */ /* / G Y \ */ /* / \ */ /* / \ 49.0 */ /* / \ */ /* 60.0 / C . R \ 44.0 ___hue=0 */ /* \ / hue=1 */ /* \ /__ 40.0 */ /* \ / 77.0 */ /* \ / */ /* \ B M / */ /* ------------- */ /* 63.5 72.0 */ /* 67.0 */ /*--------------------------------------------------------------------------------------------*/ void gtInitHertzToRGBtables(void) /* C points to array of floats: R G B. */ { /* Rough approximation to convert frequencies (visi */ short t; /* for the eye) to video RGB values. Translate via */ float hz, i; /* the the perimeter of the hue/saturation-sextangle.*/ /* (saturation always maximal (255). (Pieter, 2001.) */ for (t = 0; t < 1024; t++) /* (except at IR and UV boundaries.) */ { /* Results an RGB-imitation of MONO- */ hz = 40.0e13 + (37.0e13 * (double)t / 1023.0); /* CHROMATIC light of specified freq. */ if (hz < 42.0e13) /* Gradually fade in from infrared */ i = 0.5e-13 * (hz - 40.0e13); /* (40..42). */ else if (hz > 75.0e13) /* Gradually fade out to ultraviolet. */ i = 1.0 - (0.5e-13 * (hz - 75.0e13)); /* (75..77). */ else i = 1.0; /*---------------------------------------------------- CALCULATE RED COMPONENT: */ if ((hz <= 51.5e13) || (hz >= 72.0e13)) /* Up to Yellow, up to end. */ gtHertzToRtable[t] = i * 255.0; else if (hz < 56.5e13) /* From Yellow to Green. */ gtHertzToRtable[t] = 255.0 - (51.0e-13 * (hz - 51.5e13)); else if (hz > 63.5e13) /* Blue to Magenta. */ gtHertzToRtable[t] = 30.0e-13 * (hz - 63.5e13); else gtHertzToRtable[t] = 0.0; /* Around Cyan, the opposite. */ /*---------------------------------------------------- CALCULATE GREEN COMPONENT: */ if ((hz >= 51.5e13) && (hz <= 60.0e13)) /* From Yellow to Cyan. */ gtHertzToGtable[t] = 255.0; else if ((hz <= 44.0e13) || (hz >= 63.5e13)) /* Below red, above blue. */ gtHertzToGtable[t] = 0.0; else if (hz < 51.5e13) /* From Red to Yellow. */ gtHertzToGtable[t] = i * (34.0e-13 * (hz - 44.0e13)); else /* Cyan to Blue. */ gtHertzToGtable[t] = 255.0-(72.85714286e-13 * (hz-60.0e13)); /*---------------------------------------------------- CALCULATE BLUE COMPONENT: */ if ((hz >= 60.0e13) && (hz <= 72.0e13)) /* Hue from Cyan to Magenta. */ gtHertzToBtable[t] = 255.0; else if ((hz <= 56.5e13) && (hz >= 44.0e13)) /* Hue below Green. */ gtHertzToBtable[t] = 0.0; else if ((hz > 56.5e13) && (hz < 60.0e13)) /* Hue green to Cyan. */ gtHertzToBtable[t] = 72.85714286e-13 * (hz - 56.5e13); else if (hz < 44.0e13) /* Hue up to Red (@ 2/3rd blue). */ gtHertzToBtable[t] = i * (85.0 - (21.25e-13 * (hz - 40.0e13))); else /* Hue above Magenta (@ 2/3rd blue). */ gtHertzToBtable[t] = i * (255.0 - (34.0e-13 * (hz - 72.0e13))); } /* Start 40e13 Hz "just" before RED-center, when blue decreased from 255 to 85. */ } /*--------------------------------------------------------------------------------------------*/ void gtHertzToRGB(float hz, float intensity, float* C) /* C points to array of floats: R G B.*/ { /* Call gtInitHertzToRGBtables() be- */ short tableIndex; /* fore reading the tables!! */ if ((hz < 40.0e13) || (hz > 77.0e13)) /* Imitates MONOCHROMATIC light. */ { *C++ = 0; *C++ = 0; *C = 0; return; } /* Must be within the visible range. */ tableIndex = (short)(0.5+27.64864865e-13 * (hz - 40.0e13)); /* (Pieter Suurmond, 2001.) */ *C++ = intensity * gtHertzToRtable[tableIndex]; *C++ = intensity * gtHertzToGtable[tableIndex]; *C = intensity * gtHertzToBtable[tableIndex]; }