Merge private branch.

This commit is contained in:
castano 2010-05-27 23:18:39 +00:00
parent e7f2d1e2bc
commit 51a4fe7e2d
15 changed files with 3558 additions and 3549 deletions

View File

@ -23,10 +23,10 @@
#include "BlockDXT.h"
#include <nvimage/ColorBlock.h>
#include "ColorBlock.h"
#include <nvcore/Stream.h>
#include <nvcore/Containers.h> // swap
#include "nvcore/Stream.h"
#include "nvcore/Utils.h" // swap
using namespace nv;

View File

@ -1,26 +1,28 @@
// This code is in the public domain -- castanyo@yahoo.es
#include <nvcore/Containers.h> // swap
#include <nvmath/Box.h>
#include <nvimage/ColorBlock.h>
#include <nvimage/Image.h>
#include "ColorBlock.h"
#include "Image.h"
#include "nvmath/Box.h"
#include "nvcore/Utils.h" // swap
using namespace nv;
namespace {
// Get approximate luminance.
inline static uint colorLuminance(Color32 c)
{
return c.r + c.g + c.b;
}
// Get the euclidean distance between the given colors.
inline static uint colorDistance(Color32 c0, Color32 c1)
{
return (c0.r - c1.r) * (c0.r - c1.r) + (c0.g - c1.g) * (c0.g - c1.g) + (c0.b - c1.b) * (c0.b - c1.b);
}
// Get approximate luminance.
inline static uint colorLuminance(Color32 c)
{
return c.r + c.g + c.b;
}
// Get the euclidean distance between the given colors.
inline static uint colorDistance(Color32 c0, Color32 c1)
{
return (c0.r - c1.r) * (c0.r - c1.r) + (c0.g - c1.g) * (c0.g - c1.g) + (c0.b - c1.b) * (c0.b - c1.b);
}
} // namespace`
@ -32,450 +34,450 @@ ColorBlock::ColorBlock()
/// Init the color block from an array of colors.
ColorBlock::ColorBlock(const uint * linearImage)
{
for(uint i = 0; i < 16; i++) {
color(i) = Color32(linearImage[i]);
}
for(uint i = 0; i < 16; i++) {
color(i) = Color32(linearImage[i]);
}
}
/// Init the color block with the contents of the given block.
ColorBlock::ColorBlock(const ColorBlock & block)
{
for(uint i = 0; i < 16; i++) {
color(i) = block.color(i);
}
for(uint i = 0; i < 16; i++) {
color(i) = block.color(i);
}
}
/// Initialize this color block.
ColorBlock::ColorBlock(const Image * img, uint x, uint y)
{
init(img, x, y);
init(img, x, y);
}
void ColorBlock::init(const Image * img, uint x, uint y)
{
nvDebugCheck(img != NULL);
const uint bw = min(img->width() - x, 4U);
const uint bh = min(img->height() - y, 4U);
nvDebugCheck(bw != 0 && bh != 0);
nvDebugCheck(img != NULL);
static const int remainder[] = {
0, 0, 0, 0,
0, 1, 0, 1,
0, 1, 2, 0,
0, 1, 2, 3,
};
const uint bw = min(img->width() - x, 4U);
const uint bh = min(img->height() - y, 4U);
nvDebugCheck(bw != 0 && bh != 0);
// Blocks that are smaller than 4x4 are handled by repeating the pixels.
// @@ Thats only correct when block size is 1, 2 or 4, but not with 3. :(
static const int remainder[] = {
0, 0, 0, 0,
0, 1, 0, 1,
0, 1, 2, 0,
0, 1, 2, 3,
};
for(uint i = 0; i < 4; i++) {
//const int by = i % bh;
const int by = remainder[(bh - 1) * 4 + i];
for(uint e = 0; e < 4; e++) {
//const int bx = e % bw;
const int bx = remainder[(bw - 1) * 4 + e];
color(e, i) = img->pixel(x + bx, y + by);
}
}
// Blocks that are smaller than 4x4 are handled by repeating the pixels.
// @@ Thats only correct when block size is 1, 2 or 4, but not with 3. :(
for(uint i = 0; i < 4; i++) {
//const int by = i % bh;
const int by = remainder[(bh - 1) * 4 + i];
for(uint e = 0; e < 4; e++) {
//const int bx = e % bw;
const int bx = remainder[(bw - 1) * 4 + e];
color(e, i) = img->pixel(x + bx, y + by);
}
}
}
void ColorBlock::init(uint w, uint h, uint * data, uint x, uint y)
{
nvDebugCheck(data != NULL);
nvDebugCheck(data != NULL);
const uint bw = min(w - x, 4U);
const uint bh = min(h - y, 4U);
nvDebugCheck(bw != 0 && bh != 0);
const uint bw = min(w - x, 4U);
const uint bh = min(h - y, 4U);
nvDebugCheck(bw != 0 && bh != 0);
// Blocks that are smaller than 4x4 are handled by repeating the pixels.
// @@ Thats only correct when block size is 1, 2 or 4, but not with 3. :(
// Blocks that are smaller than 4x4 are handled by repeating the pixels.
// @@ Thats only correct when block size is 1, 2 or 4, but not with 3. :(
for (uint i = 0; i < 4; i++)
{
const int by = i % bh;
for (uint e = 0; e < 4; e++)
{
const int bx = e % bw;
const uint idx = (y + by) * w + x + bx;
for (uint i = 0; i < 4; i++)
{
const int by = i % bh;
color(e, i).u = data[idx];
}
}
for (uint e = 0; e < 4; e++)
{
const int bx = e % bw;
const uint idx = (y + by) * w + x + bx;
color(e, i).u = data[idx];
}
}
}
void ColorBlock::init(uint w, uint h, float * data, uint x, uint y)
{
nvDebugCheck(data != NULL);
nvDebugCheck(data != NULL);
const uint bw = min(w - x, 4U);
const uint bh = min(h - y, 4U);
nvDebugCheck(bw != 0 && bh != 0);
const uint bw = min(w - x, 4U);
const uint bh = min(h - y, 4U);
nvDebugCheck(bw != 0 && bh != 0);
// Blocks that are smaller than 4x4 are handled by repeating the pixels.
// @@ Thats only correct when block size is 1, 2 or 4, but not with 3. :(
// Blocks that are smaller than 4x4 are handled by repeating the pixels.
// @@ Thats only correct when block size is 1, 2 or 4, but not with 3. :(
for (uint i = 0; i < 4; i++)
{
const uint by = i % bh;
for (uint e = 0; e < 4; e++)
{
const uint bx = e % bw;
const uint idx = ((y + by) * w + x + bx) * 4;
Color32 & c = color(e, i);
c.r = uint8(255 * clamp(data[idx + 0], 0.0f, 1.0f));
c.g = uint8(255 * clamp(data[idx + 1], 0.0f, 1.0f));
c.b = uint8(255 * clamp(data[idx + 2], 0.0f, 1.0f));
c.a = uint8(255 * clamp(data[idx + 3], 0.0f, 1.0f));
}
}
for (uint i = 0; i < 4; i++)
{
const uint by = i % bh;
for (uint e = 0; e < 4; e++)
{
const uint bx = e % bw;
const uint idx = ((y + by) * w + x + bx) * 4;
Color32 & c = color(e, i);
c.r = uint8(255 * clamp(data[idx + 0], 0.0f, 1.0f));
c.g = uint8(255 * clamp(data[idx + 1], 0.0f, 1.0f));
c.b = uint8(255 * clamp(data[idx + 2], 0.0f, 1.0f));
c.a = uint8(255 * clamp(data[idx + 3], 0.0f, 1.0f));
}
}
}
static inline uint8 component(Color32 c, uint i)
{
if (i == 0) return c.r;
if (i == 1) return c.g;
if (i == 2) return c.b;
if (i == 3) return c.a;
if (i == 4) return 0xFF;
return 0;
if (i == 0) return c.r;
if (i == 1) return c.g;
if (i == 2) return c.b;
if (i == 3) return c.a;
if (i == 4) return 0xFF;
return 0;
}
void ColorBlock::swizzle(uint x, uint y, uint z, uint w)
{
for (int i = 0; i < 16; i++)
{
Color32 c = m_color[i];
m_color[i].r = component(c, x);
m_color[i].g = component(c, y);
m_color[i].b = component(c, z);
m_color[i].a = component(c, w);
}
for (int i = 0; i < 16; i++)
{
Color32 c = m_color[i];
m_color[i].r = component(c, x);
m_color[i].g = component(c, y);
m_color[i].b = component(c, z);
m_color[i].a = component(c, w);
}
}
/// Returns true if the block has a single color.
bool ColorBlock::isSingleColor() const
{
Color32 mask(0xFF, 0xFF, 0xFF, 0x00);
uint u = m_color[0].u & mask.u;
for (int i = 1; i < 16; i++)
{
if (u != (m_color[i].u & mask.u))
{
return false;
}
}
return true;
Color32 mask(0xFF, 0xFF, 0xFF, 0x00);
uint u = m_color[0].u & mask.u;
for (int i = 1; i < 16; i++)
{
if (u != (m_color[i].u & mask.u))
{
return false;
}
}
return true;
}
/*
/// Returns true if the block has a single color, ignoring transparent pixels.
bool ColorBlock::isSingleColorNoAlpha() const
{
Color32 c;
int i;
for(i = 0; i < 16; i++)
{
if (m_color[i].a != 0) {
c = m_color[i];
break;
}
}
Color32 c;
int i;
for(i = 0; i < 16; i++)
{
if (m_color[i].a != 0) {
c = m_color[i];
break;
}
}
Color32 mask(0xFF, 0xFF, 0xFF, 0x00);
uint u = c.u & mask.u;
Color32 mask(0xFF, 0xFF, 0xFF, 0x00);
uint u = c.u & mask.u;
for(; i < 16; i++)
{
if (u != (m_color[i].u & mask.u))
{
return false;
}
}
return true;
for(; i < 16; i++)
{
if (u != (m_color[i].u & mask.u))
{
return false;
}
}
return true;
}
*/
/// Count number of unique colors in this color block.
uint ColorBlock::countUniqueColors() const
{
uint count = 0;
uint count = 0;
// @@ This does not have to be o(n^2)
for(int i = 0; i < 16; i++)
{
bool unique = true;
for(int j = 0; j < i; j++) {
if( m_color[i] != m_color[j] ) {
unique = false;
}
}
if( unique ) {
count++;
}
}
return count;
// @@ This does not have to be o(n^2)
for(int i = 0; i < 16; i++)
{
bool unique = true;
for(int j = 0; j < i; j++) {
if( m_color[i] != m_color[j] ) {
unique = false;
}
}
if( unique ) {
count++;
}
}
return count;
}
/// Get average color of the block.
Color32 ColorBlock::averageColor() const
{
uint r, g, b, a;
r = g = b = a = 0;
uint r, g, b, a;
r = g = b = a = 0;
for(uint i = 0; i < 16; i++) {
r += m_color[i].r;
g += m_color[i].g;
b += m_color[i].b;
a += m_color[i].a;
}
return Color32(uint8(r / 16), uint8(g / 16), uint8(b / 16), uint8(a / 16));
for(uint i = 0; i < 16; i++) {
r += m_color[i].r;
g += m_color[i].g;
b += m_color[i].b;
a += m_color[i].a;
}
return Color32(uint8(r / 16), uint8(g / 16), uint8(b / 16), uint8(a / 16));
}
/// Return true if the block is not fully opaque.
bool ColorBlock::hasAlpha() const
{
for (uint i = 0; i < 16; i++)
{
if (m_color[i].a != 255) return true;
}
return false;
for (uint i = 0; i < 16; i++)
{
if (m_color[i].a != 255) return true;
}
return false;
}
/// Get diameter color range.
void ColorBlock::diameterRange(Color32 * start, Color32 * end) const
{
nvDebugCheck(start != NULL);
nvDebugCheck(end != NULL);
Color32 c0, c1;
uint best_dist = 0;
for(int i = 0; i < 16; i++) {
for (int j = i+1; j < 16; j++) {
uint dist = colorDistance(m_color[i], m_color[j]);
if( dist > best_dist ) {
best_dist = dist;
c0 = m_color[i];
c1 = m_color[j];
}
}
}
*start = c0;
*end = c1;
nvDebugCheck(start != NULL);
nvDebugCheck(end != NULL);
Color32 c0, c1;
uint best_dist = 0;
for(int i = 0; i < 16; i++) {
for (int j = i+1; j < 16; j++) {
uint dist = colorDistance(m_color[i], m_color[j]);
if( dist > best_dist ) {
best_dist = dist;
c0 = m_color[i];
c1 = m_color[j];
}
}
}
*start = c0;
*end = c1;
}
/// Get luminance color range.
void ColorBlock::luminanceRange(Color32 * start, Color32 * end) const
{
nvDebugCheck(start != NULL);
nvDebugCheck(end != NULL);
Color32 minColor, maxColor;
uint minLuminance, maxLuminance;
maxLuminance = minLuminance = colorLuminance(m_color[0]);
for(uint i = 1; i < 16; i++)
{
uint luminance = colorLuminance(m_color[i]);
if (luminance > maxLuminance) {
maxLuminance = luminance;
maxColor = m_color[i];
}
else if (luminance < minLuminance) {
minLuminance = luminance;
minColor = m_color[i];
}
}
nvDebugCheck(start != NULL);
nvDebugCheck(end != NULL);
*start = minColor;
*end = maxColor;
Color32 minColor, maxColor;
uint minLuminance, maxLuminance;
maxLuminance = minLuminance = colorLuminance(m_color[0]);
for(uint i = 1; i < 16; i++)
{
uint luminance = colorLuminance(m_color[i]);
if (luminance > maxLuminance) {
maxLuminance = luminance;
maxColor = m_color[i];
}
else if (luminance < minLuminance) {
minLuminance = luminance;
minColor = m_color[i];
}
}
*start = minColor;
*end = maxColor;
}
/// Get color range based on the bounding box.
void ColorBlock::boundsRange(Color32 * start, Color32 * end) const
{
nvDebugCheck(start != NULL);
nvDebugCheck(end != NULL);
nvDebugCheck(start != NULL);
nvDebugCheck(end != NULL);
Color32 minColor(255, 255, 255);
Color32 maxColor(0, 0, 0);
Color32 minColor(255, 255, 255);
Color32 maxColor(0, 0, 0);
for(uint i = 0; i < 16; i++)
{
if (m_color[i].r < minColor.r) { minColor.r = m_color[i].r; }
if (m_color[i].g < minColor.g) { minColor.g = m_color[i].g; }
if (m_color[i].b < minColor.b) { minColor.b = m_color[i].b; }
if (m_color[i].r > maxColor.r) { maxColor.r = m_color[i].r; }
if (m_color[i].g > maxColor.g) { maxColor.g = m_color[i].g; }
if (m_color[i].b > maxColor.b) { maxColor.b = m_color[i].b; }
}
for(uint i = 0; i < 16; i++)
{
if (m_color[i].r < minColor.r) { minColor.r = m_color[i].r; }
if (m_color[i].g < minColor.g) { minColor.g = m_color[i].g; }
if (m_color[i].b < minColor.b) { minColor.b = m_color[i].b; }
if (m_color[i].r > maxColor.r) { maxColor.r = m_color[i].r; }
if (m_color[i].g > maxColor.g) { maxColor.g = m_color[i].g; }
if (m_color[i].b > maxColor.b) { maxColor.b = m_color[i].b; }
}
// Offset range by 1/16 of the extents
Color32 inset;
inset.r = (maxColor.r - minColor.r) >> 4;
inset.g = (maxColor.g - minColor.g) >> 4;
inset.b = (maxColor.b - minColor.b) >> 4;
// Offset range by 1/16 of the extents
Color32 inset;
inset.r = (maxColor.r - minColor.r) >> 4;
inset.g = (maxColor.g - minColor.g) >> 4;
inset.b = (maxColor.b - minColor.b) >> 4;
minColor.r = (minColor.r + inset.r <= 255) ? minColor.r + inset.r : 255;
minColor.g = (minColor.g + inset.g <= 255) ? minColor.g + inset.g : 255;
minColor.b = (minColor.b + inset.b <= 255) ? minColor.b + inset.b : 255;
minColor.r = (minColor.r + inset.r <= 255) ? minColor.r + inset.r : 255;
minColor.g = (minColor.g + inset.g <= 255) ? minColor.g + inset.g : 255;
minColor.b = (minColor.b + inset.b <= 255) ? minColor.b + inset.b : 255;
maxColor.r = (maxColor.r >= inset.r) ? maxColor.r - inset.r : 0;
maxColor.g = (maxColor.g >= inset.g) ? maxColor.g - inset.g : 0;
maxColor.b = (maxColor.b >= inset.b) ? maxColor.b - inset.b : 0;
maxColor.r = (maxColor.r >= inset.r) ? maxColor.r - inset.r : 0;
maxColor.g = (maxColor.g >= inset.g) ? maxColor.g - inset.g : 0;
maxColor.b = (maxColor.b >= inset.b) ? maxColor.b - inset.b : 0;
*start = minColor;
*end = maxColor;
*start = minColor;
*end = maxColor;
}
/// Get color range based on the bounding box.
void ColorBlock::boundsRangeAlpha(Color32 * start, Color32 * end) const
{
nvDebugCheck(start != NULL);
nvDebugCheck(end != NULL);
nvDebugCheck(start != NULL);
nvDebugCheck(end != NULL);
Color32 minColor(255, 255, 255, 255);
Color32 maxColor(0, 0, 0, 0);
Color32 minColor(255, 255, 255, 255);
Color32 maxColor(0, 0, 0, 0);
for(uint i = 0; i < 16; i++)
{
if (m_color[i].r < minColor.r) { minColor.r = m_color[i].r; }
if (m_color[i].g < minColor.g) { minColor.g = m_color[i].g; }
if (m_color[i].b < minColor.b) { minColor.b = m_color[i].b; }
if (m_color[i].a < minColor.a) { minColor.a = m_color[i].a; }
if (m_color[i].r > maxColor.r) { maxColor.r = m_color[i].r; }
if (m_color[i].g > maxColor.g) { maxColor.g = m_color[i].g; }
if (m_color[i].b > maxColor.b) { maxColor.b = m_color[i].b; }
if (m_color[i].a > maxColor.a) { maxColor.a = m_color[i].a; }
}
for(uint i = 0; i < 16; i++)
{
if (m_color[i].r < minColor.r) { minColor.r = m_color[i].r; }
if (m_color[i].g < minColor.g) { minColor.g = m_color[i].g; }
if (m_color[i].b < minColor.b) { minColor.b = m_color[i].b; }
if (m_color[i].a < minColor.a) { minColor.a = m_color[i].a; }
if (m_color[i].r > maxColor.r) { maxColor.r = m_color[i].r; }
if (m_color[i].g > maxColor.g) { maxColor.g = m_color[i].g; }
if (m_color[i].b > maxColor.b) { maxColor.b = m_color[i].b; }
if (m_color[i].a > maxColor.a) { maxColor.a = m_color[i].a; }
}
// Offset range by 1/16 of the extents
Color32 inset;
inset.r = (maxColor.r - minColor.r) >> 4;
inset.g = (maxColor.g - minColor.g) >> 4;
inset.b = (maxColor.b - minColor.b) >> 4;
inset.a = (maxColor.a - minColor.a) >> 4;
// Offset range by 1/16 of the extents
Color32 inset;
inset.r = (maxColor.r - minColor.r) >> 4;
inset.g = (maxColor.g - minColor.g) >> 4;
inset.b = (maxColor.b - minColor.b) >> 4;
inset.a = (maxColor.a - minColor.a) >> 4;
minColor.r = (minColor.r + inset.r <= 255) ? minColor.r + inset.r : 255;
minColor.g = (minColor.g + inset.g <= 255) ? minColor.g + inset.g : 255;
minColor.b = (minColor.b + inset.b <= 255) ? minColor.b + inset.b : 255;
minColor.a = (minColor.a + inset.a <= 255) ? minColor.a + inset.a : 255;
minColor.r = (minColor.r + inset.r <= 255) ? minColor.r + inset.r : 255;
minColor.g = (minColor.g + inset.g <= 255) ? minColor.g + inset.g : 255;
minColor.b = (minColor.b + inset.b <= 255) ? minColor.b + inset.b : 255;
minColor.a = (minColor.a + inset.a <= 255) ? minColor.a + inset.a : 255;
maxColor.r = (maxColor.r >= inset.r) ? maxColor.r - inset.r : 0;
maxColor.g = (maxColor.g >= inset.g) ? maxColor.g - inset.g : 0;
maxColor.b = (maxColor.b >= inset.b) ? maxColor.b - inset.b : 0;
maxColor.a = (maxColor.a >= inset.a) ? maxColor.a - inset.a : 0;
*start = minColor;
*end = maxColor;
maxColor.r = (maxColor.r >= inset.r) ? maxColor.r - inset.r : 0;
maxColor.g = (maxColor.g >= inset.g) ? maxColor.g - inset.g : 0;
maxColor.b = (maxColor.b >= inset.b) ? maxColor.b - inset.b : 0;
maxColor.a = (maxColor.a >= inset.a) ? maxColor.a - inset.a : 0;
*start = minColor;
*end = maxColor;
}
/// Sort colors by abosolute value in their 16 bit representation.
void ColorBlock::sortColorsByAbsoluteValue()
{
// Dummy selection sort.
for( uint a = 0; a < 16; a++ ) {
uint max = a;
Color16 cmax(m_color[a]);
for( uint b = a+1; b < 16; b++ ) {
Color16 cb(m_color[b]);
if( cb.u > cmax.u ) {
max = b;
cmax = cb;
}
}
swap( m_color[a], m_color[max] );
}
// Dummy selection sort.
for( uint a = 0; a < 16; a++ ) {
uint max = a;
Color16 cmax(m_color[a]);
for( uint b = a+1; b < 16; b++ ) {
Color16 cb(m_color[b]);
if( cb.u > cmax.u ) {
max = b;
cmax = cb;
}
}
swap( m_color[a], m_color[max] );
}
}
/// Find extreme colors in the given axis.
void ColorBlock::computeRange(Vector3::Arg axis, Color32 * start, Color32 * end) const
{
nvDebugCheck(start != NULL);
nvDebugCheck(end != NULL);
int mini, maxi;
mini = maxi = 0;
float min, max;
min = max = dot(Vector3(m_color[0].r, m_color[0].g, m_color[0].b), axis);
nvDebugCheck(start != NULL);
nvDebugCheck(end != NULL);
for(uint i = 1; i < 16; i++)
{
const Vector3 vec(m_color[i].r, m_color[i].g, m_color[i].b);
float val = dot(vec, axis);
if( val < min ) {
mini = i;
min = val;
}
else if( val > max ) {
maxi = i;
max = val;
}
}
*start = m_color[mini];
*end = m_color[maxi];
int mini, maxi;
mini = maxi = 0;
float min, max;
min = max = dot(Vector3(m_color[0].r, m_color[0].g, m_color[0].b), axis);
for(uint i = 1; i < 16; i++)
{
const Vector3 vec(m_color[i].r, m_color[i].g, m_color[i].b);
float val = dot(vec, axis);
if( val < min ) {
mini = i;
min = val;
}
else if( val > max ) {
maxi = i;
max = val;
}
}
*start = m_color[mini];
*end = m_color[maxi];
}
/// Sort colors in the given axis.
void ColorBlock::sortColors(const Vector3 & axis)
{
float luma_array[16];
for(uint i = 0; i < 16; i++) {
const Vector3 vec(m_color[i].r, m_color[i].g, m_color[i].b);
luma_array[i] = dot(vec, axis);
}
// Dummy selection sort.
for( uint a = 0; a < 16; a++ ) {
uint min = a;
for( uint b = a+1; b < 16; b++ ) {
if( luma_array[b] < luma_array[min] ) {
min = b;
}
}
swap( luma_array[a], luma_array[min] );
swap( m_color[a], m_color[min] );
}
float luma_array[16];
for(uint i = 0; i < 16; i++) {
const Vector3 vec(m_color[i].r, m_color[i].g, m_color[i].b);
luma_array[i] = dot(vec, axis);
}
// Dummy selection sort.
for( uint a = 0; a < 16; a++ ) {
uint min = a;
for( uint b = a+1; b < 16; b++ ) {
if( luma_array[b] < luma_array[min] ) {
min = b;
}
}
swap( luma_array[a], luma_array[min] );
swap( m_color[a], m_color[min] );
}
}
/// Get the volume of the color block.
float ColorBlock::volume() const
{
Box bounds;
bounds.clearBounds();
for(int i = 0; i < 16; i++) {
const Vector3 point(m_color[i].r, m_color[i].g, m_color[i].b);
bounds.addPointToBounds(point);
}
return bounds.volume();
Box bounds;
bounds.clearBounds();
for(int i = 0; i < 16; i++) {
const Vector3 point(m_color[i].r, m_color[i].g, m_color[i].b);
bounds.addPointToBounds(point);
}
return bounds.volume();
}

View File

@ -1,96 +1,97 @@
// This code is in the public domain -- castanyo@yahoo.es
#pragma once
#ifndef NV_IMAGE_COLORBLOCK_H
#define NV_IMAGE_COLORBLOCK_H
#include <nvmath/Color.h>
#include "nvmath/Color.h"
namespace nv
{
class Image;
class Image;
/// Uncompressed 4x4 color block.
struct ColorBlock
{
ColorBlock();
ColorBlock(const uint * linearImage);
ColorBlock(const ColorBlock & block);
ColorBlock(const Image * img, uint x, uint y);
void init(const Image * img, uint x, uint y);
void init(uint w, uint h, uint * data, uint x, uint y);
void init(uint w, uint h, float * data, uint x, uint y);
void swizzle(uint x, uint y, uint z, uint w); // 0=r, 1=g, 2=b, 3=a, 4=0xFF, 5=0
bool isSingleColor() const;
//bool isSingleColorNoAlpha() const;
uint countUniqueColors() const;
Color32 averageColor() const;
bool hasAlpha() const;
void diameterRange(Color32 * start, Color32 * end) const;
void luminanceRange(Color32 * start, Color32 * end) const;
void boundsRange(Color32 * start, Color32 * end) const;
void boundsRangeAlpha(Color32 * start, Color32 * end) const;
void sortColorsByAbsoluteValue();
void computeRange(const Vector3 & axis, Color32 * start, Color32 * end) const;
void sortColors(const Vector3 & axis);
float volume() const;
// Accessors
const Color32 * colors() const;
/// Uncompressed 4x4 color block.
struct ColorBlock
{
ColorBlock();
ColorBlock(const uint * linearImage);
ColorBlock(const ColorBlock & block);
ColorBlock(const Image * img, uint x, uint y);
Color32 color(uint i) const;
Color32 & color(uint i);
Color32 color(uint x, uint y) const;
Color32 & color(uint x, uint y);
private:
Color32 m_color[4*4];
};
void init(const Image * img, uint x, uint y);
void init(uint w, uint h, uint * data, uint x, uint y);
void init(uint w, uint h, float * data, uint x, uint y);
void swizzle(uint x, uint y, uint z, uint w); // 0=r, 1=g, 2=b, 3=a, 4=0xFF, 5=0
bool isSingleColor() const;
//bool isSingleColorNoAlpha() const;
uint countUniqueColors() const;
Color32 averageColor() const;
bool hasAlpha() const;
void diameterRange(Color32 * start, Color32 * end) const;
void luminanceRange(Color32 * start, Color32 * end) const;
void boundsRange(Color32 * start, Color32 * end) const;
void boundsRangeAlpha(Color32 * start, Color32 * end) const;
void sortColorsByAbsoluteValue();
void computeRange(const Vector3 & axis, Color32 * start, Color32 * end) const;
void sortColors(const Vector3 & axis);
float volume() const;
// Accessors
const Color32 * colors() const;
Color32 color(uint i) const;
Color32 & color(uint i);
Color32 color(uint x, uint y) const;
Color32 & color(uint x, uint y);
private:
Color32 m_color[4*4];
};
/// Get pointer to block colors.
inline const Color32 * ColorBlock::colors() const
{
return m_color;
}
/// Get block color.
inline Color32 ColorBlock::color(uint i) const
{
nvDebugCheck(i < 16);
return m_color[i];
}
/// Get block color.
inline Color32 & ColorBlock::color(uint i)
{
nvDebugCheck(i < 16);
return m_color[i];
}
/// Get block color.
inline Color32 ColorBlock::color(uint x, uint y) const
{
nvDebugCheck(x < 4 && y < 4);
return m_color[y * 4 + x];
}
/// Get block color.
inline Color32 & ColorBlock::color(uint x, uint y)
{
nvDebugCheck(x < 4 && y < 4);
return m_color[y * 4 + x];
}
/// Get pointer to block colors.
inline const Color32 * ColorBlock::colors() const
{
return m_color;
}
/// Get block color.
inline Color32 ColorBlock::color(uint i) const
{
nvDebugCheck(i < 16);
return m_color[i];
}
/// Get block color.
inline Color32 & ColorBlock::color(uint i)
{
nvDebugCheck(i < 16);
return m_color[i];
}
/// Get block color.
inline Color32 ColorBlock::color(uint x, uint y) const
{
nvDebugCheck(x < 4 && y < 4);
return m_color[y * 4 + x];
}
/// Get block color.
inline Color32 & ColorBlock::color(uint x, uint y)
{
nvDebugCheck(x < 4 && y < 4);
return m_color[y * 4 + x];
}
} // nv namespace
#endif // NV_IMAGE_COLORBLOCK_H

File diff suppressed because it is too large Load Diff

View File

@ -21,6 +21,7 @@
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
#pragma once
#ifndef NV_IMAGE_DIRECTDRAWSURFACE_H
#define NV_IMAGE_DIRECTDRAWSURFACE_H
@ -28,134 +29,134 @@
namespace nv
{
class Image;
class Stream;
struct ColorBlock;
class Image;
class Stream;
struct ColorBlock;
struct NVIMAGE_CLASS DDSPixelFormat
{
uint size;
uint flags;
uint fourcc;
uint bitcount;
uint rmask;
uint gmask;
uint bmask;
uint amask;
};
struct NVIMAGE_CLASS DDSPixelFormat
{
uint size;
uint flags;
uint fourcc;
uint bitcount;
uint rmask;
uint gmask;
uint bmask;
uint amask;
};
struct NVIMAGE_CLASS DDSCaps
{
uint caps1;
uint caps2;
uint caps3;
uint caps4;
};
struct NVIMAGE_CLASS DDSCaps
{
uint caps1;
uint caps2;
uint caps3;
uint caps4;
};
/// DDS file header for DX10.
struct NVIMAGE_CLASS DDSHeader10
{
uint dxgiFormat;
uint resourceDimension;
uint miscFlag;
uint arraySize;
uint reserved;
};
/// DDS file header for DX10.
struct NVIMAGE_CLASS DDSHeader10
{
uint dxgiFormat;
uint resourceDimension;
uint miscFlag;
uint arraySize;
uint reserved;
};
/// DDS file header.
struct NVIMAGE_CLASS DDSHeader
{
uint fourcc;
uint size;
uint flags;
uint height;
uint width;
uint pitch;
uint depth;
uint mipmapcount;
uint reserved[11];
DDSPixelFormat pf;
DDSCaps caps;
uint notused;
DDSHeader10 header10;
// Helper methods.
DDSHeader();
void setWidth(uint w);
void setHeight(uint h);
void setDepth(uint d);
void setMipmapCount(uint count);
void setTexture2D();
void setTexture3D();
void setTextureCube();
void setLinearSize(uint size);
void setPitch(uint pitch);
void setFourCC(uint8 c0, uint8 c1, uint8 c2, uint8 c3);
void setFormatCode(uint code);
void setSwizzleCode(uint8 c0, uint8 c1, uint8 c2, uint8 c3);
void setPixelFormat(uint bitcount, uint rmask, uint gmask, uint bmask, uint amask);
void setDX10Format(uint format);
void setNormalFlag(bool b);
void setHasAlphaFlag(bool b);
void swapBytes();
bool hasDX10Header() const;
};
NVIMAGE_API Stream & operator<< (Stream & s, DDSHeader & header);
/// DDS file header.
struct NVIMAGE_CLASS DDSHeader
{
uint fourcc;
uint size;
uint flags;
uint height;
uint width;
uint pitch;
uint depth;
uint mipmapcount;
uint reserved[11];
DDSPixelFormat pf;
DDSCaps caps;
uint notused;
DDSHeader10 header10;
/// DirectDraw Surface. (DDS)
class NVIMAGE_CLASS DirectDrawSurface
{
public:
DirectDrawSurface(const char * file);
DirectDrawSurface(Stream * stream);
~DirectDrawSurface();
bool isValid() const;
bool isSupported() const;
// Helper methods.
DDSHeader();
bool hasAlpha() const;
uint mipmapCount() const;
uint width() const;
uint height() const;
uint depth() const;
bool isTexture1D() const;
bool isTexture2D() const;
bool isTexture3D() const;
bool isTextureCube() const;
void setWidth(uint w);
void setHeight(uint h);
void setDepth(uint d);
void setMipmapCount(uint count);
void setTexture2D();
void setTexture3D();
void setTextureCube();
void setLinearSize(uint size);
void setPitch(uint pitch);
void setFourCC(uint8 c0, uint8 c1, uint8 c2, uint8 c3);
void setFormatCode(uint code);
void setSwizzleCode(uint8 c0, uint8 c1, uint8 c2, uint8 c3);
void setPixelFormat(uint bitcount, uint rmask, uint gmask, uint bmask, uint amask);
void setDX10Format(uint format);
void setNormalFlag(bool b);
void setHasAlphaFlag(bool b);
void setNormalFlag(bool b);
void setHasAlphaFlag(bool b);
void mipmap(Image * img, uint f, uint m);
// void mipmap(FloatImage * img, uint f, uint m);
void printInfo() const;
void swapBytes();
private:
uint blockSize() const;
uint faceSize() const;
uint mipmapSize(uint m) const;
uint offset(uint f, uint m);
void readLinearImage(Image * img);
void readBlockImage(Image * img);
void readBlock(ColorBlock * rgba);
private:
Stream * const stream;
DDSHeader header;
DDSHeader10 header10;
};
bool hasDX10Header() const;
};
NVIMAGE_API Stream & operator<< (Stream & s, DDSHeader & header);
/// DirectDraw Surface. (DDS)
class NVIMAGE_CLASS DirectDrawSurface
{
public:
DirectDrawSurface(const char * file);
DirectDrawSurface(Stream * stream);
~DirectDrawSurface();
bool isValid() const;
bool isSupported() const;
bool hasAlpha() const;
uint mipmapCount() const;
uint width() const;
uint height() const;
uint depth() const;
bool isTexture1D() const;
bool isTexture2D() const;
bool isTexture3D() const;
bool isTextureCube() const;
void setNormalFlag(bool b);
void setHasAlphaFlag(bool b);
void mipmap(Image * img, uint f, uint m);
// void mipmap(FloatImage * img, uint f, uint m);
void printInfo() const;
private:
uint blockSize() const;
uint faceSize() const;
uint mipmapSize(uint m) const;
uint offset(uint f, uint m);
void readLinearImage(Image * img);
void readBlockImage(Image * img);
void readBlock(ColorBlock * rgba);
private:
Stream * const stream;
DDSHeader header;
DDSHeader10 header10;
};
} // nv namespace

View File

@ -35,62 +35,64 @@
#include "Filter.h"
#include <nvmath/Vector.h> // Vector4
#include <nvcore/Containers.h> // swap
#include "nvmath/Vector.h" // Vector4
#include "nvcore/Utils.h" // swap
#include <string.h> // memset
using namespace nv;
namespace
{
// Sinc function.
inline static float sincf(const float x)
{
if (fabs(x) < NV_EPSILON) {
//return 1.0;
return 1.0f + x*x*(-1.0f/6.0f + x*x*1.0f/120.0f);
}
else {
return sin(x) / x;
}
}
// Sinc function.
inline static float sincf(const float x)
{
if (fabs(x) < NV_EPSILON) {
//return 1.0;
return 1.0f + x*x*(-1.0f/6.0f + x*x*1.0f/120.0f);
}
else {
return sin(x) / x;
}
}
// Bessel function of the first kind from Jon Blow's article.
// http://mathworld.wolfram.com/BesselFunctionoftheFirstKind.html
// http://en.wikipedia.org/wiki/Bessel_function
inline static float bessel0(float x)
{
const float EPSILON_RATIO = 1e-6f;
float xh, sum, pow, ds;
int k;
// Bessel function of the first kind from Jon Blow's article.
// http://mathworld.wolfram.com/BesselFunctionoftheFirstKind.html
// http://en.wikipedia.org/wiki/Bessel_function
inline static float bessel0(float x)
{
const float EPSILON_RATIO = 1e-6f;
float xh, sum, pow, ds;
int k;
xh = 0.5f * x;
sum = 1.0f;
pow = 1.0f;
k = 0;
ds = 1.0;
while (ds > sum * EPSILON_RATIO) {
++k;
pow = pow * (xh / k);
ds = pow * pow;
sum = sum + ds;
}
xh = 0.5f * x;
sum = 1.0f;
pow = 1.0f;
k = 0;
ds = 1.0;
while (ds > sum * EPSILON_RATIO) {
++k;
pow = pow * (xh / k);
ds = pow * pow;
sum = sum + ds;
}
return sum;
}
return sum;
}
/*// Alternative bessel function from Paul Heckbert.
static float _bessel0(float x)
{
const float EPSILON_RATIO = 1E-6;
float sum = 1.0f;
float y = x * x / 4.0f;
float t = y;
for(int i = 2; t > EPSILON_RATIO; i++) {
sum += t;
t *= y / float(i * i);
}
return sum;
}*/
/*// Alternative bessel function from Paul Heckbert.
static float _bessel0(float x)
{
const float EPSILON_RATIO = 1E-6;
float sum = 1.0f;
float y = x * x / 4.0f;
float t = y;
for(int i = 2; t > EPSILON_RATIO; i++) {
sum += t;
t *= y / float(i * i);
}
return sum;
}*/
} // namespace
@ -105,42 +107,42 @@ Filter::Filter(float width) : m_width(width)
float Filter::sampleDelta(float x, float scale) const
{
return evaluate((x + 0.5f)* scale);
return evaluate((x + 0.5f)* scale);
}
float Filter::sampleBox(float x, float scale, int samples) const
{
float sum = 0;
float isamples = 1.0f / float(samples);
float sum = 0;
float isamples = 1.0f / float(samples);
for(int s = 0; s < samples; s++)
{
float p = (x + (float(s) + 0.5f) * isamples) * scale;
float value = evaluate(p);
sum += value;
}
return sum * isamples;
for(int s = 0; s < samples; s++)
{
float p = (x + (float(s) + 0.5f) * isamples) * scale;
float value = evaluate(p);
sum += value;
}
return sum * isamples;
}
float Filter::sampleTriangle(float x, float scale, int samples) const
{
float sum = 0;
float isamples = 1.0f / float(samples);
float sum = 0;
float isamples = 1.0f / float(samples);
for(int s = 0; s < samples; s++)
{
float offset = (2 * float(s) + 1.0f) * isamples;
float p = (x + offset - 0.5f) * scale;
float value = evaluate(p);
float weight = offset;
if (weight > 1.0f) weight = 2.0f - weight;
sum += value * weight;
}
return 2 * sum * isamples;
for(int s = 0; s < samples; s++)
{
float offset = (2 * float(s) + 1.0f) * isamples;
float p = (x + offset - 0.5f) * scale;
float value = evaluate(p);
float weight = offset;
if (weight > 1.0f) weight = 2.0f - weight;
sum += value * weight;
}
return 2 * sum * isamples;
}
@ -152,8 +154,8 @@ BoxFilter::BoxFilter(float width) : Filter(width) {}
float BoxFilter::evaluate(float x) const
{
if (fabs(x) <= m_width) return 1.0f;
else return 0.0f;
if (fabs(x) <= m_width) return 1.0f;
else return 0.0f;
}
@ -162,7 +164,7 @@ TriangleFilter::TriangleFilter(float width) : Filter(width) {}
float TriangleFilter::evaluate(float x) const
{
x = fabs(x);
x = fabs(x);
if( x < m_width ) return m_width - x;
return 0.0f;
}
@ -172,11 +174,11 @@ QuadraticFilter::QuadraticFilter() : Filter(1.5f) {}
float QuadraticFilter::evaluate(float x) const
{
x = fabs(x);
x = fabs(x);
if( x < 0.5f ) return 0.75f - x * x;
if( x < 1.5f ) {
float t = x - 1.5f;
return 0.5f * t * t;
float t = x - 1.5f;
return 0.5f * t * t;
}
return 0.0f;
}
@ -186,10 +188,10 @@ CubicFilter::CubicFilter() : Filter(1.0f) {}
float CubicFilter::evaluate(float x) const
{
// f(t) = 2|t|^3 - 3|t|^2 + 1, -1 <= t <= 1
x = fabs(x);
if( x < 1.0f ) return((2.0f * x - 3.0f) * x * x + 1.0f);
return 0.0f;
// f(t) = 2|t|^3 - 3|t|^2 + 1, -1 <= t <= 1
x = fabs(x);
if( x < 1.0f ) return((2.0f * x - 3.0f) * x * x + 1.0f);
return 0.0f;
}
@ -197,11 +199,11 @@ BSplineFilter::BSplineFilter() : Filter(2.0f) {}
float BSplineFilter::evaluate(float x) const
{
x = fabs(x);
x = fabs(x);
if( x < 1.0f ) return (4.0f + x * x * (-6.0f + x * 3.0f)) / 6.0f;
if( x < 2.0f ) {
float t = 2.0f - x;
return t * t * t / 6.0f;
float t = 2.0f - x;
return t * t * t / 6.0f;
}
return 0.0f;
}
@ -211,21 +213,21 @@ MitchellFilter::MitchellFilter() : Filter(2.0f) { setParameters(1.0f/3.0f, 1.0f/
float MitchellFilter::evaluate(float x) const
{
x = fabs(x);
if( x < 1.0f ) return p0 + x * x * (p2 + x * p3);
if( x < 2.0f ) return q0 + x * (q1 + x * (q2 + x * q3));
return 0.0f;
x = fabs(x);
if( x < 1.0f ) return p0 + x * x * (p2 + x * p3);
if( x < 2.0f ) return q0 + x * (q1 + x * (q2 + x * q3));
return 0.0f;
}
void MitchellFilter::setParameters(float b, float c)
{
p0 = (6.0f - 2.0f * b) / 6.0f;
p2 = (-18.0f + 12.0f * b + 6.0f * c) / 6.0f;
p3 = (12.0f - 9.0f * b - 6.0f * c) / 6.0f;
q0 = (8.0f * b + 24.0f * c) / 6.0f;
q1 = (-12.0f * b - 48.0f * c) / 6.0f;
q2 = (6.0f * b + 30.0f * c) / 6.0f;
q3 = (-b - 6.0f * c) / 6.0f;
p0 = (6.0f - 2.0f * b) / 6.0f;
p2 = (-18.0f + 12.0f * b + 6.0f * c) / 6.0f;
p3 = (12.0f - 9.0f * b - 6.0f * c) / 6.0f;
q0 = (8.0f * b + 24.0f * c) / 6.0f;
q1 = (-12.0f * b - 48.0f * c) / 6.0f;
q2 = (6.0f * b + 30.0f * c) / 6.0f;
q3 = (-b - 6.0f * c) / 6.0f;
}
@ -233,9 +235,9 @@ LanczosFilter::LanczosFilter() : Filter(3.0f) {}
float LanczosFilter::evaluate(float x) const
{
x = fabs(x);
if( x < 3.0f ) return sincf(PI * x) * sincf(PI * x / 3.0f);
return 0.0f;
x = fabs(x);
if( x < 3.0f ) return sincf(PI * x) * sincf(PI * x / 3.0f);
return 0.0f;
}
@ -243,7 +245,7 @@ SincFilter::SincFilter(float w) : Filter(w) {}
float SincFilter::evaluate(float x) const
{
return sincf(PI * x);
return sincf(PI * x);
}
@ -251,16 +253,16 @@ KaiserFilter::KaiserFilter(float w) : Filter(w) { setParameters(4.0f, 1.0f); }
float KaiserFilter::evaluate(float x) const
{
const float sinc_value = sincf(PI * x * stretch);
const float t = x / m_width;
if ((1 - t * t) >= 0) return sinc_value * bessel0(alpha * sqrtf(1 - t * t)) / bessel0(alpha);
else return 0;
const float sinc_value = sincf(PI * x * stretch);
const float t = x / m_width;
if ((1 - t * t) >= 0) return sinc_value * bessel0(alpha * sqrtf(1 - t * t)) / bessel0(alpha);
else return 0;
}
void KaiserFilter::setParameters(float alpha, float stretch)
{
this->alpha = alpha;
this->stretch = stretch;
this->alpha = alpha;
this->stretch = stretch;
}
@ -268,44 +270,44 @@ void KaiserFilter::setParameters(float alpha, float stretch)
/// Ctor.
Kernel1::Kernel1(const Filter & f, int iscale, int samples/*= 32*/)
{
nvDebugCheck(iscale > 1);
nvDebugCheck(samples > 0);
const float scale = 1.0f / iscale;
m_width = f.width() * iscale;
m_windowSize = (int)ceilf(2 * m_width);
m_data = new float[m_windowSize];
const float offset = float(m_windowSize) / 2;
float total = 0.0f;
for (int i = 0; i < m_windowSize; i++)
{
const float sample = f.sampleBox(i - offset, scale, samples);
m_data[i] = sample;
total += sample;
}
const float inv = 1.0f / total;
for (int i = 0; i < m_windowSize; i++)
{
m_data[i] *= inv;
}
nvDebugCheck(iscale > 1);
nvDebugCheck(samples > 0);
const float scale = 1.0f / iscale;
m_width = f.width() * iscale;
m_windowSize = (int)ceilf(2 * m_width);
m_data = new float[m_windowSize];
const float offset = float(m_windowSize) / 2;
float total = 0.0f;
for (int i = 0; i < m_windowSize; i++)
{
const float sample = f.sampleBox(i - offset, scale, samples);
m_data[i] = sample;
total += sample;
}
const float inv = 1.0f / total;
for (int i = 0; i < m_windowSize; i++)
{
m_data[i] *= inv;
}
}
/// Dtor.
Kernel1::~Kernel1()
{
delete m_data;
delete m_data;
}
/// Print the kernel for debugging purposes.
void Kernel1::debugPrint()
{
for (int i = 0; i < m_windowSize; i++) {
nvDebug("%d: %f\n", i, m_data[i]);
}
for (int i = 0; i < m_windowSize; i++) {
nvDebug("%d: %f\n", i, m_data[i]);
}
}
@ -313,102 +315,102 @@ void Kernel1::debugPrint()
/// Ctor.
Kernel2::Kernel2(uint ws) : m_windowSize(ws)
{
m_data = new float[m_windowSize * m_windowSize];
m_data = new float[m_windowSize * m_windowSize];
}
/// Copy ctor.
Kernel2::Kernel2(const Kernel2 & k) : m_windowSize(k.m_windowSize)
{
m_data = new float[m_windowSize * m_windowSize];
for (uint i = 0; i < m_windowSize * m_windowSize; i++) {
m_data[i] = k.m_data[i];
}
m_data = new float[m_windowSize * m_windowSize];
for (uint i = 0; i < m_windowSize * m_windowSize; i++) {
m_data[i] = k.m_data[i];
}
}
/// Dtor.
Kernel2::~Kernel2()
{
delete m_data;
delete m_data;
}
/// Normalize the filter.
void Kernel2::normalize()
{
float total = 0.0f;
for(uint i = 0; i < m_windowSize*m_windowSize; i++) {
total += fabs(m_data[i]);
}
float inv = 1.0f / total;
for(uint i = 0; i < m_windowSize*m_windowSize; i++) {
m_data[i] *= inv;
}
float total = 0.0f;
for(uint i = 0; i < m_windowSize*m_windowSize; i++) {
total += fabs(m_data[i]);
}
float inv = 1.0f / total;
for(uint i = 0; i < m_windowSize*m_windowSize; i++) {
m_data[i] *= inv;
}
}
/// Transpose the kernel.
void Kernel2::transpose()
{
for(uint i = 0; i < m_windowSize; i++) {
for(uint j = i+1; j < m_windowSize; j++) {
swap(m_data[i*m_windowSize + j], m_data[j*m_windowSize + i]);
}
}
for(uint i = 0; i < m_windowSize; i++) {
for(uint j = i+1; j < m_windowSize; j++) {
swap(m_data[i*m_windowSize + j], m_data[j*m_windowSize + i]);
}
}
}
/// Init laplacian filter, usually used for sharpening.
void Kernel2::initLaplacian()
{
nvDebugCheck(m_windowSize == 3);
// m_data[0] = -1; m_data[1] = -1; m_data[2] = -1;
// m_data[3] = -1; m_data[4] = +8; m_data[5] = -1;
// m_data[6] = -1; m_data[7] = -1; m_data[8] = -1;
m_data[0] = +0; m_data[1] = -1; m_data[2] = +0;
m_data[3] = -1; m_data[4] = +4; m_data[5] = -1;
m_data[6] = +0; m_data[7] = -1; m_data[8] = +0;
// m_data[0] = +1; m_data[1] = -2; m_data[2] = +1;
// m_data[3] = -2; m_data[4] = +4; m_data[5] = -2;
// m_data[6] = +1; m_data[7] = -2; m_data[8] = +1;
nvDebugCheck(m_windowSize == 3);
// m_data[0] = -1; m_data[1] = -1; m_data[2] = -1;
// m_data[3] = -1; m_data[4] = +8; m_data[5] = -1;
// m_data[6] = -1; m_data[7] = -1; m_data[8] = -1;
m_data[0] = +0; m_data[1] = -1; m_data[2] = +0;
m_data[3] = -1; m_data[4] = +4; m_data[5] = -1;
m_data[6] = +0; m_data[7] = -1; m_data[8] = +0;
// m_data[0] = +1; m_data[1] = -2; m_data[2] = +1;
// m_data[3] = -2; m_data[4] = +4; m_data[5] = -2;
// m_data[6] = +1; m_data[7] = -2; m_data[8] = +1;
}
/// Init simple edge detection filter.
void Kernel2::initEdgeDetection()
{
nvCheck(m_windowSize == 3);
m_data[0] = 0; m_data[1] = 0; m_data[2] = 0;
m_data[3] =-1; m_data[4] = 0; m_data[5] = 1;
m_data[6] = 0; m_data[7] = 0; m_data[8] = 0;
nvCheck(m_windowSize == 3);
m_data[0] = 0; m_data[1] = 0; m_data[2] = 0;
m_data[3] =-1; m_data[4] = 0; m_data[5] = 1;
m_data[6] = 0; m_data[7] = 0; m_data[8] = 0;
}
/// Init sobel filter.
void Kernel2::initSobel()
{
if (m_windowSize == 3)
{
m_data[0] = -1; m_data[1] = 0; m_data[2] = 1;
m_data[3] = -2; m_data[4] = 0; m_data[5] = 2;
m_data[6] = -1; m_data[7] = 0; m_data[8] = 1;
}
else if (m_windowSize == 5)
{
float elements[] = {
if (m_windowSize == 3)
{
m_data[0] = -1; m_data[1] = 0; m_data[2] = 1;
m_data[3] = -2; m_data[4] = 0; m_data[5] = 2;
m_data[6] = -1; m_data[7] = 0; m_data[8] = 1;
}
else if (m_windowSize == 5)
{
float elements[] = {
-1, -2, 0, 2, 1,
-2, -3, 0, 3, 2,
-3, -4, 0, 4, 3,
-2, -3, 0, 3, 2,
-1, -2, 0, 2, 1
};
};
for (int i = 0; i < 5*5; i++) {
m_data[i] = elements[i];
}
}
else if (m_windowSize == 7)
{
float elements[] = {
for (int i = 0; i < 5*5; i++) {
m_data[i] = elements[i];
}
}
else if (m_windowSize == 7)
{
float elements[] = {
-1, -2, -3, 0, 3, 2, 1,
-2, -3, -4, 0, 4, 3, 2,
-3, -4, -5, 0, 5, 4, 3,
@ -416,15 +418,15 @@ void Kernel2::initSobel()
-3, -4, -5, 0, 5, 4, 3,
-2, -3, -4, 0, 4, 3, 2,
-1, -2, -3, 0, 3, 2, 1
};
};
for (int i = 0; i < 7*7; i++) {
m_data[i] = elements[i];
}
}
else if (m_windowSize == 9)
{
float elements[] = {
for (int i = 0; i < 7*7; i++) {
m_data[i] = elements[i];
}
}
else if (m_windowSize == 9)
{
float elements[] = {
-1, -2, -3, -4, 0, 4, 3, 2, 1,
-2, -3, -4, -5, 0, 5, 4, 3, 2,
-3, -4, -5, -6, 0, 6, 5, 4, 3,
@ -434,47 +436,47 @@ void Kernel2::initSobel()
-3, -4, -5, -6, 0, 6, 5, 4, 3,
-2, -3, -4, -5, 0, 5, 4, 3, 2,
-1, -2, -3, -4, 0, 4, 3, 2, 1
};
for (int i = 0; i < 9*9; i++) {
m_data[i] = elements[i];
}
}
};
for (int i = 0; i < 9*9; i++) {
m_data[i] = elements[i];
}
}
}
/// Init prewitt filter.
void Kernel2::initPrewitt()
{
if (m_windowSize == 3)
{
m_data[0] = -1; m_data[1] = 0; m_data[2] = -1;
m_data[3] = -1; m_data[4] = 0; m_data[5] = -1;
m_data[6] = -1; m_data[7] = 0; m_data[8] = -1;
}
else if (m_windowSize == 5)
{
// @@ Is this correct?
float elements[] = {
if (m_windowSize == 3)
{
m_data[0] = -1; m_data[1] = 0; m_data[2] = -1;
m_data[3] = -1; m_data[4] = 0; m_data[5] = -1;
m_data[6] = -1; m_data[7] = 0; m_data[8] = -1;
}
else if (m_windowSize == 5)
{
// @@ Is this correct?
float elements[] = {
-2, -1, 0, 1, 2,
-2, -1, 0, 1, 2,
-2, -1, 0, 1, 2,
-2, -1, 0, 1, 2,
-2, -1, 0, 1, 2
};
};
for (int i = 0; i < 5*5; i++) {
m_data[i] = elements[i];
}
}
for (int i = 0; i < 5*5; i++) {
m_data[i] = elements[i];
}
}
}
/// Init blended sobel filter.
void Kernel2::initBlendedSobel(const Vector4 & scale)
{
nvCheck(m_windowSize == 9);
nvCheck(m_windowSize == 9);
{
const float elements[] = {
{
const float elements[] = {
-1, -2, -3, -4, 0, 4, 3, 2, 1,
-2, -3, -4, -5, 0, 5, 4, 3, 2,
-3, -4, -5, -6, 0, 6, 5, 4, 3,
@ -484,14 +486,14 @@ void Kernel2::initBlendedSobel(const Vector4 & scale)
-3, -4, -5, -6, 0, 6, 5, 4, 3,
-2, -3, -4, -5, 0, 5, 4, 3, 2,
-1, -2, -3, -4, 0, 4, 3, 2, 1
};
for (int i = 0; i < 9*9; i++) {
m_data[i] = elements[i] * scale.w();
}
}
{
const float elements[] = {
};
for (int i = 0; i < 9*9; i++) {
m_data[i] = elements[i] * scale.w;
}
}
{
const float elements[] = {
-1, -2, -3, 0, 3, 2, 1,
-2, -3, -4, 0, 4, 3, 2,
-3, -4, -5, 0, 5, 4, 3,
@ -499,107 +501,107 @@ void Kernel2::initBlendedSobel(const Vector4 & scale)
-3, -4, -5, 0, 5, 4, 3,
-2, -3, -4, 0, 4, 3, 2,
-1, -2, -3, 0, 3, 2, 1,
};
};
for (int i = 0; i < 7; i++) {
for (int e = 0; e < 7; e++) {
m_data[(i + 1) * 9 + e + 1] += elements[i * 7 + e] * scale.z();
}
}
}
{
const float elements[] = {
for (int i = 0; i < 7; i++) {
for (int e = 0; e < 7; e++) {
m_data[(i + 1) * 9 + e + 1] += elements[i * 7 + e] * scale.z;
}
}
}
{
const float elements[] = {
-1, -2, 0, 2, 1,
-2, -3, 0, 3, 2,
-3, -4, 0, 4, 3,
-2, -3, 0, 3, 2,
-1, -2, 0, 2, 1
};
};
for (int i = 0; i < 5; i++) {
for (int e = 0; e < 5; e++) {
m_data[(i + 2) * 9 + e + 2] += elements[i * 5 + e] * scale.y();
}
}
}
{
const float elements[] = {
for (int i = 0; i < 5; i++) {
for (int e = 0; e < 5; e++) {
m_data[(i + 2) * 9 + e + 2] += elements[i * 5 + e] * scale.y;
}
}
}
{
const float elements[] = {
-1, 0, 1,
-2, 0, 2,
-1, 0, 1,
};
};
for (int i = 0; i < 3; i++) {
for (int e = 0; e < 3; e++) {
m_data[(i + 3) * 9 + e + 3] += elements[i * 3 + e] * scale.x();
}
}
}
for (int i = 0; i < 3; i++) {
for (int e = 0; e < 3; e++) {
m_data[(i + 3) * 9 + e + 3] += elements[i * 3 + e] * scale.x;
}
}
}
}
PolyphaseKernel::PolyphaseKernel(const Filter & f, uint srcLength, uint dstLength, int samples/*= 32*/)
{
nvDebugCheck(samples > 0);
nvDebugCheck(samples > 0);
float scale = float(dstLength) / float(srcLength);
const float iscale = 1.0f / scale;
float scale = float(dstLength) / float(srcLength);
const float iscale = 1.0f / scale;
if (scale > 1) {
// Upsampling.
samples = 1;
scale = 1;
}
if (scale > 1) {
// Upsampling.
samples = 1;
scale = 1;
}
m_length = dstLength;
m_width = f.width() * iscale;
m_windowSize = (int)ceilf(m_width * 2) + 1;
m_length = dstLength;
m_width = f.width() * iscale;
m_windowSize = (int)ceilf(m_width * 2) + 1;
m_data = new float[m_windowSize * m_length];
memset(m_data, 0, sizeof(float) * m_windowSize * m_length);
m_data = new float[m_windowSize * m_length];
memset(m_data, 0, sizeof(float) * m_windowSize * m_length);
for (uint i = 0; i < m_length; i++)
{
const float center = (0.5f + i) * iscale;
const int left = (int)floorf(center - m_width);
const int right = (int)ceilf(center + m_width);
nvDebugCheck(right - left <= m_windowSize);
float total = 0.0f;
for (int j = 0; j < m_windowSize; j++)
{
const float sample = f.sampleBox(left + j - center, scale, samples);
m_data[i * m_windowSize + j] = sample;
total += sample;
}
// normalize weights.
for (int j = 0; j < m_windowSize; j++)
{
m_data[i * m_windowSize + j] /= total;
}
}
for (uint i = 0; i < m_length; i++)
{
const float center = (0.5f + i) * iscale;
const int left = (int)floorf(center - m_width);
const int right = (int)ceilf(center + m_width);
nvDebugCheck(right - left <= m_windowSize);
float total = 0.0f;
for (int j = 0; j < m_windowSize; j++)
{
const float sample = f.sampleBox(left + j - center, scale, samples);
m_data[i * m_windowSize + j] = sample;
total += sample;
}
// normalize weights.
for (int j = 0; j < m_windowSize; j++)
{
m_data[i * m_windowSize + j] /= total;
}
}
}
PolyphaseKernel::~PolyphaseKernel()
{
delete [] m_data;
delete [] m_data;
}
/// Print the kernel for debugging purposes.
void PolyphaseKernel::debugPrint() const
{
for (uint i = 0; i < m_length; i++)
{
nvDebug("%d: ", i);
for (int j = 0; j < m_windowSize; j++)
{
nvDebug(" %6.4f", m_data[i * m_windowSize + j]);
}
nvDebug("\n");
}
for (uint i = 0; i < m_length; i++)
{
nvDebug("%d: ", i);
for (int j = 0; j < m_windowSize; j++)
{
nvDebug(" %6.4f", m_data[i * m_windowSize + j]);
}
nvDebug("\n");
}
}

View File

@ -1,218 +1,219 @@
// This code is in the public domain -- castanyo@yahoo.es
#pragma once
#ifndef NV_IMAGE_FILTER_H
#define NV_IMAGE_FILTER_H
#include <nvimage/nvimage.h>
#include <nvcore/Debug.h>
#include "nvimage.h"
#include "nvcore/Debug.h"
namespace nv
{
class Vector4;
class Vector4;
/// Base filter class.
class NVIMAGE_CLASS Filter
{
public:
Filter(float width);
virtual ~Filter();
/// Base filter class.
class NVIMAGE_CLASS Filter
{
public:
Filter(float width);
virtual ~Filter();
float width() const { return m_width; }
float sampleDelta(float x, float scale) const;
float sampleBox(float x, float scale, int samples) const;
float sampleTriangle(float x, float scale, int samples) const;
float width() const { return m_width; }
float sampleDelta(float x, float scale) const;
float sampleBox(float x, float scale, int samples) const;
float sampleTriangle(float x, float scale, int samples) const;
virtual float evaluate(float x) const = 0;
virtual float evaluate(float x) const = 0;
protected:
const float m_width;
};
protected:
const float m_width;
};
// Box filter.
class NVIMAGE_CLASS BoxFilter : public Filter
{
public:
BoxFilter();
BoxFilter(float width);
virtual float evaluate(float x) const;
};
// Box filter.
class NVIMAGE_CLASS BoxFilter : public Filter
{
public:
BoxFilter();
BoxFilter(float width);
virtual float evaluate(float x) const;
};
// Triangle (bilinear/tent) filter.
class NVIMAGE_CLASS TriangleFilter : public Filter
{
public:
TriangleFilter();
TriangleFilter(float width);
virtual float evaluate(float x) const;
};
// Triangle (bilinear/tent) filter.
class NVIMAGE_CLASS TriangleFilter : public Filter
{
public:
TriangleFilter();
TriangleFilter(float width);
virtual float evaluate(float x) const;
};
// Quadratic (bell) filter.
class NVIMAGE_CLASS QuadraticFilter : public Filter
{
public:
QuadraticFilter();
virtual float evaluate(float x) const;
};
// Quadratic (bell) filter.
class NVIMAGE_CLASS QuadraticFilter : public Filter
{
public:
QuadraticFilter();
virtual float evaluate(float x) const;
};
// Cubic filter from Thatcher Ulrich.
class NVIMAGE_CLASS CubicFilter : public Filter
{
public:
CubicFilter();
virtual float evaluate(float x) const;
};
// Cubic filter from Thatcher Ulrich.
class NVIMAGE_CLASS CubicFilter : public Filter
{
public:
CubicFilter();
virtual float evaluate(float x) const;
};
// Cubic b-spline filter from Paul Heckbert.
class NVIMAGE_CLASS BSplineFilter : public Filter
{
public:
BSplineFilter();
virtual float evaluate(float x) const;
};
// Cubic b-spline filter from Paul Heckbert.
class NVIMAGE_CLASS BSplineFilter : public Filter
{
public:
BSplineFilter();
virtual float evaluate(float x) const;
};
/// Mitchell & Netravali's two-param cubic
/// @see "Reconstruction Filters in Computer Graphics", SIGGRAPH 88
class NVIMAGE_CLASS MitchellFilter : public Filter
{
public:
MitchellFilter();
virtual float evaluate(float x) const;
/// Mitchell & Netravali's two-param cubic
/// @see "Reconstruction Filters in Computer Graphics", SIGGRAPH 88
class NVIMAGE_CLASS MitchellFilter : public Filter
{
public:
MitchellFilter();
virtual float evaluate(float x) const;
void setParameters(float b, float c);
void setParameters(float b, float c);
private:
float p0, p2, p3;
float q0, q1, q2, q3;
};
private:
float p0, p2, p3;
float q0, q1, q2, q3;
};
// Lanczos3 filter.
class NVIMAGE_CLASS LanczosFilter : public Filter
{
public:
LanczosFilter();
virtual float evaluate(float x) const;
};
// Lanczos3 filter.
class NVIMAGE_CLASS LanczosFilter : public Filter
{
public:
LanczosFilter();
virtual float evaluate(float x) const;
};
// Sinc filter.
class NVIMAGE_CLASS SincFilter : public Filter
{
public:
SincFilter(float w);
virtual float evaluate(float x) const;
};
// Sinc filter.
class NVIMAGE_CLASS SincFilter : public Filter
{
public:
SincFilter(float w);
virtual float evaluate(float x) const;
};
// Kaiser filter.
class NVIMAGE_CLASS KaiserFilter : public Filter
{
public:
KaiserFilter(float w);
virtual float evaluate(float x) const;
void setParameters(float a, float stretch);
// Kaiser filter.
class NVIMAGE_CLASS KaiserFilter : public Filter
{
public:
KaiserFilter(float w);
virtual float evaluate(float x) const;
private:
float alpha;
float stretch;
};
void setParameters(float a, float stretch);
private:
float alpha;
float stretch;
};
/// A 1D kernel. Used to precompute filter weights.
class NVIMAGE_CLASS Kernel1
{
NV_FORBID_COPY(Kernel1);
public:
Kernel1(const Filter & f, int iscale, int samples = 32);
~Kernel1();
float valueAt(uint x) const {
nvDebugCheck(x < (uint)m_windowSize);
return m_data[x];
}
int windowSize() const {
return m_windowSize;
}
float width() const {
return m_width;
}
void debugPrint();
private:
int m_windowSize;
float m_width;
float * m_data;
};
/// A 1D kernel. Used to precompute filter weights.
class NVIMAGE_CLASS Kernel1
{
NV_FORBID_COPY(Kernel1);
public:
Kernel1(const Filter & f, int iscale, int samples = 32);
~Kernel1();
float valueAt(uint x) const {
nvDebugCheck(x < (uint)m_windowSize);
return m_data[x];
}
int windowSize() const {
return m_windowSize;
}
float width() const {
return m_width;
}
void debugPrint();
private:
int m_windowSize;
float m_width;
float * m_data;
};
/// A 2D kernel.
class NVIMAGE_CLASS Kernel2
{
public:
Kernel2(uint width);
Kernel2(const Kernel2 & k);
~Kernel2();
void normalize();
void transpose();
float valueAt(uint x, uint y) const {
return m_data[y * m_windowSize + x];
}
uint windowSize() const {
return m_windowSize;
}
void initLaplacian();
void initEdgeDetection();
void initSobel();
void initPrewitt();
void initBlendedSobel(const Vector4 & scale);
private:
const uint m_windowSize;
float * m_data;
};
/// A 2D kernel.
class NVIMAGE_CLASS Kernel2
{
public:
Kernel2(uint width);
Kernel2(const Kernel2 & k);
~Kernel2();
void normalize();
void transpose();
float valueAt(uint x, uint y) const {
return m_data[y * m_windowSize + x];
}
uint windowSize() const {
return m_windowSize;
}
void initLaplacian();
void initEdgeDetection();
void initSobel();
void initPrewitt();
void initBlendedSobel(const Vector4 & scale);
private:
const uint m_windowSize;
float * m_data;
};
/// A 1D polyphase kernel
class NVIMAGE_CLASS PolyphaseKernel
{
NV_FORBID_COPY(PolyphaseKernel);
public:
PolyphaseKernel(const Filter & f, uint srcLength, uint dstLength, int samples = 32);
~PolyphaseKernel();
int windowSize() const {
return m_windowSize;
}
uint length() const {
return m_length;
}
/// A 1D polyphase kernel
class NVIMAGE_CLASS PolyphaseKernel
{
NV_FORBID_COPY(PolyphaseKernel);
public:
PolyphaseKernel(const Filter & f, uint srcLength, uint dstLength, int samples = 32);
~PolyphaseKernel();
float width() const {
return m_width;
}
int windowSize() const {
return m_windowSize;
}
float valueAt(uint column, uint x) const {
nvDebugCheck(column < m_length);
nvDebugCheck(x < (uint)m_windowSize);
return m_data[column * m_windowSize + x];
}
uint length() const {
return m_length;
}
void debugPrint() const;
private:
int m_windowSize;
uint m_length;
float m_width;
float * m_data;
};
float width() const {
return m_width;
}
float valueAt(uint column, uint x) const {
nvDebugCheck(column < m_length);
nvDebugCheck(x < (uint)m_windowSize);
return m_data[column * m_windowSize + x];
}
void debugPrint() const;
private:
int m_windowSize;
uint m_length;
float m_width;
float * m_data;
};
} // nv namespace

File diff suppressed because it is too large Load Diff

View File

@ -1,275 +1,267 @@
// This code is in the public domain -- castanyo@yahoo.es
// This code is in the public domain -- castanyo@yahoo.es
#ifndef NV_IMAGE_FLOATIMAGE_H
#define NV_IMAGE_FLOATIMAGE_H
#include "nvimage.h"
#include "nvcore/Debug.h"
#include "nvcore/Utils.h" // clamp
#include <stdlib.h> // abs
namespace nv
{
class Vector4;
class Matrix;
class Image;
class Filter;
class Kernel1;
class Kernel2;
class PolyphaseKernel;
/// Multicomponent floating point image class.
class FloatImage
{
public:
enum WrapMode {
WrapMode_Clamp,
WrapMode_Repeat,
WrapMode_Mirror
};
NVIMAGE_API FloatImage();
NVIMAGE_API FloatImage(const Image * img);
NVIMAGE_API virtual ~FloatImage();
/** @name Conversion. */
//@{
NVIMAGE_API void initFrom(const Image * img);
NVIMAGE_API Image * createImage(uint base_component = 0, uint num = 4) const;
NVIMAGE_API Image * createImageGammaCorrect(float gamma = 2.2f) const;
//@}
/** @name Allocation. */
//@{
NVIMAGE_API void allocate(uint c, uint w, uint h);
NVIMAGE_API void free(); // Does not clear members.
//@}
/** @name Manipulation. */
//@{
NVIMAGE_API void clear(float f = 0.0f);
NVIMAGE_API void clear(uint component, float f = 0.0f);
NVIMAGE_API void normalize(uint base_component);
NVIMAGE_API void packNormals(uint base_component);
NVIMAGE_API void expandNormals(uint base_component);
NVIMAGE_API void scaleBias(uint base_component, uint num, float scale, float add);
//NVIMAGE_API void clamp(uint base_component, uint num);
NVIMAGE_API void clamp(float low, float high);
NVIMAGE_API void toLinear(uint base_component, uint num, float gamma = 2.2f);
NVIMAGE_API void toGamma(uint base_component, uint num, float gamma = 2.2f);
NVIMAGE_API void exponentiate(uint base_component, uint num, float power);
NVIMAGE_API void transform(uint base_component, const Matrix & m, const Vector4 & offset);
NVIMAGE_API void swizzle(uint base_component, uint r, uint g, uint b, uint a);
NVIMAGE_API FloatImage * fastDownSample() const;
NVIMAGE_API FloatImage * downSample(const Filter & filter, WrapMode wm) const;
NVIMAGE_API FloatImage * downSample(const Filter & filter, WrapMode wm, uint alpha) const;
NVIMAGE_API FloatImage * resize(const Filter & filter, uint w, uint h, WrapMode wm) const;
NVIMAGE_API FloatImage * resize(const Filter & filter, uint w, uint h, WrapMode wm, uint alpha) const;
//NVIMAGE_API FloatImage * downSample(const Kernel1 & filter, WrapMode wm) const;
//NVIMAGE_API FloatImage * downSample(const Kernel1 & filter, uint w, uint h, WrapMode wm) const;
//@}
NVIMAGE_API float applyKernel(const Kernel2 * k, int x, int y, uint c, WrapMode wm) const;
NVIMAGE_API float applyKernelVertical(const Kernel1 * k, int x, int y, uint c, WrapMode wm) const;
NVIMAGE_API float applyKernelHorizontal(const Kernel1 * k, int x, int y, uint c, WrapMode wm) const;
NVIMAGE_API void applyKernelVertical(const PolyphaseKernel & k, int x, uint c, WrapMode wm, float * output) const;
NVIMAGE_API void applyKernelHorizontal(const PolyphaseKernel & k, int y, uint c, WrapMode wm, float * output) const;
NVIMAGE_API void applyKernelVertical(const PolyphaseKernel & k, int x, uint c, uint a, WrapMode wm, float * output) const;
NVIMAGE_API void applyKernelHorizontal(const PolyphaseKernel & k, int y, uint c, uint a, WrapMode wm, float * output) const;
NVIMAGE_API void flip();
#ifndef NV_IMAGE_FLOATIMAGE_H
#define NV_IMAGE_FLOATIMAGE_H
#include <nvimage/nvimage.h>
#include <nvmath/Vector.h>
#include <nvcore/Debug.h>
#include <nvcore/Algorithms.h> // clamp
#include <stdlib.h> // abs
namespace nv
{
class Vector4;
class Matrix;
class Image;
class Filter;
class Kernel1;
class Kernel2;
class PolyphaseKernel;
/// Multicomponent floating point image class.
class FloatImage
{
public:
enum WrapMode {
WrapMode_Clamp,
WrapMode_Repeat,
WrapMode_Mirror
};
NVIMAGE_API FloatImage();
NVIMAGE_API FloatImage(const Image * img);
NVIMAGE_API virtual ~FloatImage();
/** @name Conversion. */
//@{
NVIMAGE_API void initFrom(const Image * img);
NVIMAGE_API Image * createImage(uint base_component = 0, uint num = 4) const;
NVIMAGE_API Image * createImageGammaCorrect(float gamma = 2.2f) const;
//@}
/** @name Allocation. */
//@{
NVIMAGE_API void allocate(uint c, uint w, uint h);
NVIMAGE_API void free(); // Does not clear members.
//@}
/** @name Manipulation. */
//@{
NVIMAGE_API void clear(float f=0.0f);
NVIMAGE_API void normalize(uint base_component);
NVIMAGE_API void packNormals(uint base_component);
NVIMAGE_API void expandNormals(uint base_component);
NVIMAGE_API void scaleBias(uint base_component, uint num, float scale, float bias);
NVIMAGE_API void clamp(uint base_component, uint num, float low, float high);
NVIMAGE_API void toLinear(uint base_component, uint num, float gamma = 2.2f);
NVIMAGE_API void toGamma(uint base_component, uint num, float gamma = 2.2f);
NVIMAGE_API void exponentiate(uint base_component, uint num, float power);
NVIMAGE_API void transform(uint base_component, const Matrix & m, const Vector4 & offset);
NVIMAGE_API void swizzle(uint base_component, uint r, uint g, uint b, uint a);
NVIMAGE_API FloatImage * fastDownSample() const;
NVIMAGE_API FloatImage * downSample(const Filter & filter, WrapMode wm) const;
NVIMAGE_API FloatImage * downSample(const Filter & filter, WrapMode wm, uint alpha) const;
NVIMAGE_API FloatImage * resize(const Filter & filter, uint w, uint h, WrapMode wm) const;
NVIMAGE_API FloatImage * resize(const Filter & filter, uint w, uint h, WrapMode wm, uint alpha) const;
NVIMAGE_API float applyKernel(const Kernel2 * k, int x, int y, uint c, WrapMode wm) const;
NVIMAGE_API float applyKernelVertical(const Kernel1 * k, int x, int y, uint c, WrapMode wm) const;
NVIMAGE_API float applyKernelHorizontal(const Kernel1 * k, int x, int y, uint c, WrapMode wm) const;
NVIMAGE_API void applyKernelVertical(const PolyphaseKernel & k, int x, uint c, WrapMode wm, float * output) const;
NVIMAGE_API void applyKernelHorizontal(const PolyphaseKernel & k, int y, uint c, WrapMode wm, float * output) const;
NVIMAGE_API void applyKernelVertical(const PolyphaseKernel & k, int x, uint c, uint a, WrapMode wm, float * output) const;
NVIMAGE_API void applyKernelHorizontal(const PolyphaseKernel & k, int y, uint c, uint a, WrapMode wm, float * output) const;
NVIMAGE_API void flip();
NVIMAGE_API float alphaTestCoverage(float alphaRef, int alphaChannel) const;
NVIMAGE_API void scaleAlphaToCoverage(float coverage, float alphaRef, int alphaChannel);
//@}
uint width() const { return m_width; }
uint height() const { return m_height; }
uint componentNum() const { return m_componentNum; }
uint count() const { return m_count; }
/** @name Pixel access. */
//@{
const float * channel(uint c) const;
float * channel(uint c);
const float * scanline(uint y, uint c) const;
float * scanline(uint y, uint c);
void setPixel(float f, uint x, uint y, uint c);
void addPixel(float f, uint x, uint y, uint c);
float pixel(uint x, uint y, uint c) const;
void setPixel(float f, uint idx);
float pixel(uint idx) const;
float sampleNearest(float x, float y, int c, WrapMode wm) const;
float sampleLinear(float x, float y, int c, WrapMode wm) const;
float sampleNearestClamp(float x, float y, int c) const;
float sampleNearestRepeat(float x, float y, int c) const;
float sampleNearestMirror(float x, float y, int c) const;
float sampleLinearClamp(float x, float y, int c) const;
float sampleLinearRepeat(float x, float y, int c) const;
float sampleLinearMirror(float x, float y, int c) const;
//@}
FloatImage* clone() const;
public:
uint index(uint x, uint y) const;
uint indexClamp(int x, int y) const;
uint indexRepeat(int x, int y) const;
uint indexMirror(int x, int y) const;
uint index(int x, int y, WrapMode wm) const;
public:
uint16 m_width; ///< Width of the texture.
uint16 m_height; ///< Height of the texture.
uint32 m_componentNum; ///< Number of components.
uint32 m_count; ///< Image pixel count.
float * m_mem;
};
/// Get const channel pointer.
inline const float * FloatImage::channel(uint c) const
{
nvDebugCheck(m_mem != NULL);
nvDebugCheck(c < m_componentNum);
return m_mem + c * m_width * m_height;
}
/// Get channel pointer.
inline float * FloatImage::channel(uint c) {
nvDebugCheck(m_mem != NULL);
nvDebugCheck(c < m_componentNum);
return m_mem + c * m_width * m_height;
}
/// Get const scanline pointer.
inline const float * FloatImage::scanline(uint y, uint c) const
{
nvDebugCheck(y < m_height);
return channel(c) + y * m_width;
}
/// Get scanline pointer.
inline float * FloatImage::scanline(uint y, uint c)
{
nvDebugCheck(y < m_height);
return channel(c) + y * m_width;
}
/// Set pixel component.
inline void FloatImage::setPixel(float f, uint x, uint y, uint c)
{
nvDebugCheck(m_mem != NULL);
nvDebugCheck(x < m_width);
nvDebugCheck(y < m_height);
nvDebugCheck(c < m_componentNum);
m_mem[(c * m_height + y) * m_width + x] = f;
}
/// Add to pixel component.
inline void FloatImage::addPixel(float f, uint x, uint y, uint c)
{
nvDebugCheck(m_mem != NULL);
nvDebugCheck(x < m_width);
nvDebugCheck(y < m_height);
nvDebugCheck(c < m_componentNum);
m_mem[(c * m_height + y) * m_width + x] += f;
}
/// Get pixel component.
inline float FloatImage::pixel(uint x, uint y, uint c) const
{
nvDebugCheck(m_mem != NULL);
nvDebugCheck(x < m_width);
nvDebugCheck(y < m_height);
nvDebugCheck(c < m_componentNum);
return m_mem[(c * m_height + y) * m_width + x];
}
/// Set pixel component.
inline void FloatImage::setPixel(float f, uint idx)
{
nvDebugCheck(idx < m_count);
m_mem[idx] = f;
}
/// Get pixel component.
inline float FloatImage::pixel(uint idx) const
{
nvDebugCheck(idx < m_count);
return m_mem[idx];
}
inline uint FloatImage::index(uint x, uint y) const
{
nvDebugCheck(x < m_width);
nvDebugCheck(y < m_height);
return y * m_width + x;
}
inline uint FloatImage::indexClamp(int x, int y) const
{
return nv::clamp(y, int(0), int(m_height-1)) * m_width + nv::clamp(x, int(0), int(m_width-1));
}
inline int repeat_remainder(int a, int b)
{
if (a >= 0) return a % b;
else return (a + 1) % b + b - 1;
}
inline uint FloatImage::indexRepeat(int x, int y) const
{
return repeat_remainder(y, m_height) * m_width + repeat_remainder(x, m_width);
}
inline uint FloatImage::indexMirror(int x, int y) const
{
if (m_width == 1) x = 0;
x = abs(x);
while (x >= m_width) {
x = abs(m_width + m_width - x - 2);
}
if (m_height == 1) y = 0;
y = abs(y);
while (y >= m_height) {
y = abs(m_height + m_height - y - 2);
}
return index(x, y);
}
inline uint FloatImage::index(int x, int y, WrapMode wm) const
{
if (wm == WrapMode_Clamp) return indexClamp(x, y);
if (wm == WrapMode_Repeat) return indexRepeat(x, y);
/*if (wm == WrapMode_Mirror)*/ return indexMirror(x, y);
}
} // nv namespace
#endif // NV_IMAGE_FLOATIMAGE_H
NVIMAGE_API float alphaTestCoverage(float alphaRef, int alphaChannel) const;
NVIMAGE_API void scaleAlphaToCoverage(float coverage, float alphaRef, int alphaChannel);
uint width() const { return m_width; }
uint height() const { return m_height; }
uint componentNum() const { return m_componentNum; }
uint count() const { return m_count; }
/** @name Pixel access. */
//@{
const float * channel(uint c) const;
float * channel(uint c);
const float * scanline(uint y, uint c) const;
float * scanline(uint y, uint c);
float pixel(uint x, uint y, uint c) const;
float & pixel(uint x, uint y, uint c);
float pixel(uint idx) const;
float & pixel(uint idx);
float sampleNearest(float x, float y, int c, WrapMode wm) const;
float sampleLinear(float x, float y, int c, WrapMode wm) const;
float sampleNearestClamp(float x, float y, int c) const;
float sampleNearestRepeat(float x, float y, int c) const;
float sampleNearestMirror(float x, float y, int c) const;
float sampleLinearClamp(float x, float y, int c) const;
float sampleLinearRepeat(float x, float y, int c) const;
float sampleLinearMirror(float x, float y, int c) const;
//@}
FloatImage* clone() const;
public:
uint index(uint x, uint y) const;
uint indexClamp(int x, int y) const;
uint indexRepeat(int x, int y) const;
uint indexMirror(int x, int y) const;
uint index(int x, int y, WrapMode wm) const;
public:
uint16 m_width; ///< Width of the texture.
uint16 m_height; ///< Height of the texture.
uint32 m_componentNum; ///< Number of components.
uint32 m_count; ///< Image pixel count.
float * m_mem;
};
/// Get const channel pointer.
inline const float * FloatImage::channel(uint c) const
{
nvDebugCheck(m_mem != NULL);
nvDebugCheck(c < m_componentNum);
return m_mem + c * m_width * m_height;
}
/// Get channel pointer.
inline float * FloatImage::channel(uint c) {
nvDebugCheck(m_mem != NULL);
nvDebugCheck(c < m_componentNum);
return m_mem + c * m_width * m_height;
}
/// Get const scanline pointer.
inline const float * FloatImage::scanline(uint y, uint c) const
{
nvDebugCheck(y < m_height);
return channel(c) + y * m_width;
}
/// Get scanline pointer.
inline float * FloatImage::scanline(uint y, uint c)
{
nvDebugCheck(y < m_height);
return channel(c) + y * m_width;
}
/// Get pixel component.
inline float FloatImage::pixel(uint x, uint y, uint c) const
{
nvDebugCheck(m_mem != NULL);
nvDebugCheck(x < m_width);
nvDebugCheck(y < m_height);
nvDebugCheck(c < m_componentNum);
return m_mem[(c * m_height + y) * m_width + x];
}
/// Get pixel component.
inline float & FloatImage::pixel(uint x, uint y, uint c)
{
nvDebugCheck(m_mem != NULL);
nvDebugCheck(x < m_width);
nvDebugCheck(y < m_height);
nvDebugCheck(c < m_componentNum);
return m_mem[(c * m_height + y) * m_width + x];
}
/// Get pixel component.
inline float FloatImage::pixel(uint idx) const
{
nvDebugCheck(idx < m_count);
return m_mem[idx];
}
/// Get pixel component.
inline float & FloatImage::pixel(uint idx)
{
nvDebugCheck(idx < m_count);
return m_mem[idx];
}
inline uint FloatImage::index(uint x, uint y) const
{
nvDebugCheck(x < m_width);
nvDebugCheck(y < m_height);
return y * m_width + x;
}
inline uint FloatImage::indexClamp(int x, int y) const
{
return nv::clamp(y, int(0), int(m_height-1)) * m_width + nv::clamp(x, int(0), int(m_width-1));
}
inline int repeat_remainder(int a, int b)
{
if (a >= 0) return a % b;
else return (a + 1) % b + b - 1;
}
inline uint FloatImage::indexRepeat(int x, int y) const
{
return repeat_remainder(y, m_height) * m_width + repeat_remainder(x, m_width);
}
inline uint FloatImage::indexMirror(int x, int y) const
{
if (m_width == 1) x = 0;
x = abs(x);
while (x >= m_width) {
x = abs(m_width + m_width - x - 2);
}
if (m_height == 1) y = 0;
y = abs(y);
while (y >= m_height) {
y = abs(m_height + m_height - y - 2);
}
return index(x, y);
}
inline uint FloatImage::index(int x, int y, WrapMode wm) const
{
if (wm == WrapMode_Clamp) return indexClamp(x, y);
if (wm == WrapMode_Repeat) return indexRepeat(x, y);
/*if (wm == WrapMode_Mirror)*/ return indexMirror(x, y);
}
} // nv namespace
#endif // NV_IMAGE_FLOATIMAGE_H

View File

@ -1,13 +1,13 @@
// This code is in the public domain -- castanyo@yahoo.es
#include <nvimage/Image.h>
#include <nvimage/ImageIO.h>
#include "Image.h"
#include "ImageIO.h"
#include <nvmath/Color.h>
#include "nvmath/Color.h"
#include <nvcore/Debug.h>
#include <nvcore/Ptr.h>
#include <nvcore/Containers.h> // swap
#include "nvcore/Debug.h"
#include "nvcore/Ptr.h"
#include "nvcore/Utils.h" // swap
using namespace nv;
@ -18,133 +18,133 @@ Image::Image() : m_width(0), m_height(0), m_format(Format_RGB), m_data(NULL)
Image::Image(const Image & img) : m_data(NULL)
{
allocate(img.m_width, img.m_height);
m_format = img.m_format;
memcpy(m_data, img.m_data, sizeof(Color32) * m_width * m_height);
allocate(img.m_width, img.m_height);
m_format = img.m_format;
memcpy(m_data, img.m_data, sizeof(Color32) * m_width * m_height);
}
Image::~Image()
{
free();
free();
}
const Image & Image::operator=(const Image & img)
{
allocate(img.m_width, img.m_height);
m_format = img.m_format;
memcpy(m_data, img.m_data, sizeof(Color32) * m_width * m_height);
return *this;
allocate(img.m_width, img.m_height);
m_format = img.m_format;
memcpy(m_data, img.m_data, sizeof(Color32) * m_width * m_height);
return *this;
}
void Image::allocate(uint w, uint h)
{
m_width = w;
m_height = h;
m_data = (Color32 *)nv::mem::realloc(m_data, w * h * sizeof(Color32));
m_width = w;
m_height = h;
m_data = (Color32 *)nv::mem::realloc(m_data, w * h * sizeof(Color32));
}
bool Image::load(const char * name)
{
free();
AutoPtr<Image> img(ImageIO::load(name));
if (img == NULL) {
return false;
}
swap(m_width, img->m_width);
swap(m_height, img->m_height);
swap(m_format, img->m_format);
swap(m_data, img->m_data);
return true;
free();
AutoPtr<Image> img(ImageIO::load(name));
if (img == NULL) {
return false;
}
swap(m_width, img->m_width);
swap(m_height, img->m_height);
swap(m_format, img->m_format);
swap(m_data, img->m_data);
return true;
}
void Image::wrap(void * data, uint w, uint h)
{
free();
m_data = (Color32 *)data;
m_width = w;
m_height = h;
free();
m_data = (Color32 *)data;
m_width = w;
m_height = h;
}
void Image::unwrap()
{
m_data = NULL;
m_width = 0;
m_height = 0;
m_data = NULL;
m_width = 0;
m_height = 0;
}
void Image::free()
{
nv::mem::free(m_data);
m_data = NULL;
nv::mem::free(m_data);
m_data = NULL;
}
uint Image::width() const
{
return m_width;
return m_width;
}
uint Image::height() const
{
return m_height;
return m_height;
}
const Color32 * Image::scanline(uint h) const
{
nvDebugCheck(h < m_height);
return m_data + h * m_width;
nvDebugCheck(h < m_height);
return m_data + h * m_width;
}
Color32 * Image::scanline(uint h)
{
nvDebugCheck(h < m_height);
return m_data + h * m_width;
nvDebugCheck(h < m_height);
return m_data + h * m_width;
}
const Color32 * Image::pixels() const
{
return m_data;
return m_data;
}
Color32 * Image::pixels()
{
return m_data;
return m_data;
}
const Color32 & Image::pixel(uint idx) const
{
nvDebugCheck(idx < m_width * m_height);
return m_data[idx];
nvDebugCheck(idx < m_width * m_height);
return m_data[idx];
}
Color32 & Image::pixel(uint idx)
{
nvDebugCheck(idx < m_width * m_height);
return m_data[idx];
nvDebugCheck(idx < m_width * m_height);
return m_data[idx];
}
Image::Format Image::format() const
{
return m_format;
return m_format;
}
void Image::setFormat(Image::Format f)
{
m_format = f;
m_format = f;
}
void Image::fill(Color32 c)
{
const uint size = m_width * m_height;
for (uint i = 0; i < size; ++i)
{
m_data[i] = c;
}
const uint size = m_width * m_height;
for (uint i = 0; i < size; ++i)
{
m_data[i] = c;
}
}

View File

@ -1,81 +1,82 @@
// This code is in the public domain -- castanyo@yahoo.es
#pragma once
#ifndef NV_IMAGE_IMAGE_H
#define NV_IMAGE_IMAGE_H
#include <nvcore/Debug.h>
#include <nvimage/nvimage.h>
#include "nvimage.h"
#include "nvcore/Debug.h"
namespace nv
{
class Color32;
/// 32 bit RGBA image.
class NVIMAGE_CLASS Image
{
public:
enum Format
{
Format_RGB,
Format_ARGB,
};
Image();
Image(const Image & img);
~Image();
class Color32;
const Image & operator=(const Image & img);
/// 32 bit RGBA image.
class NVIMAGE_CLASS Image
{
public:
enum Format
{
Format_RGB,
Format_ARGB,
};
Image();
Image(const Image & img);
~Image();
const Image & operator=(const Image & img);
void allocate(uint w, uint h);
bool load(const char * name);
void wrap(void * data, uint w, uint h);
void unwrap();
uint width() const;
uint height() const;
const Color32 * scanline(uint h) const;
Color32 * scanline(uint h);
const Color32 * pixels() const;
Color32 * pixels();
const Color32 & pixel(uint idx) const;
Color32 & pixel(uint idx);
const Color32 & pixel(uint x, uint y) const;
Color32 & pixel(uint x, uint y);
Format format() const;
void setFormat(Format f);
void fill(Color32 c);
void allocate(uint w, uint h);
bool load(const char * name);
private:
void free();
private:
uint m_width;
uint m_height;
Format m_format;
Color32 * m_data;
};
void wrap(void * data, uint w, uint h);
void unwrap();
uint width() const;
uint height() const;
const Color32 * scanline(uint h) const;
Color32 * scanline(uint h);
const Color32 * pixels() const;
Color32 * pixels();
const Color32 & pixel(uint idx) const;
Color32 & pixel(uint idx);
const Color32 & pixel(uint x, uint y) const;
Color32 & pixel(uint x, uint y);
Format format() const;
void setFormat(Format f);
void fill(Color32 c);
private:
void free();
private:
uint m_width;
uint m_height;
Format m_format;
Color32 * m_data;
};
inline const Color32 & Image::pixel(uint x, uint y) const
{
nvDebugCheck(x < width() && y < height());
return pixel(y * width() + x);
}
inline Color32 & Image::pixel(uint x, uint y)
{
nvDebugCheck(x < width() && y < height());
return pixel(y * width() + x);
}
inline const Color32 & Image::pixel(uint x, uint y) const
{
nvDebugCheck(x < width() && y < height());
return pixel(y * width() + x);
}
inline Color32 & Image::pixel(uint x, uint y)
{
nvDebugCheck(x < width() && y < height());
return pixel(y * width() + x);
}
} // nv namespace

View File

@ -6,12 +6,13 @@
#include "TgaFile.h"
#include "PsdFile.h"
#include <nvmath/Color.h>
#include "nvmath/Color.h"
#include <nvcore/Ptr.h>
#include <nvcore/Containers.h>
#include <nvcore/StrLib.h>
#include <nvcore/StdStream.h>
#include "nvcore/Ptr.h"
#include "nvcore/Utils.h"
#include "nvcore/Array.h"
#include "nvcore/StrLib.h"
#include "nvcore/StdStream.h"
// Extern
#if defined(HAVE_FREEIMAGE)

View File

@ -1,40 +1,42 @@
// This code is in the public domain -- castanyo@yahoo.es
#pragma once
#ifndef NV_IMAGE_IMAGEIO_H
#define NV_IMAGE_IMAGEIO_H
#include <nvimage/nvimage.h>
#include "nvimage.h"
#include <nvcore/StrLib.h>
#include "nvcore/StrLib.h"
#include "nvcore/HashMap.h"
namespace nv
{
class Image;
class FloatImage;
class Stream;
class Image;
class FloatImage;
class Stream;
namespace ImageIO
{
struct ImageMetaData
{
HashMap<String, String> tagMap;
};
namespace ImageIO
{
struct ImageMetaData
{
HashMap<String, String> tagMap;
};
NVIMAGE_API Image * load(const char * fileName);
NVIMAGE_API Image * load(const char * fileName, Stream & s);
NVIMAGE_API Image * load(const char * fileName);
NVIMAGE_API Image * load(const char * fileName, Stream & s);
NVIMAGE_API FloatImage * loadFloat(const char * fileName);
NVIMAGE_API FloatImage * loadFloat(const char * fileName, Stream & s);
NVIMAGE_API bool save(const char * fileName, const Image * img, const ImageMetaData * tags=NULL);
NVIMAGE_API bool save(const char * fileName, Stream & s, const Image * img, const ImageMetaData * tags=NULL);
NVIMAGE_API FloatImage * loadFloat(const char * fileName);
NVIMAGE_API FloatImage * loadFloat(const char * fileName, Stream & s);
NVIMAGE_API bool saveFloat(const char * fileName, const FloatImage * fimage, uint baseComponent, uint componentCount);
NVIMAGE_API bool saveFloat(const char * fileName, Stream & s, const FloatImage * fimage, uint baseComponent, uint componentCount);
NVIMAGE_API bool save(const char * fileName, const Image * img, const ImageMetaData * tags=NULL);
NVIMAGE_API bool save(const char * fileName, Stream & s, const Image * img, const ImageMetaData * tags=NULL);
NVIMAGE_API bool saveFloat(const char * fileName, const FloatImage * fimage, uint baseComponent, uint componentCount);
NVIMAGE_API bool saveFloat(const char * fileName, Stream & s, const FloatImage * fimage, uint baseComponent, uint componentCount);
} // ImageIO namespace
} // ImageIO namespace
} // nv namespace

View File

@ -65,9 +65,9 @@ static FloatImage * createNormalMap(const Image * img, FloatImage::WrapMode wm,
Vector3 n = normalize(Vector3(du, dv, heightScale));
fimage->setPixel(0.5f * n.x() + 0.5f, x, y, 0);
fimage->setPixel(0.5f * n.y() + 0.5f, x, y, 1);
fimage->setPixel(0.5f * n.z() + 0.5f, x, y, 2);
fimage->pixel(x, y, 0) = 0.5f * n.x + 0.5f;
fimage->pixel(x, y, 1) = 0.5f * n.y + 0.5f;
fimage->pixel(x, y, 2) = 0.5f * n.z + 0.5f;
}
}
@ -100,9 +100,9 @@ static FloatImage * createNormalMap(const FloatImage * img, FloatImage::WrapMode
Vector3 n = normalize(Vector3(du, dv, heightScale));
img_out->setPixel(n.x(), x, y, 0);
img_out->setPixel(n.y(), x, y, 1);
img_out->setPixel(n.z(), x, y, 2);
img_out->pixel(x, y, 0) = n.x;
img_out->pixel(x, y, 1) = n.y;
img_out->pixel(x, y, 2) = n.z;
}
}
@ -111,7 +111,7 @@ static FloatImage * createNormalMap(const FloatImage * img, FloatImage::WrapMode
{
for (uint x = 0; x < w; x++)
{
img_out->setPixel(img->pixel(x, y, 3), x, y, 3);
img_out->pixel(x, y, 3) = img->pixel(x, y, 3);
}
}

View File

@ -12,13 +12,15 @@ http://www.efg2.com/Lab/Library/ImageProcessing/DHALF.TXT
@@ This code needs to be reviewed, I'm not sure it's correct.
*/
#include <nvimage/Quantize.h>
#include <nvimage/Image.h>
#include <nvimage/PixelFormat.h>
#include "Quantize.h"
#include "Image.h"
#include "PixelFormat.h"
#include <nvmath/Color.h>
#include "nvmath/Color.h"
#include <nvcore/Containers.h> // swap
#include "nvcore/Utils.h" // swap
#include <string.h> // memset
using namespace nv;
@ -174,10 +176,10 @@ void nv::Quantize::FloydSteinberg(Image * image, uint rsize, uint gsize, uint bs
Color32 pixel = image->pixel(x, y);
// Add error.
pixel.r = clamp(int(pixel.r) + int(row0[1+x].x()), 0, 255);
pixel.g = clamp(int(pixel.g) + int(row0[1+x].y()), 0, 255);
pixel.b = clamp(int(pixel.b) + int(row0[1+x].z()), 0, 255);
pixel.a = clamp(int(pixel.a) + int(row0[1+x].w()), 0, 255);
pixel.r = clamp(int(pixel.r) + int(row0[1+x].x), 0, 255);
pixel.g = clamp(int(pixel.g) + int(row0[1+x].y), 0, 255);
pixel.b = clamp(int(pixel.b) + int(row0[1+x].z), 0, 255);
pixel.a = clamp(int(pixel.a) + int(row0[1+x].w), 0, 255);
int r = pixel.r;
int g = pixel.g;