Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0ae458e6e1 | |||
| 71a2a2c801 | |||
| 4f85ddac22 | |||
| e2ce1a4d52 |
@@ -24,6 +24,7 @@
|
||||
#include <Base/Interpreter.h>
|
||||
|
||||
#include "SoQtOffscreenRendererPy.h"
|
||||
#include <QBuffer>
|
||||
|
||||
|
||||
using namespace Gui;
|
||||
@@ -166,6 +167,33 @@ Py::Object SoQtOffscreenRendererPy::writeToImage(const Py::Tuple& args)
|
||||
}
|
||||
PYCXX_VARARGS_METHOD_DECL(SoQtOffscreenRendererPy, writeToImage)
|
||||
|
||||
Py::Object SoQtOffscreenRendererPy::writeToBuffer(const Py::Tuple& args)
|
||||
{
|
||||
const char* format = "PNG";
|
||||
int quality = -1;
|
||||
|
||||
if (!PyArg_ParseTuple(args.ptr(), "|si", &format, &quality)) {
|
||||
throw Py::Exception();
|
||||
}
|
||||
|
||||
QImage img;
|
||||
renderer.writeToImage(img);
|
||||
|
||||
QByteArray byteArray;
|
||||
QBuffer buffer(&byteArray);
|
||||
buffer.open(QIODevice::WriteOnly);
|
||||
|
||||
|
||||
if (!img.save(&buffer, format, quality)) {
|
||||
throw Py::RuntimeError("Failed to save image to buffer");
|
||||
}
|
||||
|
||||
buffer.close();
|
||||
|
||||
return Py::Bytes(byteArray.data(), byteArray.size());
|
||||
}
|
||||
PYCXX_VARARGS_METHOD_DECL(SoQtOffscreenRendererPy, writeToBuffer)
|
||||
|
||||
Py::Object SoQtOffscreenRendererPy::getWriteImageFiletypeInfo()
|
||||
{
|
||||
QStringList list = renderer.getWriteImageFiletypeInfo();
|
||||
@@ -209,6 +237,7 @@ void SoQtOffscreenRendererPy::init_type()
|
||||
);
|
||||
PYCXX_ADD_VARARGS_METHOD(render, render, "render(node)");
|
||||
PYCXX_ADD_VARARGS_METHOD(writeToImage, writeToImage, "writeToImage(string)");
|
||||
PYCXX_ADD_VARARGS_METHOD(writeToBuffer, writeToBuffer, "writeToBuffer([string, int]) -> bytes");
|
||||
PYCXX_ADD_NOARGS_METHOD(
|
||||
getWriteImageFiletypeInfo,
|
||||
getWriteImageFiletypeInfo,
|
||||
|
||||
@@ -55,6 +55,7 @@ public:
|
||||
Py::Object render(const Py::Tuple&);
|
||||
|
||||
Py::Object writeToImage(const Py::Tuple&);
|
||||
Py::Object writeToBuffer(const Py::Tuple&);
|
||||
Py::Object getWriteImageFiletypeInfo();
|
||||
|
||||
private:
|
||||
|
||||
@@ -17,6 +17,7 @@ SET(Test_SRCS
|
||||
testmakeWireString.py
|
||||
TestPythonSyntax.py
|
||||
TestPerf.py
|
||||
TestOffscreenRenderer.py
|
||||
TestTreeSelection.py
|
||||
)
|
||||
|
||||
|
||||
@@ -98,4 +98,5 @@ FreeCAD.__unit_test__ += [
|
||||
"Menu.MenuDeleteCases",
|
||||
"Menu.MenuCreateCases",
|
||||
"GuiDocument",
|
||||
"TestOffscreenRenderer",
|
||||
]
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
"""Tests for SoQtOffscreenRenderer, including writeToBuffer."""
|
||||
|
||||
import unittest
|
||||
import FreeCAD
|
||||
import FreeCADGui
|
||||
|
||||
|
||||
class TestOffscreenRendererWriteToBuffer(unittest.TestCase):
|
||||
"""Tests for the writeToBuffer method of SoQtOffscreenRenderer."""
|
||||
|
||||
def setUp(self):
|
||||
from pivy import coin
|
||||
|
||||
self.coin = coin
|
||||
|
||||
# Build a minimal scene: camera + light + a simple shape
|
||||
self.root = coin.SoSeparator()
|
||||
cam = coin.SoOrthographicCamera()
|
||||
light = coin.SoDirectionalLight()
|
||||
cone = coin.SoCone()
|
||||
self.root.addChild(cam)
|
||||
self.root.addChild(light)
|
||||
self.root.addChild(cone)
|
||||
|
||||
width, height = 64, 64
|
||||
viewport = coin.SbViewportRegion(width, height)
|
||||
cam.viewAll(self.root, viewport)
|
||||
|
||||
self.root.ref()
|
||||
self.renderer = FreeCADGui.SoQtOffscreenRenderer(width, height)
|
||||
self.renderer.setBackgroundColor(1.0, 1.0, 1.0)
|
||||
self.renderer.render(self.root)
|
||||
|
||||
def tearDown(self):
|
||||
self.root.unref()
|
||||
|
||||
def testWriteToBufferReturnsBytesDefaultPNG(self):
|
||||
"""writeToBuffer() with no arguments returns non-empty bytes."""
|
||||
result = self.renderer.writeToBuffer()
|
||||
self.assertIsInstance(result, bytes)
|
||||
self.assertGreater(len(result), 0)
|
||||
|
||||
def testWriteToBufferPNGSignature(self):
|
||||
"""writeToBuffer() default output starts with the PNG signature."""
|
||||
result = self.renderer.writeToBuffer()
|
||||
png_signature = b"\x89PNG\r\n\x1a\n"
|
||||
self.assertTrue(
|
||||
result.startswith(png_signature), "Output does not start with PNG signature"
|
||||
)
|
||||
|
||||
def testWriteToBufferExplicitPNG(self):
|
||||
"""writeToBuffer('PNG') produces valid PNG data."""
|
||||
result = self.renderer.writeToBuffer("PNG")
|
||||
png_signature = b"\x89PNG\r\n\x1a\n"
|
||||
self.assertTrue(result.startswith(png_signature))
|
||||
|
||||
def testWriteToBufferBMP(self):
|
||||
"""writeToBuffer('BMP') produces data starting with the BMP signature."""
|
||||
result = self.renderer.writeToBuffer("BMP")
|
||||
self.assertIsInstance(result, bytes)
|
||||
self.assertTrue(result.startswith(b"BM"), "Output does not start with BMP signature")
|
||||
|
||||
def testWriteToBufferJPEG(self):
|
||||
"""writeToBuffer('JPEG') produces data starting with the JPEG SOI marker."""
|
||||
result = self.renderer.writeToBuffer("JPEG")
|
||||
self.assertIsInstance(result, bytes)
|
||||
jpeg_soi = b"\xff\xd8\xff"
|
||||
self.assertTrue(result.startswith(jpeg_soi), "Output does not start with JPEG SOI marker")
|
||||
|
||||
def testWriteToBufferWithQuality(self):
|
||||
"""writeToBuffer('PNG', quality) accepts a quality parameter."""
|
||||
result = self.renderer.writeToBuffer("PNG", 50)
|
||||
self.assertIsInstance(result, bytes)
|
||||
self.assertGreater(len(result), 0)
|
||||
|
||||
def testWriteToBufferInvalidFormatRaises(self):
|
||||
"""writeToBuffer with an unsupported format raises RuntimeError."""
|
||||
with self.assertRaises(RuntimeError):
|
||||
self.renderer.writeToBuffer("NOTAFORMAT")
|
||||
|
||||
def testWriteToBufferConsistentOutput(self):
|
||||
"""Two calls to writeToBuffer produce identical output."""
|
||||
result1 = self.renderer.writeToBuffer("BMP")
|
||||
result2 = self.renderer.writeToBuffer("BMP")
|
||||
self.assertEqual(result1, result2)
|
||||
|
||||
def testWriteToBufferMatchesWriteToImage(self):
|
||||
"""writeToBuffer('PNG') produces the same content as writeToImage for PNG."""
|
||||
import tempfile
|
||||
import os
|
||||
|
||||
buf_data = self.renderer.writeToBuffer("PNG")
|
||||
|
||||
with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp:
|
||||
tmp_path = tmp.name
|
||||
|
||||
try:
|
||||
self.renderer.writeToImage(tmp_path)
|
||||
with open(tmp_path, "rb") as f:
|
||||
file_data = f.read()
|
||||
self.assertEqual(buf_data, file_data)
|
||||
finally:
|
||||
os.unlink(tmp_path)
|
||||
Reference in New Issue
Block a user