bool ProceduralTexture::savePNG(char *png_map) { std::vector<unsigned char> buffer; //create encoder and set settings and info (optional) LodePNG::Encoder encoder; encoder.addText("Comment", "CubicVR Procedural Texture"); encoder.getSettings().zlibsettings.windowSize = 2048; encoder.encode(buffer, image.empty() ? 0 : &image[0], width, height); if(encoder.hasError()) { Logger::log(LOG_ERROR,"[savePng] Error %d\n", encoder.getError()); return false; } LodePNG::saveFile(buffer, png_map); return true; }
int save_png_SDFont( const char* orig_filename, const char* font_name, int img_width, int img_height, const std::vector< unsigned char > &img_data, std::vector< sdf_glyph > &packed_glyphs, const std::map<int, int> & char_map, int font_size ) { // save my image int fn_size = strlen( orig_filename ) + 100; char *fn = new char[ fn_size ]; sprintf( fn, "%s_sdf.png", orig_filename ); printf( "'%s'\n", fn ); LodePNG::Encoder encoder; encoder.addText("Comment", "Signed Distance Font: lonesock tools"); encoder.getSettings().zlibsettings.windowSize = 512; // faster, not much worse compression std::vector<unsigned char> buffer; int tin = clock(); encoder.encode( buffer, img_data.empty() ? 0 : &img_data[0], img_width, img_height ); LodePNG::saveFile( buffer, fn ); tin = clock() - tin; // remap from unicode to codepage, get font height float ymax = 0, ymin = 0; if( char_map.size() != 0 ) { for( unsigned int i = 0; i < packed_glyphs.size(); ++i ) { std::map<int, int>::const_iterator id; if( (id = char_map.find(packed_glyphs[i].ID)) != char_map.end() ) { packed_glyphs[i].ID = id->second; float ymaxi = packed_glyphs[i].yoff; float ymini = packed_glyphs[i].yoff - packed_glyphs[i].height; if (ymax < ymaxi) ymax = ymaxi; if (ymin > ymini) ymin = ymini; } } } // now save the acompanying info sprintf( fn, "%s_sdf.txt", orig_filename ); FILE *fp = fopen( fn, "w" ); if( fp ) { fprintf( fp, "info face=\"%s\"\n", font_name ); fprintf( fp, "size=%i\n", font_size ); fprintf( fp, "ascent=%2.0f\n", ymax ); fprintf( fp, "descent=%2.0f\n", ymin ); fprintf( fp, "chars count=%zu\n", packed_glyphs.size() ); for( unsigned int i = 0; i < packed_glyphs.size(); ++i ) { fprintf( fp, "char id=%-6ix=%-6iy=%-6iwidth=%-6iheight=%-6i", packed_glyphs[i].ID, packed_glyphs[i].x, packed_glyphs[i].y, packed_glyphs[i].width, packed_glyphs[i].height ); fprintf( fp, "xoffset=%-10.3fyoffset=%-10.3fxadvance=%-10.3f", packed_glyphs[i].xoff, packed_glyphs[i].yoff, packed_glyphs[i].xadv ); fprintf( fp, " page=0 chnl=0\n" ); } fclose( fp ); } delete [] fn; return tin; }
bool render_signed_distance_image( const char* image_file, int texture_size, bool export_c_header ) { // try to load this file as an image int w, h, channels; unsigned char *img = stbi_load( image_file, &w, &h, &channels, 0 ); if( !img ) { return false; } // image loaded printf( "Loaded '%s', %i x %i, channels 0", image_file, w, h ); for( int i = 1; i < channels; ++i ) { printf( ",%i", i ); } printf( "\n" ); // check for components and resizing issues if( (w <= texture_size) && (h <= texture_size) ) { printf( "The output texture size is larger than the input image dimensions!\n" ); stbi_image_free( img ); return false; } // now, which channel do I use as the input function? int chan = 0; if( channels > 1 ) { printf( "Which channel contains the input? " ); scanf( "%i", &chan ); if( chan < 0 ) { chan = 0; } else if( chan >= channels ) { chan = channels - 1; } } printf( "Using channel %i as the input\n", chan ); std::vector<unsigned char> img_data; img_data.reserve( w*h ); for( int i = chan; i < w*h*channels; i += channels ) { img_data.push_back( img[i] ); } stbi_image_free( img ); // is this channel strictly 2 values? bool needs_threshold = false; int vmax, vmin; { int val0 = img_data[0], val = -1; vmin = img_data[0]; vmax = img_data[0]; for( int i = 0; i < w*h; ++i ) { // do I need a threshold? if( img_data[i] != val0 ) { if( val < 0 ) { // second value val = img_data[i]; } else { needs_threshold = (val != img_data[i]); } } // find min and max, just in case if( img_data[i] < vmin ) { vmin = img_data[i]; } if( img_data[i] > vmax ) { vmax = img_data[i]; } } } if( needs_threshold ) { int thresh; printf( "The image needs a threshold, between %i and %i (< threshold is 0): ", vmin, vmax ); scanf( "%i", &thresh ); if( thresh <= vmin ) { thresh = vmin + 1; } else if( thresh > vmax ) { thresh = vmax; } printf( "using threshold=%i\n", thresh ); for( int i = 0; i < w*h; ++i ) { if( img_data[i] < thresh ) { img_data[i] = 0; } else { img_data[i] = 255; } } } // OK, I'm finally ready to perform the SDF analysis int sw; if( w > h ) { sw = 2 * w / texture_size; } else { sw = 2 * h / texture_size; } std::vector<unsigned char> pdata( 4 * texture_size * texture_size, 0 ); img = &(img_data[0]); for( int j = 0; j < texture_size; ++j ) { for( int i = 0; i < texture_size; ++i ) { int sx = i * (w-1) / (texture_size-1); int sy = j * (h-1) / (texture_size-1); int pd_idx = (i+j*texture_size) * 4; pdata[pd_idx] = get_SDF_radial ( img, w, h, sx, sy, sw ); pdata[pd_idx+1] = pdata[pd_idx]; pdata[pd_idx+2] = pdata[pd_idx]; pdata[pd_idx+3] = pdata[pd_idx]; } } // save the image int fn_size = strlen( image_file ) + 100; char *fn = new char[ fn_size ]; #if 0 sprintf( fn, "%s_sdf.bmp", image_file ); stbi_write_bmp( fn, texture_size, texture_size, 4, &pdata[0] ); #endif sprintf( fn, "%s_sdf.png", image_file ); printf( "'%s'\n", fn ); LodePNG::Encoder encoder; encoder.addText("Comment", "Signed Distance Image: lonesock tools"); encoder.getSettings().zlibsettings.windowSize = 512; // faster, not much worse compression std::vector<unsigned char> buffer; int tin = clock(); encoder.encode( buffer, pdata.empty() ? 0 : &pdata[0], texture_size, texture_size ); LodePNG::saveFile( buffer, fn ); tin = clock() - tin; return true; }