/* -*- 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 <comphelper/threadpool.hxx>
#include <vcl/bitmapaccess.hxx>
#include <bitmapwriteaccess.hxx>
#include <BitmapScaleSuperFilter.hxx>
#include <algorithm>
#include <memory>
#include <sal/log.hxx>
namespace {
#define MAP( cVal0, cVal1, nFrac ) (static_cast<sal_uInt8>(((static_cast<long>(cVal0)<<7)+nFrac*(static_cast<long>(cVal1)-(cVal0)))>>7))
void generateMap(long nW, long nDstW, bool bHMirr, long* pMapIX, long* pMapFX)
{
const double fRevScaleX = (nDstW > 1) ? static_cast<double>(nW - 1) / (nDstW - 1) : 0.0;
long nTemp = nW - 2;
long nTempX = nW - 1;
for (long nX = 0; nX < nDstW; nX++)
{
double fTemp = nX * fRevScaleX;
if (bHMirr)
fTemp = nTempX - fTemp;
pMapIX[nX] = MinMax(static_cast<long>(fTemp), 0, nTemp);
pMapFX[nX] = static_cast<long>((fTemp - pMapIX[nX]) * 128.0);
}
}
struct ScaleContext {
BitmapReadAccess *mpSrc;
BitmapWriteAccess *mpDest;
long mnSrcW, mnDestW;
long mnSrcH, mnDestH;
bool mbHMirr, mbVMirr;
std::unique_ptr<long[]> mpMapIX;
std::unique_ptr<long[]> mpMapIY;
std::unique_ptr<long[]> mpMapFX;
std::unique_ptr<long[]> mpMapFY;
ScaleContext( BitmapReadAccess *pSrc,
BitmapWriteAccess *pDest,
long nSrcW, long nDestW,
long nSrcH, long nDestH,
bool bHMirr, bool bVMirr)
: mpSrc( pSrc ), mpDest( pDest )
, mnSrcW( nSrcW ), mnDestW( nDestW )
, mnSrcH( nSrcH ), mnDestH( nDestH )
, mbHMirr( bHMirr ), mbVMirr( bVMirr )
, mpMapIX( new long[ nDestW ] )
, mpMapIY( new long[ nDestH ] )
, mpMapFX( new long[ nDestW ] )
, mpMapFY( new long[ nDestH ] )
{
generateMap(nSrcW, nDestW, bHMirr, mpMapIX.get(), mpMapFX.get());
generateMap(nSrcH, nDestH, bVMirr, mpMapIY.get(), mpMapFY.get());
}
};
#define SCALE_THREAD_STRIP 32
struct ScaleRangeContext {
ScaleContext *mrCtx;
long mnStartY, mnEndY;
ScaleRangeContext( ScaleContext *rCtx, long nStartY )
: mrCtx( rCtx ), mnStartY( nStartY ),
mnEndY( nStartY + SCALE_THREAD_STRIP ) {}
};
typedef void (*ScaleRangeFn)(ScaleContext &rCtx, long nStartY, long nEndY);
class ScaleTask : public comphelper::ThreadTask
{
ScaleRangeFn mpFn;
std::vector< ScaleRangeContext > maStrips;
public:
explicit ScaleTask( const std::shared_ptr<comphelper::ThreadTaskTag>& pTag, ScaleRangeFn pFn )
: comphelper::ThreadTask(pTag), mpFn( pFn ) {}
void push( ScaleRangeContext const &aRC ) { maStrips.push_back( aRC ); }
virtual void doWork() override
{
for (auto const& strip : maStrips)
mpFn( *(strip.mrCtx), strip.mnStartY, strip.mnEndY );
}
};
void scalePallete8bit(ScaleContext &rCtx, long nStartY, long nEndY)
{
const long nStartX = 0, nEndX = rCtx.mnDestW - 1;
for( long nY = nStartY; nY <= nEndY; nY++ )
{
long nTempY = rCtx.mpMapIY[ nY ];
long nTempFY = rCtx.mpMapFY[ nY ];
Scanline pLine0 = rCtx.mpSrc->GetScanline( nTempY );
Scanline pLine1 = rCtx.mpSrc->GetScanline( ++nTempY );
Scanline pScanDest = rCtx.mpDest->GetScanline( nY );
for(long nX = nStartX, nXDst = 0; nX <= nEndX; nX++ )
{
long nTempX = rCtx.mpMapIX[ nX ];
long nTempFX = rCtx.mpMapFX[ nX ];
const BitmapColor& rCol0 = rCtx.mpSrc->GetPaletteColor( pLine0[ nTempX ] );
const BitmapColor& rCol2 = rCtx.mpSrc->GetPaletteColor( pLine1[ nTempX ] );
const BitmapColor& rCol1 = rCtx.mpSrc->GetPaletteColor( pLine0[ ++nTempX ] );
const BitmapColor& rCol3 = rCtx.mpSrc->GetPaletteColor( pLine1[ nTempX ] );
sal_uInt8 cR0 = MAP( rCol0.GetRed(), rCol1.GetRed(), nTempFX );
sal_uInt8 cG0 = MAP( rCol0.GetGreen(), rCol1.GetGreen(), nTempFX );
sal_uInt8 cB0 = MAP( rCol0.GetBlue(), rCol1.GetBlue(), nTempFX );
sal_uInt8 cR1 = MAP( rCol2.GetRed(), rCol3.GetRed(), nTempFX );
sal_uInt8 cG1 = MAP( rCol2.GetGreen(), rCol3.GetGreen(), nTempFX );
sal_uInt8 cB1 = MAP( rCol2.GetBlue(), rCol3.GetBlue(), nTempFX );
BitmapColor aColRes( MAP( cR0, cR1, nTempFY ),
MAP( cG0, cG1, nTempFY ),
MAP( cB0, cB1, nTempFY ) );
rCtx.mpDest->SetPixelOnData( pScanDest, nXDst++, aColRes );
}
}
}
void scalePalleteGeneral(ScaleContext &rCtx, long nStartY, long nEndY)
{
const long nStartX = 0, nEndX = rCtx.mnDestW - 1;
for( long nY = nStartY; nY <= nEndY; nY++ )
{
long nTempY = rCtx.mpMapIY[ nY ];
long nTempFY = rCtx.mpMapFY[ nY ];
Scanline pScanline = rCtx.mpDest->GetScanline( nY );
for( long nX = nStartX, nXDst = 0; nX <= nEndX; nX++ )
{
long nTempX = rCtx.mpMapIX[ nX ];
long nTempFX = rCtx.mpMapFX[ nX ];
BitmapColor aCol0 = rCtx.mpSrc->GetPaletteColor( rCtx.mpSrc->GetPixelIndex( nTempY, nTempX ) );
BitmapColor aCol1 = rCtx.mpSrc->GetPaletteColor( rCtx.mpSrc->GetPixelIndex( nTempY, ++nTempX ) );
sal_uInt8 cR0 = MAP( aCol0.GetRed(), aCol1.GetRed(), nTempFX );
sal_uInt8 cG0 = MAP( aCol0.GetGreen(), aCol1.GetGreen(), nTempFX );
sal_uInt8 cB0 = MAP( aCol0.GetBlue(), aCol1.GetBlue(), nTempFX );
aCol1 = rCtx.mpSrc->GetPaletteColor( rCtx.mpSrc->GetPixelIndex( ++nTempY, nTempX ) );
aCol0 = rCtx.mpSrc->GetPaletteColor( rCtx.mpSrc->GetPixelIndex( nTempY--, --nTempX ) );
sal_uInt8 cR1 = MAP( aCol0.GetRed(), aCol1.GetRed(), nTempFX );
sal_uInt8 cG1 = MAP( aCol0.GetGreen(), aCol1.GetGreen(), nTempFX );
sal_uInt8 cB1 = MAP( aCol0.GetBlue(), aCol1.GetBlue(), nTempFX );
BitmapColor aColRes( MAP( cR0, cR1, nTempFY ),
MAP( cG0, cG1, nTempFY ),
MAP( cB0, cB1, nTempFY ) );
rCtx.mpDest->SetPixelOnData( pScanline, nXDst++, aColRes );
}
}
}
void scale24bitBGR(ScaleContext &rCtx, long nStartY, long nEndY)
{
const long nStartX = 0, nEndX = rCtx.mnDestW - 1;
for( long nY = nStartY; nY <= nEndY; nY++ )
{
long nTempY = rCtx.mpMapIY[ nY ];
long nTempFY = rCtx.mpMapFY[ nY ];
Scanline pLine0 = rCtx.mpSrc->GetScanline( nTempY );
Scanline pLine1 = rCtx.mpSrc->GetScanline( ++nTempY );
Scanline pScanDest = rCtx.mpDest->GetScanline( nY );
for( long nX = nStartX, nXDst = 0; nX <= nEndX; nX++ )
{
long nOff = 3 * rCtx.mpMapIX[ nX ];
long nTempFX = rCtx.mpMapFX[ nX ];
Scanline pTmp0 = pLine0 + nOff ;
Scanline pTmp1 = pTmp0 + 3;
sal_uInt8 cB0 = MAP( *pTmp0, *pTmp1, nTempFX );
pTmp0++; pTmp1++;
sal_uInt8 cG0 = MAP( *pTmp0, *pTmp1, nTempFX );
pTmp0++; pTmp1++;
sal_uInt8 cR0 = MAP( *pTmp0, *pTmp1, nTempFX );
pTmp1 = ( pTmp0 = pLine1 + nOff ) + 3;
sal_uInt8 cB1 = MAP( *pTmp0, *pTmp1, nTempFX );
pTmp0++; pTmp1++;
sal_uInt8 cG1 = MAP( *pTmp0, *pTmp1, nTempFX );
pTmp0++; pTmp1++;
sal_uInt8 cR1 = MAP( *pTmp0, *pTmp1, nTempFX );
BitmapColor aColRes( MAP( cR0, cR1, nTempFY ),
MAP( cG0, cG1, nTempFY ),
MAP( cB0, cB1, nTempFY ) );
rCtx.mpDest->SetPixelOnData( pScanDest, nXDst++, aColRes );
}
}
}
void scale24bitRGB(ScaleContext &rCtx, long nStartY, long nEndY)
{
const long nStartX = 0, nEndX = rCtx.mnDestW - 1;
for( long nY = nStartY; nY <= nEndY; nY++ )
{
long nTempY = rCtx.mpMapIY[ nY ];
long nTempFY = rCtx.mpMapFY[ nY ];
Scanline pLine0 = rCtx.mpSrc->GetScanline( nTempY );
Scanline pLine1 = rCtx.mpSrc->GetScanline( ++nTempY );
Scanline pScanDest = rCtx.mpDest->GetScanline( nY );
for( long nX = nStartX, nXDst = 0; nX <= nEndX; nX++ )
{
long nOff = 3 * rCtx.mpMapIX[ nX ];
long nTempFX = rCtx.mpMapFX[ nX ];
Scanline pTmp0 = pLine0 + nOff;
Scanline pTmp1 = pTmp0 + 3;
sal_uInt8 cR0 = MAP( *pTmp0, *pTmp1, nTempFX );
pTmp0++; pTmp1++;
sal_uInt8 cG0 = MAP( *pTmp0, *pTmp1, nTempFX );
pTmp0++; pTmp1++;
sal_uInt8 cB0 = MAP( *pTmp0, *pTmp1, nTempFX );
pTmp1 = ( pTmp0 = pLine1 + nOff ) + 3;
sal_uInt8 cR1 = MAP( *pTmp0, *pTmp1, nTempFX );
pTmp0++; pTmp1++;
sal_uInt8 cG1 = MAP( *pTmp0, *pTmp1, nTempFX );
pTmp0++; pTmp1++;
sal_uInt8 cB1 = MAP( *pTmp0, *pTmp1, nTempFX );
BitmapColor aColRes( MAP( cR0, cR1, nTempFY ),
MAP( cG0, cG1, nTempFY ),
MAP( cB0, cB1, nTempFY ) );
rCtx.mpDest->SetPixelOnData( pScanDest, nXDst++, aColRes );
}
}
}
void scaleNonPalleteGeneral(ScaleContext &rCtx, long nStartY, long nEndY)
{
const long nStartX = 0, nEndX = rCtx.mnDestW - 1;
for( long nY = nStartY; nY <= nEndY; nY++ )
{
long nTempY = rCtx.mpMapIY[ nY ];
long nTempFY = rCtx.mpMapFY[ nY ];
Scanline pScanDest = rCtx.mpDest->GetScanline( nY );
for( long nX = nStartX, nXDst = 0; nX <= nEndX; nX++ )
{
long nTempX = rCtx.mpMapIX[ nX ];
long nTempFX = rCtx.mpMapFX[ nX ];
BitmapColor aCol0 = rCtx.mpSrc->GetPixel( nTempY, nTempX );
BitmapColor aCol1 = rCtx.mpSrc->GetPixel( nTempY, ++nTempX );
sal_uInt8 cR0 = MAP( aCol0.GetRed(), aCol1.GetRed(), nTempFX );
sal_uInt8 cG0 = MAP( aCol0.GetGreen(), aCol1.GetGreen(), nTempFX );
sal_uInt8 cB0 = MAP( aCol0.GetBlue(), aCol1.GetBlue(), nTempFX );
aCol1 = rCtx.mpSrc->GetPixel( ++nTempY, nTempX );
aCol0 = rCtx.mpSrc->GetPixel( nTempY--, --nTempX );
sal_uInt8 cR1 = MAP( aCol0.GetRed(), aCol1.GetRed(), nTempFX );
sal_uInt8 cG1 = MAP( aCol0.GetGreen(), aCol1.GetGreen(), nTempFX );
sal_uInt8 cB1 = MAP( aCol0.GetBlue(), aCol1.GetBlue(), nTempFX );
BitmapColor aColRes( MAP( cR0, cR1, nTempFY ),
MAP( cG0, cG1, nTempFY ),
MAP( cB0, cB1, nTempFY ) );
rCtx.mpDest->SetPixelOnData( pScanDest, nXDst++, aColRes );
}
}
}
void scalePallete8bit2(ScaleContext &rCtx, long nStartY, long nEndY)
{
const long nStartX = 0, nEndX = rCtx.mnDestW - 1;
const long nMax = 1 << 7;
for( long nY = nStartY; nY <= nEndY; nY++ )
{
long nTop = rCtx.mbVMirr ? ( nY + 1 ) : nY;
long nBottom = rCtx.mbVMirr ? nY : ( nY + 1 ) ;
long nLineStart, nLineRange;
if( nY == nEndY )
{
nLineStart = rCtx.mpMapIY[ nY ];
nLineRange = 0;
}
else
{
nLineStart = rCtx.mpMapIY[ nTop ] ;
nLineRange = ( rCtx.mpMapIY[ nBottom ] == rCtx.mpMapIY[ nTop ] ) ? 1 :( rCtx.mpMapIY[ nBottom ] - rCtx.mpMapIY[ nTop ] );
}
Scanline pScanDest = rCtx.mpDest->GetScanline( nY );
for( long nX = nStartX , nXDst = 0; nX <= nEndX; nX++ )
{
long nLeft = rCtx.mbHMirr ? ( nX + 1 ) : nX;
long nRight = rCtx.mbHMirr ? nX : ( nX + 1 ) ;
long nRowStart;
long nRowRange;
if( nX == nEndX )
{
nRowStart = rCtx.mpMapIX[ nX ];
nRowRange = 0;
}
else
{
nRowStart = rCtx.mpMapIX[ nLeft ];
nRowRange = ( rCtx.mpMapIX[ nRight ] == rCtx.mpMapIX[ nLeft ] )? 1 : ( rCtx.mpMapIX[ nRight ] - rCtx.mpMapIX[ nLeft ] );
}
long nSumR = 0;
long nSumG = 0;
long nSumB = 0;
long nTotalWeightY = 0;
for(long i = 0; i<= nLineRange; i++)
{
Scanline pTmpY = rCtx.mpSrc->GetScanline( nLineStart + i );
long nSumRowR = 0;
long nSumRowG = 0;
long nSumRowB = 0;
long nTotalWeightX = 0;
for(long j = 0; j <= nRowRange; j++)
{
const BitmapColor& rCol = rCtx.mpSrc->GetPaletteColor( pTmpY[ nRowStart + j ] );
if(nX == nEndX )
{
nSumRowB += rCol.GetBlue() << 7;
nSumRowG += rCol.GetGreen() << 7;
nSumRowR += rCol.GetRed() << 7;
nTotalWeightX += 1 << 7;
}
else if( j == 0 )
{
long nWeightX = (nMax- rCtx.mpMapFX[ nLeft ]) ;
nSumRowB += ( nWeightX *rCol.GetBlue()) ;
nSumRowG += ( nWeightX *rCol.GetGreen()) ;
nSumRowR += ( nWeightX *rCol.GetRed()) ;
nTotalWeightX += nWeightX;
}
else if ( nRowRange == j )
{
long nWeightX = rCtx.mpMapFX[ nRight ] ;
nSumRowB += ( nWeightX *rCol.GetBlue() );
nSumRowG += ( nWeightX *rCol.GetGreen() );
nSumRowR += ( nWeightX *rCol.GetRed() );
nTotalWeightX += nWeightX;
}
else
{
nSumRowB += rCol.GetBlue() << 7;
nSumRowG += rCol.GetGreen() << 7;
nSumRowR += rCol.GetRed() << 7;
nTotalWeightX += 1 << 7;
}
}
long nWeightY = nMax;
if( nY == nEndY )
nWeightY = nMax;
else if( i == 0 )
nWeightY = nMax - rCtx.mpMapFY[ nTop ];
else if( nLineRange == 1 )
nWeightY = rCtx.mpMapFY[ nTop ];
else if ( nLineRange == i )
nWeightY = rCtx.mpMapFY[ nBottom ];
if (nTotalWeightX)
{
nSumRowB /= nTotalWeightX;
nSumRowG /= nTotalWeightX;
nSumRowR /= nTotalWeightX;
}
nSumB += nWeightY * nSumRowB;
nSumG += nWeightY * nSumRowG;
nSumR += nWeightY * nSumRowR;
nTotalWeightY += nWeightY;
}
if (nTotalWeightY)
{
nSumR /= nTotalWeightY;
nSumG /= nTotalWeightY;
nSumB /= nTotalWeightY;
}
BitmapColor aColRes(static_cast<sal_uInt8>(nSumR), static_cast<sal_uInt8>(nSumG), static_cast<sal_uInt8>(nSumB));
rCtx.mpDest->SetPixelOnData( pScanDest, nXDst++, aColRes );
}
}
}
void scalePalleteGeneral2(ScaleContext &rCtx, long nStartY, long nEndY)
{
const long nStartX = 0, nEndX = rCtx.mnDestW - 1;
const long nMax = 1 << 7;
for( long nY = nStartY; nY <= nEndY; nY++ )
{
long nTop = rCtx.mbVMirr ? ( nY + 1 ) : nY;
long nBottom = rCtx.mbVMirr ? nY : ( nY + 1 ) ;
long nLineStart, nLineRange;
if( nY ==nEndY )
{
nLineStart = rCtx.mpMapIY[ nY ];
nLineRange = 0;
}
else
{
nLineStart = rCtx.mpMapIY[ nTop ] ;
nLineRange = ( rCtx.mpMapIY[ nBottom ] == rCtx.mpMapIY[ nTop ] ) ? 1 :( rCtx.mpMapIY[ nBottom ] - rCtx.mpMapIY[ nTop ] );
}
Scanline pScanDest = rCtx.mpDest->GetScanline( nY );
for( long nX = nStartX , nXDst = 0; nX <= nEndX; nX++ )
{
long nLeft = rCtx.mbHMirr ? ( nX + 1 ) : nX;
long nRight = rCtx.mbHMirr ? nX : ( nX + 1 ) ;
long nRowStart, nRowRange;
if( nX == nEndX )
{
nRowStart = rCtx.mpMapIX[ nX ];
nRowRange = 0;
}
else
{
nRowStart = rCtx.mpMapIX[ nLeft ];
nRowRange = ( rCtx.mpMapIX[ nRight ] == rCtx.mpMapIX[ nLeft ] )? 1 : ( rCtx.mpMapIX[ nRight ] - rCtx.mpMapIX[ nLeft ] );
}
long nSumR = 0;
long nSumG = 0;
long nSumB = 0;
long nTotalWeightY = 0;
for(long i = 0; i<= nLineRange; i++)
{
long nSumRowR = 0;
long nSumRowG = 0;
long nSumRowB = 0;
long nTotalWeightX = 0;
Scanline pScanlineSrc = rCtx.mpSrc->GetScanline( nLineStart + i );
for(long j = 0; j <= nRowRange; j++)
{
BitmapColor aCol0 = rCtx.mpSrc->GetPaletteColor ( rCtx.mpSrc->GetIndexFromData( pScanlineSrc, nRowStart + j ) );
if(nX == nEndX )
{
nSumRowB += aCol0.GetBlue() << 7;
nSumRowG += aCol0.GetGreen() << 7;
nSumRowR += aCol0.GetRed() << 7;
nTotalWeightX += 1 << 7;
}
else if( j == 0 )
{
long nWeightX = (nMax- rCtx.mpMapFX[ nLeft ]) ;
nSumRowB += ( nWeightX *aCol0.GetBlue()) ;
nSumRowG += ( nWeightX *aCol0.GetGreen()) ;
nSumRowR += ( nWeightX *aCol0.GetRed()) ;
nTotalWeightX += nWeightX;
}
else if ( nRowRange == j )
{
long nWeightX = rCtx.mpMapFX[ nRight ] ;
nSumRowB += ( nWeightX *aCol0.GetBlue() );
nSumRowG += ( nWeightX *aCol0.GetGreen() );
nSumRowR += ( nWeightX *aCol0.GetRed() );
nTotalWeightX += nWeightX;
}
else
{
nSumRowB += aCol0.GetBlue() << 7;
nSumRowG += aCol0.GetGreen() << 7;
nSumRowR += aCol0.GetRed() << 7;
nTotalWeightX += 1 << 7;
}
}
long nWeightY = nMax;
if( nY == nEndY )
nWeightY = nMax;
else if( i == 0 )
nWeightY = nMax - rCtx.mpMapFY[ nTop ];
else if( nLineRange == 1 )
nWeightY = rCtx.mpMapFY[ nTop ];
else if ( nLineRange == i )
nWeightY = rCtx.mpMapFY[ nBottom ];
if (nTotalWeightX)
{
nSumRowB /= nTotalWeightX;
nSumRowG /= nTotalWeightX;
nSumRowR /= nTotalWeightX;
}
nSumB += nWeightY * nSumRowB;
nSumG += nWeightY * nSumRowG;
nSumR += nWeightY * nSumRowR;
nTotalWeightY += nWeightY;
}
if (nTotalWeightY)
{
nSumR /= nTotalWeightY;
nSumG /= nTotalWeightY;
nSumB /= nTotalWeightY;
}
BitmapColor aColRes(static_cast<sal_uInt8>(nSumR), static_cast<sal_uInt8>(nSumG), static_cast<sal_uInt8>(nSumB));
rCtx.mpDest->SetPixelOnData( pScanDest, nXDst++, aColRes );
}
}
}
void scale24bitBGR2(ScaleContext &rCtx, long nStartY, long nEndY)
{
const long nStartX = 0, nEndX = rCtx.mnDestW - 1;
const long nMax = 1 << 7;
for( long nY = nStartY; nY <= nEndY; nY++ )
{
long nTop = rCtx.mbVMirr ? ( nY + 1 ) : nY;
long nBottom = rCtx.mbVMirr ? nY : ( nY + 1 ) ;
long nLineStart;
long nLineRange;
if( nY ==nEndY )
{
nLineStart = rCtx.mpMapIY[ nY ];
nLineRange = 0;
}
else
{
nLineStart = rCtx.mpMapIY[ nTop ] ;
nLineRange = ( rCtx.mpMapIY[ nBottom ] == rCtx.mpMapIY[ nTop ] ) ? 1 :( rCtx.mpMapIY[ nBottom ] - rCtx.mpMapIY[ nTop ] );
}
Scanline pScanDest = rCtx.mpDest->GetScanline( nY );
for( long nX = nStartX , nXDst = 0; nX <= nEndX; nX++ )
{
long nLeft = rCtx.mbHMirr ? ( nX + 1 ) : nX;
long nRight = rCtx.mbHMirr ? nX : ( nX + 1 ) ;
long nRowStart;
long nRowRange;
if( nX == nEndX )
{
nRowStart = rCtx.mpMapIX[ nX ];
nRowRange = 0;
}
else
{
nRowStart = rCtx.mpMapIX[ nLeft ];
nRowRange = ( rCtx.mpMapIX[ nRight ] == rCtx.mpMapIX[ nLeft ] )? 1 : ( rCtx.mpMapIX[ nRight ] - rCtx.mpMapIX[ nLeft ] );
}
long nSumR = 0;
long nSumG = 0;
long nSumB = 0;
long nTotalWeightY = 0;
for(long i = 0; i<= nLineRange; i++)
{
Scanline pTmpY = rCtx.mpSrc->GetScanline( nLineStart + i );
Scanline pTmpX = pTmpY + 3 * nRowStart;
long nSumRowR = 0;
long nSumRowG = 0;
long nSumRowB = 0;
long nTotalWeightX = 0;
for(long j = 0; j <= nRowRange; j++)
{
if(nX == nEndX )
{
nSumRowB += ( *pTmpX ) << 7;pTmpX++;
nSumRowG += ( *pTmpX ) << 7;pTmpX++;
nSumRowR += ( *pTmpX ) << 7;pTmpX++;
nTotalWeightX += 1 << 7;
}
else if( j == 0 )
{
long nWeightX = (nMax- rCtx.mpMapFX[ nLeft ]) ;
nSumRowB += ( nWeightX *( *pTmpX )) ;pTmpX++;
nSumRowG += ( nWeightX *( *pTmpX )) ;pTmpX++;
nSumRowR += ( nWeightX *( *pTmpX )) ;pTmpX++;
nTotalWeightX += nWeightX;
}
else if ( nRowRange == j )
{
long nWeightX = rCtx.mpMapFX[ nRight ] ;
nSumRowB += ( nWeightX *( *pTmpX ) );pTmpX++;
nSumRowG += ( nWeightX *( *pTmpX ) );pTmpX++;
nSumRowR += ( nWeightX *( *pTmpX ) );pTmpX++;
nTotalWeightX += nWeightX;
}
else
{
nSumRowB += ( *pTmpX ) << 7;pTmpX++;
nSumRowG += ( *pTmpX ) << 7;pTmpX++;
nSumRowR += ( *pTmpX ) << 7;pTmpX++;
nTotalWeightX += 1 << 7;
}
}
long nWeightY = nMax;
if( nY == nEndY )
nWeightY = nMax;
else if( i == 0 )
nWeightY = nMax - rCtx.mpMapFY[ nTop ];
else if( nLineRange == 1 )
nWeightY = rCtx.mpMapFY[ nTop ];
else if ( nLineRange == i )
nWeightY = rCtx.mpMapFY[ nBottom ];
if (nTotalWeightX)
{
nSumRowB /= nTotalWeightX;
nSumRowG /= nTotalWeightX;
nSumRowR /= nTotalWeightX;
}
nSumB += nWeightY * nSumRowB;
nSumG += nWeightY * nSumRowG;
nSumR += nWeightY * nSumRowR;
nTotalWeightY += nWeightY;
}
if (nTotalWeightY)
{
nSumR /= nTotalWeightY;
nSumG /= nTotalWeightY;
nSumB /= nTotalWeightY;
}
BitmapColor aColRes(static_cast<sal_uInt8>(nSumR), static_cast<sal_uInt8>(nSumG), static_cast<sal_uInt8>(nSumB));
rCtx.mpDest->SetPixelOnData( pScanDest, nXDst++, aColRes );
}
}
}
void scale24bitRGB2(ScaleContext &rCtx, long nStartY, long nEndY)
{
const long nStartX = 0, nEndX = rCtx.mnDestW - 1;
const long nMax = 1 << 7;
for( long nY = nStartY; nY <= nEndY; nY++ )
{
long nTop = rCtx.mbVMirr ? ( nY + 1 ) : nY;
long nBottom = rCtx.mbVMirr ? nY : ( nY + 1 ) ;
long nLineStart, nLineRange;
if( nY ==nEndY )
{
nLineStart = rCtx.mpMapIY[ nY ];
nLineRange = 0;
}
else
{
nLineStart = rCtx.mpMapIY[ nTop ] ;
nLineRange = ( rCtx.mpMapIY[ nBottom ] == rCtx.mpMapIY[ nTop ] ) ? 1 :( rCtx.mpMapIY[ nBottom ] - rCtx.mpMapIY[ nTop ] );
}
Scanline pScanDest = rCtx.mpDest->GetScanline( nY );
for( long nX = nStartX , nXDst = 0; nX <= nEndX; nX++ )
{
long nLeft = rCtx.mbHMirr ? ( nX + 1 ) : nX;
long nRight = rCtx.mbHMirr ? nX : ( nX + 1 ) ;
long nRowStart, nRowRange;
if( nX == nEndX )
{
nRowStart = rCtx.mpMapIX[ nX ];
nRowRange = 0;
}
else
{
nRowStart = rCtx.mpMapIX[ nLeft ];
nRowRange = ( rCtx.mpMapIX[ nRight ] == rCtx.mpMapIX[ nLeft ] )? 1 : ( rCtx.mpMapIX[ nRight ] - rCtx.mpMapIX[ nLeft ] );
}
long nSumR = 0;
long nSumG = 0;
long nSumB = 0;
long nTotalWeightY = 0;
for(long i = 0; i<= nLineRange; i++)
{
Scanline pTmpY = rCtx.mpSrc->GetScanline( nLineStart + i );
Scanline pTmpX = pTmpY + 3 * nRowStart;
long nSumRowR = 0;
long nSumRowG = 0;
long nSumRowB = 0;
long nTotalWeightX = 0;
for(long j = 0; j <= nRowRange; j++)
{
if(nX == nEndX )
{
nSumRowR += ( *pTmpX ) << 7;pTmpX++;
nSumRowG += ( *pTmpX ) << 7;pTmpX++;
nSumRowB += ( *pTmpX ) << 7;pTmpX++;
nTotalWeightX += 1 << 7;
}
else if( j == 0 )
{
long nWeightX = (nMax- rCtx.mpMapFX[ nLeft ]) ;
nSumRowR += ( nWeightX *( *pTmpX )) ;pTmpX++;
nSumRowG += ( nWeightX *( *pTmpX )) ;pTmpX++;
nSumRowB += ( nWeightX *( *pTmpX )) ;pTmpX++;
nTotalWeightX += nWeightX;
}
else if ( nRowRange == j )
{
long nWeightX = rCtx.mpMapFX[ nRight ] ;
nSumRowR += ( nWeightX *( *pTmpX ) );pTmpX++;
nSumRowG += ( nWeightX *( *pTmpX ) );pTmpX++;
nSumRowB += ( nWeightX *( *pTmpX ) );pTmpX++;
nTotalWeightX += nWeightX;
}
else
{
nSumRowR += ( *pTmpX ) << 7;pTmpX++;
nSumRowG += ( *pTmpX ) << 7;pTmpX++;
nSumRowB += ( *pTmpX ) << 7;pTmpX++;
nTotalWeightX += 1 << 7;
}
}
long nWeightY = nMax;
if( nY == nEndY )
nWeightY = nMax;
else if( i == 0 )
nWeightY = nMax - rCtx.mpMapFY[ nTop ];
else if( nLineRange == 1 )
nWeightY = rCtx.mpMapFY[ nTop ];
else if ( nLineRange == i )
nWeightY = rCtx.mpMapFY[ nBottom ];
if (nTotalWeightX)
{
nSumRowB /= nTotalWeightX;
nSumRowG /= nTotalWeightX;
nSumRowR /= nTotalWeightX;
}
nSumB += nWeightY * nSumRowB;
nSumG += nWeightY * nSumRowG;
nSumR += nWeightY * nSumRowR;
nTotalWeightY += nWeightY;
}
if (nTotalWeightY)
{
nSumR /= nTotalWeightY;
nSumG /= nTotalWeightY;
nSumB /= nTotalWeightY;
}
BitmapColor aColRes(static_cast<sal_uInt8>(nSumR), static_cast<sal_uInt8>(nSumG), static_cast<sal_uInt8>(nSumB));
rCtx.mpDest->SetPixelOnData( pScanDest, nXDst++, aColRes );
}
}
}
void scaleNonPalleteGeneral2(ScaleContext &rCtx, long nStartY, long nEndY)
{
const long nStartX = 0, nEndX = rCtx.mnDestW - 1;
const long nMax = 1 << 7;
for( long nY = nStartY; nY <= nEndY; nY++ )
{
long nTop = rCtx.mbVMirr ? ( nY + 1 ) : nY;
long nBottom = rCtx.mbVMirr ? nY : ( nY + 1 ) ;
long nLineStart, nLineRange;
if( nY ==nEndY )
{
nLineStart = rCtx.mpMapIY[ nY ];
nLineRange = 0;
}
else
{
nLineStart = rCtx.mpMapIY[ nTop ] ;
nLineRange = ( rCtx.mpMapIY[ nBottom ] == rCtx.mpMapIY[ nTop ] ) ? 1 :( rCtx.mpMapIY[ nBottom ] - rCtx.mpMapIY[ nTop ] );
}
Scanline pScanDest = rCtx.mpDest->GetScanline( nY );
for( long nX = nStartX , nXDst = 0; nX <= nEndX; nX++ )
{
long nLeft = rCtx.mbHMirr ? ( nX + 1 ) : nX;
long nRight = rCtx.mbHMirr ? nX : ( nX + 1 ) ;
long nRowStart, nRowRange;
if( nX == nEndX )
{
nRowStart = rCtx.mpMapIX[ nX ];
nRowRange = 0;
}
else
{
nRowStart = rCtx.mpMapIX[ nLeft ];
nRowRange = ( rCtx.mpMapIX[ nRight ] == rCtx.mpMapIX[ nLeft ] )? 1 : ( rCtx.mpMapIX[ nRight ] - rCtx.mpMapIX[ nLeft ] );
}
long nSumR = 0;
long nSumG = 0;
long nSumB = 0;
long nTotalWeightY = 0;
for(long i = 0; i<= nLineRange; i++)
{
long nSumRowR = 0;
long nSumRowG = 0;
long nSumRowB = 0;
long nTotalWeightX = 0;
Scanline pScanlineSrc = rCtx.mpSrc->GetScanline( nLineStart + i );
for(long j = 0; j <= nRowRange; j++)
{
BitmapColor aCol0 = rCtx.mpSrc->GetPixelFromData( pScanlineSrc, nRowStart + j );
if(nX == nEndX )
{
nSumRowB += aCol0.GetBlue() << 7;
nSumRowG += aCol0.GetGreen() << 7;
nSumRowR += aCol0.GetRed() << 7;
nTotalWeightX += 1 << 7;
}
else if( j == 0 )
{
long nWeightX = (nMax- rCtx.mpMapFX[ nLeft ]) ;
nSumRowB += ( nWeightX *aCol0.GetBlue()) ;
nSumRowG += ( nWeightX *aCol0.GetGreen()) ;
nSumRowR += ( nWeightX *aCol0.GetRed()) ;
nTotalWeightX += nWeightX;
}
else if ( nRowRange == j )
{
long nWeightX = rCtx.mpMapFX[ nRight ] ;
nSumRowB += ( nWeightX *aCol0.GetBlue() );
nSumRowG += ( nWeightX *aCol0.GetGreen() );
nSumRowR += ( nWeightX *aCol0.GetRed() );
nTotalWeightX += nWeightX;
}
else
{
nSumRowB += aCol0.GetBlue() << 7;
nSumRowG += aCol0.GetGreen() << 7;
nSumRowR += aCol0.GetRed() << 7;
nTotalWeightX += 1 << 7;
}
}
long nWeightY = nMax;
if( nY == nEndY )
nWeightY = nMax;
else if( i == 0 )
nWeightY = nMax - rCtx.mpMapFY[ nTop ];
else if( nLineRange == 1 )
nWeightY = rCtx.mpMapFY[ nTop ];
else if ( nLineRange == i )
nWeightY = rCtx.mpMapFY[ nBottom ];
if (nTotalWeightX)
{
nSumRowB /= nTotalWeightX;
nSumRowG /= nTotalWeightX;
nSumRowR /= nTotalWeightX;
}
nSumB += nWeightY * nSumRowB;
nSumG += nWeightY * nSumRowG;
nSumR += nWeightY * nSumRowR;
nTotalWeightY += nWeightY;
}
if (nTotalWeightY)
{
nSumR /= nTotalWeightY;
nSumG /= nTotalWeightY;
nSumB /= nTotalWeightY;
}
BitmapColor aColRes(static_cast<sal_uInt8>(nSumR), static_cast<sal_uInt8>(nSumG), static_cast<sal_uInt8>(nSumB));
rCtx.mpDest->SetPixelOnData( pScanDest, nXDst++, aColRes );
}
}
}
} // end anonymous namespace
BitmapScaleSuperFilter::BitmapScaleSuperFilter(const double& rScaleX, const double& rScaleY) :
mrScaleX(rScaleX),
mrScaleY(rScaleY)
{}
BitmapScaleSuperFilter::~BitmapScaleSuperFilter()
{}
BitmapEx BitmapScaleSuperFilter::execute(BitmapEx const& rBitmap) const
{
Bitmap aBitmap(rBitmap.GetBitmap());
bool bRet = false;
const Size aSizePix(rBitmap.GetSizePixel());
bool bHMirr = mrScaleX < 0;
bool bVMirr = mrScaleY < 0;
double fScaleX = std::fabs(mrScaleX);
double fScaleY = std::fabs(mrScaleY);
const long nDstW = FRound(aSizePix.Width() * fScaleX);
const long nDstH = FRound(aSizePix.Height() * fScaleY);
const double fScaleThresh = 0.6;
if (nDstW <= 1 || nDstH <= 1)
return BitmapEx();
{
Bitmap::ScopedReadAccess pReadAccess(aBitmap);
Bitmap aOutBmp(Size(nDstW, nDstH), 24);
Size aOutSize = aOutBmp.GetSizePixel();
if (!aOutSize.Width() || !aOutSize.Height())
{
SAL_WARN("vcl.gdi", "bmp creation failed");
return BitmapEx();
}
BitmapScopedWriteAccess pWriteAccess(aOutBmp);
const long nStartY = 0;
const long nEndY = nDstH - 1;
if (pReadAccess && pWriteAccess)
{
ScaleRangeFn pScaleRangeFn;
ScaleContext aContext( pReadAccess.get(),
pWriteAccess.get(),
pReadAccess->Width(),
pWriteAccess->Width(),
pReadAccess->Height(),
pWriteAccess->Height(),
bVMirr, bHMirr );
bool bScaleUp = fScaleX >= fScaleThresh && fScaleY >= fScaleThresh;
if( pReadAccess->HasPalette() )
{
switch( pReadAccess->GetScanlineFormat() )
{
case ScanlineFormat::N8BitPal:
pScaleRangeFn = bScaleUp ? scalePallete8bit : scalePallete8bit2;
break;
default:
pScaleRangeFn = bScaleUp ? scalePalleteGeneral
: scalePalleteGeneral2;
break;
}
}
else
{
switch( pReadAccess->GetScanlineFormat() )
{
case ScanlineFormat::N24BitTcBgr:
pScaleRangeFn = bScaleUp ? scale24bitBGR : scale24bitBGR2;
break;
case ScanlineFormat::N24BitTcRgb:
pScaleRangeFn = bScaleUp ? scale24bitRGB : scale24bitRGB2;
break;
default:
pScaleRangeFn = bScaleUp ? scaleNonPalleteGeneral
: scaleNonPalleteGeneral2;
break;
}
}
// We want to thread - only if there is a lot of work to do:
// We work hard when there is a large destination image, or
// A large source image.
bool bHorizontalWork = pReadAccess->Width() > 512 || pWriteAccess->Width() > 512;
bool bUseThreads = true;
static bool bDisableThreadedScaling = getenv ("VCL_NO_THREAD_SCALE");
if ( bDisableThreadedScaling || !bHorizontalWork ||
nEndY - nStartY < SCALE_THREAD_STRIP )
{
SAL_INFO("vcl.gdi", "Scale in main thread");
bUseThreads = false;
}
if (bUseThreads)
{
try
{
// partition and queue work
comphelper::ThreadPool &rShared = comphelper::ThreadPool::getSharedOptimalPool();
std::shared_ptr<comphelper::ThreadTaskTag> pTag = comphelper::ThreadPool::createThreadTaskTag();
sal_uInt32 nThreads = rShared.getWorkerCount();
assert( nThreads > 0 );
sal_uInt32 nStrips = ((nEndY - nStartY) + SCALE_THREAD_STRIP - 1) / SCALE_THREAD_STRIP;
sal_uInt32 nStripsPerThread = nStrips / nThreads;
SAL_INFO("vcl.gdi", "Scale in " << nStrips << " strips " << nStripsPerThread << " per thread we have " << nThreads << " CPU threads ");
long nStripY = nStartY;
for ( sal_uInt32 t = 0; t < nThreads - 1; t++ )
{
std::unique_ptr<ScaleTask> pTask(new ScaleTask( pTag, pScaleRangeFn ));
for ( sal_uInt32 j = 0; j < nStripsPerThread; j++ )
{
ScaleRangeContext aRC( &aContext, nStripY );
pTask->push( aRC );
nStripY += SCALE_THREAD_STRIP;
}
rShared.pushTask( std::move(pTask) );
}
// finish any remaining bits here
pScaleRangeFn( aContext, nStripY, nEndY );
rShared.waitUntilDone(pTag);
SAL_INFO("vcl.gdi", "All threaded scaling tasks complete");
}
catch (...)
{
SAL_WARN("vcl.gdi", "threaded bitmap scaling failed");
bUseThreads = false;
}
}
if (!bUseThreads)
pScaleRangeFn( aContext, nStartY, nEndY );
bRet = true;
if (bRet)
{
aBitmap.AdaptBitCount(aOutBmp);
aBitmap = aOutBmp;
}
}
}
if (bRet)
{
tools::Rectangle aRect(Point(0, 0), Point(nDstW, nDstH));
aBitmap.Crop(aRect);
return BitmapEx(aBitmap);
}
return BitmapEx();
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
↑ V547 Expression 'bRet' is always true.