SDL2 port: Picture with live preview

This commit is contained in:
Christophe Meneboeuf 2022-06-16 23:08:58 +02:00
parent 20f85a2dc5
commit ed03b6a48f
10 changed files with 88 additions and 107 deletions

View File

@ -30,12 +30,6 @@ add_library(${PROJECT_NAME} src/Common.h
src/HiRes.h src/HiRes.h
src/ImageQuantized.cpp src/ImageQuantized.cpp
src/ImageQuantized.h src/ImageQuantized.h
# src/Picture.h
# src/Picture.cpp
# src/Tile.h
# src/Tile.cpp
#src/Display.h
#src/Display.cpp
) )
## dependencies ## dependencies
conan_set_find_library_paths(${PROJECT_NAME}) conan_set_find_library_paths(${PROJECT_NAME})
@ -43,7 +37,10 @@ conan_target_link_libraries(${PROJECT_NAME})
# Application Picture # Application Picture
add_executable( Picture src/App_Picture.cpp) add_executable( Picture src/App_Picture.cpp
src/Display.h
src/Display.cpp
)
if(NOT WIN32) if(NOT WIN32)
set_target_properties(Picture PROPERTIES COMPILE_FLAGS -pthread LINK_FLAGS -pthread) set_target_properties(Picture PROPERTIES COMPILE_FLAGS -pthread LINK_FLAGS -pthread)
endif() endif()
@ -58,23 +55,24 @@ target_link_libraries(Picture ${PROJECT_NAME})
set_property(TARGET Picture PROPERTY set_property(TARGET Picture PROPERTY
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG}) RUNTIME_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG})
## Application Tile ## Application Tile
#add_executable(Tile src/App_Tile.cpp) #add_executable(Tile src/App_Tile.cpp
# src/Tile.h
# src/Tile.cpp
# )
### custom definitions ### custom definitions
#target_compile_definitions(Tile PRIVATE cimg_use_png) #target_compile_definitions(Tile PRIVATE cimg_use_png)
### dependencies ### dependencies
#if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND "${CMAKE_CXX_COMPILER_VERSION}" VERSION_LESS "9") #if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND "${CMAKE_CXX_COMPILER_VERSION}" VERSION_LESS "9")
# target_link_libraries(Tile stdc++fs) # filesystem lib not included in stdc++ for gcc < 9 # target_link_libraries(Tile stdc++fs) # filesystem lib not included in stdc++ for gcc < 9
#endif() #endif()
#conan_set_find_library_paths(Tile)
#conan_target_link_libraries(Tile)
#target_link_libraries(Picture ${PROJECT_NAME})
### output
#set_property(TARGET Tile PROPERTY #set_property(TARGET Tile PROPERTY
# RUNTIME_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG}) # RUNTIME_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG}
#target_include_directories(Tile PRIVATE
# ${CONAN_INCLUDE_DIRS_SDL}
# ${CONAN_INCLUDE_DIRS_TCLAP})
#target_link_libraries(Tile PRIVATE
# CONAN_PKG::sdl
# ${PROJECT_NAME}
#) #)
#
#
#

View File

@ -16,6 +16,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#define SDL_MAIN_HANDLED
#include <exception> #include <exception>
#include <iostream> #include <iostream>
@ -27,9 +28,7 @@
#include <tclap/CmdLine.h> #include <tclap/CmdLine.h>
#include "ImageQuantized.h" #include "ImageQuantized.h"
//#include "Picture.h" #include "Display.h"
//#include "Tile.h"
//#include "Display.h"
using namespace std; using namespace std;
using namespace RgbToHires; using namespace RgbToHires;
@ -108,21 +107,23 @@ int main( int argc, char *argv[] )
} }
const ImageQuantized imageHiRes{ surfaceRgb }; const ImageQuantized imageHiRes{ surfaceRgb };
if (assembly.getValue() == true) { //Ouput in ASM if (preview.getValue()) {
ofstream output(outputPath.getValue()); const auto bytes = imageHiRes.getHiresBuffer();
output << imageHiRes.getAsm(); Display::Window::GetInstance()->display(filepath, bytes->data());
} }
else { //Binary output else
ofstream output(outputPath.getValue(), ios::binary); {
const auto bytes = imageHiRes.getBlob(); if (assembly.getValue() == true) { //Ouput in ASM
output.write(reinterpret_cast<const char*>(bytes.get()), bytes->size()); ofstream output(outputPath.getValue());
output << imageHiRes.getHiresAsm();
}
else { //Binary output
ofstream output(outputPath.getValue(), ios::binary);
const auto bytes = imageHiRes.getHiresBuffer();
output.write(reinterpret_cast<const char*>(bytes.get()), bytes->size());
}
} }
//if (preview.getValue()) {
// const auto bytes = imageHiRes.getBlob();
// Display::Window::GetInstance()->display(filepath, bytes->data());
//}
for (auto surface : surfaces) for (auto surface : surfaces)
{ {
SDL_FreeSurface(surface); SDL_FreeSurface(surface);

View File

@ -82,7 +82,7 @@ int main( int argc, char *argv[] )
// Always output in asm // Always output in asm
ofstream output(outputPath.getValue()); ofstream output(outputPath.getValue());
output << tileHiRes.getAsm(); output << tileHiRes.getHiresAsm();
} }

View File

@ -1,10 +1,10 @@
#include <filesystem> #include <filesystem>
#include <stdexcept> #include <stdexcept>
#include <iostream>
#include <chrono> #include <chrono>
#define SDL_MAIN_HANDLED #define SDL_MAIN_HANDLED
#include <SDL.h> #include <SDL.h>
#include <SDL_image.h>
#include "Picture.h" #include "Picture.h"
#include "Display.h" #include "Display.h"
@ -12,8 +12,6 @@
using namespace std; using namespace std;
namespace RgbToHires
{
namespace Display namespace Display
{ {
@ -118,24 +116,29 @@ namespace RgbToHires
{ {
bool isDone = false; bool isDone = false;
int nbErrors = 0; int nbErrors = 0;
std::vector<SDL_Surface*> surfaces;
while (!isDone) // several attempts as the file may be marked modified before being written while (!isDone) // several attempts as the file may be marked modified before being written
{ {
try try
{ {
this_thread::sleep_for(std::chrono::milliseconds(500)); // 500ms between each attempt this_thread::sleep_for(std::chrono::milliseconds(500)); // 500ms between each attempt
// update hires image // update hires image
const auto imageRgb = Magick::Image{ path }; SDL_Surface* surfaceRgb = IMG_Load(path.c_str());
auto imageQuantized = ImageQuantized{ imageRgb }; surfaces.push_back(surfaceRgb);
const auto imageHiRes = Picture{ imageQuantized }; if (surfaceRgb == nullptr)
{
throw("Cannot decode " + path);
}
const RgbToHires::ImageQuantized imageHiRes{ surfaceRgb };
// rgb conversion from hires data // rgb conversion from hires data
std::lock_guard<std::mutex> lock{ this->_mutex }; // protecting pViewport std::lock_guard<std::mutex> lock{ this->_mutex }; // protecting pViewport
pViewport = ComputeRgbBuffer(imageHiRes.getBlob()->data()); pViewport = ComputeRgbBuffer(imageHiRes.getHiresBuffer()->data());
isDone = true; isDone = true;
timeModified = std::filesystem::last_write_time(path); timeModified = std::filesystem::last_write_time(path);
this->_isFileModified.store(isImgModified); this->_isFileModified.store(isImgModified);
} }
catch (Magick::Error& e) catch (std::exception& e)
{ {
++nbErrors; ++nbErrors;
if (nbErrors >= 5) { // 5 atttemps if (nbErrors >= 5) { // 5 atttemps
@ -146,6 +149,7 @@ namespace RgbToHires
} }
} }
} }
for(auto surface : surfaces) { SDL_FreeSurface(surface); }
} }
} }
}); // thread }); // thread
@ -179,24 +183,6 @@ namespace RgbToHires
} }
void SdlError(const std::string& error,
SDL_Window* const pWindow = nullptr,
SDL_Renderer* const pRenderer = nullptr
)
{
std::cout << "Error: " << error << '\n';
if (pRenderer != nullptr) {
SDL_DestroyRenderer(pRenderer);
}
if (pWindow != nullptr) {
SDL_DestroyWindow(pWindow);
}
SDL_Quit();
exit(1);
}
constexpr std::array<rgba8Bits_t, 7> Palette = { constexpr std::array<rgba8Bits_t, 7> Palette = {
rgba8Bits_t{0x00,0x00,0x00, 0xFF}, // black rgba8Bits_t{0x00,0x00,0x00, 0xFF}, // black
@ -299,9 +285,9 @@ namespace RgbToHires
// Converting line per line, pixel-block per pixel-block // Converting line per line, pixel-block per pixel-block
auto pViewport = make_unique<Screen>(); auto pViewport = make_unique<Screen>();
auto itLine = std::begin(*pViewport); auto itLine = std::begin(*pViewport);
for (const auto lineBlockOffset : LineAdresses) for (const auto lineBlockOffset : RgbToHires::LineAdresses)
{ {
for (const auto lineOffset : LineOffsets) for (const auto lineOffset : RgbToHires::LineOffsets)
{ {
const uint8_t* pHires = hires + lineBlockOffset + lineOffset; // interleaved HIRES source line const uint8_t* pHires = hires + lineBlockOffset + lineOffset; // interleaved HIRES source line
for (std::size_t x = 0; x < itLine->size(); ++x) for (std::size_t x = 0; x < itLine->size(); ++x)
@ -317,6 +303,4 @@ namespace RgbToHires
return pViewport; return pViewport;
} }
}
} }

View File

@ -29,51 +29,48 @@ struct SDL_Window;
struct SDL_Renderer; struct SDL_Renderer;
struct SDL_Texture; struct SDL_Texture;
namespace RgbToHires namespace Display
{ {
namespace Display
{
struct rgba8Bits_t struct rgba8Bits_t
{ {
uint8_t r = 0; uint8_t r = 0;
uint8_t g = 0; uint8_t g = 0;
uint8_t b = 0; uint8_t b = 0;
uint8_t a = 0xff; uint8_t a = 0xff;
}; };
using Block = std::array<rgba8Bits_t, 14>; using Block = std::array<rgba8Bits_t, 14>;
using Line = std::array<Block, 40>; using Line = std::array<Block, 40>;
using Screen = std::array<Line, 192 * 2>; using Screen = std::array<Line, 192 * 2>;
class Window class Window
{ {
public: public:
Window() = default; Window() = default;
~Window(); ~Window();
static Window* GetInstance(); static Window* GetInstance();
void display(const std::string& path, const uint8_t* hiresblob); void display(const std::string& path, const uint8_t* hiresblob);
private: private:
bool init(); bool init();
void sdlError(const std::string& msg); void sdlError(const std::string& msg);
std::thread* _pThread = nullptr; //< to survey filechange std::thread* _pThread = nullptr; //< to survey filechange
std::mutex _mutex; std::mutex _mutex;
std::atomic_bool _isFileModified = false; std::atomic_bool _isFileModified = false;
std::atomic_bool _stopFileSurvey = false; std::atomic_bool _stopFileSurvey = false;
static Window* S_pInstance; static Window* S_pInstance;
static const int SCALE = 2; static const int SCALE = 2;
SDL_Window* _pWindow = nullptr; SDL_Window* _pWindow = nullptr;
SDL_Renderer* _pRenderer = nullptr; SDL_Renderer* _pRenderer = nullptr;
SDL_Texture* _pTexture = nullptr; SDL_Texture* _pTexture = nullptr;
}; };
}
} }
#endif #endif

View File

@ -78,7 +78,7 @@ namespace RgbToHires {
} }
unique_ptr<array<uint8_t, ImageQuantized::FRAME_SIZE>> ImageQuantized::getBlob() const unique_ptr<array<uint8_t, ImageQuantized::FRAME_SIZE>> ImageQuantized::getHiresBuffer() const
{ {
auto blob = unique_ptr<array<uint8_t, FRAME_SIZE>>{ new array<uint8_t, FRAME_SIZE> }; auto blob = unique_ptr<array<uint8_t, FRAME_SIZE>>{ new array<uint8_t, FRAME_SIZE> };
auto byte_blob = begin(*blob); auto byte_blob = begin(*blob);
@ -97,7 +97,7 @@ namespace RgbToHires {
string ImageQuantized::getAsm() const string ImageQuantized::getHiresAsm() const
{ {
string assembly{ "Picture:\n" }; string assembly{ "Picture:\n" };
for (const auto& line : _hrOrderedLines) for (const auto& line : _hrOrderedLines)
@ -155,6 +155,7 @@ namespace RgbToHires {
const auto distOrange = Distance(ORANGE, color); const auto distOrange = Distance(ORANGE, color);
if (distMin > distOrange) { distMin = distOrange; } if (distMin > distOrange) { distMin = distOrange; }
const auto distViolet = Distance(VIOLET, color); const auto distViolet = Distance(VIOLET, color);
if (distMin > distViolet) { distMin = distViolet; }
if (distMin == distBlack) { if (distMin == distBlack) {
return BLACK; return BLACK;

View File

@ -49,11 +49,11 @@ namespace RgbToHires {
ImageQuantized(SDL_Surface* const source); ImageQuantized(SDL_Surface* const source);
~ImageQuantized() = default; ~ImageQuantized() = default;
/// @brief Returns the binary hires picture /// @brief Returns the binary hires picture
std::unique_ptr <std::array<uint8_t, FRAME_SIZE>> getBlob() const; std::unique_ptr <std::array<uint8_t, FRAME_SIZE>> getHiresBuffer() const;
/// @brief Returns asm code corresponding to the image in memory (CA65 format) /// @brief Returns asm code corresponding to the image in memory (CA65 format)
std::string getAsm() const; std::string getHiresAsm() const;
/// @brief Returns an HIRES block
private: private:
Color Quantize(const Color& color); Color Quantize(const Color& color);

View File

@ -41,7 +41,7 @@ namespace RgbToHires
/// @brief Returns the binary hires picture /// @brief Returns the binary hires picture
std::unique_ptr <std::array<uint8_t, FRAME_SIZE>> getBlob() const; std::unique_ptr <std::array<uint8_t, FRAME_SIZE>> getBlob() const;
/// @brief Returns asm code corresponding to the image in memory (CA65 format) /// @brief Returns asm code corresponding to the image in memory (CA65 format)
std::string getAsm() const; std::string getHiresAsm() const;
private: private:

View File

@ -23,7 +23,7 @@ RgbToHires::Tile::Tile(const ImageQuantized& source, const unsigned col, const u
} }
std::string RgbToHires::Tile::getAsm() const std::string RgbToHires::Tile::getHiresAsm() const
{ {
std::string assembly; std::string assembly;
for (const auto& line : _blob) for (const auto& line : _blob)

View File

@ -19,7 +19,7 @@ namespace RgbToHires
~Tile() = default; ~Tile() = default;
/// @brief Returns asm code corresponding to the tile: the lines are not interleaved! /// @brief Returns asm code corresponding to the tile: the lines are not interleaved!
std::string getAsm() const; std::string getHiresAsm() const;
private: private:
using LineHr = std::vector<BlockHr>; using LineHr = std::vector<BlockHr>;