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():
verboseCoordinates(false),
verboseStatistics(false),
@ -142,6 +129,7 @@ TileGenerator::TileGenerator():
m_tileMapXOffset(0),
m_tileMapYOffset(0)
{
resetPixelBlockCache();
}
TileGenerator::~TileGenerator()
@ -524,6 +512,29 @@ inline BlockPos TileGenerator::decodeBlockPos(int64_t blockId) const
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()
{
m_mapWidth = (m_xMax - m_xMin + 1) * 16;
@ -676,8 +687,12 @@ void TileGenerator::renderMap()
const BlockPos &pos = *position;
if (currentPos.x != pos.x || currentPos.z != pos.z) {
area_rendered++;
if (currentPos.z != pos.z && currentPos.z != INT_MIN && m_shading)
renderShading(currentPos.z);
if (currentPos.z != INT_MIN) {
pushPixelBlockCache(currentPos.x, currentPos.z);
resetPixelBlockCache();
if (currentPos.z != pos.z && m_shading)
renderShading(currentPos.z);
}
if (progressIndicator && currentPos.z != pos.z)
cout << "Processing Z-coordinate: " << std::setw(5) << pos.z*16 << "\r" << std::flush;
for (int i = 0; i < 16; ++i) {
@ -782,8 +797,12 @@ void TileGenerator::renderMap()
}
}
}
if(currentPos.z != INT_MIN && m_shading)
renderShading(currentPos.z);
if (currentPos.z != INT_MIN) {
pushPixelBlockCache(currentPos.x, currentPos.z);
resetPixelBlockCache();
if(m_shading)
renderShading(currentPos.z);
}
if (verboseStatistics)
cout << "Statistics"
<< ": 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();
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;
Color col;
uint8_t th;
for (int z = 0; z < 16; ++z) {
int imageY = getImageY(zBegin + 15 - z);
for (int x = 0; x < 16; ++x) {
@ -814,10 +831,6 @@ inline void TileGenerator::renderMapBlock(const unsigned_string &mapBlock, const
continue;
}
int imageX = getImageX(xBegin + x);
if(m_drawAlpha) {
col = Color(0,0,0,0);
th = 0;
}
for (int y = maxY; y >= minY; --y) {
int position = x + (y << 4) + (z << 8);
int content = readBlockContent(mapData, version, position);
@ -832,26 +845,25 @@ inline void TileGenerator::renderMapBlock(const unsigned_string &mapBlock, const
if (color != m_colors.end()) {
const Color c = color->second.to_color();
if (m_drawAlpha) {
if (col.a == 0)
col = c;
else
col = mixColors(col, c);
if(col.a == 0xFF) {
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;
PixelCacheEntry pixel = PixelCacheEntry(color->second, pos.y * 16 + y);
m_pixelBlockCache[z][x].mixUnder(pixel);
if(pixel.alpha() == 0xff) {
m_readedPixels[z] |= (1 << x);
break;
}
} 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_readedPixels[z] |= (1 << x);
m_blockPixelAttributes.attribute(15 - z, xBegin + x).height = pos.y * 16 + y;
m_readedPixels[z] |= (1 << x);
m_blockPixelAttributes.attribute(15 - z, xBegin + x).height = pos.y * 16 + y;
break;
}
} else {
m_unknownNodes.insert(name);
continue;
}
break;
}
}
}

View File

@ -20,6 +20,7 @@
#include <string>
#include "PixelAttributes.h"
#include "Color.h"
#include "PixelCacheEntry.h"
#include "db.h"
#define MINETEST_MAPBLOCK_MIN (-2048)
@ -119,6 +120,8 @@ private:
void renderMap();
std::list<int> getZValueList() const;
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 renderShading(int zPos);
void renderScale();
@ -153,6 +156,7 @@ private:
DB *m_db;
gdImagePtr m_image;
PixelAttributes m_blockPixelAttributes;
PixelCacheEntry m_pixelBlockCache[16][16];
int m_xMin;
int m_xMax;
int m_zMin;