<commit_before>/*
 * Copyright 2014 Google Inc.
 *
 * 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.google.android.apps.muzei.render

import android.content.ContentResolver
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.BitmapFactory.Options
import android.graphics.BitmapRegionDecoder
import android.graphics.Matrix
import android.graphics.Rect
import android.net.Uri
import android.os.Build
import android.support.media.ExifInterface
import android.util.Log
import kotlinx.coroutines.experimental.CommonPool
import kotlinx.coroutines.experimental.withContext
import java.io.IOException
import java.io.InputStream

/**
 * Wrapper for [BitmapRegionDecoder] with some extra functionality.
 */
class BitmapRegionLoader @Throws(IOException::class)
private constructor(private val inputStream: InputStream, private val rotation: Int = 0)
    : AutoCloseable {

    companion object {
        private const val TAG = "BitmapRegionLoader"

        suspend fun decode(
                contentResolver: ContentResolver,
                uri: Uri,
                targetWidth: Int = 0,
                targetHeight: Int = targetWidth
        ): Bitmap? = withContext(CommonPool) {
            try {
                val (originalWidth, originalHeight) = contentResolver.openInputStream(
                        uri)?.use { input ->
                    val options = BitmapFactory.Options().apply {
                        inJustDecodeBounds = true
                    }
                    BitmapFactory.decodeStream(input, null, options)
                    Pair(options.outWidth, options.outHeight)
                } ?: return@withContext null
                val rotation = getRotation(contentResolver, uri)
                val width = if (rotation == 90 || rotation == 270) originalHeight else originalWidth
                val height = if (rotation == 90 || rotation == 270) originalWidth else originalHeight
                contentResolver.openInputStream(uri)?.use { input ->
                    BitmapFactory.decodeStream(input, null,
                            BitmapFactory.Options().apply {
                                if (targetWidth != 0) {
                                    inSampleSize = Math.max(
                                            width.sampleSize(targetWidth),
                                            height.sampleSize(targetHeight))
                                }
                            })
                }?.run {
                    when (rotation) {
                        0 -> this
                        else -> {
                            val rotateMatrix = Matrix().apply {
                                postRotate(rotation.toFloat())
                            }
                            Bitmap.createBitmap(
                                    this, 0, 0,
                                    width, height,
                                    rotateMatrix, true).also { rotatedBitmap ->
                                if (rotatedBitmap != this) {
                                    recycle()
                                }
                            }
                        }
                    }
                }
            } catch (e: Exception) {
                Log.w(TAG, "Error decoding $uri", e)
                null
            }
        }

        private fun getRotation(
                contentResolver: ContentResolver,
                uri: Uri
        ): Int = try {
            contentResolver.openInputStream(uri)?.use { input ->
                val exifInterface = ExifInterface(input)
                val orientation = exifInterface.getAttributeInt(
                        ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL)
                when (orientation) {
                    ExifInterface.ORIENTATION_ROTATE_90 -> 90
                    ExifInterface.ORIENTATION_ROTATE_180 -> 180
                    ExifInterface.ORIENTATION_ROTATE_270 -> 270
                    else -> 0
                }
            }
        } catch (e: Exception) {
            Log.w(TAG, "Couldn't open EXIF interface for $uri", e)
        } ?: 0

        suspend fun newInstance(
                contentResolver: ContentResolver,
                uri: Uri
        ): BitmapRegionLoader? = withContext(CommonPool) {
            val rotation = getRotation(contentResolver, uri)
            val input = try {
                contentResolver.openInputStream(uri)
            } catch (e: IOException) {
                Log.w(TAG, "Couldn't openInputStream for $uri", e)
                null
            } ?: return@withContext null
            createInstance(input, rotation)
        }

        suspend fun newInstance(input: InputStream?, rotation: Int = 0): BitmapRegionLoader? {
            if (input == null) {
                return null
            }

            return withContext(CommonPool) {
                createInstance(input, rotation)
            }
        }<commit_msg>Move all access to BitmapRegionLoader to a single thread<commit_after>/*
 * Copyright 2014 Google Inc.
 *
 * 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.google.android.apps.muzei.render

import android.content.ContentResolver
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.BitmapFactory.Options
import android.graphics.BitmapRegionDecoder
import android.graphics.Matrix
import android.graphics.Rect
import android.net.Uri
import android.os.Build
import android.support.media.ExifInterface
import android.util.Log
import kotlinx.coroutines.experimental.newSingleThreadContext
import kotlinx.coroutines.experimental.withContext
import java.io.IOException
import java.io.InputStream

/**
 * Wrapper for [BitmapRegionDecoder] with some extra functionality.
 */
class BitmapRegionLoader @Throws(IOException::class)
private constructor(private val inputStream: InputStream, private val rotation: Int = 0)
    : AutoCloseable {

    companion object {
        private const val TAG = "BitmapRegionLoader"

        private val singleThreadContext by lazy {
            newSingleThreadContext(TAG)
        }

        suspend fun decode(
                contentResolver: ContentResolver,
                uri: Uri,
                targetWidth: Int = 0,
                targetHeight: Int = targetWidth
        ): Bitmap? = withContext(singleThreadContext) {
            try {
                val (originalWidth, originalHeight) = contentResolver.openInputStream(
                        uri)?.use { input ->
                    val options = BitmapFactory.Options().apply {
                        inJustDecodeBounds = true
                    }
                    BitmapFactory.decodeStream(input, null, options)
                    Pair(options.outWidth, options.outHeight)
                } ?: return@withContext null
                val rotation = getRotation(contentResolver, uri)
                val width = if (rotation == 90 || rotation == 270) originalHeight else originalWidth
                val height = if (rotation == 90 || rotation == 270) originalWidth else originalHeight
                contentResolver.openInputStream(uri)?.use { input ->
                    BitmapFactory.decodeStream(input, null,
                            BitmapFactory.Options().apply {
                                if (targetWidth != 0) {
                                    inSampleSize = Math.max(
                                            width.sampleSize(targetWidth),
                                            height.sampleSize(targetHeight))
                                }
                            })
                }?.run {
                    when (rotation) {
                        0 -> this
                        else -> {
                            val rotateMatrix = Matrix().apply {
                                postRotate(rotation.toFloat())
                            }
                            Bitmap.createBitmap(
                                    this, 0, 0,
                                    width, height,
                                    rotateMatrix, true).also { rotatedBitmap ->
                                if (rotatedBitmap != this) {
                                    recycle()
                                }
                            }
                        }
                    }
                }
            } catch (e: Exception) {
                Log.w(TAG, "Error decoding $uri", e)
                null
            }
        }

        private fun getRotation(
                contentResolver: ContentResolver,
                uri: Uri
        ): Int = try {
            contentResolver.openInputStream(uri)?.use { input ->
                val exifInterface = ExifInterface(input)
                val orientation = exifInterface.getAttributeInt(
                        ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL)
                when (orientation) {
                    ExifInterface.ORIENTATION_ROTATE_90 -> 90
                    ExifInterface.ORIENTATION_ROTATE_180 -> 180
                    ExifInterface.ORIENTATION_ROTATE_270 -> 270
                    else -> 0
                }
            }
        } catch (e: Exception) {
            Log.w(TAG, "Couldn't open EXIF interface for $uri", e)
        } ?: 0

        suspend fun newInstance(
                contentResolver: ContentResolver,
                uri: Uri
        ): BitmapRegionLoader? = withContext(singleThreadContext) {
            val rotation = getRotation(contentResolver, uri)
            val input = try {
                contentResolver.openInputStream(uri)
            } catch (e: IOException) {
                Log.w(TAG, "Couldn't openInputStream for $uri", e)
                null
            } ?: return@withContext null
            createInstance(input, rotation)
        }

        suspend fun newInstance(input: InputStream?, rotation: Int = 0): BitmapRegionLoader? {
            if (input == null) {
                return null
            }

            return withContext(singleThreadContext) {
                createInstance(input, rotation)
            }
        }
{'fraction_non_alphanumeric': 0.07294724668623788, 'fraction_numerical': 0.007650564896361534, 'mean_word_length': 1.4653508771929824, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 23, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 22, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3528372', 'n_tokens_mistral': 2662, 'n_tokens_neox': 2432, 'n_words': 682}