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:
Rogier 2014-04-10 22:57:37 +02:00
parent 7dc3b48a7c
commit 8faa2d4a87
3 changed files with 155 additions and 38 deletions

101
PixelCacheEntry.h Normal file
View 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

View File

@ -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;
} }
} }
} }

View File

@ -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;