diff --git a/jarkViewer/file/home.png b/jarkViewer/file/home.png index 3ea4ab2..7897ea7 100644 Binary files a/jarkViewer/file/home.png and b/jarkViewer/file/home.png differ diff --git a/jarkViewer/file/home.psd b/jarkViewer/file/home.psd index bfb9e2a..8d588a9 100644 Binary files a/jarkViewer/file/home.psd and b/jarkViewer/file/home.psd differ diff --git a/jarkViewer/file/tips.png b/jarkViewer/file/tips.png index fbe95d3..c3581c8 100644 Binary files a/jarkViewer/file/tips.png and b/jarkViewer/file/tips.png differ diff --git a/jarkViewer/file/tips.psd b/jarkViewer/file/tips.psd index ed48e3b..c77c377 100644 Binary files a/jarkViewer/file/tips.psd and b/jarkViewer/file/tips.psd differ diff --git a/jarkViewer/include/ImageDatabase.h b/jarkViewer/include/ImageDatabase.h index 25223cc..e005fb1 100644 --- a/jarkViewer/include/ImageDatabase.h +++ b/jarkViewer/include/ImageDatabase.h @@ -13,7 +13,7 @@ class ImageDatabase{ L".pbm", L".pgm", L".ppm", L".pxm",L".pnm",L".sr", L".ras", L".exr", L".tiff", L".tif", L".webp", L".hdr", L".pic", L".heic", L".heif", L".avif", L".avifs", L".gif", L".jxl", - L".ico", L".icon", L".psd", L".tga" + L".ico", L".icon", L".psd", L".tga", L".svg" }; static inline const unordered_set supportRaw { @@ -1022,6 +1022,40 @@ class ImageDatabase{ return final_result; } + static cv::Mat loadSVG(const wstring& path, const vector& buf, int fileSize){ + const int maxEdge = 1024; + + auto document = lunasvg::Document::loadFromData((const char*)buf.data(), buf.size()); + if (!document) { + Utils::log("Failed to load SVG data {}", Utils::wstringToUtf8(path)); + return cv::Mat(); + } + + // 宽高比例 + const double AspectRatio = (document->height() == 0) ? 1 : (document->width() / document->height()); + int height, width; + + if (AspectRatio == 1) { + height = width = maxEdge; + } + else if (AspectRatio > 1) { + width = maxEdge; + height = int(maxEdge / AspectRatio); + } + else { + height = maxEdge; + width = int(maxEdge * AspectRatio); + } + + auto bitmap = document->renderToBitmap(width, height); + if (!bitmap.valid()) { + Utils::log("Failed to render SVG to bitmap {}", Utils::wstringToUtf8(path)); + return cv::Mat(); + } + + return cv::Mat(height, width, CV_8UC4, bitmap.data()).clone(); + } + static vector loadMats(const wstring& path, const vector& buf, int fileSize) { vector imgs; @@ -1426,6 +1460,13 @@ class ImageDatabase{ else if (ext == L".tga") { img = loadTGA(path, buf, fileSize); } + else if (ext == L".svg") { + img = loadSVG(path, buf, fileSize); + ret.exifStr = ExifParse::getSimpleInfo(path, img.cols, img.rows, buf.data(), fileSize); + if (img.empty()) { + img = getDefaultMat(); + } + } else if (ext == L".ico" || ext == L".icon") { img = loadICO(path, buf, fileSize); ret.exifStr = ExifParse::getSimpleInfo(path, img.cols, img.rows, buf.data(), fileSize); @@ -1456,6 +1497,37 @@ class ImageDatabase{ if (img.channels() == 1) cv::cvtColor(img, img, cv::COLOR_GRAY2BGR); + const size_t idx = ret.exifStr.find("方向: "); + if (idx != string::npos) { + int exifOrientation = ret.exifStr[idx + 8] - '0'; + + switch (exifOrientation) { + case 2: // 水平翻转 + cv::flip(img, img, 1); + break; + case 3: // 旋转180度 + cv::rotate(img, img, cv::ROTATE_180); + break; + case 4: // 垂直翻转 + cv::flip(img, img, 0); + break; + case 5: // 顺时针旋转90度后垂直翻转 + cv::rotate(img, img, cv::ROTATE_90_CLOCKWISE); + cv::flip(img, img, 0); + break; + case 6: // 顺时针旋转90度 + cv::rotate(img, img, cv::ROTATE_90_CLOCKWISE); + break; + case 7: // 顺时针旋转90度后水平翻转 + cv::rotate(img, img, cv::ROTATE_90_CLOCKWISE); + cv::flip(img, img, 1); + break; + case 8: // 逆时针旋转90度 + cv::rotate(img, img, cv::ROTATE_90_COUNTERCLOCKWISE); + break; + } + } + ret.imgList.emplace_back(img, 0); return ret; diff --git a/jarkViewer/include/Utils.h b/jarkViewer/include/Utils.h index 37d1a6b..c2c6021 100644 --- a/jarkViewer/include/Utils.h +++ b/jarkViewer/include/Utils.h @@ -42,6 +42,7 @@ using std::endl; #include "png.h" #include "pngstruct.h" #include "psdsdk.h" +#include "lunasvg.h" #define STB_IMAGE_IMPLEMENTATION #include "stb_image.h" diff --git a/jarkViewer/include/lunasvg.h b/jarkViewer/include/lunasvg.h new file mode 100644 index 0000000..43308c8 --- /dev/null +++ b/jarkViewer/include/lunasvg.h @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2020 Nwutobo Samuel Ugochukwu + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. +*/ + +#ifndef LUNASVG_H +#define LUNASVG_H + +#include +#include +#include + +#if defined(_MSC_VER) && defined(LUNASVG_SHARED) +#ifdef LUNASVG_EXPORT +#define LUNASVG_API __declspec(dllexport) +#else +#define LUNASVG_API __declspec(dllimport) +#endif +#else +#define LUNASVG_API +#endif + +namespace lunasvg { + +class Rect; +class Matrix; + +class LUNASVG_API Box { +public: + Box() = default; + Box(double x, double y, double w, double h); + Box(const Rect& rect); + + Box& transform(const Matrix& matrix); + Box transformed(const Matrix& matrix) const; + +public: + double x{0}; + double y{0}; + double w{0}; + double h{0}; +}; + +class Transform; + +class LUNASVG_API Matrix { +public: + Matrix() = default; + Matrix(double a, double b, double c, double d, double e, double f); + Matrix(const Transform& transform); + + Matrix& rotate(double angle); + Matrix& rotate(double angle, double cx, double cy); + Matrix& scale(double sx, double sy); + Matrix& shear(double shx, double shy); + Matrix& translate(double tx, double ty); + Matrix& transform(double a, double b, double c, double d, double e, double f); + Matrix& identity(); + Matrix& invert(); + + Matrix& operator*=(const Matrix& matrix); + Matrix& premultiply(const Matrix& matrix); + Matrix& postmultiply(const Matrix& matrix); + + Matrix inverted() const; + Matrix operator*(const Matrix& matrix) const; + + static Matrix rotated(double angle); + static Matrix rotated(double angle, double cx, double cy); + static Matrix scaled(double sx, double sy); + static Matrix sheared(double shx, double shy); + static Matrix translated(double tx, double ty); + +public: + double a{1}; + double b{0}; + double c{0}; + double d{1}; + double e{0}; + double f{0}; +}; + +class LUNASVG_API Bitmap { +public: + /** + * @note Bitmap format is ARGB32 Premultiplied. + */ + Bitmap(); + Bitmap(std::uint8_t* data, std::uint32_t width, std::uint32_t height, std::uint32_t stride); + Bitmap(std::uint32_t width, std::uint32_t height); + + void reset(std::uint8_t* data, std::uint32_t width, std::uint32_t height, std::uint32_t stride); + void reset(std::uint32_t width, std::uint32_t height); + + std::uint8_t* data() const; + std::uint32_t width() const; + std::uint32_t height() const; + std::uint32_t stride() const; + + void clear(std::uint32_t color); + void convert(int ri, int gi, int bi, int ai, bool unpremultiply); + void convertToRGBA() { convert(0, 1, 2, 3, true); } + + bool valid() const { return !!m_impl; } + +private: + struct Impl; + std::shared_ptr m_impl; +}; + +class LayoutSymbol; + +class LUNASVG_API Document { +public: + /** + * @brief Creates a document from a file + * @param filename - file to load + * @return pointer to document on success, otherwise nullptr + */ + static std::unique_ptr loadFromFile(const std::string& filename); + + /** + * @brief Creates a document from a string + * @param string - string to load + * @return pointer to document on success, otherwise nullptr + */ + static std::unique_ptr loadFromData(const std::string& string); + + /** + * @brief Creates a document from a string data and size + * @param data - string data to load + * @param size - size of the data to load, in bytes + * @return pointer to document on success, otherwise nullptr + */ + static std::unique_ptr loadFromData(const char* data, std::size_t size); + + /** + * @brief Creates a document from a null terminated string data + * @param data - null terminated string data to load + * @return pointer to document on success, otherwise nullptr + */ + static std::unique_ptr loadFromData(const char* data); + + /** + * @brief Sets the current transformation matrix of the document + * @param matrix - current transformation matrix + */ + void setMatrix(const Matrix& matrix); + + /** + * @brief Returns the current transformation matrix of the document + * @return the current transformation matrix + */ + Matrix matrix() const; + + /** + * @brief Returns the smallest rectangle in which the document fits + * @return the smallest rectangle in which the document fits + */ + Box box() const; + + /** + * @brief Returns width of the document + * @return the width of the document in pixels + */ + double width() const; + + /** + * @brief Returns the height of the document + * @return the height of the document in pixels + */ + double height() const; + + /** + * @brief Renders the document to a bitmap + * @param matrix - the current transformation matrix + * @param bitmap - target image on which the content will be drawn + */ + void render(Bitmap bitmap, const Matrix& matrix = Matrix{}) const; + + /** + * @brief Renders the document to a bitmap + * @param width - maximum width, in pixels + * @param height - maximum height, in pixels + * @param backgroundColor - background color in 0xRRGGBBAA format + * @return the raster representation of the document + */ + Bitmap renderToBitmap(std::uint32_t width = 0, std::uint32_t height = 0, std::uint32_t backgroundColor = 0x00000000) const; + + Document(Document&&); + ~Document(); + +private: + Document(); + + std::unique_ptr root; +}; + +} //namespace lunasvg + +#endif // LUNASVG_H diff --git a/jarkViewer/jarkViewer.aps b/jarkViewer/jarkViewer.aps index 715a2c4..2de3bac 100644 Binary files a/jarkViewer/jarkViewer.aps and b/jarkViewer/jarkViewer.aps differ diff --git a/jarkViewer/jarkViewer.rc b/jarkViewer/jarkViewer.rc index 9036b57..3c1ec48 100644 Binary files a/jarkViewer/jarkViewer.rc and b/jarkViewer/jarkViewer.rc differ diff --git a/jarkViewer/jarkViewer.vcxproj b/jarkViewer/jarkViewer.vcxproj index ff1b961..13ff721 100644 --- a/jarkViewer/jarkViewer.vcxproj +++ b/jarkViewer/jarkViewer.vcxproj @@ -90,7 +90,7 @@ true true DebugFull - IlmImf.lib;ippiw.lib;ippicvmt.lib;libjpeg-turbo.lib;libopenjp2.lib;libpng.lib;libprotobuf.lib;libtiff.lib;libwebp.lib;opencv_world4100.lib;zlib.lib;ittnotify.lib;heif.lib;libde265.lib;x265-static.lib;avif.lib;yuv.lib;dav1d.lib;aom.lib;gif.lib;raw.lib;OpenGL32.Lib;GlU32.Lib;freeglut.lib;lcms2.lib;jasper.lib;brotlicommon.lib;brotlidec.lib;brotlienc.lib;charset.lib;exiv2.lib;iconv.lib;inih.lib;INIReader.lib;intl.lib;libexpatMT.lib;Psapi.lib;jxl.lib;jxl_cms.lib;jxl_threads.lib;hwy.lib;libpng16.lib;FreeImage.lib;FreeImagePlus.lib;pixman-1.lib;fontconfig.lib;Psd_MT.lib;%(AdditionalDependencies) + IlmImf.lib;ippiw.lib;ippicvmt.lib;libjpeg-turbo.lib;libopenjp2.lib;libpng.lib;libprotobuf.lib;libtiff.lib;libwebp.lib;opencv_world4100.lib;zlib.lib;ittnotify.lib;heif.lib;libde265.lib;x265-static.lib;avif.lib;yuv.lib;dav1d.lib;aom.lib;gif.lib;raw.lib;OpenGL32.Lib;GlU32.Lib;freeglut.lib;lcms2.lib;jasper.lib;brotlicommon.lib;brotlidec.lib;brotlienc.lib;charset.lib;exiv2.lib;iconv.lib;inih.lib;INIReader.lib;intl.lib;libexpatMT.lib;Psapi.lib;jxl.lib;jxl_cms.lib;jxl_threads.lib;hwy.lib;libpng16.lib;FreeImage.lib;FreeImagePlus.lib;pixman-1.lib;fontconfig.lib;Psd_MT.lib;lunasvg.lib;%(AdditionalDependencies) false 8388608 @@ -127,7 +127,7 @@ true true false - IlmImf.lib;ippiw.lib;ippicvmt.lib;libjpeg-turbo.lib;libopenjp2.lib;libpng.lib;libprotobuf.lib;libtiff.lib;libwebp.lib;opencv_world4100.lib;zlib.lib;ittnotify.lib;heif.lib;libde265.lib;x265-static.lib;avif.lib;yuv.lib;dav1d.lib;aom.lib;gif.lib;raw.lib;OpenGL32.Lib;GlU32.Lib;freeglut.lib;lcms2.lib;jasper.lib;brotlicommon.lib;brotlidec.lib;brotlienc.lib;charset.lib;exiv2.lib;iconv.lib;inih.lib;INIReader.lib;intl.lib;libexpatMT.lib;Psapi.lib;jxl.lib;jxl_cms.lib;jxl_threads.lib;hwy.lib;libpng16.lib;FreeImage.lib;FreeImagePlus.lib;pixman-1.lib;fontconfig.lib;Psd_MT.lib;%(AdditionalDependencies) + IlmImf.lib;ippiw.lib;ippicvmt.lib;libjpeg-turbo.lib;libopenjp2.lib;libpng.lib;libprotobuf.lib;libtiff.lib;libwebp.lib;opencv_world4100.lib;zlib.lib;ittnotify.lib;heif.lib;libde265.lib;x265-static.lib;avif.lib;yuv.lib;dav1d.lib;aom.lib;gif.lib;raw.lib;OpenGL32.Lib;GlU32.Lib;freeglut.lib;lcms2.lib;jasper.lib;brotlicommon.lib;brotlidec.lib;brotlienc.lib;charset.lib;exiv2.lib;iconv.lib;inih.lib;INIReader.lib;intl.lib;libexpatMT.lib;Psapi.lib;jxl.lib;jxl_cms.lib;jxl_threads.lib;hwy.lib;libpng16.lib;FreeImage.lib;FreeImagePlus.lib;pixman-1.lib;fontconfig.lib;Psd_MT.lib;lunasvg.lib;%(AdditionalDependencies) false UseFastLinkTimeCodeGeneration 8388608 diff --git a/jarkViewer/lib/lib.7z b/jarkViewer/lib/lib.7z index 8ed354d..f953d9b 100644 Binary files a/jarkViewer/lib/lib.7z and b/jarkViewer/lib/lib.7z differ diff --git a/jarkViewer/src/jarkViewer.cpp b/jarkViewer/src/jarkViewer.cpp index a515731..6a72b04 100644 --- a/jarkViewer/src/jarkViewer.cpp +++ b/jarkViewer/src/jarkViewer.cpp @@ -6,9 +6,7 @@ /* TODO -1. exif 的旋转信息 -2. avif crop 无法解码 kimono.crop.avif -3. 重构程序结构 +1. 重构程序结构(当前快速拖动会画面撕裂), 换UI框架 */ const int BG_GRID_WIDTH = 8; @@ -20,7 +18,7 @@ const int fpsMax = 120; const auto target_duration = std::chrono::microseconds(1000000 / fpsMax); auto last_end = std::chrono::high_resolution_clock::now(); -const wstring appName = L"JarkViewer V1.6"; +const wstring appName = L"JarkViewer v1.7"; const string windowName = "mainWindows"; const vector ZOOM_LIST = {