473,385 Members | 1,587 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,385 software developers and data experts.

working with bitmaps in C

Hello,
Sorry if this is not "exactly" a C topic but I thought this would be
the best place to start. I need some guidance on working with bitmap
images in ANSI C. I need to "read" in a bitmap image, then break it up
into pieces (say 32x32) then take those pieces and "save" them,
granted they do not need to be a readable image, then perform some
other operations on the pieces, and then combine the manipulated
pieces back into one image.

Any suggestions on any part of this would be great! I have found the
"EasyBMP" package from http://easybmp.sourceforge.net but apperas this
is not going to work for what I need.

Thanks!
Dec 23 '07 #1
10 5190
St******************@gmail.com said:
Hello,
Sorry if this is not "exactly" a C topic but I thought this would be
the best place to start. I need some guidance on working with bitmap
images in ANSI C.
The first thing you need to know is this: YES, it is possible, in ANSI C.
I need to "read" in a bitmap image, then break it up
into pieces (say 32x32) then take those pieces and "save" them,
granted they do not need to be a readable image, then perform some
other operations on the pieces, and then combine the manipulated
pieces back into one image.
This sounds pretty simple. I would recommend the following procedure:

1) decide on a way of representing an image in memory, that has nothing
whatsoever to do with .bmp format; I use a dynamically allocated array of
unsigned long int *, each of which points to the first element in a
dynamically allocated array of unsigned long int. In other words, I have
one unsigned long int per pixel, with the low 24 bits used for
representing the three colour channels, 8 bits each.

2) now write a bitmap loader, a function that can create an appropriately
sized in-memory representation of a bitmap file. Don't hard-code the image
size for your current task! It might sound like it'll make things simpler,
but actually it'll make them harder. That's because your task actually
involves several different sizes already (original, 32x32, 32xrightmargin,
bottommarginx32, and rightmarginxbottommargin).

3) now write a bitmap saver, a function that can create a bitmap file on
disk from an in-memory representation.

4) now write as many graphics functions as you like. On the way, write a
blitter (for copying an image region from an arbitrary part of one image
to an arbitrary part of another).

5) your task itself is now very, very simple - glue together some of the
above, and you're done.

(Although I have never written the program you need, I guess it would take
me about three or four minutes including testing - but only because I've
done all of the above work already.)

--
Richard Heathfield <http://www.cpax.org.uk>
Email: -http://www. +rjh@
Google users: <http://www.cpax.org.uk/prg/writings/googly.php>
"Usenet is a strange place" - dmr 29 July 1999
Dec 23 '07 #2

<St******************@gmail.comwrote in message
Sorry if this is not "exactly" a C topic but I thought this would be
the best place to start. I need some guidance on working with bitmap
images in ANSI C. I need to "read" in a bitmap image, then break it up
into pieces (say 32x32) then take those pieces and "save" them,
granted they do not need to be a readable image, then perform some
other operations on the pieces, and then combine the manipulated
pieces back into one image.

Any suggestions on any part of this would be great! I have found the
"EasyBMP" package from http://easybmp.sourceforge.net but apperas this
is not going to work for what I need.

Thanks!
A bit long, but then pixels are getting cheaper these days
(for regs who don't want to read this, it is just an ANSI-standard bitmap
loader /saver).
This is incorporated in my book Basic Algorithms, so any bug reports
particularly welcome.

/************************************************** *******
* bmp.c - Microsoft bitmap loading functions. *
************************************************** *******/
#include <stdio.h>
#include <stdlib.h>

typedef struct
{
int width;
int height;
int bits;
int upside_down; /* set for a bottom-up bitmap */
int core; /* set if bitmap is old version */
int palsize; /* number of palette entries */
int compression; /* type of compression in use */
} BMPHEADER;

int bmpgetinfo(char *fname, int *width, int *height);
unsigned char *loadbmp(char *fname, int *width, int *height);
unsigned char *loadbmp8bit(char *fname, int *width, int *height, unsigned
char *pal);
unsigned char *loadbmp4bit(char *fname, int *width, int *height, unsigned
char *pal);

static void loadpalette(FILE *fp, unsigned char *pal, int entries);
static void loadpalettecore(FILE *fp, unsigned char *pal, int entries);
static void savepalette(FILE *fp, unsigned char *pal, int entries);
static int loadheader(FILE *fp, BMPHEADER *hdr);
static void saveheader(FILE *fp, int width, int height, int bits);
static void loadraster8(FILE *fp, unsigned char *data, int width, int
height);
static void loadraster4(FILE *fp, unsigned char *data, int width, int
height);
static void loadraster1(FILE *fp, unsigned char *data, int width, int
height);
static long getfilesize(int width, int height, int bits);
static void swap(void *x, void *y, int len);
static void fput32le(long x, FILE *fp);
static void fput16le(int x, FILE *fp);
static long fget32le(FILE *fp);
static int fget16le(FILE *fp);
/************************************************** ******
* bmpgetinfo() - get information about a BMP file. *
* Params: fname - name of the .bmp file. *
* width - return pointer for image width. *
* height - return pointer for image height. *
* Returns: bitmap type. *
* 0 - not a valid bitmap. *
* 1 - monochrome paletted (2 entries). *
* 4 - 4-bit paletted. *
* 8 - 8 bit paletted. *
* 16 - 16 bit rgb. *
* 24 - 24 bit rgb. *
* 32 - 24 bit rgb with 1 byte wasted. *
************************************************** ******/
int bmpgetinfo(char *fname, int *width, int *height)
{
FILE *fp;
BMPHEADER bmphdr;
fp = fopen(fname, "rb");
if(!fp)
return 0;
if(loadheader(fp, &bmphdr) == -1)
{
fclose(fp);
return 0;
}
fclose(fp);
if(width)
*width = bmphdr.width;
if(height)
*height = bmphdr.height;
return bmphdr.bits;
}

/************************************************** **********
* loadbmp() - load any bitmap *
* Params: fname - pointer to file path *
* width - return pointer for image width *
* height - return pointer for image height *
* Returns: malloced pointer to image, 0 on fail *
************************************************** **********/
unsigned char *loadbmp(char *fname, int *width, int *height)
{
FILE *fp;
BMPHEADER bmpheader;
unsigned char *answer;
unsigned char *raster;
unsigned char pal[256 * 3];
int index;
int col;
int target;
int i;
int ii;
fp = fopen(fname, "rb");
if(!fp)
return 0;

if(loadheader(fp, &bmpheader) == -1)
{
fclose(fp);
return 0;
}
if(bmpheader.bits == 0 || bmpheader.compression != 0)
{
fclose(fp);
return 0;
}

answer = malloc(bmpheader.width * bmpheader.height * 3);
if(!answer)
{
fclose(fp);
return 0;
}

if(bmpheader.bits < 16)
{
raster = malloc(bmpheader.width * bmpheader.height);
if(!raster)
{
free(answer);
return 0;
}
}

switch(bmpheader.bits)
{
case 1:
if(bmpheader.core)
loadpalettecore(fp, pal, 2);
else
loadpalette(fp, pal, bmpheader.palsize);

loadraster1(fp, raster, bmpheader.width, bmpheader.height);
break;
case 4:
if(bmpheader.core)
loadpalettecore(fp, pal, 256);
else
loadpalette(fp, pal, bmpheader.palsize);

loadraster4(fp, raster, bmpheader.width, bmpheader.height);
break;

case 8:
if(bmpheader.core)
loadpalettecore(fp, pal, 256);
else
loadpalette(fp, pal, bmpheader.palsize);

loadraster8(fp, raster, bmpheader.width, bmpheader.height);
break;

case 16:
for(i=0;i<bmpheader.height;i++)
{
for(ii=0;ii<bmpheader.width;ii++)
{
target = (i * bmpheader.width * 3) + ii * 3;
col = fget16le(fp);
answer[target] = (col & 0x001F) << 3;
answer[target+1] = (col & 0x03E0) >2;
answer[target+2] = (col & 0x7A00) >7;
}
while(ii < (bmpheader.width + 1)/2 * 4)
{
fgetc(fp);
ii++;
}
}
break;

case 24:
for(i=0;i<bmpheader.height;i++)
{
for(ii=0;ii<bmpheader.width;ii++)
{
target = (i * bmpheader.width * 3) + ii * 3;
answer[target] = fgetc(fp);
answer[target+1] = fgetc(fp);
answer[target+2] = fgetc(fp);
}
while(ii < (bmpheader.width + 3)/4 * 4)
{
fgetc(fp);
ii++;
}
}
break;

case 32:
for(i=0;i<bmpheader.height;i++)
for(ii=0;ii<bmpheader.width;ii++)
{
target = (i * bmpheader.width * 3) + ii * 3;
answer[target] = fgetc(fp);
answer[target+1] = fgetc(fp);
answer[target+2] = fgetc(fp);
fgetc(fp);
}
break;
}

if(bmpheader.bits < 16)
{
for(i=0;i<bmpheader.height;i++)
for(ii=0;ii<bmpheader.width;ii++)
{
target = (i * bmpheader.width * 3) + ii * 3;
index = raster[i * bmpheader.width + ii] * 3;
answer[target] = pal[ index ];
answer[target+1] = pal[ index + 1 ];
answer[target+2] = pal[ index + 2 ];
}

free(raster);
}

if(bmpheader.upside_down)
{
for(i=0;i<bmpheader.height/2;i++)
swap( answer + i * bmpheader.width * 3,
answer + (bmpheader.height - i - 1) * bmpheader.width * 3,
bmpheader.width * 3);
}

if(ferror(fp))
{
free(answer);
answer = 0;
}

*width = bmpheader.width;
*height = bmpheader.height;

fclose(fp);

return answer;
}

/************************************************** **********
* loadbmp8bit() - load an 8-bit bitmap. *
* Params: fname - pointer to file path. *
* width - return pointer for image width. *
* height - return pointer for image height. *
* pal - return pointer to 256 rgb palette entries. *
* Returns: malloced pointer to image data, 0 on fail. *
************************************************** **********/
unsigned char *loadbmp8bit(char *fname, int *width, int *height, unsigned
char *pal)
{
BMPHEADER bmphdr;
FILE *fp;
unsigned char *answer;
int i;

fp = fopen(fname, "rb");
if(!fp)
return 0;

if(loadheader(fp, &bmphdr) == -1)
{
fclose(fp);
return 0;
}
if(bmphdr.bits != 8)
{
fclose(fp);
return 0;
}
if(bmphdr.compression != 0)
{
fclose(fp);
return 0;
}
if(bmphdr.core)
loadpalettecore(fp, pal, 256);
else
loadpalette(fp, pal, bmphdr.palsize);
answer = (unsigned char *) malloc(bmphdr.width * bmphdr.height);
if(!answer)
{
fclose(fp);
return 0;
}

loadraster8(fp, answer, bmphdr.width, bmphdr.height);
if(bmphdr.upside_down)
{
for(i=0;i<bmphdr.height/2;i++)
swap(answer + i * bmphdr.width,
answer + (bmphdr.height - i - 1) * bmphdr.width,
bmphdr.width);
}

if(ferror(fp))
{
free(answer);
answer = 0;
}

fclose(fp);
*width = bmphdr.width;
*height = bmphdr.height;

return answer;
}

/************************************************** **********
* loadbmp4bit() - load a 4-bit bitmap from disk. *
* Params: fname - pointer to the file path. *
* width - return pointer for image width. *
* height - return pointer for image height. *
* pal - return pointer for 16 rgb palette entries. *
* Returns: malloced pointer to 4-bit image data. *
************************************************** **********/
unsigned char *loadbmp4bit(char *fname, int *width, int *height, unsigned
char *pal)
{
BMPHEADER bmphdr;
FILE *fp;
unsigned char *answer;
int i;

fp = fopen(fname, "rb");
if(!fp)
return 0;

if(loadheader(fp, &bmphdr) == -1)
{
fclose(fp);
return 0;
}
if(bmphdr.bits != 4)
{
fclose(fp);
return 0;
}
if(bmphdr.compression != 0)
{
fclose(fp);
return 0;
}

if(bmphdr.core)
loadpalettecore(fp, pal, 16);
else
loadpalette(fp, pal, bmphdr.palsize);
answer = (unsigned char *) malloc(bmphdr.width * bmphdr.height);
if(!answer)
{
fclose(fp);
return 0;
}
loadraster4(fp, answer, bmphdr.width, bmphdr.height);

if(bmphdr.upside_down)
{
for(i=0;i<bmphdr.height/2;i++)
{
swap(answer + i * bmphdr.width,
answer + (bmphdr.height - i - 1) * bmphdr.width,
bmphdr.width);
}
}

if(ferror(fp))
{
free(answer);
answer = 0;
}

fclose(fp);
*width = bmphdr.width;
*height = bmphdr.height;
return answer;
}

/************************************************** *********
* save a24-bit bmp file. *
* Params: fname - name of file to save. *
* rgb - raster data in rgb format *
* width - image width *
* height - image height *
* Returns: 0 on success, -1 on fail *
************************************************** *********/
int savebmp(char *fname, unsigned char *rgb, int width, int height)
{
FILE *fp;
int i;
int ii;

fp = fopen(fname, "wb");
if(!fp)
return -1;

saveheader(fp, width, height, 24);
for(i=0;i<height;i++)
{
for(ii=0;ii<width;ii++)
{
fputc(rgb[2], fp);
fputc(rgb[1], fp);
fputc(rgb[0], fp);
rgb += 3;
}
if(( width * 3) % 4)
{
for(ii=0;ii< 4 - ( (width * 3) % 4); ii++)
{
fputc(0, fp);
}
}
}

if(ferror(fp))
{
fclose(fp);
return -1;
}

return fclose(fp);
}

/************************************************** ********
* save an 8-bit palettised bitmap . *
* Params: fname - the name of the file. *
* data - the raster data *
* width - image width *
* height - image height *
* pal - palette (RGB format) *
* Returns: 0 on success, -1 on failure *
************************************************** ********/
int savebmp8bit(char *fname, unsigned char *data, int width, int height,
unsigned char *pal)
{
FILE *fp;
int i;
int ii;

fp = fopen(fname, "wb");
if(!fp)
return -1;

saveheader(fp, width, height, 8);
savepalette(fp, pal, 256);

for(i=0;i<height;i++)
for(ii=0;ii< (width + 3)/4;ii++)
{
fputc(data[i*width + ii * 4], fp);
if(ii * 4 + 1 < width)
fputc(data[i*width + ii * 4 + 1], fp);
else
fputc(0, fp);
if(ii * 4 + 2 < width)
fputc(data[i*width + ii * 4 + 2], fp);
else
fputc(0, fp);
if(ii * 4 + 3 < width)
fputc(data[i*width + ii * 4 + 3], fp);
else
fputc(0, fp);
}

if(ferror(fp))
{
fclose(fp);
return -1;
}

return fclose(fp);
}

/************************************************** *****
* save a 4-bit palettised bitmap. *
* Params: fname - the name of the file. *
* data - raster data *
* width - image width *
* height - image height *
* pal - the palette (RGB format) *
* Returns: 0 on success, -1 on failure *
************************************************** *****/
int savebmp4bit(char *fname, unsigned char *data, int width, int height,
unsigned char *pal)
{
FILE *fp;
int i;
int ii;
int pix;

fp = fopen(fname, "wb");
if(!fp)
return -1;

saveheader(fp, width, height, 4);
savepalette(fp, pal, 16);

for(i=0;i<height;i++)
for(ii=0;ii< (width + 7)/8;ii++)
{
pix = data[i * width + ii * 8] << 4;
if(ii * 8 + 1 < width)
pix |= data[i * width + ii * 8 + 1];
fputc(pix, fp);

pix = 0;
if(ii * 8 + 2 < width)
pix = data[ i * width + ii * 8 + 2] << 4;
if(ii * 8 + 3 < width)
pix |= data[ i * width + ii * 8 + 3];
fputc(pix, fp);

pix = 0;
if(ii * 8 + 4 < width)
pix = data[ i * width + ii * 8 + 4] << 4;
if(ii * 8 + 5 < width)
pix |= data[i * width + ii * 8 + 5];
fputc(pix, fp);

pix = 0;
if(ii * 8 + 6 < width)
pix = data[ i * width + ii * 8 + 6] << 4;
if(ii * 8 + 7 < width)
pix |= data[i * width + ii * 8 + 7];
fputc(pix, fp);
}

if(ferror(fp))
{
fclose(fp);
return -1;
}

return fclose(fp);
}

/************************************************** *************
* save a 2-bit palettised bitmap. *
* Params: fname - name of file to write. *
* data - raster data, one byte per pixel. *
* width - image width. *
* height - image height. *
* pal - the palette (0 = black/white) *
* Returns: 0 on success, -1 on fail. *
************************************************** *************/
int savebmp2bit(char *fname, unsigned char *data, int width, int height,
unsigned char *pal)
{
FILE *fp;
unsigned char defpal[6] = {0, 0, 0, 255, 255, 255 };
int i;
int ii;
int iii;
int pix;

fp = fopen(fname, "wb");
if(!fp)
return -1;

saveheader(fp, width, height, 1);
if(pal)
savepalette(fp, pal, 2);
else
savepalette(fp, defpal, 2);

for(i=0;i<height;i++)
for(ii=0;ii<width;ii+=32)
{
pix = 0;
for(iii=0;iii<8;iii++)
if(ii + iii < width)
pix |= data[i * width + ii + iii] ? (1 << (7-iii) ) : 0;
fputc(pix, fp);

pix = 0;
for(iii=0;iii<8;iii++)
if(ii + iii + 8 < width)
pix |= data[i * width + ii + iii + 8] ? (1 << (7 - iii)) : 0;
fputc(pix, fp);

pix = 0;
for(iii=0;iii<8;iii++)
if(ii + iii + 16 < width)
pix |= data[i * width + ii + iii + 16] ? (1 << (7 - iii)) : 0;
fputc(pix, fp);

pix = 0;
for(iii=0;iii<8;iii++)
if(ii + iii + 24 < width)
pix |= data[i * width + ii + iii + 24] ? (1 << (7 - iii)) : 0;

fputc(pix, fp);
}

if(ferror(fp))
{
fclose(fp);
return -1;
}

return fclose(fp);
}

/************************************************** ************
* loadpalette() - load palette for a new format BMP. *
* Params: fp - pointer to an open file. *
* pal - return pointer for palette entries. *
* entries - number of entries in palette. *
************************************************** ************/
static void loadpalette(FILE *fp, unsigned char *pal, int entries)
{
int i;
for(i=0;i<entries;i++)
{
pal[2] = fgetc(fp);
pal[1] = fgetc(fp);
pal[0] = fgetc(fp);
fgetc(fp);
pal += 3;
}
}

/************************************************** ****
* loadpalettecore() - load a palette for a core BMP *
* Params: fp - pointer to an open file. *
* pal - return pointer for palette entries. *
* entries - number of entries to read. *
************************************************** ****/
static void loadpalettecore(FILE *fp, unsigned char *pal, int entries)
{
int i;
for(i=0;i<entries;i++)
{
pal[2] = fgetc(fp);
pal[1] = fgetc(fp);
pal[0] = fgetc(fp);
pal += 3;
}
}

/************************************************** ***********
* saves a palette *
* Params: fp - pointer to an open file *
* pal - the palette *
* entries - number of palette entries. *
************************************************** ***********/
static void savepalette(FILE *fp, unsigned char *pal, int entries)
{
int i;

for(i=0;i<entries;i++)
{
fputc(pal[2], fp);
fputc(pal[1], fp);
fputc(pal[0], fp);
fputc(0, fp);
pal += 3;
}
}

/*
typedef struct tagBITMAPFILEHEADER { // bmfh
WORD bfType;
DWORD bfSize;
WORD bfReserved1;
WORD bfReserved2;
DWORD bfOffBits;
} BITMAPFILEHEADER;

typedef struct tagBITMAPINFOHEADER{ // bmih
DWORD biSize;
LONG biWidth;
LONG biHeight;
WORD biPlanes;
WORD biBitCount
DWORD biCompression;
DWORD biSizeImage;
LONG biXPelsPerMeter;
LONG biYPelsPerMeter;
DWORD biClrUsed;
DWORD biClrImportant;
} BITMAPINFOHEADER;

*/

/************************************************** ****
* loadheader() - load the bitmap header information. *
* Params: fp - pinter to an opened file. *
* hdr - return pointer for header information.*
* Returns: 0 on success, -1 on fail. *
************************************************** ****/
static int loadheader(FILE *fp, BMPHEADER *hdr)
{
int size;
int hdrsize;
int id;
int i;

id = fget16le(fp);
/* is it genuinely a BMP ? */
if(id != 0x4D42)
return -1;
/* skip rubbish */
fget32le(fp);
fget16le(fp);
fget16le(fp);
/* offset to bitmap bits */
size = fget32le(fp);
hdrsize = fget32le(fp);
if(hdrsize == 40)
{
hdr->width = fget32le(fp);
hdr->height = fget32le(fp);
fget16le(fp);
hdr->bits = fget16le(fp);
hdr->compression = fget32le(fp);
/* skip rubbish */
for(i=0;i<12;i++)
fgetc(fp);
hdr->palsize = fget32le(fp);
if(hdr->palsize == 0 && hdr->bits < 16)
hdr->palsize = 1 << hdr->bits;
fget32le(fp);
if(hdr->height < 0)
{
hdr->upside_down = 0;
hdr->height = -hdr->height;
}
else
hdr->upside_down = 1;
hdr->core = 0;
}
else if(hdrsize == 12)
{
hdr->width = fget16le(fp);
hdr->height = fget16le(fp);
fget16le(fp);
hdr->bits = fget16le(fp);
hdr->compression = 0;
hdr->upside_down = 1;
hdr->core = 1;
hdr->palsize = 1 << hdr->bits;
}
else
return 0;
if(ferror(fp))
return -1;
return 0;
}

/************************************************** **************
* write a bitmap header. *
* Params: fp - pointer to an open file. *
* width - bitmap width *
* height - bitmap height *
* bit - bit depth (1, 4, 8, 16, 24, 32) *
************************************************** **************/
static void saveheader(FILE *fp, int width, int height, int bits)
{
long sz;
long offset;

/* the file header */
/* "BM" */
fputc(0x42, fp);
fputc(0x4D, fp);

/* file size */
sz = getfilesize(width, height, bits) + 40 + 14;
fput32le(sz, fp);

/* reserved */
fput16le(0, fp);
fput16le(0, fp);
/* offset of raster data from header */
if(bits < 16)
offset = 40 + 14 + 4 * (1 << bits);
else
offset = 40 + 14;
fput32le(offset, fp);

/* the infoheader */

/* size of structure */
fput32le(40, fp);
fput32le(width, fp);
/* height negative because top-down */
fput32le(-height, fp);
/* bit planes */
fput16le(1, fp);
fput16le(bits, fp);
/* compression */
fput32le(0, fp);
/* size of image (can be zero) */
fput32le(0, fp);
/* pels per metre */
fput32le(600000, fp);
fput32le(600000, fp);
/* colours used */
fput32le(0, fp);
/* colours important */
fput32le(0, fp);
}

/************************************************** *******************
* load 8-bit raster data *
* Params: fp - pointer to an open file. *
* data - return pointer for data (one byte per pixel) *
* width - image width *
* height - image height *
************************************************** *******************/
static void loadraster8(FILE *fp, unsigned char *data, int width, int
height)
{
int linewidth;
int i;
int ii;

linewidth = (width + 3)/4 * 4;

for(i=0;i<height;i++)
{
for(ii=0;ii<width;ii++)
*data++ = fgetc(fp);
while(ii < linewidth)
{
fgetc(fp);
ii++;
}
}
}

/************************************************** ********************
* load 4-bit raster data *
* Params: fp - pointer to an open file. *
* data - return pointer for data (one byte per pixel) *
* width - image width *
* height - iamge height *
************************************************** ********************/
static void loadraster4(FILE *fp, unsigned char *data, int width, int
height)
{
int linewidth;
int i;
int ii;
int pix;

linewidth = (((width + 1)/2) + 3)/4 * 4;

for(i=0;i<height;i++)
{
for(ii=0;ii<linewidth;ii++)
{
pix = fgetc(fp);
if(ii * 2 < width)
*data++ = pix >4;
if(ii * 2 + 1 < width)
*data++ = pix & 0x0F;
}
}
}

/************************************************** *****************
* load 1 bit raster data *
* Params: fp - pointer to an open file. *
* data - return pointer for data (one byte per pixel) *
* width - image width. *
* height - image height. *
************************************************** *****************/
static void loadraster1(FILE *fp, unsigned char *data, int width, int
height)
{
int linewidth;
int i;
int ii;
int iii;
int pix;

linewidth = ((width + 7)/8 + 3)/4 * 4;

for(i=0;i<height;i++)
{
for(ii=0;ii<linewidth;ii++)
{
pix = fgetc(fp);
if(ii * 8 < width)
{
for(iii=0;iii<8;iii++)
if(ii * 8 + iii < width)
*data++ = (pix & (1 << (7 - iii))) ? 1 : 0;
}
}
}

}

/************************************************** ***
* get the size of the file to be written. *
* Params: width - image width *
* height - image height *
* bits - image type *
* Returns: size of image data (excluding headers) *
************************************************** ***/
static long getfilesize(int width, int height, int bits)
{
long answer = 0;
switch(bits)
{
case 1:
answer = (width + 7)/8;
answer = (answer + 3)/4 * 4;
answer *= height;
answer += 2 * 4;
break;
case 4:
answer = 16 * 4 + (width + 1)/2;
answer = (answer + 3)/4 * 4;
answer *= height;
answer += 16 * 4;
break;
case 8:
answer = (width + 3)/4 * 4;
answer *= height;
answer += 256 * 4;
break;
case 16:
answer = (width * 2 + 3)/4 * 4;
answer *= height;
break;
case 24:
answer = (width * 3 + 3)/4 * 4;
answer *= height;
break;
case 32:
answer = width * height * 4;
break;
default:
return 0;
}

return answer;
}

/************************************************** *************
* swap an area of memory *
* Params: x - pointer to first buffer *
* y - pointer to second buffer *
* len - length of memory to swap *
************************************************** *************/
static void swap(void *x, void *y, int len)
{
unsigned char *ptr1 = x;
unsigned char *ptr2 = y;
unsigned char temp;
int i;

for(i=0;i<len;i++)
{
temp = ptr1[i];
ptr1[i] = ptr2[i];
ptr2[i] = temp;
}
}

/************************************************** *************
* write a 32-bit little-endian number to a file. *
* Params: x - the number to write *
* fp - pointer to an open file. *
************************************************** *************/
static void fput32le(long x, FILE *fp)
{
fputc(x & 0xFF, fp);
fputc( (x >8) & 0xFF, fp);
fputc( (x >16) & 0xFF, fp);
fputc( (x >24) & 0xFF, fp);
}

/************************************************** *************
* write a 16-bit little-endian number to a file. *
* Params: x - the nmuber to write *
* fp - pointer to an open file *
************************************************** *************/
static void fput16le(int x, FILE *fp)
{
fputc(x & 0xFF, fp);
fputc( (x >8) & 0xFF, fp);
}

/************************************************** *************
* fget32le() - read a 32 bit little-endian number from a file. *
* Params: fp - pointer to an open file. *
* Returns: value read as a signed integer. *
************************************************** *************/
static long fget32le(FILE *fp)
{
long answer;
answer = fgetc(fp);
answer |= (fgetc(fp) << 8);
answer |= (fgetc(fp) << 16);
answer |= (fgetc(fp) << 24);
/* check for negative */
if(answer & 0x80000000)
answer |= ((-1) << 31);
return answer;
}

/************************************************** *************
* fget16le() - read a 16 bit little-endian number from a file. *
* Params: fp - pointer to an open file. *
* Returns: value read as a signed integer. *
************************************************** *************/
static int fget16le(FILE *fp)
{
int answer;
answer = fgetc(fp);
answer |= (fgetc(fp) << 8);
/* check for negative */
if(answer & 0x8000)
answer |= ((-1) << 16);
return answer;
}
--
Free games and programming goodies.
http://www.personal.leeds.ac.uk/~bgy1mm

Dec 23 '07 #3

<St******************@gmail.comwrote in message
news:0d**********************************@q77g2000 hsh.googlegroups.com...
Hello,
Sorry if this is not "exactly" a C topic but I thought this would be
the best place to start. I need some guidance on working with bitmap
images in ANSI C. I need to "read" in a bitmap image, then break it up
into pieces (say 32x32) then take those pieces and "save" them,
granted they do not need to be a readable image, then perform some
other operations on the pieces, and then combine the manipulated
pieces back into one image.

Any suggestions on any part of this would be great! I have found the
"EasyBMP" package from http://easybmp.sourceforge.net but apperas this
is not going to work for what I need.

Thanks!
Here is some dodgy code I found
typedef unsigned short WORD;
typedef unsigned int DWORD;
typedef unsigned char CHAR;
// Bitmap file header
typedef struct tagBITMAPINFOHEADER
{
WORD bfType; //specifies the file type
DWORD bfSize; //specifies the size in bytes of the bitmap file
WORD bfReserved1; //reserved; must be 0
WORD bfReserved2; //reserved; must be 0
DWORD bOffBits; //species the offset in bytes from the bitmapfileheader to
the bitmap bits
DWORD biSize; //specifies the number of bytes required by the struct
DWORD biWidth; //specifies width in pixels
DWORD biHeight; //species height in pixels
WORD biPlanes; //specifies the number of color planes, must be 1
WORD biBitCount; //specifies the number of bit per pixel
DWORD biCompression;//spcifies the type of compression
DWORD biSizeImage; //size of image in bytes
DWORD biXPelsPerMeter; //number of pixels per meter in x axis
DWORD biYPelsPerMeter; //number of pixels per meter in y axis
DWORD biClrUsed; //number of colors used by th ebitmap
DWORD biClrImportant; //number of colors that are important
} BITMAPINFOHEADER;

// Saves picture data to a bitmap file
unsigned char *SaveBitmapFile(char *filename, BITMAPINFOHEADER
*bitmapInfoHeader, unsigned char *bitmapImage)
{
FILE *filePtr;
unsigned int imageIdx=0;
CHAR tempRGB;

//open file in write mode
filePtr = fopen(filename,"wb");
if (filePtr == NULL)
return NULL;

//save the bitmap file header
fwrite(&(bitmapInfoHeader->bfType),
sizeof(bitmapInfoHeader->bfType),1,filePtr);
fwrite(&(bitmapInfoHeader->bfSize),
sizeof(bitmapInfoHeader->bfSize),1,filePtr);
fwrite(&(bitmapInfoHeader->bfReserved1),
sizeof(bitmapInfoHeader->bfReserved1),1,filePtr);
fwrite(&(bitmapInfoHeader->bfReserved2),
sizeof(bitmapInfoHeader->bfReserved2),1,filePtr);
fwrite(&(bitmapInfoHeader->bOffBits),
sizeof(bitmapInfoHeader->bOffBits),1,filePtr);

//write info header
fwrite(&(bitmapInfoHeader->biSize),
sizeof(bitmapInfoHeader->biSize),1,filePtr);
fwrite(&(bitmapInfoHeader->biWidth),
sizeof(bitmapInfoHeader->biWidth),1,filePtr);
fwrite(&(bitmapInfoHeader->biHeight),
sizeof(bitmapInfoHeader->biHeight),1,filePtr);
fwrite(&(bitmapInfoHeader->biPlanes),
sizeof(bitmapInfoHeader->biPlanes),1,filePtr);
fwrite(&(bitmapInfoHeader->biBitCount),
sizeof(bitmapInfoHeader->biBitCount),1,filePtr);
fwrite(&(bitmapInfoHeader->biCompression),
sizeof(bitmapInfoHeader->biCompression),1,filePtr);
fwrite(&(bitmapInfoHeader->biSizeImage),
sizeof(bitmapInfoHeader->biSizeImage),1,filePtr);
fwrite(&(bitmapInfoHeader->biXPelsPerMeter),
sizeof(bitmapInfoHeader->biXPelsPerMeter),1,filePtr);
fwrite(&(bitmapInfoHeader->biYPelsPerMeter),
sizeof(bitmapInfoHeader->biYPelsPerMeter),1,filePtr);
fwrite(&(bitmapInfoHeader->biClrUsed),
sizeof(bitmapInfoHeader->biClrUsed),1,filePtr);
fwrite(&(bitmapInfoHeader->biClrImportant),
sizeof(bitmapInfoHeader->biClrImportant),1,filePtr);

//swap the r and b values to get RGB (bitmap is BGR)
for (imageIdx = 0;imageIdx < bitmapInfoHeader->biSizeImage;imageIdx+=3)
{
tempRGB = bitmapImage[imageIdx];
bitmapImage[imageIdx] = bitmapImage[imageIdx + 2];
bitmapImage[imageIdx + 2] = tempRGB;
}

//write bitmap image values
fwrite(bitmapImage,sizeof(CHAR),bitmapInfoHeader->biSizeImage,filePtr);

//swap the r and b values to get RGB (bitmap is BGR)
for (imageIdx = 0;imageIdx < bitmapInfoHeader->biSizeImage;imageIdx+=3)
{
tempRGB = bitmapImage[imageIdx];
bitmapImage[imageIdx] = bitmapImage[imageIdx + 2];
bitmapImage[imageIdx + 2] = tempRGB;
}
fclose(filePtr);

return 0;
}

// Loads picture data from a bitmap file
unsigned char *LoadBitmapFile(char *filename, BITMAPINFOHEADER
*bitmapInfoHeader)
{
FILE *filePtr; //our file pointer
CHAR *bitmapImage; //store image data
unsigned int imageIdx=0; //image index counter
CHAR tempRGB; //our swap variable

//open filename in read binary mode
filePtr = fopen(filename,"rb");
if (filePtr == NULL)
return NULL;

//read the bitmap file header
//fread(&bitmapFileHeader, sizeof(BITMAPFILEHEADER),1,filePtr);
fread(&bitmapInfoHeader->bfType,
sizeof(bitmapInfoHeader->bfType),1,filePtr);
fread(&bitmapInfoHeader->bfSize,
sizeof(bitmapInfoHeader->bfSize),1,filePtr);
fread(&bitmapInfoHeader->bfReserved1,
sizeof(bitmapInfoHeader->bfReserved1),1,filePtr);
fread(&bitmapInfoHeader->bfReserved2,
sizeof(bitmapInfoHeader->bfReserved2),1,filePtr);
fread(&bitmapInfoHeader->bOffBits,
sizeof(bitmapInfoHeader->bOffBits),1,filePtr);

//read the bitmap info header
//fread(bitmapInfoHeader, sizeof(BITMAPINFOHEADER),1,filePtr);
fread(&bitmapInfoHeader->biSize,
sizeof(bitmapInfoHeader->biSize),1,filePtr);
fread(&bitmapInfoHeader->biWidth,
sizeof(bitmapInfoHeader->biWidth),1,filePtr);
fread(&bitmapInfoHeader->biHeight,
sizeof(bitmapInfoHeader->biHeight),1,filePtr);
fread(&bitmapInfoHeader->biPlanes,
sizeof(bitmapInfoHeader->biPlanes),1,filePtr);
fread(&bitmapInfoHeader->biBitCount,
sizeof(bitmapInfoHeader->biBitCount),1,filePtr);
fread(&bitmapInfoHeader->biCompression,
sizeof(bitmapInfoHeader->biCompression),1,filePtr);
fread(&bitmapInfoHeader->biSizeImage,
sizeof(bitmapInfoHeader->biSizeImage),1,filePtr);
fread(&bitmapInfoHeader->biXPelsPerMeter,
sizeof(bitmapInfoHeader->biXPelsPerMeter),1,filePtr);
fread(&bitmapInfoHeader->biYPelsPerMeter,
sizeof(bitmapInfoHeader->biYPelsPerMeter),1,filePtr);
fread(&bitmapInfoHeader->biClrUsed,
sizeof(bitmapInfoHeader->biClrUsed),1,filePtr);
fread(&bitmapInfoHeader->biClrImportant,
sizeof(bitmapInfoHeader->biClrImportant),1,filePtr);

//verify that this is a bmp file by check bitmap id
if (bitmapInfoHeader->bfType !=0x4D42)
{
fclose(filePtr);
return NULL;
}

//move file point to the begging of bitmap data
fseek(filePtr, bitmapInfoHeader->bOffBits, SEEK_SET);

//allocate enough memory for the bitmap image data
bitmapImage = (CHAR*)malloc(bitmapInfoHeader->biSizeImage);

//verify memory allocation
if (!bitmapImage)
{
free(bitmapImage);
fclose(filePtr);
return NULL;
}

//read in the bitmap image data
fread(bitmapImage,sizeof(CHAR),bitmapInfoHeader->biSizeImage,filePtr);

//make sure bitmap image data was read
if (bitmapImage == NULL)
{
fclose(filePtr);
return NULL;
}

//swap the r and b values to get RGB (bitmap is BGR)
for (imageIdx = 0;imageIdx < bitmapInfoHeader->biSizeImage;imageIdx+=3)
{
tempRGB = bitmapImage[imageIdx];
bitmapImage[imageIdx] = bitmapImage[imageIdx + 2];
bitmapImage[imageIdx + 2] = tempRGB;
}

//close file and return bitmap iamge data
fclose(filePtr);
return bitmapImage;
}

Dec 23 '07 #4

"Malcolm McLean" <re*******@btinternet.comwrote in message
news:Qv******************************@bt.com...
>
<St******************@gmail.comwrote in message
....
>the best place to start. I need some guidance on working with bitmap
images in ANSI C. I need to "read" in a bitmap image, then break it up
into pieces (say 32x32) then take those pieces and "save" them,
....
A bit long, but then pixels are getting cheaper these days
(for regs who don't want to read this, it is just an ANSI-standard bitmap
loader /saver).
This is incorporated in my book Basic Algorithms, so any bug reports
particularly welcome.
* loadbmp() - load any bitmap *
....
unsigned char *loadbmp(char *fname, int *width, int *height)
....
answer = malloc(bmpheader.width * bmpheader.height * 3);
Don't know if this is actually wrong, but: assumes here 24 bits per pixel?
Even though it loads any size? So smaller pixel bitmaps are expanded to 24?
That's why this function doesn't return the pixel size?

Also this allows an odd number of bytes per row although I seem to remember
BMP was padded to 4n bytes per row, so the code presumably compresses the
image by eliminating the padding?

In other words, this function loads any BMP file and returns a pointer to a
string of 3-byte/24-bit pixels in a 'raw' format different from that stored
in the file?

Bart

Dec 23 '07 #5
"Bart C" <bc@freeuk.comwrote in message
"Malcolm McLean" <re*******@btinternet.comwrote in message
news:Qv******************************@bt.com...
>>
<St******************@gmail.comwrote in message
...
>>the best place to start. I need some guidance on working with bitmap
images in ANSI C. I need to "read" in a bitmap image, then break it up
into pieces (say 32x32) then take those pieces and "save" them,
...
>A bit long, but then pixels are getting cheaper these days
(for regs who don't want to read this, it is just an ANSI-standard bitmap
loader /saver).
This is incorporated in my book Basic Algorithms, so any bug reports
particularly welcome.
>* loadbmp() - load any bitmap *
...
>unsigned char *loadbmp(char *fname, int *width, int *height)
...
> answer = malloc(bmpheader.width * bmpheader.height * 3);

Don't know if this is actually wrong, but: assumes here 24 bits per pixel?
Even though it loads any size? So smaller pixel bitmaps are expanded to
24? That's why this function doesn't return the pixel size?
That's right. If you want the palette and index values for some reason call
getbitmapinfo() to ensure the file is of the right type, then call
loadbmp8bit / 4 bit.
>
Also this allows an odd number of bytes per row although I seem to
remember BMP was padded to 4n bytes per row, so the code presumably
compresses the image by eliminating the padding?

In other words, this function loads any BMP file and returns a pointer to
a string of 3-byte/24-bit pixels in a 'raw' format different from that
stored in the file?
Yes. The padding is now a quirk of the file format. Originally the idea was
that a slow PC could save a few cycles by loading the image directly into
memory, and then from memory straight to VDU memory. Nowadays that is
unlikely to matter, and probably slows it down as much as it speeds it up.

--
Free games and programming goodies.
http://www.personal.leeds.ac.uk/~bgy1mm

Dec 24 '07 #6

"Flash Gordon" <sp**@flash-gordon.me.ukwrote in message
Malcolm McLean wrote, On 27/12/07 17:16:
Actually, the best thing is normally to start off by checking the length
is within a valid range, then check the data is valid, then check that you
hit a valid tag in the place expected. If any of those things fail you
report the file as being corrupt or a format you can't handle.
The object of the exercise is to read the data, not to check the file for
adherence to the format.
>
>That's a good point. It should be returning -1 instead of zero on an
unrecognised header size, at least. Probably we should try to read it as
a 40. However to do a really good job we've got to skip to the raster
bits, which means a total rewrite and lots of complications.

Don't advertise your code as being reliable then, advertise it as
supporting some but not all BMP files.
The question is what to do with as yet unspecified versions. Presumably MS
will extend the header field, maybe add new chunks. The question is whether
the raster data will still be readable without the new information, and it
is impossible for MS to guarantee that, because they won't change the format
for fun, but because some need arises.
I should be able to read every current format, barring bugs, but some are
obsolete.

--
Free games and programming goodies.
http://www.personal.leeds.ac.uk/~bgy1mm

Dec 27 '07 #7
Malcolm McLean wrote, On 27/12/07 23:38:
>
"Flash Gordon" <sp**@flash-gordon.me.ukwrote in message
>Malcolm McLean wrote, On 27/12/07 17:16:
Actually, the best thing is normally to start off by checking the
length is within a valid range, then check the data is valid, then
check that you hit a valid tag in the place expected. If any of those
things fail you report the file as being corrupt or a format you can't
handle.
The object of the exercise is to read the data, not to check the file
for adherence to the format.
OK, so I know not to rely on any code you write for reading files. This
is because you can only reliably read data if you validate that it is in
the format that it is meant to be in. Also you were the person who
raised the problem of corrupted files, I just advised you on what works
better in real situations where you do have data corruption to deal with.
>>That's a good point. It should be returning -1 instead of zero on an
unrecognised header size, at least. Probably we should try to read it
as a 40. However to do a really good job we've got to skip to the
raster bits, which means a total rewrite and lots of complications.

Don't advertise your code as being reliable then, advertise it as
supporting some but not all BMP files.
The question is what to do with as yet unspecified versions. Presumably
MS will extend the header field, maybe add new chunks. The question is
whether the raster data will still be readable without the new
information, and it is impossible for MS to guarantee that, because they
won't change the format for fun, but because some need arises.
I should be able to read every current format, barring bugs, but some
are obsolete.
You are saying this having just had OS/2 BMPs reported as a possible
problem (in text you snipped) and having admitted to not even testing on
all the formats that MS support. Ernie raised forward compatibility as
an additional problem.
--
Flash Gordon
Dec 28 '07 #8
Flash Gordon wrote:
Malcolm McLean wrote, On 27/12/07 17:16:
>"Ernie Wright" <er****@comcast.netwrote
>>>
The specification claims that 4-bit old-style BMP has a 16-color
palette. It's been so long since I've encountered one of these
that I wouldn't know where to look for one now. Unless you know of
users that actually have to deal with these (they haven't been
written by Microsoft code since Windows 2.x), I'd be tempted to
strip all of that cruft out of your loader.

The version of Paint in Windows Vista claims to be able to save a 16
colour bitmap. I've no idea if the format is the same, but I see no
reason why it would not be.
It's not. Old-style BMP, what Malcolm's code calls "core" BMP, is an
obsolete form of BMP with a different header. This older form was used
by Windows 1.x and 2.x and OS/2 1.x.

- Ernie http://home.comcast.net/~erniew
Dec 28 '07 #9
Malcolm McLean wrote:
The object of the exercise is to read the data, not to check the file
for adherence to the format.
You have to verify that you're reading a format you understand! If at
any point the file deviates from what you expect, the only *safe* thing
to conclude is that it's not in a format you can handle.
The question is what to do with as yet unspecified versions. Presumably
MS will extend the header field, maybe add new chunks.
They already have. A long time ago, in fact. Google BITMAPV4HEADER
and BITMAPV5HEADER.

It's unlikely you'll see these in files, since they're primarily meant
to enhance aspects of the internal representation of bitmaps in Windows.

But the point is, the right thing to do with variants you don't know
about, which by definition includes future versions, is recognize that
you don't know what they contain, and fail gracefully.

- Ernie http://home.comcast.net/~erniew
Dec 28 '07 #10
Malcolm McLean wrote:
"Ernie Wright" <er****@comcast.netwrote in message
>Malcolm McLean wrote:
>>The object of the exercise is to read the data, not to check the
file for adherence to the format.

You have to verify that you're reading a format you understand!

I depends what you are doing. [...]
Most of the time an image of a tiger, one pixel out of register, is
better than no image of a tiger at all, or at least no worse,
Is that really the only consequence you can think of? That the offset
to the pixel data will be slightly off? Can you not imagine the pixel
data itself being different or even absent? If you don't even know
what's in the header, how can you possibly presume to guess what's in
the rest of the file--or even that this is an image file at all?

It's fairly common for a loader like yours to be daisychained with a
number of others, each called in sequence on a file until one of them
recognizes and loads the contents. This approach will break if you fail
to step aside when you encounter something you don't understand. There
may be a loader behind you that *does* understand the file. It's even
possible that your blind meander through the file will lead to a crash.

Your code rejects BMPs with a bits-per-pixel of 0. Why? A 0 bpp is
valid as of Windows NT 4.0, and surely there are tigers in those images
just waiting to be (perhaps imperfectly) revealed, no?

Of course, your code rightly rejects 0-bpp images because it doesn't
know what to do with them. How is an unexpected header size any
different?
>But the point is, the right thing to do with variants you don't know
about, which by definition includes future versions, is recognize that
you don't know what they contain, and fail gracefully.
See above.
- Ernie http://home.comcast.net/~erniew
Dec 30 '07 #11

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

0
by: Brad Smalling | last post by:
Does anyone know if there is a reason to prefer bitmaps over icons (or vice-versa) when populating image lists? I've always favored icons because of their built-in transparency (although I've...
13
by: Jeff Melvaine | last post by:
I note that I can write expressions like "1 << 100" and the result is stored as a long integer, which means it is stored as an integer of arbitrary length. I may need to use a large number of...
1
by: Mark Evans | last post by:
I have a dialog box and on it I want to display a bitmap, which will change at various times during the program. My problem is that the bitmaps will not be the same each time. I want the user to...
4
by: Nathan | last post by:
Hello, I'm needing some advice: I have an app for which I've built a timer out of multiple bitmaps--a clock with a moving hands. I've saved each hand position (1 second, 2 seconds, etc.) as a...
1
by: John | last post by:
I have 76 bitmaps (640 x 480) and need to combine them together to form a big one (about 3840 x 2880). Those 76 bitmaps overlap each other with a small portion. Is there any way to do it? Thanks.
1
by: Peter Stojkovic | last post by:
What is the correct way to move BITMAPS and drawings inside a windows-forms from one project to another project. The problem is the following row: Dim resources As...
2
by: Mike | last post by:
Hello everybody. I am drawing a country map that consists of 149 municipality bitmaps, each around 25 Kb. I draw it onto the in-memory bitmap, then draw it on the picture box. I use C++, but...
1
by: =?Utf-8?B?Sm9obg==?= | last post by:
Hi, I need to combine several bitmaps together to form a single bitmap. Can anyone show me some similar sample code? Thanks. AJ
1
by: nkumarin001 | last post by:
Hi, Can anyone help me in this matter:- When i was studying locally managed tablespaces i came across bitmaps that are used in locally managed tablespaces it stated that:- "Locally...
0
by: aa123db | last post by:
Variable and constants Use var or let for variables and const fror constants. Var foo ='bar'; Let foo ='bar';const baz ='bar'; Functions function $name$ ($parameters$) { } ...
0
by: ryjfgjl | last post by:
If we have dozens or hundreds of excel to import into the database, if we use the excel import function provided by database editors such as navicat, it will be extremely tedious and time-consuming...
0
by: ryjfgjl | last post by:
In our work, we often receive Excel tables with data in the same format. If we want to analyze these data, it can be difficult to analyze them because the data is spread across multiple Excel files...
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.