| #include "rive/math/mat2d.hpp" | |
| #include "rive/renderer.hpp" | |
| #include "rive/text_engine.hpp" | |
| using namespace rive; | |
| Mat2D rive::computeAlignment(Fit fit, | |
| Alignment alignment, | |
| const AABB& frame, | |
| const AABB& content, | |
| const float scaleFactor) | |
| { | |
| float contentWidth = content.width(); | |
| float contentHeight = content.height(); | |
| float x = -content.left() - contentWidth * 0.5f - | |
| (alignment.x() * contentWidth * 0.5f); | |
| float y = -content.top() - contentHeight * 0.5f - | |
| (alignment.y() * contentHeight * 0.5f); | |
| float scaleX = 1.0f, scaleY = 1.0f; | |
| switch (fit) | |
| { | |
| case Fit::fill: | |
| { | |
| scaleX = frame.width() / contentWidth; | |
| scaleY = frame.height() / contentHeight; | |
| break; | |
| } | |
| case Fit::contain: | |
| { | |
| float minScale = std::fmin(frame.width() / contentWidth, | |
| frame.height() / contentHeight); | |
| scaleX = scaleY = minScale; | |
| break; | |
| } | |
| case Fit::cover: | |
| { | |
| float maxScale = std::fmax(frame.width() / contentWidth, | |
| frame.height() / contentHeight); | |
| scaleX = scaleY = maxScale; | |
| break; | |
| } | |
| case Fit::fitHeight: | |
| { | |
| float minScale = frame.height() / contentHeight; | |
| scaleX = scaleY = minScale; | |
| break; | |
| } | |
| case Fit::fitWidth: | |
| { | |
| float minScale = frame.width() / contentWidth; | |
| scaleX = scaleY = minScale; | |
| break; | |
| } | |
| case Fit::layout: | |
| { | |
| scaleX = scaleY = scaleFactor; | |
| break; | |
| } | |
| case Fit::none: | |
| { | |
| scaleX = scaleY = 1.0f; | |
| break; | |
| } | |
| case Fit::scaleDown: | |
| { | |
| float minScale = std::fmin(frame.width() / contentWidth, | |
| frame.height() / contentHeight); | |
| scaleX = scaleY = minScale < 1.0f ? minScale : 1.0f; | |
| break; | |
| } | |
| } | |
| Mat2D translation; | |
| translation[4] = frame.left() + frame.width() * 0.5f + | |
| (alignment.x() * frame.width() * 0.5f); | |
| translation[5] = frame.top() + frame.height() * 0.5f + | |
| (alignment.y() * frame.height() * 0.5f); | |
| return translation * Mat2D::fromScale(scaleX, scaleY) * | |
| Mat2D::fromTranslate(x, y); | |
| } | |
| void Renderer::translate(float tx, float ty) | |
| { | |
| this->transform(Mat2D(1, 0, 0, 1, tx, ty)); | |
| } | |
| void Renderer::scale(float sx, float sy) | |
| { | |
| this->transform(Mat2D(sx, 0, 0, sy, 0, 0)); | |
| } | |
| void Renderer::rotate(float radians) | |
| { | |
| const float s = std::sin(radians); | |
| const float c = std::cos(radians); | |
| this->transform(Mat2D(c, s, -s, c, 0, 0)); | |
| } | |
| RenderBuffer::RenderBuffer(RenderBufferType type, | |
| RenderBufferFlags flags, | |
| size_t sizeInBytes) : | |
| m_type(type), m_flags(flags), m_sizeInBytes(sizeInBytes) | |
| {} | |
| RenderBuffer::~RenderBuffer() {} | |
| void* RenderBuffer::map() | |
| { | |
| assert(m_mapCount == 0 || | |
| !enums::is_flag_set(m_flags, | |
| RenderBufferFlags::mappedOnceAtInitialization)); | |
| assert(m_mapCount == m_unmapCount); | |
| RIVE_DEBUG_CODE(++m_mapCount;) | |
| m_dirty = true; | |
| return onMap(); | |
| } | |
| void RenderBuffer::unmap() | |
| { | |
| assert(m_unmapCount + 1 == m_mapCount); | |
| RIVE_DEBUG_CODE(++m_unmapCount;) | |
| onUnmap(); | |
| } | |
| RenderShader::RenderShader() {} | |
| RenderShader::~RenderShader() {} | |
| RenderPaint::RenderPaint() {} | |
| RenderPaint::~RenderPaint() {} | |
| RenderImage::RenderImage(const Mat2D& uvTransform) : m_uvTransform(uvTransform) | |
| {} | |
| RenderImage::RenderImage() {} | |
| RenderImage::~RenderImage() {} | |
| RenderPath::RenderPath() {} | |
| RenderPath::~RenderPath() {} | |
| void RenderPath::addUntrustedRawPath(const RawPath& path) | |
| { | |
| RawPath sanitized(path); | |
| sanitized.pruneEmptySegments(); | |
| addRawPath(sanitized); | |
| } | |
| bool rive::isWhiteSpace(Unichar c) | |
| { | |
| // 0x2028 is a Line separator. | |
| // 0x200B is a Zero width space. | |
| return c <= ' ' || c == 0x2028 || c == 0x200B; | |
| } | |
| SimpleArray<Paragraph> Font::shapeText(Span<const Unichar> text, | |
| Span<const TextRun> runs, | |
| int textDirectionFlag) const | |
| { | |
| #ifdef DEBUG | |
| size_t count = 0; | |
| for (const TextRun& tr : runs) | |
| { | |
| assert(tr.unicharCount > 0); | |
| count += tr.unicharCount; | |
| } | |
| assert(count <= text.size()); | |
| #endif | |
| SimpleArray<Paragraph> paragraphs = | |
| onShapeText(text, runs, textDirectionFlag); | |
| bool wantWhiteSpace = false; | |
| GlyphRun* lastRun = nullptr; | |
| size_t reserveSize = text.size() / 4; | |
| SimpleArrayBuilder<uint32_t> breakBuilder(reserveSize); | |
| SimpleArrayBuilder<uint32_t> joinerBuilder(reserveSize); | |
| for (const Paragraph& para : paragraphs) | |
| { | |
| for (GlyphRun& gr : para.runs) | |
| { | |
| if (lastRun != nullptr) | |
| { | |
| lastRun->breaks = std::move(breakBuilder); | |
| lastRun->joiners = std::move(joinerBuilder); | |
| // Reset the builder. | |
| breakBuilder = SimpleArrayBuilder<uint32_t>(reserveSize); | |
| joinerBuilder = SimpleArrayBuilder<uint32_t>(reserveSize); | |
| } | |
| uint32_t glyphIndex = 0; | |
| for (uint32_t offset : gr.textIndices) | |
| { | |
| Unichar unicode = text[offset]; | |
| if (unicode == '\n' || unicode == 0x2028) | |
| { | |
| breakBuilder.add(glyphIndex); | |
| breakBuilder.add(glyphIndex); | |
| } | |
| if (unicode == 0x2060) | |
| { | |
| joinerBuilder.add(offset); | |
| } | |
| if (wantWhiteSpace == isWhiteSpace(unicode)) | |
| { | |
| breakBuilder.add(glyphIndex); | |
| wantWhiteSpace = !wantWhiteSpace; | |
| } | |
| glyphIndex++; | |
| } | |
| lastRun = &gr; | |
| } | |
| } | |
| if (lastRun != nullptr) | |
| { | |
| if (wantWhiteSpace) | |
| { | |
| breakBuilder.add((uint32_t)lastRun->glyphs.size()); | |
| } | |
| else | |
| { | |
| // Consume the rest of the run. | |
| breakBuilder.add(breakBuilder.empty() ? 0 : breakBuilder.back()); | |
| breakBuilder.add((uint32_t)lastRun->glyphs.size()); | |
| } | |
| lastRun->breaks = std::move(breakBuilder); | |
| lastRun->joiners = std::move(joinerBuilder); | |
| } | |
| #ifdef DEBUG | |
| for (const Paragraph& para : paragraphs) | |
| { | |
| for (const GlyphRun& gr : para.runs) | |
| { | |
| assert(gr.glyphs.size() > 0); | |
| assert(gr.glyphs.size() == gr.textIndices.size()); | |
| assert(gr.glyphs.size() + 1 == gr.xpos.size()); | |
| } | |
| } | |
| #endif | |
| return paragraphs; | |
| } |