mirror of
https://github.com/JakubMelka/PDF4QT.git
synced 2025-01-29 16:49:32 +01:00
Free form gourad triangle shading
This commit is contained in:
parent
40290fd2ec
commit
dc6bc3e96c
@ -110,8 +110,7 @@ QImage PDFAbstractColorSpace::getImage(const PDFImageData& imageData) const
|
||||
throw PDFParserException(PDFTranslationContext::tr("Invalid size of the decoded array. Expected %1, actual %2.").arg(componentCount * 2).arg(decode.size()));
|
||||
}
|
||||
|
||||
QDataStream stream(const_cast<QByteArray*>(&imageData.getData()), QIODevice::ReadOnly);
|
||||
PDFBitReader reader(&stream, imageData.getBitsPerComponent());
|
||||
PDFBitReader reader(&imageData.getData(), imageData.getBitsPerComponent());
|
||||
|
||||
PDFColor color;
|
||||
color.resize(componentCount);
|
||||
@ -176,8 +175,7 @@ QImage PDFAbstractColorSpace::getImage(const PDFImageData& imageData) const
|
||||
throw PDFParserException(PDFTranslationContext::tr("Invalid size of the decoded array. Expected %1, actual %2.").arg(componentCount * 2).arg(decode.size()));
|
||||
}
|
||||
|
||||
QDataStream stream(const_cast<QByteArray*>(&imageData.getData()), QIODevice::ReadOnly);
|
||||
PDFBitReader reader(&stream, imageData.getBitsPerComponent());
|
||||
PDFBitReader reader(&imageData.getData(), imageData.getBitsPerComponent());
|
||||
|
||||
PDFColor color;
|
||||
color.resize(componentCount);
|
||||
@ -822,8 +820,7 @@ QImage PDFIndexedColorSpace::getImage(const PDFImageData& imageData) const
|
||||
image.fill(QColor(Qt::white));
|
||||
|
||||
unsigned int componentCount = imageData.getComponents();
|
||||
QDataStream stream(const_cast<QByteArray*>(&imageData.getData()), QIODevice::ReadOnly);
|
||||
PDFBitReader reader(&stream, imageData.getBitsPerComponent());
|
||||
PDFBitReader reader(&imageData.getData(), imageData.getBitsPerComponent());
|
||||
|
||||
if (componentCount != getColorComponentCount())
|
||||
{
|
||||
|
@ -547,8 +547,7 @@ QImage PDFImage::getImage() const
|
||||
QImage image(m_imageData.getWidth(), m_imageData.getHeight(), QImage::Format_Alpha8);
|
||||
|
||||
const bool flip01 = !m_imageData.getDecode().empty() && qFuzzyCompare(m_imageData.getDecode().front(), 1.0);
|
||||
QDataStream stream(const_cast<QByteArray*>(&m_imageData.getData()), QIODevice::ReadOnly);
|
||||
PDFBitReader reader(&stream, m_imageData.getBitsPerComponent());
|
||||
PDFBitReader reader(&m_imageData.getData(), m_imageData.getBitsPerComponent());
|
||||
|
||||
for (unsigned int i = 0, rowCount = m_imageData.getHeight(); i < rowCount; ++i)
|
||||
{
|
||||
|
@ -613,7 +613,7 @@ void PDFPageContentProcessor::processPathPainting(const QPainterPath& path, bool
|
||||
// We must create a mesh and then draw pattern
|
||||
PDFMeshQualitySettings settings;
|
||||
settings.deviceSpaceMeshingArea = getPageBoundingRectDeviceSpace();
|
||||
settings.userSpaceToDeviceSpaceMatrix = getPatternBaseMatrix();
|
||||
settings.userSpaceToDeviceSpaceMatrix = getCurrentWorldMatrix();
|
||||
settings.initDefaultResolution();
|
||||
|
||||
PDFMesh mesh = shadingPatern->createMesh(settings);
|
||||
@ -665,7 +665,7 @@ void PDFPageContentProcessor::processPathPainting(const QPainterPath& path, bool
|
||||
// We must create a mesh and then draw pattern
|
||||
PDFMeshQualitySettings settings;
|
||||
settings.deviceSpaceMeshingArea = getPageBoundingRectDeviceSpace();
|
||||
settings.userSpaceToDeviceSpaceMatrix = getPatternBaseMatrix();
|
||||
settings.userSpaceToDeviceSpaceMatrix = getCurrentWorldMatrix();
|
||||
settings.initDefaultResolution();
|
||||
|
||||
PDFMesh mesh = shadingPatern->createMesh(settings);
|
||||
|
@ -289,6 +289,82 @@ PDFPatternPtr PDFPattern::createShadingPattern(const PDFDictionary* colorSpaceDi
|
||||
return result;
|
||||
}
|
||||
|
||||
case ShadingType::FreeFormGouradTriangle:
|
||||
case ShadingType::LatticeFormGouradTriangle:
|
||||
{
|
||||
PDFFreeFormGouradTriangleShading* freeFormGouradTriangleShading = nullptr;
|
||||
PDFLatticeFormGouradTriangleShading* latticeFormGouradTriangleShading = nullptr;
|
||||
PDFGouradTriangleShading* gouradTriangleShading = nullptr;
|
||||
|
||||
if (shadingType == ShadingType::FreeFormGouradTriangle)
|
||||
{
|
||||
freeFormGouradTriangleShading = new PDFFreeFormGouradTriangleShading();
|
||||
gouradTriangleShading = freeFormGouradTriangleShading;
|
||||
}
|
||||
else
|
||||
{
|
||||
Q_ASSERT(shadingType == ShadingType::LatticeFormGouradTriangle);
|
||||
latticeFormGouradTriangleShading = new PDFLatticeFormGouradTriangleShading();
|
||||
gouradTriangleShading = latticeFormGouradTriangleShading;
|
||||
}
|
||||
PDFPatternPtr result(gouradTriangleShading);
|
||||
|
||||
PDFInteger bitsPerCoordinate = loader.readIntegerFromDictionary(shadingDictionary, "BitsPerCoordinate", -1);
|
||||
if (!contains(bitsPerCoordinate, std::initializer_list<PDFInteger>{ 1, 2, 4, 8, 12, 16, 24, 32 }))
|
||||
{
|
||||
throw PDFParserException(PDFTranslationContext::tr("Invalid bits per coordinate (%1) for gourad triangle meshing.").arg(bitsPerCoordinate));
|
||||
}
|
||||
PDFInteger bitsPerComponent = loader.readIntegerFromDictionary(shadingDictionary, "BitsPerComponent", -1);
|
||||
if (!contains(bitsPerComponent, std::initializer_list<PDFInteger>{ 1, 2, 4, 8, 12, 16 }))
|
||||
{
|
||||
throw PDFParserException(PDFTranslationContext::tr("Invalid bits per component (%1) for gourad triangle meshing.").arg(bitsPerComponent));
|
||||
}
|
||||
|
||||
std::vector<PDFReal> decode = loader.readNumberArrayFromDictionary(shadingDictionary, "Decode");
|
||||
if (!functions.empty())
|
||||
{
|
||||
if (decode.size() != 6)
|
||||
{
|
||||
throw PDFParserException(PDFTranslationContext::tr("Invalid domain of gourad triangle meshing. Expected size is 6, actual size is %1.").arg(decode.size()));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const size_t expectedSize = colorSpace->getColorComponentCount() * 2 + 4;
|
||||
if (decode.size() != expectedSize)
|
||||
{
|
||||
throw PDFParserException(PDFTranslationContext::tr("Invalid domain of gourad triangle meshing. Expected size is %1, actual size is %2.").arg(expectedSize).arg(decode.size()));
|
||||
}
|
||||
}
|
||||
|
||||
gouradTriangleShading->m_antiAlias = antialias;
|
||||
gouradTriangleShading->m_backgroundColor = backgroundColor;
|
||||
gouradTriangleShading->m_colorSpace = colorSpace;
|
||||
gouradTriangleShading->m_patternGraphicState = patternGraphicState;
|
||||
gouradTriangleShading->m_bitsPerCoordinate = static_cast<uint8_t>(bitsPerCoordinate);
|
||||
gouradTriangleShading->m_bitsPerComponent = static_cast<uint8_t>(bitsPerComponent);
|
||||
gouradTriangleShading->m_xmin = decode[0];
|
||||
gouradTriangleShading->m_xmax = decode[1];
|
||||
gouradTriangleShading->m_ymin = decode[2];
|
||||
gouradTriangleShading->m_ymax = decode[3];
|
||||
gouradTriangleShading->m_limits = std::vector<PDFReal>(std::next(decode.cbegin(), 4), decode.cend());
|
||||
gouradTriangleShading->m_colorComponentCount = !functions.empty() ? 1 : colorSpace->getColorComponentCount();
|
||||
gouradTriangleShading->m_functions = qMove(functions);
|
||||
gouradTriangleShading->m_data = document->getDecodedStream(stream);
|
||||
|
||||
if (shadingType == ShadingType::FreeFormGouradTriangle)
|
||||
{
|
||||
PDFInteger bitsPerFlag = loader.readIntegerFromDictionary(shadingDictionary, "BitsPerFlag", -1);
|
||||
if (!contains(bitsPerFlag, std::initializer_list<PDFInteger>{ 2, 4, 8 }))
|
||||
{
|
||||
throw PDFParserException(PDFTranslationContext::tr("Invalid bits per flag (%1) for gourad triangle meshing.").arg(bitsPerFlag));
|
||||
}
|
||||
freeFormGouradTriangleShading->m_bitsPerFlag = bitsPerFlag;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
throw PDFParserException(PDFTranslationContext::tr("Invalid shading pattern type (%1).").arg(static_cast<PDFInteger>(shadingType)));
|
||||
@ -1188,6 +1264,267 @@ PDFMesh PDFRadialShading::createMesh(const PDFMeshQualitySettings& settings) con
|
||||
return mesh;
|
||||
}
|
||||
|
||||
ShadingType PDFFreeFormGouradTriangleShading::getShadingType() const
|
||||
{
|
||||
return ShadingType::FreeFormGouradTriangle;
|
||||
}
|
||||
|
||||
PDFMesh PDFFreeFormGouradTriangleShading::createMesh(const PDFMeshQualitySettings& settings) const
|
||||
{
|
||||
PDFMesh mesh;
|
||||
|
||||
size_t bitsPerVertex = m_bitsPerFlag + 2 * m_bitsPerCoordinate + m_colorComponentCount * m_bitsPerComponent;
|
||||
size_t remainder = (8 - (bitsPerVertex % 8)) % 8;
|
||||
bitsPerVertex += remainder;
|
||||
size_t bytesPerVertex = bitsPerVertex / 8;
|
||||
size_t vertexCount = m_data.size() / bytesPerVertex;
|
||||
|
||||
if (vertexCount < 3)
|
||||
{
|
||||
// No mesh produced
|
||||
return mesh;
|
||||
}
|
||||
|
||||
// We have 3 vertices for start triangle, then for each new vertex, we get
|
||||
// a new triangle, or, based on flags, no triangle (if new triangle is processed)
|
||||
size_t triangleCount = vertexCount - 2;
|
||||
mesh.reserve(0, triangleCount);
|
||||
|
||||
struct VertexData
|
||||
{
|
||||
uint32_t index = 0;
|
||||
uint8_t flags = 0;
|
||||
QPointF position;
|
||||
PDFColor color;
|
||||
};
|
||||
|
||||
const PDFReal vertexScaleRatio = 1.0 / double((static_cast<uint64_t>(1) << m_bitsPerCoordinate) - 1);
|
||||
const PDFReal xScaleRatio = (m_xmax - m_xmin) * vertexScaleRatio;
|
||||
const PDFReal yScaleRatio = (m_ymax - m_ymin) * vertexScaleRatio;
|
||||
const PDFReal colorScaleRatio = 1.0 / double((static_cast<uint64_t>(1) << m_bitsPerComponent) - 1);
|
||||
|
||||
std::vector<VertexData> vertices;
|
||||
vertices.resize(vertexCount);
|
||||
std::vector<size_t> indices(vertexCount, 0);
|
||||
std::iota(indices.begin(), indices.end(), static_cast<size_t>(0));
|
||||
std::vector<QPointF> meshVertices;
|
||||
meshVertices.resize(vertexCount);
|
||||
|
||||
auto readVertex = [this, &vertices, &settings, &meshVertices, bytesPerVertex, xScaleRatio, yScaleRatio, colorScaleRatio](size_t index)
|
||||
{
|
||||
PDFBitReader reader(&m_data, 8);
|
||||
reader.seek(index * bytesPerVertex);
|
||||
|
||||
VertexData data;
|
||||
data.index = static_cast<uint32_t>(index);
|
||||
data.flags = reader.read(m_bitsPerFlag);
|
||||
const PDFReal x = m_xmin + (reader.read(m_bitsPerCoordinate)) * xScaleRatio;
|
||||
const PDFReal y = m_ymin + (reader.read(m_bitsPerCoordinate)) * yScaleRatio;
|
||||
data.position = settings.userSpaceToDeviceSpaceMatrix.map(QPointF(x, y));
|
||||
data.color.resize(m_colorComponentCount);
|
||||
meshVertices[index] = data.position;
|
||||
|
||||
for (size_t i = 0; i < m_colorComponentCount; ++i)
|
||||
{
|
||||
const double cMin = m_limits[2 * i + 0];
|
||||
const double cMax = m_limits[2 * i + 1];
|
||||
data.color[i] = cMin + (reader.read(m_bitsPerComponent)) * (cMax - cMin) * colorScaleRatio;
|
||||
}
|
||||
|
||||
if (!m_functions.empty())
|
||||
{
|
||||
Q_ASSERT(m_colorComponentCount == 1);
|
||||
const PDFReal t = data.color[0];
|
||||
std::vector<PDFReal> colorBuffer(m_colorSpace->getColorComponentCount(), 0.0);
|
||||
if (m_functions.size() == 1)
|
||||
{
|
||||
PDFFunction::FunctionResult result = m_functions.front()->apply(&t, &t + 1, colorBuffer.data(), colorBuffer.data() + colorBuffer.size());
|
||||
if (!result)
|
||||
{
|
||||
throw PDFRendererException(RenderErrorType::Error, PDFTranslationContext::tr("Error occured during mesh creation of shading: %1").arg(result.errorMessage));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (size_t i = 0, count = colorBuffer.size(); i < count; ++i)
|
||||
{
|
||||
PDFFunction::FunctionResult result = m_functions[i]->apply(&t, &t + 1, colorBuffer.data() + i, colorBuffer.data() + i + 1);
|
||||
if (!result)
|
||||
{
|
||||
throw PDFRendererException(RenderErrorType::Error, PDFTranslationContext::tr("Error occured during mesh creation of shading: %1").arg(result.errorMessage));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data.color = PDFAbstractColorSpace::convertToColor(colorBuffer);
|
||||
}
|
||||
|
||||
vertices[index] = qMove(data);
|
||||
};
|
||||
|
||||
std::for_each(std::execution::parallel_policy(), indices.begin(), indices.end(), readVertex);
|
||||
mesh.setVertices(qMove(meshVertices));
|
||||
|
||||
vertices.front().flags = 0;
|
||||
|
||||
const VertexData* va = nullptr;
|
||||
const VertexData* vb = nullptr;
|
||||
const VertexData* vc = nullptr;
|
||||
const VertexData* vd = nullptr;
|
||||
|
||||
auto addTriangle = [this, &settings, &mesh, &vertices](const VertexData* va, const VertexData* vb, const VertexData* vc)
|
||||
{
|
||||
const uint32_t via = va->index;
|
||||
const uint32_t vib = vb->index;
|
||||
const uint32_t vic = vc->index;
|
||||
|
||||
addSubdividedTriangles(settings, mesh, via, vib, vic, va->color, vb->color, vc->color);
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < vertexCount;)
|
||||
{
|
||||
vd = &vertices[i];
|
||||
|
||||
switch (vd->flags)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
if (i + 2 >= vertexCount)
|
||||
{
|
||||
throw PDFRendererException(RenderErrorType::Error, PDFTranslationContext::tr("Invalid free form gourad triangle data stream - not enough vertices."));
|
||||
}
|
||||
va = vd;
|
||||
vb = &vertices[i + 1];
|
||||
vc = &vertices[i + 2];
|
||||
i += 3;
|
||||
addTriangle(va, vb, vc);
|
||||
break;
|
||||
}
|
||||
|
||||
case 1:
|
||||
{
|
||||
// Triangle vb, vc, vd
|
||||
va = vb;
|
||||
vb = vc;
|
||||
vc = vd;
|
||||
++i;
|
||||
addTriangle(va, vb, vc);
|
||||
break;
|
||||
}
|
||||
|
||||
case 2:
|
||||
{
|
||||
// Triangle va, vc, vd
|
||||
vb = vc;
|
||||
vc = vd;
|
||||
++i;
|
||||
addTriangle(va, vb, vc);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
throw PDFRendererException(RenderErrorType::Error, PDFTranslationContext::tr("Invalid free form gourad triangle data stream - invalid vertex flag %1.").arg(vd->flags));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return mesh;
|
||||
}
|
||||
|
||||
ShadingType PDFLatticeFormGouradTriangleShading::getShadingType() const
|
||||
{
|
||||
return ShadingType::LatticeFormGouradTriangle;
|
||||
}
|
||||
|
||||
PDFMesh PDFLatticeFormGouradTriangleShading::createMesh(const PDFMeshQualitySettings& settings) const
|
||||
{
|
||||
PDFMesh mesh;
|
||||
return mesh;
|
||||
}
|
||||
|
||||
void PDFGouradTriangleShading::addSubdividedTriangles(const PDFMeshQualitySettings& settings,
|
||||
PDFMesh& mesh, uint32_t v1, uint32_t v2, uint32_t v3,
|
||||
PDFColor c1, PDFColor c2, PDFColor c3) const
|
||||
{
|
||||
// First, verify, if we can subdivide the triangle
|
||||
QLineF v12(mesh.getVertex(v1), mesh.getVertex(v2));
|
||||
QLineF v13(mesh.getVertex(v1), mesh.getVertex(v3));
|
||||
QLineF v23(mesh.getVertex(v2), mesh.getVertex(v3));
|
||||
|
||||
const qreal length12 = v12.length();
|
||||
const qreal length13 = v13.length();
|
||||
const qreal length23 = v23.length();
|
||||
const qreal maxLength = qMax(length12, qMax(length13, length23));
|
||||
|
||||
const bool isColorEqual = PDFAbstractColorSpace::isColorEqual(c1, c2, settings.tolerance) &&
|
||||
PDFAbstractColorSpace::isColorEqual(c1, c3, settings.tolerance) &&
|
||||
PDFAbstractColorSpace::isColorEqual(c2, c3, settings.tolerance);
|
||||
const bool canSubdivide = maxLength >= settings.minimalMeshResolution * 2.0; // If we subdivide, we will have length at least settings.minimalMeshResolution
|
||||
|
||||
|
||||
if (!isColorEqual && canSubdivide)
|
||||
{
|
||||
if (length23 == maxLength)
|
||||
{
|
||||
// We split line (v2, v3), create two triangles, (v1, v2, vx) and (v1, v3, vx), where
|
||||
// vx is centerpoint of line (v2, v3). We also interpolate colors.
|
||||
QPointF x = v23.center();
|
||||
PDFColor cx = PDFAbstractColorSpace::mixColors(c2, c3, 0.5);
|
||||
const uint32_t vx = mesh.addVertex(x);
|
||||
|
||||
addSubdividedTriangles(settings, mesh, v1, v2, vx, c1, c2, cx);
|
||||
addSubdividedTriangles(settings, mesh, v1, v3, vx, c1, c3, cx);
|
||||
}
|
||||
else if (length13 == maxLength)
|
||||
{
|
||||
// We split line (v1, v3), create two triangles, (v1, v2, vx) and (v2, v3, vx), where
|
||||
// vx is centerpoint of line (v1, v3). We also interpolate colors.
|
||||
QPointF x = v13.center();
|
||||
PDFColor cx = PDFAbstractColorSpace::mixColors(c1, c3, 0.5);
|
||||
const uint32_t vx = mesh.addVertex(x);
|
||||
|
||||
addSubdividedTriangles(settings, mesh, v1, v2, vx, c1, c2, cx);
|
||||
addSubdividedTriangles(settings, mesh, v2, v3, vx, c2, c3, cx);
|
||||
}
|
||||
else
|
||||
{
|
||||
Q_ASSERT(length12 == maxLength);
|
||||
|
||||
// We split line (v1, v2), create two triangles, (v1, v3, vx) and (v2, v3, vx), where
|
||||
// vx is centerpoint of line (v1, v2). We also interpolate colors.
|
||||
QPointF x = v12.center();
|
||||
PDFColor cx = PDFAbstractColorSpace::mixColors(c1, c2, 0.5);
|
||||
const uint32_t vx = mesh.addVertex(x);
|
||||
|
||||
addSubdividedTriangles(settings, mesh, v1, v3, vx, c1, c3, cx);
|
||||
addSubdividedTriangles(settings, mesh, v2, v3, vx, c2, c3, cx);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const size_t colorComponents = c1.size();
|
||||
|
||||
// Calculate color - interpolate 3 vertex colors
|
||||
PDFColor color;
|
||||
color.resize(colorComponents);
|
||||
|
||||
constexpr const PDFReal coefficient = 1.0 / 3.0;
|
||||
for (size_t i = 0; i < colorComponents; ++i)
|
||||
{
|
||||
color[i] = (c1[i] + c2[i] + c3[i]) * coefficient;
|
||||
}
|
||||
Q_ASSERT(colorComponents == m_colorSpace->getColorComponentCount());
|
||||
QColor transformedColor = m_colorSpace->getColor(color);
|
||||
|
||||
PDFMesh::Triangle triangle;
|
||||
triangle.v1 = v1;
|
||||
triangle.v2 = v2;
|
||||
triangle.v3 = v3;
|
||||
triangle.color = transformedColor.rgb();
|
||||
mesh.addTriangle(triangle);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Apply graphic state of the pattern
|
||||
// TODO: Implement settings of meshing in the settings dialog
|
||||
|
||||
|
@ -312,6 +312,59 @@ private:
|
||||
PDFReal m_r1 = 0.0;
|
||||
};
|
||||
|
||||
class PDFGouradTriangleShading : public PDFShadingPattern
|
||||
{
|
||||
public:
|
||||
explicit PDFGouradTriangleShading() = default;
|
||||
|
||||
protected:
|
||||
friend class PDFPattern;
|
||||
|
||||
void addSubdividedTriangles(const PDFMeshQualitySettings& settings, PDFMesh& mesh, uint32_t v1, uint32_t v2, uint32_t v3, PDFColor c1, PDFColor c2, PDFColor c3) const;
|
||||
|
||||
uint8_t m_bitsPerCoordinate = 0;
|
||||
uint8_t m_bitsPerComponent = 0;
|
||||
PDFReal m_xmin = 0.0;
|
||||
PDFReal m_xmax = 0.0;
|
||||
PDFReal m_ymin = 0.0;
|
||||
PDFReal m_ymax = 0.0;
|
||||
std::vector<PDFReal> m_limits;
|
||||
size_t m_colorComponentCount;
|
||||
|
||||
/// Color functions. This array can be empty. If it is empty,
|
||||
/// then colors should be determined directly from color space.
|
||||
std::vector<PDFFunctionPtr> m_functions;
|
||||
|
||||
/// Data of the shading, containing triangles and colors
|
||||
QByteArray m_data;
|
||||
};
|
||||
|
||||
class PDFFreeFormGouradTriangleShading : public PDFGouradTriangleShading
|
||||
{
|
||||
public:
|
||||
explicit PDFFreeFormGouradTriangleShading() = default;
|
||||
|
||||
virtual ShadingType getShadingType() const override;
|
||||
virtual PDFMesh createMesh(const PDFMeshQualitySettings& settings) const override;
|
||||
|
||||
private:
|
||||
friend class PDFPattern;
|
||||
|
||||
uint8_t m_bitsPerFlag = 0;
|
||||
};
|
||||
|
||||
class PDFLatticeFormGouradTriangleShading : public PDFGouradTriangleShading
|
||||
{
|
||||
public:
|
||||
explicit PDFLatticeFormGouradTriangleShading() = default;
|
||||
|
||||
virtual ShadingType getShadingType() const override;
|
||||
virtual PDFMesh createMesh(const PDFMeshQualitySettings& settings) const override;
|
||||
|
||||
private:
|
||||
friend class PDFPattern;
|
||||
};
|
||||
|
||||
} // namespace pdf
|
||||
|
||||
#endif // PDFPATTERN_H
|
||||
|
@ -21,8 +21,9 @@
|
||||
namespace pdf
|
||||
{
|
||||
|
||||
PDFBitReader::PDFBitReader(QDataStream* stream, Value bitsPerComponent) :
|
||||
PDFBitReader::PDFBitReader(const QByteArray* stream, Value bitsPerComponent) :
|
||||
m_stream(stream),
|
||||
m_position(0),
|
||||
m_bitsPerComponent(bitsPerComponent),
|
||||
m_maximalValue((static_cast<Value>(1) << m_bitsPerComponent) - static_cast<Value>(1)),
|
||||
m_buffer(0),
|
||||
@ -33,33 +34,33 @@ PDFBitReader::PDFBitReader(QDataStream* stream, Value bitsPerComponent) :
|
||||
Q_ASSERT(bitsPerComponent < 56);
|
||||
}
|
||||
|
||||
PDFBitReader::Value PDFBitReader::read()
|
||||
PDFBitReader::Value PDFBitReader::read(PDFBitReader::Value bits)
|
||||
{
|
||||
while (m_bitsInBuffer < m_bitsPerComponent)
|
||||
while (m_bitsInBuffer < bits)
|
||||
{
|
||||
if (!m_stream->atEnd())
|
||||
if (m_position < m_stream->size())
|
||||
{
|
||||
uint8_t currentByte = 0;
|
||||
(*m_stream) >> currentByte;
|
||||
uint8_t currentByte = static_cast<uint8_t>((*m_stream)[m_position++]);
|
||||
m_buffer = (m_buffer << 8) | currentByte;
|
||||
m_bitsInBuffer += 8;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw PDFParserException(PDFTranslationContext::tr("Not enough data to read %1-bit value.").arg(m_bitsPerComponent));
|
||||
throw PDFParserException(PDFTranslationContext::tr("Not enough data to read %1-bit value.").arg(bits));
|
||||
}
|
||||
}
|
||||
|
||||
// Now we have enough bits to read the data
|
||||
Value value = (m_buffer >> (m_bitsInBuffer - m_bitsPerComponent)) & m_maximalValue;
|
||||
m_bitsInBuffer -= m_bitsPerComponent;
|
||||
Value value = (m_buffer >> (m_bitsInBuffer - bits)) & ((static_cast<Value>(1) << bits) - static_cast<Value>(1));
|
||||
m_bitsInBuffer -= bits;
|
||||
return value;
|
||||
}
|
||||
|
||||
void PDFBitReader::seek(qint64 position)
|
||||
{
|
||||
if (m_stream->device()->seek(position))
|
||||
if (position < m_stream->size())
|
||||
{
|
||||
m_position = position;
|
||||
m_buffer = 0;
|
||||
m_bitsInBuffer = 0;
|
||||
}
|
||||
@ -69,4 +70,13 @@ void PDFBitReader::seek(qint64 position)
|
||||
}
|
||||
}
|
||||
|
||||
void PDFBitReader::alignToBytes()
|
||||
{
|
||||
const Value remainder = m_bitsInBuffer % 8;
|
||||
if (remainder > 0)
|
||||
{
|
||||
read(remainder);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace pdf
|
||||
|
@ -78,21 +78,29 @@ class PDFBitReader
|
||||
public:
|
||||
using Value = uint64_t;
|
||||
|
||||
explicit PDFBitReader(QDataStream* stream, Value bitsPerComponent);
|
||||
explicit PDFBitReader(const QByteArray* stream, Value bitsPerComponent);
|
||||
|
||||
/// Returns maximal value of n-bit unsigned integer.
|
||||
Value max() const { return m_maximalValue; }
|
||||
|
||||
/// Reads single n-bit value from the stream. If stream hasn't enough data,
|
||||
/// then exception is thrown.
|
||||
Value read();
|
||||
Value read() { return read(m_bitsPerComponent); }
|
||||
|
||||
/// Reads single n-bit value from the stream. If stream hasn't enough data,
|
||||
/// then exception is thrown.
|
||||
Value read(Value bits);
|
||||
|
||||
/// Seeks the desired position in the data stream. If position can't be seeked,
|
||||
/// then exception is thrown.
|
||||
void seek(qint64 position);
|
||||
|
||||
/// Seeks data to the byte boundary (number of processed bits is divisible by 8)
|
||||
void alignToBytes();
|
||||
|
||||
private:
|
||||
QDataStream* m_stream;
|
||||
const QByteArray* m_stream;
|
||||
int m_position;
|
||||
|
||||
const Value m_bitsPerComponent;
|
||||
const Value m_maximalValue;
|
||||
@ -128,6 +136,12 @@ private:
|
||||
Value* m_value;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
bool contains(T value, std::initializer_list<T> list)
|
||||
{
|
||||
return (std::find(list.begin(), list.end(), value) != list.end());
|
||||
}
|
||||
|
||||
/// Performs linear mapping of value x in interval [x_min, x_max] to the interval [y_min, y_max].
|
||||
/// \param x Value to be linearly remapped from interval [x_min, x_max] to the interval [y_min, y_max].
|
||||
/// \param x_min Start of the input interval
|
||||
|
Loading…
x
Reference in New Issue
Block a user