diff options
author | Regina Henschel <rb.henschel@t-online.de> | 2024-03-15 00:00:51 +0100 |
---|---|---|
committer | Regina Henschel <rb.henschel@t-online.de> | 2024-03-20 18:06:35 +0100 |
commit | 2eabd56ff5f65733c6fa1bcef14d4a39c21dddc7 (patch) | |
tree | 7e33716d2e9eb5c29aba17444db7e601c8d48e4d /oox/qa | |
parent | f29222eaf385891620d4868827b27e734752018e (diff) |
tdf#70039 import material of extruded shapes
This patch continues the tdf#70039 related commits 98b06ed3 and 6e5529d7.
It adds further property-value pairs to the property map which is later
used in CustomShapeProperties.pushToPropSet() for property Extrusion.
OOXML defines a set of 15 material presets in section 20.1.10.50
ST_PresetMaterialType. The specification lists some values and it has
example pictures. The example shape uses Bevel. That is not implemented,
thus the examples are not really usable. Microsoft specifies the values
it uses in section 2.1.1331 in MS-OI29500] - v20231113. The values used
in the patch are based on these resources.
MS Office defines the material by the properties 'Specular-',
'Diffuse-', 'Ambient-' and 'Emissive-Color, 'Specular Power', 'Blinn
Hightlight', 'Diffuse-' and 'Alpha-Fresnel', and 'Metal'. But ODF, API
and current implementation have for material only the properties
'Diffusion', 'Specularity', 'Shininess', 'Metal' and 'MetalType'. The
patch tries to map the values as well as possible.
The mapping works well for the legacy-foo materials which reflect the
material in binary MS Office and is base of ODF and our implementation.
But the preset has also material where transparency is added to the
reflected light or the reflected light is blended or brightened with
White. That is not possible currently and such shapes look different
than in MS Office. These are especially the materials in the
'Translucent' section in the UI of MS Office. The material type 'flat'
uses 'Emissive Color'. Such is not available at all. So material 'flat'
looks noticable different too.
More details about the used values is contained in the attachment in
the bug report.
Change-Id: I16a242446cbe98efcbdf5452058e1a3bd88dcf56
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/164853
Tested-by: Jenkins
Reviewed-by: Patrick Luby <guibomacdev@gmail.com>
Reviewed-by: Regina Henschel <rb.henschel@t-online.de>
Diffstat (limited to 'oox/qa')
-rw-r--r-- | oox/qa/unit/data/Scene3d_material_highlight.pptx | bin | 0 -> 19845 bytes | |||
-rw-r--r-- | oox/qa/unit/data/Scene3d_material_wireframe.pptx | bin | 0 -> 15288 bytes | |||
-rw-r--r-- | oox/qa/unit/testscene3d.cxx | 168 |
3 files changed, 113 insertions, 55 deletions
diff --git a/oox/qa/unit/data/Scene3d_material_highlight.pptx b/oox/qa/unit/data/Scene3d_material_highlight.pptx Binary files differnew file mode 100644 index 000000000000..c299c21bc68c --- /dev/null +++ b/oox/qa/unit/data/Scene3d_material_highlight.pptx diff --git a/oox/qa/unit/data/Scene3d_material_wireframe.pptx b/oox/qa/unit/data/Scene3d_material_wireframe.pptx Binary files differnew file mode 100644 index 000000000000..f72baeb25178 --- /dev/null +++ b/oox/qa/unit/data/Scene3d_material_wireframe.pptx diff --git a/oox/qa/unit/testscene3d.cxx b/oox/qa/unit/testscene3d.cxx index 5ca452ea3d21..48b968ba23cd 100644 --- a/oox/qa/unit/testscene3d.cxx +++ b/oox/qa/unit/testscene3d.cxx @@ -43,7 +43,7 @@ protected: uno::Reference<drawing::XShape> getShape(sal_uInt8 nShapeIndex, sal_uInt8 nPageIndex); // Converts the shape 0 on page 0 to a bitmap and returns this bitmap. // It assumes, that shape 0 on page 0 is the only shape. - void getShapeAsBitmap(BitmapEx& rBMP); + void getShapeAsBitmap(BitmapEx& rBMP, sal_uInt8 nShapeIndex); }; uno::Reference<drawing::XShape> TestScene3d::getShape(sal_uInt8 nShapeIndex, sal_uInt8 nPageIndex) @@ -60,20 +60,20 @@ uno::Reference<drawing::XShape> TestScene3d::getShape(sal_uInt8 nShapeIndex, sal return xShape; } -void TestScene3d::getShapeAsBitmap(BitmapEx& rBMP) +void TestScene3d::getShapeAsBitmap(BitmapEx& rBMP, sal_uInt8 nShapeIndex) { SfxViewShell* pViewShell = SfxViewShell::Current(); SdrView* pSdrView = pViewShell->GetDrawView(); // Mark object and convert it to bitmap - uno::Reference<drawing::XShape> xShape3D(getShape(0, 0)); + uno::Reference<drawing::XShape> xShape3D(getShape(nShapeIndex, 0)); SdrObject* pSdrShape(SdrObject::getSdrObjectFromXShape(xShape3D)); pSdrView->MarkObj(pSdrShape, pSdrView->GetSdrPageView()); dispatchCommand(mxComponent, ".uno:ConvertIntoBitmap", {}); pSdrView->UnmarkAll(); // Get graphic - uno::Reference<drawing::XShape> xShapeBmp(getShape(0, 0)); + uno::Reference<drawing::XShape> xShapeBmp(getShape(nShapeIndex, 0)); SdrGrafObj* pGrafObj = dynamic_cast<SdrGrafObj*>(SdrObject::getSdrObjectFromXShape(xShapeBmp)); CPPUNIT_ASSERT_MESSAGE("No image object created", pGrafObj); const Graphic& rGraphic = pGrafObj->GetGraphic(); @@ -81,6 +81,28 @@ void TestScene3d::getShapeAsBitmap(BitmapEx& rBMP) CPPUNIT_ASSERT_MESSAGE("No bitmap", !rBMP.IsEmpty()); } +namespace +{ +void lcl_AssertColorsApproximateEqual(const ::Color& aExpected, const ::Color& aActual) +{ + // Currently (March 2024), the import of lighting and material is only approximately possible. + // Thus colors are not identical. When the import will be improved, the tolerances should be + // reduced. The test uses HSB instead of RGB, because differences in hue are more irritating and + // should be detected as a priority. That is not possible with GetColorError() method. + sal_uInt16 nExpH; + sal_uInt16 nExpS; + sal_uInt16 nExpB; + sal_uInt16 nActH; + sal_uInt16 nActS; + sal_uInt16 nActB; + aExpected.RGBtoHSB(nExpH, nExpS, nExpB); + aActual.RGBtoHSB(nActH, nActS, nActB); + CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Hue", nExpH, nActH, 2); + CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Saturation", nExpS, nActS, 13); + CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Brightness", nExpB, nActB, 11); +} +} // end anonymous namespace + CPPUNIT_TEST_FIXTURE(TestScene3d, test_isometricRightUp) { // Given a document with a scene3d element on a shape. Without the fix, the shape was imported as @@ -357,21 +379,17 @@ CPPUNIT_TEST_FIXTURE(TestScene3d, test_lightRig_modernCamera) // The test assumes rendering with ShadeMode_FLAT. loadFromFile(u"Scene3d_lightRig_modernCamera.pptx"); BitmapEx aBMP; - getShapeAsBitmap(aBMP); + getShapeAsBitmap(aBMP, 0); // Size in pixel depends on dpi. Thus calculate positions relative to size. // Color in center sal_Int32 nX = 0.5 * aBMP.GetSizePixel().Width(); sal_Int32 nY = 0.5 * aBMP.GetSizePixel().Height(); - ::Color aExpectedCenter(248, 226, 212); - CPPUNIT_ASSERT_MESSAGE("center color wrong", - aExpectedCenter.GetColorError(aBMP.GetPixelColor(nX, nY)) < 7); + lcl_AssertColorsApproximateEqual(::Color(247, 225, 211), aBMP.GetPixelColor(nX, nY)); // Color left nX = 0.046122 * aBMP.GetSizePixel().Width(); nY = 0.5 * aBMP.GetSizePixel().Height(); - ::Color aExpectedLeft(0, 105, 48); - CPPUNIT_ASSERT_MESSAGE("left color wrong", - aExpectedLeft.GetColorError(aBMP.GetPixelColor(nX, nY)) < 7); + lcl_AssertColorsApproximateEqual(::Color(0, 103, 47), aBMP.GetPixelColor(nX, nY)); } CPPUNIT_TEST_FIXTURE(TestScene3d, test_lightRig_legacyCamera) @@ -383,21 +401,17 @@ CPPUNIT_TEST_FIXTURE(TestScene3d, test_lightRig_legacyCamera) // The test assumes rendering with ShadeMode_FLAT. loadFromFile(u"Scene3d_lightRig_legacyCamera.pptx"); BitmapEx aBMP; - getShapeAsBitmap(aBMP); + getShapeAsBitmap(aBMP, 0); // Size in pixel depends on dpi. Thus calculate positions relative to size. // Color in center sal_Int32 nX = 0.5 * aBMP.GetSizePixel().Width(); sal_Int32 nY = 0.5 * aBMP.GetSizePixel().Height(); - ::Color aExpectedCenter(96, 88, 82); - CPPUNIT_ASSERT_MESSAGE("center color wrong", - aExpectedCenter.GetColorError(aBMP.GetPixelColor(nX, nY)) < 7); + lcl_AssertColorsApproximateEqual(::Color(94, 86, 80), aBMP.GetPixelColor(nX, nY)); // Color left nX = 0.046122 * aBMP.GetSizePixel().Width(); nY = 0.5 * aBMP.GetSizePixel().Height(); - ::Color aExpectedLeft(0, 180, 82); - CPPUNIT_ASSERT_MESSAGE("left color wrong", - aExpectedLeft.GetColorError(aBMP.GetPixelColor(nX, nY)) < 7); + lcl_AssertColorsApproximateEqual(::Color(5, 185, 87), aBMP.GetPixelColor(nX, nY)); } CPPUNIT_TEST_FIXTURE(TestScene3d, test_lightRig_default) @@ -408,27 +422,21 @@ CPPUNIT_TEST_FIXTURE(TestScene3d, test_lightRig_default) // The test assumes rendering with ShadeMode_FLAT. loadFromFile(u"Scene3d_lightRig_default.pptx"); BitmapEx aBMP; - getShapeAsBitmap(aBMP); + getShapeAsBitmap(aBMP, 0); // Size in pixel depends on dpi. Thus calculate positions relative to size. // Front color sal_Int32 nX = 0.93811 * aBMP.GetSizePixel().Width(); sal_Int32 nY = 0.49904 * aBMP.GetSizePixel().Height(); - ::Color aExpectedFront(165, 187, 150); - CPPUNIT_ASSERT_MESSAGE("front color wrong", - aExpectedFront.GetColorError(aBMP.GetPixelColor(nX, nY)) < 7); + lcl_AssertColorsApproximateEqual(::Color(165, 187, 150), aBMP.GetPixelColor(nX, nY)); // Left color nX = 0.078176 * aBMP.GetSizePixel().Width(); nY = 0.49904 * aBMP.GetSizePixel().Height(); - ::Color aExpectedLeft(255, 189, 74); - CPPUNIT_ASSERT_MESSAGE("left color wrong", - aExpectedLeft.GetColorError(aBMP.GetPixelColor(nX, nY)) < 7); + lcl_AssertColorsApproximateEqual(::Color(255, 189, 74), aBMP.GetPixelColor(nX, nY)); // Top color nX = 0.48860 * aBMP.GetSizePixel().Width(); nY = 0.069098 * aBMP.GetSizePixel().Height(); - ::Color aExpectedTop(189, 100, 39); - CPPUNIT_ASSERT_MESSAGE("top color wrong", - aExpectedTop.GetColorError(aBMP.GetPixelColor(nX, nY)) < 7); + lcl_AssertColorsApproximateEqual(::Color(189, 100, 39), aBMP.GetPixelColor(nX, nY)); } CPPUNIT_TEST_FIXTURE(TestScene3d, test_lightRig_dir_rotation) @@ -439,27 +447,21 @@ CPPUNIT_TEST_FIXTURE(TestScene3d, test_lightRig_dir_rotation) // and MSO. The test assumes rendering with ShadeMode_FLAT. loadFromFile(u"Scene3d_lightRig_dir_rotation.pptx"); BitmapEx aBMP; - getShapeAsBitmap(aBMP); + getShapeAsBitmap(aBMP, 0); // Size in pixel depends on dpi. Thus calculate positions relative to size. // Front color sal_Int32 nX = 0.93811 * aBMP.GetSizePixel().Width(); sal_Int32 nY = 0.49904 * aBMP.GetSizePixel().Height(); - ::Color aExpectedFront(165, 187, 150); - CPPUNIT_ASSERT_MESSAGE("front color wrong", - aExpectedFront.GetColorError(aBMP.GetPixelColor(nX, nY)) < 7); + lcl_AssertColorsApproximateEqual(::Color(165, 187, 150), aBMP.GetPixelColor(nX, nY)); // Left color nX = 0.078176 * aBMP.GetSizePixel().Width(); nY = 0.49904 * aBMP.GetSizePixel().Height(); - ::Color aExpectedLeft(206, 108, 42); - CPPUNIT_ASSERT_MESSAGE("left color wrong", - aExpectedLeft.GetColorError(aBMP.GetPixelColor(nX, nY)) < 7); + lcl_AssertColorsApproximateEqual(::Color(206, 108, 42), aBMP.GetPixelColor(nX, nY)); // Top color nX = 0.48860 * aBMP.GetSizePixel().Width(); nY = 0.069098 * aBMP.GetSizePixel().Height(); - ::Color aExpectedTop(255, 189, 74); - CPPUNIT_ASSERT_MESSAGE("top color wrong", - aExpectedTop.GetColorError(aBMP.GetPixelColor(nX, nY)) < 7); + lcl_AssertColorsApproximateEqual(::Color(255, 189, 74), aBMP.GetPixelColor(nX, nY)); } CPPUNIT_TEST_FIXTURE(TestScene3d, test_lightRig_rot_rotation) @@ -469,32 +471,88 @@ CPPUNIT_TEST_FIXTURE(TestScene3d, test_lightRig_rot_rotation) // The test assumes rendering with ShadeMode_FLAT. loadFromFile(u"Scene3d_lightRig_rot_rotation.pptx"); BitmapEx aBMP; - getShapeAsBitmap(aBMP); + getShapeAsBitmap(aBMP, 0); // Size in pixel depends on dpi. Thus calculate positions relative to size. // Front color, same as in MS Office sal_Int32 nX = 0.93811 * aBMP.GetSizePixel().Width(); sal_Int32 nY = 0.49904 * aBMP.GetSizePixel().Height(); - ::Color aExpectedFront(88, 100, 80); - CPPUNIT_ASSERT_MESSAGE("center color wrong", - aExpectedFront.GetColorError(aBMP.GetPixelColor(nX, nY)) < 7); - // Left color, different from MS Office - // The rotation brings the second light in a position, that it contributes to the left face. - // Because the light is specular in MS Office, but current LibreOffice cannot make a second - // light specular, the colors in MS Office and LibreOffice differ noticeably. MS Office has - // here rgb(255, 214, 99). The expected color is the color in LibreOffice as of March 2024. - // The test needs to be updated, when LibreOffice rendering is improved. + lcl_AssertColorsApproximateEqual(::Color(88, 100, 80), aBMP.GetPixelColor(nX, nY)); + // Left color nX = 0.078176 * aBMP.GetSizePixel().Width(); nY = 0.49904 * aBMP.GetSizePixel().Height(); - ::Color aExpectedLeft(255, 191, 75); - CPPUNIT_ASSERT_MESSAGE("left color wrong", - aExpectedLeft.GetColorError(aBMP.GetPixelColor(nX, nY)) < 7); - // Top color, same as in MS Office + lcl_AssertColorsApproximateEqual(::Color(255, 214, 99), aBMP.GetPixelColor(nX, nY)); + // Top color nX = 0.48860 * aBMP.GetSizePixel().Width(); nY = 0.069098 * aBMP.GetSizePixel().Height(); - ::Color aExpectedTop(106, 56, 22); - CPPUNIT_ASSERT_MESSAGE("top color wrong", - aExpectedTop.GetColorError(aBMP.GetPixelColor(nX, nY)) < 7); + lcl_AssertColorsApproximateEqual(::Color(106, 56, 22), aBMP.GetPixelColor(nX, nY)); +} + +CPPUNIT_TEST_FIXTURE(TestScene3d, test_material_highlight) +{ + // The file contains six shapes with same geometry and fill and line color. The scenes use the + // camera 'orthographicFront' and the lightRig 'twoPt'. The test looks at an area of highlight + // and at an area outside the hightlight. + + loadFromFile(u"Scene3d_material_highlight.pptx"); + + BitmapEx aBMP; + getShapeAsBitmap(aBMP, 0); // material legacyPlastic + sal_Int32 nX = 0.75 * aBMP.GetSizePixel().Width(); + sal_Int32 nYhigh = 0.25 * aBMP.GetSizePixel().Height(); + sal_Int32 nYsoft = 0.75 * aBMP.GetSizePixel().Height(); + lcl_AssertColorsApproximateEqual(::Color(255, 255, 255), aBMP.GetPixelColor(nX, nYhigh)); + lcl_AssertColorsApproximateEqual(::Color(130, 95, 70), aBMP.GetPixelColor(nX, nYsoft)); + + // same geometry, thus nX, nYhigh and nYsoft unchanged + getShapeAsBitmap(aBMP, 1); // material warmMatte + lcl_AssertColorsApproximateEqual(::Color(253, 200, 164), aBMP.GetPixelColor(nX, nYhigh)); + lcl_AssertColorsApproximateEqual(::Color(132, 96, 71), aBMP.GetPixelColor(nX, nYsoft)); + + getShapeAsBitmap(aBMP, 2); // material metal + lcl_AssertColorsApproximateEqual(::Color(255, 255, 255), aBMP.GetPixelColor(nX, nYhigh)); + lcl_AssertColorsApproximateEqual(::Color(132, 96, 71), aBMP.GetPixelColor(nX, nYsoft)); + + getShapeAsBitmap(aBMP, 3); // material matte + lcl_AssertColorsApproximateEqual(::Color(190, 138, 102), aBMP.GetPixelColor(nX, nYhigh)); + lcl_AssertColorsApproximateEqual(::Color(130, 95, 70), aBMP.GetPixelColor(nX, nYsoft)); + + getShapeAsBitmap(aBMP, 4); // material dkEdge + lcl_AssertColorsApproximateEqual(::Color(255, 255, 255), aBMP.GetPixelColor(nX, nYhigh)); + lcl_AssertColorsApproximateEqual(::Color(115, 84, 62), aBMP.GetPixelColor(nX, nYsoft)); + + getShapeAsBitmap(aBMP, 5); // material legacyMetal + lcl_AssertColorsApproximateEqual(::Color(255, 255, 220), aBMP.GetPixelColor(nX, nYhigh)); + lcl_AssertColorsApproximateEqual(::Color(86, 63, 46), aBMP.GetPixelColor(nX, nYsoft)); +} + +CPPUNIT_TEST_FIXTURE(TestScene3d, test_material_wireframe) +{ + // Given a document with a shape in 3D mode with material legacyWireframe. + // It uses a projection "Oblique: Top Left". + loadFromFile(u"Scene3d_material_wireframe.pptx"); + uno::Reference<drawing::XShape> xShape(getShape(0, 0)); + + // Make sure the export to ODF has the corresponding attributes. + save("impress8"); + xmlDocUniquePtr pXmlDoc = parseExport("content.xml"); + + assertXPath(pXmlDoc, + "/office:document-content/office:body/office:presentation/draw:page/" + "draw:custom-shape/draw:enhanced-geometry"_ostr, + "extrusion-origin"_ostr, "-0.5 -0.5"); + assertXPath(pXmlDoc, + "/office:document-content/office:body/office:presentation/draw:page/" + "draw:custom-shape/draw:enhanced-geometry"_ostr, + "extrusion-skew"_ostr, "30 -45"); + assertXPath(pXmlDoc, + "/office:document-content/office:body/office:presentation/draw:page/" + "draw:custom-shape/draw:enhanced-geometry"_ostr, + "projection"_ostr, "parallel"); + assertXPath(pXmlDoc, + "/office:document-content/office:body/office:presentation/draw:page/" + "draw:custom-shape/draw:enhanced-geometry"_ostr, + "shade-mode"_ostr, "draft"); } CPPUNIT_PLUGIN_IMPLEMENT(); |