Ik zit al een tijdje met het volgende probleem en vroeg me af wat men hier van mijn voorlopige oplossing denkt:
Men leest ruwe data uit een file, een sequence bytes, en daarbij staan twee descriptors:het data type, en de dimensie van het type (formaat). Dit is goed te vergelijken met een image bestand. Dat kan 1x 8bit grayscale zijn, 3x 8bit RGB-kleur, 1x 32bit float, etc.
Nu wil ik dynamisch, at run-time, deze data uit kunnen lezen in een gewenst formaat. Vergelijkbaar met OpenGL texture reads. Als je een texture read doet krijg je een vector met 4 floats, ongeacht het onderliggende type en formaat. Zonodig treed er conversie op van bijvoorbeeld int[0,255] naar float[0,1].
Hiervoor heb ik deze C++11 code geschreven (sorry voor de lengte... maar het spreekt wel voor zich denk ik)
Vector classes werken zoals je zou denken. Code kan iets uitgebreid worden; dit is een simpel voorbeeld.
Aangezien het dynamisch moet gebeuren zie ik geen snellere algemene oplossing dan 1 indirecte call (virtual in dit geval) per gelezen pixel. Kan dat sneller als je over vele pixels itereert? Wat vindt men van deze oplossing? Zit ik op het goede spoor? Zie ik iets over het hoofd dat het simpeler/beter maakt?
Waar ik me ook aan stoor zijn die 8 virtual functions. In principe kan dat als template, maar virtual templates kunnen niet (vrij logisch als je de grootte van je vtable niet kan bepalen)...
Comments?
Men leest ruwe data uit een file, een sequence bytes, en daarbij staan twee descriptors:het data type, en de dimensie van het type (formaat). Dit is goed te vergelijken met een image bestand. Dat kan 1x 8bit grayscale zijn, 3x 8bit RGB-kleur, 1x 32bit float, etc.
Nu wil ik dynamisch, at run-time, deze data uit kunnen lezen in een gewenst formaat. Vergelijkbaar met OpenGL texture reads. Als je een texture read doet krijg je een vector met 4 floats, ongeacht het onderliggende type en formaat. Zonodig treed er conversie op van bijvoorbeeld int[0,255] naar float[0,1].
Hiervoor heb ik deze C++11 code geschreven (sorry voor de lengte... maar het spreekt wel voor zich denk ik)
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
| namespace FS { class VC { public: typedef unsigned char base_type; VC() {} virtual ~VC() {} virtual float floatAt(int i) = 0; virtual vec2<float> vec2fAt(int i) = 0; virtual vec3<float> vec3fAt(int i) = 0; virtual vec4<float> vec4fAt(int i) = 0; virtual int intAt(int i) = 0; virtual vec2<int> vec2iAt(int i) = 0; virtual vec3<int> vec3iAt(int i) = 0; virtual vec4<int> vec4iAt(int i) = 0; virtual unsigned int size() const = 0; void loadRawData(const unsigned char* data, size_t size) { m_data.resize(size); memcpy(m_data.data(), data, size); } static std::shared_ptr<VC> create(GLenum type, GLenum format) { auto it = registry.find(std::make_pair(type, format)); if (it == registry.end()) { return std::shared_ptr<VC>(); } else { return std::shared_ptr<VC>(it->second()); } } static void reg(GLenum type, GLenum format, std::function<VC*()> func) { registry[std::make_pair(type, format)] = func; } protected: std::vector<base_type> m_data; private: static std::map<std::pair<GLenum, GLenum>, std::function<VC*()> > registry; }; template <typename F, typename T> T convert(const F& v) { return std::is_integral<F>::value ? (std::is_integral<T>::value ? static_cast<T>(v) : static_cast<T>(v) / std::numeric_limits<F>::max()) : (static_cast<T>(v)) ; } constexpr int const_min(int a, int b) { return a < b ? a : b; } template <typename U, int C> class ConcreteVC : public VC { public: typedef U value_type; const static int components = C; ConcreteVC() { } virtual ~ConcreteVC() {} virtual unsigned int size() const { return m_data.size() / (sizeof(U) * C); } inline U& itemAt(int i, int offset) { return reinterpret_cast<U*>(m_data.data())[i * components + offset]; } template <typename T> inline T itemAtAs(int i, int offset) { return convert<U, T>(itemAt(i, offset)); } virtual float floatAt(int i) { return itemAtAs<float>(i, 0); } virtual vec2<float> vec2fAt(int i) { return vec2<float>(itemAtAs<float>(i, 0), itemAtAs<float>(i, const_min(1, components-1))); } virtual vec3<float> vec3fAt(int i) { return vec3<float>(itemAtAs<float>(i, 0), itemAtAs<float>(i, const_min(1, components-1)), itemAtAs<float>(i, const_min(2, components-1))); } virtual vec4<float> vec4fAt(int i) { return vec4<float>(itemAtAs<float>(i, 0), itemAtAs<float>(i, const_min(1, components-1)), itemAtAs<float>(i, const_min(2, components-1)), itemAtAs<float>(i, const_min(3, components-1))); } virtual int intAt(int i) { return itemAtAs<int>(i, 0); } virtual vec2<int> vec2iAt(int i) { return vec2<int>(itemAtAs<int>(i, 0), itemAtAs<int>(i, const_min(1, components-1))); } virtual vec3<int> vec3iAt(int i) { return vec3<int>(itemAtAs<int>(i, 0), itemAtAs<int>(i, const_min(1, components-1)), itemAtAs<int>(i, const_min(2, components-1))); } virtual vec4<int> vec4iAt(int i) { return vec4<int>(itemAtAs<int>(i, 0), itemAtAs<int>(i, const_min(1, components-1)), itemAtAs<int>(i, const_min(2, components-1)), itemAtAs<int>(i, const_min(3, components-1))); } }; } // namespace int main() { FS::VC::reg(GL_UNSIGNED_BYTE, GL_RED, [](){return new FS::ConcreteVC<unsigned char, 1>();}); FS::VC::reg(GL_UNSIGNED_BYTE, GL_RGB, [](){return new FS::ConcreteVC<unsigned char, 3>();}); std::vector<unsigned char> data = {42,127,128,255,0,1}; GLenum dataFormat = GL_RED; GLenum dataType = GL_UNSIGNED_BYTE; auto vc = FS::VC::create(dataType, dataFormat); vc->loadRawData(data.data(), data.size()); for (unsigned int i=0; i<vc->size(); ++i) { qDebug() << toQString(vc->intAt(i)) << "\t" << toQString(vc->vec3fAt(i)); } return 0; } // Output: // "42" "[0.164706, 0.164706, 0.164706]" // "127" "[0.498039, 0.498039, 0.498039]" // "128" "[0.501961, 0.501961, 0.501961]" // "255" "[1, 1, 1]" // "0" "[0, 0, 0]" // "1" "[0.00392157, 0.00392157, 0.00392157]" |
Vector classes werken zoals je zou denken. Code kan iets uitgebreid worden; dit is een simpel voorbeeld.
Aangezien het dynamisch moet gebeuren zie ik geen snellere algemene oplossing dan 1 indirecte call (virtual in dit geval) per gelezen pixel. Kan dat sneller als je over vele pixels itereert? Wat vindt men van deze oplossing? Zit ik op het goede spoor? Zie ik iets over het hoofd dat het simpeler/beter maakt?
Waar ik me ook aan stoor zijn die 8 virtual functions. In principe kan dat als template, maar virtual templates kunnen niet (vrij logisch als je de grootte van je vtable niet kan bepalen)...
Comments?