diff --git a/Common/Context.h b/Common/Context.h
index 485711d2f6ff1a6c7433b5b8b5c4ba1cd52a1e30..570e7e6daa2809e6e412ef71fea5bd3b04fa022f 100644
--- a/Common/Context.h
+++ b/Common/Context.h
@@ -180,7 +180,8 @@ class CTX {
   int bgGradient;
   // draw background image?
   std::string bgImageFileName;
-  double bgImagePosition[2];
+  double bgImagePosition[2], bgImageSize[2];
+  int bgImage3d;
   // fltk font size (and delta for palette windows)
   int fontSize, deltaFontSize;
   // font name, FLTK enum and size for opengl graphics
diff --git a/Common/DefaultOptions.h b/Common/DefaultOptions.h
index 00ce9f219bf192245f489f6f72e871cca553afdf..749e527c27c868946fe76dfdcbd05ca0358bb605 100644
--- a/Common/DefaultOptions.h
+++ b/Common/DefaultOptions.h
@@ -329,12 +329,19 @@ StringXNumber GeneralOptions_Number[] = {
 
   { F|O, "BackgroundGradient" , opt_general_background_gradient , 1. ,
     "Draw background gradient (0=none, 1=vertical, 2=horizontal, 3=radial)" },
-  { F|O, "BackgroundImagePositionX" , opt_general_background_image_position0 , 1.e5 ,
-    "X position (in pixels) of background image (< 0: measure from right edge; "
+  { F|O, "BackgroundImage3D" , opt_general_background_image_3d , 0 ,
+    "Create background image in the 3D model (units = model units) or as "
+    "2D background (units = pixels)" },
+  { F|O, "BackgroundImagePositionX" , opt_general_background_image_position0 , 0 ,
+    "X position of background image (for 2D background: < 0: measure from right edge; "
     ">= 1e5: centered)" },
-  { F|O, "BackgroundImagePositionY" , opt_general_background_image_position1 , 1.e5 ,
-    "Y position (in pixels) of background image (< 0: measure from bottom edge; "
+  { F|O, "BackgroundImagePositionY" , opt_general_background_image_position1 , 0 ,
+    "Y position of background image (for 2D background: < 0: measure from bottom edge; "
     ">= 1e5: centered)" },
+  { F|O, "BackgroundImageWidth" , opt_general_background_image_size0 , -1. ,
+    "Width of background image (0: actual width; -1: fullscreen)" },
+  { F|O, "BackgroundImageHeight" , opt_general_background_image_size1 , -1 ,
+    "Height of background image (0: actual width; -1: fullscreen)" },
 
   { F|O, "Camera" , opt_general_camera_mode, 0. ,
     "Enable camera view mode" },
diff --git a/Common/Options.cpp b/Common/Options.cpp
index 3d76466f8fad88c1c47a352a57734af759f73d5b..f85aa81b6efcb940e4b7cae0f670dc673ac5a577 100644
--- a/Common/Options.cpp
+++ b/Common/Options.cpp
@@ -3224,6 +3224,27 @@ double opt_general_background_image_position1(OPT_ARGS_NUM)
   return CTX::instance()->bgImagePosition[1];
 }
 
+double opt_general_background_image_size0(OPT_ARGS_NUM)
+{
+  if(action & GMSH_SET)
+    CTX::instance()->bgImageSize[0] = val;
+  return CTX::instance()->bgImageSize[0];
+}
+
+double opt_general_background_image_size1(OPT_ARGS_NUM)
+{
+  if(action & GMSH_SET)
+    CTX::instance()->bgImageSize[1] = val;
+  return CTX::instance()->bgImageSize[1];
+}
+
+double opt_general_background_image_3d(OPT_ARGS_NUM)
+{
+  if(action & GMSH_SET)
+    CTX::instance()->bgImage3d = (int)val;
+  return CTX::instance()->bgImage3d;
+}
+
 double opt_general_trackball(OPT_ARGS_NUM)
 {
   if(action & GMSH_SET)
diff --git a/Common/Options.h b/Common/Options.h
index 6bbb799f9fabaffdaa27706cbc340602ca151452..0dc1d9fbfd2e8d54ec267174d24d1ce191e909a1 100644
--- a/Common/Options.h
+++ b/Common/Options.h
@@ -187,6 +187,9 @@ double opt_general_color_scheme(OPT_ARGS_NUM);
 double opt_general_background_gradient(OPT_ARGS_NUM);
 double opt_general_background_image_position0(OPT_ARGS_NUM);
 double opt_general_background_image_position1(OPT_ARGS_NUM);
+double opt_general_background_image_size0(OPT_ARGS_NUM);
+double opt_general_background_image_size1(OPT_ARGS_NUM);
+double opt_general_background_image_3d(OPT_ARGS_NUM);
 double opt_general_verbosity(OPT_ARGS_NUM);
 double opt_general_progress_meter_step(OPT_ARGS_NUM);
 double opt_general_nopopup(OPT_ARGS_NUM);
diff --git a/Common/gmshPopplerWrapper.cpp b/Common/gmshPopplerWrapper.cpp
index 6d730e125053f80520c476e4ab56d5b740a6b5ab..c681920aee60b10b32707e16a42c530a929442d4 100644
--- a/Common/gmshPopplerWrapper.cpp
+++ b/Common/gmshPopplerWrapper.cpp
@@ -45,11 +45,11 @@ GLuint gmshPopplerWrapper::getTextureForPage(double xres,
 {
   int iPage = _current_page;
   std::map<int,GLuint>::iterator it = _pages2textures.find(iPage);
-  if (it != _pages2textures.end())return it->second;
+  if (it != _pages2textures.end()) return it->second;
   if (!_current_doc) return 0;
-  poppler::page *_current_page = _current_doc->create_page (iPage);
+  poppler::page *_current_page = _current_doc->create_page(iPage);
   poppler::page_renderer pr;
-  poppler::image im =  pr.render_page (_current_page,xres,yres,-1,-1,-1);
+  poppler::image im = pr.render_page(_current_page, xres, yres, -1, -1, -1);
   _w = im.width();
   _h = im.height();
   //  im.save("page.png","png");
@@ -59,9 +59,8 @@ GLuint gmshPopplerWrapper::getTextureForPage(double xres,
   _pages2textures[iPage] = texture;
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-
-  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, im.width(),im.height(), 0,
-	       GL_RGBA, GL_UNSIGNED_BYTE,im.const_data());
+  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, im.width(), im.height(), 0,
+	       GL_RGBA, GL_UNSIGNED_BYTE, im.const_data());
   return texture;
 }
 #endif
diff --git a/Common/gmshPopplerWrapper.h b/Common/gmshPopplerWrapper.h
index 0bb46dcb32ad36ebaf2235416d4f786a0fcac80d..288562094d6b964f63e06f56816425d76babae3a 100644
--- a/Common/gmshPopplerWrapper.h
+++ b/Common/gmshPopplerWrapper.h
@@ -27,7 +27,7 @@ private:
   static int _current_page;
   static poppler::document *_current_doc;
   static gmshPopplerWrapper *_instance;
-  static int _w,_h;
+  static int _w, _h;
 #if defined(HAVE_OPENGL)
   static std::map<int,GLuint> _pages2textures; // map pages to textures
 #endif
diff --git a/Graphics/drawContext.cpp b/Graphics/drawContext.cpp
index 13db4f7f0c8e7eef778dbbbba28a13c5185301e7..4bd617c4735b9332cc41576649ea48886c146d67 100644
--- a/Graphics/drawContext.cpp
+++ b/Graphics/drawContext.cpp
@@ -52,7 +52,7 @@ drawContext::drawContext(drawTransform *transform)
   vxmin = vymin = vxmax = vymax = 0.;
   pixel_equiv_x = pixel_equiv_y = 0.;
 
-  _bgImageSize[0] = _bgImageSize[1] = 0;
+  _bgImageTexture = _bgImageW = _bgImageH = 0;
 
   _quadric = 0; // cannot create it here: needs valid opengl context
   _displayLists = 0;
@@ -283,6 +283,7 @@ void drawContext::draw3d()
   if(!CTX::instance()->camera) initPosition();
   drawAxes();
   drawGeom();
+  drawBackgroundImage(true);
   drawMesh();
   drawPost();
 }
@@ -355,83 +356,107 @@ void drawContext::drawBackgroundGradient()
   }
 }
 
-void drawContext::drawBackgroundImage()
+void drawContext::drawBackgroundImage(bool threeD)
 {
-  if(CTX::instance()->bgImageFileName.empty()) return;
+  if(CTX::instance()->bgImageFileName.empty() ||
+     (CTX::instance()->bgImage3d && !threeD) ||
+     (!CTX::instance()->bgImage3d && threeD)) return;
 
-  std::string name = CTX::instance()->bgImageFileName;
+  std::string name = FixRelativePath(GModel::current()->getFileName(),
+                                     CTX::instance()->bgImageFileName);
   std::string ext = SplitFileName(CTX::instance()->bgImageFileName)[2];
 
+  double x = CTX::instance()->bgImagePosition[0];
+  double y = CTX::instance()->bgImagePosition[1];
+  double w = CTX::instance()->bgImageSize[0];
+  double h = CTX::instance()->bgImageSize[1];
+
   if(ext == ".pdf" || ext == ".PDF"){
 #if defined(HAVE_POPPLER)
     if(!gmshPopplerWrapper::instance()->hasFile()){
       if(!gmshPopplerWrapper::instance()->loadFromFile(name)){
         Msg::Error("Could not load PDF file '%s'", name.c_str());
         CTX::instance()->bgImageFileName.clear();
+        return;
       }
     }
-    GLuint texture = gmshPopplerWrapper::getTextureForPage(800,600);
-    glEnable(GL_TEXTURE_2D);
-    glBindTexture(GL_TEXTURE_2D, texture);
-    glBegin(GL_QUADS);
-    glColor4ubv((GLubyte *) & CTX::instance()->color.bg);
-    int dw = viewport[2] - viewport[0];
-    int dh = viewport[3] - viewport[1];
-    int dw_im = gmshPopplerWrapper::width();
-    int dh_im = gmshPopplerWrapper::height();
-    // conserve aspect ratio : dw / dh = dw_im / dh_im
-    //    dw = dh * dw_im / dh_im;
-    dh = dw * dh_im / dw_im;
-    glTexCoord2f(1.0f, 1.0f); glVertex2i(viewport[2],    viewport[3]-dh);
-    glTexCoord2f(1.0f, 0.0f); glVertex2i(viewport[2],    viewport[3]);
-    glTexCoord2f(0.0f, 0.0f); glVertex2i(viewport[2]-dw, viewport[3]);
-    glTexCoord2f(0.0f, 1.0f); glVertex2i(viewport[2]-dw, viewport[3]-dh);
-    glEnd();
+    _bgImageTexture = gmshPopplerWrapper::getTextureForPage(800, 600);
+    _bgImageW = gmshPopplerWrapper::width();
+    _bgImageH = gmshPopplerWrapper::height();
 #endif
   }
   else{
 #if defined(HAVE_FLTK)
-    if(_bgImage.empty()){
+    if(!_bgImageTexture){
       Fl_RGB_Image *img = 0;
       if(ext == ".jpg" || ext == ".JPG" || ext == ".jpeg" || ext == ".JPEG")
         img = new Fl_JPEG_Image(name.c_str());
       else if(ext == ".png" || ext == ".PNG")
         img = new Fl_PNG_Image(name.c_str());
-      if(img && img->d() >= 3){
-        const unsigned char *data = img->array;
-        for(int j = img->h() - 1; j >= 0; j--) {
-          for(int i = 0; i < img->w(); i++) {
-            int idx = j * img->w() * img->d() + i * img->d();
-            _bgImage.push_back((GLfloat)data[idx] / 255.F);
-            _bgImage.push_back((GLfloat)data[idx + 1] / 255.F);
-            _bgImage.push_back((GLfloat)data[idx + 2] / 255.F);
-          }
-        }
-        _bgImageSize[0] = img->w();
-        _bgImageSize[1] = img->h();
+      if(img){
+        glGenTextures(1, &_bgImageTexture);
+        glBindTexture(GL_TEXTURE_2D, _bgImageTexture);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, img->w(), img->h(), 0,
+                     GL_RGB, GL_UNSIGNED_BYTE, img->array);
+        _bgImageW = img->w();
+        _bgImageH = img->h();
+        delete img;
       }
-      if(img) delete img;
-      if(!_bgImageSize[0] || !_bgImageSize[1]){
+      else{
         Msg::Error("Could not load background image '%s'", name.c_str());
         CTX::instance()->bgImageFileName.clear();
+        return;
       }
     }
-    double x = CTX::instance()->bgImagePosition[0];
-    double y = CTX::instance()->bgImagePosition[1];
-    int c = fix2dCoordinates(&x, &y);
-    if(c & 1) x -= _bgImageSize[0] / 2.;
-    if(c & 2) y -= _bgImageSize[1] / 2.;
+#endif
+  }
+
+  if(!_bgImageTexture) return;
+
+  if(w < 0 && h == 0){
+    w = viewport[2] - viewport[0];
+    h = w * _bgImageH / _bgImageW;
+  }
+  else if(h < 0 && w == 0){
+    h = viewport[3] - viewport[1];
+    w = h * _bgImageW / _bgImageH;
+  }
+  else if(h < 0 && w < 0){
+    w = viewport[2] - viewport[0];
+    h = viewport[3] - viewport[1];
+  }
+  else if(h == 0 && w == 0){
+    w = _bgImageW;
+    h = _bgImageH;
+  }
+
+  glEnable(GL_TEXTURE_2D);
+  glBindTexture(GL_TEXTURE_2D, _bgImageTexture);
+  glBegin(GL_QUADS);
+  glColor4ubv((GLubyte *) & CTX::instance()->color.bg);
+  if(threeD){
+    if(w <= 0) w = _bgImageW;
+    if(h <= 0) h = _bgImageH;
+    glTexCoord2f(1.0f, 1.0f); glVertex2d(x+w, y);
+    glTexCoord2f(1.0f, 0.0f); glVertex2d(x+w, y+h);
+    glTexCoord2f(0.0f, 0.0f); glVertex2d(x, y+h);
+    glTexCoord2f(0.0f, 1.0f); glVertex2d(x, y);
+  }
+  else{
+    int c = fix2dCoordinates(&x, &y); // y=0 now means top
+    if(c & 1) x -= _bgImageW / 2.;
+    if(c & 2) y += _bgImageH / 2.;
     if(x < viewport[0]) x = viewport[0];
     if(y < viewport[1]) y = viewport[1];
-    glRasterPos2d(x, y);
-    glPixelStorei(GL_PACK_ALIGNMENT, 1);
-    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
-    glDrawPixels(_bgImageSize[0], _bgImageSize[1], GL_RGB, GL_FLOAT,
-                 (void*)&_bgImage[0]);
-    gl2psDrawPixels(_bgImageSize[0], _bgImageSize[1], 0, 0, GL_RGB, GL_FLOAT,
-                    (void*)&_bgImage[0]);
+    glTexCoord2f(1.0f, 1.0f); glVertex2d(x+w, y-h);
+    glTexCoord2f(1.0f, 0.0f); glVertex2d(x+w, y);
+    glTexCoord2f(0.0f, 0.0f); glVertex2d(x, y);
+    glTexCoord2f(0.0f, 1.0f); glVertex2d(x, y-h);
   }
-#endif
+  glEnd();
+  glDisable(GL_TEXTURE_2D);
 }
 
 void drawContext::initProjection(int xpick, int ypick, int wpick, int hpick)
@@ -539,7 +564,7 @@ void drawContext::initProjection(int xpick, int ypick, int wpick, int hpick)
       // hack for GL2PS (to make sure that the image is in front of the
       // gradient)
       glTranslated(0., 0., 0.01 * clip_far);
-      drawBackgroundImage();
+      drawBackgroundImage(false);
       glPopMatrix();
       glEnable(GL_DEPTH_TEST);
     }
diff --git a/Graphics/drawContext.h b/Graphics/drawContext.h
index 5fd64428ffebafaed562e4fc07951084adc1b3c7..ffcd9d57fa108394bdc6310eaef04c01cf45e2f6 100644
--- a/Graphics/drawContext.h
+++ b/Graphics/drawContext.h
@@ -110,8 +110,7 @@ class drawContext {
   GLuint _displayLists;
   std::set<GModel*> _hiddenModels;
   std::set<PView*> _hiddenViews;
-  std::vector<GLfloat> _bgImage;
-  int _bgImageSize[2];
+  GLuint _bgImageTexture, _bgImageW, _bgImageH;
  public:
   Camera camera;
   double r[3]; // current Euler angles (in degrees!)
@@ -201,7 +200,7 @@ class drawContext {
   void drawMesh();
   void drawPost();
   void drawBackgroundGradient();
-  void drawBackgroundImage();
+  void drawBackgroundImage(bool moving);
   void drawText2d();
   void drawGraph2d();
   void drawAxis(double xmin, double ymin, double zmin,