mirror of
https://github.com/librecaptcha/lc-core.git
synced 2025-04-15 00:34:19 -04:00
125 lines
3.7 KiB
Scala
125 lines
3.7 KiB
Scala
package lc
|
|
|
|
import java.awt.image.BufferedImage
|
|
import java.awt.RenderingHints
|
|
import java.awt.Font
|
|
import java.awt.font.TextAttribute
|
|
import java.awt.Color
|
|
import java.io.ByteArrayOutputStream
|
|
import javax.imageio.ImageIO
|
|
import javax.imageio.stream.ImageOutputStream;
|
|
import javax.imageio.stream.MemoryCacheImageOutputStream;
|
|
|
|
class Drop {
|
|
var x = 0
|
|
var y = 0
|
|
var yOffset = 0
|
|
var color = 0
|
|
var colorChange = 10
|
|
def mkColor = {
|
|
new Color(color, color, math.min(200, color+100))
|
|
}
|
|
}
|
|
|
|
class RainDropsCP extends ChallengeProvider {
|
|
private val alphabet = "abcdefghijklmnopqrstuvwxyz"
|
|
private val n = 6
|
|
private val bgColor = new Color(200, 200, 200)
|
|
private val textColor = new Color(208, 208, 218)
|
|
private val textHighlightColor = new Color(100, 100, 125)
|
|
|
|
def getId = "FilterChallenge"
|
|
|
|
private def extendDrops(drops: Array[Drop], steps: Int, xOffset: Int) = {
|
|
drops.map(d => {
|
|
val nd = new Drop()
|
|
nd.x + xOffset*steps
|
|
nd.y + d.yOffset*steps
|
|
nd
|
|
})
|
|
}
|
|
|
|
def returnChallenge(): Challenge = {
|
|
val r = new scala.util.Random
|
|
val secret = Stream.continually(r.nextInt(alphabet.size)).map(alphabet).take(n).mkString
|
|
val width = 450
|
|
val height = 100
|
|
val imgType = BufferedImage.TYPE_INT_RGB
|
|
val xOffset = 2+r.nextInt(3)
|
|
val xBias = (height / 10) - 2
|
|
val dropsOrig = Array.fill[Drop](2000)( new Drop())
|
|
for (d <- dropsOrig) {
|
|
d.x = r.nextInt(width) - (xBias/2)*xOffset
|
|
d.yOffset = 6+r.nextInt(6)
|
|
d.y = r.nextInt(height)
|
|
d.color = r.nextInt(240)
|
|
if (d.color > 128) {
|
|
d.colorChange *= -1
|
|
}
|
|
}
|
|
val drops = dropsOrig ++ extendDrops(dropsOrig, 1, xOffset) ++ extendDrops(dropsOrig, 2, xOffset) ++ extendDrops(dropsOrig, 3, xOffset)
|
|
|
|
|
|
val baseFont = new Font(Font.MONOSPACED, Font.BOLD, 80)
|
|
val attributes = new java.util.HashMap[TextAttribute, Object]()
|
|
attributes.put(TextAttribute.TRACKING, Double.box(0.2))
|
|
attributes.put(TextAttribute.WEIGHT, TextAttribute.WEIGHT_EXTRABOLD)
|
|
val spacedFont = baseFont.deriveFont(attributes)
|
|
|
|
val baos = new ByteArrayOutputStream();
|
|
val ios = new MemoryCacheImageOutputStream(baos);
|
|
val writer = new GifSequenceWriter(ios, imgType, 60, true);
|
|
for(i <- 0 until 60){
|
|
// val yOffset = 5+r.nextInt(5)
|
|
val canvas = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB)
|
|
val g = canvas.createGraphics()
|
|
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON)
|
|
|
|
// clear the canvas
|
|
g.setColor(bgColor)
|
|
g.fillRect(0, 0, canvas.getWidth, canvas.getHeight)
|
|
|
|
// paint the rain
|
|
for (d <- drops) {
|
|
g.setColor(d.mkColor)
|
|
g.drawLine(d.x, d.y, d.x+xOffset, d.y+d.yOffset)
|
|
d.x += xOffset/2
|
|
d.y += d.yOffset/2
|
|
d.color += d.colorChange
|
|
if (d.x > width || d.y > height) {
|
|
val ySteps = (height / d.yOffset) + 1
|
|
d.x -= xOffset*ySteps
|
|
d.y -= d.yOffset*ySteps
|
|
|
|
}
|
|
if (d.color > 200 || d.color < 21) {
|
|
d.colorChange *= -1
|
|
}
|
|
}
|
|
|
|
// center the text
|
|
g.setFont(spacedFont)
|
|
val textWidth = g.getFontMetrics().charsWidth(secret.toCharArray, 0, secret.toCharArray.length)
|
|
val textX = (width - textWidth)/2
|
|
|
|
// paint the top outline
|
|
g.setColor(textHighlightColor)
|
|
g.drawString(secret, textX, 69)
|
|
// paint the text
|
|
g.setColor(textColor)
|
|
g.drawString(secret, textX, 70)
|
|
|
|
g.dispose()
|
|
writer.writeToSequence(canvas)
|
|
}
|
|
writer.close
|
|
ios.close
|
|
|
|
// ImageIO.write(canvas,"png",baos);
|
|
new Challenge(baos.toByteArray, "image/gif", secret)
|
|
}
|
|
def checkAnswer(secret: String, answer: String): Boolean = {
|
|
secret == answer
|
|
}
|
|
}
|