1 /*
<lambda>null2  * Copyright 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.egg.quares
18 
19 import android.content.Context
20 import android.graphics.Bitmap
21 import android.graphics.Canvas
22 import android.graphics.drawable.Drawable
23 import android.graphics.drawable.Icon
24 import android.os.Parcel
25 import android.os.Parcelable
26 import java.util.ArrayList
27 import kotlin.math.abs
28 import kotlin.math.round
29 
30 class Quare(val width: Int, val height: Int, val depth: Int) : Parcelable {
31     private val data: IntArray = IntArray(width * height)
32     private val user: IntArray = data.copyOf()
33 
34     private fun loadAndQuantize(bitmap8bpp: Bitmap) {
35         bitmap8bpp.getPixels(data, 0, width, 0, 0, width, height)
36         if (depth == 8) return
37         val s = (255f / depth)
38         for (i in 0 until data.size) {
39             var f = (data[i] ushr 24).toFloat() / s
40             // f = f.pow(0.75f) // gamma adjust for bolder lines
41             f *= 1.25f // brightness adjust for bolder lines
42             f.coerceAtMost(1f)
43             data[i] = (round(f) * s).toInt() shl 24
44         }
45     }
46 
47     fun isBlank(): Boolean {
48         return data.sum() == 0
49     }
50 
51     fun load(drawable: Drawable) {
52         val resized = Bitmap.createBitmap(width, height, Bitmap.Config.ALPHA_8)
53         val canvas = Canvas(resized)
54         drawable.setBounds(0, 0, width, height)
55         drawable.setTint(0xFF000000.toInt())
56         drawable.draw(canvas)
57         loadAndQuantize(resized)
58         resized.recycle()
59     }
60 
61     fun load(context: Context, icon: Icon) {
62         icon.loadDrawable(context)?.let {
63             load(it)
64         }
65     }
66 
67     fun bitmap(): Bitmap {
68         return Bitmap.createBitmap(data, width, height, Bitmap.Config.ALPHA_8)
69     }
70 
71     fun getUserMark(x: Int, y: Int): Int {
72         return user[y * width + x] ushr 24
73     }
74 
75     fun setUserMark(x: Int, y: Int, v: Int) {
76         user[y * width + x] = v shl 24
77     }
78 
79     fun getDataAt(x: Int, y: Int): Int {
80         return data[y * width + x] ushr 24
81     }
82 
83     fun check(): Boolean {
84         return data.contentEquals(user)
85     }
86 
87     fun check(xSel: Int, ySel: Int): Boolean {
88         val xStart = if (xSel < 0) 0 else xSel
89         val xEnd = if (xSel < 0) width - 1 else xSel
90         val yStart = if (ySel < 0) 0 else ySel
91         val yEnd = if (ySel < 0) height - 1 else ySel
92         for (y in yStart..yEnd)
93             for (x in xStart..xEnd)
94                 if (getDataAt(x, y) != getUserMark(x, y)) return false
95         return true
96     }
97 
98     fun errors(): IntArray {
99         return IntArray(width * height) {
100             abs(data[it] - user[it])
101         }
102     }
103 
104     fun getRowClue(y: Int): IntArray {
105         return getClue(-1, y)
106     }
107     fun getColumnClue(x: Int): IntArray {
108         return getClue(x, -1)
109     }
110     fun getClue(xSel: Int, ySel: Int): IntArray {
111         val arr = ArrayList<Int>()
112         var len = 0
113         val xStart = if (xSel < 0) 0 else xSel
114         val xEnd = if (xSel < 0) width - 1 else xSel
115         val yStart = if (ySel < 0) 0 else ySel
116         val yEnd = if (ySel < 0) height - 1 else ySel
117         for (y in yStart..yEnd)
118             for (x in xStart..xEnd)
119                 if (getDataAt(x, y) != 0) {
120                     len++
121                 } else if (len > 0) {
122                     arr.add(len)
123                     len = 0
124                 }
125         if (len > 0) arr.add(len)
126         else if (arr.size == 0) arr.add(0)
127         return arr.toIntArray()
128     }
129 
130     fun resetUserMarks() {
131         user.forEachIndexed { index, _ -> user[index] = 0 }
132     }
133 
134     // Parcelable interface
135 
136     override fun describeContents(): Int {
137         return 0
138     }
139 
140     override fun writeToParcel(p: Parcel?, flags: Int) {
141         p?.let {
142             p.writeInt(width)
143             p.writeInt(height)
144             p.writeInt(depth)
145             p.writeIntArray(data)
146             p.writeIntArray(user)
147         }
148     }
149 
150     companion object CREATOR : Parcelable.Creator<Quare> {
151         override fun createFromParcel(p: Parcel?): Quare {
152             return p!!.let {
153                 Quare(
154                         p.readInt(), // width
155                         p.readInt(), // height
156                         p.readInt()  // depth
157                 ).also {
158                     p.readIntArray(it.data)
159                     p.readIntArray(it.user)
160                 }
161             }
162         }
163 
164         override fun newArray(size: Int): Array<Quare?> {
165             return arrayOfNulls(size)
166         }
167     }
168 }
169