我们很高兴地宣布发布适用于 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 之间提供无缝集成。通过提供一个集成的、直观的界面,它消除了包管理的复杂性,让您专注于应用程序的开发。我们希望此工具能够增强您的开发体验,我们期待着看到您用它构建的内容!