Skip to content

Commit

Permalink
update
Browse files Browse the repository at this point in the history
  • Loading branch information
LaoLittle committed May 28, 2022
1 parent 9f8ec5f commit 9eb92a1
Show file tree
Hide file tree
Showing 7 changed files with 409 additions and 12 deletions.
2 changes: 2 additions & 0 deletions src/main/kotlin/DrawMeme.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import net.mamoe.mirai.utils.ExternalResource.Companion.toExternalResource
import net.mamoe.mirai.utils.info
import org.laolittle.plugin.draw.Emoji.EmojiUtil.fullEmojiRegex
import org.laolittle.plugin.draw.Emoji.EmojiUtil.toEmoji
import org.laolittle.plugin.draw.custom.initCustomMemes
import org.laolittle.plugin.draw.meme.*
import org.laolittle.plugin.sendImage
import org.laolittle.plugin.toExternalResource
Expand All @@ -35,6 +36,7 @@ object DrawMeme : KotlinPlugin(
override fun onEnable() {
logger.info { "Plugin loaded" }

initCustomMemes()

val patReg = Regex("""^摸+([我爆头])?""")
val choReg = Regex("#5(?:000|k)兆[\\s ]*(.+)")
Expand Down
8 changes: 8 additions & 0 deletions src/main/kotlin/DrawMemeEventChannel.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package org.laolittle.plugin.draw

import kotlinx.coroutines.CoroutineExceptionHandler
import net.mamoe.mirai.event.globalEventChannel

val drawMemeEventChannel = DrawMeme.globalEventChannel(CoroutineExceptionHandler { context, e ->

})
3 changes: 2 additions & 1 deletion src/main/kotlin/FunctionConfig.kt
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
package org.laolittle.plugin.draw

import kotlinx.serialization.Serializable
import net.mamoe.mirai.console.data.AutoSavePluginConfig
import net.mamoe.mirai.console.data.value

object FunctionConfig : AutoSavePluginConfig("FunctionConfig") {
@kotlinx.serialization.Serializable
@Serializable
data class Configuration(
val enable: Boolean = true,
val groups: MutableSet<Long> = mutableSetOf()
Expand Down
25 changes: 22 additions & 3 deletions src/main/kotlin/General.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ import net.mamoe.mirai.message.data.PlainText
import net.mamoe.mirai.message.data.firstIsInstanceOrNull
import net.mamoe.mirai.message.nextMessage
import net.mamoe.mirai.utils.ExternalResource.Companion.toExternalResource
import org.jetbrains.skia.Bitmap
import org.jetbrains.skia.Rect
import org.jetbrains.skia.*
import java.nio.file.Path
import kotlin.io.path.readBytes
import org.jetbrains.skia.Image as SkImage
Expand Down Expand Up @@ -66,4 +65,24 @@ internal suspend fun MessageEvent.getOrWaitImage(): Image? {
}).firstIsInstanceOrNull<Image>()
}

fun Bitmap.asImage() = org.jetbrains.skia.Image.makeFromBitmap(this)
fun Bitmap.asImage() = org.jetbrains.skia.Image.makeFromBitmap(this)

private val linearMipmap = FilterMipmap(FilterMode.LINEAR, MipmapMode.NEAREST)
fun Canvas.drawImageRectLinear(
image: org.jetbrains.skia.Image,
src: Rect,
dst: Rect,
paint: Paint?,
strict: Boolean
) = drawImageRect(image, src, dst, linearMipmap, paint, strict)

fun Canvas.drawImageRectLinear(image: org.jetbrains.skia.Image, dst: Rect, paint: Paint?) =
drawImageRectLinear(
image,
Rect.makeWH(image.width.toFloat(), image.height.toFloat()),
dst,
paint,
true
)

fun Canvas.drawImageRectLinear(image: org.jetbrains.skia.Image, dst: Rect) = drawImageRectLinear(image, dst, null)
213 changes: 213 additions & 0 deletions src/main/kotlin/custom/CustomMeme.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
package org.laolittle.plugin.draw.custom

import kotlinx.coroutines.async
import org.jetbrains.skia.*
import org.laolittle.plugin.bytes
import org.laolittle.plugin.draw.DrawMeme
import org.laolittle.plugin.draw.drawImageRectLinear
import org.laolittle.plugin.gif.GifEncoder
import org.laolittle.plugin.gif.GifSetting
import java.io.File

private val paintDefault = Paint()
class CustomMeme(
val name: String,
val input: Codec,
) {
private val surface = Surface.makeRaster(input.imageInfo)

val isGif = input.frameCount > 1
private val frames = kotlin.run {
if (!isGif) return@run arrayOf(input.readPixels())

Array(input.frameCount) {
Bitmap().apply {
allocPixels(input.imageInfo)
input.readPixels(this, it)
}
}
}

private val _actions = ArrayList<MutableList<Canvas.(Array<out Image>) -> Unit>>(input.frameCount)
val actions: List<List<Canvas.(Array<out Image>) -> Unit>> get() = _actions

fun add(frame: Int, block: Canvas.(Array<out Image>) -> Unit) {
_actions[frame].add(block)
}

suspend fun makeImage(vararg images: Image): ByteArray {
if (!isGif) {
surface.canvas.clear(0)
surface.writePixels(frames[0], 0, 0)
actions.first().forEach {
surface.canvas.it(images)
}
return surface.makeImageSnapshot().bytes
}

val (collector, writer) = GifEncoder.new(
GifSetting(
surface.width,
surface.height,
100,
false,
GifSetting.Repeat.Finite(input.repetitionCount.toShort())
)
)

val bytes = DrawMeme.async {
writer.writeToBytes()
}

var current = 0
actions.forEachIndexed { index, each ->
surface.canvas.clear(0)
surface.writePixels(frames[index], 0, 0)
each.forEach {
surface.canvas.it(images)
}
val info = input.getFrameInfo(index)

current += info.duration
collector.addFrame(surface.makeImageSnapshot().bytes, index, info.duration / 1000.0)
}

return bytes.await()
}

companion object {
fun fromFile(file: File): CustomMeme {
/*fun String.withIndexOf(string: String, startIndex: Int = 0, ignoreCase: Boolean = false, block: (Int) -> Unit): Boolean {
val index = indexOf(string,startIndex, ignoreCase)
return index == 0
}*/

val text = ArrayList<String>(3)

val reg = Regex("""//.*""")

file.readLines().forEach {
if (it.isNotBlank()) {
val foo = it.replace(reg, "")
if (foo.isNotBlank()) text.add(foo.replace('', ',').replace('', ':'))
}
}

var name: String? = null
var input: Codec? = null
val avatarVal = hashMapOf<String, Int>()
for (i in 0..2) {
val line = text[i]
when {
null == name && line.startsWith("meme:") -> {
val foo = line.replace(" ", "")
name = foo.slice(5..foo.lastIndex)
}

null == input && line.startsWith("input:") -> {
val foo = line.indexOf('{')
val bar = line.indexOf('}')
val path = line.slice(foo + 1 until bar)

input = Codec.makeFromData(Data.makeFromFileName(file.absoluteFile.parentFile.resolve(path).absolutePath))
}

avatarVal.isEmpty() && line.startsWith("avatars:") -> {
val foo = line
.replace(" ", "")


foo.slice(8..foo.lastIndex)
.split(',')
.forEachIndexed { index, s ->
if (!s.startsWith('@')) throw IllegalArgumentException("")
avatarVal[s.slice(1..s.lastIndex)] = index
}
}
}
}

requireNotNull(name)
requireNotNull(input)

val customMeme = CustomMeme(name, input)

fun compile(input: String): Canvas.(Array<out Image>) -> Unit {
val split = input.split(' ', limit = 3)

return when (split.first()) {
"draw" -> {
val index = avatarVal[split[1]] ?: throw IllegalArgumentException("Compiler error: No such argument: ${split[1]}")

val shape = split[2]

val foo = shape.indexOf('{')
val bar = shape.indexOf('}')
val cons = shape.slice(foo + 1 until bar).split(',')

when {
shape.startsWith("Rect", true) -> {
val rect =
Rect(cons[0].toFloat(), cons[1].toFloat(), cons[2].toFloat(), cons[3].toFloat());

{
val img = it[index]

drawImageRectLinear(img, rect)
}
}

shape.startsWith("Circle", true) -> {
val x = cons[0].toFloat()
val y = cons[1].toFloat()
val radius = cons[2].toFloat();

{
val img = it[index]

// drawCircle(x, y, radius, paintTransparent) // 扫清障碍
clipRRect(RRect.makeLTRB(x - radius, y - radius, x + radius, y + radius, radius), true)
drawImageRectLinear(
img,
Rect(x - radius, y - radius, x + radius, y + radius),
paintDefault
)
}
}

else -> {
throw IllegalArgumentException(input)
}
}
}

else -> {
throw IllegalArgumentException(input)
}
}
}


var currentFrame = -1

for (i in 3..text.lastIndex) {
val line = text[i]
if (line.startsWith("frame")) {
currentFrame += 1
continue
}

customMeme.add(currentFrame, compile(line))
}

return customMeme
}
}

init {
repeat(input.frameCount) {
_actions.add(mutableListOf())
}
}
}
48 changes: 48 additions & 0 deletions src/main/kotlin/custom/DrawCustom.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package org.laolittle.plugin.draw.custom

import io.ktor.client.request.*
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import net.mamoe.mirai.contact.Contact.Companion.sendImage
import net.mamoe.mirai.event.subscribeGroupMessages
import net.mamoe.mirai.message.data.At
import net.mamoe.mirai.utils.ExternalResource.Companion.toExternalResource
import org.jetbrains.skia.Image
import org.laolittle.plugin.draw.DrawMeme
import org.laolittle.plugin.draw.drawMemeEventChannel
import org.laolittle.plugin.draw.httpClient
import java.io.File

internal val customMemeFolder = DrawMeme.dataFolder.resolve("custom").also(File::mkdirs)

internal val customMemes = mutableListOf<CustomMeme>()

internal fun initCustomMemes() {
customMemeFolder.listFiles()?.forEach {
customMemes.add(CustomMeme.fromFile(it))
}

drawMemeEventChannel.subscribeGroupMessages {
customMemes.forEach { meme ->
val s = "#${meme.name}"
startsWith(s) {
val avatars = arrayListOf<Deferred<Image>>()
message.forEach { m ->
if (m is At) {
avatars.add(DrawMeme.async {
val id = m.target
Image.makeFromEncoded(httpClient.get("https://q1.qlogo.cn/g?b=qq&nk=$id&s=640"))
})
}
}

meme.makeImage(*avatars.awaitAll().toTypedArray())
.toExternalResource(if (meme.isGif) "GIF" else null).use {
subject.sendImage(it)
}

}
}
}
}
Loading

0 comments on commit 9eb92a1

Please sign in to comment.