Ejemplo n.º 1
0
/*! \note	Still very unsafe, but I need to redo everything later anyway.
*/
void grs_run(GritShared *grs, GritRec *gr_base)
{
	// Make sure we have shared data.
	if( grs->dib==NULL && grs->palRec.data==NULL)
	{
		lprintf(LOG_WARNING, "No shared data to run with!\n");
		return;
	}

	// Make copy of gr_base for flags, etc
	GritRec *gr= grit_alloc();
	grs_free(gr->shared);

	grit_copy_options(gr, gr_base);
	grit_copy_strings(gr, gr_base);

	// Attach shared data
	gr->shared= grs;
	strrepl(&gr->symName, grs->symName);
	strrepl(&gr->dstPath, grs->dstPath);

	if(grs->dib == NULL)
	{
		// Palette only. Create new dib.
		gr->srcDib= dib_alloc(16, 16, 8, NULL);
		memset(dib_get_pal(gr->srcDib), 0, PAL_MAX*RGB_SIZE);
		memcpy(dib_get_pal(gr->srcDib), grs->palRec.data, rec_size(&grs->palRec));
	}
	else
		gr->srcDib= dib_clone(grs->dib);

	// NOTE: aliasing screws up deletion later; detach manually.
	gr->_dib= gr->srcDib;	

	// Run for shared gr
	do 
	{
		if(!grit_validate(gr))
			break;

		bool grit_prep_gfx(GritRec *gr);
		bool grit_prep_shared_pal(GritRec *gr);

		if(gr->gfxProcMode != GRIT_EXCLUDE)
			grit_prep_gfx(gr);
		
		if(gr->palProcMode != GRIT_EXCLUDE)
			grit_prep_shared_pal(gr);

		if(gr->bExport)
			grit_export(gr);

	} while(0);

	gr->_dib= NULL;

	// Detach shared data and delete gr
	gr->shared= NULL;
	grit_free(gr);
}
Ejemplo n.º 2
0
// CHK: unpadded works
// DESC: redimensions/(un)tiles a dib into a column of tiles with 
//   dimensions dstW x tileH. Can also work in reverse (if srcW<dstW)
// NOTE: padding pixels may cause problems
CLDIB *dib_redim_copy(CLDIB *src, int dstW, int tileH, int tileN)
{
	if(src == NULL)
		return NULL;

	int srcW, srcH, srcB, srcP;
	dib_get_attr(src, &srcW, &srcH, &srcB, &srcP);

	// Force byte alignment
	if( (dstW*srcB&7) )
		return NULL;

	// setup redim
	int srcR= srcW*srcB>>3, dstR= dstW*srcB>>3;	// bytes/row

	RECORD srcRec= { srcR, srcH, dib_get_img(src) };
	RECORD dstRec= { dstR, 0, 0 };

	int ii;
	BYTE *srcD= NULL;

	if(srcR&3)	// depad for src
	{
		srcD= (BYTE*)malloc(srcR*srcH);
		if(srcD == NULL)
			return NULL;
		for(ii=0; ii<srcH; ii++)
			memcpy(&srcD[ii*srcR], &srcRec.data[ii*srcP], srcR);
		srcRec.data= srcD;
	}
	bool bOK= data_redim(&srcRec, &dstRec, tileH, tileN);
	SAFE_FREE(srcD);
	if(!bOK)
		return NULL;

	CLDIB *dst= dib_alloc(dstW, dstRec.height, srcB, NULL, dib_is_topdown(src));
	if(dst == NULL)
		return NULL;

	int dstH= dib_get_height(dst);	// bytes/row
	int dstP= dib_get_pitch(dst);
	BYTE *dstD= dib_get_img(dst);

	if(dstR&3)	// repad for dst
	{
		for(ii=0; ii<dstH; ii++)
			memcpy(&dstD[ii*dstP], &dstRec.data[ii*dstR], dstP);
	}
	else
		memcpy(dstD, dstRec.data, dstP*dstH);
	SAFE_FREE(dstRec.data);

	memcpy(dib_get_pal(dst), dib_get_pal(src), 
		dib_get_nclrs(src)*RGB_SIZE);

	return dst;
}
Ejemplo n.º 3
0
/*!	Converts palette to 16bit GBA colors, compresses it and fills in
	\a gr._palRec.
*/
bool grit_prep_pal(GritRec *gr)
{
    lprintf(LOG_STATUS, "Palette preparation.\n");

    int ii, nclrs, palS;
    COLOR *palOut;
    RGBQUAD *palIn;

    nclrs= gr->palEnd - gr->palStart;
    if(dib_get_nclrs(gr->_dib) < nclrs && nclrs != 0)
        nclrs= dib_get_nclrs(gr->_dib);

    palS= nclrs*sizeof(COLOR);
    palOut= (COLOR*)malloc(palS);
    palIn= &dib_get_pal(gr->_dib)[gr->palStart];

    for(ii=0; ii<nclrs; ii++)
        palOut[ii]= RGB16(palIn[ii].rgbBlue, palIn[ii].rgbGreen, palIn[ii].rgbRed);

    RECORD rec= { 2, palS/2, (BYTE*)palOut };

    if( BYTE_ORDER == BIG_ENDIAN )
        data_byte_rev(rec.data, rec.data, rec_size(&rec), 2);

    // Attach and compress palette
    grit_compress(&rec, &rec, gr->palCompression);
    rec_alias(&gr->_palRec, &rec);

    lprintf(LOG_STATUS, "Palette preparation complete.\n");
    return true;
}
Ejemplo n.º 4
0
bool CBmpFile::Save(const char *fpath)
{
	FILE *fp= NULL;
	bool bOK= true;;

	try
	{
		if(!mDib)
			throw CImgFile::sMsgs[ERR_GENERAL];

		fp = fopen(fpath, "wb");
		if(!fp)
			throw CImgFile::sMsgs[ERR_NO_FILE];

		BITMAPINFOHEADER bmih= *dib_get_hdr(mDib);
		int dibH= dib_get_height2(mDib), dibHa= dib_get_height(mDib);
		int dibP= dib_get_pitch(mDib), dibPa= dibP;
		int dibS= dibHa*dibPa, nclrs= dib_get_nclrs(mDib);

		bmih.biHeight= dibHa;
		bmih.biSizeImage= dibS;

		// fill in fileheader
		BITMAPFILEHEADER bmfh;
		bmfh.bfType= BMP_TYPE;
		bmfh.bfOffBits= sizeof(BITMAPFILEHEADER)+BMIH_SIZE+nclrs*RGB_SIZE;
		bmfh.bfSize= bmfh.bfOffBits + dibS;
		bmfh.bfReserved1= bmfh.bfReserved2= 0;

		// write down header
		fwrite(&bmfh, sizeof(BITMAPFILEHEADER), 1, fp);
		fwrite(&bmih, BMIH_SIZE, 1, fp);
		// write down palette
		fwrite(dib_get_pal(mDib), RGB_SIZE, nclrs, fp);

		// write down rows, with possible flipping
		BYTE *dibL= dib_get_img(mDib);
		if(dibH<0)
		{
			dibL += (dibHa-1)*dibPa;
			dibP= -dibP;
		}
		for(int iy=0; iy<dibHa; iy++)
			fwrite(dibL+iy*dibP, dibPa, 1, fp);
	}
	catch(const char *msg)
	{
		SetMsg(msg);
		bOK= false;
	}

	if(fp)
		fclose(fp);	

	return bOK;
}
Ejemplo n.º 5
0
/*!	\param dib	DIB to reduce the palette of. Must be paletted. Pixels 
		will be rearranged to match the palette.
	\param extPal	External palette record. \a dib will use this 
		and its own palette. Can be NULL. The new reduced palette goes here too.
	\return	Number of reduced colors, or 0 if not 8bpp. 
	\note	The order of colors is the order of appearance, except for the 
		first one.
*/
int dib_pal_reduce(CLDIB *dib, RECORD *extPal)
{
	// Only for 8bpp (for now)
	if(dib == NULL || dib_get_bpp(dib) != 8)
		return 0;

	int ii, jj, kk, ix, iy;

	int dibW, dibH, dibP;
	dib_get_attr(dib, &dibW, &dibH, NULL, &dibP);

	BYTE *dibD= dib_get_img(dib);

	// Get palette histogram
	int histo[256];

	memset(histo, 0, sizeof(histo));
	for(iy=0; iy<dibH; iy++)
		for(ix=0; ix<dibW; ix++)
			histo[dibD[iy*dibP+ix]]++;

	// Allocate room for new palette and init with ext pal
	// NOTE: extPal is assumed reduced!
	// NOTE: double-size for rdxPal for worst-case scenario.
	// NOTE: the *Clr things are just to make comparisons easier.
	//		 pointers ftw!

	int count;
	RGBQUAD *rdxPal= (RGBQUAD*)malloc(512*RGB_SIZE);
	COLORREF *rdxClr= (COLORREF*)rdxPal, *dibClr= (COLORREF*)dib_get_pal(dib);

	memset(rdxPal, 0, 512*RGB_SIZE);
	if(extPal != NULL && extPal->data != NULL)
	{
		memcpy(rdxPal, extPal->data, rec_size(extPal));
		count= extPal->height;
	}
	else
	{
		rdxClr[0]= dibClr[0];
		count= 1;
	}

	// PONDER: always keep index 0 ?

	// Create reduced palette and prep tables for pixel conversion.
	DWORD srcIdx[PAL_MAX], dstIdx[PAL_MAX];

	kk=0;
	for(ii=0; ii<PAL_MAX; ii++)
	{
		if(histo[ii])
		{
			for(jj=0; jj<count; jj++)
				if(rdxClr[jj] == dibClr[ii])
					break;
			// Match: add color to table
			if(jj == count)
			{
				rdxClr[jj]= dibClr[ii];
				count++;
			}
			srcIdx[kk]= jj;
			dstIdx[kk]= ii;
			kk++;
		}
	}

	// PONDER: what *should* happen if nn > PAL_MAX ?
	// Fail, trunc or re-quantize?

	//  Update palette and remap pixels
	memcpy(dibClr, rdxClr, PAL_MAX*RGB_SIZE);
	dib_pixel_replace(dib, dstIdx, srcIdx, kk);

	// Update rdxPal's data
	if(extPal)
	{
		extPal->width= RGB_SIZE;
		extPal->height= count;
		free(extPal->data);

		extPal->data= (BYTE*)malloc(count*RGB_SIZE);
		memcpy(extPal->data, rdxClr, count*RGB_SIZE);
	}

	return count;
}
Ejemplo n.º 6
0
/*!	This basically does two things. First, create a bitmap from the
	designated area of the source bitmap. Then, converts it 8 or
	16 bpp, depending on \a gr.gfxBpp. Conversion to lower bpp is
	done later, when it's more convenient. The resultant bitmap is
	put into \a gr._dib, and will be used in later preparation.
*/
bool grit_prep_work_dib(GritRec *gr)
{
    int ii, nn;
    RGBQUAD *rgb;

    lprintf(LOG_STATUS, "Work-DIB creation.\n");

    // --- resize ---
    CLDIB *dib= dib_copy(gr->srcDib,
                         gr->areaLeft, gr->areaTop, gr->areaRight, gr->areaBottom,
                         false);
    if(dib == NULL)
    {
        lprintf(LOG_ERROR, "  Work-DIB creation failed.\n");
        return false;
    }
    // ... that's it? Yeah, looks like.

    // --- resample (to 8 or 16) ---
    //
    int dibB= dib_get_bpp(dib);

    // Convert to 16bpp, but ONLY for bitmaps
    if( gr->gfxBpp == 16 && gr->gfxMode != GRIT_GFX_TILE )
    {
        if(dibB != 16)
        {
            lprintf(LOG_WARNING, "  converting from %d bpp to %d bpp.\n",
                    dibB, gr->gfxBpp);

            CLDIB *dib2= dib_convert_copy(dib, 16, 0);

            // If paletted src AND -pT AND NOT -gT[!]
            //   use trans color pal[T]
            //# PONDER: did I fix this right?
            if(dibB <= 8 && gr->palHasAlpha && !gr->gfxHasAlpha)
            {
                rgb= &dib_get_pal(dib)[gr->palAlphaId];
                lprintf(LOG_WARNING,
                        "  pal->true-color conversion with transp pal-id option.\n"
                        "    using color %02X%02X%02X",
                        rgb->rgbRed, rgb->rgbGreen, rgb->rgbBlue);

                gr->gfxHasAlpha= true;
                gr->gfxAlphaColor= *rgb;
            }

            dib_free(dib);

            if(dib2 == NULL)
            {
                lprintf(LOG_ERROR, "prep: Bpp conversion failed.\n");
                return false;
            }
            dib= dib2;
        }

        // --- Dealing with 16bpp images ---
        // Modes:
        // 555      | GBA           | swap R_B
        // 555+A=1  | NDS           | swap R_B + add NDS_ALPHA
        // 555+A    | NDS, transp   | swap R_B + NDS_ALPHA if !trans-clr
        // src-pal + -pT -> NDS, transp

        // Swap palette bRGB to bBGR
        // And resolve -gT
        nn= dib_get_size_img(dib)/2;
        WORD *dibD2= (WORD*)dib_get_img(dib);

        // Single transparent color
        if(gr->gfxHasAlpha)
        {
            rgb= &gr->gfxAlphaColor;
            WORD clr= RGB16(rgb->rgbBlue, rgb->rgbGreen, rgb->rgbRed), wd;

            lprintf(LOG_STATUS,
                    "  converting to: 16bpp BGR, alpha=1, except for 0x%04X.\n",
                    clr);

            for(ii=0; ii<nn; ii++)
            {
                wd= swap_rgb16(dibD2[ii]);
                dibD2[ii]= (wd == clr ? wd : wd | NDS_ALPHA);
            }
        }
        else if(gr->gfxMode == GRIT_GFX_BMP_A)
        {
            lprintf(LOG_STATUS, "converting to: 16bpp BGR, alpha=1.\n");

            for(ii=0; ii<nn; ii++)
                dibD2[ii]= swap_rgb16(dibD2[ii]) | NDS_ALPHA;
        }
        else
        {
            lprintf(LOG_STATUS, "converting to: 16bpp, BGR.\n");

            for(ii=0; ii<nn; ii++)
                dibD2[ii]= swap_rgb16(dibD2[ii]);
        }
    }
    else if(dibB != 8)	// otherwise, convert to 8bpp
    {
        lprintf(LOG_WARNING, "  converting from %d bpp to %d bpp.\n",
                dibB, gr->gfxBpp);

        if(!dib_convert(dib, 8, 0))
        {
            dib_free(dib);
            lprintf(LOG_ERROR, "  Bpp conversion failed.\n");
            return false;
        }
    }

    // Palette transparency additions.
    if(dib_get_bpp(dib)==8)
    {
        // If gfx-trans && !pal-trans:
        //   Find gfx-trans in palette and use that
        if(gr->gfxHasAlpha && !gr->palHasAlpha)
        {
            rgb= &gr->gfxAlphaColor;
            RGBQUAD *pal= dib_get_pal(dib);

            lprintf(LOG_WARNING,
                    "  tru/pal -> pal conversion with transp color option.\n"
                    "    looking for color %02X%02X%02X in palette.\n",
                    rgb->rgbRed, rgb->rgbGreen, rgb->rgbBlue);

            uint ii_min= 0, dist, dist_min;
            dist_min= rgb_dist(rgb, &pal[0]);

            for(ii=1; ii<256; ii++)
            {
                dist= rgb_dist(rgb, &pal[ii]);
                if(dist < dist_min)
                {
                    ii_min= ii;
                    dist_min= dist;
                }
            }

            // HACK: count 'match' only if average error is < +/-14
            if(dist_min < 576)
            {
                gr->palHasAlpha= true;
                gr->palAlphaId= ii_min;
            }
        }

        // Swap alpha and pixels palette entry
        if(gr->palHasAlpha)
        {
            lprintf(LOG_STATUS, "  Palette transparency: pal[%d].\n",
                    gr->palAlphaId);
            BYTE *imgD= dib_get_img(dib);
            nn= dib_get_size_img(dib);

            for(ii=0; ii<nn; ii++)
            {
                if(imgD[ii] == 0)
                    imgD[ii]= gr->palAlphaId;
                else if(imgD[ii] == gr->palAlphaId)
                    imgD[ii]= 0;
            }
            RGBQUAD tmp, *pal= dib_get_pal(dib);
            SWAP3(pal[0], pal[gr->palAlphaId], tmp);
        }

        // TODO: Palette merging.
        if(gr->palIsShared)
        {
            lprintf(LOG_STATUS, "  Palette merging\n");
            nn= dib_pal_reduce(dib, &gr->shared->palRec);
            if(nn>PAL_MAX)
                lprintf(LOG_WARNING, "    New palette exceeds 256. Truncating.\n");
        }
    }

    dib_free(gr->_dib);
    gr->_dib= dib;

    lprintf(LOG_STATUS, "Work-DIB creation complete: %dx%d@%d.\n",
            dib_get_width(gr->_dib), dib_get_height(gr->_dib),
            dib_get_bpp(gr->_dib));
    return true;
}
Ejemplo n.º 7
0
// Yes, you can use LoadImage too, but that creates a device 
// dependent bitmap and you want to stay the fsck away from those.
bool CBmpFile::Load(const char *fpath)
{
	FILE *fp= fopen(fpath, "rb");
	CLDIB *dib= NULL;

	try
	{
		if(!fp)
			throw CImgFile::sMsgs[ERR_NO_FILE];

		BITMAPFILEHEADER bmfh;
		fread(&bmfh,  sizeof(BITMAPFILEHEADER), 1,fp);

		// Whoa, not a bitmap, back off
		if(bmfh.bfType != BMP_TYPE)	// 4D42h = "BM". 
			throw CImgFile::sMsgs[ERR_FORMAT];

		BITMAPINFOHEADER bmih;
		bool bCore;

		// check for bm version first :(
		fread(&bmih, 4, 1, fp);
		if(bmih.biSize == sizeof(BITMAPCOREHEADER))		// crap! v2.x BMP
		{
			bCore= true;
			bmih.biSize= BMIH_SIZE; 
			WORD wd;
			fread(&wd, 2,1,fp);
			bmih.biWidth= wd;
			fread(&wd, 2,1,fp);
			bmih.biHeight= wd;
			fread(&bmih.biPlanes, 2,1,fp);
			fread(&bmih.biBitCount, 2,1,fp);
			memset(&bmih.biCompression, 0, 
				BMIH_SIZE-sizeof(BITMAPCOREHEADER));
		}
		else		// normal v3.0 BMP
			fread(&bmih.biWidth, BMIH_SIZE-4, 1, fp);

		if(bmih.biPlanes > 1)				// no color planes, plz
			throw sMsgs[ERR_BMP_PLANES];
		if(bmih.biCompression != BI_RGB)	// no compression either
			throw sMsgs[ERR_BMP_CPRS];

		int dibP, dibHa, dibS;

		dibHa= abs(bmih.biHeight);
		dibP= dib_align(bmih.biWidth, bmih.biBitCount);
		dibS= dibP*dibHa;
		// set manually, just to be sure
		bmih.biSizeImage= dibS;

		// ditto for ClrUsed
		if(bmih.biBitCount <=8 && bmih.biClrUsed == 0)
			bmih.biClrUsed= 1<<bmih.biBitCount;

		// now we set-up the full bitmap
		dib= dib_alloc(bmih.biWidth, dibHa, bmih.biBitCount, NULL, true);
		if(dib == NULL)
			throw CImgFile::sMsgs[ERR_ALLOC];

		// read the palette
		fread(dib_get_pal(dib), RGB_SIZE, bmih.biClrUsed, fp);

		// read image
		fread(dib_get_img(dib), dibS, 1, fp);
		if(bmih.biHeight>=0)	// -> TD image
			dib_vflip(dib);

	}	// </try>
	catch(const char *msg)
	{
		SetMsg(msg);
		dib_free(dib);
		dib= NULL;
	}
	if(fp)
		fclose(fp);
	if(!dib)
		return false;

	// if we're here we've succeeded
	SetMsg(CImgFile::sMsgs[ERR_NONE]);
	dib_free(Attach(dib));

	SetBpp(dib_get_bpp(dib));
	SetPath(fpath);

	return true;
}
Ejemplo n.º 8
0
// === LOAD HELPERS ===================================================
static bool tga_read_pal(CLDIB *dib, const TGAHDR *hdr, 
	FILE *fp)
{
	// no palette, 's fair
	if(hdr->has_table == 0)
		return true;
	// writer of this file is an idiot
	// PONDER: is pal_len the size of the whole thing, or
	//   counting from pal_start?
	//   I'm assuming the latter
	if(hdr->pal_len <= hdr->pal_start)
		return false;

	int ii;
	int clrS= (hdr->pal_bpp != 15 ? (hdr->pal_bpp/8) : 2);
	int palN= hdr->pal_len, palS= palN*clrS;

	BYTE *palD= (BYTE*)malloc(palN*clrS);
	fread(palD, clrS, palN-hdr->pal_start, fp);

	RGBQUAD *dibPal= dib_get_pal(dib);
	memset(dibPal, 0, dib_get_nclrs(dib)*RGB_SIZE);

	if(palN>dib_get_nclrs(dib))
		palN= dib_get_nclrs(dib);
	
	switch(hdr->pal_bpp)	// damn these different options :(
	{
	case 16:
		for(ii=hdr->pal_start; ii<palN; ii++)
		{
			WORD rgb16= ((WORD*)palD)[ii];
			dibPal[ii].rgbRed=   ((rgb16>>10)&31)*255/31;
			dibPal[ii].rgbGreen= ((rgb16>> 5)&31)*255/31;
			dibPal[ii].rgbBlue=  ((rgb16    )&31)*255/31;
		}
		break;
	case 24:
		for(ii=hdr->pal_start; ii<palN; ii++)
		{
			TGA_BGR rgb24= ((TGA_BGR*)palD)[ii];
			dibPal[ii].rgbRed=   rgb24.red;
			dibPal[ii].rgbGreen= rgb24.green;
			dibPal[ii].rgbBlue=  rgb24.blue;
		}
		break;
	case 32:
		for(ii=hdr->pal_start; ii<palN; ii++)
		{
			TGA_BGRA rgb32= ((TGA_BGRA*)palD)[ii];
			dibPal[ii].rgbRed=   rgb32.red;
			dibPal[ii].rgbGreen= rgb32.green;
			dibPal[ii].rgbBlue=  rgb32.blue;
		}
		break;
	default:
		SAFE_FREE(palD);
		return false;
	}

	// clean up
	SAFE_FREE(palD);
	return true;
}
Ejemplo n.º 9
0
bool CTgaFile::Save(const char *fpath)
{

	int ii, iy;
	FILE *fp= NULL;
	bool bOK= true;

	try
	{
		if(!mDib)
			throw CImgFile::sMsgs[ERR_GENERAL];

		fp = fopen(fpath, "wb");
		if(!fp)
			throw CImgFile::sMsgs[ERR_NO_FILE];

		int imgW= dib_get_width(mDib);
		int imgH= dib_get_height(mDib), imgHs= dib_get_height2(mDib);
		int imgP= dib_get_pitch(mDib);
		int imgB= dib_get_bpp(mDib);
		int imgS= imgH*imgP, nclrs= dib_get_nclrs(mDib);

		TGAHDR hdr;
		memset(&hdr, 0, sizeof(TGAHDR));

		if(imgB==1)
			hdr.type= TGA_BW;
		else if(imgB <= 8)
			hdr.type= TGA_PAL;
		else
			hdr.type= TGA_true;

		if(imgB<=8)		// paletted
		{
			hdr.has_table= 1;
			hdr.pal_len= dib_get_nclrs(mDib);
			hdr.pal_bpp= 24;
		}
		else			// true color
			hdr.has_table=0;

		hdr.width= imgW;
		hdr.height= imgH;
		hdr.img_bpp= imgB;
		hdr.img_desc= 0;

		fwrite(&hdr, 1, sizeof(TGAHDR), fp);

		// write palette
		if(imgB <= 8)
		{
			RGBQUAD *pal= dib_get_pal(mDib);
			for(ii=0; ii<hdr.pal_len; ii++)
				fwrite(&pal[ii], 1, 3, fp);
		}

		// TGA should be bottom up:
		BYTE *imgL= dib_get_img(mDib);
		if(dib_is_topdown(mDib))
		{
			imgL += imgP*(imgH-1);
			imgP= -imgP;
		}

		// write image (not RLEd, because that's awful to do)
		int tgaP= (imgW*imgB+7)/8;
		for(iy=0; iy<imgH; iy++)
			fwrite(&imgL[iy*imgP], 1, tgaP, fp);
	}
	catch(const char *msg)
	{
		SetMsg(msg);
		bOK= false;
	}

	if(fp)
		fclose(fp);	

	return bOK;
}
Ejemplo n.º 10
0
/*!	\param width	Bitmap width
*	\param height	Bitmap height
*	\param bpp	Bitmap bitdepth (1, 4, 8, 16, 24, 32)
*	\param data	Data to fill the bitmap with. If \c NULL, data will 
*	  be uninitialized.
*	\param bTopDown	If \c true, the bitmap will be top-down (origin in 
*	  the top-left); if \c false, it'll be bottom up. Windows bitmaps 
*	  are traditionally bottom-up, with all the awkwardness that goes 
*	  with it (as matrices and screens are usually top-down). 
*	  \c CLDIBs are top-down by default.
*	\note	Always call \c dib_free() on bitmaps when you're done with it.
*/
CLDIB *dib_alloc(int width, int height, int bpp, const BYTE *data,
	bool bTopDown /* true */)
{
	int ii;
	CLDIB *dib= NULL;

	// check validity of requested bpp
	const int bpp_allowed[6]= { 1, 4, 8, 16, 24, 32 };
	for(ii=0; ii<6; ii++)
		if(bpp == bpp_allowed[ii])
			break;
	if(ii >= 6)
		return NULL;
	
	int nclrs, dibH, dibP, dibS;
	nclrs= (bpp > 8 ? 0 : 1<<bpp);
	dibH= height;
	dibP= dib_align(width, bpp);
	dibS= dibP*dibH;

	// create in two stages, first the dib itself, and then for the data
	// and conk out if either fails
	dib= (CLDIB*)malloc(sizeof(CLDIB));
	if(dib == NULL)
		return NULL;

	dib->data= (BYTE*)malloc(BMIH_SIZE + nclrs*RGB_SIZE + dibS);
	if(dib->data == NULL)
	{
		free(dib);
		return NULL;
	}

	BITMAPINFOHEADER *bmih= dib_get_hdr(dib);

	bmih->biSize= BMIH_SIZE;
	bmih->biWidth= width;
	bmih->biHeight= bTopDown ? -height : height;
	bmih->biPlanes= 1;
	bmih->biBitCount= bpp;
	bmih->biCompression= 0;
	bmih->biSizeImage= dibS; 
	bmih->biXPelsPerMeter= 0; 
	bmih->biYPelsPerMeter= 0; 
	bmih->biClrUsed= nclrs; 
	bmih->biClrImportant= 0;

	// init palette, all reds, corresponding to COLORREFs [0-nclrs>
	// PONDER: is this right?
	COLORREF *clr= (COLORREF*)dib_get_pal(dib);
	for(ii=0; ii<nclrs; ii++)
		clr[ii]= ii<<16;


	// init data
	if(data != NULL)
	{
		if(bTopDown)
			memcpy(dib_get_img(dib), data, dibS);
		else
		{
			const BYTE *srcL= &data[(height-1)*dibP];
			BYTE *dstL= dib_get_img(dib);
			for(ii=0; ii<height; ii++)
				memcpy(&dstL[ii*dibP], &srcL[-ii*dibP], dibP);
		}
	}

	return dib;
}