Advertisement
Kitomas

wip kv3 encoder

Oct 25th, 2024
36
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 16.37 KB | None | 0 0
  1. #define TEXT_ENABLE_MACROS_USING_THIS _text
  2. #include <Text.hpp>
  3. #include <memory.hpp>
  4. #include <fileio.hpp>
  5.  
  6. #include <opencv2/opencv.hpp>
  7.  
  8. #include <windows.h>
  9. #include <commdlg.h> //for the 'open file' dialogue
  10.  
  11. #include <string.h>
  12.  
  13. #define FILTER_STR "\
  14. *.* (All Files)\0*.*\0\
  15. *.png (Portable Network Graphics Image Files)\0*.png\0\
  16. *.jpg (JPEG Image Files)\0*.jpg\0\
  17. *.bmp (Windows Bitmap Image Files)\0*.bmp\0\
  18. *.mp4 (MPEG-4 Video Files)\0*.mp4\0\
  19. *.mov (QuickTime Movie Video Files)\0*.mov\0\
  20. *.mkv (Matroska Video Files)\0*.mkv\0\
  21. *.avi (Audio Video Interleave Video Files)\0*.avi\0\
  22. "
  23.  
  24. //the resolution of the largest monitor cluster is 164x81 chars,
  25.  //and with semigraphics chars of 2x3, that makes for 328x243 (approx. 4:3).
  26.  //so if the frame width and height is exactly 328x243, resizing is redundant
  27. #define TGT_X (328) //(TGT, short for target)
  28. #define TGT_Y (243)
  29. #define TGT_LEN (TGT_X*TGT_Y)
  30.  
  31. #ifndef _DEBUG
  32. //#define _DEBUG //for debugging the debug flag itself lol
  33. #endif
  34.  
  35. char _boolstr[2][6] = { "false", "true\0" };
  36. #define boolstr(boolvalue) ((boolvalue)?_boolstr[1]:_boolstr[0])
  37.  
  38.  
  39. bool shouldDither = true;
  40. bool dontCullFrames = false;
  41.  
  42.  
  43. bool paused = false;
  44. bool aborted = false;
  45.  
  46.  
  47.  
  48.  
  49.  
  50. #define KV3_MAGIC 0x0033564B
  51.  
  52. struct FileHeader {
  53.   Uint32 magic;      // = 0x0033564B = "KV3\0"
  54.   Uint16 headerSize; // = sizeof(FileHeader)
  55.  
  56.   Uint32 videoSize;
  57.   Uint32 AudioSize;
  58.  
  59.   Uint32 w, h; //width and height of video frame
  60. };
  61.  
  62.  
  63.  
  64.  
  65.  
  66. //
  67.  
  68.  
  69.  
  70.  
  71.  
  72. int _main(int argc, char** argv); //prototype
  73.  
  74. int main(int argc, char** argv){
  75.   int returnStatus = -1;
  76.  
  77.   try {
  78.     if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO)<0) throw SDL_GetError();
  79.     returnStatus = _main(argc, argv);
  80.  
  81.   } catch(const char* errorText){
  82.     Log("Error = \"%s\"", errorText);
  83.     MessageBeep(MB_ICONERROR);
  84.     SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "THE PROGRAM CRASHED!", errorText, nullptr);
  85.  
  86.   }
  87.  
  88.   Log("# OF ALLOCATIONS = %u (this should always be 0)", (unsigned)memory::getNumAllocations());
  89.   SDL_Quit(); //it's safe to call this even if SDL_Init fails
  90.  
  91.   return returnStatus;
  92. }
  93.  
  94.  
  95.  
  96.  
  97.  
  98. //
  99.  
  100.  
  101.  
  102.  
  103.  
  104. #define USE_SERPENTINE 1
  105.  
  106. void dither_formatted_mat(cv::Mat& img){
  107.   if(img.cols*img.rows != TGT_LEN) throw "img is of an incorrect size";
  108.   if(img.type() != CV_8U) throw "img is not of type unsigned char";
  109.  
  110.   float pixel_old, pixel_new, pixel_err;
  111.  
  112.  
  113.   if(!shouldDither){ //use simple threshholding instead of normal dither
  114.     Uint8* pixels = img.ptr<Uint8>();
  115.     for(int i=0; i<TGT_LEN; ++i) pixels[i] = (pixels[i] >= 128) ? 255: 0;
  116.  
  117.   } else {
  118.     //floyd-steinberg dithering
  119.     img.convertTo(img, CV_32F, 1.0/255);
  120.     float* pixels = img.ptr<float>();
  121.  
  122.  
  123.     for(int y=0; y<TGT_Y; ++y){
  124.       #if USE_SERPENTINE == 1
  125.         if((y&1)){ //left-to-right
  126.           for(int x=0; x<TGT_X; ++x){
  127.             pixel_old = pixels[x];
  128.             pixel_new = (pixel_old >= 0.5f) ? 1.0f : 0.0f;
  129.             pixels[x] = pixel_new;
  130.  
  131.             pixel_err = pixel_old - pixel_new;
  132.  
  133.             if(x<(TGT_X-1)) pixels[x+1] += pixel_err*(7.0f/16);
  134.             if(y<(TGT_Y-1)){
  135.               if(x>=1       ){ pixels[x+TGT_X-1] += pixel_err*(3.0f/16); }
  136.                              { pixels[x+TGT_X  ] += pixel_err*(5.0f/16); }
  137.               if(x<(TGT_X-1)){ pixels[x+TGT_X+1] += pixel_err*(1.0f/16); }
  138.             }
  139.  
  140.           }
  141.  
  142.  
  143.         } else { //right-to-left
  144.           for(int x=TGT_X-1; x>=0; --x){
  145.             pixel_old = pixels[x];
  146.             pixel_new = (pixel_old >= 0.5f) ? 1.0f : 0.0f;
  147.             pixels[x] = pixel_new;
  148.  
  149.             pixel_err = pixel_old - pixel_new;
  150.  
  151.             if(x>=1) pixels[x-1] += pixel_err*(7.0f/16);
  152.             if(y<(TGT_Y-1)){
  153.               if(x<(TGT_X-1)){ pixels[x+TGT_X+1] += pixel_err*(3.0f/16); }
  154.                              { pixels[x+TGT_X  ] += pixel_err*(5.0f/16); }
  155.               if(x>=1       ){ pixels[x+TGT_X-1] += pixel_err*(1.0f/16); }
  156.             }
  157.  
  158.           }
  159.  
  160.  
  161.         }
  162.  
  163.       #else
  164.         for(int x=0; x<TGT_X; ++x){
  165.           pixel_old = pixels[x];
  166.           pixel_new = (pixel_old >= 0.5f) ? 1.0f : 0.0f;
  167.           pixels[x] = pixel_new;
  168.  
  169.           pixel_err = pixel_old - pixel_new;
  170.  
  171.           if(x<(TGT_X-1)) pixels[x+1] += pixel_err*(7.0f/16);
  172.           if(y<(TGT_Y-1)){
  173.             if(x>=1       ){ pixels[x+TGT_X-1] += pixel_err*(3.0f/16); }
  174.                            { pixels[x+TGT_X  ] += pixel_err*(5.0f/16); }
  175.             if(x<(TGT_X-1)){ pixels[x+TGT_X+1] += pixel_err*(1.0f/16); }
  176.           }
  177.  
  178.         }
  179.  
  180.       #endif
  181.  
  182.       pixels += TGT_X;
  183.  
  184.     }
  185.  
  186.  
  187.     img.convertTo(img, CV_8U, 255.0);
  188.  
  189.   }
  190.  
  191. }
  192.  
  193.  
  194.  
  195.  
  196.  
  197. static inline Uint8 encode_char(Uint8* pixel_row, int pixel_column){
  198.   //>>6 so it will return to being 0x80 after an equal number of left shifts
  199.   Uint8 chr = 0x80>>6;
  200.  
  201.   chr = (chr<<1) | (pixel_row[pixel_column + 1 + TGT_X*2] != 0);
  202.   chr = (chr<<1) | (pixel_row[pixel_column + 0 + TGT_X*2] != 0);
  203.   chr = (chr<<1) | (pixel_row[pixel_column + 1 + TGT_X*1] != 0);
  204.   chr = (chr<<1) | (pixel_row[pixel_column + 0 + TGT_X*1] != 0);
  205.   chr = (chr<<1) | (pixel_row[pixel_column + 1 + TGT_X*0] != 0);
  206.   chr = (chr<<1) | (pixel_row[pixel_column + 0 + TGT_X*0] != 0);
  207.  
  208.   return chr;
  209.  
  210. }
  211.  
  212.  
  213.  
  214. void write_image_data(cv::Mat& img, File& file){
  215.   Uint8* pixels = img.ptr<Uint8>();
  216.   if(img.cols*img.rows != TGT_LEN) throw "img is of an incorrect size";
  217.   if(img.type() != CV_8U) throw "img is not of type unsigned char";
  218.   if(file.isClosed()) throw "file has already been closed";
  219.  
  220.   Uint8 row[TGT_X/2];
  221.  
  222.  
  223.   for(int y=0; y<TGT_Y; y+=3){
  224.     for(int x=0; x<TGT_X; x+=2)
  225.       row[x>>1] = encode_char(pixels, x);
  226.  
  227.     file.write(row, sizeof(row));
  228.     pixels += TGT_X*3;
  229.  
  230.   }
  231.  
  232. }
  233.  
  234.  
  235.  
  236.  
  237.  
  238. static inline bool is_image_file(const char* filePath){
  239.   try {        return !cv::imread(filePath).empty(); }
  240.   catch(...) { return false;                         }
  241. }
  242.  
  243.  
  244.  
  245.  
  246.  
  247. //returns position of '.', or position of
  248.  //original null terminator if no dot was found
  249. static inline int strip_file_extension(char* filePath){
  250.   char* end = filePath + strnlen(filePath, MAX_PATH);
  251.   char* cur = end;
  252.  
  253.   while(cur > filePath  &&  *cur != '.') --cur;
  254.  
  255.   if(cur > filePath) *cur = '\0';
  256.   else                cur = end;
  257.  
  258.   return (int)(cur-filePath);
  259.  
  260. }
  261.  
  262.  
  263.  
  264.  
  265.  
  266. //unit should be between 0.0f and 1.0f
  267. static inline SDL_Color unit_to_ryg(float unit){
  268.   unit = CLAMP(unit*2.0f, 0.0f, 2.0f);
  269.   unsigned char red = 255,  green = 255;
  270.  
  271.   #define RYG_RND(_v) (  (unsigned char)( (_v)+0.5f )  )
  272.   if(unit < 1.0f) green = RYG_RND(255.0f*(     unit));
  273.   else            red   = RYG_RND(255.0f*(2.0f-unit));
  274.   #undef RYG_RND
  275.  
  276.   SDL_Color color;
  277.   color.r = red;
  278.   color.g = green;
  279.   color.b = 0;
  280.   color.a = 255;
  281.  
  282.   return color;
  283.  
  284. }
  285.  
  286.  
  287.  
  288.  
  289.  
  290. bool handle_events(){ //returns false if program should abort
  291.   bool run = true;
  292.   SDL_Event evt;
  293.  
  294.   while(SDL_PollEvent(&evt))
  295.   switch(evt.type){
  296.     case SDL_QUIT: run = false; break;
  297.     case SDL_KEYDOWN: if(evt.key.repeat) break;
  298.     {
  299.       paused ^= 1;
  300.     } break;
  301.  
  302.   }
  303.  
  304.   return run;
  305.  
  306. }
  307.  
  308.  
  309.  
  310.  
  311.  
  312. char _filePathIn[MAX_PATH];
  313. char filePathOut[MAX_PATH] = "INVALID_FILE_NAME";
  314. int _main(int argc, char** argv){
  315.   //for(int i=0; i<argc; ++i) Log("%2i: \"%s\"", i, argv[i]);
  316.  
  317.   //will end up pointing to either _filePathIn or argv[1]
  318.   char* filePathIn = nullptr;
  319.  
  320.   if(argc < 2){
  321.     OPENFILENAME ofn;
  322.     memory::set(&_filePathIn, 0, sizeof(_filePathIn));
  323.     memory::set(&ofn, 0, sizeof(ofn));
  324.     ofn.lStructSize     = sizeof(ofn);
  325.     ofn.hwndOwner       = nullptr;
  326.     ofn.lpstrFile       = _filePathIn;
  327.     ofn.nMaxFile        = sizeof(_filePathIn);
  328.     ofn.lpstrFilter     = FILTER_STR;
  329.     ofn.nFilterIndex    = 5; //defaults to the .mp4 option
  330.     ofn.lpstrFileTitle  = nullptr;
  331.     ofn.nMaxFileTitle   = 0;
  332.     ofn.lpstrInitialDir = nullptr;
  333.     ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
  334.  
  335.     if(GetOpenFileName(&ofn) == TRUE){
  336.       filePathIn = _filePathIn;
  337.  
  338.     } else {
  339.       DWORD exterr = CommDlgExtendedError();
  340.       if(exterr) Log("CommDlgExtendedError() = 0x%04lX", exterr);
  341.  
  342.     }
  343.  
  344.  
  345.   } else {
  346.     filePathIn = argv[1];
  347.  
  348.     if(argc >= 3)
  349.     for(int i=2; i<argc; ++i){
  350.       if(argv[i][0] == '-')
  351.       switch(argv[i][1]){
  352.         case 't': shouldDither   = false; break; //-t: use threshhold instead of dither
  353.         case 'c': dontCullFrames = true;  break; //-c: disable culling of frames to match 10fps
  354.         default:;
  355.       }
  356.  
  357.     }
  358.  
  359.  
  360.   }
  361.  
  362.  
  363.  
  364.   if(filePathIn == nullptr){
  365.     Log("Error: No file was selected!");
  366.     //MessageBeep(MB_ICONWARNING);
  367.     SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_WARNING, "Failed to load file!",
  368.                              "No file was selected!", nullptr);
  369.     return -1;
  370.  
  371.   } else if(!fileio::exists(filePathIn)){
  372.     Log("Error: File \"%s\" doesn't exist!", filePathIn);
  373.     MessageBeep(MB_ICONERROR);
  374.     SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Failed to load file!",
  375.                              fstr("File \"%s\" doesn't exist!", filePathIn),
  376.                              nullptr);
  377.     return -1;
  378.  
  379.   }
  380.  
  381.  
  382.  
  383.   #ifdef _DEBUG
  384.     #define WIN_X (TGT_X*2)
  385.     #define WIN_Y (TGT_Y*2)
  386.   #else
  387.     #define WIN_X 600
  388.     #define WIN_Y 150
  389.   #endif
  390.  
  391.   #define PROGRESS_STRING(which_frame)                                   \
  392.     fstr("%04i/%04i, %6.2f%%, %04i written (press any key to pause) %s", \
  393.          (which_frame),  (frames),                                       \
  394.          ((float)(which_frame)/(frames))*100.0f,                         \
  395.          (framesWritten), (paused)?"(PAUSED)":""                         )
  396.  
  397.   SDL_Window* win = SDL_CreateWindow("(LOADING FILE DATA...)",
  398.                                      SDL_WINDOWPOS_UNDEFINED,
  399.                                      SDL_WINDOWPOS_UNDEFINED,
  400.                                      WIN_X, WIN_Y, SDL_WINDOW_RESIZABLE);
  401.   if(!win) throw SDL_GetError();
  402.  
  403.   SDL_SetWindowMinimumSize(win, WIN_X, WIN_Y);
  404.  
  405.   SDL_Renderer* rnd = SDL_CreateRenderer(win, -1, SDL_RENDERER_ACCELERATED);
  406.   if(!rnd) throw SDL_GetError();
  407.  
  408.   #ifdef _DEBUG
  409.     SDL_Texture* tex = SDL_CreateTexture(rnd, SDL_PIXELFORMAT_ABGR8888,
  410.                                          SDL_TEXTUREACCESS_STREAMING,
  411.                                          TGT_X, TGT_Y);
  412.     if(!tex) throw SDL_GetError();
  413.   #endif
  414.  
  415.   cv::Mat frame_ori, frame_gray, frame_resized;
  416.   cv::Mat* frame = &frame_resized; //switches to &frame_gray if size is already correct
  417.  
  418.   Uint64 ticksEnd,  ticksStart = SDL_GetTicks64();
  419.   int frames = 1,  framesWritten = 0;
  420.   bool is_image = is_image_file(filePathIn);
  421.  
  422.  
  423.  
  424.   //open file and write header
  425.   int dotpos = strip_file_extension(filePathIn);
  426.   strncpy(filePathOut, fstr("%s.kv3", filePathIn), MAX_PATH-1);
  427.   filePathIn[dotpos] = '.'; //put dot back (strip_file_extension sets it to \0)
  428.  
  429.   File file(filePathOut, "wb");
  430.  
  431.   //tbd
  432.   //FileHeader hdr;
  433.   //hdr.magic = KV3_MAGIC;
  434.  
  435.  
  436.  
  437.   if(is_image){
  438.     frame_gray = cv::imread(filePathIn, cv::IMREAD_GRAYSCALE);
  439.  
  440.     if(frame_gray.cols==TGT_X && frame_gray.cols==TGT_Y){
  441.       frame = &frame_gray; //no resize needed
  442.  
  443.     } else {
  444.       cv::resize(frame_gray, frame_resized, cv::Size(TGT_X, TGT_Y));
  445.  
  446.     }
  447.  
  448.     dither_formatted_mat(*frame);
  449.     write_image_data(*frame, file);
  450.     ++framesWritten;
  451.  
  452.     file.close();
  453.     ticksEnd = SDL_GetTicks64();
  454.  
  455.     #ifdef _DEBUG
  456.       SDL_SetWindowTitle(win, "Image result");
  457.       Uint8* pixels_src = frame->ptr<Uint8>();
  458.       Uint32* pixels_dst;
  459.       int pitch; //unused
  460.  
  461.       if(SDL_LockTexture(tex, nullptr, (void**)&pixels_dst, &pitch)<0)
  462.         throw SDL_GetError();
  463.       for(int i=0; i<TGT_LEN; ++i)
  464.         pixels_dst[i] = (pixels_src[i]!=0) ? 0xffffffff : 0xff000000;
  465.       SDL_UnlockTexture(tex);
  466.  
  467.       while(true){
  468.         if(!handle_events()) break;
  469.         SDL_RenderCopy(rnd, tex, nullptr, nullptr);
  470.         SDL_RenderPresent(rnd);
  471.         SDL_Delay(33);
  472.       }
  473.     #endif
  474.  
  475.  
  476.  
  477.   } else { //video
  478.     cv::VideoCapture video(filePathIn);
  479.     if(!video.isOpened()) throw "Failed to open video file!";
  480.  
  481.     double fps =      video.get(cv::CAP_PROP_FPS         );
  482.         frames = (int)video.get(cv::CAP_PROP_FRAME_COUNT ); //(declared earlier)
  483.     int  width = (int)video.get(cv::CAP_PROP_FRAME_WIDTH );
  484.     int height = (int)video.get(cv::CAP_PROP_FRAME_HEIGHT);
  485.  
  486.     #define FRAME_UPDATE_PERIOD 100
  487.     #define FRAMERATE_TARGET 20.0
  488.     double timing_var = 1.0; //so that the 1st frame is guaranteed to be written
  489.     double timing_adv = FRAMERATE_TARGET/fps;
  490.  
  491.     int waitms_original = (int)(1000.0/fps);
  492.     int frameUpdate = 0;
  493.  
  494.     SDL_Rect progress_rect;
  495.     memory_set_0(progress_rect);
  496.     progress_rect.h = WIN_Y;
  497.  
  498.     //SDL_Log instead of Log, so gcc stops complaining about unused vars in release build
  499.     SDL_Log("fps=%.2f, frames=%i, dims=(%i,%i), waitms_original=%i, frameUpdate=%i",
  500.              fps,      frames,   width, height, waitms_original,    frameUpdate);
  501.  
  502.     if(width==TGT_X && height==TGT_Y) frame = &frame_gray; //&frame_resized otherwise
  503.  
  504.  
  505.  
  506.     for(int f=0; f<frames; ++f){
  507.       if(!handle_events()){ aborted = true; break; }
  508.  
  509.       if(paused){
  510.         frameUpdate = 0;
  511.         SDL_SetWindowTitle(win, PROGRESS_STRING(f));
  512.         SDL_Delay(33);
  513.         --f; continue;
  514.       }
  515.  
  516.       #ifdef _DEBUG
  517.         Sint64 ticksFrame = (Sint64)SDL_GetTicks64();
  518.         bool frameRead = false;
  519.       #endif
  520.  
  521.  
  522.  
  523.       if(timing_var < 1.0){ //skip frame
  524.         //(video.grab advances the frame without reading it)
  525.         if(!video.grab()) throw "failed to read frame";
  526.  
  527.       } else {
  528.         if(!video.read(frame_ori)) throw "failed to read frame";
  529.         cv::cvtColor(frame_ori, frame_gray, cv::COLOR_BGR2GRAY);
  530.         cv::resize(frame_gray, frame_resized, cv::Size(TGT_X, TGT_Y));
  531.  
  532.         dither_formatted_mat(*frame);
  533.         int how_many_copies_to_write = (int)timing_var; //copies of the same frame
  534.         for(int f2=0; f2<how_many_copies_to_write; ++f2){
  535.           write_image_data(*frame, file);
  536.           ++framesWritten;
  537.         }
  538.         timing_var = SDL_fmod(timing_var, 1.0);
  539.  
  540.         #ifdef _DEBUG
  541.           frameRead = true;
  542.         #endif
  543.  
  544.       }
  545.  
  546.       timing_var += timing_adv;
  547.  
  548.  
  549.       #ifdef _DEBUG
  550.         Uint8* pixels_src = frame->ptr<Uint8>();
  551.         Uint32* pixels_dst;
  552.         int pitch; //unused
  553.  
  554.         if(SDL_LockTexture(tex, nullptr, (void**)&pixels_dst, &pitch)<0)
  555.           throw SDL_GetError();
  556.         for(int i=0; i<TGT_LEN; ++i)
  557.           pixels_dst[i] = (pixels_src[i]!=0) ? 0xffffffff : 0xff000000;
  558.         SDL_UnlockTexture(tex);
  559.  
  560.         SDL_SetWindowTitle(win, PROGRESS_STRING(f));
  561.         SDL_RenderCopy(rnd, tex, nullptr, nullptr);
  562.         SDL_RenderPresent(rnd);
  563.         #define DELAY_DIFF(ticks) MAX((Sint64)(ticks)-( ((Sint64)SDL_GetTicks64())-ticksFrame ),1)
  564.         if(dontCullFrames) SDL_Delay(DELAY_DIFF(waitms_original));
  565.         else if(frameRead) SDL_Delay(DELAY_DIFF(1000.0/FRAMERATE_TARGET));
  566.  
  567.       #else
  568.         if(!((frameUpdate++)%FRAME_UPDATE_PERIOD)){
  569.           float     unit  = (float)f/frames;
  570.           SDL_Color clr   = unit_to_ryg(unit);
  571.           progress_rect.w = WIN_X * unit;
  572.  
  573.           SDL_SetWindowTitle(win, PROGRESS_STRING(f));
  574.           SDL_SetRenderDrawColor(rnd, 0, 0, 0, 255);
  575.           SDL_RenderClear(rnd);
  576.           SDL_SetRenderDrawColor(rnd, clr.r, clr.g, clr.b, clr.a);
  577.           SDL_RenderFillRect(rnd, &progress_rect);
  578.           SDL_RenderPresent(rnd);
  579.  
  580.         }
  581.  
  582.       #endif
  583.  
  584.     }
  585.  
  586.  
  587.  
  588.     file.close();
  589.     ticksEnd = SDL_GetTicks64();
  590.  
  591.   }
  592.  
  593.  
  594.  
  595.   #ifdef _DEBUG
  596.     SDL_DestroyTexture(tex);
  597.   #endif
  598.   SDL_DestroyRenderer(rnd);
  599.   SDL_DestroyWindow(win);
  600.  
  601.  
  602.   if(!aborted){
  603.     //fstr has 2 buffers, so it's safe to do this
  604.     char* title   = fstr("Processed %i frame%c (%i written) in %.3f seconds",
  605.                          frames,  (frames > 1) ? 's' : ' ',  framesWritten,
  606.                          (float)(ticksEnd-ticksStart)/1000);
  607.     char* message = fstr("Output video saved to \"%s\"\t", filePathOut);
  608.  
  609.     MessageBeep(MB_ICONINFORMATION);
  610.     SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_INFORMATION, title, message, nullptr);
  611.  
  612.  
  613.   } else {
  614.     fileio::remove(filePathOut); //remove unfinished file
  615.  
  616.     SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_WARNING, "!!!",
  617.                              "Conversion was aborted!", nullptr);
  618.  
  619.  
  620.   }
  621.  
  622.  
  623.   return 0;
  624.  
  625. }
  626.  
  627.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement