tornavis/extern/smaa_areatex/smaa_areatex.cpp

1211 lines
31 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* Copyright (C) 2016-2017 IRIE Shinsuke
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/*
* smaa_areatex.cpp version 0.4.0
*
* This is a part of smaa-cpp that is an implementation of
* Enhanced Subpixel Morphological Antialiasing (SMAA) written in C++.
*
* This program is C++ rewrite of AreaTex.py included in the original
* SMAA ditribution:
*
* https://github.com/iryoku/smaa/tree/master/Scripts
*/
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
/*------------------------------------------------------------------------------*/
/* Type Definitions */
class Int2;
class Dbl2;
class Int2 {
public:
int x, y;
Int2() { this->x = this->y = 0; }
Int2(int x) { this->x = this->y = x; }
Int2(int x, int y) { this->x = x; this->y = y; }
operator Dbl2();
Int2 operator + (Int2 other) { return Int2(x + other.x, y + other.y); }
Int2 operator * (Int2 other) { return Int2(x * other.x, y * other.y); }
};
class Dbl2 {
public:
double x, y;
Dbl2() { this->x = this->y = 0.0; }
Dbl2(double x) { this->x = this->y = x; }
Dbl2(double x, double y) { this->x = x; this->y = y; }
Dbl2 apply(double (* func)(double)) { return Dbl2(func(x), func(y)); }
operator Int2();
Dbl2 operator + (Dbl2 other) { return Dbl2(x + other.x, y + other.y); }
Dbl2 operator - (Dbl2 other) { return Dbl2(x - other.x, y - other.y); }
Dbl2 operator * (Dbl2 other) { return Dbl2(x * other.x, y * other.y); }
Dbl2 operator / (Dbl2 other) { return Dbl2(x / other.x, y / other.y); }
Dbl2 operator += (Dbl2 other) { return Dbl2(x += other.x, y += other.y); }
bool operator == (Dbl2 other) { return (x == other.x && y == other.y); }
};
Int2::operator Dbl2() { return Dbl2((double)x, (double)y); }
Dbl2::operator Int2() { return Int2((int)x, (int)y); }
/*------------------------------------------------------------------------------*/
/* Data to Calculate Areatex */
/* Texture sizes: */
/* (it's quite possible that this is not easily configurable) */
static const int SUBSAMPLES_ORTHO = 7;
static const int SUBSAMPLES_DIAG = 5;
static const int MAX_DIST_ORTHO_COMPAT = 16;
static const int MAX_DIST_ORTHO = 20;
static const int MAX_DIST_DIAG = 20;
static const int TEX_SIZE_ORTHO = 80; /* 16 * 5 slots = 80 */
static const int TEX_SIZE_DIAG = 80; /* 20 * 4 slots = 80 */
/* Number of samples for calculating areas in the diagonal textures: */
/* (diagonal areas are calculated using brute force sampling) */
static const int SAMPLES_DIAG = 30;
/* Maximum distance for smoothing u-shapes: */
static const int SMOOTH_MAX_DISTANCE = 32;
/*------------------------------------------------------------------------------*/
/* Offset Tables */
/* Offsets for subsample rendering */
static const double subsample_offsets_ortho[SUBSAMPLES_ORTHO] = {
0.0, /* 0 */
-0.25, /* 1 */
0.25, /* 2 */
-0.125, /* 3 */
0.125, /* 4 */
-0.375, /* 5 */
0.375 /* 6 */
};
static const Dbl2 subsample_offsets_diag[SUBSAMPLES_DIAG] = {
{ 0.00, 0.00}, /* 0 */
{ 0.25, -0.25}, /* 1 */
{-0.25, 0.25}, /* 2 */
{ 0.125, -0.125}, /* 3 */
{-0.125, 0.125} /* 4 */
};
/* Mapping offsets for placing each pattern subtexture into its place */
enum edgesorthoIndices
{
EDGESORTHO_NONE_NONE = 0,
EDGESORTHO_NONE_NEGA = 1,
EDGESORTHO_NONE_POSI = 2,
EDGESORTHO_NONE_BOTH = 3,
EDGESORTHO_NEGA_NONE = 4,
EDGESORTHO_NEGA_NEGA = 5,
EDGESORTHO_NEGA_POSI = 6,
EDGESORTHO_NEGA_BOTH = 7,
EDGESORTHO_POSI_NONE = 8,
EDGESORTHO_POSI_NEGA = 9,
EDGESORTHO_POSI_POSI = 10,
EDGESORTHO_POSI_BOTH = 11,
EDGESORTHO_BOTH_NONE = 12,
EDGESORTHO_BOTH_NEGA = 13,
EDGESORTHO_BOTH_POSI = 14,
EDGESORTHO_BOTH_BOTH = 15,
};
static const Int2 edgesortho_compat[16] = {
{0, 0}, {0, 1}, {0, 3}, {0, 4}, {1, 0}, {1, 1}, {1, 3}, {1, 4},
{3, 0}, {3, 1}, {3, 3}, {3, 4}, {4, 0}, {4, 1}, {4, 3}, {4, 4}
};
static const Int2 edgesortho[16] = {
{0, 0}, {0, 1}, {0, 2}, {0, 3}, {1, 0}, {1, 1}, {1, 2}, {1, 3},
{2, 0}, {2, 1}, {2, 2}, {2, 3}, {3, 0}, {3, 1}, {3, 2}, {3, 3}
};
enum edgesdiagIndices
{
EDGESDIAG_NONE_NONE = 0,
EDGESDIAG_NONE_VERT = 1,
EDGESDIAG_NONE_HORZ = 2,
EDGESDIAG_NONE_BOTH = 3,
EDGESDIAG_VERT_NONE = 4,
EDGESDIAG_VERT_VERT = 5,
EDGESDIAG_VERT_HORZ = 6,
EDGESDIAG_VERT_BOTH = 7,
EDGESDIAG_HORZ_NONE = 8,
EDGESDIAG_HORZ_VERT = 9,
EDGESDIAG_HORZ_HORZ = 10,
EDGESDIAG_HORZ_BOTH = 11,
EDGESDIAG_BOTH_NONE = 12,
EDGESDIAG_BOTH_VERT = 13,
EDGESDIAG_BOTH_HORZ = 14,
EDGESDIAG_BOTH_BOTH = 15,
};
static const Int2 edgesdiag[16] = {
{0, 0}, {0, 1}, {0, 2}, {0, 3}, {1, 0}, {1, 1}, {1, 2}, {1, 3},
{2, 0}, {2, 1}, {2, 2}, {2, 3}, {3, 0}, {3, 1}, {3, 2}, {3, 3}
};
/*------------------------------------------------------------------------------*/
/* Miscellaneous Utility Functions */
/* Linear interpolation: */
static Dbl2 lerp(Dbl2 a, Dbl2 b, double p)
{
return a + (b - a) * Dbl2(p);
}
/* Saturates a value to [0..1] range: */
static double saturate(double x)
{
return 0.0 < x ? (x < 1.0 ? x : 1.0) : 0.0;
}
/*------------------------------------------------------------------------------*/
/* Horizontal/Vertical Areas */
class AreaOrtho {
double m_data[SUBSAMPLES_ORTHO][TEX_SIZE_ORTHO][TEX_SIZE_ORTHO][2];
bool m_compat;
bool m_orig_u;
public:
AreaOrtho(bool compat, bool orig_u) : m_compat(compat), m_orig_u(orig_u) {}
double *getData() { return (double *)&m_data; }
Dbl2 getPixel(int offset_index, Int2 coords) {
return Dbl2(m_data[offset_index][coords.y][coords.x][0],
m_data[offset_index][coords.y][coords.x][1]);
}
void areaTex(int offset_index);
private:
void putPixel(int offset_index, Int2 coords, Dbl2 pixel) {
m_data[offset_index][coords.y][coords.x][0] = pixel.x;
m_data[offset_index][coords.y][coords.x][1] = pixel.y;
}
Dbl2 smoothArea(double d, Dbl2 a1, Dbl2 a2);
Dbl2 makeQuad(int x, double d, double o);
Dbl2 area(Dbl2 p1, Dbl2 p2, int x);
Dbl2 calculate(int pattern, int left, int right, double offset);
};
/* Smoothing function for small u-patterns: */
Dbl2 AreaOrtho::smoothArea(double d, Dbl2 a1, Dbl2 a2)
{
Dbl2 b1 = (a1 * Dbl2(2.0)).apply(sqrt) * Dbl2(0.5);
Dbl2 b2 = (a2 * Dbl2(2.0)).apply(sqrt) * Dbl2(0.5);
double p = saturate(d / (double)SMOOTH_MAX_DISTANCE);
return lerp(b1, a1, p) + lerp(b2, a2, p);
}
/* Smoothing u-patterns by quadratic function: */
Dbl2 AreaOrtho::makeQuad(int x, double d, double o)
{
double r = (double)x;
/* fmin() below is a trick to smooth tiny u-patterns: */
return Dbl2(r, (1.0 - fmin(4.0, d) * r * (d - r) / (d * d)) * o);
}
/* Calculates the area under the line p1->p2, for the pixel x..x+1: */
Dbl2 AreaOrtho::area(Dbl2 p1, Dbl2 p2, int x)
{
Dbl2 d = p2 - p1;
double x1 = (double)x;
double x2 = x1 + 1.0;
if ((x1 >= p1.x && x1 < p2.x) || (x2 > p1.x && x2 <= p2.x)) { /* inside? */
double y1 = p1.y + (x1 - p1.x) * d.y / d.x;
double y2 = p1.y + (x2 - p1.x) * d.y / d.x;
if ((copysign(1.0, y1) == copysign(1.0, y2) ||
fabs(y1) < 1e-4 || fabs(y2) < 1e-4)) { /* trapezoid? */
double a = (y1 + y2) / 2.0;
if (a < 0.0)
return Dbl2(fabs(a), 0.0);
else
return Dbl2(0.0, fabs(a));
}
else { /* Then, we got two triangles: */
double x = p1.x - p1.y * d.x / d.y, xi;
double a1 = x > p1.x ? y1 * modf(x, &xi) / 2.0 : 0.0;
double a2 = x < p2.x ? y2 * (1.0 - modf(x, &xi)) / 2.0 : 0.0;
double a = fabs(a1) > fabs(a2) ? a1 : -a2;
if (a < 0.0)
return Dbl2(fabs(a1), fabs(a2));
else
return Dbl2(fabs(a2), fabs(a1));
}
}
else
return Dbl2(0.0, 0.0);
}
/* Calculates the area for a given pattern and distances to the left and to the */
/* right, biased by an offset: */
Dbl2 AreaOrtho::calculate(int pattern, int left, int right, double offset)
{
Dbl2 a1, a2;
/*
* o1 |
* .-------´
* o2 |
*
* <---d--->
*/
double d = (double)(left + right + 1);
double o1 = 0.5 + offset;
double o2 = 0.5 + offset - 1.0;
switch (pattern) {
case EDGESORTHO_NONE_NONE:
{
/*
*
* ------
*
*/
return Dbl2(0.0, 0.0);
break;
}
case EDGESORTHO_POSI_NONE:
{
/*
*
* .------
* |
*
* We only offset L patterns in the crossing edge side, to make it
* converge with the unfiltered pattern 0 (we don't want to filter the
* pattern 0 to avoid artifacts).
*/
if (left <= right)
return area(Dbl2(0.0, o2), Dbl2(d / 2.0, 0.0), left);
else
return Dbl2(0.0, 0.0);
break;
}
case EDGESORTHO_NONE_POSI:
{
/*
*
* ------.
* |
*/
if (left >= right)
return area(Dbl2(d / 2.0, 0.0), Dbl2(d, o2), left);
else
return Dbl2(0.0, 0.0);
break;
}
case EDGESORTHO_POSI_POSI:
{
/*
*
* .------.
* | |
*/
if (m_orig_u) {
a1 = area(Dbl2(0.0, o2), Dbl2(d / 2.0, 0.0), left);
a2 = area(Dbl2(d / 2.0, 0.0), Dbl2(d, o2), left);
return smoothArea(d, a1, a2);
}
else
return area(makeQuad(left, d, o2), makeQuad(left + 1, d, o2), left);
break;
}
case EDGESORTHO_NEGA_NONE:
{
/*
* |
* `------
*
*/
if (left <= right)
return area(Dbl2(0.0, o1), Dbl2(d / 2.0, 0.0), left);
else
return Dbl2(0.0, 0.0);
break;
}
case EDGESORTHO_BOTH_NONE:
{
/*
* |
* +------
* |
*/
return Dbl2(0.0, 0.0);
break;
}
case EDGESORTHO_NEGA_POSI:
{
/*
* |
* `------.
* |
*
* A problem of not offseting L patterns (see above), is that for certain
* max search distances, the pixels in the center of a Z pattern will
* detect the full Z pattern, while the pixels in the sides will detect a
* L pattern. To avoid discontinuities, we blend the full offsetted Z
* revectorization with partially offsetted L patterns.
*/
if (fabs(offset) > 0.0) {
a1 = area(Dbl2(0.0, o1), Dbl2(d, o2), left);
a2 = area(Dbl2(0.0, o1), Dbl2(d / 2.0, 0.0), left);
a2 += area(Dbl2(d / 2.0, 0.0), Dbl2(d, o2), left);
return (a1 + a2) / Dbl2(2.0);
}
else
return area(Dbl2(0.0, o1), Dbl2(d, o2), left);
break;
}
case EDGESORTHO_BOTH_POSI:
{
/*
* |
* +------.
* | |
*/
return area(Dbl2(0.0, o1), Dbl2(d, o2), left);
break;
}
case EDGESORTHO_NONE_NEGA:
{
/*
* |
* ------´
*
*/
if (left >= right)
return area(Dbl2(d / 2.0, 0.0), Dbl2(d, o1), left);
else
return Dbl2(0.0, 0.0);
break;
}
case EDGESORTHO_POSI_NEGA:
{
/*
* |
* .------´
* |
*/
if (fabs(offset) > 0.0) {
a1 = area(Dbl2(0.0, o2), Dbl2(d, o1), left);
a2 = area(Dbl2(0.0, o2), Dbl2(d / 2.0, 0.0), left);
a2 += area(Dbl2(d / 2.0, 0.0), Dbl2(d, o1), left);
return (a1 + a2) / Dbl2(2.0);
}
else
return area(Dbl2(0.0, o2), Dbl2(d, o1), left);
break;
}
case EDGESORTHO_NONE_BOTH:
{
/*
* |
* ------+
* |
*/
return Dbl2(0.0, 0.0);
break;
}
case EDGESORTHO_POSI_BOTH:
{
/*
* |
* .------+
* | |
*/
return area(Dbl2(0.0, o2), Dbl2(d, o1), left);
break;
}
case EDGESORTHO_NEGA_NEGA:
{
/*
* | |
* `------´
*
*/
if (m_orig_u) {
a1 = area(Dbl2(0.0, o1), Dbl2(d / 2.0, 0.0), left);
a2 = area(Dbl2(d / 2.0, 0.0), Dbl2(d, o1), left);
return smoothArea(d, a1, a2);
}
else
return area(makeQuad(left, d, o1), makeQuad(left + 1, d, o1), left);
break;
}
case EDGESORTHO_BOTH_NEGA:
{
/*
* | |
* +------´
* |
*/
return area(Dbl2(0.0, o2), Dbl2(d, o1), left);
break;
}
case EDGESORTHO_NEGA_BOTH:
{
/*
* | |
* `------+
* |
*/
return area(Dbl2(0.0, o1), Dbl2(d, o2), left);
break;
}
case EDGESORTHO_BOTH_BOTH:
{
/*
* | |
* +------+
* | |
*/
return Dbl2(0.0, 0.0);
break;
}
}
return Dbl2(0.0, 0.0);
}
/*------------------------------------------------------------------------------*/
/* Diagonal Areas */
class AreaDiag {
double m_data[SUBSAMPLES_DIAG][TEX_SIZE_DIAG][TEX_SIZE_DIAG][2];
bool m_numeric;
bool m_orig_u;
public:
AreaDiag(bool numeric, bool orig_u) : m_numeric(numeric), m_orig_u(orig_u) {}
double *getData() { return (double *)&m_data; }
Dbl2 getPixel(int offset_index, Int2 coords) {
return Dbl2(m_data[offset_index][coords.y][coords.x][0],
m_data[offset_index][coords.y][coords.x][1]);
}
void areaTex(int offset_index);
private:
void putPixel(int offset_index, Int2 coords, Dbl2 pixel) {
m_data[offset_index][coords.y][coords.x][0] = pixel.x;
m_data[offset_index][coords.y][coords.x][1] = pixel.y;
}
double area1(Dbl2 p1, Dbl2 p2, Int2 p);
Dbl2 area(Dbl2 p1, Dbl2 p2, int left);
Dbl2 areaTriangle(Dbl2 p1L, Dbl2 p2L, Dbl2 p1R, Dbl2 p2R, int left);
Dbl2 calculate(int pattern, int left, int right, Dbl2 offset);
};
/* Calculates the area under the line p1->p2 for the pixel 'p' using brute */
/* force sampling: */
/* (quick and dirty solution, but it works) */
double AreaDiag::area1(Dbl2 p1, Dbl2 p2, Int2 p)
{
if (p1 == p2)
return 1.0;
double xm = (p1.x + p2.x) / 2.0, ym = (p1.y + p2.y) / 2.0;
double a = p2.y - p1.y;
double b = p1.x - p2.x;
int count = 0;
for (int ix = 0; ix < SAMPLES_DIAG; ix++) {
double x = (double)p.x + (double)ix / (double)(SAMPLES_DIAG - 1);
for (int iy = 0; iy < SAMPLES_DIAG; iy++) {
double y = (double)p.y + (double)iy / (double)(SAMPLES_DIAG - 1);
if (a * (x - xm) + b * (y - ym) > 0.0) /* inside? */
count++;
}
}
return (double)count / (double)(SAMPLES_DIAG * SAMPLES_DIAG);
}
/* Calculates the area under the line p1->p2: */
/* (includes the pixel and its opposite) */
Dbl2 AreaDiag::area(Dbl2 p1, Dbl2 p2, int left)
{
if (m_numeric) {
double a1 = area1(p1, p2, Int2(1, 0) + Int2(left));
double a2 = area1(p1, p2, Int2(1, 1) + Int2(left));
return Dbl2(1.0 - a1, a2);
}
/* Calculates the area under the line p1->p2 for the pixel 'p' analytically */
Dbl2 d = p2 - p1;
if (d.x == 0.0)
return Dbl2(0.0, 1.0);
if (d.y == 0.0)
return Dbl2(1.0, 0.0);
double x1 = (double)(1 + left);
double x2 = x1 + 1.0;
double ymid = x1;
double xtop = p1.x + (ymid + 1.0 - p1.y) * d.x / d.y;
double xmid = p1.x + (ymid - p1.y) * d.x / d.y;
double xbot = p1.x + (ymid - 1.0 - p1.y) * d.x / d.y;
double y1 = p1.y + (x1 - p1.x) * d.y / d.x;
double y2 = p1.y + (x2 - p1.x) * d.y / d.x;
double fy1 = y1 - floor(y1);
double fy2 = y2 - floor(y2);
int iy1 = (int)floor(y1 - ymid);
int iy2 = (int)floor(y2 - ymid);
if (iy1 <= -2) {
if (iy2 == -1)
return Dbl2(1.0 - (x2 - xbot) * fy2 * 0.5, 0.0);
else if (iy2 == 0)
return Dbl2((xmid + xbot) * 0.5 - x1, (x2 - xmid) * fy2 * 0.5);
else if (iy2 >= 1)
return Dbl2((xmid + xbot) * 0.5 - x1, x2 - (xtop + xmid) * 0.5);
else /* iy2 < -1 */
return Dbl2(1.0, 0.0);
}
else if (iy1 == -1) {
if (iy2 == -1)
return Dbl2(1.0 - (fy1 + fy2) * 0.5, 0.0);
else if (iy2 == 0)
return Dbl2((xmid - x1) * (1.0 - fy1) * 0.5, (x2 - xmid) * fy2 * 0.5);
else if (iy2 >= 1)
return Dbl2((xmid - x1) * (1.0 - fy1) * 0.5, x2 - (xtop + xmid) * 0.5);
else /* iy2 < -1 */
return Dbl2(1.0 - (xbot - x1) * fy1 * 0.5, 0.0);
}
else if (iy1 == 0) {
if (iy2 == -1)
return Dbl2((x2 - xmid) * (1.0 - fy2) * 0.5, (xmid - x1) * fy1 * 0.5);
else if (iy2 == 0)
return Dbl2(0.0, (fy1 + fy2) * 0.5);
else if (iy2 >= 1)
return Dbl2(0.0, 1.0 - (xtop - x1) * (1.0 - fy1) * 0.5);
else /* iy2 < -1 */
return Dbl2(x2 - (xmid + xbot) * 0.5, (xmid - x1) * fy1 * 0.5);
}
else { /* iy1 > 0 */
if (iy2 == -1)
return Dbl2((x2 - xtop) * (1.0 - fy2) * 0.5, (xtop + xmid) * 0.5 - x1);
else if (iy2 == 0)
return Dbl2(0.0, 1.0 - (x1 - xtop) * (1.0 - fy2) * 0.5);
else if (iy2 >= 1)
return Dbl2(0.0, 1.0);
else /* iy2 < -1 */
return Dbl2(x2 - (xmid + xbot) * 0.5, (xtop + xmid) * 0.5 - x1);
}
}
/* Calculate u-patterns using a triangle: */
Dbl2 AreaDiag::areaTriangle(Dbl2 p1L, Dbl2 p2L, Dbl2 p1R, Dbl2 p2R, int left)
{
double x1 = (double)(1 + left);
double x2 = x1 + 1.0;
Dbl2 dL = p2L - p1L;
Dbl2 dR = p2R - p1R;
double xm = ((p1L.x * dL.y / dL.x - p1L.y) - (p1R.x * dR.y / dR.x - p1R.y)) / (dL.y / dL.x - dR.y / dR.x);
double y1 = (x1 < xm) ? p1L.y + (x1 - p1L.x) * dL.y / dL.x : p1R.y + (x1 - p1R.x) * dR.y / dR.x;
double y2 = (x2 < xm) ? p1L.y + (x2 - p1L.x) * dL.y / dL.x : p1R.y + (x2 - p1R.x) * dR.y / dR.x;
return area(Dbl2(x1, y1), Dbl2(x2, y2), left);
}
/* Calculates the area for a given pattern and distances to the left and to the */
/* right, biased by an offset: */
Dbl2 AreaDiag::calculate(int pattern, int left, int right, Dbl2 offset)
{
Dbl2 a1, a2;
double d = (double)(left + right + 1);
/*
* There is some Black Magic around diagonal area calculations. Unlike
* orthogonal patterns, the 'null' pattern (one without crossing edges) must be
* filtered, and the ends of both the 'null' and L patterns are not known: L
* and U patterns have different endings, and we don't know what is the
* adjacent pattern. So, what we do is calculate a blend of both possibilites.
*/
switch (pattern) {
case EDGESDIAG_NONE_NONE:
{
/*
*
* .-´
* .-´
* .-´
* .-´
* ´
*
*/
a1 = area(Dbl2(1.0, 1.0), Dbl2(1.0, 1.0) + Dbl2(d), left); /* 1st possibility */
a2 = area(Dbl2(1.0, 0.0), Dbl2(1.0, 0.0) + Dbl2(d), left); /* 2nd possibility */
return (a1 + a2) / Dbl2(2.0); /* Blend them */
break;
}
case EDGESDIAG_VERT_NONE:
{
/*
*
* .-´
* .-´
* .-´
* .-´
* |
* |
*/
a1 = area(Dbl2(1.0, 0.0) + offset, Dbl2(0.0, 0.0) + Dbl2(d), left);
a2 = area(Dbl2(1.0, 0.0) + offset, Dbl2(1.0, 0.0) + Dbl2(d), left);
return (a1 + a2) / Dbl2(2.0);
break;
}
case EDGESDIAG_NONE_HORZ:
{
/*
*
* .----
* .-´
* .-´
* .-´
* ´
*
*/
a1 = area(Dbl2(0.0, 0.0), Dbl2(1.0, 0.0) + Dbl2(d) + offset, left);
a2 = area(Dbl2(1.0, 0.0), Dbl2(1.0, 0.0) + Dbl2(d) + offset, left);
return (a1 + a2) / Dbl2(2.0);
break;
}
case EDGESDIAG_VERT_HORZ:
{
/*
*
* .----
* .-´
* .-´
* .-´
* |
* |
*/
if (m_orig_u)
return area(Dbl2(1.0, 0.0) + offset, Dbl2(1.0, 0.0) + Dbl2(d) + offset, left);
else
return areaTriangle(Dbl2(1.0, 0.0) + offset, Dbl2(1.0, 1.0) + Dbl2(d),
Dbl2(0.0, 0.0), Dbl2(1.0, 0.0) + Dbl2(d) + offset, left);
break;
}
case EDGESDIAG_HORZ_NONE:
{
/*
*
* .-´
* .-´
* .-´
* ----´
*
*
*/
a1 = area(Dbl2(1.0, 1.0) + offset, Dbl2(0.0, 0.0) + Dbl2(d), left);
a2 = area(Dbl2(1.0, 1.0) + offset, Dbl2(1.0, 0.0) + Dbl2(d), left);
return (a1 + a2) / Dbl2(2.0);
break;
}
case EDGESDIAG_BOTH_NONE:
{
/*
*
* .-´
* .-´
* .-´
* --.-´
* |
* |
*/
a1 = area(Dbl2(1.0, 1.0) + offset, Dbl2(0.0, 0.0) + Dbl2(d), left);
a2 = area(Dbl2(1.0, 0.0) + offset, Dbl2(1.0, 0.0) + Dbl2(d), left);
return (a1 + a2) / Dbl2(2.0);
break;
}
case EDGESDIAG_HORZ_HORZ:
{
/*
*
* .----
* .-´
* .-´
* ----´
*
*
*/
return area(Dbl2(1.0, 1.0) + offset, Dbl2(1.0, 0.0) + Dbl2(d) + offset, left);
break;
}
case EDGESDIAG_BOTH_HORZ:
{
/*
*
* .----
* .-´
* .-´
* --.-´
* |
* |
*/
a1 = area(Dbl2(1.0, 1.0) + offset, Dbl2(1.0, 0.0) + Dbl2(d) + offset, left);
a2 = area(Dbl2(1.0, 0.0) + offset, Dbl2(1.0, 0.0) + Dbl2(d) + offset, left);
return (a1 + a2) / Dbl2(2.0);
break;
}
case EDGESDIAG_NONE_VERT:
{
/*
* |
* |
* .-´
* .-´
* .-´
* ´
*
*/
a1 = area(Dbl2(0.0, 0.0), Dbl2(1.0, 1.0) + Dbl2(d) + offset, left);
a2 = area(Dbl2(1.0, 0.0), Dbl2(1.0, 1.0) + Dbl2(d) + offset, left);
return (a1 + a2) / Dbl2(2.0);
break;
}
case EDGESDIAG_VERT_VERT:
{
/*
* |
* |
* .-´
* .-´
* .-´
* |
* |
*/
return area(Dbl2(1.0, 0.0) + offset, Dbl2(1.0, 1.0) + Dbl2(d) + offset, left);
break;
}
case EDGESDIAG_NONE_BOTH:
{
/*
* |
* .----
* .-´
* .-´
* .-´
* ´
*
*/
a1 = area(Dbl2(0.0, 0.0), Dbl2(1.0, 1.0) + Dbl2(d) + offset, left);
a2 = area(Dbl2(1.0, 0.0), Dbl2(1.0, 0.0) + Dbl2(d) + offset, left);
return (a1 + a2) / Dbl2(2.0);
break;
}
case EDGESDIAG_VERT_BOTH:
{
/*
* |
* .----
* .-´
* .-´
* .-´
* |
* |
*/
a1 = area(Dbl2(1.0, 0.0) + offset, Dbl2(1.0, 1.0) + Dbl2(d) + offset, left);
a2 = area(Dbl2(1.0, 0.0) + offset, Dbl2(1.0, 0.0) + Dbl2(d) + offset, left);
return (a1 + a2) / Dbl2(2.0);
break;
}
case EDGESDIAG_HORZ_VERT:
{
/*
* |
* |
* .-´
* .-´
* ----´
*
*
*/
if (m_orig_u)
return area(Dbl2(1.0, 1.0) + offset, Dbl2(1.0, 1.0) + Dbl2(d) + offset, left);
else
return areaTriangle(Dbl2(1.0, 1.0) + offset, Dbl2(2.0, 1.0) + Dbl2(d),
Dbl2(1.0, 0.0), Dbl2(1.0, 1.0) + Dbl2(d) + offset, left);
break;
}
case EDGESDIAG_BOTH_VERT:
{
/*
* |
* |
* .-´
* .-´
* --.-´
* |
* |
*/
a1 = area(Dbl2(1.0, 1.0) + offset, Dbl2(1.0, 1.0) + Dbl2(d) + offset, left);
a2 = area(Dbl2(1.0, 0.0) + offset, Dbl2(1.0, 1.0) + Dbl2(d) + offset, left);
return (a1 + a2) / Dbl2(2.0);
break;
}
case EDGESDIAG_HORZ_BOTH:
{
/*
* |
* .----
* .-´
* .-´
* ----´
*
*
*/
a1 = area(Dbl2(1.0, 1.0) + offset, Dbl2(1.0, 1.0) + Dbl2(d) + offset, left);
a2 = area(Dbl2(1.0, 1.0) + offset, Dbl2(1.0, 0.0) + Dbl2(d) + offset, left);
return (a1 + a2) / Dbl2(2.0);
break;
}
case EDGESDIAG_BOTH_BOTH:
{
/*
* |
* .----
* .-´
* .-´
* --.-´
* |
* |
*/
a1 = area(Dbl2(1.0, 1.0) + offset, Dbl2(1.0, 1.0) + Dbl2(d) + offset, left);
a2 = area(Dbl2(1.0, 0.0) + offset, Dbl2(1.0, 0.0) + Dbl2(d) + offset, left);
return (a1 + a2) / Dbl2(2.0);
break;
}
}
return Dbl2(0.0, 0.0);
}
/*------------------------------------------------------------------------------*/
/* Main Loops */
void AreaOrtho::areaTex(int offset_index)
{
double offset = subsample_offsets_ortho[offset_index];
int max_dist = m_compat ? MAX_DIST_ORTHO_COMPAT : MAX_DIST_ORTHO;
for (int pattern = 0; pattern < 16; pattern++) {
Int2 e = Int2(max_dist) * (m_compat ? edgesortho_compat : edgesortho)[pattern];
for (int left = 0; left < max_dist; left++) {
for (int right = 0; right < max_dist; right++) {
Dbl2 p = calculate(pattern, left * left, right * right, offset);
Int2 coords = e + Int2(left, right);
putPixel(offset_index, coords, p);
}
}
}
return;
}
void AreaDiag::areaTex(int offset_index)
{
Dbl2 offset = subsample_offsets_diag[offset_index];
for (int pattern = 0; pattern < 16; pattern++) {
Int2 e = Int2(MAX_DIST_DIAG) * edgesdiag[pattern];
for (int left = 0; left < MAX_DIST_DIAG; left++) {
for (int right = 0; right < MAX_DIST_DIAG; right++) {
Dbl2 p = calculate(pattern, left, right, offset);
Int2 coords = e + Int2(left, right);
putPixel(offset_index, coords, p);
}
}
}
return;
}
/*------------------------------------------------------------------------------*/
/* Write File to Specified Location on Disk */
/* C/C++ source code (arrays of floats) */
static void write_double_array(FILE *fp, const double *ptr, int length, const char *array_name, bool quantize)
{
fprintf(fp, "static const float %s[%d] = {", array_name, length);
for (int n = 0; n < length; n++) {
if (n > 0)
fprintf(fp, ",");
fprintf(fp, (n % 8 != 0) ? " " : "\n\t");
if (quantize)
fprintf(fp, "%3d / 255.0", (int)(*(ptr++) * 255.0));
else
fprintf(fp, "%1.8lf", *(ptr++));
}
fprintf(fp, "\n};\n");
}
static void write_csource(AreaOrtho *ortho, AreaDiag *diag, FILE *fp, bool subsampling, bool quantize)
{
fprintf(fp, "/* This file was generated by smaa_areatex.cpp */\n");
fprintf(fp, "\n/* Horizontal/Vertical Areas */\n");
write_double_array(fp, ortho->getData(),
TEX_SIZE_ORTHO * TEX_SIZE_ORTHO * 2 * (subsampling ? SUBSAMPLES_ORTHO : 1),
"areatex", quantize);
fprintf(fp, "\n/* Diagonal Areas */\n");
write_double_array(fp, diag->getData(),
TEX_SIZE_DIAG * TEX_SIZE_DIAG * 2 * (subsampling ? SUBSAMPLES_DIAG : 1),
"areatex_diag", quantize);
}
/* .tga File (RGBA 32bit uncompressed) */
static void write_tga(AreaOrtho *ortho, AreaDiag *diag, FILE *fp, bool subsampling)
{
int subsamples = subsampling ? SUBSAMPLES_ORTHO : 1;
unsigned char header[18] = {0, 0,
2, /* uncompressed RGB */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
32, /* 32bit */
8}; /* 8bit alpha, left to right, bottom to top */
/* Set width and height */
header[12] = (TEX_SIZE_ORTHO + TEX_SIZE_DIAG) & 0xff;
header[13] = ((TEX_SIZE_ORTHO + TEX_SIZE_DIAG) >> 8) & 0xff;
header[14] = (subsamples * TEX_SIZE_ORTHO) & 0xff;
header[15] = ((subsamples * TEX_SIZE_ORTHO) >> 8) & 0xff;
/* Write .tga header */
fwrite(header, sizeof(unsigned char), sizeof(header) / sizeof(unsigned char), fp);
/* Write pixel data */
for (int i = subsamples - 1; i >= 0; i--) {
for (int y = TEX_SIZE_ORTHO - 1; y >= 0; y--) {
for (int x = 0; x < TEX_SIZE_ORTHO; x++) {
Dbl2 p = ortho->getPixel(i, Int2(x, y));
fputc(0, fp); /* B */
fputc((unsigned char)(p.y * 255.0), fp); /* G */
fputc((unsigned char)(p.x * 255.0), fp); /* R */
fputc(0, fp); /* A */
}
for (int x = 0; x < TEX_SIZE_DIAG; x++) {
if (i < SUBSAMPLES_DIAG) {
Dbl2 p = diag->getPixel(i, Int2(x, y));
fputc(0, fp); /* B */
fputc((unsigned char)(p.y * 255.0), fp); /* G */
fputc((unsigned char)(p.x * 255.0), fp); /* R */
fputc(0, fp); /* A */
}
else {
fputc(0, fp);
fputc(0, fp);
fputc(0, fp);
fputc(0, fp);
}
}
}
}
}
/* .raw File (R8G8 raw data) */
static void write_raw(AreaOrtho *ortho, AreaDiag *diag, FILE *fp, bool subsampling)
{
int subsamples = subsampling ? SUBSAMPLES_ORTHO : 1;
/* Write pixel data */
for (int i = 0; i < subsamples; i++) {
for (int y = 0; y < TEX_SIZE_ORTHO; y++) {
for (int x = 0; x < TEX_SIZE_ORTHO; x++) {
Dbl2 p = ortho->getPixel(i, Int2(x, y));
fputc((unsigned char)(p.x * 255.0), fp); /* R */
fputc((unsigned char)(p.y * 255.0), fp); /* G */
}
for (int x = 0; x < TEX_SIZE_DIAG; x++) {
if (i < SUBSAMPLES_DIAG) {
Dbl2 p = diag->getPixel(i, Int2(x, y));
fputc((unsigned char)(p.x * 255.0), fp); /* R */
fputc((unsigned char)(p.y * 255.0), fp); /* G */
}
else {
fputc(0, fp);
fputc(0, fp);
}
}
}
}
}
static int generate_file(AreaOrtho *ortho, AreaDiag *diag, const char *path, bool subsampling, bool quantize, bool tga, bool raw)
{
FILE *fp = fopen(path, tga ? "wb" : "w");
if (!fp) {
fprintf(stderr, "Unable to open file: %s\n", path);
return 1;
}
// fprintf(stderr, "Generating %s\n", path);
if (tga)
write_tga(ortho, diag, fp, subsampling);
else if (raw)
write_raw(ortho, diag, fp, subsampling);
else
write_csource(ortho, diag, fp, subsampling, quantize);
fclose(fp);
return 0;
}
int main(int argc, char **argv)
{
bool subsampling = false;
bool quantize = false;
bool tga = false;
bool raw = false;
bool compat = false;
bool numeric = false;
bool orig_u = false;
bool help = false;
char *outfile = NULL;
int status = 0;
for (int i = 1; i < argc; i++) {
char *ptr = argv[i];
if (*ptr++ == '-' && *ptr != '\0') {
char c;
while ((c = *ptr++) != '\0') {
if (c == 's')
subsampling = true;
else if (c == 'q')
quantize = true;
else if (c == 't')
tga = true;
else if (c == 'r')
raw = true;
else if (c == 'c')
compat = true;
else if (c == 'n')
numeric = true;
else if (c == 'u')
orig_u = true;
else if (c == 'h')
help = true;
else {
fprintf(stderr, "Unknown option: -%c\n", c);
status = 1;
break;
}
}
}
else if (outfile) {
fprintf(stderr, "Too much file names: %s, %s\n", outfile, argv[i]);
status = 1;
}
else
outfile = argv[i];
if (status != 0)
break;
}
if (status == 0 && !help && !outfile) {
fprintf(stderr, "File name was not specified.\n");
status = 1;
}
if (status != 0 || help) {
fprintf(stderr, "Usage: %s [OPTION]... OUTFILE\n", argv[0]);
fprintf(stderr, "Options:\n");
fprintf(stderr, " -s Calculate data for subpixel rendering\n");
fprintf(stderr, " -q Quantize data to 256 levels\n");
fprintf(stderr, " -t Write TGA image instead of C/C++ source\n");
fprintf(stderr, " -r Write R8G8 raw image instead of C/C++ source\n");
fprintf(stderr, " -c Generate compatible orthogonal data that subtexture size is 16\n");
fprintf(stderr, " -n Numerically calculate diagonal data using brute force sampling\n");
fprintf(stderr, " -u Process orthogonal / diagonal U patterns in older ways\n");
fprintf(stderr, " -h Print this help and exit\n");
fprintf(stderr, "File name OUTFILE usually should have an extension such as .c, .h, or .tga,\n");
fprintf(stderr, "except for a special name '-' that means standard output.\n\n");
fprintf(stderr, "Example:\n");
fprintf(stderr, " Generate TGA file exactly same as AreaTexDX10.tga bundled with the\n");
fprintf(stderr, " original implementation:\n\n");
fprintf(stderr, " $ smaa_areatex -stcnu AreaTexDX10.tga\n\n");
return status;
}
AreaOrtho *ortho = new AreaOrtho(compat, orig_u);
AreaDiag *diag = new AreaDiag(numeric, orig_u);
/* Calculate areatex data */
for (int i = 0; i < (subsampling ? SUBSAMPLES_ORTHO : 1); i++)
ortho->areaTex(i);
for (int i = 0; i < (subsampling ? SUBSAMPLES_DIAG : 1); i++)
diag->areaTex(i);
/* Generate .tga, .raw, or C/C++ source file, or write the data to stdout */
if (strcmp(outfile, "-") != 0)
status = generate_file(ortho, diag, outfile, subsampling, quantize, tga, raw);
else if (tga)
write_tga(ortho, diag, stdout, subsampling);
else if (raw)
write_raw(ortho, diag, stdout, subsampling);
else
write_csource(ortho, diag, stdout, subsampling, quantize);
delete ortho;
delete diag;
return status;
}
/* smaa_areatex.cpp ends here */