CONTRIBUTING.md (9121B)
1 # Contributing to Blend2D 2 3 The following is a set of guidelines for contributing to Blend2D and other repositories hosted in the [Blend2D Organization](https://github.com/blend2d) on GitHub. 4 5 6 ## Table of Contents 7 8 * [Asking Questions](#asking-questions) 9 * [Reporting Bugs](#reporting-bugs) 10 * [Website Enhancement](#website-enhancement) 11 * [Coding Style](#coding-style) 12 13 14 ## Asking Questions 15 16 We prefer GitHub issues to be used for reporting bugs and similar problems. So please consider using our [Gitter Channel](https://gitter.im/blend2d/blend2d) to ask questions. It is active and may provide you quick help. 17 18 * **Generic questions** should be asked on [Gitter](https://gitter.im/blend2d/blend2d) if possible 19 * **Feature Suggestions** may be discussed on [Gitter](https://gitter.im/blend2d/blend2d), we will add features that match our future plans to the roadmap 20 * For **C/C++ API Questions** use either [Gitter](https://gitter.im/blend2d/blend2d) or open a new issue on GitHub (see pinned issues first, we prefer to write a documentation regarding API functions that are not clear) 21 * Read our [FAQ](https://blend2d.com/about.html#FAQ) as well, we would gladly improve it if you found something missing or can improve the existing content 22 23 24 ## Reporting Bugs 25 26 A good bug report should contain the following information: 27 28 * Brief and detailed bug description 29 * Steps to reproduce the bug (isolated test-case is preferred but not required) 30 * Build and system information (use `<blend2d-debug.h>` to obtain it, see below) 31 32 To make the reporting bugs as easy as possible Blend2D provides a header file called `<blend2d-debug.h>`. It contains functions for C and C++ API users that can be used to query useful information from Blend2D runtime and to dump the content of some Blend2D objects. 33 34 Use the snippet below in your own code and include its output in your bug report. It will help us to identify the issue quicker. Debug API is provided for both C and C++ users. 35 36 ```c 37 #include <blend2d.h> 38 #include <blend2d-debug.h> 39 40 int main(int argc, char* argv[]) { 41 // This will query and dump Blend2D build and system information. We 42 // need this information to make the initial guess of where the problem 43 // could be. Some JIT and SIMD bugs can depend on this information. 44 bl_debug_runtime(); 45 46 // Now dump everything you have by using the following functions. 47 bl_debug_array(&arr); // Dumps the content of BLArray 48 bl_debug_context(&ctx); // Dumps the state of BLContext 49 bl_debug_matrix2d(&mat); // Dumps the content of BLMatrix2D 50 bl_debug_image(&img); // Dumps the content of BLImage (without pixels) 51 bl_debug_path(&path); // Dumps the content of BLPath 52 bl_debug_stroke_options(&opt); // Dumps the content of BLStrokeOptions 53 } 54 ``` 55 56 When reporting bugs related to incorrect renderings it is important to also include the rendered image together with the bug report as it will help us to understand the problem better. 57 58 59 ## Website Enhancement 60 61 If you found a typo or have suggestions about the content on Blend2D website you can either contact us directly via email or discuss it on Gitter. Please don't open GitHub issues that are related to the website. 62 63 64 ## Coding Style 65 66 If you plan to contribute to Blend2D make sure you follow the guidelines described in this section. 67 68 69 ### Source Files 70 71 * Source files contain a header, which contains a project name `[Blend2D]`, description, and license that refers to `LICENSE.md` file (when you are creating a new file just copy the header from any other file) 72 * Source files that include other Blend2D headers use relative paths starting with `./` or `../` 73 * Header guard format is `BLEND2D_FILE_H` for root headers and `BLEND2D_PATH_FILE_H` for headers in a subdirectory 74 * Source files (.cpp) must first include `api-build_p.h` and then other headers 75 * Source files that use compiler intrinsics (SSE, AVX, NEON) must have the following suffix: 76 * X86/X64 77 * `*_sse2.cpp` - SSE2 78 * `*_sse3.cpp` - SSE3 79 * `*_ssse3.cpp` - SSSE3 80 * `*_sse4_1.cpp` - SSE4.1 81 * `*_sse4_2.cpp` - SSE4.2 + POPCNT + PCLMULQDQ 82 * `*_avx.cpp` - AVX 83 * `*_avx2.cpp` - AVX2 + POPCNT + BMI + BMI2 84 * `*_avx2fma.cpp` - AVX2 + POPCNT + BMI + BMI2 + FMA 85 * `*_avx512.cpp` - AVX512 + POPCNT + BMI + BMI2 86 * ARM/AArch64 87 * `*_asimd.cpp` - ASIMD 88 * `*_asimd_crypto.cpp` - ASIMD + CRYPTO 89 90 91 ### API Design 92 93 #### Namespaces 94 95 * Do not use namespaces in public API if possible 96 97 Namespaces are used mostly internally. There are few exceptions like `BLRuntime` and `BLFileSystem` though. 98 99 100 #### C vs C++ API 101 102 * Public headers must place C++ specific functionality between `#ifdef __cplusplus` and `#endif` 103 * Private headers use C++ by default (no C compatibility) 104 105 Since Blend2D exports only C API and can be compiled without linking to standard C++ library it cannot use functions that depend on it. Blend2D at the moment only uses atomics and some other utilities like `std::numeric_limits<>` that don't require linking to C++ standard library and it must stay like that. 106 107 108 #### Exception Safety and Error Handling 109 110 * Do not use exceptions or RTTI 111 112 Exceptions and RTTI are never used by Blend2D. In general every public function is marked `noexcept` (or `BL_NOEXCEPT_C` for C API) that should guarantee that the compiler won't emit unwind tables even when C++ exceptions are enabled at build time. Blend2D users can use exceptions in their code, but Blend2D would never throw them. 113 114 * Use error codes for error handling and propagation 115 * Use `BLResult` as a return value in functions that can fail 116 117 Every function that can fail must return `BLResult`. Use `BL_PROPAGATE(<expression>)` to return on failure, but be careful and check how it's used first. 118 119 120 #### Default Constructed State 121 122 * Always offer a defined default constructed state. 123 124 Default constructed state guarantees that no dynamic memory is allocated when creating a default constructed instance. Only initialization like `create()` or `begin()` and using setters would turn default initialized instance into an instance that could use dynamically allocated memory. Use `.reset()` to set the state of any class back to its default constructed state and to release all resources it holds. 125 126 127 ### Coding Conventions 128 129 If you are planning to contribute to Blend2D, please read our coding conventions carefully. 130 131 * Indent by 2 spaces and never use TABs 132 * Class and struct names use *Upper Camel Case* convention and always start with `BL` prefix (`BLClassName`) 133 * Global functions and variables use *snake_case* convention and always start with `bl_` prefix (`bl_function_name`) 134 * Structs are used for everything that doesn't have initialization and must be compatible with C API 135 * Structs can have utility member functions available in C++ mode like `.reset()`, but cannot have constructors, destructors, or assignment operators 136 * Classes are only used for implementing C++ API that is based on C API 137 * Pointer `*` or reference `&` is part of the type, for example `BLImage* image` and not ~~`BLImage *image`~~ 138 * Namespaces are not indented, use the following in public headers: 139 140 ```c++ 141 namespace BLSomeNamespace { 142 [...] 143 } // {BLSomeNamespace} 144 ``` 145 146 * No line between a statement and opening bracket `{`: 147 148 ```c++ 149 class BLSomeClass { 150 void some_function() { 151 for (size_t i = 0; i < 10; i++) { 152 if (i & 0x1) { 153 [...] 154 } 155 } 156 } 157 }; 158 ``` 159 160 * No spaces in a conditional expression, function declaration, and call: 161 162 ```c++ 163 // Right: 164 if (x) { 165 some_function(x); 166 } 167 168 /* WRONG: 169 if( x ) 170 some_function ( x ); 171 */ 172 ``` 173 174 * If obvious, omit `== nullptr` and `!= nullptr` from conditional expressions, except asserts: 175 176 ```c++ 177 void* ptr; 178 179 // Right: 180 BL_ASSERT(ptr != nullptr); 181 if (ptr) { 182 [...] 183 } 184 185 /* WRONG: 186 BL_ASSERT(ptr); 187 if (ptr != nullptr) { 188 [...] 189 } 190 */ 191 ``` 192 193 * Blend2D transitions to always use blocks in branches (**if**/**else**): 194 195 ```c++ 196 // Right: 197 if (something) { 198 do_something() 199 } 200 201 if (x) { 202 first(); 203 } 204 else { 205 second(); 206 } 207 208 /* WRONG: 209 if (something) 210 do_something() 211 212 if (x) { 213 first(); 214 } 215 else 216 second(); 217 */ 218 ``` 219 220 * There is a space between a **switch** statement and its expression; **case** statement and its content are indented: 221 222 ```c++ 223 switch (expression) { 224 case 0: 225 do_something(); 226 break; 227 228 // Case that requires a block. 229 case 1: { 230 int var = something; 231 do_something_else_with_var(var); 232 break; 233 } 234 235 // If the default should never be reached, mark it so: 236 default: 237 BL_NOT_REACHED(); 238 } 239 ``` 240 241 * Sometimes **case** and its content can be inlined: 242 243 ```c++ 244 switch (condition) { 245 case 0: do_something(); break; 246 case 1: do_something_else(); break; 247 } 248 ``` 249 250 * Public enum values are *UPPER_CASED* and use `BL_` prefix 251 * Public enums usually end with `_MAX_VALUE`, which should be separated by an empty line 252 * Public enums are always `uint32_t`, use `BL_DEFINE_ENUM` to make sure they are properly defined in both C and C++ modes: 253 254 ```c++ 255 BL_DEFINE_ENUM(BLSomeEnum) { 256 BL_SOME_ENUM_A = 0, 257 BL_SOME_ENUM_B = 1, 258 259 BL_SOME_ENUM_MAX_VALUE = 1 260 }; 261 ```