mirror of
https://github.com/bsnes-emu/bsnes.git
synced 2025-09-03 12:13:19 +02:00
Update to v094r27 release.
byuu says: Added AWJ's fixes for alt/cpu (Tetris Attack framelines issue) and alt/dsp (Thread::clock reset) Added fix so that the taskbar entry appears when the application first starts on Windows. Fixed checkbox toggling inside of list views on Windows. Updated nall/image to properly protect variables that should not be written externally. New Object syntax for hiro is in. Fixed the backwards-typing on Windows with the state manager. NOTE: the list view isn't redrawing when you change the description text. It does so on the cheat editor because of the resizeColumns call; but that shouldn't be necessary. I'll try and fix this for the next WIP.
This commit is contained in:
@@ -4,35 +4,6 @@
|
||||
namespace nall {
|
||||
|
||||
struct image {
|
||||
uint8_t* data = nullptr;
|
||||
unsigned width = 0;
|
||||
unsigned height = 0;
|
||||
unsigned pitch = 0;
|
||||
unsigned size = 0;
|
||||
|
||||
bool endian = 0; //0 = lsb, 1 = msb
|
||||
unsigned depth = 32;
|
||||
unsigned stride = 4;
|
||||
|
||||
struct channel {
|
||||
uint64_t mask;
|
||||
unsigned depth;
|
||||
unsigned shift;
|
||||
|
||||
inline bool operator==(const channel& source) const {
|
||||
return mask == source.mask && depth == source.depth && shift == source.shift;
|
||||
}
|
||||
|
||||
inline bool operator!=(const channel& source) const {
|
||||
return !operator==(source);
|
||||
}
|
||||
};
|
||||
|
||||
channel alpha = {255u << 24, 8u, 24u};
|
||||
channel red = {255u << 16, 8u, 16u};
|
||||
channel green = {255u << 8, 8u, 8u};
|
||||
channel blue = {255u << 0, 8u, 0u};
|
||||
|
||||
enum class blend : unsigned {
|
||||
add,
|
||||
sourceAlpha, //color = sourceColor * sourceAlpha + targetColor * (1 - sourceAlpha)
|
||||
@@ -41,18 +12,29 @@ struct image {
|
||||
targetColor, //color = targetColor
|
||||
};
|
||||
|
||||
//static.hpp
|
||||
static inline unsigned bitDepth(uint64_t color);
|
||||
static inline unsigned bitShift(uint64_t color);
|
||||
static inline uint64_t normalize(uint64_t color, unsigned sourceDepth, unsigned targetDepth);
|
||||
struct channel {
|
||||
channel(uint64_t mask, unsigned depth, unsigned shift) : _mask(mask), _depth(depth), _shift(shift) {
|
||||
}
|
||||
|
||||
auto operator==(const channel& source) const -> bool {
|
||||
return _mask == source._mask && _depth == source._depth && _shift == source._shift;
|
||||
}
|
||||
|
||||
auto operator!=(const channel& source) const -> bool {
|
||||
return !operator==(source);
|
||||
}
|
||||
|
||||
alwaysinline auto mask() const { return _mask; }
|
||||
alwaysinline auto depth() const { return _depth; }
|
||||
alwaysinline auto shift() const { return _shift; }
|
||||
|
||||
private:
|
||||
uint64_t _mask;
|
||||
unsigned _depth;
|
||||
unsigned _shift;
|
||||
};
|
||||
|
||||
//core.hpp
|
||||
inline explicit operator bool() const;
|
||||
inline bool operator==(const image& source) const;
|
||||
inline bool operator!=(const image& source) const;
|
||||
|
||||
inline image& operator=(const image& source);
|
||||
inline image& operator=(image&& source);
|
||||
inline image(const image& source);
|
||||
inline image(image&& source);
|
||||
inline image(bool endian, unsigned depth, uint64_t alphaMask, uint64_t redMask, uint64_t greenMask, uint64_t blueMask);
|
||||
@@ -62,63 +44,106 @@ struct image {
|
||||
inline image();
|
||||
inline ~image();
|
||||
|
||||
inline uint64_t read(const uint8_t* data) const;
|
||||
inline void write(uint8_t* data, uint64_t value) const;
|
||||
inline auto operator=(const image& source) -> image&;
|
||||
inline auto operator=(image&& source) -> image&;
|
||||
|
||||
inline void free();
|
||||
inline bool empty() const;
|
||||
inline bool load(const string& filename);
|
||||
inline void allocate(unsigned width, unsigned height);
|
||||
inline explicit operator bool() const;
|
||||
inline auto operator==(const image& source) const -> bool;
|
||||
inline auto operator!=(const image& source) const -> bool;
|
||||
|
||||
inline auto read(const uint8_t* data) const -> uint64_t;
|
||||
inline auto write(uint8_t* data, uint64_t value) const -> void;
|
||||
|
||||
inline auto free() -> void;
|
||||
inline auto empty() const -> bool;
|
||||
inline auto load(const string& filename) -> bool;
|
||||
inline auto allocate(unsigned width, unsigned height) -> void;
|
||||
|
||||
//fill.hpp
|
||||
inline void fill(uint64_t color = 0);
|
||||
inline void gradient(uint64_t a, uint64_t b, uint64_t c, uint64_t d);
|
||||
inline void gradient(uint64_t a, uint64_t b, signed radiusX, signed radiusY, signed centerX, signed centerY, function<double (double, double)> callback);
|
||||
inline void crossGradient(uint64_t a, uint64_t b, signed radiusX, signed radiusY, signed centerX, signed centerY);
|
||||
inline void diamondGradient(uint64_t a, uint64_t b, signed radiusX, signed radiusY, signed centerX, signed centerY);
|
||||
inline void horizontalGradient(uint64_t a, uint64_t b, signed radiusX, signed radiusY, signed centerX, signed centerY);
|
||||
inline void radialGradient(uint64_t a, uint64_t b, signed radiusX, signed radiusY, signed centerX, signed centerY);
|
||||
inline void sphericalGradient(uint64_t a, uint64_t b, signed radiusX, signed radiusY, signed centerX, signed centerY);
|
||||
inline void squareGradient(uint64_t a, uint64_t b, signed radiusX, signed radiusY, signed centerX, signed centerY);
|
||||
inline void verticalGradient(uint64_t a, uint64_t b, signed radiusX, signed radiusY, signed centerX, signed centerY);
|
||||
inline auto fill(uint64_t color = 0) -> void;
|
||||
inline auto gradient(uint64_t a, uint64_t b, uint64_t c, uint64_t d) -> void;
|
||||
inline auto gradient(uint64_t a, uint64_t b, signed radiusX, signed radiusY, signed centerX, signed centerY, function<double (double, double)> callback) -> void;
|
||||
inline auto crossGradient(uint64_t a, uint64_t b, signed radiusX, signed radiusY, signed centerX, signed centerY) -> void;
|
||||
inline auto diamondGradient(uint64_t a, uint64_t b, signed radiusX, signed radiusY, signed centerX, signed centerY) -> void;
|
||||
inline auto horizontalGradient(uint64_t a, uint64_t b, signed radiusX, signed radiusY, signed centerX, signed centerY) -> void;
|
||||
inline auto radialGradient(uint64_t a, uint64_t b, signed radiusX, signed radiusY, signed centerX, signed centerY) -> void;
|
||||
inline auto sphericalGradient(uint64_t a, uint64_t b, signed radiusX, signed radiusY, signed centerX, signed centerY) -> void;
|
||||
inline auto squareGradient(uint64_t a, uint64_t b, signed radiusX, signed radiusY, signed centerX, signed centerY) -> void;
|
||||
inline auto verticalGradient(uint64_t a, uint64_t b, signed radiusX, signed radiusY, signed centerX, signed centerY) -> void;
|
||||
|
||||
//scale.hpp
|
||||
inline void scale(unsigned width, unsigned height, bool linear = true);
|
||||
inline auto scale(unsigned width, unsigned height, bool linear = true) -> void;
|
||||
|
||||
//blend.hpp
|
||||
inline void impose(blend mode, unsigned targetX, unsigned targetY, image source, unsigned x, unsigned y, unsigned width, unsigned height);
|
||||
inline auto impose(blend mode, unsigned targetX, unsigned targetY, image source, unsigned x, unsigned y, unsigned width, unsigned height) -> void;
|
||||
|
||||
//utility.hpp
|
||||
inline bool crop(unsigned x, unsigned y, unsigned width, unsigned height);
|
||||
inline void alphaBlend(uint64_t alphaColor);
|
||||
inline void transform(bool endian, unsigned depth, uint64_t alphaMask, uint64_t redMask, uint64_t greenMask, uint64_t blueMask);
|
||||
inline auto crop(unsigned x, unsigned y, unsigned width, unsigned height) -> bool;
|
||||
inline auto alphaBlend(uint64_t alphaColor) -> void;
|
||||
inline auto transform(const image& source = {}) -> void;
|
||||
inline auto transform(bool endian, unsigned depth, uint64_t alphaMask, uint64_t redMask, uint64_t greenMask, uint64_t blueMask) -> void;
|
||||
|
||||
protected:
|
||||
//static.hpp
|
||||
static inline auto bitDepth(uint64_t color) -> unsigned;
|
||||
static inline auto bitShift(uint64_t color) -> unsigned;
|
||||
static inline auto normalize(uint64_t color, unsigned sourceDepth, unsigned targetDepth) -> uint64_t;
|
||||
|
||||
//access
|
||||
alwaysinline auto data() { return _data; }
|
||||
alwaysinline auto data() const { return _data; }
|
||||
alwaysinline auto width() const { return _width; }
|
||||
alwaysinline auto height() const { return _height; }
|
||||
|
||||
alwaysinline auto endian() const { return _endian; }
|
||||
alwaysinline auto depth() const { return _depth; }
|
||||
alwaysinline auto stride() const { return (_depth + 7) >> 3; }
|
||||
|
||||
alwaysinline auto pitch() const { return _width * stride(); }
|
||||
alwaysinline auto size() const { return _height * pitch(); }
|
||||
|
||||
alwaysinline auto alpha() const { return _alpha; }
|
||||
alwaysinline auto red() const { return _red; }
|
||||
alwaysinline auto green() const { return _green; }
|
||||
alwaysinline auto blue() const { return _blue; }
|
||||
|
||||
private:
|
||||
//core.hpp
|
||||
inline uint8_t* allocate(unsigned width, unsigned height, unsigned stride);
|
||||
inline auto allocate(unsigned width, unsigned height, unsigned stride) -> uint8_t*;
|
||||
|
||||
//scale.hpp
|
||||
inline void scaleLinearWidth(unsigned width);
|
||||
inline void scaleLinearHeight(unsigned height);
|
||||
inline void scaleLinear(unsigned width, unsigned height);
|
||||
inline void scaleNearest(unsigned width, unsigned height);
|
||||
inline auto scaleLinearWidth(unsigned width) -> void;
|
||||
inline auto scaleLinearHeight(unsigned height) -> void;
|
||||
inline auto scaleLinear(unsigned width, unsigned height) -> void;
|
||||
inline auto scaleNearest(unsigned width, unsigned height) -> void;
|
||||
|
||||
//load.hpp
|
||||
inline bool loadBMP(const string& filename);
|
||||
inline bool loadPNG(const string& filename);
|
||||
inline bool loadPNG(const uint8_t* data, unsigned size);
|
||||
inline auto loadBMP(const string& filename) -> bool;
|
||||
inline auto loadPNG(const string& filename) -> bool;
|
||||
inline auto loadPNG(const uint8_t* data, unsigned size) -> bool;
|
||||
|
||||
//interpolation.hpp
|
||||
alwaysinline void isplit(uint64_t* component, uint64_t color);
|
||||
alwaysinline uint64_t imerge(const uint64_t* component);
|
||||
alwaysinline uint64_t interpolate1f(uint64_t a, uint64_t b, double x);
|
||||
alwaysinline uint64_t interpolate1f(uint64_t a, uint64_t b, uint64_t c, uint64_t d, double x, double y);
|
||||
alwaysinline uint64_t interpolate1i(int64_t a, int64_t b, uint32_t x);
|
||||
alwaysinline uint64_t interpolate1i(int64_t a, int64_t b, int64_t c, int64_t d, uint32_t x, uint32_t y);
|
||||
inline uint64_t interpolate4f(uint64_t a, uint64_t b, double x);
|
||||
inline uint64_t interpolate4f(uint64_t a, uint64_t b, uint64_t c, uint64_t d, double x, double y);
|
||||
inline uint64_t interpolate4i(uint64_t a, uint64_t b, uint32_t x);
|
||||
inline uint64_t interpolate4i(uint64_t a, uint64_t b, uint64_t c, uint64_t d, uint32_t x, uint32_t y);
|
||||
alwaysinline auto isplit(uint64_t* component, uint64_t color) -> void;
|
||||
alwaysinline auto imerge(const uint64_t* component) -> uint64_t;
|
||||
alwaysinline auto interpolate1f(uint64_t a, uint64_t b, double x) -> uint64_t;
|
||||
alwaysinline auto interpolate1f(uint64_t a, uint64_t b, uint64_t c, uint64_t d, double x, double y) -> uint64_t;
|
||||
alwaysinline auto interpolate1i(int64_t a, int64_t b, uint32_t x) -> uint64_t;
|
||||
alwaysinline auto interpolate1i(int64_t a, int64_t b, int64_t c, int64_t d, uint32_t x, uint32_t y) -> uint64_t;
|
||||
inline auto interpolate4f(uint64_t a, uint64_t b, double x) -> uint64_t;
|
||||
inline auto interpolate4f(uint64_t a, uint64_t b, uint64_t c, uint64_t d, double x, double y) -> uint64_t;
|
||||
inline auto interpolate4i(uint64_t a, uint64_t b, uint32_t x) -> uint64_t;
|
||||
inline auto interpolate4i(uint64_t a, uint64_t b, uint64_t c, uint64_t d, uint32_t x, uint32_t y) -> uint64_t;
|
||||
|
||||
uint8_t* _data = nullptr;
|
||||
unsigned _width = 0;
|
||||
unsigned _height = 0;
|
||||
|
||||
bool _endian = 0; //0 = lsb, 1 = msb
|
||||
unsigned _depth = 32;
|
||||
|
||||
channel _alpha{255u << 24, 8, 24};
|
||||
channel _red {255u << 16, 8, 16};
|
||||
channel _green{255u << 8, 8, 8};
|
||||
channel _blue {255u << 0, 8, 0};
|
||||
};
|
||||
|
||||
}
|
||||
|
@@ -3,41 +3,42 @@
|
||||
|
||||
namespace nall {
|
||||
|
||||
void image::impose(blend mode, unsigned targetX, unsigned targetY, image source, unsigned sourceX, unsigned sourceY, unsigned sourceWidth, unsigned sourceHeight) {
|
||||
source.transform(endian, depth, alpha.mask, red.mask, green.mask, blue.mask);
|
||||
auto image::impose(blend mode, unsigned targetX, unsigned targetY, image source, unsigned sourceX, unsigned sourceY, unsigned sourceWidth, unsigned sourceHeight) -> void {
|
||||
source.transform(_endian, _depth, _alpha.mask(), _red.mask(), _green.mask(), _blue.mask());
|
||||
|
||||
#pragma omp parallel for
|
||||
for(unsigned y = 0; y < sourceHeight; y++) {
|
||||
const uint8_t* sp = source.data + source.pitch * (sourceY + y) + source.stride * sourceX;
|
||||
uint8_t* dp = data + pitch * (targetY + y) + stride * targetX;
|
||||
const uint8_t* sp = source._data + source.pitch() * (sourceY + y) + source.stride() * sourceX;
|
||||
uint8_t* dp = _data + pitch() * (targetY + y) + stride() * targetX;
|
||||
for(unsigned x = 0; x < sourceWidth; x++) {
|
||||
uint64_t sourceColor = source.read(sp);
|
||||
uint64_t targetColor = read(dp);
|
||||
|
||||
int64_t sa = (sourceColor & alpha.mask) >> alpha.shift;
|
||||
int64_t sr = (sourceColor & red.mask ) >> red.shift;
|
||||
int64_t sg = (sourceColor & green.mask) >> green.shift;
|
||||
int64_t sb = (sourceColor & blue.mask ) >> blue.shift;
|
||||
int64_t sa = (sourceColor & _alpha.mask()) >> _alpha.shift();
|
||||
int64_t sr = (sourceColor & _red.mask() ) >> _red.shift();
|
||||
int64_t sg = (sourceColor & _green.mask()) >> _green.shift();
|
||||
int64_t sb = (sourceColor & _blue.mask() ) >> _blue.shift();
|
||||
|
||||
int64_t da = (targetColor & alpha.mask) >> alpha.shift;
|
||||
int64_t dr = (targetColor & red.mask ) >> red.shift;
|
||||
int64_t dg = (targetColor & green.mask) >> green.shift;
|
||||
int64_t db = (targetColor & blue.mask ) >> blue.shift;
|
||||
int64_t da = (targetColor & _alpha.mask()) >> _alpha.shift();
|
||||
int64_t dr = (targetColor & _red.mask() ) >> _red.shift();
|
||||
int64_t dg = (targetColor & _green.mask()) >> _green.shift();
|
||||
int64_t db = (targetColor & _blue.mask() ) >> _blue.shift();
|
||||
|
||||
uint64_t a, r, g, b;
|
||||
|
||||
switch(mode) {
|
||||
case blend::add:
|
||||
a = max(sa, da);
|
||||
r = min(red.mask >> red.shift, ((sr * sa) >> alpha.depth) + ((dr * da) >> alpha.depth));
|
||||
g = min(green.mask >> green.shift, ((sg * sa) >> alpha.depth) + ((dg * da) >> alpha.depth));
|
||||
b = min(blue.mask >> blue.shift, ((sb * sa) >> alpha.depth) + ((db * da) >> alpha.depth));
|
||||
r = min(_red.mask() >> _red.shift(), ((sr * sa) >> _alpha.depth()) + ((dr * da) >> _alpha.depth()));
|
||||
g = min(_green.mask() >> _green.shift(), ((sg * sa) >> _alpha.depth()) + ((dg * da) >> _alpha.depth()));
|
||||
b = min(_blue.mask() >> _blue.shift(), ((sb * sa) >> _alpha.depth()) + ((db * da) >> _alpha.depth()));
|
||||
break;
|
||||
|
||||
case blend::sourceAlpha:
|
||||
a = max(sa, da);
|
||||
r = dr + (((sr - dr) * sa) >> alpha.depth);
|
||||
g = dg + (((sg - dg) * sa) >> alpha.depth);
|
||||
b = db + (((sb - db) * sa) >> alpha.depth);
|
||||
r = dr + (((sr - dr) * sa) >> _alpha.depth());
|
||||
g = dg + (((sg - dg) * sa) >> _alpha.depth());
|
||||
b = db + (((sb - db) * sa) >> _alpha.depth());
|
||||
break;
|
||||
|
||||
case blend::sourceColor:
|
||||
@@ -49,9 +50,9 @@ void image::impose(blend mode, unsigned targetX, unsigned targetY, image source,
|
||||
|
||||
case blend::targetAlpha:
|
||||
a = max(sa, da);
|
||||
r = sr + (((dr - sr) * da) >> alpha.depth);
|
||||
g = sg + (((dg - sg) * da) >> alpha.depth);
|
||||
b = sb + (((db - sb) * da) >> alpha.depth);
|
||||
r = sr + (((dr - sr) * da) >> _alpha.depth());
|
||||
g = sg + (((dg - sg) * da) >> _alpha.depth());
|
||||
b = sb + (((db - sb) * da) >> _alpha.depth());
|
||||
break;
|
||||
|
||||
case blend::targetColor:
|
||||
@@ -62,9 +63,9 @@ void image::impose(blend mode, unsigned targetX, unsigned targetY, image source,
|
||||
break;
|
||||
}
|
||||
|
||||
write(dp, (a << alpha.shift) | (r << red.shift) | (g << green.shift) | (b << blue.shift));
|
||||
sp += source.stride;
|
||||
dp += stride;
|
||||
write(dp, (a << _alpha.shift()) | (r << _red.shift()) | (g << _green.shift()) | (b << _blue.shift()));
|
||||
sp += source.stride();
|
||||
dp += stride();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -3,91 +3,22 @@
|
||||
|
||||
namespace nall {
|
||||
|
||||
image::operator bool() const {
|
||||
return !empty();
|
||||
}
|
||||
|
||||
bool image::operator==(const image& source) const {
|
||||
if(width != source.width) return false;
|
||||
if(height != source.height) return false;
|
||||
if(pitch != source.pitch) return false;
|
||||
|
||||
if(endian != source.endian) return false;
|
||||
if(stride != source.stride) return false;
|
||||
|
||||
if(alpha != source.alpha) return false;
|
||||
if(red != source.red) return false;
|
||||
if(green != source.green) return false;
|
||||
if(blue != source.blue) return false;
|
||||
|
||||
return memcmp(data, source.data, width * height * stride) == 0;
|
||||
}
|
||||
|
||||
bool image::operator!=(const image& source) const {
|
||||
return !operator==(source);
|
||||
}
|
||||
|
||||
image& image::operator=(const image& source) {
|
||||
if(this == &source) return *this;
|
||||
free();
|
||||
|
||||
width = source.width;
|
||||
height = source.height;
|
||||
pitch = source.pitch;
|
||||
size = source.size;
|
||||
|
||||
endian = source.endian;
|
||||
stride = source.stride;
|
||||
|
||||
alpha = source.alpha;
|
||||
red = source.red;
|
||||
green = source.green;
|
||||
blue = source.blue;
|
||||
|
||||
data = allocate(width, height, stride);
|
||||
memcpy(data, source.data, source.size);
|
||||
return *this;
|
||||
}
|
||||
|
||||
image& image::operator=(image&& source) {
|
||||
if(this == &source) return *this;
|
||||
free();
|
||||
|
||||
width = source.width;
|
||||
height = source.height;
|
||||
pitch = source.pitch;
|
||||
size = source.size;
|
||||
|
||||
endian = source.endian;
|
||||
stride = source.stride;
|
||||
|
||||
alpha = source.alpha;
|
||||
red = source.red;
|
||||
green = source.green;
|
||||
blue = source.blue;
|
||||
|
||||
data = source.data;
|
||||
source.data = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
image::image(const image& source) {
|
||||
operator=(source);
|
||||
}
|
||||
|
||||
image::image(image&& source) {
|
||||
operator=(std::forward<image>(source));
|
||||
operator=(forward<image>(source));
|
||||
}
|
||||
|
||||
image::image(bool endian, unsigned depth, uint64_t alphaMask, uint64_t redMask, uint64_t greenMask, uint64_t blueMask) {
|
||||
this->endian = endian;
|
||||
this->depth = depth;
|
||||
this->stride = (depth / 8) + ((depth & 7) > 0);
|
||||
_endian = endian;
|
||||
_depth = depth;
|
||||
|
||||
alpha = {alphaMask, bitDepth(alphaMask), bitShift(alphaMask)};
|
||||
red = {redMask, bitDepth(redMask), bitShift(redMask )};
|
||||
green = {greenMask, bitDepth(greenMask), bitShift(greenMask)};
|
||||
blue = {blueMask, bitDepth(blueMask), bitShift(blueMask )};
|
||||
_alpha = {alphaMask, bitDepth(alphaMask), bitShift(alphaMask)};
|
||||
_red = {redMask, bitDepth(redMask), bitShift(redMask )};
|
||||
_green = {greenMask, bitDepth(greenMask), bitShift(greenMask)};
|
||||
_blue = {blueMask, bitDepth(blueMask), bitShift(blueMask )};
|
||||
}
|
||||
|
||||
image::image(const string& filename) {
|
||||
@@ -109,63 +40,122 @@ image::~image() {
|
||||
free();
|
||||
}
|
||||
|
||||
uint64_t image::read(const uint8_t* data) const {
|
||||
auto image::operator=(const image& source) -> image& {
|
||||
if(this == &source) return *this;
|
||||
free();
|
||||
|
||||
_width = source._width;
|
||||
_height = source._height;
|
||||
|
||||
_endian = source._endian;
|
||||
_depth = source._depth;
|
||||
|
||||
_alpha = source._alpha;
|
||||
_red = source._red;
|
||||
_green = source._green;
|
||||
_blue = source._blue;
|
||||
|
||||
_data = allocate(_width, _height, stride());
|
||||
memory::copy(_data, source._data, source.size());
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto image::operator=(image&& source) -> image& {
|
||||
if(this == &source) return *this;
|
||||
free();
|
||||
|
||||
_width = source._width;
|
||||
_height = source._height;
|
||||
|
||||
_endian = source._endian;
|
||||
_depth = source._depth;
|
||||
|
||||
_alpha = source._alpha;
|
||||
_red = source._red;
|
||||
_green = source._green;
|
||||
_blue = source._blue;
|
||||
|
||||
_data = source._data;
|
||||
source._data = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
image::operator bool() const {
|
||||
return !empty();
|
||||
}
|
||||
|
||||
auto image::operator==(const image& source) const -> bool {
|
||||
if(_width != source._width) return false;
|
||||
if(_height != source._height) return false;
|
||||
|
||||
if(_endian != source._endian) return false;
|
||||
if(_depth != source._depth) return false;
|
||||
|
||||
if(_alpha != source._alpha) return false;
|
||||
if(_red != source._red) return false;
|
||||
if(_green != source._green) return false;
|
||||
if(_blue != source._blue) return false;
|
||||
|
||||
return memory::compare(_data, source._data, size()) == 0;
|
||||
}
|
||||
|
||||
auto image::operator!=(const image& source) const -> bool {
|
||||
return !operator==(source);
|
||||
}
|
||||
|
||||
auto image::read(const uint8_t* data) const -> uint64_t {
|
||||
uint64_t result = 0;
|
||||
if(endian == 0) {
|
||||
for(signed n = stride - 1; n >= 0; n--) result = (result << 8) | data[n];
|
||||
if(_endian == 0) {
|
||||
for(signed n = stride() - 1; n >= 0; n--) result = (result << 8) | data[n];
|
||||
} else {
|
||||
for(signed n = 0; n < stride; n++) result = (result << 8) | data[n];
|
||||
for(signed n = 0; n < stride(); n++) result = (result << 8) | data[n];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void image::write(uint8_t* data, uint64_t value) const {
|
||||
if(endian == 0) {
|
||||
for(signed n = 0; n < stride; n++) {
|
||||
auto image::write(uint8_t* data, uint64_t value) const -> void {
|
||||
if(_endian == 0) {
|
||||
for(signed n = 0; n < stride(); n++) {
|
||||
data[n] = value;
|
||||
value >>= 8;
|
||||
}
|
||||
} else {
|
||||
for(signed n = stride - 1; n >= 0; n--) {
|
||||
for(signed n = stride() - 1; n >= 0; n--) {
|
||||
data[n] = value;
|
||||
value >>= 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void image::free() {
|
||||
if(data) delete[] data;
|
||||
data = nullptr;
|
||||
auto image::free() -> void {
|
||||
if(_data) delete[] _data;
|
||||
_data = nullptr;
|
||||
}
|
||||
|
||||
bool image::empty() const {
|
||||
if(data == nullptr) return true;
|
||||
if(width == 0 || height == 0) return true;
|
||||
return false;
|
||||
auto image::empty() const -> bool {
|
||||
return !_data || !_width || !_height;
|
||||
}
|
||||
|
||||
bool image::load(const string& filename) {
|
||||
auto image::load(const string& filename) -> bool {
|
||||
if(loadBMP(filename) == true) return true;
|
||||
if(loadPNG(filename) == true) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void image::allocate(unsigned width, unsigned height) {
|
||||
if(data != nullptr && this->width == width && this->height == height) return;
|
||||
auto image::allocate(unsigned width, unsigned height) -> void {
|
||||
if(_data && _width == width && _height == height) return;
|
||||
free();
|
||||
data = allocate(width, height, stride);
|
||||
pitch = width * stride;
|
||||
size = height * pitch;
|
||||
this->width = width;
|
||||
this->height = height;
|
||||
_width = width;
|
||||
_height = height;
|
||||
_data = allocate(_width, _height, stride());
|
||||
}
|
||||
|
||||
uint8_t* image::allocate(unsigned width, unsigned height, unsigned stride) {
|
||||
auto image::allocate(unsigned width, unsigned height, unsigned stride) -> uint8_t* {
|
||||
//allocate 1x1 larger than requested; so that linear interpolation does not require bounds-checking
|
||||
unsigned size = width * height * stride;
|
||||
unsigned padding = width * stride + stride;
|
||||
uint8_t* data = new uint8_t[size + padding];
|
||||
memset(data + size, 0x00, padding);
|
||||
auto data = new uint8_t[size + padding];
|
||||
memory::fill(data + size, padding);
|
||||
return data;
|
||||
}
|
||||
|
||||
|
@@ -3,81 +3,80 @@
|
||||
|
||||
namespace nall {
|
||||
|
||||
void image::fill(uint64_t color) {
|
||||
uint8_t* dp = data;
|
||||
for(unsigned y = 0; y < height; y++) {
|
||||
uint8_t* dp = data + pitch * y;
|
||||
for(unsigned x = 0; x < width; x++) {
|
||||
auto image::fill(uint64_t color) -> void {
|
||||
for(unsigned y = 0; y < _height; y++) {
|
||||
uint8_t* dp = _data + pitch() * y;
|
||||
for(unsigned x = 0; x < _width; x++) {
|
||||
write(dp, color);
|
||||
dp += stride;
|
||||
dp += stride();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void image::gradient(uint64_t a, uint64_t b, uint64_t c, uint64_t d) {
|
||||
for(unsigned y = 0; y < height; y++) {
|
||||
uint8_t* dp = data + pitch * y;
|
||||
double muY = (double)y / (double)height;
|
||||
for(unsigned x = 0; x < width; x++) {
|
||||
double muX = (double)x / (double)width;
|
||||
auto image::gradient(uint64_t a, uint64_t b, uint64_t c, uint64_t d) -> void {
|
||||
for(unsigned y = 0; y < _height; y++) {
|
||||
uint8_t* dp = _data + pitch() * y;
|
||||
double muY = (double)y / (double)_height;
|
||||
for(unsigned x = 0; x < _width; x++) {
|
||||
double muX = (double)x / (double)_width;
|
||||
write(dp, interpolate4f(a, b, c, d, muX, muY));
|
||||
dp += stride;
|
||||
dp += stride();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void image::gradient(uint64_t a, uint64_t b, signed radiusX, signed radiusY, signed centerX, signed centerY, function<double (double, double)> callback) {
|
||||
for(signed y = 0; y < height; y++) {
|
||||
uint8_t* dp = data + pitch * y;
|
||||
auto image::gradient(uint64_t a, uint64_t b, signed radiusX, signed radiusY, signed centerX, signed centerY, function<double (double, double)> callback) -> void {
|
||||
for(signed y = 0; y < _height; y++) {
|
||||
uint8_t* dp = _data + pitch() * y;
|
||||
double py = max(-radiusY, min(+radiusY, y - centerY)) * 1.0 / radiusY;
|
||||
for(signed x = 0; x < width; x++) {
|
||||
for(signed x = 0; x < _width; x++) {
|
||||
double px = max(-radiusX, min(+radiusX, x - centerX)) * 1.0 / radiusX;
|
||||
double mu = max(0.0, min(1.0, callback(px, py)));
|
||||
if(mu != mu) mu = 1.0; //NaN
|
||||
write(dp, interpolate4f(a, b, mu));
|
||||
dp += stride;
|
||||
dp += stride();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void image::crossGradient(uint64_t a, uint64_t b, signed radiusX, signed radiusY, signed centerX, signed centerY) {
|
||||
auto image::crossGradient(uint64_t a, uint64_t b, signed radiusX, signed radiusY, signed centerX, signed centerY) -> void {
|
||||
return gradient(a, b, radiusX, radiusY, centerX, centerY, [](double x, double y) -> double {
|
||||
x = fabs(x), y = fabs(y);
|
||||
return min(x, y) * min(x, y);
|
||||
});
|
||||
}
|
||||
|
||||
void image::diamondGradient(uint64_t a, uint64_t b, signed radiusX, signed radiusY, signed centerX, signed centerY) {
|
||||
auto image::diamondGradient(uint64_t a, uint64_t b, signed radiusX, signed radiusY, signed centerX, signed centerY) -> void {
|
||||
return gradient(a, b, radiusX, radiusY, centerX, centerY, [](double x, double y) -> double {
|
||||
return fabs(x) + fabs(y);
|
||||
});
|
||||
}
|
||||
|
||||
void image::horizontalGradient(uint64_t a, uint64_t b, signed radiusX, signed radiusY, signed centerX, signed centerY) {
|
||||
auto image::horizontalGradient(uint64_t a, uint64_t b, signed radiusX, signed radiusY, signed centerX, signed centerY) -> void {
|
||||
return gradient(a, b, radiusX, radiusY, centerX, centerY, [](double x, double y) -> double {
|
||||
return fabs(x);
|
||||
});
|
||||
}
|
||||
|
||||
void image::radialGradient(uint64_t a, uint64_t b, signed radiusX, signed radiusY, signed centerX, signed centerY) {
|
||||
auto image::radialGradient(uint64_t a, uint64_t b, signed radiusX, signed radiusY, signed centerX, signed centerY) -> void {
|
||||
return gradient(a, b, radiusX, radiusY, centerX, centerY, [](double x, double y) -> double {
|
||||
return sqrt(x * x + y * y);
|
||||
});
|
||||
}
|
||||
|
||||
void image::sphericalGradient(uint64_t a, uint64_t b, signed radiusX, signed radiusY, signed centerX, signed centerY) {
|
||||
auto image::sphericalGradient(uint64_t a, uint64_t b, signed radiusX, signed radiusY, signed centerX, signed centerY) -> void {
|
||||
return gradient(a, b, radiusX, radiusY, centerX, centerY, [](double x, double y) -> double {
|
||||
return x * x + y * y;
|
||||
});
|
||||
}
|
||||
|
||||
void image::squareGradient(uint64_t a, uint64_t b, signed radiusX, signed radiusY, signed centerX, signed centerY) {
|
||||
auto image::squareGradient(uint64_t a, uint64_t b, signed radiusX, signed radiusY, signed centerX, signed centerY) -> void {
|
||||
return gradient(a, b, radiusX, radiusY, centerX, centerY, [](double x, double y) -> double {
|
||||
return max(fabs(x), fabs(y));
|
||||
});
|
||||
}
|
||||
|
||||
void image::verticalGradient(uint64_t a, uint64_t b, signed radiusX, signed radiusY, signed centerX, signed centerY) {
|
||||
auto image::verticalGradient(uint64_t a, uint64_t b, signed radiusX, signed radiusY, signed centerX, signed centerY) -> void {
|
||||
return gradient(a, b, radiusX, radiusY, centerX, centerY, [](double x, double y) -> double {
|
||||
return fabs(y);
|
||||
});
|
||||
|
@@ -3,57 +3,57 @@
|
||||
|
||||
namespace nall {
|
||||
|
||||
void image::isplit(uint64_t* c, uint64_t color) {
|
||||
c[0] = (color & alpha.mask) >> alpha.shift;
|
||||
c[1] = (color & red.mask ) >> red.shift;
|
||||
c[2] = (color & green.mask) >> green.shift;
|
||||
c[3] = (color & blue.mask ) >> blue.shift;
|
||||
auto image::isplit(uint64_t* c, uint64_t color) -> void {
|
||||
c[0] = (color & _alpha.mask()) >> _alpha.shift();
|
||||
c[1] = (color & _red.mask() ) >> _red.shift();
|
||||
c[2] = (color & _green.mask()) >> _green.shift();
|
||||
c[3] = (color & _blue.mask() ) >> _blue.shift();
|
||||
}
|
||||
|
||||
uint64_t image::imerge(const uint64_t* c) {
|
||||
return c[0] << alpha.shift | c[1] << red.shift | c[2] << green.shift | c[3] << blue.shift;
|
||||
auto image::imerge(const uint64_t* c) -> uint64_t {
|
||||
return c[0] << _alpha.shift() | c[1] << _red.shift() | c[2] << _green.shift() | c[3] << _blue.shift();
|
||||
}
|
||||
|
||||
uint64_t image::interpolate1f(uint64_t a, uint64_t b, double x) {
|
||||
auto image::interpolate1f(uint64_t a, uint64_t b, double x) -> uint64_t {
|
||||
return a * (1.0 - x) + b * x;
|
||||
}
|
||||
|
||||
uint64_t image::interpolate1f(uint64_t a, uint64_t b, uint64_t c, uint64_t d, double x, double y) {
|
||||
auto image::interpolate1f(uint64_t a, uint64_t b, uint64_t c, uint64_t d, double x, double y) -> uint64_t {
|
||||
return a * (1.0 - x) * (1.0 - y) + b * x * (1.0 - y) + c * (1.0 - x) * y + d * x * y;
|
||||
}
|
||||
|
||||
uint64_t image::interpolate1i(int64_t a, int64_t b, uint32_t x) {
|
||||
auto image::interpolate1i(int64_t a, int64_t b, uint32_t x) -> uint64_t {
|
||||
return a + (((b - a) * x) >> 32); //a + (b - a) * x
|
||||
}
|
||||
|
||||
uint64_t image::interpolate1i(int64_t a, int64_t b, int64_t c, int64_t d, uint32_t x, uint32_t y) {
|
||||
auto image::interpolate1i(int64_t a, int64_t b, int64_t c, int64_t d, uint32_t x, uint32_t y) -> uint64_t {
|
||||
a = a + (((b - a) * x) >> 32); //a + (b - a) * x
|
||||
c = c + (((d - c) * x) >> 32); //c + (d - c) * x
|
||||
return a + (((c - a) * y) >> 32); //a + (c - a) * y
|
||||
}
|
||||
|
||||
uint64_t image::interpolate4f(uint64_t a, uint64_t b, double x) {
|
||||
auto image::interpolate4f(uint64_t a, uint64_t b, double x) -> uint64_t {
|
||||
uint64_t o[4], pa[4], pb[4];
|
||||
isplit(pa, a), isplit(pb, b);
|
||||
for(unsigned n = 0; n < 4; n++) o[n] = interpolate1f(pa[n], pb[n], x);
|
||||
return imerge(o);
|
||||
}
|
||||
|
||||
uint64_t image::interpolate4f(uint64_t a, uint64_t b, uint64_t c, uint64_t d, double x, double y) {
|
||||
auto image::interpolate4f(uint64_t a, uint64_t b, uint64_t c, uint64_t d, double x, double y) -> uint64_t {
|
||||
uint64_t o[4], pa[4], pb[4], pc[4], pd[4];
|
||||
isplit(pa, a), isplit(pb, b), isplit(pc, c), isplit(pd, d);
|
||||
for(unsigned n = 0; n < 4; n++) o[n] = interpolate1f(pa[n], pb[n], pc[n], pd[n], x, y);
|
||||
return imerge(o);
|
||||
}
|
||||
|
||||
uint64_t image::interpolate4i(uint64_t a, uint64_t b, uint32_t x) {
|
||||
auto image::interpolate4i(uint64_t a, uint64_t b, uint32_t x) -> uint64_t {
|
||||
uint64_t o[4], pa[4], pb[4];
|
||||
isplit(pa, a), isplit(pb, b);
|
||||
for(unsigned n = 0; n < 4; n++) o[n] = interpolate1i(pa[n], pb[n], x);
|
||||
return imerge(o);
|
||||
}
|
||||
|
||||
uint64_t image::interpolate4i(uint64_t a, uint64_t b, uint64_t c, uint64_t d, uint32_t x, uint32_t y) {
|
||||
auto image::interpolate4i(uint64_t a, uint64_t b, uint64_t c, uint64_t d, uint32_t x, uint32_t y) -> uint64_t {
|
||||
uint64_t o[4], pa[4], pb[4], pc[4], pd[4];
|
||||
isplit(pa, a), isplit(pb, b), isplit(pc, c), isplit(pd, d);
|
||||
for(unsigned n = 0; n < 4; n++) o[n] = interpolate1i(pa[n], pb[n], pc[n], pd[n], x, y);
|
||||
|
@@ -3,24 +3,24 @@
|
||||
|
||||
namespace nall {
|
||||
|
||||
bool image::loadBMP(const string& filename) {
|
||||
auto image::loadBMP(const string& filename) -> bool {
|
||||
uint32_t* outputData;
|
||||
unsigned outputWidth, outputHeight;
|
||||
if(bmp::read(filename, outputData, outputWidth, outputHeight) == false) return false;
|
||||
|
||||
allocate(outputWidth, outputHeight);
|
||||
const uint32_t* sp = outputData;
|
||||
uint8_t* dp = data;
|
||||
uint8_t* dp = _data;
|
||||
|
||||
for(unsigned y = 0; y < outputHeight; y++) {
|
||||
for(unsigned x = 0; x < outputWidth; x++) {
|
||||
uint32_t color = *sp++;
|
||||
uint64_t a = normalize((uint8_t)(color >> 24), 8, alpha.depth);
|
||||
uint64_t r = normalize((uint8_t)(color >> 16), 8, red.depth);
|
||||
uint64_t g = normalize((uint8_t)(color >> 8), 8, green.depth);
|
||||
uint64_t b = normalize((uint8_t)(color >> 0), 8, blue.depth);
|
||||
write(dp, (a << alpha.shift) | (r << red.shift) | (g << green.shift) | (b << blue.shift));
|
||||
dp += stride;
|
||||
uint64_t a = normalize((uint8_t)(color >> 24), 8, _alpha.depth());
|
||||
uint64_t r = normalize((uint8_t)(color >> 16), 8, _red.depth());
|
||||
uint64_t g = normalize((uint8_t)(color >> 8), 8, _green.depth());
|
||||
uint64_t b = normalize((uint8_t)(color >> 0), 8, _blue.depth());
|
||||
write(dp, (a << _alpha.shift()) | (r << _red.shift()) | (g << _green.shift()) | (b << _blue.shift()));
|
||||
dp += stride();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,19 +28,19 @@ bool image::loadBMP(const string& filename) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool image::loadPNG(const string& filename) {
|
||||
auto image::loadPNG(const string& filename) -> bool {
|
||||
if(!file::exists(filename)) return false;
|
||||
auto buffer = file::read(filename);
|
||||
return loadPNG(buffer.data(), buffer.size());
|
||||
}
|
||||
|
||||
bool image::loadPNG(const uint8_t* pngData, unsigned pngSize) {
|
||||
auto image::loadPNG(const uint8_t* pngData, unsigned pngSize) -> bool {
|
||||
Decode::PNG source;
|
||||
if(source.decode(pngData, pngSize) == false) return false;
|
||||
|
||||
allocate(source.info.width, source.info.height);
|
||||
const uint8_t* sp = source.data;
|
||||
uint8_t* dp = data;
|
||||
uint8_t* dp = _data;
|
||||
|
||||
auto decode = [&]() -> uint64_t {
|
||||
uint64_t p, r, g, b, a;
|
||||
@@ -75,18 +75,18 @@ bool image::loadPNG(const uint8_t* pngData, unsigned pngSize) {
|
||||
break;
|
||||
}
|
||||
|
||||
a = normalize(a, source.info.bitDepth, alpha.depth);
|
||||
r = normalize(r, source.info.bitDepth, red.depth);
|
||||
g = normalize(g, source.info.bitDepth, green.depth);
|
||||
b = normalize(b, source.info.bitDepth, blue.depth);
|
||||
a = normalize(a, source.info.bitDepth, _alpha.depth());
|
||||
r = normalize(r, source.info.bitDepth, _red.depth());
|
||||
g = normalize(g, source.info.bitDepth, _green.depth());
|
||||
b = normalize(b, source.info.bitDepth, _blue.depth());
|
||||
|
||||
return (a << alpha.shift) | (r << red.shift) | (g << green.shift) | (b << blue.shift);
|
||||
return (a << _alpha.shift()) | (r << _red.shift()) | (g << _green.shift()) | (b << _blue.shift());
|
||||
};
|
||||
|
||||
for(unsigned y = 0; y < height; y++) {
|
||||
for(unsigned x = 0; x < width; x++) {
|
||||
for(unsigned y = 0; y < _height; y++) {
|
||||
for(unsigned x = 0; x < _width; x++) {
|
||||
write(dp, decode());
|
||||
dp += stride;
|
||||
dp += stride();
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -3,18 +3,18 @@
|
||||
|
||||
namespace nall {
|
||||
|
||||
void image::scale(unsigned outputWidth, unsigned outputHeight, bool linear) {
|
||||
if(width == outputWidth && height == outputHeight) return; //no scaling necessary
|
||||
auto image::scale(unsigned outputWidth, unsigned outputHeight, bool linear) -> void {
|
||||
if(_width == outputWidth && _height == outputHeight) return; //no scaling necessary
|
||||
if(linear == false) return scaleNearest(outputWidth, outputHeight);
|
||||
|
||||
if(width == outputWidth ) return scaleLinearHeight(outputHeight);
|
||||
if(height == outputHeight) return scaleLinearWidth(outputWidth);
|
||||
if(_width == outputWidth ) return scaleLinearHeight(outputHeight);
|
||||
if(_height == outputHeight) return scaleLinearWidth(outputWidth);
|
||||
|
||||
//find fastest scaling method, based on number of interpolation operations required
|
||||
//magnification usually benefits from two-pass linear interpolation
|
||||
//minification usually benefits from one-pass bilinear interpolation
|
||||
unsigned d1wh = ((width * outputWidth ) + (outputWidth * outputHeight)) * 1;
|
||||
unsigned d1hw = ((height * outputHeight) + (outputWidth * outputHeight)) * 1;
|
||||
unsigned d1wh = ((_width * outputWidth ) + (outputWidth * outputHeight)) * 1;
|
||||
unsigned d1hw = ((_height * outputHeight) + (outputWidth * outputHeight)) * 1;
|
||||
unsigned d2wh = (outputWidth * outputHeight) * 3;
|
||||
|
||||
if(d1wh <= d1hw && d1wh <= d2wh) return scaleLinearWidth(outputWidth), scaleLinearHeight(outputHeight);
|
||||
@@ -22,32 +22,32 @@ void image::scale(unsigned outputWidth, unsigned outputHeight, bool linear) {
|
||||
return scaleLinear(outputWidth, outputHeight);
|
||||
}
|
||||
|
||||
void image::scaleLinearWidth(unsigned outputWidth) {
|
||||
uint8_t* outputData = allocate(outputWidth, height, stride);
|
||||
unsigned outputPitch = outputWidth * stride;
|
||||
uint64_t xstride = ((uint64_t)(width - 1) << 32) / max(1u, outputWidth - 1);
|
||||
auto image::scaleLinearWidth(unsigned outputWidth) -> void {
|
||||
uint8_t* outputData = allocate(outputWidth, _height, stride());
|
||||
unsigned outputPitch = outputWidth * stride();
|
||||
uint64_t xstride = ((uint64_t)(_width - 1) << 32) / max(1u, outputWidth - 1);
|
||||
|
||||
#pragma omp parallel for
|
||||
for(unsigned y = 0; y < height; y++) {
|
||||
for(unsigned y = 0; y < _height; y++) {
|
||||
uint64_t xfraction = 0;
|
||||
|
||||
const uint8_t* sp = data + pitch * y;
|
||||
const uint8_t* sp = _data + pitch() * y;
|
||||
uint8_t* dp = outputData + outputPitch * y;
|
||||
|
||||
uint64_t a = read(sp);
|
||||
uint64_t b = read(sp + stride);
|
||||
sp += stride;
|
||||
uint64_t b = read(sp + stride());
|
||||
sp += stride();
|
||||
|
||||
unsigned x = 0;
|
||||
while(true) {
|
||||
while(xfraction < 0x100000000 && x++ < outputWidth) {
|
||||
write(dp, interpolate4i(a, b, xfraction));
|
||||
dp += stride;
|
||||
dp += stride();
|
||||
xfraction += xstride;
|
||||
}
|
||||
if(x >= outputWidth) break;
|
||||
|
||||
sp += stride;
|
||||
sp += stride();
|
||||
a = b;
|
||||
b = read(sp);
|
||||
xfraction -= 0x100000000;
|
||||
@@ -55,37 +55,35 @@ void image::scaleLinearWidth(unsigned outputWidth) {
|
||||
}
|
||||
|
||||
free();
|
||||
data = outputData;
|
||||
width = outputWidth;
|
||||
pitch = outputPitch;
|
||||
size = height * pitch;
|
||||
_data = outputData;
|
||||
_width = outputWidth;
|
||||
}
|
||||
|
||||
void image::scaleLinearHeight(unsigned outputHeight) {
|
||||
uint8_t* outputData = allocate(width, outputHeight, stride);
|
||||
uint64_t ystride = ((uint64_t)(height - 1) << 32) / max(1u, outputHeight - 1);
|
||||
auto image::scaleLinearHeight(unsigned outputHeight) -> void {
|
||||
uint8_t* outputData = allocate(_width, outputHeight, stride());
|
||||
uint64_t ystride = ((uint64_t)(_height - 1) << 32) / max(1u, outputHeight - 1);
|
||||
|
||||
#pragma omp parallel for
|
||||
for(unsigned x = 0; x < width; x++) {
|
||||
for(unsigned x = 0; x < _width; x++) {
|
||||
uint64_t yfraction = 0;
|
||||
|
||||
const uint8_t* sp = data + stride * x;
|
||||
uint8_t* dp = outputData + stride * x;
|
||||
const uint8_t* sp = _data + stride() * x;
|
||||
uint8_t* dp = outputData + stride() * x;
|
||||
|
||||
uint64_t a = read(sp);
|
||||
uint64_t b = read(sp + pitch);
|
||||
sp += pitch;
|
||||
uint64_t b = read(sp + pitch());
|
||||
sp += pitch();
|
||||
|
||||
unsigned y = 0;
|
||||
while(true) {
|
||||
while(yfraction < 0x100000000 && y++ < outputHeight) {
|
||||
write(dp, interpolate4i(a, b, yfraction));
|
||||
dp += pitch;
|
||||
dp += pitch();
|
||||
yfraction += ystride;
|
||||
}
|
||||
if(y >= outputHeight) break;
|
||||
|
||||
sp += pitch;
|
||||
sp += pitch();
|
||||
a = b;
|
||||
b = read(sp);
|
||||
yfraction -= 0x100000000;
|
||||
@@ -93,71 +91,68 @@ void image::scaleLinearHeight(unsigned outputHeight) {
|
||||
}
|
||||
|
||||
free();
|
||||
data = outputData;
|
||||
height = outputHeight;
|
||||
size = height * pitch;
|
||||
_data = outputData;
|
||||
_height = outputHeight;
|
||||
}
|
||||
|
||||
void image::scaleLinear(unsigned outputWidth, unsigned outputHeight) {
|
||||
uint8_t* outputData = allocate(outputWidth, outputHeight, stride);
|
||||
unsigned outputPitch = outputWidth * stride;
|
||||
auto image::scaleLinear(unsigned outputWidth, unsigned outputHeight) -> void {
|
||||
uint8_t* outputData = allocate(outputWidth, outputHeight, stride());
|
||||
unsigned outputPitch = outputWidth * stride();
|
||||
|
||||
uint64_t xstride = ((uint64_t)(width - 1) << 32) / max(1u, outputWidth - 1);
|
||||
uint64_t ystride = ((uint64_t)(height - 1) << 32) / max(1u, outputHeight - 1);
|
||||
uint64_t xstride = ((uint64_t)(_width - 1) << 32) / max(1u, outputWidth - 1);
|
||||
uint64_t ystride = ((uint64_t)(_height - 1) << 32) / max(1u, outputHeight - 1);
|
||||
|
||||
#pragma omp parallel for
|
||||
for(unsigned y = 0; y < outputHeight; y++) {
|
||||
uint64_t yfraction = ystride * y;
|
||||
uint64_t xfraction = 0;
|
||||
|
||||
const uint8_t* sp = data + pitch * (yfraction >> 32);
|
||||
const uint8_t* sp = _data + pitch() * (yfraction >> 32);
|
||||
uint8_t* dp = outputData + outputPitch * y;
|
||||
|
||||
uint64_t a = read(sp);
|
||||
uint64_t b = read(sp + stride);
|
||||
uint64_t c = read(sp + pitch);
|
||||
uint64_t d = read(sp + pitch + stride);
|
||||
sp += stride;
|
||||
uint64_t b = read(sp + stride());
|
||||
uint64_t c = read(sp + pitch());
|
||||
uint64_t d = read(sp + pitch() + stride());
|
||||
sp += stride();
|
||||
|
||||
unsigned x = 0;
|
||||
while(true) {
|
||||
while(xfraction < 0x100000000 && x++ < outputWidth) {
|
||||
write(dp, interpolate4i(a, b, c, d, xfraction, yfraction));
|
||||
dp += stride;
|
||||
dp += stride();
|
||||
xfraction += xstride;
|
||||
}
|
||||
if(x >= outputWidth) break;
|
||||
|
||||
sp += stride;
|
||||
sp += stride();
|
||||
a = b;
|
||||
c = d;
|
||||
b = read(sp);
|
||||
d = read(sp + pitch);
|
||||
d = read(sp + pitch());
|
||||
xfraction -= 0x100000000;
|
||||
}
|
||||
}
|
||||
|
||||
free();
|
||||
data = outputData;
|
||||
width = outputWidth;
|
||||
height = outputHeight;
|
||||
pitch = outputPitch;
|
||||
size = height * pitch;
|
||||
_data = outputData;
|
||||
_width = outputWidth;
|
||||
_height = outputHeight;
|
||||
}
|
||||
|
||||
void image::scaleNearest(unsigned outputWidth, unsigned outputHeight) {
|
||||
uint8_t* outputData = allocate(outputWidth, outputHeight, stride);
|
||||
unsigned outputPitch = outputWidth * stride;
|
||||
auto image::scaleNearest(unsigned outputWidth, unsigned outputHeight) -> void {
|
||||
uint8_t* outputData = allocate(outputWidth, outputHeight, stride());
|
||||
unsigned outputPitch = outputWidth * stride();
|
||||
|
||||
uint64_t xstride = ((uint64_t)width << 32) / outputWidth;
|
||||
uint64_t ystride = ((uint64_t)height << 32) / outputHeight;
|
||||
uint64_t xstride = ((uint64_t)_width << 32) / outputWidth;
|
||||
uint64_t ystride = ((uint64_t)_height << 32) / outputHeight;
|
||||
|
||||
#pragma omp parallel for
|
||||
for(unsigned y = 0; y < outputHeight; y++) {
|
||||
uint64_t yfraction = ystride * y;
|
||||
uint64_t xfraction = 0;
|
||||
|
||||
const uint8_t* sp = data + pitch * (yfraction >> 32);
|
||||
const uint8_t* sp = _data + pitch() * (yfraction >> 32);
|
||||
uint8_t* dp = outputData + outputPitch * y;
|
||||
|
||||
uint64_t a = read(sp);
|
||||
@@ -166,23 +161,21 @@ void image::scaleNearest(unsigned outputWidth, unsigned outputHeight) {
|
||||
while(true) {
|
||||
while(xfraction < 0x100000000 && x++ < outputWidth) {
|
||||
write(dp, a);
|
||||
dp += stride;
|
||||
dp += stride();
|
||||
xfraction += xstride;
|
||||
}
|
||||
if(x >= outputWidth) break;
|
||||
|
||||
sp += stride;
|
||||
sp += stride();
|
||||
a = read(sp);
|
||||
xfraction -= 0x100000000;
|
||||
}
|
||||
}
|
||||
|
||||
free();
|
||||
data = outputData;
|
||||
width = outputWidth;
|
||||
height = outputHeight;
|
||||
pitch = outputPitch;
|
||||
size = height * pitch;
|
||||
_data = outputData;
|
||||
_width = outputWidth;
|
||||
_height = outputHeight;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -3,20 +3,20 @@
|
||||
|
||||
namespace nall {
|
||||
|
||||
unsigned image::bitDepth(uint64_t color) {
|
||||
auto image::bitDepth(uint64_t color) -> unsigned {
|
||||
unsigned depth = 0;
|
||||
if(color) while((color & 1) == 0) color >>= 1;
|
||||
while((color & 1) == 1) { color >>= 1; depth++; }
|
||||
return depth;
|
||||
}
|
||||
|
||||
unsigned image::bitShift(uint64_t color) {
|
||||
auto image::bitShift(uint64_t color) -> unsigned {
|
||||
unsigned shift = 0;
|
||||
if(color) while((color & 1) == 0) { color >>= 1; shift++; }
|
||||
return shift;
|
||||
}
|
||||
|
||||
uint64_t image::normalize(uint64_t color, unsigned sourceDepth, unsigned targetDepth) {
|
||||
auto image::normalize(uint64_t color, unsigned sourceDepth, unsigned targetDepth) -> uint64_t {
|
||||
if(sourceDepth == 0 || targetDepth == 0) return 0;
|
||||
while(sourceDepth < targetDepth) {
|
||||
color = (color << sourceDepth) | color;
|
||||
|
@@ -3,91 +3,93 @@
|
||||
|
||||
namespace nall {
|
||||
|
||||
bool image::crop(unsigned outputX, unsigned outputY, unsigned outputWidth, unsigned outputHeight) {
|
||||
if(outputX + outputWidth > width) return false;
|
||||
if(outputY + outputHeight > height) return false;
|
||||
auto image::crop(unsigned outputX, unsigned outputY, unsigned outputWidth, unsigned outputHeight) -> bool {
|
||||
if(outputX + outputWidth > _width) return false;
|
||||
if(outputY + outputHeight > _height) return false;
|
||||
|
||||
uint8_t* outputData = allocate(outputWidth, outputHeight, stride);
|
||||
unsigned outputPitch = outputWidth * stride;
|
||||
uint8_t* outputData = allocate(outputWidth, outputHeight, stride());
|
||||
unsigned outputPitch = outputWidth * stride();
|
||||
|
||||
#pragma omp parallel for
|
||||
for(unsigned y = 0; y < outputHeight; y++) {
|
||||
const uint8_t* sp = data + pitch * (outputY + y) + stride * outputX;
|
||||
const uint8_t* sp = _data + pitch() * (outputY + y) + stride() * outputX;
|
||||
uint8_t* dp = outputData + outputPitch * y;
|
||||
for(unsigned x = 0; x < outputWidth; x++) {
|
||||
write(dp, read(sp));
|
||||
sp += stride;
|
||||
dp += stride;
|
||||
sp += stride();
|
||||
dp += stride();
|
||||
}
|
||||
}
|
||||
|
||||
delete[] data;
|
||||
data = outputData;
|
||||
width = outputWidth;
|
||||
height = outputHeight;
|
||||
pitch = outputPitch;
|
||||
size = width * pitch;
|
||||
delete[] _data;
|
||||
_data = outputData;
|
||||
_width = outputWidth;
|
||||
_height = outputHeight;
|
||||
return true;
|
||||
}
|
||||
|
||||
void image::alphaBlend(uint64_t alphaColor) {
|
||||
uint64_t alphaR = (alphaColor & red.mask ) >> red.shift;
|
||||
uint64_t alphaG = (alphaColor & green.mask) >> green.shift;
|
||||
uint64_t alphaB = (alphaColor & blue.mask ) >> blue.shift;
|
||||
auto image::alphaBlend(uint64_t alphaColor) -> void {
|
||||
uint64_t alphaR = (alphaColor & _red.mask() ) >> _red.shift();
|
||||
uint64_t alphaG = (alphaColor & _green.mask()) >> _green.shift();
|
||||
uint64_t alphaB = (alphaColor & _blue.mask() ) >> _blue.shift();
|
||||
|
||||
#pragma omp parallel for
|
||||
for(unsigned y = 0; y < height; y++) {
|
||||
uint8_t* dp = data + pitch * y;
|
||||
for(unsigned x = 0; x < width; x++) {
|
||||
for(unsigned y = 0; y < _height; y++) {
|
||||
uint8_t* dp = _data + pitch() * y;
|
||||
for(unsigned x = 0; x < _width; x++) {
|
||||
uint64_t color = read(dp);
|
||||
|
||||
uint64_t colorA = (color & alpha.mask) >> alpha.shift;
|
||||
uint64_t colorR = (color & red.mask ) >> red.shift;
|
||||
uint64_t colorG = (color & green.mask) >> green.shift;
|
||||
uint64_t colorB = (color & blue.mask ) >> blue.shift;
|
||||
double alphaScale = (double)colorA / (double)((1 << alpha.depth) - 1);
|
||||
uint64_t colorA = (color & _alpha.mask()) >> _alpha.shift();
|
||||
uint64_t colorR = (color & _red.mask() ) >> _red.shift();
|
||||
uint64_t colorG = (color & _green.mask()) >> _green.shift();
|
||||
uint64_t colorB = (color & _blue.mask() ) >> _blue.shift();
|
||||
double alphaScale = (double)colorA / (double)((1 << _alpha.depth()) - 1);
|
||||
|
||||
colorA = (1 << alpha.depth) - 1;
|
||||
colorA = (1 << _alpha.depth()) - 1;
|
||||
colorR = (colorR * alphaScale) + (alphaR * (1.0 - alphaScale));
|
||||
colorG = (colorG * alphaScale) + (alphaG * (1.0 - alphaScale));
|
||||
colorB = (colorB * alphaScale) + (alphaB * (1.0 - alphaScale));
|
||||
|
||||
write(dp, (colorA << alpha.shift) | (colorR << red.shift) | (colorG << green.shift) | (colorB << blue.shift));
|
||||
dp += stride;
|
||||
write(dp, (colorA << _alpha.shift()) | (colorR << _red.shift()) | (colorG << _green.shift()) | (colorB << _blue.shift()));
|
||||
dp += stride();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void image::transform(bool outputEndian, unsigned outputDepth, uint64_t outputAlphaMask, uint64_t outputRedMask, uint64_t outputGreenMask, uint64_t outputBlueMask) {
|
||||
if(endian == outputEndian && depth == outputDepth && alpha.mask == outputAlphaMask && red.mask == outputRedMask && green.mask == outputGreenMask && blue.mask == outputBlueMask) return;
|
||||
auto image::transform(const image& source) -> void {
|
||||
return transform(source._endian, source._depth, source._alpha.mask(), source._red.mask(), source._green.mask(), source._blue.mask());
|
||||
}
|
||||
|
||||
auto image::transform(bool outputEndian, unsigned outputDepth, uint64_t outputAlphaMask, uint64_t outputRedMask, uint64_t outputGreenMask, uint64_t outputBlueMask) -> void {
|
||||
if(_endian == outputEndian && _depth == outputDepth && _alpha.mask() == outputAlphaMask && _red.mask() == outputRedMask && _green.mask() == outputGreenMask && _blue.mask() == outputBlueMask) return;
|
||||
|
||||
image output(outputEndian, outputDepth, outputAlphaMask, outputRedMask, outputGreenMask, outputBlueMask);
|
||||
output.allocate(width, height);
|
||||
output.allocate(_width, _height);
|
||||
|
||||
#pragma omp parallel for
|
||||
for(unsigned y = 0; y < height; y++) {
|
||||
const uint8_t* sp = data + pitch * y;
|
||||
uint8_t* dp = output.data + output.pitch * y;
|
||||
for(unsigned x = 0; x < width; x++) {
|
||||
for(unsigned y = 0; y < _height; y++) {
|
||||
const uint8_t* sp = _data + pitch() * y;
|
||||
uint8_t* dp = output._data + output.pitch() * y;
|
||||
for(unsigned x = 0; x < _width; x++) {
|
||||
uint64_t color = read(sp);
|
||||
sp += stride;
|
||||
sp += stride();
|
||||
|
||||
uint64_t a = (color & alpha.mask) >> alpha.shift;
|
||||
uint64_t r = (color & red.mask) >> red.shift;
|
||||
uint64_t g = (color & green.mask) >> green.shift;
|
||||
uint64_t b = (color & blue.mask) >> blue.shift;
|
||||
uint64_t a = (color & _alpha.mask()) >> _alpha.shift();
|
||||
uint64_t r = (color & _red.mask() ) >> _red.shift();
|
||||
uint64_t g = (color & _green.mask()) >> _green.shift();
|
||||
uint64_t b = (color & _blue.mask() ) >> _blue.shift();
|
||||
|
||||
a = normalize(a, alpha.depth, output.alpha.depth);
|
||||
r = normalize(r, red.depth, output.red.depth);
|
||||
g = normalize(g, green.depth, output.green.depth);
|
||||
b = normalize(b, blue.depth, output.blue.depth);
|
||||
a = normalize(a, _alpha.depth(), output._alpha.depth());
|
||||
r = normalize(r, _red.depth(), output._red.depth());
|
||||
g = normalize(g, _green.depth(), output._green.depth());
|
||||
b = normalize(b, _blue.depth(), output._blue.depth());
|
||||
|
||||
output.write(dp, (a << output.alpha.shift) | (r << output.red.shift) | (g << output.green.shift) | (b << output.blue.shift));
|
||||
dp += output.stride;
|
||||
output.write(dp, (a << output._alpha.shift()) | (r << output._red.shift()) | (g << output._green.shift()) | (b << output._blue.shift()));
|
||||
dp += output.stride();
|
||||
}
|
||||
}
|
||||
|
||||
operator=(std::move(output));
|
||||
operator=(move(output));
|
||||
}
|
||||
|
||||
}
|
||||
|
Reference in New Issue
Block a user