diff options
Diffstat (limited to 'vcl/source/bitmap/Octree.cxx')
-rw-r--r-- | vcl/source/bitmap/Octree.cxx | 298 |
1 files changed, 298 insertions, 0 deletions
diff --git a/vcl/source/bitmap/Octree.cxx b/vcl/source/bitmap/Octree.cxx new file mode 100644 index 000000000000..64914f76385e --- /dev/null +++ b/vcl/source/bitmap/Octree.cxx @@ -0,0 +1,298 @@ +/* -*- 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 <limits.h> + +#include <rtl/alloc.h> +#include <vcl/bitmapaccess.hxx> + +#include <bitmap/Octree.hxx> +#include <bitmap/impoctree.hxx> + +static const sal_uInt8 pImplMask[8] = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 }; + +ImpNodeCache::ImpNodeCache( const sal_uLong nInitSize ) : + pActNode( nullptr ) +{ + const sal_uLong nSize = nInitSize + 4; + + for( sal_uLong i = 0; i < nSize; i++ ) + { + OctreeNode* pNewNode = new OctreeNode; + + pNewNode->pNextInCache = pActNode; + pActNode = pNewNode; + } +} + +ImpNodeCache::~ImpNodeCache() +{ + while( pActNode ) + { + OctreeNode* pNode = pActNode; + + pActNode = pNode->pNextInCache; + delete pNode; + } +} + +Octree::Octree(const BitmapReadAccess& rReadAcc, sal_uLong nColors) + : nLeafCount(0) + , nLevel(0) + , pTree(nullptr) + , pColor(nullptr) + , pAcc(&rReadAcc) + , nPalIndex(0) +{ + sal_uLong nMax(nColors); + pNodeCache.reset( new ImpNodeCache( nColors ) ); + memset( pReduce, 0, ( OCTREE_BITS + 1 ) * sizeof( OctreeNode* ) ); + + if( !!*pAcc ) + { + const long nWidth = pAcc->Width(); + const long nHeight = pAcc->Height(); + + if( pAcc->HasPalette() ) + { + for( long nY = 0; nY < nHeight; nY++ ) + { + Scanline pScanline = pAcc->GetScanline( nY ); + for( long nX = 0; nX < nWidth; nX++ ) + { + pColor = &pAcc->GetPaletteColor( pAcc->GetIndexFromData( pScanline, nX ) ); + nLevel = 0; + ImplAdd( &pTree ); + + while( nLeafCount > nMax ) + ImplReduce(); + } + } + } + else + { + BitmapColor aColor; + + pColor = &aColor; + + for( long nY = 0; nY < nHeight; nY++ ) + { + Scanline pScanline = pAcc->GetScanline( nY ); + for( long nX = 0; nX < nWidth; nX++ ) + { + aColor = pAcc->GetPixelFromData( pScanline, nX ); + nLevel = 0; + ImplAdd( &pTree ); + + while( nLeafCount > nMax ) + ImplReduce(); + } + } + } + } +} + +Octree::~Octree() +{ + ImplDeleteOctree( &pTree ); + pNodeCache.reset(); +} + +void Octree::ImplDeleteOctree( OctreeNode** ppNode ) +{ + for (OctreeNode* i : (*ppNode)->pChild) + { + if ( i ) + ImplDeleteOctree( &i ); + } + + pNodeCache->ImplReleaseNode( *ppNode ); + *ppNode = nullptr; +} + +void Octree::ImplAdd( OctreeNode** ppNode ) +{ + // possibly generate new nodes + if( !*ppNode ) + { + *ppNode = pNodeCache->ImplGetFreeNode(); + (*ppNode)->bLeaf = ( OCTREE_BITS == nLevel ); + + if( (*ppNode)->bLeaf ) + nLeafCount++; + else + { + (*ppNode)->pNext = pReduce[ nLevel ]; + pReduce[ nLevel ] = *ppNode; + } + } + + if( (*ppNode)->bLeaf ) + { + (*ppNode)->nCount++; + (*ppNode)->nRed += pColor->GetRed(); + (*ppNode)->nGreen += pColor->GetGreen(); + (*ppNode)->nBlue += pColor->GetBlue(); + } + else + { + const sal_uLong nShift = 7 - nLevel; + const sal_uInt8 cMask = pImplMask[ nLevel ]; + const sal_uLong nIndex = ( ( ( pColor->GetRed() & cMask ) >> nShift ) << 2 ) | + ( ( ( pColor->GetGreen() & cMask ) >> nShift ) << 1 ) | + ( ( pColor->GetBlue() & cMask ) >> nShift ); + + nLevel++; + ImplAdd( &(*ppNode)->pChild[ nIndex ] ); + } +} + +void Octree::ImplReduce() +{ + sal_uLong i; + OctreeNode* pNode; + sal_uLong nRedSum = 0; + sal_uLong nGreenSum = 0; + sal_uLong nBlueSum = 0; + sal_uLong nChildren = 0; + + for ( i = OCTREE_BITS - 1; i && !pReduce[i]; i-- ) {} + + pNode = pReduce[ i ]; + pReduce[ i ] = pNode->pNext; + + for ( i = 0; i < 8; i++ ) + { + if ( pNode->pChild[ i ] ) + { + OctreeNode* pChild = pNode->pChild[ i ]; + + nRedSum += pChild->nRed; + nGreenSum += pChild->nGreen; + nBlueSum += pChild->nBlue; + pNode->nCount += pChild->nCount; + + pNodeCache->ImplReleaseNode( pNode->pChild[ i ] ); + pNode->pChild[ i ] = nullptr; + nChildren++; + } + } + + pNode->bLeaf = true; + pNode->nRed = nRedSum; + pNode->nGreen = nGreenSum; + pNode->nBlue = nBlueSum; + nLeafCount -= --nChildren; +} + +void Octree::CreatePalette( OctreeNode* pNode ) +{ + if( pNode->bLeaf ) + { + pNode->nPalIndex = nPalIndex; + aPal[ nPalIndex++ ] = BitmapColor( static_cast<sal_uInt8>( static_cast<double>(pNode->nRed) / pNode->nCount ), + static_cast<sal_uInt8>( static_cast<double>(pNode->nGreen) / pNode->nCount ), + static_cast<sal_uInt8>( static_cast<double>(pNode->nBlue) / pNode->nCount ) ); + } + else for(OctreeNode* i : pNode->pChild) + if( i ) + CreatePalette( i ); + +} + +void Octree::GetPalIndex( OctreeNode* pNode ) +{ + if ( pNode->bLeaf ) + nPalIndex = pNode->nPalIndex; + else + { + const sal_uLong nShift = 7 - nLevel; + const sal_uInt8 cMask = pImplMask[ nLevel++ ]; + const sal_uLong nIndex = ( ( ( pColor->GetRed() & cMask ) >> nShift ) << 2 ) | + ( ( ( pColor->GetGreen() & cMask ) >> nShift ) << 1 ) | + ( ( pColor->GetBlue() & cMask ) >> nShift ); + + GetPalIndex( pNode->pChild[ nIndex ] ); + } +} + +InverseColorMap::InverseColorMap( const BitmapPalette& rPal ) +{ + const int nColorMax = 1 << OCTREE_BITS; + const unsigned long xsqr = 1 << ( gnBits << 1 ); + const unsigned long xsqr2 = xsqr << 1; + const int nColors = rPal.GetEntryCount(); + const long x = 1 << gnBits; + const long x2 = x >> 1; + sal_uLong r, g, b; + long rxx, gxx, bxx; + + ImplCreateBuffers( nColorMax ); + + for( int nIndex = 0; nIndex < nColors; nIndex++ ) + { + const BitmapColor& rColor = rPal[ static_cast<sal_uInt16>(nIndex) ]; + const long cRed = rColor.GetRed(); + const long cGreen = rColor.GetGreen(); + const long cBlue = rColor.GetBlue(); + + long rdist = cRed - x2; + long gdist = cGreen - x2; + long bdist = cBlue - x2; + rdist = rdist*rdist + gdist*gdist + bdist*bdist; + + const long crinc = ( xsqr - ( cRed << gnBits ) ) << 1; + const long cginc = ( xsqr - ( cGreen << gnBits ) ) << 1; + const long cbinc = ( xsqr - ( cBlue << gnBits ) ) << 1; + + sal_uLong* cdp = reinterpret_cast<sal_uLong*>(pBuffer.get()); + sal_uInt8* crgbp = pMap.get(); + + for( r = 0, rxx = crinc; r < nColorMax; rdist += rxx, r++, rxx += xsqr2 ) + { + for( g = 0, gdist = rdist, gxx = cginc; g < nColorMax; gdist += gxx, g++, gxx += xsqr2 ) + { + for( b = 0, bdist = gdist, bxx = cbinc; b < nColorMax; bdist += bxx, b++, cdp++, crgbp++, bxx += xsqr2 ) + if ( !nIndex || static_cast<long>(*cdp) > bdist ) + { + *cdp = bdist; + *crgbp = static_cast<sal_uInt8>(nIndex); + } + } + } + } +} + +InverseColorMap::~InverseColorMap() +{ +} + +void InverseColorMap::ImplCreateBuffers( const sal_uLong nMax ) +{ + const sal_uLong nCount = nMax * nMax * nMax; + const sal_uLong nSize = nCount * sizeof( sal_uLong ); + + pMap.reset(new sal_uInt8[ nCount ]); + memset( pMap.get(), 0x00, nCount ); + + pBuffer.reset(new sal_uInt8[ nSize ]); + memset( pBuffer.get(), 0xff, nSize ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |