--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hostsupport/hostopenvg/src/src/riImage.h Wed Oct 06 17:59:01 2010 +0100
@@ -0,0 +1,1593 @@
+#ifndef __RIIMAGE_H
+#define __RIIMAGE_H
+
+/*------------------------------------------------------------------------
+ *
+ * OpenVG 1.1 Reference Implementation
+ * -----------------------------------
+ *
+ * Copyright (c) 2007 The Khronos Group Inc.
+ * Portions copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and /or associated documentation files
+ * (the "Materials "), to deal in the Materials without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Materials,
+ * and to permit persons to whom the Materials are furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Materials.
+ *
+ * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR
+ * THE USE OR OTHER DEALINGS IN THE MATERIALS.
+ *
+ *//**
+ * \file
+ * \brief Color and Image classes.
+ * \note
+ *//*-------------------------------------------------------------------*/
+
+#ifndef _OPENVG_H
+#include "VG/openvg.h"
+#endif
+
+#ifndef __RIMATH_H
+#include "riMath.h"
+#endif
+
+#ifndef __RIARRAY_H
+#include "riArray.h"
+#endif
+
+#include "sfAlphaRcp.h"
+#include "sfGammaLUT.h"
+#include "riUtils.h"
+
+//==============================================================================================
+
+namespace OpenVGRI
+{
+
+class VGContext;
+class DynamicBlitter;
+
+/*-------------------------------------------------------------------*//*!
+* \brief A class representing rectangles.
+* \param
+* \return
+* \note
+*//*-------------------------------------------------------------------*/
+
+class Rectangle
+{
+public:
+ Rectangle() : x(0), y(0), width(0), height(0) {}
+ Rectangle(int rx, int ry, int rw, int rh) : x(rx), y(ry), width(rw), height(rh) {}
+ void intersect(const Rectangle& r)
+ {
+ if(width >= 0 && r.width >= 0 && height >= 0 && r.height >= 0)
+ {
+ int x1 = RI_INT_MIN(RI_INT_ADDSATURATE(x, width), RI_INT_ADDSATURATE(r.x, r.width));
+ x = RI_INT_MAX(x, r.x);
+ width = RI_INT_MAX(x1 - x, 0);
+
+ int y1 = RI_INT_MIN(RI_INT_ADDSATURATE(y, height), RI_INT_ADDSATURATE(r.y, r.height));
+ y = RI_INT_MAX(y, r.y);
+ height = RI_INT_MAX(y1 - y, 0);
+ }
+ else
+ {
+ x = 0;
+ y = 0;
+ width = 0;
+ height = 0;
+ }
+ }
+ bool isEmpty() const { return width == 0 || height == 0; }
+
+ int x;
+ int y;
+ int width;
+ int height;
+};
+
+/*-------------------------------------------------------------------*//*!
+* \brief A class representing color for processing and converting it
+* to and from various surface formats.
+* \param
+* \return
+* \note
+*//*-------------------------------------------------------------------*/
+
+class Color
+{
+public:
+ enum FormatSize
+ {
+ SIZE_1 = 0,
+ SIZE_4 = 1,
+ SIZE_8 = 2,
+ SIZE_16 = 3,
+ SIZE_24 = 4,
+ SIZE_32 = 5
+ };
+
+ enum Shape
+ {
+ SHAPE_RGBA = 0,
+ SHAPE_RGBX = 1,
+ SHAPE_RGB = 2,
+ SHAPE_LA = 3,
+ SHAPE_L = 4,
+ SHAPE_A = 5,
+ SHAPE_ARGB = 6,
+ SHAPE_XRGB = 7,
+ SHAPE_AL = 8,
+ SHAPE_BGRA = 9,
+ SHAPE_BGRX = 10,
+ SHAPE_BGR = 11,
+ SHAPE_ABGR = 12,
+ SHAPE_XBGR = 13
+ };
+ enum InternalFormat
+ {
+ lRGBA = 0,
+ sRGBA = 1,
+ lRGBA_PRE = 2,
+ sRGBA_PRE = 3,
+ lLA = 4,
+ sLA = 5,
+ lLA_PRE = 6,
+ sLA_PRE = 7
+ };
+ enum FormatBits
+ {
+ NONLINEAR = (1<<0),
+ PREMULTIPLIED = (1<<1),
+ LUMINANCE = (1<<2)
+ };
+ struct SmallDescriptor
+ {
+ RIuint32 toUint32()
+ {
+ RIuint32 ret = 0;
+ ret = (RIuint32)size;
+ ret |= (RIuint32)shape << 3;
+ ret |= (RIuint32)internalFormat << (3 + 4);
+ return ret;
+ }
+ FormatSize size;
+ Shape shape;
+ InternalFormat internalFormat;
+ };
+ class Descriptor
+ {
+ public:
+ Descriptor() {};
+ RI_INLINE Descriptor(int dredBits, int dredShift, int dgreenBits, int dgreenShift, int dblueBits, int dblueShift, int dalphaBits, int dalphaShift, int dluminanceBits, int dluminanceShift, InternalFormat dinternalFormat, int dbpp, Shape shape);
+ RI_INLINE bool isNonlinear() const { return (internalFormat & NONLINEAR) ? true : false; }
+ RI_INLINE void setNonlinear(bool nonlinear);
+ RI_INLINE bool isPremultiplied() const { return (internalFormat & PREMULTIPLIED) ? true : false; }
+ RI_INLINE bool isLuminance() const { return (internalFormat & LUMINANCE) ? true : false; }
+ RI_INLINE bool isAlphaOnly() const { return (alphaBits && (redBits+greenBits+blueBits+luminanceBits) == 0) ? true : false; }
+ RI_INLINE bool isBW() const { return isLuminance() && (luminanceBits == 1); }
+ RI_INLINE bool hasAlpha() const { return alphaBits > 0; }
+ RI_INLINE bool operator==(const Descriptor& rhs) const;
+ RI_INLINE bool isShiftConversionToLower(const Descriptor& rhs) const;
+ RI_INLINE bool isShiftConversion(const Descriptor& rhs) const;
+ RI_INLINE bool isZeroConversion(const Descriptor& rhs) const;
+ RI_INLINE bool maybeUnsafe() const { return internalFormat & PREMULTIPLIED ? true : false; };
+ static RI_INLINE RIuint32 crossConvertToLower(RIuint32 c, const Descriptor& src, const Descriptor& dst);
+ void toSmallDescriptor(SmallDescriptor& smallDesc) const;
+ RI_INLINE RIuint32 toIndex() const;
+ static Descriptor getDummyDescriptor();
+ Shape getShape() const;
+
+ int redBits;
+ int redShift;
+ int greenBits;
+ int greenShift;
+ int blueBits;
+ int blueShift;
+ int alphaBits;
+ int alphaShift;
+ int luminanceBits;
+ int luminanceShift;
+ Shape shape;
+ VGImageFormat vgFormat; // \note Storage only
+ InternalFormat internalFormat;
+ int bitsPerPixel;
+ // Derived info:
+ int bytesPerPixel;
+ int maskBits;
+ int maskShift;
+ };
+
+ RI_INLINE Color() : r(0.0f), g(0.0f), b(0.0f), a(0.0f), m_format(sRGBA_PRE) {}
+ RI_INLINE Color(RIfloat cl, RIfloat ca, InternalFormat cs) : r(cl), g(cl), b(cl), a(ca), m_format(cs) { RI_ASSERT(cs == lLA || cs == sLA || cs == lLA_PRE || cs == sLA_PRE); }
+ RI_INLINE Color(RIfloat cr, RIfloat cg, RIfloat cb, RIfloat ca, InternalFormat cs) : r(cr), g(cg), b(cb), a(ca), m_format(cs) { RI_ASSERT(cs == lRGBA || cs == sRGBA || cs == lRGBA_PRE || cs == sRGBA_PRE || cs == lLA || cs == sLA || cs == lLA_PRE || cs == sLA_PRE); }
+ RI_INLINE Color(const Color& c) : r(c.r), g(c.g), b(c.b), a(c.a), m_format(c.m_format) {}
+ RI_INLINE Color& operator=(const Color&c) { r = c.r; g = c.g; b = c.b; a = c.a; m_format = c.m_format; return *this; }
+ RI_INLINE void operator*=(RIfloat f) { r *= f; g *= f; b *= f; a*= f; }
+ RI_INLINE void operator+=(const Color& c1) { RI_ASSERT(m_format == c1.getInternalFormat()); r += c1.r; g += c1.g; b += c1.b; a += c1.a; }
+ RI_INLINE void operator-=(const Color& c1) { RI_ASSERT(m_format == c1.getInternalFormat()); r -= c1.r; g -= c1.g; b -= c1.b; a -= c1.a; }
+
+ void set(RIfloat cl, RIfloat ca, InternalFormat cs) { RI_ASSERT(cs == lLA || cs == sLA || cs == lLA_PRE || cs == sLA_PRE); r = cl; g = cl; b = cl; a = ca; m_format = cs; }
+ void set(RIfloat cr, RIfloat cg, RIfloat cb, RIfloat ca, InternalFormat cs) { RI_ASSERT(cs == lRGBA || cs == sRGBA || cs == lRGBA_PRE || cs == sRGBA_PRE); r = cr; g = cg; b = cb; a = ca; m_format = cs; }
+ void unpack(unsigned int inputData, const Descriptor& inputDesc);
+ unsigned int pack(const Descriptor& outputDesc) const;
+ RI_INLINE InternalFormat getInternalFormat() const { return m_format; }
+
+ //clamps nonpremultiplied colors and alpha to [0,1] range, and premultiplied alpha to [0,1], colors to [0,a]
+ void clamp() { a = RI_CLAMP(a,0.0f,1.0f); RIfloat u = (m_format & PREMULTIPLIED) ? a : (RIfloat)1.0f; r = RI_CLAMP(r,0.0f,u); g = RI_CLAMP(g,0.0f,u); b = RI_CLAMP(b,0.0f,u); }
+ void convert(InternalFormat outputFormat);
+ void premultiply() { if(!(m_format & PREMULTIPLIED)) { r *= a; g *= a; b *= a; m_format = (InternalFormat)(m_format | PREMULTIPLIED); } }
+ void unpremultiply() { if(m_format & PREMULTIPLIED) { RIfloat ooa = (a != 0.0f) ? 1.0f/a : (RIfloat)0.0f; r *= ooa; g *= ooa; b *= ooa; m_format = (InternalFormat)(m_format & ~PREMULTIPLIED); } }
+ void luminanceToRGB() { if(m_format & LUMINANCE) { RI_ASSERT(r == g && g == b); m_format = (InternalFormat)(m_format & ~LUMINANCE); } }
+
+ bool isNonlinear() const { return (m_format & NONLINEAR) ? true : false; }
+ bool isPremultiplied() const { return (m_format & PREMULTIPLIED) ? true : false; }
+ bool isLuminance() const { return (m_format & LUMINANCE) ? true : false; }
+
+ RI_INLINE void assertConsistency() const;
+
+ // \note Why are these in the color class instead of descriptor?
+ static VGImageFormat descriptorToVGImageFormat(const Descriptor& desc);
+ RI_INLINE static Descriptor formatToDescriptorConst(VGImageFormat format);
+ static Descriptor formatToDescriptor(VGImageFormat format);
+ static bool isValidDescriptor(const Descriptor& desc);
+
+ RIfloat r;
+ RIfloat g;
+ RIfloat b;
+ RIfloat a;
+private:
+ InternalFormat m_format;
+};
+
+RI_INLINE Color::Descriptor::Descriptor(int dredBits, int dredShift, int dgreenBits, int dgreenShift, int dblueBits, int dblueShift, int dalphaBits, int dalphaShift, int dluminanceBits, int dluminanceShift, InternalFormat dinternalFormat, int dbpp, Shape shape) :
+ redBits(dredBits),
+ redShift(dredShift),
+ greenBits(dgreenBits),
+ greenShift(dgreenShift),
+ blueBits(dblueBits),
+ blueShift(dblueShift),
+ alphaBits(dalphaBits),
+ alphaShift(dalphaShift),
+ luminanceBits(dluminanceBits),
+ luminanceShift(dluminanceShift),
+ shape(shape),
+ internalFormat(dinternalFormat),
+ bitsPerPixel(dbpp)
+{
+ bytesPerPixel = bitsPerPixel / 8;
+
+ if (alphaBits)
+ {
+ maskBits = alphaBits;
+ maskShift = alphaShift;
+ }
+ else if (!this->isLuminance())
+ {
+ maskBits = redBits;
+ maskShift = redShift;
+ }
+ else
+ {
+ maskBits = luminanceBits;
+ maskShift = luminanceShift;
+ }
+ RI_ASSERT(getShape() == shape);
+}
+
+RI_INLINE void Color::Descriptor::setNonlinear(bool nonlinear)
+{
+ if (nonlinear)
+ internalFormat = (InternalFormat)(((RIuint32)internalFormat)|NONLINEAR);
+ else
+ internalFormat = (InternalFormat)(((RIuint32)internalFormat)&(~NONLINEAR));
+}
+
+/**
+ * \brief Creates a pixel format descriptor out of VGImageFormat
+ * \todo The formats without alpha were non-premultiplied in the reference
+ * implementation, but wouldn't it make more sense to consider them
+ * premultiplied? This would make sense at least when blitting to
+ * windows, etc., where the output color should have the alpha
+ * multiplied "in".
+ */
+RI_INLINE Color::Descriptor Color::formatToDescriptorConst(VGImageFormat format)
+{
+ switch(format)
+ {
+ case VG_sRGBX_8888:
+ return Color::Descriptor(8, 24, 8, 16, 8, 8, 0, 0, 0, 0, Color::sRGBA, 32, SHAPE_RGBX);
+ case VG_sRGBA_8888:
+ return Color::Descriptor(8, 24, 8, 16, 8, 8, 8, 0, 0, 0, Color::sRGBA, 32, SHAPE_RGBA);
+ case VG_sRGBA_8888_PRE:
+ return Color::Descriptor(8, 24, 8, 16, 8, 8, 8, 0, 0, 0, Color::sRGBA_PRE, 32, SHAPE_RGBA);
+ case VG_sRGB_565:
+ return Color::Descriptor(5, 11, 6, 5, 5, 0, 0, 0, 0, 0, Color::sRGBA, 16, SHAPE_RGB);
+ case VG_sRGBA_5551:
+ return Color::Descriptor(5, 11, 5, 6, 5, 1, 1, 0, 0, 0, Color::sRGBA, 16, SHAPE_RGBA);
+ case VG_sRGBA_4444:
+ return Color::Descriptor(4, 12, 4, 8, 4, 4, 4, 0, 0, 0, Color::sRGBA, 16, SHAPE_RGBA);
+ case VG_sL_8:
+ return Color::Descriptor(0, 0, 0, 0, 0, 0, 0, 0, 8, 0, Color::sLA, 8, SHAPE_L);
+ case VG_lRGBX_8888:
+ return Color::Descriptor(8, 24, 8, 16, 8, 8, 0, 0, 0, 0, Color::lRGBA, 32, SHAPE_RGBX);
+ case VG_lRGBA_8888:
+ return Color::Descriptor(8, 24, 8, 16, 8, 8, 8, 0, 0, 0, Color::lRGBA, 32, SHAPE_RGBA);
+ case VG_lRGBA_8888_PRE:
+ return Color::Descriptor(8, 24, 8, 16, 8, 8, 8, 0, 0, 0, Color::lRGBA_PRE, 32, SHAPE_RGBA);
+ case VG_lL_8:
+ return Color::Descriptor(0, 0, 0, 0, 0, 0, 0, 0, 8, 0, Color::lLA, 8, SHAPE_L);
+ case VG_A_8:
+ return Color::Descriptor(0, 0, 0, 0, 0, 0, 8, 0, 0, 0, Color::lRGBA, 8, SHAPE_A);
+ case VG_BW_1:
+ return Color::Descriptor(0, 0, 0, 0, 0, 0, 0, 0, 1, 0, Color::lLA, 1, SHAPE_L);
+ case VG_A_1:
+ return Color::Descriptor(0, 0, 0, 0, 0, 0, 1, 0, 0, 0, Color::lRGBA, 1, SHAPE_A);
+ case VG_A_4:
+ return Color::Descriptor(0, 0, 0, 0, 0, 0, 4, 0, 0, 0, Color::lRGBA, 4, SHAPE_A);
+
+ /* {A,X}RGB channel ordering */
+ case VG_sXRGB_8888:
+ return Color::Descriptor(8, 16, 8, 8, 8, 0, 0, 0, 0, 0, Color::sRGBA, 32, SHAPE_XRGB);
+ case VG_sARGB_8888:
+ return Color::Descriptor(8, 16, 8, 8, 8, 0, 8, 24, 0, 0, Color::sRGBA, 32, SHAPE_ARGB);
+ case VG_sARGB_8888_PRE:
+ return Color::Descriptor(8, 16, 8, 8, 8, 0, 8, 24, 0, 0, Color::sRGBA_PRE, 32, SHAPE_ARGB);
+ case VG_sARGB_1555:
+ return Color::Descriptor(5, 10, 5, 5, 5, 0, 1, 15, 0, 0, Color::sRGBA, 16, SHAPE_ARGB);
+ case VG_sARGB_4444:
+ return Color::Descriptor(4, 8, 4, 4, 4, 0, 4, 12, 0, 0, Color::sRGBA, 16, SHAPE_ARGB);
+ case VG_lXRGB_8888:
+ return Color::Descriptor(8, 16, 8, 8, 8, 0, 0, 0, 0, 0, Color::lRGBA, 32, SHAPE_XRGB);
+ case VG_lARGB_8888:
+ return Color::Descriptor(8, 16, 8, 8, 8, 0, 8, 24, 0, 0, Color::lRGBA, 32, SHAPE_ARGB);
+ case VG_lARGB_8888_PRE:
+ return Color::Descriptor(8, 16, 8, 8, 8, 0, 8, 24, 0, 0, Color::lRGBA_PRE, 32, SHAPE_ARGB);
+
+ /* BGR{A,X} channel ordering */
+ case VG_sBGRX_8888:
+ return Color::Descriptor(8, 8, 8, 16, 8, 24, 0, 0, 0, 0, Color::sRGBA, 32, SHAPE_BGRX);
+ case VG_sBGRA_8888:
+ return Color::Descriptor(8, 8, 8, 16, 8, 24, 8, 0, 0, 0, Color::sRGBA, 32, SHAPE_BGRA);
+ case VG_sBGRA_8888_PRE:
+ return Color::Descriptor(8, 8, 8, 16, 8, 24, 8, 0, 0, 0, Color::sRGBA_PRE, 32, SHAPE_BGRA);
+ case VG_sBGR_565:
+ return Color::Descriptor(5, 0, 6, 5, 5, 11, 0, 0, 0, 0, Color::sRGBA, 16, SHAPE_BGR);
+ case VG_sBGRA_5551:
+ return Color::Descriptor(5, 1, 5, 6, 5, 11, 1, 0, 0, 0, Color::sRGBA, 16, SHAPE_BGRA);
+ case VG_sBGRA_4444:
+ return Color::Descriptor(4, 4, 4, 8, 4, 12, 4, 0, 0, 0, Color::sRGBA, 16, SHAPE_BGRA);
+ case VG_lBGRX_8888:
+ return Color::Descriptor(8, 8, 8, 16, 8, 24, 0, 0, 0, 0, Color::lRGBA, 32, SHAPE_BGRX);
+ case VG_lBGRA_8888:
+ return Color::Descriptor(8, 8, 8, 16, 8, 24, 8, 0, 0, 0, Color::lRGBA, 32, SHAPE_BGRA);
+ case VG_lBGRA_8888_PRE:
+ return Color::Descriptor(8, 8, 8, 16, 8, 24, 8, 0, 0, 0, Color::lRGBA_PRE, 32, SHAPE_BGRA);
+
+ /* {A,X}BGR channel ordering */
+ case VG_sXBGR_8888:
+ return Color::Descriptor(8, 0, 8, 8, 8, 16, 0, 0, 0, 0, Color::sRGBA, 32, SHAPE_XBGR);
+ case VG_sABGR_8888:
+ return Color::Descriptor(8, 0, 8, 8, 8, 16, 8, 24, 0, 0, Color::sRGBA, 32, SHAPE_ABGR);
+ case VG_sABGR_8888_PRE:
+ return Color::Descriptor(8, 0, 8, 8, 8, 16, 8, 24, 0, 0, Color::sRGBA_PRE, 32, SHAPE_ABGR);
+ case VG_sABGR_1555:
+ return Color::Descriptor(5, 0, 5, 5, 5, 10, 1, 15, 0, 0, Color::sRGBA, 16, SHAPE_ABGR);
+ case VG_sABGR_4444:
+ return Color::Descriptor(4, 0, 4, 4, 4, 8, 4, 12, 0, 0, Color::sRGBA, 16, SHAPE_ABGR);
+ case VG_lXBGR_8888:
+ return Color::Descriptor(8, 0, 8, 8, 8, 16, 0, 0, 0, 0, Color::lRGBA, 32, SHAPE_XBGR);
+ case VG_lABGR_8888:
+ return Color::Descriptor(8, 0, 8, 8, 8, 16, 8, 24, 0, 0, Color::lRGBA, 32, SHAPE_ABGR);
+ default:
+ //case VG_lABGR_8888_PRE:
+ RI_ASSERT(format == VG_lABGR_8888_PRE);
+ return Color::Descriptor(8, 0, 8, 8, 8, 16, 8, 24, 0, 0, Color::lRGBA_PRE, 32, SHAPE_ABGR);
+ }
+}
+
+RI_INLINE bool Color::Descriptor::operator==(const Descriptor& rhs) const
+{
+ return memcmp(this, &rhs, sizeof(Descriptor)) ? false : true;
+}
+
+RI_INLINE bool Color::Descriptor::isZeroConversion(const Descriptor& rhs) const
+{
+ return (shape == rhs.shape) &&
+ (internalFormat == rhs.internalFormat) &&
+ (redBits == rhs.redBits) &&
+ (greenBits == rhs.greenBits) &&
+ (blueBits == rhs.blueBits) &&
+ (alphaBits == rhs.alphaBits) &&
+ (luminanceBits == rhs.luminanceBits);
+}
+
+RI_INLINE bool Color::Descriptor::isShiftConversion(const Descriptor& rhs) const
+{
+ // \note BW conversion is always forced to full at the moment.
+ if (isBW() != rhs.isBW())
+ return false;
+
+ return (isPremultiplied() == rhs.isPremultiplied())
+ && (isNonlinear() == rhs.isNonlinear())
+ && (isLuminance() == rhs.isLuminance());
+}
+
+RI_INLINE bool Color::Descriptor::isShiftConversionToLower(const Descriptor& rhs) const
+{
+ // \note BW conversion is always forced to full at the moment.
+ if (isBW() != rhs.isBW())
+ return false;
+ // \note Mask bits are not checked because they are derived information.
+ return (isShiftConversion(rhs)
+ && (rhs.redBits <= redBits)
+ && (rhs.greenBits <= greenBits)
+ && (rhs.blueBits <= blueBits)
+ && (rhs.alphaBits <= alphaBits)
+ && (rhs.luminanceBits <= luminanceBits));
+
+}
+
+/**
+ * \brief In-place conversion of packed color to lower bit-depth
+ * \param c Input packed color
+ * \param src Source color descriptor
+ * \param dst Destination color descriptor
+ */
+RI_INLINE RIuint32 Color::Descriptor::crossConvertToLower(RIuint32 c, const Descriptor& src, const Descriptor& dst)
+{
+ RIuint32 r = 0;
+
+ RI_ASSERT(dst.redBits <= src.redBits);
+ RI_ASSERT(dst.greenBits <= src.greenBits);
+ RI_ASSERT(dst.blueBits <= src.blueBits);
+ RI_ASSERT(dst.alphaBits <= src.alphaBits);
+
+ if (src.isLuminance())
+ {
+ RI_ASSERT(dst.isLuminance());
+ r = ((c >> (src.luminanceShift + src.luminanceBits - dst.luminanceBits)) & ((1u<<dst.luminanceBits)-1)) << dst.luminanceShift;
+ } else
+ {
+ r = ((c >> (src.redShift + src.redBits - dst.redBits)) & ((1u<<dst.redBits)-1)) << dst.redShift;
+ r |= ((c >> (src.greenShift + src.greenBits - dst.greenBits)) & ((1u<<dst.greenBits)-1)) << dst.greenShift;
+ r |= ((c >> (src.blueShift + src.blueBits - dst.blueBits)) & ((1u<<dst.blueBits)-1)) << dst.blueShift;
+ }
+
+ if (src.hasAlpha())
+ {
+ if (dst.hasAlpha())
+ r |= ((c >> (src.alphaShift + src.alphaBits - dst.alphaBits)) & ((1u<<dst.alphaBits)-1)) << dst.alphaShift;
+ else
+ {
+ // Make sure that the alpha is applied to the color if doing only a shift conversion.
+ RI_ASSERT(src.isPremultiplied() == dst.isPremultiplied());
+ }
+ }
+
+ return r;
+}
+
+RI_INLINE RIuint32 Color::Descriptor::toIndex() const
+{
+ SmallDescriptor smallDesc;
+ toSmallDescriptor(smallDesc);
+ return smallDesc.toUint32();
+}
+
+RI_INLINE Color operator*(const Color& c, RIfloat f) { return Color(c.r*f, c.g*f, c.b*f, c.a*f, c.getInternalFormat()); }
+RI_INLINE Color operator*(RIfloat f, const Color& c) { return Color(c.r*f, c.g*f, c.b*f, c.a*f, c.getInternalFormat()); }
+RI_INLINE Color operator+(const Color& c0, const Color& c1) { RI_ASSERT(c0.getInternalFormat() == c1.getInternalFormat()); return Color(c0.r+c1.r, c0.g+c1.g, c0.b+c1.b, c0.a+c1.a, c0.getInternalFormat()); }
+RI_INLINE Color operator-(const Color& c0, const Color& c1) { RI_ASSERT(c0.getInternalFormat() == c1.getInternalFormat()); return Color(c0.r-c1.r, c0.g-c1.g, c0.b-c1.b, c0.a-c1.a, c0.getInternalFormat()); }
+RI_INLINE void Color::assertConsistency() const
+{
+ RI_ASSERT(r >= 0.0f && r <= 1.0f);
+ RI_ASSERT(g >= 0.0f && g <= 1.0f);
+ RI_ASSERT(b >= 0.0f && b <= 1.0f);
+ RI_ASSERT(a >= 0.0f && a <= 1.0f);
+ RI_ASSERT(!isPremultiplied() || (r <= a && g <= a && b <= a)); //premultiplied colors must have color channels less than or equal to alpha
+ RI_ASSERT((isLuminance() && r == g && r == b) || !isLuminance()); //if luminance, r=g=b
+}
+
+class IntegerColor
+{
+public:
+
+ IntegerColor() {r = g = b = a = 0;}
+ IntegerColor(const Color& color);
+
+ RI_INLINE IntegerColor(RIuint32 packedColor, const Color::Descriptor& desc) { fromPackedColor(packedColor, desc); }
+ RI_INLINE IntegerColor(RIuint32 cr, RIuint32 cg, RIuint32 cb, RIuint32 ca) { r = cr; g = cg; b = cb; a = ca; }
+ RI_INLINE void asFixedPoint(const Color& color);
+ RI_INLINE void fromPackedColor(RIuint32 packedColor, const Color::Descriptor& desc);
+ RI_INLINE void expandColor(const Color::Descriptor& desc);
+ RI_INLINE void truncateColor(const Color::Descriptor& desc);
+ RI_INLINE void clampToAlpha();
+ RI_INLINE RIuint32 getPackedColor(const Color::Descriptor& desc) const;
+ RI_INLINE RIuint32 getPackedMaskColor(const Color::Descriptor& desc) const;
+ RI_INLINE void premultiply(bool luminance = false);
+ RI_INLINE void unpremultiply(bool luminance = false);
+ //RI_INLINE void linearToGamma(bool luminance, bool premultipliedIn, bool premultipliedOut);
+ RI_INLINE void linearToGamma(bool luminance = false);
+ RI_INLINE void gammaToLinear(bool luminance = false);
+ RI_INLINE void fromPackedMask(RIuint32 packedColor, const Color::Descriptor& desc);
+ RI_INLINE void expandMask(const Color::Descriptor& desc);
+ RI_INLINE void truncateMask(const Color::Descriptor& desc);
+ RI_INLINE void fullLuminanceToRGB(bool premultipliedIn, bool gammaIn, bool premultipliedOut, bool gammaOut);
+ RI_INLINE void fullRGBToLuminance(bool premultipliedIn, bool gammaIn, bool premultipliedOut, bool gammaOut);
+ RI_INLINE void luminanceToRGB();
+ RI_INLINE void rgbToLuminance();
+ RI_INLINE void convertToFrom(const Color::Descriptor& dst, const Color::Descriptor& src, bool srcIsMask);
+
+ RI_INLINE static IntegerColor linearBlendNS(const IntegerColor& c0, const IntegerColor& c1, int k);
+
+ RIuint32 r;
+ RIuint32 g;
+ RIuint32 b;
+ RIuint32 a;
+
+};
+
+/**
+ * \brief Blend two colors linearly. The output will not be scaled into original range.
+ * \param k Blend coefficient. Must be [0..255] for correct results.
+ * \todo Parameterize against bits in k? To perform well, that setup must be compiled rt.
+ */
+RI_INLINE IntegerColor IntegerColor::linearBlendNS(const IntegerColor& c0, const IntegerColor& c1, int k)
+{
+ RI_ASSERT(k >= 0 && k <= 255);
+ IntegerColor ret;
+ RIuint32 ik = 255 - k;
+
+ ret.r = ik * c0.r + k * c1.r;
+ ret.g = ik * c0.g + k * c1.g;
+ ret.b = ik * c0.b + k * c1.b;
+ ret.a = ik * c0.a + k * c1.a;
+
+ return ret;
+}
+
+/**
+ * \note Assumes that each individual component is in proper range (usually indicated by the
+ * corresponding shift).
+ */
+RI_INLINE RIuint32 packRGBAInteger(RIuint32 cr, int rs, RIuint32 cg, int gs, RIuint32 cb, int bs, RIuint32 ca, int as)
+{
+ return (cr << rs) | (cg << gs) | (cb << bs) | (ca << as);
+}
+
+/**
+ * \brief Packs a color into RIuint32.
+ * \note The color must have been truncated to contain correct amount of bits per channel
+ * \note This function is efficient only if runtime compilation is used.
+ */
+RI_INLINE RIuint32 IntegerColor::getPackedColor(const Color::Descriptor& desc) const
+{
+ RIuint32 res = 0;
+ if (desc.luminanceBits)
+ {
+ RI_ASSERT(desc.redBits == 0 && desc.greenBits == 0 && desc.blueBits == 0);
+ RI_ASSERT(r < (1u<<desc.luminanceBits));
+ res = r << desc.luminanceShift;
+ }
+ else if (desc.redBits)
+ {
+ RI_ASSERT(r < (1u<<desc.redBits));
+ res = r << desc.redShift;
+ if (desc.greenBits)
+ {
+ RI_ASSERT(desc.blueBits);
+ RI_ASSERT(g < (1u<<desc.greenBits));
+ RI_ASSERT(b < (1u<<desc.blueBits));
+ res |= g << desc.greenShift;
+ res |= b << desc.blueShift;
+ }
+ }
+
+ if (desc.alphaBits)
+ {
+ RI_ASSERT(a < (1u<<desc.alphaBits));
+ res |= a << desc.alphaShift;
+ }
+
+ return res;
+}
+
+RI_INLINE RIuint32 IntegerColor::getPackedMaskColor(const Color::Descriptor& desc) const
+{
+ if (desc.alphaBits)
+ return packRGBAInteger(0, desc.redShift, 0, desc.greenShift, 0, desc.blueShift, a, desc.alphaShift);
+ else if(desc.redBits)
+ return packRGBAInteger(a, desc.redShift, 0, desc.greenShift, 0, desc.blueShift, 0, desc.alphaShift);
+ else
+ {
+ RI_ASSERT(desc.luminanceBits);
+ return packRGBAInteger(a, desc.luminanceBits, 0, desc.greenShift, 0, desc.blueShift, 0, desc.alphaShift);
+ }
+
+}
+
+RI_INLINE void IntegerColor::premultiply(bool luminance)
+{
+ // \todo Check the round!!!
+ RIuint32 fxa = a + (a>>7);
+ r = (r * fxa); r = (r + (1<<7))>>8;
+
+ if (!luminance)
+ {
+ g = (g * fxa); g = (g + (1<<7))>>8;
+ b = (b * fxa); b = (b + (1<<7))>>8;
+ }
+}
+
+RI_INLINE void IntegerColor::unpremultiply(bool luminance)
+{
+ RI_ASSERT(a <= 255);
+
+ RIuint32 rcp = sc_alphaRcp[a];
+ r = (r * rcp) >> 8;
+
+ if (!luminance)
+ {
+ g = (g * rcp) >> 8;
+ b = (b * rcp) >> 8;
+ }
+}
+
+RI_INLINE void IntegerColor::linearToGamma(bool luminance)
+{
+ RI_ASSERT(r <= 255 && g <= 255 && b <= 255 && a <= 255);
+
+ r = sc_lRGB_to_sRGB[r];
+
+ if (!luminance)
+ {
+ g = sc_lRGB_to_sRGB[g];
+ b = sc_lRGB_to_sRGB[b];
+ }
+
+ // \note Alpha is _not_ converted and it must be considered linear always
+}
+
+RI_INLINE void IntegerColor::gammaToLinear(bool luminance)
+{
+ RI_ASSERT(r <= 255 && g <= 255 && b <= 255 && a <= 255);
+
+ r = sc_sRGB_to_lRGB[r];
+ if (!luminance)
+ {
+ g = sc_sRGB_to_lRGB[g];
+ b = sc_sRGB_to_lRGB[b];
+ }
+
+ // \note Alpha is _not_ converted and it must be considered linear always
+}
+
+RI_INLINE void IntegerColor::asFixedPoint(const Color& color)
+{
+ r = (RIuint32)(color.r * 256.0f + 0.5f);
+ g = (RIuint32)(color.g * 256.0f + 0.5f);
+ b = (RIuint32)(color.b * 256.0f + 0.5f);
+ a = (RIuint32)(color.a * 256.0f + 0.5f);
+}
+
+RI_INLINE void IntegerColor::fromPackedColor(RIuint32 packedColor, const Color::Descriptor& desc)
+{
+ /* \note Expand MUST be done separately! */
+
+ if (desc.luminanceBits)
+ {
+ r = (packedColor >> desc.luminanceShift) & ((1u << desc.luminanceBits)-1);
+ g = b = r;
+ }
+ else
+ {
+ r = (packedColor >> desc.redShift) & ((1u << desc.redBits)-1);
+ g = (packedColor >> desc.greenShift) & ((1u << desc.greenBits)-1);
+ b = (packedColor >> desc.blueShift) & ((1u << desc.blueBits)-1);
+ }
+
+ if (desc.alphaBits)
+ a = (packedColor >> desc.alphaShift) & ((1u << desc.alphaBits)-1);
+ else
+ a = 255;
+}
+
+/**
+ * \brief Expand color to larger (or same) bit depth as in the OpenVG specification.
+ * \todo 1 and 2 bpp!
+ */
+RI_INLINE RIuint32 expandComponent(RIuint32 c, RIuint32 srcBits)
+{
+ const RIuint32 destBits = 8;
+ RI_ASSERT(destBits >= srcBits);
+
+ if (!srcBits) return 0;
+
+ if (srcBits == destBits) return c;
+
+ switch (srcBits)
+ {
+ case 6:
+ return (c << 2) | (c >> 4);
+ case 5:
+ return (c << 3) | (c >> 2);
+ case 4:
+ return (c << 4) | c;
+ case 2:
+ return c | (c << 2) | (c << 4) | (c << 6);
+ default:
+ RI_ASSERT(srcBits == 1);
+ if (c) return 0xff;
+ return 0;
+ }
+}
+
+/**
+ * \brief Expands integer color representation to internal format (8-bits per component atm.).
+ * \todo Do nothing when bits == 8.
+ */
+RI_INLINE void IntegerColor::expandColor(const Color::Descriptor& desc)
+{
+ if (desc.luminanceBits)
+ {
+ r = expandComponent(r, desc.luminanceBits);
+ g = b = r;
+ a = 255;
+ } else
+ {
+ if (desc.redBits < 8 || desc.luminanceBits < 8)
+ r = expandComponent(r, desc.redBits);
+ if (desc.greenBits < 8)
+ g = expandComponent(g, desc.greenBits);
+ if (desc.blueBits < 8)
+ b = expandComponent(b, desc.blueBits);
+ }
+
+ if (desc.alphaBits && desc.alphaBits < 8)
+ a = expandComponent(a, desc.alphaBits);
+
+ if (desc.isAlphaOnly())
+ {
+ if (!desc.isPremultiplied())
+ r = g = b = 255;
+ else
+ r = g = b = a;
+ }
+}
+
+/**
+ * \brief Convert IntegerColor components to destination bitdepth (from internal) by
+ * shifting. Rounding does not take place.
+ */
+RI_INLINE void IntegerColor::truncateColor(const Color::Descriptor& desc)
+{
+ if (desc.luminanceBits)
+ {
+ RI_ASSERT(desc.redBits == 0 && desc.greenBits == 0 && desc.blueBits == 0);
+ if (desc.luminanceBits == 1)
+ {
+ // Round the 1-bit case a bit better?
+ r = (r + 128)>>8;
+ } else if (desc.luminanceBits < 8)
+ r >>= (8 - desc.luminanceBits);
+ }
+ else
+ {
+ if (desc.redBits < 8)
+ r >>= (8 - desc.redBits);
+ if (desc.greenBits < 8)
+ g >>= (8 - desc.greenBits);
+ if (desc.blueBits < 8)
+ b >>= (8 - desc.blueBits);
+ }
+
+ if (desc.alphaBits < 8)
+ {
+ if (desc.alphaBits == 1)
+ a = (a+128)>>8;
+ else
+ a >>= (8 - desc.alphaBits);
+ }
+}
+
+RI_INLINE void IntegerColor::truncateMask(const Color::Descriptor& desc)
+{
+ if (desc.redBits < 8 || desc.luminanceBits < 8)
+ r >>= (8 - desc.maskBits);
+ if (desc.greenBits < 8)
+ g >>= (8 - desc.maskBits);
+ if (desc.blueBits < 8)
+ b >>= (8 - desc.maskBits);
+ if (desc.alphaBits < 8)
+ a >>= (8 - desc.maskBits);
+}
+
+RI_INLINE void IntegerColor::clampToAlpha()
+{
+ if (r > a) r = a;
+ if (g > a) g = a;
+ if (b > a) b = a;
+}
+
+RI_INLINE void IntegerColor::fromPackedMask(RIuint32 packedMask, const Color::Descriptor& desc)
+{
+ RI_ASSERT(desc.maskBits);
+ a = (packedMask >> desc.maskShift) & ((1u << desc.maskBits)-1);
+}
+
+RI_INLINE void IntegerColor::expandMask(const Color::Descriptor& desc)
+{
+ a = expandComponent(a, desc.maskBits);
+ r = g = b = a;
+}
+
+#if 0
+RI_INLINE void IntegerColor::truncateMask(const Color::Descriptor& desc)
+{
+ a >>= (8 - desc.maskBits);
+}
+#endif
+
+RI_INLINE void IntegerColor::fullLuminanceToRGB(bool premultipliedIn, bool gammaIn, bool premultipliedOut, bool gammaOut)
+{
+ if (premultipliedIn)
+ unpremultiply();
+
+ luminanceToRGB();
+
+ if (gammaIn != gammaOut)
+ {
+ if (gammaIn)
+ gammaToLinear();
+ else
+ linearToGamma();
+ }
+
+ if (premultipliedOut)
+ premultiply();
+
+}
+
+RI_INLINE void IntegerColor::fullRGBToLuminance(bool premultipliedIn, bool gammaIn, bool premultipliedOut, bool gammaOut)
+{
+ if (premultipliedIn)
+ unpremultiply();
+
+ if (gammaIn)
+ gammaToLinear();
+
+ rgbToLuminance();
+
+ if (gammaOut)
+ linearToGamma();
+
+ if (premultipliedOut)
+ premultiply();
+
+}
+
+
+// \todo This should not be needed (only r-channel is used anyway)
+RI_INLINE void IntegerColor::luminanceToRGB()
+{
+ g = b = r;
+}
+
+// \todo Only write to R!
+RI_INLINE void IntegerColor::rgbToLuminance()
+{
+ enum { Rx = 871, Gx = 2929, Bx = 296, Bits = 12 };
+ //enum { Rx = 54, Gx = 183, Bx = 18, Bits = 8 };
+ RIuint32 l = Rx * r + Gx * g + Bx * b;
+ r = g = b = l >> Bits;
+}
+
+#if 0
+RI_INLINE void IntegerColor::convertFromInternal(const Color::Descriptor& dst)
+{
+}
+#endif
+
+/**
+ * \brief Convert color from one format to another using integer operations.
+ * \note Currently expands the color to intermediate format first (8 bits
+ * per component.
+ */
+RI_INLINE void IntegerColor::convertToFrom(const Color::Descriptor& dst, const Color::Descriptor& src, bool srcIsMask)
+{
+ if (src.isZeroConversion(dst))
+ return;
+
+ if (src.isShiftConversionToLower(dst))
+ {
+ if (dst.luminanceBits)
+ {
+ if (dst.luminanceBits == 1)
+ {
+ RI_ASSERT(src.luminanceBits == 8);
+ r = (r + 128)>>8;
+ }
+ else
+ r = r >> (src.luminanceBits - dst.luminanceBits);
+ } else
+ {
+ r = r >> (src.redBits - dst.redBits);
+ g = g >> (src.greenBits - dst.greenBits);
+ b = b >> (src.blueBits - dst.blueBits);
+ }
+ if (dst.alphaBits)
+ {
+ //a = (a+128)>>8;
+ if (dst.alphaBits == 1)
+ a = (a+(1<<(src.alphaBits-1)))>>src.alphaBits;
+ else
+ a = a >> (src.alphaBits - dst.alphaBits);
+ }
+
+ return;
+ }
+
+ if (!srcIsMask)
+ expandColor(src);
+ else
+ expandMask(src);
+
+
+ if (dst.isLuminance() != src.isLuminance())
+ {
+ if (src.isLuminance())
+ fullLuminanceToRGB(src.isPremultiplied(), src.isNonlinear(), dst.isPremultiplied(), dst.isNonlinear());
+ else
+ fullRGBToLuminance(src.isPremultiplied(), src.isNonlinear(), dst.isPremultiplied(), dst.isNonlinear());
+ }
+ else if (dst.isNonlinear() != src.isNonlinear())
+ {
+ // No luminance/rgb change.
+ // Change of gamma requires unpremultiplication:
+ if (src.isPremultiplied() && !(src.isAlphaOnly()))
+ unpremultiply();
+
+ if (src.isNonlinear())
+ gammaToLinear(src.isLuminance());
+ else
+ linearToGamma(src.isLuminance());
+
+ if (dst.isPremultiplied() && !(dst.isAlphaOnly()))
+ premultiply();
+ }
+ else
+ if ((dst.isPremultiplied() != src.isPremultiplied()) && !(dst.isAlphaOnly() || dst.isAlphaOnly()))
+ {
+ // \todo Make sure non-alpha formats are properly handled.
+ if (src.isPremultiplied())
+ unpremultiply(dst.isLuminance());
+ else
+ premultiply(dst.isLuminance());
+ }
+
+ truncateColor(dst);
+}
+
+//==============================================================================================
+
+/*-------------------------------------------------------------------*//*!
+* \brief Storage and operations for VGImage.
+* \param
+* \return
+* \note
+*//*-------------------------------------------------------------------*/
+
+class Surface;
+class Image
+{
+public:
+ Image(const Color::Descriptor& desc, int width, int height, VGbitfield allowedQuality); //throws bad_alloc
+ //use data from a memory buffer. NOTE: data is not copied, so it is user's responsibility to make sure the data remains valid while the Image is in use.
+ Image(const Color::Descriptor& desc, int width, int height, int stride, RIuint8* data); //throws bad_alloc
+ //child image constructor
+ Image(Image* parent, int x, int y, int width, int height); //throws bad_alloc
+ ~Image();
+
+ const Color::Descriptor& getDescriptor() const { return m_desc; }
+ int getWidth() const { return m_width; }
+ int getHeight() const { return m_height; }
+ int getStride() const { return m_stride; }
+ Image* getParent() const { return m_parent; }
+ VGbitfield getAllowedQuality() const { return m_allowedQuality; }
+ void addInUse() { m_inUse++; }
+ void removeInUse() { RI_ASSERT(m_inUse > 0); m_inUse--; }
+ int isInUse() const { return m_inUse; }
+ RIuint8* getData() const { return m_data; }
+ void addReference() { m_referenceCount++; }
+ int removeReference() { m_referenceCount--; RI_ASSERT(m_referenceCount >= 0); return m_referenceCount; }
+ bool overlaps(const Image* src) const;
+ void setUnsafe(bool unsafe) { if (unsafe && m_desc.maybeUnsafe()) m_unsafeData = unsafe; else m_unsafeData = false; }
+ bool isUnsafe() const { return m_unsafeData; }
+
+ void clear(const Color& clearColor, int x, int y, int w, int h);
+ void blit(VGContext* context, const Image* src, int sx, int sy, int dx, int dy, int w, int h, Array<Rectangle>* scissors = NULL, bool dither = false); //throws bad_alloc
+
+ RI_INLINE static const void* incrementPointer(const void* ptr, int bpp, RIint32 x);
+ RI_INLINE static void* calculateAddress(const void* basePtr, int bpp, int x, int y, int stride);
+
+ static RI_INLINE RIuint32 readPackedPixelFromAddress(const void *ptr, int bpp, int x);
+ static RI_INLINE void writePackedPixelToAddress(void* ptr, int bpp, int x, RIuint32 packedColor);
+
+ RI_INLINE RIuint32 readPackedPixel(int x, int y) const;
+ Color readPixel(int x, int y) const;
+ RI_INLINE void writePackedPixelToAddress(void* ptr, int x, RIuint32 packedColor);
+ void writePackedPixel(int x, int y, RIuint32 packedColor);
+ void writePixel(int x, int y, const Color& c);
+
+ void fillPacked(RIuint32 packedColor);
+
+ static RI_INLINE void fillPackedPixels(void* data, int bpp, int x, int y, int stride, int nPixels, RIuint32 packedColor);
+ RI_INLINE void fillPackedPixels(int x, int y, int nPixels, RIuint32 packedColor);
+ RI_INLINE void fillPackedRectangle(int x0, int y0, int width, int height, RIuint32 packedColor);
+
+ void writeFilteredPixel(int x, int y, const Color& c, VGbitfield channelMask);
+
+ RIfloat readMaskPixel(int x, int y) const; //can read any image format
+ void writeMaskPixel(int x, int y, RIfloat m); //can write only to VG_A_x
+
+ Color resample(RIfloat x, RIfloat y, const Matrix3x3& surfaceToImage, VGImageQuality quality, VGTilingMode tilingMode, const Color& tileFillColor); //throws bad_alloc
+ void makeMipMaps(); //throws bad_alloc
+
+ void colorMatrix(const Image& src, const RIfloat* matrix, bool filterFormatLinear, bool filterFormatPremultiplied, VGbitfield channelMask);
+ void convolve(const Image& src, int kernelWidth, int kernelHeight, int shiftX, int shiftY, const RIint16* kernel, RIfloat scale, RIfloat bias, VGTilingMode tilingMode, const Color& edgeFillColor, bool filterFormatLinear, bool filterFormatPremultiplied, VGbitfield channelMask);
+ void separableConvolve(const Image& src, int kernelWidth, int kernelHeight, int shiftX, int shiftY, const RIint16* kernelX, const RIint16* kernelY, RIfloat scale, RIfloat bias, VGTilingMode tilingMode, const Color& edgeFillColor, bool filterFormatLinear, bool filterFormatPremultiplied, VGbitfield channelMask);
+ void gaussianBlur(const Image& src, RIfloat stdDeviationX, RIfloat stdDeviationY, VGTilingMode tilingMode, const Color& edgeFillColor, bool filterFormatLinear, bool filterFormatPremultiplied, VGbitfield channelMask);
+ void lookup(const Image& src, const RIuint8 * redLUT, const RIuint8 * greenLUT, const RIuint8 * blueLUT, const RIuint8 * alphaLUT, bool outputLinear, bool outputPremultiplied, bool filterFormatLinear, bool filterFormatPremultiplied, VGbitfield channelMask);
+ void lookupSingle(const Image& src, const RIuint32 * lookupTable, VGImageChannel sourceChannel, bool outputLinear, bool outputPremultiplied, bool filterFormatLinear, bool filterFormatPremultiplied, VGbitfield channelMask);
+
+ RI_INLINE static int descriptorToStride(const Color::Descriptor& desc, int width) { return (width*desc.bitsPerPixel+7)/8; };
+
+ void getStorageOffset(int& x, int& y) const { x = m_storageOffsetX; y = m_storageOffsetY; }
+
+private:
+ Image(const Image&); //!< Not allowed.
+ void operator=(const Image&); //!< Not allowed.
+
+#if defined(RI_DEBUG)
+ bool ptrInImage(const void* ptr) const;
+#endif
+ Color readTexel(int u, int v, int level, VGTilingMode tilingMode, const Color& tileFillColor) const;
+
+ Color::Descriptor m_desc;
+ int m_width;
+ int m_height;
+ VGbitfield m_allowedQuality;
+ int m_inUse;
+ int m_stride;
+ RIuint8* m_data;
+ int m_referenceCount;
+ bool m_ownsData;
+ Image* m_parent;
+ int m_storageOffsetX;
+ int m_storageOffsetY;
+ bool m_unsafeData; // Data may contain incorrect pixel data
+
+#ifndef RI_COMPILE_LLVM_BYTECODE
+
+#endif /* RI_COMPILE_LLVM_BYTECODE */
+};
+
+#if defined(RI_DEBUG)
+RI_INLINE bool Image::ptrInImage(const void* ptr) const
+{
+ RIuint8* p = (RIuint8*)ptr;
+
+ if (p < m_data) return false;
+ if (p >= (m_data + m_height * m_stride)) return false;
+ return true;
+}
+#endif
+
+RI_INLINE const void* Image::incrementPointer(const void* ptr, int bpp, int x)
+{
+ if (bpp >= 8)
+ return (((RIuint8*)ptr) + (bpp >> 3));
+ // Increment the pointer only when the byte is actually about to change.
+ int mask;
+ if (bpp == 4)
+ mask = 1;
+ else if (bpp == 2)
+ mask = 3;
+ else
+ mask = 7;
+ if ((x & mask) == mask)
+ return ((RIuint8*)ptr + 1);
+ return ptr;
+}
+
+RI_INLINE void* Image::calculateAddress(const void* basePtr, int bpp, int x, int y, int stride)
+{
+ if (bpp >= 8)
+ {
+ return (void*)((RIuint8*)basePtr + y * stride + x * (bpp >> 3));
+ } else
+ {
+ // 4, 2, or 1 bits per pixel
+ RI_ASSERT(bpp == 4 || bpp == 2 || bpp == 1);
+ return (void*)((RIuint8*)basePtr + y * stride + ((x * bpp) >> 3));
+ }
+}
+
+RI_INLINE RIuint32 Image::readPackedPixel(int x, int y) const
+{
+ RI_ASSERT(m_data);
+ RI_ASSERT(x >= 0 && x < m_width);
+ RI_ASSERT(y >= 0 && y < m_height);
+ RI_ASSERT(m_referenceCount > 0);
+
+ RIuint32 p = 0;
+
+ void* ptr = Image::calculateAddress(m_data, m_desc.bitsPerPixel, x+m_storageOffsetX, y+m_storageOffsetY, m_stride);
+ p = readPackedPixelFromAddress(ptr, m_desc.bitsPerPixel, x+m_storageOffsetX);
+
+ return p;
+}
+
+
+RI_INLINE void Image::writePackedPixelToAddress(void* ptr, int bpp, int x, RIuint32 packedColor)
+{
+ // \note packedColor must contain the whole data (including < 8 bpp data)?
+ switch(bpp)
+ {
+ case 32:
+ {
+ RIuint32* s = ((RIuint32*)ptr);
+ *s = (RIuint32)packedColor;
+ break;
+ }
+
+ case 16:
+ {
+ RIuint16* s = ((RIuint16*)ptr);
+ *s = (RIuint16)packedColor;
+ break;
+ }
+
+ case 8:
+ {
+ RIuint8* s = ((RIuint8*)ptr);
+ *s = (RIuint8)packedColor;
+ break;
+ }
+ case 4:
+ {
+ RIuint8* s = ((RIuint8*)ptr);
+ *s = (RIuint8)((packedColor << ((x&1)<<2)) | ((unsigned int)*s & ~(0xf << ((x&1)<<2))));
+ break;
+ }
+
+ case 2:
+ {
+ RIuint8* s = ((RIuint8*)ptr);
+ *s = (RIuint8)((packedColor << ((x&3)<<1)) | ((unsigned int)*s & ~(0x3 << ((x&3)<<1))));
+ break;
+ }
+
+ default:
+ {
+ RI_ASSERT(bpp == 1);
+ RIuint8* s = ((RIuint8*)ptr);
+ *s = (RIuint8)((packedColor << (x&7)) | ((unsigned int)*s & ~(0x1 << (x&7))));
+ break;
+ }
+ }
+ // m_mipmapsValid = false; // \note Will never do this, must be handled outside this class somehow!
+}
+
+/**
+ * \brief Write packed pixel into address.
+ * \param x Which x-coordinate (starting from the start of the scanline
+ * pointed to) is addressed? This is only required for formats
+ * that have less than 8 bpp.
+ */
+void Image::writePackedPixelToAddress(void* address, int x, RIuint32 packedColor)
+{
+ writePackedPixelToAddress(address, m_desc.bitsPerPixel, x, packedColor);
+}
+
+/**
+ * \brief Read a packed pixel from a given address. Notice the use of param x!
+ * \param x Check which part of byte to return if bpp < 8
+ */
+RI_INLINE RIuint32 Image::readPackedPixelFromAddress(const void *ptr, int bpp, int x)
+{
+ switch(bpp)
+ {
+ case 32:
+ {
+ RIuint32* s = (RIuint32*)ptr;
+ return *s;
+ }
+
+ case 16:
+ {
+ RIuint16* s = (RIuint16*)ptr;
+ return (RIuint32)*s;
+ }
+
+ case 8:
+ {
+ RIuint8* s = (RIuint8*)ptr;
+ return (RIuint32)*s;
+ }
+ case 4:
+ {
+ RIuint8* s = ((RIuint8*)ptr);
+ return (RIuint32)(*s >> ((x&1)<<2)) & 0xf;
+ }
+
+ case 2:
+ {
+ RIuint8* s = ((RIuint8*)ptr);
+ return (RIuint32)(*s >> ((x&3)<<1)) & 0x3;
+ }
+
+ default:
+ {
+ RI_ASSERT(bpp == 1);
+ RIuint8* s = ((RIuint8*)ptr);
+ return (RIuint32)(*s >> (x&7)) & 0x1;
+ }
+ }
+}
+
+RI_INLINE void Image::writePackedPixel(int x, int y, RIuint32 packedColor)
+{
+ RI_ASSERT(m_data);
+ RI_ASSERT(x >= 0 && x < m_width);
+ RI_ASSERT(y >= 0 && y < m_height);
+ RI_ASSERT(m_referenceCount > 0);
+
+ x += m_storageOffsetX;
+ y += m_storageOffsetY;
+
+ RIuint8* scanline = m_data + y * m_stride;
+ switch(m_desc.bitsPerPixel)
+ {
+ case 32:
+ {
+ RIuint32* s = ((RIuint32*)scanline) + x;
+ *s = (RIuint32)packedColor;
+ break;
+ }
+
+ case 16:
+ {
+ RIuint16* s = ((RIuint16*)scanline) + x;
+ *s = (RIuint16)packedColor;
+ break;
+ }
+
+ case 8:
+ {
+ RIuint8* s = ((RIuint8*)scanline) + x;
+ *s = (RIuint8)packedColor;
+ break;
+ }
+ case 4:
+ {
+ RIuint8* s = ((RIuint8*)scanline) + (x>>1);
+ *s = (RIuint8)((packedColor << ((x&1)<<2)) | ((unsigned int)*s & ~(0xf << ((x&1)<<2))));
+ break;
+ }
+
+ case 2:
+ {
+ RIuint8* s = ((RIuint8*)scanline) + (x>>2);
+ *s = (RIuint8)((packedColor << ((x&3)<<1)) | ((unsigned int)*s & ~(0x3 << ((x&3)<<1))));
+ break;
+ }
+
+ default:
+ {
+ RI_ASSERT(m_desc.bitsPerPixel == 1);
+ RIuint8* s = ((RIuint8*)scanline) + (x>>3);
+ *s = (RIuint8)((packedColor << (x&7)) | ((unsigned int)*s & ~(0x1 << (x&7))));
+ break;
+ }
+ }
+ //m_mipmapsValid = false;
+}
+
+
+/**
+ * \brief Unsafe static method for setting image pixels
+ */
+RI_INLINE void Image::fillPackedPixels(void* data, int bpp, int x, int y, int stride, int nPixels, RIuint32 packedColor)
+{
+ RI_ASSERT(nPixels > 0);
+ RI_ASSERT(data);
+
+ RIuint8* scanline = (RIuint8*)data + y * stride;
+
+ switch(bpp)
+ {
+ case 32:
+ {
+ RIuint32* s = ((RIuint32*)scanline) + x;
+
+ for (int i = 0; i < nPixels; i++)
+ s[i] = packedColor;
+
+ break;
+ }
+
+ case 16:
+ {
+ RIuint16* s = ((RIuint16*)scanline) + x;
+
+ for (int i = 0; i < nPixels; i++)
+ s[i] = (RIuint16)packedColor;
+
+ break;
+ }
+
+ case 8:
+ {
+ RIuint8* s = ((RIuint8*)scanline) + x;
+
+ for (int i = 0; i < nPixels; i++)
+ s[i] = (RIuint8)packedColor;
+
+ break;
+ }
+ case 4:
+ {
+ //RI_ASSERT((packedColor & 0xf) == 0);
+ //packedColor &= 0xf;
+ RIuint8* s = ((RIuint8*)scanline) + (x>>1);
+ if (x & 1)
+ {
+ *s = (RIuint8)((packedColor << ((x&1)<<2)) | ((unsigned int)*s & ~(0xf << ((x&1)<<2))));
+ s++;
+ x++;
+ nPixels--;
+ }
+ RI_ASSERT(!(x&1));
+
+ int c = nPixels / 2;
+ RIuint8 bytePacked = packedColor | (packedColor << 4);
+ while (c)
+ {
+ *s++ = bytePacked;
+ c--;
+ x+=2;
+ }
+ nPixels &= 1;
+
+ if (nPixels)
+ {
+ *s = (RIuint8)((packedColor << ((x&1)<<2)) | ((unsigned int)*s & ~(0xf << ((x&1)<<2))));
+ s++;
+ x++;
+ nPixels--;
+ }
+ RI_ASSERT(nPixels == 0);
+ break;
+ }
+
+ case 2:
+ {
+ // This case should not be needed!
+ RI_ASSERT(false);
+ RIuint8* s = ((RIuint8*)scanline) + (x>>2);
+ *s = (RIuint8)((packedColor << ((x&3)<<1)) | ((unsigned int)*s & ~(0x3 << ((x&3)<<1))));
+ break;
+ }
+
+ default:
+ {
+ RI_ASSERT(bpp == 1);
+ RIuint8* s = ((RIuint8*)scanline) + (x>>3);
+ // \todo Get this as input instead?
+ RI_ASSERT(packedColor == 1 || packedColor == 0);
+ RIuint8 fullyPacked = (RIuint8)(-(RIint8)packedColor);
+
+ if (x & 7)
+ {
+ // Handle the first byte:
+ RIuint8 o = *s;
+ int a = x&7;
+ RI_ASSERT(a>=1);
+ int b = RI_INT_MIN(a + nPixels, 8);
+ RI_ASSERT(b > a);
+ RIuint8 emask = (1u << b)-1;
+ RIuint8 mask = (0xffu<<a) & emask;
+ RI_ASSERT(mask>0);
+ RI_ASSERT(mask<=254);
+ *s++ = (o&(~mask))|(fullyPacked & mask);
+ nPixels -= 8-(x&7);
+ x += 8-(x&7);
+ }
+
+ if (nPixels < 0)
+ return;
+
+ RI_ASSERT(!(x&1));
+
+ int c = nPixels/8;
+ while (c)
+ {
+ *s++ = fullyPacked;
+ c--;
+ x+=8;
+ }
+ nPixels -= ((nPixels/8) * 8);
+
+
+ if (nPixels)
+ {
+ RI_ASSERT((x&7) == 0);
+
+ RIuint8 o = *s;
+ int b = nPixels;
+ RI_ASSERT(b<=7);
+ RIuint8 mask = (1u<<b)-1;
+ RI_ASSERT(mask <= 127);
+ *s++ = (o&(~mask))|(fullyPacked & mask);
+ }
+ break;
+ }
+ }
+ //m_mipmapsValid = false;
+}
+RI_INLINE void Image::fillPackedPixels(int x, int y, int nPixels, RIuint32 packedColor)
+{
+ fillPackedPixels((void*)m_data, m_desc.bitsPerPixel, x + m_storageOffsetX, y + m_storageOffsetY, m_stride, nPixels, packedColor);
+}
+
+RI_INLINE void Image::fillPackedRectangle(int x0, int y0, int width, int height, RIuint32 packedColor)
+{
+ int y = y0;
+ while (height)
+ {
+ fillPackedPixels(x0, y, width, packedColor);
+ y++;
+ height--;
+ }
+}
+
+/*-------------------------------------------------------------------*//*!
+* \brief Surface class abstracting multisampled rendering surface.
+* \param
+* \return
+* \note
+*//*-------------------------------------------------------------------*/
+
+class Surface
+{
+public:
+ Surface(const Color::Descriptor& desc, int width, int height, int numSamples); //throws bad_alloc
+ Surface(Image* image); //throws bad_alloc
+ Surface(const Color::Descriptor& desc, int width, int height, int stride, RIuint8* data); //throws bad_alloc
+ ~Surface();
+
+ RI_INLINE const Image* getImage() const {return m_image;}
+ RI_INLINE const Color::Descriptor& getDescriptor() const { return m_image->getDescriptor(); }
+ RI_INLINE int getWidth() const { return m_width; }
+ RI_INLINE int getHeight() const { return m_height; }
+ RI_INLINE int getNumSamples() const { return m_numSamples; }
+ RI_INLINE void addReference() { m_referenceCount++; }
+ RI_INLINE int removeReference() { m_referenceCount--; RI_ASSERT(m_referenceCount >= 0); return m_referenceCount; }
+ RI_INLINE int isInUse() const { return m_image->isInUse(); }
+ RI_INLINE bool isInUse(Image* image) const { return image == m_image ? true : false; }
+
+ void clear(const Color& clearColor, int x, int y, int w, int h, const Array<Rectangle>* scissors = NULL);
+#if 0
+ // Currently does not support msaa surfaces
+ void blit(const Image& src, int sx, int sy, int dx, int dy, int w, int h); //throws bad_alloc
+ void blit(const Image& src, int sx, int sy, int dx, int dy, int w, int h, const Array<Rectangle>& scissors); //throws bad_alloc
+ void blit(const Surface* src, int sx, int sy, int dx, int dy, int w, int h); //throws bad_alloc
+ void blit(const Surface* src, int sx, int sy, int dx, int dy, int w, int h, const Array<Rectangle>& scissors); //throws bad_alloc
+#endif
+ void mask(DynamicBlitter& blitter, const Image* src, VGMaskOperation operation, int x, int y, int w, int h);
+
+ RI_INLINE void writePackedPixelToAddress(void* address, int x, RIuint32 p) { m_image->writePackedPixelToAddress(address, x, p); }
+ RI_INLINE RIuint32 readPackedSample(int x, int y, int sample) const { return m_image->readPackedPixel(x*m_numSamples+sample, y); }
+ RI_INLINE Color readSample(int x, int y, int sample) const { return m_image->readPixel(x*m_numSamples+sample, y); }
+ RI_INLINE void writePackedSample(int x, int y, int sample, RIuint32 p) { m_image->writePackedPixel(x*m_numSamples+sample, y, p); }
+ RI_INLINE void writeSample(int x, int y, int sample, const Color& c) { m_image->writePixel(x*m_numSamples+sample, y, c); }
+ RI_INLINE void fillPackedSamples(int x, int y, int nPixels, RIuint32 p);
+
+ RIfloat readMaskCoverage(int x, int y) const;
+ void writeMaskCoverage(int x, int y, RIfloat m);
+ unsigned int readMaskMSAA(int x, int y) const;
+ void writeMaskMSAA(int x, int y, unsigned int m);
+
+ RIuint32 FSAAResolvePacked(int x, int y) const;
+ Color FSAAResolve(int x, int y) const; //for fb=>img: vgGetPixels, vgReadPixels
+
+private:
+ Surface(const Surface&); //!< Not allowed.
+ void operator=(const Surface&); //!< Not allowed.
+
+ struct ScissorEdge
+ {
+ ScissorEdge() : x(0), miny(0), maxy(0), direction(0) {}
+ bool operator<(const ScissorEdge& e) const { return x < e.x; }
+ int x;
+ int miny;
+ int maxy;
+ int direction; //1 start, -1 end
+ };
+
+ int m_width;
+ int m_height;
+ int m_numSamples;
+ int m_referenceCount;
+
+public:
+ // \todo TERO: Broke the design of this by making it public, make proper
+ // friend/etc. C++ accessor for optimized pixel-pipelines. Combine with the
+ // removal of (remnants of) the FSAA support.
+ Image* m_image;
+};
+
+RI_INLINE void Surface::fillPackedSamples(int x, int y, int nPixels, RIuint32 p)
+{
+ m_image->fillPackedPixels(x, y, nPixels, p);
+}
+
+
+/*-------------------------------------------------------------------*//*!
+* \brief Drawable class for encapsulating color and mask buffers.
+* \param
+* \return
+* \note
+*//*-------------------------------------------------------------------*/
+
+class Drawable
+{
+public:
+ Drawable(const Color::Descriptor& desc, int width, int height, int numSamples, int maskBits); //throws bad_alloc
+ Drawable(Image* image, int maskBits); //throws bad_alloc
+ Drawable(const Color::Descriptor& desc, int width, int height, int stride, RIuint8* data, int maskBits); //throws bad_alloc
+ ~Drawable();
+
+ RI_INLINE const Color::Descriptor& getDescriptor() const { return m_color->getDescriptor(); }
+ RI_INLINE int getNumMaskBits() const { if(!m_mask) return 0; return m_mask->getDescriptor().alphaBits; }
+ RI_INLINE int getWidth() const { return m_color->getWidth(); }
+ RI_INLINE int getHeight() const { return m_color->getHeight(); }
+ RI_INLINE int getNumSamples() const { return m_color->getNumSamples(); }
+ RI_INLINE void addReference() { m_referenceCount++; }
+ RI_INLINE int removeReference() { m_referenceCount--; RI_ASSERT(m_referenceCount >= 0); return m_referenceCount; }
+ RI_INLINE int isInUse() const { return m_color->isInUse() || (m_mask && m_mask->isInUse()); }
+ RI_INLINE bool isInUse(Image* image) const { return m_color->isInUse(image) || (m_mask && m_mask->isInUse(image)); }
+ RI_INLINE Surface* getColorBuffer() const { return m_color; }
+ RI_INLINE Surface* getMaskBuffer() const { return m_mask; }
+
+ void resize(VGContext* context, int newWidth, int newHeight); //throws bad_alloc
+private:
+ Drawable(const Drawable&); //!< Not allowed.
+ void operator=(const Drawable&); //!< Not allowed.
+
+ int m_referenceCount;
+ Surface* m_color;
+ Surface* m_mask;
+};
+
+//==============================================================================================
+
+} //namespace OpenVGRI
+
+//==============================================================================================
+
+#endif /* __RIIMAGE_H */