/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /************************************************************************* * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * Copyright 2000, 2010 Oracle and/or its affiliates. * * OpenOffice.org - a multi-platform office productivity suite * * This file is part of OpenOffice.org. * * OpenOffice.org is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 * only, as published by the Free Software Foundation. * * OpenOffice.org is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License version 3 for more details * (a copy is included in the LICENSE file that accompanied this code). * * You should have received a copy of the GNU Lesser General Public License * version 3 along with OpenOffice.org. If not, see * * for a copy of the LGPLv3 License. * ************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include "grfcache.hxx" #include // ----------- // - defines - // ----------- #define MAP( cVal0, cVal1, nFrac ) ((sal_uInt8)((((long)(cVal0)<<20L)+nFrac*((long)(cVal1)-(cVal0)))>>20L)) #define WATERMARK_LUM_OFFSET 50 #define WATERMARK_CON_OFFSET -70 // ------------------ // - GraphicManager - // ------------------ GraphicManager::GraphicManager( sal_uLong nCacheSize, sal_uLong nMaxObjCacheSize ) : mpCache( new GraphicCache( *this, nCacheSize, nMaxObjCacheSize ) ) { } // ----------------------------------------------------------------------------- GraphicManager::~GraphicManager() { for( size_t i = 0, n = maObjList.size(); i < n; ++i ) maObjList[ i ]->GraphicManagerDestroyed(); delete mpCache; } // ----------------------------------------------------------------------------- void GraphicManager::SetMaxCacheSize( sal_uLong nNewCacheSize ) { mpCache->SetMaxDisplayCacheSize( nNewCacheSize ); } // ----------------------------------------------------------------------------- void GraphicManager::SetMaxObjCacheSize( sal_uLong nNewMaxObjSize, sal_Bool bDestroyGreaterCached ) { mpCache->SetMaxObjDisplayCacheSize( nNewMaxObjSize, bDestroyGreaterCached ); } // ----------------------------------------------------------------------------- void GraphicManager::SetCacheTimeout( sal_uLong nTimeoutSeconds ) { mpCache->SetCacheTimeout( nTimeoutSeconds ); } // ----------------------------------------------------------------------------- void GraphicManager::ReleaseFromCache( const GraphicObject& /*rObj*/ ) { // !!! } // ----------------------------------------------------------------------------- sal_Bool GraphicManager::IsInCache( OutputDevice* pOut, const Point& rPt, const Size& rSz, const GraphicObject& rObj, const GraphicAttr& rAttr ) const { return mpCache->IsInDisplayCache( pOut, rPt, rSz, rObj, rAttr ); } // ----------------------------------------------------------------------------- sal_Bool GraphicManager::DrawObj( OutputDevice* pOut, const Point& rPt, const Size& rSz, GraphicObject& rObj, const GraphicAttr& rAttr, const sal_uLong nFlags, sal_Bool& rCached ) { Point aPt( rPt ); Size aSz( rSz ); sal_Bool bRet = sal_False; rCached = sal_False; if( ( rObj.GetType() == GRAPHIC_BITMAP ) || ( rObj.GetType() == GRAPHIC_GDIMETAFILE ) ) { // create output and fill cache const Size aOutSize( pOut->GetOutputSizePixel() ); if( rObj.IsAnimated() || ( pOut->GetOutDevType() == OUTDEV_PRINTER ) || ( !( nFlags & GRFMGR_DRAW_NO_SUBSTITUTE ) && ( ( nFlags & GRFMGR_DRAW_SUBSTITUTE ) || !( nFlags & GRFMGR_DRAW_CACHED ) || ( pOut->GetConnectMetaFile() && !pOut->IsOutputEnabled() ) ) ) ) { // simple output of transformed graphic const Graphic aGraphic( rObj.GetTransformedGraphic( &rAttr ) ); if( aGraphic.IsSupportedGraphic() ) { const sal_uInt16 nRot10 = rAttr.GetRotation() % 3600; if( nRot10 ) { Polygon aPoly( Rectangle( aPt, aSz ) ); aPoly.Rotate( aPt, nRot10 ); const Rectangle aRotBoundRect( aPoly.GetBoundRect() ); aPt = aRotBoundRect.TopLeft(); aSz = aRotBoundRect.GetSize(); } aGraphic.Draw( pOut, aPt, aSz ); } bRet = sal_True; } if( !bRet ) { // cached/direct drawing if( !mpCache->DrawDisplayCacheObj( pOut, aPt, aSz, rObj, rAttr ) ) bRet = ImplDraw( pOut, aPt, aSz, rObj, rAttr, nFlags, rCached ); else bRet = rCached = sal_True; } } return bRet; } // ----------------------------------------------------------------------------- void GraphicManager::ImplRegisterObj( const GraphicObject& rObj, Graphic& rSubstitute, const rtl::OString* pID, const GraphicObject* pCopyObj ) { maObjList.push_back( (GraphicObject*)&rObj ); mpCache->AddGraphicObject( rObj, rSubstitute, pID, pCopyObj ); } // ----------------------------------------------------------------------------- void GraphicManager::ImplUnregisterObj( const GraphicObject& rObj ) { mpCache->ReleaseGraphicObject( rObj ); for( GraphicObjectList_impl::iterator it = maObjList.begin(); it != maObjList.end(); ++it ) { if ( *it == &rObj ) { maObjList.erase( it ); break; } } } // ----------------------------------------------------------------------------- void GraphicManager::ImplGraphicObjectWasSwappedOut( const GraphicObject& rObj ) { mpCache->GraphicObjectWasSwappedOut( rObj ); } // ----------------------------------------------------------------------------- rtl::OString GraphicManager::ImplGetUniqueID( const GraphicObject& rObj ) const { return mpCache->GetUniqueID( rObj ); } // ----------------------------------------------------------------------------- sal_Bool GraphicManager::ImplFillSwappedGraphicObject( const GraphicObject& rObj, Graphic& rSubstitute ) { return( mpCache->FillSwappedGraphicObject( rObj, rSubstitute ) ); } // ----------------------------------------------------------------------------- void GraphicManager::ImplGraphicObjectWasSwappedIn( const GraphicObject& rObj ) { mpCache->GraphicObjectWasSwappedIn( rObj ); } // ----------------------------------------------------------------------------- sal_Bool GraphicManager::ImplDraw( OutputDevice* pOut, const Point& rPt, const Size& rSz, GraphicObject& rObj, const GraphicAttr& rAttr, const sal_uLong nFlags, sal_Bool& rCached ) { const Graphic& rGraphic = rObj.GetGraphic(); sal_Bool bRet = sal_False; if( rGraphic.IsSupportedGraphic() && !rGraphic.IsSwapOut() ) { if( GRAPHIC_BITMAP == rGraphic.GetType() ) { const BitmapEx aSrcBmpEx( rGraphic.GetBitmapEx() ); // #i46805# No point in caching a bitmap that is rendered // via RectFill on the OutDev if( !(pOut->GetDrawMode() & ( DRAWMODE_BLACKBITMAP | DRAWMODE_WHITEBITMAP )) && mpCache->IsDisplayCacheable( pOut, rPt, rSz, rObj, rAttr ) ) { BitmapEx aDstBmpEx; if( ImplCreateOutput( pOut, rPt, rSz, aSrcBmpEx, rAttr, nFlags, &aDstBmpEx ) ) { rCached = mpCache->CreateDisplayCacheObj( pOut, rPt, rSz, rObj, rAttr, aDstBmpEx ); bRet = sal_True; } } if( !bRet ) bRet = ImplCreateOutput( pOut, rPt, rSz, aSrcBmpEx, rAttr, nFlags ); } else { const GDIMetaFile& rSrcMtf = rGraphic.GetGDIMetaFile(); if( mpCache->IsDisplayCacheable( pOut, rPt, rSz, rObj, rAttr ) ) { GDIMetaFile aDstMtf; BitmapEx aContainedBmpEx; if( ImplCreateOutput( pOut, rPt, rSz, rSrcMtf, rAttr, nFlags, aDstMtf, aContainedBmpEx ) ) { if( !!aContainedBmpEx ) { // #117889# Use bitmap output method, if // metafile basically contains only a single // bitmap BitmapEx aDstBmpEx; if( ImplCreateOutput( pOut, rPt, rSz, aContainedBmpEx, rAttr, nFlags, &aDstBmpEx ) ) { rCached = mpCache->CreateDisplayCacheObj( pOut, rPt, rSz, rObj, rAttr, aDstBmpEx ); bRet = sal_True; } } else { rCached = mpCache->CreateDisplayCacheObj( pOut, rPt, rSz, rObj, rAttr, aDstMtf ); bRet = sal_True; } } } if( !bRet ) { const Graphic aGraphic( rObj.GetTransformedGraphic( &rAttr ) ); if( aGraphic.IsSupportedGraphic() ) { aGraphic.Draw( pOut, rPt, rSz ); bRet = sal_True; } } } } return bRet; } // ----------------------------------------------------------------------------- sal_Bool GraphicManager::ImplCreateOutput( OutputDevice* pOut, const Point& rPt, const Size& rSz, const BitmapEx& rBmpEx, const GraphicAttr& rAttr, const sal_uLong nFlags, BitmapEx* pBmpEx ) { sal_uInt16 nRot10 = rAttr.GetRotation() % 3600; Point aOutPtPix; Size aOutSzPix; Size aUnrotatedSzPix( pOut->LogicToPixel( rSz ) ); sal_Bool bRet = sal_False; if( nRot10 ) { Polygon aPoly( Rectangle( rPt, rSz ) ); aPoly.Rotate( rPt, nRot10 ); const Rectangle aRotBoundRect( aPoly.GetBoundRect() ); aOutPtPix = pOut->LogicToPixel( aRotBoundRect.TopLeft() ); aOutSzPix = pOut->LogicToPixel( aRotBoundRect.GetSize() ); } else { aOutPtPix = pOut->LogicToPixel( rPt ); aOutSzPix = aUnrotatedSzPix; } if( aUnrotatedSzPix.Width() && aUnrotatedSzPix.Height() ) { BitmapEx aBmpEx( rBmpEx ); BitmapEx aOutBmpEx; Point aOutPt; Size aOutSz; const Size& rBmpSzPix = rBmpEx.GetSizePixel(); const long nW = rBmpSzPix.Width(); const long nH = rBmpSzPix.Height(); const long nNewW = aUnrotatedSzPix.Width(); const long nNewH = aUnrotatedSzPix.Height(); double fTmp; long* pMapIX = new long[ nNewW ]; long* pMapFX = new long[ nNewW ]; long* pMapIY = new long[ nNewH ]; long* pMapFY = new long[ nNewH ]; long nStartX = -1, nStartY = -1, nEndX = -1, nEndY = -1; long nX, nY, nTmp, nTmpX, nTmpY; sal_Bool bHMirr = ( rAttr.GetMirrorFlags() & BMP_MIRROR_HORZ ) != 0; sal_Bool bVMirr = ( rAttr.GetMirrorFlags() & BMP_MIRROR_VERT ) != 0; if( !( nFlags & GRFMGR_DRAW_SMOOTHSCALE )) { // #98290# Use a different mapping for non-interpolating mode, to avoid missing rows/columns const double fRevScaleX = ( nNewW > 1L ) ? ( (double) nW / nNewW ) : 0.0; const double fRevScaleY = ( nNewH > 1L ) ? ( (double) nH / nNewH ) : 0.0; // create horizontal mapping table for( nX = 0L, nTmpX = nW - 1L, nTmp = nW - 2L; nX < nNewW; nX++ ) { fTmp = nX * fRevScaleX; if( bHMirr ) fTmp = nTmpX - fTmp; // #98290# Do not use round to zero, otherwise last column will be missing pMapIX[ nX ] = MinMax( (long) fTmp, 0, nTmp ); pMapFX[ nX ] = fTmp >= nTmp+1 ? 1048576 : 0; } // create vertical mapping table for( nY = 0L, nTmpY = nH - 1L, nTmp = nH - 2L; nY < nNewH; nY++ ) { fTmp = nY * fRevScaleY; if( bVMirr ) fTmp = nTmpY - fTmp; // #98290# Do not use round to zero, otherwise last row will be missing pMapIY[ nY ] = MinMax( (long) fTmp, 0, nTmp ); pMapFY[ nY ] = fTmp >= nTmp+1 ? 1048576 : 0; } } // calculate output sizes if( !pBmpEx ) { Point aPt; Rectangle aOutRect( aPt, pOut->GetOutputSizePixel() ); Rectangle aBmpRect( aOutPtPix, aOutSzPix ); if( pOut->GetOutDevType() == OUTDEV_WINDOW ) { const Region aPaintRgn( ( (Window*) pOut )->GetPaintRegion() ); if( !aPaintRgn.IsNull() ) aOutRect.Intersection( pOut->LogicToPixel( aPaintRgn.GetBoundRect() ) ); } aOutRect.Intersection( aBmpRect ); if( !aOutRect.IsEmpty() ) { aOutPt = pOut->PixelToLogic( aOutRect.TopLeft() ); aOutSz = pOut->PixelToLogic( aOutRect.GetSize() ); nStartX = aOutRect.Left() - aBmpRect.Left(); nStartY = aOutRect.Top() - aBmpRect.Top(); nEndX = aOutRect.Right() - aBmpRect.Left(); nEndY = aOutRect.Bottom() - aBmpRect.Top(); } else nStartX = -1L; // invalid } else { aOutPt = pOut->PixelToLogic( aOutPtPix ); aOutSz = pOut->PixelToLogic( aOutSzPix ); nStartX = nStartY = 0; nEndX = aOutSzPix.Width() - 1L; nEndY = aOutSzPix.Height() - 1L; } // do transformation if( nStartX >= 0L ) { const sal_Bool bSimple = ( 1 == nW || 1 == nH ); if( nRot10 ) { if( bSimple ) { bRet = ( aOutBmpEx = aBmpEx ).Scale( aUnrotatedSzPix ); if( bRet ) bRet = aOutBmpEx.Rotate( nRot10, COL_TRANSPARENT ); } else if( nFlags & GRFMGR_DRAW_SMOOTHSCALE ) { // Scale using the Box filter, rather than this algorithm, as that one provides // better quality, while being somewhat slower (the result should be cached though). aOutBmpEx = aBmpEx; bRet = true; if( bHMirr || bVMirr ) bRet = aOutBmpEx.Mirror(( bHMirr ? BMP_MIRROR_HORZ : BMP_MIRROR_NONE ) | ( bVMirr ? BMP_MIRROR_VERT : BMP_MIRROR_NONE )); if( bRet ) bRet = aOutBmpEx.Rotate( nRot10, COL_TRANSPARENT ); if( bRet ) // scale as last (rotating would destroy the smooth scaling) bRet = aOutBmpEx.Scale( Size( nEndX - nStartX + 1, nEndY - nStartY + 1 ), BMP_SCALE_BOX ); } else { bRet = ImplCreateRotatedScaled( aBmpEx, nRot10, aOutSzPix, aUnrotatedSzPix, pMapIX, pMapFX, pMapIY, pMapFY, nStartX, nEndX, nStartY, nEndY, aOutBmpEx ); } } else { // #105229# Don't scale if output size equals bitmap size // #107226# Copy through only if we're not mirroring if( !bHMirr && !bVMirr && aOutSzPix == rBmpSzPix ) { // #107226# Use original dimensions when just copying through aOutPt = pOut->PixelToLogic( aOutPtPix ); aOutSz = pOut->PixelToLogic( aOutSzPix ); aOutBmpEx = aBmpEx; bRet = sal_True; } else { if( bSimple ) bRet = ( aOutBmpEx = aBmpEx ).Scale( Size( nEndX - nStartX + 1, nEndY - nStartY + 1 ) ); else if( nFlags & GRFMGR_DRAW_SMOOTHSCALE ) { // Scale using the Box filter, rather than this algorithm, as that one provides // better quality, while being somewhat slower (the result should be cached though). aOutBmpEx = aBmpEx; bRet = aOutBmpEx.Scale( Size( nEndX - nStartX + 1, nEndY - nStartY + 1 ), BMP_SCALE_BOX ); if( bRet && ( bHMirr || bVMirr )) bRet = aOutBmpEx.Mirror(( bHMirr ? BMP_MIRROR_HORZ : BMP_MIRROR_NONE ) | ( bVMirr ? BMP_MIRROR_VERT : BMP_MIRROR_NONE )); } else { bRet = ImplCreateScaled( aBmpEx, pMapIX, pMapFX, pMapIY, pMapFY, nStartX, nEndX, nStartY, nEndY, aOutBmpEx ); } } } if( bRet ) { // attribute adjustment if neccessary if( rAttr.IsSpecialDrawMode() || rAttr.IsAdjusted() || rAttr.IsTransparent() ) ImplAdjust( aOutBmpEx, rAttr, ADJUSTMENT_DRAWMODE | ADJUSTMENT_COLORS | ADJUSTMENT_TRANSPARENCY ); // OutDev adjustment if neccessary if( pOut->GetOutDevType() != OUTDEV_PRINTER && pOut->GetBitCount() <= 8 && aOutBmpEx.GetBitCount() >= 8 ) aOutBmpEx.Dither( BMP_DITHER_MATRIX ); } } // delete lookup tables delete[] pMapIX; delete[] pMapFX; delete[] pMapIY; delete[] pMapFY; // create output if( bRet ) { if( !pBmpEx ) pOut->DrawBitmapEx( aOutPt, aOutSz, aOutBmpEx ); else { if( !rAttr.IsTransparent() && !aOutBmpEx.IsAlpha() ) aOutBmpEx = BitmapEx( aOutBmpEx.GetBitmap().CreateDisplayBitmap( pOut ), aOutBmpEx.GetMask() ); pOut->DrawBitmapEx( aOutPt, aOutSz, *pBmpEx = aOutBmpEx ); } } } return bRet; } // ----------------------------------------------------------------------------- // This function checks whether the bitmap is usable for skipping // mtf rendering by using just this one bitmap (i.e. in case the metafile // contains just this one pixmap that covers the entire metafile area). static BitmapEx checkMetadataBitmap( const BitmapEx& rBmpEx, Point rSrcPoint, Size rSrcSize, const Point& rDestPoint, const Size& rDestSize, const Size& rRefSize, bool& o_rbNonBitmapActionEncountered ) { // NOTE: If you do changes in this function, change checkMetadataBitmap() in grfcache.cxx too. BitmapEx aBmpEx; if( rSrcSize == Size()) rSrcSize = rBmpEx.GetSizePixel(); if( rDestPoint != Point( 0, 0 )) { // The pixmap in the metafile has an offset (and so would not cover) // the entire result -> fall back to mtf rendering. o_rbNonBitmapActionEncountered = true; return aBmpEx; } if( rDestSize != rRefSize ) { // The pixmap is not fullscale (does not cover the entire metafile area). // HACK: The code here should refuse to use the bitmap directly // and fall back to mtf rendering, but there seem to be metafiles // that do not specify exactly their area (the Windows API requires apps // the specify it manually, the rectangle is specified as topleft/bottomright // rather than topleft/size [which may be confusing], and the docs // on the exact meaning are somewhat confusing as well), so if it turns // out this metafile really contains just one bitmap and no other painting, // and if the sizes almost match, just use the pixmap (which will be scaled // to fit exactly the requested size, so there should not be any actual problem // caused by this small difference). This will allow caching of the resulting // (scaled) pixmap, which can make a noticeable performance difference. if( rBmpEx.GetSizePixel().Width() > 100 && rBmpEx.GetSizePixel().Height() > 100 && abs( rDestSize.Width() - rRefSize.Width()) < 5 && abs( rDestSize.Height() - rRefSize.Height()) < 5 ) ; // ok, assume it's close enough else { // fall back to mtf rendering o_rbNonBitmapActionEncountered = true; return aBmpEx; } } aBmpEx = rBmpEx; if( (rSrcPoint.X() != 0 && rSrcPoint.Y() != 0) || rSrcSize != rBmpEx.GetSizePixel() ) { // crop bitmap to given source rectangle (no // need to copy and convert the whole bitmap) const Rectangle aCropRect( rSrcPoint, rSrcSize ); aBmpEx.Crop( aCropRect ); } return aBmpEx; } sal_Bool GraphicManager::ImplCreateOutput( OutputDevice* pOut, const Point& rPt, const Size& rSz, const GDIMetaFile& rMtf, const GraphicAttr& rAttr, const sal_uLong /*nFlags*/, GDIMetaFile& rOutMtf, BitmapEx& rOutBmpEx ) { const Size aNewSize( rMtf.GetPrefSize() ); rOutMtf = rMtf; // #117889# count bitmap actions, and flag actions that paint, but // are no bitmaps. sal_Int32 nNumBitmaps(0); bool bNonBitmapActionEncountered(false); if( aNewSize.Width() && aNewSize.Height() && rSz.Width() && rSz.Height() ) { const double fGrfWH = (double) aNewSize.Width() / aNewSize.Height(); const double fOutWH = (double) rSz.Width() / rSz.Height(); const double fScaleX = fOutWH / fGrfWH; const double fScaleY = 1.0; const MapMode& rPrefMapMode( rMtf.GetPrefMapMode() ); const Size& rSizePix( pOut->LogicToPixel( aNewSize, rPrefMapMode ) ); // NOTE: If you do changes in this function, check GraphicDisplayCacheEntry::IsCacheableAsBitmap // in grfcache.cxx too. // Determine whether the metafile basically displays // a single bitmap (in which case that bitmap is simply used directly // instead of playing the metafile). Note that // the solution, as implemented here, is quite suboptimal (the // cases where a mtf consisting basically of a single bitmap, // that fail to pass the test below, are probably frequent). A // better solution would involve FSAA, but that's currently // expensive, and might trigger bugs on display drivers, if // VDevs get bigger than the actual screen. sal_uInt32 nCurPos; MetaAction* pAct; for( nCurPos = 0, pAct = (MetaAction*)rOutMtf.FirstAction(); pAct; pAct = (MetaAction*)rOutMtf.NextAction(), nCurPos++ ) { MetaAction* pModAct = NULL; switch( pAct->GetType() ) { case META_FONT_ACTION: { MetaFontAction* pA = (MetaFontAction*)pAct; Font aFont( pA->GetFont() ); if ( !aFont.GetWidth() ) { FontMetric aFontMetric( pOut->GetFontMetric( aFont ) ); aFont.SetWidth( aFontMetric.GetWidth() ); pModAct = new MetaFontAction( aFont ); } } // FALLTHROUGH intended case META_NULL_ACTION: // FALLTHROUGH intended // OutDev state changes (which don't affect bitmap // output) case META_LINECOLOR_ACTION: // FALLTHROUGH intended case META_FILLCOLOR_ACTION: // FALLTHROUGH intended case META_TEXTCOLOR_ACTION: // FALLTHROUGH intended case META_TEXTFILLCOLOR_ACTION: // FALLTHROUGH intended case META_TEXTALIGN_ACTION: // FALLTHROUGH intended case META_TEXTLINECOLOR_ACTION: // FALLTHROUGH intended case META_TEXTLINE_ACTION: // FALLTHROUGH intended case META_PUSH_ACTION: // FALLTHROUGH intended case META_POP_ACTION: // FALLTHROUGH intended case META_LAYOUTMODE_ACTION: // FALLTHROUGH intended case META_TEXTLANGUAGE_ACTION: // FALLTHROUGH intended case META_COMMENT_ACTION: break; // bitmap output methods case META_BMP_ACTION: if( !nNumBitmaps && !bNonBitmapActionEncountered ) { MetaBmpAction* pAction = (MetaBmpAction*)pAct; rOutBmpEx = checkMetadataBitmap( BitmapEx( pAction->GetBitmap()), Point(), Size(), pOut->LogicToPixel( pAction->GetPoint(), rPrefMapMode ), pAction->GetBitmap().GetSizePixel(), rSizePix, bNonBitmapActionEncountered ); } ++nNumBitmaps; break; case META_BMPSCALE_ACTION: if( !nNumBitmaps && !bNonBitmapActionEncountered ) { MetaBmpScaleAction* pAction = (MetaBmpScaleAction*)pAct; rOutBmpEx = checkMetadataBitmap( BitmapEx( pAction->GetBitmap()), Point(), Size(), pOut->LogicToPixel( pAction->GetPoint(), rPrefMapMode ), pOut->LogicToPixel( pAction->GetSize(), rPrefMapMode ), rSizePix, bNonBitmapActionEncountered ); } ++nNumBitmaps; break; case META_BMPSCALEPART_ACTION: if( !nNumBitmaps && !bNonBitmapActionEncountered ) { MetaBmpScalePartAction* pAction = (MetaBmpScalePartAction*)pAct; rOutBmpEx = checkMetadataBitmap( BitmapEx( pAction->GetBitmap() ), pAction->GetSrcPoint(), pAction->GetSrcSize(), pOut->LogicToPixel( pAction->GetDestPoint(), rPrefMapMode ), pOut->LogicToPixel( pAction->GetDestSize(), rPrefMapMode ), rSizePix, bNonBitmapActionEncountered ); } ++nNumBitmaps; break; case META_BMPEX_ACTION: if( !nNumBitmaps && !bNonBitmapActionEncountered ) { MetaBmpExAction* pAction = (MetaBmpExAction*)pAct; rOutBmpEx = checkMetadataBitmap( pAction->GetBitmapEx(), Point(), Size(), pOut->LogicToPixel( pAction->GetPoint(), rPrefMapMode ), pAction->GetBitmapEx().GetSizePixel(), rSizePix, bNonBitmapActionEncountered ); } ++nNumBitmaps; break; case META_BMPEXSCALE_ACTION: if( !nNumBitmaps && !bNonBitmapActionEncountered ) { MetaBmpExScaleAction* pAction = (MetaBmpExScaleAction*)pAct; rOutBmpEx = checkMetadataBitmap( pAction->GetBitmapEx(), Point(), Size(), pOut->LogicToPixel( pAction->GetPoint(), rPrefMapMode ), pOut->LogicToPixel( pAction->GetSize(), rPrefMapMode ), rSizePix, bNonBitmapActionEncountered ); } ++nNumBitmaps; break; case META_BMPEXSCALEPART_ACTION: if( !nNumBitmaps && !bNonBitmapActionEncountered ) { MetaBmpExScalePartAction* pAction = (MetaBmpExScalePartAction*)pAct; rOutBmpEx = checkMetadataBitmap( pAction->GetBitmapEx(), pAction->GetSrcPoint(), pAction->GetSrcSize(), pOut->LogicToPixel( pAction->GetDestPoint(), rPrefMapMode ), pOut->LogicToPixel( pAction->GetDestSize(), rPrefMapMode ), rSizePix, bNonBitmapActionEncountered ); } ++nNumBitmaps; break; // these actions actually output something (that's // different from a bitmap) case META_RASTEROP_ACTION: if( ((MetaRasterOpAction*)pAct)->GetRasterOp() == ROP_OVERPAINT ) break; // FALLTHROUGH intended case META_PIXEL_ACTION: // FALLTHROUGH intended case META_POINT_ACTION: // FALLTHROUGH intended case META_LINE_ACTION: // FALLTHROUGH intended case META_RECT_ACTION: // FALLTHROUGH intended case META_ROUNDRECT_ACTION: // FALLTHROUGH intended case META_ELLIPSE_ACTION: // FALLTHROUGH intended case META_ARC_ACTION: // FALLTHROUGH intended case META_PIE_ACTION: // FALLTHROUGH intended case META_CHORD_ACTION: // FALLTHROUGH intended case META_POLYLINE_ACTION: // FALLTHROUGH intended case META_POLYGON_ACTION: // FALLTHROUGH intended case META_POLYPOLYGON_ACTION: // FALLTHROUGH intended case META_TEXT_ACTION: // FALLTHROUGH intended case META_TEXTARRAY_ACTION: // FALLTHROUGH intended case META_STRETCHTEXT_ACTION: // FALLTHROUGH intended case META_TEXTRECT_ACTION: // FALLTHROUGH intended case META_MASK_ACTION: // FALLTHROUGH intended case META_MASKSCALE_ACTION: // FALLTHROUGH intended case META_MASKSCALEPART_ACTION: // FALLTHROUGH intended case META_GRADIENT_ACTION: // FALLTHROUGH intended case META_HATCH_ACTION: // FALLTHROUGH intended case META_WALLPAPER_ACTION: // FALLTHROUGH intended case META_TRANSPARENT_ACTION: // FALLTHROUGH intended case META_EPS_ACTION: // FALLTHROUGH intended case META_FLOATTRANSPARENT_ACTION: // FALLTHROUGH intended case META_GRADIENTEX_ACTION: // FALLTHROUGH intended case META_RENDERGRAPHIC_ACTION: // FALLTHROUGH intended // OutDev state changes that _do_ affect bitmap // output case META_CLIPREGION_ACTION: // FALLTHROUGH intended case META_ISECTRECTCLIPREGION_ACTION: // FALLTHROUGH intended case META_ISECTREGIONCLIPREGION_ACTION: // FALLTHROUGH intended case META_MOVECLIPREGION_ACTION: // FALLTHROUGH intended case META_MAPMODE_ACTION: // FALLTHROUGH intended case META_REFPOINT_ACTION: // FALLTHROUGH intended default: bNonBitmapActionEncountered = true; break; } if ( pModAct ) { rOutMtf.ReplaceAction( pModAct, nCurPos ); pAct->Delete(); } else { if( pAct->GetRefCount() > 1 ) { rOutMtf.ReplaceAction( pModAct = pAct->Clone(), nCurPos ); pAct->Delete(); } else pModAct = pAct; } pModAct->Scale( fScaleX, fScaleY ); } rOutMtf.SetPrefSize( Size( FRound( aNewSize.Width() * fScaleX ), FRound( aNewSize.Height() * fScaleY ) ) ); } if( nNumBitmaps != 1 || bNonBitmapActionEncountered ) { if( rAttr.IsSpecialDrawMode() || rAttr.IsAdjusted() || rAttr.IsMirrored() || rAttr.IsRotated() || rAttr.IsTransparent() ) ImplAdjust( rOutMtf, rAttr, ADJUSTMENT_ALL ); ImplDraw( pOut, rPt, rSz, rOutMtf, rAttr ); rOutBmpEx = BitmapEx(); } return sal_True; } // ----------------------------------------------------------------------------- sal_Bool GraphicManager::ImplCreateScaled( const BitmapEx& rBmpEx, long* pMapIX, long* pMapFX, long* pMapIY, long* pMapFY, long nStartX, long nEndX, long nStartY, long nEndY, BitmapEx& rOutBmpEx ) { Bitmap aBmp( rBmpEx.GetBitmap() ); Bitmap aOutBmp; BitmapReadAccess* pAcc = aBmp.AcquireReadAccess(); BitmapWriteAccess* pWAcc; BitmapColor aCol0, aCol1, aColRes; const long nDstW = nEndX - nStartX + 1L; const long nDstH = nEndY - nStartY + 1L; long nX, nY, nTmpX, nTmpY, nTmpFX, nTmpFY; long nXDst, nYDst; sal_uInt8 cR0, cG0, cB0, cR1, cG1, cB1; sal_Bool bRet = sal_False; DBG_ASSERT( aBmp.GetSizePixel() == rBmpEx.GetSizePixel(), "GraphicManager::ImplCreateScaled(): bmp size inconsistent" ); if( pAcc ) { aOutBmp = Bitmap( Size( nDstW, nDstH ), 24 ); pWAcc = aOutBmp.AcquireWriteAccess(); if( pWAcc ) { if( pAcc->HasPalette() ) { if( pAcc->GetScanlineFormat() == BMP_FORMAT_8BIT_PAL ) { Scanline pLine0, pLine1; for( nY = nStartY, nYDst = 0L; nY <= nEndY; nY++, nYDst++ ) { nTmpY = pMapIY[ nY ]; nTmpFY = pMapFY[ nY ]; pLine0 = pAcc->GetScanline( nTmpY ); pLine1 = pAcc->GetScanline( ++nTmpY ); for( nX = nStartX, nXDst = 0L; nX <= nEndX; nX++ ) { nTmpX = pMapIX[ nX ]; nTmpFX = pMapFX[ nX ]; const BitmapColor& rCol0 = pAcc->GetPaletteColor( pLine0[ nTmpX ] ); const BitmapColor& rCol2 = pAcc->GetPaletteColor( pLine1[ nTmpX ] ); const BitmapColor& rCol1 = pAcc->GetPaletteColor( pLine0[ ++nTmpX ] ); const BitmapColor& rCol3 = pAcc->GetPaletteColor( pLine1[ nTmpX ] ); cR0 = MAP( rCol0.GetRed(), rCol1.GetRed(), nTmpFX ); cG0 = MAP( rCol0.GetGreen(), rCol1.GetGreen(), nTmpFX ); cB0 = MAP( rCol0.GetBlue(), rCol1.GetBlue(), nTmpFX ); cR1 = MAP( rCol2.GetRed(), rCol3.GetRed(), nTmpFX ); cG1 = MAP( rCol2.GetGreen(), rCol3.GetGreen(), nTmpFX ); cB1 = MAP( rCol2.GetBlue(), rCol3.GetBlue(), nTmpFX ); aColRes.SetRed( MAP( cR0, cR1, nTmpFY ) ); aColRes.SetGreen( MAP( cG0, cG1, nTmpFY ) ); aColRes.SetBlue( MAP( cB0, cB1, nTmpFY ) ); pWAcc->SetPixel( nYDst, nXDst++, aColRes ); } } } else { for( nY = nStartY, nYDst = 0L; nY <= nEndY; nY++, nYDst++ ) { nTmpY = pMapIY[ nY ], nTmpFY = pMapFY[ nY ]; for( nX = nStartX, nXDst = 0L; nX <= nEndX; nX++ ) { nTmpX = pMapIX[ nX ]; nTmpFX = pMapFX[ nX ]; aCol0 = pAcc->GetPaletteColor( pAcc->GetPixel( nTmpY, nTmpX ) ); aCol1 = pAcc->GetPaletteColor( pAcc->GetPixel( nTmpY, ++nTmpX ) ); cR0 = MAP( aCol0.GetRed(), aCol1.GetRed(), nTmpFX ); cG0 = MAP( aCol0.GetGreen(), aCol1.GetGreen(), nTmpFX ); cB0 = MAP( aCol0.GetBlue(), aCol1.GetBlue(), nTmpFX ); aCol1 = pAcc->GetPaletteColor( pAcc->GetPixel( ++nTmpY, nTmpX ) ); aCol0 = pAcc->GetPaletteColor( pAcc->GetPixel( nTmpY--, --nTmpX ) ); cR1 = MAP( aCol0.GetRed(), aCol1.GetRed(), nTmpFX ); cG1 = MAP( aCol0.GetGreen(), aCol1.GetGreen(), nTmpFX ); cB1 = MAP( aCol0.GetBlue(), aCol1.GetBlue(), nTmpFX ); aColRes.SetRed( MAP( cR0, cR1, nTmpFY ) ); aColRes.SetGreen( MAP( cG0, cG1, nTmpFY ) ); aColRes.SetBlue( MAP( cB0, cB1, nTmpFY ) ); pWAcc->SetPixel( nYDst, nXDst++, aColRes ); } } } } else { if( pAcc->GetScanlineFormat() == BMP_FORMAT_24BIT_TC_BGR ) { Scanline pLine0, pLine1, pTmp0, pTmp1; long nOff; for( nY = nStartY, nYDst = 0L; nY <= nEndY; nY++, nYDst++ ) { nTmpY = pMapIY[ nY ]; nTmpFY = pMapFY[ nY ]; pLine0 = pAcc->GetScanline( nTmpY ); pLine1 = pAcc->GetScanline( ++nTmpY ); for( nX = nStartX, nXDst = 0L; nX <= nEndX; nX++ ) { nOff = 3L * ( nTmpX = pMapIX[ nX ] ); nTmpFX = pMapFX[ nX ]; pTmp1 = ( pTmp0 = pLine0 + nOff ) + 3L; cB0 = MAP( *pTmp0, *pTmp1, nTmpFX ); pTmp0++; pTmp1++; cG0 = MAP( *pTmp0, *pTmp1, nTmpFX ); pTmp0++; pTmp1++; cR0 = MAP( *pTmp0, *pTmp1, nTmpFX ); pTmp1 = ( pTmp0 = pLine1 + nOff ) + 3L; cB1 = MAP( *pTmp0, *pTmp1, nTmpFX ); pTmp0++; pTmp1++; cG1 = MAP( *pTmp0, *pTmp1, nTmpFX ); pTmp0++; pTmp1++; cR1 = MAP( *pTmp0, *pTmp1, nTmpFX ); aColRes.SetRed( MAP( cR0, cR1, nTmpFY ) ); aColRes.SetGreen( MAP( cG0, cG1, nTmpFY ) ); aColRes.SetBlue( MAP( cB0, cB1, nTmpFY ) ); pWAcc->SetPixel( nYDst, nXDst++, aColRes ); } } } else if( pAcc->GetScanlineFormat() == BMP_FORMAT_24BIT_TC_RGB ) { Scanline pLine0, pLine1, pTmp0, pTmp1; long nOff; for( nY = nStartY, nYDst = 0L; nY <= nEndY; nY++, nYDst++ ) { nTmpY = pMapIY[ nY ]; nTmpFY = pMapFY[ nY ]; pLine0 = pAcc->GetScanline( nTmpY ); pLine1 = pAcc->GetScanline( ++nTmpY ); for( nX = nStartX, nXDst = 0L; nX <= nEndX; nX++ ) { nOff = 3L * ( nTmpX = pMapIX[ nX ] ); nTmpFX = pMapFX[ nX ]; pTmp1 = ( pTmp0 = pLine0 + nOff ) + 3L; cR0 = MAP( *pTmp0, *pTmp1, nTmpFX ); pTmp0++; pTmp1++; cG0 = MAP( *pTmp0, *pTmp1, nTmpFX ); pTmp0++; pTmp1++; cB0 = MAP( *pTmp0, *pTmp1, nTmpFX ); pTmp1 = ( pTmp0 = pLine1 + nOff ) + 3L; cR1 = MAP( *pTmp0, *pTmp1, nTmpFX ); pTmp0++; pTmp1++; cG1 = MAP( *pTmp0, *pTmp1, nTmpFX ); pTmp0++; pTmp1++; cB1 = MAP( *pTmp0, *pTmp1, nTmpFX ); aColRes.SetRed( MAP( cR0, cR1, nTmpFY ) ); aColRes.SetGreen( MAP( cG0, cG1, nTmpFY ) ); aColRes.SetBlue( MAP( cB0, cB1, nTmpFY ) ); pWAcc->SetPixel( nYDst, nXDst++, aColRes ); } } } else { for( nY = nStartY, nYDst = 0L; nY <= nEndY; nY++, nYDst++ ) { nTmpY = pMapIY[ nY ]; nTmpFY = pMapFY[ nY ]; for( nX = nStartX, nXDst = 0L; nX <= nEndX; nX++ ) { nTmpX = pMapIX[ nX ]; nTmpFX = pMapFX[ nX ]; aCol0 = pAcc->GetPixel( nTmpY, nTmpX ); aCol1 = pAcc->GetPixel( nTmpY, ++nTmpX ); cR0 = MAP( aCol0.GetRed(), aCol1.GetRed(), nTmpFX ); cG0 = MAP( aCol0.GetGreen(), aCol1.GetGreen(), nTmpFX ); cB0 = MAP( aCol0.GetBlue(), aCol1.GetBlue(), nTmpFX ); aCol1 = pAcc->GetPixel( ++nTmpY, nTmpX ); aCol0 = pAcc->GetPixel( nTmpY--, --nTmpX ); cR1 = MAP( aCol0.GetRed(), aCol1.GetRed(), nTmpFX ); cG1 = MAP( aCol0.GetGreen(), aCol1.GetGreen(), nTmpFX ); cB1 = MAP( aCol0.GetBlue(), aCol1.GetBlue(), nTmpFX ); aColRes.SetRed( MAP( cR0, cR1, nTmpFY ) ); aColRes.SetGreen( MAP( cG0, cG1, nTmpFY ) ); aColRes.SetBlue( MAP( cB0, cB1, nTmpFY ) ); pWAcc->SetPixel( nYDst, nXDst++, aColRes ); } } } } aOutBmp.ReleaseAccess( pWAcc ); bRet = sal_True; } aBmp.ReleaseAccess( pAcc ); } if( bRet && rBmpEx.IsTransparent() ) { bRet = sal_False; if( rBmpEx.IsAlpha() ) { DBG_ASSERT( rBmpEx.GetAlpha().GetSizePixel() == rBmpEx.GetSizePixel(), "GraphicManager::ImplCreateScaled(): alpha mask size inconsistent" ); AlphaMask aAlpha( rBmpEx.GetAlpha() ); AlphaMask aOutAlpha; pAcc = aAlpha.AcquireReadAccess(); if( pAcc ) { aOutAlpha = AlphaMask( Size( nDstW, nDstH ) ); pWAcc = aOutAlpha.AcquireWriteAccess(); if( pWAcc ) { if( pAcc->GetScanlineFormat() == BMP_FORMAT_8BIT_PAL && pWAcc->GetScanlineFormat() == BMP_FORMAT_8BIT_PAL ) { Scanline pLine0, pLine1, pLineW; for( nY = nStartY, nYDst = 0L; nY <= nEndY; nY++, nYDst++ ) { nTmpY = pMapIY[ nY ]; nTmpFY = pMapFY[ nY ]; pLine0 = pAcc->GetScanline( nTmpY ); pLine1 = pAcc->GetScanline( ++nTmpY ); pLineW = pWAcc->GetScanline( nYDst ); for( nX = nStartX, nXDst = 0L; nX <= nEndX; nX++, nXDst++ ) { nTmpX = pMapIX[ nX ]; nTmpFX = pMapFX[ nX ]; const long nAlpha0 = pLine0[ nTmpX ]; const long nAlpha2 = pLine1[ nTmpX ]; const long nAlpha1 = pLine0[ ++nTmpX ]; const long nAlpha3 = pLine1[ nTmpX ]; const long n0 = MAP( nAlpha0, nAlpha1, nTmpFX ); const long n1 = MAP( nAlpha2, nAlpha3, nTmpFX ); *pLineW++ = MAP( n0, n1, nTmpFY ); } } } else { BitmapColor aAlphaValue( 0 ); for( nY = nStartY, nYDst = 0L; nY <= nEndY; nY++, nYDst++ ) { nTmpY = pMapIY[ nY ], nTmpFY = pMapFY[ nY ]; for( nX = nStartX, nXDst = 0L; nX <= nEndX; nX++ ) { nTmpX = pMapIX[ nX ]; nTmpFX = pMapFX[ nX ]; long nAlpha0 = pAcc->GetPixel( nTmpY, nTmpX ).GetIndex(); long nAlpha1 = pAcc->GetPixel( nTmpY, ++nTmpX ).GetIndex(); const long n0 = MAP( nAlpha0, nAlpha1, nTmpFX ); nAlpha1 = pAcc->GetPixel( ++nTmpY, nTmpX ).GetIndex(); nAlpha0 = pAcc->GetPixel( nTmpY--, --nTmpX ).GetIndex(); const long n1 = MAP( nAlpha0, nAlpha1, nTmpFX ); aAlphaValue.SetIndex( MAP( n0, n1, nTmpFY ) ); pWAcc->SetPixel( nYDst, nXDst++, aAlphaValue ); } } } aOutAlpha.ReleaseAccess( pWAcc ); bRet = sal_True; } aAlpha.ReleaseAccess( pAcc ); if( bRet ) rOutBmpEx = BitmapEx( aOutBmp, aOutAlpha ); } } else { DBG_ASSERT( rBmpEx.GetMask().GetSizePixel() == rBmpEx.GetSizePixel(), "GraphicManager::ImplCreateScaled(): mask size inconsistent" ); Bitmap aMsk( rBmpEx.GetMask() ); Bitmap aOutMsk; pAcc = aMsk.AcquireReadAccess(); if( pAcc ) { // #i40115# Use the same palette for destination // bitmap. Otherwise, we'd have to color-map even the // case below, when both masks are one bit deep. if( pAcc->HasPalette() ) aOutMsk = Bitmap( Size( nDstW, nDstH ), 1, &pAcc->GetPalette() ); else aOutMsk = Bitmap( Size( nDstW, nDstH ), 1 ); pWAcc = aOutMsk.AcquireWriteAccess(); if( pWAcc ) { long* pMapLX = new long[ nDstW ]; long* pMapLY = new long[ nDstH ]; // create new horizontal mapping table for( nX = 0UL, nTmpX = nStartX; nX < nDstW; nTmpX++ ) pMapLX[ nX++ ] = FRound( (double) pMapIX[ nTmpX ] + pMapFX[ nTmpX ] / 1048576. ); // create new vertical mapping table for( nY = 0UL, nTmpY = nStartY; nY < nDstH; nTmpY++ ) pMapLY[ nY++ ] = FRound( (double) pMapIY[ nTmpY ] + pMapFY[ nTmpY ] / 1048576. ); // do normal scaling if( pAcc->GetScanlineFormat() == BMP_FORMAT_1BIT_MSB_PAL && pWAcc->GetScanlineFormat() == BMP_FORMAT_1BIT_MSB_PAL ) { // optimized for( nY = 0; nY < nDstH; nY++ ) { Scanline pSrc = pAcc->GetScanline( pMapLY[ nY ] ); Scanline pDst = pWAcc->GetScanline( nY ); for( nX = 0L; nX < nDstW; nX++ ) { const long nSrcX = pMapLX[ nX ]; if( pSrc[ nSrcX >> 3 ] & ( 1 << ( 7 - ( nSrcX & 7 ) ) ) ) pDst[ nX >> 3 ] |= 1 << ( 7 - ( nX & 7 ) ); else pDst[ nX >> 3 ] &= ~( 1 << ( 7 - ( nX & 7 ) ) ); } } } else { const BitmapColor aB( pAcc->GetBestMatchingColor( Color( COL_BLACK ) ) ); const BitmapColor aWB( pWAcc->GetBestMatchingColor( Color( COL_BLACK ) ) ); const BitmapColor aWW( pWAcc->GetBestMatchingColor( Color( COL_WHITE ) ) ); if( pAcc->HasPalette() ) { for( nY = 0L; nY < nDstH; nY++ ) { for( nX = 0L; nX < nDstW; nX++ ) { if( pAcc->GetPaletteColor( (sal_uInt8) pAcc->GetPixel( pMapLY[ nY ], pMapLX[ nX ] ) ) == aB ) pWAcc->SetPixel( nY, nX, aWB ); else pWAcc->SetPixel( nY, nX, aWW ); } } } else { for( nY = 0L; nY < nDstH; nY++ ) { for( nX = 0L; nX < nDstW; nX++ ) { if( pAcc->GetPixel( pMapLY[ nY ], pMapLX[ nX ] ) == aB ) pWAcc->SetPixel( nY, nX, aWB ); else pWAcc->SetPixel( nY, nX, aWW ); } } } } delete[] pMapLX; delete[] pMapLY; aOutMsk.ReleaseAccess( pWAcc ); bRet = sal_True; } aMsk.ReleaseAccess( pAcc ); if( bRet ) rOutBmpEx = BitmapEx( aOutBmp, aOutMsk ); } } if( !bRet ) rOutBmpEx = aOutBmp; } else rOutBmpEx = aOutBmp; return bRet; } // ----------------------------------------------------------------------------- sal_Bool GraphicManager::ImplCreateRotatedScaled( const BitmapEx& rBmpEx, sal_uInt16 nRot10, const Size& /*rOutSzPix*/, const Size& rUnrotatedSzPix, long* pMapIX, long* pMapFX, long* pMapIY, long* pMapFY, long nStartX, long nEndX, long nStartY, long nEndY, BitmapEx& rOutBmpEx ) { Point aPt; Bitmap aBmp( rBmpEx.GetBitmap() ); Bitmap aOutBmp; BitmapReadAccess* pAcc = aBmp.AcquireReadAccess(); BitmapWriteAccess* pWAcc; Polygon aPoly( Rectangle( aPt, rUnrotatedSzPix ) ); aPoly.Rotate( Point(), nRot10 ); Rectangle aNewBound( aPoly.GetBoundRect() ); const double fCosAngle = cos( nRot10 * F_PI1800 ), fSinAngle = sin( nRot10 * F_PI1800 ); double fTmp; const long nDstW = nEndX - nStartX + 1L; const long nDstH = nEndY - nStartY + 1L; const long nUnRotW = rUnrotatedSzPix.Width(); const long nUnRotH = rUnrotatedSzPix.Height(); long* pCosX = new long[ nDstW ]; long* pSinX = new long[ nDstW ]; long* pCosY = new long[ nDstH ]; long* pSinY = new long[ nDstH ]; long nX, nY, nTmpX, nTmpY, nTmpFX, nTmpFY, nUnRotX, nUnRotY, nSinY, nCosY; sal_uInt8 cR0, cG0, cB0, cR1, cG1, cB1; sal_Bool bRet = sal_False; // create horizontal mapping table for( nX = 0L, nTmpX = aNewBound.Left() + nStartX; nX < nDstW; nX++ ) { fTmp = nTmpX++ << 8; pCosX[ nX ] = FRound( fCosAngle * fTmp ); pSinX[ nX ] = FRound( fSinAngle * fTmp ); } // create vertical mapping table for( nY = 0L, nTmpY = aNewBound.Top() + nStartY; nY < nDstH; nY++ ) { fTmp = nTmpY++ << 8; pCosY[ nY ] = FRound( fCosAngle * fTmp ); pSinY[ nY ] = FRound( fSinAngle * fTmp ); } if( pAcc ) { aOutBmp = Bitmap( Size( nDstW, nDstH ), 24 ); pWAcc = aOutBmp.AcquireWriteAccess(); if( pWAcc ) { BitmapColor aColRes; if( pAcc->HasPalette() ) { for( nY = 0; nY < nDstH; nY++ ) { nSinY = pSinY[ nY ]; nCosY = pCosY[ nY ]; for( nX = 0; nX < nDstW; nX++ ) { nUnRotX = ( pCosX[ nX ] - nSinY ) >> 8; nUnRotY = ( pSinX[ nX ] + nCosY ) >> 8; if( ( nUnRotX >= 0L ) && ( nUnRotX < nUnRotW ) && ( nUnRotY >= 0L ) && ( nUnRotY < nUnRotH ) ) { nTmpX = pMapIX[ nUnRotX ]; nTmpFX = pMapFX[ nUnRotX ]; nTmpY = pMapIY[ nUnRotY ], nTmpFY = pMapFY[ nUnRotY ]; const BitmapColor& rCol0 = pAcc->GetPaletteColor( pAcc->GetPixel( nTmpY, nTmpX ) ); const BitmapColor& rCol1 = pAcc->GetPaletteColor( pAcc->GetPixel( nTmpY, ++nTmpX ) ); cR0 = MAP( rCol0.GetRed(), rCol1.GetRed(), nTmpFX ); cG0 = MAP( rCol0.GetGreen(), rCol1.GetGreen(), nTmpFX ); cB0 = MAP( rCol0.GetBlue(), rCol1.GetBlue(), nTmpFX ); const BitmapColor& rCol3 = pAcc->GetPaletteColor( pAcc->GetPixel( ++nTmpY, nTmpX ) ); const BitmapColor& rCol2 = pAcc->GetPaletteColor( pAcc->GetPixel( nTmpY, --nTmpX ) ); cR1 = MAP( rCol2.GetRed(), rCol3.GetRed(), nTmpFX ); cG1 = MAP( rCol2.GetGreen(), rCol3.GetGreen(), nTmpFX ); cB1 = MAP( rCol2.GetBlue(), rCol3.GetBlue(), nTmpFX ); aColRes.SetRed( MAP( cR0, cR1, nTmpFY ) ); aColRes.SetGreen( MAP( cG0, cG1, nTmpFY ) ); aColRes.SetBlue( MAP( cB0, cB1, nTmpFY ) ); pWAcc->SetPixel( nY, nX, aColRes ); } } } } else { BitmapColor aCol0, aCol1; for( nY = 0; nY < nDstH; nY++ ) { nSinY = pSinY[ nY ]; nCosY = pCosY[ nY ]; for( nX = 0; nX < nDstW; nX++ ) { nUnRotX = ( pCosX[ nX ] - nSinY ) >> 8; nUnRotY = ( pSinX[ nX ] + nCosY ) >> 8; if( ( nUnRotX >= 0L ) && ( nUnRotX < nUnRotW ) && ( nUnRotY >= 0L ) && ( nUnRotY < nUnRotH ) ) { nTmpX = pMapIX[ nUnRotX ]; nTmpFX = pMapFX[ nUnRotX ]; nTmpY = pMapIY[ nUnRotY ], nTmpFY = pMapFY[ nUnRotY ]; aCol0 = pAcc->GetPixel( nTmpY, nTmpX ); aCol1 = pAcc->GetPixel( nTmpY, ++nTmpX ); cR0 = MAP( aCol0.GetRed(), aCol1.GetRed(), nTmpFX ); cG0 = MAP( aCol0.GetGreen(), aCol1.GetGreen(), nTmpFX ); cB0 = MAP( aCol0.GetBlue(), aCol1.GetBlue(), nTmpFX ); aCol1 = pAcc->GetPixel( ++nTmpY, nTmpX ); aCol0 = pAcc->GetPixel( nTmpY, --nTmpX ); cR1 = MAP( aCol0.GetRed(), aCol1.GetRed(), nTmpFX ); cG1 = MAP( aCol0.GetGreen(), aCol1.GetGreen(), nTmpFX ); cB1 = MAP( aCol0.GetBlue(), aCol1.GetBlue(), nTmpFX ); aColRes.SetRed( MAP( cR0, cR1, nTmpFY ) ); aColRes.SetGreen( MAP( cG0, cG1, nTmpFY ) ); aColRes.SetBlue( MAP( cB0, cB1, nTmpFY ) ); pWAcc->SetPixel( nY, nX, aColRes ); } } } } aOutBmp.ReleaseAccess( pWAcc ); bRet = sal_True; } aBmp.ReleaseAccess( pAcc ); } // mask processing if( bRet && ( rBmpEx.IsTransparent() || ( nRot10 != 900 && nRot10 != 1800 && nRot10 != 2700 ) ) ) { bRet = sal_False; if( rBmpEx.IsAlpha() ) { AlphaMask aAlpha( rBmpEx.GetAlpha() ); AlphaMask aOutAlpha; pAcc = aAlpha.AcquireReadAccess(); if( pAcc ) { aOutAlpha = AlphaMask( Size( nDstW, nDstH ) ); pWAcc = aOutAlpha.AcquireWriteAccess(); if( pWAcc ) { if( pAcc->GetScanlineFormat() == BMP_FORMAT_8BIT_PAL && pWAcc->GetScanlineFormat() == BMP_FORMAT_8BIT_PAL ) { Scanline pLine0, pLine1, pLineW; for( nY = 0; nY < nDstH; nY++ ) { nSinY = pSinY[ nY ], nCosY = pCosY[ nY ]; pLineW = pWAcc->GetScanline( nY ); for( nX = 0; nX < nDstW; nX++ ) { nUnRotX = ( pCosX[ nX ] - nSinY ) >> 8; nUnRotY = ( pSinX[ nX ] + nCosY ) >> 8; if( ( nUnRotX >= 0L ) && ( nUnRotX < nUnRotW ) && ( nUnRotY >= 0L ) && ( nUnRotY < nUnRotH ) ) { nTmpX = pMapIX[ nUnRotX ], nTmpFX = pMapFX[ nUnRotX ]; nTmpY = pMapIY[ nUnRotY ], nTmpFY = pMapFY[ nUnRotY ]; pLine0 = pAcc->GetScanline( nTmpY++ ); pLine1 = pAcc->GetScanline( nTmpY ); const long nAlpha0 = pLine0[ nTmpX ]; const long nAlpha2 = pLine1[ nTmpX++ ]; const long nAlpha1 = pLine0[ nTmpX ]; const long nAlpha3 = pLine1[ nTmpX ]; const long n0 = MAP( nAlpha0, nAlpha1, nTmpFX ); const long n1 = MAP( nAlpha2, nAlpha3, nTmpFX ); *pLineW++ = MAP( n0, n1, nTmpFY ); } else *pLineW++ = 255; } } } else { const BitmapColor aTrans( pWAcc->GetBestMatchingColor( Color( COL_WHITE ) ) ); BitmapColor aAlphaVal( 0 ); for( nY = 0; nY < nDstH; nY++ ) { nSinY = pSinY[ nY ], nCosY = pCosY[ nY ]; for( nX = 0; nX < nDstW; nX++ ) { nUnRotX = ( pCosX[ nX ] - nSinY ) >> 8; nUnRotY = ( pSinX[ nX ] + nCosY ) >> 8; if( ( nUnRotX >= 0L ) && ( nUnRotX < nUnRotW ) && ( nUnRotY >= 0L ) && ( nUnRotY < nUnRotH ) ) { nTmpX = pMapIX[ nUnRotX ]; nTmpFX = pMapFX[ nUnRotX ]; nTmpY = pMapIY[ nUnRotY ], nTmpFY = pMapFY[ nUnRotY ]; const long nAlpha0 = pAcc->GetPixel( nTmpY, nTmpX ).GetIndex(); const long nAlpha1 = pAcc->GetPixel( nTmpY, ++nTmpX ).GetIndex(); const long nAlpha3 = pAcc->GetPixel( ++nTmpY, nTmpX ).GetIndex(); const long nAlpha2 = pAcc->GetPixel( nTmpY, --nTmpX ).GetIndex(); const long n0 = MAP( nAlpha0, nAlpha1, nTmpFX ); const long n1 = MAP( nAlpha2, nAlpha3, nTmpFX ); aAlphaVal.SetIndex( MAP( n0, n1, nTmpFY ) ); pWAcc->SetPixel( nY, nX, aAlphaVal ); } else pWAcc->SetPixel( nY, nX, aTrans ); } } } aOutAlpha.ReleaseAccess( pWAcc ); bRet = sal_True; } aAlpha.ReleaseAccess( pAcc ); } if( bRet ) rOutBmpEx = BitmapEx( aOutBmp, aOutAlpha ); } else { Bitmap aOutMsk( Size( nDstW, nDstH ), 1 ); pWAcc = aOutMsk.AcquireWriteAccess(); if( pWAcc ) { Bitmap aMsk( rBmpEx.GetMask() ); const BitmapColor aB( pWAcc->GetBestMatchingColor( Color( COL_BLACK ) ) ); const BitmapColor aW( pWAcc->GetBestMatchingColor( Color( COL_WHITE ) ) ); BitmapReadAccess* pMAcc = NULL; if( !aMsk || ( ( pMAcc = aMsk.AcquireReadAccess() ) != NULL ) ) { long* pMapLX = new long[ nUnRotW ]; long* pMapLY = new long[ nUnRotH ]; BitmapColor aTestB; if( pMAcc ) aTestB = pMAcc->GetBestMatchingColor( Color( COL_BLACK ) ); // create new horizontal mapping table for( nX = 0UL; nX < nUnRotW; nX++ ) pMapLX[ nX ] = FRound( (double) pMapIX[ nX ] + pMapFX[ nX ] / 1048576. ); // create new vertical mapping table for( nY = 0UL; nY < nUnRotH; nY++ ) pMapLY[ nY ] = FRound( (double) pMapIY[ nY ] + pMapFY[ nY ] / 1048576. ); // do mask rotation for( nY = 0; nY < nDstH; nY++ ) { nSinY = pSinY[ nY ]; nCosY = pCosY[ nY ]; for( nX = 0; nX < nDstW; nX++ ) { nUnRotX = ( pCosX[ nX ] - nSinY ) >> 8; nUnRotY = ( pSinX[ nX ] + nCosY ) >> 8; if( ( nUnRotX >= 0L ) && ( nUnRotX < nUnRotW ) && ( nUnRotY >= 0L ) && ( nUnRotY < nUnRotH ) ) { if( pMAcc ) { if( pMAcc->GetPixel( pMapLY[ nUnRotY ], pMapLX[ nUnRotX ] ) == aTestB ) pWAcc->SetPixel( nY, nX, aB ); else pWAcc->SetPixel( nY, nX, aW ); } else pWAcc->SetPixel( nY, nX, aB ); } else pWAcc->SetPixel( nY, nX, aW ); } } delete[] pMapLX; delete[] pMapLY; if( pMAcc ) aMsk.ReleaseAccess( pMAcc ); bRet = sal_True; } aOutMsk.ReleaseAccess( pWAcc ); } if( bRet ) rOutBmpEx = BitmapEx( aOutBmp, aOutMsk ); } if( !bRet ) rOutBmpEx = aOutBmp; } else rOutBmpEx = aOutBmp; delete[] pSinX; delete[] pCosX; delete[] pSinY; delete[] pCosY; return bRet; } // ----------------------------------------------------------------------------- void GraphicManager::ImplAdjust( BitmapEx& rBmpEx, const GraphicAttr& rAttr, sal_uLong nAdjustmentFlags ) { GraphicAttr aAttr( rAttr ); if( ( nAdjustmentFlags & ADJUSTMENT_DRAWMODE ) && aAttr.IsSpecialDrawMode() ) { switch( aAttr.GetDrawMode() ) { case( GRAPHICDRAWMODE_MONO ): rBmpEx.Convert( BMP_CONVERSION_1BIT_THRESHOLD ); break; case( GRAPHICDRAWMODE_GREYS ): rBmpEx.Convert( BMP_CONVERSION_8BIT_GREYS ); break; case( GRAPHICDRAWMODE_WATERMARK ): { aAttr.SetLuminance( aAttr.GetLuminance() + WATERMARK_LUM_OFFSET ); aAttr.SetContrast( aAttr.GetContrast() + WATERMARK_CON_OFFSET ); } break; default: break; } } if( ( nAdjustmentFlags & ADJUSTMENT_COLORS ) && aAttr.IsAdjusted() ) { rBmpEx.Adjust( aAttr.GetLuminance(), aAttr.GetContrast(), aAttr.GetChannelR(), aAttr.GetChannelG(), aAttr.GetChannelB(), aAttr.GetGamma(), aAttr.IsInvert() ); } if( ( nAdjustmentFlags & ADJUSTMENT_MIRROR ) && aAttr.IsMirrored() ) { rBmpEx.Mirror( aAttr.GetMirrorFlags() ); } if( ( nAdjustmentFlags & ADJUSTMENT_ROTATE ) && aAttr.IsRotated() ) { rBmpEx.Rotate( aAttr.GetRotation(), Color( COL_TRANSPARENT ) ); } if( ( nAdjustmentFlags & ADJUSTMENT_TRANSPARENCY ) && aAttr.IsTransparent() ) { AlphaMask aAlpha; sal_uInt8 cTrans = aAttr.GetTransparency(); if( !rBmpEx.IsTransparent() ) aAlpha = AlphaMask( rBmpEx.GetSizePixel(), &cTrans ); else if( !rBmpEx.IsAlpha() ) { aAlpha = rBmpEx.GetMask(); aAlpha.Replace( 0, cTrans ); } else { aAlpha = rBmpEx.GetAlpha(); BitmapWriteAccess* pA = aAlpha.AcquireWriteAccess(); if( pA ) { sal_uLong nTrans = cTrans, nNewTrans; const long nWidth = pA->Width(), nHeight = pA->Height(); if( pA->GetScanlineFormat() == BMP_FORMAT_8BIT_PAL ) { for( long nY = 0; nY < nHeight; nY++ ) { Scanline pAScan = pA->GetScanline( nY ); for( long nX = 0; nX < nWidth; nX++ ) { nNewTrans = nTrans + *pAScan; *pAScan++ = (sal_uInt8) ( ( nNewTrans & 0xffffff00 ) ? 255 : nNewTrans ); } } } else { BitmapColor aAlphaValue( 0 ); for( long nY = 0; nY < nHeight; nY++ ) { for( long nX = 0; nX < nWidth; nX++ ) { nNewTrans = nTrans + pA->GetPixel( nY, nX ).GetIndex(); aAlphaValue.SetIndex( (sal_uInt8) ( ( nNewTrans & 0xffffff00 ) ? 255 : nNewTrans ) ); pA->SetPixel( nY, nX, aAlphaValue ); } } } aAlpha.ReleaseAccess( pA ); } } rBmpEx = BitmapEx( rBmpEx.GetBitmap(), aAlpha ); } } // ----------------------------------------------------------------------------- void GraphicManager::ImplAdjust( GDIMetaFile& rMtf, const GraphicAttr& rAttr, sal_uLong nAdjustmentFlags ) { GraphicAttr aAttr( rAttr ); if( ( nAdjustmentFlags & ADJUSTMENT_DRAWMODE ) && aAttr.IsSpecialDrawMode() ) { switch( aAttr.GetDrawMode() ) { case( GRAPHICDRAWMODE_MONO ): rMtf.Convert( MTF_CONVERSION_1BIT_THRESHOLD ); break; case( GRAPHICDRAWMODE_GREYS ): rMtf.Convert( MTF_CONVERSION_8BIT_GREYS ); break; case( GRAPHICDRAWMODE_WATERMARK ): { aAttr.SetLuminance( aAttr.GetLuminance() + WATERMARK_LUM_OFFSET ); aAttr.SetContrast( aAttr.GetContrast() + WATERMARK_CON_OFFSET ); } break; default: break; } } if( ( nAdjustmentFlags & ADJUSTMENT_COLORS ) && aAttr.IsAdjusted() ) { rMtf.Adjust( aAttr.GetLuminance(), aAttr.GetContrast(), aAttr.GetChannelR(), aAttr.GetChannelG(), aAttr.GetChannelB(), aAttr.GetGamma(), aAttr.IsInvert() ); } if( ( nAdjustmentFlags & ADJUSTMENT_MIRROR ) && aAttr.IsMirrored() ) { rMtf.Mirror( aAttr.GetMirrorFlags() ); } if( ( nAdjustmentFlags & ADJUSTMENT_ROTATE ) && aAttr.IsRotated() ) { rMtf.Rotate( aAttr.GetRotation() ); } if( ( nAdjustmentFlags & ADJUSTMENT_TRANSPARENCY ) && aAttr.IsTransparent() ) { OSL_FAIL( "Missing implementation: Mtf-Transparency" ); } } // ----------------------------------------------------------------------------- void GraphicManager::ImplAdjust( Animation& rAnimation, const GraphicAttr& rAttr, sal_uLong nAdjustmentFlags ) { GraphicAttr aAttr( rAttr ); if( ( nAdjustmentFlags & ADJUSTMENT_DRAWMODE ) && aAttr.IsSpecialDrawMode() ) { switch( aAttr.GetDrawMode() ) { case( GRAPHICDRAWMODE_MONO ): rAnimation.Convert( BMP_CONVERSION_1BIT_THRESHOLD ); break; case( GRAPHICDRAWMODE_GREYS ): rAnimation.Convert( BMP_CONVERSION_8BIT_GREYS ); break; case( GRAPHICDRAWMODE_WATERMARK ): { aAttr.SetLuminance( aAttr.GetLuminance() + WATERMARK_LUM_OFFSET ); aAttr.SetContrast( aAttr.GetContrast() + WATERMARK_CON_OFFSET ); } break; default: break; } } if( ( nAdjustmentFlags & ADJUSTMENT_COLORS ) && aAttr.IsAdjusted() ) { rAnimation.Adjust( aAttr.GetLuminance(), aAttr.GetContrast(), aAttr.GetChannelR(), aAttr.GetChannelG(), aAttr.GetChannelB(), aAttr.GetGamma(), aAttr.IsInvert() ); } if( ( nAdjustmentFlags & ADJUSTMENT_MIRROR ) && aAttr.IsMirrored() ) { rAnimation.Mirror( aAttr.GetMirrorFlags() ); } if( ( nAdjustmentFlags & ADJUSTMENT_ROTATE ) && aAttr.IsRotated() ) { OSL_FAIL( "Missing implementation: Animation-Rotation" ); } if( ( nAdjustmentFlags & ADJUSTMENT_TRANSPARENCY ) && aAttr.IsTransparent() ) { OSL_FAIL( "Missing implementation: Animation-Transparency" ); } } // ----------------------------------------------------------------------------- void GraphicManager::ImplDraw( OutputDevice* pOut, const Point& rPt, const Size& rSz, const GDIMetaFile& rMtf, const GraphicAttr& rAttr ) { sal_uInt16 nRot10 = rAttr.GetRotation() % 3600; Point aOutPt( rPt ); Size aOutSz( rSz ); if( nRot10 ) { Polygon aPoly( Rectangle( aOutPt, aOutSz ) ); aPoly.Rotate( aOutPt, nRot10 ); const Rectangle aRotBoundRect( aPoly.GetBoundRect() ); aOutPt = aRotBoundRect.TopLeft(); aOutSz = aRotBoundRect.GetSize(); } pOut->Push( PUSH_CLIPREGION ); pOut->IntersectClipRegion( Rectangle( aOutPt, aOutSz ) ); ( (GDIMetaFile&) rMtf ).WindStart(); ( (GDIMetaFile&) rMtf ).Play( pOut, aOutPt, aOutSz ); ( (GDIMetaFile&) rMtf ).WindStart(); pOut->Pop(); } // ----------------------------------------------------------------------------- struct ImplTileInfo { ImplTileInfo() : aTileTopLeft(), aNextTileTopLeft(), aTileSizePixel(), nTilesEmptyX(0), nTilesEmptyY(0) {} Point aTileTopLeft; // top, left position of the rendered tile Point aNextTileTopLeft; // top, left position for next recursion // level's tile Size aTileSizePixel; // size of the generated tile (might // differ from // aNextTileTopLeft-aTileTopLeft, because // this is nExponent*prevTileSize. The // generated tile is always nExponent // times the previous tile, such that it // can be used in the next stage. The // required area coverage is often // less. The extraneous area covered is // later overwritten by the next stage) int nTilesEmptyX; // number of original tiles empty right of // this tile. This counts from // aNextTileTopLeft, i.e. the additional // area covered by aTileSizePixel is not // considered here. This is for // unification purposes, as the iterative // calculation of the next level's empty // tiles has to be based on this value. int nTilesEmptyY; // as above, for Y }; bool GraphicObject::ImplRenderTempTile( VirtualDevice& rVDev, int nExponent, int nNumTilesX, int nNumTilesY, const Size& rTileSizePixel, const GraphicAttr* pAttr, sal_uLong nFlags ) { if( nExponent <= 1 ) return false; // determine MSB factor int nMSBFactor( 1 ); while( nNumTilesX / nMSBFactor != 0 || nNumTilesY / nMSBFactor != 0 ) { nMSBFactor *= nExponent; } // one less nMSBFactor /= nExponent; ImplTileInfo aTileInfo; // #105229# Switch off mapping (converting to logic and back to // pixel might cause roundoff errors) sal_Bool bOldMap( rVDev.IsMapModeEnabled() ); rVDev.EnableMapMode( sal_False ); bool bRet( ImplRenderTileRecursive( rVDev, nExponent, nMSBFactor, nNumTilesX, nNumTilesY, nNumTilesX, nNumTilesY, rTileSizePixel, pAttr, nFlags, aTileInfo ) ); rVDev.EnableMapMode( bOldMap ); return bRet; } // ----------------------------------------------------------------------------- // define for debug drawings //#define DBG_TEST // see header comment. this works similar to base conversion of a // number, i.e. if the exponent is 10, then the number for every tile // size is given by the decimal place of the corresponding decimal // representation. bool GraphicObject::ImplRenderTileRecursive( VirtualDevice& rVDev, int nExponent, int nMSBFactor, int nNumOrigTilesX, int nNumOrigTilesY, int nRemainderTilesX, int nRemainderTilesY, const Size& rTileSizePixel, const GraphicAttr* pAttr, sal_uLong nFlags, ImplTileInfo& rTileInfo ) { // gets loaded with our tile bitmap GraphicObject aTmpGraphic; // stores a flag that renders the zero'th tile position // (i.e. (0,0)+rCurrPos) only if we're at the bottom of the // recursion stack. All other position already have that tile // rendered, because the lower levels painted their generated tile // there. bool bNoFirstTileDraw( false ); // what's left when we're done with our tile size const int nNewRemainderX( nRemainderTilesX % nMSBFactor ); const int nNewRemainderY( nRemainderTilesY % nMSBFactor ); // gets filled out from the recursive call with info of what's // been generated ImplTileInfo aTileInfo; // current output position while drawing Point aCurrPos; int nX, nY; // check for recursion's end condition: LSB place reached? if( nMSBFactor == 1 ) { aTmpGraphic = *this; // set initial tile size -> orig size aTileInfo.aTileSizePixel = rTileSizePixel; aTileInfo.nTilesEmptyX = nNumOrigTilesX; aTileInfo.nTilesEmptyY = nNumOrigTilesY; } else if( ImplRenderTileRecursive( rVDev, nExponent, nMSBFactor/nExponent, nNumOrigTilesX, nNumOrigTilesY, nNewRemainderX, nNewRemainderY, rTileSizePixel, pAttr, nFlags, aTileInfo ) ) { // extract generated tile -> see comment on the first loop below BitmapEx aTileBitmap( rVDev.GetBitmap( aTileInfo.aTileTopLeft, aTileInfo.aTileSizePixel ) ); aTmpGraphic = GraphicObject( aTileBitmap ); // fill stripes left over from upstream levels: // // x0000 // 0 // 0 // 0 // 0 // // where x denotes the place filled by our recursive predecessors // check whether we have to fill stripes here. Although not // obvious, there is one case where we can skip this step: if // the previous recursion level (the one who filled our // aTileInfo) had zero area to fill, then there are no white // stripes left, naturally. This happens if the digit // associated to that level has a zero, and can be checked via // aTileTopLeft==aNextTileTopLeft. if( aTileInfo.aTileTopLeft != aTileInfo.aNextTileTopLeft ) { // now fill one row from aTileInfo.aNextTileTopLeft.X() all // the way to the right aCurrPos.X() = aTileInfo.aNextTileTopLeft.X(); aCurrPos.Y() = aTileInfo.aTileTopLeft.Y(); for( nX=0; nX < aTileInfo.nTilesEmptyX; nX += nMSBFactor ) { if( !aTmpGraphic.Draw( &rVDev, aCurrPos, aTileInfo.aTileSizePixel, pAttr, nFlags ) ) return false; aCurrPos.X() += aTileInfo.aTileSizePixel.Width(); } #ifdef DBG_TEST // rVDev.SetFillColor( COL_WHITE ); rVDev.SetFillColor(); rVDev.SetLineColor( Color( 255 * nExponent / nMSBFactor, 255 - 255 * nExponent / nMSBFactor, 128 - 255 * nExponent / nMSBFactor ) ); rVDev.DrawEllipse( Rectangle(aTileInfo.aNextTileTopLeft.X(), aTileInfo.aTileTopLeft.Y(), aTileInfo.aNextTileTopLeft.X() - 1 + (aTileInfo.nTilesEmptyX/nMSBFactor)*aTileInfo.aTileSizePixel.Width(), aTileInfo.aTileTopLeft.Y() + aTileInfo.aTileSizePixel.Height() - 1) ); #endif // now fill one column from aTileInfo.aNextTileTopLeft.Y() all // the way to the bottom aCurrPos.X() = aTileInfo.aTileTopLeft.X(); aCurrPos.Y() = aTileInfo.aNextTileTopLeft.Y(); for( nY=0; nY < aTileInfo.nTilesEmptyY; nY += nMSBFactor ) { if( !aTmpGraphic.Draw( &rVDev, aCurrPos, aTileInfo.aTileSizePixel, pAttr, nFlags ) ) return false; aCurrPos.Y() += aTileInfo.aTileSizePixel.Height(); } #ifdef DBG_TEST rVDev.DrawEllipse( Rectangle(aTileInfo.aTileTopLeft.X(), aTileInfo.aNextTileTopLeft.Y(), aTileInfo.aTileTopLeft.X() + aTileInfo.aTileSizePixel.Width() - 1, aTileInfo.aNextTileTopLeft.Y() - 1 + (aTileInfo.nTilesEmptyY/nMSBFactor)*aTileInfo.aTileSizePixel.Height()) ); #endif } else { // Thought that aTileInfo.aNextTileTopLeft tile has always // been drawn already, but that's wrong: typically, // _parts_ of that tile have been drawn, since the // previous level generated the tile there. But when // aTileInfo.aNextTileTopLeft!=aTileInfo.aTileTopLeft, the // difference between these two values is missing in the // lower right corner of this first tile. So, can do that // only here. bNoFirstTileDraw = true; } } else { return false; } // calc number of original tiles in our drawing area without // remainder nRemainderTilesX -= nNewRemainderX; nRemainderTilesY -= nNewRemainderY; // fill tile info for calling method rTileInfo.aTileTopLeft = aTileInfo.aNextTileTopLeft; rTileInfo.aNextTileTopLeft = Point( rTileInfo.aTileTopLeft.X() + rTileSizePixel.Width()*nRemainderTilesX, rTileInfo.aTileTopLeft.Y() + rTileSizePixel.Height()*nRemainderTilesY ); rTileInfo.aTileSizePixel = Size( rTileSizePixel.Width()*nMSBFactor*nExponent, rTileSizePixel.Height()*nMSBFactor*nExponent ); rTileInfo.nTilesEmptyX = aTileInfo.nTilesEmptyX - nRemainderTilesX; rTileInfo.nTilesEmptyY = aTileInfo.nTilesEmptyY - nRemainderTilesY; // init output position aCurrPos = aTileInfo.aNextTileTopLeft; // fill our drawing area. Fill possibly more, to create the next // bigger tile size -> see bitmap extraction above. This does no // harm, since everything right or below our actual area is // overdrawn by our caller. Just in case we're in the last level, // we don't draw beyond the right or bottom border. for( nY=0; nY < aTileInfo.nTilesEmptyY && nY < nExponent*nMSBFactor; nY += nMSBFactor ) { aCurrPos.X() = aTileInfo.aNextTileTopLeft.X(); for( nX=0; nX < aTileInfo.nTilesEmptyX && nX < nExponent*nMSBFactor; nX += nMSBFactor ) { if( bNoFirstTileDraw ) bNoFirstTileDraw = false; // don't draw first tile position else if( !aTmpGraphic.Draw( &rVDev, aCurrPos, aTileInfo.aTileSizePixel, pAttr, nFlags ) ) return false; aCurrPos.X() += aTileInfo.aTileSizePixel.Width(); } aCurrPos.Y() += aTileInfo.aTileSizePixel.Height(); } #ifdef DBG_TEST // rVDev.SetFillColor( COL_WHITE ); rVDev.SetFillColor(); rVDev.SetLineColor( Color( 255 * nExponent / nMSBFactor, 255 - 255 * nExponent / nMSBFactor, 128 - 255 * nExponent / nMSBFactor ) ); rVDev.DrawRect( Rectangle((rTileInfo.aTileTopLeft.X())*rTileSizePixel.Width(), (rTileInfo.aTileTopLeft.Y())*rTileSizePixel.Height(), (rTileInfo.aNextTileTopLeft.X())*rTileSizePixel.Width()-1, (rTileInfo.aNextTileTopLeft.Y())*rTileSizePixel.Height()-1) ); #endif return true; } // ----------------------------------------------------------------------------- bool GraphicObject::ImplDrawTiled( OutputDevice* pOut, const Rectangle& rArea, const Size& rSizePixel, const Size& rOffset, const GraphicAttr* pAttr, sal_uLong nFlags, int nTileCacheSize1D ) { // how many tiles to generate per recursion step enum{ SubdivisionExponent=2 }; const MapMode aOutMapMode( pOut->GetMapMode() ); const MapMode aMapMode( aOutMapMode.GetMapUnit(), Point(), aOutMapMode.GetScaleX(), aOutMapMode.GetScaleY() ); bool bRet( false ); // #i42643# Casting to Int64, to avoid integer overflow for // huge-DPI output devices if( GetGraphic().GetType() == GRAPHIC_BITMAP && static_cast(rSizePixel.Width()) * rSizePixel.Height() < static_cast(nTileCacheSize1D)*nTileCacheSize1D ) { // First combine very small bitmaps into a larger tile // =================================================== VirtualDevice aVDev; const int nNumTilesInCacheX( (nTileCacheSize1D + rSizePixel.Width()-1) / rSizePixel.Width() ); const int nNumTilesInCacheY( (nTileCacheSize1D + rSizePixel.Height()-1) / rSizePixel.Height() ); aVDev.SetOutputSizePixel( Size( nNumTilesInCacheX*rSizePixel.Width(), nNumTilesInCacheY*rSizePixel.Height() ) ); aVDev.SetMapMode( aMapMode ); // draw bitmap content if( ImplRenderTempTile( aVDev, SubdivisionExponent, nNumTilesInCacheX, nNumTilesInCacheY, rSizePixel, pAttr, nFlags ) ) { BitmapEx aTileBitmap( aVDev.GetBitmap( Point(0,0), aVDev.GetOutputSize() ) ); // draw alpha content, if any if( IsTransparent() ) { GraphicObject aAlphaGraphic; if( GetGraphic().IsAlpha() ) aAlphaGraphic.SetGraphic( GetGraphic().GetBitmapEx().GetAlpha().GetBitmap() ); else aAlphaGraphic.SetGraphic( GetGraphic().GetBitmapEx().GetMask() ); if( aAlphaGraphic.ImplRenderTempTile( aVDev, SubdivisionExponent, nNumTilesInCacheX, nNumTilesInCacheY, rSizePixel, pAttr, nFlags ) ) { // Combine bitmap and alpha/mask if( GetGraphic().IsAlpha() ) aTileBitmap = BitmapEx( aTileBitmap.GetBitmap(), AlphaMask( aVDev.GetBitmap( Point(0,0), aVDev.GetOutputSize() ) ) ); else aTileBitmap = BitmapEx( aTileBitmap.GetBitmap(), aVDev.GetBitmap( Point(0,0), aVDev.GetOutputSize() ).CreateMask( Color(COL_WHITE) ) ); } } // paint generated tile GraphicObject aTmpGraphic( aTileBitmap ); bRet = aTmpGraphic.ImplDrawTiled( pOut, rArea, aTileBitmap.GetSizePixel(), rOffset, pAttr, nFlags, nTileCacheSize1D ); } } else { const Size aOutOffset( pOut->LogicToPixel( rOffset, aOutMapMode ) ); const Rectangle aOutArea( pOut->LogicToPixel( rArea, aOutMapMode ) ); // number of invisible (because out-of-area) tiles int nInvisibleTilesX; int nInvisibleTilesY; // round towards -infty for negative offset if( aOutOffset.Width() < 0 ) nInvisibleTilesX = (aOutOffset.Width() - rSizePixel.Width() + 1) / rSizePixel.Width(); else nInvisibleTilesX = aOutOffset.Width() / rSizePixel.Width(); // round towards -infty for negative offset if( aOutOffset.Height() < 0 ) nInvisibleTilesY = (aOutOffset.Height() - rSizePixel.Height() + 1) / rSizePixel.Height(); else nInvisibleTilesY = aOutOffset.Height() / rSizePixel.Height(); // origin from where to 'virtually' start drawing in pixel const Point aOutOrigin( pOut->LogicToPixel( Point( rArea.Left() - rOffset.Width(), rArea.Top() - rOffset.Height() ) ) ); // position in pixel from where to really start output const Point aOutStart( aOutOrigin.X() + nInvisibleTilesX*rSizePixel.Width(), aOutOrigin.Y() + nInvisibleTilesY*rSizePixel.Height() ); pOut->Push( PUSH_CLIPREGION ); pOut->IntersectClipRegion( rArea ); // Paint all tiles // =============== bRet = ImplDrawTiled( *pOut, aOutStart, (aOutArea.GetWidth() + aOutArea.Left() - aOutStart.X() + rSizePixel.Width() - 1) / rSizePixel.Width(), (aOutArea.GetHeight() + aOutArea.Top() - aOutStart.Y() + rSizePixel.Height() - 1) / rSizePixel.Height(), rSizePixel, pAttr, nFlags ); pOut->Pop(); } return bRet; } // ----------------------------------------------------------------------------- bool GraphicObject::ImplDrawTiled( OutputDevice& rOut, const Point& rPosPixel, int nNumTilesX, int nNumTilesY, const Size& rTileSizePixel, const GraphicAttr* pAttr, sal_uLong nFlags ) { Point aCurrPos( rPosPixel ); Size aTileSizeLogic( rOut.PixelToLogic( rTileSizePixel ) ); int nX, nY; // #107607# Use logical coordinates for metafile playing, too bool bDrawInPixel( rOut.GetConnectMetaFile() == NULL && GRAPHIC_BITMAP == GetType() ); sal_Bool bRet( sal_False ); // #105229# Switch off mapping (converting to logic and back to // pixel might cause roundoff errors) sal_Bool bOldMap( rOut.IsMapModeEnabled() ); if( bDrawInPixel ) rOut.EnableMapMode( sal_False ); for( nY=0; nY < nNumTilesY; ++nY ) { aCurrPos.X() = rPosPixel.X(); for( nX=0; nX < nNumTilesX; ++nX ) { // #105229# work with pixel coordinates here, mapping is disabled! // #104004# don't disable mapping for metafile recordings // #108412# don't quit the loop if one draw fails // update return value. This method should return true, if // at least one of the looped Draws succeeded. bRet |= Draw( &rOut, bDrawInPixel ? aCurrPos : rOut.PixelToLogic( aCurrPos ), bDrawInPixel ? rTileSizePixel : aTileSizeLogic, pAttr, nFlags ); aCurrPos.X() += rTileSizePixel.Width(); } aCurrPos.Y() += rTileSizePixel.Height(); } if( bDrawInPixel ) rOut.EnableMapMode( bOldMap ); return bRet; } // ----------------------------------------------------------------------------- void GraphicObject::ImplTransformBitmap( BitmapEx& rBmpEx, const GraphicAttr& rAttr, const Size& rCropLeftTop, const Size& rCropRightBottom, const Rectangle& rCropRect, const Size& rDstSize, sal_Bool bEnlarge ) const { // #107947# Extracted from svdograf.cxx // #104115# Crop the bitmap if( rAttr.IsCropped() ) { rBmpEx.Crop( rCropRect ); // #104115# Negative crop sizes mean: enlarge bitmap and pad if( bEnlarge && ( rCropLeftTop.Width() < 0 || rCropLeftTop.Height() < 0 || rCropRightBottom.Width() < 0 || rCropRightBottom.Height() < 0 ) ) { Size aBmpSize( rBmpEx.GetSizePixel() ); sal_Int32 nPadLeft( rCropLeftTop.Width() < 0 ? -rCropLeftTop.Width() : 0 ); sal_Int32 nPadTop( rCropLeftTop.Height() < 0 ? -rCropLeftTop.Height() : 0 ); sal_Int32 nPadTotalWidth( aBmpSize.Width() + nPadLeft + (rCropRightBottom.Width() < 0 ? -rCropRightBottom.Width() : 0) ); sal_Int32 nPadTotalHeight( aBmpSize.Height() + nPadTop + (rCropRightBottom.Height() < 0 ? -rCropRightBottom.Height() : 0) ); BitmapEx aBmpEx2; if( rBmpEx.IsTransparent() ) { if( rBmpEx.IsAlpha() ) aBmpEx2 = BitmapEx( rBmpEx.GetBitmap(), rBmpEx.GetAlpha() ); else aBmpEx2 = BitmapEx( rBmpEx.GetBitmap(), rBmpEx.GetMask() ); } else { // #104115# Generate mask bitmap and init to zero Bitmap aMask( aBmpSize, 1 ); aMask.Erase( Color(0,0,0) ); // #104115# Always generate transparent bitmap, we need the border transparent aBmpEx2 = BitmapEx( rBmpEx.GetBitmap(), aMask ); // #104115# Add opaque mask to source bitmap, otherwise the destination remains transparent rBmpEx = aBmpEx2; } aBmpEx2.SetSizePixel( Size(nPadTotalWidth, nPadTotalHeight) ); aBmpEx2.Erase( Color(0xFF,0,0,0) ); aBmpEx2.CopyPixel( Rectangle( Point(nPadLeft, nPadTop), aBmpSize ), Rectangle( Point(0, 0), aBmpSize ), &rBmpEx ); rBmpEx = aBmpEx2; } } const Size aSizePixel( rBmpEx.GetSizePixel() ); if( rAttr.GetRotation() != 0 && !IsAnimated() ) { if( aSizePixel.Width() && aSizePixel.Height() && rDstSize.Width() && rDstSize.Height() ) { double fSrcWH = (double) aSizePixel.Width() / aSizePixel.Height(); double fDstWH = (double) rDstSize.Width() / rDstSize.Height(); double fScaleX = 1.0, fScaleY = 1.0; // always choose scaling to shrink bitmap if( fSrcWH < fDstWH ) fScaleY = aSizePixel.Width() / ( fDstWH * aSizePixel.Height() ); else fScaleX = fDstWH * aSizePixel.Height() / aSizePixel.Width(); rBmpEx.Scale( fScaleX, fScaleY ); } } } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */