Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support multiple image watermarks #113

Merged
merged 6 commits into from
Jul 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion .github/workflows/jekyll-gh-pages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,17 @@ jobs:
- name: Build with Jekyll
uses: actions/jekyll-build-pages@v1
with:
source: ./docs
source: ./docs/latest
destination: ./_site
- name: Upload artifact
uses: actions/upload-pages-artifact@v1
- name: Build with Jekyll v1.0.1
uses: actions/jekyll-build-pages@v1
with:
source: ./docs/v1.0.1
destination: ./_site/v1.0.1
- name: Upload artifact
uses: actions/upload-pages-artifact@v1

# Deployment job
deploy:
Expand Down
42 changes: 32 additions & 10 deletions README.MD
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ Add text or icon watermark to your images

## Features

* Add **multiple text** watermarks to images
* Add **multiple texts** watermarks to images
* add **multiple icons** watermarks to images *(Android >= N, iOS >= iOS 13)*
* Add **icon watermark** to images
* Support **rotating** background images and image watermarks.
* Support setting opacity for background images and image watermarks.
Expand All @@ -22,8 +23,10 @@ Add text or icon watermark to your images
* text align
* Compatible with both Android and iOS

## sample
## Samples

<p>
<img src="https://raw.githubusercontent.com/JimmyDaddy/react-native-image-marker/master/asset/multiple_icon_markers.png" width='200'>
<img src="https://raw.githubusercontent.com/JimmyDaddy/react-native-image-marker/master/asset/alphabgonly.png" width='200'>
<img src="https://raw.githubusercontent.com/JimmyDaddy/react-native-image-marker/master/asset/alphicononly.png" width='200'>
<img src="https://raw.githubusercontent.com/JimmyDaddy/react-native-image-marker/master/asset/imagewatermark.png" width='200'>
Expand All @@ -42,6 +45,16 @@ Add text or icon watermark to your images
<img src="https://raw.githubusercontent.com/JimmyDaddy/react-native-image-marker/master/asset/shadow_bg_sy.png" width='200'>
</p>

## Version Compatibility

| React Native Version | react-native-image-marker Version |
| ---------------------| --------------------------------- |
| < 0.60.0 | v0.5.2 or earlier |
| >= 0.60.0, iOS < 13, Android < N | v1.0.1 |
| >= 0.60.0, other cases | v1.1.0 or later |

> Note: This table is only applicable to major versions of react-native-image-marker. Patch versions should be backwards compatible.

## Installation

* npm install react-native-image-marker --save
Expand Down Expand Up @@ -163,15 +176,24 @@ const options = {
rotate: 20,
alpha: 0.5,
},
watermarkImage: {
watermarkImages: [{
src: require('./images/watermark.png'),
scale: 1,
rotate: 20,
alpha: 0.5,
},
watermarkPositions: {
position: Position.center,
},
position: {
position: Position.center,
}
}, {
src: require('./images/watermark1.png'),
scale: 1,
rotate: 20,
alpha: 0.5,
position: {
X: 20,
Y: 20,
}
}],
quality: 100,
filename: 'test',
saveFormat: ImageFormat.png,
Expand All @@ -189,9 +211,9 @@ await ImageMarker.markImage(options);

## API

[the latest version](https://jimmydaddy.github.io/react-native-image-marker/classes/Marker.html)

If you are using a version lower than 1.0.0, please go to [v0.9.2](https://github.com/JimmyDaddy/react-native-image-marker/wiki/0.9.2)
* [the latest version](https://jimmydaddy.github.io/react-native-image-marker/classes/Marker.html)
* [v1.0.1](https://jimmydaddy.github.io/react-native-image-marker/v1.0.1/classes/Marker.html)
* If you are using a version lower than 1.0.0, please go to [v0.9.2](https://github.com/JimmyDaddy/react-native-image-marker/wiki/0.9.2)

## Extra about Android decoding image

Expand Down
3 changes: 3 additions & 0 deletions android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ buildscript {
}

dependencies {
classpath "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.0"
classpath "com.android.tools.build:gradle:7.2.1"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
Expand Down Expand Up @@ -69,6 +70,7 @@ dependencies {
// For < 0.71, this will be from the local maven repo
// For > 0.71, this will be replaced by `com.facebook.react:react-android:$version` by react gradle plugin
//noinspection GradleDynamicVersion
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.0"
implementation "com.facebook.react:react-native:+"
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
}
Expand All @@ -80,3 +82,4 @@ if (isNewArchitectureEnabled()) {
codegenJavaPackageName = "com.jimmydaddy.imagemarker"
}
}

132 changes: 132 additions & 0 deletions android/src/main/java/com/jimmydaddy/imagemarker/ImageLoader.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
package com.jimmydaddy.imagemarker

import android.annotation.SuppressLint
import android.content.res.Resources
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.os.Build
import android.util.Log
import androidx.annotation.RequiresApi
import com.facebook.common.references.CloseableReference
import com.facebook.datasource.DataSource
import com.facebook.drawee.backends.pipeline.Fresco
import com.facebook.imagepipeline.core.ImagePipelineConfig
import com.facebook.imagepipeline.datasource.BaseBitmapDataSubscriber
import com.facebook.imagepipeline.image.CloseableImage
import com.facebook.imagepipeline.request.ImageRequest
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.modules.systeminfo.ReactNativeVersion
import com.jimmydaddy.imagemarker.base.Constants.IMAGE_MARKER_TAG
import com.jimmydaddy.imagemarker.base.ErrorCode
import com.jimmydaddy.imagemarker.base.ImageOptions
import com.jimmydaddy.imagemarker.base.MarkerError
import com.jimmydaddy.imagemarker.base.Utils
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.withContext
import java.util.concurrent.CompletableFuture
import java.util.concurrent.Executor
import java.util.concurrent.Executors

class ImageLoader(private val context: ReactApplicationContext, private val maxSize: Int) {

private val resources: Resources
get() = context.resources

@RequiresApi(Build.VERSION_CODES.N)
suspend fun loadImages(images: List<ImageOptions>): List<Bitmap?> = withContext(Dispatchers.IO) {
val deferredList = images.map { img ->
async {
try {
if (isFrescoImg(img.uri)) {
val future = CompletableFuture<Bitmap?>()
val imageRequest = ImageRequest.fromUri(img.uri)
if (maxSize > 0) {
setMaxBitmapSize(maxSize)
}
val dataSource = Fresco.getImagePipeline().fetchDecodedImage(imageRequest, null)
val executor: Executor = Executors.newSingleThreadExecutor()
dataSource.subscribe(object : BaseBitmapDataSubscriber() {
public override fun onNewResultImpl(bitmap: Bitmap?) {
if (bitmap != null) {
val bg = Utils.scaleBitmap(bitmap, img.scale)
future.complete(bg)
} else {
future.completeExceptionally(MarkerError(ErrorCode.LOAD_IMAGE_FAILED,
"Can't retrieve the file from the src: " + img.uri))
}
}

override fun onFailureImpl(dataSource: DataSource<CloseableReference<CloseableImage>>) {
future.completeExceptionally(MarkerError(ErrorCode.LOAD_IMAGE_FAILED,
"Can't retrieve the file from the src: " + img.uri))
}
}, executor)
return@async future.get()

} else {
val resId = getDrawableResourceByName(img.uri)
if (resId == 0) {
Log.d(IMAGE_MARKER_TAG, "cannot find res")
throw MarkerError(ErrorCode.GET_RESOURCE_FAILED, "Can't get resource by the path: ${img.uri}")
} else {
Log.d(IMAGE_MARKER_TAG, "res:$resId")
val r = resources
// InputStream is = r.openRawResource(resId);
val bitmap = BitmapFactory.decodeResource(r, resId)
// Bitmap bitmap = BitmapFactory.decodeStream(is);
Log.d(IMAGE_MARKER_TAG, bitmap!!.height.toString() + "")
val bg = Utils.scaleBitmap(
bitmap, img.scale
)
Log.d(IMAGE_MARKER_TAG, bg!!.height.toString() + "")
if (!bitmap.isRecycled && img.scale != 1f) {
bitmap.recycle()
System.gc()
}
return@async bg;
}
}
} catch (e: Exception) {
Log.e("ImageLoader", "Failed to load image: ${img.uri}", e)
null
}
}
}
deferredList.awaitAll()
}

private fun isFrescoImg(uri: String?): Boolean {
// val base64Pattern =
// "^data:(image|img)/(bmp|jpg|png|tif|gif|pcx|tga|exif|fpx|svg|psd|cdr|pcd|dxf|ufo|eps|ai|raw|WMF|webp);base64,(([[A-Za-z0-9+/])*\\s\\S*)*"
return uri!!.startsWith("http://") || uri.startsWith("https://") || uri.startsWith("file://") || uri.startsWith(
"data:"
) && uri.contains("base64") && (uri.contains("img") || uri.contains("image"))
}

@SuppressLint("DiscouragedApi")
private fun getDrawableResourceByName(name: String?): Int {
return resources.getIdentifier(
name,
"drawable",
context.packageName
)
}

private fun setMaxBitmapSize(maxSize: Int) {
val major = Utils.getStringSafe("major", ReactNativeVersion.VERSION)
val minor = Utils.getStringSafe("minor", ReactNativeVersion.VERSION)
val patch = Utils.getStringSafe("patch", ReactNativeVersion.VERSION)
if (Integer.valueOf(major.toString()) >= 0 && Integer.valueOf(minor.toString()) >= 60 && Integer.valueOf(
patch.toString()
) >= 0
) {
val config =
ImagePipelineConfig.newBuilder(context).experiment().setMaxBitmapSize(maxSize)
.build()
Fresco.initialize(context, config)
}
}

}
Loading