static bool create_dds_tex(const crn_comp_params ¶ms, dds_texture &dds_tex) { image_u8 images[cCRNMaxFaces][cCRNMaxLevels]; bool has_alpha = false; for (uint face_index = 0; face_index < params.m_faces; face_index++) { for (uint level_index = 0; level_index < params.m_levels; level_index++) { const uint width = math::maximum(1U, params.m_width >> level_index); const uint height = math::maximum(1U, params.m_height >> level_index); if (!params.m_pImages[face_index][level_index]) return false; images[face_index][level_index].alias((color_quad_u8*)params.m_pImages[face_index][level_index], width, height); if (!has_alpha) has_alpha = image_utils::has_alpha(images[face_index][level_index]); } } for (uint face_index = 0; face_index < params.m_faces; face_index++) for (uint level_index = 0; level_index < params.m_levels; level_index++) images[face_index][level_index].set_component_valid(3, has_alpha); face_vec faces(params.m_faces); for (uint face_index = 0; face_index < params.m_faces; face_index++) { for (uint level_index = 0; level_index < params.m_levels; level_index++) { mip_level *pMip = crnlib_new<mip_level>(); image_u8 *pImage = crnlib_new<image_u8>(); pImage->swap(images[face_index][level_index]); pMip->assign(pImage); faces[face_index].push_back(pMip); } } dds_tex.assign(faces); #ifdef CRNLIB_BUILD_DEBUG CRNLIB_ASSERT(dds_tex.check()); #endif return true; }
static bool write_compressed_texture( dds_texture& work_tex, convert_params& params, crn_comp_params &comp_params, pixel_format dst_format, progress_params& progress_state, bool perceptual, convert_stats &stats) { comp_params.m_file_type = (params.m_dst_file_type == texture_file_types::cFormatCRN) ? cCRNFileTypeCRN : cCRNFileTypeDDS; comp_params.m_pProgress_func = crn_progress_callback; comp_params.m_pProgress_func_data = &progress_state; comp_params.set_flag(cCRNCompFlagPerceptual, perceptual); crn_format crn_fmt = pixel_format_helpers::convert_pixel_format_to_best_crn_format(dst_format); comp_params.m_format = crn_fmt; console::message(L"Writing %s texture to file: \"%s\"", crn_get_format_string(crn_fmt), params.m_dst_filename.get_ptr()); uint32 actual_quality_level; float actual_bitrate; bool status = work_tex.write_to_file(params.m_dst_filename.get_ptr(), params.m_dst_file_type, &comp_params, &actual_quality_level, &actual_bitrate); if (!status) return convert_error(params, L"Failed writing output file!"); if (!params.m_no_stats) { if (!stats.init(params.m_pInput_texture->get_source_filename().get_ptr(), params.m_dst_filename.get_ptr(), *params.m_pIntermediate_texture, params.m_dst_file_type, params.m_lzma_stats)) { console::warning(L"Unable to compute output statistics for file: %s", params.m_pInput_texture->get_source_filename().get_ptr()); } } return true; }
bool create_texture_mipmaps(dds_texture &work_tex, const crn_comp_params ¶ms, const crn_mipmap_params &mipmap_params, bool generate_mipmaps) { crn_comp_params new_params(params); bool generate_new_mips = false; switch (mipmap_params.m_mode) { case cCRNMipModeUseSourceOrGenerateMips: { if (work_tex.get_num_levels() == 1) generate_new_mips = true; break; } case cCRNMipModeUseSourceMips: { break; } case cCRNMipModeGenerateMips: { generate_new_mips = true; break; } case cCRNMipModeNoMips: { work_tex.discard_mipmaps(); break; } default: { CRNLIB_ASSERT(0); break; } } rect window_rect(mipmap_params.m_window_left, mipmap_params.m_window_top, mipmap_params.m_window_right, mipmap_params.m_window_bottom); if (!window_rect.is_empty()) { if (work_tex.get_num_faces() > 1) { console::warning(L"Can't crop cubemap textures"); } else { console::info(L"Cropping input texture from window (%ux%u)-(%ux%u)", window_rect.get_left(), window_rect.get_top(), window_rect.get_right(), window_rect.get_bottom()); if (!work_tex.crop(window_rect.get_left(), window_rect.get_top(), window_rect.get_width(), window_rect.get_height())) console::warning(L"Failed cropping window rect"); } } int new_width = work_tex.get_width(); int new_height = work_tex.get_height(); if ((mipmap_params.m_clamp_width) && (mipmap_params.m_clamp_height)) { if ((new_width > (int)mipmap_params.m_clamp_width) || (new_height > (int)mipmap_params.m_clamp_height)) { if (!mipmap_params.m_clamp_scale) { if (work_tex.get_num_faces() > 1) { console::warning(L"Can't crop cubemap textures"); } else { new_width = math::minimum<uint>(mipmap_params.m_clamp_width, new_width); new_height = math::minimum<uint>(mipmap_params.m_clamp_height, new_height); console::info(L"Clamping input texture to %ux%u", new_width, new_height); work_tex.crop(0, 0, new_width, new_height); } } } } if (mipmap_params.m_scale_mode != cCRNSMDisabled) { bool is_pow2 = math::is_power_of_2((uint32)new_width) && math::is_power_of_2((uint32)new_height); switch (mipmap_params.m_scale_mode) { case cCRNSMAbsolute: { new_width = (uint)mipmap_params.m_scale_x; new_height = (uint)mipmap_params.m_scale_y; break; } case cCRNSMRelative: { new_width = (uint)(mipmap_params.m_scale_x * new_width + .5f); new_height = (uint)(mipmap_params.m_scale_y * new_height + .5f); break; } case cCRNSMLowerPow2: { if (!is_pow2) math::compute_lower_pow2_dim(new_width, new_height); break; } case cCRNSMNearestPow2: { if (!is_pow2) { int lwidth = new_width; int lheight = new_height; math::compute_lower_pow2_dim(lwidth, lheight); int uwidth = new_width; int uheight = new_height; math::compute_upper_pow2_dim(uwidth, uheight); if (labs(new_width - lwidth) < labs(new_width - uwidth)) new_width = lwidth; else new_width = uwidth; if (labs(new_height - lheight) < labs(new_height - uheight)) new_height = lheight; else new_height = uheight; } break; } case cCRNSMNextPow2: { if (!is_pow2) math::compute_upper_pow2_dim(new_width, new_height); break; } default: break; } } if ((mipmap_params.m_clamp_width) && (mipmap_params.m_clamp_height)) { if ((new_width > (int)mipmap_params.m_clamp_width) || (new_height > (int)mipmap_params.m_clamp_height)) { if (mipmap_params.m_clamp_scale) { new_width = math::minimum<uint>(mipmap_params.m_clamp_width, new_width); new_height = math::minimum<uint>(mipmap_params.m_clamp_height, new_height); } } } new_width = math::clamp<int>(new_width, 1, cCRNMaxLevelResolution); new_height = math::clamp<int>(new_height, 1, cCRNMaxLevelResolution); if ((new_width != (int)work_tex.get_width()) || (new_height != (int)work_tex.get_height())) { console::info(L"Resampling input texture to %ux%u", new_width, new_height); const char* pFilter = crn_get_mip_filter_name(mipmap_params.m_filter); bool srgb = mipmap_params.m_gamma_filtering != 0; dds_texture::resample_params res_params; res_params.m_pFilter = pFilter; res_params.m_wrapping = mipmap_params.m_tiled != 0; if (work_tex.get_num_faces()) res_params.m_wrapping = false; res_params.m_renormalize = mipmap_params.m_renormalize != 0; res_params.m_filter_scale = 1.0f; res_params.m_gamma = mipmap_params.m_gamma; res_params.m_srgb = srgb; res_params.m_multithreaded = (params.m_num_helper_threads > 0); if (!work_tex.resize(new_width, new_height, res_params)) { console::error(L"Failed resizing texture!"); return false; } } if ((generate_new_mips) && (generate_mipmaps)) { bool srgb = mipmap_params.m_gamma_filtering != 0; const char* pFilter = crn_get_mip_filter_name(mipmap_params.m_filter); dds_texture::generate_mipmap_params gen_params; gen_params.m_pFilter = pFilter; gen_params.m_wrapping = mipmap_params.m_tiled != 0; gen_params.m_renormalize = mipmap_params.m_renormalize != 0; gen_params.m_filter_scale = mipmap_params.m_blurriness; gen_params.m_gamma = mipmap_params.m_gamma; gen_params.m_srgb = srgb; gen_params.m_multithreaded = params.m_num_helper_threads > 0; gen_params.m_max_mips = mipmap_params.m_max_levels; gen_params.m_min_mip_size = mipmap_params.m_min_mip_size; console::info(L"Generating mipmaps using filter \"%S\"", pFilter); timer tm; tm.start(); if (!work_tex.generate_mipmaps(gen_params, true)) { console::error(L"Failed generating mipmaps!"); return false; } double t = tm.get_elapsed_secs(); console::info(L"Generated %u mipmap levels in %3.3fs", work_tex.get_num_levels() - 1, t); } return true; }
static bool convert_and_write_normal_texture(dds_texture& work_tex, convert_params& params, const crn_comp_params &comp_params, pixel_format dst_format, progress_params& progress_state, bool formats_differ, bool perceptual, convert_stats& stats) { if (formats_differ) { dxt_image::pack_params pack_params; pack_params.m_perceptual = perceptual; pack_params.m_compressor = comp_params.m_dxt_compressor_type; pack_params.m_pProgress_callback = dxt_progress_callback_func; pack_params.m_pProgress_callback_user_data_ptr = &progress_state; pack_params.m_dxt1a_alpha_threshold = comp_params.m_dxt1a_alpha_threshold; pack_params.m_quality = comp_params.m_dxt_quality; pack_params.m_endpoint_caching = !comp_params.get_flag(cCRNCompFlagDisableEndpointCaching); pack_params.m_grayscale_sampling = comp_params.get_flag(cCRNCompFlagGrayscaleSampling); if ((!comp_params.get_flag(cCRNCompFlagUseBothBlockTypes)) && (!comp_params.get_flag(cCRNCompFlagDXT1AForTransparency))) pack_params.m_use_both_block_types = false; pack_params.m_num_helper_threads = comp_params.m_num_helper_threads; pack_params.m_use_transparent_indices_for_black = comp_params.get_flag(cCRNCompFlagUseTransparentIndicesForBlack); console::info(L"Converting texture format from %s to %s", pixel_format_helpers::get_pixel_format_string(work_tex.get_format()), pixel_format_helpers::get_pixel_format_string(dst_format)); timer tm; tm.start(); bool status = work_tex.convert(dst_format, pack_params); double t = tm.get_elapsed_secs(); console::info(L""); if (!status) { if (progress_state.m_canceled) { params.m_canceled = true; return false; } else { return convert_error(params, L"Failed converting texture to output format!"); } } console::info(L"Texture format conversion took %3.3fs", t); } if (params.m_write_mipmaps_to_multiple_files) { for (uint f = 0; f < work_tex.get_num_faces(); f++) { for (uint l = 0; l < work_tex.get_num_levels(); l++) { dynamic_wstring filename(params.m_dst_filename.get_ptr()); dynamic_wstring drv, dir, fn, ext; if (!split_path(params.m_dst_filename.get_ptr(), &drv, &dir, &fn, &ext)) return false; fn += dynamic_wstring(cVarArg, L"_face%u_mip%u", f, l).get_ptr(); filename = drv + dir + fn + ext; mip_level *pLevel = work_tex.get_level(f, l); face_vec face(1); face[0].push_back(crnlib_new<mip_level>(*pLevel)); dds_texture new_tex; new_tex.assign(face); console::info(L"Writing texture face %u mip level %u to file %s", f, l, filename.get_ptr()); if (!new_tex.write_to_file(filename.get_ptr(), params.m_dst_file_type, NULL, NULL, NULL)) return convert_error(params, L"Failed writing output file!"); } } } else { console::message(L"Writing texture to file: \"%s\"", params.m_dst_filename.get_ptr()); if (!work_tex.write_to_file(params.m_dst_filename.get_ptr(), params.m_dst_file_type, NULL, NULL, NULL)) return convert_error(params, L"Failed writing output file!"); if (!params.m_no_stats) { if (!stats.init(params.m_pInput_texture->get_source_filename().get_ptr(), params.m_dst_filename.get_ptr(), *params.m_pIntermediate_texture, params.m_dst_file_type, params.m_lzma_stats)) { console::warning(L"Unable to compute output statistics for file: %s", params.m_pInput_texture->get_source_filename().get_ptr()); } } } return true; }
static pixel_format choose_pixel_format(convert_params& params, const crn_comp_params &comp_params, const dds_texture& src_tex, texture_type tex_type) { if (params.m_use_source_format) return src_tex.get_format(); const bool is_normal_map = (tex_type == cTextureTypeNormalMap); if (params.m_dst_file_type == texture_file_types::cFormatCRN) { if (is_normal_map) { switch (src_tex.get_format()) { case PIXEL_FMT_DXN: case PIXEL_FMT_3DC: case PIXEL_FMT_DXT5_xGBR: case PIXEL_FMT_DXT5_AGBR: case PIXEL_FMT_DXT5_xGxR: return src_tex.get_format(); default: return PIXEL_FMT_DXT5_AGBR; } } } else if (params.m_dst_file_type == texture_file_types::cFormatDDS) { if (src_tex.get_source_file_type() != texture_file_types::cFormatCRN) { if (is_normal_map) { switch (src_tex.get_format()) { case PIXEL_FMT_DXN: case PIXEL_FMT_3DC: case PIXEL_FMT_DXT5_xGBR: case PIXEL_FMT_DXT5_AGBR: case PIXEL_FMT_DXT5_xGxR: return src_tex.get_format(); default: return PIXEL_FMT_DXT5_AGBR; } } else if (pixel_format_helpers::is_grayscale(src_tex.get_format())) { if (pixel_format_helpers::has_alpha(src_tex.get_format())) return comp_params.get_flag(cCRNCompFlagDXT1AForTransparency) ? PIXEL_FMT_DXT1A : PIXEL_FMT_DXT5; else return PIXEL_FMT_DXT1; } else if (pixel_format_helpers::has_alpha(src_tex.get_format())) return comp_params.get_flag(cCRNCompFlagDXT1AForTransparency) ? PIXEL_FMT_DXT1A : PIXEL_FMT_DXT5; else return PIXEL_FMT_DXT1; } } else { // A regular image format. if (pixel_format_helpers::is_grayscale(src_tex.get_format())) { if (pixel_format_helpers::has_alpha(src_tex.get_format())) return PIXEL_FMT_A8L8; else return PIXEL_FMT_L8; } else if (pixel_format_helpers::has_alpha(src_tex.get_format())) return PIXEL_FMT_A8R8G8B8; else return PIXEL_FMT_R8G8B8; } return src_tex.get_format(); }