/* * ===================================================================== * Version: 1.0 * Created: 25.08.2012 10:55:27 * Author: Miroslav Bendík * Company: LinuxOS.sk * ===================================================================== */ #include #include #include #include "PixelAttributes.h" using namespace std; PixelAttribute::AlphaMixingMode PixelAttribute::m_mixMode = PixelAttribute::AlphaMixCumulative; PixelAttributes::PixelAttributes(): m_pixelAttributes(0) { } PixelAttributes::~PixelAttributes() { freeAttributes(); } void PixelAttributes::setParameters(int width, int lines, int nextY, int scale, bool defaultEmpty) { freeAttributes(); m_width = width + 1; // 1px gradient calculation m_previousLine = 0; m_firstLine = 1; m_lastLine = m_firstLine + lines - 1; m_emptyLine = m_lastLine + 1; m_lineCount = m_emptyLine + 1; m_firstY = 0; m_nextY = nextY; m_lastY = -1; m_firstUnshadedY = 0; m_scale = scale; m_pixelAttributes = new PixelAttribute *[m_lineCount]; if (!m_pixelAttributes) throw std::runtime_error("Failed to allocate memory for PixelAttributes"); for (int i = 0; i < m_lineCount; ++i) { m_pixelAttributes[i] = new PixelAttribute[m_width]; if (!m_pixelAttributes[i]) throw std::runtime_error("Failed to allocate memory for PixelAttributes"); } for (int i=0; i 0) { int i; for (i = m_previousLine; i + scroll <= m_lastLine; i++) { PixelAttribute *tmp; tmp = m_pixelAttributes[i]; m_pixelAttributes[i] = m_pixelAttributes[i + scroll]; m_pixelAttributes[i + scroll] = tmp; } size_t lineLength = m_width * sizeof(PixelAttribute); for (; i <= m_lastLine; ++i) { memcpy(m_pixelAttributes[i], m_pixelAttributes[m_emptyLine], lineLength); } m_firstY += scroll; m_nextY = m_firstY; m_firstUnshadedY -= scroll; if (m_firstUnshadedY < m_firstY) m_firstUnshadedY = m_firstY; } } void PixelAttributes::freeAttributes() { if (m_pixelAttributes) { for (int i = 0; i < m_lineCount; ++i) { if (m_pixelAttributes[i] != 0) { delete[] m_pixelAttributes[i]; } } delete[] m_pixelAttributes; m_pixelAttributes = 0; } } static inline double colorSafeBounds(double color) { if (color > 1) { return 1; } else if (color < 0) { return 0; } else { return color; } } void PixelAttributes::renderShading(double emphasis, bool drawAlpha) { int y; for (y = yCoord2Line(m_firstUnshadedY); y <= yCoord2Line(m_lastY); y++) { for (int x = 1; x < m_width; x++) { if (m_pixelAttributes[y][x].nextEmpty) { x += 16 / m_scale - 1; continue; } if (!m_pixelAttributes[y][x].isNormalized()) m_pixelAttributes[y][x].normalize(); if (!m_pixelAttributes[y][x].is_valid()) { if (x + 1 < m_width && !m_pixelAttributes[y][x + 1].isNormalized()) m_pixelAttributes[y][x + 1].normalize(); x++; continue; } if (!m_pixelAttributes[y - 1][x].is_valid() || !m_pixelAttributes[y][x - 1].is_valid()) continue; if (!m_pixelAttributes[y][x].m_a) continue; double h = m_pixelAttributes[y][x].m_h; double h1 = m_pixelAttributes[y][x - 1].m_a ? m_pixelAttributes[y][x - 1].m_h : h; double h2 = m_pixelAttributes[y - 1][x].m_a ? m_pixelAttributes[y - 1][x].m_h : h; double d = (h - h1) + (h - h2); if (d > 3) { d = 3; } d = d * 12 / 255 * emphasis; #define pixel (m_pixelAttributes[y][x]) //PixelAttribute &pixel = m_pixelAttributes[y][x]; if (drawAlpha) d = d * (1 - pixel.m_t); pixel.m_r = colorSafeBounds(pixel.m_r + d); pixel.m_g = colorSafeBounds(pixel.m_g + d); pixel.m_b = colorSafeBounds(pixel.m_b + d); #undef pixel } } m_firstUnshadedY = y - yCoord2Line(0); } // Meaning and usage of parameter 'n'. // // When n==0, all other values should be interpreted as // plain color / height / etc. // // When n is positive, some kind of pixel average is being // computed, and the other values represent a sum, with // n being the number of items summed. // // There is a twist when summing colors: Transparent colors // should not contribute the same amount to the final average // color as opaque colors do. // For that reason, the color values (r, g, b) are not simply // summed, but they are multiplied by their alpha values // first. // // Conversion from pixelattributes with n>0 to pixelattributes // with n==0 is performed as follows: // = / // = / n // = / n // = / n // n = 0 // The converse is (n would normally be set to 1): // = * // n = 1 // Color values with n>0 can be summed. // normalize() converts from n>0 to n==0 representation void PixelAttribute::normalize(double count, Color defColor) { if (!m_n) { // Already normalized return; } if (m_n < count) { m_r += (defColor.r / 255.0) * (defColor.a / 255.0) * (count - m_n); m_g += (defColor.g / 255.0) * (defColor.a / 255.0) * (count - m_n); m_b += (defColor.b / 255.0) * (defColor.a / 255.0) * (count - m_n); m_a += (defColor.a / 255.0) * (count - m_n); m_h *= double(count) / m_n; m_t *= double(count) / m_n; m_n = count; } if (m_n != 1) { m_r /= m_a; m_g /= m_a; m_b /= m_a; m_a /= m_n; m_t /= m_n; m_h /= m_n; } m_n = 0; } void PixelAttribute::add(const PixelAttribute &p) { if (!m_n) { m_r *= m_a; m_g *= m_a; m_b *= m_a; m_n = 1; } if (!is_valid()) { m_r = p.m_r; m_g = p.m_g; m_b = p.m_b; m_a = p.m_a; m_t = 0; m_h = p.m_h; m_n = p.m_n; } else if (!p.m_n) { m_r += p.m_r * p.m_a; m_g += p.m_g * p.m_a; m_b += p.m_b * p.m_a; m_a += p.m_a; m_t += p.m_t; m_h += p.m_h; m_n++; } else { m_r += p.m_r; m_g += p.m_g; m_b += p.m_b; m_a += p.m_a; m_t += p.m_t; m_h += p.m_h; m_n += p.m_n; } } void PixelAttribute::mixUnder(const PixelAttribute &p) { if (!is_valid() || m_a == 0) { if (!is_valid() || p.m_a != 0) { m_n = p.m_n; m_r = p.m_r; m_g = p.m_g; m_b = p.m_b; m_a = p.m_a; m_t = 0; } m_h = p.m_h; } else if ((m_mixMode & AlphaMixCumulative) == AlphaMixCumulative || (m_mixMode == AlphaMixAverage && p.m_a == 1)) { PixelAttribute pp(p); #ifdef DEBUG assert(pp.isNormalized()); #else if (!pp.isNormalized()) pp.normalize(); #endif if (!isNormalized()) normalize(); int prev_alpha = alpha(); m_r = (m_a * m_r + pp.m_a * (1 - m_a) * pp.m_r); m_g = (m_a * m_g + pp.m_a * (1 - m_a) * pp.m_g); m_b = (m_a * m_b + pp.m_a * (1 - m_a) * pp.m_b); m_a = (m_a + (1 - m_a) * pp.m_a); if (pp.m_a != 1) m_t = (m_t + pp.m_t) / 2; else m_h = pp.m_h; if ((m_mixMode & AlphaMixDarkenBit) && prev_alpha >= 254 && pp.alpha() < 255) { // Darken // Parameters make deep water look good :-) // (maybe this setting should be per-node-type, and obtained from the colors file ?) m_r = m_r * 0.95; m_g = m_g * 0.95; m_b = m_b * 0.95; } } #ifdef DEBUG else if (m_mixMode == AlphaMixAverage && p.m_a != 1) { #else else { #endif if (p.m_a == 1) normalize(); double h = p.m_h; double t = m_t; add(p); if (p.m_a == 1) { normalize(); m_t = t; m_a = 1; m_h = m_n * h; } } #ifdef DEBUG else { // Internal error assert(1 && m_mixMode); } #endif }