// 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; }
/*! \param hdc Destination DC. * \param rectDest Destination rectangle. If NULL, the DIB's * dimensions are used. * \param dib DIB to blit. * \param rectSrc Source rectangle. If NULL, the DIB's * dimensions is used. * \param flags Blit flags. * \note Tested for top-down and bottom-up DIB's */ int dib_to_hdc(HDC hdc, const RECT *rectDst, CLDIB *dib, const RECT *rectSrc, DWORD flags) { RECT dstR, srcR; int dibHa= dib_get_height(dib); if(rectDst == NULL) SetRect(&dstR, 0, 0, dib_get_width(dib), dibHa); else CopyRect(&dstR, rectDst); if(rectSrc == NULL) SetRect(&srcR, 0, 0, dib_get_width(dib), dibHa); else CopyRect(&srcR, rectSrc); int srcW= srcR.right-srcR.left, srcH= srcR.bottom-srcR.top; int dstW= dstR.right-dstR.left, dstH= dstR.bottom-dstR.top; SetStretchBltMode(hdc, COLORONCOLOR); return StretchDIBits( hdc, dstR.left, dstR.bottom, dstW, -dstH, srcR.left, dibHa-srcR.top, srcW, -srcH, dib_get_img(dib), dib_get_info(dib), DIB_RGB_COLORS, flags); }
/*! \param dib Valid CLDIB * \return Initialized HBITMAP; NULL on failure. */ HBITMAP dib_to_hbm(CLDIB *dib) { if(dib == NULL) return NULL; HDC hdc = GetDC(NULL); BYTE *hbmD; HBITMAP hbm= CreateDIBSection(hdc, dib_get_info(dib), DIB_RGB_COLORS, (void**)&hbmD, NULL, 0); // while the following _should_ work, it sometimes doesn't // for some inexplicable reason. //SetDIBits(hdc, hbm, 0, dib_get_height(dib), // dib_get_img(dib), dib_get_info(dib), DIB_RGB_COLORS); ReleaseDC(NULL, hdc); if(hbm == NULL) return NULL; // so we're doing it manually memcpy(hbmD, dib_get_img(dib), dib_get_size_img(dib)); // PONDER: palette? return hbm; }
/*! \param dib bitmap to flip. * \note This is an in-place flip. */ bool dib_vflip(CLDIB *dib) { BYTE *topL, *botL, *tempD; if(dib == NULL) return false; DWORD dibP= dib_get_pitch(dib), dibH= dib_get_height(dib); tempD= (BYTE*)malloc(dibP); if(tempD == NULL) return false; topL= dib_get_img(dib); botL= topL+dibP*(dibH-1); dibH /= 2; while(dibH--) { memcpy(tempD, topL, dibP); memcpy(topL, botL, dibP); memcpy(botL, tempD, dibP); topL += dibP; botL -= dibP; } free(tempD); return true; }
/*! \param dib valid HBITMAP * \return initialized CLDIB; NULL on failure. */ CLDIB *dib_from_hbm(HBITMAP hbm) { if(hbm == NULL) return NULL; BITMAP bm; GetObject(hbm, sizeof(BITMAP), &bm); CLDIB *dib= dib_alloc(bm.bmWidth, bm.bmHeight, bm.bmBitsPixel, NULL); if(dib == NULL) return NULL; // The WinXP GetDIBits somehow overwrites biClrUsed // Hack to fix that BITMAPINFOHEADER *bmih= dib_get_hdr(dib); int nclrs= bmih->biClrUsed; HDC hdc = GetDC(NULL); GetDIBits(hdc, hbm, 0, dib_get_height(dib), dib_get_img(dib), dib_get_info(dib), DIB_RGB_COLORS); ReleaseDC(NULL, hdc); bmih->biClrUsed= nclrs; // man, this is f@%#@%g weak return dib; }
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; }
// TGA RLE; // * lines are byte-aligned // * RLE can cross lines // * RLE works by the pixel for true clr, by byte for paletted // * The RLE header byte, ch: // ch{0-6} : # chunks -1 // ch{7} : stretch. void tga_unrle(CLDIB *dib, TGAHDR *hdr, FILE *fp) { int ii, ix, iy; int imgW= dib_get_width(dib); int imgH= dib_get_height(dib); int imgB= dib_get_bpp(dib); int imgP= dib_get_pitch(dib); BYTE *imgL= dib_get_img(dib), *imgEnd= imgL+imgH*imgP; int tgaP= (imgW*imgB+7)/8; // pseudo pitch int tgaN= (imgB+7)/8; // chunk size BYTE ch, chunk[4]; ix=0, iy=0; while(1) { ch= fgetc(fp); if(ch>127) // stretch { ch -= 127; fread(chunk, 1, tgaN, fp); while(ch--) { for(ii=0; ii<tgaN; ii++) imgL[ix+ii]= chunk[ii]; ix += tgaN; if(ix >= tgaP) { imgL += imgP; ix= 0; if(imgL >= imgEnd) return; } } } // </stretch> else // no stretch { ch++; while(ch--) { fread(&imgL[ix], 1, tgaN, fp); ix += tgaN; if(ix >= tgaP) { imgL += imgP; ix= 0; if(imgL >= imgEnd) return; } } } // </no stretch> } // </while(1)> }
/*! Similar to \c dib_clone, but can crop the image too. The * boundaries of the new bitmap need not fit inside the source; the * outside parts will be zeroed. * \param src Source bitmap. * \param ll Left of rectangle. * \param tt Top of rectangle. * \param rr Right of rectangle. * \param bb Bottom of rectangle. * \param bClip If \c true, the rectangle will be clipped to the * dimensions of \c src. * \return Copied and cropped bitmap. */ CLDIB *dib_copy(CLDIB *src, int ll, int tt, int rr, int bb, bool bClip) { if(src==NULL || ll==rr || tt==bb) return NULL; int srcW= dib_get_width(src); int srcH= dib_get_height(src); int srcB= dib_get_bpp(src); int srcP= dib_get_pitch(src); // Normalize rect if(rr<ll) { int tmp=ll; ll=rr; rr=tmp; } if(bb<tt) { int tmp=tt; tt=bb; bb=tmp; } // Clip if requested if(bClip) { if(ll<0) ll= 0; if(tt<0) tt= 0; if(rr>srcW) rr= srcW; if(bb>srcH) bb= srcH; } int dstW= rr-ll, dstH= bb-tt; CLDIB *dst= dib_alloc(dstW, dstH, srcB, NULL, true); if(dst==NULL) return NULL; dib_pal_cpy(dst, src); // set base src,dst pointers int dstP= dib_get_pitch(dst); BYTE *srcL= dib_get_img(src), *dstL= dib_get_img(dst); if(ll>=0) // set horz base srcL += ll*srcB>>3; else
/*! \param hdc Destination DC. * \param dX Destination left * \param dY Destination top (?) * \param dW Destination width * \param dH Destination height * \param dib DIB to blit * \param sX Source left * \param sY Source top (?) * \param sW Source width * \param sH Source height. * \param flags Blit flags. * \note Tested for top-down and bottom-up DIB's */ int dib_blit(HDC hdc, int dX, int dY, int dW, int dH, CLDIB *dib, int sX, int sY, int sW, int sH, DWORD flags) { if(!dib) return 0; int dibHa= dib_get_height(dib); SetStretchBltMode(hdc, COLORONCOLOR); return StretchDIBits( hdc, dX, dY+dH, dW, -dH, sX, dibHa-sY+1, sW, -sH, dib_get_img(dib), dib_get_info(dib), DIB_RGB_COLORS, flags); }
/*! Prepares the work dib for export, i.e. converts to the final bitdepth, compresses the data and fills in \a gr._gfxRec. */ bool grit_prep_gfx(GritRec *gr) { lprintf(LOG_STATUS, "Graphics preparation.\n"); int srcB= dib_get_bpp(gr->_dib); // should be 8 or 16 by now int srcP= dib_get_pitch(gr->_dib); int srcS= dib_get_size_img(gr->_dib); BYTE *srcD= dib_get_img(gr->_dib); int dstB= gr->gfxBpp; // # dst bytes, with # src pixels as 'width' int dstS= dib_align(srcS*8/srcB, dstB); dstS= ALIGN4(dstS); BYTE *dstD= (BYTE*)malloc(dstS); if(dstD == NULL) { lprintf(LOG_ERROR, " Can't allocate graphics data.\n"); return false; } // Convert to final bitdepth // NOTE: do not use dib_convert here, because of potential // problems with padding // NOTE: we're already at 8 or 16 bpp here, with 16 bpp already // accounted for. Only have to do 8->1,2,4 // TODO: offset if(srcB == 8 && srcB != dstB) { lprintf(LOG_STATUS, " Bitpacking: %d -> %d.\n", srcB, dstB); data_bit_pack(dstD, srcD, srcS, srcB, dstB, 0); } else memcpy(dstD, srcD, dstS); RECORD rec= { 1, dstS, dstD }; if( BYTE_ORDER == BIG_ENDIAN && gr->gfxBpp > 8 ) data_byte_rev(rec.data, rec.data, rec_size(&rec), gr->gfxBpp/8); // attach and compress graphics grit_compress(&rec, &rec, gr->gfxCompression); rec_alias(&gr->_gfxRec, &rec); lprintf(LOG_STATUS, "Graphics preparation complete.\n"); return true; }
//! Flip a bitmap horizontally (8, 16, 24, 32) bool dib_hflip(CLDIB *dib) { if(dib == NULL) return false; int dibW, dibH, dibB, dibP; dib_get_attr(dib, &dibW, &dibH, &dibB, &dibP); if(dibB < 8) return false; BYTE *dibL, *bufD; bufD= (BYTE*)malloc(dibP); if(bufD == NULL) return false; dibL= dib_get_img(dib); int ix, iy, ib, nb= dibB/8; for(iy=0; iy<dibH; iy++) { memcpy(bufD, dibL, dibP); //# TODO: 1, 4 bpp for(ix=0; ix<dibW; ix++) { for(ib=0; ib<nb; ib++) dibL[ix*nb+ib] = bufD[(dibW-1-ix)*nb+ib]; } dibL += dibP; } free(bufD); return true; }
// xxx_load(const char *fname, PDIBDATA *ppdib, IMG_FMT_INFO *pifi) bool CTgaFile::Load(const char *fpath) { FILE *fp= fopen(fpath, "rb"); CLDIB *dib= NULL; TGAHDR hdr; try { if(!fp) throw CImgFile::sMsgs[ERR_NO_FILE]; fread(&hdr, sizeof(TGAHDR), 1, fp); // ignore image desc (if any) fseek(fp, hdr.id_len, SEEK_CUR); int imgW, imgH, imgB, imgP; imgW= hdr.width; imgH= hdr.height; imgB= hdr.img_bpp; imgP= dib_align(imgW, imgB); // Set-up the full bitmap dib= dib_alloc(imgW, imgH, imgB, NULL, true); if(dib == NULL) throw CImgFile::sMsgs[ERR_ALLOC]; // === get color map === if(hdr.has_table) { if(!tga_read_pal(dib, &hdr, fp)) throw sMsgs[ERR_TGA_BADPAL]; } int ii; int tgaP= (imgW*imgB+7)/8; BYTE *imgD= dib_get_img(dib); switch(hdr.type) { case TGA_BW: case TGA_PAL: case TGA_true: for(ii=0; ii<imgH; ii++) fread(&imgD[ii*imgP], 1, tgaP, fp); break; case TGA_BW_RLE: case TGA_PAL_RLE: case TGA_true_RLE: tga_unrle(dib, &hdr, fp); break; default: throw sMsgs[ERR_TGA_VERSION]; } // TGA's are bottom-up by default, flip if necessary if(~hdr.img_desc & TGA_VFLIP) dib_vflip(dib); } // </try> catch(const char *msg) { SetMsg(msg); dib_free(dib); dib= NULL; } // cleanup 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; }
/*! \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; }
/*! 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; }
// 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; }
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; }
/*! \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; }
/*! \note Works at the byte level. If you have sub-8 bpps, you'll * still have to shift a bit. */ BYTE *dib_get_img_at(CLDIB *dib, int x, int y) { BYTE *dibD= dib_get_img(dib); return &dibD[x*dib_get_bpp(dib)/8 + y*dib_get_pitch(dib)]; }