mirror of
https://github.com/RetroDECK/ES-DE.git
synced 2024-11-25 23:55:38 +00:00
Moved most CImg functions from MiximageGenerator to a new utility module.
This commit is contained in:
parent
a9a6b606db
commit
05990d0457
|
@ -322,14 +322,14 @@ bool MiximageGenerator::generateImage()
|
|||
|
||||
CImg<unsigned char> screenshotImage(fileWidth, fileHeight, 1, 4, 0);
|
||||
|
||||
// Convert image to CImg internal format.
|
||||
convertToCImgFormat(screenshotImage, screenshotVector);
|
||||
// Convert the RGBA image to CImg internal format.
|
||||
Utils::CImg::convertRGBAToCImg(screenshotVector, screenshotImage);
|
||||
screenshotVector.clear();
|
||||
|
||||
if (Settings::getInstance()->getBool("MiximageRemoveLetterboxes"))
|
||||
cropLetterboxes(screenshotImage);
|
||||
Utils::CImg::cropLetterboxes(screenshotImage);
|
||||
if (Settings::getInstance()->getBool("MiximageRemovePillarboxes"))
|
||||
cropPillarboxes(screenshotImage);
|
||||
Utils::CImg::cropPillarboxes(screenshotImage);
|
||||
|
||||
if (Settings::getInstance()->getString("MiximageScreenshotScaling") == "smooth") {
|
||||
// Lanczos scaling is normally not recommended for low resolution graphics as
|
||||
|
@ -391,8 +391,8 @@ bool MiximageGenerator::generateImage()
|
|||
marqueeImage = CImg<unsigned char>(FreeImage_GetWidth(marqueeFile),
|
||||
FreeImage_GetHeight(marqueeFile), 1, 4, 0);
|
||||
|
||||
convertToCImgFormat(marqueeImage, marqueeVector);
|
||||
removeTransparentPadding(marqueeImage);
|
||||
Utils::CImg::convertRGBAToCImg(marqueeVector, marqueeImage);
|
||||
Utils::CImg::removeTransparentPadding(marqueeImage);
|
||||
|
||||
unsigned int marqueeWidth = static_cast<unsigned int>(marqueeImage.width());
|
||||
unsigned int marqueeHeight = static_cast<unsigned int>(marqueeImage.height());
|
||||
|
@ -402,7 +402,8 @@ bool MiximageGenerator::generateImage()
|
|||
// We use Lanczos3 which is the highest quality resampling method available.
|
||||
marqueeImage.resize(marqueeWidth, marqueeHeight, 1, 4, 6);
|
||||
|
||||
addDropShadow(marqueeImage, marqueeShadowSize);
|
||||
// Add a drop shadow using 4 iterations of box blur.
|
||||
Utils::CImg::addDropShadow(marqueeImage, marqueeShadowSize, 0.6, 4);
|
||||
|
||||
xPosMarquee = canvasImage.width() - marqueeImage.width();
|
||||
yPosMarquee = 0;
|
||||
|
@ -432,8 +433,8 @@ bool MiximageGenerator::generateImage()
|
|||
boxImage = CImg<unsigned char>(FreeImage_GetWidth(boxFile),
|
||||
FreeImage_GetHeight(boxFile), 1, 4);
|
||||
|
||||
convertToCImgFormat(boxImage, boxVector);
|
||||
removeTransparentPadding(boxImage);
|
||||
Utils::CImg::convertRGBAToCImg(boxVector, boxImage);
|
||||
Utils::CImg::removeTransparentPadding(boxImage);
|
||||
|
||||
float scaleFactor = static_cast<float>(boxTargetHeight) /
|
||||
static_cast<float>(boxImage.height());
|
||||
|
@ -457,7 +458,7 @@ bool MiximageGenerator::generateImage()
|
|||
boxImage.resize(width, boxTargetHeight, 1, 4, 6);
|
||||
}
|
||||
|
||||
addDropShadow(boxImage, boxShadowSize);
|
||||
Utils::CImg::addDropShadow(boxImage, boxShadowSize, 0.6, 4);
|
||||
|
||||
xPosBox = 0;
|
||||
yPosBox = canvasImage.height() - boxImage.height();
|
||||
|
@ -522,8 +523,8 @@ bool MiximageGenerator::generateImage()
|
|||
|
||||
std::vector<unsigned char> canvasVector;
|
||||
|
||||
// Convert image from CImg internal format.
|
||||
convertFromCImgFormat(canvasImage, canvasVector);
|
||||
// Convert the image from CImg internal format.
|
||||
Utils::CImg::convertCImgToRGBA(canvasImage, canvasVector);
|
||||
|
||||
FIBITMAP* mixImage = nullptr;
|
||||
mixImage = FreeImage_ConvertFromRawBits(&canvasVector.at(0), canvasImage.width(),
|
||||
|
@ -553,169 +554,6 @@ bool MiximageGenerator::generateImage()
|
|||
return true;
|
||||
}
|
||||
|
||||
void MiximageGenerator::cropLetterboxes(CImg<unsigned char>& image)
|
||||
{
|
||||
double pixelValueSum = 0.0l;
|
||||
int rowCounterUpper = 0;
|
||||
int rowCounterLower = 0;
|
||||
|
||||
// Count the number of rows that are pure black.
|
||||
for (int i = image.height() - 1; i > 0; i--) {
|
||||
CImg<unsigned char> imageRow = image.get_rows(i, i);
|
||||
// Ignore the alpha channel.
|
||||
imageRow.channels(0, 2);
|
||||
pixelValueSum = imageRow.sum();
|
||||
if (pixelValueSum == 0.0l)
|
||||
rowCounterUpper++;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
for (int i = 0; i < image.height(); i++) {
|
||||
CImg<unsigned char> imageRow = image.get_rows(i, i);
|
||||
imageRow.channels(0, 2);
|
||||
pixelValueSum = imageRow.sum();
|
||||
if (pixelValueSum == 0.0l)
|
||||
rowCounterLower++;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
if (rowCounterUpper > 0)
|
||||
image.crop(0, 0, 0, 3, image.width() - 1, image.height() - 1 - rowCounterUpper, 0, 0);
|
||||
|
||||
if (rowCounterLower > 0)
|
||||
image.crop(0, rowCounterLower, 0, 3, image.width() - 1, image.height() - 1, 0, 0);
|
||||
}
|
||||
|
||||
void MiximageGenerator::cropPillarboxes(CImg<unsigned char>& image)
|
||||
{
|
||||
double pixelValueSum = 0.0l;
|
||||
unsigned int columnCounterLeft = 0;
|
||||
unsigned int columnCounterRight = 0;
|
||||
|
||||
// Count the number of columns that are pure black.
|
||||
for (int i = 0; i < image.width(); i++) {
|
||||
CImg<unsigned char> imageColumn = image.get_columns(i, i);
|
||||
// Ignore the alpha channel.
|
||||
imageColumn.channels(0, 2);
|
||||
pixelValueSum = imageColumn.sum();
|
||||
if (pixelValueSum == 0.0l)
|
||||
columnCounterLeft++;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
for (int i = image.width() - 1; i > 0; i--) {
|
||||
CImg<unsigned char> imageColumn = image.get_columns(i, i);
|
||||
imageColumn.channels(0, 2);
|
||||
pixelValueSum = imageColumn.sum();
|
||||
if (pixelValueSum == 0.0l)
|
||||
columnCounterRight++;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
if (columnCounterLeft > 0)
|
||||
image.crop(columnCounterLeft, 0, 0, 3, image.width() - 1, image.height() - 1, 0, 0);
|
||||
|
||||
if (columnCounterRight > 0)
|
||||
image.crop(0, 0, 0, 3, image.width() - columnCounterRight - 1, image.height() - 1, 0, 0);
|
||||
}
|
||||
|
||||
void MiximageGenerator::removeTransparentPadding(CImg<unsigned char>& image)
|
||||
{
|
||||
if (image.spectrum() != 4)
|
||||
return;
|
||||
|
||||
double pixelValueSum = 0.0l;
|
||||
int rowCounterUpper = 0;
|
||||
int rowCounterLower = 0;
|
||||
unsigned int columnCounterLeft = 0;
|
||||
unsigned int columnCounterRight = 0;
|
||||
|
||||
// Count the number of rows and columns that are completely transparent.
|
||||
for (int i = image.height() - 1; i > 0; i--) {
|
||||
CImg<unsigned char> imageRow = image.get_rows(i, i);
|
||||
pixelValueSum = imageRow.get_shared_channel(3).sum();
|
||||
if (pixelValueSum == 0.0l)
|
||||
rowCounterUpper++;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
for (int i = 0; i < image.height(); i++) {
|
||||
CImg<unsigned char> imageRow = image.get_rows(i, i);
|
||||
pixelValueSum = imageRow.get_shared_channel(3).sum();
|
||||
if (pixelValueSum == 0.0l)
|
||||
rowCounterLower++;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
for (int i = 0; i < image.width(); i++) {
|
||||
CImg<unsigned char> imageColumn = image.get_columns(i, i);
|
||||
pixelValueSum = imageColumn.get_shared_channel(3).sum();
|
||||
if (pixelValueSum == 0.0l)
|
||||
columnCounterLeft++;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
for (int i = image.width() - 1; i > 0; i--) {
|
||||
CImg<unsigned char> imageColumn = image.get_columns(i, i);
|
||||
pixelValueSum = imageColumn.get_shared_channel(3).sum();
|
||||
if (pixelValueSum == 0.0l)
|
||||
columnCounterRight++;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
if (rowCounterUpper > 0)
|
||||
image.crop(0, 0, 0, 3, image.width() - 1, image.height() - 1 - rowCounterUpper, 0, 0);
|
||||
|
||||
if (rowCounterLower > 0)
|
||||
image.crop(0, rowCounterLower, 0, 3, image.width() - 1, image.height() - 1, 0, 0);
|
||||
|
||||
if (columnCounterLeft > 0)
|
||||
image.crop(columnCounterLeft, 0, 0, 3, image.width() - 1, image.height() - 1, 0, 0);
|
||||
|
||||
if (columnCounterRight > 0)
|
||||
image.crop(0, 0, 0, 3, image.width() - columnCounterRight - 1, image.height() - 1, 0, 0);
|
||||
}
|
||||
|
||||
void MiximageGenerator::addDropShadow(CImg<unsigned char>& image, unsigned int shadowDistance)
|
||||
{
|
||||
// Make the shadow image larger than the source image to leave space for the drop shadow.
|
||||
CImg<unsigned char> shadowImage(image.width() + shadowDistance * 3,
|
||||
image.height() + shadowDistance * 3, 1, 4, 0);
|
||||
|
||||
// Create a mask image.
|
||||
CImg<unsigned char> maskImage(image.width(), image.height(), 1, 4, 0);
|
||||
maskImage.draw_image(0, 0, image);
|
||||
// Fill the RGB channels with white so we end up with a simple mask.
|
||||
maskImage.get_shared_channels(0, 2).fill(255);
|
||||
|
||||
// Make a black outline of the source image as a basis for the shadow.
|
||||
shadowImage.draw_image(shadowDistance, shadowDistance, image);
|
||||
shadowImage.get_shared_channels(0, 2).fill(0);
|
||||
// Lower the transparency and apply the blur.
|
||||
shadowImage.get_shared_channel(3) /= 0.6f;
|
||||
shadowImage.blur_box(static_cast<const float>(shadowDistance),
|
||||
static_cast<const float>(shadowDistance), 1, true, 4);
|
||||
|
||||
// Add the mask to the alpha channel of the shadow image.
|
||||
shadowImage.get_shared_channel(3).draw_image(0, 0, maskImage.get_shared_channels(0, 0),
|
||||
maskImage.get_shared_channel(3), 1, 255);
|
||||
// Draw the source image on top of the shadow image.
|
||||
shadowImage.draw_image(0, 0, image.get_shared_channels(0, 2),
|
||||
image.get_shared_channel(3), 1, 255);
|
||||
// Remove the any unused space that we added to leave room for the shadow.
|
||||
removeTransparentPadding(shadowImage);
|
||||
|
||||
image = shadowImage;
|
||||
}
|
||||
|
||||
void MiximageGenerator::calculateMarqueeSize(const unsigned int& targetWidth,
|
||||
const unsigned int& targetHeight, unsigned int& width, unsigned int& height)
|
||||
{
|
||||
|
@ -810,35 +648,6 @@ void MiximageGenerator::sampleFrameColor(CImg<unsigned char>& screenshotImage,
|
|||
frameColor[3] = 255;
|
||||
}
|
||||
|
||||
void MiximageGenerator::convertToCImgFormat(CImg<unsigned char>& image,
|
||||
std::vector<unsigned char> imageVector)
|
||||
{
|
||||
// CImg does not interleave the pixels as in RGBARGBARGBA so a conversion is required.
|
||||
int counter = 0;
|
||||
for (int r = 0; r < image.height(); r++) {
|
||||
for (int c = 0; c < image.width(); c++) {
|
||||
image(c, r, 0, 0) = imageVector[counter + 2];
|
||||
image(c, r, 0, 1) = imageVector[counter + 1];
|
||||
image(c, r, 0, 2) = imageVector[counter + 0];
|
||||
image(c, r, 0, 3) = imageVector[counter + 3];
|
||||
counter += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MiximageGenerator::convertFromCImgFormat(CImg<unsigned char> image,
|
||||
std::vector<unsigned char>& imageVector)
|
||||
{
|
||||
for (int r = image.height() - 1; r >= 0; r--) {
|
||||
for (int c = 0; c < image.width(); c++) {
|
||||
imageVector.push_back((unsigned char)image(c,r,0,2));
|
||||
imageVector.push_back((unsigned char)image(c,r,0,1));
|
||||
imageVector.push_back((unsigned char)image(c,r,0,0));
|
||||
imageVector.push_back((unsigned char)image(c,r,0,3));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string MiximageGenerator::getSavePath()
|
||||
{
|
||||
const std::string name = Utils::FileSystem::getStem(mGame->getPath());
|
||||
|
|
|
@ -10,13 +10,10 @@
|
|||
#ifndef ES_APP_SCRAPERS_MIXIMAGE_GENERATOR_H
|
||||
#define ES_APP_SCRAPERS_MIXIMAGE_GENERATOR_H
|
||||
|
||||
// Disable the CImg display capabilities.
|
||||
#define cimg_display 0
|
||||
|
||||
#include "utils/CImgUtil.h"
|
||||
#include "FileData.h"
|
||||
#include "GuiComponent.h"
|
||||
|
||||
#include <CImg.h>
|
||||
#include <FreeImage.h>
|
||||
#include <future>
|
||||
|
||||
|
@ -32,17 +29,10 @@ public:
|
|||
|
||||
private:
|
||||
bool generateImage();
|
||||
void cropLetterboxes(CImg<unsigned char>& image);
|
||||
void cropPillarboxes(CImg<unsigned char>& image);
|
||||
void removeTransparentPadding(CImg<unsigned char>& image);
|
||||
void addDropShadow(CImg<unsigned char>& image, unsigned int shadowDistance);
|
||||
void calculateMarqueeSize(const unsigned int& targetWidth, const unsigned int& targetHeight,
|
||||
unsigned int& width, unsigned int& height);
|
||||
void sampleFrameColor(CImg<unsigned char>& screenshotImage, unsigned char (&frameColor)[4]);
|
||||
|
||||
void convertToCImgFormat(CImg<unsigned char>& image, std::vector<unsigned char> imageVector);
|
||||
void convertFromCImgFormat(CImg<unsigned char> image, std::vector<unsigned char>& imageVector);
|
||||
|
||||
std::string getSavePath();
|
||||
|
||||
FileData* mGame;
|
||||
|
|
|
@ -78,6 +78,7 @@ set(CORE_HEADERS
|
|||
${CMAKE_CURRENT_SOURCE_DIR}/src/resources/TextureResource.h
|
||||
|
||||
# Utils
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/utils/CImgUtil.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/utils/FileSystemUtil.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/utils/StringUtil.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/utils/TimeUtil.h
|
||||
|
@ -157,6 +158,7 @@ set(CORE_SOURCES
|
|||
${CMAKE_CURRENT_SOURCE_DIR}/src/resources/TextureDataManager.cpp
|
||||
|
||||
# Utils
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/utils/CImgUtil.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/utils/FileSystemUtil.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/utils/StringUtil.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/utils/TimeUtil.cpp
|
||||
|
|
275
es-core/src/utils/CImgUtil.cpp
Normal file
275
es-core/src/utils/CImgUtil.cpp
Normal file
|
@ -0,0 +1,275 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
// EmulationStation Desktop Edition
|
||||
// CImgUtil.cpp
|
||||
//
|
||||
// Utility functions using the CImg image processing library.
|
||||
//
|
||||
|
||||
#include "utils/CImgUtil.h"
|
||||
|
||||
namespace Utils
|
||||
{
|
||||
namespace CImg
|
||||
{
|
||||
void convertRGBAToCImg(std::vector<unsigned char> imageRGBA,
|
||||
cimg_library::CImg<unsigned char>& image)
|
||||
{
|
||||
// CImg does not interleave the pixels as in RGBARGBARGBA so a conversion is required.
|
||||
int counter = 0;
|
||||
for (int r = 0; r < image.height(); r++) {
|
||||
for (int c = 0; c < image.width(); c++) {
|
||||
image(c, r, 0, 0) = imageRGBA[counter + 2];
|
||||
image(c, r, 0, 1) = imageRGBA[counter + 1];
|
||||
image(c, r, 0, 2) = imageRGBA[counter + 0];
|
||||
image(c, r, 0, 3) = imageRGBA[counter + 3];
|
||||
counter += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void convertCImgToRGBA(cimg_library::CImg<unsigned char> image,
|
||||
std::vector<unsigned char>& imageRGBA)
|
||||
{
|
||||
for (int r = image.height() - 1; r >= 0; r--) {
|
||||
for (int c = 0; c < image.width(); c++) {
|
||||
imageRGBA.push_back((unsigned char)image(c,r,0,2));
|
||||
imageRGBA.push_back((unsigned char)image(c,r,0,1));
|
||||
imageRGBA.push_back((unsigned char)image(c,r,0,0));
|
||||
imageRGBA.push_back((unsigned char)image(c,r,0,3));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void getTransparentPaddingCoords(cimg_library::CImg<unsigned char>& image,
|
||||
int (&imageCoords)[4])
|
||||
{
|
||||
// Check that the image actually has an alpha channel.
|
||||
if (image.spectrum() != 4)
|
||||
return;
|
||||
|
||||
double pixelValueSum = 0.0l;
|
||||
int rowCounterTop = 0;
|
||||
int rowCounterBottom = 0;
|
||||
unsigned int columnCounterLeft = 0;
|
||||
unsigned int columnCounterRight = 0;
|
||||
|
||||
// Count the number of rows and columns that are completely transparent.
|
||||
for (int i = image.height() - 1; i > 0; i--) {
|
||||
cimg_library::CImg<unsigned char> imageRow = image.get_rows(i, i);
|
||||
pixelValueSum = imageRow.get_shared_channel(3).sum();
|
||||
if (pixelValueSum == 0.0l)
|
||||
rowCounterTop++;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
for (int i = 0; i < image.height(); i++) {
|
||||
cimg_library::CImg<unsigned char> imageRow = image.get_rows(i, i);
|
||||
pixelValueSum = imageRow.get_shared_channel(3).sum();
|
||||
if (pixelValueSum == 0.0l)
|
||||
rowCounterBottom++;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
for (int i = 0; i < image.width(); i++) {
|
||||
cimg_library::CImg<unsigned char> imageColumn = image.get_columns(i, i);
|
||||
pixelValueSum = imageColumn.get_shared_channel(3).sum();
|
||||
if (pixelValueSum == 0.0l)
|
||||
columnCounterLeft++;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
for (int i = image.width() - 1; i > 0; i--) {
|
||||
cimg_library::CImg<unsigned char> imageColumn = image.get_columns(i, i);
|
||||
pixelValueSum = imageColumn.get_shared_channel(3).sum();
|
||||
if (pixelValueSum == 0.0l)
|
||||
columnCounterRight++;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
imageCoords[0] = columnCounterLeft;
|
||||
imageCoords[1] = rowCounterTop;
|
||||
imageCoords[2] = columnCounterRight;
|
||||
imageCoords[3] = rowCounterBottom;
|
||||
}
|
||||
|
||||
void removeTransparentPadding(cimg_library::CImg<unsigned char>& image)
|
||||
{
|
||||
// Check that the image actually has an alpha channel.
|
||||
if (image.spectrum() != 4)
|
||||
return;
|
||||
|
||||
double pixelValueSum = 0.0l;
|
||||
int rowCounterTop = 0;
|
||||
int rowCounterBottom = 0;
|
||||
unsigned int columnCounterLeft = 0;
|
||||
unsigned int columnCounterRight = 0;
|
||||
|
||||
// Count the number of rows and columns that are completely transparent.
|
||||
for (int i = image.height() - 1; i > 0; i--) {
|
||||
cimg_library::CImg<unsigned char> imageRow = image.get_rows(i, i);
|
||||
pixelValueSum = imageRow.get_shared_channel(3).sum();
|
||||
if (pixelValueSum == 0.0l)
|
||||
rowCounterTop++;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
for (int i = 0; i < image.height(); i++) {
|
||||
cimg_library::CImg<unsigned char> imageRow = image.get_rows(i, i);
|
||||
pixelValueSum = imageRow.get_shared_channel(3).sum();
|
||||
if (pixelValueSum == 0.0l)
|
||||
rowCounterBottom++;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
for (int i = 0; i < image.width(); i++) {
|
||||
cimg_library::CImg<unsigned char> imageColumn = image.get_columns(i, i);
|
||||
pixelValueSum = imageColumn.get_shared_channel(3).sum();
|
||||
if (pixelValueSum == 0.0l)
|
||||
columnCounterLeft++;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
for (int i = image.width() - 1; i > 0; i--) {
|
||||
cimg_library::CImg<unsigned char> imageColumn = image.get_columns(i, i);
|
||||
pixelValueSum = imageColumn.get_shared_channel(3).sum();
|
||||
if (pixelValueSum == 0.0l)
|
||||
columnCounterRight++;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
if (rowCounterTop > 0)
|
||||
image.crop(0, 0, 0, 3, image.width() - 1, image.height() - 1 -
|
||||
rowCounterTop, 0, 0);
|
||||
|
||||
if (rowCounterBottom > 0)
|
||||
image.crop(0, rowCounterBottom, 0, 3, image.width() - 1, image.height() - 1, 0, 0);
|
||||
|
||||
if (columnCounterLeft > 0)
|
||||
image.crop(columnCounterLeft, 0, 0, 3, image.width() - 1, image.height() - 1, 0, 0);
|
||||
|
||||
if (columnCounterRight > 0)
|
||||
image.crop(0, 0, 0, 3, image.width() - columnCounterRight - 1,
|
||||
image.height() - 1, 0, 0);
|
||||
}
|
||||
|
||||
void cropLetterboxes(cimg_library::CImg<unsigned char>& image)
|
||||
{
|
||||
double pixelValueSum = 0.0l;
|
||||
int rowCounterUpper = 0;
|
||||
int rowCounterLower = 0;
|
||||
|
||||
// Count the number of rows that are pure black.
|
||||
for (int i = image.height() - 1; i > 0; i--) {
|
||||
cimg_library::CImg<unsigned char> imageRow = image.get_rows(i, i);
|
||||
// Ignore the alpha channel.
|
||||
imageRow.channels(0, 2);
|
||||
pixelValueSum = imageRow.sum();
|
||||
if (pixelValueSum == 0.0l)
|
||||
rowCounterUpper++;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
for (int i = 0; i < image.height(); i++) {
|
||||
cimg_library::CImg<unsigned char> imageRow = image.get_rows(i, i);
|
||||
imageRow.channels(0, 2);
|
||||
pixelValueSum = imageRow.sum();
|
||||
if (pixelValueSum == 0.0l)
|
||||
rowCounterLower++;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
if (rowCounterUpper > 0)
|
||||
image.crop(0, 0, 0, 3, image.width() - 1, image.height() - 1 -
|
||||
rowCounterUpper, 0, 0);
|
||||
|
||||
if (rowCounterLower > 0)
|
||||
image.crop(0, rowCounterLower, 0, 3, image.width() - 1, image.height() - 1, 0, 0);
|
||||
}
|
||||
|
||||
void cropPillarboxes(cimg_library::CImg<unsigned char>& image)
|
||||
{
|
||||
double pixelValueSum = 0.0l;
|
||||
unsigned int columnCounterLeft = 0;
|
||||
unsigned int columnCounterRight = 0;
|
||||
|
||||
// Count the number of columns that are pure black.
|
||||
for (int i = 0; i < image.width(); i++) {
|
||||
cimg_library::CImg<unsigned char> imageColumn = image.get_columns(i, i);
|
||||
// Ignore the alpha channel.
|
||||
imageColumn.channels(0, 2);
|
||||
pixelValueSum = imageColumn.sum();
|
||||
if (pixelValueSum == 0.0l)
|
||||
columnCounterLeft++;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
for (int i = image.width() - 1; i > 0; i--) {
|
||||
cimg_library::CImg<unsigned char> imageColumn = image.get_columns(i, i);
|
||||
imageColumn.channels(0, 2);
|
||||
pixelValueSum = imageColumn.sum();
|
||||
if (pixelValueSum == 0.0l)
|
||||
columnCounterRight++;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
if (columnCounterLeft > 0)
|
||||
image.crop(columnCounterLeft, 0, 0, 3, image.width() - 1, image.height() - 1, 0, 0);
|
||||
|
||||
if (columnCounterRight > 0)
|
||||
image.crop(0, 0, 0, 3, image.width() - columnCounterRight - 1,
|
||||
image.height() - 1, 0, 0);
|
||||
}
|
||||
|
||||
void addDropShadow(cimg_library::CImg<unsigned char>& image, unsigned int shadowDistance,
|
||||
float transparency, unsigned int iterations)
|
||||
{
|
||||
// Check that the image actually has an alpha channel.
|
||||
if (image.spectrum() != 4)
|
||||
return;
|
||||
|
||||
// Make the shadow image larger than the source image to leave space for the drop shadow.
|
||||
cimg_library::CImg<unsigned char> shadowImage(image.width() + shadowDistance * 3,
|
||||
image.height() + shadowDistance * 3, 1, 4, 0);
|
||||
|
||||
// Create a mask image.
|
||||
cimg_library::CImg<unsigned char> maskImage(image.width(), image.height(), 1, 4, 0);
|
||||
maskImage.draw_image(0, 0, image);
|
||||
// Fill the RGB channels with white so we end up with a simple mask.
|
||||
maskImage.get_shared_channels(0, 2).fill(255);
|
||||
|
||||
// Make a black outline of the source image as a basis for the shadow.
|
||||
shadowImage.draw_image(shadowDistance, shadowDistance, image);
|
||||
shadowImage.get_shared_channels(0, 2).fill(0);
|
||||
// Lower the transparency and apply the blur.
|
||||
shadowImage.get_shared_channel(3) /= transparency;
|
||||
shadowImage.blur_box(static_cast<const float>(shadowDistance),
|
||||
static_cast<const float>(shadowDistance), 1, true, iterations);
|
||||
|
||||
// Add the mask to the alpha channel of the shadow image.
|
||||
shadowImage.get_shared_channel(3).draw_image(0, 0, maskImage.get_shared_channels(0, 0),
|
||||
maskImage.get_shared_channel(3), 1, 255);
|
||||
// Draw the source image on top of the shadow image.
|
||||
shadowImage.draw_image(0, 0, image.get_shared_channels(0, 2),
|
||||
image.get_shared_channel(3), 1, 255);
|
||||
// Remove the any unused space that we added to leave room for the shadow.
|
||||
removeTransparentPadding(shadowImage);
|
||||
|
||||
image = shadowImage;
|
||||
}
|
||||
|
||||
} // CImg::
|
||||
|
||||
} // Utils::
|
36
es-core/src/utils/CImgUtil.h
Normal file
36
es-core/src/utils/CImgUtil.h
Normal file
|
@ -0,0 +1,36 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
// EmulationStation Desktop Edition
|
||||
// CImgUtil.h
|
||||
//
|
||||
// Utility functions using the CImg image processing library.
|
||||
//
|
||||
|
||||
#ifndef ES_CORE_UTILS_CIMG_UTIL_H
|
||||
#define ES_CORE_UTILS_CIMG_UTIL_H
|
||||
|
||||
// Disable the CImg display capabilities.
|
||||
#define cimg_display 0
|
||||
|
||||
#include <CImg.h>
|
||||
#include <vector>
|
||||
|
||||
namespace Utils
|
||||
{
|
||||
namespace CImg
|
||||
{
|
||||
void convertRGBAToCImg(std::vector<unsigned char> imageRGBA,
|
||||
cimg_library::CImg<unsigned char>& image);
|
||||
void convertCImgToRGBA(cimg_library::CImg<unsigned char> image,
|
||||
std::vector<unsigned char>& imageRGBA);
|
||||
void getTransparentPaddingCoords(cimg_library::CImg<unsigned char>& image,
|
||||
int (&imageCoords)[4]);
|
||||
void removeTransparentPadding(cimg_library::CImg<unsigned char>& image);
|
||||
void cropLetterboxes(cimg_library::CImg<unsigned char>& image);
|
||||
void cropPillarboxes(cimg_library::CImg<unsigned char>& image);
|
||||
void addDropShadow(cimg_library::CImg<unsigned char>& image, unsigned int shadowDistance,
|
||||
float transparency, unsigned int iterations);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // ES_CORE_UTILS_CIMG_UTIL_H
|
Loading…
Reference in a new issue