File: texture.hh

package info (click to toggle)
performous 1.1%2Bgit20181118-2
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 11,712 kB
  • sloc: cpp: 30,008; ansic: 2,751; sh: 801; xml: 464; python: 374; makefile: 22
file content (216 lines) | stat: -rw-r--r-- 7,566 bytes parent folder | download | duplicates (3)
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
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
#pragma once

#include "glutil.hh"
#include "image.hh"
#include "video_driver.hh"

#include <cairo.h>

#include <algorithm>
#include <memory>
#include <stdexcept>
#include <string>
#include <vector>

/// class for geometry stuff
class Dimensions {
  public:
	/** Initialize with aspect ratio but no size, centered at screen center. **/
	Dimensions(float ar_ = 0.0f): m_ar(ar_), m_x(), m_y(), m_w(), m_h(), m_xAnchor(), m_yAnchor(), m_screenAnchor() {}
	/** Initialize with top-left corner and width & height **/
	Dimensions(float x1, float y1, float w, float h): m_ar(), m_x(x1), m_y(y1), m_w(w), m_h(h), m_xAnchor(LEFT), m_yAnchor(TOP), m_screenAnchor() {}
	/// sets middle
	Dimensions& middle(float x = 0.0f) { m_x = x; m_xAnchor = MIDDLE; return *this; }
	/// sets left
	Dimensions& left(float x = 0.0f) { m_x = x; m_xAnchor = LEFT; return *this; }
	/// sets right
	Dimensions& right(float x = 0.0f) { m_x = x; m_xAnchor = RIGHT; return *this; }
	/// sets center
	Dimensions& center(float y = 0.0f) { m_y = y; m_yAnchor = CENTER; return *this; }
	/// sets top
	Dimensions& top(float y = 0.0f) { m_y = y; m_yAnchor = TOP; return *this; }
	/// sets bottom
	Dimensions& bottom(float y = 0.0f) { m_y = y; m_yAnchor = BOTTOM; return *this; }
	/// reset aspect ratio and switch to fixed width mode
	Dimensions& ar(float ar) { m_ar = ar; return fixedWidth(m_w); }
	/// fixes width
	Dimensions& fixedWidth(float w) { m_w = w; m_h = w / m_ar; return *this; }
	/// fixes height
	Dimensions& fixedHeight(float h) { m_w = h * m_ar; m_h = h; return *this; }
	/// fits inside
	Dimensions& fitInside(float w, float h) { if (w / h > m_ar) fixedHeight(h); else fixedWidth(w); return *this; }
	/// fits outside
	Dimensions& fitOutside(float w, float h) { if (w / h > m_ar) fixedWidth(w); else fixedHeight(h); return *this; }
	/// stretches dimensions
	Dimensions& stretch(float w, float h) { m_w = w; m_h = h; m_ar = w / h; return *this; }
	/// sets screen center
	Dimensions& screenCenter(float y = 0.0f) { m_screenAnchor = CENTER; center(y); return *this; }
	/// sets screen top
	Dimensions& screenTop(float y = 0.0f) { m_screenAnchor = TOP; top(y); return *this; }
	/// sets screen bottom
	Dimensions& screenBottom(float y = 0.0f) { m_screenAnchor = BOTTOM; bottom(y); return *this; }
	/// move the object without affecting anchoring
	Dimensions& move(float x, float y) { m_x += x; m_y += y; return *this; }
	/// returns ar XXX
	float ar() const { return m_ar; }
	/// returns left
	float x1() const {
		switch (m_xAnchor) {
		  case LEFT: return m_x;
		  case MIDDLE: return m_x - 0.5 * m_w;
		  case RIGHT: return m_x - m_w;
		}
		throw std::logic_error("Unknown value in Dimensions::m_xAnchor");
	}
	/// returns top
	float y1() const {
		switch (m_yAnchor) {
		  case TOP: return screenY() + m_y;
		  case CENTER: return screenY() + m_y - 0.5 * m_h;
		  case BOTTOM: return screenY() + m_y - m_h;
		}
		throw std::logic_error("Unknown value in Dimensions::m_yAnchor");
	}
	/// returns right
	float x2() const { return x1() + w(); }
	/// returns bottom
	float y2() const { return y1() + h(); }
	/// returns x center
	float xc() const { return x1() + 0.5 * w(); }
	/// returns y center
	float yc() const { return y1() + 0.5 * h(); }
	/// returns width
	float w() const { return m_w; }
	/// returns height
	float h() const { return m_h; }

  private:
	float screenY() const;
	float m_ar;
	float m_x, m_y, m_w, m_h;
	enum XAnchor { MIDDLE, LEFT, RIGHT } m_xAnchor;
	enum YAnchor { CENTER, TOP, BOTTOM } m_yAnchor, m_screenAnchor;
};

/// texture coordinates
struct TexCoords {
	float x1; ///< left
	float y1; ///< top
	float x2; ///< right
	float y2; ///< bottom
	/// constructor
	TexCoords(float x1_ = 0.0, float y1_ = 0.0, float x2_ = 1.0, float y2_ = 1.0):
	  x1(x1_), y1(y1_), x2(x2_), y2(y2_) {}
	bool outOfBounds() const {
		return test(x1) || test(y1) || test(x2) || test(y2);
	}
private:
	static bool test(float x) { return x < 0.0 || x > 1.0; }
};

/// This function hides the ugly global vari-- I mean singleton access to ScreenManager...
Shader& getShader(std::string const& name);

/** @short A RAII wrapper for allocating/deallocating OpenGL texture ID **/
template <GLenum Type> class OpenGLTexture {
  public:
	OpenGLTexture(const OpenGLTexture&) = delete;
  	const OpenGLTexture& operator=(const OpenGLTexture&) = delete;
	/// return Type
	static GLenum type() { return Type; };
	static Shader& shader() { return getShader("texture"); }
	OpenGLTexture(): m_id() { glGenTextures(1, &m_id); }
	~OpenGLTexture() { glDeleteTextures(1, &m_id); }
	/// returns id
	GLuint id() const { return m_id; };
	/// draw in given dimensions, with given texture coordinates
	void draw(Dimensions const& dim, TexCoords const& tex = TexCoords()) const;
	/// draw a subsection of the orig dimensions, cropping by tex
	void drawCropped(Dimensions const& orig, TexCoords const& tex) const;
  private:
	GLuint m_id;
};

/** @short A RAII wrapper for binding to a texture (using it, modifying it) **/
class UseTexture {
  public:
  	UseTexture(const UseTexture&) = delete;
  	const UseTexture& operator=(const UseTexture&) = delete;
	/// constructor
	template <GLenum Type> UseTexture(OpenGLTexture<Type> const& tex):
	  m_shader(/* hack of the year */ (glutil::GLErrorChecker("UseTexture"), glActiveTexture(GL_TEXTURE0), glBindTexture(Type, tex.id()), tex.shader())) {}

  private:
	UseShader m_shader;
};

template <GLenum Type> void OpenGLTexture<Type>::draw(Dimensions const& dim, TexCoords const& tex) const {
	glutil::GLErrorChecker glerror("OpenGLTexture::draw()");
	glutil::VertexArray va;

	UseTexture texture(*this);
	glerror.check("texture");

	// The texture wraps over at the edges (repeat)
	const bool repeating = tex.outOfBounds();
	glTexParameterf(type(), GL_TEXTURE_WRAP_S, repeating ? GL_REPEAT : GL_CLAMP_TO_EDGE);
	glTexParameterf(type(), GL_TEXTURE_WRAP_T, repeating ? GL_REPEAT : GL_CLAMP_TO_EDGE);
	glerror.check("repeat mode");

	va.texCoord(tex.x1, tex.y1).vertex(dim.x1(), dim.y1());
	va.texCoord(tex.x2, tex.y1).vertex(dim.x2(), dim.y1());
	va.texCoord(tex.x1, tex.y2).vertex(dim.x1(), dim.y2());
	va.texCoord(tex.x2, tex.y2).vertex(dim.x2(), dim.y2());

	va.draw();
}

template <GLenum Type> void OpenGLTexture<Type>::drawCropped(Dimensions const& orig, TexCoords const& tex) const {
	Dimensions dim(
	  orig.x1() + tex.x1 * orig.w(),
	  orig.y1() + tex.y1 * orig.h(),
	  orig.w() * (tex.x2 - tex.x1),
	  orig.h() * (tex.y2 - tex.y1)
	);
	draw(dim, tex);
}

void updateTextures();

/**
* @short High level texture/image wrapper on top of OpenGLTexture
**/
class Texture: public OpenGLTexture<GL_TEXTURE_2D> {
public:
	struct Impl;
	/// dimensions
	Dimensions dimensions;
	/// texture coordinates
	TexCoords tex;
	Texture(): m_width(0), m_height(0), m_premultiplied(true) {}
	/// creates texture from file
	Texture(fs::path const& filename);
	~Texture();
	bool empty() const { return m_width * m_height == 0; } ///< Test if the loading has failed
	/// draws texture
	void draw() const;
	using OpenGLTexture<GL_TEXTURE_2D>::draw;
	/// loads texture into buffer
	void load(Bitmap const& bitmap, bool isText = false);
	Shader& shader() { return m_texture.shader(); }
	float width() const { return m_width; }
	float height() const { return m_height; }
private:
	float m_width, m_height;
	bool m_premultiplied;
	OpenGLTexture<GL_TEXTURE_2D> m_texture;
};

/// A RAII wrapper for texture loading worker thread. There must be exactly one (global) instance whenever any Textures exist.
class TextureLoader {
public:
	TextureLoader();
	~TextureLoader();
	class Impl;
};