/*! \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); }
BOOL CxpGbaDlg::OnInitDialog() { ASSERT(mpDib); int nclrs= dib_get_nclrs(mpDib); if(nclrs > 0 && mPalStart+mPalCount>nclrs) { mPalStart= 0; mPalCount= nclrs; } if(moAreaSize != 0) { mAreaLeft= 0; mAreaWidth= dib_get_width(mpDib); mAreaTop=0; mAreaHeight= dib_get_height(mpDib); } if(mbSymChk == FALSE) mSymName= mDstTitle; CDialog::OnInitDialog(); OnPalChk(); OnGfxChk(); OnMapChk(); UpdateObj(); UpdateMap(); UpdateArea(); GetDlgItem(IDC_VAR_NAME)->EnableWindow(mbSymChk); //UpdateSummary(); return TRUE; }
//! Initialize palette and gfx-related functions from the DIB. bool grit_init_from_dib(GritRec *gr) { if(gr == NULL) return false; CLDIB *dib= gr->srcDib; if(dib == NULL) { lprintf(LOG_ERROR, "No bitmap to initialize GritRec from.\n"); return false; } int nclrs = dib_get_nclrs(dib); gr->palEnd= ( nclrs ? nclrs : 256 ); if(dib_get_bpp(dib) > 8) gr->gfxBpp= 16; else gr->gfxBpp= dib_get_bpp(dib); gr->areaRight= dib_get_width(dib); gr->areaBottom=dib_get_height(dib); return true; }
/*! \param dib Bitmap to retile. \param dstW Destination width (= tile width). \param tileH Tile height. \param tileN Number of tiles. \notes If dstW<srcW, it will convert from matrix to array; if dstW>srcW, the transformation is from array to matrix. */ bool dib_redim(CLDIB *dib, int dstW, int tileH, int tileN) { // Nothing to do if(dib && dib_get_width(dib)==dstW) return true; CLDIB *tmp= dib_redim_copy(dib, dstW, tileH, tileN); return dib_mov(dib, tmp); }
// 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)> }
/*! This only runs for tiled images and can have up to three stages. First, Rearrange into screen-blocks (option -mLs); then into metatiles (-Mw, -Mh), then base-tiles. */ bool grit_prep_tiles(GritRec *gr) { lprintf(LOG_STATUS, "Tile preparation.\n"); // Main sizes int imgW= dib_get_width(gr->_dib), imgH= dib_get_height(gr->_dib); int tileW= gr->tileWidth, tileH= gr->tileHeight; int metaW= gr->metaWidth, metaH= gr->metaHeight; // Tiling sizes for stages int frameW=tileW*metaW, frameH=tileH*metaH; // For meta tiling bool bMeta= gr->isMetaTiled(); bool bTile= tileW*tileH > 1; // Change things for column-major ordering if(gr->bColMajor) { int lastH= imgH, tmp; if(bMeta) SWAP3(frameH, lastH, tmp); if(bTile) SWAP3(tileH, lastH, tmp); } // Meta redim if(bMeta) { lprintf(LOG_STATUS, " tiling to %dx%d tiles.\n", frameW, frameH); if(!dib_redim(gr->_dib, frameW, frameH, 0)) { lprintf(LOG_ERROR, " meta-tiling failed.\n"); return false; } } // Tile redim if(bTile) { lprintf(LOG_STATUS, " tiling to %dx%d tiles.\n", tileW, tileH); if(!dib_redim(gr->_dib, tileW, tileH, 0)) { lprintf(LOG_ERROR, " tiling failed.\n"); return false; } } lprintf(LOG_STATUS, "Tile preparation complete.\n"); return true; }
//! Get the main attributes of a DIB bool dib_get_attr(CLDIB *dib, int *width, int *height, int *bpp, int *pitch) { if(dib == NULL) return false; if(width) *width= dib_get_width(dib); if(height) *height= dib_get_height(dib); if(bpp) *bpp= dib_get_bpp(dib); if(pitch) *pitch= dib_get_pitch(dib); return true; }
void CWingritView::OnDraw(CDC* pdc) { CWingritDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); CLDIB *dib= pDoc->GetDib(); if(dib != NULL) { int ww= dib_get_width(dib), hh= dib_get_height(dib); dib_blit(pdc->GetSafeHdc(), mptOrg.x, mptOrg.y, mZoom*ww>>8, mZoom*hh>>8, dib, 0, 0, ww, hh, SRCCOPY); }
/*! 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
void CxpGbaDlg::OnOK() { CString logpath; GetRootDir(logpath); logpath += "\\gxplog.txt"; FILE *fp= fopen(logpath, "w"); if(fp) log_init(LOG_STATUS, fp); if(Validate() == FALSE) { if(fp) { log_exit(); fclose(fp); } return; } GritShared *grs= mgr->shared; // Read external tileset if required // PONDER: move inside grit_run? if(mgr->gfxIsShared) { // read file grs->dib= dib_load(grs->tilePath, NULL); if(grs->dib) { if( dib_get_bpp(grs->dib) != 8 ) { lprintf(LOG_WARNING, " External tileset must be 8bpp. Converting.\n"); dib_convert(grs->dib, 8, 0); } if( dib_get_width(grs->dib) != 8 ) { lprintf(LOG_WARNING, " External tileset not tiled yet. Doing so now.\n"); dib_redim(grs->dib, 8, 8, 0); } } else lprintf(LOG_WARNING, " Couldn't load external tileset from \n %s.\n", grs->tilePath); // PONDER: reduce here too? } int res= grit_run(mgr); // Save external tileset if required // PONDER: move inside grit_run? if(mgr->gfxIsShared) { if( !dib_save(grs->dib, grs->tilePath, NULL) ) lprintf(LOG_WARNING, " Couldn't save external tileset to \n %s.\n", grs->tilePath); } if(fp) { log_exit(); fclose(fp); } if(res == TRUE) { // PONDER: ask to see log? res= MessageBox("Export succeeded. Show log?", NULL, MB_ICONINFORMATION | MB_YESNO | MB_DEFBUTTON2); if(res == IDYES) { CLogDlg dlg; dlg.SetTextFromFile(logpath); dlg.DoModal(); } CDialog::OnOK(); } else { // PONDER: ask to see log? res= MessageBox("Export failed. Show Log?", NULL, MB_ICONEXCLAMATION | MB_YESNO); if(res == IDYES) { CLogDlg dlg; dlg.SetTextFromFile(logpath); dlg.DoModal(); } } }
BOOL CxpGbaDlg::UpdateGritRec(BOOL b2gr) { DWORD dw; char str[MAXPATHLEN]; // TODO: al/gfx trans ; img modes ; tileset if(b2gr) // data -> gr { UpdateData(TRUE); SetDstPath(mDstPath); // clear previous gr data grit_clear(mgr); grs_clear(mgr->shared); mgr->bExport= true; // Yes, yes, very wasteful mgr->srcDib= dib_clone(mpDib); mgr->bHeader= mbHeader==TRUE; mgr->bAppend= mbAppend==TRUE; mgr->fileType= mFileType; // TODO check whether this is all OK!! if(mbSymChk) mgr->symName= strdup(mSymName); else mgr->symName= strdup(mDstTitle); // make sure it's a valid C varname str_fix_ident(mgr->symName, mgr->symName, strlen(mgr->symName)); sprintf(str, "%s/%s.%s", mDstDir, mDstTitle, c_fileTypes[mFileType]); mgr->dstPath= strdup(str); // --- palette --- if(mbPal) { mgr->palProcMode= GRIT_EXPORT; mgr->palStart= mPalStart; mgr->palEnd= mPalStart+mPalCount; if(mgr->palEnd > 256) mgr->palEnd= 256; if(mPalTrans != 0) { mgr->palHasAlpha= true; mgr->palAlphaId= mPalTrans; } } // --- image --- // NOTE: we should probably always process the image, // just not necessarily export if(1) { if(mbGfx) mgr->gfxProcMode= GRIT_EXPORT; switch(mGfxMode) { case GFX_MODE_TILE: mgr->gfxMode= GRIT_GFX_TILE; break; case GFX_MODE_BMP: mgr->gfxMode= GRIT_GFX_BMP; break; case GFX_MODE_BMP_A: mgr->gfxMode= GRIT_GFX_BMP_A; break; case GFX_MODE_BMP_T: mgr->gfxMode= GRIT_GFX_BMP; // Empty color && non-zero pal-trans -> use pal-trans // Empty color otherwise: use FF00FF // non-empty: use non-empty dw= strlen(mGfxTransStr); if( dw != 0 || !mgr->palHasAlpha ) { if(dw==0) mGfxTransStr= "FF00FF"; mgr->gfxAlphaColor= str2rgb(mGfxTransStr); mgr->gfxHasAlpha= true; } break; } mgr->gfxOffset= 0; mgr->gfxBpp= 1<<mGfxBpp; mgr->gfxCompression= mGfxCprs; } // --- map --- if(mbMap) { mgr->mapProcMode= GRIT_EXPORT; mgr->mapCompression= mMapCprs; if(mbMapRdx) { mgr->mapRedux |= GRIT_RDX_TILE; if(mbMapRdxFlip) mgr->mapRedux |= GRIT_RDX_FLIP; if(mbMapRdxPal) mgr->mapRedux |= GRIT_RDX_PBANK; } if(mbMetaPal) mgr->mapRedux |= GRIT_META_PAL; mgr->mapLayout= moMapFormat; if(mgr->mapLayout == GRIT_MAP_AFFINE) mgr->msFormat= c_mapselGbaAffine; else mgr->msFormat= c_mapselGbaText; mgr->msFormat.base= mMapOffset; // Add external tileset stuff if(mbTileset) { if(mTilesetPath.IsEmpty()) mTilesetPath= mDstDir + mDstTitle + "tiles.bmp"; mgr->shared->tilePath= strdup(mTilesetPath); mgr->gfxIsShared= true; } } if(mbObjCustom) { mgr->metaWidth= mObjHorz; mgr->metaHeight= mObjVert; } else { dw= moObjShape*4 + moObjSize; mgr->metaWidth= cObjSizes[dw][0]; mgr->metaHeight= cObjSizes[dw][1]; } // area dw= moAreaSize+IDC_AREA_CSM; if(dw == IDC_AREA_CSM) { mgr->areaLeft = mAreaLeft; mgr->areaTop = mAreaTop; mgr->areaRight = mAreaLeft+mAreaWidth; mgr->areaBottom= mAreaTop+mAreaHeight; } else { mgr->areaLeft = 0; mgr->areaTop = 0; mgr->areaRight = dib_get_width(mpDib); mgr->areaBottom= dib_get_height(mpDib); } mgr->gfxDataType= moVarChunk; mgr->mapDataType= moVarChunk; mgr->palDataType= moVarChunk; // RIFF overrides if(IsRiffed()) { mgr->bRiff= true; if(mgr->gfxCompression==GRIT_CPRS_OFF) mgr->gfxCompression= GRIT_CPRS_HEADER; if(mgr->mapCompression==GRIT_CPRS_OFF) mgr->mapCompression= GRIT_CPRS_HEADER; if(mgr->palCompression==GRIT_CPRS_OFF) mgr->palCompression= GRIT_CPRS_HEADER; } } else // gr -> data { // --- file --- mbHeader = mgr->bHeader; mbAppend = mgr->bAppend; // --- palette --- mPalStart= mgr->palStart; mPalCount= mgr->palEnd-mgr->palStart; mPalTrans= mgr->palAlphaId; // --- image --- if(mgr->gfxHasAlpha) { RGBQUAD *rgb= &mgr->gfxAlphaColor; mGfxMode= GFX_MODE_BMP_T; mGfxTransStr.Format("%02X%02X%02X", rgb->rgbRed, rgb->rgbGreen, rgb->rgbBlue); } else if(mgr->gfxMode == GRIT_GFX_BMP_A) mGfxMode= GFX_MODE_BMP_T; else if(mgr->gfxMode == GRIT_GFX_BMP) mGfxMode= GFX_MODE_BMP; else mGfxMode= GFX_MODE_TILE; //mgr->gfx_ofs; //mgr->gfxBpp; // map if(mbMap) { moMapFormat= mgr->mapLayout; if(mgr->mapRedux & GRIT_RDX_TILE) { mbMapRdx= TRUE; mbMapRdxFlip= mbMapRdxPal= FALSE; if(mgr->mapRedux & GRIT_RDX_FLIP) mbMapRdxFlip= TRUE; if(mgr->mapRedux & GRIT_RDX_PBANK) mbMapRdxPal= TRUE; } mMapOffset= mgr->msFormat.base; } // meta if(mbObjCustom) { mObjHorz= mgr->metaWidth; mObjVert= mgr->metaHeight; } // area mAreaLeft= mgr->areaLeft; mAreaTop= mgr->areaTop; mAreaWidth= mgr->areaRight-mgr->areaLeft; mAreaHeight= mgr->areaBottom-mgr->areaTop; UpdateData(FALSE); } return TRUE; }
// TODO: reformat for new options void CxpGbaDlg::UpdateSummary() { // anti-bounce code (trust me, you need this) if(!mbBusy) UpdateData(TRUE); DWORD dw; CString sPal, sGfx, sArea, sMeta, sMap, sFiles; CString str; BOOL bPal, bGfx, bMeta, bMap, bArea; ASSERT(moMapFormat<3); bPal= mbPal; bGfx= mbGfx; bArea= mbGfx || mbMap; bMeta= GetDlgItem(IDC_OBJ_CUSTOM)->IsWindowEnabled(); bMap= mbMap && IsTiled(); // TODO: Validation!!! // --- image --- if(bGfx) { // TODO: transparent color GetDlgItem(IDC_IMG_MODE)->GetWindowText(str); sGfx= "gfx: " + str; if(mGfxMode == GFX_MODE_BMP_T) { RGBQUAD rgb= str2rgb(mGfxTransStr); str.Format(" (%d, %d, %d)", rgb.rgbRed, rgb.rgbGreen, rgb.rgbBlue); sGfx += str; } str.Format(", %dbpp, cprs: ", (1<<mGfxBpp)); sGfx += str; GetDlgItem(IDC_IMG_CPRS)->GetWindowText(str); sGfx += str; } else sGfx= "gfx: -"; // --- area --- if(bArea) { dw= moAreaSize+IDC_AREA_CSM; if(dw == IDC_AREA_CSM) { sArea.Format("area: (%d,%d)-(%d,%d) [%d, %d]", mAreaLeft, mAreaTop, mAreaLeft+mAreaWidth, mAreaTop+mAreaHeight, mAreaWidth, mAreaHeight); } else if(dw == IDC_AREA_IMG) { sArea.Format("area: (%d,%d)-(%d,%d) [%d, %d]", 0, 0, dib_get_width(mpDib), dib_get_height(mpDib), dib_get_width(mpDib), dib_get_height(mpDib)); } } else sArea="area: -"; // --- object --- if(bMeta) { SIZE sz; if(mbObjCustom) { sz.cx= mObjHorz; sz.cy= mObjVert; } else { sz.cx= cObjSizes[moObjShape*4 + moObjSize][0]; sz.cy= cObjSizes[moObjShape*4 + moObjSize][1]; } sMeta.Format("meta: %dx%d tiles (%dx%d px)", sz.cx, sz.cy, sz.cx<<3, sz.cy<<3); } else sMeta= "obj: -"; // --- map --- if(bMap) { dw= moMapFormat+IDC_MAP_FLAT; switch(dw) { case IDC_MAP_FLAT: str= "flat"; break; case IDC_MAP_SBB: str= "in SBBs"; break; case IDC_MAP_AFF: str= "affine"; break; } sMap.Format("map: %s, +%d ", str, mMapOffset); if(IsRdx()) { sMap += "reduced [t"; if(mbMapRdxFlip) sMap += "f"; if(mbMapRdxPal) sMap += "p"; sMap += "]"; } } else sMap= "map: -"; // --- palette --- if(bPal) { if(mPalStart+mPalCount>256) mPalCount= 256-mPalStart; //GetDlgItem(IDC_START)->GetWindowText(str); sPal.Format("pal: %d-%d [%d]", mPalStart, mPalStart+mPalCount, mPalCount); // TODO: palette transparency index } else sPal= "pal: -"; // --- files --- // TODO: add tileset file switch(mFileType) { case FMT_C: // C (+h) str= mDstTitle + ".c "; break; case FMT_GAS: // assembly (+h) str= mDstTitle + ".s "; break; case -1: // COFF (+h) str= mDstTitle + ".o "; break; case FMT_BIN: str.Empty(); if(bPal) str += mDstTitle + ".pal.bin "; if(bGfx) str += mDstTitle + ".img.bin "; if(bMap) str += mDstTitle + ".map.bin "; case FMT_GBFS: str= mDstTitle + ".gbfs "; } sFiles= "files: "; if(mbHeader) sFiles += mDstTitle + ".h "; sFiles += str; mSummary.Format("%s\r\n%s\r\n%s\r\n%s\r\n%s\r\n%s", sGfx, sArea, sMeta, sMap, sPal, sFiles); GetDlgItem(IDC_SUMMARY)->SetWindowText(mSummary); }
bool grit_validate_area(GritRec *gr) { int tmp; //# FIXME: consider other proc-modes? // area size if(gr->gfxProcMode != GRIT_EXCLUDE || gr->mapProcMode != GRIT_EXCLUDE) { int al, at, ar, ab; // area int aw, ah; // area size int blockW, blockH; // block size al= gr->areaLeft; at= gr->areaTop; ar= gr->areaRight; ab= gr->areaBottom; // Normalize if(al > ar) { lprintf(LOG_WARNING, " Area: left (%d) > right (%d). Swapping.\n", gr->areaLeft, gr->areaRight); SWAP3(al, ar, tmp); } if(at > ab) { lprintf(LOG_WARNING, " Area: top (%d) > bottom (%d). Swapping.\n", gr->areaTop, gr->areaBottom); SWAP3(at, ab, tmp); } if(ar == 0) ar= dib_get_width(gr->srcDib); if(ab == 0) ab= dib_get_height(gr->srcDib); aw= ar-al; ah= ab-at; // metatile size (in px) // area MUST fit at least one metatile // Tiles: either // - one meta tile // - one screenblock if in SBB layout // PONDER: non-integral metatile/sbb ?? if(gr->metaWidth < 1) gr->metaWidth = 1; if(gr->metaHeight < 1) gr->metaHeight= 1; if(gr->gfxMode == GRIT_GFX_TILE) { if(gr->tileWidth < 4) gr->tileWidth = 8; if(gr->tileHeight < 4) gr->tileHeight= 8; } else { if(gr->tileWidth < 1) gr->tileWidth = 1; if(gr->tileHeight < 1) gr->tileHeight= 1; } // Normally, the blocks are tw*mw, th*mh in size. // But for SBB-aligned maps it's a little different. if(!(gr->mapProcMode==GRIT_EXPORT && gr->mapLayout==GRIT_MAP_REG)) { blockW= gr->mtileWidth(); blockH= gr->mtileHeight(); } else blockW= blockH= 256; if(aw%blockW != 0) { lprintf(LOG_WARNING, "Non-integer tiling in width (%d vs %d)\n", aw, blockW); aw= align(aw, blockW); } if(ah%blockH != 0) { lprintf(LOG_WARNING, "Non-integer tiling in height (%d vs %d)\n", ah, blockH); ah= align(ah, blockH); } // area must be multiple of metas (rounding up here) //aw= (aw+blw-1)/blw * blw; //ah= (ah+blh-1)/blh * blh; // PONDER: but what about the image size? // *shrug* what about it? ar= al+aw; ab= at+ah; gr->areaLeft= al; gr->areaTop= at; gr->areaRight= ar; gr->areaBottom= ab; } return true; }
/*! Does map creation and layout, tileset reduction and map compression. Updates \a gr._dib with the new tileset, and fills in \a gr._mapRec and \a gr._metaRec. \note The work bitmap must be 8 bpp here, and already rearranged to a tile strip, which are the results of \c grit_prep_work_dib() and \c grit_prep_tiles(), respectively. */ bool grit_prep_map(GritRec *gr) { if(dib_get_bpp(gr->_dib) < 8) { lprintf(LOG_ERROR, " Can't map for bpp<8.\n"); return false; } CLDIB *workDib= gr->_dib; // --- if SBB-mode, tile to 256x256. --- if(gr->mapLayout == GRIT_MAP_REG) { lprintf(LOG_STATUS, " tiling to Screenblock size (256x256p).\n"); int blockW= 256, blockH= 256; if(gr->bColMajor) { blockW= dib_get_width(workDib); blockH= dib_get_height(workDib); dib_redim(workDib, 256, blockH, 0); } if(!dib_redim(workDib, blockW, blockH, 0)) { lprintf(LOG_ERROR, " SBB tiling failed.\n"); return false; } } ETmapFlags flags; Tilemap *metaMap= NULL, *map= NULL; RECORD metaRec= { 0, 0, NULL }, mapRec= { 0, 0, NULL }; MapselFormat mf; CLDIB *extDib= NULL; int tileN= 0; uint extW= 0, extH= 0, tileW= gr->tileWidth, tileH= gr->tileHeight; uint mtileW= gr->mtileWidth(), mtileH= gr->mtileHeight(); if(gr->gfxIsShared) { extDib= gr->shared->dib; extW= extDib ? dib_get_width(extDib) : 0; extH= extDib ? dib_get_height(extDib) : 0; } // --- If metatiled, convert to metatiles. --- if(gr->isMetaTiled()) { lprintf(LOG_STATUS, " Performing metatile reduction: tiles%s%s\n", (gr->mapRedux & GRIT_META_PAL ? ", palette" : "") ); flags = TMAP_DEFAULT; if(gr->mapRedux & GRIT_META_PAL) flags |= TMAP_PBANK; if(gr->bColMajor) flags |= TMAP_COLMAJOR; metaMap= tmap_alloc(); if(extW == mtileW) { lprintf(LOG_STATUS, " Using external metatileset.\n"); tmap_init_from_dib(metaMap, workDib, mtileW, mtileH, flags, extDib); } else tmap_init_from_dib(metaMap, workDib, mtileW, mtileH, flags, NULL); mf= c_mapselGbaText; tileN= tmap_get_tilecount(metaMap); if(tileN >= (1<<mf.idLen)) lprintf(LOG_WARNING, " Number of metatiles (%d) exceeds field limit (%d).\n", tileN, 1<<mf.idLen); tmap_pack(metaMap, &metaRec, &mf); if( BYTE_ORDER == BIG_ENDIAN && mf.bitDepth > 8 ) data_byte_rev(metaRec.data, metaRec.data, rec_size(&metaRec), mf.bitDepth/8); // Make temp copy for base-tiling and try to avoid aliasing pointers. // Gawd, I hate manual mem-mgt >_<. dib_free(workDib); if(gr->bColMajor) workDib= dib_redim_copy(metaMap->tiles, tileN*mtileW, mtileH, 0); else workDib= dib_clone(metaMap->tiles); } // ---Convert to base tiles. --- flags = 0; if(gr->mapRedux & GRIT_RDX_TILE) flags |= TMAP_TILE; if(gr->mapRedux & GRIT_RDX_FLIP) flags |= TMAP_FLIP; if(gr->mapRedux & GRIT_RDX_PBANK) flags |= TMAP_PBANK; if(gr->bColMajor) flags |= TMAP_COLMAJOR; lprintf(LOG_STATUS, " Performing tile reduction: %s%s%s\n", (flags & TMAP_TILE ? "unique tiles; " : ""), (flags & TMAP_FLIP ? "flip; " : ""), (flags & TMAP_PBANK ? "palswap; " : "")); map= tmap_alloc(); if(extW == tileW) { lprintf(LOG_STATUS, " Using external tileset.\n"); tmap_init_from_dib(map, workDib, tileW, tileH, flags, extDib); } else tmap_init_from_dib(map, workDib, tileW, tileH, flags, NULL); // --- Pack/Reformat and compress --- //# TODO: allow custom mapsel format. mf= gr->msFormat; tileN= tmap_get_tilecount(metaMap); if(tileN >= (1<<mf.idLen)) lprintf(LOG_WARNING, " Number of tiles (%d) exceeds field limit (%d).\n", tileN, 1<<mf.idLen); tmap_pack(map, &mapRec, &mf); if( BYTE_ORDER == BIG_ENDIAN && mf.bitDepth > 8 ) data_byte_rev(mapRec.data, mapRec.data, rec_size(&mapRec), mf.bitDepth/8); grit_compress(&mapRec, &mapRec, gr->mapCompression); // --- Cleanup --- // Make extra copy for external tile dib. if(gr->gfxIsShared) { dib_free(gr->shared->dib); // Use metatileset for external, unless the old external was a // base tileset. if(gr->isMetaTiled() && extW != tileW) gr->shared->dib= dib_clone(metaMap->tiles); else gr->shared->dib= dib_clone(map->tiles); } // Attach tileset for later processing. gr->_dib= tmap_detach_tiles(map); rec_alias(&gr->_mapRec, &mapRec); rec_alias(&gr->_metaRec, &metaRec); tmap_free(map); tmap_free(metaMap); lprintf(LOG_STATUS, "Map preparation complete.\n"); return true; }
/*! 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; }
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; }