[orx-svg] Add support for images
This commit is contained in:
@@ -2,6 +2,8 @@ package org.openrndr.extra.svg
|
|||||||
|
|
||||||
import io.github.oshai.kotlinlogging.KotlinLogging
|
import io.github.oshai.kotlinlogging.KotlinLogging
|
||||||
import org.jsoup.nodes.*
|
import org.jsoup.nodes.*
|
||||||
|
import org.openrndr.draw.ColorBuffer
|
||||||
|
import org.openrndr.draw.loadImage
|
||||||
import org.openrndr.extra.composition.*
|
import org.openrndr.extra.composition.*
|
||||||
import org.openrndr.math.*
|
import org.openrndr.math.*
|
||||||
import org.openrndr.shape.*
|
import org.openrndr.shape.*
|
||||||
@@ -74,6 +76,7 @@ internal open class SVGGroup(val element: Element, val elements: MutableList<SVG
|
|||||||
private fun handleChildren() {
|
private fun handleChildren() {
|
||||||
this.element.children().forEach { child ->
|
this.element.children().forEach { child ->
|
||||||
when (child.tagName()) {
|
when (child.tagName()) {
|
||||||
|
"image" -> elements.add(SVGImage(child))
|
||||||
in Tag.graphicsList -> elements.add(SVGPath(child))
|
in Tag.graphicsList -> elements.add(SVGPath(child))
|
||||||
else -> elements.add(SVGGroup(child))
|
else -> elements.add(SVGGroup(child))
|
||||||
}
|
}
|
||||||
@@ -106,6 +109,37 @@ internal fun Double.toBoolean(): Boolean? = when (this) {
|
|||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal class SVGImage(val element: Element? = null) : SVGElement(element) {
|
||||||
|
|
||||||
|
val x = element?.attribute("x")?.value?.toDoubleOrNull() ?: 0.0
|
||||||
|
val y = element?.attribute("y")?.value?.toDoubleOrNull() ?: 0.0
|
||||||
|
|
||||||
|
val width = element?.attribute("width")?.value?.toDoubleOrNull() ?: 0.0
|
||||||
|
val height = element?.attribute("height")?.value?.toDoubleOrNull() ?: 0.0
|
||||||
|
|
||||||
|
|
||||||
|
fun image(): ColorBuffer {
|
||||||
|
return loadImage(element!!.attr("xlink:href"))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun handleAttribute(attribute: Attribute) {
|
||||||
|
if (this.element is Element) {
|
||||||
|
when (attribute.key) {
|
||||||
|
// Attributes can also be style properties, in which case they're passed on
|
||||||
|
in Prop.list -> styleProperty(attribute.key, attribute.value)
|
||||||
|
Attr.TRANSFORM -> style.transform = SVGParse.transform(this.element)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
if (element != null) {
|
||||||
|
style.transform = SVGParse.transform(this.element)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
internal class SVGPath(val element: Element? = null) : SVGElement(element) {
|
internal class SVGPath(val element: Element? = null) : SVGElement(element) {
|
||||||
val commands = mutableListOf<Command>()
|
val commands = mutableListOf<Command>()
|
||||||
|
|
||||||
@@ -198,8 +232,9 @@ internal class SVGPath(val element: Element? = null) : SVGElement(element) {
|
|||||||
|
|
||||||
// I don't know why we can't just have segments from the above map,
|
// I don't know why we can't just have segments from the above map,
|
||||||
// but this is the only way this works.
|
// but this is the only way this works.
|
||||||
segments += contours.flatMap { it.segments}
|
segments += contours.flatMap { it.segments }
|
||||||
}
|
}
|
||||||
|
|
||||||
"M" -> {
|
"M" -> {
|
||||||
// TODO: Log an error when this nulls
|
// TODO: Log an error when this nulls
|
||||||
cursor = points!!.firstOrNull() ?: return@forEach
|
cursor = points!!.firstOrNull() ?: return@forEach
|
||||||
@@ -212,6 +247,7 @@ internal class SVGPath(val element: Element? = null) : SVGElement(element) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
"m" -> {
|
"m" -> {
|
||||||
// TODO: Log an error when this nulls
|
// TODO: Log an error when this nulls
|
||||||
cursor += points!!.firstOrNull() ?: return@forEach
|
cursor += points!!.firstOrNull() ?: return@forEach
|
||||||
@@ -224,6 +260,7 @@ internal class SVGPath(val element: Element? = null) : SVGElement(element) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
"L" -> {
|
"L" -> {
|
||||||
segments += points!!.map {
|
segments += points!!.map {
|
||||||
Segment2D(cursor, it).apply {
|
Segment2D(cursor, it).apply {
|
||||||
@@ -231,6 +268,7 @@ internal class SVGPath(val element: Element? = null) : SVGElement(element) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
"l" -> {
|
"l" -> {
|
||||||
segments += points!!.map {
|
segments += points!!.map {
|
||||||
Segment2D(cursor, cursor + it).apply {
|
Segment2D(cursor, cursor + it).apply {
|
||||||
@@ -238,6 +276,7 @@ internal class SVGPath(val element: Element? = null) : SVGElement(element) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
"H" -> {
|
"H" -> {
|
||||||
segments += command.operands.map {
|
segments += command.operands.map {
|
||||||
val target = Vector2(it, cursor.y)
|
val target = Vector2(it, cursor.y)
|
||||||
@@ -246,6 +285,7 @@ internal class SVGPath(val element: Element? = null) : SVGElement(element) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
"h" -> {
|
"h" -> {
|
||||||
segments += command.operands.map {
|
segments += command.operands.map {
|
||||||
val target = cursor + Vector2(it, 0.0)
|
val target = cursor + Vector2(it, 0.0)
|
||||||
@@ -254,6 +294,7 @@ internal class SVGPath(val element: Element? = null) : SVGElement(element) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
"V" -> {
|
"V" -> {
|
||||||
segments += command.operands.map {
|
segments += command.operands.map {
|
||||||
val target = Vector2(cursor.x, it)
|
val target = Vector2(cursor.x, it)
|
||||||
@@ -262,6 +303,7 @@ internal class SVGPath(val element: Element? = null) : SVGElement(element) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
"v" -> {
|
"v" -> {
|
||||||
segments += command.operands.map {
|
segments += command.operands.map {
|
||||||
val target = cursor + Vector2(0.0, it)
|
val target = cursor + Vector2(0.0, it)
|
||||||
@@ -270,6 +312,7 @@ internal class SVGPath(val element: Element? = null) : SVGElement(element) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
"C" -> {
|
"C" -> {
|
||||||
segments += points!!.windowed(3, 3, true).map {
|
segments += points!!.windowed(3, 3, true).map {
|
||||||
if (it.size != 3) {
|
if (it.size != 3) {
|
||||||
@@ -284,6 +327,7 @@ internal class SVGPath(val element: Element? = null) : SVGElement(element) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
"c" -> {
|
"c" -> {
|
||||||
segments += points!!.windowed(3, 3, true).map {
|
segments += points!!.windowed(3, 3, true).map {
|
||||||
if (it.size != 3) {
|
if (it.size != 3) {
|
||||||
@@ -298,6 +342,7 @@ internal class SVGPath(val element: Element? = null) : SVGElement(element) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
"S" -> {
|
"S" -> {
|
||||||
segments += points!!.windowed(2, 2, true).map {
|
segments += points!!.windowed(2, 2, true).map {
|
||||||
if (it.size != 2) {
|
if (it.size != 2) {
|
||||||
@@ -313,6 +358,7 @@ internal class SVGPath(val element: Element? = null) : SVGElement(element) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
"s" -> {
|
"s" -> {
|
||||||
segments += points!!.windowed(2, 2, true).map {
|
segments += points!!.windowed(2, 2, true).map {
|
||||||
if (it.size != 2) {
|
if (it.size != 2) {
|
||||||
@@ -328,6 +374,7 @@ internal class SVGPath(val element: Element? = null) : SVGElement(element) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
"Q" -> {
|
"Q" -> {
|
||||||
segments += points!!.windowed(2, 2, true).map {
|
segments += points!!.windowed(2, 2, true).map {
|
||||||
if (it.size != 2) {
|
if (it.size != 2) {
|
||||||
@@ -342,6 +389,7 @@ internal class SVGPath(val element: Element? = null) : SVGElement(element) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
"q" -> {
|
"q" -> {
|
||||||
segments += points!!.windowed(2, 2, true).map {
|
segments += points!!.windowed(2, 2, true).map {
|
||||||
if (it.size != 2) {
|
if (it.size != 2) {
|
||||||
@@ -356,6 +404,7 @@ internal class SVGPath(val element: Element? = null) : SVGElement(element) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
"T" -> {
|
"T" -> {
|
||||||
points!!.forEach {
|
points!!.forEach {
|
||||||
val cp = 2.0 * cursor - (prevQuadCtrlPoint ?: cursor)
|
val cp = 2.0 * cursor - (prevQuadCtrlPoint ?: cursor)
|
||||||
@@ -365,6 +414,7 @@ internal class SVGPath(val element: Element? = null) : SVGElement(element) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
"t" -> {
|
"t" -> {
|
||||||
points!!.forEach {
|
points!!.forEach {
|
||||||
val cp = 2.0 * cursor - (prevQuadCtrlPoint ?: cursor)
|
val cp = 2.0 * cursor - (prevQuadCtrlPoint ?: cursor)
|
||||||
@@ -374,6 +424,7 @@ internal class SVGPath(val element: Element? = null) : SVGElement(element) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
"Z", "z" -> {
|
"Z", "z" -> {
|
||||||
if ((cursor - anchor).length >= 0.001) {
|
if ((cursor - anchor).length >= 0.001) {
|
||||||
segments += Segment2D(cursor, anchor)
|
segments += Segment2D(cursor, anchor)
|
||||||
@@ -381,6 +432,7 @@ internal class SVGPath(val element: Element? = null) : SVGElement(element) {
|
|||||||
cursor = anchor
|
cursor = anchor
|
||||||
closed = true
|
closed = true
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
// The spec declares we should still attempt to render
|
// The spec declares we should still attempt to render
|
||||||
// the path up until the erroneous command as to visually
|
// the path up until the erroneous command as to visually
|
||||||
|
|||||||
@@ -63,6 +63,12 @@ internal class SVGDocument(private val root: SVGSVGElement, val namespaces: Map<
|
|||||||
this.id = svgElem.id
|
this.id = svgElem.id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
is SVGImage -> {
|
||||||
|
ImageNode(svgElem.image(),svgElem.x, svgElem.y, svgElem.width, svgElem.height).apply {
|
||||||
|
style = svgElem.style
|
||||||
|
this.id = svgElem.id
|
||||||
|
}
|
||||||
|
}
|
||||||
}.apply {
|
}.apply {
|
||||||
transform = svgElem.style.transform.value
|
transform = svgElem.style.transform.value
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user