拼接漫画图片

master
pan 3 years ago
parent cbc9a26b7d
commit 93bf0c9730
  1. 2
      src/commonMain/kotlin/Data.kt
  2. 74
      src/jsMain/kotlin/image.kt
  3. 35
      src/jsMain/kotlin/welcome.kt
  4. 32
      src/jvmMain/kotlin/plugins/Routing.kt

@ -34,4 +34,4 @@ data class UrlParam(val url:String,val html:String)
const val websocketPath="/webSocket"
const val websiteTitle="朴实无华的漫画解析工具"
const val websiteTitle="朴实无华的takeshobo漫画解析工具"

@ -1,6 +1,8 @@
import kotlinext.js.getOwnPropertyNames
import kotlinx.browser.document
import kotlinx.css.head
import kotlinx.browser.window
import org.khronos.webgl.ArrayBuffer
import org.khronos.webgl.DataView
import org.khronos.webgl.Uint8Array
import org.khronos.webgl.set
import org.w3c.dom.CanvasRenderingContext2D
@ -15,7 +17,7 @@ import kotlin.math.ceil
import kotlin.math.max
import kotlin.math.min
import kotlin.math.round
import org.w3c.dom.WindowOrWorkerGlobalScope
data class un(val width: Int, val height: Int)
@ -130,6 +132,8 @@ class ImageLoader(val urlResult: UrlResult){
private var imageHeight=0.0
private val tasks= mutableListOf<Promise<Image>>()
//speedbinb.js?dmy=016301:formatted:8766
private fun callback(t:un): List<n> {
d.urlResult=urlResult
@ -198,39 +202,76 @@ class ImageLoader(val urlResult: UrlResult){
}
}
Promise<Image>(executor = { resolve, _ ->
canvasToBlob(t=canvas,{blob: Blob ->
console.info("blob.size:${blob.size}")
val i=URL.createObjectURL(blob=blob)
console.info("漫画URL:${i}")
console.info("加载图片dataUrl:\n${i}")
Image().apply {
onload={event: Event ->
canvasHtml.let {
canvas->
val ctx:CanvasRenderingContext2D= canvas.getContext("2d") as CanvasRenderingContext2D
ctx.drawImage(this,0.0,imageHeight,naturalWidth.toDouble(),naturalHeight.toDouble().apply {
imageHeight+=this
console.info("拼接图片imageHeight:${imageHeight}")
})
}
imageHeight+=this.naturalHeight
resolve(this)
}
src=i
}
})
}).apply {
tasks.add(this)
}
}
fun dataURItoBlob(dataURI:String): Blob {
// convert base64 to raw binary data held in a string
val byteString = window.atob(dataURI.split(',')[1])
// separate out the mime component
val mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0]
// write the bytes of the string to an ArrayBuffer
val arrayBuffer = ArrayBuffer(byteString.length)
val _ia = Uint8Array(arrayBuffer)
byteString.withIndex().forEach {
_ia[it.index] = it.value.code.toByte()
}
fun create(){
val dataView = DataView(arrayBuffer);
val blob = Blob(arrayOf(dataView),options = BlobPropertyBag(type = mimeString));
return blob
}
private fun create(){
Promise.all(promise = tasks.toTypedArray()).then {
canvasHtml.let {
canvas->
canvas.width=it.first().naturalWidth
canvas.height=imageHeight.toInt()
val ctx:CanvasRenderingContext2D= canvas.getContext("2d") as CanvasRenderingContext2D
ctx.clearRect(0.0, 0.0, canvas.width.toDouble(), canvas.height.toDouble())
var dy=0.0
it.forEach {
ctx.drawImage(image=it,dx=0.0,dy=dy,
dw=it.naturalWidth.toDouble(),dh=it.naturalHeight.toDouble().apply {
dy+=this.toInt()-1
})
}
Image().apply {
onload={event: Event ->
onload={
console.info("拼接图片大小naturalWidth:${naturalWidth},naturalHeight:${naturalHeight}")
}
src=canvasHtml.toDataURL()
src=canvas.toDataURL().let { URL.createObjectURL(dataURItoBlob(it)) }.apply {
console.info("拼接图片url:\n$this")
}
}
}
}
}
//speedbinb.js?dmy=016301:formatted:3798
fun canvasToBlob(
private fun canvasToBlob(
t:HTMLCanvasElement, callback:(t:Blob)->Unit, n:String="image/jpeg", r: Double =.9){
val i=t.toDataURL(type=n,quality = r).split(",")[1]
console.info("url length:${i.length}")
@ -282,6 +323,7 @@ class ImageLoader(val urlResult: UrlResult){
// val n=t(width = t.width,height = t.height)
us(t=t,image=it)
}
create()
}
}

@ -26,7 +26,7 @@ external interface WelcomeProps : RProps {
var webSocket:WebSocket
}
data class WelcomeState(var url:String="",var result:String="",var percentage:kotlin.Float=0F) : RState
data class WelcomeState(var url:String="",var result:String="",var percentage:kotlin.Float=0F,var allowInput:Boolean=true) : RState
fun Double.format(digits: Int): String = this.asDynamic().toFixed(digits)
fun Float.format(digits: Int): String = this.asDynamic().toFixed(digits)
@ -43,13 +43,11 @@ class Welcome(props: WelcomeProps) : RComponent<WelcomeProps, WelcomeState>(prop
if(data.contains("ptimg-version")){
val urlResult=Json.decodeFromString<UrlResult>(data)
console.info("ptimg_version:${urlResult.t.ptimg_version}")
ImageLoader(urlResult = urlResult).apply {
rebuild()
create()
}
ImageLoader(urlResult = urlResult).rebuild()
}else{
val task=Json.decodeFromString<ParseTask>(data)
state.result="解析进度:${task.percentage}%"
state.allowInput=(state.percentage==100F)
setState(state)
}
}
@ -72,9 +70,9 @@ class Welcome(props: WelcomeProps) : RComponent<WelcomeProps, WelcomeState>(prop
href="https://gammaplus.takeshobo.co.jp"
target="_blank"
}
+"漫画"
+"takeshobo"
}
+"解析工具"
+"漫画解析工具"
}
styledDiv{
@ -93,6 +91,7 @@ class Welcome(props: WelcomeProps) : RComponent<WelcomeProps, WelcomeState>(prop
attrs{
type=InputType.text
value = state.url
disabled = !state.allowInput
onChangeFunction = { event ->
(event.target as HTMLInputElement).let {
console.info(it.value)
@ -117,36 +116,42 @@ class Welcome(props: WelcomeProps) : RComponent<WelcomeProps, WelcomeState>(prop
button {
attrs {
disabled=!state.allowInput
onClickFunction={
state.allowInput=false
state.result="初始化解析任务请稍等"
setState(state)
val formData=FormData()
formData.append("url",state.url)
window.fetch("/api/json", RequestInit(method = "post",body = formData)).then {
window.fetch("/api/json", RequestInit(method = "post",body = formData))
.then {
it.text()
}.then {
console.info(it)
state.result=Json.decodeFromString<MessageResponse>(it).message
setState(state)
}
}
}
+"开始解析"
}
if(state.result!=""){
styledDiv {
div {
+state.result
}
if(state.percentage>0F&&state.percentage<100F){
button {
attrs {
onClickFunction={
state.result=""
props.webSocket.send("cancel")
state=WelcomeState()
setState(state)
}
}
+"清空消息"
+"取消解析任务"
}
}
}
}

@ -11,6 +11,8 @@ import io.ktor.request.*
import io.ktor.response.*
import io.ktor.routing.*
import io.ktor.websocket.*
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancel
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.launch
import kotlinx.html.HTML
@ -25,6 +27,7 @@ const val website = "https://gammaplus.takeshobo.co.jp"
var taskChannel = Channel<ParseTask>()
val urlResultChannel = Channel<UrlResult>()
val htmlChannel= Channel<UrlParam>()
var currentJob: Job?=null
fun Application.parse(){
log.info("初始化解析任务")
@ -35,6 +38,11 @@ fun Application.parse(){
launch {
while (true){
val resHtml= htmlChannel.receive()
currentJob?.let {
if(it.isActive) this.cancel()
}
launch {
log.info("开始解析:${resHtml.url}")
val client = HttpClient(CIO)
@ -82,10 +90,12 @@ fun Application.parse(){
}
client.close()
}.apply {
currentJob=this
invokeOnCompletion {
log.info("${resHtml.url}解析完成")
}
}
}
}
@ -94,7 +104,7 @@ fun Application.parse(){
fun Application.configureRouting() {
routing {
val uploadDir=environment.config.property("ktor.deployment.filePath").getString()
val uploadDir = environment.config.property("ktor.deployment.filePath").getString()
static(uploadDir) {
resources(uploadDir)
}
@ -111,8 +121,8 @@ fun Application.configureRouting() {
}
}
launch {
while (true){
val task=taskChannel.receive()
while (true) {
val task = taskChannel.receive()
outgoing.send(Frame.Text(Json.encodeToString(task)))
}
}
@ -120,11 +130,19 @@ fun Application.configureRouting() {
when (frame) {
is Frame.Text -> {
val text = frame.readText()
outgoing.send(Frame.Text("YOU SAID: $text"))
if (text.equals("bye", ignoreCase = true)) {
close(CloseReason(CloseReason.Codes.NORMAL, "Client said BYE"))
when {
"cancel" == text -> {
if (currentJob?.isActive == true) {
currentJob?.cancel()
outgoing.send(Frame.Text("当前服务器解析任务已停止"))
} else {
outgoing.send(Frame.Text("当前服务器没有正在运行的解析任务"))
}
}
"exit" == text -> close(CloseReason(CloseReason.Codes.NORMAL, "Client said exit"))
}
}
else -> log.warn("无法处理${frame.frameType}类型消息")
}
}
}
@ -142,7 +160,7 @@ fun Application.configureRouting() {
val response: HttpResponse = client.get(urlParam)
if (response.status == HttpStatusCode.OK) {
val resHtml = response.readText()
htmlChannel.send(UrlParam(url = urlParam,html = resHtml))
htmlChannel.send(UrlParam(url = urlParam, html = resHtml))
call.respond(MessageResponse(message = "开始执行解析任务"))
} else {
log.warn("http code:${response.status}")

Loading…
Cancel
Save