geBitmap_Palette * createPaletteFast(const geBitmap_Info * Info,const void * Bits) { octNode * root; int nLeaves,minCount,gotLeaves; octNode ** leaves,**leavesPtr; uint8 palette[768]; int palEntries = 256; geBitmap_Palette * Pal; pushTSC(); // read the whole image into an octree // this is the only pass over the input plane MemPool_Reset(octNodePool); root = MemPool_GetHunk(octNodePool); assert(root); nLeaves = createOctTree(root,Info,Bits,GE_FALSE); leaves = geRam_AllocateClear(sizeof(octNode *)*nLeaves); assert(leaves); // gather leaves into a linear array // for speed we ignore leaves with a count lower than [x] gotLeaves = 0; for( minCount = 3; minCount >= 0 && gotLeaves < palEntries ; minCount-- ) { leavesPtr = leaves; gatherLeaves(root,&leavesPtr,minCount); gotLeaves = ((uint32)leavesPtr - (uint32)leaves)/sizeof(octNode *); } // sort the leaves by count qsort(leaves,gotLeaves,sizeof(octNode *),leafCompareCount); // read the sorted leaves in by count; we try to only read in leaves // that are farther than 'minDistance' from nodes already in the palette readLeavesToPal(leaves,gotLeaves,palette,palEntries); destroy(leaves); showPopTSC("createPalFast"); Pal = geBitmap_Palette_Create(GE_PIXELFORMAT_24BIT_RGB,palEntries); if ( ! Pal ) return NULL; if ( ! geBitmap_Palette_SetData(Pal,palette,GE_PIXELFORMAT_24BIT_RGB,palEntries) ) assert(0); return Pal; }
geBitmap_Palette * createPaletteGoodSub(const geBitmap_Info * Info,const void * Bits) { octNode * root; int nLeaves,i,gotLeaves,radixN; octNode ** leaves,**leavesPtr; octNode *leaf,*node; octNode *radix; uint8 palette[768],*palPtr; int palEntries = 256; geBitmap_Palette * Pal; pushTSC(); // read the whole image into an octree // this is the only pass over the input plane MemPool_Reset(octNodePool); root = MemPool_GetHunk(octNodePool); assert(root); nLeaves = createOctTree(root,Info,Bits,GE_TRUE); leaves = geRam_AllocateClear(sizeof(octNode *)*nLeaves); assert(leaves); computeOctRGBs(root); root->parent = root; computeCutCosts(root); root->parent = NULL; // gather leaves into a linear array // for speed we ignore leaves with a count lower than [x] leavesPtr = leaves; gatherLeavesCutting(root,&leavesPtr); gotLeaves = ((uint32)leavesPtr - (uint32)leaves)/sizeof(octNode *); // if gotLeaves < palEntries, just exit asap if ( gotLeaves < palEntries ) { readLeavesToPal(leaves,gotLeaves,palette,palEntries); goto done; } // sort the leaves by cutCost // radix sort instead of qsort radix = geRam_AllocateClear(sizeof(octNode)*RADIX_SIZE); assert(radix); for(i=0;i<RADIX_SIZE;i++) radix[i].next = radix[i].prev = &radix[i]; for(i=0;i<gotLeaves;i++) insertRadix(radix,leaves[i]); // keep cutting the tail radixN = 0; while(gotLeaves > palEntries) { while( (leaf = radix[radixN].next) == &(radix[radixN]) ) { radixN++; assert( radixN < RADIX_SIZE ); } // cut it leaf->prev->next = leaf->next; leaf->next->prev = leaf->prev; node = leaf->parent; assert(node); node->nKids --; // might turn its parent into a leaf; // if so, add it to the list // nKids no longer corresponds to the actual number of active kids if ( node->nKids == 0 ) insertRadix(radix,node); else gotLeaves--; } // read the sorted leaves in by count; we try to only read in leaves // that are farther than 'minDistance' from nodes already in the palette palPtr = palette; radixN = RADIX_SIZE-1; leaf = radix[radixN].prev; for(i=0;i<palEntries && radixN>0;i++) { *palPtr++ = leaf->R; *palPtr++ = leaf->G; *palPtr++ = leaf->B; leaf = leaf->prev; while ( leaf == &(radix[radixN]) ) { radixN --; if ( ! radixN ) break; leaf = radix[radixN].prev; } } destroy(radix); done: destroy(leaves); showPopTSC("createPalGood"); YUVb_to_RGBb_line(palette,palette,palEntries); Pal = geBitmap_Palette_Create(GE_PIXELFORMAT_24BIT_RGB,palEntries); if ( ! Pal ) return NULL; if ( ! geBitmap_Palette_SetData(Pal,palette,GE_PIXELFORMAT_24BIT_RGB,palEntries) ) assert(0); return Pal; }
void paletteOptimize(const geBitmap_Info * BmInfo,const void * Bits,uint8 *palette,int palEntries,int maxSamples) { palInfo *palInfo; int pal,R,G,B,A; uint32 mse,last_mse; uint8 *palPtr; uint8 savePalette[768]; int extraStepIndex,extraStepSize,extraStepSizeBytes=0,samples,totSamples; gePixelFormat_ColorGetter GetColor; const gePixelFormat_Operations * PixelOps; uint8 *ptr,*ptrEnd; int d; palOptInfo optInfo[256]; assert(palEntries <= 256); pushTSC(); // palette is 768 bytes R = palette[(palEntries-1)*3 + 0]; G = palette[(palEntries-1)*3 + 1]; B = palette[(palEntries-1)*3 + 2]; for(pal=palEntries;pal<256;pal++) { palette[pal*3 + 0] = R; palette[pal*3 + 1] = G; palette[pal*3 + 2] = B; } PixelOps = gePixelFormat_GetOperations(BmInfo->Format); GetColor = PixelOps->GetColor; ptrEnd = (uint8 *)Bits + BmInfo->Stride * BmInfo->Height * PixelOps->BytesPerPel; mse = ~(uint32)0; extraStepIndex = 0; extraStepSize = -1; totSamples = 0; if ( maxSamples <= 0 ) maxSamples = 0x0FFFFFFF; for(;;) { if ( extraStepSize != 0 ) { extraStepSize = ( stepTable[ extraStepIndex ] - 1 ); extraStepSizeBytes = extraStepSize * PixelOps->BytesPerPel; extraStepIndex++; } last_mse = mse; // <> this 'closestPal' is not great for this application // it's approximate & has a large initialization overhead // we should use the methods from the "Local K-Means" paper palInfo = closestPalInit(palette); if ( ! palInfo ) return; memclear(optInfo,sizeof(palOptInfo)*palEntries); mse = 0; samples =0; ptr = (uint8 *)Bits; while( ptr < ptrEnd ) { GetColor(&ptr,&R,&G,&B,&A); pal = closestPal(R,G,B,palInfo); if ( pal >= palEntries ) pal = palEntries-1; palPtr = palette + pal*3; d = R - (*palPtr++); mse += d*d; d = G - (*palPtr++); mse += d*d; d = B - (*palPtr ); mse += d*d; optInfo[pal].totR += R; optInfo[pal].totG += G; optInfo[pal].totB += B; optInfo[pal].count ++; ptr += extraStepSizeBytes; samples ++; } closestPalFree(palInfo); if ( samples == 0 ) continue; mse = (int)(((double)mse*256.0)/samples); totSamples += samples; if ( mse >= last_mse && extraStepSize == 0 ) { memcpy(palette,savePalette,768); mse = last_mse; break; } else if ( mse > last_mse ) { #if 0 // seems to slow convergence (!?) memcpy(palette,savePalette,768); mse = last_mse; #endif } else { memcpy(savePalette,palette,768); } Log_Printf("mse*256 = %7d , extrastep = %4d, samples = %7d\n",mse,extraStepSize,samples); if ( totSamples >= maxSamples ) break; for(pal=0,palPtr = palette; pal<palEntries ; pal++,palPtr += 3) { double fd; int diffR,diffG,diffB; if ( optInfo[pal].count == 0 ) continue; fd = 1.0 / optInfo[pal].count; diffR = (int)(optInfo[pal].totR * fd) - palPtr[0]; diffG = (int)(optInfo[pal].totG * fd) - palPtr[1]; diffB = (int)(optInfo[pal].totB * fd) - palPtr[2]; #if 1 #define DIV(diff) if ( diff < 0 ) diff = - ((-diff + 1)>>1); else if ( diff > 0 ) diff = ((diff + 1)>>1) DIV(diffR); DIV(diffG); DIV(diffB); #endif #if 0 // this helps sometimes, hurts sometimes diffR = GaussianRand(diffR,abs(diffR)); diffG = GaussianRand(diffG,abs(diffG)); diffB = GaussianRand(diffB,abs(diffB)); #endif palPtr[0] = minmax( palPtr[0]+diffR , 0,255); palPtr[1] = minmax( palPtr[1]+diffG , 0,255); palPtr[2] = minmax( palPtr[2]+diffB , 0,255); } if ( abs(mse - last_mse) < 50 && extraStepSize == 0 ) { break; } } showPopTSC("palOptimize"); }
//===================================================================================== // Vis_VisWorld //===================================================================================== geBoolean Vis_VisWorld(geEngine *Engine, geWorld *World, const geCamera *Camera, Frustum_Info *Fi) { uint8 *VisData; int32 k, i, Area; GFX_Node *GFXNodes; GFX_Leaf *GFXLeafs; Surf_SurfInfo *SurfInfo; uint8 *GFXVisData; int32 Leaf, Cluster; GFX_Model *GFXModels; int32 *GFXLeafFaces; GFX_Cluster *GFXClusters; GBSP_BSPData *BSPData; geWorld_Model *Models; const geVec3d *Pos; GFX_Leaf *pLeaf; #ifdef _TSC pushTSC(); #endif Pos = geCamera_GetVisPov(Camera); BSPData = &World->CurrentBSP->BSPData; GFXNodes = BSPData->GFXNodes; GFXLeafs = BSPData->GFXLeafs; GFXClusters = BSPData->GFXClusters; GFXVisData = BSPData->GFXVisData; GFXModels = BSPData->GFXModels; GFXLeafFaces = BSPData->GFXLeafFaces; SurfInfo = World->CurrentBSP->SurfInfo; Leaf = Plane_FindLeaf(World, GFXModels[0].RootNode[0], Pos); Area = GFXLeafs[Leaf].Area; // Check to see if we cen get rid of most of the work load by seeing if the leaf has not changed... if (World->CurrentLeaf == Leaf && !World->ForceVis) goto LeafDidNotChange; World->ForceVis = GE_FALSE; // Reset force vis flag World->CurrentLeaf = Leaf; World->CurFrameStatic++; // Make all old vis info obsolete Cluster = GFXLeafs[Leaf].Cluster; if (Cluster == -1 || GFXClusters[Cluster].VisOfs == -1) { World->VisInfo = GE_FALSE; return GE_TRUE; } if (Area) Vis_FloodAreas_r(World, Area); World->VisInfo = GE_TRUE; VisData = &GFXVisData[GFXClusters[Cluster].VisOfs]; // Mark all visible clusters for (i=0; i<GFXModels[0].NumClusters; i++) { if (VisData[i>>3] & (1<<(i&7)) ) World->CurrentBSP->ClusterVisFrame[i] = World->CurFrameStatic; } pLeaf = &GFXLeafs[GFXModels[0].FirstLeaf]; // Go through and find all visible leafs based on the visible clusters the leafs are in for (i=0; i< GFXModels[0].NumLeafs; i++, pLeaf++) { int32 *pFace; Cluster = pLeaf->Cluster; if (Cluster == -1) // No cluster info for this leaf (must be solid) continue; // If the cluster is not visible, then the leaf is not visible if (World->CurrentBSP->ClusterVisFrame[Cluster] != World->CurFrameStatic) continue; // If the area is not visible, then the leaf is not visible if (World->CurrentBSP->AreaVisFrame[pLeaf->Area] != World->CurFrameStatic) continue; // Mark all visible nodes by bubbling up the tree from the leaf MarkVisibleParents(World, i); // Mark the leafs vis frame to worlds current frame World->CurrentBSP->LeafData[i].VisFrame = World->CurFrameStatic; pFace = &GFXLeafFaces[pLeaf->FirstFace]; // Go ahead and vis surfaces here... for (k=0; k< pLeaf->NumFaces; k++) { // Update each surface infos visframe thats touches each visible leaf SurfInfo[*pFace++].VisFrame = World->CurFrameStatic; } } LeafDidNotChange: // The world is always visible as a model World->CurrentBSP->Models[0].VisFrame = World->CurFrameDynamic; Models = &World->CurrentBSP->Models[1]; // Do models, skipping world models (it's always visible) for (i=1; i< BSPData->NumGFXModels; i++, Models++) { #if 0 int32 Cluster; // First, lets cheat, and see if the center is in a valid location to test for vis Leaf = Plane_FindLeaf(World, GFXModels[0].RootNode[0], &Models->Pivot); Cluster = GFXLeafs[Leaf].Cluster; if (Cluster >= 0 && GFXClusters[Cluster].VisOfs >= 0) // If there is vis data for this leaf { if (World->CurrentBSP->LeafData[Leaf].VisFrame == World->CurFrameStatic) Models->VisFrame = World->CurFrameDynamic; else { GFX_Model *pModel; pModel = &BSPData->GFXModels[i]; if (pModel->Areas[0] == Area || pModel->Areas[1] == Area && World->CurrentBSP->ClusterVisFrame[Cluster] == World->CurFrameStatic) Models->VisFrame = World->CurFrameDynamic; } } else if (ModelVisible(World, Models)) Models->VisFrame = World->CurFrameDynamic; #else if (ModelVisible(World, Models)) Models->VisFrame = World->CurFrameDynamic; #endif Models->ChangedFlags &= ~MODEL_CHANGED_XFORM; } VisFog(Engine, World, Camera, Fi, Area); #ifdef _TSC showPopTSC("Vis_VisWorld"); #endif return GE_TRUE; }