forked from vpinball/vpinball
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Texture.h
180 lines (154 loc) · 6 KB
/
Texture.h
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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
#pragma once
#define MIN_TEXTURE_SIZE 8
struct FIBITMAP;
// texture stored in main memory in 32bit ARGB uchar format or 96bit RGB float
class BaseTexture
{
public:
enum Format
{
RGBA,
RGB_FP
};
BaseTexture()
: m_width(0), m_height(0), m_realWidth(0), m_realHeight(0), m_format(RGBA), m_has_alpha(false)
{ }
BaseTexture(const int w, const int h, const Format format, const bool has_alpha)
: m_width(w), m_height(h), m_data((format == RGBA ? 4 : 3*4) * (w*h)), m_realWidth(w), m_realHeight(h), m_format(format), m_has_alpha(has_alpha)
{ }
int width() const { return m_width; }
int height() const { return m_height; }
int pitch() const { return (m_format == RGBA ? 4 : 3*4) * m_width; } // pitch in bytes
BYTE* data() { return m_data.data(); }
private:
int m_width;
int m_height;
public:
std::vector<BYTE> m_data;
int m_realWidth, m_realHeight;
Format m_format;
bool m_has_alpha;
bool Needs_ConvertAlpha_Tonemap() const { return (m_format == RGB_FP) || ((m_format == RGBA) && m_has_alpha); }
void CopyTo_ConvertAlpha_Tonemap(BYTE* const __restrict bits) const // premultiplies alpha (as Win32 AlphaBlend() wants it like that) OR converts rgb_fp format to 32bits
{
if(m_format == RGB_FP) // Tonemap for 8bpc-Display
{
const float * const __restrict src = (float*)m_data.data();
unsigned int o = 0;
for (int j = 0; j < m_height; ++j)
for (int i = 0; i < m_width; ++i, ++o)
{
const float r = src[o * 3];
const float g = src[o * 3 + 1];
const float b = src[o * 3 + 2];
const float l = r*0.176204f + g*0.812985f + b*0.0108109f;
const float n = (l*(float)(255.*0.25) + 255.0f) / (l + 1.0f); // simple tonemap and scale by 255, overflow is handled by clamp below
((DWORD*)bits)[o] = (int)clamp(b*n, 0.f, 255.f) |
((int)clamp(g*n, 0.f, 255.f)<< 8) |
((int)clamp(r*n, 0.f, 255.f)<<16) |
( 255u <<24);
}
}
else
{
if (!m_has_alpha)
memcpy(bits, m_data.data(), m_height * pitch());
else
if (GetWinVersion() >= 2600) // For everything newer than Windows XP: use the alpha in the bitmap, thus RGB needs to be premultiplied with alpha, due to how AlphaBlend() works
{
unsigned int o = 0;
for (int j = 0; j < m_height; ++j)
for (int i = 0; i < m_width; ++i, ++o)
{
const unsigned int src = ((DWORD*)m_data.data())[o];
const unsigned int alpha = src>>24;
if (alpha == 0) // adds a checkerboard where completely transparent (for the image manager display)
{
const DWORD c = ((((i >> 4) ^ (j >> 4)) & 1) << 7) + 127;
((DWORD*)bits)[o] = c | (c<<8) | (c<<16) | (0<<24);
}
else if (alpha != 255) // premultiply alpha for win32 AlphaBlend()
{
((DWORD*)bits)[o] = (( (src &0xFF) * alpha) >> 8) |
(((((src>> 8)&0xFF) * alpha) >> 8)<< 8) |
(((((src>>16)&0xFF) * alpha) >> 8)<<16) |
( alpha <<24);
}
else
((DWORD*)bits)[o] = src;
}
}
else // adds a checkerboard pattern where alpha is set to output bits
{
unsigned int o = 0;
for (int j = 0; j < m_height; ++j)
for (int i = 0; i < m_width; ++i, ++o)
{
const unsigned int src = ((DWORD*)m_data.data())[o];
const unsigned int alpha = src>>24;
if (alpha != 255)
{
const unsigned int c = (((((i >> 4) ^ (j >> 4)) & 1) << 7) + 127) * (255 - alpha);
((DWORD*)bits)[o] = (( (src &0xFF) * alpha + c) >> 8) |
(((((src>> 8)&0xFF) * alpha + c) >> 8)<< 8) |
(((((src>>16)&0xFF) * alpha + c) >> 8)<<16) |
( alpha <<24);
}
else
((DWORD*)bits)[o] = src;
}
}
}
}
static BaseTexture *CreateFromHBitmap(const HBITMAP hbm);
static BaseTexture *CreateFromFile(const string& filename);
static BaseTexture *CreateFromFreeImage(FIBITMAP* dib); // also free's/delete's the dib inside!
static BaseTexture *CreateFromData(const void *data, const size_t size);
};
class Texture : public ILoadable
{
public:
Texture();
Texture(BaseTexture * const base);
virtual ~Texture();
// ILoadable callback
virtual bool LoadToken(const int id, BiffReader * const pbr);
HRESULT SaveToStream(IStream *pstream, const PinTable *pt);
HRESULT LoadFromStream(IStream *pstream, int version, PinTable *pt);
void FreeStuff();
void CreateGDIVersion();
BaseTexture *CreateFromHBitmap(const HBITMAP hbm);
void CreateFromResource(const int id);
bool IsHDR() const
{
if (m_pdsBuffer == nullptr)
return false;
else
return (m_pdsBuffer->m_format == BaseTexture::RGB_FP);
}
void SetSizeFrom(const BaseTexture* const tex)
{
m_width = tex->width();
m_height = tex->height();
m_realWidth = tex->m_realWidth;
m_realHeight = tex->m_realHeight;
}
// create/release a DC which contains a (read-only) copy of the texture; for editor use
void GetTextureDC(HDC *pdc);
void ReleaseTextureDC(HDC dc);
private:
bool LoadFromMemory(BYTE * const data, const DWORD size);
public:
// width and height of texture can be different than width and height
// of m_pdsBuffer, since the surface can be limited to smaller sizes by the user
int m_width, m_height;
int m_realWidth, m_realHeight;
float m_alphaTestValue;
BaseTexture* m_pdsBuffer;
HBITMAP m_hbmGDIVersion; // HBitmap at screen depth and converted/visualized alpha so GDI draws it fast
PinBinary *m_ppb; // if this image should be saved as a binary stream, otherwise just LZW compressed from the live bitmap
string m_szName;
string m_szPath;
private:
HBITMAP m_oldHBM; // this is to cache the result of SelectObject()
};