Skip to content

Commit 55cc873

Browse files
committed
Add platform-specific Image implementations for Linux and Windows
Introduces image_linux.cpp and image_windows.cpp with platform-specific implementations of the Image class using GdkPixbuf (Linux) and GDI+ (Windows). Updates CMakeLists.txt to link additional libraries required for image handling (gdiplus, crypt32 on Windows).
1 parent b201499 commit 55cc873

File tree

3 files changed

+756
-1
lines changed

3 files changed

+756
-1
lines changed

src/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,5 +67,5 @@ if(APPLE)
6767
elseif (CMAKE_SYSTEM_NAME STREQUAL "Linux")
6868
target_link_libraries(nativeapi PUBLIC PkgConfig::GTK PkgConfig::X11 PkgConfig::XI PkgConfig::AYATANA_APPINDICATOR pthread)
6969
elseif(WIN32)
70-
target_link_libraries(nativeapi PUBLIC user32 shell32 dwmapi)
70+
target_link_libraries(nativeapi PUBLIC user32 shell32 dwmapi gdiplus crypt32)
7171
endif ()

src/platform/linux/image_linux.cpp

Lines changed: 317 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,317 @@
1+
#include <gtk/gtk.h>
2+
#include <gdk-pixbuf/gdk-pixbuf.h>
3+
#include <glib.h>
4+
#include <cstring>
5+
#include <memory>
6+
#include <string>
7+
#include <vector>
8+
#include "../../foundation/geometry.h"
9+
#include "../../image.h"
10+
11+
namespace nativeapi {
12+
13+
// Linux-specific implementation of Image class using GdkPixbuf
14+
class Image::Impl {
15+
public:
16+
GdkPixbuf* pixbuf_;
17+
std::string source_;
18+
Size size_;
19+
std::string format_;
20+
21+
Impl() : pixbuf_(nullptr), size_({0, 0}), format_("Unknown") {}
22+
23+
~Impl() {
24+
if (pixbuf_) {
25+
g_object_unref(pixbuf_);
26+
}
27+
}
28+
29+
Impl(const Impl& other)
30+
: pixbuf_(nullptr), source_(other.source_), size_(other.size_), format_(other.format_) {
31+
if (other.pixbuf_) {
32+
pixbuf_ = gdk_pixbuf_copy(other.pixbuf_);
33+
}
34+
}
35+
36+
Impl& operator=(const Impl& other) {
37+
if (this != &other) {
38+
if (pixbuf_) {
39+
g_object_unref(pixbuf_);
40+
}
41+
pixbuf_ = nullptr;
42+
source_ = other.source_;
43+
size_ = other.size_;
44+
format_ = other.format_;
45+
if (other.pixbuf_) {
46+
pixbuf_ = gdk_pixbuf_copy(other.pixbuf_);
47+
}
48+
}
49+
return *this;
50+
}
51+
};
52+
53+
Image::Image() : pimpl_(std::make_unique<Impl>()) {}
54+
55+
Image::~Image() = default;
56+
57+
Image::Image(const Image& other) : pimpl_(std::make_unique<Impl>(*other.pimpl_)) {}
58+
59+
Image::Image(Image&& other) noexcept : pimpl_(std::move(other.pimpl_)) {}
60+
61+
std::shared_ptr<Image> Image::FromFile(const std::string& file_path) {
62+
auto image = std::shared_ptr<Image>(new Image());
63+
64+
GError* error = nullptr;
65+
GdkPixbuf* pixbuf = gdk_pixbuf_new_from_file(file_path.c_str(), &error);
66+
67+
if (pixbuf) {
68+
image->pimpl_->pixbuf_ = pixbuf;
69+
image->pimpl_->source_ = file_path;
70+
71+
// Get actual image size
72+
int width = gdk_pixbuf_get_width(pixbuf);
73+
int height = gdk_pixbuf_get_height(pixbuf);
74+
image->pimpl_->size_ = {static_cast<double>(width), static_cast<double>(height)};
75+
76+
// Determine format from file extension
77+
size_t dotPos = file_path.find_last_of('.');
78+
if (dotPos != std::string::npos) {
79+
std::string extension = file_path.substr(dotPos + 1);
80+
// Convert to lowercase
81+
for (auto& c : extension) {
82+
c = std::tolower(c);
83+
}
84+
85+
if (extension == "png") {
86+
image->pimpl_->format_ = "PNG";
87+
} else if (extension == "jpg" || extension == "jpeg") {
88+
image->pimpl_->format_ = "JPEG";
89+
} else if (extension == "gif") {
90+
image->pimpl_->format_ = "GIF";
91+
} else if (extension == "bmp") {
92+
image->pimpl_->format_ = "BMP";
93+
} else if (extension == "tiff" || extension == "tif") {
94+
image->pimpl_->format_ = "TIFF";
95+
} else if (extension == "ico") {
96+
image->pimpl_->format_ = "ICO";
97+
} else if (extension == "svg") {
98+
image->pimpl_->format_ = "SVG";
99+
} else if (extension == "xpm") {
100+
image->pimpl_->format_ = "XPM";
101+
} else {
102+
image->pimpl_->format_ = "Unknown";
103+
}
104+
}
105+
} else {
106+
if (error) {
107+
g_error_free(error);
108+
}
109+
return nullptr;
110+
}
111+
112+
return image;
113+
}
114+
115+
// Helper function to decode base64
116+
static std::vector<unsigned char> DecodeBase64(const std::string& base64_data) {
117+
std::vector<unsigned char> result;
118+
119+
gsize out_len = 0;
120+
guchar* decoded = g_base64_decode(base64_data.c_str(), &out_len);
121+
122+
if (decoded && out_len > 0) {
123+
result.assign(decoded, decoded + out_len);
124+
g_free(decoded);
125+
}
126+
127+
return result;
128+
}
129+
130+
std::shared_ptr<Image> Image::FromBase64(const std::string& base64_data) {
131+
auto image = std::shared_ptr<Image>(new Image());
132+
133+
// Remove data URI prefix if present
134+
std::string cleanBase64 = base64_data;
135+
size_t commaPos = base64_data.find(',');
136+
if (commaPos != std::string::npos) {
137+
cleanBase64 = base64_data.substr(commaPos + 1);
138+
}
139+
140+
// Decode base64
141+
std::vector<unsigned char> imageData = DecodeBase64(cleanBase64);
142+
143+
if (!imageData.empty()) {
144+
GError* error = nullptr;
145+
GInputStream* stream = g_memory_input_stream_new_from_data(
146+
imageData.data(), imageData.size(), nullptr);
147+
148+
GdkPixbuf* pixbuf = gdk_pixbuf_new_from_stream(stream, nullptr, &error);
149+
g_object_unref(stream);
150+
151+
if (pixbuf) {
152+
image->pimpl_->pixbuf_ = pixbuf;
153+
image->pimpl_->source_ = base64_data;
154+
155+
// Get actual image size
156+
int width = gdk_pixbuf_get_width(pixbuf);
157+
int height = gdk_pixbuf_get_height(pixbuf);
158+
image->pimpl_->size_ = {static_cast<double>(width),
159+
static_cast<double>(height)};
160+
161+
// Default assumption for base64 images
162+
image->pimpl_->format_ = "PNG";
163+
} else {
164+
if (error) {
165+
g_error_free(error);
166+
}
167+
return nullptr;
168+
}
169+
} else {
170+
return nullptr;
171+
}
172+
173+
return image;
174+
}
175+
176+
std::shared_ptr<Image> Image::FromSystemIcon(const std::string& icon_name) {
177+
auto image = std::shared_ptr<Image>(new Image());
178+
179+
// Try to load icon from the current icon theme
180+
GtkIconTheme* icon_theme = gtk_icon_theme_get_default();
181+
if (!icon_theme) {
182+
return nullptr;
183+
}
184+
185+
// Try to load icon at default size (48x48)
186+
GtkIconInfo* icon_info = gtk_icon_theme_lookup_icon(
187+
icon_theme, icon_name.c_str(), 48, GTK_ICON_LOOKUP_FORCE_SIZE);
188+
189+
if (icon_info) {
190+
GError* error = nullptr;
191+
GdkPixbuf* pixbuf = gtk_icon_info_load_icon(icon_info, &error);
192+
193+
if (pixbuf) {
194+
image->pimpl_->pixbuf_ = pixbuf;
195+
image->pimpl_->source_ = icon_name;
196+
197+
// Get actual image size
198+
int width = gdk_pixbuf_get_width(pixbuf);
199+
int height = gdk_pixbuf_get_height(pixbuf);
200+
image->pimpl_->size_ = {static_cast<double>(width), static_cast<double>(height)};
201+
image->pimpl_->format_ = "System";
202+
} else {
203+
if (error) {
204+
g_error_free(error);
205+
}
206+
g_object_unref(icon_info);
207+
return nullptr;
208+
}
209+
210+
g_object_unref(icon_info);
211+
} else {
212+
return nullptr;
213+
}
214+
215+
return image;
216+
}
217+
218+
Size Image::GetSize() const {
219+
return pimpl_->size_;
220+
}
221+
222+
std::string Image::GetFormat() const {
223+
return pimpl_->format_;
224+
}
225+
226+
// Helper function to encode to base64
227+
static std::string EncodeBase64(const unsigned char* data, size_t length) {
228+
gchar* encoded = g_base64_encode(data, length);
229+
std::string result(encoded);
230+
g_free(encoded);
231+
return result;
232+
}
233+
234+
std::string Image::ToBase64() const {
235+
if (!pimpl_->pixbuf_) {
236+
return "";
237+
}
238+
239+
gchar* buffer = nullptr;
240+
gsize buffer_size = 0;
241+
GError* error = nullptr;
242+
243+
// Save pixbuf to PNG in memory
244+
gboolean success = gdk_pixbuf_save_to_buffer(
245+
pimpl_->pixbuf_, &buffer, &buffer_size, "png", &error, nullptr);
246+
247+
if (!success || !buffer) {
248+
if (error) {
249+
g_error_free(error);
250+
}
251+
if (buffer) {
252+
g_free(buffer);
253+
}
254+
return "";
255+
}
256+
257+
// Convert to base64
258+
std::string base64String = EncodeBase64(reinterpret_cast<unsigned char*>(buffer), buffer_size);
259+
g_free(buffer);
260+
261+
return "data:image/png;base64," + base64String;
262+
}
263+
264+
bool Image::SaveToFile(const std::string& file_path) const {
265+
if (!pimpl_->pixbuf_) {
266+
return false;
267+
}
268+
269+
// Determine file type from extension
270+
size_t dotPos = file_path.find_last_of('.');
271+
std::string type = "png"; // default
272+
273+
if (dotPos != std::string::npos) {
274+
std::string extension = file_path.substr(dotPos + 1);
275+
// Convert to lowercase
276+
for (auto& c : extension) {
277+
c = std::tolower(c);
278+
}
279+
280+
if (extension == "jpg" || extension == "jpeg") {
281+
type = "jpeg";
282+
} else if (extension == "png") {
283+
type = "png";
284+
} else if (extension == "bmp") {
285+
type = "bmp";
286+
} else if (extension == "ico") {
287+
type = "ico";
288+
} else if (extension == "tiff" || extension == "tif") {
289+
type = "tiff";
290+
}
291+
}
292+
293+
GError* error = nullptr;
294+
gboolean success = FALSE;
295+
296+
if (type == "jpeg") {
297+
// For JPEG, specify quality
298+
success = gdk_pixbuf_save(pimpl_->pixbuf_, file_path.c_str(), type.c_str(),
299+
&error, "quality", "90", nullptr);
300+
} else {
301+
success = gdk_pixbuf_save(pimpl_->pixbuf_, file_path.c_str(), type.c_str(),
302+
&error, nullptr);
303+
}
304+
305+
if (error) {
306+
g_error_free(error);
307+
}
308+
309+
return success == TRUE;
310+
}
311+
312+
void* Image::GetNativeObjectInternal() const {
313+
return pimpl_->pixbuf_;
314+
}
315+
316+
} // namespace nativeapi
317+

0 commit comments

Comments
 (0)