platf-imagick/src/nativeTest/kotlin/Utils.kt (141 lines of code) (raw):

import ImageMagick.DrawingWand import demoAndTestShared.ImageComparer import demoAndTestShared.NativeBitmapIO import kotlinx.cinterop.* import org.jetbrains.letsPlot.commons.encoding.Png import org.jetbrains.letsPlot.commons.geometry.Vector import org.jetbrains.letsPlot.commons.intern.async.Async import org.jetbrains.letsPlot.commons.intern.async.Asyncs import org.jetbrains.letsPlot.commons.intern.io.Native import org.jetbrains.letsPlot.commons.values.Bitmap import org.jetbrains.letsPlot.commons.values.Color import org.jetbrains.letsPlot.commons.values.Colors import org.jetbrains.letsPlot.core.canvas.* import org.jetbrains.letsPlot.imagick.canvas.MagickCanvas import org.jetbrains.letsPlot.imagick.canvas.MagickFontManager import org.jetbrains.letsPlot.imagick.canvas.MagickFontManager.FontSet import org.jetbrains.letsPlot.imagick.canvas.MagickSnapshot import org.jetbrains.letsPlot.imagick.canvas.MagickUtil /* * Copyright (c) 2025. JetBrains s.r.o. * Use of this source code is governed by the MIT license that can be found in the LICENSE file. */ fun Context2d.setFont(family: String, size: Number, style: FontStyle = FontStyle.NORMAL, weight: FontWeight = FontWeight.NORMAL) { val font = Font( fontFamily = family, fontSize = size.toDouble(), fontStyle = style, fontWeight = weight ) setFont(font) } var Context2d.lineWidth: Double get() = error("lineWidth is write only") set(value) { setLineWidth(value) } val black = ImageMagick.NewPixelWand().apply { ImageMagick.PixelSetColor(this, "black") } val none = ImageMagick.NewPixelWand().apply { ImageMagick.PixelSetColor(this, "none") } val white = ImageMagick.NewPixelWand().apply { ImageMagick.PixelSetColor(this, "white") } val green = ImageMagick.NewPixelWand().apply { ImageMagick.PixelSetColor(this, "green") } val alphaBlack = ImageMagick.NewPixelWand().apply { ImageMagick.PixelSetColor(this, "rgba(0,0,0,0.5)") } fun defineClipPath(wand: CPointer<DrawingWand>, clipPathId: String, block: () -> Unit) { ImageMagick.DrawPushDefs(wand) ImageMagick.DrawPushClipPath(wand, clipPathId) ImageMagick.PushDrawingWand(wand) block() ImageMagick.PopDrawingWand(wand) ImageMagick.DrawPopClipPath(wand) ImageMagick.DrawPopDefs(wand) } fun drawAnnotation(wand: CPointer<DrawingWand>, x: Double, y: Double, text: String) { memScoped { ImageMagick.DrawAnnotation(wand, x, y, text.cstr.ptr.reinterpret()) } } fun drawAffine( wand: CPointer<DrawingWand>, sx: Number = 1, rx: Number = 0, ry: Number = 0, sy: Number = 1, tx: Number = 0, ty: Number = 0 ) { memScoped { val m = alloc<ImageMagick.AffineMatrix>() m.sx = sx.toDouble() m.sy = sy.toDouble() m.rx = rx.toDouble() m.ry = ry.toDouble() m.tx = tx.toDouble() m.ty = ty.toDouble() ImageMagick.DrawAffine(wand, m.ptr) } } var Context2d.fillStyle: Any? get() = error("fillStyle is write only") set(value) { val color = when (value) { is Color -> value is String -> Colors.parseColor(value) null -> null else -> error("Unsupported fill style: $value") } setFillStyle(color) } var Context2d.strokeStyle: Any? get() = error("strokeStyle is write only") set(value) { val color = when (value) { is Color -> value is String -> Colors.parseColor(value) null -> null else -> error("Unsupported fill style: $value") } setStrokeStyle(color) } val resourcesDir = Native.getCurrentDir() + "/src/nativeTest/resources/" val notoSerifRegularFontPath = resourcesDir + "fonts/NotoSerif-Regular.ttf" val notoSerifBoldFontPath = resourcesDir + "fonts/NotoSerif-Bold.ttf" val notoSerifItalicFontPath = resourcesDir + "fonts/NotoSerif-Italic.ttf" val notoSerifBoldItalicFontPath = resourcesDir + "fonts/NotoSerif-BoldItalic.ttf" val notoSansMonoRegularFontPath = resourcesDir + "fonts/NotoSansMono-Regular.ttf" val notoSansMonoBoldFontPath = resourcesDir + "fonts/NotoSansMono-Bold.ttf" fun embeddedFontsManager() = MagickFontManager.configured( "serif" to FontSet(embedded = true, familyName = "serif", notoSerifRegularFontPath) ) fun createImageComparer(fontManager: MagickFontManager): ImageComparer { return ImageComparer( expectedDir = Native.getCurrentDir() + "/src/nativeTest/resources/expected/", outDir = Native.getCurrentDir() + "/build/reports/", canvasProvider = MagickCanvasProvider(fontManager), bitmapIO = NativeBitmapIO, tol = 1 ) } class MagickCanvasProvider( private val magickFontManager: MagickFontManager, ) : CanvasProvider { override fun createCanvas(size: Vector): MagickCanvas { return MagickCanvas.create(size.x, size.y, 1.0, magickFontManager) } override fun createSnapshot(bitmap: Bitmap): MagickSnapshot { return MagickSnapshot.fromBitmap(bitmap) } override fun decodeDataImageUrl(dataUrl: String): Async<Canvas.Snapshot> { println("MagickCanvasControl.createSnapshot(dataUrl): dataUrl.size = ${dataUrl.length}") val bitmap = Png.decodeDataImage(dataUrl) return Asyncs.constant(MagickSnapshot.fromBitmap(bitmap)) } override fun decodePng(png: ByteArray): Async<Canvas.Snapshot> { val img = MagickUtil.fromBitmap(Png.decode(png)) return Asyncs.constant(MagickSnapshot(img)) } }