diff --git a/src/raylib.h b/src/raylib.h index 6800b260..e03fc9b2 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -358,18 +358,19 @@ typedef enum { } TextureFormat; // Gestures type +// NOTE: It could be used as flags to enable only some gestures typedef enum { - GESTURE_NONE = 0, - GESTURE_TAP, - GESTURE_DOUBLETAP, - GESTURE_HOLD, - GESTURE_DRAG, - GESTURE_SWIPE_RIGHT, - GESTURE_SWIPE_LEFT, - GESTURE_SWIPE_UP, - GESTURE_SWIPE_DOWN, - GESTURE_PINCH_IN, - GESTURE_PINCH_OUT + GESTURE_NONE = 1, + GESTURE_TAP = 2, + GESTURE_DOUBLETAP = 4, + GESTURE_HOLD = 8, + GESTURE_DRAG = 16, + GESTURE_SWIPE_RIGHT = 32, + GESTURE_SWIPE_LEFT = 64, + GESTURE_SWIPE_UP = 128, + GESTURE_SWIPE_DOWN = 256, + GESTURE_PINCH_IN = 512, + GESTURE_PINCH_OUT = 1024 } Gestures; #ifdef __cplusplus @@ -431,14 +432,15 @@ void SetCameraMode(int mode); // Select camera mod Camera UpdateCamera(Vector3 *position); // Update camera with position void SetCameraControls(int front, int left, int back, int right, int up, int down); -void SetMouseSensitivity(float sensitivity); -void SetResetPosition(Vector3 resetPosition); -void SetResetControl(int resetKey); -void SetPawnControl(int pawnControlKey); -void SetFnControl(int fnControlKey); -void SetSmoothZoomControl(int smoothZoomControlKey); -void SetOrbitalTarget(Vector3 target); - +void SetCameraMouseSensitivity(float sensitivity); +void SetCameraResetPosition(Vector3 resetPosition); +void SetCameraResetControl(int resetKey); +void SetCameraPawnControl(int pawnControlKey); +void SetCameraFnControl(int fnControlKey); +void SetCameraSmoothZoomControl(int smoothZoomControlKey); +void SetCameraOrbitalTarget(Vector3 target); + +// Shaders control functions int GetShaderLocation(Shader shader, const char *uniformName); void SetShaderValue(Shader shader, int uniformLoc, float *value, int size); @@ -488,12 +490,14 @@ Vector2 GetTouchPosition(void); // Returns touch positio // Gestures System (module: gestures) bool IsGestureDetected(void); int GetGestureType(void); -float GetDragIntensity(void); -float GetDragAngle(void); -Vector2 GetDragVector(void); -int GetHoldDuration(void); // Hold time in frames -float GetPinchDelta(void); -float GetPinchAngle(void); +void SetGesturesEnabled(unsigned int gestureFlags); + +float GetGestureDragIntensity(void); +float GetGestureDragAngle(void); +Vector2 GetGestureDragVector(void); +int GetGestureHoldDuration(void); // Hold time in frames +float GetGesturePinchDelta(void); +float GetGesturePinchAngle(void); #endif //------------------------------------------------------------------------------------ @@ -530,16 +534,19 @@ bool CheckCollisionPointTriangle(Vector2 point, Vector2 p1, Vector2 p2, Vector2 // Texture Loading and Drawing Functions (Module: textures) //------------------------------------------------------------------------------------ Image LoadImage(const char *fileName); // Load an image into CPU memory (RAM) +Image LoadImageEx(Color *pixels, int width, int height); // Load image data from Color array data (RGBA - 32bit) Image LoadImageFromRES(const char *rresName, int resId); // Load an image from rRES file (raylib Resource) -Image LoadImageFromData(Color *pixels, int width, int height, int format); // Load image from Color array data Texture2D LoadTexture(const char *fileName); // Load an image as texture into GPU memory -Texture2D LoadTextureEx(void *data, int width, int height, int textureFormat, int mipmapCount, bool genMipmaps); // Load a texture from raw data into GPU memory +Texture2D LoadTextureEx(void *data, int width, int height, int textureFormat, int mipmapCount); // Load a texture from raw data into GPU memory Texture2D LoadTextureFromRES(const char *rresName, int resId); // Load an image as texture from rRES file (raylib Resource) -Texture2D LoadTextureFromImage(Image image, bool genMipmaps); // Load a texture from image data (and generate mipmaps) +Texture2D LoadTextureFromImage(Image image); // Load a texture from image data (and generate mipmaps) void UnloadImage(Image image); // Unload image from CPU memory (RAM) void UnloadTexture(Texture2D texture); // Unload texture from GPU memory -void ConvertToPOT(Image *image, Color fillColor); // Convert image to POT (power-of-two) +void ImageConvertToPOT(Image *image, Color fillColor); // Convert image to POT (power-of-two) +void ImageConvertFormat(Image *image, int newFormat); // Convert image data to desired format Color *GetPixelData(Image image); // Get pixel data from image as a Color struct array +Image GetTextureData(Texture2D texture); // Get pixel data from GPU texture and return an Image +void GenTextureMipmaps(Texture2D texture); // Generate GPU mipmaps for a texture void DrawTexture(Texture2D texture, int posX, int posY, Color tint); // Draw a Texture2D void DrawTextureV(Texture2D texture, Vector2 position, Color tint); // Draw a Texture2D with position defined as Vector2 diff --git a/src/rlgl.c b/src/rlgl.c index 157d8f20..4ff652b0 100644 --- a/src/rlgl.c +++ b/src/rlgl.c @@ -221,7 +221,7 @@ static bool vaoSupported = false; // VAO support (OpenGL ES2 could not support static bool npotSupported = false; // NPOT textures full support // Compressed textures support flags -static bool texCompDXTSupported = false; // DDS texture compression support +//static bool texCompDXTSupported = false; // DDS texture compression support static bool texCompETC1Supported = false; // ETC1 texture compression support static bool texCompETC2Supported = false; // ETC2/EAC texture compression support static bool texCompPVRTSupported = false; // PVR texture compression support @@ -232,6 +232,9 @@ static GLuint fbo, fboColorTexture, fboDepthTexture; static Model postproQuad; #endif +// Compressed textures support flags +static bool texCompDXTSupported = false; // DDS texture compression support + #if defined(GRAPHICS_API_OPENGL_ES2) // NOTE: VAO functionality is exposed through extensions (OES) static PFNGLGENVERTEXARRAYSOESPROC glGenVertexArrays; @@ -263,7 +266,9 @@ static int GenerateMipmaps(unsigned char *data, int baseWidth, int baseHeight); static pixel *GenNextMipmap(pixel *srcData, int srcWidth, int srcHeight); #endif +#if defined(GRAPHICS_API_OPENGL_ES2) static char** StringSplit(char *baseString, const char delimiter, int *numExt); +#endif //---------------------------------------------------------------------------------- // Module Functions Definition - Matrix operations @@ -967,7 +972,7 @@ void rlglInit(void) // Create default white texture for plain colors (required by shader) unsigned char pixels[4] = { 255, 255, 255, 255 }; // 1 pixel RGBA (4 bytes) - whiteTexture = rlglLoadTexture(pixels, 1, 1, UNCOMPRESSED_R8G8B8A8, 1, false); + whiteTexture = rlglLoadTexture(pixels, 1, 1, UNCOMPRESSED_R8G8B8A8, 1); if (whiteTexture != 0) TraceLog(INFO, "[TEX ID %i] Base white texture loaded successfully", whiteTexture); else TraceLog(WARNING, "Base white texture could not be loaded"); @@ -1623,7 +1628,7 @@ Vector3 rlglUnproject(Vector3 source, Matrix proj, Matrix view) } // Convert image data to OpenGL texture (returns OpenGL valid Id) -unsigned int rlglLoadTexture(void *data, int width, int height, int textureFormat, int mipmapCount, bool genMipmaps) +unsigned int rlglLoadTexture(void *data, int width, int height, int textureFormat, int mipmapCount) { glBindTexture(GL_TEXTURE_2D, 0); // Free any old binding @@ -1642,7 +1647,7 @@ unsigned int rlglLoadTexture(void *data, int width, int height, int textureForma TraceLog(WARNING, "DXT compressed texture format not supported"); return id; } - +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) if ((!texCompETC1Supported) && (textureFormat == COMPRESSED_ETC1_RGB)) { TraceLog(WARNING, "ETC1 compressed texture format not supported"); @@ -1666,7 +1671,8 @@ unsigned int rlglLoadTexture(void *data, int width, int height, int textureForma TraceLog(WARNING, "ASTC compressed texture format not supported"); return id; } - +#endif + glGenTextures(1, &id); // Generate Pointer to the texture #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) @@ -1755,23 +1761,63 @@ unsigned int rlglLoadTexture(void *data, int width, int height, int textureForma } #endif + // Texture parameters configuration + // NOTE: glTexParameteri does NOT affect texture uploading, just the way it's used +#if defined(GRAPHICS_API_OPENGL_ES2) + // NOTE: OpenGL ES 2.0 with no GL_OES_texture_npot support (i.e. WebGL) has limited NPOT support, so CLAMP_TO_EDGE must be used + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); // Set texture to clamp on x-axis + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); // Set texture to clamp on y-axis +#else + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // Set texture to repeat on x-axis + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); // Set texture to repeat on y-axis +#endif + + // Magnification and minification filters + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // Filter for pixel-perfect drawing, alternative: GL_LINEAR + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); // Filter for pixel-perfect drawing, alternative: GL_LINEAR + +#if defined(GRAPHICS_API_OPENGL_33) + if (mipmapCount > 1) + { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); // Activate Trilinear filtering for mipmaps (must be available) + } +#endif + + // At this point we have the texture loaded in GPU and texture parameters configured + + // NOTE: If mipmaps were not in data, they are not generated automatically + + // Unbind current texture + glBindTexture(GL_TEXTURE_2D, 0); + + if (id > 0) TraceLog(INFO, "[TEX ID %i] Texture created successfully (%ix%i)", id, width, height); + else TraceLog(WARNING, "Texture could not be created"); + + return id; +} + +// Generate mipmap data for selected texture +void rlglGenerateMipmaps(unsigned int textureId) +{ + glBindTexture(GL_TEXTURE_2D, textureId); + + int width, height; + + glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &width); + glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &height); + // Check if texture is power-of-two (POT) to enable mipmap generation bool texIsPOT = false; if (((width > 0) && ((width & (width - 1)) == 0)) && ((height > 0) && ((height & (height - 1)) == 0))) texIsPOT = true; - if (genMipmaps && !texIsPOT) + if (texIsPOT) { - TraceLog(WARNING, "[TEX ID %i] Texture is not power-of-two, mipmaps can not be generated", id); - genMipmaps = false; - } - - // Generate mipmaps if required - // TODO: Improve mipmaps support #if defined(GRAPHICS_API_OPENGL_11) - if (genMipmaps) - { // Compute required mipmaps + void *data = rlglReadTexturePixels(textureId, UNCOMPRESSED_R8G8B8A8); // TODO: Detect internal format + // NOTE: data size is reallocated to fit mipmaps data int mipmapCount = GenerateMipmaps(data, width, height); @@ -1794,48 +1840,21 @@ unsigned int rlglLoadTexture(void *data, int width, int height, int textureForma mipHeight /= 2; } - TraceLog(WARNING, "[TEX ID %i] Mipmaps generated manually on CPU side", id); - } + TraceLog(WARNING, "[TEX ID %i] Mipmaps generated manually on CPU side", textureId); + #elif defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - if ((mipmapCount == 1) && (genMipmaps)) - { - glGenerateMipmap(GL_TEXTURE_2D); // Generate mipmaps automatically - TraceLog(INFO, "[TEX ID %i] Mipmaps generated automatically for new texture", id); - } -#endif - - // Texture parameters configuration - // NOTE: glTexParameteri does NOT affect texture uploading, just the way it's used -#if defined(GRAPHICS_API_OPENGL_ES2) - // NOTE: OpenGL ES 2.0 with no GL_OES_texture_npot support (i.e. WebGL) has limited NPOT support, so CLAMP_TO_EDGE must be used - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); // Set texture to clamp on x-axis - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); // Set texture to clamp on y-axis -#else - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // Set texture to repeat on x-axis - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); // Set texture to repeat on y-axis + glGenerateMipmap(GL_TEXTURE_2D); // Generate mipmaps automatically + TraceLog(INFO, "[TEX ID %i] Mipmaps generated automatically", textureId); #endif - // Magnification and minification filters - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // Filter for pixel-perfect drawing, alternative: GL_LINEAR - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); // Filter for pixel-perfect drawing, alternative: GL_LINEAR - #if defined(GRAPHICS_API_OPENGL_33) - if ((mipmapCount > 1) || (genMipmaps)) - { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); // Activate Trilinear filtering for mipmaps (must be available) - } #endif - - // At this point we have the texture loaded in GPU, with mipmaps generated (if desired) and texture parameters configured - - // Unbind current texture + } + else TraceLog(WARNING, "[TEX ID %i] Texture is not power-of-two, mipmaps can not be generated", textureId); + glBindTexture(GL_TEXTURE_2D, 0); - - if (id > 0) TraceLog(INFO, "[TEX ID %i] Texture created successfully (%ix%i)", id, width, height); - else TraceLog(WARNING, "Texture could not be created"); - - return id; } // Load vertex data into a VAO (if supported) and VBO @@ -2039,6 +2058,47 @@ unsigned char *rlglReadScreenPixels(int width, int height) return imgData; // NOTE: image data should be freed } +// Read texture pixel data +void *rlglReadTexturePixels(unsigned int textureId, unsigned int format) +{ + int width, height; + + glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &width); + glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &height); + //glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT, &format); + //GL_TEXTURE_RED_SIZE, GL_TEXTURE_GREEN_SIZE, GL_TEXTURE_BLUE_SIZE, GL_TEXTURE_ALPHA_SIZE + + int glFormat, glType; + void *pixels = NULL; + unsigned int size = width*height; + + switch (format) + { + case UNCOMPRESSED_GRAYSCALE: pixels = (unsigned char *)malloc(size); glFormat = GL_LUMINANCE; glType = GL_UNSIGNED_BYTE; // 8 bit per pixel (no alpha) + case UNCOMPRESSED_GRAY_ALPHA: pixels = (unsigned char *)malloc(size*2); glFormat = GL_LUMINANCE_ALPHA; glType = GL_UNSIGNED_BYTE; // 16 bpp (2 channels) + case UNCOMPRESSED_R5G6B5: pixels = (unsigned short *)malloc(size); glFormat = GL_RGB; glType = GL_UNSIGNED_SHORT_5_6_5; // 16 bpp + case UNCOMPRESSED_R8G8B8: pixels = (unsigned char *)malloc(size*3); glFormat = GL_RGB; glType = GL_UNSIGNED_BYTE; // 24 bpp + case UNCOMPRESSED_R5G5B5A1: pixels = (unsigned short *)malloc(size); glFormat = GL_RGBA; glType = GL_UNSIGNED_SHORT_5_5_5_1; // 16 bpp (1 bit alpha) + case UNCOMPRESSED_R4G4B4A4: pixels = (unsigned short *)malloc(size); glFormat = GL_RGBA; glType = GL_UNSIGNED_SHORT_4_4_4_4; // 16 bpp (4 bit alpha) + case UNCOMPRESSED_R8G8B8A8: pixels = (unsigned char *)malloc(size*4); glFormat = GL_RGBA; glType = GL_UNSIGNED_BYTE; // 32 bpp + default: TraceLog(WARNING, "Texture format not suported"); break; + } + + glBindTexture(GL_TEXTURE_2D, textureId); + + // NOTE: Each row written to or read from by OpenGL pixel operations like glGetTexImage are aligned to a 4 byte boundary by default, which may add some padding. + // Use glPixelStorei to modify padding with the GL_[UN]PACK_ALIGNMENT setting. + // GL_PACK_ALIGNMENT affects operations that read from OpenGL memory (glReadPixels, glGetTexImage, etc.) + // GL_UNPACK_ALIGNMENT affects operations that write to OpenGL memory (glTexImage, etc.) + glPixelStorei(GL_PACK_ALIGNMENT, 1); + + glGetTexImage(GL_TEXTURE_2D, 0, glFormat, glType, pixels); + + glBindTexture(GL_TEXTURE_2D, 0); + + return pixels; +} + // Load a shader (vertex shader + fragment shader) from files Shader rlglLoadShader(char *vsFileName, char *fsFileName) { @@ -2159,15 +2219,18 @@ void rlglSetDefaultShader(void) int GetShaderLocation(Shader shader, const char *uniformName) { - int location = glGetUniformLocation(shader.id, uniformName); + int location = -1; +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + location = glGetUniformLocation(shader.id, uniformName); if (location == -1) TraceLog(WARNING, "[SHDR ID %i] Shader location for %s could not be found", shader.id, uniformName); - +#endif return location; } void SetShaderValue(Shader shader, int uniformLoc, float *value, int size) { +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) glUseProgram(shader.id); if (size == 1) glUniform1fv(uniformLoc, 1, value); // Shader uniform type: float @@ -2177,10 +2240,12 @@ void SetShaderValue(Shader shader, int uniformLoc, float *value, int size) else TraceLog(WARNING, "Shader value float array size not recognized"); glUseProgram(0); +#endif } void SetShaderMapDiffuse(Shader *shader, Texture2D texture) { +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) shader->texDiffuseId = texture.id; glUseProgram(shader->id); @@ -2193,10 +2258,12 @@ void SetShaderMapDiffuse(Shader *shader, Texture2D texture) glBindTexture(GL_TEXTURE_2D, 0); glActiveTexture(GL_TEXTURE0); glUseProgram(0); +#endif } void SetShaderMapNormal(Shader *shader, const char *uniformName, Texture2D texture) { +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) shader->mapNormalLoc = glGetUniformLocation(shader->id, uniformName); if (shader->mapNormalLoc == -1) TraceLog(WARNING, "[SHDR ID %i] Shader location for %s could not be found", shader->id, uniformName); @@ -2215,10 +2282,12 @@ void SetShaderMapNormal(Shader *shader, const char *uniformName, Texture2D textu glActiveTexture(GL_TEXTURE0); glUseProgram(0); } +#endif } void SetShaderMapSpecular(Shader *shader, const char *uniformName, Texture2D texture) { +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) shader->mapSpecularLoc = glGetUniformLocation(shader->id, uniformName); if (shader->mapSpecularLoc == -1) TraceLog(WARNING, "[SHDR ID %i] Shader location for %s could not be found", shader->id, uniformName); @@ -2237,6 +2306,7 @@ void SetShaderMapSpecular(Shader *shader, const char *uniformName, Texture2D tex glActiveTexture(GL_TEXTURE0); glUseProgram(0); } +#endif } #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) @@ -2865,6 +2935,7 @@ void TraceLog(int msgType, const char *text, ...) } #endif +#if defined(GRAPHICS_API_OPENGL_ES2) static char **StringSplit(char *baseString, const char delimiter, int *numExt) { char **result = 0; @@ -2913,4 +2984,5 @@ static char **StringSplit(char *baseString, const char delimiter, int *numExt) *numExt = (count - 1); return result; -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/src/rlgl.h b/src/rlgl.h index 41c13ef7..e241a9c1 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -209,7 +209,9 @@ void rlglClose(void); // De-init rlgl void rlglDraw(void); // Draw VAO/VBO void rlglInitGraphics(int offsetX, int offsetY, int width, int height); // Initialize Graphics (OpenGL stuff) -unsigned int rlglLoadTexture(void *data, int width, int height, int textureFormat, int mipmapCount, bool genMipmaps); // Load in GPU OpenGL texture +unsigned int rlglLoadTexture(void *data, int width, int height, int textureFormat, int mipmapCount); // Load in GPU OpenGL texture +void rlglGenerateMipmaps(unsigned int textureId); // Generate mipmap data for selected texture + Shader rlglLoadShader(char *vsFileName, char *fsFileName); // Load a shader (vertex shader + fragment shader) from files unsigned int rlglLoadShaderFromText(char *vShaderStr, char *fShaderStr); // Load a shader from text data void rlglInitPostpro(void); // Initialize postprocessing system @@ -222,9 +224,10 @@ void rlglSetDefaultShader(void); // Set default shader to be used Model rlglLoadModel(VertexData mesh); // Upload vertex data into GPU and provided VAO/VBO ids void rlglDrawModel(Model model, Vector3 position, float rotationAngle, Vector3 rotationAxis, Vector3 scale, Color color, bool wires); -Vector3 rlglUnproject(Vector3 source, Matrix proj, Matrix view); // Get world coordinates from screen coordinates +Vector3 rlglUnproject(Vector3 source, Matrix proj, Matrix view); // Get world coordinates from screen coordinates -byte *rlglReadScreenPixels(int width, int height); // Read screen pixel data (color buffer) +unsigned char *rlglReadScreenPixels(int width, int height); // Read screen pixel data (color buffer) +void *rlglReadTexturePixels(unsigned int textureId, unsigned int format); // Read texture pixel data #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) void PrintProjectionMatrix(void); // DEBUG: Print projection matrix diff --git a/src/text.c b/src/text.c index 1647070c..dbbb962f 100644 --- a/src/text.c +++ b/src/text.c @@ -171,11 +171,12 @@ extern void LoadDefaultFont(void) //fwrite(image.pixels, 1, 128*128*4, myimage); //fclose(myimage); - Image image = LoadImageFromData(imagePixels, imWidth, imHeight, UNCOMPRESSED_GRAY_ALPHA); + Image image = LoadImageEx(imagePixels, imWidth, imHeight); + ImageConvertFormat(&image, UNCOMPRESSED_GRAY_ALPHA); free(imagePixels); - defaultFont.texture = LoadTextureFromImage(image, false); // Convert loaded image to OpenGL texture + defaultFont.texture = LoadTextureFromImage(image); UnloadImage(image); // Reconstruct charSet using charsWidth[], charsHeight, charsDivisor, numChars @@ -240,7 +241,7 @@ SpriteFont LoadSpriteFont(const char *fileName) Color *imagePixels = GetPixelData(image); #if defined(PLATFORM_RPI) || defined(PLATFORM_WEB) - ConvertToPOT(&image, MAGENTA); + ImageConvertToPOT(&image, MAGENTA); #endif // Process bitmap Font pixel data to get measures (Character array) // spriteFont.charSet data is filled inside the function and memory is allocated! @@ -251,7 +252,7 @@ SpriteFont LoadSpriteFont(const char *fileName) spriteFont.numChars = numChars; - spriteFont.texture = LoadTextureFromImage(image, false); // Convert loaded image to OpenGL texture + spriteFont.texture = LoadTextureFromImage(image); // Convert loaded image to OpenGL texture free(imagePixels); UnloadImage(image); @@ -556,13 +557,14 @@ static SpriteFont LoadRBMF(const char *fileName) counter++; } - Image image = LoadImageFromData(imagePixels, rbmfHeader.imgWidth, rbmfHeader.imgHeight, UNCOMPRESSED_GRAY_ALPHA); + Image image = LoadImageEx(imagePixels, rbmfHeader.imgWidth, rbmfHeader.imgHeight); + ImageConvertFormat(&image, UNCOMPRESSED_GRAY_ALPHA); free(imagePixels); TraceLog(INFO, "[%s] Image reconstructed correctly, now converting it to texture", fileName); - spriteFont.texture = LoadTextureFromImage(image, false); + spriteFont.texture = LoadTextureFromImage(image); UnloadImage(image); // Unload image data //TraceLog(INFO, "[%s] Starting charSet reconstruction", fileName); @@ -689,7 +691,7 @@ static SpriteFont LoadTTF(const char *fileName, int fontSize) */ font.numChars = 95; font.charSet = (Character *)malloc(font.numChars*sizeof(Character)); - font.texture = LoadTextureFromImage(image, false); + font.texture = LoadTextureFromImage(image); //stbtt_aligned_quad letter; //int x = 0, y = 0; diff --git a/src/textures.c b/src/textures.c index e6c6cccb..42c7810b 100644 --- a/src/textures.c +++ b/src/textures.c @@ -123,6 +123,32 @@ Image LoadImage(const char *fileName) return image; } +// Load image data from Color array data (RGBA - 32bit) +Image LoadImageEx(Color *pixels, int width, int height) +{ + Image image; + image.data = NULL; + image.width = width; + image.height = height; + image.mipmaps = 1; + image.format = UNCOMPRESSED_R8G8B8A8; + + int k = 0; + + image.data = (unsigned char *)malloc(image.width*image.height*4*sizeof(unsigned char)); + + for (int i = 0; i < image.width*image.height*4; i += 4) + { + ((unsigned char *)image.data)[i] = pixels[k].r; + ((unsigned char *)image.data)[i + 1] = pixels[k].g; + ((unsigned char *)image.data)[i + 2] = pixels[k].b; + ((unsigned char *)image.data)[i + 3] = pixels[k].a; + k++; + } + + return image; +} + // Load an image from rRES file (raylib Resource) // TODO: Review function to support multiple color modes Image LoadImageFromRES(const char *rresName, int resId) @@ -243,12 +269,12 @@ Texture2D LoadTexture(const char *fileName) Image image = LoadImage(fileName); #if defined(PLATFORM_RPI) || defined(PLATFORM_WEB) - ConvertToPOT(&image, BLANK); + ImageConvertToPOT(&image, BLANK); #endif if (image.data != NULL) { - texture = LoadTextureFromImage(image, false); + texture = LoadTextureFromImage(image); UnloadImage(image); } else @@ -261,7 +287,7 @@ Texture2D LoadTexture(const char *fileName) return texture; } -Texture2D LoadTextureEx(void *data, int width, int height, int textureFormat, int mipmapCount, bool genMipmaps) +Texture2D LoadTextureEx(void *data, int width, int height, int textureFormat, int mipmapCount) { Texture2D texture; @@ -270,14 +296,14 @@ Texture2D LoadTextureEx(void *data, int width, int height, int textureFormat, in texture.mipmaps = mipmapCount; texture.format = textureFormat; - texture.id = rlglLoadTexture(data, width, height, textureFormat, mipmapCount, genMipmaps); + texture.id = rlglLoadTexture(data, width, height, textureFormat, mipmapCount); return texture; } // Load a texture from image data // NOTE: image is not unloaded, it must be done manually -Texture2D LoadTextureFromImage(Image image, bool genMipmaps) +Texture2D LoadTextureFromImage(Image image) { Texture2D texture; @@ -288,7 +314,7 @@ Texture2D LoadTextureFromImage(Image image, bool genMipmaps) texture.mipmaps = 0; texture.format = 0; - texture.id = rlglLoadTexture(image.data, image.width, image.height, image.format, image.mipmaps, false); + texture.id = rlglLoadTexture(image.data, image.width, image.height, image.format, image.mipmaps); texture.width = image.width; texture.height = image.height; @@ -304,7 +330,7 @@ Texture2D LoadTextureFromRES(const char *rresName, int resId) Texture2D texture; Image image = LoadImageFromRES(rresName, resId); - texture = LoadTextureFromImage(image, false); + texture = LoadTextureFromImage(image); UnloadImage(image); return texture; @@ -324,7 +350,7 @@ void UnloadTexture(Texture2D texture) // Convert image to POT (power-of-two) // NOTE: Requirement on OpenGL ES 2.0 (RPI, HTML5) -void ConvertToPOT(Image *image, Color fillColor) +void ImageConvertToPOT(Image *image, Color fillColor) { // TODO: Review for new image struct /* @@ -447,142 +473,172 @@ Color *GetPixelData(Image image) return pixels; } -// Fill image data with pixels Color data (RGBA - 32bit) -// NOTE: Data is transformed to desired format -Image LoadImageFromData(Color *pixels, int width, int height, int format) +// Get pixel data from GPU texture and return an Image +Image GetTextureData(Texture2D texture) { Image image; - image.data = NULL; - image.width = width; - image.height = height; - image.mipmaps = 1; - image.format = format; - - int k = 0; - switch (format) + image.data = rlglReadTexturePixels(texture.id, texture.format); + + if (image.data != NULL) { - case UNCOMPRESSED_GRAYSCALE: - { - image.data = (unsigned char *)malloc(image.width*image.height*sizeof(unsigned char)); - - for (int i = 0; i < image.width*image.height; i++) - { - ((unsigned char *)image.data)[i] = (unsigned char)((float)pixels[k].r*0.299f + (float)pixels[k].g*0.587f + (float)pixels[k].b*0.114f); - k++; - } + image.width = texture.width; + image.height = texture.height; + image.format = texture.format; + image.mipmaps = 1; + } + else TraceLog(WARNING, "Texture pixel data could not be obtained"); - } break; - case UNCOMPRESSED_GRAY_ALPHA: + return image; +} + +// Convert image data to desired format +void ImageConvertFormat(Image *image, int newFormat) +{ + if ((image->format != newFormat) && (image->format < 8) && (newFormat < 8)) + { + Color *pixels = GetPixelData(*image); + + free(image->data); + + image->format = newFormat; + + int k = 0; + + switch (image->format) { - image.data = (unsigned char *)malloc(image.width*image.height*2*sizeof(unsigned char)); - - for (int i = 0; i < image.width*image.height*2; i += 2) + case UNCOMPRESSED_GRAYSCALE: { - ((unsigned char *)image.data)[i] = (unsigned char)((float)pixels[k].r*0.299f + (float)pixels[k].g*0.587f + (float)pixels[k].b*0.114f); - ((unsigned char *)image.data)[i + 1] = pixels[k].a; - k++; - } + image->data = (unsigned char *)malloc(image->width*image->height*sizeof(unsigned char)); + + for (int i = 0; i < image->width*image->height; i++) + { + ((unsigned char *)image->data)[i] = (unsigned char)((float)pixels[k].r*0.299f + (float)pixels[k].g*0.587f + (float)pixels[k].b*0.114f); + k++; + } + + } break; + case UNCOMPRESSED_GRAY_ALPHA: + { + image->data = (unsigned char *)malloc(image->width*image->height*2*sizeof(unsigned char)); + + for (int i = 0; i < image->width*image->height*2; i += 2) + { + ((unsigned char *)image->data)[i] = (unsigned char)((float)pixels[k].r*0.299f + (float)pixels[k].g*0.587f + (float)pixels[k].b*0.114f); + ((unsigned char *)image->data)[i + 1] = pixels[k].a; + k++; + } - } break; - case UNCOMPRESSED_R5G6B5: - { - image.data = (unsigned short *)malloc(image.width*image.height*sizeof(unsigned short)); - - unsigned char r; - unsigned char g; - unsigned char b; - - for (int i = 0; i < image.width*image.height; i++) + } break; + case UNCOMPRESSED_R5G6B5: { - r = (unsigned char)(round((float)pixels[k].r*31/255)); - g = (unsigned char)(round((float)pixels[k].g*63/255)); - b = (unsigned char)(round((float)pixels[k].b*31/255)); + image->data = (unsigned short *)malloc(image->width*image->height*sizeof(unsigned short)); - ((unsigned short *)image.data)[i] = (unsigned short)r << 11 | (unsigned short)g << 5 | (unsigned short)b; + unsigned char r; + unsigned char g; + unsigned char b; + + for (int i = 0; i < image->width*image->height; i++) + { + r = (unsigned char)(round((float)pixels[k].r*31/255)); + g = (unsigned char)(round((float)pixels[k].g*63/255)); + b = (unsigned char)(round((float)pixels[k].b*31/255)); + + ((unsigned short *)image->data)[i] = (unsigned short)r << 11 | (unsigned short)g << 5 | (unsigned short)b; - k++; - } + k++; + } - } break; - case UNCOMPRESSED_R8G8B8: - { - image.data = (unsigned char *)malloc(image.width*image.height*3*sizeof(unsigned char)); - - for (int i = 0; i < image.width*image.height*3; i += 3) + } break; + case UNCOMPRESSED_R8G8B8: { - ((unsigned char *)image.data)[i] = pixels[k].r; - ((unsigned char *)image.data)[i + 1] = pixels[k].g; - ((unsigned char *)image.data)[i + 2] = pixels[k].b; - k++; - } - } break; - case UNCOMPRESSED_R5G5B5A1: - { - image.data = (unsigned short *)malloc(image.width*image.height*sizeof(unsigned short)); - - unsigned char r; - unsigned char g; - unsigned char b; - unsigned char a = 1; - - for (int i = 0; i < image.width*image.height; i++) + image->data = (unsigned char *)malloc(image->width*image->height*3*sizeof(unsigned char)); + + for (int i = 0; i < image->width*image->height*3; i += 3) + { + ((unsigned char *)image->data)[i] = pixels[k].r; + ((unsigned char *)image->data)[i + 1] = pixels[k].g; + ((unsigned char *)image->data)[i + 2] = pixels[k].b; + k++; + } + } break; + case UNCOMPRESSED_R5G5B5A1: { - r = (unsigned char)(round((float)pixels[k].r*31/255)); - g = (unsigned char)(round((float)pixels[k].g*31/255)); - b = (unsigned char)(round((float)pixels[k].b*31/255)); - a = (pixels[k].a > 50) ? 1 : 0; + image->data = (unsigned short *)malloc(image->width*image->height*sizeof(unsigned short)); - ((unsigned short *)image.data)[i] = (unsigned short)r << 11 | (unsigned short)g << 6 | (unsigned short)b << 1| (unsigned short)a; - - k++; - } + unsigned char r; + unsigned char g; + unsigned char b; + unsigned char a = 1; + + for (int i = 0; i < image->width*image->height; i++) + { + r = (unsigned char)(round((float)pixels[k].r*31/255)); + g = (unsigned char)(round((float)pixels[k].g*31/255)); + b = (unsigned char)(round((float)pixels[k].b*31/255)); + a = (pixels[k].a > 50) ? 1 : 0; + + ((unsigned short *)image->data)[i] = (unsigned short)r << 11 | (unsigned short)g << 6 | (unsigned short)b << 1| (unsigned short)a; + + k++; + } - } break; - case UNCOMPRESSED_R4G4B4A4: - { - image.data = (unsigned short *)malloc(image.width*image.height*sizeof(unsigned short)); - - unsigned char r; - unsigned char g; - unsigned char b; - unsigned char a; - - for (int i = 0; i < image.width*image.height; i++) + } break; + case UNCOMPRESSED_R4G4B4A4: { - r = (unsigned char)(round((float)pixels[k].r*15/255)); - g = (unsigned char)(round((float)pixels[k].g*15/255)); - b = (unsigned char)(round((float)pixels[k].b*15/255)); - a = (unsigned char)(round((float)pixels[k].a*15/255)); + image->data = (unsigned short *)malloc(image->width*image->height*sizeof(unsigned short)); - ((unsigned short *)image.data)[i] = (unsigned short)r << 12 | (unsigned short)g << 8| (unsigned short)b << 4| (unsigned short)a; - - k++; - } - - } break; - case UNCOMPRESSED_R8G8B8A8: - { - image.data = (unsigned char *)malloc(image.width*image.height*4*sizeof(unsigned char)); - - for (int i = 0; i < image.width*image.height*4; i += 4) + unsigned char r; + unsigned char g; + unsigned char b; + unsigned char a; + + for (int i = 0; i < image->width*image->height; i++) + { + r = (unsigned char)(round((float)pixels[k].r*15/255)); + g = (unsigned char)(round((float)pixels[k].g*15/255)); + b = (unsigned char)(round((float)pixels[k].b*15/255)); + a = (unsigned char)(round((float)pixels[k].a*15/255)); + + ((unsigned short *)image->data)[i] = (unsigned short)r << 12 | (unsigned short)g << 8| (unsigned short)b << 4| (unsigned short)a; + + k++; + } + + } break; + case UNCOMPRESSED_R8G8B8A8: { - ((unsigned char *)image.data)[i] = pixels[k].r; - ((unsigned char *)image.data)[i + 1] = pixels[k].g; - ((unsigned char *)image.data)[i + 2] = pixels[k].b; - ((unsigned char *)image.data)[i + 3] = pixels[k].a; - k++; - } - } break; - default: - { - TraceLog(WARNING, "Format not recognized, image could not be loaded"); - - return image; - } break; + image->data = (unsigned char *)malloc(image->width*image->height*4*sizeof(unsigned char)); + + for (int i = 0; i < image->width*image->height*4; i += 4) + { + ((unsigned char *)image->data)[i] = pixels[k].r; + ((unsigned char *)image->data)[i + 1] = pixels[k].g; + ((unsigned char *)image->data)[i + 2] = pixels[k].b; + ((unsigned char *)image->data)[i + 3] = pixels[k].a; + k++; + } + } break; + default: break; + } + + free(pixels); } + else TraceLog(WARNING, "Image data format is compressed, can not be converted"); +} - return image; +/* +Image ImageCopy(Image image); +void ImageCrop(Image *image, Rectangle crop); +void ImageResize(Image *image, int newWidth, int newHeight); +void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec); +void ImageDrawText(Image *dst, const char *text, Vector2 position, int size, Color color); +*/ + +// Generate GPU mipmaps for a texture +void GenTextureMipmaps(Texture2D texture) +{ + rlglGenerateMipmaps(texture.id); } // Draw a Texture2D @@ -1224,6 +1280,8 @@ static Image LoadASTC(const char *fileName) // NOTE: Assuming Little Endian (could it be wrong?) image.width = 0x00000000 | ((int)header.width[2] << 16) | ((int)header.width[1] << 8) | ((int)header.width[0]); image.height = 0x00000000 | ((int)header.height[2] << 16) | ((int)header.height[1] << 8) | ((int)header.height[0]); + + // NOTE: ASTC format only contains one mipmap level image.mipmaps = 1; TraceLog(DEBUG, "ASTC image width: %i", image.width); @@ -1250,5 +1308,49 @@ static Image LoadASTC(const char *fileName) fclose(astcFile); } + return image; +} + +// Load RAW image file +static Image LoadRAW(const char *fileName, int width, int height, int format, int headerSize) +{ + Image image; + + image.data = NULL; + image.width = 0; + image.height = 0; + image.mipmaps = 0; + image.format = 0; + + FILE *rawFile = fopen(fileName, "rb"); + + if (rawFile == NULL) + { + TraceLog(WARNING, "[%s] RAW image file could not be opened", fileName); + } + else + { + if (headerSize > 0) fseek(rawFile, headerSize, SEEK_SET); + + int dataSize = 0; + + // TODO: Calculate data size and allocate memory + switch (format) + { + + } + + fread(image.data, dataSize, 1, rawFile); + + // TODO: Check if data have been read + + image.width = width; + image.height = height; + image.mipmaps = 0; + image.format = format; + + fclose(rawFile); + } + return image; } \ No newline at end of file