我们很高兴地宣布发布适用于 Visual Studio 的全新 Conan 扩展,现在完全兼容 Conan 2。此扩展旨在简化使用 Visual Studio(2022 及更高版本)和 Conan 进行 C++ 项目开发的开发人员的工作流程。本文将通过一个实际示例指导您使用此扩展:从互联网下载图像、加载图像并将其以 ASCII 艺术的形式显示在控制台中。为此,我们将使用以下库,这些库可在 Conan Center 中找到:

  • libcurl 用于从互联网下载图像。
  • stb 用于加载下载的图像。
  • fmt 用于将彩色 ASCII 艺术输出打印到控制台。

From picture to ASCII art

扩展概述

适用于 Visual Studio 的 Conan 扩展提供了在 IDE 中管理 C++ 包的无缝集成,无需进行外部配置或终端命令。以下是入门方法:

安装

Install Conan Visual Studio extension

直接在 Visual Studio 中安装 Conan 扩展。

  • 导航到“扩展 > 管理扩展”。
  • 在在线市场中搜索“Conan”。
  • 点击“下载”按钮并按照安装提示操作。
  • 重新启动 Visual Studio 以激活扩展。

创建新的 C++ 项目

安装 Conan 扩展后,首先在 Visual Studio 中创建一个新的 C++ 项目。让我们创建一个名为 Image2ASCII 的新 C++ 控制台应用程序 项目。

Configure new project

配置扩展

首先,通过转到 视图 > 其他窗口 > Conan C/C++ 包管理器 并将其停靠在您易于访问的位置(例如,解决方案资源管理器旁边)来启动扩展。

Start the Conan extension

首次使用时,通过点击 ⚙️(齿轮)符号在 Conan 工具窗口中配置 Conan 可执行文件路径。您可以指定自定义路径(例如,用于虚拟环境)或使用系统范围内的安装。

Configure the Conan extension

设置 Conan 可执行文件的有效路径后,搜索框将被启用,我们就可以开始搜索 Conan 包并将它们添加到我们的项目中了。

添加所需的依赖项

现在让我们开始将依赖项添加到项目中并编写我们的 ASCII 艺术生成器代码。

请首先添加 libcurl 依赖项以下载图像。

  1. 打开 Conan 工具窗口并搜索 libcurl
  2. 选择所需的版本并点击 添加需求将其包含在您的项目中。
  3. 扩展会自动创建 conanfile.pyconandata.yml,用于存储依赖项并在稍后调用 MSBuildDeps 生成器以进行构建。
  4. 一个预构建事件已添加到您的项目设置中,确保在编译之前安装依赖项。

Add libcurl requirement

stbfmt 库重复相同的步骤。

添加代码

现在我们准备为 ASCII 艺术生成器添加代码。我们不会深入探讨代码的具体细节,因为这超出了本教程的范围。但是,此示例的所有源代码都可以在 Conan 2.0 示例存储库 中找到。

打开 Visual Studio 生成的 Image2ASCII.cpp 文件,并将示例代码替换为以下代码(您也可以从 存储库 中复制它)。

#include <iostream>
#include <curl/curl.h>
#include <vector>
#include <string>
#include <fmt/core.h>
#include <fmt/color.h>

#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"

// Size of ASCII art
static const int new_width = 80;

// Ascii gradient
static const std::string ASCII_CHARS = " .:-=+#%@@@";

// Function to scale the luminance into an ASCII character
char map_luminance_to_ascii(float luminance) {
    size_t position = luminance * (ASCII_CHARS.size() - 1);
    return ASCII_CHARS[position];
}

// Function to write data from curl call
static size_t write_data(void* ptr, size_t size, size_t nmemb, void* stream) {
    ((std::string*)stream)->append((char*)ptr, size * nmemb);
    return size * nmemb;
}

std::string download_image(const std::string& url) {
    CURL* curl = curl_easy_init();
    if (!curl) {
        std::cerr << "curl init failed" << std::endl;
        return "";
    }

    std::string response_string;
    curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response_string);
    curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); // Enable following redirection

    // Deactivate SSL verification, just for development!
    curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
    curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);

    CURLcode res = curl_easy_perform(curl);
    if (res != CURLE_OK) {
        std::cerr << "curl_easy_perform() failed: " << curl_easy_strerror(res) << std::endl;
    }

    curl_easy_cleanup(curl);
    return response_string;
}

// Function to convert image to ASCII art
void image_to_ascii(const std::string& image_data) {
    int width, height, channels;
    unsigned char* data = stbi_load_from_memory(
        reinterpret_cast<const stbi_uc*>(image_data.data()),
        image_data.size(),
        &width,
        &height,
        &channels,
        0
    );

    if (data == nullptr) {
        std::cerr << "Error loading image" << std::endl;
        return;
    }

    // Adjust aspect ratio for ASCII art
    int new_height = static_cast<int>(static_cast<double>(height) / width * new_width * 0.45);

    for (int i = 0; i < new_height; ++i) {
        for (int j = 0; j < new_width; ++j) {
            int old_i = i * height / new_height;
            int old_j = j * width / new_width;

            float r = data[(old_i * width + old_j) * channels + 0] / 255.0f;
            float g = data[(old_i * width + old_j) * channels + 1] / 255.0f;
            float b = data[(old_i * width + old_j) * channels + 2] / 255.0f;
            float luminance = (0.2126f * r + 0.7152f * g + 0.0722f * b);

            char ascii_char = map_luminance_to_ascii(luminance);

            // Use fmt to print ASCII character with color
            fmt::print(fmt::fg(fmt::rgb(uint8_t(r * 255), uint8_t(g * 255), uint8_t(b * 255))), "{}", ascii_char);
        }
        fmt::print("\n");
    }

    stbi_image_free(data);
}

int main(int argc, char** argv) {
    // Picture by Roshan Kamath from pexels.com
    std::string url = "https://images.pexels.com/photos/1661179/pexels-photo-1661179.jpeg";

    if (argc > 1) {
        url = argv[1]; // Use provided URL if available
    }

    std::string image_data = download_image(url);
    image_to_ascii(image_data);

    return 0;
}

从本质上讲,此应用程序接受图像 URL 作为参数(如果未提供则默认为特定 URL),并使用 libcurldownload_image() 函数下载它。随后,它使用 stb 读取 RGB 值,并使用 image_to_ascii() 函数将亮度值转换为 ASCII 字符,并使用 fmt 在控制台中打印彩色字符。

编译和执行

像往常一样在 Visual Studio 中构建您的项目。**第一次构建将触发 Conan 对您的依赖项的安装**。每次您添加需求或所选配置尚未安装需求时,预构建事件都会启动 Conan 以在构建之前安装依赖项。在项目的第一次构建之后,一旦 Conan 安装了所有需求,您将需要**重新启动构建**,以便所有包含包位置的属性都正确注入到项目中。

Conan installation success

现在点击运行按钮启动程序。

ASCII art bird

结论

与 Conan 2 兼容的全新 Conan Visual Studio 扩展旨在在 Conan 包管理器和 Visual Studio IDE 之间提供无缝集成。通过提供集成的、直观的界面,它消除了包管理的复杂性,让您能够专注于应用程序的开发。我们希望此工具能够增强您的开发体验,并且我们热切期待着看到您用它构建的内容!