Skip to content

Commit

Permalink
Merge pull request #207 from numandev1/fix/png-transparent-background
Browse files Browse the repository at this point in the history
fix: png transparent background
  • Loading branch information
numandev1 authored Oct 5, 2023
2 parents d1a3fea + cdea764 commit a302273
Show file tree
Hide file tree
Showing 7 changed files with 66 additions and 66 deletions.
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,12 @@ await clearCache(); // this will clear cache of thumbnails cache directory

- ###### `output: OutputType` (default: jpg)

Can be either `jpg` or `png`, defines the output image format.
The quality modifier for the `JPEG` file format, can be specified when output is `PNG` but will be ignored. if you wanna apply quality modifier then you can enable `disablePngTransparency:true`,
**Note:** if you png image have no transparent background then enable `disablePngTransparency:true` modifier is recommended

- ###### `disablePngTransparency: boolean` (default: false)

when user add `output:'png'` then by default compressed image will have transparent background, and quality will be ignored, if you wanna apply quality then you have to disablePngTransparency like `disablePngTransparency:true`, it will convert transparent background to white

- ###### `returnableOutputType: ReturnableOutputType` (default: uri)
Can be either `uri` or `base64`, defines the Returnable output image format.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,41 +71,44 @@ object ImageCompressor {
}

fun resize(image: Bitmap, maxWidth: Int, maxHeight: Int): Bitmap {
val size = findActualSize(image, maxWidth, maxHeight)
val scaledImage = Bitmap.createBitmap(size.width, size.height, image.config)
val scaleMatrix = Matrix()
val canvas = Canvas(scaledImage)
val paint = Paint(Paint.FILTER_BITMAP_FLAG)
scaleMatrix.setScale(size.scale, size.scale, 0f, 0f)
paint.isDither = true
paint.isAntiAlias = true
paint.isFilterBitmap = true
canvas.drawBitmap(image, scaleMatrix, paint)
return scaledImage
val size = findActualSize(image, maxWidth, maxHeight)
val scaledImage = Bitmap.createBitmap(size.width, size.height, Bitmap.Config.ARGB_8888)
val scaleMatrix = Matrix()
val canvas = Canvas(scaledImage)
val paint = Paint(Paint.FILTER_BITMAP_FLAG)
scaleMatrix.setScale(size.scale, size.scale, 0f, 0f)
paint.isDither = true
paint.isAntiAlias = true
paint.isFilterBitmap = true
canvas.drawBitmap(image, scaleMatrix, paint)
return scaledImage
}

fun compress(image: Bitmap?, output: ImageCompressorOptions.OutputType, quality: Float): ByteArrayOutputStream {
fun compress(image: Bitmap?, output: ImageCompressorOptions.OutputType, quality: Float,disablePngTransparency:Boolean): ByteArrayOutputStream {
var stream = ByteArrayOutputStream()
if (output === ImageCompressorOptions.OutputType.jpg)
{
image!!.compress(CompressFormat.JPEG, Math.round(100 * quality), stream)
}
else
{
val pngStream = ByteArrayOutputStream()
image!!.compress(CompressFormat.JPEG, Math.round(100 * quality), stream)
val byteArray: ByteArray = stream.toByteArray()
stream=ByteArrayOutputStream()
val bitmap = BitmapFactory.decodeByteArray(byteArray, 0, byteArray.size)
bitmap.compress(CompressFormat.PNG, 100, stream)
var bitmap = image
if(disablePngTransparency)
{
image!!.compress(CompressFormat.JPEG, Math.round(100 * quality), stream)
val byteArray: ByteArray = stream.toByteArray()
stream=ByteArrayOutputStream()
bitmap = BitmapFactory.decodeByteArray(byteArray, 0, byteArray.size)
}
bitmap!!.compress(CompressFormat.PNG, 100, stream)
}
return stream
}

fun manualCompressImage(imagePath: String?, options: ImageCompressorOptions, reactContext: ReactApplicationContext?): String? {
val image = if (options.input === ImageCompressorOptions.InputType.base64) decodeImage(imagePath) else loadImage(imagePath)
val resizedImage = resize(image, options.maxWidth, options.maxHeight)
val imageDataByteArrayOutputStream = compress(resizedImage, options.output, options.quality)
val imageDataByteArrayOutputStream = compress(resizedImage, options.output, options.quality,options.disablePngTransparency)
val isBase64 = options.returnableOutputType === ImageCompressorOptions.ReturnableOutputType.base64
return encodeImage(imageDataByteArrayOutputStream, isBase64, options.output.toString(), reactContext)
}
Expand Down Expand Up @@ -152,7 +155,7 @@ object ImageCompressor {
exception.printStackTrace()
}
try {
scaledBitmap = Bitmap.createBitmap(actualWidth, actualHeight, Bitmap.Config.RGB_565)
scaledBitmap = Bitmap.createBitmap(actualWidth, actualHeight, Bitmap.Config.ARGB_8888)
} catch (exception: OutOfMemoryError) {
exception.printStackTrace()
}
Expand All @@ -169,7 +172,7 @@ object ImageCompressor {
bmp.recycle()
}
scaledBitmap = correctImageOrientation(scaledBitmap, imagePath)
val imageDataByteArrayOutputStream = compress(scaledBitmap, compressorOptions.output, compressorOptions.quality)
val imageDataByteArrayOutputStream = compress(scaledBitmap, compressorOptions.output, compressorOptions.quality,compressorOptions.disablePngTransparency)
return encodeImage(imageDataByteArrayOutputStream, isBase64, compressorOptions.output.toString(), reactContext)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ class ImageCompressorOptions {
var output = OutputType.jpg
var uuid: String? = ""
var returnableOutputType = ReturnableOutputType.uri
var disablePngTransparency:Boolean = false

companion object {
fun fromMap(map: ReadableMap): ImageCompressorOptions {
Expand All @@ -49,6 +50,7 @@ class ImageCompressorOptions {
"output" -> options.output = OutputType.valueOf(map.getString(key)!!)
"returnableOutputType" -> options.returnableOutputType = ReturnableOutputType.valueOf(map.getString(key)!!)
"uuid" -> options.uuid = map.getString(key)
"disablePngTransparency" -> options.disablePngTransparency = map.getBoolean(key)
}
}
return options
Expand Down
4 changes: 2 additions & 2 deletions example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -881,7 +881,7 @@ PODS:
- React-Codegen
- React-RCTFabric
- ReactCommon/turbomodule/core
- react-native-compressor (1.8.8):
- react-native-compressor (1.8.11):
- hermes-engine
- NextLevelSessionExporter
- RCT-Folly (= 2021.07.22.00)
Expand Down Expand Up @@ -1381,7 +1381,7 @@ SPEC CHECKSUMS:
React-jsinspector: aaed4cf551c4a1c98092436518c2d267b13a673f
React-logger: da1ebe05ae06eb6db4b162202faeafac4b435e77
react-native-cameraroll: 5d9523136a929b58f092fd7f0a9a13367a4b46e3
react-native-compressor: 8de39845e794d65b089161278ba0d185d269f1eb
react-native-compressor: f807dbe3d5dac8cc2efab2adf8bed7949f70d5a9
react-native-document-picker: c9ac93d7b511413f4a0ed61c92ff6c7b1bcf4f94
react-native-get-random-values: dee677497c6a740b71e5612e8dbd83e7539ed5bb
react-native-image-picker: 9b4b1d0096500050cbdabf8f4fd00b771065d983
Expand Down
67 changes: 25 additions & 42 deletions ios/Image/ImageCompressor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -100,20 +100,25 @@ class ImageCompressor {
}
return UIImage()
}


static func manualCompress(_ image: UIImage, output: Int, quality: Float, outputExtension: String, isBase64: Bool) -> String {
static func writeImage(_ image: UIImage, output: Int, quality: Float, outputExtension: String, isBase64: Bool,disablePngTransparency:Bool)-> String{
var data: Data
var exception: NSException?

switch OutputType(rawValue: output)! {
case .jpg:
data = image.jpegData(compressionQuality: CGFloat(quality))!
case .png:
data = image.pngData()!
data = image.jpegData(compressionQuality: CGFloat(quality))!
let compressedImage = UIImage(data: data)
data = compressedImage!.pngData()!
if(disablePngTransparency)
{
data = image.jpegData(compressionQuality: CGFloat(quality))!
let compressedImage = UIImage(data: data)
data = compressedImage!.pngData()!
}
else
{
data=image.pngData()!
}

}

if isBase64 {
Expand All @@ -129,11 +134,15 @@ class ImageCompressor {
exception?.raise()
}
}

return ""
}


static func manualCompress(_ image: UIImage, output: Int, quality: Float, outputExtension: String, isBase64: Bool,disablePngTransparency:Bool) -> String {
return writeImage(image, output: output, quality: quality, outputExtension: outputExtension, isBase64: isBase64, disablePngTransparency: disablePngTransparency)
}


static func scaleAndRotateImage(_ image: UIImage) -> UIImage {
if image.imageOrientation == .up {
return image
Expand Down Expand Up @@ -215,7 +224,7 @@ class ImageCompressor {
let outputExtension = ImageCompressorOptions.getOutputInString(options.output)
let resizedImage = ImageCompressor.manualResize(_image, maxWidth: options.maxWidth, maxHeight: options.maxHeight)
let isBase64 = options.returnableOutputType == .rbase64
return ImageCompressor.manualCompress(resizedImage, output: options.output.rawValue, quality: options.quality, outputExtension: outputExtension, isBase64: isBase64)
return ImageCompressor.manualCompress(resizedImage, output: options.output.rawValue, quality: options.quality, outputExtension: outputExtension, isBase64: isBase64,disablePngTransparency: options.disablePngTransparency)
} else {
exception = NSException(name: NSExceptionName(rawValue: "unsupported_value"), reason: "Unsupported value type.", userInfo: nil)
exception?.raise()
Expand All @@ -228,19 +237,19 @@ class ImageCompressor {
static func autoCompressHandler(_ imagePath: String, options: ImageCompressorOptions) -> String {
var exception: NSException?
var image = ImageCompressor.loadImage(imagePath)

if var image = image {
image = ImageCompressor.scaleAndRotateImage(image)
let outputExtension = ImageCompressorOptions.getOutputInString(options.output)

var actualHeight = image.size.height
var actualWidth = image.size.width
let maxHeight: CGFloat = CGFloat(options.maxHeight)
let maxWidth: CGFloat = CGFloat(options.maxWidth)
var imgRatio = actualWidth / actualHeight
let maxRatio = maxWidth / maxHeight
let compressionQuality: CGFloat = CGFloat(options.quality)

if actualHeight > maxHeight || actualWidth > maxWidth {
if imgRatio < maxRatio {
imgRatio = maxHeight / actualHeight
Expand All @@ -255,42 +264,16 @@ class ImageCompressor {
actualWidth = maxWidth
}
}

let rect = CGRect(x: 0.0, y: 0.0, width: actualWidth, height: actualHeight)
UIGraphicsBeginImageContext(rect.size)
image.draw(in: rect)
let isBase64 = options.returnableOutputType == .rbase64

if let img = UIGraphicsGetImageFromCurrentImageContext() {
var imageData: Data
switch OutputType(rawValue: options.output.rawValue)! {
case .jpg:
imageData = img.jpegData(compressionQuality: compressionQuality)!
case .png:
imageData = img.jpegData(compressionQuality: compressionQuality)!
let compressedImage = UIImage(data: imageData)
imageData = compressedImage!.pngData()!
}

UIGraphicsEndImageContext()
let isBase64 = options.returnableOutputType == .rbase64
if isBase64 {
return imageData.base64EncodedString(options: [])
} else {
let filePath = Utils.generateCacheFilePath(outputExtension)
do {
try imageData.write(to: URL(fileURLWithPath: filePath), options: .atomic)
let returnablePath = ImageCompressor.makeValidUri(filePath)
return returnablePath
} catch {
exception = NSException(name: NSExceptionName(rawValue: "file_error"), reason: "Error writing file", userInfo: nil)
exception?.raise()
}
}
return writeImage(img, output: options.output.rawValue, quality: Float(compressionQuality), outputExtension: outputExtension, isBase64: isBase64, disablePngTransparency: options.disablePngTransparency)
}
} else {
exception = NSException(name: NSExceptionName(rawValue: "unsupported_value"), reason: "Unsupported value type.", userInfo: nil)
exception?.raise()
}

return ""
}

Expand Down
3 changes: 3 additions & 0 deletions ios/Image/ImageCompressorOptions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ class ImageCompressorOptions: NSObject {
options.parseReturnableOutput(value as? String)
case "uuid":
options.uuid = value as? String
case "disablePngTransparency":
options.disablePngTransparency = value as? Bool ?? false
default:
break
}
Expand All @@ -65,6 +67,7 @@ class ImageCompressorOptions: NSObject {
var output: OutputType = .jpg
var returnableOutputType: ReturnableOutputType = .ruri
var uuid: String?
var disablePngTransparency: Bool = false

override init() {
super.init()
Expand Down
4 changes: 4 additions & 0 deletions src/Image/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ export type CompressorOptions = {
* The output image type.
*/
output?: OutputType;
/***
* when user add `output:'png'` then by default compressed image will have transparent background, and quality will be ignored, if you wanna apply quality then you have to disablePngTransparency like `disablePngTransparency:true`, it will convert transparent background to white
*/
disablePngTransparency?: boolean;
/***
* The output that will return to user.
*/
Expand Down

0 comments on commit a302273

Please sign in to comment.