int main(int argc, char* argv[]) { int r, g, b; cmsUInt8Number RGB[3], RGB_OUT[3]; cmsHTRANSFORM xform; cmsHPROFILE hProfile; double err, SumX=0, SumX2=0, Peak = 0, n = 0; if (argc != 2) { printf("roundtrip <RGB icc profile>\n"); return 1; } hProfile = cmsOpenProfileFromFile(argv[1], "r"); xform = cmsCreateTransform(hProfile,TYPE_RGB_8, hProfile, TYPE_RGB_8, INTENT_RELATIVE_COLORIMETRIC, cmsFLAGS_NOOPTIMIZE); for (r=0; r< 256; r++) { printf("%d \r", r); for (g=0; g < 256; g++) { for (b=0; b < 256; b++) { RGB[0] = r; RGB[1] = g; RGB[2] = b; cmsDoTransform(xform, RGB, RGB_OUT, 1); err = VecDist(RGB, RGB_OUT); SumX += err; SumX2 += err * err; n += 1.0; if (err > Peak) Peak = err; } } } printf("Average %g\n", SumX / n); printf("Max %g\n", Peak); printf("Std %g\n", sqrt((n*SumX2 - SumX * SumX) / (n*(n-1)))); cmsCloseProfile(hProfile); cmsDeleteTransform(xform); return 0; }
void QM_Subdivide( QM_Model *m, float maxShooterQuadEdgeLength, float maxGathererQuadEdgeLength ) // Subdivide the original quads in the model to smaller // shooter quads and even-smaller gatherer quads. // Each shooter quad cannot have edge longer than maxShooterQuadEdgeLength, and // each gatherer quad cannot have edge longer than maxGathererQuadEdgeLength. { if ( m == NULL || m->numSurfaces <= 0 ) return; // Subdivide original quads to get shooter quads. int modelTotalShooters = 0; // This will contain the total number of shooter quads in model. for ( int s = 0; s < m->numSurfaces; s++ ) { QM_Surface *surface = &(m->surfaces[s]); // Find longest edge length of all original quads in the current surface. float maxEdgeLen = 0.0f; for ( int q = 0; q < surface->numOrigQuads; q++ ) { QM_OrigQuad *origQuad = &(surface->origQuads[q]); float edgeLen; edgeLen = VecDist( origQuad->v[0], origQuad->v[1] ); if ( edgeLen > maxEdgeLen ) maxEdgeLen = edgeLen; edgeLen = VecDist( origQuad->v[1], origQuad->v[2] ); if ( edgeLen > maxEdgeLen ) maxEdgeLen = edgeLen; edgeLen = VecDist( origQuad->v[2], origQuad->v[3] ); if ( edgeLen > maxEdgeLen ) maxEdgeLen = edgeLen; edgeLen = VecDist( origQuad->v[3], origQuad->v[0] ); if ( edgeLen > maxEdgeLen ) maxEdgeLen = edgeLen; } // Compute how many regular segments to divide the longest edge into so that // every resulting segment is not longer than maxShooterQuadEdgeLength. int numSegments = (int) ceil( maxEdgeLen / maxShooterQuadEdgeLength ); // Each original quad on the current surface is going to be subdivided into // (numSegments*numSegments) shooter quads. // Therefore, the total number of shooter quads for this surface is... surface->numShooterQuads = surface->numOrigQuads * numSegments * numSegments; surface->shooters = (QM_ShooterQuad *) CheckedMalloc( sizeof(QM_ShooterQuad) * surface->numShooterQuads ); int surfShootersCount = 0; // This will contain the number of shooters in this surface. for ( int q = 0; q < surface->numOrigQuads; q++ ) { QM_OrigQuad *origQuad = &(surface->origQuads[q]); for ( int y = 0; y < numSegments; y++ ) for ( int x = 0; x < numSegments; x++ ) { float newv[4][3]; QuadBilinearInterpolate( newv[0], (float) x / numSegments, (float) y / numSegments, origQuad->v ); QuadBilinearInterpolate( newv[1], (float) (x+1) / numSegments, (float) y / numSegments, origQuad->v ); QuadBilinearInterpolate( newv[2], (float) (x+1) / numSegments, (float) (y+1) / numSegments, origQuad->v ); QuadBilinearInterpolate( newv[3], (float) x / numSegments, (float) (y+1) / numSegments, origQuad->v ); QM_ShooterQuad *shooterQuad = &(surface->shooters[ surfShootersCount ]); for ( int i = 0; i < 4; i++ ) CopyArray3( shooterQuad->v[i], newv[i] ); QuadCentroid( shooterQuad->centroid, shooterQuad->v ); CopyArray3( shooterQuad->normal, origQuad->normal ); shooterQuad->area = QuadArea( shooterQuad->v ); // Initialize the unshot power of the shooter quad. shooterQuad->unshotPower[0] = surface->emission[0] * shooterQuad->area; shooterQuad->unshotPower[1] = surface->emission[1] * shooterQuad->area; shooterQuad->unshotPower[2] = surface->emission[2] * shooterQuad->area; shooterQuad->surface = surface; surfShootersCount++; modelTotalShooters++; } } } // Build an array of pointers to all the shooters in the model. m->totalShooters = modelTotalShooters; m->shooters = (QM_ShooterQuad **) CheckedMalloc( sizeof(QM_ShooterQuad *) * modelTotalShooters ); int modelTotalShootersCount = 0; for ( int s = 0; s < m->numSurfaces; s++ ) for ( int q = 0; q < m->surfaces[s].numShooterQuads; q++ ) { m->shooters[ modelTotalShootersCount ] = &(m->surfaces[s].shooters[q]); modelTotalShootersCount++; } // Subdivide shooter quads to get gatherer quads. int modelTotalGatherers = 0; // This will contain the total number of gatherers quads in model. for ( int s = 0; s < m->numSurfaces; s++ ) { QM_Surface *surface = &(m->surfaces[s]); // Find longest edge length of all shooter quads in the current surface. float maxEdgeLen = 0.0f; for ( int q = 0; q < surface->numShooterQuads; q++ ) { QM_ShooterQuad *shooterQuad = &(surface->shooters[q]); float edgeLen; edgeLen = VecDist( shooterQuad->v[0], shooterQuad->v[1] ); if ( edgeLen > maxEdgeLen ) maxEdgeLen = edgeLen; edgeLen = VecDist( shooterQuad->v[1], shooterQuad->v[2] ); if ( edgeLen > maxEdgeLen ) maxEdgeLen = edgeLen; edgeLen = VecDist( shooterQuad->v[2], shooterQuad->v[3] ); if ( edgeLen > maxEdgeLen ) maxEdgeLen = edgeLen; edgeLen = VecDist( shooterQuad->v[3], shooterQuad->v[0] ); if ( edgeLen > maxEdgeLen ) maxEdgeLen = edgeLen; } // Compute how many regular segments to divide the longest edge into so that // every resulting segment is not longer than maxGathererQuadEdgeLength. int numSegments = (int) ceil( maxEdgeLen / maxGathererQuadEdgeLength ); // Each shooter quad on the current surface is going to be subdivided into // (numSegments*numSegments) gatherer quads. // Therefore, the total number of gatherer quads for this surface is... surface->numGathererQuads = surface->numShooterQuads * numSegments * numSegments; surface->gatherers = (QM_GathererQuad *) CheckedMalloc( sizeof(QM_GathererQuad) * surface->numGathererQuads ); int surfGatherersCount = 0; // This will contain the number of gatherers in this surface. for ( int q = 0; q < surface->numShooterQuads; q++ ) { QM_ShooterQuad *shooterQuad = &(surface->shooters[q]); for ( int y = 0; y < numSegments; y++ ) for ( int x = 0; x < numSegments; x++ ) { float newv[4][3]; QuadBilinearInterpolate( newv[0], (float) x / numSegments, (float) y / numSegments, shooterQuad->v ); QuadBilinearInterpolate( newv[1], (float) (x+1) / numSegments, (float) y / numSegments, shooterQuad->v ); QuadBilinearInterpolate( newv[2], (float) (x+1) / numSegments, (float) (y+1) / numSegments, shooterQuad->v ); QuadBilinearInterpolate( newv[3], (float) x / numSegments, (float) (y+1) / numSegments, shooterQuad->v ); QM_GathererQuad *gathererQuad = &(surface->gatherers[ surfGatherersCount ]); for ( int i = 0; i < 4; i++ ) CopyArray3( gathererQuad->v[i], newv[i] ); CopyArray3( gathererQuad->normal, shooterQuad->normal ); gathererQuad->area = QuadArea( gathererQuad->v ); // Initialize the radiosity of the gatherer quad. gathererQuad->radiosity[0] = surface->emission[0]; gathererQuad->radiosity[1] = surface->emission[1]; gathererQuad->radiosity[2] = surface->emission[2]; CopyArray3( gathererQuad->vRadiosity[0], ZERO_VEC_3F ); CopyArray3( gathererQuad->vRadiosity[1], ZERO_VEC_3F ); CopyArray3( gathererQuad->vRadiosity[2], ZERO_VEC_3F ); CopyArray3( gathererQuad->vRadiosity[3], ZERO_VEC_3F ); gathererQuad->shooter = shooterQuad; gathererQuad->surface = surface; surfGatherersCount++; modelTotalGatherers++; } } } // Build an array of pointers to all the gatherers in the model. m->totalGatherers = modelTotalGatherers; m->gatherers = (QM_GathererQuad **) CheckedMalloc( sizeof(QM_GathererQuad *) * modelTotalGatherers ); int modelTotalGatherersCount = 0; for ( int s = 0; s < m->numSurfaces; s++ ) for ( int q = 0; q < m->surfaces[s].numGathererQuads; q++ ) { m->gatherers[ modelTotalGatherersCount ] = &(m->surfaces[s].gatherers[q]); modelTotalGatherersCount++; } return; }