blob: eb77362a0be2c069ce38a637a0dc20783860d4d6 [file] [log] [blame]
/*
* Copyright 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.egg.quares
import android.content.Context
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.drawable.Drawable
import android.graphics.drawable.Icon
import android.os.Parcel
import android.os.Parcelable
import java.util.ArrayList
import kotlin.math.abs
import kotlin.math.round
class Quare(val width: Int, val height: Int, val depth: Int) : Parcelable {
private val data: IntArray = IntArray(width * height)
private val user: IntArray = data.copyOf()
private fun loadAndQuantize(bitmap8bpp: Bitmap) {
bitmap8bpp.getPixels(data, 0, width, 0, 0, width, height)
if (depth == 8) return
val s = (255f / depth)
for (i in 0 until data.size) {
var f = (data[i] ushr 24).toFloat() / s
// f = f.pow(0.75f) // gamma adjust for bolder lines
f *= 1.25f // brightness adjust for bolder lines
f.coerceAtMost(1f)
data[i] = (round(f) * s).toInt() shl 24
}
}
fun isBlank(): Boolean {
return data.sum() == 0
}
fun load(drawable: Drawable) {
val resized = Bitmap.createBitmap(width, height, Bitmap.Config.ALPHA_8)
val canvas = Canvas(resized)
drawable.setBounds(0, 0, width, height)
drawable.setTint(0xFF000000.toInt())
drawable.draw(canvas)
loadAndQuantize(resized)
resized.recycle()
}
fun load(context: Context, icon: Icon) {
icon.loadDrawable(context)?.let {
load(it)
}
}
fun bitmap(): Bitmap {
return Bitmap.createBitmap(data, width, height, Bitmap.Config.ALPHA_8)
}
fun getUserMark(x: Int, y: Int): Int {
return user[y * width + x] ushr 24
}
fun setUserMark(x: Int, y: Int, v: Int) {
user[y * width + x] = v shl 24
}
fun getDataAt(x: Int, y: Int): Int {
return data[y * width + x] ushr 24
}
fun check(): Boolean {
return data.contentEquals(user)
}
fun check(xSel: Int, ySel: Int): Boolean {
val xStart = if (xSel < 0) 0 else xSel
val xEnd = if (xSel < 0) width - 1 else xSel
val yStart = if (ySel < 0) 0 else ySel
val yEnd = if (ySel < 0) height - 1 else ySel
for (y in yStart..yEnd)
for (x in xStart..xEnd)
if (getDataAt(x, y) != getUserMark(x, y)) return false
return true
}
fun errors(): IntArray {
return IntArray(width * height) {
abs(data[it] - user[it])
}
}
fun getRowClue(y: Int): IntArray {
return getClue(-1, y)
}
fun getColumnClue(x: Int): IntArray {
return getClue(x, -1)
}
fun getClue(xSel: Int, ySel: Int): IntArray {
val arr = ArrayList<Int>()
var len = 0
val xStart = if (xSel < 0) 0 else xSel
val xEnd = if (xSel < 0) width - 1 else xSel
val yStart = if (ySel < 0) 0 else ySel
val yEnd = if (ySel < 0) height - 1 else ySel
for (y in yStart..yEnd)
for (x in xStart..xEnd)
if (getDataAt(x, y) != 0) {
len++
} else if (len > 0) {
arr.add(len)
len = 0
}
if (len > 0) arr.add(len)
else if (arr.size == 0) arr.add(0)
return arr.toIntArray()
}
fun resetUserMarks() {
user.forEachIndexed { index, _ -> user[index] = 0 }
}
// Parcelable interface
override fun describeContents(): Int {
return 0
}
override fun writeToParcel(p: Parcel?, flags: Int) {
p?.let {
p.writeInt(width)
p.writeInt(height)
p.writeInt(depth)
p.writeIntArray(data)
p.writeIntArray(user)
}
}
companion object CREATOR : Parcelable.Creator<Quare> {
override fun createFromParcel(p: Parcel?): Quare {
return p!!.let {
Quare(
p.readInt(), // width
p.readInt(), // height
p.readInt() // depth
).also {
p.readIntArray(it.data)
p.readIntArray(it.user)
}
}
}
override fun newArray(size: Int): Array<Quare?> {
return arrayOfNulls(size)
}
}
}