Created
July 5, 2024 22:05
-
-
Save fabiogaluppo/d2f6bfebbf05e16005b9ff4bf9460910 to your computer and use it in GitHub Desktop.
SDL PPM P3 Viewer
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Source code SDL PPM P3 Viewer por Fabio Galuppo | |
//C++ MasterClass - https://www.linkedin.com/company/cppmasterclass - https://www.youtube.com/@CPPMasterClass | |
//Fabio Galuppo - http://member.acm.org/~fabiogaluppo - [email protected] | |
//July 2024 | |
#define SDL_MAIN_HANDLED | |
#include <SDL.h> | |
#include <stdio.h> | |
#include <stdbool.h> | |
#include <stdint.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <math.h> | |
#include <limits.h> | |
#include "sdl_helpers.h" | |
#include "getopt.h" | |
struct point2d | |
{ | |
int x, y; | |
}; | |
struct rgb | |
{ | |
int r, g, b; | |
}; | |
static void set_pixel(SDL_Surface* surface, struct point2d pos, struct rgb color) | |
{ | |
*((Uint32*)surface->pixels + pos.y * surface->w + pos.x) = (color.r << 16) + (color.g << 8) + color.b; | |
} | |
struct opt_args | |
{ | |
char* input_path; | |
bool gray_mode; | |
}; | |
static struct opt_args getopt_args(int argc, char** argv) | |
{ | |
int ch; | |
bool gray_mode = false, normal_mode = false, usage = false; | |
char* input_path = NULL; | |
while ((ch = getopt(argc, argv, "ngi:")) != -1) | |
{ | |
switch ((char)ch) | |
{ | |
case 'n': | |
++optarg; | |
normal_mode = true; | |
break; | |
case 'g': | |
++optarg; | |
gray_mode = true; | |
break; | |
case 'i': | |
input_path = optarg; | |
break; | |
case '?': | |
default: | |
usage = true; | |
} | |
} | |
usage = usage || ((normal_mode && gray_mode) || input_path == NULL); | |
if (usage) | |
{ | |
const char* msg = "sdl_ppm_p3_viewer -i <input_path> [-g|-n]\n" | |
"where\n" | |
" i: input file path (required)\n" | |
" g: gray image mode (optional)\n" | |
" n: normal image mode (optional)"; | |
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_WARNING, "Usage", msg, NULL); | |
exit(EXIT_FAILURE); | |
} | |
struct opt_args args = { input_path, gray_mode }; | |
return args; | |
} | |
int main(int argc, char** argv) | |
{ | |
static const int FPS = 15; | |
static const int FRAME_RATE_MS = 1000 / FPS; | |
static const Uint32 COLOR_BLACK = 0xFF000000; | |
static const Uint32 COLOR_RED = 0xFFFF0000; | |
static const Uint32 COLOR_YELLOW = 0xFFFFFF00; | |
static const Uint32 COLOR_GREEN = 0xFF00FF00; | |
static const Uint32 COLOR_PURPLE = 0xFF800080; | |
struct opt_args args = getopt_args(argc, argv); | |
char input_path[256]; | |
strncpy(input_path, args.input_path, sizeof(input_path) / sizeof(input_path[0])); | |
FILE* ppm_p3 = fopen(input_path, "r"); | |
if (ppm_p3 == NULL) | |
{ | |
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Open file", "The fopen function has failed", NULL); | |
exit(EXIT_FAILURE); | |
} | |
char buffer[16] = {0}; | |
fgets(buffer, sizeof(buffer), ppm_p3); | |
fgets(buffer, sizeof(buffer), ppm_p3); | |
int width, height; | |
sscanf(buffer, "%d %d", &width, &height); | |
fgets(buffer, sizeof(buffer), ppm_p3); | |
int status = 0; | |
if (SDL_Init(SDL_INIT_VIDEO) < 0) | |
{ | |
status = -1; | |
goto cleanup; | |
} | |
SDL_Window* window = SDL_CreateWindow | |
( | |
"PPM P3 Viewer", | |
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, | |
width, height, SDL_WINDOW_HIDDEN | |
); | |
if (window == NULL) | |
{ | |
status = -1; | |
goto cleanup; | |
} | |
SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_SOFTWARE); | |
if (renderer == NULL) | |
{ | |
status = -1; | |
goto cleanup; | |
} | |
SDL_Surface* surface = SDL_GetWindowSurface(window); | |
if (surface == NULL) | |
{ | |
status = -1; | |
goto cleanup; | |
} | |
for (int y = 0; y < height; ++y) | |
{ | |
for (int x = 0; x < width; ++x) | |
{ | |
struct rgb color; | |
fgets(buffer, sizeof(buffer), ppm_p3); | |
sscanf(buffer, "%d %d %d", &color.r, &color.g, &color.b); | |
if (args.gray_mode) | |
{ | |
//Luminosity Method formula per component = 0.3 * R + 0.59 * G + 0.11 * B | |
int grayscale = 0.3 * color.r + 0.59 * color.g + 0.11 * color.b; | |
color.r = grayscale; | |
color.g = grayscale; | |
color.b = grayscale; | |
} | |
struct point2d pos = { x, y }; | |
set_pixel(surface, pos, color); | |
} | |
} | |
fclose(ppm_p3); | |
SDL_ShowWindow(window); | |
SDL_Event event; | |
while (true) | |
{ | |
//Update | |
Uint64 start_ticks = SDL_GetTicks64(); | |
if (SDL_PollEvent(&event) != 0) | |
{ | |
if (event.type == SDL_QUIT) | |
break; | |
if (event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_ESCAPE) | |
break; | |
} | |
int w, h; | |
SDL_GetWindowSize(window, &w, &h); | |
if (w != width || h != height) | |
{ | |
SDL_HideWindow(window); | |
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_WARNING, "SDL Window", "The window size has changed\nThis viewer is closing", NULL); | |
break; | |
} | |
//Render | |
display_flip(renderer, surface); | |
clock_tick(FRAME_RATE_MS, start_ticks); | |
} | |
cleanup: | |
//Cleanup resources | |
if (renderer) | |
SDL_DestroyRenderer(renderer); | |
if (window) | |
SDL_DestroyWindow(window); | |
SDL_Quit(); | |
return status; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Source code SDL PPM P3 Viewer por Fabio Galuppo | |
//C++ MasterClass - https://www.linkedin.com/company/cppmasterclass - https://www.youtube.com/@CPPMasterClass | |
//Fabio Galuppo - http://member.acm.org/~fabiogaluppo - [email protected] | |
//July 2024 | |
//Adapted from getopt from FreeBSD.org | |
//Extract declaration from: https://svnweb.freebsd.org/base/stable/9/include/unistd.h?view=markup | |
//Extract definition from: https://svnweb.freebsd.org/base/stable/9/lib/libc/stdlib/getopt.c?view=markup | |
/* | |
* Copyright (c) 1987, 1993, 1994 | |
* The Regents of the University of California. All rights reserved. | |
* | |
* Redistribution and use in source and binary forms, with or without | |
* modification, are permitted provided that the following conditions | |
* are met: | |
* 1. Redistributions of source code must retain the above copyright | |
* notice, this list of conditions and the following disclaimer. | |
* 2. 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. | |
* 4. Neither the name of the University nor the names of its contributors | |
* may be used to endorse or promote products derived from this software | |
* without specific prior written permission. | |
* | |
* THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. | |
*/ | |
#ifndef GETOPT_H | |
#define GETOPT_H | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
static int opterr = 1; | |
static int optind = 1; | |
static int optopt; | |
static int optreset; | |
static char *optarg; | |
#define BADCH (int)'?' | |
#define BADARG (int)':' | |
#define EMSG "" | |
/* | |
* getopt -- | |
* Parse argc/argv argument vector. | |
*/ | |
static int getopt(int nargc, char* const nargv[], const char* ostr) | |
{ | |
static char* place = EMSG; /* option letter processing */ | |
char* oli; /* option letter list index */ | |
if (optreset || *place == 0) { /* update scanning pointer */ | |
optreset = 0; | |
place = nargv[optind]; | |
if (optind >= nargc || *place++ != '-') { | |
/* Argument is absent or is not an option */ | |
place = EMSG; | |
return (-1); | |
} | |
optopt = *place++; | |
if (optopt == '-' && *place == 0) { | |
++optind; | |
place = EMSG; | |
return (-1); | |
} | |
if (optopt == 0) { | |
/* Solitary '-', treat as a '-' option | |
if the program (eg su) is looking for it. */ | |
place = EMSG; | |
if (strchr(ostr, '-') == NULL) | |
return (-1); | |
optopt = '-'; | |
} | |
} | |
else | |
optopt = *place++; | |
/* See if option letter is one the caller wanted... */ | |
if (optopt == ':' || (oli = (char*)(strchr(ostr, optopt))) == NULL) { | |
if (*place == 0) | |
++optind; | |
if (opterr && *ostr != ':') | |
(void) fprintf(stderr, | |
"%s: illegal option -- %c\n", "getopt", | |
optopt); | |
return (BADCH); | |
} | |
/* Does this option need an argument? */ | |
if (oli[1] != ':') { | |
/* don't need argument */ | |
optarg = NULL; | |
if (*place == 0) | |
++optind; | |
} | |
else { | |
/* Option-argument is either the rest of this argument or the | |
entire next argument. */ | |
if (*place) | |
optarg = place; | |
else if (nargc > ++optind) | |
optarg = nargv[optind]; | |
else { | |
/* option-argument absent */ | |
place = EMSG; | |
if (*ostr == ':') | |
return (BADARG); | |
if (opterr) | |
(void) fprintf(stderr, | |
"%s: option requires an argument -- %c\n", | |
"getopt", optopt); | |
return (BADCH); | |
} | |
place = EMSG; | |
++optind; | |
} | |
return (optopt); /* return option letter */ | |
} | |
#endif /* GETOPT_H */ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Source code SDL PPM P3 Viewer por Fabio Galuppo | |
//C++ MasterClass - https://www.linkedin.com/company/cppmasterclass - https://www.youtube.com/@CPPMasterClass | |
//Fabio Galuppo - http://member.acm.org/~fabiogaluppo - [email protected] | |
//July 2024 | |
#ifndef SDL_HELPERS_H | |
#define SDL_HELPERS_H | |
#include <SDL.h> | |
static void display_flip(SDL_Renderer* renderer, SDL_Surface* surface) | |
{ | |
SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, surface); | |
SDL_RenderCopy(renderer, texture, NULL, NULL); | |
SDL_DestroyTexture(texture); | |
SDL_RenderPresent(renderer); | |
} | |
static void clock_tick(int frame_rate_ms, Uint64 start_ticks) | |
{ | |
Uint64 elapsed_ms = SDL_GetTicks64() - start_ticks; | |
if (elapsed_ms < frame_rate_ms) | |
SDL_Delay(frame_rate_ms - elapsed_ms); | |
} | |
static void screen_fill(SDL_Renderer* renderer, SDL_Surface* surface, Uint32 color) | |
{ | |
SDL_RenderClear(renderer); | |
SDL_FillRect(surface, NULL, color); | |
} | |
#endif /* SDL_HELPERS_H */ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment