mirror of
https://codeberg.org/tom79/Fedilab.git
synced 2024-12-22 16:50:04 +02:00
comment #2 - Fix watermarks
This commit is contained in:
parent
de744fedfa
commit
ed8303c8b0
23 changed files with 2755 additions and 6 deletions
|
@ -119,4 +119,5 @@ dependencies {
|
|||
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
|
||||
// debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.8.1'
|
||||
|
||||
}
|
|
@ -33,7 +33,9 @@ import android.content.res.Resources;
|
|||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.PorterDuffColorFilter;
|
||||
import android.graphics.drawable.Drawable;
|
||||
|
@ -126,6 +128,8 @@ import app.fedilab.android.client.mastodon.entities.Attachment;
|
|||
import app.fedilab.android.exception.DBException;
|
||||
import app.fedilab.android.sqlite.Sqlite;
|
||||
import app.fedilab.android.viewmodel.mastodon.OauthVM;
|
||||
import app.fedilab.android.watermark.androidwm.WatermarkBuilder;
|
||||
import app.fedilab.android.watermark.androidwm.bean.WatermarkText;
|
||||
import app.fedilab.android.webview.CustomWebview;
|
||||
import es.dmoral.toasty.Toasty;
|
||||
import okhttp3.MediaType;
|
||||
|
@ -1242,6 +1246,60 @@ public class Helper {
|
|||
return MultipartBody.Part.createFormData(paramName, attachment.filename, requestFile);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates MultipartBody.Part from Uri
|
||||
*
|
||||
* @return MultipartBody.Part for the given Uri
|
||||
*/
|
||||
public static MultipartBody.Part getMultipartBodyWithWM(Context context, String waterMark, @NonNull String paramName, @NonNull Attachment attachment) {
|
||||
File files = new File(attachment.local_path);
|
||||
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
float scale = sharedpreferences.getFloat(context.getString(R.string.SET_FONT_SCALE), 1.0f);
|
||||
float textSize = 15;
|
||||
Paint mPaint = new Paint();
|
||||
mPaint.setTextSize(textSize);
|
||||
float width = mPaint.measureText(waterMark, 0, waterMark.length()) * scale;
|
||||
try {
|
||||
|
||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
Bitmap backgroundBitmap = BitmapFactory.decodeFile(files.getAbsolutePath(), options);
|
||||
|
||||
int w = options.outWidth;
|
||||
int h = options.outHeight;
|
||||
float valx = (float) 1.0 - ((Helper.convertDpToPixel(width, context) + 90)) / (float) w;
|
||||
if (valx < 0)
|
||||
valx = 0;
|
||||
float valy = (h - Helper.convertDpToPixel(textSize, context) - 30) / (float) h;
|
||||
|
||||
WatermarkText watermarkText = new WatermarkText(waterMark)
|
||||
.setPositionX(valx)
|
||||
.setPositionY(valy)
|
||||
.setTextColor(Color.WHITE)
|
||||
.setTextShadow(0.1f, 1, 1, Color.LTGRAY)
|
||||
.setTextAlpha(200)
|
||||
.setRotation(0)
|
||||
.setTextSize(textSize);
|
||||
|
||||
Bitmap bitmap = WatermarkBuilder
|
||||
.create(context, backgroundBitmap)
|
||||
.loadWatermarkText(watermarkText)
|
||||
.getWatermark()
|
||||
.getOutputImage();
|
||||
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
bitmap.compress(Bitmap.CompressFormat.PNG, 0, bos);
|
||||
byte[] bitmapdata = bos.toByteArray();
|
||||
RequestBody requestFile = RequestBody.create(MediaType.parse(attachment.mimeType), bitmapdata);
|
||||
return MultipartBody.Part.createFormData(paramName, attachment.filename, requestFile);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
RequestBody requestFile = RequestBody.create(MediaType.parse(attachment.mimeType), new File(attachment.local_path));
|
||||
return MultipartBody.Part.createFormData(paramName, attachment.filename, requestFile);
|
||||
}
|
||||
|
||||
|
||||
public static MultipartBody.Part getMultipartBody(Context context, @NonNull String paramName, @NonNull Uri uri) {
|
||||
byte[] imageBytes = uriToByteArray(context, uri);
|
||||
ContentResolver cR = context.getApplicationContext().getContentResolver();
|
||||
|
|
|
@ -23,6 +23,7 @@ import android.app.NotificationChannel;
|
|||
import android.app.NotificationManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
|
@ -31,6 +32,7 @@ import android.text.Html;
|
|||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
import androidx.preference.PreferenceManager;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
|
@ -41,6 +43,7 @@ import java.util.concurrent.TimeUnit;
|
|||
import app.fedilab.android.BaseMainActivity;
|
||||
import app.fedilab.android.R;
|
||||
import app.fedilab.android.activities.ContextActivity;
|
||||
import app.fedilab.android.activities.MainActivity;
|
||||
import app.fedilab.android.client.entities.Account;
|
||||
import app.fedilab.android.client.entities.PostState;
|
||||
import app.fedilab.android.client.entities.StatusDraft;
|
||||
|
@ -173,6 +176,9 @@ public class PostMessageService extends IntentService {
|
|||
}
|
||||
totalMediaSize = 0;
|
||||
totalBitRead = 0;
|
||||
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(PostMessageService.this);
|
||||
boolean watermark = sharedPreferences.getBoolean(getString(R.string.SET_WATERMARK), false);
|
||||
String watermarkText = sharedPreferences.getString(getString(R.string.SET_WATERMARK_TEXT) + MainActivity.currentUserID + MainActivity.currentInstance, null);
|
||||
for (int i = startingPosition; i < statuses.size(); i++) {
|
||||
if (statuses.get(i).media_attachments != null && statuses.get(i).media_attachments.size() > 0) {
|
||||
for (Attachment attachment : statuses.get(i).media_attachments) {
|
||||
|
@ -180,6 +186,15 @@ public class PostMessageService extends IntentService {
|
|||
}
|
||||
}
|
||||
}
|
||||
if (watermarkText == null) {
|
||||
try {
|
||||
Account account = new Account(PostMessageService.this).getAccountByToken(token);
|
||||
watermarkText = account.mastodon_account.username + "@" + account.instance;
|
||||
} catch (DBException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
messageToSend = statuses.size() - startingPosition;
|
||||
messageSent = 0;
|
||||
for (int i = startingPosition; i < statuses.size(); i++) {
|
||||
|
@ -194,7 +209,11 @@ public class PostMessageService extends IntentService {
|
|||
attachmentIds = new ArrayList<>();
|
||||
for (Attachment attachment : statuses.get(i).media_attachments) {
|
||||
MultipartBody.Part fileMultipartBody;
|
||||
if (watermark && attachment.mimeType.contains("image")) {
|
||||
fileMultipartBody = Helper.getMultipartBodyWithWM(PostMessageService.this, watermarkText, "file", attachment);
|
||||
} else {
|
||||
fileMultipartBody = Helper.getMultipartBody("file", attachment);
|
||||
}
|
||||
Call<Attachment> attachmentCall = mastodonStatusesService.postMedia(token, fileMultipartBody, null, attachment.description, null);
|
||||
if (attachmentCall != null) {
|
||||
try {
|
||||
|
|
|
@ -17,10 +17,14 @@ package app.fedilab.android.ui.fragment.settings;
|
|||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.preference.EditTextPreference;
|
||||
import androidx.preference.PreferenceFragmentCompat;
|
||||
import androidx.preference.PreferenceManager;
|
||||
import androidx.preference.SwitchPreferenceCompat;
|
||||
|
||||
import app.fedilab.android.R;
|
||||
import app.fedilab.android.activities.MainActivity;
|
||||
import app.fedilab.android.helper.Helper;
|
||||
|
||||
public class FragmentComposeSettings extends PreferenceFragmentCompat implements SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
|
||||
|
@ -31,15 +35,23 @@ public class FragmentComposeSettings extends PreferenceFragmentCompat implements
|
|||
}
|
||||
|
||||
private void createPref() {
|
||||
|
||||
SwitchPreferenceCompat SET_WATERMARK = findPreference(getString(R.string.SET_WATERMARK));
|
||||
if (SET_WATERMARK != null) {
|
||||
SET_WATERMARK.getContext().setTheme(Helper.dialogStyle());
|
||||
}
|
||||
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireActivity());
|
||||
EditTextPreference SET_WATERMARK_TEXT = findPreference(getString(R.string.SET_WATERMARK_TEXT));
|
||||
if (SET_WATERMARK_TEXT != null) {
|
||||
String val = sharedPreferences.getString(getString(R.string.SET_WATERMARK_TEXT) + MainActivity.currentUserID + MainActivity.currentInstance, sharedPreferences.getString(getString(R.string.SET_WATERMARK_TEXT), null));
|
||||
SET_WATERMARK_TEXT.setText(val);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
|
||||
if (getActivity() != null) {
|
||||
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(requireActivity());
|
||||
SharedPreferences.Editor editor = sharedpreferences.edit();
|
||||
|
||||
if (key.equalsIgnoreCase(getString(R.string.SET_WATERMARK_TEXT))) {
|
||||
SharedPreferences.Editor editor = sharedPreferences.edit();
|
||||
editor.putString(getString(R.string.SET_WATERMARK_TEXT) + MainActivity.currentUserID + MainActivity.currentInstance, sharedPreferences.getString(getString(R.string.SET_WATERMARK_TEXT), null));
|
||||
editor.apply();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,278 @@
|
|||
/*
|
||||
* Copyright 2018 Yizheng Huang
|
||||
*
|
||||
* 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 app.fedilab.android.watermark.androidwm;
|
||||
|
||||
import static app.fedilab.android.watermark.androidwm.utils.BitmapUtils.resizeBitmap;
|
||||
import static app.fedilab.android.watermark.androidwm.utils.BitmapUtils.textAsBitmap;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapShader;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.Shader;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import app.fedilab.android.watermark.androidwm.bean.AsyncTaskParams;
|
||||
import app.fedilab.android.watermark.androidwm.bean.WatermarkImage;
|
||||
import app.fedilab.android.watermark.androidwm.bean.WatermarkText;
|
||||
import app.fedilab.android.watermark.androidwm.listener.BuildFinishListener;
|
||||
import app.fedilab.android.watermark.androidwm.task.FDWatermarkTask;
|
||||
import app.fedilab.android.watermark.androidwm.task.LSBWatermarkTask;
|
||||
import app.fedilab.android.watermark.androidwm.utils.BitmapUtils;
|
||||
|
||||
/**
|
||||
* The main class for watermark processing library.
|
||||
*
|
||||
* @author huangyz0918 (huangyz0918@gmail.com)
|
||||
*/
|
||||
public class Watermark {
|
||||
private final WatermarkText watermarkText;
|
||||
private final WatermarkImage watermarkImg;
|
||||
private final Bitmap backgroundImg;
|
||||
private final Context context;
|
||||
private final boolean isTileMode;
|
||||
private final boolean isInvisible;
|
||||
private final boolean isLSB;
|
||||
private final BuildFinishListener<Bitmap> buildFinishListener;
|
||||
private Bitmap outputImage;
|
||||
private Bitmap canvasBitmap;
|
||||
|
||||
/**
|
||||
* Constructors for WatermarkImage
|
||||
*/
|
||||
@SuppressWarnings("PMD")
|
||||
Watermark(@NonNull Context context,
|
||||
@NonNull Bitmap backgroundImg,
|
||||
@Nullable WatermarkImage watermarkImg,
|
||||
@Nullable List<WatermarkImage> wmBitmapList,
|
||||
@Nullable WatermarkText inputText,
|
||||
@Nullable List<WatermarkText> wmTextList,
|
||||
boolean isTileMode,
|
||||
boolean isInvisible,
|
||||
boolean isLSB,
|
||||
@Nullable BuildFinishListener<Bitmap> buildFinishListener) {
|
||||
|
||||
this.context = context;
|
||||
this.isTileMode = isTileMode;
|
||||
this.watermarkImg = watermarkImg;
|
||||
this.backgroundImg = backgroundImg;
|
||||
this.watermarkText = inputText;
|
||||
this.isInvisible = isInvisible;
|
||||
this.buildFinishListener = buildFinishListener;
|
||||
this.isLSB = isLSB;
|
||||
|
||||
canvasBitmap = backgroundImg;
|
||||
outputImage = backgroundImg;
|
||||
|
||||
createWatermarkImage(watermarkImg);
|
||||
createWatermarkImages(wmBitmapList);
|
||||
createWatermarkText(watermarkText);
|
||||
createWatermarkTexts(wmTextList);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* interface for getting the watermark bitmap.
|
||||
*
|
||||
* @return {@link Bitmap} in watermark.
|
||||
*/
|
||||
public Bitmap getWatermarkBitmap() {
|
||||
return watermarkImg.getImage();
|
||||
}
|
||||
|
||||
/**
|
||||
* interface for getting the watermark text.
|
||||
*
|
||||
* @return {@link Bitmap} in watermark.
|
||||
*/
|
||||
public String getWatermarkText() {
|
||||
return watermarkText.getText();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creating the composite image with {@link WatermarkImage}.
|
||||
* This method cannot be called outside.
|
||||
*/
|
||||
private void createWatermarkImage(WatermarkImage watermarkImg) {
|
||||
if (watermarkImg != null && backgroundImg != null) {
|
||||
if (isInvisible) {
|
||||
Bitmap scaledWMBitmap = resizeBitmap(watermarkImg.getImage(), (float) watermarkImg.getSize(), backgroundImg);
|
||||
if (isLSB) {
|
||||
new LSBWatermarkTask(buildFinishListener).execute(
|
||||
new AsyncTaskParams(context, backgroundImg, scaledWMBitmap)
|
||||
);
|
||||
} else {
|
||||
new FDWatermarkTask(buildFinishListener).execute(
|
||||
new AsyncTaskParams(context, backgroundImg, scaledWMBitmap)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
Paint watermarkPaint = new Paint();
|
||||
watermarkPaint.setAlpha(watermarkImg.getAlpha());
|
||||
Bitmap newBitmap = Bitmap.createBitmap(backgroundImg.getWidth(),
|
||||
backgroundImg.getHeight(), backgroundImg.getConfig());
|
||||
Canvas watermarkCanvas = new Canvas(newBitmap);
|
||||
watermarkCanvas.drawBitmap(canvasBitmap, 0, 0, null);
|
||||
Bitmap scaledWMBitmap = resizeBitmap(watermarkImg.getImage(), (float) watermarkImg.getSize(), backgroundImg);
|
||||
scaledWMBitmap = adjustPhotoRotation(scaledWMBitmap,
|
||||
(int) watermarkImg.getPosition().getRotation());
|
||||
|
||||
if (isTileMode) {
|
||||
watermarkPaint.setShader(new BitmapShader(scaledWMBitmap,
|
||||
Shader.TileMode.REPEAT,
|
||||
Shader.TileMode.REPEAT));
|
||||
Rect bitmapShaderRect = watermarkCanvas.getClipBounds();
|
||||
watermarkCanvas.drawRect(bitmapShaderRect, watermarkPaint);
|
||||
} else {
|
||||
watermarkCanvas.drawBitmap(scaledWMBitmap,
|
||||
(float) watermarkImg.getPosition().getPositionX() * backgroundImg.getWidth(),
|
||||
(float) watermarkImg.getPosition().getPositionY() * backgroundImg.getHeight(),
|
||||
watermarkPaint);
|
||||
}
|
||||
|
||||
canvasBitmap = newBitmap;
|
||||
outputImage = newBitmap;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creating the composite image with {@link WatermarkImage}.
|
||||
* The input of the method is a set of {@link WatermarkImage}s.
|
||||
*/
|
||||
private void createWatermarkImages(List<WatermarkImage> watermarkImages) {
|
||||
if (watermarkImages != null) {
|
||||
for (int i = 0; i < watermarkImages.size(); i++) {
|
||||
createWatermarkImage(watermarkImages.get(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creating the composite image with {@link WatermarkText}.
|
||||
* This method cannot be called outside.
|
||||
*/
|
||||
private void createWatermarkText(WatermarkText watermarkText) {
|
||||
|
||||
if (watermarkText != null && backgroundImg != null) {
|
||||
if (isInvisible) {
|
||||
if (isLSB) {
|
||||
new LSBWatermarkTask(buildFinishListener).execute(
|
||||
new AsyncTaskParams(context, backgroundImg, watermarkText)
|
||||
);
|
||||
} else {
|
||||
new FDWatermarkTask(buildFinishListener).execute(
|
||||
new AsyncTaskParams(context, backgroundImg, watermarkText)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
Paint watermarkPaint = new Paint();
|
||||
watermarkPaint.setAlpha(watermarkText.getTextAlpha());
|
||||
Bitmap newBitmap = Bitmap.createBitmap(backgroundImg.getWidth(),
|
||||
backgroundImg.getHeight(), backgroundImg.getConfig());
|
||||
Canvas watermarkCanvas = new Canvas(newBitmap);
|
||||
watermarkCanvas.drawBitmap(canvasBitmap, 0, 0, null);
|
||||
Bitmap scaledWMBitmap = textAsBitmap(context, watermarkText);
|
||||
scaledWMBitmap = adjustPhotoRotation(scaledWMBitmap,
|
||||
(int) watermarkText.getPosition().getRotation());
|
||||
|
||||
if (isTileMode) {
|
||||
watermarkPaint.setShader(new BitmapShader(scaledWMBitmap,
|
||||
Shader.TileMode.REPEAT,
|
||||
Shader.TileMode.REPEAT));
|
||||
Rect bitmapShaderRect = watermarkCanvas.getClipBounds();
|
||||
watermarkCanvas.drawRect(bitmapShaderRect, watermarkPaint);
|
||||
} else {
|
||||
watermarkCanvas.drawBitmap(scaledWMBitmap,
|
||||
(float) watermarkText.getPosition().getPositionX() * backgroundImg.getWidth(),
|
||||
(float) watermarkText.getPosition().getPositionY() * backgroundImg.getHeight(),
|
||||
watermarkPaint);
|
||||
}
|
||||
|
||||
canvasBitmap = newBitmap;
|
||||
outputImage = newBitmap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creating the composite image with {@link WatermarkText}.
|
||||
* The input of the method is a set of {@link WatermarkText}s.
|
||||
*/
|
||||
|
||||
private void createWatermarkTexts(List<WatermarkText> watermarkTexts) {
|
||||
if (watermarkTexts != null) {
|
||||
for (int i = 0; i < watermarkTexts.size(); i++) {
|
||||
createWatermarkText(watermarkTexts.get(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The interface for getting the output image.
|
||||
*
|
||||
* @return {@link Bitmap} out bitmap.
|
||||
*/
|
||||
public Bitmap getOutputImage() {
|
||||
return outputImage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save output png image to local.
|
||||
*
|
||||
* @param path the output path of image.
|
||||
*/
|
||||
public void saveToLocalPng(String path) {
|
||||
BitmapUtils.saveAsPNG(outputImage, path, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* You can use this function to set the composite
|
||||
* image into an ImageView.
|
||||
*
|
||||
* @param target the target {@link ImageView}.
|
||||
*/
|
||||
public void setToImageView(ImageView target) {
|
||||
target.setImageBitmap(outputImage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjust the rotation of a bitmap.
|
||||
*
|
||||
* @param bitmap input bitmap.
|
||||
* @param orientationAngle the orientation angle.
|
||||
* @return {@link Bitmap} the new bitmap.
|
||||
*/
|
||||
private Bitmap adjustPhotoRotation(Bitmap bitmap, final int orientationAngle) {
|
||||
Matrix matrix = new Matrix();
|
||||
matrix.setRotate(orientationAngle,
|
||||
(float) bitmap.getWidth() / 2, (float) bitmap.getHeight() / 2);
|
||||
return Bitmap.createBitmap(bitmap,
|
||||
0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,344 @@
|
|||
/*
|
||||
* Copyright 2018 Yizheng Huang
|
||||
*
|
||||
* 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 app.fedilab.android.watermark.androidwm;
|
||||
|
||||
import static app.fedilab.android.watermark.androidwm.utils.BitmapUtils.resizeBitmap;
|
||||
import static app.fedilab.android.watermark.androidwm.utils.Constant.MAX_IMAGE_SIZE;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import androidx.annotation.DrawableRes;
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import app.fedilab.android.watermark.androidwm.bean.WatermarkImage;
|
||||
import app.fedilab.android.watermark.androidwm.bean.WatermarkPosition;
|
||||
import app.fedilab.android.watermark.androidwm.bean.WatermarkText;
|
||||
import app.fedilab.android.watermark.androidwm.listener.BuildFinishListener;
|
||||
|
||||
/**
|
||||
* A builder class for setting default structural classes for watermark to use.
|
||||
*
|
||||
* @author huangyz0918 (huangyz0918@gmail.com)
|
||||
*/
|
||||
public final class WatermarkBuilder {
|
||||
private final Context context;
|
||||
private final boolean resizeBackgroundImg;
|
||||
private Bitmap backgroundImg;
|
||||
private boolean isTileMode = false;
|
||||
private boolean isLSB = false;
|
||||
private BuildFinishListener<Bitmap> buildFinishListener = null;
|
||||
|
||||
private WatermarkImage watermarkImage;
|
||||
private WatermarkText watermarkText;
|
||||
private List<WatermarkText> watermarkTexts = new ArrayList<>();
|
||||
private List<WatermarkImage> watermarkBitmaps = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Constructors for WatermarkBuilder
|
||||
*/
|
||||
private WatermarkBuilder(@NonNull Context context, @NonNull Bitmap backgroundImg, boolean resizeBackgroundImg) {
|
||||
this.context = context;
|
||||
this.resizeBackgroundImg = resizeBackgroundImg;
|
||||
if (resizeBackgroundImg) {
|
||||
this.backgroundImg = resizeBitmap(backgroundImg, MAX_IMAGE_SIZE);
|
||||
} else {
|
||||
this.backgroundImg = backgroundImg;
|
||||
}
|
||||
}
|
||||
|
||||
private WatermarkBuilder(@NonNull Context context, @NonNull ImageView backgroundImageView, boolean resizeBackgroundImg) {
|
||||
this.context = context;
|
||||
this.resizeBackgroundImg = resizeBackgroundImg;
|
||||
backgroundFromImageView(backgroundImageView);
|
||||
}
|
||||
|
||||
private WatermarkBuilder(@NonNull Context context, @DrawableRes int backgroundDrawable, boolean resizeBackgroundImg) {
|
||||
this.context = context;
|
||||
this.resizeBackgroundImg = resizeBackgroundImg;
|
||||
if (resizeBackgroundImg) {
|
||||
this.backgroundImg = resizeBitmap(BitmapFactory.decodeResource(context.getResources(),
|
||||
backgroundDrawable), MAX_IMAGE_SIZE);
|
||||
} else {
|
||||
this.backgroundImg = BitmapFactory.decodeResource(context.getResources(),
|
||||
backgroundDrawable);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private WatermarkBuilder(@NonNull Context context, @NonNull Bitmap backgroundImg) {
|
||||
this(context, backgroundImg, true);
|
||||
}
|
||||
|
||||
private WatermarkBuilder(@NonNull Context context, @NonNull ImageView backgroundImageView) {
|
||||
this(context, backgroundImageView, true);
|
||||
}
|
||||
|
||||
private WatermarkBuilder(@NonNull Context context, @DrawableRes int backgroundDrawable) {
|
||||
this(context, backgroundDrawable, true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* to get an instance form class.
|
||||
*
|
||||
* @return instance of {@link WatermarkBuilder}
|
||||
*/
|
||||
@SuppressWarnings("PMD")
|
||||
public static WatermarkBuilder create(Context context, Bitmap backgroundImg) {
|
||||
return new WatermarkBuilder(context, backgroundImg);
|
||||
}
|
||||
|
||||
/**
|
||||
* to get an instance form class.
|
||||
* Load the background image from a {@link ImageView}。
|
||||
*
|
||||
* @return instance of {@link WatermarkBuilder}
|
||||
*/
|
||||
@SuppressWarnings("PMD")
|
||||
public static WatermarkBuilder create(Context context, ImageView imageView) {
|
||||
return new WatermarkBuilder(context, imageView);
|
||||
}
|
||||
|
||||
/**
|
||||
* to get an instance form class.
|
||||
* Load the background image from a DrawableRes。
|
||||
*
|
||||
* @return instance of {@link WatermarkBuilder}
|
||||
*/
|
||||
@SuppressWarnings("PMD")
|
||||
public static WatermarkBuilder create(Context context, @DrawableRes int backgroundDrawable) {
|
||||
return new WatermarkBuilder(context, backgroundDrawable);
|
||||
}
|
||||
|
||||
/**
|
||||
* to get an instance form class.
|
||||
* with background image resize option
|
||||
*
|
||||
* @return instance of {@link WatermarkBuilder}
|
||||
*/
|
||||
@SuppressWarnings("PMD")
|
||||
public static WatermarkBuilder create(Context context, Bitmap backgroundImg, boolean resizeBackgroundImg) {
|
||||
return new WatermarkBuilder(context, backgroundImg, resizeBackgroundImg);
|
||||
}
|
||||
|
||||
/**
|
||||
* to get an instance form class.
|
||||
* Load the background image from a {@link ImageView}。
|
||||
* with background image resize option
|
||||
*
|
||||
* @return instance of {@link WatermarkBuilder}
|
||||
*/
|
||||
@SuppressWarnings("PMD")
|
||||
public static WatermarkBuilder create(Context context, ImageView imageView, boolean resizeBackgroundImg) {
|
||||
return new WatermarkBuilder(context, imageView, resizeBackgroundImg);
|
||||
}
|
||||
|
||||
/**
|
||||
* to get an instance form class.
|
||||
* Load the background image from a DrawableRes。
|
||||
* with background image resize option
|
||||
*
|
||||
* @return instance of {@link WatermarkBuilder}
|
||||
*/
|
||||
@SuppressWarnings("PMD")
|
||||
public static WatermarkBuilder create(Context context, @DrawableRes int backgroundDrawable, boolean resizeBackgroundImg) {
|
||||
return new WatermarkBuilder(context, backgroundDrawable, resizeBackgroundImg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link String} as the args
|
||||
* which ready for adding to a watermark.
|
||||
* Using the default position.
|
||||
*
|
||||
* @param inputText The text to add.
|
||||
* @return This {@link WatermarkBuilder}.
|
||||
*/
|
||||
public WatermarkBuilder loadWatermarkText(@NonNull String inputText) {
|
||||
watermarkText = new WatermarkText(inputText);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link String} as the args
|
||||
* which ready for adding to a watermark.
|
||||
* Using the new position.
|
||||
*
|
||||
* @param inputText The text to add.
|
||||
* @param position The position in the background image.
|
||||
* @return This {@link WatermarkBuilder}.
|
||||
*/
|
||||
public WatermarkBuilder loadWatermarkText(@NonNull String inputText,
|
||||
@NonNull WatermarkPosition position) {
|
||||
watermarkText = new WatermarkText(inputText, position);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link String} as the args
|
||||
* which ready for adding to a watermark.
|
||||
*
|
||||
* @param watermarkString The {@link WatermarkText} object.
|
||||
* @return This {@link WatermarkBuilder}.
|
||||
*/
|
||||
public WatermarkBuilder loadWatermarkText(@NonNull WatermarkText watermarkString) {
|
||||
watermarkText = watermarkString;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link String} as the args
|
||||
* which ready for adding to a watermark.
|
||||
* And, this is a set of Strings.
|
||||
*
|
||||
* @param watermarkTexts The texts to add.
|
||||
* @return This {@link WatermarkBuilder}.
|
||||
*/
|
||||
public WatermarkBuilder loadWatermarkTexts(@NonNull List<WatermarkText> watermarkTexts) {
|
||||
this.watermarkTexts = watermarkTexts;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link Bitmap} as the args
|
||||
* which ready for adding to a background.
|
||||
* Using the default position.
|
||||
*
|
||||
* @param wmImg The image to add.
|
||||
* @return This {@link WatermarkBuilder}.
|
||||
*/
|
||||
public WatermarkBuilder loadWatermarkImage(@NonNull Bitmap wmImg) {
|
||||
watermarkImage = new WatermarkImage(wmImg);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link Bitmap} as the args
|
||||
* which ready for adding to a background.
|
||||
* Using the new position.
|
||||
*
|
||||
* @param position The position in the background image.
|
||||
* @param wmImg The bitmap to add into.
|
||||
* @return This {@link WatermarkBuilder}.
|
||||
*/
|
||||
public WatermarkBuilder loadWatermarkImage(@NonNull Bitmap wmImg,
|
||||
@NonNull WatermarkPosition position) {
|
||||
watermarkImage = new WatermarkImage(wmImg, position);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link Bitmap} as the args
|
||||
* which ready for adding to a background.
|
||||
*
|
||||
* @param watermarkImg The {@link WatermarkImage} object.
|
||||
* @return This {@link WatermarkBuilder}.
|
||||
*/
|
||||
public WatermarkBuilder loadWatermarkImage(@NonNull WatermarkImage watermarkImg) {
|
||||
watermarkImage = watermarkImg;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link Bitmap} as the args
|
||||
* which ready for adding into the background.
|
||||
* And, this is a set of bitmaps.
|
||||
*
|
||||
* @param bitmapList The bitmaps to add.
|
||||
* @return This {@link WatermarkBuilder}.
|
||||
*/
|
||||
public WatermarkBuilder loadWatermarkImages(@NonNull List<WatermarkImage> bitmapList) {
|
||||
this.watermarkBitmaps = bitmapList;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set mode to tile. We need to draw watermark over the
|
||||
* whole background.
|
||||
*/
|
||||
public WatermarkBuilder setTileMode(boolean tileMode) {
|
||||
this.isTileMode = tileMode;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* set a listener for building progress.
|
||||
*/
|
||||
public void setInvisibleWMListener(
|
||||
boolean isLSB,
|
||||
BuildFinishListener<Bitmap> listener
|
||||
) {
|
||||
this.buildFinishListener = listener;
|
||||
this.isLSB = isLSB;
|
||||
new Watermark(
|
||||
context,
|
||||
backgroundImg,
|
||||
watermarkImage,
|
||||
watermarkBitmaps,
|
||||
watermarkText,
|
||||
watermarkTexts,
|
||||
isTileMode,
|
||||
true,
|
||||
isLSB,
|
||||
buildFinishListener
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* load a bitmap as background image from a ImageView.
|
||||
*
|
||||
* @param imageView the {@link ImageView} we need to use.
|
||||
*/
|
||||
private void backgroundFromImageView(ImageView imageView) {
|
||||
imageView.invalidate();
|
||||
if (imageView.getDrawable() != null) {
|
||||
BitmapDrawable drawable = (BitmapDrawable) imageView.getDrawable();
|
||||
if (resizeBackgroundImg) {
|
||||
backgroundImg = resizeBitmap(drawable.getBitmap(), MAX_IMAGE_SIZE);
|
||||
} else {
|
||||
backgroundImg = drawable.getBitmap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* let the watermark builder to build a new watermark object
|
||||
*
|
||||
* @return a new {@link Watermark} object
|
||||
*/
|
||||
public Watermark getWatermark() {
|
||||
return new Watermark(
|
||||
context,
|
||||
backgroundImg,
|
||||
watermarkImage,
|
||||
watermarkBitmaps,
|
||||
watermarkText,
|
||||
watermarkTexts,
|
||||
isTileMode,
|
||||
false,
|
||||
isLSB,
|
||||
buildFinishListener
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* Copyright 2018 Yizheng Huang
|
||||
*
|
||||
* 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 app.fedilab.android.watermark.androidwm;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import app.fedilab.android.watermark.androidwm.listener.DetectFinishListener;
|
||||
import app.fedilab.android.watermark.androidwm.task.FDDetectionTask;
|
||||
import app.fedilab.android.watermark.androidwm.task.LSBDetectionTask;
|
||||
import io.reactivex.annotations.NonNull;
|
||||
|
||||
/**
|
||||
* This is for detecting the invisible watermark in one picture.
|
||||
*
|
||||
* @author huangyz0918 (huangyz0918@gmail.com)
|
||||
*/
|
||||
public final class WatermarkDetector {
|
||||
private final Bitmap imageWithWatermark;
|
||||
private final boolean isLSB;
|
||||
|
||||
private WatermarkDetector(
|
||||
@NonNull Bitmap imageWithWatermark,
|
||||
boolean isLSB) {
|
||||
this.imageWithWatermark = imageWithWatermark;
|
||||
this.isLSB = isLSB;
|
||||
}
|
||||
|
||||
/**
|
||||
* to get an instance form class.
|
||||
*
|
||||
* @return instance of {@link WatermarkDetector}
|
||||
*/
|
||||
public static WatermarkDetector create(@NonNull Bitmap imageWithWatermark, boolean isLSB) {
|
||||
return new WatermarkDetector(imageWithWatermark, isLSB);
|
||||
}
|
||||
|
||||
/**
|
||||
* to get an instance form class.
|
||||
* If the imageView has no src or bitmap image, it will throws a {@link NullPointerException}.
|
||||
*
|
||||
* @return instance of {@link WatermarkDetector}
|
||||
*/
|
||||
public static WatermarkDetector create(ImageView imageView, boolean isLSB) {
|
||||
BitmapDrawable drawable = (BitmapDrawable) imageView.getDrawable();
|
||||
return new WatermarkDetector(drawable.getBitmap(), isLSB);
|
||||
}
|
||||
|
||||
/**
|
||||
* The method for watermark detecting.
|
||||
*/
|
||||
public void detect(DetectFinishListener listener) {
|
||||
if (isLSB) {
|
||||
new LSBDetectionTask(listener).execute(imageWithWatermark);
|
||||
} else {
|
||||
new FDDetectionTask(listener).execute(imageWithWatermark);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* Copyright 2018 Yizheng Huang
|
||||
*
|
||||
* 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 app.fedilab.android.watermark.androidwm.bean;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
|
||||
/**
|
||||
* This is a simple class that can help we put multiple primitive
|
||||
* parameters into the task.
|
||||
*
|
||||
* @author huangyz0918 (huangyz0918@gmail.com)
|
||||
*/
|
||||
public class AsyncTaskParams {
|
||||
private Bitmap backgroundImg;
|
||||
private WatermarkText watermarkText;
|
||||
private Bitmap watermarkImg;
|
||||
private Context context;
|
||||
|
||||
public AsyncTaskParams(Context context, Bitmap backgroundImg, WatermarkText watermarkText, Bitmap watermarkImg) {
|
||||
this.backgroundImg = backgroundImg;
|
||||
this.watermarkText = watermarkText;
|
||||
this.watermarkImg = watermarkImg;
|
||||
}
|
||||
|
||||
public AsyncTaskParams(Context context, Bitmap backgroundImg, Bitmap watermarkImg) {
|
||||
this.backgroundImg = backgroundImg;
|
||||
this.watermarkImg = watermarkImg;
|
||||
}
|
||||
|
||||
public AsyncTaskParams(Context context, Bitmap backgroundImg, WatermarkText watermarkText) {
|
||||
this.backgroundImg = backgroundImg;
|
||||
this.watermarkText = watermarkText;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getters and Setters for {@link AsyncTaskParams}.
|
||||
*/
|
||||
public Bitmap getBackgroundImg() {
|
||||
return backgroundImg;
|
||||
}
|
||||
|
||||
public void setBackgroundImg(Bitmap backgroundImg) {
|
||||
this.backgroundImg = backgroundImg;
|
||||
}
|
||||
|
||||
public WatermarkText getWatermarkText() {
|
||||
return watermarkText;
|
||||
}
|
||||
|
||||
public void setWatermarkText(WatermarkText watermarkText) {
|
||||
this.watermarkText = watermarkText;
|
||||
}
|
||||
|
||||
public Bitmap getWatermarkImg() {
|
||||
return watermarkImg;
|
||||
}
|
||||
|
||||
public void setWatermarkImg(Bitmap watermarkImg) {
|
||||
this.watermarkImg = watermarkImg;
|
||||
}
|
||||
|
||||
public Context getContext() {
|
||||
return context;
|
||||
}
|
||||
|
||||
public void setContext(Context context) {
|
||||
this.context = context;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,159 @@
|
|||
/*
|
||||
* Copyright 2018 Yizheng Huang
|
||||
*
|
||||
* 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 app.fedilab.android.watermark.androidwm.bean;
|
||||
|
||||
import static app.fedilab.android.watermark.androidwm.utils.BitmapUtils.resizeBitmap;
|
||||
import static app.fedilab.android.watermark.androidwm.utils.Constant.MAX_IMAGE_SIZE;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import androidx.annotation.DrawableRes;
|
||||
import androidx.annotation.FloatRange;
|
||||
|
||||
/**
|
||||
* It's a wrapper of the watermark image.
|
||||
*
|
||||
* @author huangyz0918 (huangyz0918@gmail.com)
|
||||
* @since 29/08/2018
|
||||
*/
|
||||
public class WatermarkImage {
|
||||
private Bitmap image;
|
||||
@DrawableRes
|
||||
private int imageDrawable;
|
||||
private int alpha = 50;
|
||||
private Context context;
|
||||
private double size = 0.2;
|
||||
// set the default values for the position.
|
||||
private WatermarkPosition position = new WatermarkPosition(0, 0, 0);
|
||||
|
||||
/**
|
||||
* Constructors for WatermarkImage.
|
||||
* since we use the mobile to calculate the image, the image cannot be to large,
|
||||
* we set the maxsize of an image to 1024x1024.
|
||||
*/
|
||||
public WatermarkImage(Bitmap image) {
|
||||
this.image = resizeBitmap(image, MAX_IMAGE_SIZE);
|
||||
}
|
||||
|
||||
public WatermarkImage(Context context, @DrawableRes int imageDrawable, WatermarkPosition position) {
|
||||
this.imageDrawable = imageDrawable;
|
||||
this.position = position;
|
||||
this.context = context;
|
||||
this.image = getBitmapFromDrawable(imageDrawable);
|
||||
}
|
||||
|
||||
public WatermarkImage(Context context, @DrawableRes int imageDrawable) {
|
||||
this.imageDrawable = imageDrawable;
|
||||
this.context = context;
|
||||
this.image = getBitmapFromDrawable(imageDrawable);
|
||||
}
|
||||
|
||||
public WatermarkImage(Bitmap image, WatermarkPosition position) {
|
||||
this.image = resizeBitmap(image, MAX_IMAGE_SIZE);
|
||||
this.position = position;
|
||||
}
|
||||
|
||||
public WatermarkImage(ImageView imageView) {
|
||||
watermarkFromImageView(imageView);
|
||||
}
|
||||
|
||||
/**
|
||||
* Getters and Setters for those attrs.
|
||||
*/
|
||||
public Bitmap getImage() {
|
||||
return image;
|
||||
}
|
||||
|
||||
public int getAlpha() {
|
||||
return alpha;
|
||||
}
|
||||
|
||||
public WatermarkPosition getPosition() {
|
||||
return position;
|
||||
}
|
||||
|
||||
public WatermarkImage setPosition(WatermarkPosition position) {
|
||||
this.position = position;
|
||||
return this;
|
||||
}
|
||||
|
||||
public double getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param size can be set to 0-1 as the proportion of
|
||||
* background image.
|
||||
*/
|
||||
public WatermarkImage setSize(@FloatRange(from = 0, to = 1) double size) {
|
||||
this.size = size;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getImageDrawable() {
|
||||
return imageDrawable;
|
||||
}
|
||||
|
||||
public WatermarkImage setImageDrawable(@DrawableRes int imageDrawable) {
|
||||
this.imageDrawable = imageDrawable;
|
||||
return this;
|
||||
}
|
||||
|
||||
public WatermarkImage setPositionX(@FloatRange(from = 0, to = 1) double x) {
|
||||
this.position.setPositionX(x);
|
||||
return this;
|
||||
}
|
||||
|
||||
public WatermarkImage setPositionY(@FloatRange(from = 0, to = 1) double y) {
|
||||
this.position.setPositionY(y);
|
||||
return this;
|
||||
}
|
||||
|
||||
public WatermarkImage setRotation(double rotation) {
|
||||
this.position.setRotation(rotation);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param imageAlpha can be set to 0-255.
|
||||
*/
|
||||
public WatermarkImage setImageAlpha(int imageAlpha) {
|
||||
this.alpha = imageAlpha;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* load a bitmap as watermark image from a ImageView.
|
||||
*
|
||||
* @param imageView the ImageView we need to use.
|
||||
*/
|
||||
private void watermarkFromImageView(ImageView imageView) {
|
||||
imageView.invalidate();
|
||||
BitmapDrawable drawable = (BitmapDrawable) imageView.getDrawable();
|
||||
// set the limitation of input bitmap.
|
||||
this.image = resizeBitmap(drawable.getBitmap(), MAX_IMAGE_SIZE);
|
||||
}
|
||||
|
||||
private Bitmap getBitmapFromDrawable(@DrawableRes int imageDrawable) {
|
||||
return resizeBitmap(BitmapFactory.decodeResource(context.getResources(),
|
||||
imageDrawable), MAX_IMAGE_SIZE);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* Copyright 2018 Yizheng Huang
|
||||
*
|
||||
* 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 app.fedilab.android.watermark.androidwm.bean;
|
||||
|
||||
|
||||
import androidx.annotation.FloatRange;
|
||||
|
||||
/**
|
||||
* It's a class for saving the position of watermark.
|
||||
* Can be used for a single image/text or a set of
|
||||
* images/texts.
|
||||
*
|
||||
* @author huangyz0918 (huangyz0918@gmail.com)
|
||||
* @since 29/08/2018
|
||||
*/
|
||||
public class WatermarkPosition {
|
||||
|
||||
private double positionX;
|
||||
private double positionY;
|
||||
private double rotation;
|
||||
|
||||
/**
|
||||
* Constructors for WatermarkImage
|
||||
*/
|
||||
public WatermarkPosition(@FloatRange(from = 0, to = 1) double positionX,
|
||||
@FloatRange(from = 0, to = 1) double positionY) {
|
||||
this.positionX = positionX;
|
||||
this.positionY = positionY;
|
||||
}
|
||||
|
||||
public WatermarkPosition(@FloatRange(from = 0, to = 1) double positionX,
|
||||
@FloatRange(from = 0, to = 1) double positionY,
|
||||
double rotation) {
|
||||
this.positionX = positionX;
|
||||
this.positionY = positionY;
|
||||
this.rotation = rotation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getters and Setters for those attrs.
|
||||
*/
|
||||
public double getPositionX() {
|
||||
return positionX;
|
||||
}
|
||||
|
||||
public WatermarkPosition setPositionX(double positionX) {
|
||||
this.positionX = positionX;
|
||||
return this;
|
||||
}
|
||||
|
||||
public double getPositionY() {
|
||||
return positionY;
|
||||
}
|
||||
|
||||
public WatermarkPosition setPositionY(double positionY) {
|
||||
this.positionY = positionY;
|
||||
return this;
|
||||
}
|
||||
|
||||
public double getRotation() {
|
||||
return rotation;
|
||||
}
|
||||
|
||||
public WatermarkPosition setRotation(double rotation) {
|
||||
this.rotation = rotation;
|
||||
return this;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,214 @@
|
|||
/*
|
||||
* Copyright 2018 Yizheng Huang
|
||||
*
|
||||
* 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 app.fedilab.android.watermark.androidwm.bean;
|
||||
|
||||
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.widget.EditText;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.ColorInt;
|
||||
import androidx.annotation.FontRes;
|
||||
|
||||
/**
|
||||
* It's a wrapper of the watermark text.
|
||||
*
|
||||
* @author huangyz0918 (huangyz0918@gmail.com)
|
||||
* @since 29/08/2018
|
||||
*/
|
||||
public class WatermarkText {
|
||||
|
||||
private String text;
|
||||
private int alpha = 50;
|
||||
private double size = 20;
|
||||
@ColorInt
|
||||
private int color = Color.BLACK;
|
||||
@ColorInt
|
||||
private int backgroundColor = Color.TRANSPARENT;
|
||||
private Paint.Style style = Paint.Style.FILL;
|
||||
@FontRes
|
||||
private int typeFaceId = 0;
|
||||
private float textShadowBlurRadius;
|
||||
private float textShadowXOffset;
|
||||
private float textShadowYOffset;
|
||||
@ColorInt
|
||||
private int textShadowColor = Color.WHITE;
|
||||
// set the default values for the position.
|
||||
private WatermarkPosition position = new WatermarkPosition(0, 0, 0);
|
||||
|
||||
/**
|
||||
* Constructors for WatermarkText
|
||||
*/
|
||||
public WatermarkText(String text) {
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
public WatermarkText(String text, WatermarkPosition position) {
|
||||
this.text = text;
|
||||
this.position = position;
|
||||
}
|
||||
|
||||
public WatermarkText(TextView textView) {
|
||||
textFromTextView(textView);
|
||||
}
|
||||
|
||||
public WatermarkText(EditText editText) {
|
||||
textFromEditText(editText);
|
||||
}
|
||||
|
||||
/**
|
||||
* Getters and Setters for those attrs.
|
||||
*/
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
|
||||
public int getTextAlpha() {
|
||||
return alpha;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param textAlpha can be set to 0-255.
|
||||
*/
|
||||
public WatermarkText setTextAlpha(int textAlpha) {
|
||||
this.alpha = textAlpha;
|
||||
return this;
|
||||
}
|
||||
|
||||
public WatermarkPosition getPosition() {
|
||||
return position;
|
||||
}
|
||||
|
||||
public WatermarkText setPosition(WatermarkPosition position) {
|
||||
this.position = position;
|
||||
return this;
|
||||
}
|
||||
|
||||
public double getTextSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param size can be set to normal text size.
|
||||
*/
|
||||
public WatermarkText setTextSize(double size) {
|
||||
this.size = size;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getTextColor() {
|
||||
return color;
|
||||
}
|
||||
|
||||
public WatermarkText setTextColor(int color) {
|
||||
this.color = color;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Paint.Style getTextStyle() {
|
||||
return style;
|
||||
}
|
||||
|
||||
public WatermarkText setTextStyle(Paint.Style style) {
|
||||
this.style = style;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getBackgroundColor() {
|
||||
return backgroundColor;
|
||||
}
|
||||
|
||||
public WatermarkText setBackgroundColor(int backgroundColor) {
|
||||
this.backgroundColor = backgroundColor;
|
||||
return this;
|
||||
}
|
||||
|
||||
public float getTextShadowBlurRadius() {
|
||||
return textShadowBlurRadius;
|
||||
}
|
||||
|
||||
public float getTextShadowXOffset() {
|
||||
return textShadowXOffset;
|
||||
}
|
||||
|
||||
public float getTextShadowYOffset() {
|
||||
return textShadowYOffset;
|
||||
}
|
||||
|
||||
public int getTextShadowColor() {
|
||||
return textShadowColor;
|
||||
}
|
||||
|
||||
public int getTextFont() {
|
||||
return typeFaceId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the typeface path to get the text typeface.
|
||||
*/
|
||||
public WatermarkText setTextFont(@FontRes int typeFaceId) {
|
||||
this.typeFaceId = typeFaceId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public WatermarkText setPositionX(double x) {
|
||||
this.position.setPositionX(x);
|
||||
return this;
|
||||
}
|
||||
|
||||
public WatermarkText setPositionY(double y) {
|
||||
this.position.setPositionY(y);
|
||||
return this;
|
||||
}
|
||||
|
||||
public WatermarkText setRotation(double rotation) {
|
||||
this.position.setRotation(rotation);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the shadow of the text watermark.
|
||||
*/
|
||||
public WatermarkText setTextShadow(final float blurRadius, final float shadowXOffset,
|
||||
final float shadowYOffset, @ColorInt final int shadowColor) {
|
||||
this.textShadowBlurRadius = blurRadius;
|
||||
this.textShadowXOffset = shadowXOffset;
|
||||
this.textShadowYOffset = shadowYOffset;
|
||||
this.textShadowColor = shadowColor;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* load a string text as watermark text from a {@link TextView}.
|
||||
*
|
||||
* @param textView the {@link TextView} we need to use.
|
||||
*/
|
||||
private void textFromTextView(TextView textView) {
|
||||
this.text = textView.getText().toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* load a string text as watermark text from a {@link EditText}.
|
||||
*
|
||||
* @param editText the {@link EditText} we need to use.
|
||||
*/
|
||||
private void textFromEditText(EditText editText) {
|
||||
this.text = editText.getText().toString();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Copyright 2018 Yizheng Huang
|
||||
*
|
||||
* 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 app.fedilab.android.watermark.androidwm.listener;
|
||||
|
||||
|
||||
/**
|
||||
* This interface is for listening if the task of
|
||||
* creating invisible watermark is finished.
|
||||
*
|
||||
* @param <T> can be the image and the string watermarks.
|
||||
* @author huangyz0918 (huangyz0918@gmail.com)
|
||||
*/
|
||||
public interface BuildFinishListener<T> {
|
||||
|
||||
void onSuccess(T object);
|
||||
|
||||
void onFailure(String message);
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright 2018 Yizheng Huang
|
||||
*
|
||||
* 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 app.fedilab.android.watermark.androidwm.listener;
|
||||
|
||||
|
||||
import app.fedilab.android.watermark.androidwm.task.DetectionReturnValue;
|
||||
|
||||
/**
|
||||
* This interface is for listening if the task of
|
||||
* detecting invisible watermark is finished.
|
||||
*
|
||||
* @author huangyz0918 (huangyz0918@gmail.com)
|
||||
*/
|
||||
public interface DetectFinishListener {
|
||||
|
||||
void onSuccess(DetectionReturnValue returnValue);
|
||||
|
||||
void onFailure(String message);
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Copyright 2018 Yizheng Huang
|
||||
*
|
||||
* 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 app.fedilab.android.watermark.androidwm.task;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
|
||||
/**
|
||||
* This is a simple class that can help we get more than two kinds of
|
||||
* return values in the task.
|
||||
*
|
||||
* @author huangyz0918 (huangyz0918@gmail.com)
|
||||
*/
|
||||
public class DetectionReturnValue {
|
||||
|
||||
private Bitmap watermarkBitmap;
|
||||
private String watermarkString;
|
||||
|
||||
public DetectionReturnValue() {
|
||||
|
||||
}
|
||||
|
||||
public DetectionReturnValue(Bitmap watermarkBitmap, String watermarkString) {
|
||||
this.watermarkBitmap = watermarkBitmap;
|
||||
this.watermarkString = watermarkString;
|
||||
}
|
||||
|
||||
public Bitmap getWatermarkBitmap() {
|
||||
return watermarkBitmap;
|
||||
}
|
||||
|
||||
protected void setWatermarkBitmap(Bitmap watermarkBitmap) {
|
||||
this.watermarkBitmap = watermarkBitmap;
|
||||
}
|
||||
|
||||
public String getWatermarkString() {
|
||||
return watermarkString;
|
||||
}
|
||||
|
||||
protected void setWatermarkString(String watermarkString) {
|
||||
this.watermarkString = watermarkString;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
* Copyright 2018 Yizheng Huang
|
||||
*
|
||||
* 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 app.fedilab.android.watermark.androidwm.task;
|
||||
|
||||
import static app.fedilab.android.watermark.androidwm.utils.BitmapUtils.getBitmapPixels;
|
||||
import static app.fedilab.android.watermark.androidwm.utils.BitmapUtils.pixel2ARGBArray;
|
||||
import static app.fedilab.android.watermark.androidwm.utils.Constant.CHUNK_SIZE;
|
||||
import static app.fedilab.android.watermark.androidwm.utils.Constant.ERROR_BITMAP_NULL;
|
||||
import static app.fedilab.android.watermark.androidwm.utils.Constant.ERROR_DETECT_FAILED;
|
||||
import static app.fedilab.android.watermark.androidwm.utils.Constant.MAX_IMAGE_SIZE;
|
||||
import static app.fedilab.android.watermark.androidwm.utils.Constant.WARNING_BIG_IMAGE;
|
||||
import static app.fedilab.android.watermark.androidwm.utils.StringUtils.copyFromIntArray;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.os.AsyncTask;
|
||||
|
||||
import app.fedilab.android.watermark.androidwm.listener.DetectFinishListener;
|
||||
import app.fedilab.android.watermark.androidwm.utils.FastDctFft;
|
||||
|
||||
/**
|
||||
* This is a task for watermark image detection.
|
||||
* In FD mode, all the task will return a bitmap;
|
||||
*
|
||||
* @author huangyz0918 (huangyz0918@gmail.com)
|
||||
*/
|
||||
@SuppressWarnings("PMD")
|
||||
public class FDDetectionTask extends AsyncTask<Bitmap, Void, DetectionReturnValue> {
|
||||
|
||||
private final DetectFinishListener listener;
|
||||
|
||||
public FDDetectionTask(DetectFinishListener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DetectionReturnValue doInBackground(Bitmap... bitmaps) {
|
||||
Bitmap markedBitmap = bitmaps[0];
|
||||
DetectionReturnValue resultValue = new DetectionReturnValue();
|
||||
|
||||
if (markedBitmap == null) {
|
||||
listener.onFailure(ERROR_BITMAP_NULL);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (markedBitmap.getWidth() > MAX_IMAGE_SIZE || markedBitmap.getHeight() > MAX_IMAGE_SIZE) {
|
||||
listener.onFailure(WARNING_BIG_IMAGE);
|
||||
return null;
|
||||
}
|
||||
|
||||
int[] pixels = getBitmapPixels(markedBitmap);
|
||||
|
||||
// divide and conquer
|
||||
if (pixels.length < CHUNK_SIZE) {
|
||||
int[] watermarkRGB = pixel2ARGBArray(pixels);
|
||||
double[] watermarkArray = copyFromIntArray(watermarkRGB);
|
||||
FastDctFft.transform(watermarkArray);
|
||||
|
||||
//TODO: do some operations with colorTempArray.
|
||||
|
||||
|
||||
} else {
|
||||
int numOfChunks = (int) Math.ceil((double) pixels.length / CHUNK_SIZE);
|
||||
for (int i = 0; i < numOfChunks; i++) {
|
||||
int start = i * CHUNK_SIZE;
|
||||
int length = Math.min(pixels.length - start, CHUNK_SIZE);
|
||||
int[] temp = new int[length];
|
||||
System.arraycopy(pixels, start, temp, 0, length);
|
||||
double[] colorTempArray = copyFromIntArray(pixel2ARGBArray(temp));
|
||||
FastDctFft.transform(colorTempArray);
|
||||
|
||||
//TODO: do some operations with colorTempArray.
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO: new detection operations will replace this block.
|
||||
String resultString;
|
||||
|
||||
if (binaryString.contains(LSB_TEXT_PREFIX_FLAG) && binaryString.contains(LSB_TEXT_SUFFIX_FLAG)) {
|
||||
resultString = getBetweenStrings(binaryString, true, listener);
|
||||
resultString = binaryToString(resultString);
|
||||
resultValue.setWatermarkString(resultString);
|
||||
} else if (binaryString.contains(LSB_IMG_PREFIX_FLAG) && binaryString.contains(LSB_IMG_SUFFIX_FLAG)) {
|
||||
binaryString = getBetweenStrings(binaryString, false, listener);
|
||||
resultString = binaryToString(binaryString);
|
||||
resultValue.setWatermarkBitmap(BitmapUtils.stringToBitmap(resultString));
|
||||
}*/
|
||||
|
||||
return resultValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(DetectionReturnValue detectionReturnValue) {
|
||||
if (detectionReturnValue == null) {
|
||||
listener.onFailure(ERROR_DETECT_FAILED);
|
||||
return;
|
||||
}
|
||||
|
||||
if (detectionReturnValue.getWatermarkString() != null &&
|
||||
!"".equals(detectionReturnValue.getWatermarkString()) ||
|
||||
detectionReturnValue.getWatermarkBitmap() != null) {
|
||||
listener.onSuccess(detectionReturnValue);
|
||||
} else {
|
||||
listener.onFailure(ERROR_DETECT_FAILED);
|
||||
}
|
||||
super.onPostExecute(detectionReturnValue);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,189 @@
|
|||
/*
|
||||
* Copyright 2018 Yizheng Huang
|
||||
*
|
||||
* 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 app.fedilab.android.watermark.androidwm.task;
|
||||
|
||||
|
||||
import static app.fedilab.android.watermark.androidwm.utils.BitmapUtils.getBitmapPixels;
|
||||
import static app.fedilab.android.watermark.androidwm.utils.BitmapUtils.pixel2ARGBArray;
|
||||
import static app.fedilab.android.watermark.androidwm.utils.BitmapUtils.textAsBitmap;
|
||||
import static app.fedilab.android.watermark.androidwm.utils.Constant.ERROR_CREATE_FAILED;
|
||||
import static app.fedilab.android.watermark.androidwm.utils.Constant.ERROR_NO_BACKGROUND;
|
||||
import static app.fedilab.android.watermark.androidwm.utils.Constant.ERROR_NO_WATERMARKS;
|
||||
import static app.fedilab.android.watermark.androidwm.utils.Constant.ERROR_PIXELS_NOT_ENOUGH;
|
||||
import static app.fedilab.android.watermark.androidwm.utils.StringUtils.copyFromIntArray;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Color;
|
||||
import android.os.AsyncTask;
|
||||
|
||||
import app.fedilab.android.watermark.androidwm.bean.AsyncTaskParams;
|
||||
import app.fedilab.android.watermark.androidwm.bean.WatermarkText;
|
||||
import app.fedilab.android.watermark.androidwm.listener.BuildFinishListener;
|
||||
import app.fedilab.android.watermark.androidwm.utils.FastDctFft;
|
||||
|
||||
/**
|
||||
* This is a tack that use Fast Fourier Transform for an image, to
|
||||
* build the image and text watermark into a frequency domain.
|
||||
*
|
||||
* @author huangyz0918 (huangyz0918@gmail.com)
|
||||
*/
|
||||
public class FDWatermarkTask extends AsyncTask<AsyncTaskParams, Void, Bitmap> {
|
||||
|
||||
private final BuildFinishListener<Bitmap> listener;
|
||||
|
||||
public FDWatermarkTask(BuildFinishListener<Bitmap> callback) {
|
||||
this.listener = callback;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Bitmap doInBackground(AsyncTaskParams... params) {
|
||||
Bitmap backgroundBitmap = params[0].getBackgroundImg();
|
||||
WatermarkText watermarkText = params[0].getWatermarkText();
|
||||
Bitmap watermarkBitmap = params[0].getWatermarkImg();
|
||||
Context context = params[0].getContext();
|
||||
|
||||
if (backgroundBitmap == null) {
|
||||
listener.onFailure(ERROR_NO_BACKGROUND);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (watermarkText != null) {
|
||||
watermarkBitmap = textAsBitmap(context, watermarkText);
|
||||
}
|
||||
|
||||
if (watermarkBitmap == null) {
|
||||
listener.onFailure(ERROR_NO_WATERMARKS);
|
||||
return null;
|
||||
}
|
||||
|
||||
int[] watermarkPixels = getBitmapPixels(watermarkBitmap);
|
||||
int[] watermarkColorArray = pixel2ARGBArray(watermarkPixels);
|
||||
Bitmap outputBitmap = Bitmap.createBitmap(backgroundBitmap.getWidth(), backgroundBitmap.getHeight(),
|
||||
backgroundBitmap.getConfig());
|
||||
|
||||
// convert the background bitmap into pixel array.
|
||||
int[] backgroundPixels = getBitmapPixels(backgroundBitmap);
|
||||
|
||||
if (watermarkColorArray.length > backgroundPixels.length * 4) {
|
||||
listener.onFailure(ERROR_PIXELS_NOT_ENOUGH);
|
||||
} else {
|
||||
// divide and conquer
|
||||
// use fixed chunk size or the size of watermark image.
|
||||
if (backgroundPixels.length < watermarkColorArray.length) {
|
||||
int[] backgroundColorArray = pixel2ARGBArray(backgroundPixels);
|
||||
double[] backgroundColorArrayD = copyFromIntArray(backgroundColorArray);
|
||||
|
||||
FastDctFft.transform(backgroundColorArrayD);
|
||||
|
||||
//TODO: do the operations.
|
||||
|
||||
FastDctFft.inverseTransform(backgroundColorArrayD);
|
||||
for (int i = 0; i < backgroundPixels.length; i++) {
|
||||
int color = Color.argb(
|
||||
(int) backgroundColorArrayD[4 * i],
|
||||
(int) backgroundColorArrayD[4 * i + 1],
|
||||
(int) backgroundColorArrayD[4 * i + 2],
|
||||
(int) backgroundColorArrayD[4 * i + 3]
|
||||
);
|
||||
|
||||
backgroundPixels[i] = color;
|
||||
}
|
||||
} else {
|
||||
// use fixed chunk size or the size of watermark image.
|
||||
int numOfChunks = (int) Math.ceil((double) backgroundPixels.length / watermarkColorArray.length);
|
||||
for (int i = 0; i < numOfChunks; i++) {
|
||||
int start = i * watermarkColorArray.length;
|
||||
int length = Math.min(backgroundPixels.length - start, watermarkColorArray.length);
|
||||
int[] temp = new int[length];
|
||||
System.arraycopy(backgroundPixels, start, temp, 0, length);
|
||||
double[] colorTempD = copyFromIntArray(pixel2ARGBArray(temp));
|
||||
FastDctFft.transform(colorTempD);
|
||||
|
||||
// for (int j = 0; j < length; j++) {
|
||||
// colorTempD[4 * j] = colorTempD[4 * j] + watermarkColorArray[j];
|
||||
// colorTempD[4 * j + 1] = colorTempD[4 * j + 1] + watermarkColorArray[j];
|
||||
// colorTempD[4 * j + 2] = colorTempD[4 * j + 2] + watermarkColorArray[j];
|
||||
// colorTempD[4 * j + 3] = colorTempD[4 * j + 3] + watermarkColorArray[j];
|
||||
// }
|
||||
|
||||
double enhanceNum = 1;
|
||||
|
||||
// The energy in frequency scaled.
|
||||
for (int j = 0; j < length; j++) {
|
||||
colorTempD[4 * j] = colorTempD[4 * j] * enhanceNum;
|
||||
colorTempD[4 * j + 1] = colorTempD[4 * j + 1] * enhanceNum;
|
||||
colorTempD[4 * j + 2] = colorTempD[4 * j + 2] * enhanceNum;
|
||||
colorTempD[4 * j + 3] = colorTempD[4 * j + 3] * enhanceNum;
|
||||
}
|
||||
|
||||
//TODO: do the operations.
|
||||
|
||||
|
||||
FastDctFft.inverseTransform(colorTempD);
|
||||
|
||||
for (int j = 0; j < length; j++) {
|
||||
int color = Color.argb(
|
||||
(int) colorTempD[4 * j],
|
||||
(int) colorTempD[4 * j + 1],
|
||||
(int) colorTempD[4 * j + 2],
|
||||
(int) colorTempD[4 * j + 3]
|
||||
);
|
||||
|
||||
backgroundPixels[start + j] = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
outputBitmap.setPixels(backgroundPixels, 0, backgroundBitmap.getWidth(), 0, 0,
|
||||
backgroundBitmap.getWidth(), backgroundBitmap.getHeight());
|
||||
return outputBitmap;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Bitmap bitmap) {
|
||||
if (listener != null) {
|
||||
if (bitmap != null) {
|
||||
listener.onSuccess(bitmap);
|
||||
} else {
|
||||
listener.onFailure(ERROR_CREATE_FAILED);
|
||||
}
|
||||
}
|
||||
super.onPostExecute(bitmap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize array.
|
||||
*
|
||||
* @param inputArray The array to be normalized.
|
||||
* @return The result of the normalization.
|
||||
*/
|
||||
public double[] normalizeArray(double[] inputArray, double dataHigh,
|
||||
double dataLow, double normalizedHigh,
|
||||
double normalizedLow) {
|
||||
for (int i = 0; i < inputArray.length; i++) {
|
||||
inputArray[i] = ((inputArray[i] - dataLow)
|
||||
/ (dataHigh - dataLow))
|
||||
* (normalizedHigh - normalizedLow) + normalizedLow;
|
||||
}
|
||||
return inputArray;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
* Copyright 2018 Yizheng Huang
|
||||
*
|
||||
* 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 app.fedilab.android.watermark.androidwm.task;
|
||||
|
||||
import static app.fedilab.android.watermark.androidwm.utils.BitmapUtils.getBitmapPixels;
|
||||
import static app.fedilab.android.watermark.androidwm.utils.BitmapUtils.pixel2ARGBArray;
|
||||
import static app.fedilab.android.watermark.androidwm.utils.Constant.ERROR_BITMAP_NULL;
|
||||
import static app.fedilab.android.watermark.androidwm.utils.Constant.ERROR_DETECT_FAILED;
|
||||
import static app.fedilab.android.watermark.androidwm.utils.Constant.LSB_IMG_PREFIX_FLAG;
|
||||
import static app.fedilab.android.watermark.androidwm.utils.Constant.LSB_IMG_SUFFIX_FLAG;
|
||||
import static app.fedilab.android.watermark.androidwm.utils.Constant.LSB_TEXT_PREFIX_FLAG;
|
||||
import static app.fedilab.android.watermark.androidwm.utils.Constant.LSB_TEXT_SUFFIX_FLAG;
|
||||
import static app.fedilab.android.watermark.androidwm.utils.Constant.MAX_IMAGE_SIZE;
|
||||
import static app.fedilab.android.watermark.androidwm.utils.Constant.WARNING_BIG_IMAGE;
|
||||
import static app.fedilab.android.watermark.androidwm.utils.StringUtils.binaryToString;
|
||||
import static app.fedilab.android.watermark.androidwm.utils.StringUtils.getBetweenStrings;
|
||||
import static app.fedilab.android.watermark.androidwm.utils.StringUtils.intArrayToStringJ;
|
||||
import static app.fedilab.android.watermark.androidwm.utils.StringUtils.replaceNinesJ;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.os.AsyncTask;
|
||||
|
||||
import app.fedilab.android.watermark.androidwm.listener.DetectFinishListener;
|
||||
import app.fedilab.android.watermark.androidwm.utils.BitmapUtils;
|
||||
|
||||
/**
|
||||
* This is a task for watermark image detection.
|
||||
* In LSB mode, all the task will return a bitmap;
|
||||
*
|
||||
* @author huangyz0918 (huangyz0918@gmail.com)
|
||||
*/
|
||||
public class LSBDetectionTask extends AsyncTask<Bitmap, Void, DetectionReturnValue> {
|
||||
|
||||
private final DetectFinishListener listener;
|
||||
|
||||
public LSBDetectionTask(DetectFinishListener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DetectionReturnValue doInBackground(Bitmap... bitmaps) {
|
||||
Bitmap markedBitmap = bitmaps[0];
|
||||
DetectionReturnValue resultValue = new DetectionReturnValue();
|
||||
|
||||
if (markedBitmap == null) {
|
||||
listener.onFailure(ERROR_BITMAP_NULL);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (markedBitmap.getWidth() > MAX_IMAGE_SIZE || markedBitmap.getHeight() > MAX_IMAGE_SIZE) {
|
||||
listener.onFailure(WARNING_BIG_IMAGE);
|
||||
return null;
|
||||
}
|
||||
|
||||
int[] pixels = getBitmapPixels(markedBitmap);
|
||||
int[] colorArray = pixel2ARGBArray(pixels);
|
||||
|
||||
for (int i = 0; i < colorArray.length; i++) {
|
||||
colorArray[i] = colorArray[i] % 10;
|
||||
}
|
||||
|
||||
replaceNinesJ(colorArray);
|
||||
String binaryString = intArrayToStringJ(colorArray);
|
||||
String resultString;
|
||||
|
||||
if (binaryString.contains(LSB_TEXT_PREFIX_FLAG) && binaryString.contains(LSB_TEXT_SUFFIX_FLAG)) {
|
||||
resultString = getBetweenStrings(binaryString, true, listener);
|
||||
resultString = binaryToString(resultString);
|
||||
resultValue.setWatermarkString(resultString);
|
||||
} else if (binaryString.contains(LSB_IMG_PREFIX_FLAG) && binaryString.contains(LSB_IMG_SUFFIX_FLAG)) {
|
||||
binaryString = getBetweenStrings(binaryString, false, listener);
|
||||
resultString = binaryToString(binaryString);
|
||||
resultValue.setWatermarkBitmap(BitmapUtils.stringToBitmap(resultString));
|
||||
}
|
||||
|
||||
return resultValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(DetectionReturnValue detectionReturnValue) {
|
||||
if (detectionReturnValue == null) {
|
||||
listener.onFailure(ERROR_DETECT_FAILED);
|
||||
return;
|
||||
}
|
||||
|
||||
if (detectionReturnValue.getWatermarkString() != null &&
|
||||
!"".equals(detectionReturnValue.getWatermarkString()) ||
|
||||
detectionReturnValue.getWatermarkBitmap() != null) {
|
||||
listener.onSuccess(detectionReturnValue);
|
||||
} else {
|
||||
listener.onFailure(ERROR_DETECT_FAILED);
|
||||
}
|
||||
super.onPostExecute(detectionReturnValue);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,143 @@
|
|||
/*
|
||||
* Copyright 2018 Yizheng Huang
|
||||
*
|
||||
* 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 app.fedilab.android.watermark.androidwm.task;
|
||||
|
||||
import static app.fedilab.android.watermark.androidwm.utils.BitmapUtils.getBitmapPixels;
|
||||
import static app.fedilab.android.watermark.androidwm.utils.BitmapUtils.pixel2ARGBArray;
|
||||
import static app.fedilab.android.watermark.androidwm.utils.Constant.ERROR_CREATE_FAILED;
|
||||
import static app.fedilab.android.watermark.androidwm.utils.Constant.ERROR_NO_BACKGROUND;
|
||||
import static app.fedilab.android.watermark.androidwm.utils.Constant.ERROR_NO_WATERMARKS;
|
||||
import static app.fedilab.android.watermark.androidwm.utils.Constant.ERROR_PIXELS_NOT_ENOUGH;
|
||||
import static app.fedilab.android.watermark.androidwm.utils.Constant.LSB_IMG_PREFIX_FLAG;
|
||||
import static app.fedilab.android.watermark.androidwm.utils.Constant.LSB_IMG_SUFFIX_FLAG;
|
||||
import static app.fedilab.android.watermark.androidwm.utils.Constant.LSB_TEXT_PREFIX_FLAG;
|
||||
import static app.fedilab.android.watermark.androidwm.utils.Constant.LSB_TEXT_SUFFIX_FLAG;
|
||||
import static app.fedilab.android.watermark.androidwm.utils.StringUtils.replaceSingleDigit;
|
||||
import static app.fedilab.android.watermark.androidwm.utils.StringUtils.stringToBinary;
|
||||
import static app.fedilab.android.watermark.androidwm.utils.StringUtils.stringToIntArray;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Color;
|
||||
import android.os.AsyncTask;
|
||||
|
||||
import app.fedilab.android.watermark.androidwm.bean.AsyncTaskParams;
|
||||
import app.fedilab.android.watermark.androidwm.bean.WatermarkText;
|
||||
import app.fedilab.android.watermark.androidwm.listener.BuildFinishListener;
|
||||
import app.fedilab.android.watermark.androidwm.utils.BitmapUtils;
|
||||
|
||||
/**
|
||||
* This is a background task for adding the specific invisible text
|
||||
* into the background image. We don't need to read every pixel's
|
||||
* RGB value, we just read the length values that can put our encrypted
|
||||
* text in.
|
||||
*
|
||||
* @author huangyz0918 (huangyz0918@gmail.com)
|
||||
*/
|
||||
public class LSBWatermarkTask extends AsyncTask<AsyncTaskParams, Void, Bitmap> {
|
||||
|
||||
private final BuildFinishListener<Bitmap> listener;
|
||||
|
||||
public LSBWatermarkTask(BuildFinishListener<Bitmap> callback) {
|
||||
this.listener = callback;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Bitmap doInBackground(AsyncTaskParams... params) {
|
||||
Bitmap backgroundBitmap = params[0].getBackgroundImg();
|
||||
WatermarkText watermarkText = params[0].getWatermarkText();
|
||||
Bitmap watermarkBitmap = params[0].getWatermarkImg();
|
||||
String watermarkString;
|
||||
|
||||
if (backgroundBitmap == null) {
|
||||
listener.onFailure(ERROR_NO_BACKGROUND);
|
||||
return null;
|
||||
}
|
||||
|
||||
// convert the watermark bitmap into a String.
|
||||
if (watermarkBitmap != null) {
|
||||
watermarkString = BitmapUtils.bitmapToString(watermarkBitmap);
|
||||
} else {
|
||||
watermarkString = watermarkText.getText();
|
||||
}
|
||||
|
||||
if (watermarkString == null) {
|
||||
listener.onFailure(ERROR_NO_WATERMARKS);
|
||||
return null;
|
||||
}
|
||||
|
||||
Bitmap outputBitmap = Bitmap.createBitmap(backgroundBitmap.getWidth(), backgroundBitmap.getHeight(),
|
||||
backgroundBitmap.getConfig());
|
||||
|
||||
int[] backgroundPixels = getBitmapPixels(backgroundBitmap);
|
||||
int[] backgroundColorArray = pixel2ARGBArray(backgroundPixels);
|
||||
|
||||
// convert the Sting into a binary string, and, replace the single digit number.
|
||||
// using the rebuilt pixels to create a new watermarked image.
|
||||
String watermarkBinary = stringToBinary(watermarkString);
|
||||
|
||||
if (watermarkBitmap != null) {
|
||||
watermarkBinary = LSB_IMG_PREFIX_FLAG + watermarkBinary + LSB_IMG_SUFFIX_FLAG;
|
||||
} else {
|
||||
watermarkBinary = LSB_TEXT_PREFIX_FLAG + watermarkBinary + LSB_TEXT_SUFFIX_FLAG;
|
||||
}
|
||||
|
||||
int[] watermarkColorArray = stringToIntArray(watermarkBinary);
|
||||
if (watermarkColorArray.length > backgroundColorArray.length) {
|
||||
listener.onFailure(ERROR_PIXELS_NOT_ENOUGH);
|
||||
} else {
|
||||
int chunkSize = watermarkColorArray.length;
|
||||
int numOfChunks = (int) Math.ceil((double) backgroundColorArray.length / chunkSize);
|
||||
for (int i = 0; i < numOfChunks - 1; i++) {
|
||||
int start = i * chunkSize;
|
||||
for (int j = 0; j < chunkSize; j++) {
|
||||
backgroundColorArray[start + j] = replaceSingleDigit(backgroundColorArray[start + j]
|
||||
, watermarkColorArray[j]);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < backgroundPixels.length; i++) {
|
||||
int color = Color.argb(
|
||||
backgroundColorArray[4 * i],
|
||||
backgroundColorArray[4 * i + 1],
|
||||
backgroundColorArray[4 * i + 2],
|
||||
backgroundColorArray[4 * i + 3]
|
||||
);
|
||||
backgroundPixels[i] = color;
|
||||
}
|
||||
|
||||
outputBitmap.setPixels(backgroundPixels, 0, backgroundBitmap.getWidth(), 0, 0,
|
||||
backgroundBitmap.getWidth(), backgroundBitmap.getHeight());
|
||||
|
||||
return outputBitmap;
|
||||
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Bitmap resultBitmap) {
|
||||
if (listener != null) {
|
||||
if (resultBitmap != null) {
|
||||
listener.onSuccess(resultBitmap);
|
||||
} else {
|
||||
listener.onFailure(ERROR_CREATE_FAILED);
|
||||
}
|
||||
}
|
||||
super.onPostExecute(resultBitmap);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,236 @@
|
|||
/*
|
||||
* Copyright 2018 Yizheng Huang
|
||||
*
|
||||
* 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 app.fedilab.android.watermark.androidwm.utils;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.Typeface;
|
||||
import android.os.Environment;
|
||||
import android.text.StaticLayout;
|
||||
import android.text.TextPaint;
|
||||
import android.util.Base64;
|
||||
import android.util.TypedValue;
|
||||
|
||||
import androidx.core.content.res.ResourcesCompat;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Calendar;
|
||||
import java.util.Locale;
|
||||
|
||||
import app.fedilab.android.watermark.androidwm.bean.WatermarkImage;
|
||||
import app.fedilab.android.watermark.androidwm.bean.WatermarkText;
|
||||
|
||||
/**
|
||||
* Util class for operations with {@link Bitmap}.
|
||||
*
|
||||
* @author huangyz0918
|
||||
*/
|
||||
public class BitmapUtils {
|
||||
|
||||
/**
|
||||
* build a bitmap from a text.
|
||||
*
|
||||
* @return {@link Bitmap} the bitmap return.
|
||||
*/
|
||||
public static Bitmap textAsBitmap(Context context, WatermarkText watermarkText) {
|
||||
TextPaint watermarkPaint = new TextPaint();
|
||||
watermarkPaint.setColor(watermarkText.getTextColor());
|
||||
watermarkPaint.setStyle(watermarkText.getTextStyle());
|
||||
|
||||
if (watermarkText.getTextAlpha() >= 0 && watermarkText.getTextAlpha() <= 255) {
|
||||
watermarkPaint.setAlpha(watermarkText.getTextAlpha());
|
||||
}
|
||||
|
||||
float value = (float) watermarkText.getTextSize();
|
||||
int pixel = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
|
||||
value, context.getResources().getDisplayMetrics());
|
||||
watermarkPaint.setTextSize(pixel);
|
||||
|
||||
if (watermarkText.getTextShadowBlurRadius() != 0
|
||||
|| watermarkText.getTextShadowXOffset() != 0
|
||||
|| watermarkText.getTextShadowYOffset() != 0) {
|
||||
watermarkPaint.setShadowLayer(watermarkText.getTextShadowBlurRadius(),
|
||||
watermarkText.getTextShadowXOffset(),
|
||||
watermarkText.getTextShadowYOffset(),
|
||||
watermarkText.getTextShadowColor());
|
||||
}
|
||||
|
||||
if (watermarkText.getTextFont() != 0) {
|
||||
Typeface typeface = ResourcesCompat.getFont(context, watermarkText.getTextFont());
|
||||
watermarkPaint.setTypeface(typeface);
|
||||
}
|
||||
|
||||
watermarkPaint.setAntiAlias(true);
|
||||
watermarkPaint.setTextAlign(Paint.Align.LEFT);
|
||||
watermarkPaint.setStrokeWidth(5);
|
||||
|
||||
float baseline = (int) (-watermarkPaint.ascent() + 1f);
|
||||
Rect bounds = new Rect();
|
||||
watermarkPaint.getTextBounds(watermarkText.getText(),
|
||||
0, watermarkText.getText().length(), bounds);
|
||||
|
||||
int boundWidth = bounds.width() + 20;
|
||||
int mTextMaxWidth = (int) watermarkPaint.measureText(watermarkText.getText());
|
||||
if (boundWidth > mTextMaxWidth) {
|
||||
boundWidth = mTextMaxWidth;
|
||||
}
|
||||
StaticLayout staticLayout = new StaticLayout(watermarkText.getText(),
|
||||
0, watermarkText.getText().length(),
|
||||
watermarkPaint, mTextMaxWidth, android.text.Layout.Alignment.ALIGN_NORMAL, 2.0f,
|
||||
2.0f, false);
|
||||
|
||||
int lineCount = staticLayout.getLineCount();
|
||||
int height = (int) (baseline + watermarkPaint.descent() + 3) * lineCount;
|
||||
Bitmap image = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
|
||||
if (boundWidth > 0 && height > 0) {
|
||||
image = Bitmap.createBitmap(boundWidth, height, Bitmap.Config.ARGB_8888);
|
||||
}
|
||||
Canvas canvas = new Canvas(image);
|
||||
canvas.drawColor(watermarkText.getBackgroundColor());
|
||||
staticLayout.draw(canvas);
|
||||
return image;
|
||||
}
|
||||
|
||||
/**
|
||||
* this method is for image resizing, we should get
|
||||
* the size from the input {@link WatermarkImage}
|
||||
* objects, and, set the size from 0 to 1 ,which means:
|
||||
* size = watermarkImageWidth / backgroundImageWidth
|
||||
*
|
||||
* @return {@link Bitmap} the new bitmap.
|
||||
*/
|
||||
public static Bitmap resizeBitmap(Bitmap watermarkImg, float size, Bitmap backgroundImg) {
|
||||
int bitmapWidth = watermarkImg.getWidth();
|
||||
int bitmapHeight = watermarkImg.getHeight();
|
||||
float scale = (backgroundImg.getWidth() * size) / bitmapWidth;
|
||||
Matrix matrix = new Matrix();
|
||||
matrix.postScale(scale, scale);
|
||||
return Bitmap.createBitmap(watermarkImg, 0, 0,
|
||||
bitmapWidth, bitmapHeight, matrix, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* this method is for image resizing, used in invisible watermark
|
||||
* creating progress. To make the progress faster, we should do
|
||||
* some pre-settings, user can set whether to do this part.
|
||||
* <p>
|
||||
* We set the new {@link Bitmap} to a fixed width = 512 pixels.
|
||||
*
|
||||
* @return {@link Bitmap} the new bitmap.
|
||||
*/
|
||||
public static Bitmap resizeBitmap(Bitmap inputBitmap, int maxImageSize) {
|
||||
float ratio = Math.min(
|
||||
(float) maxImageSize / inputBitmap.getWidth(),
|
||||
(float) maxImageSize / inputBitmap.getHeight());
|
||||
int width = Math.round(ratio * inputBitmap.getWidth());
|
||||
int height = Math.round(ratio * inputBitmap.getHeight());
|
||||
|
||||
return Bitmap.createScaledBitmap(inputBitmap, width,
|
||||
height, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a Bitmap to a String.
|
||||
*/
|
||||
public static String bitmapToString(Bitmap bitmap) {
|
||||
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
||||
bitmap.compress(Bitmap.CompressFormat.PNG, 100, byteArrayOutputStream);
|
||||
byte[] b = byteArrayOutputStream.toByteArray();
|
||||
return Base64.encodeToString(b, Base64.DEFAULT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a String to a Bitmap.
|
||||
*
|
||||
* @return bitmap (from given string)
|
||||
*/
|
||||
public static Bitmap stringToBitmap(String encodedString) {
|
||||
try {
|
||||
byte[] encodeByte = Base64.decode(encodedString, Base64.DEFAULT);
|
||||
return BitmapFactory.decodeByteArray(encodeByte, 0, encodeByte.length);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Saving a bitmap instance into local PNG.
|
||||
*/
|
||||
public static void saveAsPNG(Bitmap inputBitmap, String filePath, boolean withTime) {
|
||||
String sdStatus = Environment.getExternalStorageState();
|
||||
|
||||
@SuppressLint("SimpleDateFormat") String timeStamp =
|
||||
new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss", Locale.US).format(Calendar.getInstance().getTime());
|
||||
|
||||
FileOutputStream out = null;
|
||||
try {
|
||||
if (withTime) {
|
||||
out = new FileOutputStream(filePath + timeStamp + ".png");
|
||||
} else {
|
||||
out = new FileOutputStream(filePath + "watermarked" + ".png");
|
||||
}
|
||||
inputBitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
|
||||
// PNG is a lossless format, the compression factor (100) is ignored
|
||||
} catch (Exception ignored) {
|
||||
|
||||
} finally {
|
||||
try {
|
||||
if (out != null) {
|
||||
out.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* convert the background bitmap into pixel array.
|
||||
*/
|
||||
public static int[] getBitmapPixels(Bitmap inputBitmap) {
|
||||
int[] backgroundPixels = new int[inputBitmap.getWidth() * inputBitmap.getHeight()];
|
||||
inputBitmap.getPixels(backgroundPixels, 0, inputBitmap.getWidth(), 0, 0,
|
||||
inputBitmap.getWidth(), inputBitmap.getHeight());
|
||||
return backgroundPixels;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Bitmap to Pixels then converting it to an ARGB int array.
|
||||
*/
|
||||
public static int[] pixel2ARGBArray(int[] inputPixels) {
|
||||
int[] bitmapArray = new int[4 * inputPixels.length];
|
||||
for (int i = 0; i < inputPixels.length; i++) {
|
||||
bitmapArray[4 * i] = Color.alpha(inputPixels[i]);
|
||||
bitmapArray[4 * i + 1] = Color.red(inputPixels[i]);
|
||||
bitmapArray[4 * i + 2] = Color.green(inputPixels[i]);
|
||||
bitmapArray[4 * i + 3] = Color.blue(inputPixels[i]);
|
||||
}
|
||||
|
||||
return bitmapArray;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright 2018 Yizheng Huang
|
||||
*
|
||||
* 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 app.fedilab.android.watermark.androidwm.utils;
|
||||
|
||||
/**
|
||||
* the constant pool.
|
||||
*
|
||||
* @author huangyz0918 (huangyz0918@gmail.com)
|
||||
*/
|
||||
public class Constant {
|
||||
public static final String LSB_IMG_PREFIX_FLAG = "1212";
|
||||
public static final String LSB_TEXT_PREFIX_FLAG = "2323";
|
||||
public static final String LSB_IMG_SUFFIX_FLAG = "3434";
|
||||
public static final String LSB_TEXT_SUFFIX_FLAG = "4545";
|
||||
|
||||
public static final int MAX_IMAGE_SIZE = 1024;
|
||||
// use the watermark image's size
|
||||
public static final int CHUNK_SIZE = 5000;
|
||||
|
||||
public static final String ERROR_NO_WATERMARKS = "No input text or image! please load an image or a text in your WatermarkBuilder!";
|
||||
public static final String ERROR_CREATE_FAILED = "created watermark failed!";
|
||||
public static final String ERROR_NO_BACKGROUND = "No background image! please load an image in your WatermarkBuilder!";
|
||||
public static final String ERROR_PIXELS_NOT_ENOUGH = "The Pixels in background are too small to put the watermark in, " +
|
||||
"the data has been lost! Please make sure the maxImageSize is bigger enough!";
|
||||
|
||||
public static final String ERROR_DETECT_FAILED = "Failed to detect the watermark!";
|
||||
public static final String ERROR_NO_WATERMARK_FOUND = "No watermarks found in this image!";
|
||||
public static final String ERROR_BITMAP_NULL = "Cannot detect the watermark! markedBitmap is null object!";
|
||||
|
||||
public static final String WARNING_BIG_IMAGE = "The input image may be too large to put into the memory, please be careful of the OOM!";
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
* Copyright 2018 Yizheng Huang
|
||||
*
|
||||
* 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 app.fedilab.android.watermark.androidwm.utils;
|
||||
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
|
||||
public final class FastDctFft {
|
||||
|
||||
/**
|
||||
* Computes the unscaled DCT type II on the specified array in place.
|
||||
* The array length must be a power of 2 or zero.
|
||||
*
|
||||
* @param vector the vector of numbers to transform
|
||||
* @throws NullPointerException if the array is {@code null}
|
||||
*/
|
||||
public static void transform(double[] vector) {
|
||||
Objects.requireNonNull(vector);
|
||||
int len = vector.length;
|
||||
int halfLen = len / 2;
|
||||
double[] real = new double[len];
|
||||
|
||||
for (int i = 0; i < halfLen; i++) {
|
||||
real[i] = vector[i * 2];
|
||||
real[len - 1 - i] = vector[i * 2 + 1];
|
||||
}
|
||||
|
||||
if (len % 2 == 1) {
|
||||
real[halfLen] = vector[len - 1];
|
||||
}
|
||||
|
||||
Arrays.fill(vector, 0.0);
|
||||
Fft.transform(real, vector);
|
||||
for (int i = 0; i < len; i++) {
|
||||
double temp = i * Math.PI / (len * 2);
|
||||
vector[i] = real[i] * Math.cos(temp) + vector[i] * Math.sin(temp);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Computes the unscaled DCT type III on the specified array in place.
|
||||
* The array length must be a power of 2 or zero.
|
||||
*
|
||||
* @param vector the vector of numbers to transform
|
||||
* @throws NullPointerException if the array is {@code null}
|
||||
*/
|
||||
public static void inverseTransform(double[] vector) {
|
||||
Objects.requireNonNull(vector);
|
||||
int len = vector.length;
|
||||
if (len > 0) {
|
||||
vector[0] = vector[0] / 2;
|
||||
}
|
||||
|
||||
double[] real = new double[len];
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
double temp = i * Math.PI / (len * 2);
|
||||
real[i] = vector[i] * Math.cos(temp);
|
||||
vector[i] *= -Math.sin(temp);
|
||||
}
|
||||
|
||||
Fft.transform(real, vector);
|
||||
|
||||
int halfLen = len / 2;
|
||||
for (int i = 0; i < halfLen; i++) {
|
||||
vector[i * 2] = real[i];
|
||||
vector[i * 2 + 1] = real[len - 1 - i];
|
||||
}
|
||||
|
||||
if (len % 2 == 1) {
|
||||
vector[len - 1] = real[halfLen];
|
||||
}
|
||||
|
||||
double scale = (double) len / 2;
|
||||
for (int i = 0; i < len; i++) {
|
||||
vector[i] = (int) Math.round(vector[i] / scale);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,202 @@
|
|||
/*
|
||||
* Copyright 2018 Yizheng Huang
|
||||
*
|
||||
* 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 app.fedilab.android.watermark.androidwm.utils;
|
||||
|
||||
|
||||
public final class Fft {
|
||||
|
||||
/**
|
||||
* Computes the discrete Fourier transform (DFT) of the given complex vector,
|
||||
* storing the result back into the vector.
|
||||
* <p>
|
||||
* The vector can have any length. This is a wrapper function.
|
||||
*/
|
||||
public static void transform(double[] real, double[] imag) {
|
||||
int n = real.length;
|
||||
if (n != imag.length) {
|
||||
throw new IllegalArgumentException("Mismatched lengths");
|
||||
}
|
||||
if ((n & (n - 1)) == 0) {
|
||||
transformRadix2(real, imag);
|
||||
} else {
|
||||
transformBlueStein(real, imag);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Computes the inverse discrete Fourier transform (IDFT) of the given
|
||||
* complex vector, storing the result back into the vector.
|
||||
* <p>
|
||||
* The vector can have any length. This is a wrapper function.
|
||||
* This transform does not perform scaling, so the inverse is not a true inverse.
|
||||
*/
|
||||
private static void inverseTransform(double[] real, double[] imag) {
|
||||
transform(imag, real);
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the discrete Fourier transform (DFT) of the given complex vector,
|
||||
* storing the result back into the vector.
|
||||
* <p>
|
||||
* The vector's length must be a power of 2. Uses the Cooley-Tukey
|
||||
* decimation-in-time radix-2 algorithm.
|
||||
*/
|
||||
private static void transformRadix2(double[] real, double[] imag) {
|
||||
int n = real.length;
|
||||
if (n != imag.length) {
|
||||
throw new IllegalArgumentException("Mismatched lengths");
|
||||
}
|
||||
|
||||
int levels = 31 - Integer.numberOfLeadingZeros(n);
|
||||
if (1 << levels != n) {
|
||||
throw new IllegalArgumentException("Length is not a power of 2");
|
||||
}
|
||||
|
||||
double[] cosTable = new double[n / 2];
|
||||
double[] sinTable = new double[n / 2];
|
||||
for (int i = 0; i < n / 2; i++) {
|
||||
cosTable[i] = Math.cos(2 * Math.PI * i / n);
|
||||
sinTable[i] = Math.sin(2 * Math.PI * i / n);
|
||||
}
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
int j = Integer.reverse(i) >>> (32 - levels);
|
||||
if (j > i) {
|
||||
double temp = real[i];
|
||||
real[i] = real[j];
|
||||
real[j] = temp;
|
||||
temp = imag[i];
|
||||
imag[i] = imag[j];
|
||||
imag[j] = temp;
|
||||
}
|
||||
}
|
||||
|
||||
for (int size = 2; size <= n; size *= 2) {
|
||||
int halfSize = size / 2;
|
||||
int tableStep = n / size;
|
||||
|
||||
for (int i = 0; i < n; i += size) {
|
||||
for (int j = i, k = 0; j < i + halfSize; j++, k += tableStep) {
|
||||
int l = j + halfSize;
|
||||
double tpre = real[l] * cosTable[k] + imag[l] * sinTable[k];
|
||||
double tpim = -real[l] * sinTable[k] + imag[l] * cosTable[k];
|
||||
real[l] = real[j] - tpre;
|
||||
imag[l] = imag[j] - tpim;
|
||||
real[j] += tpre;
|
||||
imag[j] += tpim;
|
||||
}
|
||||
}
|
||||
|
||||
if (size == n) {
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Computes the discrete Fourier transform (DFT) of the given complex vector,
|
||||
* storing the result back into the vector.
|
||||
* <p>
|
||||
* The vector can have any length. This requires the convolution function,
|
||||
* which in turn requires the radix-2 FFT function.
|
||||
* <p>
|
||||
* Uses Bluestein's chirp z-transform algorithm.
|
||||
*/
|
||||
private static void transformBlueStein(double[] real, double[] imag) {
|
||||
int n = real.length;
|
||||
if (n != imag.length) {
|
||||
throw new IllegalArgumentException("Mismatched lengths");
|
||||
}
|
||||
if (n >= 0x20000000) {
|
||||
throw new IllegalArgumentException("Array too large");
|
||||
}
|
||||
|
||||
int m = Integer.highestOneBit(n) * 4;
|
||||
|
||||
double[] cosTable = new double[n];
|
||||
double[] sinTable = new double[n];
|
||||
for (int i = 0; i < n; i++) {
|
||||
int j = (int) ((long) i * i % (n * 2));
|
||||
cosTable[i] = Math.cos(Math.PI * j / n);
|
||||
sinTable[i] = Math.sin(Math.PI * j / n);
|
||||
}
|
||||
|
||||
double[] aReal = new double[m];
|
||||
double[] aImag = new double[m];
|
||||
for (int i = 0; i < n; i++) {
|
||||
aReal[i] = real[i] * cosTable[i] + imag[i] * sinTable[i];
|
||||
aImag[i] = -real[i] * sinTable[i] + imag[i] * cosTable[i];
|
||||
}
|
||||
double[] bReal = new double[m];
|
||||
double[] bImag = new double[m];
|
||||
bReal[0] = cosTable[0];
|
||||
bImag[0] = sinTable[0];
|
||||
for (int i = 1; i < n; i++) {
|
||||
bReal[i] = bReal[m - i] = cosTable[i];
|
||||
bImag[i] = bImag[m - i] = sinTable[i];
|
||||
}
|
||||
|
||||
double[] cReal = new double[m];
|
||||
double[] cImag = new double[m];
|
||||
convolve(aReal, aImag, bReal, bImag, cReal, cImag);
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
real[i] = cReal[i] * cosTable[i] + cImag[i] * sinTable[i];
|
||||
imag[i] = -cReal[i] * sinTable[i] + cImag[i] * cosTable[i];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the circular convolution of the given complex vectors.
|
||||
* Each vector's length must be the same.
|
||||
*/
|
||||
private static void convolve(double[] xReal, double[] xImag,
|
||||
double[] yReal, double[] yImag, double[] outReal, double[] outImag) {
|
||||
|
||||
int n = xReal.length;
|
||||
if (n != xImag.length || n != yReal.length || n != yImag.length
|
||||
|| n != outReal.length || n != outImag.length) {
|
||||
throw new IllegalArgumentException("Mismatched lengths");
|
||||
}
|
||||
|
||||
xReal = xReal.clone();
|
||||
xImag = xImag.clone();
|
||||
yReal = yReal.clone();
|
||||
yImag = yImag.clone();
|
||||
transform(xReal, xImag);
|
||||
transform(yReal, yImag);
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
double temp = xReal[i] * yReal[i] - xImag[i] * yImag[i];
|
||||
xImag[i] = xImag[i] * yReal[i] + xReal[i] * yImag[i];
|
||||
xReal[i] = temp;
|
||||
}
|
||||
|
||||
inverseTransform(xReal, xImag);
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
outReal[i] = xReal[i] / n;
|
||||
outImag[i] = xImag[i] / n;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,158 @@
|
|||
/*
|
||||
* Copyright 2018 Yizheng Huang
|
||||
*
|
||||
* 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 app.fedilab.android.watermark.androidwm.utils;
|
||||
|
||||
|
||||
import static app.fedilab.android.watermark.androidwm.utils.Constant.ERROR_NO_WATERMARK_FOUND;
|
||||
import static app.fedilab.android.watermark.androidwm.utils.Constant.LSB_IMG_PREFIX_FLAG;
|
||||
import static app.fedilab.android.watermark.androidwm.utils.Constant.LSB_IMG_SUFFIX_FLAG;
|
||||
import static app.fedilab.android.watermark.androidwm.utils.Constant.LSB_TEXT_PREFIX_FLAG;
|
||||
import static app.fedilab.android.watermark.androidwm.utils.Constant.LSB_TEXT_SUFFIX_FLAG;
|
||||
|
||||
import app.fedilab.android.watermark.androidwm.listener.DetectFinishListener;
|
||||
|
||||
/**
|
||||
* Util class for operations with {@link String}.
|
||||
*
|
||||
* @author huangyz0918
|
||||
*/
|
||||
public class StringUtils {
|
||||
|
||||
static {
|
||||
System.loadLibrary("Watermark");
|
||||
}
|
||||
|
||||
/**
|
||||
* Converting a {@link String} text into a binary text.
|
||||
* <p>
|
||||
* This is the native version.
|
||||
*/
|
||||
public static native String stringToBinary(String inputText);
|
||||
|
||||
/**
|
||||
* String to integer array.
|
||||
* <p>
|
||||
* This is the native version.
|
||||
*/
|
||||
public static native int[] stringToIntArray(String inputString);
|
||||
|
||||
/**
|
||||
* Converting a binary string to a ASCII string.
|
||||
*/
|
||||
public static native String binaryToString(String inputText);
|
||||
|
||||
/**
|
||||
* Replace the wrong rgb number in a form of binary,
|
||||
* the only case is 0 - 1 = 9, so, we need to replace
|
||||
* all nines to zero.
|
||||
*/
|
||||
public static native void replaceNines(int[] inputArray);
|
||||
|
||||
public static void replaceNinesJ(int[] inputArray) {
|
||||
for (int i = 0; i < inputArray.length; i++) {
|
||||
if (inputArray[i] == 9) {
|
||||
inputArray[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Int array to string.
|
||||
*/
|
||||
public static native String intArrayToString(int[] inputArray);
|
||||
|
||||
public static String intArrayToStringJ(int[] inputArray) {
|
||||
StringBuilder binary = new StringBuilder();
|
||||
for (int num : inputArray) {
|
||||
binary.append(num);
|
||||
}
|
||||
return binary.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* native method for calculating the Convolution 1D.
|
||||
*/
|
||||
public static native double[] calConv1D(double[] inputArray1, double[] inputArray2);
|
||||
|
||||
/**
|
||||
* get the single digit number and set it to the target one.
|
||||
*/
|
||||
public static int replaceSingleDigit(int target, int singleDigit) {
|
||||
return (target / 10) * 10 + singleDigit;
|
||||
}
|
||||
|
||||
public static int replaceSingleDigit(double target, int singleDigit) {
|
||||
return ((int) target / 10) * 10 + singleDigit;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get text between two strings. Passed limiting strings are not
|
||||
* included into result.
|
||||
*
|
||||
* @param text Text to search in.
|
||||
*/
|
||||
public static String getBetweenStrings(String text, boolean isText, DetectFinishListener listener) {
|
||||
String result = null;
|
||||
if (isText) {
|
||||
try {
|
||||
result = text.substring(text.indexOf(LSB_TEXT_PREFIX_FLAG) + LSB_TEXT_SUFFIX_FLAG.length()
|
||||
);
|
||||
result = result.substring(0, result.indexOf(LSB_TEXT_SUFFIX_FLAG));
|
||||
} catch (StringIndexOutOfBoundsException e) {
|
||||
listener.onFailure(ERROR_NO_WATERMARK_FOUND);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
result = text.substring(text.indexOf(LSB_IMG_PREFIX_FLAG) + LSB_IMG_SUFFIX_FLAG.length()
|
||||
);
|
||||
result = result.substring(0, result.indexOf(LSB_IMG_SUFFIX_FLAG));
|
||||
} catch (StringIndexOutOfBoundsException e) {
|
||||
listener.onFailure(ERROR_NO_WATERMARK_FOUND);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* cast an int array to a double array.
|
||||
* System.arrayCopy cannot cast the int array to a double one.
|
||||
*/
|
||||
@SuppressWarnings("PMD")
|
||||
public static double[] copyFromIntArray(int[] source) {
|
||||
double[] dest = new double[source.length];
|
||||
for (int i = 0; i < source.length; i++) {
|
||||
dest[i] = source[i];
|
||||
}
|
||||
return dest;
|
||||
}
|
||||
|
||||
/**
|
||||
* cast a double array to an int array.
|
||||
* System.arrayCopy cannot cast the double array to an int one.
|
||||
*/
|
||||
@SuppressWarnings("PMD")
|
||||
public static int[] copyFromDoubleArray(double[] source) {
|
||||
int[] dest = new int[source.length];
|
||||
for (int i = 0; i < source.length; i++) {
|
||||
dest[i] = (int) source[i];
|
||||
}
|
||||
return dest;
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in a new issue