OSDN Git Service

内蔵カメラでキャッシュ表示中に表示している画像を保存できるようにする。
[gokigen/mangle.git] / app / src / main / java / jp / osdn / gokigen / gokigenassets / liveview / LiveImageView.kt
1 package jp.osdn.gokigen.gokigenassets.liveview
2
3 import android.content.Context
4 import android.content.res.Configuration
5 import android.graphics.*
6 import android.os.Looper
7 import android.util.AttributeSet
8 import android.util.DisplayMetrics
9 import android.util.Log
10 import android.util.TypedValue
11 import android.view.MotionEvent
12 import android.view.View
13 import android.widget.ImageView
14 import android.widget.SeekBar
15 import android.widget.SeekBar.OnSeekBarChangeListener
16 import jp.osdn.gokigen.gokigenassets.camera.interfaces.ICameraControl
17 import jp.osdn.gokigen.gokigenassets.camera.interfaces.IFocusingModeNotify
18 import jp.osdn.gokigen.gokigenassets.constants.IApplicationConstantConvert.Companion.ID_DRAWABLE_BACKGROUND_IMAGE
19 import jp.osdn.gokigen.gokigenassets.constants.IApplicationConstantConvert.Companion.MAX_VALUE_SEEKBAR
20 import jp.osdn.gokigen.gokigenassets.liveview.focusframe.IAutoFocusFrameDisplay
21 import jp.osdn.gokigen.gokigenassets.liveview.focusframe.IFocusFrameDrawer
22 import jp.osdn.gokigen.gokigenassets.liveview.gridframe.GridFrameFactory
23 import jp.osdn.gokigen.gokigenassets.liveview.gridframe.IGridFrameDrawer
24 import jp.osdn.gokigen.gokigenassets.liveview.gridframe.IShowGridFrame
25 import jp.osdn.gokigen.gokigenassets.liveview.image.IImageProvider
26 import jp.osdn.gokigen.gokigenassets.liveview.message.IMessageDrawer
27 import jp.osdn.gokigen.gokigenassets.liveview.message.InformationDrawer
28 import java.util.*
29 import kotlin.math.min
30
31 class LiveImageView : View, ILiveView, ILiveViewRefresher, IShowGridFrame, OnSeekBarChangeListener, IFocusingModeNotify, IFocusFrameDrawer, IAutoFocusFrameDisplay, ICachePositionProvider
32 {
33     companion object
34     {
35         private val TAG = LiveImageView::class.java.simpleName
36     }
37
38     private var sliderPosition : Float = 0.0f
39     private var imageRotationDegrees : Int = 0
40     private var showGrid : Boolean = false
41     private var showingFocusFrame = false
42     private val imageScaleType: ImageView.ScaleType = ImageView.ScaleType.FIT_CENTER
43     private lateinit var imageBitmap: Bitmap
44     private var focusFrameStatus: IAutoFocusFrameDisplay.FocusFrameStatus = IAutoFocusFrameDisplay.FocusFrameStatus.None
45     private lateinit var imageProvider : IImageProvider
46     private lateinit var gridFrameDrawer : IGridFrameDrawer
47     private lateinit var informationDrawer : InformationDrawer
48     private lateinit var indicatorControl: IndicatorControl
49     private var focusFrameRect: RectF? = null
50     private var focusFrameHideTimer: Timer? = null
51     private var isRotationImage = false
52
53     constructor(context: Context) : super(context)
54     {
55         initComponent(context)
56     }
57
58     constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
59     {
60         initComponent(context)
61     }
62
63     constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
64     {
65         initComponent(context)
66     }
67
68     constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes)
69     {
70         initComponent(context)
71     }
72
73     private fun initComponent(context: Context)
74     {
75         gridFrameDrawer = GridFrameFactory().getGridFrameDrawer(0)
76         //focusFrameDrawer = FocusFrameDrawer(context)
77         informationDrawer = InformationDrawer(this)
78         indicatorControl = IndicatorControl()
79         imageBitmap = BitmapFactory.decodeResource(context.resources, ID_DRAWABLE_BACKGROUND_IMAGE)
80     }
81
82     fun injectDisplay(cameraControl: ICameraControl)
83     {
84         isRotationImage = cameraControl.needRotateImage()
85         cameraControl.getDisplayInjector()?.injectDisplay(this, indicatorControl, this)
86     }
87
88     override fun getMessageDrawer() : IMessageDrawer
89     {
90         return (informationDrawer)
91     }
92
93     override fun refresh()
94     {
95         refreshCanvas()
96     }
97
98     override fun setImageProvider(provider: IImageProvider)
99     {
100         this.imageProvider = provider
101     }
102
103     override fun updateImageRotation(degrees: Int)
104     {
105         this.imageRotationDegrees = degrees
106         refreshCanvas()
107     }
108
109     override fun onDraw(canvas: Canvas?)
110     {
111         super.onDraw(canvas)
112         if ((canvas == null)||(!(::imageProvider.isInitialized)))
113         {
114             Log.v(TAG, " ===== onDraw : canvas is not ready. ==== ")
115             return
116         }
117         //Log.v(TAG, " ----- onDraw() ----- ")
118         canvas.drawARGB(255, 0, 0, 0)
119         val imageRectF = drawImage(canvas)
120         if (showGrid)
121         {
122             gridFrameDrawer.drawFramingGrid(canvas, imageRectF)
123         }
124         this.drawFocusFrame(canvas,imageRectF.width(), imageRectF.height())
125         informationDrawer.drawInformationMessages(canvas, imageRectF)
126         informationDrawer.drawLevelGauge(canvas, imageRotationDegrees)
127     }
128
129     override fun showGridFrame(isShowGrid: Boolean)
130     {
131         this.showGrid = isShowGrid
132         refreshCanvas()
133     }
134
135     private fun drawImage(canvas: Canvas) : RectF
136     {
137         val centerX = canvas.width / 2
138         val centerY = canvas.height / 2
139
140         val paint = Paint(Color.LTGRAY)
141         paint.strokeWidth = 1.0f
142         paint.style = Paint.Style.STROKE
143
144         imageBitmap = imageProvider.getImage(sliderPosition)
145
146         var addDegrees = 0
147         if (isRotationImage)
148         {
149             try
150             {
151                 val config = context.resources.configuration
152                 if (config.orientation == Configuration.ORIENTATION_LANDSCAPE)
153                 {
154                     addDegrees = 90
155                 }
156             }
157             catch (e: Exception)
158             {
159                 e.printStackTrace()
160             }
161         }
162         val degrees = imageRotationDegrees + addDegrees
163         val viewRect = decideViewRect(canvas, imageBitmap, degrees)
164         val width : Int = imageBitmap.width
165         val height : Int = imageBitmap.height
166         val imageRect = Rect(0, 0, width, height)
167
168         //Log.v(TAG, " canvas:   ${canvas.width} x ${canvas.height} (D: ${rotationDegrees}) ")
169         //Log.v(TAG, " bitmap:   ${imageBitmap.width} x ${imageBitmap.height} (D: ${rotationDegrees}) ")
170         //Log.v(TAG, " imageRect: [${imageRect.left},${imageRect.top}]-[${imageRect.right},${imageRect.bottom}] ")
171         //Log.v(TAG, " viewRect: [${viewRect.left},${viewRect.top}]-[${viewRect.right},${viewRect.bottom}] ")
172
173         canvas.rotate(degrees.toFloat(), centerX.toFloat(), centerY.toFloat())
174         canvas.drawBitmap(imageBitmap, imageRect, viewRect, paint)
175         canvas.rotate(-degrees.toFloat(), centerX.toFloat(), centerY.toFloat())
176
177         return (viewRect)
178     }
179
180     private fun refreshCanvas()
181     {
182         //Log.v(TAG, " refreshCanvas()")
183         if (Looper.getMainLooper().thread === Thread.currentThread())
184         {
185             invalidate()
186         }
187         else
188         {
189             postInvalidate()
190         }
191     }
192
193     private fun decideViewRect(canvas: Canvas, bitmapToShow: Bitmap?, rotationDegrees: Int): RectF
194     {
195         if (bitmapToShow == null)
196         {
197             return (RectF(0.0f, 0.0f, 1.0f, 1.0f))
198         }
199         val srcWidth: Int
200         val srcHeight: Int
201         if ((rotationDegrees == 0)||(rotationDegrees == 180))
202         {
203             srcWidth = bitmapToShow.width
204             srcHeight = bitmapToShow.height
205         }
206         else
207         {
208             srcWidth = bitmapToShow.height
209             srcHeight = bitmapToShow.width
210         }
211         val maxWidth = canvas.width
212         val maxHeight = canvas.height
213         val centerX = canvas.width / 2
214         val centerY = canvas.height / 2
215         val widthRatio = maxWidth / srcWidth.toFloat()
216         val heightRatio = maxHeight / srcHeight.toFloat()
217         val smallRatio = min(widthRatio, heightRatio)
218         val dstWidth: Float
219         val dstHeight: Float
220         if (widthRatio < heightRatio)
221         {
222             dstWidth = maxWidth.toFloat()
223             dstHeight = (smallRatio * srcHeight)
224         }
225         else
226         {
227             dstHeight = maxHeight.toFloat()
228             dstWidth = (smallRatio * srcWidth)
229         }
230         val halfWidth = dstWidth * 0.5f
231         val halfHeight = dstHeight * 0.5f
232         return (if (rotationDegrees == 0 || rotationDegrees == 180)
233         {
234             RectF(
235                 (centerX - halfWidth),
236                 (centerY - halfHeight),
237                 ((centerX - halfWidth) + dstWidth),
238                 ((centerY - halfHeight) + dstHeight)
239             )
240         }
241         else
242         {
243             RectF(
244                 (centerX - halfHeight),
245                 (centerY - halfWidth),
246                 ((centerX - halfHeight) + dstHeight),
247                 ((centerY - halfWidth) + dstWidth)
248             )
249         })
250     }
251
252     override fun getCachePosition(): Float
253     {
254         //Log.v(TAG, " ----- sliderPosition : $sliderPosition")
255         return (sliderPosition)
256     }
257
258     override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean)
259     {
260         Log.v(TAG, " onProgressChanged() Progress: $progress (fromUser:$fromUser)")
261         sliderPosition = (((progress).toFloat()) / ((MAX_VALUE_SEEKBAR).toFloat()))
262         refreshCanvas()
263     }
264
265     override fun onStartTrackingTouch(seekBar: SeekBar?)
266     {
267         Log.v(TAG, " onStartTrackingTouch() ")
268     }
269
270     override fun onStopTrackingTouch(seekBar: SeekBar?)
271     {
272         Log.v(TAG, " onStopTrackingTouch() ")
273     }
274
275     override fun changedFocusingMode()
276     {
277         Log.v(TAG, " changedFocusingMode()")
278     }
279
280     override fun drawFocusFrame(canvas: Canvas, imageWidth : Float, imageHeight : Float)
281     {
282
283         val focusRectOnImage: RectF = convertRectOnViewfinderIntoLiveImage(focusFrameRect, imageWidth, imageHeight, imageRotationDegrees)
284         val focusRectOnView: RectF = convertRectFromImageArea(focusRectOnImage)
285
286         // Draw a rectangle to the canvas.
287         // Draw a rectangle to the canvas.
288         val focusFramePaint = Paint()
289         focusFramePaint.style = Paint.Style.STROKE
290         when (focusFrameStatus)
291         {
292             IAutoFocusFrameDisplay.FocusFrameStatus.Running -> focusFramePaint.color = Color.WHITE
293             IAutoFocusFrameDisplay.FocusFrameStatus.Focused -> focusFramePaint.color = Color.GREEN
294             IAutoFocusFrameDisplay.FocusFrameStatus.Failed -> focusFramePaint.color = Color.RED
295             IAutoFocusFrameDisplay.FocusFrameStatus.Errored -> focusFramePaint.color = Color.YELLOW
296             else -> focusFramePaint.color = Color.BLACK
297         }
298         val focusFrameStrokeWidth = 2.0f
299         val dm: DisplayMetrics = context.resources.displayMetrics
300         val strokeWidth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, focusFrameStrokeWidth, dm)
301         focusFramePaint.strokeWidth = strokeWidth
302
303         canvas.drawRect(focusRectOnView, focusFramePaint)
304     }
305
306     override fun getContentSizeWidth(): Float
307     {
308         return (imageBitmap.width).toFloat()
309     }
310
311     override fun getContentSizeHeight(): Float
312     {
313         return (imageBitmap.height).toFloat()
314     }
315
316     override fun getPointWithEvent(event: MotionEvent?): PointF?
317     {
318         if (event == null)
319         {
320             return null
321         }
322
323         val pointOnView = PointF(event.x - x, event.y - y) // Viewの表示位置に補正
324         val pointOnImage: PointF = convertPointFromViewArea(pointOnView)
325         val imageWidth: Float
326         val imageHeight: Float
327         if (imageRotationDegrees == 0 || imageRotationDegrees == 180)
328         {
329             imageWidth = imageBitmap.width.toFloat()
330             imageHeight = imageBitmap.height.toFloat()
331         }
332         else
333         {
334             imageWidth = imageBitmap.height.toFloat()
335             imageHeight = imageBitmap.width.toFloat()
336         }
337         return convertPointOnLiveImageIntoViewfinder(
338             pointOnImage,
339             imageWidth,
340             imageHeight,
341             imageRotationDegrees
342         )
343     }
344
345     override fun isContainsPoint(point: PointF?): Boolean
346     {
347         return point != null && RectF(0.0f, 0.0f, 1.0f, 1.0f).contains(point.x, point.y)
348     }
349
350     override fun showFocusFrame(rect : RectF?, status : IAutoFocusFrameDisplay.FocusFrameStatus, duration : Float)
351     {
352         try
353         {
354             if (focusFrameHideTimer != null) {
355                 focusFrameHideTimer?.cancel()
356                 focusFrameHideTimer = null
357             }
358
359             showingFocusFrame = true
360             focusFrameStatus = status
361             focusFrameRect = rect
362
363             refreshCanvas()
364
365             if (duration > 0) {
366                 focusFrameHideTimer = Timer()
367                 focusFrameHideTimer?.schedule(object : TimerTask() {
368                     override fun run() {
369                         hideFocusFrame()
370                     }
371                 }, (duration * 1000).toLong())
372             }
373         }
374         catch (e : Exception)
375         {
376             e.printStackTrace()
377         }
378     }
379
380     override fun hideFocusFrame()
381     {
382         try
383         {
384             if (focusFrameHideTimer != null)
385             {
386                 focusFrameHideTimer?.cancel()
387                 focusFrameHideTimer = null
388             }
389             showingFocusFrame = false
390             refreshCanvas()
391         }
392         catch (e : Exception)
393         {
394             e.printStackTrace()
395         }
396     }
397
398     private fun convertRectOnViewfinderIntoLiveImage(rect: RectF?, width: Float, height: Float, rotatedDegrees: Int): RectF
399     {
400         var top = 0.0f
401         var bottom = 1.0f
402         var left = 0.0f
403         var right = 1.0f
404         try
405         {
406             if (rect != null)
407             {
408                 if (rotatedDegrees == 0 || rotatedDegrees == 180)
409                 {
410                     top = rect.top * height
411                     bottom = rect.bottom * height
412                     left = rect.left * width
413                     right = rect.right * width
414                 }
415                 else
416                 {
417                     left = rect.top * height
418                     right = rect.bottom * height
419                     top = rect.left * width
420                     bottom = rect.right * width
421                 }
422             }
423         }
424         catch (e: Exception)
425         {
426             e.printStackTrace()
427         }
428         //Log.v(TAG, " [$left,$top]-[$right,$bottom]")
429         return RectF(left, top, right, bottom)
430     }
431
432     private fun convertRectFromImageArea(rect: RectF): RectF
433     {
434         val imageTopLeft = PointF(rect.left, rect.top)
435         val imageBottomRight = PointF(rect.right, rect.bottom)
436         val viewTopLeft: PointF = convertPointFromImageArea(imageTopLeft)
437         val viewBottomRight: PointF = convertPointFromImageArea(imageBottomRight)
438         return RectF(viewTopLeft.x, viewTopLeft.y, viewBottomRight.x, viewBottomRight.y)
439     }
440
441     private fun convertPointFromImageArea(point: PointF): PointF
442     {
443         var viewPointX = point.x
444         var viewPointY = point.y
445         val imageSizeWidth: Float
446         val imageSizeHeight: Float
447         if (imageRotationDegrees == 0 || imageRotationDegrees == 180)
448         {
449             imageSizeWidth = (imageBitmap.width).toFloat()
450             imageSizeHeight = (imageBitmap.height).toFloat()
451         }
452         else
453         {
454             imageSizeWidth = imageBitmap.height.toFloat()
455             imageSizeHeight = imageBitmap.width.toFloat()
456         }
457         val viewSizeWidth: Float = this.width.toFloat()
458         val viewSizeHeight: Float = this.height.toFloat()
459         val ratioX = viewSizeWidth / imageSizeWidth
460         val ratioY = viewSizeHeight / imageSizeHeight
461         val scale: Float
462         when (imageScaleType)
463         {
464             ImageView.ScaleType.FIT_XY -> {
465                 viewPointX *= ratioX
466                 viewPointY *= ratioY
467             }
468             ImageView.ScaleType.FIT_CENTER, ImageView.ScaleType.CENTER_INSIDE -> {
469                 scale = ratioX.coerceAtMost(ratioY)
470                 //viewPointX *= scale
471                 //viewPointY *= scale
472                 viewPointX += (viewSizeWidth - imageSizeWidth * scale)  / 2.0f
473                 viewPointY += (viewSizeHeight - imageSizeHeight * scale) / 2.0f
474             }
475             ImageView.ScaleType.CENTER_CROP -> {
476                 scale = ratioX.coerceAtLeast(ratioY)
477                 viewPointX *= scale
478                 viewPointY *= scale
479                 viewPointX += (viewSizeWidth - imageSizeWidth * scale) / 2.0f
480                 viewPointY += (viewSizeHeight - imageSizeHeight * scale) / 2.0f
481             }
482             ImageView.ScaleType.CENTER -> {
483                 viewPointX += (viewSizeWidth / 2.0f - imageSizeWidth / 2.0f)
484                 viewPointY += (viewSizeHeight / 2.0f - imageSizeHeight / 2.0f)
485             }
486             else -> {
487             }
488         }
489         // Log.v(TAG, "(viewPointX : $viewPointX, viewPointY : $viewPointY) : $imageSizeWidth x $imageSizeHeight")
490         return PointF(viewPointX, viewPointY)
491     }
492
493     /**
494      *   ライブビュー座標系の点座標をビューファインダー座標系の点座標に変換
495      *
496      */
497     private fun convertPointOnLiveImageIntoViewfinder(point: PointF, width: Float, height: Float, rotatedDegrees: Int): PointF
498     {
499         var viewFinderPointX = 0.5f
500         var viewFinderPointY = 0.5f
501         try
502         {
503             if (rotatedDegrees == 0 || rotatedDegrees == 180) {
504                 viewFinderPointX = point.x / width
505                 viewFinderPointY = point.y / height
506             } else {
507                 viewFinderPointX = point.y / width
508                 viewFinderPointY = point.x / height
509             }
510         }
511         catch (e: java.lang.Exception)
512         {
513             e.printStackTrace()
514         }
515         return PointF(viewFinderPointX, viewFinderPointY)
516     }
517
518     /**
519      * Converts a point on view area to a point on image area.
520      *
521      * @param point A point on view area. (e.g. a touch panel view)
522      * @return A point on image area. (e.g. a live preview image)
523      */
524     private fun convertPointFromViewArea(point: PointF): PointF
525     {
526         var imagePointX = point.x
527         var imagePointY = point.y
528         val imageSizeWidth: Float
529         val imageSizeHeight: Float
530         if (imageRotationDegrees == 0 || imageRotationDegrees == 180) {
531             imageSizeWidth = imageBitmap.width.toFloat()
532             imageSizeHeight = imageBitmap.height.toFloat()
533         } else {
534             imageSizeWidth = imageBitmap.height.toFloat()
535             imageSizeHeight = imageBitmap.width.toFloat()
536         }
537         val viewSizeWidth = this.width.toFloat()
538         val viewSizeHeight = this.height.toFloat()
539         val ratioX = viewSizeWidth / imageSizeWidth
540         val ratioY = viewSizeHeight / imageSizeHeight
541         val scale: Float // = 1.0f;
542         when (imageScaleType) {
543             ImageView.ScaleType.FIT_XY -> {
544                 imagePointX /= ratioX
545                 imagePointY /= ratioY
546             }
547             ImageView.ScaleType.FIT_CENTER, ImageView.ScaleType.CENTER_INSIDE -> {
548                 scale = ratioX.coerceAtMost(ratioY)
549                 imagePointX -= (viewSizeWidth - imageSizeWidth * scale) / 2.0f
550                 imagePointY -= (viewSizeHeight - imageSizeHeight * scale) / 2.0f
551                 imagePointX /= scale
552                 imagePointY /= scale
553             }
554             ImageView.ScaleType.CENTER_CROP -> {
555                 scale = ratioX.coerceAtLeast(ratioY)
556                 imagePointX -= (viewSizeWidth - imageSizeWidth * scale) / 2.0f
557                 imagePointY -= (viewSizeHeight - imageSizeHeight * scale) / 2.0f
558                 imagePointX /= scale
559                 imagePointY /= scale
560             }
561             ImageView.ScaleType.CENTER -> {
562                 imagePointX -= (viewSizeWidth - imageSizeWidth) / 2.0f
563                 imagePointY -= (viewSizeHeight - imageSizeHeight) / 2.0f
564             }
565             else -> {
566             }
567         }
568         return PointF(imagePointX, imagePointY)
569     }
570
571 }