/* -*- 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 #include #include #include #include #include #include #include #include #include #include #include "intconversion.hxx" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace basebmp { static const o3tl::enumarray bitsPerPixel = { 0, // NONE 1, // ONE_BIT_MSB_GREY 1, // ONE_BIT_LSB_GREY 1, // ONE_BIT_MSB_PAL 1, // ONE_BIT_LSB_PAL 4, // FOUR_BIT_MSB_GREY 4, // FOUR_BIT_LSB_GREY 4, // FOUR_BIT_MSB_PAL 4, // FOUR_BIT_LSB_PAL 8, // EIGHT_BIT_PAL 8, // EIGHT_BIT_GREY 16, // SIXTEEN_BIT_LSB_TC_MASK 16, // SIXTEEN_BIT_MSB_TC_MASK 24, // TWENTYFOUR_BIT_TC_MASK 32, // THIRTYTWO_BIT_TC_MASK_BGRA 32, // THIRTYTWO_BIT_TC_MASK_ARGB 32, // THIRTYTWO_BIT_TC_MASK_ABGR 32, // THIRTYTWO_BIT_TC_MASK_RGBA }; namespace { /** Create the type for an accessor that takes the (mask,bitmap) input value generated from a JoinImageAccessorAdapter, and pipe that through a mask functor. @tpl DestAccessor Destination bitmap accessor @tpl JoinedAccessor Input accessor, is expected to generate a std::pair as the value type @tpl MaskFunctorMode Either FastMask or NoFastMask, depending on whether the mask is guaranteed to contain only 0s and 1s. */ template< class DestAccessor, class JoinedAccessor, bool polarity, typename MaskFunctorMode > struct masked_input_splitting_accessor { typedef BinarySetterFunctionAccessorAdapter< DestAccessor, BinaryFunctorSplittingWrapper< typename outputMaskFunctorSelector< typename JoinedAccessor::value_type::first_type, typename JoinedAccessor::value_type::second_type, polarity, MaskFunctorMode >::type > > type; }; // Actual BitmapDevice implementation (templatized by accessor and iterator) /** Implementation of the BitmapDevice interface @tpl DestIterator Iterator to access bitmap memory @tpl RawAccessor Raw accessor, to access pixel values directly @tpl AccessorSelector Accessor adapter selector, which, when applying the nested template metafunction wrap_accessor to one of the raw bitmap accessors, yields a member type named 'type', which is a wrapped accessor that map color values. @tpl Masks Traits template, containing nested traits clipmask_format_traits and alphamask_format_traits, which determine what specialized formats are to be used for clip and alpha masks. With those mask formats, clipping and alpha blending is handled natively. */ template< class DestIterator, class RawAccessor, class AccessorSelector, class Masks > class BitmapRenderer : public BitmapDevice { public: typedef DestIterator dest_iterator_type; typedef RawAccessor raw_accessor_type; typedef AccessorSelector accessor_selector; typedef typename AccessorSelector::template wrap_accessor< raw_accessor_type >::type dest_accessor_type; typedef AccessorTraits< dest_accessor_type > accessor_traits; typedef AccessorTraits< raw_accessor_type > raw_accessor_traits; typedef typename uInt32Converter< typename raw_accessor_type::value_type>::to to_uint32_functor; dest_iterator_type maBegin; typename accessor_traits::color_lookup maColorLookup; to_uint32_functor maToUInt32Converter; dest_accessor_type maAccessor; raw_accessor_type maRawAccessor; BitmapRenderer( const basegfx::B2IBox& rBounds, const basegfx::B2IVector& rBufferSize, Format nScanlineFormat, sal_Int32 nScanlineStride, sal_uInt8* pFirstScanline, dest_iterator_type begin, raw_accessor_type rawAccessor, dest_accessor_type accessor, const RawMemorySharedArray& rMem, const PaletteMemorySharedVector& rPalette ) : BitmapDevice( rBounds, rBufferSize, nScanlineFormat, nScanlineStride, pFirstScanline, rMem, rPalette ), maBegin( begin ), maColorLookup(), maToUInt32Converter(), maAccessor( accessor ), maRawAccessor( rawAccessor ) {} private: static std::shared_ptr getCompatibleBitmap( const BitmapDeviceSharedPtr& bmp ) { return std::dynamic_pointer_cast< BitmapRenderer >( bmp ); } virtual bool isCompatibleBitmap( const BitmapDeviceSharedPtr& bmp ) const override { // TODO(P1): dynamic_cast usually called twice for // compatible formats return getCompatibleBitmap(bmp).get() != nullptr; } virtual Color getPixel_i(const basegfx::B2IPoint& rPt ) override { const DestIterator pixel( maBegin + vigra::Diff2D(rPt.getX(), rPt.getY()) ); return maAccessor(pixel); } virtual sal_uInt32 getPixelData_i( const basegfx::B2IPoint& rPt ) override { const DestIterator pixel( maBegin + vigra::Diff2D(rPt.getX(), rPt.getY()) ); return maToUInt32Converter(maRawAccessor(pixel)); } template< typename Iterator, typename RawAcc > void implDrawBitmap(const BitmapDeviceSharedPtr& rSrcBitmap, const Iterator& begin, const RawAcc& acc) { const basegfx::B2IVector& rSrcSize( rSrcBitmap->getSize() ); const basegfx::B2IBox aRect(0, 0, rSrcSize.getX(),rSrcSize.getY()); std::shared_ptr pSrcBmp( getCompatibleBitmap(rSrcBitmap) ); OSL_ASSERT( pSrcBmp ); scaleImage( srcIterRange(pSrcBmp->maBegin, pSrcBmp->maRawAccessor, aRect), destIterRange(begin, acc, aRect), isSharedBuffer(rSrcBitmap) ); } template< typename Iterator, typename Acc > static void implDrawBitmapGeneric(const BitmapDeviceSharedPtr& rSrcBitmap, const Iterator& begin, const Acc& acc) { const basegfx::B2IVector& rSrcSize( rSrcBitmap->getSize() ); const basegfx::B2IBox aRect(0, 0, rSrcSize.getX(),rSrcSize.getY()); GenericColorImageAccessor aSrcAcc( rSrcBitmap ); scaleImage( srcIterRange(vigra::Diff2D(), aSrcAcc, aRect), destIterRange(begin, acc, aRect)); } void implDrawBitmapDirect(const BitmapDeviceSharedPtr& rSrcBitmap) { const basegfx::B2IVector& rSrcSize(rSrcBitmap->getSize()); sal_Int32 nSrcWidth = rSrcSize.getX(); sal_Int32 nSrcHeight = rSrcSize.getY(); char* dstBuf = reinterpret_cast(getBuffer().get()); char* srcBuf = reinterpret_cast(rSrcBitmap->getBuffer().get()); sal_Int32 dstStride = getScanlineStride(); sal_Int32 srcStride = rSrcBitmap->getScanlineStride(); sal_Int32 bytesPerPixel = (bitsPerPixel[getScanlineFormat()] + 7) >> 3; // round up to bytes char* dstline = dstBuf; char* srcline = srcBuf; sal_Int32 lineBytes = nSrcWidth * bytesPerPixel; for(; 0 < nSrcHeight; nSrcHeight--) { memmove(dstline, srcline, lineBytes); dstline += dstStride; srcline += srcStride; } } virtual void copyBitmap_i(const BitmapDeviceSharedPtr& rSrcBitmap) override { if( isCompatibleBitmap( rSrcBitmap ) ) { if (bitsPerPixel[getScanlineFormat()] >= 8 && rSrcBitmap->getScanlineFormat() == getScanlineFormat()) implDrawBitmapDirect(rSrcBitmap); else implDrawBitmap(rSrcBitmap, maBegin, maRawAccessor); } else { implDrawBitmapGeneric(rSrcBitmap, maBegin, maAccessor); } } }; } // namespace struct ImplBitmapDevice { /** Bitmap memory plus deleter. Always points to the start of the mem */ RawMemorySharedArray mpMem; /// Palette memory plus deleter (might be NULL) PaletteMemorySharedVector mpPalette; /** Bounds of the device. maBounds.getWidth()/getHeight() yield the true size of the device (i.e. the rectangle given by maBounds covers the device area under the including-the-bottommost-and-rightmost-pixels fill rule) */ basegfx::B2IBox maBounds; //// Size of the actual frame buffer basegfx::B2IVector maBufferSize; /// Scanline format, as provided at the constructor Format mnScanlineFormat; /// Scanline stride. Negative for bottom-to-top formats sal_Int32 mnScanlineStride; /// raw ptr to 0th scanline. used for cloning a generic renderer sal_uInt8* mpFirstScanline; /** (Optional) device sharing the same memory, and used for input clip masks/alpha masks/bitmaps that don't match our exact bitmap format. This is to avoid the combinatorial explosion when dealing with n bitmap formats, which could be combined with n clip masks, alpha masks and bitmap masks (yielding a total of n^4 combinations). Since each BitmapRenderer is specialized for one specific combination of said formats, a lot of duplicate code would be generated, most of which probably never used. Therefore, only the most common combinations are specialized templates, the remainder gets handled by this generic renderer (via runtime polymorphism). */ BitmapDeviceSharedPtr mpGenericRenderer; }; BitmapDevice::BitmapDevice( const basegfx::B2IBox& rBounds, const basegfx::B2IVector& rBufferSize, Format nScanlineFormat, sal_Int32 nScanlineStride, sal_uInt8* pFirstScanline, const RawMemorySharedArray& rMem, const PaletteMemorySharedVector& rPalette ) : mpImpl( new ImplBitmapDevice ) { mpImpl->mpMem = rMem; mpImpl->mpPalette = rPalette; mpImpl->maBounds = rBounds; mpImpl->maBufferSize = rBufferSize; mpImpl->mnScanlineFormat = nScanlineFormat; mpImpl->mnScanlineStride = nScanlineStride; mpImpl->mpFirstScanline = pFirstScanline; } BitmapDevice::~BitmapDevice() { // outline, because of internal ImplBitmapDevice SAL_INFO( "basebmp.bitmapdevice", "~BitmapDevice(" << this << ")" ); } basegfx::B2IVector BitmapDevice::getSize() const { return basegfx::B2IVector( mpImpl->maBounds.getMaxX() - mpImpl->maBounds.getMinX(), mpImpl->maBounds.getMaxY() - mpImpl->maBounds.getMinY() ); } basegfx::B2IVector BitmapDevice::getBufferSize() const { return mpImpl->maBufferSize; } Format BitmapDevice::getScanlineFormat() const { return mpImpl->mnScanlineFormat; } sal_Int32 BitmapDevice::getScanlineStride() const { return mpImpl->mnScanlineStride < 0 ? -mpImpl->mnScanlineStride : mpImpl->mnScanlineStride; } RawMemorySharedArray BitmapDevice::getBuffer() const { return mpImpl->mpMem; } PaletteMemorySharedVector BitmapDevice::getPalette() const { return mpImpl->mpPalette; } bool BitmapDevice::isSharedBuffer( const BitmapDeviceSharedPtr& rOther ) const { return rOther.get()->getBuffer().get() == getBuffer().get(); } Color BitmapDevice::getPixel( const basegfx::B2IPoint& rPt ) { if( mpImpl->maBounds.isInside(rPt) ) return getPixel_i(rPt); return Color(); } sal_uInt32 BitmapDevice::getPixelData( const basegfx::B2IPoint& rPt ) { if( mpImpl->maBounds.isInside(rPt) ) return getPixelData_i(rPt); return 0; } void BitmapDevice::copyBitmap( const BitmapDeviceSharedPtr& rSrcBitmap ) { copyBitmap_i( rSrcBitmap ); } /** Standard clip and alpha masks */ struct StdMasks { typedef PixelFormatTraits_GREY1_MSB clipmask_format_traits; typedef PixelFormatTraits_GREY8 alphamask_format_traits; /// Clipmask: 0 means opaque static const bool clipmask_polarity = false; /// Alpha mask: 0 means fully transparent static const bool alphamask_polarity = true; }; /// Produces a specialized renderer for the given pixel format template< class FormatTraits, class MaskTraits > BitmapDeviceSharedPtr createRenderer( const basegfx::B2IBox& rBounds, const basegfx::B2IVector& rBufferSize, Format nScanlineFormat, sal_Int32 nScanlineStride, sal_uInt8* pFirstScanline, typename FormatTraits::raw_accessor_type const& rRawAccessor, typename FormatTraits::accessor_selector::template wrap_accessor< typename FormatTraits::raw_accessor_type>::type const& rAccessor, boost::shared_array< sal_uInt8 > pMem, const PaletteMemorySharedVector& pPal ) { typedef typename FormatTraits::iterator_type Iterator; typedef BitmapRenderer< Iterator, typename FormatTraits::raw_accessor_type, typename FormatTraits::accessor_selector, MaskTraits > Renderer; return BitmapDeviceSharedPtr( new Renderer( rBounds, rBufferSize, nScanlineFormat, nScanlineStride, pFirstScanline, Iterator( reinterpret_cast( pFirstScanline), nScanlineStride), rRawAccessor, rAccessor, pMem, pPal )); } /// Create standard grey level palette PaletteMemorySharedVector createStandardPalette( const PaletteMemorySharedVector& pPal, sal_Int32 nNumEntries ) { if( pPal || nNumEntries <= 0 ) return pPal; std::shared_ptr< std::vector > pLocalPal( new std::vector(nNumEntries) ); const sal_Int32 nIncrement( 0x00FFFFFF/nNumEntries ); --nNumEntries; for( sal_Int32 i=0, c=0; iat(i) = Color(0xFF000000 | c); pLocalPal->at(nNumEntries) = Color(0xFFFFFFFF); return pLocalPal; } template< class FormatTraits, class MaskTraits > BitmapDeviceSharedPtr createRenderer( const basegfx::B2IBox& rBounds, const basegfx::B2IVector& rBufferSize, Format nScanlineFormat, sal_Int32 nScanlineStride, sal_uInt8* pFirstScanline, boost::shared_array< sal_uInt8 > pMem, const PaletteMemorySharedVector& pPal ) { return createRenderer(rBounds, rBufferSize, nScanlineFormat, nScanlineStride, pFirstScanline, typename FormatTraits::raw_accessor_type(), typename FormatTraits::accessor_selector::template wrap_accessor< typename FormatTraits::raw_accessor_type>::type(), pMem, pPal ); } template< class FormatTraits, class MaskTraits > BitmapDeviceSharedPtr createRenderer( const basegfx::B2IBox& rBounds, const basegfx::B2IVector& rBufferSize, Format nScanlineFormat, sal_Int32 nScanlineStride, sal_uInt8* pFirstScanline, boost::shared_array< sal_uInt8 > pMem, PaletteMemorySharedVector pPal, int nBitsPerPixel ) { pPal = createStandardPalette(pPal, 1UL << nBitsPerPixel); OSL_ASSERT(pPal); return createRenderer(rBounds, rBufferSize, nScanlineFormat, nScanlineStride, pFirstScanline, typename FormatTraits::raw_accessor_type(), typename FormatTraits::accessor_selector::template wrap_accessor< typename FormatTraits::raw_accessor_type>::type( &pPal->at(0), pPal->size()), pMem, pPal ); } namespace { BitmapDeviceSharedPtr createBitmapDeviceImplInner( const basegfx::B2IVector& rSize, Format nScanlineFormat, boost::shared_array< sal_uInt8 > pMem, PaletteMemorySharedVector pPal, const basegfx::B2IBox* pSubset, bool bBlack = true) { OSL_ASSERT(rSize.getX() > 0 && rSize.getY() > 0); if( nScanlineFormat <= Format::NONE || nScanlineFormat > Format::LAST ) return BitmapDeviceSharedPtr(); sal_uInt8 nBitsPerPixel = bitsPerPixel[nScanlineFormat]; if (rSize.getX() > (SAL_MAX_INT32-7) / nBitsPerPixel) { SAL_WARN("basebmp", "suspicious bitmap width " << rSize.getX() << " for depth " << nBitsPerPixel); return BitmapDeviceSharedPtr(); } sal_Int32 nScanlineStride = getBitmapDeviceStrideForWidth(nScanlineFormat, rSize.getX()); const sal_uInt32 nWidth(nScanlineStride < 0 ? -nScanlineStride : nScanlineStride); const sal_uInt32 nHeight(rSize.getY()); if (nHeight && nWidth && nWidth > SAL_MAX_INT32 / nHeight) { SAL_WARN( "basebmp", "suspicious massive alloc " << nWidth << " * " << nHeight); return BitmapDeviceSharedPtr(); } const std::size_t nMemSize(nWidth * nHeight); if( !pMem ) { pMem.reset( static_cast(rtl_allocateMemory( nMemSize )), &rtl_freeMemory ); if (pMem.get() == nullptr && nMemSize != 0) return BitmapDeviceSharedPtr(); if (bBlack) memset(pMem.get(), 0, nMemSize); else memset(pMem.get(), 0xFF, nMemSize); } sal_uInt8* pFirstScanline = nScanlineStride < 0 ? pMem.get() + nMemSize + nScanlineStride : pMem.get(); // shrink render area to given subset, if given basegfx::B2IBox aBounds(0,0,rSize.getX(),rSize.getY()); if( pSubset ) aBounds.intersect( *pSubset ); switch( nScanlineFormat ) { // one bit formats case Format::OneBitMsbGrey: return createRenderer( aBounds, rSize, nScanlineFormat, nScanlineStride, pFirstScanline, pMem, pPal ); case Format::OneBitLsbGrey: return createRenderer( aBounds, rSize, nScanlineFormat, nScanlineStride, pFirstScanline, pMem, pPal ); case Format::OneBitMsbPal: return createRenderer( aBounds, rSize, nScanlineFormat, nScanlineStride, pFirstScanline, pMem, pPal, bitsPerPixel[nScanlineFormat] ); case Format::OneBitLsbPal: return createRenderer( aBounds, rSize, nScanlineFormat, nScanlineStride, pFirstScanline, pMem, pPal, bitsPerPixel[nScanlineFormat] ); // four bit formats case Format::FourBitMsbGrey: return createRenderer( aBounds, rSize, nScanlineFormat, nScanlineStride, pFirstScanline, pMem, pPal ); case Format::FourBitLsbGrey: return createRenderer( aBounds, rSize, nScanlineFormat, nScanlineStride, pFirstScanline, pMem, pPal ); case Format::FourBitMsbPal: return createRenderer( aBounds, rSize, nScanlineFormat, nScanlineStride, pFirstScanline, pMem, pPal, bitsPerPixel[nScanlineFormat] ); case Format::FourBitLsbPal: return createRenderer( aBounds, rSize, nScanlineFormat, nScanlineStride, pFirstScanline, pMem, pPal, bitsPerPixel[nScanlineFormat] ); // eight bit formats case Format::EightBitGrey: return createRenderer( aBounds, rSize, nScanlineFormat, nScanlineStride, pFirstScanline, pMem, pPal ); case Format::EightBitPal: return createRenderer( aBounds, rSize, nScanlineFormat, nScanlineStride, pFirstScanline, pMem, pPal, bitsPerPixel[nScanlineFormat] ); // sixteen bit formats case Format::SixteenBitLsbTcMask: return createRenderer( aBounds, rSize, nScanlineFormat, nScanlineStride, pFirstScanline, pMem, pPal ); case Format::SixteenBitMsbTcMask: return createRenderer( aBounds, rSize, nScanlineFormat, nScanlineStride, pFirstScanline, pMem, pPal ); // twentyfour bit formats case Format::TwentyFourBitTcMask: return createRenderer( aBounds, rSize, nScanlineFormat, nScanlineStride, pFirstScanline, pMem, pPal ); // thirtytwo bit formats case Format::ThirtyTwoBitTcMaskBGRA: return createRenderer( aBounds, rSize, nScanlineFormat, nScanlineStride, pFirstScanline, pMem, pPal ); case Format::ThirtyTwoBitTcMaskARGB: return createRenderer( aBounds, rSize, nScanlineFormat, nScanlineStride, pFirstScanline, pMem, pPal ); case Format::ThirtyTwoBitTcMaskABGR: return createRenderer( aBounds, rSize, nScanlineFormat, nScanlineStride, pFirstScanline, pMem, pPal ); case Format::ThirtyTwoBitTcMaskRGBA: return createRenderer( aBounds, rSize, nScanlineFormat, nScanlineStride, pFirstScanline, pMem, pPal ); default: assert(false); // this cannot happen } // TODO(F3): other formats not yet implemented return BitmapDeviceSharedPtr(); } BitmapDeviceSharedPtr createBitmapDeviceImpl( const basegfx::B2IVector& rSize, Format nScanlineFormat, boost::shared_array< sal_uInt8 > pMem, PaletteMemorySharedVector pPal, const basegfx::B2IBox* pSubset, bool bBlack = true) { BitmapDeviceSharedPtr result( createBitmapDeviceImplInner( rSize, nScanlineFormat, pMem, pPal, pSubset, bBlack ) ); #ifdef SAL_LOG_INFO std::ostringstream subset; if (pSubset) subset << " subset=" << pSubset->getWidth() << "x" << pSubset->getHeight() << "@(" << pSubset->getMinX() << "," << pSubset->getMinY() << ")"; SAL_INFO( "basebmp.bitmapdevice", "createBitmapDevice: " << rSize.getX() << "x" << rSize.getY() << subset.str() << " = " << result.get() ); #endif return result; } } // namespace sal_Int32 getBitmapDeviceStrideForWidth(Format nScanlineFormat, sal_Int32 nWidth) { sal_uInt8 nBitsPerPixel = bitsPerPixel[nScanlineFormat]; // round up to full 8 bit, divide by 8 sal_Int32 nScanlineStride = (nWidth*nBitsPerPixel + 7) >> 3; // pixman (cairo) and GDI (windows) pad to multiples of 32bits // so do the same to be easily compatible nScanlineStride = (nScanlineStride + 3) & ~0x3; return nScanlineStride; } BitmapDeviceSharedPtr createBitmapDevice( const basegfx::B2IVector& rSize, Format nScanlineFormat ) { return createBitmapDeviceImpl( rSize, nScanlineFormat, boost::shared_array< sal_uInt8 >(), PaletteMemorySharedVector(), nullptr ); } BitmapDeviceSharedPtr createBitmapDevice( const basegfx::B2IVector& rSize, Format nScanlineFormat, const PaletteMemorySharedVector& rPalette ) { return createBitmapDeviceImpl( rSize, nScanlineFormat, boost::shared_array< sal_uInt8 >(), rPalette, nullptr ); } BitmapDeviceSharedPtr createBitmapDevice( const basegfx::B2IVector& rSize, Format nScanlineFormat, const RawMemorySharedArray& rMem, const PaletteMemorySharedVector& rPalette ) { return createBitmapDeviceImpl( rSize, nScanlineFormat, rMem, rPalette, nullptr ); } BitmapDeviceSharedPtr cloneBitmapDevice( const basegfx::B2IVector& rSize, const BitmapDeviceSharedPtr& rProto ) { return createBitmapDeviceImpl( rSize, rProto->getScanlineFormat(), boost::shared_array< sal_uInt8 >(), rProto->getPalette(), nullptr ); } /// Clone our device, with GenericImageAccessor to handle all formats BitmapDeviceSharedPtr BitmapDevice::getGenericRenderer() const { return mpImpl->mpGenericRenderer; } } // namespace basebmp /* vim:set shiftwidth=4 softtabstop=4 expandtab: */