From 3576503dc3c77e17944613e7e91e167ef5b86b4b Mon Sep 17 00:00:00 2001 From: Vincent-FK Date: Tue, 25 Feb 2020 12:48:48 +0100 Subject: [PATCH] add sierra lite dithering 32bpp or 24bpp to 16bpp for all images, reloadable media or scrolling list if requested by field dithering in xml Signed-off-by: Vincent-FK --- RetroFE/Source/Graphics/Component/Image.cpp | 32 +++++- RetroFE/Source/Graphics/Component/Image.h | 5 +- .../Graphics/Component/ImageBuilder.cpp | 4 +- .../Source/Graphics/Component/ImageBuilder.h | 2 +- .../Graphics/Component/ReloadableMedia.cpp | 7 +- .../Graphics/Component/ReloadableMedia.h | 3 +- .../Graphics/Component/ScrollingList.cpp | 14 +-- .../Source/Graphics/Component/ScrollingList.h | 4 +- RetroFE/Source/Graphics/PageBuilder.cpp | 36 ++++++- RetroFE/Source/SDL.cpp | 101 +++++++++++++++++- RetroFE/Source/SDL.h | 6 +- 11 files changed, 184 insertions(+), 30 deletions(-) diff --git a/RetroFE/Source/Graphics/Component/Image.cpp b/RetroFE/Source/Graphics/Component/Image.cpp index a5fad3e..c892089 100644 --- a/RetroFE/Source/Graphics/Component/Image.cpp +++ b/RetroFE/Source/Graphics/Component/Image.cpp @@ -19,10 +19,13 @@ #include "../../Utility/Log.h" #include -Image::Image(std::string file, std::string altFile, Page &p, float scaleX, float scaleY) +Image::Image(std::string file, std::string altFile, Page &p, float scaleX, float scaleY, bool dithering) : Component(p) , texture_(NULL) , texture_prescaled_(NULL) + , ditheringAuthorized_(dithering) + , needDithering_(false) + , imgBitsPerPx_(32) , file_(file) , altFile_(altFile) , scaleX_(scaleX) @@ -39,6 +42,7 @@ Image::~Image() void Image::freeGraphicsMemory() { Component::freeGraphicsMemory(); + //printf("freeGraphicsMemory: %s\n", file_.c_str()); SDL_LockMutex(SDL::getMutex()); if (texture_ != NULL) @@ -64,7 +68,7 @@ void Image::allocateGraphicsMemory() SDL_LockMutex(SDL::getMutex()); /* Load image */ - SDL_Surface *img_tmp = NULL; + SDL_Surface * img_tmp = NULL; //printf("Loading image: %s\n", file_.c_str()); img_tmp = IMG_Load(file_.c_str()); if (!img_tmp && altFile_ != "") @@ -76,6 +80,11 @@ void Image::allocateGraphicsMemory() if (img_tmp != NULL) { + /* Check if dithering needed */ + imgBitsPerPx_ = img_tmp->format->BitsPerPixel; + if( imgBitsPerPx_ > 16 && ditheringAuthorized_){ + needDithering_ = true; + } /* Convert to RGB 32bit if necessary */ if(img_tmp->format->BitsPerPixel != 32){ @@ -126,7 +135,8 @@ void Image::draw() scaling_needed = rect.w!=0 && rect.h!=0 && (texture_->w != rect.w || texture_->h != rect.h); if(scaling_needed){ cache_scaling_needed = (texture_prescaled_ == NULL)?true:(texture_prescaled_->w != rect.w || texture_prescaled_->h != rect.h); - if(cache_scaling_needed){ + if(cache_scaling_needed && imgBitsPerPx_ > 16 && ditheringAuthorized_){ + needDithering_ = true; texture_prescaled_ = SDL::zoomSurface(texture_, NULL, &rect); if(texture_prescaled_ == NULL){ printf("ERROR in %s - Could not create texture_prescaled_\n", __func__); @@ -139,11 +149,23 @@ void Image::draw() } } + /* Surface to display */ + SDL_Surface * surfaceToRender = NULL; if(use_prescaled && texture_prescaled_ != NULL){ - SDL::renderCopy(texture_prescaled_, baseViewInfo.Alpha, NULL, &rect, baseViewInfo); + surfaceToRender = texture_prescaled_; } else{ - SDL::renderCopy(texture_, baseViewInfo.Alpha, NULL, &rect, baseViewInfo); + surfaceToRender = texture_; } + + /* Dithering */ + if(needDithering_){ + //printf("Dither: %s\n", file_.c_str()); + SDL::ditherSurface32bppTo16Bpp(surfaceToRender); + needDithering_ = false; + } + + /* Render */ + SDL::renderCopy(surfaceToRender, baseViewInfo.Alpha, NULL, &rect, baseViewInfo); } } diff --git a/RetroFE/Source/Graphics/Component/Image.h b/RetroFE/Source/Graphics/Component/Image.h index af4060e..f115c8a 100644 --- a/RetroFE/Source/Graphics/Component/Image.h +++ b/RetroFE/Source/Graphics/Component/Image.h @@ -22,7 +22,7 @@ class Image : public Component { public: - Image(std::string file, std::string altFile, Page &p, float scaleX, float scaleY); + Image(std::string file, std::string altFile, Page &p, float scaleX, float scaleY, bool dithering); virtual ~Image(); void freeGraphicsMemory(); void allocateGraphicsMemory(); @@ -35,4 +35,7 @@ protected: std::string altFile_; float scaleX_; float scaleY_; + bool ditheringAuthorized_; + bool needDithering_; + int imgBitsPerPx_; }; diff --git a/RetroFE/Source/Graphics/Component/ImageBuilder.cpp b/RetroFE/Source/Graphics/Component/ImageBuilder.cpp index c0845aa..f2ecc8c 100644 --- a/RetroFE/Source/Graphics/Component/ImageBuilder.cpp +++ b/RetroFE/Source/Graphics/Component/ImageBuilder.cpp @@ -18,7 +18,7 @@ #include "../../Utility/Log.h" #include -Image * ImageBuilder::CreateImage(std::string path, Page &p, std::string name, float scaleX, float scaleY) +Image * ImageBuilder::CreateImage(std::string path, Page &p, std::string name, float scaleX, float scaleY, bool dithering) { Image *image = NULL; std::vector extensions; @@ -36,7 +36,7 @@ Image * ImageBuilder::CreateImage(std::string path, Page &p, std::string name, f //printf(" findMatchingFile, prefix = %s, file = %s\n", prefix.c_str(), file.c_str()); if(Utils::findMatchingFile(prefix, extensions, file)) { - image = new Image(file, "", p, scaleX, scaleY); + image = new Image(file, "", p, scaleX, scaleY, dithering); } return image; diff --git a/RetroFE/Source/Graphics/Component/ImageBuilder.h b/RetroFE/Source/Graphics/Component/ImageBuilder.h index be3dc31..8d63112 100644 --- a/RetroFE/Source/Graphics/Component/ImageBuilder.h +++ b/RetroFE/Source/Graphics/Component/ImageBuilder.h @@ -24,5 +24,5 @@ class ImageBuilder { public: - Image * CreateImage(std::string path, Page &p, std::string name, float scaleX, float scaleY); + Image * CreateImage(std::string path, Page &p, std::string name, float scaleX, float scaleY, bool dithering); }; diff --git a/RetroFE/Source/Graphics/Component/ReloadableMedia.cpp b/RetroFE/Source/Graphics/Component/ReloadableMedia.cpp index 3a0f223..d8ccc01 100644 --- a/RetroFE/Source/Graphics/Component/ReloadableMedia.cpp +++ b/RetroFE/Source/Graphics/Component/ReloadableMedia.cpp @@ -28,7 +28,7 @@ #include #include -ReloadableMedia::ReloadableMedia(Configuration &config, bool systemMode, bool layoutMode, bool commonMode, bool menuMode, std::string type, Page &p, int displayOffset, bool isVideo, Font *font, float scaleX, float scaleY) +ReloadableMedia::ReloadableMedia(Configuration &config, bool systemMode, bool layoutMode, bool commonMode, bool menuMode, std::string type, Page &p, int displayOffset, bool isVideo, Font *font, float scaleX, float scaleY, bool dithering) : Component(p) , config_(config) , systemMode_(systemMode) @@ -39,6 +39,7 @@ ReloadableMedia::ReloadableMedia(Configuration &config, bool systemMode, bool la , videoInst_(NULL) , isVideo_(isVideo) , FfntInst_(font) + , ditheringAuthorized_(dithering) , textFallback_(false) , imageFallback_(false) , imageAndTextPadding_(0) @@ -424,7 +425,7 @@ void ReloadableMedia::reloadTexture( bool previousItem ) ImageBuilder imageBuild; imagePath = Utils::combinePath(Configuration::absolutePath, "collections", collectionName ); imagePath = Utils::combinePath( imagePath, "system_artwork" ); - loadedComponent_ = imageBuild.CreateImage( imagePath, page, std::string("fallback"), scaleX_, scaleY_ ); + loadedComponent_ = imageBuild.CreateImage( imagePath, page, std::string("fallback"), scaleX_, scaleY_, ditheringAuthorized_ ); } // if image and artwork was not specified, fall back to displaying text @@ -492,7 +493,7 @@ Component *ReloadableMedia::findComponent(std::string collection, std::string ty } else { - component = imageBuild.CreateImage(imagePath, page, basename, scaleX_, scaleY_); + component = imageBuild.CreateImage(imagePath, page, basename, scaleX_, scaleY_, ditheringAuthorized_); } return component; diff --git a/RetroFE/Source/Graphics/Component/ReloadableMedia.h b/RetroFE/Source/Graphics/Component/ReloadableMedia.h index 7034a8a..1b057f9 100644 --- a/RetroFE/Source/Graphics/Component/ReloadableMedia.h +++ b/RetroFE/Source/Graphics/Component/ReloadableMedia.h @@ -27,7 +27,7 @@ class Image; class ReloadableMedia : public Component { public: - ReloadableMedia(Configuration &config, bool systemMode, bool layoutMode, bool commonMode, bool menuMode, std::string type, Page &page, int displayOffset, bool isVideo, Font *font, float scaleX, float scaleY); + ReloadableMedia(Configuration &config, bool systemMode, bool layoutMode, bool commonMode, bool menuMode, std::string type, Page &page, int displayOffset, bool isVideo, Font *font, float scaleX, float scaleY, bool dithering); virtual ~ReloadableMedia(); void update(float dt); void draw(); @@ -52,6 +52,7 @@ private: IVideo *videoInst_; bool isVideo_; Font *FfntInst_; + bool ditheringAuthorized_; bool imageAndText_; float imageAndTextPadding_; bool textFallback_; diff --git a/RetroFE/Source/Graphics/Component/ScrollingList.cpp b/RetroFE/Source/Graphics/Component/ScrollingList.cpp index 837e23e..601e6bd 100644 --- a/RetroFE/Source/Graphics/Component/ScrollingList.cpp +++ b/RetroFE/Source/Graphics/Component/ScrollingList.cpp @@ -48,7 +48,8 @@ ScrollingList::ScrollingList( Configuration &c, float scaleY, Font *font, std::string layoutKey, - std::string imageType ) + std::string imageType , + bool dithering) : Component( p ) , horizontalScroll( false ) , layoutMode_( layoutMode ) @@ -69,6 +70,7 @@ ScrollingList::ScrollingList( Configuration &c, , fontInst_( font ) , layoutKey_( layoutKey ) , imageType_( imageType ) + , ditheringAuthorized_( dithering ) , items_( NULL ) { } @@ -672,7 +674,7 @@ bool ScrollingList::allocateTexture( unsigned int index, Item *item ) else config_.getMediaPropertyAbsolutePath( collectionName, imageType_, false, imagePath ); } - t = imageBuild.CreateImage( imagePath, page, names[n], scaleX_, scaleY_ ); + t = imageBuild.CreateImage( imagePath, page, names[n], scaleX_, scaleY_, ditheringAuthorized_ ); // check sub-collection path for art if ( !t && !commonMode_ ) @@ -686,7 +688,7 @@ bool ScrollingList::allocateTexture( unsigned int index, Item *item ) { config_.getMediaPropertyAbsolutePath( item->collectionInfo->name, imageType_, false, imagePath ); } - t = imageBuild.CreateImage( imagePath, page, names[n], scaleX_, scaleY_ ); + t = imageBuild.CreateImage( imagePath, page, names[n], scaleX_, scaleY_, ditheringAuthorized_ ); } } @@ -714,19 +716,19 @@ bool ScrollingList::allocateTexture( unsigned int index, Item *item ) config_.getMediaPropertyAbsolutePath( item->name, imageType_, true, imagePath ); } } - t = imageBuild.CreateImage( imagePath, page, imageType_, scaleX_, scaleY_ ); + t = imageBuild.CreateImage( imagePath, page, imageType_, scaleX_, scaleY_, ditheringAuthorized_ ); } // check rom directory path for art if ( !t ){ - t = imageBuild.CreateImage( item->filepath, page, imageType_, scaleX_, scaleY_ ); + t = imageBuild.CreateImage( item->filepath, page, imageType_, scaleX_, scaleY_, ditheringAuthorized_ ); } // Image fallback if ( !t && imageType_.compare(std::string("null"))){ imagePath = Utils::combinePath(Configuration::absolutePath, "collections", collectionName ); imagePath = Utils::combinePath( imagePath, "system_artwork" ); - t = imageBuild.CreateImage( imagePath, page, std::string("fallback"), scaleX_, scaleY_ ); + t = imageBuild.CreateImage( imagePath, page, std::string("fallback"), scaleX_, scaleY_, ditheringAuthorized_ ); } if ( !t ) diff --git a/RetroFE/Source/Graphics/Component/ScrollingList.h b/RetroFE/Source/Graphics/Component/ScrollingList.h index 175a6d3..e427aa7 100644 --- a/RetroFE/Source/Graphics/Component/ScrollingList.h +++ b/RetroFE/Source/Graphics/Component/ScrollingList.h @@ -41,7 +41,8 @@ public: float scaleY, Font *font, std::string layoutKey, - std::string imageType ); + std::string imageType, + bool dithering); ScrollingList( const ScrollingList © ); virtual ~ScrollingList( ); @@ -124,6 +125,7 @@ private: Font *fontInst_; std::string layoutKey_; std::string imageType_; + bool ditheringAuthorized_; std::vector *items_; std::vector components_; diff --git a/RetroFE/Source/Graphics/PageBuilder.cpp b/RetroFE/Source/Graphics/PageBuilder.cpp index 19c5103..8d252d8 100644 --- a/RetroFE/Source/Graphics/PageBuilder.cpp +++ b/RetroFE/Source/Graphics/PageBuilder.cpp @@ -429,6 +429,15 @@ bool PageBuilder::buildComponents(xml_node<> *layout, Page *page) { xml_attribute<> *src = componentXml->first_attribute("src"); xml_attribute<> *idXml = componentXml->first_attribute("id"); + xml_attribute<> *ditheringXml = componentXml->first_attribute("dithering"); + + bool dithering =false; + if (ditheringXml && + (Utils::toLower(ditheringXml->value()) == "true" || + Utils::toLower(ditheringXml->value()) == "yes")) + { + dithering = true; + } int id = -1; if (idXml) @@ -449,7 +458,7 @@ bool PageBuilder::buildComponents(xml_node<> *layout, Page *page) std::string altImagePath; altImagePath = Utils::combinePath(Configuration::absolutePath, "layouts", layoutName, std::string(src->value())); - Image *c = new Image(imagePath, altImagePath, *page, scaleX_, scaleY_); + Image *c = new Image(imagePath, altImagePath, *page, scaleX_, scaleY_, dithering); c->setId( id ); xml_attribute<> *menuScrollReload = componentXml->first_attribute("menuScrollReload"); if (menuScrollReload && @@ -780,10 +789,20 @@ void PageBuilder::loadReloadableImages(xml_node<> *layout, std::string tagName, } } } - else + else //ReloadableMedia { Font *font = addFont(componentXml, NULL); - c = new ReloadableMedia(config_, systemMode, layoutMode, commonMode, menuMode, type->value(), *page, selectedOffset, (tagName == "reloadableVideo"), font, scaleX_, scaleY_); + + xml_attribute<> *ditheringXml = componentXml->first_attribute("dithering"); + bool dithering =false; + if (ditheringXml && + (Utils::toLower(ditheringXml->value()) == "true" || + Utils::toLower(ditheringXml->value()) == "yes")) + { + dithering = true; + } + + c = new ReloadableMedia(config_, systemMode, layoutMode, commonMode, menuMode, type->value(), *page, selectedOffset, (tagName == "reloadableVideo"), font, scaleX_, scaleY_, dithering); c->setId( id ); xml_attribute<> *menuScrollReload = componentXml->first_attribute("menuScrollReload"); if (menuScrollReload && @@ -1025,6 +1044,7 @@ ScrollingList * PageBuilder::buildMenu(xml_node<> *menuXml, Page &page) xml_attribute<> *scrollTimeXml = menuXml->first_attribute("scrollTime"); xml_attribute<> *scrollAccelerationXml = menuXml->first_attribute("scrollAcceleration"); xml_attribute<> *scrollOrientationXml = menuXml->first_attribute("orientation"); + xml_attribute<> *ditheringXml = menuXml->first_attribute("dithering"); if(menuTypeXml) { @@ -1065,7 +1085,15 @@ ScrollingList * PageBuilder::buildMenu(xml_node<> *menuXml, Page &page) // on default, text will be rendered to the menu. Preload it into cache. Font *font = addFont(itemDefaults, NULL); - menu = new ScrollingList(config_, page, layoutMode, commonMode, scaleX_, scaleY_, font, layoutKey, imageType); + bool dithering =false; + if (ditheringXml && + (Utils::toLower(ditheringXml->value()) == "true" || + Utils::toLower(ditheringXml->value()) == "yes")) + { + dithering = true; + } + + menu = new ScrollingList(config_, page, layoutMode, commonMode, scaleX_, scaleY_, font, layoutKey, imageType, dithering); if(scrollTimeXml) { diff --git a/RetroFE/Source/SDL.cpp b/RetroFE/Source/SDL.cpp index 478faa2..fbd4160 100644 --- a/RetroFE/Source/SDL.cpp +++ b/RetroFE/Source/SDL.cpp @@ -20,7 +20,22 @@ #include "Utility/Log.h" #include //#include -#include +//#include + +/* Basic math */ +#define MIN(a,b) (((a)<(b))?(a):(b)) +#define MAX(a,b) (((a)>(b))?(a):(b)) + +/* Get 16bit closest color */ +#define CLOSEST_RB(c) (MIN(c+4,0xff) >> 3 << 3) +#define CLOSEST_G(c) (MIN(c+2,0xff) >> 2 << 2) + +/* RGB8888 */ +#define RGB32BIT(a, r, g, b) ((a<<24) | (r<<16) | (g<<8) | b) +#define GET_A_32BIT(c) ((c & 0xff000000) >> 24) +#define GET_R_32BIT(c) ((c & 0x00ff0000) >> 16) +#define GET_G_32BIT(c) ((c & 0x0000ff00) >> 8) +#define GET_B_32BIT(c) (c & 0x000000ff) //SDL_Window *SDL::window_ = NULL; @@ -353,6 +368,90 @@ void SDL::SDL_Rotate_270(SDL_Surface * src, SDL_Surface * dst){ } +/* Dithering 32bpp RGB Surface + * + * error diffusion with "Filter Lite" also called "Sierra lite" method + * + */ +void SDL::ditherSurface32bppTo16Bpp(SDL_Surface * src_surface){ + + /* Vars */ + int x, y; + uint8_t r_old, g_old, b_old; + uint8_t r_new, g_new, b_new; + int r_error, g_error, b_error; + uint32_t cur_px; + + /* Sanity check */ + if(src_surface->format->BitsPerPixel != 32){ + printf("Error: src_surface is %dBpp while dst_surface is not 32\n", src_surface->format->BitsPerPixel); + return; + } + + /* Loop for dithering */ + for (y=0; yh; y++){ + for (x=0; x < src_surface->w; x++){ + + /* Get old and new rgb values of current pixel */ + cur_px = *((uint32_t*)src_surface->pixels + (y)*src_surface->w + (x)); + r_old = GET_R_32BIT(cur_px); + g_old = GET_G_32BIT(cur_px); + b_old = GET_B_32BIT(cur_px); + r_new = CLOSEST_RB(r_old); + g_new = CLOSEST_G(g_old); + b_new = CLOSEST_RB(b_old); + + /* Set new pixel value */ + *((uint32_t*)src_surface->pixels + (y)*src_surface->w + (x)) = RGB32BIT(GET_A_32BIT(cur_px), r_new, g_new, b_new); + + /* Get errors */ + r_error = r_old - r_new; + g_error = g_old - g_new; + b_error = b_old - b_new; + + /* Right pixel */ + if(x + 1 < src_surface->w){ + cur_px = *((uint32_t*)src_surface->pixels + (y)*src_surface->w + (x+1)); + r_old = GET_R_32BIT(cur_px); + g_old = GET_G_32BIT(cur_px); + b_old = GET_B_32BIT(cur_px); + *((uint32_t*)src_surface->pixels + (y)*src_surface->w + (x+1)) = + RGB32BIT( GET_A_32BIT(cur_px), + MAX(MIN((int)r_old + (r_error>>1), 0xff), 0), + MAX(MIN((int)g_old + (g_error>>1), 0xff), 0), + MAX(MIN((int)b_old + (b_error>>1), 0xff), 0) ); + } + + /* Bottom pixel */ + if(y + 1 < src_surface->h){ + cur_px = *((uint32_t*)src_surface->pixels + (y+1)*src_surface->w + (x)); + r_old = GET_R_32BIT(cur_px); + g_old = GET_G_32BIT(cur_px); + b_old = GET_B_32BIT(cur_px); + *((uint32_t*)src_surface->pixels + (y+1)*src_surface->w + (x)) = + RGB32BIT( GET_A_32BIT(cur_px), + MAX(MIN((int)r_old + (r_error>>2), 0xff), 0), + MAX(MIN((int)g_old + (g_error>>2), 0xff), 0), + MAX(MIN((int)b_old + (b_error>>2), 0xff), 0) ); + } + + /* Bottom left pixel */ + if( x > 0 && y + 1 < src_surface->h){ + cur_px = *((uint32_t*)src_surface->pixels + (y+1)*src_surface->w + (x-1)); + r_old = GET_R_32BIT(cur_px); + g_old = GET_G_32BIT(cur_px); + b_old = GET_B_32BIT(cur_px); + *((uint32_t*)src_surface->pixels + (y+1)*src_surface->w + (x-1)) = + RGB32BIT( GET_A_32BIT(cur_px), + MAX(MIN((int)r_old + (r_error>>2), 0xff), 0), + MAX(MIN((int)g_old + (g_error>>2), 0xff), 0), + MAX(MIN((int)b_old + (b_error>>2), 0xff), 0) ); + } + } + } +} + + // Copy virtual window to HW window and Flip display void SDL::renderAndFlipWindow( ) { diff --git a/RetroFE/Source/SDL.h b/RetroFE/Source/SDL.h index 3d103f2..a0bcfd7 100644 --- a/RetroFE/Source/SDL.h +++ b/RetroFE/Source/SDL.h @@ -33,13 +33,11 @@ class SDL public: static bool initialize( Configuration &config ); static bool deInitialize( ); - //static SDL_Renderer *getRenderer( ); static SDL_mutex *getMutex( ); - //static SDL_Window *getWindow( ); static SDL_Surface *getWindow( ); static void renderAndFlipWindow( ); - //static bool renderCopy( SDL_Texture *texture, float alpha, SDL_Rect *src, SDL_Rect *dest, ViewInfo &viewInfo ); static SDL_Surface * zoomSurface(SDL_Surface *surface_ptr, SDL_Rect *src_rect_origin, SDL_Rect *dst_rect); + static void ditherSurface32bppTo16Bpp(SDL_Surface *src_surface); static bool renderCopy( SDL_Surface *texture, float alpha, SDL_Rect *src, SDL_Rect *dest, ViewInfo &viewInfo ); static int getWindowWidth( ) { @@ -56,8 +54,6 @@ public: static void SDL_Rotate_270(SDL_Surface * dst, SDL_Surface * src); private: - //static SDL_Window *window_; - //static SDL_Renderer *renderer_; static Uint32 get_pixel32( SDL_Surface *surface, int x, int y ); static void put_pixel32( SDL_Surface *surface, int x, int y, Uint32 pixel ); static SDL_Surface * flip_surface( SDL_Surface *surface, int flags );