/* -*- 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 "dx_graphicsprovider.hxx" #include "dx_impltools.hxx" #include "dx_surfacebitmap.hxx" #include "dx_surfacegraphics.hxx" using namespace ::com::sun::star; namespace dxcanvas { namespace { // DXColorBuffer struct DXColorBuffer : public canvas::IColorBuffer { public: DXColorBuffer( const sal::systools::COMReference& rSurface, const ::basegfx::B2ISize& rSize ) : maSize(rSize) , maLockedRect{} , mpSurface(rSurface) { } // implementation of the 'IColorBuffer' interface public: virtual sal_uInt8* lock() const override; virtual void unlock() const override; virtual sal_uInt32 getWidth() const override; virtual sal_uInt32 getHeight() const override; virtual sal_uInt32 getStride() const override; virtual Format getFormat() const override; private: ::basegfx::B2ISize maSize; mutable D3DLOCKED_RECT maLockedRect; sal::systools::COMReference mpSurface; }; sal_uInt8* DXColorBuffer::lock() const { if(SUCCEEDED(mpSurface->LockRect(&maLockedRect,nullptr,D3DLOCK_NOSYSLOCK|D3DLOCK_READONLY))) return static_cast(maLockedRect.pBits); return nullptr; } void DXColorBuffer::unlock() const { mpSurface->UnlockRect(); } sal_uInt32 DXColorBuffer::getWidth() const { return maSize.getWidth(); } sal_uInt32 DXColorBuffer::getHeight() const { return maSize.getHeight(); } sal_uInt32 DXColorBuffer::getStride() const { return maLockedRect.Pitch; } canvas::IColorBuffer::Format DXColorBuffer::getFormat() const { return canvas::IColorBuffer::Format::X8R8G8B8; } // GDIColorBuffer struct GDIColorBuffer : public canvas::IColorBuffer { public: GDIColorBuffer( const BitmapSharedPtr& rSurface, const ::basegfx::B2ISize& rSize ) : maSize(rSize) , aBmpData{} , mpGDIPlusBitmap(rSurface) { } // implementation of the 'IColorBuffer' interface public: virtual sal_uInt8* lock() const override; virtual void unlock() const override; virtual sal_uInt32 getWidth() const override; virtual sal_uInt32 getHeight() const override; virtual sal_uInt32 getStride() const override; virtual Format getFormat() const override; private: ::basegfx::B2ISize maSize; mutable Gdiplus::BitmapData aBmpData; BitmapSharedPtr mpGDIPlusBitmap; }; sal_uInt8* GDIColorBuffer::lock() const { aBmpData.Width = maSize.getWidth(); aBmpData.Height = maSize.getHeight(); aBmpData.Stride = 4*aBmpData.Width; aBmpData.PixelFormat = PixelFormat32bppARGB; aBmpData.Scan0 = nullptr; const Gdiplus::Rect aRect( 0,0,aBmpData.Width,aBmpData.Height ); if( Gdiplus::Ok != mpGDIPlusBitmap->LockBits( &aRect, Gdiplus::ImageLockModeRead, PixelFormat32bppARGB, &aBmpData ) ) { return nullptr; } return static_cast(aBmpData.Scan0); } void GDIColorBuffer::unlock() const { mpGDIPlusBitmap->UnlockBits( &aBmpData ); } sal_uInt32 GDIColorBuffer::getWidth() const { return maSize.getWidth(); } sal_uInt32 GDIColorBuffer::getHeight() const { return maSize.getHeight(); } sal_uInt32 GDIColorBuffer::getStride() const { return aBmpData.Stride; } canvas::IColorBuffer::Format GDIColorBuffer::getFormat() const { return canvas::IColorBuffer::Format::A8R8G8B8; } } // DXSurfaceBitmap::DXSurfaceBitmap DXSurfaceBitmap::DXSurfaceBitmap( const ::basegfx::B2ISize& rSize, const std::shared_ptr& rMgr, const IDXRenderModuleSharedPtr& rRenderModule, bool bWithAlpha ) : mpGdiPlusUser( GDIPlusUser::createInstance() ), maSize(rSize), mpRenderModule(rRenderModule), mpSurfaceManager(rMgr), mpSurfaceProxy(), mpSurface(), mpGDIPlusBitmap(), mpGraphics(), mpColorBuffer(), mbIsSurfaceDirty(true), mbAlpha(bWithAlpha) { init(); } // DXSurfaceBitmap::getSize ::basegfx::B2ISize DXSurfaceBitmap::getSize() const { return maSize; } // DXSurfaceBitmap::init void DXSurfaceBitmap::init() { // create container for pixel data if(mbAlpha) { mpGDIPlusBitmap = std::make_shared( maSize.getWidth(), maSize.getHeight(), PixelFormat32bppARGB ); mpGraphics = tools::createGraphicsFromBitmap(mpGDIPlusBitmap); // create the colorbuffer object, which is basically a simple // wrapper around the directx surface. the colorbuffer is the // interface which is used by the surfaceproxy to support any // kind of underlying structure for the pixel data container. mpColorBuffer = std::make_shared(mpGDIPlusBitmap, maSize); } else { mpSurface = mpRenderModule->createSystemMemorySurface(maSize); // create the colorbuffer object, which is basically a simple // wrapper around the directx surface. the colorbuffer is the // interface which is used by the surfaceproxy to support any // kind of underlying structure for the pixel data container. mpColorBuffer = std::make_shared(mpSurface, maSize); } // create a (possibly hardware accelerated) mirror surface. mpSurfaceProxy = mpSurfaceManager->createSurfaceProxy(mpColorBuffer); } // DXSurfaceBitmap::resize bool DXSurfaceBitmap::resize(const ::basegfx::B2ISize& rSize) { if(maSize != rSize) { maSize = rSize; init(); } return true; } // DXSurfaceBitmap::clear void DXSurfaceBitmap::clear() { GraphicsSharedPtr pGraphics(getGraphics()); Gdiplus::Color transColor(255,0,0,0); pGraphics->SetCompositingMode( Gdiplus::CompositingModeSourceCopy ); pGraphics->Clear( transColor ); } // DXSurfaceBitmap::hasAlpha bool DXSurfaceBitmap::hasAlpha() const { return mbAlpha; } // DXSurfaceBitmap::getGraphics GraphicsSharedPtr DXSurfaceBitmap::getGraphics() { // since clients will most probably draw directly // to the GDI+ bitmap, we need to mark it as dirty // to ensure that the corresponding dxsurface will // be updated. mbIsSurfaceDirty = true; if(hasAlpha()) return mpGraphics; else return createSurfaceGraphics(mpSurface); } // DXSurfaceBitmap::getBitmap BitmapSharedPtr DXSurfaceBitmap::getBitmap() const { if(hasAlpha()) return mpGDIPlusBitmap; BitmapSharedPtr pResult; D3DLOCKED_RECT aLockedRect; if(SUCCEEDED(mpSurface->LockRect(&aLockedRect,nullptr,D3DLOCK_NOSYSLOCK|D3DLOCK_READONLY))) { // decide about the format we pass the gdi+, the directx surface is always // 32bit, either with or without alpha component. Gdiplus::PixelFormat nFormat = hasAlpha() ? PixelFormat32bppARGB : PixelFormat32bppRGB; // construct a gdi+ bitmap from the raw pixel data. pResult = std::make_shared(maSize.getWidth(), maSize.getHeight(), aLockedRect.Pitch, nFormat, static_cast(aLockedRect.pBits) ); mpSurface->UnlockRect(); } return pResult; } // DXSurfaceBitmap::draw bool DXSurfaceBitmap::draw( double fAlpha, const ::basegfx::B2DPoint& rPos, const ::basegfx::B2DPolyPolygon& rClipPoly, const ::basegfx::B2DHomMatrix& rTransform ) { if( mbIsSurfaceDirty ) { mpSurfaceProxy->setColorBufferDirty(); mbIsSurfaceDirty = false; } return mpSurfaceProxy->draw( fAlpha, rPos, rClipPoly, rTransform ); } // DXSurfaceBitmap::draw bool DXSurfaceBitmap::draw( double fAlpha, const ::basegfx::B2DPoint& rPos, const ::basegfx::B2DRange& rArea, const ::basegfx::B2DHomMatrix& rTransform ) { if( mbIsSurfaceDirty ) { mpSurfaceProxy->setColorBufferDirty(); mbIsSurfaceDirty = false; } return mpSurfaceProxy->draw( fAlpha, rPos, rArea, rTransform ); } // DXSurfaceBitmap::draw bool DXSurfaceBitmap::draw( double fAlpha, const ::basegfx::B2DPoint& rPos, const ::basegfx::B2DHomMatrix& rTransform ) { if( mbIsSurfaceDirty ) { mpSurfaceProxy->setColorBufferDirty(); mbIsSurfaceDirty = false; } return mpSurfaceProxy->draw( fAlpha, rPos, rTransform ); } // DXSurfaceBitmap::draw bool DXSurfaceBitmap::draw( const ::basegfx::B2IRange& rArea ) { if( mbIsSurfaceDirty ) { mpSurfaceProxy->setColorBufferDirty(); mbIsSurfaceDirty = false; } const double fAlpha(1.0); const ::basegfx::B2DHomMatrix aTransform; const ::basegfx::B2DRange aIEEEArea( rArea ); return mpSurfaceProxy->draw(fAlpha, ::basegfx::B2DPoint(), aIEEEArea, aTransform); } // DXSurfaceBitmap::getData uno::Sequence< sal_Int8 > DXSurfaceBitmap::getData( rendering::IntegerBitmapLayout& rBitmapLayout, const geometry::IntegerRectangle2D& rect ) { if(hasAlpha()) { uno::Sequence< sal_Int8 > aRes( (rect.X2-rect.X1)*(rect.Y2-rect.Y1)*4 ); // TODO(F1): Be format-agnostic here const Gdiplus::Rect aRect( tools::gdiPlusRectFromIntegerRectangle2D( rect ) ); Gdiplus::BitmapData aBmpData; aBmpData.Width = rect.X2-rect.X1; aBmpData.Height = rect.Y2-rect.Y1; aBmpData.Stride = 4*aBmpData.Width; aBmpData.PixelFormat = PixelFormat32bppARGB; aBmpData.Scan0 = aRes.getArray(); // TODO(F1): Support more pixel formats natively // read data from bitmap if( Gdiplus::Ok != mpGDIPlusBitmap->LockBits( &aRect, Gdiplus::ImageLockModeRead | Gdiplus::ImageLockModeUserInputBuf, PixelFormat32bppARGB, // TODO(F1): Adapt to // Graphics native // format/change // getMemoryLayout &aBmpData ) ) { // failed to lock, bail out return uno::Sequence< sal_Int8 >(); } mpGDIPlusBitmap->UnlockBits( &aBmpData ); return aRes; } else { sal_uInt32 nWidth = rect.X2-rect.X1; sal_uInt32 nHeight = rect.Y2-rect.Y1; uno::Sequence< sal_Int8 > aRes(nWidth*nHeight*4); D3DLOCKED_RECT aLockedRect; if(FAILED(mpSurface->LockRect(&aLockedRect,nullptr,D3DLOCK_NOSYSLOCK|D3DLOCK_READONLY))) return uno::Sequence< sal_Int8 >(); D3DSURFACE_DESC aDesc; if(FAILED(mpSurface->GetDesc(&aDesc))) return uno::Sequence< sal_Int8 >(); assert(aDesc.Format == D3DFMT_A8R8G8B8 || aDesc.Format == D3DFMT_X8R8G8B8); sal_uInt8 *pSrc = (static_cast(aLockedRect.pBits)+(rect.Y1*aLockedRect.Pitch))+rect.X1; sal_uInt8 *pDst = reinterpret_cast(aRes.getArray()); sal_uInt32 nSegmentSizeInBytes = nWidth*4; for(sal_uInt32 y=0; ygetComponentTags().getArray()[0] == rendering::ColorComponentTag::RGB_RED && rBitmapLayout.ColorSpace->getComponentTags().getArray()[2] == rendering::ColorComponentTag::RGB_BLUE) { pDst = reinterpret_cast(aRes.getArray()); for(sal_uInt32 y=0; yUnlockRect(); return aRes; } } // DXSurfaceBitmap::setData void DXSurfaceBitmap::setData( const uno::Sequence< sal_Int8 >& data, const rendering::IntegerBitmapLayout& /*bitmapLayout*/, const geometry::IntegerRectangle2D& rect ) { if(hasAlpha()) { const Gdiplus::Rect aRect( tools::gdiPlusRectFromIntegerRectangle2D( rect ) ); Gdiplus::BitmapData aBmpData; aBmpData.Width = rect.X2-rect.X1; aBmpData.Height = rect.Y2-rect.Y1; aBmpData.Stride = 4*aBmpData.Width; aBmpData.PixelFormat = PixelFormat32bppARGB; aBmpData.Scan0 = const_cast(data.getConstArray()); // TODO(F1): Support more pixel formats natively if( Gdiplus::Ok != mpGDIPlusBitmap->LockBits( &aRect, Gdiplus::ImageLockModeWrite | Gdiplus::ImageLockModeUserInputBuf, PixelFormat32bppARGB, // TODO: Adapt to // Graphics native // format/change // getMemoryLayout &aBmpData ) ) { throw uno::RuntimeException("GDIPlus method call was unsuccessful, problem with locking bitmap aRect object"); } // commit data to bitmap mpGDIPlusBitmap->UnlockBits( &aBmpData ); } else { sal_uInt32 nWidth = rect.X2-rect.X1; sal_uInt32 nHeight = rect.Y2-rect.Y1; // lock the directx surface to receive the pointer to the surface memory. D3DLOCKED_RECT aLockedRect; if(FAILED(mpSurface->LockRect(&aLockedRect,nullptr,D3DLOCK_NOSYSLOCK|D3DLOCK_READONLY))) throw uno::RuntimeException("failed to lock directx surface to surface memory"); sal_uInt8 const *pSrc = reinterpret_cast(data.getConstArray()); sal_uInt8 *pDst = (static_cast(aLockedRect.pBits)+(rect.Y1*aLockedRect.Pitch))+rect.X1; sal_uInt32 nSegmentSizeInBytes = nWidth<<4; for(sal_uInt32 y=0; yUnlockRect(); } mbIsSurfaceDirty = true; } // DXSurfaceBitmap::setPixel void DXSurfaceBitmap::setPixel( const uno::Sequence< sal_Int8 >& color, const rendering::IntegerBitmapLayout& /*bitmapLayout*/, const geometry::IntegerPoint2D& pos ) { if(hasAlpha()) { const geometry::IntegerSize2D aSize( maSize.getWidth(), maSize.getHeight() ); ENSURE_ARG_OR_THROW( pos.X >= 0 && pos.X < aSize.Width, "CanvasHelper::setPixel: X coordinate out of bounds" ); ENSURE_ARG_OR_THROW( pos.Y >= 0 && pos.Y < aSize.Height, "CanvasHelper::setPixel: Y coordinate out of bounds" ); ENSURE_ARG_OR_THROW( color.getLength() > 3, "CanvasHelper::setPixel: not enough color components" ); if( Gdiplus::Ok != mpGDIPlusBitmap->SetPixel( pos.X, pos.Y, Gdiplus::Color( tools::sequenceToArgb( color )))) { throw uno::RuntimeException("Problem with setting the color of bitmap object"); } } else { ENSURE_ARG_OR_THROW( pos.X >= 0 && pos.X < maSize.getWidth(), "CanvasHelper::setPixel: X coordinate out of bounds" ); ENSURE_ARG_OR_THROW( pos.Y >= 0 && pos.Y < maSize.getHeight(), "CanvasHelper::setPixel: Y coordinate out of bounds" ); ENSURE_ARG_OR_THROW( color.getLength() > 3, "CanvasHelper::setPixel: not enough color components" ); Gdiplus::Color aColor(tools::sequenceToArgb(color)); // lock the directx surface to receive the pointer to the surface memory. D3DLOCKED_RECT aLockedRect; if(FAILED(mpSurface->LockRect(&aLockedRect,nullptr,D3DLOCK_NOSYSLOCK|D3DLOCK_READONLY))) throw uno::RuntimeException("cannot lock the directx surface to surface memory"); sal_uInt32 *pDst = reinterpret_cast((static_cast(aLockedRect.pBits)+(pos.Y*aLockedRect.Pitch))+pos.X); *pDst = aColor.GetValue(); mpSurface->UnlockRect(); } mbIsSurfaceDirty = true; } // DXSurfaceBitmap::getPixel uno::Sequence< sal_Int8 > DXSurfaceBitmap::getPixel( rendering::IntegerBitmapLayout& /*bitmapLayout*/, const geometry::IntegerPoint2D& pos ) { if(hasAlpha()) { const geometry::IntegerSize2D aSize(maSize.getWidth(), maSize.getHeight()); ENSURE_ARG_OR_THROW( pos.X >= 0 && pos.X < aSize.Width, "CanvasHelper::getPixel: X coordinate out of bounds" ); ENSURE_ARG_OR_THROW( pos.Y >= 0 && pos.Y < aSize.Height, "CanvasHelper::getPixel: Y coordinate out of bounds" ); Gdiplus::Color aColor; if( Gdiplus::Ok != mpGDIPlusBitmap->GetPixel( pos.X, pos.Y, &aColor ) ) return uno::Sequence< sal_Int8 >(); return tools::argbToIntSequence(aColor.GetValue()); } else { ENSURE_ARG_OR_THROW( pos.X >= 0 && pos.X < maSize.getWidth(), "CanvasHelper::getPixel: X coordinate out of bounds" ); ENSURE_ARG_OR_THROW( pos.Y >= 0 && pos.Y < maSize.getHeight(), "CanvasHelper::getPixel: Y coordinate out of bounds" ); // lock the directx surface to receive the pointer to the surface memory. D3DLOCKED_RECT aLockedRect; if(FAILED(mpSurface->LockRect(&aLockedRect,nullptr,D3DLOCK_NOSYSLOCK|D3DLOCK_READONLY))) throw uno::RuntimeException("failed to lock directX surface to surface memory"); sal_uInt32 *pDst = reinterpret_cast((static_cast(aLockedRect.pBits)+(pos.Y*aLockedRect.Pitch))+pos.X); Gdiplus::Color aColor(*pDst); mpSurface->UnlockRect(); return tools::argbToIntSequence(aColor.GetValue()); } } } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */