Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added rudimentary SVG support. #2738

Merged
merged 6 commits into from
Sep 2, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 10 additions & 7 deletions examples/textures/textures_svg_loading.c
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
/*******************************************************************************************
*
* raylib [textures] example - Image loading and texture creation
* raylib [textures] example - SVG loading and texture creation
*
* NOTE: Images are loaded in CPU memory (RAM); textures are loaded in GPU memory (VRAM)
*
* Example originally created with raylib 1.3, last time updated with raylib 1.3
* Example originally created with raylib 4.2, last time updated with raylib 4.2
*
* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
* BSD-like license that allows static linking with closed source software
*
* Copyright (c) 2015-2022 Ramon Santamaria (@raysan5)
* Copyright (c) 2022 Dennis Meinen (@bixxy#4258 on Discord)
*
********************************************************************************************/

Expand All @@ -25,11 +25,11 @@ int main(void)
const int screenWidth = 800;
const int screenHeight = 450;

InitWindow(screenWidth, screenHeight, "raylib [textures] example - image loading");
InitWindow(screenWidth, screenHeight, "raylib [textures] example - svg loading");

// NOTE: Textures MUST be loaded after Window initialization (OpenGL context is required)

Image image = LoadImage("resources/raylib_logo.png"); // Loaded in CPU memory (RAM)
Image image = LoadImageSvg("resources/test.svg", 400, 350); // Loaded in CPU memory (RAM)
Texture2D texture = LoadTextureFromImage(image); // Image converted to texture, GPU memory (VRAM)
UnloadImage(image); // Once image has been converted to texture and uploaded to VRAM, it can be unloaded from RAM

Expand All @@ -52,7 +52,10 @@ int main(void)

DrawTexture(texture, screenWidth/2 - texture.width/2, screenHeight/2 - texture.height/2, WHITE);

DrawText("this IS a texture loaded from an image!", 300, 370, 10, GRAY);
//Red boarder to illustrate how the SVG is centered within the specified dimensions
DrawRectangleLines((screenWidth / 2 - texture.width / 2) - 1, (screenHeight / 2 - texture.height / 2) - 1, texture.width + 2, texture.height + 2, RED);

DrawText("this IS a texture loaded from an SVG file!", 300, 410, 10, GRAY);

EndDrawing();
//----------------------------------------------------------------------------------
Expand All @@ -66,4 +69,4 @@ int main(void)
//--------------------------------------------------------------------------------------

return 0;
}
}
1 change: 1 addition & 0 deletions src/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@
//#define SUPPORT_FILEFORMAT_ASTC 1
//#define SUPPORT_FILEFORMAT_PKM 1
//#define SUPPORT_FILEFORMAT_PVR 1
#define SUPPORT_FILEFORMAT_SVG 1

// Support image export functionality (.png, .bmp, .tga, .jpg, .qoi)
#define SUPPORT_IMAGE_EXPORT 1
Expand Down
4 changes: 2 additions & 2 deletions src/raylib.h
Original file line number Diff line number Diff line change
Expand Up @@ -1226,8 +1226,8 @@ RLAPI Rectangle GetCollisionRec(Rectangle rec1, Rectangle rec2);
// NOTE: This functions do not require GPU access
RLAPI Image LoadImage(const char *fileName); // Load image from file into CPU memory (RAM)
RLAPI Image LoadImageRaw(const char *fileName, int width, int height, int format, int headerSize); // Load image from RAW file data
RLAPI Image LoadImageSvg(const char* fileName); // Load image from SVG file data with default size from SVG
RLAPI Image LoadImageSvgWithSize(const char* fileName, int width, int height); // Load image from SVG file data with specified size
RLAPI Image LoadImageSvg(const char *fileName, int width, int height); // Load image from SVG file data with specified size
RLAPI Image LoadImageSvgFromString(const char *string, int width, int height); // Load an image from a SVG string with custom size
RLAPI Image LoadImageAnim(const char *fileName, int *frames); // Load image sequence from file (frames appended to image.data)
RLAPI Image LoadImageFromMemory(const char *fileType, const unsigned char *fileData, int dataSize); // Load image from memory buffer, fileType refers to extension: i.e. '.png'
RLAPI Image LoadImageFromTexture(Texture2D texture); // Load image from GPU texture data
Expand Down
86 changes: 46 additions & 40 deletions src/rtextures.c
Original file line number Diff line number Diff line change
Expand Up @@ -187,12 +187,13 @@
#include "external/stb_image_resize.h" // Required for: stbir_resize_uint8() [ImageResize()]
#endif

#define NANOSVG_IMPLEMENTATION // Expands implementation
#include "external/nanosvg.h"

#define NANOSVGRAST_IMPLEMENTATION
#include "external/nanosvgrast.h"
#if defined(SUPPORT_FILEFORMAT_SVG)
#define NANOSVG_IMPLEMENTATION // Expands implementation
#include "external/nanosvg.h"

#define NANOSVGRAST_IMPLEMENTATION
#include "external/nanosvgrast.h"
#endif

//----------------------------------------------------------------------------------
// Defines and Macros
Expand Down Expand Up @@ -282,70 +283,57 @@ Image LoadImageRaw(const char *fileName, int width, int height, int format, int
return image;
}

// Load image from SVG file data with default size from SVG
Image LoadImageSvg(const char* fileName)
// Load an image from SVG file data with a custom size
Image LoadImageSvg(const char *fileName, int width, int height)
{
Image image = { 0 };

unsigned int dataSize = 0;
unsigned char* fileData = LoadFileData(fileName, &dataSize);
unsigned char *string = LoadFileData(fileName, &dataSize);

if (fileData != NULL)
if (string != NULL)
{
struct NSVGimage* svgImage = nsvgParse(fileData, "px", 96.0f);

const int width = (int)svgImage->width;
const int height = (int)svgImage->height;
// Delete
nsvgDelete(svgImage);

return LoadImageSvgWithSize(fileName, width, height);
image = LoadImageSvgFromString(string, width, height);
RL_FREE(string);
}


return image;
}

// Load an image from SVG file data with a custom size
Image LoadImageSvgWithSize(const char* fileName, int width, int height)
// Load an image from a SVG string with custom size
Image LoadImageSvgFromString(const char *string, int width, int height)
{
Image image = { 0 };

unsigned int dataSize = 0;
unsigned char* fileData = LoadFileData(fileName, &dataSize);

if (fileData != NULL)
if (string != NULL)
{
struct NSVGimage* svgImage = nsvgParse(fileData, "px", 96.0f);


struct NSVGimage *svgImage = nsvgParse(string, "px", 96.0f);

// Allocate memory for image
unsigned char* img = malloc(width * height * 4);
unsigned char *img = malloc(width*height*4);

// Calculate scales for both the width and the height
const float scaleWidth = width / svgImage->width;
const float scaleHeight = height / svgImage->height;
const float scaleWidth = width/svgImage->width;
const float scaleHeight = height/svgImage->height;

// Set the largest of the 2 scales to be the scale to use
const float scale = (scaleHeight > scaleWidth) ? scaleWidth : scaleHeight;
const float scale = (scaleHeight > scaleWidth) ? scaleWidth : scaleHeight;

int offsetX = 0;
int offsetY = 0;

if (scaleHeight > scaleWidth)
{
offsetY = (height - svgImage->height * scale) / 2;
offsetY = (height - svgImage->height*scale) / 2;
}
else
else
{
offsetX = (width - svgImage->width * scale) / 2;
offsetX = (width - svgImage->width*scale) / 2;
}


// Rasterize
// Rasterize
struct NSVGrasterizer* rast = nsvgCreateRasterizer();
nsvgRasterize(rast, svgImage, (int)offsetX, (int)offsetY, scale, img, width, height, width * 4);
nsvgRasterize(rast, svgImage, (int)offsetX, (int)offsetY, scale, img, width, height, width*4);

// Populate image struct with all data
image.data = img;
Expand All @@ -354,16 +342,13 @@ Image LoadImageSvgWithSize(const char* fileName, int width, int height)
image.mipmaps = 1;
image.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;

raysan5 marked this conversation as resolved.
Show resolved Hide resolved

// Delete
nsvgDelete(svgImage);
RL_FREE(fileData);
}

return image;
}


// Load animated image data
// - Image.data buffer includes all frames: [image#0][image#1][image#2][...]
// - Number of frames is returned through 'frames' parameter
Expand Down Expand Up @@ -517,6 +502,27 @@ Image LoadImageFromMemory(const char *fileType, const unsigned char *fileData, i
{
image.data = rl_load_astc_from_memory(fileData, dataSize, &image.width, &image.height, &image.format, &image.mipmaps);
}
#endif
#if defined(SUPPORT_FILEFORMAT_SVG)
else if (strcmp(fileType, ".svg") == 0)
{
if (fileData != NULL)
raysan5 marked this conversation as resolved.
Show resolved Hide resolved
{
// Creating a duplicate svg to read sizes from due to nsvgParse modifiying the string buffer.
unsigned char *duplicate = (unsigned char*)RL_MALLOC(dataSize);
memcpy(duplicate, fileData, dataSize);
struct NSVGimage *svgImage = nsvgParse(duplicate, "px", 96.0f);
RL_FREE(duplicate);

const int width = (int)svgImage->width;
const int height = (int)svgImage->height;
// Delete
nsvgDelete(svgImage);


image = LoadImageSvgFromString(fileData, width, height);
}
}
#endif
else TRACELOG(LOG_WARNING, "IMAGE: Data format not supported");

Expand Down