[MSPAINT] Implement skew (#4362)
authorKatayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
Mon, 14 Feb 2022 07:18:18 +0000 (16:18 +0900)
committerGitHub <noreply@github.com>
Mon, 14 Feb 2022 07:18:18 +0000 (16:18 +0900)
- Add SkewDIB helper function in dib.cpp.
- Implement Stretch and Skew feature by SelectionModel::StretchSkew and ImageModel::StretchSkew.
- Move ColorKeyedMaskBlt function.
CORE-16634

base/applications/mspaint/dib.cpp
base/applications/mspaint/dib.h
base/applications/mspaint/drawing.cpp
base/applications/mspaint/drawing.h
base/applications/mspaint/history.cpp
base/applications/mspaint/selection.cpp
base/applications/mspaint/selectionmodel.cpp
base/applications/mspaint/selectionmodel.h
base/applications/mspaint/winproc.cpp

index 7110717..01c23fa 100644 (file)
@@ -9,6 +9,7 @@
 /* INCLUDES *********************************************************/
 
 #include "precomp.h"
+#include <math.h>
 
 /* FUNCTIONS ********************************************************/
 
@@ -243,3 +244,58 @@ HBITMAP Rotate90DegreeBlt(HDC hDC1, INT cx, INT cy, BOOL bRight)
     DeleteDC(hDC2);
     return hbm2;
 }
+
+#ifndef M_PI
+    #define M_PI 3.14159265
+#endif
+
+HBITMAP SkewDIB(HDC hDC1, HBITMAP hbm, INT nDegree, BOOL bVertical)
+{
+    if (nDegree == 0)
+        return CopyDIBImage(hbm);
+
+    const double eTan = tan(abs(nDegree) * M_PI / 180);
+
+    BITMAP bm;
+    GetObjectW(hbm, sizeof(bm), &bm);
+    INT cx = bm.bmWidth, cy = bm.bmHeight, dx = 0, dy = 0;
+    if (bVertical)
+        dy = INT(cx * eTan);
+    else
+        dx = INT(cy * eTan);
+
+    if (dx == 0 && dy == 0)
+        return CopyDIBImage(hbm);
+
+    HBITMAP hbmNew = CreateColorDIB(cx + dx, cy + dy, RGB(255, 255, 255));
+    if (!hbmNew)
+        return NULL;
+
+    HDC hDC2 = CreateCompatibleDC(NULL);
+    HGDIOBJ hbm2Old = SelectObject(hDC2, hbmNew);
+    if (bVertical)
+    {
+        for (INT x = 0; x < cx; ++x)
+        {
+            INT delta = INT(x * eTan);
+            if (nDegree > 0)
+                BitBlt(hDC2, x, (dy - delta), 1, cy, hDC1, x, 0, SRCCOPY);
+            else
+                BitBlt(hDC2, x, delta, 1, cy, hDC1, x, 0, SRCCOPY);
+        }
+    }
+    else
+    {
+        for (INT y = 0; y < cy; ++y)
+        {
+            INT delta = INT(y * eTan);
+            if (nDegree > 0)
+                BitBlt(hDC2, (dx - delta), y, cx, 1, hDC1, 0, y, SRCCOPY);
+            else
+                BitBlt(hDC2, delta, y, cx, 1, hDC1, 0, y, SRCCOPY);
+        }
+    }
+    SelectObject(hDC2, hbm2Old);
+    DeleteDC(hDC2);
+    return hbmNew;
+}
index b045be1..e0f8d14 100644 (file)
@@ -29,3 +29,5 @@ void ShowFileLoadError(LPCTSTR name);
 HBITMAP SetBitmapAndInfo(HBITMAP hBitmap, LPCTSTR name, DWORD dwFileSize, BOOL isFile);
 
 HBITMAP Rotate90DegreeBlt(HDC hDC1, INT cx, INT cy, BOOL bRight);
+
+HBITMAP SkewDIB(HDC hDC1, HBITMAP hbm, INT nDegree, BOOL bVertical);
index 0595592..490ff9a 100644 (file)
@@ -307,3 +307,36 @@ Text(HDC hdc, LONG x1, LONG y1, LONG x2, LONG y2, COLORREF fg, COLORREF bg, LPCT
 
     RestoreDC(hdc, iSaveDC); // Restore
 }
+
+BOOL
+ColorKeyedMaskBlt(HDC hdcDest, int nXDest, int nYDest, int nWidth, int nHeight,
+                  HDC hdcSrc, int nXSrc, int nYSrc, HBITMAP hbmMask, int xMask, int yMask,
+                  DWORD dwRop, COLORREF keyColor)
+{
+    HDC hTempDC;
+    HDC hTempDC2;
+    HBITMAP hTempBm;
+    HBRUSH hTempBrush;
+    HBITMAP hTempMask;
+
+    hTempDC = CreateCompatibleDC(hdcSrc);
+    hTempDC2 = CreateCompatibleDC(hdcSrc);
+    hTempBm = CreateCompatibleBitmap(hTempDC, nWidth, nHeight);
+    SelectObject(hTempDC, hTempBm);
+    hTempBrush = CreateSolidBrush(keyColor);
+    SelectObject(hTempDC, hTempBrush);
+    BitBlt(hTempDC, 0, 0, nWidth, nHeight, hdcSrc, nXSrc, nYSrc, SRCCOPY);
+    PatBlt(hTempDC, 0, 0, nWidth, nHeight, PATINVERT);
+    hTempMask = CreateBitmap(nWidth, nHeight, 1, 1, NULL);
+    SelectObject(hTempDC2, hTempMask);
+    BitBlt(hTempDC2, 0, 0, nWidth, nHeight, hTempDC, 0, 0, SRCCOPY);
+    SelectObject(hTempDC, hbmMask);
+    BitBlt(hTempDC2, 0, 0, nWidth, nHeight, hTempDC, xMask, yMask, SRCAND);
+    MaskBlt(hdcDest, nXDest, nYDest, nWidth, nHeight, hdcSrc, nXSrc, nYSrc, hTempMask, xMask, yMask, dwRop);
+    DeleteDC(hTempDC);
+    DeleteDC(hTempDC2);
+    DeleteObject(hTempBm);
+    DeleteObject(hTempBrush);
+    DeleteObject(hTempMask);
+    return TRUE;
+}
index 017b71f..26178a8 100644 (file)
@@ -35,3 +35,8 @@ void RectSel(HDC hdc, LONG x1, LONG y1, LONG x2, LONG y2);
 void SelectionFrame(HDC hdc, LONG x1, LONG y1, LONG x2, LONG y2, COLORREF system_selection_color);
 
 void Text(HDC hdc, LONG x1, LONG y1, LONG x2, LONG y2, COLORREF fg, COLORREF bg, LPCTSTR lpchText, HFONT font, LONG style);
+
+extern BOOL
+ColorKeyedMaskBlt(HDC hdcDest, int nXDest, int nYDest, int nWidth, int nHeight,
+                  HDC hdcSrc, int nXSrc, int nYSrc, HBITMAP hbmMask, int xMask, int yMask,
+                  DWORD dwRop, COLORREF keyColor);
index d771fd3..f0a3691 100644 (file)
@@ -192,7 +192,21 @@ void ImageModel::StretchSkew(int nStretchPercentX, int nStretchPercentY, int nSk
     int oldHeight = GetHeight();
     INT newWidth = oldWidth * nStretchPercentX / 100;
     INT newHeight = oldHeight * nStretchPercentY / 100;
-    Insert(CopyDIBImage(hBms[currInd], newWidth, newHeight));
+    if (oldWidth != newWidth || oldHeight != newHeight)
+    {
+        HBITMAP hbm0 = CopyDIBImage(hBms[currInd], newWidth, newHeight);
+        Insert(hbm0);
+    }
+    if (nSkewDegX)
+    {
+        HBITMAP hbm1 = SkewDIB(hDrawingDC, hBms[currInd], nSkewDegX, FALSE);
+        Insert(hbm1);
+    }
+    if (nSkewDegY)
+    {
+        HBITMAP hbm2 = SkewDIB(hDrawingDC, hBms[currInd], nSkewDegY, TRUE);
+        Insert(hbm2);
+    }
     if (GetWidth() != oldWidth || GetHeight() != oldHeight)
         NotifyDimensionsChanged();
     NotifyImageChanged();
index 992b946..3a87a21 100644 (file)
@@ -21,39 +21,6 @@ const LPCTSTR CSelectionWindow::m_lpszCursorLUT[9] = { /* action to mouse cursor
     IDC_SIZENESW, IDC_SIZENS, IDC_SIZENWSE
 };
 
-BOOL
-ColorKeyedMaskBlt(HDC hdcDest, int nXDest, int nYDest, int nWidth, int nHeight,
-                  HDC hdcSrc, int nXSrc, int nYSrc, HBITMAP hbmMask, int xMask, int yMask,
-                  DWORD dwRop, COLORREF keyColor)
-{
-    HDC hTempDC;
-    HDC hTempDC2;
-    HBITMAP hTempBm;
-    HBRUSH hTempBrush;
-    HBITMAP hTempMask;
-
-    hTempDC = CreateCompatibleDC(hdcSrc);
-    hTempDC2 = CreateCompatibleDC(hdcSrc);
-    hTempBm = CreateCompatibleBitmap(hTempDC, nWidth, nHeight);
-    SelectObject(hTempDC, hTempBm);
-    hTempBrush = CreateSolidBrush(keyColor);
-    SelectObject(hTempDC, hTempBrush);
-    BitBlt(hTempDC, 0, 0, nWidth, nHeight, hdcSrc, nXSrc, nYSrc, SRCCOPY);
-    PatBlt(hTempDC, 0, 0, nWidth, nHeight, PATINVERT);
-    hTempMask = CreateBitmap(nWidth, nHeight, 1, 1, NULL);
-    SelectObject(hTempDC2, hTempMask);
-    BitBlt(hTempDC2, 0, 0, nWidth, nHeight, hTempDC, 0, 0, SRCCOPY);
-    SelectObject(hTempDC, hbmMask);
-    BitBlt(hTempDC2, 0, 0, nWidth, nHeight, hTempDC, xMask, yMask, SRCAND);
-    MaskBlt(hdcDest, nXDest, nYDest, nWidth, nHeight, hdcSrc, nXSrc, nYSrc, hTempMask, xMask, yMask, dwRop);
-    DeleteDC(hTempDC);
-    DeleteDC(hTempDC2);
-    DeleteObject(hTempBm);
-    DeleteObject(hTempBrush);
-    DeleteObject(hTempMask);
-    return TRUE;
-}
-
 void CSelectionWindow::ForceRefreshSelectionContents()
 {
     if (::IsWindowVisible(selectionWindow))
index c65ae21..3cd41e1 100644 (file)
@@ -124,9 +124,6 @@ void SelectionModel::DrawBackgroundRect(HDC hDCImage, COLORREF crBg)
     Rect(hDCImage, m_rcSrc.left, m_rcSrc.top, m_rcSrc.right, m_rcSrc.bottom, crBg, crBg, 0, 1);
 }
 
-extern BOOL
-ColorKeyedMaskBlt(HDC hdcDest, int nXDest, int nYDest, int nWidth, int nHeight, HDC hdcSrc, int nXSrc, int nYSrc, HBITMAP hbmMask, int xMask, int yMask, DWORD dwRop, COLORREF keyColor);
-
 void SelectionModel::DrawSelection(HDC hDCImage, COLORREF crBg, BOOL bBgTransparent)
 {
     if (!bBgTransparent)
@@ -239,6 +236,49 @@ void SelectionModel::RotateNTimes90Degrees(int iN)
     NotifyRefreshNeeded();
 }
 
+void SelectionModel::StretchSkew(int nStretchPercentX, int nStretchPercentY, int nSkewDegX, int nSkewDegY)
+{
+    if (nStretchPercentX == 100 && nStretchPercentY == 100 && nSkewDegX == 0 && nSkewDegY == 0)
+        return;
+
+    imageModel.DeleteSelection();
+    imageModel.CopyPrevious();
+
+    INT oldWidth = RECT_WIDTH(m_rcDest);
+    INT oldHeight = RECT_HEIGHT(m_rcDest);
+    INT newWidth = oldWidth * nStretchPercentX / 100;
+    INT newHeight = oldHeight * nStretchPercentY / 100;
+
+    if (oldWidth != newWidth || oldHeight != newHeight)
+    {
+        SelectObject(m_hDC, m_hBm);
+        HBITMAP hbm0 = CopyDIBImage(m_hBm, newWidth, newHeight);
+        InsertFromHBITMAP(hbm0, m_rcDest.left, m_rcDest.top);
+        DeleteObject(hbm0);
+    }
+
+    if (nSkewDegX)
+    {
+        SelectObject(m_hDC, m_hBm);
+        HBITMAP hbm1 = SkewDIB(m_hDC, m_hBm, nSkewDegX, FALSE);
+        InsertFromHBITMAP(hbm1, m_rcDest.left, m_rcDest.top);
+        DeleteObject(hbm1);
+    }
+
+    if (nSkewDegY)
+    {
+        SelectObject(m_hDC, m_hBm);
+        HBITMAP hbm2 = SkewDIB(m_hDC, m_hBm, nSkewDegY, TRUE);
+        InsertFromHBITMAP(hbm2, m_rcDest.left, m_rcDest.top);
+        DeleteObject(hbm2);
+    }
+
+    selectionWindow.ShowWindow(SW_SHOWNOACTIVATE);
+    selectionWindow.ForceRefreshSelectionContents();
+    placeSelWin();
+    NotifyRefreshNeeded();
+}
+
 HBITMAP SelectionModel::GetBitmap() const
 {
     return m_hBm;
index b3a6982..fbb208f 100644 (file)
@@ -54,6 +54,7 @@ public:
     void FlipHorizontally();
     void FlipVertically();
     void RotateNTimes90Degrees(int iN);
+    void StretchSkew(int nStretchPercentX, int nStretchPercentY, int nSkewDegX = 0, int nSkewDegY = 0);
     HBITMAP GetBitmap() const;
     int PtStackSize() const;
     void DrawFramePoly(HDC hDCImage);
index 3bc14f6..74e27a6 100644 (file)
@@ -695,8 +695,16 @@ LRESULT CMainWindow::OnCommand(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bH
         {
             if (stretchSkewDialog.DoModal(mainWindow.m_hWnd))
             {
-                imageModel.StretchSkew(stretchSkewDialog.percentage.x, stretchSkewDialog.percentage.y,
-                                       stretchSkewDialog.angle.x, stretchSkewDialog.angle.y);
+                if (::IsWindowVisible(selectionWindow))
+                {
+                    selectionModel.StretchSkew(stretchSkewDialog.percentage.x, stretchSkewDialog.percentage.y,
+                                               stretchSkewDialog.angle.x, stretchSkewDialog.angle.y);
+                }
+                else
+                {
+                    imageModel.StretchSkew(stretchSkewDialog.percentage.x, stretchSkewDialog.percentage.y,
+                                           stretchSkewDialog.angle.x, stretchSkewDialog.angle.y);
+                }
             }
             break;
         }