diff options
Diffstat (limited to 'vcl/source/bitmap/BitmapComboScaleFilter.cxx')
-rw-r--r-- | vcl/source/bitmap/BitmapComboScaleFilter.cxx | 370 |
1 files changed, 370 insertions, 0 deletions
diff --git a/vcl/source/bitmap/BitmapComboScaleFilter.cxx b/vcl/source/bitmap/BitmapComboScaleFilter.cxx new file mode 100644 index 000000000000..b88678badd1e --- /dev/null +++ b/vcl/source/bitmap/BitmapComboScaleFilter.cxx @@ -0,0 +1,370 @@ +/* -*- 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 <BitmapComboScaleFilter.hxx> + +#include <vcl/BitmapWriteAccess.hxx> +#include <bitmap/BitmapScaleSuperFilter.hxx> + +#include <tools/helpers.hxx> +#include <algorithm> +#include <memory> +#include <sal/log.hxx> +#include <tools/ScopedNanoTimer.hxx> +#include <tools/cpuid.hxx> + +#include <bitmap/ScanlineHalfScaler.hxx> + +namespace vcl +{ +namespace +{ +constexpr long constScaleThreadStrip = 4; + +constexpr bool ALLOW_HORIZONTAL_VERTICAL = false; + +typedef void (*ScaleRangeFn)(ScaleContext& rContext, long nStartY, long nEndY); + +class ScaleTask : public comphelper::ThreadTask +{ + ScaleRangeFn const mpScaleRangeFunction; + ScaleContext& mrContext; + const long mnStartY; + const long mnEndY; + +public: + explicit ScaleTask(const std::shared_ptr<comphelper::ThreadTaskTag>& pTag, + ScaleRangeFn pScaleRangeFunction, ScaleContext& rContext, long nStartY, + long nEndY) + : comphelper::ThreadTask(pTag) + , mpScaleRangeFunction(pScaleRangeFunction) + , mrContext(rContext) + , mnStartY(nStartY) + , mnEndY(nEndY) + { + } + + virtual void doWork() override { mpScaleRangeFunction(mrContext, mnStartY, mnEndY); } +}; + +} // anonymous namespace + +BitmapComboScaleFilter::BitmapComboScaleFilter(const double& rScaleX, const double& rScaleY) + : mrScaleX(rScaleX) + , mrScaleY(rScaleY) + , mpThreadPoolTag(comphelper::ThreadPool::createThreadTaskTag()) +{ +} + +BitmapComboScaleFilter::~BitmapComboScaleFilter() {} + +bool BitmapComboScaleFilter::scale(ScaleType eType, Bitmap& rBitmap) +{ + if (eType == ScaleType::NONE) + return false; + + bool bResult = false; + + const Size aSizePix(rBitmap.GetSizePixel()); + + bool bMirrorHorizontal = mrScaleX < 0; + bool bMirrorVertical = mrScaleY < 0; + + double nScaleX = 0.0; + double nScaleY = 0.0; + + switch (eType) + { + case ScaleType::OCTAL: + nScaleX = 0.125; + nScaleY = 0.125; + break; + case ScaleType::QUARTER: + nScaleX = 0.25; + nScaleY = 0.25; + break; + case ScaleType::QUARTER_HORIZONTAL: + nScaleX = 0.25; + nScaleY = 1.0; + break; + case ScaleType::QUARTER_VERTICAL: + nScaleX = 1.0; + nScaleY = 0.25; + break; + case ScaleType::HALF: + nScaleX = 0.5; + nScaleY = 0.5; + break; + case ScaleType::HALF_HORIZONTAL: + nScaleX = 0.5; + nScaleY = 1.0; + break; + case ScaleType::HALF_VERTICAL: + nScaleX = 1.0; + nScaleY = 0.5; + break; + case ScaleType::NONE: + break; + } + + const Size aDestinationSize(std::lround(aSizePix.Width() * nScaleX), + std::lround(aSizePix.Height() * nScaleY)); + + if (aDestinationSize.Width() == aSizePix.Width() + && aDestinationSize.Height() == aSizePix.Height()) + { + return true; + } + + if (aDestinationSize.Width() <= 1L || aDestinationSize.Height() <= 1L) + { + return false; + } + + auto eSourcePixelFormat = rBitmap.getPixelFormat(); + auto ePixelFormat = eSourcePixelFormat; + if (sal_uInt16(eSourcePixelFormat) < 24) + ePixelFormat = vcl::PixelFormat::N24_BPP; + + Bitmap aOutBmp(aDestinationSize, ePixelFormat); + Size aOutSize = aOutBmp.GetSizePixel(); + + const long nStartY = 0; + const long nEndY = aDestinationSize.Height() - 1; + + if (!aOutSize.Width() || !aOutSize.Height()) + { + SAL_WARN("vcl.gdi", "bmp creation failed"); + return false; + } + + { + BitmapScopedReadAccess pReadAccess(rBitmap); + BitmapScopedWriteAccess pWriteAccess(aOutBmp); + + if (pReadAccess && pWriteAccess) + { + ScaleRangeFn pScaleRangeFn; + ScaleContext aContext(pReadAccess.get(), pWriteAccess.get(), pReadAccess->Width(), + pWriteAccess->Width(), pReadAccess->Height(), + pWriteAccess->Height(), bMirrorVertical, bMirrorHorizontal); + + switch (eType) + { + case ScaleType::OCTAL: + { + switch (pReadAccess->GetScanlineFormat()) + { + case ScanlineFormat::N24BitTcBgr: + case ScanlineFormat::N24BitTcRgb: + pScaleRangeFn = scaleOctal24; + break; + case ScanlineFormat::N32BitTcRgba: + case ScanlineFormat::N32BitTcBgra: + case ScanlineFormat::N32BitTcArgb: + case ScanlineFormat::N32BitTcAbgr: + pScaleRangeFn = scaleOctal32; + break; + default: + pScaleRangeFn = scaleOctalGeneral; + break; + } + + break; + } + case ScaleType::QUARTER: + switch (pReadAccess->GetScanlineFormat()) + { + case ScanlineFormat::N24BitTcBgr: + case ScanlineFormat::N24BitTcRgb: + pScaleRangeFn = scaleQuarter24; + break; + case ScanlineFormat::N32BitTcRgba: + case ScanlineFormat::N32BitTcBgra: + case ScanlineFormat::N32BitTcArgb: + case ScanlineFormat::N32BitTcAbgr: + pScaleRangeFn = scaleQuarter32_SSE; + break; + default: + pScaleRangeFn = scaleQuarterGeneral; + break; + } + + break; + case ScaleType::QUARTER_HORIZONTAL: + pScaleRangeFn = scaleQuarterGeneralHorizontal; + break; + case ScaleType::QUARTER_VERTICAL: + pScaleRangeFn = scaleQuarterGeneralVertical; + break; + case ScaleType::HALF: + switch (pReadAccess->GetScanlineFormat()) + { + case ScanlineFormat::N24BitTcBgr: + case ScanlineFormat::N24BitTcRgb: + pScaleRangeFn = scaleHalf24; + break; + case ScanlineFormat::N32BitTcRgba: + case ScanlineFormat::N32BitTcBgra: + case ScanlineFormat::N32BitTcArgb: + case ScanlineFormat::N32BitTcAbgr: + pScaleRangeFn = scaleHalf32_SSE2; + break; + default: + pScaleRangeFn = scaleHalfGeneral; + break; + } + + break; + case ScaleType::HALF_HORIZONTAL: + pScaleRangeFn = scaleHalfGeneralHorizontal; + break; + case ScaleType::HALF_VERTICAL: + pScaleRangeFn = scaleHalfGeneralVertical; + break; + default: + return false; + } + + bool bUseThreads = true; + + static bool bDisableThreadedScaling = getenv("VCL_NO_THREAD_SCALE"); + if (bDisableThreadedScaling) + { + SAL_INFO("vcl.gdi", "Scale in main thread"); + bUseThreads = false; + } + + if (bUseThreads) + { + try + { + comphelper::ThreadPool& rShared + = comphelper::ThreadPool::getSharedOptimalPool(); + // partition and queue work + long nStripYStart = nStartY; + long nStripYEnd = nStripYStart + constScaleThreadStrip - 1; + + while (nStripYEnd < nEndY) + { + std::unique_ptr<ScaleTask> pTask(new ScaleTask( + mpThreadPoolTag, pScaleRangeFn, aContext, nStripYStart, nStripYEnd)); + rShared.pushTask(std::move(pTask)); + nStripYStart += constScaleThreadStrip; + nStripYEnd += constScaleThreadStrip; + } + if (nStripYStart <= nEndY) + { + std::unique_ptr<ScaleTask> pTask(new ScaleTask( + mpThreadPoolTag, pScaleRangeFn, aContext, nStripYStart, nEndY)); + rShared.pushTask(std::move(pTask)); + } + rShared.waitUntilDone(mpThreadPoolTag); + 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); + + bResult = true; + } + } + + if (bResult) + { + rBitmap = aOutBmp; + } + return bResult; +} + +bool BitmapComboScaleFilter::fastPrescale(Bitmap& rBitmap) +{ + const Size aSize(rBitmap.GetSizePixel()); + + const long nDestinationWidth = std::lround(aSize.Width() * std::fabs(mrScaleX)); + const long nDestinationHeight = std::lround(aSize.Height() * std::fabs(mrScaleY)); + + bool bResult = false; + ScaleType eType = ScaleType::NONE; + if (mrScaleX <= 0.125 && mrScaleY <= 0.125) + { + eType = ScaleType::OCTAL; + } + else if (mrScaleX <= 0.25 && mrScaleY <= 0.25) + { + eType = ScaleType::QUARTER; + } + else if (mrScaleX <= 0.25 && ALLOW_HORIZONTAL_VERTICAL) + { + eType = ScaleType::QUARTER_HORIZONTAL; + } + else if (mrScaleY <= 0.25 && ALLOW_HORIZONTAL_VERTICAL) + { + eType = ScaleType::QUARTER_VERTICAL; + } + else if (mrScaleX <= 0.5 && mrScaleY <= 0.5) + { + eType = ScaleType::HALF; + } + else if (mrScaleX <= 0.5 && ALLOW_HORIZONTAL_VERTICAL) + { + eType = ScaleType::HALF_HORIZONTAL; + } + else if (mrScaleY <= 0.5 && ALLOW_HORIZONTAL_VERTICAL) + { + eType = ScaleType::HALF_VERTICAL; + } + + bResult = scale(eType, rBitmap); + + if (bResult) + { + const Size aNewSize(rBitmap.GetSizePixel()); + mrScaleX = nDestinationWidth / double(aNewSize.Width()); + mrScaleY = nDestinationHeight / double(aNewSize.Height()); + printf("Combo: %ld %ld -> %ld %ld\n", aSize.Width(), aSize.Height(), aNewSize.Width(), + aNewSize.Height()); + } + + return bResult; +} + +BitmapEx BitmapComboScaleFilter::execute(BitmapEx const& rBitmapEx) const +{ + Bitmap aBitmap(rBitmapEx.GetBitmap()); + + while (const_cast<BitmapComboScaleFilter*>(this)->fastPrescale(aBitmap)) + ; + + BitmapEx aBitmapEx(aBitmap); + BitmapScaleSuperFilter aScaleSuper(mrScaleX, mrScaleY); + BitmapEx aResult = aScaleSuper.execute(aBitmapEx); + + return aResult; +} + +} // end namespace vcl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |