Add CFX_RenderDevice::AutoRestorer()

Avoid cleanup on every return path.

Change-Id: I6978adb6f31020d812ac88c5d46c703d1461d373
Reviewed-on: https://pdfium-review.googlesource.com/4435
Reviewed-by: Lei Zhang <thestig@chromium.org>
Reviewed-by: Tom Sepez <tsepez@chromium.org>
Commit-Queue: Tom Sepez <tsepez@chromium.org>
diff --git a/core/fpdfapi/render/cpdf_rendercontext.cpp b/core/fpdfapi/render/cpdf_rendercontext.cpp
index 07af2cc..e4ef9af 100644
--- a/core/fpdfapi/render/cpdf_rendercontext.cpp
+++ b/core/fpdfapi/render/cpdf_rendercontext.cpp
@@ -64,34 +64,24 @@
                                 const CPDF_RenderOptions* pOptions,
                                 const CFX_Matrix* pLastMatrix) {
   for (auto& layer : m_Layers) {
-    pDevice->SaveState();
+    CFX_RenderDevice::StateRestorer restorer(pDevice);
+    CPDF_RenderStatus status;
     if (pLastMatrix) {
       CFX_Matrix FinalMatrix = layer.m_Matrix;
       FinalMatrix.Concat(*pLastMatrix);
-      CPDF_RenderStatus status;
       status.Initialize(this, pDevice, pLastMatrix, pStopObj, nullptr, nullptr,
                         pOptions, layer.m_pObjectHolder->m_Transparency, false,
                         nullptr);
       status.RenderObjectList(layer.m_pObjectHolder, &FinalMatrix);
-      if (status.m_Options.m_Flags & RENDER_LIMITEDIMAGECACHE)
-        m_pPageCache->CacheOptimization(status.m_Options.m_dwLimitCacheSize);
-      if (status.m_bStopped) {
-        pDevice->RestoreState(false);
-        break;
-      }
     } else {
-      CPDF_RenderStatus status;
       status.Initialize(this, pDevice, nullptr, pStopObj, nullptr, nullptr,
                         pOptions, layer.m_pObjectHolder->m_Transparency, false,
                         nullptr);
       status.RenderObjectList(layer.m_pObjectHolder, &layer.m_Matrix);
-      if (status.m_Options.m_Flags & RENDER_LIMITEDIMAGECACHE)
-        m_pPageCache->CacheOptimization(status.m_Options.m_dwLimitCacheSize);
-      if (status.m_bStopped) {
-        pDevice->RestoreState(false);
-        break;
-      }
     }
-    pDevice->RestoreState(false);
+    if (status.m_Options.m_Flags & RENDER_LIMITEDIMAGECACHE)
+      m_pPageCache->CacheOptimization(status.m_Options.m_dwLimitCacheSize);
+    if (status.m_bStopped)
+      break;
   }
 }
diff --git a/core/fpdfapi/render/cpdf_renderstatus.cpp b/core/fpdfapi/render/cpdf_renderstatus.cpp
index 465105a..d41561c 100644
--- a/core/fpdfapi/render/cpdf_renderstatus.cpp
+++ b/core/fpdfapi/render/cpdf_renderstatus.cpp
@@ -1252,10 +1252,11 @@
                     &m_Options, m_Transparency, m_bDropObjects, pResources,
                     false);
   status.m_curBlend = m_curBlend;
-  m_pDevice->SaveState();
-  status.RenderObjectList(pFormObj->m_pForm.get(), &matrix);
-  m_bStopped = status.m_bStopped;
-  m_pDevice->RestoreState(false);
+  {
+    CFX_RenderDevice::StateRestorer restorer(m_pDevice);
+    status.RenderObjectList(pFormObj->m_pForm.get(), &matrix);
+    m_bStopped = status.m_bStopped;
+  }
 #if defined _SKIA_SUPPORT_
   DebugVerifyDeviceIsPreMultiplied();
 #endif
@@ -1876,9 +1877,9 @@
                           pFormResource, false, pType3Char, fill_argb);
         status.m_Type3FontCache = m_Type3FontCache;
         status.m_Type3FontCache.push_back(pType3Font);
-        m_pDevice->SaveState();
+
+        CFX_RenderDevice::StateRestorer restorer(m_pDevice);
         status.RenderObjectList(pType3Char->m_pForm.get(), &matrix);
-        m_pDevice->RestoreState(false);
       } else {
         CFX_FloatRect rect_f = pType3Char->m_pForm->CalcBoundingBox();
         matrix.TransformRect(rect_f);
@@ -2131,22 +2132,19 @@
   if (!pattern->Load())
     return;
 
-  m_pDevice->SaveState();
+  CFX_RenderDevice::StateRestorer restorer(m_pDevice);
   if (pPageObj->IsPath()) {
-    if (!SelectClipPath(pPageObj->AsPath(), pObj2Device, bStroke)) {
-      m_pDevice->RestoreState(false);
+    if (!SelectClipPath(pPageObj->AsPath(), pObj2Device, bStroke))
       return;
-    }
   } else if (pPageObj->IsImage()) {
     m_pDevice->SetClip_Rect(pPageObj->GetBBox(pObj2Device));
   } else {
     return;
   }
   FX_RECT rect;
-  if (GetObjectClippedRect(pPageObj, pObj2Device, false, rect)) {
-    m_pDevice->RestoreState(false);
+  if (GetObjectClippedRect(pPageObj, pObj2Device, false, rect))
     return;
-  }
+
   CFX_Matrix matrix = *pattern->pattern_to_form();
   matrix.Concat(*pObj2Device);
   GetScaledMatrix(matrix);
@@ -2155,7 +2153,6 @@
                                  : pPageObj->m_GeneralState.GetFillAlpha()));
   DrawShading(pattern, &matrix, rect, alpha,
               m_Options.m_ColorMode == RENDER_COLOR_ALPHA);
-  m_pDevice->RestoreState(false);
 }
 
 void CPDF_RenderStatus::ProcessShading(const CPDF_ShadingObject* pShadingObj,
@@ -2180,22 +2177,20 @@
   if (!pPattern->Load())
     return;
 
-  m_pDevice->SaveState();
+  CFX_RenderDevice::StateRestorer restorer(m_pDevice);
   if (pPageObj->IsPath()) {
-    if (!SelectClipPath(pPageObj->AsPath(), pObj2Device, bStroke)) {
-      m_pDevice->RestoreState(false);
+    if (!SelectClipPath(pPageObj->AsPath(), pObj2Device, bStroke))
       return;
-    }
   } else if (pPageObj->IsImage()) {
     m_pDevice->SetClip_Rect(pPageObj->GetBBox(pObj2Device));
   } else {
     return;
   }
+
   FX_RECT clip_box = m_pDevice->GetClipBox();
-  if (clip_box.IsEmpty()) {
-    m_pDevice->RestoreState(false);
+  if (clip_box.IsEmpty())
     return;
-  }
+
   CFX_Matrix dCTM = m_pDevice->GetCTM();
   float sa = fabs(dCTM.a);
   float sd = fabs(dCTM.d);
@@ -2254,17 +2249,15 @@
         CFX_Matrix matrix = *pObj2Device;
         matrix.Translate(original.x - mtPattern2Device.e,
                          original.y - mtPattern2Device.f);
-        m_pDevice->SaveState();
+        CFX_RenderDevice::StateRestorer restorer2(m_pDevice);
         CPDF_RenderStatus status;
         status.Initialize(m_pContext, m_pDevice, nullptr, nullptr, this,
                           pStates.get(), &m_Options,
                           pPattern->form()->m_Transparency, m_bDropObjects,
                           pFormResource);
         status.RenderObjectList(pPattern->form(), &matrix);
-        m_pDevice->RestoreState(false);
       }
     }
-    m_pDevice->RestoreState(false);
     return;
   }
   if (bAligned) {
@@ -2299,10 +2292,9 @@
         m_pContext->GetDocument(), m_pContext->GetPageCache(), pPattern,
         pObj2Device, width, height, m_Options.m_Flags);
   }
-  if (!pPatternBitmap) {
-    m_pDevice->RestoreState(false);
+  if (!pPatternBitmap)
     return;
-  }
+
   if (m_Options.m_ColorMode == RENDER_COLOR_GRAY) {
     pPatternBitmap->ConvertColorScale(m_Options.m_ForeColor,
                                       m_Options.m_BackColor);
@@ -2364,7 +2356,6 @@
   }
   CompositeDIBitmap(pScreen, clip_box.left, clip_box.top, 0, 255,
                     FXDIB_BLEND_NORMAL, false);
-  m_pDevice->RestoreState(false);
 }
 
 void CPDF_RenderStatus::DrawPathWithPattern(CPDF_PathObject* pPathObj,
diff --git a/core/fxge/cfx_renderdevice.h b/core/fxge/cfx_renderdevice.h
index 794c2fe..6aa4528 100644
--- a/core/fxge/cfx_renderdevice.h
+++ b/core/fxge/cfx_renderdevice.h
@@ -83,6 +83,17 @@
 
 class CFX_RenderDevice {
  public:
+  class StateRestorer {
+   public:
+    explicit StateRestorer(CFX_RenderDevice* pDevice) : m_pDevice(pDevice) {
+      m_pDevice->SaveState();
+    }
+    ~StateRestorer() { m_pDevice->RestoreState(false); }
+
+   private:
+    CFX_RenderDevice* m_pDevice;
+  };
+
   CFX_RenderDevice();
   virtual ~CFX_RenderDevice();
 
diff --git a/fpdfsdk/fpdfformfill.cpp b/fpdfsdk/fpdfformfill.cpp
index 6610f81..a050dc0 100644
--- a/fpdfsdk/fpdfformfill.cpp
+++ b/fpdfsdk/fpdfformfill.cpp
@@ -115,37 +115,37 @@
 #endif
   CFX_RetainPtr<CFX_DIBitmap> holder(CFXBitmapFromFPDFBitmap(bitmap));
   pDevice->Attach(holder, false, nullptr, false);
-  pDevice->SaveState();
-  pDevice->SetClip_Rect(clip);
+  {
+    CFX_RenderDevice::StateRestorer restorer(pDevice.get());
+    pDevice->SetClip_Rect(clip);
 
-  CPDF_RenderOptions options;
-  if (flags & FPDF_LCD_TEXT)
-    options.m_Flags |= RENDER_CLEARTYPE;
-  else
-    options.m_Flags &= ~RENDER_CLEARTYPE;
+    CPDF_RenderOptions options;
+    if (flags & FPDF_LCD_TEXT)
+      options.m_Flags |= RENDER_CLEARTYPE;
+    else
+      options.m_Flags &= ~RENDER_CLEARTYPE;
 
-  // Grayscale output
-  if (flags & FPDF_GRAYSCALE) {
-    options.m_ColorMode = RENDER_COLOR_GRAY;
-    options.m_ForeColor = 0;
-    options.m_BackColor = 0xffffff;
-  }
-  options.m_AddFlags = flags >> 8;
-  options.m_bDrawAnnots = flags & FPDF_ANNOT;
+    // Grayscale output
+    if (flags & FPDF_GRAYSCALE) {
+      options.m_ColorMode = RENDER_COLOR_GRAY;
+      options.m_ForeColor = 0;
+      options.m_BackColor = 0xffffff;
+    }
+    options.m_AddFlags = flags >> 8;
+    options.m_bDrawAnnots = flags & FPDF_ANNOT;
 
 #ifdef PDF_ENABLE_XFA
-  options.m_pOCContext =
-      pdfium::MakeRetain<CPDF_OCContext>(pPDFDoc, CPDF_OCContext::View);
-  if (CPDFSDK_PageView* pPageView = pFormFillEnv->GetPageView(pPage, true))
-    pPageView->PageView_OnDraw(pDevice.get(), &matrix, &options, clip);
+    options.m_pOCContext =
+        pdfium::MakeRetain<CPDF_OCContext>(pPDFDoc, CPDF_OCContext::View);
+    if (CPDFSDK_PageView* pPageView = pFormFillEnv->GetPageView(pPage, true))
+      pPageView->PageView_OnDraw(pDevice.get(), &matrix, &options, clip);
 #else   // PDF_ENABLE_XFA
-  options.m_pOCContext = pdfium::MakeRetain<CPDF_OCContext>(
-      pPage->m_pDocument, CPDF_OCContext::View);
-  if (CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, pPage))
-    pPageView->PageView_OnDraw(pDevice.get(), &matrix, &options);
+    options.m_pOCContext = pdfium::MakeRetain<CPDF_OCContext>(
+        pPage->m_pDocument, CPDF_OCContext::View);
+    if (CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, pPage))
+      pPageView->PageView_OnDraw(pDevice.get(), &matrix, &options);
 #endif  // PDF_ENABLE_XFA
-
-  pDevice->RestoreState(false);
+  }
 #ifdef _SKIA_SUPPORT_PATHS_
   pDevice->Flush();
   holder->UnPreMultiply();
diff --git a/fpdfsdk/fxedit/fxet_edit.cpp b/fpdfsdk/fxedit/fxet_edit.cpp
index 5b84c9b..cf4fdc3 100644
--- a/fpdfsdk/fxedit/fxet_edit.cpp
+++ b/fpdfsdk/fxedit/fxet_edit.cpp
@@ -681,7 +681,7 @@
   CFX_ByteTextBuf sTextBuf;
   int32_t nFontIndex = -1;
   CFX_PointF ptBT;
-  pDevice->SaveState();
+  CFX_RenderDevice::StateRestorer restorer(pDevice);
   if (!rcClip.IsEmpty()) {
     CFX_FloatRect rcTemp = rcClip;
     pUser2Device->TransformRect(rcTemp);
@@ -770,8 +770,6 @@
                      sTextBuf.MakeString(), crOldFill, nHorzScale);
     }
   }
-
-  pDevice->RestoreState(false);
 }
 
 CFX_Edit::CFX_Edit()
diff --git a/xfa/fxfa/cxfa_ffwidget.cpp b/xfa/fxfa/cxfa_ffwidget.cpp
index 06de436..b74e1dc 100644
--- a/xfa/fxfa/cxfa_ffwidget.cpp
+++ b/xfa/fxfa/cxfa_ffwidget.cpp
@@ -886,8 +886,7 @@
     rtFit.top = rtImage.bottom() - rtImage.height;
   }
   CFX_RenderDevice* pRenderDevice = pGS->GetRenderDevice();
-  pRenderDevice->SaveState();
-
+  CFX_RenderDevice::StateRestorer restorer(pRenderDevice);
   CFX_PathData path;
   path.AppendRect(rtImage.left, rtImage.bottom(), rtImage.right(), rtImage.top);
   pRenderDevice->SetClip_PathFill(&path, pMatrix, FXFILL_WINDING);
@@ -898,12 +897,12 @@
   mtImage.Concat(*pMatrix);
 
   CXFA_ImageRenderer imageRender;
-  bool bRet = imageRender.Start(pRenderDevice, pDIBitmap, 0, 255, &mtImage,
-                                FXDIB_INTERPOL);
-  while (bRet)
-    bRet = imageRender.Continue(nullptr);
-
-  pRenderDevice->RestoreState(false);
+  if (!imageRender.Start(pRenderDevice, pDIBitmap, 0, 255, &mtImage,
+                         FXDIB_INTERPOL)) {
+    return;
+  }
+  while (imageRender.Continue(nullptr))
+    continue;
 }
 
 static const uint8_t g_inv_base64[128] = {
diff --git a/xfa/fxgraphics/cfx_graphics.cpp b/xfa/fxgraphics/cfx_graphics.cpp
index a3821dd..b7595ee 100644
--- a/xfa/fxgraphics/cfx_graphics.cpp
+++ b/xfa/fxgraphics/cfx_graphics.cpp
@@ -375,11 +375,9 @@
                         m_info.fillColor->m_info.pattern->m_foreArgb);
     }
   }
-
-  m_renderDevice->SaveState();
+  CFX_RenderDevice::StateRestorer restorer(m_renderDevice);
   m_renderDevice->SetClip_PathFill(path->GetPathData(), matrix, fillMode);
   SetDIBitsWithMatrix(bmp, &pattern->m_matrix);
-  m_renderDevice->RestoreState(false);
 }
 
 void CFX_Graphics::FillPathWithShading(CFX_Path* path,
@@ -494,10 +492,9 @@
     }
   }
   if (result) {
-    m_renderDevice->SaveState();
+    CFX_RenderDevice::StateRestorer restorer(m_renderDevice);
     m_renderDevice->SetClip_PathFill(path->GetPathData(), matrix, fillMode);
     SetDIBitsWithMatrix(bmp, matrix);
-    m_renderDevice->RestoreState(false);
   }
 }