xiaozhuai / imageinfo

Free Palestine🇵🇸🇵🇸🇵🇸Cross platform super fast single header c++ library to get image size and format without loading/decoding. Support avif, bmp, cur, dds, gif, hdr (pic), heic (heif), icns, ico, j2k, jp2, jpeg (jpg), jpx, ktx, png, psd, qoi, tga, tiff (tif), webp ...
MIT License
105 stars 26 forks source link

Is it possible that a C# wrapper will be provided in the future? #11

Open Charltsing opened 1 month ago

Charltsing commented 1 month ago

Is it possible that a C# wrapper will be provided in the future?

Thank you for your excellent work

xiaozhuai commented 1 month ago

Hi @Charltsing , a C# wrapper is possible. You can wrap a dll and then use something like [DllImport("MyLibrary.dll", EntryPoint = "NativeMethod")].

Since this project is a c++ header only library, wrapping dlls need to be done by yourself, I believe this can be done within 10 lines of code.

Or completely rewriting imageinfo using C# would also be a good idea, just like the rust version imageinfo-rs.

: )

Charltsing commented 1 month ago

I'm looking forward to an official version

xiaozhuai commented 3 weeks ago

I'm looking forward to an official version

Unfortunately, there is currently no official port for C#. And currently I have no plans to port to C# for the time being. If you are willing to get this done, I would be happy to help. Any question is welcome.

xiaozhuai commented 2 weeks ago

@Charltsing I'm closing this as complete. Feel free to reopen it if you want to continue discussion.

Charltsing commented 2 weeks ago

我只懂C#,可以编译你的项目,但是生成的却是exe文件。这就麻烦了。 以我的C水平,能编译已经是能力极限。把你这个工程改成符合c#要求的dll,有点费劲。

没找到 extern "C" __declspec(dllexport) 导出函数,是不是得我自己写啊。

————

另外,ImageInfo的输出对C# 也不太友好。 C#获取c++的输出,通常是数值、字节数组、字符串。后两者还需要知道分配的内存大小。 或者直接返回一个定长的结构,以便C#分配内存。

所以,是不是考虑将输出改成C#友好的版本: 输出的结构包括:图像格式枚举,图像宽高数值,以及输出的其它字符串和字符串长度。 或者直接输出定长的结构,字符串改成定长,例如图像格式用8个字符,错误信息64个字符之类的。

如果实在麻烦的话,也许直接只写一个导出函数,能提供图像格式枚举和宽高数值就足够了,不需要字符串输出。因为我没在代码里面发现其它什么有用的图像信息。

这就是为啥我希望等官方出一个C#包装,毕竟需要修改你的源代码才方便包装成C#版本。

xiaozhuai commented 2 weeks ago

没找到 extern "C" __declspec(dllexport) 导出函数,是不是得我自己写啊。

是的

这就是为啥我希望等官方出一个C#包装,毕竟需要修改你的源代码才方便包装成C#版本。

实际上并不需要修改现有的代码,正如我前面说过的,这是一个仅头文件库。但确实需要编写少量的c++代码来集成到c#工程中。晚点我会提供一个简单的C#封装示例。

xiaozhuai commented 2 weeks ago

@Charltsing 下载下面的zip包,解压后将imageinfo-sharp目录放入imageinfo工程的根目录,然后用Visual Studio打开imageinfo-sharp目录下面的imageinfo-sharp.sln工程文件即可。 注意修改一下Program.cs中的测试文件的路径。

imageinfo-sharp.zip

QQ_1722319137807

Charltsing commented 2 weeks ago

我把imageinfo.hpp放到imageinfo-sharp\imageinfo-native目录下,编译成功。

谢谢您的帮助。

xiaozhuai commented 2 weeks ago

我把imageinfo.hpp放到imageinfo-sharp\imageinfo-native目录下,编译成功。

这一步不是必须的,可以修改一下imageinfo-nativeinclude_directories即包含目录,增加一个../../include即可,注意如果有多个Configuration,则都需要修改。 可能我发你的时候忘了改了,你可以自己改一下。 或者就按照你的做法,复制一下imageinfo.hpp也是可以的。

最后需要注意的是: 格式枚举的值有可能会改变,你需要在C#中小心的处理映射关系,当前导出的是int类型,你可能会需要增加一个enum类型用于表示图片格式。 如果后续你更新了imageinfo.hpp,那么你需要检查一下你的枚举量映射关系是否正确。(因为未来可能会发生改变,此库只保证源码兼容,不保证二进制兼容。)

xiaozhuai commented 2 weeks ago

@Charltsing 没什么问题的话,我就关闭这个issue了。有问题的话你可以再重新打开。

Charltsing commented 2 weeks ago

@Charltsing 没什么问题的话,我就关闭这个issue了。有问题的话你可以再重新打开。

那个啥,你这个版本不支持中文吗?英文没啥问题了

我测试中文路径和中文文件名都提示: - Error : Unrecognized format

没找到在哪reopen

顺便问一下,字符串能不能以Unicode方式传进native dll,这样就不需要utf8转换了。

xiaozhuai commented 2 weeks ago

那个啥,你这个版本不支持中文吗?

是的,不支持中文,支持中文需要做一些小小的更改。

QQ_1722335624090

找到entrypoint.cpp,修改如下

#define WIN32_LEAN_AND_MEAN

#include "imageinfo.hpp"
#include <iostream>
#include <Windows.h>

std::wstring utf8_to_wstring(const char *str, size_t len) {
    if (len == 0) {
        return std::wstring();
    }

    int size = MultiByteToWideChar(CP_UTF8, 0, str, (int)len, nullptr, 0);
    if (size == 0) {
        return std::wstring();
    }

    std::wstring wstr(size, 0);
    MultiByteToWideChar(CP_UTF8, 0, str, (int)len, &wstr[0], size);
    return wstr;
}

class WFilePathReader {
public:
    explicit WFilePathReader(const std::wstring &path) : file_(path, std::ios::in | std::ios::binary) {
    }

    ~WFilePathReader() {
        if (file_.is_open()) {
            file_.close();
        }
    }

    inline size_t size() {
        if (file_.is_open()) {
            file_.seekg(0, std::ios::end);
            return (size_t)file_.tellg();
        } else {
            return 0;
        }
    }

    inline void read(void *buf, off_t offset, size_t size) {
        file_.seekg(offset, std::ios::beg);
        file_.read((char *)buf, (std::streamsize)size);
    }

private:
    std::ifstream file_;
};

#define IMAGE_INFO_API      extern "C" __declspec(dllexport)

IMAGE_INFO_API imageinfo::ImageInfo *imageinfo_parse_file(const char *path, size_t len) {
    imageinfo::ImageInfo *info = new imageinfo::ImageInfo();
    *info = imageinfo::parse<WFilePathReader>(utf8_to_wstring(path, len));
    return info;
}

IMAGE_INFO_API imageinfo::ImageInfo *imageinfo_parse_memory(const void *data, size_t size) {
    imageinfo::ImageInfo *info = new imageinfo::ImageInfo();
    *info = imageinfo::parse<imageinfo::RawDataReader>(imageinfo::RawData(data, size));
    return info;
}

IMAGE_INFO_API void imageinfo_free(imageinfo::ImageInfo *info) {
    delete info;
}

IMAGE_INFO_API int imageinfo_get_error(const imageinfo::ImageInfo *info) {
    return info->error();
}

IMAGE_INFO_API const char *imageinfo_get_error_message(const imageinfo::ImageInfo *info) {
    return info->error_msg();
}

IMAGE_INFO_API int imageinfo_get_format(const imageinfo::ImageInfo *info) {
    return info->format();
}

IMAGE_INFO_API const char *imageinfo_get_ext(const imageinfo::ImageInfo *info) {
    return info->ext();
}

IMAGE_INFO_API const char *imageinfo_get_full_ext(const imageinfo::ImageInfo *info) {
    return info->full_ext();
}

IMAGE_INFO_API const char *imageinfo_get_mimetype(const imageinfo::ImageInfo *info) {
    return info->mimetype();
}

IMAGE_INFO_API int64_t imageinfo_get_width(const imageinfo::ImageInfo *info) {
    return info->size().width;
}

IMAGE_INFO_API int64_t imageinfo_get_height(const imageinfo::ImageInfo *info) {
    return info->size().height;
}

IMAGE_INFO_API int imageinfo_get_entries_count(const imageinfo::ImageInfo *info) {
    return static_cast<int>(info->entry_sizes().size());
}

IMAGE_INFO_API int64_t imageinfo_get_entry_width(const imageinfo::ImageInfo *info, int index) {
    return info->entry_sizes()[index].width;
}

IMAGE_INFO_API int64_t imageinfo_get_entry_height(const imageinfo::ImageInfo *info, int index) {
    return info->entry_sizes()[index].height;
}

然后找到ImageInfo.cs中的ParseFile修改如下

    public static ImageInfo ParseFile(string path)
    {
        unsafe
        {
            var utf8 = System.Text.Encoding.UTF8.GetBytes(path);
            fixed (byte* p = utf8)
            {
                return new ImageInfo(NativeMethods.parse_file(p, utf8.Length));
            }
        }
    }
Charltsing commented 2 weeks ago

反正你也是把Utf8转成Unicode,不如直接用Unicode

我把代码改成这样了:

entrypoint.cpp IMAGE_INFO_API imageinfo::ImageInfo imageinfo_parse_file_unicode(const wchar_t path) { imageinfo::ImageInfo info = new imageinfo::ImageInfo(); info = imageinfo::parse(path); return info;

ImageInfo.cs [DllImport(DllPath, EntryPoint = "imageinfo_parse_file_unicode", CharSet = CharSet.Unicode)] public static extern unsafe IntPtr parse_file_unicode(string path);

public static ImageInfo ParseFileUnicode(string path) { return new ImageInfo(NativeMethods.parse_file_unicode(path));
}

xiaozhuai commented 2 weeks ago

不如直接用Unicode

也可以,我对c#不是很熟悉,如果你上面的代码能work的话那就没问题

xiaozhuai commented 2 weeks ago

我试了一下你的代码没有问题,注意WFilePathReader还是需要的。

xiaozhuai commented 2 weeks ago

@Charltsing 我提供的C#示例实际上并非最优的实现,例如要考虑到ImageInfo要对序列化/反序列化友好,需要将EntrySizes作为一个List属性,当前是提供了两个get方法。然后就是windows下的宽字符文件路径的支持,这个刚刚已经解决了。可能还会有别的一些问题或需要优化的地方。 如果你愿意维护C#的封装,我会考虑将其合并到此工程作为官方C#支持。因为我本人确实不是很想再多维护C#的部分,主要也不熟。所以如果你愿意之后维护C#部分的话,欢迎提交PR。

xiaozhuai commented 2 weeks ago

另外作为官方C#支持,至少还需要添加以下内容:

  1. C# tests,参考tests.cpp对所有示例文件进行测试,并确保所有的测试通过。
  2. 添加对应的CI工作流,自动编译和测试C#部分。可以参考现有的 ci.yml
Charltsing commented 2 weeks ago

目前在辽宁联通和移动上github都很麻烦。每次只能几分钟,然后就被断网。 间隔十几分钟或者更长时间才能再上,然后再被断网。

所以我目前不具备维护项目的条件。

不过,我过些日子发一个内存加载native dll的C#版给你,你可以用它修改一下,放到你的存储库里面做官方示例,以后我找起来也方便。

C#版的例子有一个就行,也不需要天天维护。

Charltsing commented 1 week ago

ImageInfo-sharp包括imageinfo-native,ImageInfo-sharp,imageinfo-demo三个项目

ImageInfo-sharp采用内存加载的方式调用原生dll。使用的MemoryModule来自https://github.com/wwh1004/MemoryModule 这个项目也是国内大神搞的。测试了三年,在上万台电脑跑过,很可靠。

其它具体项目说明看readme.txt

ImageInfo-sharp.zip

使用MemoryModule的目的是避免多出两个原生dll文件。可以单文件部署或者调用。

如果你觉得可用的话,以你的名义放在存储库里面吧。方便我以后升级查找。因为entrypoint.cpp还需要你来维护。

xiaozhuai commented 1 week ago

@Charltsing 感谢贡献。 我暂时会将此issue置顶,以便有需要的人方便获取。