Gamp v0.0.7-54-gccdc599
Gamp: Graphics, Audio, Multimedia and Processing
Loading...
Searching...
No Matches
Texture.hpp
Go to the documentation of this file.
1/*
2 * Author: Sven Gothel <sgothel@jausoft.com>
3 * Copyright Gothel Software e.K.
4 *
5 * SPDX-License-Identifier: MIT
6 *
7 * This Source Code Form is subject to the terms of the MIT License
8 * If a copy of the MIT was not distributed with this file,
9 * you can obtain one at https://opensource.org/license/mit/.
10 */
11#ifndef GAMP_RENDER_GL_TEXTURE_TEXTURE_HPP_
12#define GAMP_RENDER_GL_TEXTURE_TEXTURE_HPP_
13
14#include <jau/basic_types.hpp>
15#include <jau/int_math.hpp>
16#include <jau/math/recti.hpp>
17#include <jau/math/vec2i.hpp>
18#include <jau/string_util.hpp>
19
20#include <gamp/GampTypes.hpp>
26
28 /** \addtogroup Gamp_GL
29 *
30 * @{
31 */
32
34
35 struct TextureData {
36 };
37
38 /**
39 * Represents an OpenGL texture object. Contains convenience routines
40 * for enabling/disabling OpenGL texture state, binding this texture,
41 * and computing texture coordinates for both the entire image as well
42 * as a sub-image.
43 *
44 * <a name="textureCallOrder"><h5>Order of Texture Commands</h5></a>
45 * <p>
46 * Due to many confusions w/ texture usage, following list described the order
47 * and semantics of texture unit selection, binding and enabling.
48 * <ul>
49 * <li><i>Optional:</i> Set active textureUnit via <code>gl.glActiveTexture(GL.GL_TEXTURE0 + textureUnit)</code>, <code>0</code> is default.</li>
50 * <li>Bind <code>textureId</code> -> active <code>textureUnit</code>'s <code>textureTarget</code> via <code>gl.glBindTexture(textureTarget, textureId)</code></li>
51 * <li><i>Compatible Context Only:</i> Enable active <code>textureUnit</code>'s <code>textureTarget</code> via <code>glEnable(textureTarget)</code>.
52 * <li><i>Optional:</i> Fiddle with the texture parameters and/or environment settings.</li>
53 * <li>GLSL: Use <code>textureUnit</code> in your shader program, enable shader program.</li>
54 * <li>Issue draw commands</li>
55 * </ul>
56 * </p>
57 *
58 * <p><a name="nonpow2"><b>Non-power-of-two restrictions</b></a>
59 * <br> When creating an OpenGL texture object, the Texture class will
60 * attempt to use <i>non-power-of-two textures</i> (NPOT) if available, see {@link GL#isNPOTTextureAvailable()}.
61 * Further more,
62 * <a href="http://www.opengl.org/registry/specs/ARB/texture_rectangle.txt">GL_ARB_texture_rectangle</a>
63 * (RECT) will be attempted on OSX w/ ATI drivers.
64 * If NPOT is not available or RECT not chosen, the Texture class will simply upload a non-pow2-sized
65 * image into a standard pow2-sized texture (without any special
66 * scaling).
67 * Since the choice of extension (or whether one is used at
68 * all) depends on the user's machine configuration, developers are
69 * recommended to use {@link #getImageTexCoords} and {@link
70 * #getSubImageTexCoords}, as those methods will calculate the
71 * appropriate texture coordinates for the situation.
72 *
73 * <p>One caveat in this approach is that certain texture wrap modes
74 * (e.g. <code>GL_REPEAT</code>) are not legal when the GL_ARB_texture_rectangle
75 * extension is in use. Another issue to be aware of is that in the
76 * default pow2 scenario, if the original image does not have pow2
77 * dimensions, then wrapping may not work as one might expect since
78 * the image does not extend to the edges of the pow2 texture. If
79 * texture wrapping is important, it is recommended to use only
80 * pow2-sized images with the Texture class.
81 *
82 * <p><a name="perftips"><b>Performance Tips</b></a>
83 * <br> For best performance, try to avoid calling {@link #enable} /
84 * {@link #bind} / {@link #disable} any more than necessary. For
85 * example, applications using many Texture objects in the same scene
86 * may want to reduce the number of calls to both {@link #enable} and
87 * {@link #disable}. To do this it is necessary to call {@link
88 * #getTarget} to make sure the OpenGL texture target is the same for
89 * all of the Texture objects in use; non-power-of-two textures using
90 * the GL_ARB_texture_rectangle extension use a different target than
91 * power-of-two textures using the GL_TEXTURE_2D target. Note that
92 * when switching between textures it is necessary to call {@link
93 * #bind}, but when drawing many triangles all using the same texture,
94 * for best performance only one call to {@link #bind} should be made.
95 * User may also utilize multiple texture units,
96 * see <a href="#textureCallOrder"> order of texture commands above</a>.
97 *
98 * <p><a name="premult"><b>Alpha premultiplication and blending</b></a>
99 * <p>
100 * <i>Disclaimer: Consider performing alpha premultiplication in shader code, if really desired! Otherwise use RGBA.</i><br/>
101 * </p>
102 * <p>
103 * The Texture class does not convert RGBA image data into
104 * premultiplied data when storing it into an OpenGL texture.
105 * </p>
106 * <p>
107 * The mathematically correct way to perform blending in OpenGL
108 * with the SrcOver "source over destination" mode, or any other
109 * Porter-Duff rule, is to use <i>premultiplied color components</i>,
110 * which means the R/G/ B color components must have been multiplied by
111 * the alpha value. If using <i>premultiplied color components</i>
112 * it is important to use the correct blending function; for
113 * example, the SrcOver rule is expressed as:
114 <pre>
115 ::glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
116 </pre>
117 * Also, when using a texture function like <code>GL_MODULATE</code> where
118 * the current color plays a role, it is important to remember to make
119 * sure that the color is specified in a premultiplied form, for
120 * example:
121 <pre>
122 float a = ...;
123 float r = r * a;
124 float g = g * a;
125 float b = b * a;
126 ::glColor4f(r, g, b, a);
127 </pre>
128 *
129 * For reference, here is a list of the Porter-Duff compositing rules
130 * and the associated OpenGL blend functions (source and destination
131 * factors) to use in the face of premultiplied alpha:
132 *
133 <CENTER>
134 <TABLE WIDTH="75%">
135 <TR> <TD> Rule <TD> Source <TD> Dest
136 <TR> <TD> Clear <TD> GL_ZERO <TD> GL_ZERO
137 <TR> <TD> Src <TD> GL_ONE <TD> GL_ZERO
138 <TR> <TD> SrcOver <TD> GL_ONE <TD> GL_ONE_MINUS_SRC_ALPHA
139 <TR> <TD> DstOver <TD> GL_ONE_MINUS_DST_ALPHA <TD> GL_ONE
140 <TR> <TD> SrcIn <TD> GL_DST_ALPHA <TD> GL_ZERO
141 <TR> <TD> DstIn <TD> GL_ZERO <TD> GL_SRC_ALPHA
142 <TR> <TD> SrcOut <TD> GL_ONE_MINUS_DST_ALPHA <TD> GL_ZERO
143 <TR> <TD> DstOut <TD> GL_ZERO <TD> GL_ONE_MINUS_SRC_ALPHA
144 <TR> <TD> Dst <TD> GL_ZERO <TD> GL_ONE
145 <TR> <TD> SrcAtop <TD> GL_DST_ALPHA <TD> GL_ONE_MINUS_SRC_ALPHA
146 <TR> <TD> DstAtop <TD> GL_ONE_MINUS_DST_ALPHA <TD> GL_SRC_ALPHA
147 <TR> <TD> AlphaXor <TD> GL_ONE_MINUS_DST_ALPHA <TD> GL_ONE_MINUS_SRC_ALPHA
148 </TABLE>
149 </CENTER>
150 */
151 class Texture {
152 private:
153 /** The GL target type for this texture. */
154 GLenum m_target;
155 /** The image GL target type for this texture, or its sub-components if cubemap. */
156 GLenum m_imageTarget;
157 /** The GL texture ID. */
158 GLuint m_texID;
159 /** Owning texture texID */
160 bool m_ownsTextureID;
161 Point2u32 m_texSize;
162 Point2u32 m_imgSize;
163 /** The original aspect ratio of the image, before any rescaling
164 that might have occurred due to using the GLU mipmap routines. */
165 float m_aspectRatio;
166 /** Indicates whether the TextureData requires a vertical flip of
167 the texture coords. */
168 bool m_mustFlipVertically;
169 /** Indicates whether we're using automatic mipmap generation
170 support (GL_GENERATE_MIPMAP). */
171 bool m_usingAutoMipmapGeneration;
172
173 /** The texture coordinates corresponding to the entire image. */
174 TextureCoords m_coords;
175
176 /** An estimate of the amount of texture memory this texture consumes. */
177 size_t m_estimatedMemorySize;
178
179 constexpr static bool DEBUG_MODE = false; // Debug.debug("Texture");
180 constexpr static bool VERBOSE = false; // Debug.verbose();
181
182 public:
183 std::string toString() const noexcept {
184 std::string r("Texture[target ");
185 r.append(jau::toHexString(m_target));
186 if( m_target == m_imageTarget ) {
187 r.append(" - image ").append(jau::toHexString(m_imageTarget));
188 }
189 r.append(", name ").append(std::to_string(m_texID)).append(", {");
190 r.append(m_imgSize.toString()).append("} / {").append(m_texSize.toString()).append("}, y-flip ")
191 .append(jau::to_string(m_mustFlipVertically)).append(", ")
192 .append(std::to_string(m_estimatedMemorySize)).append(" bytes]");
193 return r;
194 }
195
197 : m_target(0), m_imageTarget(0), m_texID(0), m_ownsTextureID(true),
198 m_texSize(), m_imgSize(),
199 m_aspectRatio(1), m_mustFlipVertically(false), m_usingAutoMipmapGeneration(false),
200 m_coords(), m_estimatedMemorySize(0)
201 {
203 }
204
205 /**
206 * Constructor for use when creating e.g. cube maps, where there is
207 * no initial texture data
208 * @param target the OpenGL texture target, eg GL.GL_TEXTURE_2D,
209 * GL2.GL_TEXTURE_RECTANGLE
210 */
212 : m_target(target), m_imageTarget(target), m_texID(0), m_ownsTextureID(true),
213 m_texSize(), m_imgSize(),
214 m_aspectRatio(1), m_mustFlipVertically(false), m_usingAutoMipmapGeneration(false),
215 m_coords(), m_estimatedMemorySize(0)
216 { }
217
218 /**
219 * Constructor to wrap an OpenGL texture ID from an external library and allows
220 * some of the base methods from the Texture class, such as
221 * binding and querying of texture coordinates, to be used with
222 * it. Attempts to update such textures' contents will yield
223 * undefined results.
224 *
225 * @param textureID the valid OpenGL texture object to wrap
226 * @param ownsTextureID pass {@code true} if this {@link Texture} instance takes ownership of {@code textureID} texture
227 * and {@link GL#glDeleteTextures(int, int[], int) deletes the texture} at {@link #destroy(GL)}.
228 * Otherwise, if {@code false}, {@code textureID} texture will not be {@link GL#glDeleteTextures(int, int[], int) deleted} at {@link #destroy(GL)}.
229 * @param target the OpenGL texture target, eg GL.GL_TEXTURE_2D,
230 * GL2.GL_TEXTURE_RECTANGLE
231 * @param texWidth the width of the texture in pixels
232 * @param texHeight the height of the texture in pixels
233 * @param imgWidth the width of the image within the texture in
234 * pixels (if the content is a sub-rectangle in the upper
235 * left corner); otherwise, pass in texWidth
236 * @param imgHeight the height of the image within the texture in
237 * pixels (if the content is a sub-rectangle in the upper
238 * left corner); otherwise, pass in texHeight
239 * @param mustFlipVertically indicates whether the texture
240 * coordinates must be flipped vertically
241 * in order to properly display the
242 * texture
243 */
244 Texture(GLuint textureID, bool ownsTextureID,
245 GLenum target,
246 const Point2u32& texSize, const Point2u32& imgSize,
247 bool mustFlipVertically)
248 : m_target(target), m_imageTarget(target), m_texID(textureID), m_ownsTextureID(ownsTextureID),
249 m_texSize(texSize), m_imgSize(imgSize),
250 m_aspectRatio((float) imgSize.x / (float) imgSize.y),
251 m_mustFlipVertically(mustFlipVertically), m_usingAutoMipmapGeneration(false),
252 m_coords(), m_estimatedMemorySize(0)
253 {
254 updateTexCoords();
255 if ( 0 == m_texID ) {
256 throw RenderException("External texture ID invalid: texID "+std::to_string(textureID), E_FILE_LINE);
257 }
258 }
259 /**
260 * Pending setup or update of texture and image dimensions
261 * @param texSize the texture dimension in pixels
262 * @param imgSize the image dimension within the texture in
263 * pixels (if the content is a sub-rectangle in the upper
264 * left corner); otherwise, pass in texSize
265 */
266 void set(const Point2u32& texSize, const Point2u32& imgSize) {
267 m_texSize = texSize;
268 m_imgSize = imgSize;
269 m_aspectRatio = (float) imgSize.x / (float) imgSize.y;
270 updateTexCoords();
271 }
272
273 /**
274 * Enables this texture's target (e.g., GL_TEXTURE_2D) in the
275 * given GL context's state. This method is a shorthand equivalent
276 * of the following OpenGL code:
277 * <pre>
278 * gl.glEnable(texture.getTarget());
279 * </pre>
280 * <p>
281 * Call is ignored if the {@link GL} object's context
282 * is using a core profile, see GLProfile::isGLcore(),
283 * or if {@link #getTarget()} is GLES2's GL_TEXTURE_EXTERNAL_OES.
284 * </p>
285 * <p>
286 * See the <a href="#perftips">performance tips</a> above for hints
287 * on how to maximize performance when using many Texture objects.
288 * </p>
289 * @param gl the current GL object
290 *
291 * @throws GLException if no OpenGL context was current or if any
292 * OpenGL-related errors occurred
293 */
294 void enable(const GL& gl) const noexcept {
295 if( !gl.glProfile().isGLcore() && GL_TEXTURE_EXTERNAL_OES != m_target) {
296 ::glEnable(m_target);
297 }
298 }
299
300 /**
301 * Disables this texture's target (e.g., GL_TEXTURE_2D) in the
302 * given GL state. This method is a shorthand equivalent
303 * of the following OpenGL code:
304 * <pre>
305 * gl.glDisable(texture.getTarget());
306 * </pre>
307 * <p>
308 * Call is ignored if the {@link GL} object's context
309 * is using a core profile, see {@link GL#isGLcore()},
310 * or if {@link #getTarget()} is {@link GLES2#GL_TEXTURE_EXTERNAL_OES}.
311 * </p>
312 * <p>
313 * See the <a href="#perftips">performance tips</a> above for hints
314 * on how to maximize performance when using many Texture objects.
315 * </p>
316 * @param gl the current GL object
317 *
318 * @throws GLException if no OpenGL context was current or if any
319 * OpenGL-related errors occurred
320 */
321 void disable(const GL& gl) const noexcept {
322 if( !gl.glProfile().isGLcore() && GL_TEXTURE_EXTERNAL_OES != m_target) {
323 ::glDisable(m_target);
324 }
325 }
326
327 /**
328 * Binds this texture to the given GL context. This method is a
329 * shorthand equivalent of the following OpenGL code:
330 <pre>
331 gl.glBindTexture(texture.getTarget(), texture.getTextureObject());
332 </pre>
333 *
334 * See the <a href="#perftips">performance tips</a> above for hints
335 * on how to maximize performance when using many Texture objects.
336 *
337 * @param gl the current GL context
338 * @throws GLException if no OpenGL context was current or if any
339 * OpenGL-related errors occurred
340 */
341 void bind(const GL& gl) noexcept {
342 validateTexID(gl, true);
343 ::glBindTexture(m_target, m_texID);
344 }
345
346 /**
347 * Destroys and {@code null}s the {@link #getTextureObject() underlying native texture} used by this {@link Texture} instance
348 * if {@link #ownsTexture() owned}, otherwise just {@code null}s the {@link #getTextureObject() underlying native texture}.
349 *
350 * @throws GLException if any OpenGL-related errors occurred
351 */
352 void destroy(const GL&) noexcept {
353 if( 0 != m_texID && m_ownsTextureID ) {
354 ::glDeleteTextures(1, &m_texID);
355 }
356 m_texID = 0;
357 }
358
359 /**
360 * Returns the OpenGL "target" of this texture.
361 * @see com.jogamp.opengl.GL#GL_TEXTURE_2D
362 * @see com.jogamp.opengl.GL2#GL_TEXTURE_RECTANGLE_ARB
363 */
364 constexpr GLenum target() const noexcept { return m_target; }
365
366 /**
367 * Returns the image OpenGL "target" of this texture, or its sub-components if cubemap.
368 * @see com.jogamp.opengl.GL#GL_TEXTURE_2D
369 * @see com.jogamp.opengl.GL2#GL_TEXTURE_RECTANGLE_ARB
370 */
371 constexpr GLenum imageTarget() const noexcept { return m_imageTarget; }
372
373 /**
374 * Returns the dimension of the allocated OpenGL texture in pixels.
375 * Note that the texture width will be greater than or equal to the
376 * width of the image contained within.
377 */
378 constexpr const Point2u32& texSize() const noexcept { return m_texSize; }
379
380 /**
381 * Returns the dimension of the image contained within this texture.
382 * Note that for non-power-of-two textures in particular this may
383 * not be equal to the result of {@link #getWidth}. It is
384 * recommended that applications call {@link #getImageTexCoords} and
385 * {@link #getSubImageTexCoords} rather than using this API
386 * directly.
387 */
388 constexpr const Point2u32& imgSize() const noexcept { return m_imgSize; }
389
390 /**
391 * Returns the original aspect ratio of the image, defined as (image
392 * width) / (image height), before any scaling that might have
393 * occurred as a result of using the GLU mipmap routines.
394 */
395 constexpr float aspectRatio() const noexcept { return m_aspectRatio; }
396
397 /**
398 * Returns the set of texture coordinates corresponding to the
399 * entire image. If the TextureData indicated that the texture
400 * coordinates must be flipped vertically, the returned
401 * TextureCoords will take that into account.
402 */
403 const TextureCoords& imageTexCoords() const noexcept { return m_coords; }
404
405 /**
406 * Returns the set of texture coordinates corresponding to the
407 * specified sub-image. The (x1, y1) and (x2, y2) points are
408 * specified in terms of pixels starting from the lower-left of the
409 * image. (x1, y1) should specify the lower-left corner of the
410 * sub-image and (x2, y2) the upper-right corner of the sub-image.
411 * If the TextureData indicated that the texture coordinates must be
412 * flipped vertically, the returned TextureCoords will take that
413 * into account; this should not be handled by the end user in the
414 * specification of the y1 and y2 coordinates.
415 *
416 * @return the texture coordinates corresponding to the specified sub-image
417 */
419 if (GL_TEXTURE_RECTANGLE_ARB == m_imageTarget) {
420 if (m_mustFlipVertically) {
421 return TextureCoords(float(bl.x), float(m_texSize.y - bl.y), float(tr.x), float(m_texSize.y - tr.y));
422 } else {
423 return TextureCoords(bl, tr);
424 }
425 } else {
426 jau::math::Point2f t_bl(float(bl.x)/float(m_texSize.x), float(bl.y)/float(m_texSize.y));
427 jau::math::Point2f t_tr(float(tr.x)/float(m_texSize.x), float(tr.y)/float(m_texSize.y));
428
429 if (m_mustFlipVertically) {
430 const float yMax = (float) m_imgSize.y / (float) m_texSize.y;
431 t_bl.y = yMax - t_bl.y;
432 t_tr.y = yMax - t_tr.y;
433 }
434 return TextureCoords(t_bl, t_tr);
435 }
436 }
437
438 /**
439 * Updates the entire content area incl. {@link TextureCoords}
440 * of this texture using the data in the given image.
441 */
442 void updateImage(const GL& gl, const TextureData& data) {
443 updateImage(gl, data, 0);
444 }
445
446 /**
447 * Indicates whether this texture's texture coordinates must be
448 * flipped vertically in order to properly display the texture. This
449 * is handled automatically by {@link #getImageTexCoords
450 * getImageTexCoords} and {@link #getSubImageTexCoords
451 * getSubImageTexCoords}, but applications may generate or otherwise
452 * produce texture coordinates which must be corrected.
453 */
454 constexpr bool mustFlipVertically() const noexcept { return m_mustFlipVertically; }
455
456 /**
457 * Change whether the TextureData requires a vertical flip of
458 * the texture coords.
459 * <p>
460 * No-op if no change, otherwise generates new {@link TextureCoords}.
461 * </p>
462 */
463 void setMustFlipVertically(bool v) noexcept {
464 if( v != m_mustFlipVertically ) {
465 m_mustFlipVertically = v;
466 updateTexCoords();
467 }
468 }
469
470 /**
471 * Updates the content area incl. {@link TextureCoords} of the specified target of this texture
472 * using the data in the given image. In general this is intended
473 * for construction of cube maps.
474 *
475 * @throws GLException if any OpenGL-related errors occurred
476 */
477 void updateImage(const GL& gl, const TextureData& data, int targetOverride) {
478 validateTexID(gl, true);
479
480 m_imgSize = data.getSize();
481 m_aspectRatio = (float) m_imgSize.x / (float) m_imgSize.y;
482 m_mustFlipVertically = data.getMustFlipVertically();
483
484 int texTarget = 0;
485 int texParamTarget = m_target;
486
487 // See whether we have automatic mipmap generation support
488 bool haveAutoMipmapGeneration =
489 (gl.isExtensionAvailable(GLExtensions::VERSION_1_4) ||
490 gl.isExtensionAvailable(GLExtensions::SGIS_generate_mipmap));
491
492 // Indicate to the TextureData what functionality is available
493 data.setHaveEXTABGR(gl.isExtensionAvailable(GLExtensions::EXT_abgr));
494 data.setHaveGL12(gl.isExtensionAvailable(GLExtensions::VERSION_1_2));
495
496 // Indicates whether both width and height are power of two
497 bool isPOT = jau::is_power_of_2(m_imgSize.x) && jau::is_power_of_2(m_imgSize.y);
498
499 // Note that automatic mipmap generation doesn't work for
500 // GL_ARB_texture_rectangle
501 if (!isPOT && !haveNPOT(gl)) {
502 haveAutoMipmapGeneration = false;
503 }
504
505 bool expandingCompressedTexture = false;
506 bool done = false;
507 if (data.getMipmap() && !haveAutoMipmapGeneration) {
508 // GLU always scales the texture's dimensions to be powers of
509 // two. It also doesn't really matter exactly what the texture
510 // width and height are because the texture coords are always
511 // between 0.0 and 1.0.
512 imgWidth = jau::bit_ceil(imgWidth);
513 imgHeight = jau::bit_ceil(imgHeight);
514 texWidth = imgWidth;
515 texHeight = imgHeight;
516 texTarget = GL.GL_TEXTURE_2D;
517 done = true;
518 }
519
520 if (!done && preferTexRect(gl) && !isPOT &&
521 haveTexRect(gl) && !data.isDataCompressed() && !gl.isGL3() && !gl.isGLES()) {
522 // GL_ARB_texture_rectangle does not work for compressed textures
523 if (DEBUG) {
524 System.err.println("Using GL_ARB_texture_rectangle preferentially on this hardware");
525 }
526
527 texWidth = imgWidth;
528 texHeight = imgHeight;
529 texTarget = GL2.GL_TEXTURE_RECTANGLE_ARB;
530 done = true;
531 }
532
533 if (!done && (isPOT || haveNPOT(gl))) {
534 if (DEBUG) {
535 if (isPOT) {
536 System.err.println("Power-of-two texture");
537 } else {
538 System.err.println("Using GL_ARB_texture_non_power_of_two");
539 }
540 }
541
542 texWidth = imgWidth;
543 texHeight = imgHeight;
544 texTarget = GL.GL_TEXTURE_2D;
545 done = true;
546 }
547
548 if (!done && haveTexRect(gl) && !data.isDataCompressed() && !gl.isGL3() && !gl.isGLES()) {
549 // GL_ARB_texture_rectangle does not work for compressed textures
550 if (DEBUG) {
551 System.err.println("Using GL_ARB_texture_rectangle");
552 }
553
554 texWidth = imgWidth;
555 texHeight = imgHeight;
556 texTarget = GL2.GL_TEXTURE_RECTANGLE_ARB;
557 done = true;
558 }
559
560 if (!done) {
561 // If we receive non-power-of-two compressed texture data and
562 // don't have true hardware support for compressed textures, we
563 // can fake this support by producing an empty "compressed"
564 // texture image, using glCompressedTexImage2D with that to
565 // allocate the texture, and glCompressedTexSubImage2D with the
566 // incoming data.
567 if (data.isDataCompressed()) {
568 if (data.getMipmapData() != null) {
569
570 // We don't currently support expanding of compressed,
571 // mipmapped non-power-of-two textures to the nearest power
572 // of two; the obvious port of the non-mipmapped code didn't
573 // work
574 throw new GLException("Mipmapped non-power-of-two compressed textures only supported on OpenGL 2.0 hardware (GL_ARB_texture_non_power_of_two)");
575 }
576
577 expandingCompressedTexture = true;
578 }
579
580 if (DEBUG) {
581 System.err.println("Expanding texture to power-of-two dimensions");
582 }
583
584 if (data.getBorder() != 0) {
585 throw new RuntimeException("Scaling up a non-power-of-two texture which has a border won't work");
586 }
587 texWidth = Bitfield.Util.roundToPowerOf2(imgWidth);
588 texHeight = Bitfield.Util.roundToPowerOf2(imgHeight);
589 texTarget = GL.GL_TEXTURE_2D;
590 }
591 texParamTarget = texTarget;
592 imageTarget = texTarget;
593 updateTexCoords();
594
595 if (targetOverride != 0) {
596 // Allow user to override auto detection and skip bind step (for
597 // cubemap construction)
598 if (this.target == 0) {
599 throw new GLException("Override of target failed; no target specified yet");
600 }
601 texTarget = targetOverride;
602 texParamTarget = this.target;
603 gl.glBindTexture(texParamTarget, texID);
604 } else {
605 gl.glBindTexture(texTarget, texID);
606 }
607
608 if (data.getMipmap() && !haveAutoMipmapGeneration) {
609 final int[] align = new int[1];
610 gl.glGetIntegerv(GL.GL_UNPACK_ALIGNMENT, align, 0); // save alignment
611 gl.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, data.getAlignment());
612
613 if (data.isDataCompressed()) {
614 throw new GLException("May not request mipmap generation for compressed textures");
615 }
616
617 try {
618 // FIXME: may need check for GLUnsupportedException
619 final GLU glu = GLU.createGLU(gl);
620 glu.gluBuild2DMipmaps(texTarget, data.getInternalFormat(),
621 data.getWidth(), data.getHeight(),
622 data.getPixelFormat(), data.getPixelType(), data.getBuffer());
623 } finally {
624 gl.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, align[0]); // restore alignment
625 }
626 } else {
627 checkCompressedTextureExtensions(gl, data);
628 final Buffer[] mipmapData = data.getMipmapData();
629 if (mipmapData != null) {
630 int width = texWidth;
631 int height = texHeight;
632 for (int i = 0; i < mipmapData.length; i++) {
633 if (data.isDataCompressed()) {
634 // Need to use glCompressedTexImage2D directly to allocate and fill this image
635 // Avoid spurious memory allocation when possible
636 gl.glCompressedTexImage2D(texTarget, i, data.getInternalFormat(),
637 width, height, data.getBorder(),
638 mipmapData[i].remaining(), mipmapData[i]);
639 } else {
640 // Allocate texture image at this level
641 ::glTexImage2D(texTarget, i, data.getInternalFormat(),
642 width, height, data.getBorder(),
643 data.getPixelFormat(), data.getPixelType(), null);
644 updateSubImageImpl(gl, data, texTarget, i, 0, 0, 0, 0, data.getWidth(), data.getHeight());
645 }
646
647 width = Math.max(width / 2, 1);
648 height = Math.max(height / 2, 1);
649 }
650 } else {
651 if (data.isDataCompressed()) {
652 if (!expandingCompressedTexture) {
653 // Need to use glCompressedTexImage2D directly to allocate and fill this image
654 // Avoid spurious memory allocation when possible
655 gl.glCompressedTexImage2D(texTarget, 0, data.getInternalFormat(),
656 texWidth, texHeight, data.getBorder(),
657 data.getBuffer().capacity(), data.getBuffer());
658 } else {
659 final ByteBuffer buf = DDSImage.allocateBlankBuffer(texWidth,
660 texHeight,
661 data.getInternalFormat());
662 gl.glCompressedTexImage2D(texTarget, 0, data.getInternalFormat(),
663 texWidth, texHeight, data.getBorder(),
664 buf.capacity(), buf);
665 updateSubImageImpl(gl, data, texTarget, 0, 0, 0, 0, 0, data.getWidth(), data.getHeight());
666 }
667 } else {
668 if (data.getMipmap() && haveAutoMipmapGeneration && gl.isGL2ES1()) {
669 // For now, only use hardware mipmapping for uncompressed 2D
670 // textures where the user hasn't explicitly specified
671 // mipmap data; don't know about interactions between
672 // GL_GENERATE_MIPMAP and glCompressedTexImage2D
673 gl.glTexParameteri(texParamTarget, GL2ES1.GL_GENERATE_MIPMAP, GL.GL_TRUE);
674 usingAutoMipmapGeneration = true;
675 }
676
677 gl.glTexImage2D(texTarget, 0, data.getInternalFormat(),
678 texWidth, texHeight, data.getBorder(),
679 data.getPixelFormat(), data.getPixelType(), null);
680 updateSubImageImpl(gl, data, texTarget, 0, 0, 0, 0, 0, data.getWidth(), data.getHeight());
681 }
682 }
683 }
684
685 final int minFilter = (data.getMipmap() ? GL.GL_LINEAR_MIPMAP_LINEAR : GL.GL_LINEAR);
686 final int magFilter = GL.GL_LINEAR;
687 final int wrapMode = (gl.isExtensionAvailable(GLExtensions.VERSION_1_2) || !gl.isGL2()) ? GL.GL_CLAMP_TO_EDGE : GL2.GL_CLAMP;
688
689 // REMIND: figure out what to do for GL_TEXTURE_RECTANGLE_ARB
690 if (texTarget != GL2.GL_TEXTURE_RECTANGLE_ARB) {
691 gl.glTexParameteri(texParamTarget, GL.GL_TEXTURE_MIN_FILTER, minFilter);
692 gl.glTexParameteri(texParamTarget, GL.GL_TEXTURE_MAG_FILTER, magFilter);
693 gl.glTexParameteri(texParamTarget, GL.GL_TEXTURE_WRAP_S, wrapMode);
694 gl.glTexParameteri(texParamTarget, GL.GL_TEXTURE_WRAP_T, wrapMode);
695 if (this.target == GL.GL_TEXTURE_CUBE_MAP) {
696 gl.glTexParameteri(texParamTarget, GL2ES2.GL_TEXTURE_WRAP_R, wrapMode);
697 }
698 }
699
700 // Don't overwrite target if we're loading e.g. faces of a cube
701 // map
702 if ((this.target == 0) ||
703 (this.target == GL.GL_TEXTURE_2D) ||
704 (this.target == GL2.GL_TEXTURE_RECTANGLE_ARB)) {
705 this.target = texTarget;
706 }
707
708 // This estimate will be wrong for cube maps
709 estimatedMemorySize = data.getEstimatedMemorySize();
710 }
711
712 /**
713 * Updates a subregion of the content area of this texture using the
714 * given data. If automatic mipmap generation is in use (see {@link
715 * #isUsingAutoMipmapGeneration isUsingAutoMipmapGeneration}),
716 * updates to the base (level 0) mipmap will cause the lower-level
717 * mipmaps to be regenerated, and updates to other mipmap levels
718 * will be ignored. Otherwise, if automatic mipmap generation is not
719 * in use, only updates the specified mipmap level and does not
720 * re-generate mipmaps if they were originally produced or loaded.
721 *
722 * @param data the image data to be uploaded to this texture
723 * @param mipmapLevel the mipmap level of the texture to set. If
724 * this is non-zero and the TextureData contains mipmap data, the
725 * appropriate mipmap level will be selected.
726 * @param x the x offset (in pixels) relative to the lower-left corner
727 * of this texture
728 * @param y the y offset (in pixels) relative to the lower-left corner
729 * of this texture
730 *
731 * @throws GLException if any OpenGL-related errors occurred
732 */
733 void updateSubImage(const GL& gl, const TextureData& data, int mipmapLevel, int x, int y) {
734 if (m_usingAutoMipmapGeneration && mipmapLevel != 0) {
735 // When we're using mipmap generation via GL_GENERATE_MIPMAP, we
736 // don't need to update other mipmap levels
737 return;
738 }
739 bind(gl);
740 updateSubImageImpl(gl, data, target, mipmapLevel, x, y, 0, 0, data.getWidth(), data.getHeight());
741 }
742
743 /**
744 * Updates a subregion of the content area of this texture using the
745 * specified sub-region of the given data. If automatic mipmap
746 * generation is in use (see {@link #isUsingAutoMipmapGeneration
747 * isUsingAutoMipmapGeneration}), updates to the base (level 0)
748 * mipmap will cause the lower-level mipmaps to be regenerated, and
749 * updates to other mipmap levels will be ignored. Otherwise, if
750 * automatic mipmap generation is not in use, only updates the
751 * specified mipmap level and does not re-generate mipmaps if they
752 * were originally produced or loaded. This method is only supported
753 * for uncompressed TextureData sources.
754 *
755 * @param data the image data to be uploaded to this texture
756 * @param mipmapLevel the mipmap level of the texture to set. If
757 * this is non-zero and the TextureData contains mipmap data, the
758 * appropriate mipmap level will be selected.
759 * @param dstx the x offset (in pixels) relative to the lower-left corner
760 * of this texture where the update will be applied
761 * @param dsty the y offset (in pixels) relative to the lower-left corner
762 * of this texture where the update will be applied
763 * @param srcx the x offset (in pixels) relative to the lower-left corner
764 * of the supplied TextureData from which to fetch the update rectangle
765 * @param srcy the y offset (in pixels) relative to the lower-left corner
766 * of the supplied TextureData from which to fetch the update rectangle
767 * @param width the width (in pixels) of the rectangle to be updated
768 * @param height the height (in pixels) of the rectangle to be updated
769 *
770 * @throws GLException if no OpenGL context was current or if any
771 * OpenGL-related errors occurred
772 */
773 void updateSubImage(const GL& gl, const TextureData& data, int mipmapLevel,
774 int dstx, int dsty,
775 int srcx, int srcy,
776 int width, int height) {
777 if (data.isDataCompressed()) {
778 throw RenderException("updateSubImage specifying a sub-rectangle is not supported for compressed TextureData", E_FILE_LINE);
779 }
780 if (m_usingAutoMipmapGeneration && mipmapLevel != 0) {
781 // When we're using mipmap generation via GL_GENERATE_MIPMAP, we
782 // don't need to update other mipmap levels
783 return;
784 }
785 bind(gl);
786 updateSubImageImpl(gl, data, target, mipmapLevel, dstx, dsty, srcx, srcy, width, height);
787 }
788
789 /**
790 * Sets the OpenGL floating-point texture parameter for the
791 * texture's target. This gives control over parameters such as
792 * GL_TEXTURE_MAX_ANISOTROPY_EXT. Causes this texture to be bound to
793 * the current texture state.
794 *
795 * @throws GLException if no OpenGL context was current or if any
796 * OpenGL-related errors occurred
797 */
798 void setTexParameterf(const GL& gl, GLenum parameterName, GLfloat value) {
799 bind(gl);
800 ::glTexParameterf(target, parameterName, value);
801 }
802
803 /**
804 * Sets the OpenGL multi-floating-point texture parameter for the
805 * texture's target. Causes this texture to be bound to the current
806 * texture state.
807 *
808 * @throws GLException if any OpenGL-related errors occurred
809 */
810 void setTexParameterfv(const GL& gl, GLenum parameterName, GLfloat params[]) {
811 bind(gl);
812 ::glTexParameterfv(target, parameterName, params);
813 }
814
815 /**
816 * Sets the OpenGL integer texture parameter for the texture's
817 * target. This gives control over parameters such as
818 * GL_TEXTURE_WRAP_S and GL_TEXTURE_WRAP_T, which by default are set
819 * to GL_CLAMP_TO_EDGE if OpenGL 1.2 is supported on the current
820 * platform and GL_CLAMP if not. Causes this texture to be bound to
821 * the current texture state.
822 *
823 * @throws GLException if any OpenGL-related errors occurred
824 */
825 void setTexParameteri(const GL& gl, GLenum parameterName, GLint value) {
826 bind(gl);
827 ::glTexParameteri(target, parameterName, value);
828 }
829
830 /**
831 * Sets the OpenGL multi-integer texture parameter for the texture's
832 * target. Causes this texture to be bound to the current texture
833 * state.
834 *
835 * @throws GLException if any OpenGL-related errors occurred
836 */
837 void setTexParameteriv(const GL& gl, GLenum parameterName, GLint params[]) {
838 bind(gl);
839 ::glTexParameteriv(target, parameterName, params);
840 }
841
842 /**
843 * Returns the underlying OpenGL texture object for this texture
844 * and generates it if not done yet.
845 * <p>
846 * Most applications will not need to access this, since it is
847 * handled automatically by the bind(GL) and destroy(GL) APIs.
848 * </p>
849 * @param gl required to be valid and current in case the texture object has not been generated yet,
850 * otherwise it may be <code>null</code>.
851 * @see #getTextureObject()
852 */
853 GLuint getTextureObject(const GL& gl) noexcept {
854 validateTexID(gl, false);
855 return texID;
856 }
857
858 /**
859 * Returns the underlying OpenGL texture object for this texture,
860 * maybe <code>0</code> if not yet generated.
861 * <p>
862 * Most applications will not need to access this, since it is
863 * handled automatically by the bind(GL) and destroy(GL) APIs.
864 * </p>
865 * @see #getTextureObject(GL)
866 */
867 constexpr GLuint getTextureObject() { return m_texID; }
868
869 /** Returns whether {@link #getTextureObject()} is owned by this {@link Texture} instance. */
870 constexpr bool ownsTexture() { return m_ownsTextureID; }
871
872 /** Returns an estimate of the amount of texture memory in bytes
873 this Texture consumes. It should only be treated as an estimate;
874 most applications should not need to query this but instead let
875 the OpenGL implementation page textures in and out as
876 necessary. */
877 constexpr size_t getEstimatedMemorySize() const noexcept { return m_estimatedMemorySize; }
878
879 /** Indicates whether this Texture is using automatic mipmap
880 generation (via the OpenGL texture parameter
881 GL_GENERATE_MIPMAP). This will automatically be used when
882 mipmapping is requested via the TextureData and either OpenGL
883 1.4 or the GL_SGIS_generate_mipmap extension is available. If
884 so, updates to the base image (mipmap level 0) will
885 automatically propagate down to the lower mipmap levels. Manual
886 updates of the mipmap data at these lower levels will be
887 ignored. */
889 return usingAutoMipmapGeneration;
890 }
891
892 private:
893 void updateTexCoords() {
894 if ( GL2.GL_TEXTURE_RECTANGLE_ARB == imageTarget ) {
895 if (mustFlipVertically) {
896 coords = new TextureCoords(0, imgHeight, imgWidth, 0);
897 } else {
898 coords = new TextureCoords(0, 0, imgWidth, imgHeight);
899 }
900 } else {
901 if (mustFlipVertically) {
902 coords = new TextureCoords(0, // l
903 (float) imgHeight / (float) texHeight, // b
904 (float) imgWidth / (float) texWidth, // r
905 0 // t
906 );
907 } else {
908 coords = new TextureCoords(0, // l
909 0, // b
910 (float) imgWidth / (float) texWidth, // r
911 (float) imgHeight / (float) texHeight // t
912 );
913 }
914 }
915 }
916
917 void updateSubImageImpl(const GL& gl, const TextureData& data, int newTarget, int mipmapLevel,
918 int dstx, int dsty,
919 int srcx, int srcy, int width, int height) {
920 data.setHaveEXTABGR(gl.isExtensionAvailable(GLExtensions.EXT_abgr));
921 data.setHaveGL12(gl.isExtensionAvailable(GLExtensions.VERSION_1_2));
922
923 Buffer buffer = data.getBuffer();
924 if (buffer == null && data.getMipmapData() == null) {
925 // Assume user just wanted to get the Texture object allocated
926 return;
927 }
928
929 int rowlen = data.getRowLength();
930 int dataWidth = data.getWidth();
931 int dataHeight = data.getHeight();
932 if (data.getMipmapData() != null) {
933 // Compute the width, height and row length at the specified mipmap level
934 // Note we do not support specification of the row length for
935 // mipmapped textures at this point
936 for (int i = 0; i < mipmapLevel; i++) {
937 width = Math.max(width / 2, 1);
938 height = Math.max(height / 2, 1);
939
940 dataWidth = Math.max(dataWidth / 2, 1);
941 dataHeight = Math.max(dataHeight / 2, 1);
942 }
943 rowlen = 0;
944 buffer = data.getMipmapData()[mipmapLevel];
945 }
946
947 // Clip incoming rectangles to what is available both on this
948 // texture and in the incoming TextureData
949 if (srcx < 0) {
950 width += srcx;
951 srcx = 0;
952 }
953 if (srcy < 0) {
954 height += srcy;
955 srcy = 0;
956 }
957 // NOTE: not sure whether the following two are the correct thing to do
958 if (dstx < 0) {
959 width += dstx;
960 dstx = 0;
961 }
962 if (dsty < 0) {
963 height += dsty;
964 dsty = 0;
965 }
966
967 if (srcx + width > dataWidth) {
968 width = dataWidth - srcx;
969 }
970 if (srcy + height > dataHeight) {
971 height = dataHeight - srcy;
972 }
973 if (dstx + width > texWidth) {
974 width = texWidth - dstx;
975 }
976 if (dsty + height > texHeight) {
977 height = texHeight - dsty;
978 }
979
980 checkCompressedTextureExtensions(gl, data);
981
982 if (data.isDataCompressed()) {
983 gl.glCompressedTexSubImage2D(newTarget, mipmapLevel,
984 dstx, dsty, width, height,
985 data.getInternalFormat(),
986 buffer.remaining(), buffer);
987 } else {
988 final int[] align = { 0 };
989 final int[] rowLength = { 0 };
990 final int[] skipRows = { 0 };
991 final int[] skipPixels = { 0 };
992 gl.glGetIntegerv(GL.GL_UNPACK_ALIGNMENT, align, 0); // save alignment
993 if(gl.isGL2GL3()) {
994 gl.glGetIntegerv(GL2ES2.GL_UNPACK_ROW_LENGTH, rowLength, 0); // save row length
995 gl.glGetIntegerv(GL2ES2.GL_UNPACK_SKIP_ROWS, skipRows, 0); // save skipped rows
996 gl.glGetIntegerv(GL2ES2.GL_UNPACK_SKIP_PIXELS, skipPixels, 0); // save skipped pixels
997 }
998 gl.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, data.getAlignment());
999 if (DEBUG && VERBOSE) {
1000 System.out.println("Row length = " + rowlen);
1001 System.out.println("skip pixels = " + srcx);
1002 System.out.println("skip rows = " + srcy);
1003 System.out.println("dstx = " + dstx);
1004 System.out.println("dsty = " + dsty);
1005 System.out.println("width = " + width);
1006 System.out.println("height = " + height);
1007 }
1008 if(gl.isGL2GL3()) {
1009 gl.glPixelStorei(GL2ES2.GL_UNPACK_ROW_LENGTH, rowlen);
1010 gl.glPixelStorei(GL2ES2.GL_UNPACK_SKIP_ROWS, srcy);
1011 gl.glPixelStorei(GL2ES2.GL_UNPACK_SKIP_PIXELS, srcx);
1012 } else {
1013 if ( rowlen!=0 && rowlen!=width &&
1014 srcy!=0 && srcx!=0 ) {
1015 throw new GLException("rowlen and/or x/y offset only available for GL2");
1016 }
1017 }
1018
1019 gl.glTexSubImage2D(newTarget, mipmapLevel,
1020 dstx, dsty, width, height,
1021 data.getPixelFormat(), data.getPixelType(),
1022 buffer);
1023 gl.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, align[0]); // restore alignment
1024 if(gl.isGL2GL3()) {
1025 gl.glPixelStorei(GL2ES2.GL_UNPACK_ROW_LENGTH, rowLength[0]); // restore row length
1026 gl.glPixelStorei(GL2ES2.GL_UNPACK_SKIP_ROWS, skipRows[0]); // restore skipped rows
1027 gl.glPixelStorei(GL2ES2.GL_UNPACK_SKIP_PIXELS, skipPixels[0]); // restore skipped pixels
1028 }
1029 }
1030 }
1031
1032 void checkCompressedTextureExtensions(const GL& gl, const TextureData& data) {
1033 if (data.isDataCompressed()) {
1034 switch (data.getInternalFormat()) {
1035 case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
1036 case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
1037 case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
1038 case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
1039 if (!gl.isExtensionAvailable(GLExtensions.EXT_texture_compression_s3tc) &&
1040 !gl.isExtensionAvailable(GLExtensions.NV_texture_compression_vtc)) {
1041 throw new GLException("DXTn compressed textures not supported by this graphics card");
1042 }
1043 break;
1044 default:
1045 // FI1027GXME: should test availability of more texture
1046 // compression extensions here
1047 break;
1048 }
1049 }
1050 }
1051
1052 bool validateTexID(const GL& gl, bool throwException) {
1053 if( 0 == m_texID ) {
1054 if( m_ownsTextureID ) {
1055 ::glGenTextures(1, &m_texID);
1056 if ( 0 == m_texID && throwException ) {
1057 throw RenderException("Create texture ID invalid: texID "+std::to_string(texID)+", glerr "+jau::to_hexstring(::glGetError()), E_FILE_LINE);
1058 }
1059 } else if ( throwException ) {
1060 if( !ownsTextureID ) {
1061 throw RenderException("Invalid external texture ID", E_FILE_LINE;
1062 } else {
1063 throw RenderException("No GL context given, can't create texture ID", E_FILE_LINE);
1064 }
1065 }
1066 }
1067 return 0 != m_texID;
1068 }
1069
1070 // Helper routines for disabling certain codepaths
1071 static bool haveNPOT(const GL& gl) {
1072 return !disableNPOT && gl.isNPOTTextureAvailable();
1073 }
1074
1075 static bool haveTexRect(const GL& gl) {
1076 return (!disableTexRect &&
1077 TextureIO.isTexRectEnabled() &&
1078 gl.isExtensionAvailable(GLExtensions.ARB_texture_rectangle));
1079 }
1080
1081 private static boolean preferTexRect(final GL gl) {
1082 // Prefer GL_ARB_texture_rectangle on ATI hardware on Mac OS X
1083 // due to software fallbacks
1084
1085 if (NativeWindowFactory.TYPE_MACOSX == NativeWindowFactory.getNativeWindowType(false)) {
1086 final String vendor = gl.glGetString(GL.GL_VENDOR);
1087 if (vendor != null && vendor.startsWith("ATI")) {
1088 return true;
1089 }
1090 }
1091
1092 return false;
1093 }
1094 };
1095
1096 /**@}*/
1097
1098} // namespace gamp::render::gl
1099
1100#endif /* GAMP_RENDER_GL_TEXTURE_TEXTURE_HPP_ */
#define E_FILE_LINE
Class holding OpenGL extension strings, commonly used by Gamp's implementation.
static constexpr std::string_view SGIS_generate_mipmap
static constexpr std::string_view VERSION_1_4
static constexpr std::string_view EXT_abgr
static constexpr std::string_view VERSION_1_2
Rectangular texture coordinates.
constexpr size_t getEstimatedMemorySize() const noexcept
Returns an estimate of the amount of texture memory in bytes this Texture consumes.
Definition Texture.hpp:877
constexpr GLenum target() const noexcept
Returns the OpenGL "target" of this texture.
Definition Texture.hpp:364
std::string toString() const noexcept
Definition Texture.hpp:183
Texture(GLenum target)
Constructor for use when creating e.g.
Definition Texture.hpp:211
constexpr float aspectRatio() const noexcept
Returns the original aspect ratio of the image, defined as (image width) / (image height),...
Definition Texture.hpp:395
TextureCoords getSubImageTexCoords(const jau::math::Point2i &bl, const jau::math::Point2i &tr) const noexcept
Returns the set of texture coordinates corresponding to the specified sub-image.
Definition Texture.hpp:418
void setTexParameterf(const GL &gl, GLenum parameterName, GLfloat value)
Sets the OpenGL floating-point texture parameter for the texture's target.
Definition Texture.hpp:798
Texture(GLuint textureID, bool ownsTextureID, GLenum target, const Point2u32 &texSize, const Point2u32 &imgSize, bool mustFlipVertically)
Constructor to wrap an OpenGL texture ID from an external library and allows some of the base methods...
Definition Texture.hpp:244
Texture(GL &gl, const TextureData &data)
Definition Texture.hpp:196
constexpr const Point2u32 & texSize() const noexcept
Returns the dimension of the allocated OpenGL texture in pixels.
Definition Texture.hpp:378
void updateImage(const GL &gl, const TextureData &data)
Updates the entire content area incl.
Definition Texture.hpp:442
constexpr bool mustFlipVertically() const noexcept
Indicates whether this texture's texture coordinates must be flipped vertically in order to properly ...
Definition Texture.hpp:454
void updateSubImage(const GL &gl, const TextureData &data, int mipmapLevel, int dstx, int dsty, int srcx, int srcy, int width, int height)
Updates a subregion of the content area of this texture using the specified sub-region of the given d...
Definition Texture.hpp:773
bool isUsingAutoMipmapGeneration()
Indicates whether this Texture is using automatic mipmap generation (via the OpenGL texture parameter...
Definition Texture.hpp:888
void enable(const GL &gl) const noexcept
Enables this texture's target (e.g., GL_TEXTURE_2D) in the given GL context's state.
Definition Texture.hpp:294
void setTexParameteriv(const GL &gl, GLenum parameterName, GLint params[])
Sets the OpenGL multi-integer texture parameter for the texture's target.
Definition Texture.hpp:837
constexpr GLenum imageTarget() const noexcept
Returns the image OpenGL "target" of this texture, or its sub-components if cubemap.
Definition Texture.hpp:371
void destroy(const GL &) noexcept
Destroys and nulls the underlying native texture used by this Texture instance if owned,...
Definition Texture.hpp:352
void setMustFlipVertically(bool v) noexcept
Change whether the TextureData requires a vertical flip of the texture coords.
Definition Texture.hpp:463
constexpr GLuint getTextureObject()
Returns the underlying OpenGL texture object for this texture, maybe 0 if not yet generated.
Definition Texture.hpp:867
constexpr bool ownsTexture()
Returns whether getTextureObject() is owned by this Texture instance.
Definition Texture.hpp:870
void setTexParameteri(const GL &gl, GLenum parameterName, GLint value)
Sets the OpenGL integer texture parameter for the texture's target.
Definition Texture.hpp:825
void updateImage(const GL &gl, const TextureData &data, int targetOverride)
Updates the content area incl.
Definition Texture.hpp:477
void disable(const GL &gl) const noexcept
Disables this texture's target (e.g., GL_TEXTURE_2D) in the given GL state.
Definition Texture.hpp:321
void setTexParameterfv(const GL &gl, GLenum parameterName, GLfloat params[])
Sets the OpenGL multi-floating-point texture parameter for the texture's target.
Definition Texture.hpp:810
const TextureCoords & imageTexCoords() const noexcept
Returns the set of texture coordinates corresponding to the entire image.
Definition Texture.hpp:403
constexpr const Point2u32 & imgSize() const noexcept
Returns the dimension of the image contained within this texture.
Definition Texture.hpp:388
void set(const Point2u32 &texSize, const Point2u32 &imgSize)
Pending setup or update of texture and image dimensions.
Definition Texture.hpp:266
void bind(const GL &gl) noexcept
Binds this texture to the given GL context.
Definition Texture.hpp:341
void updateSubImage(const GL &gl, const TextureData &data, int mipmapLevel, int x, int y)
Updates a subregion of the content area of this texture using the given data.
Definition Texture.hpp:733
GLuint getTextureObject(const GL &gl) noexcept
Returns the underlying OpenGL texture object for this texture and generates it if not done yet.
Definition Texture.hpp:853
value_type y
Definition vec2f.hpp:63
constexpr T bit_ceil(const T n) noexcept
If the given n is not is_power_of_2() return next_power_of_2(), otherwise return n unchanged.
Definition int_math.hpp:186
constexpr bool is_power_of_2(const T x) noexcept
Power of 2 test in O(1), i.e.
Definition int_math.hpp:134
Point2I< int > Point2i
Definition vec2i.hpp:334
Point2F< float > Point2f
Definition vec2f.hpp:417
Point2I< uint32_t > Point2u32
Definition vec2i.hpp:348
std::string toHexString(const void *data, const nsize_t length, const lb_endian_t byteOrder=lb_endian_t::big, const LoUpCase capitalization=LoUpCase::lower, const PrefixOpt prefix=PrefixOpt::prefix) noexcept
Produce a hexadecimal string representation of the given lsb-first byte values.
std::string to_string(const bit_order_t v) noexcept
Return std::string representation of the given bit_order_t.
Represents the data for an OpenGL texture.
Definition Texture.hpp:35
TextureData(const GLProfile &glp, GLenum internalFormat, Point2u32 &size, unsigned border, GLenum pixelFormat, GLenum pixelType, bool mipmap, bool dataIsCompressed, bool mustFlipVertically, bytes_t &buffer)
Constructs a new TextureData object with the specified parameters and data contained in the given Buf...