diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt
index 312a82c4c..dd602a4d7 100644
--- a/src/libslic3r/CMakeLists.txt
+++ b/src/libslic3r/CMakeLists.txt
@@ -130,13 +130,10 @@ add_library(libslic3r STATIC
     Print.hpp
     PrintBase.cpp
     PrintBase.hpp
-    PrintExport.hpp
     PrintConfig.cpp
     PrintConfig.hpp
     PrintObject.cpp
     PrintRegion.cpp
-    Rasterizer/Rasterizer.hpp
-    Rasterizer/Rasterizer.cpp
     SLAPrint.cpp
     SLAPrint.hpp
     SLA/SLAAutoSupports.hpp
@@ -175,6 +172,10 @@ add_library(libslic3r STATIC
     SLA/SLARotfinder.cpp
     SLA/SLABoostAdapter.hpp
     SLA/SLASpatIndex.hpp
+    SLA/SLARaster.hpp
+    SLA/SLARaster.cpp
+    SLA/SLARasterWriter.hpp
+    SLA/SLARasterWriter.cpp
 )
 
 if (SLIC3R_PCH AND NOT SLIC3R_SYNTAXONLY)
diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp
index 87ea26301..89e21934a 100644
--- a/src/libslic3r/PrintConfig.cpp
+++ b/src/libslic3r/PrintConfig.cpp
@@ -2258,6 +2258,20 @@ void PrintConfigDef::init_sla_params()
     def->min = 100;
     def->set_default_value(new ConfigOptionInt(1440));
 
+    def = this->add("display_mirror_x", coBool);
+    def->full_label = L("Display horizontal mirroring");
+    def->label = L("Mirror horizontally");
+    def->tooltip = L("Enable horizontal mirroring of output images");
+    def->mode = comExpert;
+    def->set_default_value(new ConfigOptionBool(true));
+
+    def = this->add("display_mirror_y", coBool);
+    def->full_label = L("Display vertical mirroring");
+    def->label = L("Mirror vertically");
+    def->tooltip = L("Enable vertical mirroring of output images");
+    def->mode = comExpert;
+    def->set_default_value(new ConfigOptionBool(false));
+
     def = this->add("display_orientation", coEnum);
     def->label = L("Display orientation");
     def->tooltip = L("Set the actual LCD display orientation inside the SLA printer."
diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp
index 1da22b377..248b89e32 100644
--- a/src/libslic3r/PrintConfig.hpp
+++ b/src/libslic3r/PrintConfig.hpp
@@ -1083,6 +1083,8 @@ public:
     ConfigOptionInt                         display_pixels_x;
     ConfigOptionInt                         display_pixels_y;
     ConfigOptionEnum<SLADisplayOrientation> display_orientation;
+    ConfigOptionBool                        display_mirror_x;
+    ConfigOptionBool                        display_mirror_y;
     ConfigOptionFloats                      relative_correction;
     ConfigOptionFloat                       absolute_correction;
     ConfigOptionFloat                       gamma_correction;
@@ -1099,6 +1101,8 @@ protected:
         OPT_PTR(display_height);
         OPT_PTR(display_pixels_x);
         OPT_PTR(display_pixels_y);
+        OPT_PTR(display_mirror_x);
+        OPT_PTR(display_mirror_y);
         OPT_PTR(display_orientation);
         OPT_PTR(relative_correction);
         OPT_PTR(absolute_correction);
diff --git a/src/libslic3r/PrintExport.hpp b/src/libslic3r/PrintExport.hpp
deleted file mode 100644
index f6537ed32..000000000
--- a/src/libslic3r/PrintExport.hpp
+++ /dev/null
@@ -1,327 +0,0 @@
-#ifndef PRINTEXPORT_HPP
-#define PRINTEXPORT_HPP
-
-// For png export of the sliced model
-#include <fstream>
-#include <sstream>
-#include <vector>
-
-#include <boost/log/trivial.hpp>
-#include <boost/filesystem/path.hpp>
-
-#include "Rasterizer/Rasterizer.hpp"
-//#include <tbb/parallel_for.h>
-//#include <tbb/spin_mutex.h>//#include "tbb/mutex.h"
-
-namespace Slic3r {
-
-// Used for addressing parameters of FilePrinter::set_statistics()
-enum ePrintStatistics
-{
-    psUsedMaterial = 0,
-    psNumFade,
-    psNumSlow,
-    psNumFast,
-
-    psCnt
-};
-
-enum class FilePrinterFormat {
-    SLA_PNGZIP,
-    SVG
-};
-
-/*
- * Interface for a file printer of the slices. Implementation can be an SVG
- * or PNG printer or any other format.
- *
- * The format argument specifies the output format of the printer and it enables
- * different implementations of this class template for each supported format.
- *
- */
-template<FilePrinterFormat format>
-class FilePrinter {
-public:
-
-    // Draw a polygon which is a polygon inside a slice on the specified layer.
-    void draw_polygon(const ExPolygon& p, unsigned lyr);
-    void draw_polygon(const ClipperLib::Polygon& p, unsigned lyr);
-
-    // Tell the printer how many layers should it consider.
-    void layers(unsigned layernum);
-
-    // Get the number of layers in the print.
-    unsigned layers() const;
-
-    /* Switch to a particular layer. If there where less layers then the
-     * specified layer number than an appropriate number of layers will be
-     * allocated in the printer.
-     */
-    void begin_layer(unsigned layer);
-
-    // Allocate a new layer on top of the last and switch to it.
-    void begin_layer();
-
-    /*
-     * Finish the selected layer. It means that no drawing is allowed on that
-     * layer anymore. This fact can be used to prepare the file system output
-     * data like png comprimation and so on.
-     */
-    void finish_layer(unsigned layer);
-
-    // Finish the top layer.
-    void finish_layer();
-
-    // Save all the layers into the file (or dir) specified in the path argument
-    // An optional project name can be added to be used for the layer file names
-    void save(const std::string& path, const std::string& projectname = "");
-
-    // Save only the selected layer to the file specified in path argument.
-    void save_layer(unsigned lyr, const std::string& path);
-};
-
-// Provokes static_assert in the right way.
-template<class T = void> struct VeryFalse { static const bool value = false; };
-
-// This can be explicitly implemented in the gui layer or the default Zipper
-// API in libslic3r with minz.
-template<class Fmt> class LayerWriter {
-public:
-
-    LayerWriter(const std::string& /*zipfile_path*/)
-    {
-        static_assert(VeryFalse<Fmt>::value,
-                      "No layer writer implementation provided!");
-    }
-
-    // Should create a new file within the zip with the given filename. It
-    // should also finish any previous entry.
-    void next_entry(const std::string& /*fname*/) {}
-
-    // Should create a new file within the archive and write the provided data.
-    void binary_entry(const std::string& /*fname*/,
-                      const std::uint8_t* buf, size_t len);
-
-    // Test whether the object can still be used for writing.
-    bool is_ok() { return false; }
-
-    // Write some data (text) into the current file (entry) within the archive.
-    template<class T> LayerWriter& operator<<(T&& /*arg*/) {
-        return *this;
-    }
-
-    // Flush the current entry into the archive.
-    void finalize() {}
-};
-
-// Implementation for PNG raster output
-// Be aware that if a large number of layers are allocated, it can very well
-// exhaust the available memory especially on 32 bit platform.
-template<> class FilePrinter<FilePrinterFormat::SLA_PNGZIP>
-{
-    struct Layer {
-        Raster raster;
-        RawBytes rawbytes;
-
-        Layer() {}
-
-        Layer(const Layer&) = delete;
-        Layer(Layer&& m):
-            raster(std::move(m.raster)) {}
-    };
-
-    // We will save the compressed PNG data into stringstreams which can be done
-    // in parallel. Later we can write every layer to the disk sequentially.
-    std::vector<Layer> m_layers_rst;
-    Raster::Resolution m_res;
-    Raster::PixelDim m_pxdim;
-    double m_exp_time_s = .0, m_exp_time_first_s = .0;
-    double m_layer_height = .0;
-    Raster::Origin m_o = Raster::Origin::TOP_LEFT;
-    double m_gamma;
-
-    double m_used_material = 0.0;
-    int    m_cnt_fade_layers = 0;
-    int    m_cnt_slow_layers = 0;
-    int    m_cnt_fast_layers = 0;
-
-    std::string createIniContent(const std::string& projectname) {
-        using std::string;
-        using std::to_string;
-
-        auto expt_str = to_string(m_exp_time_s);
-        auto expt_first_str = to_string(m_exp_time_first_s);
-        auto layerh_str = to_string(m_layer_height);
-
-        const std::string cnt_fade_layers = to_string(m_cnt_fade_layers);
-        const std::string cnt_slow_layers = to_string(m_cnt_slow_layers);
-        const std::string cnt_fast_layers = to_string(m_cnt_fast_layers);
-        const std::string used_material   = to_string(m_used_material);
-
-        return string(
-        "action = print\n"
-        "jobDir = ") + projectname + "\n" +
-        "expTime = " + expt_str + "\n"
-        "expTimeFirst = " + expt_first_str + "\n"
-        "numFade = " + cnt_fade_layers + "\n"
-        "layerHeight = " + layerh_str + "\n"
-        "usedMaterial = " + used_material + "\n"
-        "numSlow = " + cnt_slow_layers + "\n"
-        "numFast = " + cnt_fast_layers + "\n";
-    }
-
-public:
-
-    enum RasterOrientation {
-        RO_LANDSCAPE,
-        RO_PORTRAIT
-    };
-
-    // We will play with the raster's coordinate origin parameter. When the
-    // printer should print in landscape mode it should have the Y axis flipped
-    // because the layers should be displayed upside down. PNG has its
-    // coordinate origin in the top-left corner so normally the Raster objects
-    // should be instantiated with the TOP_LEFT flag. However, in landscape mode
-    // we do want the pictures to be upside down so we will make BOTTOM_LEFT
-    // type rasters and the PNG format will do the flipping automatically.
-
-    // In case of portrait images, we have to rotate the image by a 90 degrees
-    // and flip the y axis. To get the correct upside-down orientation of the
-    // slice images, we can flip the x and y coordinates of the input polygons
-    // and do the Y flipping of the image. This will generate the correct
-    // orientation in portrait mode.
-
-    inline FilePrinter(double width_mm, double height_mm,
-                       unsigned width_px, unsigned height_px,
-                       double layer_height,
-                       double exp_time, double exp_time_first,
-                       RasterOrientation ro = RO_PORTRAIT,
-                       double gamma = 1.0):
-        m_res(width_px, height_px),
-        m_pxdim(width_mm/width_px, height_mm/height_px),
-        m_exp_time_s(exp_time),
-        m_exp_time_first_s(exp_time_first),
-        m_layer_height(layer_height),
-
-        // Here is the trick with the orientation.
-        m_o(ro == RO_LANDSCAPE? Raster::Origin::BOTTOM_LEFT :
-                                Raster::Origin::TOP_LEFT ),
-        m_gamma(gamma)
-    {
-    }
-
-    FilePrinter(const FilePrinter& ) = delete;
-    FilePrinter(FilePrinter&& m):
-        m_layers_rst(std::move(m.m_layers_rst)),
-        m_res(m.m_res),
-        m_pxdim(m.m_pxdim) {}
-
-    inline void layers(unsigned cnt) { if(cnt > 0) m_layers_rst.resize(cnt); }
-    inline unsigned layers() const { return unsigned(m_layers_rst.size()); }
-
-    inline void draw_polygon(const ExPolygon& p, unsigned lyr) {
-        assert(lyr < m_layers_rst.size());
-        m_layers_rst[lyr].raster.draw(p);
-    }
-
-    inline void draw_polygon(const ClipperLib::Polygon& p, unsigned lyr) {
-        assert(lyr < m_layers_rst.size());
-        m_layers_rst[lyr].raster.draw(p);
-    }
-
-    inline void begin_layer(unsigned lyr) {
-        if(m_layers_rst.size() <= lyr) m_layers_rst.resize(lyr+1);
-        m_layers_rst[lyr].raster.reset(m_res, m_pxdim, m_o, m_gamma);
-    }
-
-    inline void begin_layer() {
-        m_layers_rst.emplace_back();
-        m_layers_rst.front().raster.reset(m_res, m_pxdim, m_o, m_gamma);
-    }
-
-    inline void finish_layer(unsigned lyr_id) {
-        assert(lyr_id < m_layers_rst.size());
-        m_layers_rst[lyr_id].rawbytes =
-                m_layers_rst[lyr_id].raster.save(Raster::Compression::PNG);
-        m_layers_rst[lyr_id].raster.reset();
-    }
-
-    inline void finish_layer() {
-        if(!m_layers_rst.empty()) {
-            m_layers_rst.back().rawbytes =
-                    m_layers_rst.back().raster.save(Raster::Compression::PNG);
-            m_layers_rst.back().raster.reset();
-        }
-    }
-
-    template<class LyrFmt>
-    inline void save(const std::string& fpath, const std::string& prjname = "")
-    {
-        try {
-            LayerWriter<LyrFmt> writer(fpath);
-            if(!writer.is_ok()) return;
-
-            std::string project = prjname.empty()?
-                       boost::filesystem::path(fpath).stem().string() : prjname;
-
-            writer.next_entry("config.ini");
-            if(!writer.is_ok()) return;
-
-            writer << createIniContent(project);
-
-            for(unsigned i = 0; i < m_layers_rst.size() && writer.is_ok(); i++)
-            {
-                if(m_layers_rst[i].rawbytes.size() > 0) {
-                    char lyrnum[6];
-                    std::sprintf(lyrnum, "%.5d", i);
-                    auto zfilename = project + lyrnum + ".png";
-                    if(!writer.is_ok()) break;
-
-                    writer.binary_entry(zfilename,
-                                        m_layers_rst[i].rawbytes.data(),
-                                        m_layers_rst[i].rawbytes.size());
-                }
-            }
-
-            writer.finalize();
-        } catch(std::exception& e) {
-            BOOST_LOG_TRIVIAL(error) << e.what();
-            // Rethrow the exception
-            throw;
-        }
-    }
-
-    void save_layer(unsigned lyr, const std::string& path) {
-        unsigned i = lyr;
-        assert(i < m_layers_rst.size());
-
-        char lyrnum[6];
-        std::sprintf(lyrnum, "%.5d", lyr);
-        std::string loc = path + "layer" + lyrnum + ".png";
-
-        std::fstream out(loc, std::fstream::out | std::fstream::binary);
-        if(out.good()) {
-            m_layers_rst[i].raster.save(out, Raster::Compression::PNG);
-        } else {
-            BOOST_LOG_TRIVIAL(error) << "Can't create file for layer";
-        }
-
-        out.close();
-        m_layers_rst[i].raster.reset();
-    }
-
-    void set_statistics(const std::vector<double> statistics)
-    {
-        if (statistics.size() != psCnt)
-            return;
-
-        m_used_material   = statistics[psUsedMaterial];
-        m_cnt_fade_layers = int(statistics[psNumFade]);
-        m_cnt_slow_layers = int(statistics[psNumSlow]);
-        m_cnt_fast_layers = int(statistics[psNumFast]);
-    }
-};
-
-}
-
-#endif // PRINTEXPORT_HPP
diff --git a/src/libslic3r/Rasterizer/Rasterizer.cpp b/src/libslic3r/SLA/SLARaster.cpp
similarity index 72%
rename from src/libslic3r/Rasterizer/Rasterizer.cpp
rename to src/libslic3r/SLA/SLARaster.cpp
index e1213555c..32a88b1b5 100644
--- a/src/libslic3r/Rasterizer/Rasterizer.cpp
+++ b/src/libslic3r/SLA/SLARaster.cpp
@@ -1,5 +1,10 @@
-#include "Rasterizer.hpp"
-#include <ExPolygon.hpp>
+#ifndef SLARASTER_CPP
+#define SLARASTER_CPP
+
+#include <functional>
+
+#include "SLARaster.hpp"
+#include "libslic3r/ExPolygon.hpp"
 #include <libnest2d/backends/clipper/clipper_polygon.hpp>
 
 // For rasterizing
@@ -19,11 +24,13 @@
 
 namespace Slic3r {
 
-const Polygon& contour(const ExPolygon& p) { return p.contour; }
-const ClipperLib::Path& contour(const ClipperLib::Polygon& p) { return p.Contour; }
+inline const Polygon& contour(const ExPolygon& p) { return p.contour; }
+inline const ClipperLib::Path& contour(const ClipperLib::Polygon& p) { return p.Contour; }
 
-const Polygons& holes(const ExPolygon& p) { return p.holes; }
-const ClipperLib::Paths& holes(const ClipperLib::Polygon& p) { return p.Holes; }
+inline const Polygons& holes(const ExPolygon& p) { return p.holes; }
+inline const ClipperLib::Paths& holes(const ClipperLib::Polygon& p) { return p.Holes; }
+
+namespace sla {
 
 class Raster::Impl {
 public:
@@ -39,7 +46,7 @@ public:
     static const TPixel ColorWhite;
     static const TPixel ColorBlack;
 
-    using Origin = Raster::Origin;
+    using Format = Raster::Format;
 
 private:
     Raster::Resolution m_resolution;
@@ -52,16 +59,21 @@ private:
     TRendererAA m_renderer;
     
     std::function<double(double)> m_gammafn;
-    Origin m_o;
+    std::array<bool, 2> m_mirror;
+    Format m_fmt = Format::PNG;
     
     inline void flipy(agg::path_storage& path) const {
         path.flip_y(0, m_resolution.height_px);
     }
+    
+    inline void flipx(agg::path_storage& path) const {
+        path.flip_x(0, m_resolution.width_px);
+    }
 
 public:
 
     inline Impl(const Raster::Resolution& res, const Raster::PixelDim &pd,
-                Origin o, double gamma = 1.0):
+                const std::array<bool, 2>& mirror, double gamma = 1.0):
         m_resolution(res), 
 //        m_pxdim(pd), 
         m_pxdim_scaled(SCALING_FACTOR / pd.w_mm, SCALING_FACTOR / pd.h_mm),
@@ -72,7 +84,7 @@ public:
         m_pixfmt(m_rbuf),
         m_raw_renderer(m_pixfmt),
         m_renderer(m_raw_renderer),
-        m_o(o)
+        m_mirror(mirror)
     {
         m_renderer.color(ColorWhite);
         
@@ -81,6 +93,19 @@ public:
         
         clear();
     }
+    
+    inline Impl(const Raster::Resolution& res, 
+                const Raster::PixelDim &pd,
+                Format fmt, 
+                double gamma = 1.0): 
+        Impl(res, pd, {false, false}, gamma) 
+    {
+        switch (fmt) {
+        case Format::PNG: m_mirror = {false, true}; break;
+        case Format::RAW: m_mirror = {false, false}; break;
+        }
+        m_fmt = fmt;
+    }
 
     template<class P> void draw(const P &poly) {
         agg::rasterizer_scanline_aa<> ras;
@@ -89,14 +114,16 @@ public:
         ras.gamma(m_gammafn);
 
         auto&& path = to_path(contour(poly));
-
-        if(m_o == Origin::TOP_LEFT) flipy(path);
+        
+        if(m_mirror[X]) flipx(path);
+        if(m_mirror[Y]) flipy(path);
 
         ras.add_path(path);
 
         for(auto& h : holes(poly)) {
             auto&& holepath = to_path(h);
-            if(m_o == Origin::TOP_LEFT) flipy(holepath);
+            if(m_mirror[X]) flipx(holepath);
+            if(m_mirror[Y]) flipy(holepath);
             ras.add_path(holepath);
         }
 
@@ -108,11 +135,11 @@ public:
     }
 
     inline TBuffer& buffer()  { return m_buf; }
+    
+    inline Format format() const { return m_fmt; }
 
     inline const Raster::Resolution resolution() { return m_resolution; }
-
-    inline Origin origin() const /*noexcept*/ { return m_o; }
-
+   
 private:
     inline double getPx(const Point& p) {
         return p(0) * m_pxdim_scaled.w_mm;
@@ -154,30 +181,30 @@ private:
 const Raster::Impl::TPixel Raster::Impl::ColorWhite = Raster::Impl::TPixel(255);
 const Raster::Impl::TPixel Raster::Impl::ColorBlack = Raster::Impl::TPixel(0);
 
-Raster::Raster(const Resolution &r, const PixelDim &pd, Origin o, double g):
-    m_impl(new Impl(r, pd, o, g)) {}
+template<> Raster::Raster() { reset(); };
+Raster::~Raster() = default;
 
-Raster::Raster() {}
+// Raster::Raster(Raster &&m) = default;
+// Raster& Raster::operator=(Raster&&) = default;
 
-Raster::~Raster() {}
-
-Raster::Raster(Raster &&m):
-    m_impl(std::move(m.m_impl)) {}
-
-void Raster::reset(const Raster::Resolution &r, const Raster::PixelDim &pd, 
-                   double g)
-{
-    // Free up the unnecessary memory and make sure it stays clear after
-    // an exception
-    auto o = m_impl? m_impl->origin() : Origin::TOP_LEFT;
-    reset(r, pd, o, g);
+// FIXME: remove after migrating to higher version of windows compiler
+Raster::Raster(Raster &&m): m_impl(std::move(m.m_impl)) {}
+Raster& Raster::operator=(Raster &&m) {
+    m_impl = std::move(m.m_impl); return *this;
 }
 
 void Raster::reset(const Raster::Resolution &r, const Raster::PixelDim &pd,
-                   Raster::Origin o, double gamma)
+                   Format fmt, double gamma)
 {
     m_impl.reset();
-    m_impl.reset(new Impl(r, pd, o, gamma));
+    m_impl.reset(new Impl(r, pd, fmt, gamma));
+}
+
+void Raster::reset(const Raster::Resolution &r, const Raster::PixelDim &pd,
+                   const std::array<bool, 2>& mirror, double gamma)
+{
+    m_impl.reset();
+    m_impl.reset(new Impl(r, pd, mirror, gamma));
 }
 
 void Raster::reset()
@@ -208,13 +235,13 @@ void Raster::draw(const ClipperLib::Polygon &poly)
     m_impl->draw(poly);
 }
 
-void Raster::save(std::ostream& stream, Compression comp)
+void Raster::save(std::ostream& stream, Format fmt)
 {
     assert(m_impl);
     if(!stream.good()) return;
 
-    switch(comp) {
-    case Compression::PNG: {
+    switch(fmt) {
+    case Format::PNG: {
         auto& b = m_impl->buffer();
         size_t out_len = 0;
         void * rawdata = tdefl_write_image_to_png_file_in_memory(
@@ -231,7 +258,7 @@ void Raster::save(std::ostream& stream, Compression comp)
 
         break;
     }
-    case Compression::RAW: {
+    case Format::RAW: {
         stream << "P5 "
                << m_impl->resolution().width_px << " "
                << m_impl->resolution().height_px << " "
@@ -244,14 +271,19 @@ void Raster::save(std::ostream& stream, Compression comp)
     }
 }
 
-RawBytes Raster::save(Raster::Compression comp)
+void Raster::save(std::ostream &stream)
+{
+    save(stream, m_impl->format());
+}
+
+RawBytes Raster::save(Format fmt)
 {
     assert(m_impl);
 
     std::vector<std::uint8_t> data; size_t s = 0;
 
-    switch(comp) {
-    case Compression::PNG: {
+    switch(fmt) {
+    case Format::PNG: {
         void *rawdata = tdefl_write_image_to_png_file_in_memory(
                     m_impl->buffer().data(),
                     int(resolution().width_px),
@@ -265,7 +297,7 @@ RawBytes Raster::save(Raster::Compression comp)
         MZ_FREE(rawdata);
         break;
     }
-    case Compression::RAW: {
+    case Format::RAW: {
         auto header = std::string("P5 ") +
                 std::to_string(m_impl->resolution().width_px) + " " +
                 std::to_string(m_impl->resolution().height_px) + " " + "255 ";
@@ -286,4 +318,12 @@ RawBytes Raster::save(Raster::Compression comp)
     return {std::move(data)};
 }
 
+RawBytes Raster::save()
+{
+    return save(m_impl->format());
 }
+
+}
+}
+
+#endif // SLARASTER_CPP
diff --git a/src/libslic3r/Rasterizer/Rasterizer.hpp b/src/libslic3r/SLA/SLARaster.hpp
similarity index 64%
rename from src/libslic3r/Rasterizer/Rasterizer.hpp
rename to src/libslic3r/SLA/SLARaster.hpp
index 3fffe1a36..d3bd52d92 100644
--- a/src/libslic3r/Rasterizer/Rasterizer.hpp
+++ b/src/libslic3r/SLA/SLARaster.hpp
@@ -1,17 +1,21 @@
-#ifndef RASTERIZER_HPP
-#define RASTERIZER_HPP
+#ifndef SLARASTER_HPP
+#define SLARASTER_HPP
 
 #include <ostream>
 #include <memory>
 #include <vector>
+#include <array>
+#include <utility>
 #include <cstdint>
 
 namespace ClipperLib { struct Polygon; }
 
-namespace Slic3r {
+namespace Slic3r { 
 
 class ExPolygon;
 
+namespace sla {
+
 // Raw byte buffer paired with its size. Suitable for compressed PNG data.
 class RawBytes {
 
@@ -23,15 +27,18 @@ public:
     
     size_t size() const { return m_buffer.size(); }
     const uint8_t * data() { return m_buffer.data(); }
+    
+    RawBytes(const RawBytes&) = delete;
+    RawBytes& operator=(const RawBytes&) = delete;
 
     // /////////////////////////////////////////////////////////////////////////
     // FIXME: the following is needed for MSVC2013 compatibility
     // /////////////////////////////////////////////////////////////////////////
 
-    RawBytes(const RawBytes&) = delete;
-    RawBytes(RawBytes&& mv) : m_buffer(std::move(mv.m_buffer)) {}
+    // RawBytes(RawBytes&&) = default;
+    // RawBytes& operator=(RawBytes&&) = default;
 
-    RawBytes& operator=(const RawBytes&) = delete;
+    RawBytes(RawBytes&& mv) : m_buffer(std::move(mv.m_buffer)) {}
     RawBytes& operator=(RawBytes&& mv) {
         m_buffer = std::move(mv.m_buffer);
         return *this;
@@ -54,28 +61,19 @@ class Raster {
 public:
 
     /// Supported compression types
-    enum class Compression {
+    enum class Format {
         RAW,    //!> Uncompressed pixel data
         PNG     //!> PNG compression
     };
 
-    /// The Rasterizer expects the input polygons to have their coordinate
-    /// system origin in the bottom left corner. If the raster is then
-    /// configured with the TOP_LEFT origin parameter (in the constructor) than
-    /// it will flip the Y axis in output to maintain the correct orientation.
-    /// This is the default case with PNG images. They have the origin in the
-    /// top left corner. Without the flipping, the image would be upside down
-    /// with the scaled (clipper) coordinate system of the input polygons.
-    enum class Origin {
-        TOP_LEFT,
-        BOTTOM_LEFT
-    };
-
     /// Type that represents a resolution in pixels.
     struct Resolution {
         unsigned width_px;
         unsigned height_px;
-        inline Resolution(unsigned w, unsigned h): width_px(w), height_px(h) {}
+
+        inline Resolution(unsigned w = 0, unsigned h = 0):
+            width_px(w), height_px(h) {}
+
         inline unsigned pixels() const /*noexcept*/ {
             return width_px * height_px;
         }
@@ -85,24 +83,34 @@ public:
     struct PixelDim {
         double w_mm;
         double h_mm;
-        inline PixelDim(double px_width_mm, double px_height_mm ):
+        inline PixelDim(double px_width_mm = 0.0, double px_height_mm = 0.0):
             w_mm(px_width_mm), h_mm(px_height_mm) {}
     };
 
     /// Constructor taking the resolution and the pixel dimension.
-    Raster(const Resolution& r,  const PixelDim& pd, 
-           Origin o = Origin::BOTTOM_LEFT, double gamma = 1.0);
+    template <class...Args> Raster(Args...args) { 
+        reset(std::forward<Args>(args)...); 
+    }
     
-    Raster();
     Raster(const Raster& cpy) = delete;
     Raster& operator=(const Raster& cpy) = delete;
     Raster(Raster&& m);
+    Raster& operator=(Raster&&);
     ~Raster();
 
     /// Reallocated everything for the given resolution and pixel dimension.
-    void reset(const Resolution& r, const PixelDim& pd, double gamma = 1.0);
-    void reset(const Resolution& r, const PixelDim& pd, Origin o, double gamma);
-
+    /// The third parameter is either the X, Y mirroring or a supported format 
+    /// for which the correct mirroring will be configured.
+    void reset(const Resolution&, 
+               const PixelDim&, 
+               const std::array<bool, 2>& mirror, 
+               double gamma = 1.0);
+    
+    void reset(const Resolution& r, 
+               const PixelDim& pd, 
+               Format o, 
+               double gamma = 1.0);
+    
     /**
      * Release the allocated resources. Drawing in this state ends in
      * unspecified behavior.
@@ -119,11 +127,24 @@ public:
     void draw(const ExPolygon& poly);
     void draw(const ClipperLib::Polygon& poly);
 
+    // Saving the raster: 
+    // It is possible to override the format given in the constructor but
+    // be aware that the mirroring will not be modified.
+    
     /// Save the raster on the specified stream.
-    void save(std::ostream& stream, Compression comp = Compression::RAW);
+    void save(std::ostream& stream, Format);
+    void save(std::ostream& stream);
 
-    RawBytes save(Compression comp = Compression::RAW);
+    /// Save into a continuous byte stream which is returned.
+    RawBytes save(Format fmt);
+    RawBytes save();
 };
 
-}
-#endif // RASTERIZER_HPP
+// This prevents the duplicate default constructor warning on MSVC2013
+template<> Raster::Raster();
+
+
+} // sla
+} // Slic3r
+
+#endif // SLARASTER_HPP
diff --git a/src/libslic3r/SLA/SLARasterWriter.cpp b/src/libslic3r/SLA/SLARasterWriter.cpp
new file mode 100644
index 000000000..f7c3925ac
--- /dev/null
+++ b/src/libslic3r/SLA/SLARasterWriter.cpp
@@ -0,0 +1,136 @@
+#include "SLARasterWriter.hpp"
+#include "libslic3r/Zipper.hpp"
+#include "ExPolygon.hpp"
+#include <libnest2d/backends/clipper/clipper_polygon.hpp>
+
+#include <boost/log/trivial.hpp>
+#include <boost/filesystem/path.hpp>
+
+namespace Slic3r { namespace sla {
+
+std::string SLARasterWriter::createIniContent(const std::string& projectname) const 
+{
+    auto expt_str = std::to_string(m_exp_time_s);
+    auto expt_first_str = std::to_string(m_exp_time_first_s);
+    auto layerh_str = std::to_string(m_layer_height);
+
+    const std::string cnt_fade_layers = std::to_string(m_cnt_fade_layers);
+    const std::string cnt_slow_layers = std::to_string(m_cnt_slow_layers);
+    const std::string cnt_fast_layers = std::to_string(m_cnt_fast_layers);
+    const std::string used_material   = std::to_string(m_used_material);
+
+    return std::string(
+    "action = print\n"
+    "jobDir = ") + projectname + "\n" +
+    "expTime = " + expt_str + "\n"
+    "expTimeFirst = " + expt_first_str + "\n"
+    "numFade = " + cnt_fade_layers + "\n"
+    "layerHeight = " + layerh_str + "\n"
+    "usedMaterial = " + used_material + "\n"
+    "numSlow = " + cnt_slow_layers + "\n"
+                                     "numFast = " + cnt_fast_layers + "\n";
+}
+
+void SLARasterWriter::flpXY(ClipperLib::Polygon &poly)
+{
+    for(auto& p : poly.Contour) std::swap(p.X, p.Y);
+    std::reverse(poly.Contour.begin(), poly.Contour.end());
+    
+    for(auto& h : poly.Holes) {
+        for(auto& p : h) std::swap(p.X, p.Y);
+        std::reverse(h.begin(), h.end());
+    }
+}
+
+void SLARasterWriter::flpXY(ExPolygon &poly)
+{
+    for(auto& p : poly.contour.points) p = Point(p.y(), p.x());
+    std::reverse(poly.contour.points.begin(), poly.contour.points.end());
+    
+    for(auto& h : poly.holes) {
+        for(auto& p : h.points) p = Point(p.y(), p.x());
+        std::reverse(h.points.begin(), h.points.end());
+    }
+}
+
+SLARasterWriter::SLARasterWriter(const SLAPrinterConfig &cfg, 
+                                 const SLAMaterialConfig &mcfg, 
+                                 double layer_height)
+{
+    double w = cfg.display_width.getFloat();
+    double h = cfg.display_height.getFloat();
+    auto pw = unsigned(cfg.display_pixels_x.getInt());
+    auto ph = unsigned(cfg.display_pixels_y.getInt());
+    
+    m_mirror[X] = cfg.display_mirror_x.getBool();
+    
+    // PNG raster will implicitly do an Y mirror
+    m_mirror[Y] = ! cfg.display_mirror_y.getBool();
+        
+    auto ro = cfg.display_orientation.getInt();
+    
+    if(ro == roPortrait) {
+        std::swap(w, h);
+        std::swap(pw, ph);
+        m_o = roPortrait;
+        
+        // XY flipping implicitly does an X mirror
+        m_mirror[X] = ! m_mirror[X];
+    } else m_o = roLandscape;
+    
+    m_res = Raster::Resolution(pw, ph);
+    m_pxdim = Raster::PixelDim(w/pw, h/ph);
+    m_exp_time_s = mcfg.exposure_time.getFloat();
+    m_exp_time_first_s = mcfg.initial_exposure_time.getFloat();
+    m_layer_height = layer_height;
+    
+    m_gamma = cfg.gamma_correction.getFloat();
+}
+
+void SLARasterWriter::save(const std::string &fpath, const std::string &prjname)
+{
+    try {
+        Zipper zipper(fpath); // zipper with no compression
+        
+        std::string project = prjname.empty()?
+                    boost::filesystem::path(fpath).stem().string() : prjname;
+        
+        zipper.add_entry("config.ini");
+        
+        zipper << createIniContent(project);
+        
+        for(unsigned i = 0; i < m_layers_rst.size(); i++)
+        {
+            if(m_layers_rst[i].rawbytes.size() > 0) {
+                char lyrnum[6];
+                std::sprintf(lyrnum, "%.5d", i);
+                auto zfilename = project + lyrnum + ".png";
+                
+                // Add binary entry to the zipper
+                zipper.add_entry(zfilename,
+                                 m_layers_rst[i].rawbytes.data(),
+                                 m_layers_rst[i].rawbytes.size());
+            }
+        }
+        
+        zipper.finalize();
+    } catch(std::exception& e) {
+        BOOST_LOG_TRIVIAL(error) << e.what();
+        // Rethrow the exception
+        throw;
+    }
+}
+
+void SLARasterWriter::set_statistics(const std::vector<double> statistics)
+{
+    if (statistics.size() != psCnt)
+        return;
+    
+    m_used_material   = statistics[psUsedMaterial];
+    m_cnt_fade_layers = int(statistics[psNumFade]);
+    m_cnt_slow_layers = int(statistics[psNumSlow]);
+    m_cnt_fast_layers = int(statistics[psNumFast]);
+}
+
+} // namespace sla
+} // namespace Slic3r
diff --git a/src/libslic3r/SLA/SLARasterWriter.hpp b/src/libslic3r/SLA/SLARasterWriter.hpp
new file mode 100644
index 000000000..7133d2dde
--- /dev/null
+++ b/src/libslic3r/SLA/SLARasterWriter.hpp
@@ -0,0 +1,167 @@
+#ifndef SLARASTERWRITER_HPP
+#define SLARASTERWRITER_HPP
+
+// For png export of the sliced model
+#include <fstream>
+#include <sstream>
+#include <vector>
+#include <array>
+
+#include "libslic3r/PrintConfig.hpp"
+
+#include "SLARaster.hpp"
+
+namespace Slic3r { namespace sla {
+
+// Implementation for PNG raster output
+// Be aware that if a large number of layers are allocated, it can very well
+// exhaust the available memory especially on 32 bit platform.
+// This class is designed to be used in parallel mode. Layers have an ID and
+// each layer can be written and compressed independently (in parallel).
+// At the end when all layers where written, the save method can be used to 
+// write out the result into a zipped archive.
+class SLARasterWriter
+{
+public:
+    enum RasterOrientation {
+        roLandscape,
+        roPortrait
+    };
+    
+    // Used for addressing parameters of set_statistics()
+    enum ePrintStatistics
+    {
+        psUsedMaterial = 0,
+        psNumFade,
+        psNumSlow,
+        psNumFast,
+    
+        psCnt
+    };
+    
+private:
+    
+    // A struct to bind the raster image data and its compressed bytes together.
+    struct Layer {
+        Raster raster;
+        RawBytes rawbytes;
+
+        Layer() = default;
+        Layer(const Layer&) = delete; // The image is big, do not copy by accident
+        Layer& operator=(const Layer&) = delete;
+        
+        // /////////////////////////////////////////////////////////////////////
+        // FIXME: the following is needed for MSVC2013 compatibility
+        // /////////////////////////////////////////////////////////////////////
+
+        // Layer(Layer&& m) = default;
+        // Layer& operator=(Layer&&) = default;
+        Layer(Layer &&m):
+            raster(std::move(m.raster)), rawbytes(std::move(m.rawbytes)) {}
+        Layer& operator=(Layer &&m) {
+            raster = std::move(m.raster); rawbytes = std::move(m.rawbytes);
+            return *this;
+        }
+    };
+
+    // We will save the compressed PNG data into RawBytes type buffers in 
+    // parallel. Later we can write every layer to the disk sequentially.
+    std::vector<Layer> m_layers_rst;
+    Raster::Resolution m_res;
+    Raster::PixelDim m_pxdim;
+    double m_exp_time_s = .0, m_exp_time_first_s = .0;
+    double m_layer_height = .0;
+    RasterOrientation m_o = roPortrait;
+    std::array<bool, 2> m_mirror;
+    
+    double m_gamma;
+
+    double m_used_material = 0.0;
+    int    m_cnt_fade_layers = 0;
+    int    m_cnt_slow_layers = 0;
+    int    m_cnt_fast_layers = 0;
+
+    std::string createIniContent(const std::string& projectname) const;
+    
+    static void flpXY(ClipperLib::Polygon& poly);
+    static void flpXY(ExPolygon& poly);
+
+public:
+
+    SLARasterWriter(const SLAPrinterConfig& cfg, 
+                    const SLAMaterialConfig& mcfg, 
+                    double layer_height);
+
+    SLARasterWriter(const SLARasterWriter& ) = delete;
+    SLARasterWriter& operator=(const SLARasterWriter&) = delete;
+
+    // /////////////////////////////////////////////////////////////////////////
+    // FIXME: the following is needed for MSVC2013 compatibility
+    // /////////////////////////////////////////////////////////////////////////
+
+    // SLARasterWriter(SLARasterWriter&& m) = default;
+    // SLARasterWriter& operator=(SLARasterWriter&&) = default;
+    SLARasterWriter(SLARasterWriter&& m):
+        m_layers_rst(std::move(m.m_layers_rst)),
+        m_res(m.m_res),
+        m_pxdim(m.m_pxdim),
+        m_exp_time_s(m.m_exp_time_s),
+        m_exp_time_first_s(m.m_exp_time_first_s),
+        m_layer_height(m.m_layer_height),
+        m_o(m.m_o),
+        m_mirror(std::move(m.m_mirror)),
+        m_gamma(m.m_gamma),
+        m_used_material(m.m_used_material),
+        m_cnt_fade_layers(m.m_cnt_fade_layers),
+        m_cnt_slow_layers(m.m_cnt_slow_layers),
+        m_cnt_fast_layers(m.m_cnt_fast_layers)
+    {}
+
+    // /////////////////////////////////////////////////////////////////////////
+
+    inline void layers(unsigned cnt) { if(cnt > 0) m_layers_rst.resize(cnt); }
+    inline unsigned layers() const { return unsigned(m_layers_rst.size()); }
+    
+    template<class Poly> void draw_polygon(const Poly& p, unsigned lyr) {
+        assert(lyr < m_layers_rst.size());
+        if(m_o == roPortrait) {
+            Poly poly(p); flpXY(poly);
+            m_layers_rst[lyr].raster.draw(poly);
+        }
+        else m_layers_rst[lyr].raster.draw(p);
+    }
+
+    inline void begin_layer(unsigned lyr) {
+        if(m_layers_rst.size() <= lyr) m_layers_rst.resize(lyr+1);
+        m_layers_rst[lyr].raster.reset(m_res, m_pxdim, m_mirror, m_gamma);
+    }
+
+    inline void begin_layer() {
+        m_layers_rst.emplace_back();
+        m_layers_rst.front().raster.reset(m_res, m_pxdim, m_mirror, m_gamma);
+    }
+
+    inline void finish_layer(unsigned lyr_id) {
+        assert(lyr_id < m_layers_rst.size());
+        m_layers_rst[lyr_id].rawbytes =
+                m_layers_rst[lyr_id].raster.save(Raster::Format::PNG);
+        m_layers_rst[lyr_id].raster.reset();
+    }
+
+    inline void finish_layer() {
+        if(!m_layers_rst.empty()) {
+            m_layers_rst.back().rawbytes =
+                    m_layers_rst.back().raster.save(Raster::Format::PNG);
+            m_layers_rst.back().raster.reset();
+        }
+    }
+
+    void save(const std::string& fpath, const std::string& prjname = "");
+
+    void set_statistics(const std::vector<double> statistics);
+};
+
+} // namespace sla
+} // namespace Slic3r
+
+#endif // SLARASTERWRITER_HPP
diff --git a/src/libslic3r/Rasterizer/bicubic.h b/src/libslic3r/SLA/bicubic.h
similarity index 100%
rename from src/libslic3r/Rasterizer/bicubic.h
rename to src/libslic3r/SLA/bicubic.h
diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp
index 324008cf0..c73ae5650 100644
--- a/src/libslic3r/SLAPrint.cpp
+++ b/src/libslic3r/SLAPrint.cpp
@@ -747,8 +747,8 @@ void SLAPrint::process()
         {
             // We apply the printer correction offset here.
             if(clpr_offs != 0)
-                po.m_model_slices[id] =
-                    offset_ex(po.m_model_slices[id], float(clpr_offs));
+                po.m_model_slices[id] = 
+                        offset_ex(po.m_model_slices[id], float(clpr_offs));
             
             mit->set_model_slice_idx(po, id); ++mit;
         }
@@ -1014,7 +1014,7 @@ void SLAPrint::process()
         namespace sl = libnest2d::shapelike;    // For algorithms
 
         // If the raster has vertical orientation, we will flip the coordinates
-        bool flpXY = m_printer_config.display_orientation.getInt() == SLADisplayOrientation::sladoPortrait;
+//        bool flpXY = m_printer_config.display_orientation.getInt() == SLADisplayOrientation::sladoPortrait;
 
         // Set up custom union and diff functions for clipper polygons
         auto polyunion = [] (const ClipperPolygons& subjects)
@@ -1072,9 +1072,9 @@ void SLAPrint::process()
 
         // get polygons for all instances in the object
         auto get_all_polygons =
-                [flpXY](const ExPolygons& input_polygons,
-                        const std::vector<SLAPrintObject::Instance>& instances,
-                        bool is_lefthanded)
+                [](const ExPolygons& input_polygons,
+                   const std::vector<SLAPrintObject::Instance>& instances,
+                   bool is_lefthanded)
         {
             ClipperPolygons polygons;
             polygons.reserve(input_polygons.size() * instances.size());
@@ -1088,7 +1088,7 @@ void SLAPrint::process()
 
                     // We need to reverse if flpXY OR is_lefthanded is true but
                     // not if both are true which is a logical inequality (XOR)
-                    bool needreverse = flpXY != is_lefthanded;
+                    bool needreverse = /*flpXY !=*/ is_lefthanded;
 
                     // should be a move
                     poly.Contour.reserve(polygon.contour.size() + 1);
@@ -1123,10 +1123,10 @@ void SLAPrint::process()
                     sl::translate(poly, ClipperPoint{instances[i].shift(X),
                                                      instances[i].shift(Y)});
 
-                    if (flpXY) {
-                        for(auto& p : poly.Contour) std::swap(p.X, p.Y);
-                        for(auto& h : poly.Holes) for(auto& p : h) std::swap(p.X, p.Y);
-                    }
+//                    if (flpXY) {
+//                        for(auto& p : poly.Contour) std::swap(p.X, p.Y);
+//                        for(auto& h : poly.Holes) for(auto& p : h) std::swap(p.X, p.Y);
+//                    }
 
                     polygons.emplace_back(std::move(poly));
                 }
@@ -1295,35 +1295,11 @@ void SLAPrint::process()
     auto rasterize = [this]() {
         if(canceled()) return;
 
-        // collect all the keys
-
-        // If the raster has vertical orientation, we will flip the coordinates
-        bool flpXY = m_printer_config.display_orientation.getInt() ==
-                SLADisplayOrientation::sladoPortrait;
-
         { // create a raster printer for the current print parameters
-            // I don't know any better
-            auto& ocfg = m_objects.front()->m_config;
-            auto& matcfg = m_material_config;
-            auto& printcfg = m_printer_config;
-
-            double w = printcfg.display_width.getFloat();
-            double h = printcfg.display_height.getFloat();
-            auto pw = unsigned(printcfg.display_pixels_x.getInt());
-            auto ph = unsigned(printcfg.display_pixels_y.getInt());
-            double lh = ocfg.layer_height.getFloat();
-            double exp_t = matcfg.exposure_time.getFloat();
-            double iexp_t = matcfg.initial_exposure_time.getFloat();
-            
-            double gamma = m_printer_config.gamma_correction.getFloat();
-
-            if(flpXY) { std::swap(w, h); std::swap(pw, ph); }
-
-            m_printer.reset(
-                new SLAPrinter(w, h, pw, ph, lh, exp_t, iexp_t,
-                               flpXY? SLAPrinter::RO_PORTRAIT : 
-                                      SLAPrinter::RO_LANDSCAPE, 
-                               gamma));
+            double layerh = m_default_object_config.layer_height.getFloat();
+            m_printer.reset(new SLAPrinter(m_printer_config, 
+                                           m_material_config, 
+                                           layerh));
         }
 
         // Allocate space for all the layers
@@ -1511,6 +1487,8 @@ bool SLAPrint::invalidate_state_by_config_options(const std::vector<t_config_opt
         "display_height",
         "display_pixels_x",
         "display_pixels_y",
+        "display_mirror_x",
+        "display_mirror_y",
         "display_orientation"
     };
 
diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp
index 0c7d92ff2..b0c5a8fdc 100644
--- a/src/libslic3r/SLAPrint.hpp
+++ b/src/libslic3r/SLAPrint.hpp
@@ -3,11 +3,11 @@
 
 #include <mutex>
 #include "PrintBase.hpp"
-#include "PrintExport.hpp"
+//#include "PrintExport.hpp"
+#include "SLA/SLARasterWriter.hpp"
 #include "Point.hpp"
 #include "MTUtils.hpp"
 #include <libnest2d/backends/clipper/clipper_polygon.hpp>
-#include "Zipper.hpp"
 
 namespace Slic3r {
 
@@ -326,37 +326,6 @@ struct SLAPrintStatistics
     }
 };
 
-// The implementation of creating zipped archives with wxWidgets
-template<> class LayerWriter<Zipper> {
-    Zipper m_zip;
-public:
-
-    LayerWriter(const std::string& zipfile_path): m_zip(zipfile_path) {}
-
-    void next_entry(const std::string& fname) { m_zip.add_entry(fname); }
-
-    void binary_entry(const std::string& fname,
-                      const std::uint8_t* buf,
-                      size_t l)
-    {
-        m_zip.add_entry(fname, buf, l);
-    }
-
-    template<class T> inline LayerWriter& operator<<(T&& arg) {
-        m_zip << std::forward<T>(arg); return *this;
-    }
-
-    bool is_ok() const {
-        return true; // m_zip blows up if something goes wrong...
-    }
-
-    // After finalize, no writing to the archive will have an effect. The only
-    // valid operation is to dispose the object calling the destructor which
-    // should close the file. This method can throw and signal potential errors
-    // when flushing the archive. This is why its present.
-    void finalize() { m_zip.finalize(); }
-};
-
 /**
  * @brief This class is the high level FSM for the SLA printing process.
  *
@@ -389,11 +358,10 @@ public:
     // Returns true if the last step was finished with success.
     bool                finished() const override { return this->is_step_done(slaposSliceSupports) && this->Inherited::is_step_done(slapsRasterize); }
 
-    template<class Fmt = Zipper>
     inline void export_raster(const std::string& fpath,
-                       const std::string& projectname = "")
+                              const std::string& projectname = "")
     {
-        if(m_printer) m_printer->save<Fmt>(fpath, projectname);
+        if(m_printer) m_printer->save(fpath, projectname);
     }
 
     const PrintObjects& objects() const { return m_objects; }
@@ -454,7 +422,7 @@ public:
     const std::vector<PrintLayer>& print_layers() const { return m_printer_input; }
 
 private:
-    using SLAPrinter = FilePrinter<FilePrinterFormat::SLA_PNGZIP>;
+    using SLAPrinter = sla::SLARasterWriter;
     using SLAPrinterPtr = std::unique_ptr<SLAPrinter>;
 
     // Implement same logic as in SLAPrintObject
diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp
index cfb6c3950..e729a2ff7 100644
--- a/src/slic3r/GUI/Plater.cpp
+++ b/src/slic3r/GUI/Plater.cpp
@@ -9,6 +9,7 @@
 #include <boost/algorithm/string.hpp>
 #include <boost/optional.hpp>
 #include <boost/filesystem/path.hpp>
+#include <boost/log/trivial.hpp>
 
 #include <wx/sizer.h>
 #include <wx/stattext.h>
diff --git a/src/slic3r/GUI/Preset.cpp b/src/slic3r/GUI/Preset.cpp
index 22ddf3449..7192d485c 100644
--- a/src/slic3r/GUI/Preset.cpp
+++ b/src/slic3r/GUI/Preset.cpp
@@ -509,6 +509,7 @@ const std::vector<std::string>& Preset::sla_printer_options()
             "printer_technology",
             "bed_shape", "max_print_height",
             "display_width", "display_height", "display_pixels_x", "display_pixels_y",
+            "display_mirror_x", "display_mirror_y",
             "display_orientation",
             "fast_tilt_time", "slow_tilt_time", "area_fill",
             "relative_correction",
diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp
index 6cd32d397..22aecf38d 100644
--- a/src/slic3r/GUI/Tab.cpp
+++ b/src/slic3r/GUI/Tab.cpp
@@ -2087,6 +2087,10 @@ void TabPrinter::build_sla()
     line.append_option(optgroup->get_option("display_pixels_y"));
     optgroup->append_line(line);
     optgroup->append_single_option_line("display_orientation");
+    
+    // FIXME: This should be on one line in the UI
+    optgroup->append_single_option_line("display_mirror_x");
+    optgroup->append_single_option_line("display_mirror_y");
 
     optgroup = page->new_optgroup(_(L("Tilt")));
     line = { _(L("Tilt time")), "" };