vcl: parallelize JPEG import from ImportGraphics()

Just the expensive, pixel reading part, not the bitmap creation, which
isn't thread-safe.

Change-Id: I3723d80aa29ee5f2e604800cb2061c59b0b0324d
Reviewed-on: https://gerrit.libreoffice.org/37978
Tested-by: Jenkins <ci@libreoffice.org>
Reviewed-by: Miklos Vajna <vmiklos@collabora.co.uk>
diff --git a/vcl/README.vars b/vcl/README.vars
index e97cf0d..e5b6f7b 100644
--- a/vcl/README.vars
+++ b/vcl/README.vars
@@ -20,6 +20,7 @@ VCL_DEBUG_DISABLE_PDFCOMPRESSION - disable compression in the PDF writer
Bitmap
------
VCL_NO_THREAD_SCALE - disable threaded bitmap scale
VCL_NO_THREAD_IMPORT - disable threaded bitmap import
EMF_PLUS_DISABLE - use EMF rendering and ignore EMF+ specifics

OpenGL
diff --git a/vcl/source/filter/graphicfilter.cxx b/vcl/source/filter/graphicfilter.cxx
index c5bab5c..d7eb5d7 100644
--- a/vcl/source/filter/graphicfilter.cxx
+++ b/vcl/source/filter/graphicfilter.cxx
@@ -23,6 +23,7 @@
#include <osl/mutex.hxx>
#include <comphelper/processfactory.hxx>
#include <comphelper/string.hxx>
#include <comphelper/threadpool.hxx>
#include <ucbhelper/content.hxx>
#include <cppuhelper/implbase.hxx>
#include <tools/fract.hxx>
@@ -1336,10 +1337,44 @@ struct GraphicImportContext
    GraphicFilterImportFlags m_nImportFlags = GraphicFilterImportFlags::NONE;
};

/// Graphic import worker that gets executed on a thread.
class GraphicImportTask : public comphelper::ThreadTask
{
    GraphicImportContext& m_rContext;
public:
    GraphicImportTask(const std::shared_ptr<comphelper::ThreadTaskTag>& pTag, GraphicImportContext& rContext);
    void doWork() override;
    /// Shared code between threaded and non-threaded version.
    static void doImport(GraphicImportContext& rContext);
};

GraphicImportTask::GraphicImportTask(const std::shared_ptr<comphelper::ThreadTaskTag>& pTag, GraphicImportContext& rContext)
    : comphelper::ThreadTask(pTag),
      m_rContext(rContext)
{
}

void GraphicImportTask::doWork()
{
    GraphicImportTask::doImport(m_rContext);
}

void GraphicImportTask::doImport(GraphicImportContext& rContext)
{
    if (!ImportJPEG(*rContext.m_pStream, *rContext.m_pGraphic, rContext.m_nImportFlags | GraphicFilterImportFlags::UseExistingBitmap, rContext.m_pAccess.get()))
        rContext.m_nStatus = GRFILTER_FILTERERROR;
    else
        rContext.m_eLinkType = GfxLinkType::NativeJpg;
}

void GraphicFilter::ImportGraphics(std::vector< std::shared_ptr<Graphic> >& rGraphics, const std::vector< std::shared_ptr<SvStream> >& rStreams)
{
    static bool bThreads = !getenv("VCL_NO_THREAD_IMPORT");
    std::vector<GraphicImportContext> aContexts;
    aContexts.reserve(rStreams.size());
    comphelper::ThreadPool& rSharedPool = comphelper::ThreadPool::getSharedOptimalPool();
    std::shared_ptr<comphelper::ThreadTaskTag> pTag = comphelper::ThreadPool::createThreadTaskTag();

    for (const auto& pStream : rStreams)
    {
        aContexts.push_back(GraphicImportContext());
@@ -1374,10 +1409,10 @@ void GraphicFilter::ImportGraphics(std::vector< std::shared_ptr<Graphic> >& rGra
                        Bitmap& rBitmap = const_cast<Bitmap&>(rContext.m_pGraphic->GetBitmapExRef().GetBitmapRef());
                        rContext.m_pAccess = o3tl::make_unique<Bitmap::ScopedWriteAccess>(rBitmap);
                        pStream->Seek(rContext.m_nStreamBegin);
                        if (!ImportJPEG(*pStream, *rContext.m_pGraphic, rContext.m_nImportFlags | GraphicFilterImportFlags::UseExistingBitmap, rContext.m_pAccess.get()))
                            rContext.m_nStatus = GRFILTER_FILTERERROR;
                        if (bThreads)
                            rSharedPool.pushTask(new GraphicImportTask(pTag, rContext));
                        else
                            rContext.m_eLinkType = GfxLinkType::NativeJpg;
                            GraphicImportTask::doImport(rContext);
                    }
                }
                else
@@ -1386,6 +1421,8 @@ void GraphicFilter::ImportGraphics(std::vector< std::shared_ptr<Graphic> >& rGra
        }
    }

    rSharedPool.waitUntilDone(pTag);

    // Process data after import.
    for (auto& rContext : aContexts)
    {