Better color typing
This commit is contained in:
2
Makefile
2
Makefile
@@ -1,6 +1,6 @@
|
||||
all: piphoto
|
||||
|
||||
objects = piphoto.o lut.o util.o
|
||||
objects = piphoto.o color.o lut.o util.o
|
||||
|
||||
piphoto: $(objects) Makefile
|
||||
clang-3.9 -O3 -g -Weverything -Werror --std=c++1z --stdlib=libc++ -o piphoto $(objects) -lc++ -lunwind -lpng
|
||||
|
||||
4
color.cc
Normal file
4
color.cc
Normal file
@@ -0,0 +1,4 @@
|
||||
#include "color.h"
|
||||
|
||||
RgbColor::RgbColor(const Color<3>& src)
|
||||
: Color<3>(src) {}
|
||||
12
color.h
12
color.h
@@ -10,14 +10,22 @@
|
||||
constexpr int32_t kMinColor = 0;
|
||||
constexpr int32_t kMaxColor = UINT16_MAX;
|
||||
|
||||
class ColorBase {};
|
||||
|
||||
template <int32_t C>
|
||||
struct Color : public Array<int32_t, C> {
|
||||
struct Color : public Array<int32_t, C>, public ColorBase {
|
||||
constexpr int32_t AbsDiff(const Color<C>& other) const;
|
||||
constexpr Color<C> Interpolate(const Color<C>& other, int32_t mul, int32_t div) const;
|
||||
constexpr Color<C> Crop() const;
|
||||
};
|
||||
|
||||
struct RgbColor : public Color<3> {};
|
||||
|
||||
struct RgbColor : public Color<3> {
|
||||
public:
|
||||
RgbColor() = default;
|
||||
RgbColor(const Color<3>& src);
|
||||
};
|
||||
|
||||
|
||||
template <int32_t C>
|
||||
constexpr int32_t Color<C>::AbsDiff(const Color<C>& other) const {
|
||||
|
||||
@@ -14,7 +14,9 @@
|
||||
// Maximum LUT size that has each point adjacent to at least one ColorChecker color.
|
||||
typedef Lut3d<4, 3, 3> ColorCheckerLut3d;
|
||||
|
||||
constexpr Array<RgbColor, 24> kColorCheckerSrgb = {{{
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wglobal-constructors"
|
||||
const Array<RgbColor, 24> kColorCheckerSrgb = {{{
|
||||
{{{{{0x7300, 0x5200, 0x4400}}}}},
|
||||
{{{{{0xc200, 0x9600, 0x8200}}}}},
|
||||
{{{{{0x6200, 0x7a00, 0x9d00}}}}},
|
||||
@@ -40,11 +42,10 @@ constexpr Array<RgbColor, 24> kColorCheckerSrgb = {{{
|
||||
{{{{{0x5500, 0x5500, 0x5500}}}}},
|
||||
{{{{{0x3400, 0x3400, 0x3400}}}}},
|
||||
}}};
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
template <int32_t X, int32_t Y, int32_t C>
|
||||
Array<Coord<2>, kColorCheckerSrgb.size()> FindClosest(const Image<X, Y, C>& image) {
|
||||
static_assert(C == 3);
|
||||
|
||||
template <int32_t X, int32_t Y>
|
||||
Array<Coord<2>, kColorCheckerSrgb.size()> FindClosest(const Image<X, Y, RgbColor>& image) {
|
||||
Array<Coord<2>, kColorCheckerSrgb.size()> closest;
|
||||
Array<int32_t, kColorCheckerSrgb.size()> diff;
|
||||
diff.fill(INT32_MAX);
|
||||
@@ -68,10 +69,8 @@ Array<Coord<2>, kColorCheckerSrgb.size()> FindClosest(const Image<X, Y, C>& imag
|
||||
return closest;
|
||||
}
|
||||
|
||||
template <int32_t X, int32_t Y, int32_t C>
|
||||
int32_t ScoreLut(const Image<X, Y, C>& image, const LutBase& lut) {
|
||||
static_assert(C == 3);
|
||||
|
||||
template <int32_t X, int32_t Y>
|
||||
int32_t ScoreLut(const Image<X, Y, RgbColor>& image, const LutBase& lut) {
|
||||
Array<int32_t, kColorCheckerSrgb.size()> diff;
|
||||
diff.fill(INT32_MAX);
|
||||
|
||||
@@ -93,11 +92,9 @@ int32_t ScoreLut(const Image<X, Y, C>& image, const LutBase& lut) {
|
||||
return std::accumulate(diff.begin(), diff.end(), 0);
|
||||
}
|
||||
|
||||
template <int32_t X, int32_t Y, int32_t C>
|
||||
std::unique_ptr<Image<X, Y, C>> HighlightClosest(const Image<X, Y, C>& image) {
|
||||
static_assert(C == 3);
|
||||
|
||||
auto out = std::make_unique<Image<X, Y, C>>(image);
|
||||
template <int32_t X, int32_t Y>
|
||||
std::unique_ptr<Image<X, Y, RgbColor>> HighlightClosest(const Image<X, Y, RgbColor>& image) {
|
||||
auto out = std::make_unique<Image<X, Y, RgbColor>>(image);
|
||||
|
||||
auto closest = FindClosest(*out);
|
||||
for (int32_t cc = 0; cc < kColorCheckerSrgb.ssize(); ++cc) {
|
||||
@@ -112,10 +109,8 @@ std::unique_ptr<Image<X, Y, C>> HighlightClosest(const Image<X, Y, C>& image) {
|
||||
return out;
|
||||
}
|
||||
|
||||
template <int32_t LUT_X, int32_t LUT_Y, int32_t LUT_Z, int32_t IMG_X, int32_t IMG_Y, int32_t C>
|
||||
int32_t OptimizeLut(const Image<IMG_X, IMG_Y, C>& image, Lut3d<LUT_X, LUT_Y, LUT_Z>* lut) {
|
||||
static_assert(C == 3);
|
||||
|
||||
template <int32_t LUT_X, int32_t LUT_Y, int32_t LUT_Z, int32_t IMG_X, int32_t IMG_Y>
|
||||
int32_t OptimizeLut(const Image<IMG_X, IMG_Y, RgbColor>& image, Lut3d<LUT_X, LUT_Y, LUT_Z>* lut) {
|
||||
auto snapshot = *lut;
|
||||
int32_t diff = 0;
|
||||
|
||||
@@ -130,7 +125,7 @@ int32_t OptimizeLut(const Image<IMG_X, IMG_Y, C>& image, Lut3d<LUT_X, LUT_Y, LUT
|
||||
|
||||
std::cout << Coord<3>{{{{x, y, z}}}} << std::endl;
|
||||
|
||||
for (int32_t c = 0; c < C; ++c) {
|
||||
for (int32_t c = 0; c < color.size(); ++c) {
|
||||
auto& channel = color.at(c);
|
||||
|
||||
auto min = FindPossibleMinimum<int32_t, int32_t, 8>(
|
||||
@@ -154,10 +149,8 @@ int32_t OptimizeLut(const Image<IMG_X, IMG_Y, C>& image, Lut3d<LUT_X, LUT_Y, LUT
|
||||
return diff;
|
||||
}
|
||||
|
||||
template <int32_t LUT_X, int32_t IMG_X, int32_t IMG_Y, int32_t C>
|
||||
int32_t OptimizeLut(const Image<IMG_X, IMG_Y, C>& image, Lut1d<LUT_X>* lut) {
|
||||
static_assert(C == 3);
|
||||
|
||||
template <int32_t LUT_X, int32_t IMG_X, int32_t IMG_Y>
|
||||
int32_t OptimizeLut(const Image<IMG_X, IMG_Y, RgbColor>& image, Lut1d<LUT_X>* lut) {
|
||||
auto snapshot = *lut;
|
||||
int32_t diff = 0;
|
||||
|
||||
@@ -166,7 +159,7 @@ int32_t OptimizeLut(const Image<IMG_X, IMG_Y, C>& image, Lut1d<LUT_X>* lut) {
|
||||
|
||||
std::cout << Coord<1>{{{{x}}}} << std::endl;
|
||||
|
||||
for (int32_t c = 0; c < C; ++c) {
|
||||
for (int32_t c = 0; c < color.ssize(); ++c) {
|
||||
auto& channel = color.at(c);
|
||||
|
||||
auto min = FindPossibleMinimum<int32_t, int32_t, 8>(
|
||||
|
||||
7
colors.h
7
colors.h
@@ -2,5 +2,8 @@
|
||||
|
||||
#include "color.h"
|
||||
|
||||
constexpr RgbColor kBlack = {{{{{0x0000, 0x0000, 0x0000}}}}};
|
||||
constexpr RgbColor kWhite = {{{{{0xffff, 0xffff, 0xffff}}}}};
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wglobal-constructors"
|
||||
const RgbColor kBlack = {{{{{0x0000, 0x0000, 0x0000}}}}};
|
||||
const RgbColor kWhite = {{{{{0xffff, 0xffff, 0xffff}}}}};
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
44
image.h
44
image.h
@@ -9,57 +9,57 @@
|
||||
#include "color.h"
|
||||
#include "coord.h"
|
||||
|
||||
template <int32_t X, int32_t Y, int32_t C>
|
||||
class Image : public Array<Array<Color<C>, X>, Y> {
|
||||
template <int32_t X, int32_t Y, class C>
|
||||
class Image : public Array<Array<C, X>, Y> {
|
||||
public:
|
||||
constexpr const Color<C>& GetPixel(const Coord<2>& coord) const;
|
||||
constexpr const C& GetPixel(const Coord<2>& coord) const;
|
||||
|
||||
void SetPixel(const Coord<2>& coord, const Color<C>& color);
|
||||
void DrawXLine(const Coord<2>& start, const Color<C>& color, int32_t length);
|
||||
void DrawYLine(const Coord<2>& start, const Color<C>& color, int32_t length);
|
||||
void DrawRectangle(const Coord<2>& start, const Color<C>& color, int32_t x_length, int32_t y_length);
|
||||
void DrawSquare(const Coord<2>& start, const Color<C>& color, int32_t length);
|
||||
void SetPixel(const Coord<2>& coord, const C& color);
|
||||
void DrawXLine(const Coord<2>& start, const C& color, int32_t length);
|
||||
void DrawYLine(const Coord<2>& start, const C& color, int32_t length);
|
||||
void DrawRectangle(const Coord<2>& start, const C& color, int32_t x_length, int32_t y_length);
|
||||
void DrawSquare(const Coord<2>& start, const C& color, int32_t length);
|
||||
|
||||
std::string ToPng();
|
||||
};
|
||||
|
||||
template <int32_t X, int32_t Y, int32_t C>
|
||||
constexpr const Color<C>& Image<X, Y, C>::GetPixel(const Coord<2>& coord) const {
|
||||
template <int32_t X, int32_t Y, class C>
|
||||
constexpr const C& Image<X, Y, C>::GetPixel(const Coord<2>& coord) const {
|
||||
return this->at(coord.at(1)).at(coord.at(0));
|
||||
}
|
||||
|
||||
template <int32_t X, int32_t Y, int32_t C>
|
||||
void Image<X, Y, C>::SetPixel(const Coord<2>& coord, const Color<C>& color) {
|
||||
template <int32_t X, int32_t Y, class C>
|
||||
void Image<X, Y, C>::SetPixel(const Coord<2>& coord, const C& color) {
|
||||
if (coord.at(0) >= X || coord.at(1) >= Y) {
|
||||
return;
|
||||
}
|
||||
this->at(coord.at(1)).at(coord.at(0)) = color;
|
||||
}
|
||||
|
||||
template <int32_t X, int32_t Y, int32_t C>
|
||||
void Image<X, Y, C>::DrawXLine(const Coord<2>& coord, const Color<C>& color, int32_t length) {
|
||||
template <int32_t X, int32_t Y, class C>
|
||||
void Image<X, Y, C>::DrawXLine(const Coord<2>& coord, const C& color, int32_t length) {
|
||||
for (int32_t x = coord.at(0); x <= coord.at(0) + length; ++x) {
|
||||
SetPixel({{{{x, coord.at(1)}}}}, color);
|
||||
}
|
||||
}
|
||||
|
||||
template <int32_t X, int32_t Y, int32_t C>
|
||||
void Image<X, Y, C>::DrawYLine(const Coord<2>& coord, const Color<C>& color, int32_t length) {
|
||||
template <int32_t X, int32_t Y, class C>
|
||||
void Image<X, Y, C>::DrawYLine(const Coord<2>& coord, const C& color, int32_t length) {
|
||||
for (int32_t y = coord.at(1); y <= coord.at(1) + length; ++y) {
|
||||
SetPixel({{{{coord.at(0), y}}}}, color);
|
||||
}
|
||||
}
|
||||
|
||||
template <int32_t X, int32_t Y, int32_t C>
|
||||
void Image<X, Y, C>::DrawRectangle(const Coord<2>& start, const Color<C>& color, int32_t x_length, int32_t y_length) {
|
||||
template <int32_t X, int32_t Y, class C>
|
||||
void Image<X, Y, C>::DrawRectangle(const Coord<2>& start, const C& color, int32_t x_length, int32_t y_length) {
|
||||
DrawXLine(start, color, x_length);
|
||||
DrawXLine({{{{start.at(0), start.at(1) + y_length}}}}, color, x_length);
|
||||
DrawYLine(start, color, y_length);
|
||||
DrawYLine({{{{start.at(0) + x_length, start.at(1)}}}}, color, y_length);
|
||||
}
|
||||
|
||||
template <int32_t X, int32_t Y, int32_t C>
|
||||
void Image<X, Y, C>::DrawSquare(const Coord<2>& start, const Color<C>& color, int32_t length) {
|
||||
template <int32_t X, int32_t Y, class C>
|
||||
void Image<X, Y, C>::DrawSquare(const Coord<2>& start, const C& color, int32_t length) {
|
||||
DrawRectangle(start, color, length, length);
|
||||
}
|
||||
|
||||
@@ -68,9 +68,9 @@ static inline void WriteCallback(png_structp png_ptr, png_bytep data, png_size_t
|
||||
dest->append(reinterpret_cast<char*>(data), length);
|
||||
}
|
||||
|
||||
template <int32_t X, int32_t Y, int32_t C>
|
||||
template <int32_t X, int32_t Y, class C>
|
||||
std::string Image<X, Y, C>::ToPng() {
|
||||
static_assert(C == 3); // PNG only supports RGB
|
||||
// TODO: specialize this to RgbColor
|
||||
|
||||
std::string ret;
|
||||
|
||||
|
||||
5
lut.h
5
lut.h
@@ -11,9 +11,10 @@ class LutBase {
|
||||
LutBase(const LutBase&) = default;
|
||||
virtual ~LutBase();
|
||||
|
||||
// TODO: Allow other color dimensions
|
||||
virtual Color<3> MapColor(const Color<3>& in) const = 0;
|
||||
|
||||
template <int32_t X, int32_t Y, int32_t C>
|
||||
template <int32_t X, int32_t Y, class C>
|
||||
std::unique_ptr<Image<X, Y, C>> MapImage(const Image<X, Y, C>& in) const;
|
||||
|
||||
protected:
|
||||
@@ -21,7 +22,7 @@ class LutBase {
|
||||
static constexpr int32_t BlockSize(int32_t points);
|
||||
};
|
||||
|
||||
template <int32_t X, int32_t Y, int32_t C>
|
||||
template <int32_t X, int32_t Y, class C>
|
||||
std::unique_ptr<Image<X, Y, C>> LutBase::MapImage(const Image<X, Y, C>& in) const {
|
||||
auto out = std::make_unique<Image<X, Y, C>>();
|
||||
|
||||
|
||||
57
piraw.h
57
piraw.h
@@ -10,15 +10,15 @@ namespace std {
|
||||
using string_view = experimental::string_view;
|
||||
}
|
||||
|
||||
template <int32_t X, int32_t Y, int32_t C, int32_t D, int32_t A, int32_t P>
|
||||
template <int32_t X, int32_t Y, int32_t D, int32_t A, int32_t P>
|
||||
class PiRaw {
|
||||
public:
|
||||
PiRaw() = delete;
|
||||
PiRaw(const PiRaw&) = delete;
|
||||
PiRaw(PiRaw&&) = delete;
|
||||
|
||||
static std::unique_ptr<Image<X / 2, Y / 2, C>> FromJpeg(const std::string_view& jpeg);
|
||||
static std::unique_ptr<Image<X / 2, Y / 2, C>> FromRaw(const std::string_view& raw);
|
||||
static std::unique_ptr<Image<X / 2, Y / 2, RgbColor>> FromJpeg(const std::string_view& jpeg);
|
||||
static std::unique_ptr<Image<X / 2, Y / 2, RgbColor>> FromRaw(const std::string_view& raw);
|
||||
|
||||
private:
|
||||
static constexpr int32_t kJpegHeaderBytes = 32768;
|
||||
@@ -36,30 +36,27 @@ class PiRaw {
|
||||
typedef Array<int32_t, kPixelsPerChunk> Chunk;
|
||||
|
||||
static constexpr Chunk GetChunk(const std::string_view& raw, const int32_t x_chunk, const int32_t y);
|
||||
static constexpr Color<C> CombineRaw(int32_t y0x0, int32_t y0x1, int32_t y1x0, int32_t y1x1);
|
||||
static constexpr RgbColor CombineRaw(int32_t y0x0, int32_t y0x1, int32_t y1x0, int32_t y1x1);
|
||||
};
|
||||
|
||||
typedef PiRaw<3280, 2464, 3, 10, 16, 2> PiRaw2;
|
||||
|
||||
template <int32_t X, int32_t Y, int32_t C, int32_t D, int32_t A, int32_t P>
|
||||
typename std::unique_ptr<Image<X / 2, Y / 2, C>> PiRaw<X, Y, C, D, A, P>::FromJpeg(const std::string_view& jpeg) {
|
||||
static_assert(C == 3);
|
||||
typedef PiRaw<3280, 2464, 10, 16, 2> PiRaw2;
|
||||
|
||||
template <int32_t X, int32_t Y, int32_t D, int32_t A, int32_t P>
|
||||
typename std::unique_ptr<Image<X / 2, Y / 2, RgbColor>> PiRaw<X, Y, D, A, P>::FromJpeg(const std::string_view& jpeg) {
|
||||
size_t container_len = GetRawBytes() + kJpegHeaderBytes;
|
||||
assert(jpeg.substr(jpeg.size() - container_len, 4) == kJpegHeaderMagic);
|
||||
return FromRaw(jpeg.substr(jpeg.size() - GetRawBytes(), GetRawBytes()));
|
||||
}
|
||||
|
||||
template <int32_t X, int32_t Y, int32_t C, int32_t D, int32_t A, int32_t P>
|
||||
typename std::unique_ptr<Image<X / 2, Y / 2, C>> PiRaw<X, Y, C, D, A, P>::FromRaw(const std::string_view& raw) {
|
||||
static_assert(C == 3);
|
||||
template <int32_t X, int32_t Y, int32_t D, int32_t A, int32_t P>
|
||||
typename std::unique_ptr<Image<X / 2, Y / 2, RgbColor>> PiRaw<X, Y, D, A, P>::FromRaw(const std::string_view& raw) {
|
||||
static_assert(X % 2 == 0);
|
||||
static_assert(Y % 2 == 0);
|
||||
static_assert(kPixelsPerChunk == 4);
|
||||
|
||||
assert(raw.size() == GetRawBytes());
|
||||
|
||||
auto image = std::make_unique<Image<X / 2, Y / 2, C>>();
|
||||
auto image = std::make_unique<Image<X / 2, Y / 2, RgbColor>>();
|
||||
|
||||
for (int32_t y = 0, out_y = 0; y < Y; y += 2, ++out_y) {
|
||||
for (int32_t x_chunk = 0, out_x = 0; x_chunk < X / kPixelsPerChunk; ++x_chunk, out_x += kPixelsPerChunk / 2) {
|
||||
@@ -72,35 +69,34 @@ typename std::unique_ptr<Image<X / 2, Y / 2, C>> PiRaw<X, Y, C, D, A, P>::FromRa
|
||||
return image;
|
||||
}
|
||||
|
||||
template <int32_t X, int32_t Y, int32_t C, int32_t D, int32_t A, int32_t P>
|
||||
constexpr int32_t PiRaw<X, Y, C, D, A, P>::GetRawBytes() {
|
||||
template <int32_t X, int32_t Y, int32_t D, int32_t A, int32_t P>
|
||||
constexpr int32_t PiRaw<X, Y, D, A, P>::GetRawBytes() {
|
||||
return GetRowBytes() * GetNumRows();
|
||||
}
|
||||
|
||||
template <int32_t X, int32_t Y, int32_t C, int32_t D, int32_t A, int32_t P>
|
||||
constexpr int32_t PiRaw<X, Y, C, D, A, P>::GetRowBytes() {
|
||||
template <int32_t X, int32_t Y, int32_t D, int32_t A, int32_t P>
|
||||
constexpr int32_t PiRaw<X, Y, D, A, P>::GetRowBytes() {
|
||||
return Align(Align(X + P) * D / kBitsPerByte);
|
||||
}
|
||||
|
||||
template <int32_t X, int32_t Y, int32_t C, int32_t D, int32_t A, int32_t P>
|
||||
constexpr int32_t PiRaw<X, Y, C, D, A, P>::GetNumRows() {
|
||||
template <int32_t X, int32_t Y, int32_t D, int32_t A, int32_t P>
|
||||
constexpr int32_t PiRaw<X, Y, D, A, P>::GetNumRows() {
|
||||
return Align(Y + P);
|
||||
}
|
||||
|
||||
template <int32_t X, int32_t Y, int32_t C, int32_t D, int32_t A, int32_t P>
|
||||
constexpr int32_t PiRaw<X, Y, C, D, A, P>::GetChunkBytes() {
|
||||
template <int32_t X, int32_t Y, int32_t D, int32_t A, int32_t P>
|
||||
constexpr int32_t PiRaw<X, Y, D, A, P>::GetChunkBytes() {
|
||||
return D * kPixelsPerChunk / kBitsPerByte;
|
||||
}
|
||||
|
||||
template <int32_t X, int32_t Y, int32_t C, int32_t D, int32_t A, int32_t P>
|
||||
constexpr int32_t PiRaw<X, Y, C, D, A, P>::Align(int32_t val) {
|
||||
template <int32_t X, int32_t Y, int32_t D, int32_t A, int32_t P>
|
||||
constexpr int32_t PiRaw<X, Y, D, A, P>::Align(int32_t val) {
|
||||
return (~(A - 1)) & ((val) + (A - 1));
|
||||
}
|
||||
|
||||
template <int32_t X, int32_t Y, int32_t C, int32_t D, int32_t A, int32_t P>
|
||||
constexpr typename PiRaw<X, Y, C, D, A, P>::Chunk PiRaw<X, Y, C, D, A, P>::GetChunk(const std::string_view& raw, const int32_t x_chunk, const int32_t y) {
|
||||
template <int32_t X, int32_t Y, int32_t D, int32_t A, int32_t P>
|
||||
constexpr typename PiRaw<X, Y, D, A, P>::Chunk PiRaw<X, Y, D, A, P>::GetChunk(const std::string_view& raw, const int32_t x_chunk, const int32_t y) {
|
||||
// Function is bit depth & layout specific
|
||||
static_assert(C == 3);
|
||||
static_assert(D == 10);
|
||||
|
||||
size_t start = static_cast<size_t>(y * GetRowBytes() + x_chunk * GetChunkBytes());
|
||||
@@ -118,12 +114,9 @@ constexpr typename PiRaw<X, Y, C, D, A, P>::Chunk PiRaw<X, Y, C, D, A, P>::GetCh
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <int32_t X, int32_t Y, int32_t C, int32_t D, int32_t A, int32_t P>
|
||||
constexpr Color<C> PiRaw<X, Y, C, D, A, P>::CombineRaw(int32_t y0x0, int32_t y0x1, int32_t y1x0, int32_t y1x1) {
|
||||
// Function is bit layout specific
|
||||
static_assert(C == 3);
|
||||
|
||||
Color<C> ret;
|
||||
template <int32_t X, int32_t Y, int32_t D, int32_t A, int32_t P>
|
||||
constexpr RgbColor PiRaw<X, Y, D, A, P>::CombineRaw(int32_t y0x0, int32_t y0x1, int32_t y1x0, int32_t y1x1) {
|
||||
RgbColor ret;
|
||||
ret.at(0) = static_cast<int32_t>(y1x1);
|
||||
ret.at(1) = static_cast<int32_t>((y0x1 + y1x0) / 2);
|
||||
ret.at(2) = static_cast<int32_t>(y0x0);
|
||||
|
||||
Reference in New Issue
Block a user