1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
21 #include <sal/log.hxx>
28 #include <com/sun/star/configuration/theDefaultProvider.hpp>
29 #include <com/sun/star/container/XHierarchicalNameAccess.hpp>
30 #include <com/sun/star/container/XNameAccess.hpp>
31 #include <com/sun/star/lang/IllegalArgumentException.hpp>
32 #include <com/sun/star/lang/XServiceInfo.hpp>
33 #include <com/sun/star/lang/XServiceName.hpp>
34 #include <com/sun/star/uno/XComponentContext.hpp>
35 #include <comphelper/propertysequence.hxx>
36 #include <cppuhelper/implbase.hxx>
37 #include <cppuhelper/supportsservice.hxx>
38 #include <o3tl/functional.hxx>
39 #include <o3tl/string_view.hxx>
40 #include <vcl/skia/SkiaHelper.hxx>
41 #include <comphelper/configuration.hxx>
42 #include <officecfg/Office/Canvas.hxx>
44 using namespace ::com::sun::star
;
45 using namespace ::com::sun::star::uno
;
52 : public ::cppu::WeakImplHelper
< lang::XServiceInfo
,
53 lang::XMultiComponentFactory
,
54 lang::XMultiServiceFactory
>
56 typedef std::pair
< OUString
, Sequence
< OUString
> > AvailPair
;
57 typedef std::pair
< OUString
, OUString
> CachePair
;
58 typedef std::vector
< AvailPair
> AvailVector
;
59 typedef std::vector
< CachePair
> CacheVector
;
62 mutable std::mutex m_mutex
;
63 Reference
<XComponentContext
> m_xContext
;
64 AvailVector m_aAvailableImplementations
;
65 AvailVector m_aAcceleratedImplementations
;
66 AvailVector m_aAAImplementations
;
67 mutable CacheVector m_aCachedImplementations
;
68 mutable bool m_bCacheHasForcedLastImpl
;
69 mutable bool m_bCacheHasUseAcceleratedEntry
;
70 mutable bool m_bCacheHasUseAAEntry
;
72 void checkConfigFlag( bool& r_bFlag
,
74 bool bCurrentConfigValue
) const;
75 Reference
<XInterface
> use(
76 OUString
const & serviceName
,
77 Sequence
<Any
> const & args
,
78 Reference
<XComponentContext
> const & xContext
) const;
79 Reference
<XInterface
> lookupAndUse(
80 OUString
const & serviceName
, Sequence
<Any
> const & args
,
81 Reference
<XComponentContext
> const & xContext
) const;
84 virtual ~CanvasFactory() override
;
85 explicit CanvasFactory( Reference
<XComponentContext
> const & xContext
);
88 virtual OUString SAL_CALL
getImplementationName() override
;
89 virtual sal_Bool SAL_CALL
supportsService( OUString
const & serviceName
) override
;
90 virtual Sequence
<OUString
> SAL_CALL
getSupportedServiceNames() override
;
92 // XMultiComponentFactory
93 virtual Sequence
<OUString
> SAL_CALL
getAvailableServiceNames() override
;
94 virtual Reference
<XInterface
> SAL_CALL
createInstanceWithContext(
95 OUString
const & name
,
96 Reference
<XComponentContext
> const & xContext
) override
;
97 virtual Reference
<XInterface
> SAL_CALL
98 createInstanceWithArgumentsAndContext(
99 OUString
const & name
,
100 Sequence
<Any
> const & args
,
101 Reference
<XComponentContext
> const & xContext
) override
;
103 // XMultiServiceFactory
104 virtual Reference
<XInterface
> SAL_CALL
createInstance(
105 OUString
const & name
) override
;
106 virtual Reference
<XInterface
> SAL_CALL
createInstanceWithArguments(
107 OUString
const & name
, Sequence
<Any
> const & args
) override
;
110 CanvasFactory::CanvasFactory( Reference
<XComponentContext
> const & xContext
) :
111 m_xContext(xContext
),
112 m_bCacheHasForcedLastImpl(),
113 m_bCacheHasUseAcceleratedEntry(),
114 m_bCacheHasUseAAEntry()
116 if (!comphelper::IsFuzzing())
120 // read out configuration for preferred services:
122 Reference
<container::XNameAccess
> xNameAccess
= officecfg::Office::Canvas::CanvasServiceList::get();
123 Reference
<container::XHierarchicalNameAccess
> xHierarchicalNameAccess(
124 xNameAccess
, UNO_QUERY_THROW
);
127 for (auto& serviceName
: xNameAccess
->getElementNames())
129 Reference
<container::XNameAccess
> xEntryNameAccess(
130 xHierarchicalNameAccess
->getByHierarchicalName(serviceName
),
133 if( xEntryNameAccess
.is() )
135 Sequence
<OUString
> implementationList
;
136 if( xEntryNameAccess
->getByName(u
"PreferredImplementations"_ustr
) >>= implementationList
)
138 m_aAvailableImplementations
.emplace_back(serviceName
, implementationList
);
140 if( xEntryNameAccess
->getByName(u
"AcceleratedImplementations"_ustr
) >>= implementationList
)
142 m_aAcceleratedImplementations
.emplace_back(serviceName
, implementationList
);
144 if( xEntryNameAccess
->getByName(u
"AntialiasingImplementations"_ustr
) >>= implementationList
)
146 m_aAAImplementations
.emplace_back(serviceName
, implementationList
);
151 catch (const RuntimeException
&)
155 catch (const Exception
&)
160 if (m_aAvailableImplementations
.empty())
162 // Ugh. Looks like configuration is borked. Fake minimal
164 m_aAvailableImplementations
.emplace_back(u
"com.sun.star.rendering.Canvas"_ustr
,
165 Sequence
<OUString
>{ u
"com.sun.star.comp.rendering.Canvas.VCL"_ustr
} );
167 m_aAvailableImplementations
.emplace_back(u
"com.sun.star.rendering.SpriteCanvas"_ustr
,
168 Sequence
<OUString
>{ u
"com.sun.star.comp.rendering.SpriteCanvas.VCL"_ustr
} );
172 CanvasFactory::~CanvasFactory()
178 OUString
CanvasFactory::getImplementationName()
180 return u
"com.sun.star.comp.rendering.CanvasFactory"_ustr
;
183 sal_Bool
CanvasFactory::supportsService( OUString
const & serviceName
)
185 return cppu::supportsService(this, serviceName
);
188 Sequence
<OUString
> CanvasFactory::getSupportedServiceNames()
190 return { u
"com.sun.star.rendering.CanvasFactory"_ustr
};
193 // XMultiComponentFactory
194 Sequence
<OUString
> CanvasFactory::getAvailableServiceNames()
196 Sequence
<OUString
> aServiceNames(m_aAvailableImplementations
.size());
197 std::transform(m_aAvailableImplementations
.begin(),
198 m_aAvailableImplementations
.end(),
199 aServiceNames
.getArray(),
200 o3tl::select1st
< AvailPair
>());
201 return aServiceNames
;
204 Reference
<XInterface
> CanvasFactory::createInstanceWithContext(
205 OUString
const & name
, Reference
<XComponentContext
> const & xContext
)
207 return createInstanceWithArgumentsAndContext(
208 name
, Sequence
<Any
>(), xContext
);
212 Reference
<XInterface
> CanvasFactory::use(
213 OUString
const & serviceName
,
214 Sequence
<Any
> const & args
,
215 Reference
<XComponentContext
> const & xContext
) const
218 return m_xContext
->getServiceManager()->createInstanceWithArgumentsAndContext(
219 serviceName
, args
, xContext
);
221 catch (css::lang::IllegalArgumentException
&)
223 return Reference
<XInterface
>();
225 catch (const RuntimeException
&)
229 catch (const Exception
&)
231 return Reference
<XInterface
>();
236 void CanvasFactory::checkConfigFlag( bool& r_bFlag
,
238 bool bCurrentConfigValue
) const
240 r_bFlag
= bCurrentConfigValue
;
242 if( r_CacheFlag
!= r_bFlag
)
244 // cache is invalid, because of different order of
246 r_CacheFlag
= r_bFlag
;
247 m_aCachedImplementations
.clear();
252 Reference
<XInterface
> CanvasFactory::lookupAndUse(
253 OUString
const & serviceName
, Sequence
<Any
> const & args
,
254 Reference
<XComponentContext
> const & xContext
) const
256 std::scoped_lock
guard(m_mutex
);
258 // forcing last entry from impl list, if config flag set
259 bool bForceLastEntry(false);
260 checkConfigFlag( bForceLastEntry
,
261 m_bCacheHasForcedLastImpl
,
262 officecfg::Office::Canvas::ForceSafeServiceImpl::get() );
264 // use anti-aliasing canvas, if config flag set (or not existing)
265 bool bUseAAEntry(true);
266 checkConfigFlag( bUseAAEntry
,
267 m_bCacheHasUseAAEntry
,
268 officecfg::Office::Canvas::UseAntialiasingCanvas::get() );
270 // use accelerated canvas, if config flag set (or not existing)
271 bool bUseAcceleratedEntry(true);
272 checkConfigFlag( bUseAcceleratedEntry
,
273 m_bCacheHasUseAcceleratedEntry
,
274 officecfg::Office::Canvas::UseAcceleratedCanvas::get() );
276 // try to reuse last working implementation for given service name
277 const CacheVector::iterator
aEnd(m_aCachedImplementations
.end());
278 auto aMatch
= std::find_if(
279 m_aCachedImplementations
.begin(),
281 [&serviceName
](CachePair
const& cp
)
282 { return serviceName
== cp
.first
; }
284 if( aMatch
!= aEnd
) {
285 Reference
<XInterface
> xCanvas( use( aMatch
->second
, args
, xContext
) );
290 // lookup in available service list
291 const AvailVector::const_iterator
aAvailEnd(m_aAvailableImplementations
.end());
292 auto aAvailImplsMatch
= std::find_if(
293 m_aAvailableImplementations
.begin(),
295 [&serviceName
](AvailPair
const& ap
)
296 { return serviceName
== ap
.first
; }
298 if( aAvailImplsMatch
== aAvailEnd
) {
299 return Reference
<XInterface
>();
302 const AvailVector::const_iterator
aAAEnd(m_aAAImplementations
.end());
303 auto aAAImplsMatch
= std::find_if(
304 m_aAAImplementations
.begin(),
306 [&serviceName
](AvailPair
const& ap
)
307 { return serviceName
== ap
.first
; }
309 if( aAAImplsMatch
== aAAEnd
) {
310 return Reference
<XInterface
>();
313 const AvailVector::const_iterator
aAccelEnd(m_aAcceleratedImplementations
.end());
314 auto aAccelImplsMatch
= std::find_if(
315 m_aAcceleratedImplementations
.begin(),
317 [&serviceName
](AvailPair
const& ap
)
318 { return serviceName
== ap
.first
; }
320 if( aAccelImplsMatch
== aAccelEnd
) {
321 return Reference
<XInterface
>();
324 const Sequence
<OUString
> aPreferredImpls( aAvailImplsMatch
->second
);
325 const OUString
* pCurrImpl
= aPreferredImpls
.begin();
326 const OUString
* const pEndImpl
= aPreferredImpls
.end();
328 const Sequence
<OUString
> aAAImpls( aAAImplsMatch
->second
);
330 const Sequence
<OUString
> aAccelImpls( aAccelImplsMatch
->second
);
332 // force last entry from impl list, if config flag set
333 if (bForceLastEntry
&& pCurrImpl
!= pEndImpl
)
334 pCurrImpl
= pEndImpl
-1;
336 for(; pCurrImpl
!= pEndImpl
; ++pCurrImpl
)
338 const OUString
aCurrName(pCurrImpl
->trim());
340 // Skia works only with vclcanvas.
341 if( SkiaHelper::isVCLSkiaEnabled() && !aCurrName
.endsWith(".VCL"))
344 // check whether given canvas service is listed in the
345 // sequence of "accelerated canvas implementations"
346 const bool bIsAcceleratedImpl(
347 std::any_of(aAccelImpls
.begin(), aAccelImpls
.end(),
348 [&aCurrName
](OUString
const& src
)
349 { return aCurrName
== o3tl::trim(src
); }
352 // check whether given canvas service is listed in the
353 // sequence of "antialiasing canvas implementations"
354 const bool bIsAAImpl(
355 std::any_of(aAAImpls
.begin(), aAAImpls
.end(),
356 [&aCurrName
](OUString
const& src
)
357 { return aCurrName
== o3tl::trim(src
); }
360 // try to instantiate canvas *only* if either accel and AA
361 // property match preference, *or*, if there's a mismatch, only
362 // go for a less capable canvas (that effectively let those
363 // pour canvas impls still work as fallbacks, should an
364 // accelerated/AA one fail). Property implies configuration:
365 // http://en.wikipedia.org/wiki/Truth_table#Logical_implication
366 if( (!bIsAAImpl
|| bUseAAEntry
) && (!bIsAcceleratedImpl
|| bUseAcceleratedEntry
) )
368 Reference
<XInterface
> xCanvas(use(aCurrName
, args
, xContext
));
374 // cache entry exists, replace dysfunctional
375 // implementation name
376 aMatch
->second
= aCurrName
;
380 // new service name, add new cache entry
381 m_aCachedImplementations
.emplace_back(serviceName
, aCurrName
);
389 return Reference
<XInterface
>();
393 Reference
<XInterface
> CanvasFactory::createInstanceWithArgumentsAndContext(
394 OUString
const & preferredOne
, Sequence
<Any
> const & args
,
395 Reference
<XComponentContext
> const & xContext
)
397 Reference
<XInterface
> xCanvas(lookupAndUse(preferredOne
, args
, xContext
));
399 // last resort: try service name directly
400 xCanvas
= use(preferredOne
, args
, xContext
);
404 Reference
<lang::XServiceName
> xServiceName(xCanvas
, uno::UNO_QUERY
);
405 SAL_INFO("canvas", "using " << (xServiceName
.is() ? xServiceName
->getServiceName()
406 : u
"(unknown)"_ustr
));
411 // XMultiServiceFactory
413 Reference
<XInterface
> CanvasFactory::createInstance( OUString
const & name
)
415 return createInstanceWithArgumentsAndContext(
416 name
, Sequence
<Any
>(), m_xContext
);
420 Reference
<XInterface
> CanvasFactory::createInstanceWithArguments(
421 OUString
const & name
, Sequence
<Any
> const & args
)
423 return createInstanceWithArgumentsAndContext(
424 name
, args
, m_xContext
);
430 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
431 com_sun_star_comp_rendering_CanvasFactory_get_implementation(css::uno::XComponentContext
* context
,
432 css::uno::Sequence
<css::uno::Any
> const &)
434 return cppu::acquire(new CanvasFactory(context
));
438 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */