/* -*- 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 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); } BitmapScopedReadAccess& VclCanvasBitmap::getBitmapReadAccess() { // BitmapReadAccess is more expensive than BitmapInfoAccess, // as the latter requires also pixels, which may need converted // from the system format (and even fetched). Most calls here // need only info access, create read access only on demand. if(!m_pBmpReadAcc) m_pBmpReadAcc.emplace(m_aBitmap); return *m_pBmpReadAcc; } BitmapScopedReadAccess& VclCanvasBitmap::getAlphaReadAccess() { if(!m_pAlphaReadAcc) m_pAlphaReadAcc.emplace(m_aAlpha); return *m_pAlphaReadAcc; } VclCanvasBitmap::VclCanvasBitmap( const BitmapEx& rBitmap ) : m_aBmpEx( rBitmap ), m_aBitmap( rBitmap.GetBitmap() ), m_pBmpAcc( m_aBitmap ), m_nBitsPerInputPixel(0), m_nBitsPerOutputPixel(0), m_nRedIndex(-1), m_nGreenIndex(-1), m_nBlueIndex(-1), m_nAlphaIndex(-1), m_nIndexIndex(-1), m_bPalette(false) { if( m_aBmpEx.IsAlpha() ) { m_aAlpha = m_aBmpEx.GetAlphaMask().GetBitmap(); m_pAlphaAcc = m_aAlpha; } 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 ) return; 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 ScanlineFormat::N1BitMsbPal: m_bPalette = true; m_nBitsPerInputPixel = 1; m_aLayout.IsMsbFirst = true; break; case ScanlineFormat::N8BitPal: m_bPalette = true; m_nBitsPerInputPixel = 8; m_aLayout.IsMsbFirst = false; // doesn't matter break; case ScanlineFormat::N24BitTcBgr: m_bPalette = false; m_nBitsPerInputPixel = 24; m_aLayout.IsMsbFirst = false; // doesn't matter setComponentInfo( static_cast(0xff0000UL), static_cast(0x00ff00UL), static_cast(0x0000ffUL) ); break; case ScanlineFormat::N24BitTcRgb: m_bPalette = false; m_nBitsPerInputPixel = 24; m_aLayout.IsMsbFirst = false; // doesn't matter setComponentInfo( static_cast(0x0000ffUL), static_cast(0x00ff00UL), static_cast(0xff0000UL) ); break; case ScanlineFormat::N32BitTcAbgr: { m_bPalette = false; m_nBitsPerInputPixel = 32; m_aLayout.IsMsbFirst = false; // doesn't matter m_aComponentTags = { /* 0 */ rendering::ColorComponentTag::ALPHA, /* 1 */ rendering::ColorComponentTag::RGB_BLUE, /* 2 */ rendering::ColorComponentTag::RGB_GREEN, /* 3 */ rendering::ColorComponentTag::RGB_RED }; m_aComponentBitCounts = { /* 0 */ 8, /* 1 */ 8, /* 2 */ 8, /* 3 */ 8 }; m_nRedIndex = 3; m_nGreenIndex = 2; m_nBlueIndex = 1; m_nAlphaIndex = 0; } break; case ScanlineFormat::N32BitTcArgb: { m_bPalette = false; m_nBitsPerInputPixel = 32; m_aLayout.IsMsbFirst = false; // doesn't matter m_aComponentTags = { /* 0 */ rendering::ColorComponentTag::ALPHA, /* 1 */ rendering::ColorComponentTag::RGB_RED, /* 2 */ rendering::ColorComponentTag::RGB_GREEN, /* 3 */ rendering::ColorComponentTag::RGB_BLUE }; m_aComponentBitCounts = { /* 0 */ 8, /* 1 */ 8, /* 2 */ 8, /* 3 */ 8 }; m_nRedIndex = 1; m_nGreenIndex = 2; m_nBlueIndex = 3; m_nAlphaIndex = 0; } break; case ScanlineFormat::N32BitTcBgra: { m_bPalette = false; m_nBitsPerInputPixel = 32; m_aLayout.IsMsbFirst = false; // doesn't matter m_aComponentTags = { /* 0 */ rendering::ColorComponentTag::RGB_BLUE, /* 1 */ rendering::ColorComponentTag::RGB_GREEN, /* 2 */ rendering::ColorComponentTag::RGB_RED, /* 3 */ rendering::ColorComponentTag::ALPHA }; m_aComponentBitCounts = { /* 0 */ 8, /* 1 */ 8, /* 2 */ 8, /* 3 */ 8 }; m_nRedIndex = 2; m_nGreenIndex = 1; m_nBlueIndex = 0; m_nAlphaIndex = 3; } break; case ScanlineFormat::N32BitTcRgba: { m_bPalette = false; m_nBitsPerInputPixel = 32; m_aLayout.IsMsbFirst = false; // doesn't matter m_aComponentTags = { /* 0 */ rendering::ColorComponentTag::RGB_RED, /* 1 */ rendering::ColorComponentTag::RGB_GREEN, /* 2 */ rendering::ColorComponentTag::RGB_BLUE, /* 3 */ rendering::ColorComponentTag::ALPHA }; m_aComponentBitCounts = { /* 0 */ 8, /* 1 */ 8, /* 2 */ 8, /* 3 */ 8 }; m_nRedIndex = 0; m_nGreenIndex = 1; m_nBlueIndex = 2; m_nAlphaIndex = 3; } break; case ScanlineFormat::N32BitTcMask: m_bPalette = false; m_nBitsPerInputPixel = 32; 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 = { rendering::ColorComponentTag::INDEX }; m_aComponentBitCounts = { m_nBitsPerInputPixel }; m_nIndexIndex = 0; } m_nBitsPerOutputPixel = m_nBitsPerInputPixel; if( !m_aBmpEx.IsAlpha() ) return; // 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.getArray()[m_aComponentTags.getLength()-1] = rendering::ColorComponentTag::ALPHA; m_aComponentBitCounts.realloc(m_aComponentBitCounts.getLength()+1); m_aComponentBitCounts.getArray()[m_aComponentBitCounts.getLength()-1] = m_aBmpEx.IsAlpha() ? 8 : 1; // 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() { } // XBitmap geometry::IntegerSize2D SAL_CALL VclCanvasBitmap::getSize() { SolarMutexGuard aGuard; return integerSize2DFromSize( m_aBitmap.GetSizePixel() ); } sal_Bool SAL_CALL VclCanvasBitmap::hasAlpha() { SolarMutexGuard aGuard; return m_aBmpEx.IsAlpha(); } uno::Reference< rendering::XBitmap > SAL_CALL VclCanvasBitmap::getScaledBitmap( const geometry::RealSize2D& newSize, sal_Bool beFast ) { 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 ) { SolarMutexGuard aGuard; bitmapLayout = getMemoryLayout(); const ::tools::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.IsAlpha() && !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; tools::Rectangle aRequestedBytes( aRequestedArea ); // adapt to byte boundaries aRequestedBytes.SetLeft( aRequestedArea.Left()*m_nBitsPerOutputPixel/8 ); aRequestedBytes.SetRight( (aRequestedArea.Right()*m_nBitsPerOutputPixel + 7)/8 ); // copy stuff to output sequence aRet.realloc(aRequestedBytes.getOpenWidth()*aRequestedBytes.getOpenHeight()); sal_Int8* pOutBuf = aRet.getArray(); bitmapLayout.ScanLines = aRequestedBytes.getOpenHeight(); bitmapLayout.ScanLineBytes = bitmapLayout.ScanLineStride= aRequestedBytes.getOpenWidth(); sal_Int32 nScanlineStride=bitmapLayout.ScanLineStride; if (m_pBmpAcc->IsBottomUp()) { pOutBuf += bitmapLayout.ScanLineStride*(aRequestedBytes.getOpenHeight()-1); nScanlineStride *= -1; } if( !m_aBmpEx.IsAlpha() ) { BitmapScopedReadAccess& pBmpAcc = getBitmapReadAccess(); OSL_ENSURE(pBmpAcc,"Invalid bmp read access"); // can return bitmap data as-is for( tools::Long y=aRequestedBytes.Top(); yGetScanline(y); memcpy(pOutBuf, pScan+aRequestedBytes.Left(), aRequestedBytes.getOpenWidth()); pOutBuf += nScanlineStride; } } else { BitmapScopedReadAccess& pBmpAcc = getBitmapReadAccess(); BitmapScopedReadAccess& pAlphaAcc = getAlphaReadAccess(); OSL_ENSURE(pBmpAcc,"Invalid bmp read access"); OSL_ENSURE(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( tools::Long y=aRequestedArea.Top(); yGetPixelIndex(y,x); // vcl used to store transparency. Now it stores alpha. But we need the UNO // interface to still preserve the old interface. *pOutScan++ = 255 - pAlphaAcc->GetPixelIndex(y,x); } } else { const tools::Long nNonAlphaBytes( m_nBitsPerInputPixel/8 ); const tools::Long nScanlineOffsetLeft(aRequestedArea.Left()*nNonAlphaBytes); Scanline pScan = pBmpAcc->GetScanline(y) + nScanlineOffsetLeft; Scanline pScanlineAlpha = pAlphaAcc->GetScanline( y ); // input integer multiple of byte - copy directly for( tools::Long x=aRequestedArea.Left(); xGetIndexFromData( pScanlineAlpha, x ); } } pOutBuf += nScanlineStride; } } return aRet; } uno::Sequence< sal_Int8 > SAL_CALL VclCanvasBitmap::getPixel( rendering::IntegerBitmapLayout& bitmapLayout, const geometry::IntegerPoint2D& pos ) { SolarMutexGuard aGuard; bitmapLayout = getMemoryLayout(); // Invalid/empty bitmap: no data available if( !m_pBmpAcc ) throw lang::IndexOutOfBoundsException(); if( m_aBmpEx.IsAlpha() && !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 tools::Long nScanlineLeftOffset( pos.X*m_nBitsPerInputPixel/8 ); if( !m_aBmpEx.IsAlpha() ) { BitmapScopedReadAccess& pBmpAcc = getBitmapReadAccess(); assert(pBmpAcc && "Invalid bmp read access"); // can return bitmap data as-is Scanline pScan = pBmpAcc->GetScanline(pos.Y); memcpy(pOutBuf, pScan+nScanlineLeftOffset, aRet.getLength() ); } else { BitmapScopedReadAccess& pBmpAcc = getBitmapReadAccess(); BitmapScopedReadAccess& pAlphaAcc = getAlphaReadAccess(); assert(pBmpAcc && "Invalid bmp read access"); assert(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++ = pBmpAcc->GetPixelIndex(pos.Y,pos.X); // vcl used to store transparency. Now it stores alpha. But we need the UNO // interface to still preserve the old interface. *pOutBuf = 255 - pAlphaAcc->GetPixelIndex(pos.Y,pos.X); } else { const tools::Long nNonAlphaBytes( m_nBitsPerInputPixel/8 ); Scanline pScan = pBmpAcc->GetScanline(pos.Y); // input integer multiple of byte - copy directly memcpy(pOutBuf, pScan+nScanlineLeftOffset, nNonAlphaBytes ); pOutBuf += nNonAlphaBytes; // vcl used to store transparency. Now it stores alpha. But we need the UNO // interface to still preserve the old interface. *pOutBuf++ = 255 - pAlphaAcc->GetPixelIndex(pos.Y,pos.X); } } return aRet; } uno::Reference< rendering::XBitmapPalette > VclCanvasBitmap::getPalette() { SolarMutexGuard aGuard; uno::Reference< XBitmapPalette > aRet; if( m_bPalette ) aRet.set(this); return aRet; } rendering::IntegerBitmapLayout SAL_CALL VclCanvasBitmap::getMemoryLayout() { 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() { 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 ) { 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(u"Palette index out of range"_ustr, 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 true; // no palette transparency here. } sal_Bool SAL_CALL VclCanvasBitmap::setIndex( const uno::Sequence< double >&, sal_Bool, sal_Int32 nIndex ) { 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(u"Palette index out of range"_ustr, static_cast(this)); return false; // read-only implementation } uno::Reference< rendering::XColorSpace > SAL_CALL VclCanvasBitmap::getColorSpace( ) { // this is the method from XBitmapPalette. Return palette color // space here static uno::Reference gColorSpace = vcl::unotools::createStandardColorSpace(); return gColorSpace; } sal_Int8 SAL_CALL VclCanvasBitmap::getType( ) { return rendering::ColorSpaceType::RGB; } uno::Sequence< ::sal_Int8 > SAL_CALL VclCanvasBitmap::getComponentTags( ) { SolarMutexGuard aGuard; return m_aComponentTags; } sal_Int8 SAL_CALL VclCanvasBitmap::getRenderingIntent( ) { return rendering::RenderingIntent::PERCEPTUAL; } uno::Sequence< ::beans::PropertyValue > SAL_CALL VclCanvasBitmap::getProperties( ) { return uno::Sequence< ::beans::PropertyValue >(); } uno::Sequence< double > SAL_CALL VclCanvasBitmap::convertColorSpace( const uno::Sequence< double >& deviceColor, const uno::Reference< ::rendering::XColorSpace >& targetColorSpace ) { // 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 ) { SolarMutexGuard aGuard; const std::size_t 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( std::size_t 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( std::size_t i=0; i SAL_CALL VclCanvasBitmap::convertToARGB( const uno::Sequence< double >& deviceColor ) { SolarMutexGuard aGuard; const std::size_t 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( std::size_t 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( std::size_t i=0; i SAL_CALL VclCanvasBitmap::convertToPARGB( const uno::Sequence< double >& deviceColor ) { SolarMutexGuard aGuard; const std::size_t 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( std::size_t 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( std::size_t i=0; i SAL_CALL VclCanvasBitmap::convertFromRGB( const uno::Sequence& rgbColor ) { SolarMutexGuard aGuard; const std::size_t nLen( rgbColor.getLength() ); const sal_Int32 nComponentsPerPixel(m_aComponentTags.getLength()); uno::Sequence< double > aRes(nLen*nComponentsPerPixel); double* pColors=aRes.getArray(); if( m_bPalette ) { for( const auto& rIn : rgbColor ) { pColors[m_nIndexIndex] = m_pBmpAcc->GetBestPaletteIndex( BitmapColor(toByteColor(rIn.Red), toByteColor(rIn.Green), toByteColor(rIn.Blue))); if( m_nAlphaIndex != -1 ) pColors[m_nAlphaIndex] = 1.0; pColors += nComponentsPerPixel; } } else { for( const auto& rIn : rgbColor ) { pColors[m_nRedIndex] = rIn.Red; pColors[m_nGreenIndex] = rIn.Green; pColors[m_nBlueIndex] = rIn.Blue; if( m_nAlphaIndex != -1 ) pColors[m_nAlphaIndex] = 1.0; pColors += nComponentsPerPixel; } } return aRes; } uno::Sequence< double > SAL_CALL VclCanvasBitmap::convertFromARGB( const uno::Sequence& rgbColor ) { SolarMutexGuard aGuard; const std::size_t nLen( rgbColor.getLength() ); const sal_Int32 nComponentsPerPixel(m_aComponentTags.getLength()); uno::Sequence< double > aRes(nLen*nComponentsPerPixel); double* pColors=aRes.getArray(); if( m_bPalette ) { for( const auto& rIn : rgbColor ) { pColors[m_nIndexIndex] = m_pBmpAcc->GetBestPaletteIndex( BitmapColor(toByteColor(rIn.Red), toByteColor(rIn.Green), toByteColor(rIn.Blue))); if( m_nAlphaIndex != -1 ) pColors[m_nAlphaIndex] = rIn.Alpha; pColors += nComponentsPerPixel; } } else { for( const auto& rIn : rgbColor ) { pColors[m_nRedIndex] = rIn.Red; pColors[m_nGreenIndex] = rIn.Green; pColors[m_nBlueIndex] = rIn.Blue; if( m_nAlphaIndex != -1 ) pColors[m_nAlphaIndex] = rIn.Alpha; pColors += nComponentsPerPixel; } } return aRes; } uno::Sequence< double > SAL_CALL VclCanvasBitmap::convertFromPARGB( const uno::Sequence& rgbColor ) { SolarMutexGuard aGuard; const std::size_t nLen( rgbColor.getLength() ); const sal_Int32 nComponentsPerPixel(m_aComponentTags.getLength()); uno::Sequence< double > aRes(nLen*nComponentsPerPixel); double* pColors=aRes.getArray(); if( m_bPalette ) { for( const auto& rIn : rgbColor ) { const double nAlpha( rIn.Alpha ); pColors[m_nIndexIndex] = m_pBmpAcc->GetBestPaletteIndex( BitmapColor(toByteColor(rIn.Red / nAlpha), toByteColor(rIn.Green / nAlpha), toByteColor(rIn.Blue / nAlpha))); if( m_nAlphaIndex != -1 ) pColors[m_nAlphaIndex] = nAlpha; pColors += nComponentsPerPixel; } } else { for( const auto& rIn : rgbColor ) { const double nAlpha( rIn.Alpha ); pColors[m_nRedIndex] = rIn.Red / nAlpha; pColors[m_nGreenIndex] = rIn.Green / nAlpha; pColors[m_nBlueIndex] = rIn.Blue / nAlpha; if( m_nAlphaIndex != -1 ) pColors[m_nAlphaIndex] = nAlpha; pColors += nComponentsPerPixel; } } return aRes; } sal_Int32 SAL_CALL VclCanvasBitmap::getBitsPerPixel( ) { return m_nBitsPerOutputPixel; } uno::Sequence< ::sal_Int32 > SAL_CALL VclCanvasBitmap::getComponentBitCounts( ) { return m_aComponentBitCounts; } sal_Int8 SAL_CALL VclCanvasBitmap::getEndianness( ) { return util::Endianness::LITTLE; } uno::Sequence SAL_CALL VclCanvasBitmap::convertFromIntegerColorSpace( const uno::Sequence< ::sal_Int8 >& deviceColor, const uno::Reference< ::rendering::XColorSpace >& targetColorSpace ) { if( dynamic_cast(targetColorSpace.get()) ) { SolarMutexGuard aGuard; const std::size_t 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( std::size_t 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( std::size_t 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 ) { 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 ) { SolarMutexGuard aGuard; const sal_uInt8* pIn( reinterpret_cast(deviceColor.getConstArray()) ); const std::size_t 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() ); BitmapScopedReadAccess& pBmpAcc = getBitmapReadAccess(); ENSURE_OR_THROW(pBmpAcc, "Unable to get BitmapAccess"); if( m_aBmpEx.IsAlpha() ) { const sal_Int32 nBytesPerPixel((m_nBitsPerOutputPixel+7)/8); for( std::size_t i=0; iGetPaletteColor(*pIn) : 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( pBmpAcc->GetPixelFromData( pIn, i ).GetIndex()) : 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 ) { SolarMutexGuard aGuard; const sal_uInt8* pIn( reinterpret_cast(deviceColor.getConstArray()) ); const std::size_t 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() ); BitmapScopedReadAccess& pBmpAcc = getBitmapReadAccess(); ENSURE_OR_THROW(pBmpAcc, "Unable to get BitmapAccess"); if( m_aBmpEx.IsAlpha() ) { const tools::Long nNonAlphaBytes( (m_nBitsPerInputPixel+7)/8 ); const sal_Int32 nBytesPerPixel((m_nBitsPerOutputPixel+7)/8); for( std::size_t i=0; iGetPaletteColor(*pIn) : pBmpAcc->GetPixelFromData(pIn,0); // TODO(F3): Convert result to sRGB color space *pOut++ = rendering::ARGBColor(1.0 - toDoubleColor(pIn[nNonAlphaBytes]), toDoubleColor(aCol.GetRed()), toDoubleColor(aCol.GetGreen()), toDoubleColor(aCol.GetBlue())); pIn += nBytesPerPixel; } } else { for( sal_Int32 i=0; iGetPaletteColor( pBmpAcc->GetPixelFromData( pIn, i ).GetIndex() ) : 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 ) { SolarMutexGuard aGuard; const sal_uInt8* pIn( reinterpret_cast(deviceColor.getConstArray()) ); const std::size_t 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() ); BitmapScopedReadAccess& pBmpAcc = getBitmapReadAccess(); ENSURE_OR_THROW(pBmpAcc, "Unable to get BitmapAccess"); if( m_aBmpEx.IsAlpha() ) { const tools::Long nNonAlphaBytes( (m_nBitsPerInputPixel+7)/8 ); const sal_Int32 nBytesPerPixel((m_nBitsPerOutputPixel+7)/8); for( std::size_t i=0; iGetPaletteColor(*pIn) : pBmpAcc->GetPixelFromData(pIn,0); // TODO(F3): Convert result to sRGB color space const double nAlpha( 1.0 - toDoubleColor(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( pBmpAcc->GetPixelFromData( pIn, i ).GetIndex() ) : 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 ) { SolarMutexGuard aGuard; const std::size_t 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()); BitmapScopedReadAccess& pBmpAcc = getBitmapReadAccess(); if( m_aBmpEx.IsAlpha() ) { const tools::Long nNonAlphaBytes( (m_nBitsPerInputPixel+7)/8 ); for( std::size_t i=0; i(pBmpAcc->GetBestPaletteIndex( aCol ))) : aCol; pBmpAcc->SetPixelOnData(pColors,i,aCol2); pColors += nNonAlphaBytes; *pColors++ = sal_uInt8(255); } } else { for( std::size_t i=0; i(pBmpAcc->GetBestPaletteIndex( aCol ))) : aCol; pBmpAcc->SetPixelOnData(pColors,i,aCol2); } } return aRes; } uno::Sequence< ::sal_Int8 > SAL_CALL VclCanvasBitmap::convertIntegerFromARGB( const uno::Sequence& rgbColor ) { SolarMutexGuard aGuard; const std::size_t 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()); BitmapScopedReadAccess& pBmpAcc = getBitmapReadAccess(); if( m_aBmpEx.IsAlpha() ) { const tools::Long nNonAlphaBytes( (m_nBitsPerInputPixel+7)/8 ); for( std::size_t i=0; i(pBmpAcc->GetBestPaletteIndex( aCol ))) : aCol; pBmpAcc->SetPixelOnData(pColors,i,aCol2); pColors += nNonAlphaBytes; *pColors++ = 255 - toByteColor(rgbColor[i].Alpha); } } else { for( std::size_t i=0; i(pBmpAcc->GetBestPaletteIndex( aCol ))) : aCol; pBmpAcc->SetPixelOnData(pColors,i,aCol2); } } return aRes; } uno::Sequence< ::sal_Int8 > SAL_CALL VclCanvasBitmap::convertIntegerFromPARGB( const uno::Sequence& rgbColor ) { SolarMutexGuard aGuard; const std::size_t 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()); BitmapScopedReadAccess& pBmpAcc = getBitmapReadAccess(); if( m_aBmpEx.IsAlpha() ) { const tools::Long nNonAlphaBytes( (m_nBitsPerInputPixel+7)/8 ); for( std::size_t i=0; i(pBmpAcc->GetBestPaletteIndex( aCol ))) : aCol; pBmpAcc->SetPixelOnData(pColors,i,aCol2); pColors += nNonAlphaBytes; *pColors++ = 255 - toByteColor(nAlpha); } } else { for( std::size_t i=0; i(pBmpAcc->GetBestPaletteIndex( aCol ))) : aCol; pBmpAcc->SetPixelOnData(pColors,i,aCol2); } } return aRes; } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */