/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ #include #include #include #include #include #include #include #include #include #include #include #include using namespace vcl::unotools; using namespace ::com::sun::star; namespace { // TODO(Q3): move to o3tl bithacks or somesuch. A similar method is in canvas/canvastools.hxx // Good ole HAKMEM tradition. Calc number of 1 bits in 32bit word, // unrolled loop. See e.g. Hackers Delight, p. 66 inline sal_Int32 bitcount( sal_uInt32 val ) { val = val - ((val >> 1) & 0x55555555); val = (val & 0x33333333) + ((val >> 2) & 0x33333333); val = (val + (val >> 4)) & 0x0F0F0F0F; val = val + (val >> 8); val = val + (val >> 16); return sal_Int32(val & 0x0000003F); } } void VclCanvasBitmap::setComponentInfo( sal_uInt32 redShift, sal_uInt32 greenShift, sal_uInt32 blueShift ) { // sort channels in increasing order of appearance in the pixel // (starting with the least significant bits) sal_Int8 redPos(0); sal_Int8 greenPos(1); sal_Int8 bluePos(2); if( redShift > greenShift ) { std::swap(redPos,greenPos); if( redShift > blueShift ) { std::swap(redPos,bluePos); if( greenShift > blueShift ) std::swap(greenPos,bluePos); } } else { if( greenShift > blueShift ) { std::swap(greenPos,bluePos); if( redShift > blueShift ) std::swap(redPos,bluePos); } } m_aComponentTags.realloc(3); sal_Int8* pTags = m_aComponentTags.getArray(); pTags[redPos] = rendering::ColorComponentTag::RGB_RED; pTags[greenPos] = rendering::ColorComponentTag::RGB_GREEN; pTags[bluePos] = rendering::ColorComponentTag::RGB_BLUE; m_aComponentBitCounts.realloc(3); sal_Int32* pCounts = m_aComponentBitCounts.getArray(); pCounts[redPos] = bitcount(redShift); pCounts[greenPos] = bitcount(greenShift); pCounts[bluePos] = bitcount(blueShift); } VclCanvasBitmap::VclCanvasBitmap( const BitmapEx& rBitmap ) : m_aBmpEx( rBitmap ), m_aBitmap( rBitmap.GetBitmap() ), m_aAlpha(), m_pBmpAcc( m_aBitmap.AcquireReadAccess() ), m_pAlphaAcc( nullptr ), m_aComponentTags(), m_aComponentBitCounts(), m_aLayout(), m_nBitsPerInputPixel(0), m_nBitsPerOutputPixel(0), m_nRedIndex(-1), m_nGreenIndex(-1), m_nBlueIndex(-1), m_nAlphaIndex(-1), m_nIndexIndex(-1), m_nEndianness(0), m_bPalette(false) { if( m_aBmpEx.IsTransparent() ) { m_aAlpha = m_aBmpEx.IsAlpha() ? m_aBmpEx.GetAlpha().GetBitmap() : m_aBmpEx.GetMask(); m_pAlphaAcc = m_aAlpha.AcquireReadAccess(); } m_aLayout.ScanLines = 0; m_aLayout.ScanLineBytes = 0; m_aLayout.ScanLineStride = 0; m_aLayout.PlaneStride = 0; m_aLayout.ColorSpace.clear(); m_aLayout.Palette.clear(); m_aLayout.IsMsbFirst = false; if( m_pBmpAcc ) { m_aLayout.ScanLines = m_pBmpAcc->Height(); m_aLayout.ScanLineBytes = (m_pBmpAcc->GetBitCount()*m_pBmpAcc->Width() + 7) / 8; m_aLayout.ScanLineStride = m_pBmpAcc->GetScanlineSize(); m_aLayout.PlaneStride = 0; switch( m_pBmpAcc->GetScanlineFormat() ) { case BMP_FORMAT_1BIT_MSB_PAL: m_bPalette = true; m_nBitsPerInputPixel = 1; m_nEndianness = util::Endianness::LITTLE; // doesn't matter m_aLayout.IsMsbFirst = true; break; case BMP_FORMAT_1BIT_LSB_PAL: m_bPalette = true; m_nBitsPerInputPixel = 1; m_nEndianness = util::Endianness::LITTLE; // doesn't matter m_aLayout.IsMsbFirst = false; break; case BMP_FORMAT_4BIT_MSN_PAL: m_bPalette = true; m_nBitsPerInputPixel = 4; m_nEndianness = util::Endianness::LITTLE; // doesn't matter m_aLayout.IsMsbFirst = true; break; case BMP_FORMAT_4BIT_LSN_PAL: m_bPalette = true; m_nBitsPerInputPixel = 4; m_nEndianness = util::Endianness::LITTLE; // doesn't matter m_aLayout.IsMsbFirst = false; break; case BMP_FORMAT_8BIT_PAL: m_bPalette = true; m_nBitsPerInputPixel = 8; m_nEndianness = util::Endianness::LITTLE; // doesn't matter m_aLayout.IsMsbFirst = false; // doesn't matter break; case BMP_FORMAT_8BIT_TC_MASK: m_bPalette = false; m_nBitsPerInputPixel = 8; m_nEndianness = util::Endianness::LITTLE; // doesn't matter m_aLayout.IsMsbFirst = false; // doesn't matter setComponentInfo( m_pBmpAcc->GetColorMask().GetRedMask(), m_pBmpAcc->GetColorMask().GetGreenMask(), m_pBmpAcc->GetColorMask().GetBlueMask() ); break; case BMP_FORMAT_16BIT_TC_MSB_MASK: m_bPalette = false; m_nBitsPerInputPixel = 16; m_nEndianness = util::Endianness::BIG; m_aLayout.IsMsbFirst = false; // doesn't matter setComponentInfo( m_pBmpAcc->GetColorMask().GetRedMask(), m_pBmpAcc->GetColorMask().GetGreenMask(), m_pBmpAcc->GetColorMask().GetBlueMask() ); break; case BMP_FORMAT_16BIT_TC_LSB_MASK: m_bPalette = false; m_nBitsPerInputPixel = 16; m_nEndianness = util::Endianness::LITTLE; m_aLayout.IsMsbFirst = false; // doesn't matter setComponentInfo( m_pBmpAcc->GetColorMask().GetRedMask(), m_pBmpAcc->GetColorMask().GetGreenMask(), m_pBmpAcc->GetColorMask().GetBlueMask() ); break; case BMP_FORMAT_24BIT_TC_BGR: m_bPalette = false; m_nBitsPerInputPixel = 24; m_nEndianness = util::Endianness::LITTLE; m_aLayout.IsMsbFirst = false; // doesn't matter setComponentInfo( static_cast(0xff0000UL), static_cast(0x00ff00UL), static_cast(0x0000ffUL) ); break; case BMP_FORMAT_24BIT_TC_RGB: m_bPalette = false; m_nBitsPerInputPixel = 24; m_nEndianness = util::Endianness::LITTLE; m_aLayout.IsMsbFirst = false; // doesn't matter setComponentInfo( static_cast(0x0000ffUL), static_cast(0x00ff00UL), static_cast(0xff0000UL) ); break; case BMP_FORMAT_24BIT_TC_MASK: m_bPalette = false; m_nBitsPerInputPixel = 24; m_nEndianness = util::Endianness::LITTLE; m_aLayout.IsMsbFirst = false; // doesn't matter setComponentInfo( m_pBmpAcc->GetColorMask().GetRedMask(), m_pBmpAcc->GetColorMask().GetGreenMask(), m_pBmpAcc->GetColorMask().GetBlueMask() ); break; case BMP_FORMAT_32BIT_TC_ABGR: { m_bPalette = false; m_nBitsPerInputPixel = 32; m_nEndianness = util::Endianness::LITTLE; m_aLayout.IsMsbFirst = false; // doesn't matter m_aComponentTags.realloc(4); sal_Int8* pTags = m_aComponentTags.getArray(); pTags[0] = rendering::ColorComponentTag::ALPHA; pTags[1] = rendering::ColorComponentTag::RGB_BLUE; pTags[2] = rendering::ColorComponentTag::RGB_GREEN; pTags[3] = rendering::ColorComponentTag::RGB_RED; m_aComponentBitCounts.realloc(4); sal_Int32* pCounts = m_aComponentBitCounts.getArray(); pCounts[0] = 8; pCounts[1] = 8; pCounts[2] = 8; pCounts[3] = 8; m_nRedIndex = 3; m_nGreenIndex = 2; m_nBlueIndex = 1; m_nAlphaIndex = 0; } break; case BMP_FORMAT_32BIT_TC_ARGB: { m_bPalette = false; m_nBitsPerInputPixel = 32; m_nEndianness = util::Endianness::LITTLE; m_aLayout.IsMsbFirst = false; // doesn't matter m_aComponentTags.realloc(4); sal_Int8* pTags = m_aComponentTags.getArray(); pTags[0] = rendering::ColorComponentTag::ALPHA; pTags[1] = rendering::ColorComponentTag::RGB_RED; pTags[2] = rendering::ColorComponentTag::RGB_GREEN; pTags[3] = rendering::ColorComponentTag::RGB_BLUE; m_aComponentBitCounts.realloc(4); sal_Int32* pCounts = m_aComponentBitCounts.getArray(); pCounts[0] = 8; pCounts[1] = 8; pCounts[2] = 8; pCounts[3] = 8; m_nRedIndex = 1; m_nGreenIndex = 2; m_nBlueIndex = 3; m_nAlphaIndex = 0; } break; case BMP_FORMAT_32BIT_TC_BGRA: { m_bPalette = false; m_nBitsPerInputPixel = 32; m_nEndianness = util::Endianness::LITTLE; m_aLayout.IsMsbFirst = false; // doesn't matter m_aComponentTags.realloc(4); sal_Int8* pTags = m_aComponentTags.getArray(); pTags[0] = rendering::ColorComponentTag::RGB_BLUE; pTags[1] = rendering::ColorComponentTag::RGB_GREEN; pTags[2] = rendering::ColorComponentTag::RGB_RED; pTags[3] = rendering::ColorComponentTag::ALPHA; m_aComponentBitCounts.realloc(4); sal_Int32* pCounts = m_aComponentBitCounts.getArray(); pCounts[0] = 8; pCounts[1] = 8; pCounts[2] = 8; pCounts[3] = 8; m_nRedIndex = 2; m_nGreenIndex = 1; m_nBlueIndex = 0; m_nAlphaIndex = 3; } break; case BMP_FORMAT_32BIT_TC_RGBA: { m_bPalette = false; m_nBitsPerInputPixel = 32; m_nEndianness = util::Endianness::LITTLE; m_aLayout.IsMsbFirst = false; // doesn't matter m_aComponentTags.realloc(4); sal_Int8* pTags = m_aComponentTags.getArray(); pTags[0] = rendering::ColorComponentTag::RGB_RED; pTags[1] = rendering::ColorComponentTag::RGB_GREEN; pTags[2] = rendering::ColorComponentTag::RGB_BLUE; pTags[3] = rendering::ColorComponentTag::ALPHA; m_aComponentBitCounts.realloc(4); sal_Int32* pCounts = m_aComponentBitCounts.getArray(); pCounts[0] = 8; pCounts[1] = 8; pCounts[2] = 8; pCounts[3] = 8; m_nRedIndex = 0; m_nGreenIndex = 1; m_nBlueIndex = 2; m_nAlphaIndex = 3; } break; case BMP_FORMAT_32BIT_TC_MASK: m_bPalette = false; m_nBitsPerInputPixel = 32; m_nEndianness = util::Endianness::LITTLE; m_aLayout.IsMsbFirst = false; // doesn't matter setComponentInfo( m_pBmpAcc->GetColorMask().GetRedMask(), m_pBmpAcc->GetColorMask().GetGreenMask(), m_pBmpAcc->GetColorMask().GetBlueMask() ); break; default: OSL_FAIL( "unsupported bitmap format" ); break; } if( m_bPalette ) { m_aComponentTags.realloc(1); m_aComponentTags[0] = rendering::ColorComponentTag::INDEX; m_aComponentBitCounts.realloc(1); m_aComponentBitCounts[0] = m_nBitsPerInputPixel; m_nIndexIndex = 0; } m_nBitsPerOutputPixel = m_nBitsPerInputPixel; if( m_aBmpEx.IsTransparent() ) { // TODO(P1): need to interleave alpha with bitmap data - // won't fuss with less-than-8 bit for now m_nBitsPerOutputPixel = std::max(sal_Int32(8),m_nBitsPerInputPixel); // check whether alpha goes in front or behind the // bitcount sequence. If pixel format is little endian, // put it behind all the other channels. If it's big // endian, put it in front (because later, the actual data // always gets written after the pixel data) // TODO(Q1): slight catch - in the case of the // BMP_FORMAT_32BIT_XX_ARGB formats, duplicate alpha // channels might happen! m_aComponentTags.realloc(m_aComponentTags.getLength()+1); m_aComponentTags[m_aComponentTags.getLength()-1] = rendering::ColorComponentTag::ALPHA; m_aComponentBitCounts.realloc(m_aComponentBitCounts.getLength()+1); m_aComponentBitCounts[m_aComponentBitCounts.getLength()-1] = m_aBmpEx.IsAlpha() ? 8 : 1; if( m_nEndianness == util::Endianness::BIG ) { // put alpha in front of all the color channels sal_Int8* pTags =m_aComponentTags.getArray(); sal_Int32* pCounts=m_aComponentBitCounts.getArray(); std::rotate(pTags, pTags+m_aComponentTags.getLength()-1, pTags+m_aComponentTags.getLength()); std::rotate(pCounts, pCounts+m_aComponentBitCounts.getLength()-1, pCounts+m_aComponentBitCounts.getLength()); ++m_nRedIndex; ++m_nGreenIndex; ++m_nBlueIndex; ++m_nIndexIndex; m_nAlphaIndex=0; } // always add a full byte to the pixel size, otherwise // pixel packing hell breaks loose. m_nBitsPerOutputPixel += 8; // adapt scanline parameters const Size aSize = m_aBitmap.GetSizePixel(); m_aLayout.ScanLineBytes = m_aLayout.ScanLineStride = (aSize.Width()*m_nBitsPerOutputPixel + 7)/8; } } } VclCanvasBitmap::~VclCanvasBitmap() { if( m_pAlphaAcc ) Bitmap::ReleaseAccess(m_pAlphaAcc); if( m_pBmpAcc ) Bitmap::ReleaseAccess(m_pBmpAcc); } // XBitmap geometry::IntegerSize2D SAL_CALL VclCanvasBitmap::getSize() throw (uno::RuntimeException, std::exception) { SolarMutexGuard aGuard; return integerSize2DFromSize( m_aBitmap.GetSizePixel() ); } sal_Bool SAL_CALL VclCanvasBitmap::hasAlpha() throw (uno::RuntimeException, std::exception) { SolarMutexGuard aGuard; return m_aBmpEx.IsTransparent(); } uno::Reference< rendering::XBitmap > SAL_CALL VclCanvasBitmap::getScaledBitmap( const geometry::RealSize2D& newSize, sal_Bool beFast ) throw (uno::RuntimeException, std::exception) { SolarMutexGuard aGuard; BitmapEx aNewBmp( m_aBitmap ); aNewBmp.Scale( sizeFromRealSize2D( newSize ), beFast ? BmpScaleFlag::Default : BmpScaleFlag::BestQuality ); return uno::Reference( new VclCanvasBitmap( aNewBmp ) ); } // XIntegerReadOnlyBitmap uno::Sequence< sal_Int8 > SAL_CALL VclCanvasBitmap::getData( rendering::IntegerBitmapLayout& bitmapLayout, const geometry::IntegerRectangle2D& rect ) throw( lang::IndexOutOfBoundsException, rendering::VolatileContentDestroyedException, uno::RuntimeException, std::exception) { SolarMutexGuard aGuard; bitmapLayout = getMemoryLayout(); const ::Rectangle aRequestedArea( vcl::unotools::rectangleFromIntegerRectangle2D(rect) ); if( aRequestedArea.IsEmpty() ) return uno::Sequence< sal_Int8 >(); // Invalid/empty bitmap: no data available if( !m_pBmpAcc ) throw lang::IndexOutOfBoundsException(); if( m_aBmpEx.IsTransparent() && !m_pAlphaAcc ) throw lang::IndexOutOfBoundsException(); if( aRequestedArea.Left() < 0 || aRequestedArea.Top() < 0 || aRequestedArea.Right() > m_pBmpAcc->Width() || aRequestedArea.Bottom() > m_pBmpAcc->Height() ) { throw lang::IndexOutOfBoundsException(); } uno::Sequence< sal_Int8 > aRet; Rectangle aRequestedBytes( aRequestedArea ); // adapt to byte boundaries aRequestedBytes.Left() = aRequestedArea.Left()*m_nBitsPerOutputPixel/8; aRequestedBytes.Right() = (aRequestedArea.Right()*m_nBitsPerOutputPixel + 7)/8; // copy stuff to output sequence aRet.realloc(aRequestedBytes.getWidth()*aRequestedBytes.getHeight()); sal_Int8* pOutBuf = aRet.getArray(); bitmapLayout.ScanLines = aRequestedBytes.getHeight(); bitmapLayout.ScanLineBytes = bitmapLayout.ScanLineStride= aRequestedBytes.getWidth(); sal_Int32 nScanlineStride=bitmapLayout.ScanLineStride; if( !(m_pBmpAcc->GetScanlineFormat() & BMP_FORMAT_TOP_DOWN) ) { pOutBuf += bitmapLayout.ScanLineStride*(aRequestedBytes.getHeight()-1); nScanlineStride *= -1; } if( !m_aBmpEx.IsTransparent() ) { OSL_ENSURE(m_pBmpAcc,"Invalid bmp read access"); // can return bitmap data as-is for( long y=aRequestedBytes.Top(); yGetScanline(y); memcpy(pOutBuf, pScan+aRequestedBytes.Left(), aRequestedBytes.getWidth()); pOutBuf += nScanlineStride; } } else { OSL_ENSURE(m_pBmpAcc,"Invalid bmp read access"); OSL_ENSURE(m_pAlphaAcc,"Invalid alpha read access"); // interleave alpha with bitmap data - note, bitcount is // always integer multiple of 8 OSL_ENSURE((m_nBitsPerOutputPixel & 0x07) == 0, "Transparent bitmap bitcount not integer multiple of 8" ); for( long y=aRequestedArea.Top(); yGetPixelIndex(y,x); *pOutScan++ = m_pAlphaAcc->GetPixelIndex(y,x); } } else { const long nNonAlphaBytes( m_nBitsPerInputPixel/8 ); const long nScanlineOffsetLeft(aRequestedArea.Left()*nNonAlphaBytes); Scanline pScan = m_pBmpAcc->GetScanline(y) + nScanlineOffsetLeft; // input integer multiple of byte - copy directly for( long x=aRequestedArea.Left(); xGetPixelIndex( y, x ); } } pOutBuf += nScanlineStride; } } return aRet; } uno::Sequence< sal_Int8 > SAL_CALL VclCanvasBitmap::getPixel( rendering::IntegerBitmapLayout& bitmapLayout, const geometry::IntegerPoint2D& pos ) throw (lang::IndexOutOfBoundsException, rendering::VolatileContentDestroyedException, uno::RuntimeException, std::exception) { SolarMutexGuard aGuard; bitmapLayout = getMemoryLayout(); // Invalid/empty bitmap: no data available if( !m_pBmpAcc ) throw lang::IndexOutOfBoundsException(); if( m_aBmpEx.IsTransparent() && !m_pAlphaAcc ) throw lang::IndexOutOfBoundsException(); if( pos.X < 0 || pos.Y < 0 || pos.X > m_pBmpAcc->Width() || pos.Y > m_pBmpAcc->Height() ) { throw lang::IndexOutOfBoundsException(); } uno::Sequence< sal_Int8 > aRet((m_nBitsPerOutputPixel + 7)/8); sal_Int8* pOutBuf = aRet.getArray(); // copy stuff to output sequence bitmapLayout.ScanLines = 1; bitmapLayout.ScanLineBytes = bitmapLayout.ScanLineStride= aRet.getLength(); const long nScanlineLeftOffset( pos.X*m_nBitsPerInputPixel/8 ); if( !m_aBmpEx.IsTransparent() ) { assert(m_pBmpAcc && "Invalid bmp read access"); // can return bitmap data as-is Scanline pScan = m_pBmpAcc->GetScanline(pos.Y); memcpy(pOutBuf, pScan+nScanlineLeftOffset, aRet.getLength() ); } else { assert(m_pBmpAcc && "Invalid bmp read access"); assert(m_pAlphaAcc && "Invalid alpha read access"); // interleave alpha with bitmap data - note, bitcount is // always integer multiple of 8 assert((m_nBitsPerOutputPixel & 0x07) == 0 && "Transparent bitmap bitcount not integer multiple of 8" ); if( m_nBitsPerInputPixel < 8 ) { // input less than a byte - copy via GetPixel() *pOutBuf++ = m_pBmpAcc->GetPixelIndex(pos.Y,pos.X); *pOutBuf = m_pAlphaAcc->GetPixelIndex(pos.Y,pos.X); } else { const long nNonAlphaBytes( m_nBitsPerInputPixel/8 ); Scanline pScan = m_pBmpAcc->GetScanline(pos.Y); // input integer multiple of byte - copy directly memcpy(pOutBuf, pScan+nScanlineLeftOffset, nNonAlphaBytes ); pOutBuf += nNonAlphaBytes; *pOutBuf++ = m_pAlphaAcc->GetPixelIndex(pos.Y,pos.X); } } return aRet; } uno::Reference< rendering::XBitmapPalette > SAL_CALL VclCanvasBitmap::getPalette() throw (uno::RuntimeException) { SolarMutexGuard aGuard; uno::Reference< XBitmapPalette > aRet; if( m_bPalette ) aRet.set(this); return aRet; } rendering::IntegerBitmapLayout SAL_CALL VclCanvasBitmap::getMemoryLayout() throw (uno::RuntimeException, std::exception) { SolarMutexGuard aGuard; rendering::IntegerBitmapLayout aLayout( m_aLayout ); // only set references to self on separate copy of // IntegerBitmapLayout - if we'd set that on m_aLayout, we'd have // a circular reference! if( m_bPalette ) aLayout.Palette.set( this ); aLayout.ColorSpace.set( this ); return aLayout; } sal_Int32 SAL_CALL VclCanvasBitmap::getNumberOfEntries() throw (uno::RuntimeException, std::exception) { SolarMutexGuard aGuard; if( !m_pBmpAcc ) return 0; return m_pBmpAcc->HasPalette() ? m_pBmpAcc->GetPaletteEntryCount() : 0 ; } sal_Bool SAL_CALL VclCanvasBitmap::getIndex( uno::Sequence< double >& o_entry, sal_Int32 nIndex ) throw (lang::IndexOutOfBoundsException, uno::RuntimeException, std::exception) { SolarMutexGuard aGuard; const sal_uInt16 nCount( m_pBmpAcc ? (m_pBmpAcc->HasPalette() ? m_pBmpAcc->GetPaletteEntryCount() : 0 ) : 0 ); OSL_ENSURE(nIndex >= 0 && nIndex < nCount,"Palette index out of range"); if( nIndex < 0 || nIndex >= nCount ) throw lang::IndexOutOfBoundsException("Palette index out of range", static_cast(this)); const BitmapColor aCol = m_pBmpAcc->GetPaletteColor(sal::static_int_cast(nIndex)); o_entry.realloc(3); double* pColor=o_entry.getArray(); pColor[0] = aCol.GetRed(); pColor[1] = aCol.GetGreen(); pColor[2] = aCol.GetBlue(); return sal_True; // no palette transparency here. } sal_Bool SAL_CALL VclCanvasBitmap::setIndex( const uno::Sequence< double >&, sal_Bool, sal_Int32 nIndex ) throw (lang::IndexOutOfBoundsException, lang::IllegalArgumentException, uno::RuntimeException, std::exception) { SolarMutexGuard aGuard; const sal_uInt16 nCount( m_pBmpAcc ? (m_pBmpAcc->HasPalette() ? m_pBmpAcc->GetPaletteEntryCount() : 0 ) : 0 ); OSL_ENSURE(nIndex >= 0 && nIndex < nCount,"Palette index out of range"); if( nIndex < 0 || nIndex >= nCount ) throw lang::IndexOutOfBoundsException("Palette index out of range", static_cast(this)); return sal_False; // read-only implementation } namespace { struct PaletteColorSpaceHolder: public rtl::StaticWithInit, PaletteColorSpaceHolder> { uno::Reference operator()() { return vcl::unotools::createStandardColorSpace(); } }; } uno::Reference< rendering::XColorSpace > SAL_CALL VclCanvasBitmap::getColorSpace( ) throw (uno::RuntimeException, std::exception) { // this is the method from XBitmapPalette. Return palette color // space here return PaletteColorSpaceHolder::get(); } sal_Int8 SAL_CALL VclCanvasBitmap::getType( ) throw (uno::RuntimeException, std::exception) { return rendering::ColorSpaceType::RGB; } uno::Sequence< ::sal_Int8 > SAL_CALL VclCanvasBitmap::getComponentTags( ) throw (uno::RuntimeException, std::exception) { SolarMutexGuard aGuard; return m_aComponentTags; } sal_Int8 SAL_CALL VclCanvasBitmap::getRenderingIntent( ) throw (uno::RuntimeException, std::exception) { return rendering::RenderingIntent::PERCEPTUAL; } uno::Sequence< ::beans::PropertyValue > SAL_CALL VclCanvasBitmap::getProperties( ) throw (uno::RuntimeException, std::exception) { return uno::Sequence< ::beans::PropertyValue >(); } uno::Sequence< double > SAL_CALL VclCanvasBitmap::convertColorSpace( const uno::Sequence< double >& deviceColor, const uno::Reference< ::rendering::XColorSpace >& targetColorSpace ) throw (uno::RuntimeException, std::exception) { // TODO(P3): if we know anything about target // colorspace, this can be greatly sped up uno::Sequence aIntermediate( convertToARGB(deviceColor)); return targetColorSpace->convertFromARGB(aIntermediate); } uno::Sequence SAL_CALL VclCanvasBitmap::convertToRGB( const uno::Sequence< double >& deviceColor ) throw (lang::IllegalArgumentException,uno::RuntimeException, std::exception) { SolarMutexGuard aGuard; const sal_Size nLen( deviceColor.getLength() ); const sal_Int32 nComponentsPerPixel(m_aComponentTags.getLength()); ENSURE_ARG_OR_THROW2(nLen%nComponentsPerPixel==0, "number of channels no multiple of pixel element count", static_cast(this), 01); uno::Sequence< rendering::RGBColor > aRes(nLen/nComponentsPerPixel); rendering::RGBColor* pOut( aRes.getArray() ); if( m_bPalette ) { OSL_ENSURE(m_nIndexIndex != -1, "Invalid color channel indices"); ENSURE_OR_THROW(m_pBmpAcc, "Unable to get BitmapAccess"); for( sal_Size i=0; iGetPaletteColor( sal::static_int_cast(deviceColor[i+m_nIndexIndex])); // TODO(F3): Convert result to sRGB color space *pOut++ = rendering::RGBColor(toDoubleColor(aCol.GetRed()), toDoubleColor(aCol.GetGreen()), toDoubleColor(aCol.GetBlue())); } } else { OSL_ENSURE(m_nRedIndex != -1 && m_nGreenIndex != -1 && m_nBlueIndex != -1, "Invalid color channel indices"); for( sal_Size i=0; i SAL_CALL VclCanvasBitmap::convertToARGB( const uno::Sequence< double >& deviceColor ) throw (lang::IllegalArgumentException,uno::RuntimeException, std::exception) { SolarMutexGuard aGuard; const sal_Size nLen( deviceColor.getLength() ); const sal_Int32 nComponentsPerPixel(m_aComponentTags.getLength()); ENSURE_ARG_OR_THROW2(nLen%nComponentsPerPixel==0, "number of channels no multiple of pixel element count", static_cast(this), 01); uno::Sequence< rendering::ARGBColor > aRes(nLen/nComponentsPerPixel); rendering::ARGBColor* pOut( aRes.getArray() ); if( m_bPalette ) { OSL_ENSURE(m_nIndexIndex != -1, "Invalid color channel indices"); ENSURE_OR_THROW(m_pBmpAcc, "Unable to get BitmapAccess"); for( sal_Size i=0; iGetPaletteColor( sal::static_int_cast(deviceColor[i+m_nIndexIndex])); // TODO(F3): Convert result to sRGB color space const double nAlpha( m_nAlphaIndex != -1 ? 1.0 - deviceColor[i+m_nAlphaIndex] : 1.0 ); *pOut++ = rendering::ARGBColor(nAlpha, toDoubleColor(aCol.GetRed()), toDoubleColor(aCol.GetGreen()), toDoubleColor(aCol.GetBlue())); } } else { OSL_ENSURE(m_nRedIndex != -1 && m_nGreenIndex != -1 && m_nBlueIndex != -1, "Invalid color channel indices"); for( sal_Size i=0; i SAL_CALL VclCanvasBitmap::convertToPARGB( const uno::Sequence< double >& deviceColor ) throw (lang::IllegalArgumentException,uno::RuntimeException, std::exception) { SolarMutexGuard aGuard; const sal_Size nLen( deviceColor.getLength() ); const sal_Int32 nComponentsPerPixel(m_aComponentTags.getLength()); ENSURE_ARG_OR_THROW2(nLen%nComponentsPerPixel==0, "number of channels no multiple of pixel element count", static_cast(this), 01); uno::Sequence< rendering::ARGBColor > aRes(nLen/nComponentsPerPixel); rendering::ARGBColor* pOut( aRes.getArray() ); if( m_bPalette ) { OSL_ENSURE(m_nIndexIndex != -1, "Invalid color channel indices"); ENSURE_OR_THROW(m_pBmpAcc, "Unable to get BitmapAccess"); for( sal_Size i=0; iGetPaletteColor( sal::static_int_cast(deviceColor[i+m_nIndexIndex])); // TODO(F3): Convert result to sRGB color space const double nAlpha( m_nAlphaIndex != -1 ? 1.0 - deviceColor[i+m_nAlphaIndex] : 1.0 ); *pOut++ = rendering::ARGBColor(nAlpha, nAlpha*toDoubleColor(aCol.GetRed()), nAlpha*toDoubleColor(aCol.GetGreen()), nAlpha*toDoubleColor(aCol.GetBlue())); } } else { OSL_ENSURE(m_nRedIndex != -1 && m_nGreenIndex != -1 && m_nBlueIndex != -1, "Invalid color channel indices"); for( sal_Size i=0; i SAL_CALL VclCanvasBitmap::convertFromRGB( const uno::Sequence& rgbColor ) throw (lang::IllegalArgumentException,uno::RuntimeException, std::exception) { SolarMutexGuard aGuard; const sal_Size nLen( rgbColor.getLength() ); const sal_Int32 nComponentsPerPixel(m_aComponentTags.getLength()); uno::Sequence< double > aRes(nLen*nComponentsPerPixel); double* pColors=aRes.getArray(); if( m_bPalette ) { for( sal_Size i=0; iGetBestPaletteIndex( BitmapColor(toByteColor(rgbColor[i].Red), toByteColor(rgbColor[i].Green), toByteColor(rgbColor[i].Blue))); if( m_nAlphaIndex != -1 ) pColors[m_nAlphaIndex] = 1.0; pColors += nComponentsPerPixel; } } else { for( sal_Size i=0; i SAL_CALL VclCanvasBitmap::convertFromARGB( const uno::Sequence& rgbColor ) throw (lang::IllegalArgumentException,uno::RuntimeException, std::exception) { SolarMutexGuard aGuard; const sal_Size nLen( rgbColor.getLength() ); const sal_Int32 nComponentsPerPixel(m_aComponentTags.getLength()); uno::Sequence< double > aRes(nLen*nComponentsPerPixel); double* pColors=aRes.getArray(); if( m_bPalette ) { for( sal_Size i=0; iGetBestPaletteIndex( BitmapColor(toByteColor(rgbColor[i].Red), toByteColor(rgbColor[i].Green), toByteColor(rgbColor[i].Blue))); if( m_nAlphaIndex != -1 ) pColors[m_nAlphaIndex] = rgbColor[i].Alpha; pColors += nComponentsPerPixel; } } else { for( sal_Size i=0; i SAL_CALL VclCanvasBitmap::convertFromPARGB( const uno::Sequence& rgbColor ) throw (lang::IllegalArgumentException,uno::RuntimeException, std::exception) { SolarMutexGuard aGuard; const sal_Size nLen( rgbColor.getLength() ); const sal_Int32 nComponentsPerPixel(m_aComponentTags.getLength()); uno::Sequence< double > aRes(nLen*nComponentsPerPixel); double* pColors=aRes.getArray(); if( m_bPalette ) { for( sal_Size i=0; iGetBestPaletteIndex( BitmapColor(toByteColor(rgbColor[i].Red / nAlpha), toByteColor(rgbColor[i].Green / nAlpha), toByteColor(rgbColor[i].Blue / nAlpha))); if( m_nAlphaIndex != -1 ) pColors[m_nAlphaIndex] = nAlpha; pColors += nComponentsPerPixel; } } else { for( sal_Size i=0; i SAL_CALL VclCanvasBitmap::getComponentBitCounts( ) throw (uno::RuntimeException, std::exception) { SolarMutexGuard aGuard; return m_aComponentBitCounts; } sal_Int8 SAL_CALL VclCanvasBitmap::getEndianness( ) throw (uno::RuntimeException, std::exception) { SolarMutexGuard aGuard; return m_nEndianness; } uno::Sequence SAL_CALL VclCanvasBitmap::convertFromIntegerColorSpace( const uno::Sequence< ::sal_Int8 >& deviceColor, const uno::Reference< ::rendering::XColorSpace >& targetColorSpace ) throw (lang::IllegalArgumentException,uno::RuntimeException, std::exception) { if( dynamic_cast(targetColorSpace.get()) ) { SolarMutexGuard aGuard; const sal_Size nLen( deviceColor.getLength() ); const sal_Int32 nComponentsPerPixel(m_aComponentTags.getLength()); ENSURE_ARG_OR_THROW2(nLen%nComponentsPerPixel==0, "number of channels no multiple of pixel element count", static_cast(this), 01); uno::Sequence aRes(nLen); double* pOut( aRes.getArray() ); if( m_bPalette ) { OSL_ENSURE(m_nIndexIndex != -1, "Invalid color channel indices"); ENSURE_OR_THROW(m_pBmpAcc, "Unable to get BitmapAccess"); for( sal_Size i=0; iGetPaletteColor( sal::static_int_cast(deviceColor[i+m_nIndexIndex])); // TODO(F3): Convert result to sRGB color space const double nAlpha( m_nAlphaIndex != -1 ? 1.0 - deviceColor[i+m_nAlphaIndex] : 1.0 ); *pOut++ = toDoubleColor(aCol.GetRed()); *pOut++ = toDoubleColor(aCol.GetGreen()); *pOut++ = toDoubleColor(aCol.GetBlue()); *pOut++ = nAlpha; } } else { OSL_ENSURE(m_nRedIndex != -1 && m_nGreenIndex != -1 && m_nBlueIndex != -1, "Invalid color channel indices"); for( sal_Size i=0; i aIntermediate( convertIntegerToARGB(deviceColor)); return targetColorSpace->convertFromARGB(aIntermediate); } } uno::Sequence< ::sal_Int8 > SAL_CALL VclCanvasBitmap::convertToIntegerColorSpace( const uno::Sequence< ::sal_Int8 >& deviceColor, const uno::Reference< ::rendering::XIntegerBitmapColorSpace >& targetColorSpace ) throw (lang::IllegalArgumentException,uno::RuntimeException, std::exception) { if( dynamic_cast(targetColorSpace.get()) ) { // it's us, so simply pass-through the data return deviceColor; } else { // TODO(P3): if we know anything about target // colorspace, this can be greatly sped up uno::Sequence aIntermediate( convertIntegerToARGB(deviceColor)); return targetColorSpace->convertIntegerFromARGB(aIntermediate); } } uno::Sequence SAL_CALL VclCanvasBitmap::convertIntegerToRGB( const uno::Sequence< ::sal_Int8 >& deviceColor ) throw (lang::IllegalArgumentException,uno::RuntimeException, std::exception) { SolarMutexGuard aGuard; const sal_uInt8* pIn( reinterpret_cast(deviceColor.getConstArray()) ); const sal_Size nLen( deviceColor.getLength() ); const sal_Int32 nNumColors((nLen*8 + m_nBitsPerOutputPixel-1)/m_nBitsPerOutputPixel); uno::Sequence< rendering::RGBColor > aRes(nNumColors); rendering::RGBColor* pOut( aRes.getArray() ); ENSURE_OR_THROW(m_pBmpAcc, "Unable to get BitmapAccess"); if( m_aBmpEx.IsTransparent() ) { const sal_Int32 nBytesPerPixel((m_nBitsPerOutputPixel+7)/8); for( sal_Size i=0; iGetPaletteColor(*pIn) : m_pBmpAcc->GetPixelFromData(pIn,0); // TODO(F3): Convert result to sRGB color space *pOut++ = rendering::RGBColor(toDoubleColor(aCol.GetRed()), toDoubleColor(aCol.GetGreen()), toDoubleColor(aCol.GetBlue())); // skips alpha pIn += nBytesPerPixel; } } else { for( sal_Int32 i=0; iGetPaletteColor( m_pBmpAcc->GetPixelFromData( pIn, i ).GetIndex()) : m_pBmpAcc->GetPixelFromData(pIn, i); // TODO(F3): Convert result to sRGB color space *pOut++ = rendering::RGBColor(toDoubleColor(aCol.GetRed()), toDoubleColor(aCol.GetGreen()), toDoubleColor(aCol.GetBlue())); } } return aRes; } uno::Sequence SAL_CALL VclCanvasBitmap::convertIntegerToARGB( const uno::Sequence< ::sal_Int8 >& deviceColor ) throw (lang::IllegalArgumentException,uno::RuntimeException, std::exception) { SolarMutexGuard aGuard; const sal_uInt8* pIn( reinterpret_cast(deviceColor.getConstArray()) ); const sal_Size nLen( deviceColor.getLength() ); const sal_Int32 nNumColors((nLen*8 + m_nBitsPerOutputPixel-1)/m_nBitsPerOutputPixel); uno::Sequence< rendering::ARGBColor > aRes(nNumColors); rendering::ARGBColor* pOut( aRes.getArray() ); ENSURE_OR_THROW(m_pBmpAcc, "Unable to get BitmapAccess"); if( m_aBmpEx.IsTransparent() ) { const long nNonAlphaBytes( (m_nBitsPerInputPixel+7)/8 ); const sal_Int32 nBytesPerPixel((m_nBitsPerOutputPixel+7)/8); const sal_uInt8 nAlphaFactor( m_aBmpEx.IsAlpha() ? 1 : 255 ); for( sal_Size i=0; iGetPaletteColor(*pIn) : m_pBmpAcc->GetPixelFromData(pIn,0); // TODO(F3): Convert result to sRGB color space *pOut++ = rendering::ARGBColor(1.0 - toDoubleColor(nAlphaFactor*pIn[nNonAlphaBytes]), toDoubleColor(aCol.GetRed()), toDoubleColor(aCol.GetGreen()), toDoubleColor(aCol.GetBlue())); pIn += nBytesPerPixel; } } else { for( sal_Int32 i=0; iGetPaletteColor( m_pBmpAcc->GetPixelFromData( pIn, i ).GetIndex() ) : m_pBmpAcc->GetPixelFromData(pIn, i); // TODO(F3): Convert result to sRGB color space *pOut++ = rendering::ARGBColor(1.0, toDoubleColor(aCol.GetRed()), toDoubleColor(aCol.GetGreen()), toDoubleColor(aCol.GetBlue())); } } return aRes; } uno::Sequence SAL_CALL VclCanvasBitmap::convertIntegerToPARGB( const uno::Sequence< ::sal_Int8 >& deviceColor ) throw (lang::IllegalArgumentException,uno::RuntimeException, std::exception) { SolarMutexGuard aGuard; const sal_uInt8* pIn( reinterpret_cast(deviceColor.getConstArray()) ); const sal_Size nLen( deviceColor.getLength() ); const sal_Int32 nNumColors((nLen*8 + m_nBitsPerOutputPixel-1)/m_nBitsPerOutputPixel); uno::Sequence< rendering::ARGBColor > aRes(nNumColors); rendering::ARGBColor* pOut( aRes.getArray() ); ENSURE_OR_THROW(m_pBmpAcc, "Unable to get BitmapAccess"); if( m_aBmpEx.IsTransparent() ) { const long nNonAlphaBytes( (m_nBitsPerInputPixel+7)/8 ); const sal_Int32 nBytesPerPixel((m_nBitsPerOutputPixel+7)/8); const sal_uInt8 nAlphaFactor( m_aBmpEx.IsAlpha() ? 1 : 255 ); for( sal_Size i=0; iGetPaletteColor(*pIn) : m_pBmpAcc->GetPixelFromData(pIn,0); // TODO(F3): Convert result to sRGB color space const double nAlpha( 1.0 - toDoubleColor(nAlphaFactor*pIn[nNonAlphaBytes]) ); *pOut++ = rendering::ARGBColor(nAlpha, nAlpha*toDoubleColor(aCol.GetRed()), nAlpha*toDoubleColor(aCol.GetGreen()), nAlpha*toDoubleColor(aCol.GetBlue())); pIn += nBytesPerPixel; } } else { for( sal_Int32 i=0; iGetPaletteColor( m_pBmpAcc->GetPixelFromData( pIn, i ).GetIndex() ) : m_pBmpAcc->GetPixelFromData(pIn, i); // TODO(F3): Convert result to sRGB color space *pOut++ = rendering::ARGBColor(1.0, toDoubleColor(aCol.GetRed()), toDoubleColor(aCol.GetGreen()), toDoubleColor(aCol.GetBlue())); } } return aRes; } uno::Sequence< ::sal_Int8 > SAL_CALL VclCanvasBitmap::convertIntegerFromRGB( const uno::Sequence& rgbColor ) throw (lang::IllegalArgumentException,uno::RuntimeException, std::exception) { SolarMutexGuard aGuard; const sal_Size nLen( rgbColor.getLength() ); const sal_Int32 nNumBytes((nLen*m_nBitsPerOutputPixel+7)/8); uno::Sequence< sal_Int8 > aRes(nNumBytes); sal_uInt8* pColors=reinterpret_cast(aRes.getArray()); if( m_aBmpEx.IsTransparent() ) { const long nNonAlphaBytes( (m_nBitsPerInputPixel+7)/8 ); for( sal_Size i=0; i(m_pBmpAcc->GetBestPaletteIndex( aCol ))) : aCol; m_pBmpAcc->SetPixelOnData(pColors,i,aCol2); pColors += nNonAlphaBytes; *pColors++ = sal_uInt8(255); } } else { for( sal_Size i=0; i(m_pBmpAcc->GetBestPaletteIndex( aCol ))) : aCol; m_pBmpAcc->SetPixelOnData(pColors,i,aCol2); } } return aRes; } uno::Sequence< ::sal_Int8 > SAL_CALL VclCanvasBitmap::convertIntegerFromARGB( const uno::Sequence& rgbColor ) throw (lang::IllegalArgumentException,uno::RuntimeException, std::exception) { SolarMutexGuard aGuard; const sal_Size nLen( rgbColor.getLength() ); const sal_Int32 nNumBytes((nLen*m_nBitsPerOutputPixel+7)/8); uno::Sequence< sal_Int8 > aRes(nNumBytes); sal_uInt8* pColors=reinterpret_cast(aRes.getArray()); if( m_aBmpEx.IsTransparent() ) { const long nNonAlphaBytes( (m_nBitsPerInputPixel+7)/8 ); for( sal_Size i=0; i(m_pBmpAcc->GetBestPaletteIndex( aCol ))) : aCol; m_pBmpAcc->SetPixelOnData(pColors,i,aCol2); pColors += nNonAlphaBytes; *pColors++ = 255 - toByteColor(rgbColor[i].Alpha); } } else { for( sal_Size i=0; i(m_pBmpAcc->GetBestPaletteIndex( aCol ))) : aCol; m_pBmpAcc->SetPixelOnData(pColors,i,aCol2); } } return aRes; } uno::Sequence< ::sal_Int8 > SAL_CALL VclCanvasBitmap::convertIntegerFromPARGB( const uno::Sequence& rgbColor ) throw (lang::IllegalArgumentException,uno::RuntimeException, std::exception) { SolarMutexGuard aGuard; const sal_Size nLen( rgbColor.getLength() ); const sal_Int32 nNumBytes((nLen*m_nBitsPerOutputPixel+7)/8); uno::Sequence< sal_Int8 > aRes(nNumBytes); sal_uInt8* pColors=reinterpret_cast(aRes.getArray()); if( m_aBmpEx.IsTransparent() ) { const long nNonAlphaBytes( (m_nBitsPerInputPixel+7)/8 ); for( sal_Size i=0; i(m_pBmpAcc->GetBestPaletteIndex( aCol ))) : aCol; m_pBmpAcc->SetPixelOnData(pColors,i,aCol2); pColors += nNonAlphaBytes; *pColors++ = 255 - toByteColor(nAlpha); } } else { for( sal_Size i=0; i(m_pBmpAcc->GetBestPaletteIndex( aCol ))) : aCol; m_pBmpAcc->SetPixelOnData(pColors,i,aCol2); } } return aRes; } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */