cool#13988 sw redline render mode: add colored border for anchored images
So far the non-standard redline render mode for images focused on graying out images when they are meant to be "omitted". In the meantime, text got red/green colors since commit 9bc163b5637572684ac6cc5985d276c4bc01679f (cool#13574 sw redline render mode: somewhat color ins/del as green/red, 2026-01-20), for the case when the inserted/deleted text is not omitted. Do the same for images: if an anchored image is not omitted, then provide a red/green border for deleted/inserted images. This is for anchored images, inline images need more work. Two alternatives would have been to draw this border at the end of SwFlyFrame::PaintSwFrame() (like the standard redline mode does its cross for deletes, but doesn't work due to clipping problems) or as part of SwLayoutFrame::PaintSubsidiaryLines() (like the gray "boundary" indicator does, also has clipping problems). Doing it in SwFrame::PaintSwFrameShadowAndBorder() is free from these clipping problems. Change-Id: I7489e5be5c7081a8e6dc243cb30d8fd2dc4f1917 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/198254 Reviewed-by: Miklos Vajna <vmiklos@collabora.com> Tested-by: Jenkins
This commit is contained in:
@@ -150,17 +150,41 @@ CPPUNIT_TEST_FIXTURE(Test, testRedlineRenderModeOmitInsertDelete)
|
||||
CPPUNIT_ASSERT_EQUAL(120, GetColorHue(aColor3));
|
||||
}
|
||||
|
||||
bool IsGrayScale(const Bitmap& rBitmap)
|
||||
struct ImageInfo
|
||||
{
|
||||
BitmapScopedReadAccess pReadAccess(rBitmap);
|
||||
Size aSize = rBitmap.GetSizePixel();
|
||||
Bitmap m_aBitmap;
|
||||
tools::Rectangle m_aRectangle;
|
||||
};
|
||||
|
||||
bool IsGrayScale(const ImageInfo& rInfo)
|
||||
{
|
||||
Bitmap aBitmap = rInfo.m_aBitmap;
|
||||
BitmapScopedReadAccess pReadAccess(aBitmap);
|
||||
Size aSize = rInfo.m_aBitmap.GetSizePixel();
|
||||
Color aColor = pReadAccess->GetColor(aSize.getHeight() / 2, aSize.getWidth() / 2);
|
||||
return aColor.GetRed() == aColor.GetGreen() && aColor.GetRed() == aColor.GetBlue();
|
||||
}
|
||||
|
||||
std::vector<Bitmap> GetMetaFileImages(const GDIMetaFile& rMetaFile)
|
||||
bool RectangleContainsPolygons(const tools::Rectangle& rRectangle,
|
||||
const std::vector<tools::Polygon>& rPolygons)
|
||||
{
|
||||
std::vector<Bitmap> aImages;
|
||||
static constexpr SwTwips nPixel = 15;
|
||||
tools::Rectangle aRectangle(rRectangle.Left() - nPixel, rRectangle.Top() - nPixel,
|
||||
rRectangle.Right() + nPixel, rRectangle.Bottom() + nPixel);
|
||||
for (const auto& rPolygon : rPolygons)
|
||||
{
|
||||
if (!aRectangle.Contains(rPolygon.GetBoundRect()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<ImageInfo> GetMetaFileImages(const GDIMetaFile& rMetaFile)
|
||||
{
|
||||
std::vector<ImageInfo> aImages;
|
||||
for (size_t nAction = 0; nAction < rMetaFile.GetActionSize(); ++nAction)
|
||||
{
|
||||
MetaAction* pAction = rMetaFile.GetAction(nAction);
|
||||
@@ -170,11 +194,31 @@ std::vector<Bitmap> GetMetaFileImages(const GDIMetaFile& rMetaFile)
|
||||
}
|
||||
|
||||
auto pAct = static_cast<MetaBmpExScaleAction*>(pAction);
|
||||
aImages.push_back(pAct->GetBitmap());
|
||||
ImageInfo aInfo;
|
||||
aInfo.m_aBitmap = pAct->GetBitmap();
|
||||
aInfo.m_aRectangle = { pAct->GetPoint(), pAct->GetSize() };
|
||||
aImages.push_back(aInfo);
|
||||
}
|
||||
return aImages;
|
||||
}
|
||||
|
||||
std::vector<tools::Polygon> GetMetaFilePolylines(const GDIMetaFile& rMetaFile)
|
||||
{
|
||||
std::vector<tools::Polygon> aPolygons;
|
||||
for (size_t nAction = 0; nAction < rMetaFile.GetActionSize(); ++nAction)
|
||||
{
|
||||
MetaAction* pAction = rMetaFile.GetAction(nAction);
|
||||
if (pAction->GetType() != MetaActionType::POLYLINE)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
auto pAct = static_cast<MetaPolyLineAction*>(pAction);
|
||||
aPolygons.push_back(pAct->GetPolygon());
|
||||
}
|
||||
return aPolygons;
|
||||
}
|
||||
|
||||
CPPUNIT_TEST_FIXTURE(Test, testAnchoredImageRedlineRenderModeOmitInsertDelete)
|
||||
{
|
||||
// Given a document with a normal, a deleted and an inserted image:
|
||||
@@ -185,11 +229,14 @@ CPPUNIT_TEST_FIXTURE(Test, testAnchoredImageRedlineRenderModeOmitInsertDelete)
|
||||
std::shared_ptr<GDIMetaFile> xMetaFile = pDocShell->GetPreviewMetaFile();
|
||||
|
||||
// Then make sure none of the images are grayscale:
|
||||
std::vector<Bitmap> aImages = GetMetaFileImages(*xMetaFile);
|
||||
std::vector<ImageInfo> aImages = GetMetaFileImages(*xMetaFile);
|
||||
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(3), aImages.size());
|
||||
CPPUNIT_ASSERT(!IsGrayScale(aImages[0]));
|
||||
CPPUNIT_ASSERT(!IsGrayScale(aImages[1]));
|
||||
CPPUNIT_ASSERT(!IsGrayScale(aImages[2]));
|
||||
std::vector<tools::Polygon> aPolygons = GetMetaFilePolylines(*xMetaFile);
|
||||
// No frames around images.
|
||||
CPPUNIT_ASSERT(aPolygons.empty());
|
||||
|
||||
// Omit insert: default, default, grayscale.
|
||||
SwWrtShell* pWrtShell = pDocShell->GetWrtShell();
|
||||
@@ -206,6 +253,10 @@ CPPUNIT_TEST_FIXTURE(Test, testAnchoredImageRedlineRenderModeOmitInsertDelete)
|
||||
// Without the accompanying fix in place, this test would have failed, the image's center pixel
|
||||
// wasn't gray.
|
||||
CPPUNIT_ASSERT(IsGrayScale(aImages[2]));
|
||||
aPolygons = GetMetaFilePolylines(*xMetaFile);
|
||||
// Frame around the deleted image. This failed, there was no frame around the deleted image.
|
||||
CPPUNIT_ASSERT(!aPolygons.empty());
|
||||
CPPUNIT_ASSERT(RectangleContainsPolygons(aImages[1].m_aRectangle, aPolygons));
|
||||
|
||||
// Omit deletes: default, grayscale, default.
|
||||
aOpt.SetRedlineRenderMode(SwRedlineRenderMode::OmitDeletes);
|
||||
@@ -218,6 +269,10 @@ CPPUNIT_TEST_FIXTURE(Test, testAnchoredImageRedlineRenderModeOmitInsertDelete)
|
||||
CPPUNIT_ASSERT(!IsGrayScale(aImages[0]));
|
||||
CPPUNIT_ASSERT(IsGrayScale(aImages[1]));
|
||||
CPPUNIT_ASSERT(!IsGrayScale(aImages[2]));
|
||||
aPolygons = GetMetaFilePolylines(*xMetaFile);
|
||||
// Frame around the inserted image.
|
||||
CPPUNIT_ASSERT(!aPolygons.empty());
|
||||
CPPUNIT_ASSERT(RectangleContainsPolygons(aImages[2].m_aRectangle, aPolygons));
|
||||
}
|
||||
|
||||
CPPUNIT_TEST_FIXTURE(Test, testInlineImageRedlineRenderModeOmitInsertDelete)
|
||||
@@ -230,7 +285,7 @@ CPPUNIT_TEST_FIXTURE(Test, testInlineImageRedlineRenderModeOmitInsertDelete)
|
||||
std::shared_ptr<GDIMetaFile> xMetaFile = pDocShell->GetPreviewMetaFile();
|
||||
|
||||
// Then make sure none of the images are grayscale:
|
||||
std::vector<Bitmap> aImages = GetMetaFileImages(*xMetaFile);
|
||||
std::vector<ImageInfo> aImages = GetMetaFileImages(*xMetaFile);
|
||||
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(3), aImages.size());
|
||||
CPPUNIT_ASSERT(!IsGrayScale(aImages[0]));
|
||||
CPPUNIT_ASSERT(!IsGrayScale(aImages[1]));
|
||||
|
||||
@@ -313,6 +313,8 @@ public:
|
||||
|
||||
bool IsSplitButNotYetMovedFollow() const;
|
||||
|
||||
bool GetRedlineRenderModeFrame(SvxBoxItem& rBoxItem) const;
|
||||
|
||||
private:
|
||||
void UpdateUnfloatButton(SwWrtShell* pWrtSh, bool bShow) const;
|
||||
void PaintDecorators() const;
|
||||
|
||||
@@ -2330,6 +2330,48 @@ bool SwFlyFrame::IsSplitButNotYetMovedFollow() const
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SwFlyFrame::GetRedlineRenderModeFrame(SvxBoxItem& rBoxItem) const
|
||||
{
|
||||
// If we're in non-standard redline mode, then color deletes and inserts depending on the
|
||||
// redline render mode. Similar to what SwFntObj::DrawText() does for redlined text.
|
||||
const SwViewShell* pViewShell = getRootFrame()->GetCurrShell();
|
||||
if (!pViewShell)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const SwViewOption* pViewOptions = pViewShell->GetViewOptions();
|
||||
if (!pViewOptions)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
SwRedlineRenderMode eRedlineRenderMode = pViewOptions->GetRedlineRenderMode();
|
||||
std::optional<Color> oColor;
|
||||
if (eRedlineRenderMode == SwRedlineRenderMode::OmitInserts && IsDeleted())
|
||||
{
|
||||
oColor.emplace(COL_RED);
|
||||
}
|
||||
else if (eRedlineRenderMode == SwRedlineRenderMode::OmitDeletes && IsInserted())
|
||||
{
|
||||
oColor.emplace(COL_GREEN);
|
||||
}
|
||||
if (!oColor)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
editeng::SvxBorderLine aBorderLine;
|
||||
aBorderLine.SetWidth(1);
|
||||
aBorderLine.SetBorderLineStyle(SvxBorderLineStyle::SOLID);
|
||||
aBorderLine.SetColor(*oColor);
|
||||
rBoxItem.SetLine(&aBorderLine, SvxBoxItemLine::LEFT);
|
||||
rBoxItem.SetLine(&aBorderLine, SvxBoxItemLine::RIGHT);
|
||||
rBoxItem.SetLine(&aBorderLine, SvxBoxItemLine::TOP);
|
||||
rBoxItem.SetLine(&aBorderLine, SvxBoxItemLine::BOTTOM);
|
||||
return true;
|
||||
}
|
||||
|
||||
SwTwips SwFlyFrame::Grow_(SwTwips nDist, SwResizeLimitReason& reason, bool bTst)
|
||||
{
|
||||
if (!Lower())
|
||||
|
||||
@@ -4323,8 +4323,11 @@ void SwFlyFrame::PaintSwFrame(vcl::RenderContext& rRenderContext, SwRect const&
|
||||
}
|
||||
}
|
||||
// paint of margin needed.
|
||||
const SwViewOption* pViewOptions = pShell ? pShell->GetViewOptions() : nullptr;
|
||||
SwRedlineRenderMode eRedlineRenderMode = pViewOptions ? pViewOptions->GetRedlineRenderMode()
|
||||
: SwRedlineRenderMode::Standard;
|
||||
const bool bPaintMarginOnly( !bPaintCompleteBack &&
|
||||
getFramePrintArea().SSize() != getFrameArea().SSize() );
|
||||
(getFramePrintArea().SSize() != getFrameArea().SSize() || eRedlineRenderMode != SwRedlineRenderMode::Standard));
|
||||
|
||||
// #i47804# - paint background of parent fly frame
|
||||
// for transparent graphics in layer Hell, if parent fly frame isn't
|
||||
@@ -5515,7 +5518,8 @@ void SwFrame::PaintSwFrameShadowAndBorder(
|
||||
if (GetType() & (SwFrameType::NoTxt|SwFrameType::Row|SwFrameType::Body|SwFrameType::Footnote|SwFrameType::Column|SwFrameType::Root))
|
||||
return;
|
||||
|
||||
if (IsCellFrame() && !gProp.pSGlobalShell->GetViewOptions()->IsTable())
|
||||
const SwViewOption& rViewOptions = *gProp.pSGlobalShell->GetViewOptions();
|
||||
if (IsCellFrame() && !rViewOptions.IsTable())
|
||||
return;
|
||||
|
||||
// #i29550#
|
||||
@@ -5534,7 +5538,8 @@ void SwFrame::PaintSwFrameShadowAndBorder(
|
||||
return;
|
||||
}
|
||||
|
||||
const bool bLine = rAttrs.IsLine();
|
||||
SwRedlineRenderMode eRedlineRenderMode = rViewOptions.GetRedlineRenderMode();
|
||||
const bool bLine = (rAttrs.IsLine() || (IsFlyFrame() && eRedlineRenderMode != SwRedlineRenderMode::Standard));
|
||||
const bool bShadow = rAttrs.GetShadow().GetLocation() != SvxShadowLocation::NONE;
|
||||
|
||||
// - flag to control,
|
||||
@@ -5613,6 +5618,21 @@ void SwFrame::PaintSwFrameShadowAndBorder(
|
||||
const SvxBorderLine* pTopBorder(rBox.GetTop());
|
||||
const SvxBorderLine* pBottomBorder(rBox.GetBottom());
|
||||
|
||||
auto pFlyFrame = IsFlyFrame() ? static_cast<const SwFlyFrame*>(this) : nullptr;
|
||||
SvxBoxItem aBoxItem(RES_BOX);
|
||||
if (pFlyFrame)
|
||||
{
|
||||
// This is a fly frame, see if it wants to paint a custom border based on the redline
|
||||
// mode and status.
|
||||
if (pFlyFrame->GetRedlineRenderModeFrame(aBoxItem))
|
||||
{
|
||||
pLeftBorder = aBoxItem.GetLeft();
|
||||
pRightBorder = aBoxItem.GetRight();
|
||||
pTopBorder = aBoxItem.GetTop();
|
||||
pBottomBorder = aBoxItem.GetBottom();
|
||||
}
|
||||
}
|
||||
|
||||
// if R2L, exchange Right/Left
|
||||
const bool bR2L(IsCellFrame() && IsRightToLeft());
|
||||
|
||||
|
||||
Reference in New Issue
Block a user