Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -787,6 +787,9 @@ if(UHDR_BUILD_EXAMPLES)
target_link_options(${UHDR_SAMPLE_APP} PRIVATE -static)
endif()
target_link_libraries(${UHDR_SAMPLE_APP} PRIVATE ${UHDR_CORE_LIB_NAME})
if(WIN32)
target_link_libraries(${UHDR_SAMPLE_APP} PRIVATE shell32)
endif()
endif()

if(UHDR_BUILD_TESTS OR UHDR_BUILD_BENCHMARK)
Expand Down Expand Up @@ -828,6 +831,16 @@ if(UHDR_BUILD_TESTS)
set_target_properties(ultrahdr_unit_test PROPERTIES BUILD_RPATH "${LIBHEIF_LIB_PREFIX}")
endif()
add_test(NAME UHDRUnitTests COMMAND ultrahdr_unit_test)
if(WIN32 AND UHDR_BUILD_EXAMPLES)
add_test(
NAME UHDRWindowsUnicodeCliPaths
COMMAND ${CMAKE_COMMAND}
"-DUHDR_APP=$<TARGET_FILE:${UHDR_SAMPLE_APP}>"
"-DTEST_INPUT=${TESTS_DIR}/data/raw_p010_image.p010"
"-DTEST_OUTPUT_DIR=${CMAKE_CURRENT_BINARY_DIR}/unicode_cli_test"
-P "${TESTS_DIR}/windows_unicode_cli_test.cmake"
)
endif()
endif()

if(UHDR_BUILD_BENCHMARK)
Expand Down
83 changes: 73 additions & 10 deletions examples/ultrahdr_app.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@

#ifdef _WIN32
#include <Windows.h>
#include <shellapi.h>

#include <filesystem>
#else
#include <sys/time.h>
#endif
Expand All @@ -23,6 +26,8 @@
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>

#include "ultrahdr_api.h"

Expand Down Expand Up @@ -65,6 +70,53 @@ const float BT2020RGBtoYUVMatrix[9] = {0.2627f,
// remove these once introduced in ultrahdr_api.h
const int UHDR_IMG_FMT_48bppYCbCr444 = 101;

#ifdef _WIN32
static std::filesystem::path filePath(const char* filename) {
return std::filesystem::u8path(filename);
}
#else
static const char* filePath(const char* filename) {
return filename;
}
#endif

#ifdef _WIN32
static bool getUtf8Arguments(std::vector<std::string>& argument_storage,
std::vector<char*>& arguments) {
int wide_argc = 0;
wchar_t** wide_argv = CommandLineToArgvW(GetCommandLineW(), &wide_argc);
if (wide_argv == nullptr) return false;

argument_storage.reserve(wide_argc);
for (int index = 0; index < wide_argc; ++index) {
const int wide_length = lstrlenW(wide_argv[index]);
if (wide_length == 0) {
argument_storage.emplace_back();
continue;
}
const int utf8_length = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, wide_argv[index],
wide_length, nullptr, 0, nullptr, nullptr);
if (utf8_length <= 0) {
LocalFree(wide_argv);
return false;
}
std::string argument(utf8_length, '\0');
if (WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, wide_argv[index], wide_length,
argument.data(), utf8_length, nullptr, nullptr) != utf8_length) {
LocalFree(wide_argv);
return false;
}
argument_storage.push_back(std::move(argument));
}
LocalFree(wide_argv);

arguments.reserve(argument_storage.size() + 1);
for (auto& argument : argument_storage) arguments.push_back(argument.data());
arguments.push_back(nullptr);
return true;
}
#endif

int optind_s = 1;
int optopt_s = 0;
char* optarg_s = nullptr;
Expand Down Expand Up @@ -153,7 +205,7 @@ static bool loadFile(const char* filename, void*& result, std::streamoff length)
<< " bytes from file : " << filename << std::endl;
return false;
}
std::ifstream ifd(filename, std::ios::binary | std::ios::ate);
std::ifstream ifd(filePath(filename), std::ios::binary | std::ios::ate);
if (ifd.good()) {
auto size = ifd.tellg();
if (size < length) {
Expand All @@ -176,7 +228,7 @@ static bool loadFile(const char* filename, void*& result, std::streamoff length)
}

static bool loadFile(const char* filename, uhdr_raw_image_t* handle) {
std::ifstream ifd(filename, std::ios::binary);
std::ifstream ifd(filePath(filename), std::ios::binary);
if (ifd.good()) {
if (handle->fmt == UHDR_IMG_FMT_24bppYCbCrP010) {
const size_t bpp = 2;
Expand Down Expand Up @@ -205,7 +257,7 @@ static bool loadFile(const char* filename, uhdr_raw_image_t* handle) {
}

static bool writeFile(const char* filename, void*& result, size_t length) {
std::ofstream ofd(filename, std::ios::binary);
std::ofstream ofd(filePath(filename), std::ios::binary);
if (ofd.is_open()) {
ofd.write(static_cast<char*>(result), length);
return true;
Expand All @@ -215,7 +267,7 @@ static bool writeFile(const char* filename, void*& result, size_t length) {
}

static bool writeFile(const char* filename, uhdr_raw_image_t* img) {
std::ofstream ofd(filename, std::ios::binary);
std::ofstream ofd(filePath(filename), std::ios::binary);
if (ofd.is_open()) {
if (img->fmt == UHDR_IMG_FMT_32bppRGBA8888 || img->fmt == UHDR_IMG_FMT_64bppRGBAHalfFloat ||
img->fmt == UHDR_IMG_FMT_32bppRGBA1010102) {
Expand Down Expand Up @@ -529,7 +581,7 @@ bool UltraHdrAppInput::fillRGBA8888ImageHandle() {
}

bool UltraHdrAppInput::fillSdrCompressedImageHandle() {
std::ifstream ifd(mSdrIntentCompressedFile, std::ios::binary | std::ios::ate);
std::ifstream ifd(filePath(mSdrIntentCompressedFile), std::ios::binary | std::ios::ate);
if (ifd.good()) {
auto size = ifd.tellg();
mSdrIntentCompressedImage.capacity = size;
Expand All @@ -545,7 +597,7 @@ bool UltraHdrAppInput::fillSdrCompressedImageHandle() {
}

bool UltraHdrAppInput::fillGainMapCompressedImageHandle() {
std::ifstream ifd(mGainMapCompressedFile, std::ios::binary | std::ios::ate);
std::ifstream ifd(filePath(mGainMapCompressedFile), std::ios::binary | std::ios::ate);
if (ifd.good()) {
auto size = ifd.tellg();
mGainMapCompressedImage.capacity = size;
Expand Down Expand Up @@ -582,7 +634,7 @@ void parse_argument(uhdr_gainmap_metadata* metadata, char* argument, float* valu
}

bool UltraHdrAppInput::fillGainMapMetadataDescriptor() {
std::ifstream file(mGainMapMetadataCfgFile);
std::ifstream file(filePath(mGainMapMetadataCfgFile));
if (!file.is_open()) {
return false;
}
Expand All @@ -602,7 +654,7 @@ bool UltraHdrAppInput::fillGainMapMetadataDescriptor() {
}

bool UltraHdrAppInput::fillExifMemoryBlock() {
std::ifstream ifd(mExifFile, std::ios::binary | std::ios::ate);
std::ifstream ifd(filePath(mExifFile), std::ios::binary | std::ios::ate);
if (ifd.good()) {
auto size = ifd.tellg();
mExifBlock.data = nullptr;
Expand Down Expand Up @@ -650,7 +702,7 @@ void UltraHdrAppInput::writeGainMapMetadataToFile(uhdr_gainmap_metadata_t* metad
}

bool UltraHdrAppInput::fillUhdrImageHandle() {
std::ifstream ifd(mUhdrFile, std::ios::binary | std::ios::ate);
std::ifstream ifd(filePath(mUhdrFile), std::ios::binary | std::ios::ate);
if (ifd.good()) {
auto size = ifd.tellg();
mUhdrImage.capacity = size;
Expand Down Expand Up @@ -859,7 +911,7 @@ bool UltraHdrAppInput::decode() {
uhdr_release_decoder(handle);
return true;
}
std::ofstream file(mGainMapMetadataCfgFile);
std::ofstream file(filePath(mGainMapMetadataCfgFile));
if (file.is_open()) {
writeGainMapMetadataToFile(metadata, file);
file.close();
Expand Down Expand Up @@ -1594,6 +1646,17 @@ static void usage(const char* name) {
}

int main(int argc, char* argv[]) {
#ifdef _WIN32
std::vector<std::string> argument_storage;
std::vector<char*> arguments;
if (!getUtf8Arguments(argument_storage, arguments)) {
std::cerr << "failed to read the Windows command line" << std::endl;
return -1;
}
argc = static_cast<int>(argument_storage.size());
argv = arguments.data();
#endif

char opt_string[] = "p:y:i:g:f:w:h:C:c:t:q:o:O:m:j:e:a:b:z:R:s:M:Q:G:x:u:D:k:K:L:P";
char *hdr_intent_raw_file = nullptr, *sdr_intent_raw_file = nullptr, *uhdr_file = nullptr,
*sdr_intent_compressed_file = nullptr, *gainmap_compressed_file = nullptr,
Expand Down
40 changes: 40 additions & 0 deletions tests/windows_unicode_cli_test.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Copyright (C) 2023 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
# https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
# <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
# option. This file may not be copied, modified, or distributed
# except according to those terms.

if(NOT DEFINED UHDR_APP OR NOT DEFINED TEST_INPUT OR NOT DEFINED TEST_OUTPUT_DIR)
message(FATAL_ERROR "UHDR_APP, TEST_INPUT, and TEST_OUTPUT_DIR are required")
endif()

set(test_dir "${TEST_OUTPUT_DIR}/路径-🌈")
set(input_file "${test_dir}/输入图像.p010")
set(output_file "${test_dir}/输出图像.jpeg")

file(REMOVE_RECURSE "${TEST_OUTPUT_DIR}")
file(MAKE_DIRECTORY "${test_dir}")
configure_file("${TEST_INPUT}" "${input_file}" COPYONLY)

execute_process(
COMMAND "${UHDR_APP}" -m 0 -p "${input_file}" -w 1280 -h 720 -a 0 -z "${output_file}"
RESULT_VARIABLE encode_result
ERROR_VARIABLE encode_error
)
if(NOT encode_result EQUAL 0)
message(FATAL_ERROR "Unicode-path encode failed: ${encode_error}")
endif()
if(NOT EXISTS "${output_file}")
message(FATAL_ERROR "Unicode-path encode did not create ${output_file}")
endif()

execute_process(
COMMAND "${UHDR_APP}" -m 1 -j "${output_file}" -P
RESULT_VARIABLE probe_result
ERROR_VARIABLE probe_error
)
if(NOT probe_result EQUAL 0)
message(FATAL_ERROR "Unicode-path probe failed: ${probe_error}")
endif()
Loading