initial commit (just stuffing all my test project files into a git repo)
This commit is contained in:
commit
05f68bc409
9
.gitignore
vendored
Normal file
9
.gitignore
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
.DS_Store
|
||||
/Makefile
|
||||
CMakeFiles
|
||||
CMakeCache.txt
|
||||
cmake_install.cmake
|
||||
.idea
|
||||
/*.log
|
||||
/SoftwareRasterizer
|
||||
/*.exe
|
21
CMakeLists.txt
Normal file
21
CMakeLists.txt
Normal file
|
@ -0,0 +1,21 @@
|
|||
cmake_minimum_required(VERSION 2.8.4)
|
||||
|
||||
set(PROJECT_NAME SoftwareRasterizer)
|
||||
set(PROJECT_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
project(SoftwareRasterizer)
|
||||
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SRC_DIR}/cmake")
|
||||
find_package(SDL2 REQUIRED)
|
||||
|
||||
file(GLOB_RECURSE SOURCE_FILES "."
|
||||
"${PROJECT_SRC_DIR}/src/*.cpp"
|
||||
"${PROJECT_SRC_DIR}/src/*.c"
|
||||
"${PROJECT_SRC_DIR}/src/*.h"
|
||||
"${PROJECT_SRC_DIR}/include/*.h"
|
||||
)
|
||||
include_directories(${SDL2_INCLUDE_DIR} "${PROJECT_SRC_DIR}/include")
|
||||
add_executable(${PROJECT_NAME} ${SOURCE_FILES})
|
||||
|
||||
target_link_libraries(${PROJECT_NAME} ${SDL2_LIBRARY})
|
||||
|
22
README.md
Normal file
22
README.md
Normal file
|
@ -0,0 +1,22 @@
|
|||
# Software Rasterizer
|
||||
|
||||
A 2D and 3D graphics software rasterizer implemented in C. Includes SDL backend to abstract OS-specifics (window management, input, etc).
|
||||
|
||||
This is basically a "for fun" toy project of mine. Probably not too noteworthy and definitely has been done better before by people far smarter then me!
|
||||
|
||||
**In early development! Pre-alpha quality code!**
|
||||
|
||||
2D features:
|
||||
|
||||
* 24 bit (RGB) and 32 bit (RGBA) surface support
|
||||
* drawing primitives: pixel, line, rectangle (outline and filled)
|
||||
* sprite drawing: solid, transparent, blending, color tinting
|
||||
* clipping support on all drawing operations as well as "fast" variants which skip clipping
|
||||
* image -> surface loading via stb_image
|
||||
* TTF -> sprite font texture atlas via stb_truetype (TODO)
|
||||
|
||||
3D features:
|
||||
|
||||
* TODO
|
||||
|
||||
|
249
cmake/FindSDL2.cmake
Normal file
249
cmake/FindSDL2.cmake
Normal file
|
@ -0,0 +1,249 @@
|
|||
# Locate SDL2 library
|
||||
# This module defines
|
||||
# SDL2_LIBRARY, the name of the library to link against
|
||||
# SDL2_FOUND, if false, do not try to link to SDL2
|
||||
# SDL2_INCLUDE_DIR, where to find SDL.h
|
||||
#
|
||||
# This module responds to the the flag:
|
||||
# SDL2_BUILDING_LIBRARY
|
||||
# If this is defined, then no SDL2_main will be linked in because
|
||||
# only applications need main().
|
||||
# Otherwise, it is assumed you are building an application and this
|
||||
# module will attempt to locate and set the the proper link flags
|
||||
# as part of the returned SDL2_LIBRARY variable.
|
||||
#
|
||||
# Don't forget to include SDL2main.h and SDL2main.m your project for the
|
||||
# OS X framework based version. (Other versions link to -lSDL2main which
|
||||
# this module will try to find on your behalf.) Also for OS X, this
|
||||
# module will automatically add the -framework Cocoa on your behalf.
|
||||
#
|
||||
#
|
||||
# Additional Note: If you see an empty SDL2_LIBRARY_TEMP in your configuration
|
||||
# and no SDL2_LIBRARY, it means CMake did not find your SDL2 library
|
||||
# (SDL2.dll, libsdl2.so, SDL2.framework, etc).
|
||||
# Set SDL2_LIBRARY_TEMP to point to your SDL2 library, and configure again.
|
||||
# Similarly, if you see an empty SDL2MAIN_LIBRARY, you should set this value
|
||||
# as appropriate. These values are used to generate the final SDL2_LIBRARY
|
||||
# variable, but when these values are unset, SDL2_LIBRARY does not get created.
|
||||
#
|
||||
#
|
||||
# $SDL2 is an environment variable that would
|
||||
# correspond to the ./configure --prefix=$SDL2
|
||||
# used in building SDL2.
|
||||
# l.e.galup 9-20-02
|
||||
#
|
||||
# Modified by Eric Wing.
|
||||
# Added code to assist with automated building by using environmental variables
|
||||
# and providing a more controlled/consistent search behavior.
|
||||
# Added new modifications to recognize OS X frameworks and
|
||||
# additional Unix paths (FreeBSD, etc).
|
||||
# Also corrected the header search path to follow "proper" SDL2 guidelines.
|
||||
# Added a search for SDL2main which is needed by some platforms.
|
||||
# Added a search for threads which is needed by some platforms.
|
||||
# Added needed compile switches for MinGW.
|
||||
#
|
||||
# On OSX, this will prefer the Framework version (if found) over others.
|
||||
# People will have to manually change the cache values of
|
||||
# SDL2_LIBRARY to override this selection or set the CMake environment
|
||||
# CMAKE_INCLUDE_PATH to modify the search paths.
|
||||
#
|
||||
# Note that the header path has changed from SDL2/SDL.h to just SDL.h
|
||||
# This needed to change because "proper" SDL2 convention
|
||||
# is #include "SDL.h", not <SDL2/SDL.h>. This is done for portability
|
||||
# reasons because not all systems place things in SDL2/ (see FreeBSD).
|
||||
#
|
||||
# Ported by Johnny Patterson. This is a literal port for SDL2 of the FindSDL.cmake
|
||||
# module with the minor edit of changing "SDL" to "SDL2" where necessary. This
|
||||
# was not created for redistribution, and exists temporarily pending official
|
||||
# SDL2 CMake modules.
|
||||
#
|
||||
# Note that on windows this will only search for the 32bit libraries, to search
|
||||
# for 64bit change x86/i686-w64 to x64/x86_64-w64
|
||||
|
||||
#=============================================================================
|
||||
# Copyright 2003-2009 Kitware, Inc.
|
||||
#
|
||||
# CMake - Cross Platform Makefile Generator
|
||||
# Copyright 2000-2014 Kitware, Inc.
|
||||
# Copyright 2000-2011 Insight Software Consortium
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
#
|
||||
# * Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
#
|
||||
# * Neither the names of Kitware, Inc., the Insight Software Consortium,
|
||||
# nor the names of their contributors may be used to endorse or promote
|
||||
# products derived from this software without specific prior written
|
||||
# permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#
|
||||
# This software is distributed WITHOUT ANY WARRANTY; without even the
|
||||
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
# See the License for more information.
|
||||
#=============================================================================
|
||||
# (To distribute this file outside of CMake, substitute the full
|
||||
# License text for the above reference.)
|
||||
|
||||
FIND_PATH(SDL2_INCLUDE_DIR SDL.h
|
||||
HINTS
|
||||
$ENV{SDL2}
|
||||
PATH_SUFFIXES include/SDL2 include SDL2
|
||||
i686-w64-mingw32/include/SDL2
|
||||
x86_64-w64-mingw32/include/SDL2
|
||||
PATHS
|
||||
~/Library/Frameworks
|
||||
/Library/Frameworks
|
||||
/usr/local/include/SDL2
|
||||
/usr/include/SDL2
|
||||
/sw # Fink
|
||||
/opt/local # DarwinPorts
|
||||
/opt/csw # Blastwave
|
||||
/opt
|
||||
)
|
||||
|
||||
# Lookup the 64 bit libs on x64
|
||||
IF(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
FIND_LIBRARY(SDL2_LIBRARY_TEMP SDL2
|
||||
HINTS
|
||||
$ENV{SDL2}
|
||||
PATH_SUFFIXES lib64 lib
|
||||
lib/x64
|
||||
x86_64-w64-mingw32/lib
|
||||
PATHS
|
||||
/sw
|
||||
/opt/local
|
||||
/opt/csw
|
||||
/opt
|
||||
)
|
||||
# On 32bit build find the 32bit libs
|
||||
ELSE(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
FIND_LIBRARY(SDL2_LIBRARY_TEMP SDL2
|
||||
HINTS
|
||||
$ENV{SDL2}
|
||||
PATH_SUFFIXES lib
|
||||
lib/x86
|
||||
i686-w64-mingw32/lib
|
||||
PATHS
|
||||
/sw
|
||||
/opt/local
|
||||
/opt/csw
|
||||
/opt
|
||||
)
|
||||
ENDIF(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
|
||||
IF(NOT SDL2_BUILDING_LIBRARY)
|
||||
IF(NOT ${SDL2_INCLUDE_DIR} MATCHES ".framework")
|
||||
# Non-OS X framework versions expect you to also dynamically link to
|
||||
# SDL2main. This is mainly for Windows and OS X. Other (Unix) platforms
|
||||
# seem to provide SDL2main for compatibility even though they don't
|
||||
# necessarily need it.
|
||||
# Lookup the 64 bit libs on x64
|
||||
IF(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
FIND_LIBRARY(SDL2MAIN_LIBRARY
|
||||
NAMES SDL2main
|
||||
HINTS
|
||||
$ENV{SDL2}
|
||||
PATH_SUFFIXES lib64 lib
|
||||
lib/x64
|
||||
x86_64-w64-mingw32/lib
|
||||
PATHS
|
||||
/sw
|
||||
/opt/local
|
||||
/opt/csw
|
||||
/opt
|
||||
)
|
||||
# On 32bit build find the 32bit libs
|
||||
ELSE(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
FIND_LIBRARY(SDL2MAIN_LIBRARY
|
||||
NAMES SDL2main
|
||||
HINTS
|
||||
$ENV{SDL2}
|
||||
PATH_SUFFIXES lib
|
||||
lib/x86
|
||||
i686-w64-mingw32/lib
|
||||
PATHS
|
||||
/sw
|
||||
/opt/local
|
||||
/opt/csw
|
||||
/opt
|
||||
)
|
||||
ENDIF(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
ENDIF(NOT ${SDL2_INCLUDE_DIR} MATCHES ".framework")
|
||||
ENDIF(NOT SDL2_BUILDING_LIBRARY)
|
||||
|
||||
# SDL2 may require threads on your system.
|
||||
# The Apple build may not need an explicit flag because one of the
|
||||
# frameworks may already provide it.
|
||||
# But for non-OSX systems, I will use the CMake Threads package.
|
||||
IF(NOT APPLE)
|
||||
FIND_PACKAGE(Threads)
|
||||
ENDIF(NOT APPLE)
|
||||
|
||||
# MinGW needs an additional library, mwindows
|
||||
# It's total link flags should look like -lmingw32 -lSDL2main -lSDL2 -lmwindows
|
||||
# (Actually on second look, I think it only needs one of the m* libraries.)
|
||||
IF(MINGW)
|
||||
SET(MINGW32_LIBRARY mingw32 CACHE STRING "mwindows for MinGW")
|
||||
ENDIF(MINGW)
|
||||
|
||||
SET(SDL2_FOUND "NO")
|
||||
IF(SDL2_LIBRARY_TEMP)
|
||||
# For SDL2main
|
||||
IF(NOT SDL2_BUILDING_LIBRARY)
|
||||
IF(SDL2MAIN_LIBRARY)
|
||||
SET(SDL2_LIBRARY_TEMP ${SDL2MAIN_LIBRARY} ${SDL2_LIBRARY_TEMP})
|
||||
ENDIF(SDL2MAIN_LIBRARY)
|
||||
ENDIF(NOT SDL2_BUILDING_LIBRARY)
|
||||
|
||||
# For OS X, SDL2 uses Cocoa as a backend so it must link to Cocoa.
|
||||
# CMake doesn't display the -framework Cocoa string in the UI even
|
||||
# though it actually is there if I modify a pre-used variable.
|
||||
# I think it has something to do with the CACHE STRING.
|
||||
# So I use a temporary variable until the end so I can set the
|
||||
# "real" variable in one-shot.
|
||||
IF(APPLE)
|
||||
SET(SDL2_LIBRARY_TEMP ${SDL2_LIBRARY_TEMP} "-framework Cocoa")
|
||||
ENDIF(APPLE)
|
||||
|
||||
# For threads, as mentioned Apple doesn't need this.
|
||||
# In fact, there seems to be a problem if I used the Threads package
|
||||
# and try using this line, so I'm just skipping it entirely for OS X.
|
||||
IF(NOT APPLE)
|
||||
SET(SDL2_LIBRARY_TEMP ${SDL2_LIBRARY_TEMP} ${CMAKE_THREAD_LIBS_INIT})
|
||||
ENDIF(NOT APPLE)
|
||||
|
||||
# For MinGW library
|
||||
IF(MINGW)
|
||||
SET(SDL2_LIBRARY_TEMP ${MINGW32_LIBRARY} ${SDL2_LIBRARY_TEMP})
|
||||
ENDIF(MINGW)
|
||||
|
||||
# Set the final string here so the GUI reflects the final state.
|
||||
SET(SDL2_LIBRARY ${SDL2_LIBRARY_TEMP} CACHE STRING "Where the SDL2 Library can be found")
|
||||
# Set the temp variable to INTERNAL so it is not seen in the CMake GUI
|
||||
SET(SDL2_LIBRARY_TEMP "${SDL2_LIBRARY_TEMP}" CACHE INTERNAL "")
|
||||
|
||||
SET(SDL2_FOUND "YES")
|
||||
ENDIF(SDL2_LIBRARY_TEMP)
|
||||
|
||||
INCLUDE(FindPackageHandleStandardArgs)
|
||||
|
||||
FIND_PACKAGE_HANDLE_STANDARD_ARGS(SDL2 REQUIRED_VARS SDL2_LIBRARY SDL2_INCLUDE_DIR)
|
15
include/assets/image.h
Normal file
15
include/assets/image.h
Normal file
|
@ -0,0 +1,15 @@
|
|||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "../fbgfx/surface.h"
|
||||
#include "../types.h"
|
||||
|
||||
SURFACE* image_load_file(const char *filename);
|
||||
SURFACE* image_load_memory(const void *image_file_bin, uint32_t size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
17
include/fbgfx/clipping.h
Normal file
17
include/fbgfx/clipping.h
Normal file
|
@ -0,0 +1,17 @@
|
|||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "../types.h"
|
||||
#include "../math/rect.h"
|
||||
|
||||
bool point_in_bounds(const RECT *clip_region, int x, int y);
|
||||
bool line_in_bounds(const RECT *clip_region, int x1, int y1, int x2, int y2);
|
||||
bool clamp_to_region(const RECT *clip_region, int *x1, int *y1, int *x2, int *y2);
|
||||
bool clip_blit_region(const RECT *dest_clip_region, RECT *source_blit_region, int *dest_x, int *dest_y);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
179
include/fbgfx/color.h
Normal file
179
include/fbgfx/color.h
Normal file
|
@ -0,0 +1,179 @@
|
|||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "../types.h"
|
||||
|
||||
typedef uint32_t COLOR;
|
||||
typedef uint8_t COLOR_COMPONENT;
|
||||
|
||||
static const COLOR_COMPONENT TRANSPARENT = 0;
|
||||
static const COLOR_COMPONENT OPAQUE = 255;
|
||||
|
||||
static inline COLOR_COMPONENT color_get_a(COLOR color) {
|
||||
return (color & 0xff000000) >> 24;
|
||||
}
|
||||
|
||||
static inline COLOR_COMPONENT color_get_r(COLOR color) {
|
||||
return (color & 0x00ff0000) >> 16;
|
||||
}
|
||||
|
||||
static inline COLOR_COMPONENT color_get_g(COLOR color) {
|
||||
return (color & 0x0000ff00) >> 8;
|
||||
}
|
||||
|
||||
static inline COLOR_COMPONENT color_get_b(COLOR color) {
|
||||
return (color & 0x000000ff);
|
||||
}
|
||||
|
||||
static inline void color_extract(COLOR color, COLOR_COMPONENT *r, COLOR_COMPONENT *g, COLOR_COMPONENT *b, COLOR_COMPONENT *a) {
|
||||
if (a)
|
||||
*a = color_get_a(color);
|
||||
if (r)
|
||||
*r = color_get_r(color);
|
||||
if (g)
|
||||
*g = color_get_g(color);
|
||||
if (b)
|
||||
*b = color_get_b(color);
|
||||
}
|
||||
|
||||
static inline COLOR color_create_rgba(COLOR_COMPONENT r, COLOR_COMPONENT g, COLOR_COMPONENT b, COLOR_COMPONENT a) {
|
||||
return (a << 24) | (r << 16) | (g << 8) | b;
|
||||
}
|
||||
|
||||
static inline COLOR color_create_rgb(COLOR_COMPONENT r, COLOR_COMPONENT g, COLOR_COMPONENT b) {
|
||||
return color_create_rgba(r, g, b, OPAQUE);
|
||||
}
|
||||
|
||||
|
||||
/* floating-point color components */
|
||||
|
||||
typedef float COLORF_COMPONENT;
|
||||
|
||||
static const COLORF_COMPONENT TRANSPARENT_F = 0.0f;
|
||||
static const COLORF_COMPONENT OPAQUE_F = 1.0f;
|
||||
|
||||
static inline COLORF_COMPONENT colorf_get_a(COLOR color) {
|
||||
return (float)((color & 0xff000000) >> 24) / 255;
|
||||
}
|
||||
|
||||
static inline COLORF_COMPONENT colorf_get_r(COLOR color) {
|
||||
return (float)((color & 0x00ff0000) >> 16) / 255;
|
||||
}
|
||||
|
||||
static inline COLORF_COMPONENT colorf_get_g(COLOR color) {
|
||||
return (float)((color & 0x0000ff00) >> 8) / 255;
|
||||
}
|
||||
|
||||
static inline COLORF_COMPONENT colorf_get_b(COLOR color) {
|
||||
return (float)(color & 0x000000ff) / 255;
|
||||
}
|
||||
|
||||
static inline void colorf_extract(COLOR color, COLORF_COMPONENT *r, COLORF_COMPONENT *g, COLORF_COMPONENT *b, COLORF_COMPONENT *a) {
|
||||
if (a)
|
||||
*a = colorf_get_a(color);
|
||||
if (r)
|
||||
*r = colorf_get_r(color);
|
||||
if (g)
|
||||
*g = colorf_get_g(color);
|
||||
if (b)
|
||||
*b = colorf_get_b(color);
|
||||
}
|
||||
|
||||
static inline COLOR colorf_create_rgba(COLORF_COMPONENT r, COLORF_COMPONENT g, COLORF_COMPONENT b, COLORF_COMPONENT a) {
|
||||
return ((COLOR_COMPONENT)(a * 255) << 24) |
|
||||
((COLOR_COMPONENT)(r * 255) << 16) |
|
||||
((COLOR_COMPONENT)(g * 255) << 8) |
|
||||
(COLOR_COMPONENT)(b * 255);
|
||||
}
|
||||
|
||||
static inline COLOR colorf_create_rgb(COLORF_COMPONENT r, COLORF_COMPONENT g, COLORF_COMPONENT b, COLORF_COMPONENT a) {
|
||||
return colorf_create_rgba(r, g, b, OPAQUE_F);
|
||||
}
|
||||
|
||||
static inline COLOR color_blend(COLOR source, COLOR destination) {
|
||||
int sR = color_get_r(source);
|
||||
int sG = color_get_g(source);
|
||||
int sB = color_get_b(source);
|
||||
int sA = color_get_a(source);
|
||||
int dR = color_get_r(destination);
|
||||
int dG = color_get_g(destination);
|
||||
int dB = color_get_b(destination);
|
||||
int dA = color_get_a(destination);
|
||||
|
||||
// this is equivalent to "glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)"
|
||||
return color_create_rgba(
|
||||
(COLOR_COMPONENT)(((sR * sA) + (dR * (255 - sA))) / 255),
|
||||
(COLOR_COMPONENT)(((sG * sA) + (dG * (255 - sA))) / 255),
|
||||
(COLOR_COMPONENT)(((sB * sA) + (dB * (255 - sA))) / 255),
|
||||
(COLOR_COMPONENT)(((sA * sA) + (dA * (255 - sA))) / 255)
|
||||
);
|
||||
}
|
||||
|
||||
static inline COLOR color_tint(COLOR color, COLOR tint) {
|
||||
int cR = color_get_r(color);
|
||||
int cG = color_get_g(color);
|
||||
int cB = color_get_b(color);
|
||||
int cA = color_get_a(color);
|
||||
int tR = color_get_r(tint);
|
||||
int tG = color_get_g(tint);
|
||||
int tB = color_get_b(tint);
|
||||
int tA = color_get_a(tint);
|
||||
|
||||
return color_create_rgba(
|
||||
(COLOR_COMPONENT)((cR * tR) / 255),
|
||||
(COLOR_COMPONENT)((cG * tG) / 255),
|
||||
(COLOR_COMPONENT)((cB * tB) / 255),
|
||||
(COLOR_COMPONENT)((cA * tA) / 255)
|
||||
);
|
||||
}
|
||||
|
||||
static inline COLOR color_tint_alpha(COLOR_COMPONENT alpha, COLOR tint) {
|
||||
return color_create_rgba(
|
||||
color_get_r(tint),
|
||||
color_get_g(tint),
|
||||
color_get_b(tint),
|
||||
(COLOR_COMPONENT)(((uint32_t)alpha * color_get_a(tint)) / 255)
|
||||
);
|
||||
}
|
||||
|
||||
static inline void color_convert_rgba_to_rgb(COLOR rgba, COLOR_COMPONENT *r, COLOR_COMPONENT *g, COLOR_COMPONENT *b) {
|
||||
COLOR_COMPONENT a = color_get_a(rgba);
|
||||
*r = (COLOR_COMPONENT)(((uint32_t)color_get_r(rgba) * a) / 255);
|
||||
*g = (COLOR_COMPONENT)(((uint32_t)color_get_g(rgba) * a) / 255);
|
||||
*b = (COLOR_COMPONENT)(((uint32_t)color_get_b(rgba) * a) / 255);
|
||||
}
|
||||
|
||||
/* -- */
|
||||
|
||||
#define COLOR_WHITE (color_create_rgb(255, 255, 255))
|
||||
#define COLOR_RED (color_create_rgb(255, 0, 0))
|
||||
#define COLOR_GREEN (color_create_rgb(0, 255, 0))
|
||||
#define COLOR_BLUE (color_create_rgb(0, 0, 255))
|
||||
#define COLOR_YELLOW (color_create_rgb(255, 255, 0))
|
||||
#define COLOR_CYAN (color_create_rgb(0, 255, 255))
|
||||
#define COLOR_MAGENTA (color_create_rgb(255, 0, 255))
|
||||
#define COLOR_BLACK (color_create_rgb(0, 0, 0))
|
||||
|
||||
#define COLOR_DOS_BLACK (color_create_rgb(0, 0, 0))
|
||||
#define COLOR_DOS_BLUE (color_create_rgb(0, 0, 170))
|
||||
#define COLOR_DOS_GREEN (color_create_rgb(0, 170, 0))
|
||||
#define COLOR_DOS_CYAN (color_create_rgb(0, 170, 170))
|
||||
#define COLOR_DOS_RED (color_create_rgb(170, 0, 0))
|
||||
#define COLOR_DOS_MAGENTA (color_create_rgb(170, 0, 170))
|
||||
#define COLOR_DOS_BROWN (color_create_rgb(170, 85, 0))
|
||||
#define COLOR_DOS_WHITE (color_create_rgb(170, 170, 170))
|
||||
#define COLOR_DOS_GRAY (color_create_rgb(85, 85, 85))
|
||||
#define COLOR_DOS_LIGHT_BLUE (color_create_rgb(85, 85, 255))
|
||||
#define COLOR_DOS_LIGHT_GREEN (color_create_rgb(85, 255, 64))
|
||||
#define COLOR_DOS_LIGHT_CYAN (color_create_rgb(85, 255, 255))
|
||||
#define COLOR_DOS_LIGHT_RED (color_create_rgb(255, 85, 85))
|
||||
#define COLOR_DOS_LIGHT_MAGENTA (color_create_rgb(255, 85, 255))
|
||||
#define COLOR_DOS_YELLOW (color_create_rgb(255, 255, 85))
|
||||
#define COLOR_DOS_BRIGHT_WHITE (color_create_rgb(255, 255, 255))
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
188
include/fbgfx/surface.h
Normal file
188
include/fbgfx/surface.h
Normal file
|
@ -0,0 +1,188 @@
|
|||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "../types.h"
|
||||
#include "../math/rect.h"
|
||||
#include "color.h"
|
||||
|
||||
typedef enum {
|
||||
SURFACE_FORMAT_ALPHA = 8,
|
||||
SURFACE_FORMAT_RGB = 24,
|
||||
SURFACE_FORMAT_RGBA = 32
|
||||
} SURFACE_FORMAT;
|
||||
|
||||
typedef enum {
|
||||
SURFACE_FLAGS_NONE = 0,
|
||||
SURFACE_FLAGS_SIDEWAYS_BUFFER = 1
|
||||
} SURFACE_FLAGS;
|
||||
|
||||
typedef struct SURFACE {
|
||||
SURFACE_FLAGS flags;
|
||||
COLOR_COMPONENT *pixels;
|
||||
bool wrapped_buffer;
|
||||
SURFACE_FORMAT format;
|
||||
int bytes_per_pixel;
|
||||
int width;
|
||||
int height;
|
||||
int x_inc;
|
||||
int y_inc;
|
||||
RECT clip_region;
|
||||
} SURFACE;
|
||||
|
||||
extern SURFACE *top_screen_surface;
|
||||
extern SURFACE *bottom_screen_surface;
|
||||
|
||||
void surface_init();
|
||||
void surface_exit();
|
||||
|
||||
void surface_swap_framebuffers();
|
||||
|
||||
SURFACE* surface_create(int width, int height, SURFACE_FORMAT format, SURFACE_FLAGS flags);
|
||||
SURFACE* surface_wrap_existing(void *framebuffer, int width, int height, SURFACE_FORMAT format, SURFACE_FLAGS flags);
|
||||
void surface_resize(SURFACE *surface, int width, int height);
|
||||
void surface_destroy(SURFACE *surface);
|
||||
|
||||
void surface_convert_rgb_to_bgr(SURFACE *surface);
|
||||
|
||||
void surface_clear(SURFACE *surface, COLOR color);
|
||||
|
||||
void surface_set_pixel(SURFACE *surface, int x, int y, COLOR color);
|
||||
void surface_set_pixel_fast(SURFACE *surface, int x, int y, COLOR color);
|
||||
COLOR surface_get_pixel(const SURFACE *surface, int x, int y);
|
||||
COLOR surface_get_pixel_fast(SURFACE *surface, int x, int y);
|
||||
|
||||
void surface_line(SURFACE *surface, int x1, int y1, int x2, int y2, COLOR color);
|
||||
void surface_line_fast(SURFACE *surface, int x1, int y1, int x2, int y2, COLOR color);
|
||||
void surface_hline(SURFACE *surface, int x1, int x2, int y, COLOR color);
|
||||
void surface_hline_fast(SURFACE *surface, int x1, int x2, int y, COLOR color);
|
||||
void surface_vline(SURFACE *surface, int x, int y1, int y2, COLOR color);
|
||||
void surface_vline_fast(SURFACE *surface, int x, int y1, int y2, COLOR color);
|
||||
void surface_rect(SURFACE *surface, int x1, int y1, int x2, int y2, COLOR color);
|
||||
void surface_rect_fast(SURFACE *surface, int x1, int y1, int x2, int y2, COLOR color);
|
||||
void surface_rect_filled(SURFACE *surface, int x1, int y1, int x2, int y2, COLOR color);
|
||||
void surface_rect_filled_fast(SURFACE *surface, int x1, int y1, int x2, int y2, COLOR color);
|
||||
|
||||
void surface_blit_region(SURFACE *source, SURFACE *destination, int source_x, int source_y, int source_width, int source_height, int dest_x, int dest_y);
|
||||
void surface_blit_blend_region(SURFACE *source, SURFACE *destination, int source_x, int source_y, int source_width, int source_height, int dest_x, int dest_y);
|
||||
void surface_blit_trans_region(SURFACE *source, SURFACE *destination, int source_x, int source_y, int source_width, int source_height, int dest_x, int dest_y);
|
||||
void surface_blit_tint_region(SURFACE *source, SURFACE *destination, int source_x, int source_y, int source_width, int source_height, int dest_x, int dest_y, COLOR tint);
|
||||
void surface_blit_blend_tint_region(SURFACE *source, SURFACE *destination, int source_x, int source_y, int source_width, int source_height, int dest_x, int dest_y, COLOR tint);
|
||||
|
||||
#define GET_PIXEL_FAST(fmt, ...) surface_get_pixel_ ## fmt ## _fast(__VA_ARGS__)
|
||||
#define SET_PIXEL_FAST(fmt, ...) surface_set_pixel_ ## fmt ## _fast(__VA_ARGS__)
|
||||
#define GET_PIXEL_IDX_FAST(fmt, ...) surface_get_pixel_idx_ ## fmt ## _fast(__VA_ARGS__)
|
||||
#define SET_PIXEL_IDX_FAST(fmt, ...) surface_set_pixel_idx_ ## fmt ## _fast(__VA_ARGS__)
|
||||
|
||||
////////////////
|
||||
|
||||
static inline uint32_t surface_get_buffer_size(const SURFACE *surface) {
|
||||
return (uint32_t)(surface->width * surface->height) * surface->bytes_per_pixel;
|
||||
}
|
||||
|
||||
static inline uint32_t surface_get_index_of(const SURFACE *surface, int x, int y) {
|
||||
if (surface->flags & SURFACE_FLAGS_SIDEWAYS_BUFFER)
|
||||
return (uint32_t)((x * surface->height) + (surface->height - y - 1)) * surface->bytes_per_pixel;
|
||||
else
|
||||
return (uint32_t)(x + (y * surface->width)) * surface->bytes_per_pixel;
|
||||
}
|
||||
|
||||
static inline COLOR_COMPONENT* surface_get_pointer_to(const SURFACE *surface, int x, int y) {
|
||||
return surface->pixels + surface_get_index_of(surface, x, y);
|
||||
}
|
||||
|
||||
////////////////
|
||||
|
||||
static inline void surface_set_pixel_idx_alpha_fast(SURFACE *surface, uint32_t index, COLOR color) {
|
||||
COLOR_COMPONENT alpha = color_get_a(color);
|
||||
COLOR_COMPONENT *p = (surface->pixels + index);
|
||||
*p = alpha;
|
||||
}
|
||||
|
||||
static inline void surface_set_pixel_idx_rgb_fast(SURFACE *surface, uint32_t index, COLOR color) {
|
||||
COLOR_COMPONENT *p = (surface->pixels + index);
|
||||
color_convert_rgba_to_rgb(color, (p + 2), (p + 1), p);
|
||||
}
|
||||
|
||||
static inline void surface_set_pixel_idx_rgba_fast(SURFACE *surface, uint32_t index, COLOR color) {
|
||||
COLOR_COMPONENT *p = (surface->pixels + index);
|
||||
color_extract(color, (p + 2), (p + 1), p, (p + 3));
|
||||
}
|
||||
|
||||
static inline COLOR surface_get_pixel_idx_alpha_fast(const SURFACE *surface, uint32_t index) {
|
||||
COLOR_COMPONENT *p = (surface->pixels + index);
|
||||
return color_create_rgba(0, 0, 0, *p);
|
||||
}
|
||||
|
||||
static inline COLOR surface_get_pixel_idx_rgb_fast(const SURFACE *surface, uint32_t index) {
|
||||
COLOR_COMPONENT *p = (surface->pixels + index);
|
||||
COLOR_COMPONENT r = p[2];
|
||||
COLOR_COMPONENT g = p[1];
|
||||
COLOR_COMPONENT b = p[0];
|
||||
return color_create_rgb(r, g, b);
|
||||
}
|
||||
|
||||
static inline COLOR surface_get_pixel_idx_rgba_fast(const SURFACE *surface, uint32_t index) {
|
||||
COLOR_COMPONENT *p = (surface->pixels + index);
|
||||
COLOR_COMPONENT a = p[3];
|
||||
COLOR_COMPONENT r = p[2];
|
||||
COLOR_COMPONENT g = p[1];
|
||||
COLOR_COMPONENT b = p[0];
|
||||
return color_create_rgba(r, g, b, a);
|
||||
}
|
||||
|
||||
static inline void surface_set_pixel_alpha_fast(SURFACE *surface, int x, int y, COLOR color) {
|
||||
uint32_t index = surface_get_index_of(surface, x, y);
|
||||
surface_set_pixel_idx_alpha_fast(surface, index, color);
|
||||
}
|
||||
|
||||
static inline void surface_set_pixel_rgb_fast(SURFACE *surface, int x, int y, COLOR color) {
|
||||
uint32_t index = surface_get_index_of(surface, x, y);
|
||||
surface_set_pixel_idx_rgb_fast(surface, index, color);
|
||||
}
|
||||
|
||||
static inline void surface_set_pixel_rgba_fast(SURFACE *surface, int x, int y, COLOR color) {
|
||||
uint32_t index = surface_get_index_of(surface, x, y);
|
||||
surface_set_pixel_idx_rgba_fast(surface, index, color);
|
||||
}
|
||||
|
||||
static inline COLOR surface_get_pixel_alpha_fast(const SURFACE *surface, int x, int y) {
|
||||
uint32_t index = surface_get_index_of(surface, x, y);
|
||||
return surface_get_pixel_idx_alpha_fast(surface, index);
|
||||
}
|
||||
|
||||
static inline COLOR surface_get_pixel_rgb_fast(const SURFACE *surface, int x, int y) {
|
||||
uint32_t index = surface_get_index_of(surface, x, y);
|
||||
return surface_get_pixel_idx_rgb_fast(surface, index);
|
||||
}
|
||||
|
||||
static inline COLOR surface_get_pixel_rgba_fast(const SURFACE *surface, int x, int y) {
|
||||
uint32_t index = surface_get_index_of(surface, x, y);
|
||||
return surface_get_pixel_idx_rgba_fast(surface, index);
|
||||
}
|
||||
|
||||
static inline void surface_blit(SURFACE *source, SURFACE *destination, int x, int y) {
|
||||
surface_blit_region(source, destination, 0, 0, source->width, source->height, x, y);
|
||||
}
|
||||
|
||||
static inline void surface_blit_blend(SURFACE *source, SURFACE *destination, int x, int y) {
|
||||
surface_blit_blend_region(source, destination, 0, 0, source->width, source->height, x, y);
|
||||
}
|
||||
|
||||
static inline void surface_blit_trans(SURFACE *source, SURFACE *destination, int x, int y) {
|
||||
surface_blit_trans_region(source, destination, 0, 0, source->width, source->height, x, y);
|
||||
}
|
||||
|
||||
static inline void surface_blit_tint(SURFACE *source, SURFACE *destination, int x, int y, COLOR tint) {
|
||||
surface_blit_tint_region(source, destination, 0, 0, source->width, source->height, x, y, tint);
|
||||
}
|
||||
|
||||
static inline void surface_blit_blend_tint(SURFACE *source, SURFACE *destination, int x, int y, COLOR tint) {
|
||||
surface_blit_blend_tint_region(source, destination, 0, 0, source->width, source->height, x, y, tint);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
39
include/math/rect.h
Normal file
39
include/math/rect.h
Normal file
|
@ -0,0 +1,39 @@
|
|||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
int x;
|
||||
int y;
|
||||
int width;
|
||||
int height;
|
||||
} RECT;
|
||||
|
||||
static inline RECT rect_create(int x, int y, int width, int height) {
|
||||
RECT rect;
|
||||
rect.x = x;
|
||||
rect.y = y;
|
||||
rect.width = width;
|
||||
rect.height = height;
|
||||
return rect;
|
||||
}
|
||||
|
||||
static inline int rect_get_right(const RECT *rect) {
|
||||
if (rect->width > 0)
|
||||
return rect->x + rect->width - 1;
|
||||
else
|
||||
return rect->x;
|
||||
}
|
||||
|
||||
static inline int rect_get_bottom(const RECT *rect) {
|
||||
if (rect->height > 0)
|
||||
return rect->y + rect->height - 1;
|
||||
else
|
||||
return rect->y;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
13
include/types.h
Normal file
13
include/types.h
Normal file
|
@ -0,0 +1,13 @@
|
|||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define SWAP(T, a, b) \
|
||||
do { \
|
||||
T tmp = a; \
|
||||
a = b; \
|
||||
b = tmp; \
|
||||
} while (0)
|
||||
|
63
src/assets/image.c
Normal file
63
src/assets/image.c
Normal file
|
@ -0,0 +1,63 @@
|
|||
#include "assets/image.h"
|
||||
#include "../vendor/stb_image.h"
|
||||
#include "fbgfx/surface.h"
|
||||
#include "types.h"
|
||||
#include <stdio.h>
|
||||
|
||||
SURFACE* create_surface_from_pixels(uint8_t *pixels, int width, int height, int bpp) {
|
||||
SURFACE *surface = NULL;
|
||||
SURFACE_FORMAT format;
|
||||
|
||||
switch (bpp) {
|
||||
case 1: format = SURFACE_FORMAT_ALPHA; break;
|
||||
case 3: format = SURFACE_FORMAT_RGB; break;
|
||||
case 4: format = SURFACE_FORMAT_RGBA; break;
|
||||
|
||||
default:
|
||||
stbi_image_free(pixels);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
surface = surface_wrap_existing(pixels, width, height, format, 0);
|
||||
if (!surface) {
|
||||
stbi_image_free(pixels);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// HACK: this one little time, we're going go manually toggle this setting in a way we shouldn't be!
|
||||
// (all that stbi_image_free() does is call free(), so it's fine if we leave cleanup of the
|
||||
// stb_image pixel buffer to surface_destroy() ...)
|
||||
surface->wrapped_buffer = false;
|
||||
|
||||
surface_convert_rgb_to_bgr(surface);
|
||||
return surface;
|
||||
}
|
||||
|
||||
SURFACE* image_load_file(const char *filename) {
|
||||
if (!filename)
|
||||
return NULL;
|
||||
|
||||
int width;
|
||||
int height;
|
||||
int bpp;
|
||||
uint8_t *pixels = stbi_load(filename, &width, &height, &bpp, 0);
|
||||
if (!pixels)
|
||||
return NULL;
|
||||
else
|
||||
return create_surface_from_pixels(pixels, width, height, bpp);
|
||||
}
|
||||
|
||||
SURFACE* image_load_memory(const void *image_file_bin, uint32_t size) {
|
||||
if (!image_file_bin || !size)
|
||||
return NULL;
|
||||
|
||||
int width;
|
||||
int height;
|
||||
int bpp;
|
||||
uint8_t *pixels = stbi_load_from_memory(image_file_bin, size, &width, &height, &bpp, 0);
|
||||
if (!pixels)
|
||||
return NULL;
|
||||
else
|
||||
return create_surface_from_pixels(pixels, width, height, bpp);
|
||||
}
|
||||
|
115
src/fbgfx/clipping.c
Normal file
115
src/fbgfx/clipping.c
Normal file
|
@ -0,0 +1,115 @@
|
|||
#include "fbgfx/clipping.h"
|
||||
|
||||
bool point_in_bounds(const RECT *clip_region, int x, int y) {
|
||||
return (
|
||||
x >= clip_region->x &&
|
||||
y >= clip_region->y &&
|
||||
x <= rect_get_right(clip_region) &&
|
||||
y <= rect_get_bottom(clip_region)
|
||||
);
|
||||
}
|
||||
|
||||
bool line_in_bounds(const RECT *clip_region, int x1, int y1, int x2, int y2) {
|
||||
int clip_region_right = rect_get_right(clip_region);
|
||||
int clip_region_bottom = rect_get_bottom(clip_region);
|
||||
|
||||
if (y1 < clip_region->y &&
|
||||
y2 < clip_region->y)
|
||||
return false;
|
||||
if (y1 > clip_region_bottom &&
|
||||
y2 > clip_region_bottom)
|
||||
return false;
|
||||
if (x1 < clip_region->x &&
|
||||
x2 < clip_region->x)
|
||||
return false;
|
||||
if (x1 > clip_region_right &&
|
||||
x2 > clip_region_right)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool clamp_to_region(const RECT *clip_region, int *x1, int *y1, int *x2, int *y2) {
|
||||
int clip_region_right = rect_get_right(clip_region);
|
||||
int clip_region_bottom = rect_get_bottom(clip_region);
|
||||
|
||||
if (*y1 < clip_region->y &&
|
||||
*y2 < clip_region->y)
|
||||
return false;
|
||||
if (*y1 > clip_region_bottom &&
|
||||
*y2 > clip_region_bottom)
|
||||
return false;
|
||||
if (*x1 < clip_region->x &&
|
||||
*x2 < clip_region->x)
|
||||
return false;
|
||||
if (*x1 > clip_region_right &&
|
||||
*x2 > clip_region_right)
|
||||
return false;
|
||||
|
||||
if (*y1 < clip_region->y)
|
||||
*y1 = clip_region->y;
|
||||
if (*y1 > clip_region_bottom)
|
||||
*y1 = clip_region_bottom;
|
||||
|
||||
if (*y2 < clip_region->y)
|
||||
*y2 = clip_region->y;
|
||||
if (*y2 > clip_region_bottom)
|
||||
*y2 = clip_region_bottom;
|
||||
|
||||
if (*x1 < clip_region->x)
|
||||
*x1 = clip_region->x;
|
||||
if (*x1 > clip_region_right)
|
||||
*x1 = clip_region_right;
|
||||
|
||||
if (*x2 < clip_region->x)
|
||||
*x2 = clip_region->x;
|
||||
if (*x2 > clip_region_right)
|
||||
*x2 = clip_region_right;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool clip_blit_region(const RECT *dest_clip_region, RECT *source_blit_region, int *dest_x, int *dest_y) {
|
||||
// check for "completely out of bounds" scenarios first to return early
|
||||
if (*dest_x > rect_get_right(dest_clip_region))
|
||||
return false;
|
||||
if (*dest_x + source_blit_region->width < dest_clip_region->x)
|
||||
return false;
|
||||
if (*dest_y > rect_get_bottom(dest_clip_region))
|
||||
return false;
|
||||
if (*dest_y + source_blit_region->height < dest_clip_region->y)
|
||||
return false;
|
||||
|
||||
// destination region is fully / partially within the clip region
|
||||
|
||||
// off the left side?
|
||||
if (*dest_x < dest_clip_region->x) {
|
||||
int offset = dest_clip_region->x - *dest_x;
|
||||
source_blit_region->x += offset;
|
||||
source_blit_region->width -= offset;
|
||||
*dest_x = dest_clip_region->x;
|
||||
}
|
||||
|
||||
// off the right side?
|
||||
if (*dest_x > (dest_clip_region->width - source_blit_region->width)) {
|
||||
int offset = *dest_x + source_blit_region->width - dest_clip_region->width;
|
||||
source_blit_region->width -= offset;
|
||||
}
|
||||
|
||||
// off the top edge?
|
||||
if (*dest_y < dest_clip_region->y) {
|
||||
int offset = dest_clip_region->y - *dest_y;
|
||||
source_blit_region->y += offset;
|
||||
source_blit_region->height -= offset;
|
||||
*dest_y = dest_clip_region->y;
|
||||
}
|
||||
|
||||
// off the bottom edge?
|
||||
if (*dest_y > (dest_clip_region->height - source_blit_region->height)) {
|
||||
int offset = *dest_y + source_blit_region->height - dest_clip_region->height;
|
||||
source_blit_region->height -= offset;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
161
src/fbgfx/surface.c
Normal file
161
src/fbgfx/surface.c
Normal file
|
@ -0,0 +1,161 @@
|
|||
#include "fbgfx/surface.h"
|
||||
#include "types.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
SURFACE *top_screen_surface = NULL;
|
||||
SURFACE *bottom_screen_surface = NULL;
|
||||
|
||||
void surface_init() {
|
||||
if (top_screen_surface || bottom_screen_surface)
|
||||
return; // TODO: assert
|
||||
|
||||
/*
|
||||
top_screen_surface = surface_wrap_existing(
|
||||
gfxGetFramebuffer(GFX_TOP, GFX_LEFT, NULL, NULL),
|
||||
400, 240,
|
||||
SURFACE_FORMAT_RGB,
|
||||
SURFACE_FLAGS_SIDEWAYS_BUFFER
|
||||
);
|
||||
|
||||
bottom_screen_surface = surface_wrap_existing(
|
||||
gfxGetFramebuffer(GFX_BOTTOM, GFX_LEFT, NULL, NULL),
|
||||
320, 240,
|
||||
SURFACE_FORMAT_RGB,
|
||||
SURFACE_FLAGS_SIDEWAYS_BUFFER
|
||||
);
|
||||
*/
|
||||
}
|
||||
|
||||
void surface_exit() {
|
||||
if (top_screen_surface)
|
||||
surface_destroy(top_screen_surface);
|
||||
if (bottom_screen_surface)
|
||||
surface_destroy(bottom_screen_surface);
|
||||
|
||||
top_screen_surface = NULL;
|
||||
bottom_screen_surface = NULL;
|
||||
}
|
||||
|
||||
void surface_swap_framebuffers() {
|
||||
/*
|
||||
top_screen_surface->pixels = gfxGetFramebuffer(GFX_TOP, GFX_LEFT, NULL, NULL);
|
||||
bottom_screen_surface->pixels = gfxGetFramebuffer(GFX_BOTTOM, GFX_LEFT, NULL, NULL);
|
||||
*/
|
||||
}
|
||||
|
||||
void setup_initial_surface_properties(SURFACE *surface, int width, int height, SURFACE_FORMAT format, SURFACE_FLAGS flags) {
|
||||
surface->width = width;
|
||||
surface->height = height;
|
||||
surface->format = format;
|
||||
surface->flags = flags;
|
||||
|
||||
surface->bytes_per_pixel = surface->format / 8;
|
||||
|
||||
surface->clip_region = rect_create(0, 0, surface->width, surface->height);
|
||||
|
||||
// lazy calculation for the win
|
||||
uint32_t reference_index = surface_get_index_of(surface, 0, 0);
|
||||
surface->x_inc = surface_get_index_of(surface, 1, 0) - reference_index;
|
||||
surface->y_inc = surface_get_index_of(surface, 0, 1) - reference_index;
|
||||
}
|
||||
|
||||
SURFACE* surface_create(int width, int height, SURFACE_FORMAT format, SURFACE_FLAGS flags) {
|
||||
SURFACE *surface = malloc(sizeof(SURFACE));
|
||||
if (surface == NULL)
|
||||
return NULL;
|
||||
|
||||
setup_initial_surface_properties(surface, width, height, format, flags);
|
||||
|
||||
size_t pixels_buffer_size = surface_get_buffer_size(surface);
|
||||
|
||||
surface->pixels = malloc(pixels_buffer_size);
|
||||
if (surface->pixels == NULL) {
|
||||
free(surface);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
surface->wrapped_buffer = false;
|
||||
|
||||
memset(surface->pixels, 0, pixels_buffer_size);
|
||||
|
||||
return surface;
|
||||
}
|
||||
|
||||
SURFACE* surface_wrap_existing(void *framebuffer, int width, int height, SURFACE_FORMAT format, SURFACE_FLAGS flags) {
|
||||
SURFACE *surface = malloc(sizeof(SURFACE));
|
||||
if (surface == NULL)
|
||||
return NULL;
|
||||
|
||||
setup_initial_surface_properties(surface, width, height, format, flags);
|
||||
|
||||
surface->pixels = (COLOR_COMPONENT*)framebuffer;
|
||||
surface->wrapped_buffer = true;
|
||||
|
||||
// note: not clearing framebuffer. since we're using existing memory, maybe the
|
||||
// calling code wants it the way it is now? don't want to assume anything!
|
||||
|
||||
return surface;
|
||||
}
|
||||
|
||||
void surface_resize(SURFACE *surface, int width, int height) {
|
||||
if (!surface)
|
||||
return; // TODO: assert
|
||||
if (surface->wrapped_buffer)
|
||||
return; // TODO: assert
|
||||
|
||||
setup_initial_surface_properties(surface, width, height, surface->format, surface->flags);
|
||||
size_t new_pixels_buffer_size = surface_get_buffer_size(surface);
|
||||
|
||||
COLOR_COMPONENT *pixels = malloc(new_pixels_buffer_size);
|
||||
if (pixels == NULL)
|
||||
return; // TODO: assert
|
||||
|
||||
free(surface->pixels);
|
||||
surface->pixels = pixels;
|
||||
|
||||
memset(surface->pixels, 0, new_pixels_buffer_size);
|
||||
}
|
||||
|
||||
void surface_destroy(SURFACE *surface) {
|
||||
if (!surface)
|
||||
return; // TODO: assert
|
||||
|
||||
if (!surface->wrapped_buffer)
|
||||
free(surface->pixels);
|
||||
free(surface);
|
||||
}
|
||||
|
||||
//////////////
|
||||
|
||||
void surface_clear(SURFACE *surface, COLOR color) {
|
||||
size_t pixels_buffer_size = surface_get_buffer_size(surface);
|
||||
COLOR_COMPONENT *p = surface->pixels;
|
||||
COLOR_COMPONENT *end = p + pixels_buffer_size;
|
||||
|
||||
COLOR_COMPONENT r, g, b, a;
|
||||
color_extract(color, &r, &g, &b, &a);
|
||||
|
||||
switch (surface->format) {
|
||||
case SURFACE_FORMAT_ALPHA:
|
||||
memset(surface->pixels, a, pixels_buffer_size);
|
||||
break;
|
||||
|
||||
case SURFACE_FORMAT_RGB:
|
||||
for (; p < end; p += 3) {
|
||||
p[0] = b;
|
||||
p[1] = g;
|
||||
p[2] = r;
|
||||
}
|
||||
break;
|
||||
|
||||
case SURFACE_FORMAT_RGBA:
|
||||
for (; p < end; p += 4) {
|
||||
p[0] = b;
|
||||
p[1] = g;
|
||||
p[2] = r;
|
||||
p[3] = a;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
192
src/fbgfx/surface_blit.c
Normal file
192
src/fbgfx/surface_blit.c
Normal file
|
@ -0,0 +1,192 @@
|
|||
#include <math/rect.h>
|
||||
#include <fbgfx/surface.h>
|
||||
#include "fbgfx/surface.h"
|
||||
#include "fbgfx/clipping.h"
|
||||
#include "math/rect.h"
|
||||
#include "types.h"
|
||||
|
||||
typedef struct {
|
||||
COLOR tint;
|
||||
} BLIT_PARAMS;
|
||||
|
||||
#define BLIT_SOLID_PIXEL(src, dst, src_index, dst_index, params, src_fmt, dst_fmt) \
|
||||
source_pixel = GET_PIXEL_IDX_FAST(src_fmt, src, src_index); \
|
||||
SET_PIXEL_IDX_FAST(dst_fmt, dst, dst_index, source_pixel);
|
||||
|
||||
#define BLIT_BLEND_PIXEL(src, dst, src_index, dst_index, params, src_fmt, dst_fmt) \
|
||||
source_pixel = GET_PIXEL_IDX_FAST(src_fmt, src, src_index); \
|
||||
dest_pixel = GET_PIXEL_IDX_FAST(dst_fmt, dst, dst_index); \
|
||||
SET_PIXEL_IDX_FAST(dst_fmt, dst, dst_index, color_blend(source_pixel, dest_pixel));
|
||||
|
||||
#define BLIT_TRANS_PIXEL(src, dst, src_index, dst_index, params, src_fmt, dst_fmt) \
|
||||
source_pixel = GET_PIXEL_IDX_FAST(src_fmt, src, src_index); \
|
||||
if (color_get_a(source_pixel) > 0) \
|
||||
SET_PIXEL_IDX_FAST(dst_fmt, dst, dst_index, source_pixel);
|
||||
|
||||
#define BLIT_TINT_PIXEL(src, dst, src_index, dst_index, params, src_fmt, dst_fmt) \
|
||||
source_pixel = GET_PIXEL_IDX_FAST(src_fmt, src, src_index); \
|
||||
SET_PIXEL_IDX_FAST(dst_fmt, dst, dst_index, color_tint(source_pixel, params.tint));
|
||||
|
||||
#define BLIT_TINTALPHA_PIXEL(src, dst, src_index, dst_index, params, src_fmt, dst_fmt) \
|
||||
source_pixel = GET_PIXEL_IDX_FAST(src_fmt, src, src_index); \
|
||||
source_pixel = color_tint_alpha(color_get_a(source_pixel), params.tint); \
|
||||
SET_PIXEL_IDX_FAST(dst_fmt, dst, dst_index, source_pixel);
|
||||
|
||||
#define BLIT_BLEND_TINT_PIXEL(src, dst, src_index, dst_index, params, src_fmt, dst_fmt) \
|
||||
source_pixel = GET_PIXEL_IDX_FAST(src_fmt, src, src_index); \
|
||||
dest_pixel = GET_PIXEL_IDX_FAST(dst_fmt, dst, dst_index); \
|
||||
source_pixel = color_tint(source_pixel, params.tint); \
|
||||
SET_PIXEL_IDX_FAST(dst_fmt, dst, dst_index, color_blend(source_pixel, dest_pixel));
|
||||
|
||||
#define BLIT_BLEND_TINTALPHA_PIXEL(src, dst, src_index, dst_index, params, src_fmt, dst_fmt) \
|
||||
source_pixel = GET_PIXEL_IDX_FAST(src_fmt, src, src_index); \
|
||||
dest_pixel = GET_PIXEL_IDX_FAST(dst_fmt, dst, dst_index); \
|
||||
source_pixel = color_tint_alpha(color_get_a(source_pixel), params.tint); \
|
||||
SET_PIXEL_IDX_FAST(dst_fmt, dst, dst_index, color_blend(source_pixel, dest_pixel));
|
||||
|
||||
#define BLIT_INNER_LOOP(src, dst, blit_pixel, params, src_fmt, dst_fmt) \
|
||||
blit_pixel(src, dst, src_x_index, dst_x_index, params, src_fmt, dst_fmt); \
|
||||
src_x_index += src->x_inc; \
|
||||
dst_x_index += dst->x_inc; \
|
||||
|
||||
#define BLIT(src, dst, src_region, dst_x, dst_y, blit_pixel, params, src_fmt, dst_fmt) \
|
||||
do { \
|
||||
uint32_t src_x_index; \
|
||||
uint32_t src_y_index; \
|
||||
uint32_t dst_x_index; \
|
||||
uint32_t dst_y_index; \
|
||||
COLOR source_pixel; \
|
||||
COLOR dest_pixel; \
|
||||
\
|
||||
src_y_index = surface_get_index_of(src, src_region.x, src_region.y); \
|
||||
dst_y_index = surface_get_index_of(dst, dst_x, dst_y); \
|
||||
\
|
||||
int x; \
|
||||
int y; \
|
||||
for (y = 0; y < src_region.height; ++y) { \
|
||||
src_x_index = src_y_index; \
|
||||
dst_x_index = dst_y_index; \
|
||||
\
|
||||
for (x = 0; x < src_region.width; ++x) { \
|
||||
BLIT_INNER_LOOP(src, dst, blit_pixel, params, src_fmt, dst_fmt); \
|
||||
} \
|
||||
\
|
||||
src_y_index += src->y_inc; \
|
||||
dst_y_index += dst->y_inc; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#define BLIT_UNROLLED8(src, dst, src_region, dst_x, dst_y, blit_pixel, params, src_fmt, dst_fmt) \
|
||||
do { \
|
||||
uint32_t src_x_index; \
|
||||
uint32_t src_y_index; \
|
||||
uint32_t dst_x_index; \
|
||||
uint32_t dst_y_index; \
|
||||
COLOR source_pixel; \
|
||||
COLOR dest_pixel; \
|
||||
\
|
||||
src_y_index = surface_get_index_of(src, src_region.x, src_region.y); \
|
||||
dst_y_index = surface_get_index_of(dst, dst_x, dst_y); \
|
||||
\
|
||||
int x; \
|
||||
int y; \
|
||||
for (y = 0; y < src_region.height; ++y) { \
|
||||
src_x_index = src_y_index; \
|
||||
dst_x_index = dst_y_index; \
|
||||
\
|
||||
for (x = 0; x < src_region.width; x += 8) { \
|
||||
BLIT_INNER_LOOP(src, dst, blit_pixel, params, src_fmt, dst_fmt); \
|
||||
BLIT_INNER_LOOP(src, dst, blit_pixel, params, src_fmt, dst_fmt); \
|
||||
BLIT_INNER_LOOP(src, dst, blit_pixel, params, src_fmt, dst_fmt); \
|
||||
BLIT_INNER_LOOP(src, dst, blit_pixel, params, src_fmt, dst_fmt); \
|
||||
BLIT_INNER_LOOP(src, dst, blit_pixel, params, src_fmt, dst_fmt); \
|
||||
BLIT_INNER_LOOP(src, dst, blit_pixel, params, src_fmt, dst_fmt); \
|
||||
BLIT_INNER_LOOP(src, dst, blit_pixel, params, src_fmt, dst_fmt); \
|
||||
BLIT_INNER_LOOP(src, dst, blit_pixel, params, src_fmt, dst_fmt); \
|
||||
} \
|
||||
\
|
||||
src_y_index += src->y_inc; \
|
||||
dst_y_index += dst->y_inc; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#define BLIT_FMT(src_fmt, dst_fmt) ((src_fmt << 16) | dst_fmt)
|
||||
|
||||
#define SELECT_BLIT(src, dst, src_region, dst_x, dst_y, blit, blit_pixel, params) \
|
||||
switch (BLIT_FMT(src->format, dst->format)) { \
|
||||
case BLIT_FMT(SURFACE_FORMAT_ALPHA, SURFACE_FORMAT_ALPHA): blit(src, dst, src_region, dst_x, dst_y, blit_pixel, params, alpha, alpha); break; \
|
||||
case BLIT_FMT(SURFACE_FORMAT_ALPHA, SURFACE_FORMAT_RGB): blit(src, dst, src_region, dst_x, dst_y, blit_pixel, params, alpha, rgb); break; \
|
||||
case BLIT_FMT(SURFACE_FORMAT_ALPHA, SURFACE_FORMAT_RGBA): blit(src, dst, src_region, dst_x, dst_y, blit_pixel, params, alpha, rgba); break; \
|
||||
case BLIT_FMT(SURFACE_FORMAT_RGB, SURFACE_FORMAT_ALPHA): blit(src, dst, src_region, dst_x, dst_y, blit_pixel, params, rgb, alpha); break; \
|
||||
case BLIT_FMT(SURFACE_FORMAT_RGB, SURFACE_FORMAT_RGB): blit(src, dst, src_region, dst_x, dst_y, blit_pixel, params, rgb, rgb); break; \
|
||||
case BLIT_FMT(SURFACE_FORMAT_RGB, SURFACE_FORMAT_RGBA): blit(src, dst, src_region, dst_x, dst_y, blit_pixel, params, rgb, rgba); break; \
|
||||
case BLIT_FMT(SURFACE_FORMAT_RGBA, SURFACE_FORMAT_ALPHA): blit(src, dst, src_region, dst_x, dst_y, blit_pixel, params, rgba, alpha); break; \
|
||||
case BLIT_FMT(SURFACE_FORMAT_RGBA, SURFACE_FORMAT_RGB): blit(src, dst, src_region, dst_x, dst_y, blit_pixel, params, rgba, rgb); break; \
|
||||
case BLIT_FMT(SURFACE_FORMAT_RGBA, SURFACE_FORMAT_RGBA): blit(src, dst, src_region, dst_x, dst_y, blit_pixel, params, rgba, rgba); break; \
|
||||
}
|
||||
|
||||
#define DO_BLIT(src, dst, src_region, dst_x, dst_y, blit_pixel, params) \
|
||||
if (src_region.width % 8 == 0) { \
|
||||
SELECT_BLIT(src, dst, src_region, dst_x, dst_y, BLIT_UNROLLED8, blit_pixel, params); \
|
||||
} else { \
|
||||
SELECT_BLIT(src, dst, src_region, dst_x, dst_y, BLIT, blit_pixel, params); \
|
||||
}
|
||||
|
||||
void surface_blit_region(SURFACE *source, SURFACE *destination, int source_x, int source_y, int source_width, int source_height, int dest_x, int dest_y) {
|
||||
RECT source_region = rect_create(source_x, source_y, source_width, source_height);
|
||||
bool on_screen = clip_blit_region(&destination->clip_region, &source_region, &dest_x, &dest_y);
|
||||
if (!on_screen)
|
||||
return;
|
||||
|
||||
DO_BLIT(source, destination, source_region, dest_x, dest_y, BLIT_SOLID_PIXEL, NULL);
|
||||
}
|
||||
|
||||
void surface_blit_blend_region(SURFACE *source, SURFACE *destination, int source_x, int source_y, int source_width, int source_height, int dest_x, int dest_y) {
|
||||
RECT source_region = rect_create(source_x, source_y, source_width, source_height);
|
||||
bool on_screen = clip_blit_region(&destination->clip_region, &source_region, &dest_x, &dest_y);
|
||||
if (!on_screen)
|
||||
return;
|
||||
|
||||
DO_BLIT(source, destination, source_region, dest_x, dest_y, BLIT_BLEND_PIXEL, NULL);
|
||||
}
|
||||
|
||||
void surface_blit_trans_region(SURFACE *source, SURFACE *destination, int source_x, int source_y, int source_width, int source_height, int dest_x, int dest_y) {
|
||||
RECT source_region = rect_create(source_x, source_y, source_width, source_height);
|
||||
bool on_screen = clip_blit_region(&destination->clip_region, &source_region, &dest_x, &dest_y);
|
||||
if (!on_screen)
|
||||
return;
|
||||
|
||||
DO_BLIT(source, destination, source_region, dest_x, dest_y, BLIT_TRANS_PIXEL, NULL);
|
||||
}
|
||||
|
||||
void surface_blit_tint_region(SURFACE *source, SURFACE *destination, int source_x, int source_y, int source_width, int source_height, int dest_x, int dest_y, COLOR tint) {
|
||||
RECT source_region = rect_create(source_x, source_y, source_width, source_height);
|
||||
bool on_screen = clip_blit_region(&destination->clip_region, &source_region, &dest_x, &dest_y);
|
||||
if (!on_screen)
|
||||
return;
|
||||
|
||||
BLIT_PARAMS params;
|
||||
params.tint = tint;
|
||||
|
||||
if (source->format == SURFACE_FORMAT_ALPHA) {
|
||||
DO_BLIT(source, destination, source_region, dest_x, dest_y, BLIT_TINTALPHA_PIXEL, params);
|
||||
} else {
|
||||
DO_BLIT(source, destination, source_region, dest_x, dest_y, BLIT_TINT_PIXEL, params);
|
||||
}
|
||||
}
|
||||
|
||||
void surface_blit_blend_tint_region(SURFACE *source, SURFACE *destination, int source_x, int source_y, int source_width, int source_height, int dest_x, int dest_y, COLOR tint) {
|
||||
RECT source_region = rect_create(source_x, source_y, source_width, source_height);
|
||||
bool on_screen = clip_blit_region(&destination->clip_region, &source_region, &dest_x, &dest_y);
|
||||
if (!on_screen)
|
||||
return;
|
||||
|
||||
BLIT_PARAMS params;
|
||||
params.tint = tint;
|
||||
|
||||
if (source->format == SURFACE_FORMAT_ALPHA) {
|
||||
DO_BLIT(source, destination, source_region, dest_x, dest_y, BLIT_BLEND_TINTALPHA_PIXEL, params);
|
||||
} else {
|
||||
DO_BLIT(source, destination, source_region, dest_x, dest_y, BLIT_BLEND_TINT_PIXEL, params);
|
||||
}
|
||||
}
|
16
src/fbgfx/surface_convert.c
Normal file
16
src/fbgfx/surface_convert.c
Normal file
|
@ -0,0 +1,16 @@
|
|||
#include "fbgfx/surface.h"
|
||||
#include "types.h"
|
||||
|
||||
void surface_convert_rgb_to_bgr(SURFACE *surface) {
|
||||
if (surface->format != SURFACE_FORMAT_RGB && surface->format != SURFACE_FORMAT_RGBA)
|
||||
return;
|
||||
|
||||
uint32_t buffer_size = surface_get_buffer_size(surface);
|
||||
|
||||
COLOR_COMPONENT *p = surface->pixels;
|
||||
COLOR_COMPONENT *end = p + buffer_size;
|
||||
for (; p < end; p += surface->bytes_per_pixel) {
|
||||
SWAP(COLOR_COMPONENT, p[0], p[2]);
|
||||
}
|
||||
}
|
||||
|
280
src/fbgfx/surface_primitives.c
Normal file
280
src/fbgfx/surface_primitives.c
Normal file
|
@ -0,0 +1,280 @@
|
|||
#include "fbgfx/surface.h"
|
||||
#include "fbgfx/clipping.h"
|
||||
#include "math/rect.h"
|
||||
#include "types.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
#define SIGN(x) ((x < 0) ? -1 : ((x > 0) ? 1 : 0))
|
||||
|
||||
|
||||
/**
|
||||
* PIXEL PLOTTING
|
||||
*/
|
||||
|
||||
static inline void set_pixel_fast(SURFACE *surface, int x, int y, COLOR color) {
|
||||
switch (surface->format) {
|
||||
case SURFACE_FORMAT_ALPHA: surface_set_pixel_alpha_fast(surface, x, y, color); break;
|
||||
case SURFACE_FORMAT_RGB: surface_set_pixel_rgb_fast(surface, x, y, color); break;
|
||||
case SURFACE_FORMAT_RGBA: surface_set_pixel_rgba_fast(surface, x, y, color); break;
|
||||
}
|
||||
}
|
||||
|
||||
static inline COLOR get_pixel_fast(const SURFACE *surface, int x, int y) {
|
||||
switch (surface->format) {
|
||||
case SURFACE_FORMAT_ALPHA: return surface_get_pixel_alpha_fast(surface, x, y);
|
||||
case SURFACE_FORMAT_RGB: return surface_get_pixel_rgb_fast(surface, x, y);
|
||||
case SURFACE_FORMAT_RGBA: return surface_get_pixel_rgba_fast(surface, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
void surface_set_pixel(SURFACE *surface, int x, int y, COLOR color) {
|
||||
if (!point_in_bounds(&surface->clip_region, x, y))
|
||||
return;
|
||||
|
||||
set_pixel_fast(surface, x, y, color);
|
||||
}
|
||||
|
||||
void surface_set_pixel_fast(SURFACE *surface, int x, int y, COLOR color) {
|
||||
set_pixel_fast(surface, x, y, color);
|
||||
}
|
||||
|
||||
COLOR surface_get_pixel(const SURFACE *surface, int x, int y) {
|
||||
if (!point_in_bounds(&surface->clip_region, x, y))
|
||||
return 0;
|
||||
|
||||
return get_pixel_fast(surface, x, y);
|
||||
}
|
||||
|
||||
COLOR surface_get_pixel_fast(SURFACE *surface, int x, int y) {
|
||||
return get_pixel_fast(surface, x, y);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* LINE DRAWING
|
||||
*/
|
||||
|
||||
#define DRAW_LINE(surface, x1, y1, x2, y2, color, draw_pixel) \
|
||||
do { \
|
||||
int delta_x = x2 - x1; \
|
||||
int delta_y = y2 - y1; \
|
||||
int delta_x_abs = abs(delta_x); \
|
||||
int delta_y_abs = abs(delta_y); \
|
||||
int delta_x_sign = SIGN(delta_x); \
|
||||
int delta_y_sign = SIGN(delta_y); \
|
||||
int x = delta_y_abs / 2; \
|
||||
int y = delta_x_abs / 2; \
|
||||
int px = x1; \
|
||||
int py = y1; \
|
||||
\
|
||||
draw_pixel(surface, px, py, color); \
|
||||
\
|
||||
if (delta_x_abs >= delta_y_abs) { \
|
||||
int i; \
|
||||
for(i = 0; i < delta_x_abs; ++i) { \
|
||||
y += delta_y_abs; \
|
||||
\
|
||||
if (y >= delta_x_abs) { \
|
||||
y -= delta_x_abs; \
|
||||
py += delta_y_sign; \
|
||||
} \
|
||||
\
|
||||
px += delta_x_sign; \
|
||||
draw_pixel(surface, px, py, color); \
|
||||
} \
|
||||
\
|
||||
} else { \
|
||||
int i; \
|
||||
for(i = 0; i < delta_y_abs; ++i) { \
|
||||
x += delta_x_abs; \
|
||||
\
|
||||
if (x >= delta_y_abs) { \
|
||||
x -= delta_y_abs; \
|
||||
px += delta_x_sign; \
|
||||
} \
|
||||
\
|
||||
py += delta_y_sign; \
|
||||
draw_pixel(surface, px, py, color); \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
void surface_line_fast(SURFACE *surface, int x1, int y1, int x2, int y2, COLOR color) {
|
||||
switch (surface->format) {
|
||||
case SURFACE_FORMAT_ALPHA: DRAW_LINE(surface, x1, y1, x2, y2, color, surface_set_pixel_alpha_fast); break;
|
||||
case SURFACE_FORMAT_RGB: DRAW_LINE(surface, x1, y1, x2, y2, color, surface_set_pixel_rgb_fast); break;
|
||||
case SURFACE_FORMAT_RGBA: DRAW_LINE(surface, x1, y1, x2, y2, color, surface_set_pixel_rgba_fast); break;
|
||||
}
|
||||
}
|
||||
|
||||
void surface_line(SURFACE *surface, int x1, int y1, int x2, int y2, COLOR color) {
|
||||
if (!line_in_bounds(&surface->clip_region, x1, y1, x2, y2))
|
||||
return;
|
||||
|
||||
DRAW_LINE(surface, x1, y1, x2, y2, color, surface_set_pixel);
|
||||
}
|
||||
|
||||
#define DRAW_FAST_HLINE(surface, x1, x2, y, color, fmt) \
|
||||
do { \
|
||||
if (x2 < x1) \
|
||||
SWAP(int, x1, x2); \
|
||||
\
|
||||
int x; \
|
||||
uint32_t i = surface_get_index_of(surface, x1, y); \
|
||||
for (x = x1; x <= x2; ++x, i += surface->x_inc) \
|
||||
SET_PIXEL_IDX_FAST(fmt, surface, i, color); \
|
||||
} while(0)
|
||||
|
||||
void surface_hline_fast(SURFACE *surface, int x1, int x2, int y, COLOR color) {
|
||||
switch (surface->format) {
|
||||
case SURFACE_FORMAT_ALPHA: DRAW_FAST_HLINE(surface, x1, x2, y, color, alpha); break;
|
||||
case SURFACE_FORMAT_RGB: DRAW_FAST_HLINE(surface, x1, x2, y, color, rgb); break;
|
||||
case SURFACE_FORMAT_RGBA: DRAW_FAST_HLINE(surface, x1, x2, y, color, rgba); break;
|
||||
}
|
||||
}
|
||||
|
||||
void surface_hline(SURFACE *surface, int x1, int x2, int y, COLOR color) {
|
||||
if (!clamp_to_region(&surface->clip_region, &x1, &y, &x2, &y))
|
||||
return;
|
||||
|
||||
surface_hline_fast(surface, x1, x2, y, color);
|
||||
}
|
||||
|
||||
#define DRAW_FAST_VLINE(surface, x, y1, y2, color, fmt) \
|
||||
do { \
|
||||
if (y2 < y1) \
|
||||
SWAP(int, y1, y2); \
|
||||
\
|
||||
int y; \
|
||||
uint32_t i = surface_get_index_of(surface, x, y1); \
|
||||
for (y = y1; y <= y2; ++y, i += surface->y_inc) \
|
||||
SET_PIXEL_IDX_FAST(fmt, surface, i, color); \
|
||||
} while (0)
|
||||
|
||||
void surface_vline_fast(SURFACE *surface, int x, int y1, int y2, COLOR color) {
|
||||
switch (surface->format) {
|
||||
case SURFACE_FORMAT_ALPHA: DRAW_FAST_VLINE(surface, x, y1, y2, color, alpha); break;
|
||||
case SURFACE_FORMAT_RGB: DRAW_FAST_VLINE(surface, x, y1, y2, color, rgb); break;
|
||||
case SURFACE_FORMAT_RGBA: DRAW_FAST_VLINE(surface, x, y1, y2, color, rgba); break;
|
||||
}
|
||||
}
|
||||
|
||||
void surface_vline(SURFACE *surface, int x, int y1, int y2, COLOR color) {
|
||||
if (!clamp_to_region(&surface->clip_region, &x, &y1, &x, &y2))
|
||||
return;
|
||||
|
||||
surface_vline_fast(surface, x, y1, y2, color);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* RECTANGLE DRAWING
|
||||
*/
|
||||
|
||||
void surface_rect(SURFACE *surface, int x1, int y1, int x2, int y2, COLOR color) {
|
||||
if (!line_in_bounds(&surface->clip_region, x1, y1, x2, y2))
|
||||
return;
|
||||
|
||||
if (x2 < x1)
|
||||
SWAP(int, x1, x2);
|
||||
if (y2 < y1)
|
||||
SWAP(int, y1, y2);
|
||||
|
||||
int x;
|
||||
for (x = x1; x <= x2; ++x) {
|
||||
surface_set_pixel(surface, x, y1, color);
|
||||
surface_set_pixel(surface, x, y2, color);
|
||||
}
|
||||
|
||||
int y;
|
||||
for (y = y1; y <= y2; ++y) {
|
||||
surface_set_pixel(surface, x1, y, color);
|
||||
surface_set_pixel(surface, x2, y, color);
|
||||
}
|
||||
}
|
||||
|
||||
#define DRAW_FAST_RECT(surface, x1, y1, x2, y2, color, fmt) \
|
||||
do { \
|
||||
if (x2 < x1) \
|
||||
SWAP(int, x1, x2); \
|
||||
if (y2 < y1) \
|
||||
SWAP(int, y1, y2); \
|
||||
\
|
||||
uint32_t a; \
|
||||
uint32_t b; \
|
||||
\
|
||||
int x = x1; \
|
||||
a = surface_get_index_of(surface, x, y1); \
|
||||
b = surface_get_index_of(surface, x, y2); \
|
||||
for (; x <= x2; ++x) { \
|
||||
SET_PIXEL_IDX_FAST(fmt, surface, a, color); \
|
||||
SET_PIXEL_IDX_FAST(fmt, surface, b, color); \
|
||||
a += surface->x_inc; \
|
||||
b += surface->x_inc; \
|
||||
} \
|
||||
\
|
||||
int y = y1; \
|
||||
a = surface_get_index_of(surface, x1, y); \
|
||||
b = surface_get_index_of(surface, x2, y); \
|
||||
for (; y <= y2; ++y) { \
|
||||
SET_PIXEL_IDX_FAST(fmt, surface, a, color); \
|
||||
SET_PIXEL_IDX_FAST(fmt, surface, b, color); \
|
||||
a += surface->y_inc; \
|
||||
b += surface->y_inc; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
void surface_rect_fast(SURFACE *surface, int x1, int y1, int x2, int y2, COLOR color) {
|
||||
switch (surface->format) {
|
||||
case SURFACE_FORMAT_ALPHA: DRAW_FAST_RECT(surface, x1, y1, x2, y2, color, alpha); break;
|
||||
case SURFACE_FORMAT_RGB: DRAW_FAST_RECT(surface, x1, y1, x2, y2, color, rgb); break;
|
||||
case SURFACE_FORMAT_RGBA: DRAW_FAST_RECT(surface, x1, y1, x2, y2, color, rgba); break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* FILLED RECTANGLE DRAWING
|
||||
*/
|
||||
|
||||
#define DRAW_FAST_RECT_FILLED(surface, x1, y1, x2, y2, color, fmt) \
|
||||
do { \
|
||||
if (x2 < x1) \
|
||||
SWAP(int, x1, x2); \
|
||||
if (y2 < y1) \
|
||||
SWAP(int, y1, y2); \
|
||||
\
|
||||
uint32_t x_index; \
|
||||
uint32_t y_index; \
|
||||
int x; \
|
||||
int y; \
|
||||
\
|
||||
y_index = surface_get_index_of(surface, x1, y1); \
|
||||
for (y = y1; y <= y2; ++y) { \
|
||||
x_index = y_index; \
|
||||
for (x = x1; x <= x2; ++x) { \
|
||||
SET_PIXEL_IDX_FAST(fmt, surface, x_index, color); \
|
||||
x_index += surface->x_inc; \
|
||||
} \
|
||||
\
|
||||
y_index += surface->y_inc; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
void surface_rect_filled_fast(SURFACE *surface, int x1, int y1, int x2, int y2, COLOR color) {
|
||||
switch (surface->format) {
|
||||
case SURFACE_FORMAT_ALPHA: DRAW_FAST_RECT_FILLED(surface, x1, y1, x2, y2, color, alpha); break;
|
||||
case SURFACE_FORMAT_RGB: DRAW_FAST_RECT_FILLED(surface, x1, y1, x2, y2, color, rgb); break;
|
||||
case SURFACE_FORMAT_RGBA: DRAW_FAST_RECT_FILLED(surface, x1, y1, x2, y2, color, rgba); break;
|
||||
}
|
||||
}
|
||||
|
||||
void surface_rect_filled(SURFACE *surface, int x1, int y1, int x2, int y2, COLOR color) {
|
||||
if (!clamp_to_region(&surface->clip_region, &x1, &y1, &x2, &y2))
|
||||
return;
|
||||
|
||||
surface_rect_filled_fast(surface, x1, y1, x2, y2, color);
|
||||
}
|
69
src/main.cpp
Normal file
69
src/main.cpp
Normal file
|
@ -0,0 +1,69 @@
|
|||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#include <fbgfx/surface.h>
|
||||
#include "vm/vm.h"
|
||||
#include "fbgfx/surface.h"
|
||||
#include "fbgfx/clipping.h"
|
||||
#include "assets/image.h"
|
||||
#include "vm/vm_window.h"
|
||||
|
||||
static inline double get_seconds() {
|
||||
return (double)clock() / CLOCKS_PER_SEC;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
log_init();
|
||||
input_init();
|
||||
WINDOW *window = window_init("Rasterizer Test", 1280, 960, 320, 240);
|
||||
if (!window)
|
||||
return 1;
|
||||
|
||||
SURFACE *sprite = image_load_file("/Users/gered/resources/grass01.bmp");
|
||||
SURFACE *sprite2 = surface_create(16, 16, SURFACE_FORMAT_ALPHA, SURFACE_FLAGS_NONE);
|
||||
surface_clear(sprite2, color_create_rgba(0, 0, 0, 0));
|
||||
surface_rect_filled(sprite2, 4, 4, 11, 11, color_create_rgba(0, 0, 0, 128));
|
||||
surface_line(sprite2, 0, 0, 15, 15, color_create_rgba(0, 0, 0, 255));
|
||||
|
||||
double before;
|
||||
double after;
|
||||
int i;
|
||||
|
||||
before = get_seconds();
|
||||
|
||||
for (i = 0; i < 500000; ++i) {
|
||||
surface_blit(sprite, window->surface, 0, 0);
|
||||
//surface_blit_blend(sprite, window->surface, 0, 0);
|
||||
}
|
||||
|
||||
after = get_seconds();
|
||||
|
||||
printf("time taken: %f\n", after - before);
|
||||
|
||||
while (true) {
|
||||
if (!window_do_events(window))
|
||||
break;
|
||||
if (input_is_key_pressed(KSYM_ESCAPE))
|
||||
break;
|
||||
|
||||
//surface_clear(window->surface, color_create_rgba(192, 192, 192, 255));
|
||||
int x, y;
|
||||
for (y = 0; y < window->surface->height; y += 16) {
|
||||
for (x = 0; x < window->surface->width; x += 16) {
|
||||
surface_blit(sprite, window->surface, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
surface_line(window->surface, window->viewport_width / 2, window->viewport_height / 2, input_get_mouse_x(), input_get_mouse_y(), COLOR_YELLOW);
|
||||
//surface_blit_tint(sprite, window->surface, INPUT_GetMouseX(), INPUT_GetMouseY(), COLOR_DOS_BROWN);
|
||||
surface_blit_blend(sprite2, window->surface, 0, 0);
|
||||
surface_blit_blend_tint(sprite2, window->surface, 16, 0, color_create_rgba(255, 0, 0, 64));
|
||||
|
||||
window_render(window);
|
||||
}
|
||||
|
||||
window_destroy(window);
|
||||
input_destroy();
|
||||
log_end();
|
||||
|
||||
return 0;
|
||||
}
|
8
src/vendor/stb_image.c
vendored
Normal file
8
src/vendor/stb_image.c
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
/*
|
||||
* This source file obviously only exists to provide a spot
|
||||
* for the stb_image implementation to exist.
|
||||
*/
|
||||
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include "stb_image.h"
|
||||
|
4687
src/vendor/stb_image.h
vendored
Normal file
4687
src/vendor/stb_image.h
vendored
Normal file
File diff suppressed because it is too large
Load diff
8
src/vendor/stb_truetype.c
vendored
Normal file
8
src/vendor/stb_truetype.c
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
/*
|
||||
* This source file obviously only exists to provide a spot
|
||||
* for the stb_typetype implementation to exist.
|
||||
*/
|
||||
|
||||
#define STB_TRUETYPE_IMPLEMENTATION
|
||||
#include "stb_truetype.h"
|
||||
|
2100
src/vendor/stb_truetype.h
vendored
Normal file
2100
src/vendor/stb_truetype.h
vendored
Normal file
File diff suppressed because it is too large
Load diff
14
src/vm/vm.h
Normal file
14
src/vm/vm.h
Normal file
|
@ -0,0 +1,14 @@
|
|||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "vm_stdinc.h"
|
||||
#include "vm_log.h"
|
||||
#include "vm_window.h"
|
||||
#include "vm_input.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
123
src/vm/vm_input.c
Normal file
123
src/vm/vm_input.c
Normal file
|
@ -0,0 +1,123 @@
|
|||
#include "vm_input.h"
|
||||
#include "vm_log.h"
|
||||
#include <string.h>
|
||||
|
||||
const int MAX_KEYS = SDL_NUM_SCANCODES;
|
||||
const int MAX_MOUSE_BUTTONS = 3;
|
||||
|
||||
struct {
|
||||
bool keys[MAX_KEYS];
|
||||
bool locked_keys[MAX_KEYS];
|
||||
bool mouse_buttons[MAX_MOUSE_BUTTONS];
|
||||
bool locked_mouse_buttons[MAX_MOUSE_BUTTONS];
|
||||
uint mouse_x;
|
||||
uint mouse_y;
|
||||
int mouse_delta_x;
|
||||
int mouse_delta_y;
|
||||
} input_state;
|
||||
|
||||
bool input_init() {
|
||||
LOG_INFO("input_init\n");
|
||||
|
||||
memset(input_state.keys, 0, sizeof(bool) * MAX_KEYS);
|
||||
memset(input_state.locked_keys, 0, sizeof(bool) * MAX_KEYS);
|
||||
memset(input_state.mouse_buttons, 0, sizeof(bool) * MAX_MOUSE_BUTTONS);
|
||||
memset(input_state.locked_mouse_buttons, 0, sizeof(bool) * MAX_MOUSE_BUTTONS);
|
||||
input_state.mouse_x = 0;
|
||||
input_state.mouse_y = 0;
|
||||
input_state.mouse_delta_x = 0;
|
||||
input_state.mouse_delta_y = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void input_destroy() {
|
||||
LOG_INFO("input_destroy\n");
|
||||
}
|
||||
|
||||
void input_on_key_event(SDL_Event *event) {
|
||||
if (event->type == SDL_KEYUP) {
|
||||
int key = event->key.keysym.scancode;
|
||||
input_state.keys[key] = false;
|
||||
input_state.locked_keys[key] = false;
|
||||
|
||||
} else if (event->type == SDL_KEYDOWN) {
|
||||
int key = event->key.keysym.scancode;
|
||||
input_state.keys[key] = !(input_state.locked_keys[key]);
|
||||
}
|
||||
}
|
||||
|
||||
void input_on_mouse_event(SDL_Event *event) {
|
||||
int button;
|
||||
|
||||
switch (event->type) {
|
||||
case SDL_MOUSEMOTION:
|
||||
input_state.mouse_x = event->motion.x;
|
||||
input_state.mouse_y = event->motion.y;
|
||||
input_state.mouse_delta_x = event->motion.xrel;
|
||||
input_state.mouse_delta_y = event->motion.yrel;
|
||||
break;
|
||||
|
||||
case SDL_MOUSEBUTTONUP:
|
||||
button = event->button.button;
|
||||
input_state.mouse_buttons[button] = false;
|
||||
input_state.locked_mouse_buttons[button] = false;
|
||||
break;
|
||||
|
||||
case SDL_MOUSEBUTTONDOWN:
|
||||
button = event->button.button;
|
||||
input_state.mouse_buttons[button] = !(input_state.locked_mouse_buttons[button]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool input_is_key_down(KEYS key) {
|
||||
SDL_Scancode scancode = SDL_GetScancodeFromKey(key);
|
||||
return input_state.keys[scancode] && !input_state.locked_keys[scancode];
|
||||
}
|
||||
|
||||
bool input_is_key_pressed(KEYS key) {
|
||||
SDL_Scancode scancode = SDL_GetScancodeFromKey(key);
|
||||
if (input_state.keys[scancode] && !input_state.locked_keys[scancode]) {
|
||||
input_state.locked_keys[scancode] = true;
|
||||
return true;
|
||||
} else
|
||||
return false;
|
||||
}
|
||||
|
||||
void input_lock_key(KEYS key) {
|
||||
SDL_Scancode scancode = SDL_GetScancodeFromKey(key);
|
||||
input_state.locked_keys[scancode] = true;
|
||||
}
|
||||
|
||||
bool input_is_mouse_button_down(MOUSE_BUTTONS button) {
|
||||
return input_state.mouse_buttons[button] && !input_state.locked_mouse_buttons[button];
|
||||
}
|
||||
|
||||
bool input_is_mouse_button_pressed(MOUSE_BUTTONS button) {
|
||||
if (input_state.mouse_buttons[button] && !input_state.locked_mouse_buttons[button]) {
|
||||
input_state.locked_mouse_buttons[button] = true;
|
||||
return true;
|
||||
} else
|
||||
return false;
|
||||
}
|
||||
|
||||
void input_lock_mouse_button(MOUSE_BUTTONS button) {
|
||||
input_state.locked_mouse_buttons[button] = true;
|
||||
}
|
||||
|
||||
int input_get_mouse_delta_x() {
|
||||
return input_state.mouse_delta_x;
|
||||
}
|
||||
|
||||
int input_get_mouse_delta_y() {
|
||||
return input_state.mouse_delta_y;
|
||||
}
|
||||
|
||||
uint input_get_mouse_x() {
|
||||
return input_state.mouse_x;
|
||||
}
|
||||
|
||||
uint input_get_mouse_y() {
|
||||
return input_state.mouse_y;
|
||||
}
|
30
src/vm/vm_input.h
Normal file
30
src/vm/vm_input.h
Normal file
|
@ -0,0 +1,30 @@
|
|||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "vm_stdinc.h"
|
||||
#include "vm_input_codes.h"
|
||||
#include "SDL.h"
|
||||
|
||||
bool input_init();
|
||||
void input_destroy();
|
||||
|
||||
void input_on_key_event(SDL_Event *event);
|
||||
void input_on_mouse_event(SDL_Event *event);
|
||||
|
||||
bool input_is_key_down(KEYS key);
|
||||
bool input_is_key_pressed(KEYS key);
|
||||
void input_lock_key(KEYS key);
|
||||
bool input_is_mouse_button_down(MOUSE_BUTTONS button);
|
||||
bool input_is_mouse_button_pressed(MOUSE_BUTTONS button);
|
||||
void input_lock_mouse_button(MOUSE_BUTTONS button);
|
||||
int input_get_mouse_delta_x();
|
||||
int input_get_mouse_delta_y();
|
||||
uint input_get_mouse_x();
|
||||
uint input_get_mouse_y();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
107
src/vm/vm_input_codes.h
Normal file
107
src/vm/vm_input_codes.h
Normal file
|
@ -0,0 +1,107 @@
|
|||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "SDL.h"
|
||||
|
||||
typedef enum {
|
||||
MOUSE_LEFT = 1,
|
||||
MOUSE_MIDDLE = 2,
|
||||
MOUSE_RIGHT = 3
|
||||
} MOUSE_BUTTONS;
|
||||
|
||||
// the idea behind this enum is that on other platforms (that may or may not use SDL)
|
||||
// we could just remap to the platform's API, but use the same key constants
|
||||
|
||||
typedef enum {
|
||||
KSYM_UNKNOWN = 0,
|
||||
KSYM_ESCAPE = SDLK_ESCAPE,
|
||||
KSYM_BACKSPACE = SDLK_BACKSPACE,
|
||||
KSYM_END = SDLK_END,
|
||||
KSYM_SOFT_LEFT = 0,
|
||||
KSYM_SOFT_RIGHT = 0,
|
||||
KSYM_HOME = SDLK_HOME,
|
||||
KSYM_BACK = 0,
|
||||
KSYM_CALL = 0,
|
||||
KSYM_ENDCALL = 0,
|
||||
KSYM_0 = SDLK_0,
|
||||
KSYM_1 = SDLK_1,
|
||||
KSYM_2 = SDLK_2,
|
||||
KSYM_3 = SDLK_3,
|
||||
KSYM_4 = SDLK_4,
|
||||
KSYM_5 = SDLK_5,
|
||||
KSYM_6 = SDLK_6,
|
||||
KSYM_7 = SDLK_7,
|
||||
KSYM_8 = SDLK_8,
|
||||
KSYM_9 = SDLK_9,
|
||||
KSYM_ASTERISK = SDLK_ASTERISK,
|
||||
KSYM_HASH = SDLK_HASH,
|
||||
KSYM_UP = SDLK_UP,
|
||||
KSYM_DOWN = SDLK_DOWN,
|
||||
KSYM_LEFT = SDLK_LEFT,
|
||||
KSYM_RIGHT = SDLK_RIGHT,
|
||||
KSYM_DPAD_CENTER = 0,
|
||||
KSYM_VOLUME_UP = 0,
|
||||
KSYM_VOLUME_DOWN = 0,
|
||||
KSYM_CAMERA = 0,
|
||||
KSYM_CLEAR = 0,
|
||||
KSYM_A = SDLK_a,
|
||||
KSYM_B = SDLK_b,
|
||||
KSYM_C = SDLK_c,
|
||||
KSYM_D = SDLK_d,
|
||||
KSYM_E = SDLK_e,
|
||||
KSYM_F = SDLK_f,
|
||||
KSYM_G = SDLK_g,
|
||||
KSYM_H = SDLK_h,
|
||||
KSYM_I = SDLK_i,
|
||||
KSYM_J = SDLK_j,
|
||||
KSYM_K = SDLK_k,
|
||||
KSYM_L = SDLK_l,
|
||||
KSYM_M = SDLK_m,
|
||||
KSYM_N = SDLK_n,
|
||||
KSYM_O = SDLK_o,
|
||||
KSYM_P = SDLK_p,
|
||||
KSYM_Q = SDLK_q,
|
||||
KSYM_R = SDLK_r,
|
||||
KSYM_S = SDLK_s,
|
||||
KSYM_T = SDLK_t,
|
||||
KSYM_U = SDLK_u,
|
||||
KSYM_V = SDLK_v,
|
||||
KSYM_W = SDLK_w,
|
||||
KSYM_X = SDLK_x,
|
||||
KSYM_Y = SDLK_y,
|
||||
KSYM_Z = SDLK_z,
|
||||
KSYM_COMMA = SDLK_COMMA,
|
||||
KSYM_PERIOD = SDLK_PERIOD,
|
||||
KSYM_LALT = SDLK_LALT,
|
||||
KSYM_RALT = SDLK_RALT,
|
||||
KSYM_RCTRL = SDLK_RCTRL,
|
||||
KSYM_LCTRL = SDLK_LCTRL,
|
||||
KSYM_LSHIFT = SDLK_LSHIFT,
|
||||
KSYM_RSHIFT = SDLK_RSHIFT,
|
||||
KSYM_TAB = SDLK_TAB,
|
||||
KSYM_SPACE = SDLK_SPACE,
|
||||
KSYM_SYM = 0,
|
||||
KSYM_RETURN = SDLK_RETURN,
|
||||
KSYM_DELETE = SDLK_DELETE,
|
||||
KSYM_MINUS = SDLK_MINUS,
|
||||
KSYM_EQUALS = SDLK_EQUALS,
|
||||
KSYM_LEFTBRACKET = SDLK_LEFTBRACKET,
|
||||
KSYM_RIGHTBRACKET = SDLK_RIGHTBRACKET,
|
||||
KSYM_BACKSLASH = SDLK_BACKSLASH,
|
||||
KSYM_SEMICOLON = SDLK_SEMICOLON,
|
||||
KSYM_QUOTE = SDLK_QUOTE,
|
||||
KSYM_SLASH = SDLK_SLASH,
|
||||
KSYM_AT = SDLK_AT,
|
||||
KSYM_PLUS = SDLK_PLUS,
|
||||
KSYM_MENU = 0,
|
||||
KSYM_SEARCH = 0,
|
||||
KSYM_PAGEUP = SDLK_PAGEUP,
|
||||
KSYM_PAGEDOWN = SDLK_PAGEDOWN
|
||||
} KEYS;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
73
src/vm/vm_log.c
Normal file
73
src/vm/vm_log.c
Normal file
|
@ -0,0 +1,73 @@
|
|||
#include "vm_log.h"
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <time.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/timeb.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_EXTRA_LEAN
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
FILE *fp_log = NULL;
|
||||
const char *log_filename = "run.log";
|
||||
|
||||
const size_t LOG_BUFFER_SIZE = 8096;
|
||||
char _temp_buffer[LOG_BUFFER_SIZE];
|
||||
char _buffer[LOG_BUFFER_SIZE];
|
||||
|
||||
void log_init() {
|
||||
fp_log = fopen(log_filename, "w");
|
||||
if (!fp_log)
|
||||
{
|
||||
snprintf(_buffer, LOG_BUFFER_SIZE, "Log file \"%s\" could not be opened. Further LOG_Write() calls will only appear in STDOUT.\n", log_filename);
|
||||
printf("%s", _buffer);
|
||||
#ifdef _MSC_VER
|
||||
OutputDebugStringA(_buffer);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void log_end() {
|
||||
if (fp_log)
|
||||
fclose(fp_log);
|
||||
}
|
||||
|
||||
void log_write(const char *tag, const char *format, ...) {
|
||||
static char timestamp[15];
|
||||
|
||||
int hour;
|
||||
int minute;
|
||||
int second;
|
||||
unsigned int milliseconds;
|
||||
|
||||
struct timeb seconds;
|
||||
ftime(&seconds);
|
||||
struct tm *currentDate = localtime(&seconds.time);
|
||||
|
||||
hour = currentDate->tm_hour;
|
||||
minute = currentDate->tm_min;
|
||||
second = currentDate->tm_sec;
|
||||
milliseconds = seconds.millitm;
|
||||
|
||||
// Prepend the date/time
|
||||
snprintf(timestamp, 15, "[%02d:%02d:%02d,%03d]", hour, minute, second, milliseconds);
|
||||
|
||||
// now the user-provided format string and arguments...
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
vsnprintf(_temp_buffer, LOG_BUFFER_SIZE, format, args);
|
||||
va_end(args);
|
||||
|
||||
// write it all out
|
||||
snprintf(_buffer, LOG_BUFFER_SIZE, "%s [%s] %s", timestamp, tag, _temp_buffer);
|
||||
|
||||
if (fp_log)
|
||||
fprintf(fp_log, "%s", _buffer);
|
||||
printf("%s", _buffer);
|
||||
#ifdef _MSC_VER
|
||||
OutputDebugStringA(_buffer);
|
||||
#endif
|
||||
}
|
24
src/vm/vm_log.h
Normal file
24
src/vm/vm_log.h
Normal file
|
@ -0,0 +1,24 @@
|
|||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "vm_stdinc.h"
|
||||
|
||||
void log_init();
|
||||
void log_end();
|
||||
void log_write(const char *tag, const char *format, ...);
|
||||
|
||||
#define LOG_INFO(...) (log_write("INFO", __VA_ARGS__))
|
||||
#define LOG_WARN(...) (log_write("WARN", __VA_ARGS__))
|
||||
#define LOG_ERROR(...) (log_write("ERROR", __VA_ARGS__))
|
||||
#ifdef DEBUG
|
||||
#define LOG_DEBUG(...) (log_write("DEBUG", __VA_ARGS__))
|
||||
#else
|
||||
#define LOG_DEBUG(...)
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
18
src/vm/vm_stdinc.h
Normal file
18
src/vm/vm_stdinc.h
Normal file
|
@ -0,0 +1,18 @@
|
|||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef unsigned long ulong;
|
||||
typedef unsigned int uint;
|
||||
typedef unsigned short ushort;
|
||||
typedef unsigned char uchar;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
190
src/vm/vm_window.c
Normal file
190
src/vm/vm_window.c
Normal file
|
@ -0,0 +1,190 @@
|
|||
#include "vm_window.h"
|
||||
#include "vm_log.h"
|
||||
#include "vm_input.h"
|
||||
#include "fbgfx/surface.h"
|
||||
|
||||
static SDL_Texture*create_texture(SDL_Renderer *renderer, int width, int height) {
|
||||
return SDL_CreateTexture(renderer, SDL_PIXELFORMAT_BGR24, SDL_TEXTUREACCESS_STREAMING, width, height);
|
||||
}
|
||||
|
||||
static bool resize_texture(SDL_Renderer *renderer, SDL_Texture **texture, int width, int height) {
|
||||
if (*texture)
|
||||
SDL_DestroyTexture(*texture);
|
||||
*texture = create_texture(renderer, width, height);
|
||||
if (*texture == NULL) {
|
||||
LOG_ERROR("Failed to resize texture to %dx%d\n", width, height);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool resize_window(WINDOW *window, int width, int height) {
|
||||
if (window->fixed_viewport)
|
||||
return true;
|
||||
|
||||
LOG_INFO("Resizing window to %dx%d\n", width, height);
|
||||
window->width = width;
|
||||
window->height = height;
|
||||
bool result = resize_texture(window->renderer, &window->texture, width, height);
|
||||
if (result)
|
||||
surface_resize(window->surface, width, height);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
WINDOW * window_init(const char *title, int width, int height, int viewport_width, int viewport_height) {
|
||||
LOG_INFO("window_init(%s, %d, %d, %d, %d)\n", title, width, height, viewport_width, viewport_height);
|
||||
|
||||
WINDOW *window = malloc(sizeof(WINDOW));
|
||||
window->surface = NULL;
|
||||
|
||||
LOG_INFO("Initializing SDL.\n");
|
||||
if (SDL_Init(SDL_INIT_EVERYTHING) != 0) {
|
||||
LOG_ERROR("SDL_Init error: %s\n", SDL_GetError());
|
||||
window_destroy(window);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
window->initialized = true;
|
||||
|
||||
window->width = width;
|
||||
window->height = height;
|
||||
|
||||
LOG_INFO("Creating a %dx%d window.\n", window->width, window->height);
|
||||
window->window = SDL_CreateWindow(title,
|
||||
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
|
||||
window->width, window->height,
|
||||
SDL_WINDOW_RESIZABLE | SDL_WINDOW_SHOWN);
|
||||
if (window->window == NULL) {
|
||||
LOG_ERROR("SDL_CreateWindow error: %s\n", SDL_GetError());
|
||||
window_destroy(window);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
LOG_INFO("Creating an SDL renderer.\n");
|
||||
window->renderer = SDL_CreateRenderer(window->window, -1, 0);
|
||||
if (window->renderer == NULL) {
|
||||
LOG_ERROR("SDL_CreateRenderer error: %s\n", SDL_GetError());
|
||||
window_destroy(window);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (viewport_width != 0 && viewport_height != 0) {
|
||||
LOG_INFO("Using fixed viewport of %dx%d\n", viewport_width, viewport_height);
|
||||
window->fixed_viewport = true;
|
||||
window->viewport_width = viewport_width;
|
||||
window->viewport_height = viewport_height;
|
||||
|
||||
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "nearest");
|
||||
SDL_RenderSetLogicalSize(window->renderer, window->viewport_width, window->viewport_height);
|
||||
} else {
|
||||
LOG_INFO("Using automatically sized viewport (always match window dimensions)\n");
|
||||
window->fixed_viewport = false;
|
||||
window->viewport_width = width;
|
||||
window->viewport_height = height;
|
||||
}
|
||||
|
||||
LOG_INFO("Creating a %dx%d texture for the 2D framebuffer.\n", window->viewport_width, window->viewport_height);
|
||||
window->texture = create_texture(window->renderer, window->viewport_width, window->viewport_height);
|
||||
if (window->texture == NULL) {
|
||||
LOG_ERROR("SDL_CreateTexture error: %s\n", SDL_GetError());
|
||||
window_destroy(window);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
window->surface = surface_create(window->viewport_width, window->viewport_height, SURFACE_FORMAT_RGB, SURFACE_FLAGS_NONE);
|
||||
|
||||
return window;
|
||||
}
|
||||
|
||||
bool window_do_events(WINDOW *window) {
|
||||
bool quit = false;
|
||||
SDL_Event e;
|
||||
|
||||
while (SDL_PollEvent(&e)) {
|
||||
switch (e.type) {
|
||||
case SDL_QUIT:
|
||||
LOG_INFO("Event: SDL_QUIT\n");
|
||||
quit = true;
|
||||
break;
|
||||
|
||||
case SDL_WINDOWEVENT:
|
||||
if (e.window.event == SDL_WINDOWEVENT_RESIZED) {
|
||||
LOG_INFO("Event: SDL_WINDOWEVENT_RESIZED\n");
|
||||
if (!resize_window(window, e.window.data1, e.window.data2))
|
||||
quit = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_KEYDOWN:
|
||||
case SDL_KEYUP:
|
||||
input_on_key_event(&e);
|
||||
break;
|
||||
|
||||
case SDL_MOUSEMOTION:
|
||||
case SDL_MOUSEBUTTONDOWN:
|
||||
case SDL_MOUSEBUTTONUP:
|
||||
input_on_mouse_event(&e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return !quit;
|
||||
}
|
||||
|
||||
void window_render(WINDOW *window) {
|
||||
// HACK: there seems to be either some weird behaviour i'm just not understanding or a bug somewhere in
|
||||
// either SDL or my own code that sets up these window / surface dimensions...
|
||||
// basically, the "pass a NULL SDL_Rect to specify the entire surface region" does not seem to
|
||||
// work as expected. if I use NULL's for all those arguments below, then I get a full-width
|
||||
// but half-ish height (yes, not exactly half... seems random...) texture being displayed.
|
||||
// eeerrrrggghh! wtf!?
|
||||
|
||||
SDL_Rect src_region;
|
||||
src_region.x = 0;
|
||||
src_region.y = 0;
|
||||
src_region.w = window->surface->width;
|
||||
src_region.h = window->surface->height;
|
||||
|
||||
SDL_Rect dest_region;
|
||||
dest_region.x = 0;
|
||||
dest_region.y = 0;
|
||||
dest_region.w = window->width;
|
||||
dest_region.h = window->height;
|
||||
|
||||
SDL_UpdateTexture(window->texture, NULL, (void*)window->surface->pixels, window->surface->width * window->surface->bytes_per_pixel);
|
||||
|
||||
SDL_RenderClear(window->renderer);
|
||||
// SDL_RenderCopy(window->renderer, window->texture, &src_region, &dest_region);
|
||||
SDL_RenderCopy(window->renderer, window->texture, NULL, NULL);
|
||||
SDL_RenderPresent(window->renderer);
|
||||
}
|
||||
|
||||
void window_destroy(WINDOW *window) {
|
||||
LOG_INFO("window_destroy\n");
|
||||
|
||||
surface_destroy(window->surface);
|
||||
|
||||
if (window->texture) {
|
||||
LOG_INFO("Destroying 2D framebuffer texture.\n");
|
||||
SDL_DestroyTexture(window->texture);
|
||||
}
|
||||
|
||||
if (window->renderer) {
|
||||
LOG_INFO("Destroying SDL renderer.\n");
|
||||
SDL_DestroyRenderer(window->renderer);
|
||||
}
|
||||
|
||||
if (window->window) {
|
||||
LOG_INFO("Destroying window.\n");
|
||||
SDL_DestroyWindow(window->window);
|
||||
}
|
||||
|
||||
if (window->initialized) {
|
||||
LOG_INFO("SDL_Quit\n");
|
||||
SDL_Quit();
|
||||
}
|
||||
|
||||
free(window);
|
||||
}
|
30
src/vm/vm_window.h
Normal file
30
src/vm/vm_window.h
Normal file
|
@ -0,0 +1,30 @@
|
|||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "vm_stdinc.h"
|
||||
#include "SDL.h"
|
||||
|
||||
typedef struct {
|
||||
bool initialized;
|
||||
SDL_Window *window;
|
||||
int width;
|
||||
int height;
|
||||
SDL_Renderer *renderer;
|
||||
SDL_Texture *texture;
|
||||
bool fixed_viewport;
|
||||
int viewport_width;
|
||||
int viewport_height;
|
||||
struct SURFACE *surface;
|
||||
} WINDOW;
|
||||
|
||||
WINDOW * window_init(const char *title, int width, int height, int viewport_width, int viewport_height);
|
||||
bool window_do_events(WINDOW *window);
|
||||
void window_render(WINDOW *window);
|
||||
void window_destroy(WINDOW *window);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
Reference in a new issue