Fix/improve handling of colors with alpha channel
Color mixing using alpha channel now spans map blocks. So for instance, water is partly translucent to great depths. Water (or other translucent material) now also becomes darker with depth.
This commit is contained in:
parent
7dc3b48a7c
commit
8faa2d4a87
101
PixelCacheEntry.h
Normal file
101
PixelCacheEntry.h
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
|
||||||
|
#ifndef PIXELCACHEINFO_H
|
||||||
|
#define PIXELCACHEINFO_H
|
||||||
|
|
||||||
|
#include<cmath>
|
||||||
|
#include"Color.h"
|
||||||
|
|
||||||
|
struct PixelCacheEntry {
|
||||||
|
PixelCacheEntry();
|
||||||
|
PixelCacheEntry(unsigned int color, double height=NAN);
|
||||||
|
PixelCacheEntry(const Color &color, double height=NAN);
|
||||||
|
PixelCacheEntry(const ColorEntry &entry, double height=NAN);
|
||||||
|
Color to_color(void) const;
|
||||||
|
ColorEntry to_colorEntry(void) const;
|
||||||
|
void reset(void);
|
||||||
|
void mixUnder(const PixelCacheEntry &p);
|
||||||
|
uint8_t red(void) const { return int(r*255+0.5); }
|
||||||
|
uint8_t green(void) const { return int(g*255+0.5); }
|
||||||
|
uint8_t blue(void) const { return int(b*255+0.5); }
|
||||||
|
uint8_t alpha(void) const { return int(a*255+0.5); }
|
||||||
|
uint8_t thicken(void) const { return int(t*255+0.5); }
|
||||||
|
int height(void) const { return int(h+0.5); }
|
||||||
|
|
||||||
|
double r;
|
||||||
|
double g;
|
||||||
|
double b;
|
||||||
|
double a;
|
||||||
|
double t;
|
||||||
|
double h;
|
||||||
|
int n;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline PixelCacheEntry::PixelCacheEntry() :
|
||||||
|
r(0), g(0), b(0), a(0), t(0), h(NAN), n(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
inline PixelCacheEntry::PixelCacheEntry(unsigned int color, double height) :
|
||||||
|
r(((color>>16)&0xff)/255.0), g(((color>>8)&0xff)/255.0), b((color&0xff)/255.0),
|
||||||
|
a(((color>>24)&0xff)/255.0), t(0), h(height), n(1)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
inline PixelCacheEntry::PixelCacheEntry(const Color &color, double height) :
|
||||||
|
r(color.r/255.0), g(color.g/255.0), b(color.b/255.0),
|
||||||
|
a(color.a/255.0), t(0), h(height), n(1)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
inline PixelCacheEntry::PixelCacheEntry(const ColorEntry &entry, double height) :
|
||||||
|
r(entry.r/255.0), g(entry.g/255.0), b(entry.b/255.0),
|
||||||
|
a(entry.a/255.0), t(entry.t/255.0), h(height), n(1)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Color PixelCacheEntry::to_color(void)
|
||||||
|
{
|
||||||
|
return Color(int(r*255+0.5),int(g*255+0.5),int(b*255+0.5),int(a*255+0.5));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ColorEntry PixelCacheEntry::to_colorEntry(void)
|
||||||
|
{
|
||||||
|
return ColorEntry(int(r*255+0.5),int(g*255+0.5),int(b*255+0.5),int(a*255+0.5),int(t*255+0.5));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void PixelCacheEntry::reset(void)
|
||||||
|
{
|
||||||
|
r=0; g=0; b=0; a=0; t=0; h=NAN; n=0;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void PixelCacheEntry::mixUnder(const PixelCacheEntry &p)
|
||||||
|
{
|
||||||
|
int prev_alpha = alpha();
|
||||||
|
if (!n || a==0) {
|
||||||
|
r = p.r;
|
||||||
|
g = p.g;
|
||||||
|
b = p.b;
|
||||||
|
a = p.a;
|
||||||
|
t = p.t;
|
||||||
|
h = p.h;
|
||||||
|
n = p.n;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Regular mix
|
||||||
|
r = (a * r + p.a * (1 - a) * p.r);
|
||||||
|
g = (a * g + p.a * (1 - a) * p.g);
|
||||||
|
b = (a * b + p.a * (1 - a) * p.b);
|
||||||
|
a = (a + (1 - a) * p.a);
|
||||||
|
t = (t + p.t) / 2;
|
||||||
|
h = p.h;
|
||||||
|
}
|
||||||
|
if (prev_alpha == 255 && p.alpha() < 255) {
|
||||||
|
// Darken
|
||||||
|
// Parameters make deep water look good :-)
|
||||||
|
r = r * (0.85 + 0.1 * (1 - p.a));
|
||||||
|
g = g * (0.85 + 0.1 * (1 - p.a));
|
||||||
|
b = b * (0.85 + 0.1 * (1 - p.a));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // PIXELCACHEINFO_H
|
@ -88,19 +88,6 @@ static inline int colorSafeBounds(int color)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline Color mixColors(Color a, Color b)
|
|
||||||
{
|
|
||||||
Color result;
|
|
||||||
double a1 = a.a / 255.0;
|
|
||||||
double a2 = b.a / 255.0;
|
|
||||||
|
|
||||||
result.r = (int) (a1 * a.r + a2 * (1 - a1) * b.r);
|
|
||||||
result.g = (int) (a1 * a.g + a2 * (1 - a1) * b.g);
|
|
||||||
result.b = (int) (a1 * a.b + a2 * (1 - a1) * b.b);
|
|
||||||
result.a = (int) (255 * (a1 + a2 * (1 - a1)));
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
TileGenerator::TileGenerator():
|
TileGenerator::TileGenerator():
|
||||||
verboseCoordinates(false),
|
verboseCoordinates(false),
|
||||||
verboseStatistics(false),
|
verboseStatistics(false),
|
||||||
@ -142,6 +129,7 @@ TileGenerator::TileGenerator():
|
|||||||
m_tileMapXOffset(0),
|
m_tileMapXOffset(0),
|
||||||
m_tileMapYOffset(0)
|
m_tileMapYOffset(0)
|
||||||
{
|
{
|
||||||
|
resetPixelBlockCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
TileGenerator::~TileGenerator()
|
TileGenerator::~TileGenerator()
|
||||||
@ -524,6 +512,29 @@ inline BlockPos TileGenerator::decodeBlockPos(int64_t blockId) const
|
|||||||
return pos;
|
return pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TileGenerator::resetPixelBlockCache(void) {
|
||||||
|
for (int x=0; x<16; x++) {
|
||||||
|
for (int z=0; z<16; z++) {
|
||||||
|
m_pixelBlockCache[z][x].reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TileGenerator::pushPixelBlockCache(int xPos, int zPos) {
|
||||||
|
int xBegin = (xPos - m_xMin) * 16;
|
||||||
|
int zBegin = (m_zMax - zPos) * 16;
|
||||||
|
for (int x=0; x<16; x++) {
|
||||||
|
for (int z=0; z<16; z++) {
|
||||||
|
PixelCacheEntry &pixel = m_pixelBlockCache[z][x];
|
||||||
|
if (pixel.n && !std::isnan(pixel.h)) {
|
||||||
|
m_image->tpixels[getImageY(zBegin + (15 - z))][getImageX(xBegin + x)] = color2libgd(pixel.to_color());
|
||||||
|
m_blockPixelAttributes.attribute(15 - z, xBegin + x).thicken = pixel.thicken();
|
||||||
|
m_blockPixelAttributes.attribute(15 - z, xBegin + x).height = pixel.height();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void TileGenerator::createImage()
|
void TileGenerator::createImage()
|
||||||
{
|
{
|
||||||
m_mapWidth = (m_xMax - m_xMin + 1) * 16;
|
m_mapWidth = (m_xMax - m_xMin + 1) * 16;
|
||||||
@ -676,8 +687,12 @@ void TileGenerator::renderMap()
|
|||||||
const BlockPos &pos = *position;
|
const BlockPos &pos = *position;
|
||||||
if (currentPos.x != pos.x || currentPos.z != pos.z) {
|
if (currentPos.x != pos.x || currentPos.z != pos.z) {
|
||||||
area_rendered++;
|
area_rendered++;
|
||||||
if (currentPos.z != pos.z && currentPos.z != INT_MIN && m_shading)
|
if (currentPos.z != INT_MIN) {
|
||||||
renderShading(currentPos.z);
|
pushPixelBlockCache(currentPos.x, currentPos.z);
|
||||||
|
resetPixelBlockCache();
|
||||||
|
if (currentPos.z != pos.z && m_shading)
|
||||||
|
renderShading(currentPos.z);
|
||||||
|
}
|
||||||
if (progressIndicator && currentPos.z != pos.z)
|
if (progressIndicator && currentPos.z != pos.z)
|
||||||
cout << "Processing Z-coordinate: " << std::setw(5) << pos.z*16 << "\r" << std::flush;
|
cout << "Processing Z-coordinate: " << std::setw(5) << pos.z*16 << "\r" << std::flush;
|
||||||
for (int i = 0; i < 16; ++i) {
|
for (int i = 0; i < 16; ++i) {
|
||||||
@ -782,8 +797,12 @@ void TileGenerator::renderMap()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(currentPos.z != INT_MIN && m_shading)
|
if (currentPos.z != INT_MIN) {
|
||||||
renderShading(currentPos.z);
|
pushPixelBlockCache(currentPos.x, currentPos.z);
|
||||||
|
resetPixelBlockCache();
|
||||||
|
if(m_shading)
|
||||||
|
renderShading(currentPos.z);
|
||||||
|
}
|
||||||
if (verboseStatistics)
|
if (verboseStatistics)
|
||||||
cout << "Statistics"
|
cout << "Statistics"
|
||||||
<< ": blocks read: " << m_db->getBlocksReadCount()
|
<< ": blocks read: " << m_db->getBlocksReadCount()
|
||||||
@ -805,8 +824,6 @@ inline void TileGenerator::renderMapBlock(const unsigned_string &mapBlock, const
|
|||||||
const unsigned char *mapData = mapBlock.c_str();
|
const unsigned char *mapData = mapBlock.c_str();
|
||||||
int minY = (pos.y < m_reqYMin) ? 16 : (pos.y > m_reqYMin) ? 0 : m_reqYMinNode;
|
int minY = (pos.y < m_reqYMin) ? 16 : (pos.y > m_reqYMin) ? 0 : m_reqYMinNode;
|
||||||
int maxY = (pos.y > m_reqYMax) ? -1 : (pos.y < m_reqYMax) ? 15 : m_reqYMaxNode;
|
int maxY = (pos.y > m_reqYMax) ? -1 : (pos.y < m_reqYMax) ? 15 : m_reqYMaxNode;
|
||||||
Color col;
|
|
||||||
uint8_t th;
|
|
||||||
for (int z = 0; z < 16; ++z) {
|
for (int z = 0; z < 16; ++z) {
|
||||||
int imageY = getImageY(zBegin + 15 - z);
|
int imageY = getImageY(zBegin + 15 - z);
|
||||||
for (int x = 0; x < 16; ++x) {
|
for (int x = 0; x < 16; ++x) {
|
||||||
@ -814,10 +831,6 @@ inline void TileGenerator::renderMapBlock(const unsigned_string &mapBlock, const
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
int imageX = getImageX(xBegin + x);
|
int imageX = getImageX(xBegin + x);
|
||||||
if(m_drawAlpha) {
|
|
||||||
col = Color(0,0,0,0);
|
|
||||||
th = 0;
|
|
||||||
}
|
|
||||||
for (int y = maxY; y >= minY; --y) {
|
for (int y = maxY; y >= minY; --y) {
|
||||||
int position = x + (y << 4) + (z << 8);
|
int position = x + (y << 4) + (z << 8);
|
||||||
int content = readBlockContent(mapData, version, position);
|
int content = readBlockContent(mapData, version, position);
|
||||||
@ -832,26 +845,25 @@ inline void TileGenerator::renderMapBlock(const unsigned_string &mapBlock, const
|
|||||||
if (color != m_colors.end()) {
|
if (color != m_colors.end()) {
|
||||||
const Color c = color->second.to_color();
|
const Color c = color->second.to_color();
|
||||||
if (m_drawAlpha) {
|
if (m_drawAlpha) {
|
||||||
if (col.a == 0)
|
PixelCacheEntry pixel = PixelCacheEntry(color->second, pos.y * 16 + y);
|
||||||
col = c;
|
m_pixelBlockCache[z][x].mixUnder(pixel);
|
||||||
else
|
if(pixel.alpha() == 0xff) {
|
||||||
col = mixColors(col, c);
|
m_readedPixels[z] |= (1 << x);
|
||||||
if(col.a == 0xFF) {
|
break;
|
||||||
m_image->tpixels[imageY][imageX] = color2libgd(col);
|
|
||||||
m_blockPixelAttributes.attribute(15 - z, xBegin + x).thicken = th;
|
|
||||||
} else {
|
|
||||||
th = (th + color->second.t) / 2.0;
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
} else
|
else if(0 && m_pixelBlockCache[z][x].alpha() == 0xff) {
|
||||||
|
m_readedPixels[z] |= (1 << x);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
m_image->tpixels[imageY][imageX] = color2libgd(c);
|
m_image->tpixels[imageY][imageX] = color2libgd(c);
|
||||||
m_readedPixels[z] |= (1 << x);
|
m_readedPixels[z] |= (1 << x);
|
||||||
m_blockPixelAttributes.attribute(15 - z, xBegin + x).height = pos.y * 16 + y;
|
m_blockPixelAttributes.attribute(15 - z, xBegin + x).height = pos.y * 16 + y;
|
||||||
|
break;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
m_unknownNodes.insert(name);
|
m_unknownNodes.insert(name);
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include "PixelAttributes.h"
|
#include "PixelAttributes.h"
|
||||||
#include "Color.h"
|
#include "Color.h"
|
||||||
|
#include "PixelCacheEntry.h"
|
||||||
#include "db.h"
|
#include "db.h"
|
||||||
|
|
||||||
#define MINETEST_MAPBLOCK_MIN (-2048)
|
#define MINETEST_MAPBLOCK_MIN (-2048)
|
||||||
@ -119,6 +120,8 @@ private:
|
|||||||
void renderMap();
|
void renderMap();
|
||||||
std::list<int> getZValueList() const;
|
std::list<int> getZValueList() const;
|
||||||
Block getBlockOnPos(BlockPos pos);
|
Block getBlockOnPos(BlockPos pos);
|
||||||
|
void resetPixelBlockCache(void);
|
||||||
|
void pushPixelBlockCache(int xPos, int zPos);
|
||||||
void renderMapBlock(const unsigned_string &mapBlock, const BlockPos &pos, int version);
|
void renderMapBlock(const unsigned_string &mapBlock, const BlockPos &pos, int version);
|
||||||
void renderShading(int zPos);
|
void renderShading(int zPos);
|
||||||
void renderScale();
|
void renderScale();
|
||||||
@ -153,6 +156,7 @@ private:
|
|||||||
DB *m_db;
|
DB *m_db;
|
||||||
gdImagePtr m_image;
|
gdImagePtr m_image;
|
||||||
PixelAttributes m_blockPixelAttributes;
|
PixelAttributes m_blockPixelAttributes;
|
||||||
|
PixelCacheEntry m_pixelBlockCache[16][16];
|
||||||
int m_xMin;
|
int m_xMin;
|
||||||
int m_xMax;
|
int m_xMax;
|
||||||
int m_zMin;
|
int m_zMin;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user