mirror of
https://github.com/raysan5/raylib.git
synced 2026-10-15 14:11:55 -04:00
Compare commits
3 Commits
c042cdc025
...
5f886e208a
| Author | SHA1 | Date | |
|---|---|---|---|
| 5f886e208a | |||
| d33888dd28 | |||
| 5f83146cac |
35
src/external/rlsw.h
vendored
35
src/external/rlsw.h
vendored
@ -1402,12 +1402,10 @@ static SW_INLINE void sw_color8_to_color(float *SW_RESTRICT dst, const uint8_t *
|
||||
uint32x4_t dwords = vmovl_u16(vget_low_u16(words));
|
||||
float32x4_t fvals = vmulq_f32(vcvtq_f32_u32(dwords), vdupq_n_f32(SW_INV_255));
|
||||
vst1q_f32(dst, fvals);
|
||||
|
||||
#elif defined(SW_HAS_SSE41)
|
||||
__m128i bytes = _mm_loadu_si32(src);
|
||||
__m128 fvals = _mm_mul_ps(_mm_cvtepi32_ps(_mm_cvtepu8_epi32(bytes)), _mm_set1_ps(SW_INV_255));
|
||||
_mm_storeu_ps(dst, fvals);
|
||||
|
||||
#elif defined(SW_HAS_SSE2)
|
||||
__m128i zero = _mm_setzero_si128();
|
||||
__m128i bytes = _mm_loadu_si32(src);
|
||||
@ -1415,22 +1413,13 @@ static SW_INLINE void sw_color8_to_color(float *SW_RESTRICT dst, const uint8_t *
|
||||
__m128i dwords = _mm_unpacklo_epi16(words, zero);
|
||||
__m128 fvals = _mm_mul_ps(_mm_cvtepi32_ps(dwords), _mm_set1_ps(SW_INV_255));
|
||||
_mm_storeu_ps(dst, fvals);
|
||||
|
||||
#elif defined(SW_HAS_RVV)
|
||||
// TODO: WARNING: Sample code generated by AI, needs testing and review
|
||||
size_t vl = __riscv_vsetvl_e8m1(4); // Set vector length for 8-bit input elements
|
||||
vuint8m1_t vsrc_u8 = __riscv_vle8_v_u8m1(src, vl); // Load 4 unsigned 8-bit integers
|
||||
vuint32m1_t vsrc_u32 = __riscv_vwcvt_xu_u_v_u32m1(vsrc_u8, vl); // Widen to 32-bit unsigned integers
|
||||
vfloat32m1_t vsrc_f32 = __riscv_vfcvt_f_xu_v_f32m1(vsrc_u32, vl); // Convert to float32
|
||||
vfloat32m1_t vnorm = __riscv_vfmul_vf_f32m1(vsrc_f32, SW_INV_255, vl); // Multiply by 1/255.0 to normalize
|
||||
__riscv_vse32_v_f32m1(dst, vnorm, vl); // Store result
|
||||
|
||||
// TODO: Implement RVV intrinsics
|
||||
#elif RLSW_USE_COLOR_LUT
|
||||
dst[0] = SW_LUT_UINT8_TO_FLOAT[src[0]];
|
||||
dst[1] = SW_LUT_UINT8_TO_FLOAT[src[1]];
|
||||
dst[2] = SW_LUT_UINT8_TO_FLOAT[src[2]];
|
||||
dst[3] = SW_LUT_UINT8_TO_FLOAT[src[3]];
|
||||
|
||||
#else
|
||||
dst[0] = (float)src[0]*SW_INV_255;
|
||||
dst[1] = (float)src[1]*SW_INV_255;
|
||||
@ -1447,39 +1436,21 @@ static SW_INLINE void sw_color_to_color8(uint8_t *SW_RESTRICT dst, const float *
|
||||
uint16x4_t i16 = vmovn_u32(i32);
|
||||
uint8x8_t i8 = vmovn_u16(vcombine_u16(i16, i16));
|
||||
vst1_lane_u32((uint32_t *)dst, vreinterpret_u32_u8(i8), 0);
|
||||
|
||||
#elif defined(SW_HAS_SSE41)
|
||||
__m128 fvals = _mm_mul_ps(_mm_loadu_ps(src), _mm_set1_ps(255.0f));
|
||||
__m128i i32 = _mm_cvttps_epi32(fvals);
|
||||
__m128i i16 = _mm_packus_epi32(i32, i32);
|
||||
__m128i i8 = _mm_packus_epi16(i16, i16);
|
||||
_mm_storeu_si32(dst, i8);
|
||||
|
||||
#elif defined(SW_HAS_SSE2)
|
||||
__m128 fvals = _mm_mul_ps(_mm_loadu_ps(src), _mm_set1_ps(255.0f));
|
||||
__m128i i32 = _mm_cvttps_epi32(fvals);
|
||||
__m128i i16 = _mm_packs_epi32(i32, i32);
|
||||
__m128i i8 = _mm_packus_epi16(i16, i16);
|
||||
_mm_storeu_si32(dst, i8);
|
||||
|
||||
#elif defined(SW_HAS_RVV)
|
||||
// TODO: WARNING: Sample code generated by AI, needs testing and review
|
||||
// REVIEW: It shouldn't perform so many operations; take inspiration from other versions
|
||||
// TODO: Implement RVV intrinsics
|
||||
// NOTE: RVV 1.0 specs define the use of __riscv_ prefix for instrinsic functions
|
||||
size_t vl = __riscv_vsetvl_e32m1(4); // Load up to 4 floats into a vector register
|
||||
vfloat32m1_t vsrc = __riscv_vle32_v_f32m1(src, vl); // Load float32 values
|
||||
|
||||
// Multiply by 255.0f and add 0.5f for rounding
|
||||
vfloat32m1_t vscaled = __riscv_vfmul_vf_f32m1(vsrc, 255.0f, vl);
|
||||
vscaled = __riscv_vfadd_vf_f32m1(vscaled, 0.5f, vl);
|
||||
|
||||
// Convert to unsigned integer (truncate toward zero)
|
||||
vuint32m1_t vu32 = __riscv_vfcvt_xu_f_v_u32m1(vscaled, vl);
|
||||
|
||||
// Narrow from u32 -> u8
|
||||
vuint8m1_t vu8 = __riscv_vnclipu_wx_u8m1(vu32, 0, vl); // Round toward zero
|
||||
__riscv_vse8_v_u8m1(dst, vu8, vl); // Store result
|
||||
|
||||
#else
|
||||
dst[0] = (uint8_t)(src[0]*255.0f);
|
||||
dst[1] = (uint8_t)(src[1]*255.0f);
|
||||
@ -4062,7 +4033,6 @@ void *swGetColorBuffer(int *width, int *height)
|
||||
return RLSW.framebuffer.color.pixels;
|
||||
}
|
||||
|
||||
|
||||
void swSwapColorBuffers(void)
|
||||
{
|
||||
#if RLSW_DOUBLE_BUFFERING
|
||||
@ -4077,7 +4047,6 @@ void swSwapColorBuffers(void)
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void swEnable(SWstate state)
|
||||
{
|
||||
switch (state)
|
||||
|
||||
@ -1222,7 +1222,6 @@ void OpenURL(const char *url)
|
||||
#if defined(__APPLE__)
|
||||
sprintf(cmd, "open '%s'", url);
|
||||
#endif
|
||||
// TODO: Replace system() call by custom process
|
||||
int result = system(cmd);
|
||||
|
||||
if (result == -1) TRACELOG(LOG_WARNING, "OpenURL() child process could not be created");
|
||||
|
||||
@ -2236,7 +2236,7 @@ static void PollKeyboardEvents(void)
|
||||
|
||||
if (CORE.Input.Keyboard.charPressedQueueCount < MAX_CHAR_PRESSED_QUEUE)
|
||||
{
|
||||
// TODO/FIXME: This is not actually converting to unicode properly because it's not taking things like shift into account
|
||||
// TODO: WARNING: This is not actually converting to unicode properly because it's not taking things like shift into account
|
||||
CORE.Input.Keyboard.charPressedQueue[CORE.Input.Keyboard.charPressedQueueCount] = evkeyToUnicodeLUT[event.code];
|
||||
CORE.Input.Keyboard.charPressedQueueCount++;
|
||||
}
|
||||
|
||||
@ -54,8 +54,8 @@
|
||||
//----------------------------------------------------------------------------------
|
||||
// Defines and Macros
|
||||
//----------------------------------------------------------------------------------
|
||||
// TODO: HACK: Added flag if not provided by GLFW when using external library
|
||||
// Latest GLFW release (GLFW 3.3.8) does not implement this flag, it was added for 3.4.0-dev
|
||||
// NOTE: Added flag if not provided by GLFW when using external library
|
||||
// WARNING: Latest GLFW release (GLFW 3.3.8) does not implement this flag, it was added for 3.4.0-dev
|
||||
#if !defined(GLFW_MOUSE_PASSTHROUGH)
|
||||
#define GLFW_MOUSE_PASSTHROUGH 0x0002000D
|
||||
#endif
|
||||
@ -1313,9 +1313,8 @@ int InitPlatform(void)
|
||||
}, platform.canvasId);
|
||||
|
||||
// Load memory framebuffer with desired screen size
|
||||
// NOTE: Despite using a software framebuffer for blitting, GLFW still creates a WebGL canvas,
|
||||
// WARNING: Despite using a software framebuffer for blitting, GLFW still creates a WebGL canvas,
|
||||
// but it is not being used, on SwapScreenBuffer() the pure software renderer is used
|
||||
// TODO: Consider requesting another type of canvas, not a WebGL one --> Replace GLFW-web by Emscripten?
|
||||
platform.pixels = (unsigned int *)RL_CALLOC(CORE.Window.screen.width*CORE.Window.screen.height, sizeof(unsigned int));
|
||||
#else
|
||||
if (FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE))
|
||||
@ -1406,7 +1405,9 @@ int InitPlatform(void)
|
||||
glfwSetCursorEnterCallback(platform.handle, MouseEnterCallback);
|
||||
|
||||
glfwMakeContextCurrent(platform.handle);
|
||||
result = true; // TODO: WARNING: glfwGetError(NULL); symbol can not be found in Web
|
||||
|
||||
// WARNING: result = glfwGetError(NULL), symbol can not be found in GLFW Web
|
||||
result = true;
|
||||
|
||||
// Check context activation
|
||||
if (result == true) //(result != GLFW_NO_WINDOW_CONTEXT) && (result != GLFW_PLATFORM_ERROR))
|
||||
|
||||
@ -389,7 +389,6 @@ RMAPI float Vector2LineAngle(Vector2 start, Vector2 end)
|
||||
{
|
||||
float result = 0.0f;
|
||||
|
||||
// TODO(10/9/2023): Currently angles move clockwise, determine if this is wanted behavior
|
||||
result = -atan2f(end.y - start.y, end.x - start.x);
|
||||
|
||||
return result;
|
||||
@ -2703,7 +2702,7 @@ RMAPI Matrix MatrixCompose(Vector3 translation, Quaternion rotation, Vector3 sca
|
||||
}
|
||||
|
||||
// Decompose a transformation matrix into its rotational, translational and scaling components and remove shear
|
||||
// TODO: This function is not following raymath conventions defined in header: NOT self-contained
|
||||
// TODO: WARNING: Following raymath convention and make the function self-contained
|
||||
RMAPI void MatrixDecompose(Matrix mat, Vector3 *translation, Quaternion *rotation, Vector3 *scale)
|
||||
{
|
||||
float eps = (float)1e-9;
|
||||
|
||||
10
src/rcore.c
10
src/rcore.c
@ -478,7 +478,7 @@ static const char *autoEventTypeName[] = {
|
||||
|
||||
static AutomationEventList *currentEventList = NULL; // Current automation events list, set by user, keep internal pointer
|
||||
static bool automationEventRecording = false; // Recording automation events flag
|
||||
//static short automationEventEnabled = 0b0000001111111111; // TODO: Automation events enabled for recording/playing
|
||||
//static short automationEventEnabled = 0b0000001111111111; // Automation events enabled for recording/playing
|
||||
#endif
|
||||
//-----------------------------------------------------------------------------------
|
||||
|
||||
@ -535,8 +535,6 @@ const char *TextFormat(const char *text, ...); // Formatting of text with variab
|
||||
#elif defined(PLATFORM_MEMORY)
|
||||
#include "platforms/rcore_memory.c"
|
||||
#else
|
||||
// TODO: Include your custom platform backend!
|
||||
// i.e software rendering backend or console backend!
|
||||
#pragma message ("WARNING: No [rcore] platform defined")
|
||||
#endif
|
||||
|
||||
@ -611,8 +609,6 @@ void InitWindow(int width, int height, const char *title)
|
||||
#elif defined(PLATFORM_MEMORY)
|
||||
TRACELOG(LOG_INFO, "Platform backend: MEMORY (No OS)");
|
||||
#else
|
||||
// TODO: Include your custom platform backend!
|
||||
// i.e software rendering backend or console backend!
|
||||
TRACELOG(LOG_INFO, "Platform backend: CUSTOM");
|
||||
#endif
|
||||
|
||||
@ -4570,7 +4566,7 @@ static void RecordAutomationEvent(void)
|
||||
if ((CORE.Input.Gamepad.currentState[gamepad] != CORE.Input.Gamepad.previousState[gamepad]) &&
|
||||
(CORE.Input.Gamepad.currentState[gamepad])) // Check if changed to ready
|
||||
{
|
||||
// TODO: Save gamepad connect event
|
||||
// TODO: Automation event: Save gamepad connect event
|
||||
}
|
||||
*/
|
||||
|
||||
@ -4579,7 +4575,7 @@ static void RecordAutomationEvent(void)
|
||||
if ((CORE.Input.Gamepad.currentState[gamepad] != CORE.Input.Gamepad.previousState[gamepad]) &&
|
||||
(!CORE.Input.Gamepad.currentState[gamepad])) // Check if changed to not-ready
|
||||
{
|
||||
// TODO: Save gamepad disconnect event
|
||||
// TODO: Automation event: Save gamepad disconnect event
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
@ -886,7 +886,7 @@ RLAPI void rlLoadDrawQuad(void); // Load and draw a quad
|
||||
#elif defined(GRAPHICS_API_OPENGL_ES2)
|
||||
// NOTE: OpenGL ES 2.0 can be enabled on Desktop platforms,
|
||||
// in that case, functions are loaded from a custom glad for OpenGL ES 2.0
|
||||
// TODO: OpenGL ES 2.0 support shouldn't be platform-dependent, neither require GLAD
|
||||
// TODO: rlgl OpenGL ES 2.0 selection shouldn't depend on raylib platform macros, a specific GLAD shouldn't be neither required
|
||||
#if defined(PLATFORM_DESKTOP_GLFW) || defined(PLATFORM_DESKTOP_SDL)
|
||||
#define GLAD_GLES2_IMPLEMENTATION
|
||||
#include "external/glad_gles2.h"
|
||||
@ -2966,8 +2966,9 @@ void rlDrawRenderBatch(rlRenderBatch *batch)
|
||||
// Activate elements VAO
|
||||
if (RLGL.ExtSupported.vao) glBindVertexArray(batch->vertexBuffer[batch->currentBuffer].vaoId);
|
||||
|
||||
// TODO: If no data changed on the CPU arrays there is no need to re-upload data to GPU,
|
||||
// a flag can be used to detect changes but it would imply keeping a copy buffer and memcmp() both, does it worth it?
|
||||
// NOTE: If no data changed on the CPU arrays there is no need to re-upload data to GPU,
|
||||
// a flag can be used to detect changes but it would imply keeping a copy buffer and memcmp() both
|
||||
// It was tested and the gain seemed to be minimal or nothing at all, probably driver already takes care of that
|
||||
|
||||
// Vertex positions buffer
|
||||
glBindBuffer(GL_ARRAY_BUFFER, batch->vertexBuffer[batch->currentBuffer].vboId[0]);
|
||||
@ -3240,7 +3241,6 @@ unsigned int rlLoadTexture(const void *data, int width, int height, int format,
|
||||
#if defined(GRAPHICS_API_OPENGL_11)
|
||||
if (format >= RL_PIXELFORMAT_COMPRESSED_DXT1_RGB)
|
||||
{
|
||||
// TODO: Support texture data decompression
|
||||
TRACELOG(RL_LOG_WARNING, "GL: OpenGL 1.1 does not support GPU compressed texture formats");
|
||||
return id;
|
||||
}
|
||||
|
||||
@ -2041,12 +2041,12 @@ bool ExportMesh(Mesh mesh, const char *fileName)
|
||||
{
|
||||
// TODO: Implement gltf/glb support
|
||||
/*
|
||||
cgltf_size expected = cgltf_write(options, NULL, 0, data);
|
||||
char *buffer = (char *)RL_CALLOC(expected, 0);
|
||||
cgltf_size actual = cgltf_write(options, buffer, expected, data);
|
||||
cgltf_size expectedSize = cgltf_write(options, NULL, 0, data);
|
||||
char *buffer = (char *)RL_CALLOC(expectedSize, 0);
|
||||
cgltf_size actualSize = cgltf_write(options, buffer, expectedSize, data);
|
||||
|
||||
// NOTE: cgltf_write() includes a NULL terminator that should be ommited in case of a .glb
|
||||
if (options->type == cgltf_file_type_glb) cgltf_write_glb(file, buffer, actual - 1, data->bin, data->bin_size);
|
||||
if (options->type == cgltf_file_type_glb) cgltf_write_glb(fileName, buffer, actual - 1, data->bin, data->bin_size);
|
||||
else SaveFileText(fileName, buffer); // Write a plain JSON file
|
||||
*/
|
||||
}
|
||||
@ -2197,8 +2197,6 @@ Material *LoadMaterials(const char *fileName, int *materialCount)
|
||||
Material *materials = NULL;
|
||||
unsigned int count = 0;
|
||||
|
||||
// TODO: Support IQM and GLTF for materials parsing
|
||||
|
||||
#if SUPPORT_FILEFORMAT_MTL
|
||||
if (IsFileExtension(fileName, ".mtl"))
|
||||
{
|
||||
@ -5142,7 +5140,7 @@ static ModelAnimation *LoadModelAnimationsIQM(const char *fileName, int *animCou
|
||||
animations[a].keyframeCount = anim[a].num_frames;
|
||||
animations[a].keyframePoses = (Transform **)RL_CALLOC(anim[a].num_frames, sizeof(Transform *));
|
||||
memcpy(animations[a].name, fileDataPtr + iqmHeader->ofs_text + anim[a].name, 32);
|
||||
// TODO: Use animation framerate data?
|
||||
// TODO: Store animation framerate data?
|
||||
//animations[a].framerate = anim.framerate;
|
||||
|
||||
TRACELOG(LOG_INFO, "MODEL: [%s] Loaded animation: %s | Frames: %d | Framerate: %f", fileName, animations[a].name, animations[a].keyframeCount, anim[a].framerate);
|
||||
|
||||
@ -3773,10 +3773,7 @@ void DrawSplineLinear(const Vector2 *points, int pointCount, float thick, Color
|
||||
normal.y = 0.0f;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
normal = prevNormal;
|
||||
}
|
||||
else normal = prevNormal;
|
||||
|
||||
Vector2 radius = { prevNormal.x + normal.x, prevNormal.y + normal.y };
|
||||
float radiusLength = sqrtf(radius.x*radius.x + radius.y*radius.y);
|
||||
|
||||
@ -638,7 +638,6 @@ GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSiz
|
||||
{
|
||||
bool genFontChars = false;
|
||||
stbtt_fontinfo fontInfo = { 0 };
|
||||
// TODO: Should a shallow copy be created to avoid "dealing" with a const user array?
|
||||
int *requiredCodepoints = (int *)codepoints;
|
||||
|
||||
if (stbtt_InitFont(&fontInfo, (unsigned char *)fileData, 0)) // Initialize font for data reading
|
||||
@ -1797,8 +1796,6 @@ char *TextReplace(const char *text, const char *search, const char *replacement)
|
||||
|
||||
if ((textLen + count*(replaceLen - searchLen)) < (MAX_TEXT_BUFFER_LENGTH - 1))
|
||||
{
|
||||
// TODO: Allow copying data replaced up to maximum buffer size and stop
|
||||
|
||||
tempPtr = buffer; // Point to result start
|
||||
|
||||
// First time through the loop, all the variable are set correctly from here on,
|
||||
@ -1988,8 +1985,6 @@ char *TextInsert(const char *text, const char *insert, int position)
|
||||
|
||||
if ((textLen + insertLen) < (MAX_TEXT_BUFFER_LENGTH - 1))
|
||||
{
|
||||
// TODO: Allow copying data inserted up to maximum buffer size and stop
|
||||
|
||||
for (int i = 0; i < position; i++) buffer[i] = text[i];
|
||||
for (int i = 0; i < insertLen; i++) buffer[i+position] = insert[i];
|
||||
for (int i = position; i < textLen; i++) buffer[i+insertLen] = text[i];
|
||||
@ -2131,7 +2126,7 @@ int TextFindIndex(const char *text, const char *search)
|
||||
|
||||
// Get upper case version of provided string
|
||||
// WARNING: Limited functionality, only basic characters set
|
||||
// TODO: Support UTF-8 diacritics to upper-case, check codepoints
|
||||
// TODO: Support UTF-8 diacritics (á, ñ, ü...) to upper-case, check codepoints
|
||||
char *TextToUpper(const char *text)
|
||||
{
|
||||
static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 };
|
||||
|
||||
@ -1592,7 +1592,7 @@ Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Co
|
||||
TRACELOG(LOG_INFO, "IMAGE: Text scaled by factor: %f", scaleFactor);
|
||||
|
||||
// Using nearest-neighbor scaling algorithm for default font
|
||||
// TODO: Allow defining the preferred scaling mechanism externally
|
||||
// TODO: Support selection of preferred scaling mechanism, use flag?
|
||||
if (font.texture.id == GetFontDefault().texture.id) ImageResizeNN(&imText, (int)(imSize.x*scaleFactor), (int)(imSize.y*scaleFactor));
|
||||
else ImageResize(&imText, (int)(imSize.x*scaleFactor), (int)(imSize.y*scaleFactor));
|
||||
}
|
||||
@ -1654,7 +1654,7 @@ Image ImageFromChannel(Image image, int selectedChannel)
|
||||
selectedChannel = 3;
|
||||
}
|
||||
|
||||
// TODO: Consider other one-channel formats: R16, R32
|
||||
// TODO: Support R16 and R32 channel sizes, not only 8-bit
|
||||
result.format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE;
|
||||
result.height = image.height;
|
||||
result.width = image.width;
|
||||
@ -3965,7 +3965,51 @@ void ImageDrawRectangleLinesEx(Image *dst, Rectangle rec, int thick, Color color
|
||||
// Draw rectangle with gradient colors within an image, counter-clockwise color order
|
||||
void ImageDrawRectangleGradientEx(Image *dst, Rectangle rec, Color col1, Color col2, Color col3, Color col4)
|
||||
{
|
||||
// TODO: NEW: Implement ImageDrawRectangleGradientEx()
|
||||
if ((dst == NULL) || (rec.width <= 0) || (rec.height <= 0)) return;
|
||||
|
||||
int x0 = (int)floorf(rec.x);
|
||||
int y0 = (int)floorf(rec.y);
|
||||
int x1 = (int)ceilf(rec.x + rec.width);
|
||||
int y1 = (int)ceilf(rec.y + rec.height);
|
||||
|
||||
// Limit draw to image bounds
|
||||
if (x0 < 0) x0 = 0;
|
||||
if (y0 < 0) y0 = 0;
|
||||
if (x1 > dst->width) x1 = dst->width;
|
||||
if (y1 > dst->height) y1 = dst->height;
|
||||
|
||||
// Safety check
|
||||
if ((x0 >= x1) || (y0 >= y1)) return;
|
||||
|
||||
for (int y = y0; y < y1; y++)
|
||||
{
|
||||
float ty = (y - rec.y)/rec.height;
|
||||
if (ty < 0.0f) ty = 0.0f;
|
||||
if (ty > 1.0f) ty = 1.0f;
|
||||
|
||||
for (int x = x0; x < x1; x++)
|
||||
{
|
||||
float tx = (x - rec.x)/rec.width;
|
||||
if (tx < 0.0f) tx = 0.0f;
|
||||
if (tx > 1.0f) tx = 1.0f;
|
||||
|
||||
// Bilinear interpolation weights
|
||||
float w1 = (1.0f - tx)*(1.0f - ty);
|
||||
float w2 = (1.0f - tx)*ty;
|
||||
float w3 = tx*ty;
|
||||
float w4 = tx*(1.0f - ty);
|
||||
|
||||
Color color = {
|
||||
(unsigned char)(col1.r*w1 + col2.r*w2 + col3.r*w3 + col4.r*w4),
|
||||
(unsigned char)(col1.g*w1 + col2.g*w2 + col3.g*w3 + col4.g*w4),
|
||||
(unsigned char)(col1.b*w1 + col2.b*w2 + col3.b*w3 + col4.b*w4),
|
||||
(unsigned char)(col1.a*w1 + col2.a*w2 + col3.a*w3 + col4.a*w4)
|
||||
};
|
||||
|
||||
// Using ImageDrawPixel() to let it manage color format conversion
|
||||
ImageDrawPixel(dst, x, y, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Draw circle within an image
|
||||
|
||||
Reference in New Issue
Block a user