blending.c 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. /*
  2. * Blending combines a source color 'src',
  3. * with the pixels already on the screen 'dst',
  4. * to produce transparency and other visual effects.
  5. *
  6. * formula: dst := (a * dst) op (b * src)
  7. *
  8. * where:
  9. * dst: existed pixel on the screen.
  10. * src: new pixel.
  11. * a: dst factor.
  12. * b: src factor.
  13. * op: blend operation (usually addition).
  14. *
  15. * In graphics programming, color and alpha are usually blended separately:
  16. * dstRGB := (a * srcRGB) op (b * dstRGB)
  17. * dstA := (c * srcA) op (d * dstA)
  18. *
  19. * This example uses SDL_SetTextureBlendMode() to apply blending to textures,
  20. * and uses SDL_ComposeCustomBlendMode() to create a custom blend mode.
  21. *
  22. * You can also use SDL_SetRenderDrawBlendMode() to apply blending to the
  23. * entire renderer, but it only affects filled rects and lines, not textures.
  24. *
  25. * This code is public domain. Feel free to use it for any purpose!
  26. */
  27. #define SDL_MAIN_USE_CALLBACKS 1
  28. #include <SDL3/SDL.h>
  29. #include <SDL3/SDL_main.h>
  30. #define WINDOW_WIDTH 640
  31. #define WINDOW_HEIGHT 480
  32. /* UI Constants */
  33. #define ROWS 2
  34. #define COLS 3
  35. #define GRID_SIZE ((WINDOW_WIDTH - 1) / 18.0f)
  36. #define PANEL_SIZE (GRID_SIZE * 4)
  37. #define ROW_OFFSET ((WINDOW_HEIGHT - ROWS * PANEL_SIZE) / 4)
  38. #define COL_OFFSET (GRID_SIZE * COLS)
  39. #define RECT_SIZE 50.0f
  40. #define RED_OFFSET (GRID_SIZE)
  41. #define GREEN_OFFSET (RECT_SIZE / 3 + GRID_SIZE)
  42. #define BLUE_OFFSET (RECT_SIZE * 2 / 3 + GRID_SIZE)
  43. static SDL_FRect panels[ROWS*COLS];
  44. static SDL_Window *window = NULL;
  45. static SDL_Renderer *renderer = NULL;
  46. static SDL_Texture *red_rect_texture = NULL;
  47. static SDL_Texture *green_rect_texture = NULL;
  48. static SDL_Texture *blue_rect_texture = NULL;
  49. static Uint8 alpha = 255;
  50. static SDL_BlendMode blend_modes[] = {
  51. /*The default no blending: dstRGB := srcRGB
  52. dstA := srcA */
  53. SDL_BLENDMODE_NONE,
  54. /* Alpha blending: dstRGB := srcA * srcRGB + (1 - srcA) * dstRGB
  55. dstA := srcA + (1 - srcA) * dstA */
  56. SDL_BLENDMODE_BLEND,
  57. /* Additive blending: dstRGB := srcRGB + dstRGB
  58. dstA := srcA + dstA */
  59. SDL_BLENDMODE_ADD,
  60. /* Modulate blending: dstRGB := srcRGB * dstRGB
  61. dstA := dstA */
  62. SDL_BLENDMODE_MOD,
  63. /* Multiply blending: dstRGB := srcRGB * dstRGB + (1 - srcA) * dstRGB
  64. dstA := dstA */
  65. SDL_BLENDMODE_MUL,
  66. /* Our custom blending 'Screen Blending': dstRGB := 1 - (1 - dstRGB) * (1 - srcRGB)
  67. dstA := dstA */
  68. 0
  69. };
  70. static const char *blend_mode_names[] = { "NONE", "BLEND", "ADD", "MOD", "MUL", "SCREEN \"CUSTOM\"" };
  71. SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
  72. {
  73. SDL_Surface *surface = NULL;
  74. SDL_SetAppMetadata("Example Blending", "1.0", "com.example.blending");
  75. if (!SDL_Init(SDL_INIT_VIDEO)) {
  76. SDL_Log("Couldn't initialize SDL: %s", SDL_GetError());
  77. return SDL_APP_FAILURE;
  78. }
  79. if (!SDL_CreateWindowAndRenderer("examples/renderer/blending", WINDOW_WIDTH, WINDOW_HEIGHT, SDL_WINDOW_RESIZABLE, &window, &renderer)) {
  80. SDL_Log("Couldn't create window/renderer: %s", SDL_GetError());
  81. return SDL_APP_FAILURE;
  82. }
  83. SDL_SetRenderLogicalPresentation(renderer, WINDOW_WIDTH, WINDOW_HEIGHT, SDL_LOGICAL_PRESENTATION_LETTERBOX);
  84. int row = 0;
  85. int col = 0;
  86. for (row = 0; row < ROWS; row++)
  87. {
  88. for (col = 0; col < COLS; col++)
  89. {
  90. panels[col + row*COLS] = (SDL_FRect){ col*PANEL_SIZE + col*COL_OFFSET, row*PANEL_SIZE + (row+1)*ROW_OFFSET, PANEL_SIZE, PANEL_SIZE };
  91. }
  92. }
  93. /* Create 'screen blend' mode */
  94. blend_modes[ROWS*COLS - 1] = SDL_ComposeCustomBlendMode(
  95. SDL_BLENDFACTOR_ONE_MINUS_DST_COLOR, /* srcRGB factor := (1 - dstRGB) */
  96. SDL_BLENDFACTOR_ONE, /* dstRGB factor := 1 */
  97. SDL_BLENDOPERATION_ADD, /* RGB operation := + */
  98. SDL_BLENDFACTOR_ZERO, /* srcA factor := 0 */
  99. SDL_BLENDFACTOR_ONE, /* dstA factor := dstA */
  100. SDL_BLENDOPERATION_ADD /* A operation := + */
  101. );
  102. surface = SDL_CreateSurface((int)RECT_SIZE, (int)RECT_SIZE, SDL_PIXELFORMAT_RGBA8888);
  103. if (!surface) {
  104. SDL_Log("Couldn't create surface: %s", SDL_GetError());
  105. return SDL_APP_FAILURE;
  106. }
  107. SDL_FillSurfaceRect(surface, NULL, 0xFF0000FF); /* Red */
  108. red_rect_texture = SDL_CreateTextureFromSurface(renderer, surface);
  109. if (!red_rect_texture) {
  110. SDL_Log("Couldn't create texture: %s", SDL_GetError());
  111. return SDL_APP_FAILURE;
  112. }
  113. SDL_FillSurfaceRect(surface, NULL, 0x00FF00FF); /* Green */
  114. green_rect_texture = SDL_CreateTextureFromSurface(renderer, surface);
  115. if (!green_rect_texture) {
  116. SDL_Log("Couldn't create texture: %s", SDL_GetError());
  117. return SDL_APP_FAILURE;
  118. }
  119. SDL_FillSurfaceRect(surface, NULL, 0x0000FFFF); /* Blue */
  120. blue_rect_texture = SDL_CreateTextureFromSurface(renderer, surface);
  121. if (!blue_rect_texture) {
  122. SDL_Log("Couldn't create texture: %s", SDL_GetError());
  123. return SDL_APP_FAILURE;
  124. }
  125. SDL_DestroySurface(surface);
  126. return SDL_APP_CONTINUE;
  127. }
  128. SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event)
  129. {
  130. if (event->type == SDL_EVENT_QUIT) {
  131. return SDL_APP_SUCCESS;
  132. }
  133. if (event->type == SDL_EVENT_KEY_DOWN) {
  134. /* UP arrow increase alpha */
  135. if (event->key.key == SDLK_UP && alpha <= 255-8) alpha += 8;
  136. /* DOWN arrow decrease alpha */
  137. if (event->key.key == SDLK_DOWN && alpha >= 8) alpha -= 8;
  138. }
  139. return SDL_APP_CONTINUE;
  140. }
  141. SDL_AppResult SDL_AppIterate(void *appstate)
  142. {
  143. SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE);
  144. SDL_RenderClear(renderer);
  145. int i = 0;
  146. float x;
  147. float y;
  148. /* Render checkerboard panels */
  149. for (i = 0; i < ROWS*COLS; i++)
  150. {
  151. /* Loop through the panel pixels */
  152. for (y = panels[i].y; y < PANEL_SIZE + panels[i].y; y += GRID_SIZE)
  153. {
  154. for (x = panels[i].x; x < PANEL_SIZE + panels[i].x; x += GRID_SIZE)
  155. {
  156. SDL_FRect grid = { x, y, GRID_SIZE, GRID_SIZE };
  157. bool dark = (int)(x/GRID_SIZE + y/GRID_SIZE) % 2;
  158. if (dark) SDL_SetRenderDrawColor(renderer, 70, 70, 70, 255); /* Darker color */
  159. else SDL_SetRenderDrawColor(renderer, 110, 110, 110, 255); /* Lighter color */
  160. SDL_RenderFillRect(renderer, &grid);
  161. }
  162. }
  163. /* Label the blend mode */
  164. SDL_SetRenderDrawColor(renderer, 255, 255, 255, SDL_ALPHA_OPAQUE);
  165. SDL_RenderDebugText(renderer, panels[i].x, panels[i].y - 15, blend_mode_names[i]);
  166. }
  167. /* Render panels */
  168. SDL_RenderRects(renderer, panels, ROWS*COLS);
  169. /* Render UI text */
  170. SDL_RenderDebugText(renderer, (WINDOW_WIDTH - 176) / 2, WINDOW_HEIGHT - 30, "UP/DOWN: CHANGE ALPHA");
  171. SDL_RenderDebugTextFormat(renderer, (WINDOW_WIDTH - 80) / 2, WINDOW_HEIGHT - 20, "ALPHA: %d", alpha);
  172. /* Update textures alpha mod */
  173. SDL_SetTextureAlphaMod(red_rect_texture, alpha);
  174. SDL_SetTextureAlphaMod(green_rect_texture, alpha);
  175. SDL_SetTextureAlphaMod(blue_rect_texture, alpha);
  176. /* Render panels */
  177. for (i = 0; i < ROWS*COLS; i++) {
  178. /* Update rects destination */
  179. SDL_FRect red_dst = { panels[i].x + RED_OFFSET, panels[i].y + RED_OFFSET, RECT_SIZE, RECT_SIZE };
  180. SDL_FRect green_dst = { panels[i].x + GREEN_OFFSET, panels[i].y + GREEN_OFFSET, RECT_SIZE, RECT_SIZE };
  181. SDL_FRect blue_dst = { panels[i].x + BLUE_OFFSET, panels[i].y + BLUE_OFFSET, RECT_SIZE, RECT_SIZE };
  182. /* Apply the current blend mode */
  183. const bool supported = SDL_SetTextureBlendMode(red_rect_texture, blend_modes[i]); /* just make sure the renderer supports this blend mode */
  184. SDL_SetTextureBlendMode(green_rect_texture, blend_modes[i]);
  185. SDL_SetTextureBlendMode(blue_rect_texture, blend_modes[i]);
  186. /* Render textures */
  187. SDL_RenderTexture(renderer, red_rect_texture, NULL, &red_dst);
  188. SDL_RenderTexture(renderer, green_rect_texture, NULL, &green_dst);
  189. SDL_RenderTexture(renderer, blue_rect_texture, NULL, &blue_dst);
  190. /* Not all renderers support all blend modes. The renderer will try to pick something close in this case,
  191. but it should be noted that the results might be unexpected, so we add "[UNSUPPORTED]" to this panel. */
  192. if (!supported) {
  193. const float textwidth = 104.0f;
  194. const SDL_FRect dst = { panels[i].x + ((panels[i].w - textwidth) / 2.0f), panels[i].y + (panels[i].h - 8), textwidth, 8 };
  195. SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE);
  196. SDL_RenderFillRect(renderer, &dst);
  197. SDL_SetRenderDrawColor(renderer, 255, 255, 255, SDL_ALPHA_OPAQUE);
  198. SDL_RenderDebugText(renderer, dst.x, dst.y, "[UNSUPPORTED]");
  199. }
  200. }
  201. SDL_RenderPresent(renderer);
  202. return SDL_APP_CONTINUE;
  203. }
  204. void SDL_AppQuit(void *appstate, SDL_AppResult result)
  205. {
  206. SDL_DestroyTexture(red_rect_texture);
  207. SDL_DestroyTexture(green_rect_texture);
  208. SDL_DestroyTexture(blue_rect_texture);
  209. }