diff -r 5f371025658c -r f767bd5f4cfc egl/sfopenvg/riImage.cpp --- a/egl/sfopenvg/riImage.cpp Fri Jul 16 18:54:03 2010 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2673 +0,0 @@ -/*------------------------------------------------------------------------ - * - * OpenVG 1.1 Reference Implementation - * ----------------------------------- - * - * Copyright (c) 2007 The Khronos Group Inc. - * - * 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 Implementation of Color and Image functions. - * \note - *//*-------------------------------------------------------------------*/ - -#include "riImage.h" -#include "riRasterizer.h" -//============================================================================================== - -namespace OpenVGRI -{ - -/*-------------------------------------------------------------------*//*! -* \brief Converts from numBits into a shifted mask -* \param -* \return -* \note -*//*-------------------------------------------------------------------*/ - -static unsigned int bitsToMask(unsigned int bits, unsigned int shift) -{ - return ((1<> ls, (1<> rs, (1<> gs, (1<> bs, (1<> as, (1<= 0 && baseFormat < numBaseFormats); - int swizzleBits = ((int)format >> 6) & 3; - - /* base formats - VG_sRGBX_8888 = 0, - VG_sRGBA_8888 = 1, - VG_sRGBA_8888_PRE = 2, - VG_sRGB_565 = 3, - VG_sRGBA_5551 = 4, - VG_sRGBA_4444 = 5, - VG_sL_8 = 6, - VG_lRGBX_8888 = 7, - VG_lRGBA_8888 = 8, - VG_lRGBA_8888_PRE = 9, - VG_lL_8 = 10, - VG_A_8 = 11, - VG_BW_1 = 12, - VG_A_1 = 13, - VG_A_4 = 14, - */ - - static const int redBits[numBaseFormats] = {8, 8, 8, 5, 5, 4, 0, 8, 8, 8, 0, 0, 0, 0, 0}; - static const int greenBits[numBaseFormats] = {8, 8, 8, 6, 5, 4, 0, 8, 8, 8, 0, 0, 0, 0, 0}; - static const int blueBits[numBaseFormats] = {8, 8, 8, 5, 5, 4, 0, 8, 8, 8, 0, 0, 0, 0, 0}; - static const int alphaBits[numBaseFormats] = {0, 8, 8, 0, 1, 4, 0, 0, 8, 8, 0, 8, 0, 1, 4}; - static const int luminanceBits[numBaseFormats] = {0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 8, 0, 1, 0, 0}; - - static const int redShifts[4*numBaseFormats] = {24, 24, 24, 11, 11, 12, 0, 24, 24, 24, 0, 0, 0, 0, 0, //RGBA - 16, 16, 16, 11, 10, 8, 0, 16, 16, 16, 0, 0, 0, 0, 0, //ARGB - 8, 8, 8, 0, 1, 4, 0, 8, 8, 8, 0, 0, 0, 0, 0, //BGRA - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; //ABGR - - static const int greenShifts[4*numBaseFormats] = {16, 16, 16, 5, 6, 8, 0, 16, 16, 16, 0, 0, 0, 0, 0, //RGBA - 8, 8, 8, 5, 5, 4, 0, 8, 8, 8, 0, 0, 0, 0, 0, //ARGB - 16, 16, 16, 5, 6, 8, 0, 16, 16, 16, 0, 0, 0, 0, 0, //BGRA - 8, 8, 8, 5, 5, 4, 0, 8, 8, 8, 0, 0, 0, 0, 0};//ABGR - - static const int blueShifts[4*numBaseFormats] = {8, 8, 8, 0, 1, 4, 0, 8, 8, 8, 0, 0, 0, 0, 0, //RGBA - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //ARGB - 24, 24, 24, 11, 11, 12, 0, 24, 24, 24, 0, 0, 0, 0, 0, //BGRA - 16, 16, 16, 11, 10, 8, 0, 16, 16, 16, 0, 0, 0, 0, 0};//ABGR - - static const int alphaShifts[4*numBaseFormats] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //RGBA - 0, 24, 24, 0, 15, 12, 0, 0, 24, 24, 0, 0, 0, 0, 0, //ARGB - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //BGRA - 0, 24, 24, 0, 15, 12, 0, 0, 24, 24, 0, 0, 0, 0, 0};//ABGR - - static const int bpps[numBaseFormats] = {32, 32, 32, 16, 16, 16, 8, 32, 32, 32, 8, 8, 1, 1, 4}; - - static const InternalFormat internalFormats[numBaseFormats] = {sRGBA, sRGBA, sRGBA_PRE, sRGBA, sRGBA, sRGBA, sLA, lRGBA, lRGBA, lRGBA_PRE, lLA, lRGBA, lLA, lRGBA, lRGBA}; - - desc.redBits = redBits[baseFormat]; - desc.greenBits = greenBits[baseFormat]; - desc.blueBits = blueBits[baseFormat]; - desc.alphaBits = alphaBits[baseFormat]; - desc.luminanceBits = luminanceBits[baseFormat]; - - desc.redShift = redShifts[swizzleBits * numBaseFormats + baseFormat]; - desc.greenShift = greenShifts[swizzleBits * numBaseFormats + baseFormat]; - desc.blueShift = blueShifts[swizzleBits * numBaseFormats + baseFormat]; - desc.alphaShift = alphaShifts[swizzleBits * numBaseFormats + baseFormat]; - desc.luminanceShift = 0; //always zero - - desc.format = format; - desc.bitsPerPixel = bpps[baseFormat]; - desc.internalFormat = internalFormats[baseFormat]; - - return desc; -} - -/*-------------------------------------------------------------------*//*! -* \brief Checks if the pixel format descriptor is valid (i.e. all the -* values are supported by the RI) -* \param -* \return -* \note -*//*-------------------------------------------------------------------*/ - -bool Color::isValidDescriptor(const Color::Descriptor& desc) -{ - //A valid descriptor has 1, 2, 4, 8, 16, or 32 bits per pixel, and either luminance or rgba channels, but not both. - //Any of the rgba channels can be missing, and not all bits need to be used. Maximum channel bit depth is 8. - int rb = desc.redBits; - int gb = desc.greenBits; - int bb = desc.blueBits; - int ab = desc.alphaBits; - int lb = desc.luminanceBits; - int rs = desc.redShift; - int gs = desc.greenShift; - int bs = desc.blueShift; - int as = desc.alphaShift; - int ls = desc.luminanceShift; - int bpp = desc.bitsPerPixel; - - int rgbaBits = rb + gb + bb + ab; - if(rb < 0 || rb > 8 || rs < 0 || rs + rb > bpp || !(rb || !rs)) - return false; //invalid channel description - if(gb < 0 || gb > 8 || gs < 0 || gs + gb > bpp || !(gb || !gs)) - return false; //invalid channel description - if(bb < 0 || bb > 8 || bs < 0 || bs + bb > bpp || !(bb || !bs)) - return false; //invalid channel description - if(ab < 0 || ab > 8 || as < 0 || as + ab > bpp || !(ab || !as)) - return false; //invalid channel description - if(lb < 0 || lb > 8 || ls < 0 || ls + lb > bpp || !(lb || !ls)) - return false; //invalid channel description - - if(rgbaBits && lb) - return false; //can't have both rgba and luminance - if(!rgbaBits && !lb) - return false; //must have either rgba or luminance - if(rgbaBits) - { //rgba - if(rb+gb+bb == 0) - { //alpha only - if(rs || gs || bs || as || ls) - return false; //wrong shifts (even alpha shift must be zero) - if((ab != 1 && ab != 2 && ab != 4 && ab != 8) || bpp != ab) - return false; //alpha size must be 1, 2, 4, or, 8, bpp must match - } - else - { //rgba - if(rgbaBits > bpp) - return false; //bpp must be greater than or equal to the sum of rgba bits - if(!(bpp == 32 || bpp == 16 || bpp == 8)) - return false; //only 1, 2, and 4 byte formats are supported for rgba - - unsigned int rm = bitsToMask((unsigned int)rb, (unsigned int)rs); - unsigned int gm = bitsToMask((unsigned int)gb, (unsigned int)gs); - unsigned int bm = bitsToMask((unsigned int)bb, (unsigned int)bs); - unsigned int am = bitsToMask((unsigned int)ab, (unsigned int)as); - if((rm & gm) || (rm & bm) || (rm & am) || (gm & bm) || (gm & am) || (bm & am)) - return false; //channels overlap - } - } - else - { //luminance - if(rs || gs || bs || as || ls) - return false; //wrong shifts (even luminance shift must be zero) - if(!(lb == 1 || lb == 8) || bpp != lb) - return false; //luminance size must be either 1 or 8, bpp must match - } - - if(desc.format != -1) - { - if(!isValidImageFormat(desc.format)) - return false; //invalid image format - - Descriptor d = formatToDescriptor(desc.format); - if(d.redBits != rb || d.greenBits != gb || d.blueBits != bb || d.alphaBits != ab || d.luminanceBits != lb || - d.redShift != rs || d.greenShift != gs || d.blueShift != bs || d.alphaShift != as || d.luminanceShift != ls || - d.bitsPerPixel != bpp) - return false; //if the descriptor has a VGImageFormat, it must match the bits, shifts, and bpp - } - - if((unsigned int)desc.internalFormat & ~(Color::PREMULTIPLIED | Color::NONLINEAR | Color::LUMINANCE)) - return false; //invalid internal format - - return true; -} - -//============================================================================================== - - - - -//============================================================================================== - -/*-------------------------------------------------------------------*//*! -* \brief Constructs a blank image. -* \param -* \return -* \note -*//*-------------------------------------------------------------------*/ - -Image::Image(const Color::Descriptor& desc, int width, int height, VGbitfield allowedQuality) : - m_desc(desc), - m_width(width), - m_height(height), - m_allowedQuality(allowedQuality), - m_inUse(0), - m_stride(0), - m_data(NULL), - m_referenceCount(0), - m_ownsData(true), - m_parent(NULL), - m_storageOffsetX(0), - m_storageOffsetY(0), - m_mipmapsValid(false), - m_mipmaps() -{ - RI_ASSERT(Color::isValidDescriptor(m_desc)); - RI_ASSERT(width > 0 && height > 0); - - m_stride = (m_width*m_desc.bitsPerPixel+7)/8; - - m_data = RI_NEW_ARRAY(RIuint8, m_stride*m_height); //throws bad_alloc - memset(m_data, 0, m_stride*m_height); //clear image -} - -/*-------------------------------------------------------------------*//*! -* \brief Constructs an image that uses an external array for its data -* storage. -* \param -* \return -* \note this is meant for internal use to make blitting easier -*//*-------------------------------------------------------------------*/ - -Image::Image(const Color::Descriptor& desc, int width, int height, int stride, RIuint8* data) : - m_desc(desc), - m_width(width), - m_height(height), - m_allowedQuality(0), - m_inUse(0), - m_stride(stride), - m_data(data), - m_referenceCount(0), - m_ownsData(false), - m_parent(NULL), - m_storageOffsetX(0), - m_storageOffsetY(0), - m_mipmapsValid(false), - m_mipmaps() -{ - RI_ASSERT(Color::isValidDescriptor(m_desc)); - RI_ASSERT(width > 0 && height > 0); - RI_ASSERT(data); -} - -/*-------------------------------------------------------------------*//*! -* \brief Construcs a child image. -* \param -* \return -* \note -*//*-------------------------------------------------------------------*/ - -Image::Image(Image* parent, int x, int y, int width, int height) : - m_desc(Color::formatToDescriptor(VG_sRGBA_8888)), //dummy initialization, will be overwritten below (can't read from parent->m_desc before knowing the pointer is valid) - m_width(width), - m_height(height), - m_allowedQuality(0), - m_inUse(0), - m_stride(0), - m_data(NULL), - m_referenceCount(0), - m_ownsData(false), - m_parent(parent), - m_storageOffsetX(0), - m_storageOffsetY(0), - m_mipmapsValid(false), - m_mipmaps() -{ - RI_ASSERT(parent); - RI_ASSERT(x >= 0 && y >= 0 && width > 0 && height > 0); - RI_ASSERT(RI_INT_ADDSATURATE(x,width) <= parent->m_width && RI_INT_ADDSATURATE(y,height) <= parent->m_height); //child image must be contained in parent - - m_desc = parent->m_desc; - RI_ASSERT(Color::isValidDescriptor(m_desc)); - m_allowedQuality = parent->m_allowedQuality; - m_stride = parent->m_stride; - m_data = parent->m_data; - m_storageOffsetX = parent->m_storageOffsetX + x; - m_storageOffsetY = parent->m_storageOffsetY + y; - - //increase the reference and use count of the parent - addInUse(); - parent->addInUse(); - parent->addReference(); -} - -/*-------------------------------------------------------------------*//*! -* \brief Image destructor. -* \param -* \return -* \note -*//*-------------------------------------------------------------------*/ - -Image::~Image() -{ - RI_ASSERT(m_referenceCount == 0); - - if(m_parent) - { - //decrease the reference and use count of the parent - removeInUse(); - m_parent->removeInUse(); - if(!m_parent->removeReference()) - RI_DELETE(m_parent); - } - RI_ASSERT(m_inUse == 0); - - for(int i=0;iremoveReference()) - RI_DELETE(m_mipmaps[i]); - else - { - RI_ASSERT(0); //there can't be any other references to the mipmap levels - } - } - m_mipmaps.clear(); - - if(m_ownsData) - { - RI_ASSERT(!m_parent); //can't have parent if owns the data - RI_DELETE_ARRAY(m_data); //delete image data if we own it - } -} - -/*-------------------------------------------------------------------*//*! -* \brief Returns true if the two images share pixels. -* \param -* \return -* \note -*//*-------------------------------------------------------------------*/ - -bool Image::overlaps(const Image* src) const -{ - RI_ASSERT(src); - - if(m_data != src->m_data) - return false; //images don't share data - - //check if the image storage regions overlap - Rectangle r(m_storageOffsetX, m_storageOffsetY, m_width, m_height); - r.intersect(Rectangle(src->m_storageOffsetX, src->m_storageOffsetY, src->m_width, src->m_height)); - if(!r.width || !r.height) - return false; //intersection is empty, images don't overlap - - return true; -} - -/*-------------------------------------------------------------------*//*! -* \brief Clears a rectangular portion of an image with the given clear color. -* \param -* \return -* \note -*//*-------------------------------------------------------------------*/ - -void Image::clear(const Color& clearColor, int x, int y, int w, int h) -{ - RI_ASSERT(m_data); - RI_ASSERT(m_referenceCount > 0); - - //intersect clear region with image bounds - Rectangle r(0,0,m_width,m_height); - r.intersect(Rectangle(x,y,w,h)); - if(!r.width || !r.height) - return; //intersection is empty or one of the rectangles is invalid - - Color col = clearColor; - col.clamp(); - col.convert(m_desc.internalFormat); - - for(int j=r.y;j m) ic += 1.0f; - return RI_MIN(ic / (RIfloat)((1< 0 && h > 0); - sx = RI_INT_MIN(RI_INT_MAX(sx, (int)(RI_INT32_MIN>>2)), (int)(RI_INT32_MAX>>2)); - sy = RI_INT_MIN(RI_INT_MAX(sy, (int)(RI_INT32_MIN>>2)), (int)(RI_INT32_MAX>>2)); - dx = RI_INT_MIN(RI_INT_MAX(dx, (int)(RI_INT32_MIN>>2)), (int)(RI_INT32_MAX>>2)); - dy = RI_INT_MIN(RI_INT_MAX(dy, (int)(RI_INT32_MIN>>2)), (int)(RI_INT32_MAX>>2)); - w = RI_INT_MIN(w, (int)(RI_INT32_MAX>>2)); - h = RI_INT_MIN(h, (int)(RI_INT32_MAX>>2)); - int srcsx = sx, srcex = sx + w, dstsx = dx, dstex = dx + w; - if(srcsx < 0) - { - dstsx -= srcsx; - srcsx = 0; - } - if(srcex > srcWidth) - { - dstex -= srcex - srcWidth; - srcex = srcWidth; - } - if(dstsx < 0) - { - srcsx -= dstsx; - dstsx = 0; - } - if(dstex > dstWidth) - { - srcex -= dstex - dstWidth; - dstex = dstWidth; - } - RI_ASSERT(srcsx >= 0 && dstsx >= 0 && srcex <= srcWidth && dstex <= dstWidth); - w = srcex - srcsx; - RI_ASSERT(w == dstex - dstsx); - - int srcsy = sy, srcey = sy + h, dstsy = dy, dstey = dy + h; - if(srcsy < 0) - { - dstsy -= srcsy; - srcsy = 0; - } - if(srcey > srcHeight) - { - dstey -= srcey - srcHeight; - srcey = srcHeight; - } - if(dstsy < 0) - { - srcsy -= dstsy; - dstsy = 0; - } - if(dstey > dstHeight) - { - srcey -= dstey - dstHeight; - dstey = dstHeight; - } - RI_ASSERT(srcsy >= 0 && dstsy >= 0 && srcey <= srcHeight && dstey <= dstHeight); - h = srcey - srcsy; - RI_ASSERT(h == dstey - dstsy); - sx = srcsx; - sy = srcsy; - dx = dstsx; - dy = dstsy; -} - -void Image::blit(const Image& src, int sx, int sy, int dx, int dy, int w, int h, bool dither) -{ - //img=>img: vgCopyImage - //img=>user: vgGetImageSubData - //user=>img: vgImageSubData - RI_ASSERT(src.m_data); //source exists - RI_ASSERT(m_data); //destination exists - RI_ASSERT(m_referenceCount > 0 && src.m_referenceCount > 0); - - computeBlitRegion(sx, sy, dx, dy, w, h, src.m_width, src.m_height, m_width, m_height); - if(w <= 0 || h <= 0) - return; //zero area - - Array tmp; - tmp.resize(w*h); //throws bad_alloc - - //copy source region to tmp - for(int j=0;jimg: vgGetPixels - //fb=>user: vgReadPixels - RI_ASSERT(!src->isInUse(this)); - - computeBlitRegion(sx, sy, dx, dy, w, h, src->getWidth(), src->getHeight(), m_width, m_height); - if(w <= 0 || h <= 0) - return; //zero area - - for(int y=0;yFSAAResolve(sx + x, sy + y); - r.convert(getDescriptor().internalFormat); - writePixel(dx + x, dy + y, r); - } - } -} - -/*-------------------------------------------------------------------*//*! -* \brief Returns the color at pixel (x,y). -* \param -* \return -* \note -*//*-------------------------------------------------------------------*/ - -Color Image::readPixel(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); - x += m_storageOffsetX; - y += m_storageOffsetY; - - unsigned int p = 0; - RIuint8* scanline = m_data + y * m_stride; - switch(m_desc.bitsPerPixel) - { - case 32: - { - RIuint32* s = (((RIuint32*)scanline) + x); - p = (unsigned int)*s; - break; - } - - case 16: - { - RIuint16* s = ((RIuint16*)scanline) + x; - p = (unsigned int)*s; - break; - } - - case 8: - { - RIuint8* s = ((RIuint8*)scanline) + x; - p = (unsigned int)*s; - break; - } - - case 4: - { - RIuint8* s = ((RIuint8*)scanline) + (x>>1); - p = (unsigned int)(*s >> ((x&1)<<2)) & 0xf; - break; - } - - case 2: - { - RIuint8* s = ((RIuint8*)scanline) + (x>>2); - p = (unsigned int)(*s >> ((x&3)<<1)) & 0x3; - break; - } - - default: - { - RI_ASSERT(m_desc.bitsPerPixel == 1); - RIuint8* s = ((RIuint8*)scanline) + (x>>3); - p = (unsigned int)(*s >> (x&7)) & 0x1; - break; - } - } - Color c; - c.unpack(p, m_desc); - return c; -} - -/*-------------------------------------------------------------------*//*! -* \brief Writes the color to pixel (x,y). Internal color formats must -* match. -* \param -* \return -* \note -*//*-------------------------------------------------------------------*/ - -void Image::writePixel(int x, int y, const Color& c) -{ - RI_ASSERT(m_data); - RI_ASSERT(x >= 0 && x < m_width); - RI_ASSERT(y >= 0 && y < m_height); - RI_ASSERT(m_referenceCount > 0); - RI_ASSERT(c.getInternalFormat() == m_desc.internalFormat); - x += m_storageOffsetX; - y += m_storageOffsetY; - - unsigned int p = c.pack(m_desc); - RIuint8* scanline = m_data + y * m_stride; - switch(m_desc.bitsPerPixel) - { - case 32: - { - RIuint32* s = ((RIuint32*)scanline) + x; - *s = (RIuint32)p; - break; - } - - case 16: - { - RIuint16* s = ((RIuint16*)scanline) + x; - *s = (RIuint16)p; - break; - } - - case 8: - { - RIuint8* s = ((RIuint8*)scanline) + x; - *s = (RIuint8)p; - break; - } - case 4: - { - RIuint8* s = ((RIuint8*)scanline) + (x>>1); - *s = (RIuint8)((p << ((x&1)<<2)) | ((unsigned int)*s & ~(0xf << ((x&1)<<2)))); - break; - } - - case 2: - { - RIuint8* s = ((RIuint8*)scanline) + (x>>2); - *s = (RIuint8)((p << ((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)((p << (x&7)) | ((unsigned int)*s & ~(0x1 << (x&7)))); - break; - } - } - m_mipmapsValid = false; -} - -/*-------------------------------------------------------------------*//*! -* \brief Writes a filtered color to destination surface -* \param -* \return -* \note -*//*-------------------------------------------------------------------*/ - -void Image::writeFilteredPixel(int i, int j, const Color& color, VGbitfield channelMask) -{ - //section 3.4.4: before color space conversion, premultiplied colors are - //clamped to alpha, and the color is converted to nonpremultiplied format - //section 11.2: how to deal with channel mask - //step 1 - Color f = color; - f.clamp(); //vgColorMatrix and vgLookups can produce colors that exceed alpha or [0,1] range - - //step 2: color space conversion - f.convert((Color::InternalFormat)(m_desc.internalFormat & (Color::NONLINEAR | Color::LUMINANCE))); - - //step 3: read the destination color and convert it to nonpremultiplied - Color d = readPixel(i,j); - d.unpremultiply(); - RI_ASSERT(d.getInternalFormat() == f.getInternalFormat()); - - //step 4: replace the destination channels specified by the channelMask (channelmask is ignored for luminance formats) - if(!m_desc.isLuminance()) - { //rgba format => use channelmask - if(channelMask & VG_RED) - d.r = f.r; - if(channelMask & VG_GREEN) - d.g = f.g; - if(channelMask & VG_BLUE) - d.b = f.b; - if(channelMask & VG_ALPHA) - d.a = f.a; - } - else d = f; - - //step 5: if destination is premultiplied, convert to premultiplied format - if(m_desc.isPremultiplied()) - d.premultiply(); - //write the color to destination - writePixel(i,j,d); -} - -/*-------------------------------------------------------------------*//*! -* \brief Reads the pixel (x,y) and converts it into an alpha mask value. -* \param -* \return -* \note -*//*-------------------------------------------------------------------*/ - -RIfloat Image::readMaskPixel(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); - - Color c = readPixel(x,y); - if(m_desc.isLuminance()) - { - return c.r; - } - else - { //rgba - if(m_desc.alphaBits) - return c.a; - return c.r; - } -} - -/*-------------------------------------------------------------------*//*! -* \brief Writes the alpha mask to pixel (x,y). -* \param -* \return -* \note Overwrites color. -*//*-------------------------------------------------------------------*/ - -void Image::writeMaskPixel(int x, int y, RIfloat m) -{ - RI_ASSERT(m_data); - RI_ASSERT(x >= 0 && x < m_width); - RI_ASSERT(y >= 0 && y < m_height); - RI_ASSERT(m_referenceCount > 0); - - //if luminance or no alpha, red channel will be used, otherwise alpha channel will be used - writePixel(x, y, Color(m,m,m,m,m_desc.internalFormat)); -} - -/*-------------------------------------------------------------------*//*! -* \brief Reads a texel (u,v) at the given mipmap level. Tiling modes and -* color space conversion are applied. Outputs color in premultiplied -* format. -* \param -* \return -* \note -*//*-------------------------------------------------------------------*/ - -Color Image::readTexel(int u, int v, int level, VGTilingMode tilingMode, const Color& tileFillColor) const -{ - const Image* image = this; - if( level > 0 ) - { - RI_ASSERT(level <= m_mipmaps.size()); - image = m_mipmaps[level-1]; - } - RI_ASSERT(image); - - Color p; - if(tilingMode == VG_TILE_FILL) - { - if(u < 0 || v < 0 || u >= image->m_width || v >= image->m_height) - p = tileFillColor; - else - p = image->readPixel(u, v); - } - else if(tilingMode == VG_TILE_PAD) - { - u = RI_INT_MIN(RI_INT_MAX(u,0),image->m_width-1); - v = RI_INT_MIN(RI_INT_MAX(v,0),image->m_height-1); - p = image->readPixel(u, v); - } - else if(tilingMode == VG_TILE_REPEAT) - { - u = RI_INT_MOD(u, image->m_width); - v = RI_INT_MOD(v, image->m_height); - p = image->readPixel(u, v); - } - else - { - RI_ASSERT(tilingMode == VG_TILE_REFLECT); - - u = RI_INT_MOD(u, image->m_width*2); - v = RI_INT_MOD(v, image->m_height*2); - if( u >= image->m_width ) u = image->m_width*2-1 - u; - if( v >= image->m_height ) v = image->m_height*2-1 - v; - p = image->readPixel(u, v); - } - - p.premultiply(); //interpolate in premultiplied format - return p; -} - -/*-------------------------------------------------------------------*//*! -* \brief Maps point (x,y) to an image and returns a filtered, -* premultiplied color value. -* \param -* \return -* \note -*//*-------------------------------------------------------------------*/ - -Color Image::resample(RIfloat x, RIfloat y, const Matrix3x3& surfaceToImage, VGImageQuality quality, VGTilingMode tilingMode, const Color& tileFillColor) //throws bad_alloc -{ - RI_ASSERT(m_referenceCount > 0); - - VGbitfield aq = getAllowedQuality(); - aq &= (VGbitfield)quality; - - Vector3 uvw(x,y,1.0f); - uvw = surfaceToImage * uvw; - RIfloat oow = 1.0f / uvw.z; - uvw *= oow; - - if(aq & VG_IMAGE_QUALITY_BETTER) - { //EWA on mipmaps - makeMipMaps(); //throws bad_alloc - - Color::InternalFormat procFormat = (Color::InternalFormat)(m_desc.internalFormat | Color::PREMULTIPLIED); - - RIfloat m_pixelFilterRadius = 1.25f; - RIfloat m_resamplingFilterRadius = 1.25f; - - RIfloat Ux = (surfaceToImage[0][0] - uvw.x * surfaceToImage[2][0]) * oow * m_pixelFilterRadius; - RIfloat Vx = (surfaceToImage[1][0] - uvw.y * surfaceToImage[2][0]) * oow * m_pixelFilterRadius; - RIfloat Uy = (surfaceToImage[0][1] - uvw.x * surfaceToImage[2][1]) * oow * m_pixelFilterRadius; - RIfloat Vy = (surfaceToImage[1][1] - uvw.y * surfaceToImage[2][1]) * oow * m_pixelFilterRadius; - RIfloat U0 = uvw.x; - RIfloat V0 = uvw.y; - - //calculate mip level - int level = 0; - RIfloat axis1sq = Ux*Ux + Vx*Vx; - RIfloat axis2sq = Uy*Uy + Vy*Vy; - RIfloat minorAxissq = RI_MIN(axis1sq,axis2sq); - while(minorAxissq > 9.0f && level < m_mipmaps.size()) //half the minor axis must be at least three texels - { - level++; - minorAxissq *= 0.25f; - } - - RIfloat sx = 1.0f; - RIfloat sy = 1.0f; - if(level > 0) - { - sx = (RIfloat)m_mipmaps[level-1]->m_width / (RIfloat)m_width; - sy = (RIfloat)m_mipmaps[level-1]->m_height / (RIfloat)m_height; - } - Ux *= sx; - Vx *= sx; - U0 *= sx; - Uy *= sy; - Vy *= sy; - V0 *= sy; - - //clamp filter size so that filtering doesn't take excessive amount of time (clamping results in aliasing) - RIfloat lim = 100.0f; - axis1sq = Ux*Ux + Vx*Vx; - axis2sq = Uy*Uy + Vy*Vy; - if( axis1sq > lim*lim ) - { - RIfloat s = lim / (RIfloat)sqrt(axis1sq); - Ux *= s; - Vx *= s; - } - if( axis2sq > lim*lim ) - { - RIfloat s = lim / (RIfloat)sqrt(axis2sq); - Uy *= s; - Vy *= s; - } - - - //form elliptic filter by combining texel and pixel filters - RIfloat A = Vx*Vx + Vy*Vy + 1.0f; - RIfloat B = -2.0f*(Ux*Vx + Uy*Vy); - RIfloat C = Ux*Ux + Uy*Uy + 1.0f; - //scale by the user-defined size of the kernel - A *= m_resamplingFilterRadius; - B *= m_resamplingFilterRadius; - C *= m_resamplingFilterRadius; - - //calculate bounding box in texture space - RIfloat usize = (RIfloat)sqrt(C); - RIfloat vsize = (RIfloat)sqrt(A); - int u1 = (int)floor(U0 - usize + 0.5f); - int u2 = (int)floor(U0 + usize + 0.5f); - int v1 = (int)floor(V0 - vsize + 0.5f); - int v2 = (int)floor(V0 + vsize + 0.5f); - if( u1 == u2 || v1 == v2 ) - return Color(0,0,0,0,procFormat); - - //scale the filter so that Q = 1 at the cutoff radius - RIfloat F = A*C - 0.25f * B*B; - if( F <= 0.0f ) - return Color(0,0,0,0,procFormat); //invalid filter shape due to numerical inaccuracies => return black - RIfloat ooF = 1.0f / F; - A *= ooF; - B *= ooF; - C *= ooF; - - //evaluate filter by using forward differences to calculate Q = A*U^2 + B*U*V + C*V^2 - Color color(0,0,0,0,procFormat); - RIfloat sumweight = 0.0f; - RIfloat DDQ = 2.0f * A; - RIfloat U = (RIfloat)u1 - U0 + 0.5f; - for(int v=v1;v= 0.0f && Q < 1.0f ) - { //Q = r^2, fit gaussian to the range [0,1] - RIfloat weight = (RIfloat)exp(-0.5f * 10.0f * Q); //gaussian at radius 10 equals 0.0067 - color += weight * readTexel(u, v, level, tilingMode, tileFillColor); - sumweight += weight; - } - Q += DQ; - DQ += DDQ; - } - } - if( sumweight == 0.0f ) - return Color(0,0,0,0,procFormat); - RI_ASSERT(sumweight > 0.0f); - sumweight = 1.0f / sumweight; - return color * sumweight; - } - else if(aq & VG_IMAGE_QUALITY_FASTER) - { //bilinear - uvw.x -= 0.5f; - uvw.y -= 0.5f; - int u = (int)floor(uvw.x); - int v = (int)floor(uvw.y); - Color c00 = readTexel(u,v, 0, tilingMode, tileFillColor); - Color c10 = readTexel(u+1,v, 0, tilingMode, tileFillColor); - Color c01 = readTexel(u,v+1, 0, tilingMode, tileFillColor); - Color c11 = readTexel(u+1,v+1, 0, tilingMode, tileFillColor); - RIfloat fu = uvw.x - (RIfloat)u; - RIfloat fv = uvw.y - (RIfloat)v; - Color c0 = c00 * (1.0f - fu) + c10 * fu; - Color c1 = c01 * (1.0f - fu) + c11 * fu; - return c0 * (1.0f - fv) + c1 * fv; - } - else - { //point sampling - return readTexel((int)floor(uvw.x), (int)floor(uvw.y), 0, tilingMode, tileFillColor); - } -} - -/*-------------------------------------------------------------------*//*! -* \brief Generates mip maps for an image. -* \param -* \return -* \note Downsampling is done in the input color space. We use a box -* filter for downsampling. -*//*-------------------------------------------------------------------*/ - -void Image::makeMipMaps() -{ - RI_ASSERT(m_data); - RI_ASSERT(m_referenceCount > 0); - - if(m_mipmapsValid) - return; - - //delete existing mipmaps - for(int i=0;iremoveReference()) - RI_DELETE(m_mipmaps[i]); - else - { - RI_ASSERT(0); //there can't be any other references to the mipmap levels - } - } - m_mipmaps.clear(); - - try - { - Color::InternalFormat procFormat = m_desc.internalFormat; - procFormat = (Color::InternalFormat)(procFormat | Color::PREMULTIPLIED); //premultiplied - - //generate mipmaps until width and height are one - Image* prev = this; - while( prev->m_width > 1 || prev->m_height > 1 ) - { - int nextw = (int)ceil(prev->m_width*0.5f); - int nexth = (int)ceil(prev->m_height*0.5f); - RI_ASSERT(nextw >= 1 && nexth >= 1); - RI_ASSERT(nextw < prev->m_width || nexth < prev->m_height); - - m_mipmaps.resize(m_mipmaps.size()+1); //throws bad_alloc - m_mipmaps[m_mipmaps.size()-1] = NULL; - - Image* next = RI_NEW(Image, (m_desc, nextw, nexth, m_allowedQuality)); //throws bad_alloc - next->addReference(); - for(int j=0;jm_height;j++) - { - for(int i=0;im_width;i++) - { - RIfloat u0 = (RIfloat)i / (RIfloat)next->m_width; - RIfloat u1 = (RIfloat)(i+1) / (RIfloat)next->m_width; - RIfloat v0 = (RIfloat)j / (RIfloat)next->m_height; - RIfloat v1 = (RIfloat)(j+1) / (RIfloat)next->m_height; - - u0 *= prev->m_width; - u1 *= prev->m_width; - v0 *= prev->m_height; - v1 *= prev->m_height; - - int su = (int)floor(u0); - int eu = (int)ceil(u1); - int sv = (int)floor(v0); - int ev = (int)ceil(v1); - - Color c(0,0,0,0,procFormat); - int samples = 0; - for(int y=sv;yreadPixel(x, y); - p.convert(procFormat); - c += p; - samples++; - } - } - c *= (1.0f/samples); - c.convert(m_desc.internalFormat); - next->writePixel(i,j,c); - } - } - m_mipmaps[m_mipmaps.size()-1] = next; - prev = next; - } - RI_ASSERT(prev->m_width == 1 && prev->m_height == 1); - m_mipmapsValid = true; - } - catch(std::bad_alloc) - { - //delete existing mipmaps - for(int i=0;iremoveReference()) - RI_DELETE(m_mipmaps[i]); - else - { - RI_ASSERT(0); //there can't be any other references to the mipmap levels - } - } - } - m_mipmaps.clear(); - m_mipmapsValid = false; - throw; - } -} - -/*-------------------------------------------------------------------*//*! -* \brief Applies color matrix filter. -* \param -* \return -* \note -*//*-------------------------------------------------------------------*/ - -void Image::colorMatrix(const Image& src, const RIfloat* matrix, bool filterFormatLinear, bool filterFormatPremultiplied, VGbitfield channelMask) -{ - RI_ASSERT(src.m_data); //source exists - RI_ASSERT(m_data); //destination exists - RI_ASSERT(matrix); - RI_ASSERT(m_referenceCount > 0 && src.m_referenceCount > 0); - - int w = RI_INT_MIN(m_width, src.m_width); - int h = RI_INT_MIN(m_height, src.m_height); - RI_ASSERT(w > 0 && h > 0); - - Color::InternalFormat srcFormat = src.m_desc.internalFormat; - Color::InternalFormat procFormat = (Color::InternalFormat)(srcFormat & ~Color::LUMINANCE); //process in RGB, not luminance - if(filterFormatLinear) - procFormat = (Color::InternalFormat)(procFormat & ~Color::NONLINEAR); - else - procFormat = (Color::InternalFormat)(procFormat | Color::NONLINEAR); - - if(filterFormatPremultiplied) - procFormat = (Color::InternalFormat)(procFormat | Color::PREMULTIPLIED); - else - procFormat = (Color::InternalFormat)(procFormat & ~Color::PREMULTIPLIED); - - for(int j=0;j& image, const Color& edge) -{ - Color s; - if(x < 0 || x >= w || y < 0 || y >= h) - { //apply tiling mode - switch(tilingMode) - { - case VG_TILE_FILL: - s = edge; - break; - case VG_TILE_PAD: - x = RI_INT_MIN(RI_INT_MAX(x, 0), w-1); - y = RI_INT_MIN(RI_INT_MAX(y, 0), h-1); - RI_ASSERT(x >= 0 && x < w && y >= 0 && y < h); - s = image[y*w+x]; - break; - case VG_TILE_REPEAT: - x = RI_INT_MOD(x, w); - y = RI_INT_MOD(y, h); - RI_ASSERT(x >= 0 && x < w && y >= 0 && y < h); - s = image[y*w+x]; - break; - default: - RI_ASSERT(tilingMode == VG_TILE_REFLECT); - x = RI_INT_MOD(x, w*2); - y = RI_INT_MOD(y, h*2); - if(x >= w) x = w*2-1-x; - if(y >= h) y = h*2-1-y; - RI_ASSERT(x >= 0 && x < w && y >= 0 && y < h); - s = image[y*w+x]; - break; - } - } - else - { - RI_ASSERT(x >= 0 && x < w && y >= 0 && y < h); - s = image[y*w+x]; - } - return s; -} - -/*-------------------------------------------------------------------*//*! -* \brief Returns processing format for filtering. -* \param -* \return -* \note -*//*-------------------------------------------------------------------*/ - -static Color::InternalFormat getProcessingFormat(Color::InternalFormat srcFormat, bool filterFormatLinear, bool filterFormatPremultiplied) -{ - Color::InternalFormat procFormat = (Color::InternalFormat)(srcFormat & ~Color::LUMINANCE); //process in RGB, not luminance - if(filterFormatLinear) - procFormat = (Color::InternalFormat)(procFormat & ~Color::NONLINEAR); - else - procFormat = (Color::InternalFormat)(procFormat | Color::NONLINEAR); - - if(filterFormatPremultiplied) - procFormat = (Color::InternalFormat)(procFormat | Color::PREMULTIPLIED); - else - procFormat = (Color::InternalFormat)(procFormat & ~Color::PREMULTIPLIED); - return procFormat; -} - -/*-------------------------------------------------------------------*//*! -* \brief Applies convolution filter. -* \param -* \return -* \note -*//*-------------------------------------------------------------------*/ - -void Image::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) -{ - RI_ASSERT(src.m_data); //source exists - RI_ASSERT(m_data); //destination exists - RI_ASSERT(kernel && kernelWidth > 0 && kernelHeight > 0); - RI_ASSERT(m_referenceCount > 0 && src.m_referenceCount > 0); - - //the area to be written is an intersection of source and destination image areas. - //lower-left corners of the images are aligned. - int w = RI_INT_MIN(m_width, src.m_width); - int h = RI_INT_MIN(m_height, src.m_height); - RI_ASSERT(w > 0 && h > 0); - - Color::InternalFormat procFormat = getProcessingFormat(src.m_desc.internalFormat, filterFormatLinear, filterFormatPremultiplied); - - Color edge = edgeFillColor; - edge.clamp(); - edge.convert(procFormat); - - Array tmp; - tmp.resize(src.m_width*src.m_height); //throws bad_alloc - - //copy source region to tmp and do conversion - for(int j=0;j= 0 && kx < kernelWidth && ky >= 0 && ky < kernelHeight); - - sum += (RIfloat)kernel[kx*kernelHeight+ky] * s; - } - } - - sum *= scale; - sum.r += bias; - sum.g += bias; - sum.b += bias; - sum.a += bias; - - writeFilteredPixel(i, j, sum, channelMask); - } - } -} - -/*-------------------------------------------------------------------*//*! -* \brief Applies separable convolution filter. -* \param -* \return -* \note -*//*-------------------------------------------------------------------*/ - -void Image::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) -{ - RI_ASSERT(src.m_data); //source exists - RI_ASSERT(m_data); //destination exists - RI_ASSERT(kernelX && kernelY && kernelWidth > 0 && kernelHeight > 0); - RI_ASSERT(m_referenceCount > 0 && src.m_referenceCount > 0); - - //the area to be written is an intersection of source and destination image areas. - //lower-left corners of the images are aligned. - int w = RI_INT_MIN(m_width, src.m_width); - int h = RI_INT_MIN(m_height, src.m_height); - RI_ASSERT(w > 0 && h > 0); - - Color::InternalFormat procFormat = getProcessingFormat(src.m_desc.internalFormat, filterFormatLinear, filterFormatPremultiplied); - - Color edge = edgeFillColor; - edge.clamp(); - edge.convert(procFormat); - - Array tmp; - tmp.resize(src.m_width*src.m_height); //throws bad_alloc - - //copy source region to tmp and do conversion - for(int j=0;j tmp2; - tmp2.resize(w*src.m_height); //throws bad_alloc - for(int j=0;j= 0 && kx < kernelWidth); - - sum += (RIfloat)kernelX[kx] * s; - } - tmp2[j*w+i] = sum; - } - } - - if(tilingMode == VG_TILE_FILL) - { //convolve the edge color - Color sum(0,0,0,0,procFormat); - for(int ki=0;ki= 0 && ky < kernelHeight); - - sum += (RIfloat)kernelY[ky] * s; - } - - sum *= scale; - sum.r += bias; - sum.g += bias; - sum.b += bias; - sum.a += bias; - - writeFilteredPixel(i, j, sum, channelMask); - } - } -} - -/*-------------------------------------------------------------------*//*! -* \brief Applies Gaussian blur filter. -* \param -* \return -* \note -*//*-------------------------------------------------------------------*/ - -void Image::gaussianBlur(const Image& src, RIfloat stdDeviationX, RIfloat stdDeviationY, VGTilingMode tilingMode, const Color& edgeFillColor, bool filterFormatLinear, bool filterFormatPremultiplied, VGbitfield channelMask) -{ - RI_ASSERT(src.m_data); //source exists - RI_ASSERT(m_data); //destination exists - RI_ASSERT(stdDeviationX > 0.0f && stdDeviationY > 0.0f); - RI_ASSERT(stdDeviationX <= RI_MAX_GAUSSIAN_STD_DEVIATION && stdDeviationY <= RI_MAX_GAUSSIAN_STD_DEVIATION); - RI_ASSERT(m_referenceCount > 0 && src.m_referenceCount > 0); - - //the area to be written is an intersection of source and destination image areas. - //lower-left corners of the images are aligned. - int w = RI_INT_MIN(m_width, src.m_width); - int h = RI_INT_MIN(m_height, src.m_height); - RI_ASSERT(w > 0 && h > 0); - - Color::InternalFormat procFormat = getProcessingFormat(src.m_desc.internalFormat, filterFormatLinear, filterFormatPremultiplied); - - Color edge = edgeFillColor; - edge.clamp(); - edge.convert(procFormat); - - Array tmp; - tmp.resize(src.m_width*src.m_height); //throws bad_alloc - - //copy source region to tmp and do conversion - for(int j=0;j kernelX; - kernelX.resize(kernelWidth*2+1); - int shiftX = kernelWidth; - RIfloat scaleX = 0.0f; - for(int i=0;i kernelY; - kernelY.resize(kernelHeight*2+1); - int shiftY = kernelHeight; - RIfloat scaleY = 0.0f; - for(int i=0;i tmp2; - tmp2.resize(w*src.m_height); //throws bad_alloc - //horizontal pass - for(int j=0;j 0 && src.m_referenceCount > 0); - - //the area to be written is an intersection of source and destination image areas. - //lower-left corners of the images are aligned. - int w = RI_INT_MIN(m_width, src.m_width); - int h = RI_INT_MIN(m_height, src.m_height); - RI_ASSERT(w > 0 && h > 0); - - Color::InternalFormat procFormat = getProcessingFormat(src.m_desc.internalFormat, filterFormatLinear, filterFormatPremultiplied); - Color::InternalFormat lutFormat = getLUTFormat(outputLinear, outputPremultiplied); - - for(int j=0;j 0 && src.m_referenceCount > 0); - - //the area to be written is an intersection of source and destination image areas. - //lower-left corners of the images are aligned. - int w = RI_INT_MIN(m_width, src.m_width); - int h = RI_INT_MIN(m_height, src.m_height); - RI_ASSERT(w > 0 && h > 0); - - if(src.m_desc.isLuminance()) - sourceChannel = VG_RED; - else if(src.m_desc.redBits + src.m_desc.greenBits + src.m_desc.blueBits == 0) - { - RI_ASSERT(src.m_desc.alphaBits); - sourceChannel = VG_ALPHA; - } - - Color::InternalFormat procFormat = getProcessingFormat(src.m_desc.internalFormat, filterFormatLinear, filterFormatPremultiplied); - Color::InternalFormat lutFormat = getLUTFormat(outputLinear, outputPremultiplied); - - for(int j=0;j>24), 255); - d.g = intToColor((l>>16), 255); - d.b = intToColor((l>> 8), 255); - d.a = intToColor((l ), 255); - - writeFilteredPixel(i, j, d, channelMask); - } - } -} - - -/*-------------------------------------------------------------------*//*! -* \brief -* \param -* \return -* \note -*//*-------------------------------------------------------------------*/ - -Surface::Surface(const Color::Descriptor& desc, int width, int height, int numSamples) : - m_width(width), - m_height(height), - m_numSamples(numSamples), - m_referenceCount(0), - m_image(NULL) -{ - RI_ASSERT(width > 0 && height > 0 && numSamples > 0 && numSamples <= 32); - m_image = RI_NEW(Image, (desc, width*numSamples, height, 0)); //throws bad_alloc - m_image->addReference(); -} - -/*-------------------------------------------------------------------*//*! -* \brief -* \param -* \return -* \note -*//*-------------------------------------------------------------------*/ - -Surface::Surface(Image* image) : - m_width(0), - m_height(0), - m_numSamples(1), - m_referenceCount(0), - m_image(image) -{ - RI_ASSERT(image); - m_width = image->getWidth(); - m_height = image->getHeight(); - m_image->addReference(); -} - -/*-------------------------------------------------------------------*//*! -* \brief -* \param -* \return -* \note -*//*-------------------------------------------------------------------*/ - -Surface::Surface(const Color::Descriptor& desc, int width, int height, int stride, RIuint8* data) : - m_width(width), - m_height(height), - m_numSamples(1), - m_referenceCount(0), - m_image(NULL) -{ - RI_ASSERT(width > 0 && height > 0); - m_image = RI_NEW(Image, (desc, width, height, stride, data)); //throws bad_alloc - m_image->addReference(); -} - -/*-------------------------------------------------------------------*//*! -* \brief -* \param -* \return -* \note -*//*-------------------------------------------------------------------*/ - -Surface::~Surface() -{ - RI_ASSERT(m_referenceCount == 0); - if(!m_image->removeReference()) - RI_DELETE(m_image); -} - -/*-------------------------------------------------------------------*//*! -* \brief -* \param -* \return -* \note -*//*-------------------------------------------------------------------*/ - -void Surface::clear(const Color& clearColor, int x, int y, int w, int h) -{ - Rectangle rect; - rect.x = 0; - rect.y = 0; - rect.width = getWidth(); - rect.height = getHeight(); - Array scissors; - scissors.push_back(rect); - clear(clearColor, x, y, w, h, scissors); -} - -/*-------------------------------------------------------------------*//*! -* \brief -* \param -* \return -* \note -*//*-------------------------------------------------------------------*/ - -void Surface::clear(const Color& clearColor, int x, int y, int w, int h, const Array& scissors) -{ - RI_ASSERT(w > 0 && h > 0); - - //intersect clear region with image bounds - Rectangle r(0,0,getWidth(),getHeight()); - r.intersect(Rectangle(x,y,w,h)); - if(!r.width || !r.height) - return; //intersection is empty or one of the rectangles is invalid - - Array scissorEdges; - for(int i=0;i 0 && scissors[i].height > 0) - { - ScissorEdge e; - e.miny = scissors[i].y; - e.maxy = RI_INT_ADDSATURATE(scissors[i].y, scissors[i].height); - - e.x = scissors[i].x; - e.direction = 1; - scissorEdges.push_back(e); //throws bad_alloc - e.x = RI_INT_ADDSATURATE(scissors[i].x, scissors[i].width); - e.direction = -1; - scissorEdges.push_back(e); //throws bad_alloc - } - } - if(!scissorEdges.size()) - return; //there are no scissor rectangles => nothing is visible - - //sort scissor edges by edge x - scissorEdges.sort(); - - //clear the image - Color col = clearColor; - col.clamp(); - col.convert(m_image->getDescriptor().internalFormat); - - Array scissorAet; - for(int j=r.y;j= se.miny && j < se.maxy) - scissorAet.push_back(scissorEdges[e]); //throws bad_alloc - } - if(!scissorAet.size()) - continue; //scissoring is on, but there are no scissor rectangles on this scanline - - //clear a scanline - int scissorWinding = 0; - int scissorIndex = 0; - for(int i=r.x;i= 0); - - if(scissorWinding) - { - for(int s=0;s scissors; - scissors.push_back(rect); - blit(src, sx, sy, dx, dy, w, h, scissors); -} - -/*-------------------------------------------------------------------*//*! -* \brief -* \param -* \return -* \note no overlap is possible. Single sample to single or multisample (replicate) -*//*-------------------------------------------------------------------*/ - -void Surface::blit(const Image& src, int sx, int sy, int dx, int dy, int w, int h, const Array& scissors) -{ - //img=>fb: vgSetPixels - //user=>fb: vgWritePixels - computeBlitRegion(sx, sy, dx, dy, w, h, src.getWidth(), src.getHeight(), getWidth(), getHeight()); - if(w <= 0 || h <= 0) - return; //zero area - - Array scissorEdges; - for(int i=0;i 0 && scissors[i].height > 0) - { - ScissorEdge e; - e.miny = scissors[i].y; - e.maxy = RI_INT_ADDSATURATE(scissors[i].y, scissors[i].height); - - e.x = scissors[i].x; - e.direction = 1; - scissorEdges.push_back(e); //throws bad_alloc - e.x = RI_INT_ADDSATURATE(scissors[i].x, scissors[i].width); - e.direction = -1; - scissorEdges.push_back(e); //throws bad_alloc - } - } - if(!scissorEdges.size()) - return; //there are no scissor rectangles => nothing is visible - - //sort scissor edges by edge x - scissorEdges.sort(); - - Array scissorAet; - for(int j=0;j= se.miny && dy + j < se.maxy) - scissorAet.push_back(scissorEdges[e]); //throws bad_alloc - } - if(!scissorAet.size()) - continue; //scissoring is on, but there are no scissor rectangles on this scanline - - //blit a scanline - int scissorWinding = 0; - int scissorIndex = 0; - for(int i=0;i= 0); - - if(scissorWinding) - { - Color c = src.readPixel(sx + i, sy + j); - c.convert(getDescriptor().internalFormat); - for(int s=0;s scissors; - scissors.push_back(rect); - blit(src, sx, sy, dx, dy, w, h, scissors); -} - -/*-------------------------------------------------------------------*//*! -* \brief -* \param -* \return -* \note -*//*-------------------------------------------------------------------*/ - -void Surface::blit(const Surface* src, int sx, int sy, int dx, int dy, int w, int h, const Array& scissors) -{ - RI_ASSERT(m_numSamples == src->m_numSamples); - - //fb=>fb: vgCopyPixels - computeBlitRegion(sx, sy, dx, dy, w, h, src->getWidth(), src->getHeight(), getWidth(), getHeight()); - if(w <= 0 || h <= 0) - return; //zero area - - Array scissorEdges; - for(int i=0;i 0 && scissors[i].height > 0) - { - ScissorEdge e; - e.miny = scissors[i].y; - e.maxy = RI_INT_ADDSATURATE(scissors[i].y, scissors[i].height); - - e.x = scissors[i].x; - e.direction = 1; - scissorEdges.push_back(e); //throws bad_alloc - e.x = RI_INT_ADDSATURATE(scissors[i].x, scissors[i].width); - e.direction = -1; - scissorEdges.push_back(e); //throws bad_alloc - } - } - if(!scissorEdges.size()) - return; //there are no scissor rectangles => nothing is visible - - //sort scissor edges by edge x - scissorEdges.sort(); - - Array tmp; - tmp.resize(w*m_numSamples*h); //throws bad_alloc - - //copy source region to tmp - for(int j=0;jm_image->readPixel((sx + i)*m_numSamples+s, sy + j); - c.convert(m_image->getDescriptor().internalFormat); - tmp[(j*w+i)*m_numSamples+s] = c; - } - } - } - - Array scissorAet; - for(int j=0;j= se.miny && dy + j < se.maxy) - scissorAet.push_back(scissorEdges[e]); //throws bad_alloc - } - if(!scissorAet.size()) - continue; //scissoring is on, but there are no scissor rectangles on this scanline - - //blit a scanline - int scissorWinding = 0; - int scissorIndex = 0; - for(int i=0;i= 0); - - if(scissorWinding) - { - int numSamples = m_numSamples; - for(int s=0;swritePixel((dx + i)*m_numSamples+s, dy + j, tmp[(j*w+i)*m_numSamples+s]); - } - } - } - } -} - -/*-------------------------------------------------------------------*//*! -* \brief -* \param -* \return -* \note -*//*-------------------------------------------------------------------*/ - -void Surface::mask(const Image* src, VGMaskOperation operation, int x, int y, int w, int h) -{ - RI_ASSERT(w > 0 && h > 0); - - if(operation == VG_CLEAR_MASK || operation == VG_FILL_MASK) - { - //intersect clear region with image bounds - Rectangle r(0,0,getWidth(),getHeight()); - r.intersect(Rectangle(x,y,w,h)); - if(!r.width || !r.height) - return; //intersection is empty or one of the rectangles is invalid - - if(m_numSamples == 1) - { - RIfloat m = 0.0f; - if(operation == VG_FILL_MASK) - m = 1.0f; - for(int j=r.y;jgetWidth(), src->getHeight(), getWidth(), getHeight()); - if(w <= 0 || h <= 0) - return; //zero area - - if(m_numSamples == 1) - { - for(int j=0;jreadMaskPixel(sx + i, sy + j); - if(operation == VG_SET_MASK) - writeMaskCoverage(dx + i, dy + j, amask); - else - { - RIfloat aprev = readMaskCoverage(dx + i, dy + j); - RIfloat anew = 0.0f; - switch(operation) - { - case VG_UNION_MASK: anew = 1.0f - (1.0f - amask)*(1.0f - aprev); break; - case VG_INTERSECT_MASK: anew = amask * aprev; break; - default: anew = aprev * (1.0f - amask); RI_ASSERT(operation == VG_SUBTRACT_MASK); break; - } - writeMaskCoverage(dx + i, dy + j, anew); - } - } - } - } - else - { - for(int j=0;jreadMaskPixel(sx + i, sy + j); - //TODO implement dithering? - unsigned int amask = fmask > 0.5f ? (1< 0 && h > 0); - - if(operation == VG_CLEAR_MASK || operation == VG_FILL_MASK) - { - //intersect clear region with image bounds - Rectangle r(0,0,getWidth(),getHeight()); - r.intersect(Rectangle(x,y,w,h)); - if(!r.width || !r.height) - return; //intersection is empty or one of the rectangles is invalid - - if(m_numSamples == 1) - { - RIfloat m = 0.0f; - if(operation == VG_FILL_MASK) - m = 1.0f; - for(int j=r.y;jm_numSamples); - - int sx = 0, sy = 0, dx = x, dy = y; - computeBlitRegion(sx, sy, dx, dy, w, h, src->getWidth(), src->getHeight(), getWidth(), getHeight()); - if(w <= 0 || h <= 0) - return; //zero area - - if(m_numSamples == 1) - { - for(int j=0;jreadMaskCoverage(sx + i, sy + j); - if(operation == VG_SET_MASK) - writeMaskCoverage(dx + i, dy + j, amask); - else - { - RIfloat aprev = readMaskCoverage(dx + i, dy + j); - RIfloat anew = 0.0f; - switch(operation) - { - case VG_UNION_MASK: anew = 1.0f - (1.0f - amask)*(1.0f - aprev); break; - case VG_INTERSECT_MASK: anew = amask * aprev; break; - default: anew = aprev * (1.0f - amask); RI_ASSERT(operation == VG_SUBTRACT_MASK); break; - } - writeMaskCoverage(dx + i, dy + j, anew); - } - } - } - } - else - { - for(int j=0;jreadMaskMSAA(sx + i, sy + j); - if(operation == VG_SET_MASK) - writeMaskMSAA(dx + i, dy + j, amask); - else - { - unsigned int aprev = readMaskMSAA(dx + i, dy + j); - unsigned int anew = 0; - switch(operation) - { - case VG_UNION_MASK: anew = amask | aprev; break; - case VG_INTERSECT_MASK: anew = amask & aprev; break; - default: anew = ~amask & aprev; RI_ASSERT(operation == VG_SUBTRACT_MASK); break; - } - writeMaskMSAA(dx + i, dy + j, anew); - } - } - } - } - } -} - -/*-------------------------------------------------------------------*//*! -* \brief -* \param -* \return -* \note -*//*-------------------------------------------------------------------*/ - -RIfloat Surface::readMaskCoverage(int x, int y) const -{ - RI_ASSERT(x >= 0 && x < m_width && y >= 0 && y < m_height); - RI_ASSERT(m_numSamples == 1); - return m_image->readMaskPixel(x, y); -} - -void Surface::writeMaskCoverage(int x, int y, RIfloat m) -{ - RI_ASSERT(x >= 0 && x < m_width && y >= 0 && y < m_height); - RI_ASSERT(m_numSamples == 1); - m_image->writeMaskPixel(x, y, m); //TODO support other than alpha formats but don't write to color channels? -} - -unsigned int Surface::readMaskMSAA(int x, int y) const -{ - RI_ASSERT(x >= 0 && x < m_width && y >= 0 && y < m_height); - RI_ASSERT(m_numSamples > 1); - unsigned int m = 0; - for(int i=0;ireadMaskPixel(x*m_numSamples+i, y) > 0.5f) //TODO is this the right formula for converting alpha to bit mask? does it matter? - m |= 1<= 0 && x < m_width && y >= 0 && y < m_height); - RI_ASSERT(m_numSamples > 1); - for(int i=0;iwriteMaskPixel(x*m_numSamples+i, y, a); //TODO support other than alpha formats but don't write to color channels? - } -} - -/*-------------------------------------------------------------------*//*! -* \brief -* \param -* \return -* \note -*//*-------------------------------------------------------------------*/ - -Color Surface::FSAAResolve(int x, int y) const -{ - if(m_numSamples == 1) - return readSample(x, y, 0); - - Color::InternalFormat aaFormat = getDescriptor().isLuminance() ? Color::lLA_PRE : Color::lRGBA_PRE; //antialias in linear color space - Color r(0.0f, 0.0f, 0.0f, 0.0f, aaFormat); - for(int i=0;i 0 && height > 0 && numSamples > 0 && numSamples <= 32); - RI_ASSERT(maskBits == 0 || maskBits == 1 || maskBits == 4 || maskBits == 8); - m_color = RI_NEW(Surface, (desc, width, height, numSamples)); //throws bad_alloc - m_color->addReference(); - if(maskBits) - { - VGImageFormat mf = VG_A_1; - if(maskBits == 4) - mf = VG_A_4; - else if(maskBits == 8) - mf = VG_A_8; - m_mask = RI_NEW(Surface, (Color::formatToDescriptor(mf), width, height, numSamples)); - m_mask->addReference(); - m_mask->clear(Color(1,1,1,1,Color::sRGBA), 0, 0, width, height); - } -} - -/*-------------------------------------------------------------------*//*! -* \brief -* \param -* \return -* \note -*//*-------------------------------------------------------------------*/ - -Drawable::Drawable(Image* image, int maskBits) : - m_referenceCount(0), - m_color(NULL), - m_mask(NULL) -{ - RI_ASSERT(maskBits == 0 || maskBits == 1 || maskBits == 4 || maskBits == 8); - RI_ASSERT(image); - m_color = RI_NEW(Surface, (image)); - m_color->addReference(); - if(maskBits) - { - VGImageFormat mf = VG_A_1; - if(maskBits == 4) - mf = VG_A_4; - else if(maskBits == 8) - mf = VG_A_8; - m_mask = RI_NEW(Surface, (Color::formatToDescriptor(mf), image->getWidth(), image->getHeight(), 1)); - m_mask->addReference(); - m_mask->clear(Color(1,1,1,1,Color::sRGBA), 0, 0, image->getWidth(), image->getHeight()); - } -} - -/*-------------------------------------------------------------------*//*! -* \brief -* \param -* \return -* \note -*//*-------------------------------------------------------------------*/ - -Drawable::Drawable(const Color::Descriptor& desc, int width, int height, int stride, RIuint8* data, int maskBits) : - m_referenceCount(0), - m_color(NULL), - m_mask(NULL) -{ - RI_ASSERT(width > 0 && height > 0); - RI_ASSERT(maskBits == 0 || maskBits == 1 || maskBits == 4 || maskBits == 8); - m_color = RI_NEW(Surface, (desc, width, height, stride, data)); //throws bad_alloc - m_color->addReference(); - if(maskBits) - { - VGImageFormat mf = VG_A_1; - if(maskBits == 4) - mf = VG_A_4; - else if(maskBits == 8) - mf = VG_A_8; - m_mask = RI_NEW(Surface, (Color::formatToDescriptor(mf), width, height, 1)); - m_mask->addReference(); - m_mask->clear(Color(1,1,1,1,Color::sRGBA), 0, 0, width, height); - } -} - -/*-------------------------------------------------------------------*//*! -* \brief -* \param -* \return -* \note -*//*-------------------------------------------------------------------*/ - -Drawable::~Drawable() -{ - RI_ASSERT(m_referenceCount == 0); - if(!m_color->removeReference()) - RI_DELETE(m_color); - if(m_mask) - if(!m_mask->removeReference()) - RI_DELETE(m_mask); -} - -/*-------------------------------------------------------------------*//*! -* \brief -* \param -* \return -* \note -*//*-------------------------------------------------------------------*/ - -void Drawable::resize(int newWidth, int newHeight) -{ - Surface* oldcolor = m_color; - Surface* oldmask = m_mask; - int oldWidth = m_color->getWidth(); - int oldHeight = m_color->getHeight(); - - //TODO check that image is not a proxy - m_color = RI_NEW(Surface, (m_color->getDescriptor(), newWidth, newHeight, m_color->getNumSamples())); - m_color->addReference(); - if(m_mask) - { - m_mask = RI_NEW(Surface, (m_mask->getDescriptor(), newWidth, newHeight, m_mask->getNumSamples())); - m_mask->addReference(); - } - - int wmin = RI_INT_MIN(newWidth,oldWidth); - int hmin = RI_INT_MIN(newHeight,oldHeight); - m_color->clear(Color(0.0f, 0.0f, 0.0f, 0.0f, getDescriptor().internalFormat), 0, 0, m_color->getWidth(), m_color->getHeight()); - m_color->blit(oldcolor, 0, 0, 0, 0, wmin, hmin); - if(m_mask) - { - m_mask->clear(Color(1.0f, 1.0f, 1.0f, 1.0f, getDescriptor().internalFormat), 0, 0, m_mask->getWidth(), m_mask->getHeight()); - m_mask->blit(oldmask, 0, 0, 0, 0, wmin, hmin); - } - - if(!oldcolor->removeReference()) - RI_DELETE(oldcolor); - if(oldmask) - if(!oldmask->removeReference()) - RI_DELETE(oldmask); -} - -//============================================================================================== - -} //namespace OpenVGRI - -//==============================================================================================