diff --git a/android/app/build.gradle b/android/app/build.gradle index 6db03ec8..c4df110f 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -50,4 +50,5 @@ flutter { dependencies { implementation "androidx.window:window:1.0.0" + implementation 'com.esotericsoftware:kryo:5.5.0' } diff --git a/android/app/src/main/kotlin/org/catrobat/colorpicker/ColorHistory.kt b/android/app/src/main/kotlin/org/catrobat/colorpicker/ColorHistory.kt new file mode 100644 index 00000000..d7202362 --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/colorpicker/ColorHistory.kt @@ -0,0 +1,22 @@ +package org.catrobat.paintroid.colorpicker + +import java.io.Serializable + +const val COLOR_HISTORY_SIZE = 4 + +class ColorHistory : Serializable { + private val colorHistory: ArrayList = arrayListOf() + + val colors: ArrayList + get() = colorHistory + + fun addColor(color: Int) { + if (colorHistory.lastOrNull() != color) { + colorHistory.add(color) + } + + if (colorHistory.size > COLOR_HISTORY_SIZE) { + colorHistory.removeFirst() + } + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/FileReader.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/FileReader.kt new file mode 100644 index 00000000..9740ec09 --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/FileReader.kt @@ -0,0 +1,213 @@ +package org.catrobat.paintroid + +import android.content.Context +import android.net.Uri +import com.esotericsoftware.kryo.Kryo +import com.esotericsoftware.kryo.io.Input +import com.esotericsoftware.kryo.io.Output +import java.io.InputStream +import org.catrobat.paintroid.model.Layer + +import org.catrobat.paintroid.command.serialization.VersionSerializer +import org.catrobat.paintroid.command.Command +import org.catrobat.paintroid.command.implementation.CompositeCommand +import org.catrobat.paintroid.command.serialization.CompositeCommandSerializer +import org.catrobat.paintroid.command.serialization.DataStructuresSerializer +import org.catrobat.paintroid.model.CommandManagerModel +import org.catrobat.paintroid.command.serialization.CommandManagerModelSerializer +import org.catrobat.paintroid.command.implementation.SetDimensionCommand +import org.catrobat.paintroid.command.implementation.SprayCommand +import org.catrobat.paintroid.command.serialization.SetDimensionCommandSerializer +import org.catrobat.paintroid.command.serialization.SprayCommandSerializer +import org.catrobat.paintroid.command.serialization.PaintSerializer + +import org.catrobat.paintroid.command.implementation.AddEmptyLayerCommand +import org.catrobat.paintroid.command.serialization.AddLayerCommandSerializer + +import org.catrobat.paintroid.command.implementation.SelectLayerCommand +import org.catrobat.paintroid.command.serialization.SelectLayerCommandSerializer + +import org.catrobat.paintroid.command.implementation.LoadCommand +import org.catrobat.paintroid.command.serialization.LoadCommandSerializer + +import org.catrobat.paintroid.command.implementation.TextToolCommand +import org.catrobat.paintroid.command.serialization.TextToolCommandSerializer + +import org.catrobat.paintroid.command.implementation.FillCommand +import org.catrobat.paintroid.command.serialization.FillCommandSerializer + +import org.catrobat.paintroid.command.implementation.FlipCommand +import org.catrobat.paintroid.command.serialization.FlipCommandSerializer + +import org.catrobat.paintroid.command.implementation.CropCommand +import org.catrobat.paintroid.command.serialization.CropCommandSerializer + +import org.catrobat.paintroid.command.implementation.CutCommand +import org.catrobat.paintroid.command.serialization.CutCommandSerializer + + + +import org.catrobat.paintroid.command.implementation.ResizeCommand +import org.catrobat.paintroid.command.serialization.ResizeCommandSerializer + +import org.catrobat.paintroid.command.implementation.RotateCommand +import org.catrobat.paintroid.command.serialization.RotateCommandSerializer +import org.catrobat.paintroid.command.implementation.ResetCommand +import org.catrobat.paintroid.command.serialization.ResetCommandSerializer + + +import org.catrobat.paintroid.command.implementation.ReorderLayersCommand +import org.catrobat.paintroid.command.serialization.ReorderLayersCommandSerializer + +import org.catrobat.paintroid.command.implementation.RemoveLayerCommand +import org.catrobat.paintroid.command.serialization.RemoveLayerCommandSerializer + + + +import org.catrobat.paintroid.command.implementation.MergeLayersCommand +import org.catrobat.paintroid.command.serialization.MergeLayersCommandSerializer + +import org.catrobat.paintroid.command.implementation.PathCommand +import org.catrobat.paintroid.command.serialization.PathCommandSerializer + + +import org.catrobat.paintroid.command.serialization.SerializablePath + +import org.catrobat.paintroid.command.implementation.LoadLayerListCommand +import org.catrobat.paintroid.command.serialization.LoadLayerListCommandSerializer + + +import org.catrobat.paintroid.command.implementation.GeometricFillCommand +import org.catrobat.paintroid.command.serialization.GeometricFillCommandSerializer + + +import org.catrobat.paintroid.command.implementation.ClipboardCommand +import org.catrobat.paintroid.command.serialization.ClipboardCommandSerializer + + +import org.catrobat.paintroid.command.serialization.SerializableTypeface + + +import org.catrobat.paintroid.command.implementation.PointCommand +import org.catrobat.paintroid.command.serialization.PointCommandSerializer + + +import org.catrobat.paintroid.command.serialization.BitmapSerializer + + +import org.catrobat.paintroid.command.implementation.SmudgePathCommand +import org.catrobat.paintroid.command.serialization.SmudgePathCommandSerializer + + +//import org.catrobat.paintroid.command.implementation.SmudgePathCommand +import org.catrobat.paintroid.colorpicker.ColorHistory +import org.catrobat.paintroid.command.serialization.ColorHistorySerializer + + + +import org.catrobat.paintroid.command.implementation.ClippingCommand +import org.catrobat.paintroid.command.serialization.ClippingCommandSerializer + + +import org.catrobat.paintroid.command.implementation.LayerOpacityCommand +import org.catrobat.paintroid.command.serialization.LayerOpacityCommandSerializer + +import android.graphics.Paint +import android.graphics.Point +import android.graphics.PointF +import android.graphics.RectF +import android.graphics.Bitmap + +import android.content.ContentResolver +import android.content.ContentUris +import android.content.ContentValues + + +import org.catrobat.paintroid.tools.drawable.HeartDrawable +import org.catrobat.paintroid.tools.drawable.OvalDrawable +import org.catrobat.paintroid.tools.drawable.RectangleDrawable +import org.catrobat.paintroid.tools.drawable.ShapeDrawable +import org.catrobat.paintroid.tools.drawable.StarDrawable + + +class FileReader(private val context : Context) +{ + private lateinit var activityContext: Context // MAYBE CAUSE A CRASH + private val kryo = Kryo() + private val registerMap = LinkedHashMap, VersionSerializer<*>?>() + companion object { + const val MAGIC_VALUE = "CatrobatImg" + const val CURRENT_IMAGE_VERSION = 2 // handle 1 look up how to do it in the native verson + } + init { + setRegisterMapVersion(CURRENT_IMAGE_VERSION) + registerClasses() + } + /* fun readFromFile(uri: String): CatrobatFileContent{ + var commandModel: CommandManagerModel + var colorHistory: ColorHistory? = null + }*/ + private fun setRegisterMapVersion(version: Int) { + // Only add new classes at the end + // because Kryo assigns an ID to each class + with(registerMap) { + put(Command::class.java, null) + put(CompositeCommand::class.java, CompositeCommandSerializer(version)) + put(FloatArray::class.java, DataStructuresSerializer.FloatArraySerializer(version)) + put(PointF::class.java, DataStructuresSerializer.PointFSerializer(version)) + put(Point::class.java, DataStructuresSerializer.PointSerializer(version)) + put(CommandManagerModel::class.java, CommandManagerModelSerializer(version)) + put(SetDimensionCommand::class.java, SetDimensionCommandSerializer(version)) + put(SprayCommand::class.java, SprayCommandSerializer(version)) + put(Paint::class.java, PaintSerializer(version, activityContext)) // maybe will cause a crash ? activityContext is lateinnit + put(AddEmptyLayerCommand::class.java, AddLayerCommandSerializer(version)) + put(SelectLayerCommand::class.java, SelectLayerCommandSerializer(version)) + put(LoadCommand::class.java, LoadCommandSerializer(version)) + put(TextToolCommand::class.java, TextToolCommandSerializer(version, activityContext)) + put(Array::class.java, DataStructuresSerializer.StringArraySerializer(version)) + put(FillCommand::class.java, FillCommandSerializer(version)) + put(FlipCommand::class.java, FlipCommandSerializer(version)) + put(CropCommand::class.java, CropCommandSerializer(version)) + put(CutCommand::class.java, CutCommandSerializer(version)) + put(ResizeCommand::class.java, ResizeCommandSerializer(version)) + put(RotateCommand::class.java, RotateCommandSerializer(version)) + put(ResetCommand::class.java, ResetCommandSerializer(version)) + put(ReorderLayersCommand::class.java, ReorderLayersCommandSerializer(version)) + put(RemoveLayerCommand::class.java, RemoveLayerCommandSerializer(version)) + put(MergeLayersCommand::class.java, MergeLayersCommandSerializer(version)) + put(PathCommand::class.java, PathCommandSerializer(version)) + put(SerializablePath::class.java, SerializablePath.PathSerializer(version)) + put(SerializablePath.Move::class.java, SerializablePath.PathActionMoveSerializer(version)) + put(SerializablePath.Line::class.java, SerializablePath.PathActionLineSerializer(version)) + put(SerializablePath.Quad::class.java, SerializablePath.PathActionQuadSerializer(version)) + put(SerializablePath.Rewind::class.java, SerializablePath.PathActionRewindSerializer(version)) + put(LoadLayerListCommand::class.java, LoadLayerListCommandSerializer(version)) + put(GeometricFillCommand::class.java, GeometricFillCommandSerializer(version)) + put(HeartDrawable::class.java, GeometricFillCommandSerializer.HeartDrawableSerializer(version)) + put(OvalDrawable::class.java, GeometricFillCommandSerializer.OvalDrawableSerializer(version)) + put(RectangleDrawable::class.java, GeometricFillCommandSerializer.RectangleDrawableSerializer(version)) + put(StarDrawable::class.java, GeometricFillCommandSerializer.StarDrawableSerializer(version)) + put(ShapeDrawable::class.java, null) + put(RectF::class.java, DataStructuresSerializer.RectFSerializer(version)) + put(ClipboardCommand::class.java, ClipboardCommandSerializer(version)) + put(SerializableTypeface::class.java, SerializableTypeface.TypefaceSerializer(version)) + put(PointCommand::class.java, PointCommandSerializer(version)) + put(SerializablePath.Cube::class.java, SerializablePath.PathActionCubeSerializer(version)) + put(Bitmap::class.java, BitmapSerializer(version)) + put(SmudgePathCommand::class.java, SmudgePathCommandSerializer(version)) + put(ColorHistory::class.java, ColorHistorySerializer(version)) + put(ClippingCommand::class.java, ClippingCommandSerializer(version)) + put(LayerOpacityCommand::class.java, LayerOpacityCommandSerializer(version)) + } + } + private fun registerClasses() { + registerMap.forEach { (classRegister, serializer) -> + val registration = kryo.register(classRegister) + serializer?.let { + registration.serializer = serializer + } + } + } + + +} \ No newline at end of file diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/MainActivity.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/MainActivity.kt index 60ac0235..3b05fe9f 100644 --- a/android/app/src/main/kotlin/org/catrobat/paintroid/MainActivity.kt +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/MainActivity.kt @@ -13,8 +13,14 @@ import io.flutter.embedding.engine.FlutterEngine import io.flutter.plugin.common.MethodCall import io.flutter.plugin.common.MethodChannel import java.io.IOException +import android.util.Log +import com.esotericsoftware.kryo.Kryo +import com.esotericsoftware.kryo.io.Input +import com.esotericsoftware.kryo.io.Output +import java.nio.ByteBuffer class MainActivity : FlutterActivity() { + private val kryo = Kryo() private val hasWritePermission: Boolean get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q || ContextCompat.checkSelfPermission( @@ -26,6 +32,7 @@ class MainActivity : FlutterActivity() { super.configureFlutterEngine(flutterEngine) setupPhotoLibraryChannel(flutterEngine) setupDeviceChannel(flutterEngine) + setupNativeCatrobat(flutterEngine) } private fun setupDeviceChannel(flutterEngine: FlutterEngine) { @@ -72,6 +79,43 @@ class MainActivity : FlutterActivity() { } } + // TODO getHeightInPixels + private fun setupNativeCatrobat(flutterEngine: FlutterEngine) { + MethodChannel(flutterEngine.dartExecutor.binaryMessenger, "org.catrobat.paintroid/native").apply { + setMethodCallHandler { call, result -> + when (call.method) { + "getNativeClassData" -> { + val parameter = call.argument("path") + if (parameter == null) { + Log.d("MethodChannel", "No path received") + result.error("NO_PARAM", "No path provided", null) + } else { + try { + //Log.d("MethodChannel", "Received path: $parameter") + //val data = getNativeClassData(parameter) + val byteBuffer = ByteBuffer.allocate(4) // Allocate a ByteBuffer with space for 4 bytes + byteBuffer.put(0x01) // Example byte + byteBuffer.put(0x02) // Example byte + byteBuffer.put(0x03) // Example byte + byteBuffer.put(0x04) // Example byte + val byteArray = ByteArray(byteBuffer.remaining()) + byteBuffer.get(byteArray) + result.success(byteArray) + } catch (e: Exception) { + Log.e("MethodChannel", "Error processing data", e) + result.error("ERROR_PROCESSING", "Failed to process data", e.localizedMessage) + } + } + } + else -> { + Log.d("MethodChannel", "Method not implemented") + result.notImplemented() + } + } + } + } + } + private fun saveImageToPictures(filename: String, data: ByteArray) { val picturesUri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY) @@ -109,3 +153,11 @@ class MainActivity : FlutterActivity() { return Pair(filename, imageData) } } + + + +private fun getNativeClassData(path: String): ByteArray { + // Assuming you have a method to process the data, e.g., using Kryo for serialization + // Replace with actual data processing logic + return byteArrayOf() // Placeholder for actual data processing +} \ No newline at end of file diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/command/Command.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/command/Command.kt new file mode 100644 index 00000000..bae630b5 --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/command/Command.kt @@ -0,0 +1,28 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.catrobat.paintroid.command + +import android.graphics.Canvas +import org.catrobat.paintroid.contract.LayerContracts + +interface Command { + fun run(canvas: Canvas, layerModel: LayerContracts.Model) + fun freeResources() +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/command/CommandFactory.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/command/CommandFactory.kt new file mode 100644 index 00000000..73cfecc5 --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/command/CommandFactory.kt @@ -0,0 +1,126 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.command + +import android.content.Context +import android.graphics.Bitmap +import android.graphics.Paint +import android.graphics.Point +import android.graphics.PointF +import android.graphics.RectF +import org.catrobat.paintroid.command.implementation.FlipCommand.FlipDirection +import org.catrobat.paintroid.command.implementation.RotateCommand.RotateDirection +import org.catrobat.paintroid.command.serialization.SerializablePath +import org.catrobat.paintroid.command.serialization.SerializableTypeface +import org.catrobat.paintroid.contract.LayerContracts +import org.catrobat.paintroid.tools.Tool +import org.catrobat.paintroid.tools.drawable.ShapeDrawable + +interface CommandFactory { + fun createInitCommand(width: Int, height: Int): Command + + fun createInitCommand(bitmap: Bitmap): Command + + fun createInitCommand(layers: List): Command + + fun createResetCommand(): Command + + fun createAddEmptyLayerCommand(): Command + + fun createSelectLayerCommand(position: Int): Command + + fun createLayerOpacityCommand(position: Int, opacityPercentage: Int): Command + + fun createRemoveLayerCommand(index: Int): Command + + fun createReorderLayersCommand(position: Int, swapWith: Int): Command + + fun createMergeLayersCommand(position: Int, mergeWith: Int): Command + + fun createRotateCommand(rotateDirection: RotateDirection): Command + + fun createFlipCommand(flipDirection: FlipDirection): Command + + fun createCropCommand( + resizeCoordinateXLeft: Int, + resizeCoordinateYTop: Int, + resizeCoordinateXRight: Int, + resizeCoordinateYBottom: Int, + maximumBitmapResolution: Int + ): Command + + fun createPointCommand(paint: Paint, coordinate: PointF): Command + + fun createFillCommand(x: Int, y: Int, paint: Paint, colorTolerance: Float): Command + + fun createGeometricFillCommand( + shapeDrawable: ShapeDrawable, + position: Point, + box: RectF, + boxRotation: Float, + paint: Paint + ): Command + + fun createPathCommand(paint: Paint, path: SerializablePath): Command + + fun createSmudgePathCommand( + bitmap: Bitmap, + pointPath: MutableList, + maxPressure: Float, + maxSize: Float, + minSize: Float + ): Command + + fun createTextToolCommand( + multilineText: Array, + textPaint: Paint, + boxOffset: Int, + boxWidth: Float, + boxHeight: Float, + toolPosition: PointF, + boxRotation: Float, + typefaceInfo: SerializableTypeface + ): Command + + fun createResizeCommand(newWidth: Int, newHeight: Int): Command + + fun createClipboardCommand( + bitmap: Bitmap, + toolPosition: PointF, + boxWidth: Float, + boxHeight: Float, + boxRotation: Float + ): Command + + fun createSprayCommand(sprayedPoints: FloatArray, paint: Paint): Command + + fun createCutCommand( + toolPosition: PointF, + boxWidth: Float, + boxHeight: Float, + boxRotation: Float + ): Command + + fun createColorChangedCommand(tool: Tool?, context: Context, color: Int): Command + + fun createClippingCommand( + bitmap: Bitmap, + pathBitmap: Bitmap + ): Command +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/command/CommandManager.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/command/CommandManager.kt new file mode 100644 index 00000000..2c32275b --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/command/CommandManager.kt @@ -0,0 +1,79 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.command + +import org.catrobat.paintroid.model.CommandManagerModel + +interface CommandManager { + val isUndoAvailable: Boolean + val isRedoAvailable: Boolean + val lastExecutedCommand: Command? + val isBusy: Boolean + val commandManagerModel: CommandManagerModel? + + fun addCommandListener(commandListener: CommandListener) + + fun removeCommandListener(commandListener: CommandListener) + + fun addCommand(command: Command?) + + fun addCommandWithoutUndo(command: Command?) + + fun setInitialStateCommand(command: Command) + + fun loadCommandsCatrobatImage(model: CommandManagerModel?) + + fun undo() + + fun redo() + + fun reset() + + fun shutdown() + + fun undoIgnoringColorChanges() + + fun undoIgnoringColorChangesAndAddCommand(command: Command) + + fun undoInConnectedLinesMode() + + fun redoInConnectedLinesMode() + + fun getCommandManagerModelForCatrobatImage(): CommandManagerModel? + + fun adjustUndoListForClippingTool() + + fun undoInClippingTool() + + fun popFirstCommandInUndo() + + fun popFirstCommandInRedo() + + fun executeAllCommands() + + fun getUndoCommandCount(): Int + + fun getColorCommandCount(): Int + + fun isLastColorCommandOnTop(): Boolean + + interface CommandListener { + fun commandPostExecute() + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/command/MainActivityConstants.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/command/MainActivityConstants.kt new file mode 100644 index 00000000..263e9e0f --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/command/MainActivityConstants.kt @@ -0,0 +1,94 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.common + +import androidx.annotation.IntDef +import java.lang.AssertionError + +const val SAVE_IMAGE_DEFAULT = 1 +const val SAVE_IMAGE_NEW_EMPTY = 2 +const val SAVE_IMAGE_LOAD_NEW = 3 +const val SAVE_IMAGE_FINISH = 4 + +const val LOAD_IMAGE_DEFAULT = 1 +const val LOAD_IMAGE_IMPORT_PNG = 2 +const val LOAD_IMAGE_CATROID = 3 + +const val CREATE_FILE_DEFAULT = 1 + +const val REQUEST_CODE_IMPORT_PNG = 1 +const val REQUEST_CODE_LOAD_PICTURE = 2 +const val REQUEST_CODE_INTRO = 3 + +const val PERMISSION_EXTERNAL_STORAGE_SAVE = 1 +const val PERMISSION_EXTERNAL_STORAGE_SAVE_COPY = 2 +const val PERMISSION_EXTERNAL_STORAGE_SAVE_CONFIRMED_LOAD_NEW = 3 +const val PERMISSION_EXTERNAL_STORAGE_SAVE_CONFIRMED_NEW_EMPTY = 4 +const val PERMISSION_EXTERNAL_STORAGE_SAVE_CONFIRMED_FINISH = 5 +const val PERMISSION_REQUEST_CODE_REPLACE_PICTURE = 6 +const val PERMISSION_REQUEST_CODE_IMPORT_PICTURE = 7 + +const val RESULT_INTRO_MW_NOT_SUPPORTED = 10 + +class MainActivityConstants private constructor() { + @IntDef( + SAVE_IMAGE_DEFAULT, + SAVE_IMAGE_NEW_EMPTY, + SAVE_IMAGE_LOAD_NEW, + SAVE_IMAGE_FINISH + ) + @Retention(AnnotationRetention.SOURCE) + annotation class SaveImageRequestCode + + @IntDef( + LOAD_IMAGE_DEFAULT, + LOAD_IMAGE_IMPORT_PNG, + LOAD_IMAGE_CATROID + ) + @Retention(AnnotationRetention.SOURCE) + annotation class LoadImageRequestCode + + @IntDef(CREATE_FILE_DEFAULT) + @Retention(AnnotationRetention.SOURCE) + annotation class CreateFileRequestCode + + @IntDef( + REQUEST_CODE_IMPORT_PNG, + REQUEST_CODE_LOAD_PICTURE, + REQUEST_CODE_INTRO + ) + @Retention(AnnotationRetention.SOURCE) + annotation class ActivityRequestCode + + @IntDef( + PERMISSION_EXTERNAL_STORAGE_SAVE, + PERMISSION_EXTERNAL_STORAGE_SAVE_COPY, + PERMISSION_EXTERNAL_STORAGE_SAVE_CONFIRMED_LOAD_NEW, + PERMISSION_EXTERNAL_STORAGE_SAVE_CONFIRMED_NEW_EMPTY, + PERMISSION_EXTERNAL_STORAGE_SAVE_CONFIRMED_FINISH, + PERMISSION_REQUEST_CODE_REPLACE_PICTURE, + PERMISSION_REQUEST_CODE_IMPORT_PICTURE + ) + @Retention(AnnotationRetention.SOURCE) + annotation class PermissionRequestCode + + init { + throw AssertionError() + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/command/implementation/AddEmptyLayerCommand.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/command/implementation/AddEmptyLayerCommand.kt new file mode 100644 index 00000000..f711a1c6 --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/command/implementation/AddEmptyLayerCommand.kt @@ -0,0 +1,44 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.command.implementation + +import android.graphics.Bitmap +import android.graphics.Canvas +import org.catrobat.paintroid.command.Command +import org.catrobat.paintroid.common.CommonFactory +import org.catrobat.paintroid.contract.LayerContracts +import org.catrobat.paintroid.model.Layer + +class AddEmptyLayerCommand(private val commonFactory: CommonFactory) : Command { + + override fun run(canvas: Canvas, layerModel: LayerContracts.Model) { + val layer = Layer( + commonFactory.createBitmap( + layerModel.width, layerModel.height, + Bitmap.Config.ARGB_8888 + ) + ) + layerModel.addLayerAt(0, layer) + layerModel.currentLayer = layer + } + + override fun freeResources() { + // No resources to free + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/command/implementation/ClipboardCommand.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/command/implementation/ClipboardCommand.kt new file mode 100644 index 00000000..11ceb490 --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/command/implementation/ClipboardCommand.kt @@ -0,0 +1,112 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.catrobat.paintroid.command.implementation + +import android.graphics.Bitmap +import android.graphics.Canvas +import android.graphics.Point +import android.graphics.RectF +import androidx.annotation.VisibleForTesting + +import org.catrobat.paintroid.command.Command +import org.catrobat.paintroid.contract.LayerContracts +import java.io.File +import java.io.FileOutputStream +import java.util.Random + +class ClipboardCommand(bitmap: Bitmap, position: Point, width: Float, height: Float, rotation: Float) : + Command { + + var bitmap: Bitmap? = bitmap.copy(Bitmap.Config.ARGB_8888, false); private set + var coordinates = position; private set + var boxRotation = rotation; private set + var boxWidth = width; private set + var boxHeight = height; private set + var fileToStoredBitmap: File? = null + + companion object { + private const val COMPRESSION_QUALITY = 100 + } + + override fun run(canvas: Canvas, layerModel: LayerContracts.Model) { + var bitmapToDraw = bitmap + if (fileToStoredBitmap != null) { + // bitmapToDraw = FileIO.getBitmapFromFile(fileToStoredBitmap) + } + bitmapToDraw ?: return + + val rect = RectF(-boxWidth / 2f, -boxHeight / 2f, boxWidth / 2f, boxHeight / 2f) + with(canvas) { + save() + translate(coordinates.x.toFloat(), coordinates.y.toFloat()) + rotate(boxRotation) + drawBitmap(bitmapToDraw, null, rect, null) + restore() + } + + if (fileToStoredBitmap == null) { + storeBitmap(bitmapToDraw, boxWidth, boxHeight) + } + bitmap = recycleBitmap(bitmapToDraw) + } + + @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) + fun storeBitmap(bitmapToStore: Bitmap, boxWidth: Float, boxHeight: Float) { + // val random = Random() + // random.setSeed(System.currentTimeMillis()) + /* fileToStoredBitmap = + File(PaintroidApplication.cacheDir?.absolutePath, random.nextLong().toString()) + val resizedBitmap = resizeBitmap(bitmapToStore, boxWidth, boxHeight) + FileOutputStream(fileToStoredBitmap).use { stream -> + resizedBitmap.compress(Bitmap.CompressFormat.PNG, COMPRESSION_QUALITY, stream) + } + + */ + } + + private fun resizeBitmap(bitmapToResize: Bitmap, boxWidth: Float, boxHeight: Float): Bitmap { + val newWidth = + if (boxWidth < bitmapToResize.width) boxWidth.toInt() else bitmapToResize.width + val newHeight = + if (boxHeight < bitmapToResize.height) boxHeight.toInt() else bitmapToResize.height + if (newWidth == bitmapToResize.width && newHeight == bitmapToResize.height) { + return bitmapToResize + } + return Bitmap.createScaledBitmap(bitmapToResize, newWidth, newHeight, false) + } + + private fun recycleBitmap(bitmapToRecycle: Bitmap?): Bitmap? { + return bitmapToRecycle?.let { bitmap -> + if (!bitmap.isRecycled) { + bitmap.recycle() + } + null + } + } + + override fun freeResources() { + bitmap = recycleBitmap(bitmap) + fileToStoredBitmap?.let { file -> + if (file.exists()) { + file.delete() + } + } + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/command/implementation/ClippingCommand.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/command/implementation/ClippingCommand.kt new file mode 100644 index 00000000..2ebf7f70 --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/command/implementation/ClippingCommand.kt @@ -0,0 +1,36 @@ +package org.catrobat.paintroid.command.implementation + +import android.graphics.Bitmap +import android.graphics.Canvas +import android.graphics.Color +import android.graphics.Paint +import android.graphics.PorterDuff +import android.graphics.PorterDuffXfermode +import android.graphics.Rect +import org.catrobat.paintroid.command.Command +import org.catrobat.paintroid.contract.LayerContracts + +class ClippingCommand(bitmap: Bitmap, pathBitmap: Bitmap) : Command { + + var bitmap: Bitmap? = bitmap.copy(bitmap.config, true); private set + var pathBitmap: Bitmap? = pathBitmap.copy(pathBitmap.config, true); private set + + override fun run(canvas: Canvas, layerModel: LayerContracts.Model) { + val bitmapToDraw = bitmap + bitmapToDraw ?: return + val wholeRect = Rect(0, 0, bitmapToDraw.width, bitmapToDraw.height) + layerModel.currentLayer?.bitmap?.eraseColor(Color.TRANSPARENT) + val paint = Paint() + with(canvas) { + save() + pathBitmap?.let { drawBitmap(it, null, wholeRect, null) } + paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_IN) + bitmap?.let { drawBitmap(it, null, wholeRect, paint) } + restore() + } + } + override fun freeResources() { + bitmap?.recycle() + pathBitmap?.recycle() + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/command/implementation/CompositeCommand.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/command/implementation/CompositeCommand.kt new file mode 100644 index 00000000..76b7b0c1 --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/command/implementation/CompositeCommand.kt @@ -0,0 +1,47 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.command.implementation + +import android.graphics.Canvas +import org.catrobat.paintroid.command.Command +import org.catrobat.paintroid.contract.LayerContracts + +class CompositeCommand : Command { + + var commands = mutableListOf(); private set + + fun addCommand(command: Command) { + commands.add(command) + } + + override fun run(canvas: Canvas, layerModel: LayerContracts.Model) { + commands.forEach { command -> + layerModel.currentLayer?.let { layer -> + canvas.setBitmap(layer.bitmap) + } + command.run(canvas, layerModel) + } + } + + override fun freeResources() { + commands.forEach { command -> + command.freeResources() + } + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/command/implementation/CropCommand.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/command/implementation/CropCommand.kt new file mode 100644 index 00000000..874d249a --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/command/implementation/CropCommand.kt @@ -0,0 +1,72 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.catrobat.paintroid.command.implementation + +import android.graphics.Bitmap +import android.graphics.Canvas +import org.catrobat.paintroid.command.Command +import org.catrobat.paintroid.contract.LayerContracts + +class CropCommand( + resizeCoordinateXLeft: Int, + resizeCoordinateYTop: Int, + resizeCoordinateXRight: Int, + resizeCoordinateYBottom: Int, + maximumBitmapResolution: Int +) : Command { + + var resizeCoordinateXLeft = resizeCoordinateXLeft; private set + var resizeCoordinateYTop = resizeCoordinateYTop; private set + var resizeCoordinateXRight = resizeCoordinateXRight; private set + var resizeCoordinateYBottom = resizeCoordinateYBottom; private set + var maximumBitmapResolution = maximumBitmapResolution; private set + + override fun run(canvas: Canvas, layerModel: LayerContracts.Model) { + if (resizeCoordinateXRight < resizeCoordinateXLeft || resizeCoordinateYBottom < resizeCoordinateYTop) { + return + } + if (resizeCoordinateXLeft >= layerModel.width || resizeCoordinateXRight < 0 || resizeCoordinateYTop >= layerModel.height || resizeCoordinateYBottom < 0) { + return + } + if (resizeCoordinateXLeft == 0 && resizeCoordinateXRight == layerModel.width - 1 && resizeCoordinateYBottom == layerModel.height - 1 && resizeCoordinateYTop == 0) { + return + } + if ((resizeCoordinateXRight + 1 - resizeCoordinateXLeft) * (resizeCoordinateYBottom + 1 - resizeCoordinateYTop) > maximumBitmapResolution) { + return + } + val width = resizeCoordinateXRight + 1 - resizeCoordinateXLeft + val height = resizeCoordinateYBottom + 1 - resizeCoordinateYTop + val iterator = layerModel.listIterator(0) + while (iterator.hasNext()) { + val currentLayer = iterator.next() + val currentBitmap = currentLayer.bitmap ?: Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888) + val resizedBitmap = Bitmap.createBitmap(width, height, currentBitmap.config) + val resizedCanvas = Canvas(resizedBitmap) + resizedCanvas.drawBitmap(currentBitmap, -resizeCoordinateXLeft.toFloat(), -resizeCoordinateYTop.toFloat(), null) + currentLayer.bitmap = resizedBitmap + } + layerModel.height = height + layerModel.width = width + } + + override fun freeResources() { + // No resources to free + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/command/implementation/CutCommand.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/command/implementation/CutCommand.kt new file mode 100644 index 00000000..e26fd408 --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/command/implementation/CutCommand.kt @@ -0,0 +1,53 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.command.implementation + +import android.graphics.Canvas +import android.graphics.Paint +import android.graphics.Point +import android.graphics.PorterDuff +import android.graphics.PorterDuffXfermode +import android.graphics.RectF +import org.catrobat.paintroid.command.Command +import org.catrobat.paintroid.contract.LayerContracts + +class CutCommand( + val toolPosition: Point, + val boxWidth: Float, + val boxHeight: Float, + val boxRotation: Float +) : Command { + private val boxRect = RectF(-boxWidth / 2f, -boxHeight / 2f, boxWidth / 2f, boxHeight / 2f) + private val paint: Paint = Paint().apply { + xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR) + alpha = 0 + } + + override fun run(canvas: Canvas, layerModel: LayerContracts.Model) { + canvas.save() + canvas.translate(toolPosition.x.toFloat(), toolPosition.y.toFloat()) + canvas.rotate(boxRotation) + canvas.drawRect(boxRect, paint) + canvas.restore() + } + + override fun freeResources() { + // No resources to free + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/command/implementation/FillCommand.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/command/implementation/FillCommand.kt new file mode 100644 index 00000000..d3a9bc13 --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/command/implementation/FillCommand.kt @@ -0,0 +1,55 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.catrobat.paintroid.command.implementation + +import android.graphics.Canvas +import android.graphics.Paint +import android.graphics.Point +import org.catrobat.paintroid.command.Command +import org.catrobat.paintroid.contract.LayerContracts +import org.catrobat.paintroid.tools.helper.FillAlgorithmFactory + +class FillCommand(private val fillAlgorithmFactory: FillAlgorithmFactory, clickedPixel: Point, paint: Paint, colorTolerance: Float) : Command { + + var clickedPixel = clickedPixel; private set + var paint = paint; private set + var colorTolerance = colorTolerance; private set + + override fun run(canvas: Canvas, layerModel: LayerContracts.Model) { + val currentLayer = layerModel.currentLayer + currentLayer ?: return + currentLayer.bitmap.let { bitmap -> + val colorToBeReplaced = bitmap.getPixel(clickedPixel.x, clickedPixel.y) + val fillAlgorithm = fillAlgorithmFactory.createFillAlgorithm() + fillAlgorithm.setParameters( + bitmap, + clickedPixel, + paint.color, + colorToBeReplaced, + colorTolerance + ) + fillAlgorithm.performFilling() + } + } + + override fun freeResources() { + // No resources to free + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/command/implementation/FlipCommand.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/command/implementation/FlipCommand.kt new file mode 100644 index 00000000..aefd9a5d --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/command/implementation/FlipCommand.kt @@ -0,0 +1,61 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.catrobat.paintroid.command.implementation + +import android.graphics.Canvas +import android.graphics.Color +import android.graphics.Matrix +import android.graphics.Paint +import org.catrobat.paintroid.command.Command +import org.catrobat.paintroid.contract.LayerContracts + +class FlipCommand(flipDirection: FlipDirection) : Command { + + var flipDirection = flipDirection; private set + + override fun run(canvas: Canvas, layerModel: LayerContracts.Model) { + val flipMatrix = Matrix().apply { + when (flipDirection) { + FlipDirection.FLIP_HORIZONTAL -> { + setScale(1f, -1f) + postTranslate(0f, layerModel.height.toFloat()) + } + FlipDirection.FLIP_VERTICAL -> { + setScale(-1f, 1f) + postTranslate(layerModel.width.toFloat(), 0f) + } + } + } + layerModel.currentLayer?.bitmap?.let { bitmap -> + val bitmapCopy = bitmap.copy(bitmap.config, bitmap.isMutable) + val flipCanvas = Canvas(bitmap) + bitmap.eraseColor(Color.TRANSPARENT) + flipCanvas.drawBitmap(bitmapCopy, flipMatrix, Paint()) + } + } + + override fun freeResources() { + // No resources to free + } + + enum class FlipDirection { + FLIP_HORIZONTAL, FLIP_VERTICAL + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/command/implementation/GeometricFillCommand.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/command/implementation/GeometricFillCommand.kt new file mode 100644 index 00000000..65d9c200 --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/command/implementation/GeometricFillCommand.kt @@ -0,0 +1,58 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.catrobat.paintroid.command.implementation + +import android.graphics.Canvas +import android.graphics.Paint +import android.graphics.RectF +import org.catrobat.paintroid.command.Command +import org.catrobat.paintroid.contract.LayerContracts +import org.catrobat.paintroid.tools.drawable.ShapeDrawable + +class GeometricFillCommand( + shapeDrawable: ShapeDrawable, + pointX: Int, + pointY: Int, + boxRect: RectF, + boxRotation: Float, + paint: Paint +) : Command { + + var shapeDrawable = shapeDrawable; private set + var pointX = pointX; private set + var pointY = pointY; private set + var boxRect = boxRect; private set + var boxRotation = boxRotation; private set + var paint = paint; private set + + override fun run(canvas: Canvas, layerModel: LayerContracts.Model) { + with(canvas) { + save() + translate(pointX.toFloat(), pointY.toFloat()) + rotate(boxRotation) + shapeDrawable.draw(this, boxRect, paint) + restore() + } + } + + override fun freeResources() { + // No resources to free + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/command/implementation/LayerOpacityCommand.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/command/implementation/LayerOpacityCommand.kt new file mode 100644 index 00000000..ba1cc68b --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/command/implementation/LayerOpacityCommand.kt @@ -0,0 +1,38 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.command.implementation + +import android.graphics.Canvas +import org.catrobat.paintroid.command.Command +import org.catrobat.paintroid.contract.LayerContracts + +class LayerOpacityCommand( + val position: Int, + val opacityPercentage: Int +) : Command { + + override fun run(canvas: Canvas, layerModel: LayerContracts.Model) { + val layer = layerModel.getLayerAt(position) + layer?.opacityPercentage = opacityPercentage + } + + override fun freeResources() { + // No resources to free + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/command/implementation/LoadCommand.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/command/implementation/LoadCommand.kt new file mode 100644 index 00000000..b1ff89bb --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/command/implementation/LoadCommand.kt @@ -0,0 +1,41 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.catrobat.paintroid.command.implementation + +import android.graphics.Bitmap +import android.graphics.Canvas +import org.catrobat.paintroid.command.Command +import org.catrobat.paintroid.contract.LayerContracts +import org.catrobat.paintroid.model.Layer + +class LoadCommand(loadedBitmap: Bitmap) : Command { + + var loadedBitmap = loadedBitmap; private set + + override fun run(canvas: Canvas, layerModel: LayerContracts.Model) { + val currentLayer = Layer(loadedBitmap.copy(Bitmap.Config.ARGB_8888, true)) + layerModel.addLayerAt(0, currentLayer) + layerModel.currentLayer = currentLayer + } + + override fun freeResources() { + // No resources to free + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/command/implementation/LoadLayerListCommand.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/command/implementation/LoadLayerListCommand.kt new file mode 100644 index 00000000..abdf969d --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/command/implementation/LoadLayerListCommand.kt @@ -0,0 +1,44 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.catrobat.paintroid.command.implementation + +import android.graphics.Bitmap +import android.graphics.Canvas +import org.catrobat.paintroid.command.Command +import org.catrobat.paintroid.contract.LayerContracts +import org.catrobat.paintroid.model.Layer + +class LoadLayerListCommand(loadedLayers: List) : Command { + + var loadedLayers = loadedLayers; private set + + override fun run(canvas: Canvas, layerModel: LayerContracts.Model) { + loadedLayers.forEachIndexed { index, layer -> + val currentLayer = Layer(layer.bitmap.copy(Bitmap.Config.ARGB_8888, true)) + currentLayer.opacityPercentage = layer.opacityPercentage + layerModel.addLayerAt(index, currentLayer) + } + layerModel.currentLayer = layerModel.getLayerAt(0) + } + + override fun freeResources() { + // No resources to free + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/command/implementation/MergeLayersCommand.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/command/implementation/MergeLayersCommand.kt new file mode 100644 index 00000000..c4b43491 --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/command/implementation/MergeLayersCommand.kt @@ -0,0 +1,63 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.catrobat.paintroid.command.implementation + +import android.graphics.Canvas +import android.util.Log +import org.catrobat.paintroid.command.Command +import org.catrobat.paintroid.contract.LayerContracts + +class MergeLayersCommand(position: Int, mergeWith: Int) : Command { + + var position = position; private set + var mergeWith = mergeWith; private set + + companion object { + private val TAG = MergeLayersCommand::class.java.simpleName + } + + override fun run(canvas: Canvas, layerModel: LayerContracts.Model) { + val sourceLayer = layerModel.getLayerAt(position) + val destinationLayer = layerModel.getLayerAt(mergeWith) + var success = false + if (sourceLayer != null && destinationLayer != null) { + val destinationBitmap = destinationLayer.bitmap + destinationBitmap ?: return + val copyBitmap = destinationBitmap.copy(destinationBitmap.config, true) + val copyCanvas = Canvas(copyBitmap) + copyCanvas.drawBitmap(sourceLayer.bitmap ?: return, 0f, 0f, null) + if (layerModel.removeLayerAt(position)) { + destinationLayer.bitmap = copyBitmap + if (sourceLayer == layerModel.currentLayer) { + layerModel.currentLayer = destinationLayer + } + success = true + } + } + + if (!success) { + Log.e(TAG, "Could not merge layers!") + } + } + + override fun freeResources() { + // No resources to free + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/command/implementation/PathCommand.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/command/implementation/PathCommand.kt new file mode 100644 index 00000000..f0d84776 --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/command/implementation/PathCommand.kt @@ -0,0 +1,39 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.catrobat.paintroid.command.implementation + +import android.graphics.Canvas +import android.graphics.Paint +import android.graphics.Path +import org.catrobat.paintroid.command.Command +import org.catrobat.paintroid.contract.LayerContracts + +class PathCommand(val paint: Paint, path: Path) : Command { + + var path = path; private set + + override fun run(canvas: Canvas, layerModel: LayerContracts.Model) { + canvas.drawPath(path, paint) + } + + override fun freeResources() { + // No resources to free + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/command/implementation/PointCommand.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/command/implementation/PointCommand.kt new file mode 100644 index 00000000..e072214b --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/command/implementation/PointCommand.kt @@ -0,0 +1,39 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.catrobat.paintroid.command.implementation + +import android.graphics.Canvas +import android.graphics.Paint +import android.graphics.PointF +import org.catrobat.paintroid.command.Command +import org.catrobat.paintroid.contract.LayerContracts + +class PointCommand(var paint: Paint, point: PointF) : Command { + + var point = point; private set + + override fun run(canvas: Canvas, layerModel: LayerContracts.Model) { + canvas.drawPoint(point.x, point.y, paint) + } + + override fun freeResources() { + // No resources to free + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/command/implementation/RemoveLayerCommand.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/command/implementation/RemoveLayerCommand.kt new file mode 100644 index 00000000..46c18ac3 --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/command/implementation/RemoveLayerCommand.kt @@ -0,0 +1,39 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.catrobat.paintroid.command.implementation + +import android.graphics.Canvas +import org.catrobat.paintroid.command.Command +import org.catrobat.paintroid.contract.LayerContracts + +class RemoveLayerCommand(position: Int) : Command { + + var position = position; private set + + override fun run(canvas: Canvas, layerModel: LayerContracts.Model) { + if (layerModel.removeLayerAt(position)) { + layerModel.getLayerAt(0)?.let { layerModel.currentLayer = it } + } + } + + override fun freeResources() { + // No resources to free + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/command/implementation/ReorderLayersCommand.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/command/implementation/ReorderLayersCommand.kt new file mode 100644 index 00000000..f3b89684 --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/command/implementation/ReorderLayersCommand.kt @@ -0,0 +1,54 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.catrobat.paintroid.command.implementation + +import android.graphics.Canvas +import android.util.Log +import org.catrobat.paintroid.command.Command +import org.catrobat.paintroid.contract.LayerContracts + +class ReorderLayersCommand(position: Int, destination: Int) : Command { + + var position = position; private set + var destination = destination; private set + + companion object { + private val TAG = ReorderLayersCommand::class.java.simpleName + } + + override fun run(canvas: Canvas, layerModel: LayerContracts.Model) { + layerModel.run { + var success = false + getLayerAt(position)?.let { layer -> + if (removeLayerAt(position)) { + success = addLayerAt(destination, layer) + } + } + + if (!success) { + Log.e(TAG, "Could not retrieve layer to reorder!") + } + } + } + + override fun freeResources() { + // No resources to free + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/command/implementation/ResetCommand.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/command/implementation/ResetCommand.kt new file mode 100644 index 00000000..b2632d68 --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/command/implementation/ResetCommand.kt @@ -0,0 +1,35 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.catrobat.paintroid.command.implementation + +import android.graphics.Canvas +import org.catrobat.paintroid.command.Command +import org.catrobat.paintroid.contract.LayerContracts + +class ResetCommand : Command { + + override fun run(canvas: Canvas, layerModel: LayerContracts.Model) { + layerModel.reset() + } + + override fun freeResources() { + // No resources to free + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/command/implementation/ResizeCommand.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/command/implementation/ResizeCommand.kt new file mode 100644 index 00000000..346b1c7e --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/command/implementation/ResizeCommand.kt @@ -0,0 +1,47 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2015 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.command.implementation + +import android.graphics.Bitmap +import android.graphics.Canvas +import org.catrobat.paintroid.command.Command +import org.catrobat.paintroid.contract.LayerContracts + +class ResizeCommand(newWidth: Int, newHeight: Int) : Command { + + var newWidth = newWidth; private set + var newHeight = newHeight; private set + + override fun run(canvas: Canvas, layerModel: LayerContracts.Model) { + val iterator = layerModel.listIterator(0) + while (iterator.hasNext()) { + val currentLayer = iterator.next() + currentLayer.bitmap?.let { currentBitmap -> + val resizedBitmap = Bitmap.createScaledBitmap(currentBitmap, newWidth, newHeight, true) + currentLayer.bitmap = resizedBitmap + } + } + layerModel.height = newHeight + layerModel.width = newWidth + } + + override fun freeResources() { + // No resources to free + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/command/implementation/RotateCommand.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/command/implementation/RotateCommand.kt new file mode 100644 index 00000000..7a2d2764 --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/command/implementation/RotateCommand.kt @@ -0,0 +1,64 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.catrobat.paintroid.command.implementation + +import android.graphics.Bitmap +import android.graphics.Canvas +import android.graphics.Matrix +import org.catrobat.paintroid.command.Command +import org.catrobat.paintroid.contract.LayerContracts + +class RotateCommand(rotateDirection: RotateDirection) : Command { + + var rotateDirection = rotateDirection; private set + + companion object { + private const val ANGLE = 90f + } + + override fun run(canvas: Canvas, layerModel: LayerContracts.Model) { + val rotateMatrix = Matrix().apply { + when (rotateDirection) { + RotateDirection.ROTATE_RIGHT -> postRotate(ANGLE) + RotateDirection.ROTATE_LEFT -> postRotate(-ANGLE) + } + } + val iterator: Iterator = layerModel.listIterator(0) + while (iterator.hasNext()) { + val currentLayer = iterator.next() + val rotatedBitmap = Bitmap.createBitmap( + currentLayer.bitmap, 0, 0, + layerModel.width, layerModel.height, rotateMatrix, true + ) + currentLayer.bitmap = rotatedBitmap + } + val tmpWidth = layerModel.width + layerModel.width = layerModel.height + layerModel.height = tmpWidth + } + + override fun freeResources() { + // No resources to free + } + + enum class RotateDirection { + ROTATE_LEFT, ROTATE_RIGHT + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/command/implementation/SelectLayerCommand.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/command/implementation/SelectLayerCommand.kt new file mode 100644 index 00000000..d476ad10 --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/command/implementation/SelectLayerCommand.kt @@ -0,0 +1,37 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.catrobat.paintroid.command.implementation + +import android.graphics.Canvas +import org.catrobat.paintroid.command.Command +import org.catrobat.paintroid.contract.LayerContracts + +class SelectLayerCommand(position: Int) : Command { + + var position = position; private set + + override fun run(canvas: Canvas, layerModel: LayerContracts.Model) { + layerModel.currentLayer = layerModel.layers[position] + } + + override fun freeResources() { + // No resources to free + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/command/implementation/SetDimensionCommand.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/command/implementation/SetDimensionCommand.kt new file mode 100644 index 00000000..be8ee3c0 --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/command/implementation/SetDimensionCommand.kt @@ -0,0 +1,39 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.catrobat.paintroid.command.implementation + +import android.graphics.Canvas +import org.catrobat.paintroid.command.Command +import org.catrobat.paintroid.contract.LayerContracts + +class SetDimensionCommand(width: Int, height: Int) : Command { + + var width = width; private set + var height = height; private set + + override fun run(canvas: Canvas, layerModel: LayerContracts.Model) { + layerModel.width = width + layerModel.height = height + } + + override fun freeResources() { + // No resources to free + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/command/implementation/SmudgePathCommand.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/command/implementation/SmudgePathCommand.kt new file mode 100644 index 00000000..0f96452c --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/command/implementation/SmudgePathCommand.kt @@ -0,0 +1,79 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2015 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.catrobat.paintroid.command.implementation + +import android.graphics.Bitmap +import android.graphics.PointF +import android.graphics.Canvas +import android.graphics.ColorMatrix +import android.graphics.Paint +import android.graphics.ColorMatrixColorFilter +import android.graphics.RectF +import org.catrobat.paintroid.command.Command +import org.catrobat.paintroid.contract.LayerContracts +import org.catrobat.paintroid.tools.implementation.PRESSURE_UPDATE_STEP + +class SmudgePathCommand(bitmap: Bitmap, pointPath: MutableList, maxPressure: Float, maxSize: Float, minSize: Float) : Command { + + var originalBitmap = bitmap; private set + var pointPath = pointPath; private set + var maxPressure = maxPressure; private set + var maxSize = maxSize; private set + var minSize = minSize; private set + + override fun run(canvas: Canvas, layerModel: LayerContracts.Model) { + val step = (maxSize - minSize) / pointPath.size + var size = maxSize + var pressure = maxPressure + val colorMatrix = ColorMatrix() + val paint = Paint() + var bitmap = originalBitmap.copy(Bitmap.Config.ARGB_8888, false) + + pointPath.forEach { + colorMatrix.setScale(1f, 1f, 1f, pressure) + paint.colorFilter = ColorMatrixColorFilter(colorMatrix) + + val newBitmap = Bitmap.createBitmap(maxSize.toInt(), maxSize.toInt(), Bitmap.Config.ARGB_8888) + + newBitmap.let { + Canvas(it).apply { + drawBitmap(bitmap, 0f, 0f, paint) + } + } + + bitmap.recycle() + bitmap = newBitmap + + val rect = RectF(-size / 2f, -size / 2f, size / 2f, size / 2f) + with(canvas) { + save() + translate(it.x, it.y) + drawBitmap(bitmap, null, rect, Paint(Paint.DITHER_FLAG)) + restore() + } + size -= step + pressure -= PRESSURE_UPDATE_STEP + } + } + + override fun freeResources() { + originalBitmap.recycle() + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/command/implementation/SprayCommand.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/command/implementation/SprayCommand.kt new file mode 100644 index 00000000..711db0fd --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/command/implementation/SprayCommand.kt @@ -0,0 +1,38 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.command.implementation + +import android.graphics.Canvas +import android.graphics.Paint +import org.catrobat.paintroid.command.Command +import org.catrobat.paintroid.contract.LayerContracts + +class SprayCommand( + val sprayedPoints: FloatArray, + val paint: Paint +) : Command { + + override fun run(canvas: Canvas, layerModel: LayerContracts.Model) { + canvas.drawPoints(sprayedPoints, paint) + } + + override fun freeResources() { + // nothing to free + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/command/implementation/TextToolCommand.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/command/implementation/TextToolCommand.kt new file mode 100644 index 00000000..713420f8 --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/command/implementation/TextToolCommand.kt @@ -0,0 +1,92 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.catrobat.paintroid.command.implementation + +import android.graphics.Canvas +import android.graphics.Paint +import android.graphics.PointF +import org.catrobat.paintroid.command.Command +import org.catrobat.paintroid.command.serialization.SerializableTypeface +import org.catrobat.paintroid.common.ITALIC_FONT_BOX_ADJUSTMENT +import org.catrobat.paintroid.contract.LayerContracts + +class TextToolCommand( + multilineText: Array, + textPaint: Paint, + boxOffset: Float, + boxWidth: Float, + boxHeight: Float, + toolPosition: PointF, + rotationAngle: Float, + typeFaceInfo: SerializableTypeface +) : Command { + + var multilineText = multilineText.clone(); private set + var textPaint = textPaint; private set + var boxOffset = boxOffset; private set + var boxWidth = boxWidth; private set + var boxHeight = boxHeight; private set + var toolPosition = toolPosition; private set + var rotationAngle = rotationAngle; private set + var typeFaceInfo = typeFaceInfo; private set + + override fun run(canvas: Canvas, layerModel: LayerContracts.Model) { + val textAscent = textPaint.ascent() + val textDescent = textPaint.descent() + val textHeight = (textDescent - textAscent) * multilineText.size + val lineHeight = textHeight / multilineText.size + var maxTextWidth = multilineText.maxOf { line -> + textPaint.measureText(line) + } + + if (typeFaceInfo.italic) { + maxTextWidth *= ITALIC_FONT_BOX_ADJUSTMENT + } + + with(canvas) { + save() + translate(toolPosition.x, toolPosition.y) + rotate(rotationAngle) + + val widthScaling = (boxWidth - 2 * boxOffset) / maxTextWidth + val heightScaling = (boxHeight - 2 * boxOffset) / textHeight + canvas.scale(widthScaling, heightScaling) + + val scaledHeightOffset = boxOffset / heightScaling + val scaledWidthOffset = boxOffset / widthScaling + val scaledBoxWidth = boxWidth / widthScaling + val scaledBoxHeight = boxHeight / heightScaling + + multilineText.forEachIndexed { index, textLine -> + canvas.drawText( + textLine, + scaledWidthOffset - scaledBoxWidth / 2 / if (typeFaceInfo.italic) ITALIC_FONT_BOX_ADJUSTMENT else 1f, + -(scaledBoxHeight / 2) + scaledHeightOffset - textAscent + lineHeight * index, + textPaint + ) + } + restore() + } + } + + override fun freeResources() { + // No resources to free + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/AddLayerCommandSerializer.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/AddLayerCommandSerializer.kt new file mode 100644 index 00000000..7574d019 --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/AddLayerCommandSerializer.kt @@ -0,0 +1,37 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.command.serialization + +import com.esotericsoftware.kryo.Kryo +import com.esotericsoftware.kryo.io.Input +import com.esotericsoftware.kryo.io.Output +import org.catrobat.paintroid.command.implementation.AddEmptyLayerCommand +import org.catrobat.paintroid.common.CommonFactory + +class AddLayerCommandSerializer(version: Int) : VersionSerializer(version) { + override fun write(kryo: Kryo, output: Output, command: AddEmptyLayerCommand) { + // Has no member variables to save + } + + override fun read(kryo: Kryo, input: Input, type: Class): AddEmptyLayerCommand = + super.handleVersions(this, kryo, input, type) + + override fun readCurrentVersion(kryo: Kryo, input: Input, type: Class): AddEmptyLayerCommand = + AddEmptyLayerCommand(CommonFactory()) +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/BitmapSerializer.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/BitmapSerializer.kt new file mode 100644 index 00000000..750f6bd6 --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/BitmapSerializer.kt @@ -0,0 +1,39 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.command.serialization + +import android.graphics.Bitmap +import android.graphics.BitmapFactory +import com.esotericsoftware.kryo.Kryo +import com.esotericsoftware.kryo.io.Input +import com.esotericsoftware.kryo.io.Output + +const val BITMAP_SERIALIZATION_COMPRESSION_QUALITY = 100 + +class BitmapSerializer(version: Int) : VersionSerializer(version) { + override fun write(kryo: Kryo, output: Output, bitmap: Bitmap) { + bitmap.compress(Bitmap.CompressFormat.PNG, BITMAP_SERIALIZATION_COMPRESSION_QUALITY, output) + } + + override fun read(kryo: Kryo, input: Input, type: Class): Bitmap = + super.handleVersions(this, kryo, input, type) + + override fun readCurrentVersion(kryo: Kryo, input: Input, type: Class): Bitmap = + BitmapFactory.decodeStream(input) +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/ClipboardCommandSerializer.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/ClipboardCommandSerializer.kt new file mode 100644 index 00000000..59711007 --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/ClipboardCommandSerializer.kt @@ -0,0 +1,55 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.command.serialization + +import android.graphics.Bitmap +import android.graphics.BitmapFactory +import android.graphics.Point +import com.esotericsoftware.kryo.Kryo +import com.esotericsoftware.kryo.KryoException +import com.esotericsoftware.kryo.io.Input +import com.esotericsoftware.kryo.io.Output +import org.catrobat.paintroid.command.implementation.ClipboardCommand + +class ClipboardCommandSerializer(version: Int) : VersionSerializer(version) { + + companion object { + private const val COMPRESSION_QUALITY = 100 + } + + override fun write(kryo: Kryo, output: Output, command: ClipboardCommand) { + + } + + override fun read(kryo: Kryo, input: Input, type: Class): ClipboardCommand = + super.handleVersions(this, kryo, input, type) + + override fun readCurrentVersion(kryo: Kryo, input: Input, type: Class): ClipboardCommand { + return with(kryo) { + with(input) { + val bitmap = BitmapFactory.decodeStream(input) + val coordinates = readObject(input, Point::class.java) + val width = readFloat() + val height = readFloat() + val rotation = readFloat() + ClipboardCommand(bitmap, coordinates, width, height, rotation) + } + } + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/ClippingCommandSerializer.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/ClippingCommandSerializer.kt new file mode 100644 index 00000000..e6f4ab26 --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/ClippingCommandSerializer.kt @@ -0,0 +1,27 @@ +package org.catrobat.paintroid.command.serialization + +import android.graphics.Bitmap +import com.esotericsoftware.kryo.Kryo +import com.esotericsoftware.kryo.io.Input +import com.esotericsoftware.kryo.io.Output +import org.catrobat.paintroid.command.implementation.ClippingCommand + +class ClippingCommandSerializer(version: Int) : VersionSerializer(version) { + override fun write(kryo: Kryo, output: Output, command: ClippingCommand) { + with(kryo) { + writeObject(output, command.bitmap) + writeObject(output, command.pathBitmap) + } + } + + override fun read(kryo: Kryo, input: Input, type: Class): ClippingCommand = + super.handleVersions(this, kryo, input, type) + + override fun readCurrentVersion(kryo: Kryo, input: Input, type: Class): ClippingCommand { + return with(kryo) { + val bitmap = readObject(input, Bitmap::class.java) + val pathBitmap = readObject(input, Bitmap::class.java) + ClippingCommand(bitmap, pathBitmap) + } + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/ColorHistorySerializer.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/ColorHistorySerializer.kt new file mode 100644 index 00000000..d6d81e37 --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/ColorHistorySerializer.kt @@ -0,0 +1,50 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.command.serialization + +import com.esotericsoftware.kryo.Kryo +import com.esotericsoftware.kryo.io.Input +import com.esotericsoftware.kryo.io.Output +import org.catrobat.paintroid.colorpicker.ColorHistory + +class ColorHistorySerializer(version: Int) : VersionSerializer(version) { + override fun write(kryo: Kryo, output: Output, colorHistory: ColorHistory) { + with(output) { + val colors = colorHistory.colors + writeInt(colors.size) + colors.forEach { intValue -> + writeInt(intValue) + } + } + } + + override fun read(kryo: Kryo, input: Input, type: Class): ColorHistory = + super.handleVersions(this, kryo, input, type) + + override fun readCurrentVersion(kryo: Kryo, input: Input, type: Class): ColorHistory { + return with(input) { + val size = readInt() + val colorHistory = ColorHistory() + repeat(size) { + colorHistory.addColor(readInt()) + } + colorHistory + } + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/CommandManagerModelSerializer.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/CommandManagerModelSerializer.kt new file mode 100644 index 00000000..65326c65 --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/CommandManagerModelSerializer.kt @@ -0,0 +1,52 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.command.serialization + +import com.esotericsoftware.kryo.Kryo +import com.esotericsoftware.kryo.io.Input +import com.esotericsoftware.kryo.io.Output +import org.catrobat.paintroid.command.Command +import org.catrobat.paintroid.model.CommandManagerModel + +class CommandManagerModelSerializer(version: Int) : VersionSerializer(version) { + override fun write(kryo: Kryo, output: Output, model: CommandManagerModel) { + with(kryo) { + writeClassAndObject(output, model.initialCommand) + output.writeInt(model.commands.size) + model.commands.forEach { command -> + writeClassAndObject(output, command) + } + } + } + + override fun read(kryo: Kryo, input: Input, type: Class): CommandManagerModel = + super.handleVersions(this, kryo, input, type) + + override fun readCurrentVersion(kryo: Kryo, input: Input, type: Class): CommandManagerModel { + return with(kryo) { + val initCommand = kryo.readClassAndObject(input) as Command + val size = input.readInt() + val commandList = ArrayList() + repeat(size) { + commandList.add(kryo.readClassAndObject(input) as Command) + } + CommandManagerModel(initCommand, commandList) + } + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/CompositeCommandSerializer.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/CompositeCommandSerializer.kt new file mode 100644 index 00000000..74c69450 --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/CompositeCommandSerializer.kt @@ -0,0 +1,46 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.command.serialization + +import com.esotericsoftware.kryo.Kryo +import com.esotericsoftware.kryo.io.Input +import com.esotericsoftware.kryo.io.Output +import org.catrobat.paintroid.command.Command +import org.catrobat.paintroid.command.implementation.CompositeCommand + +class CompositeCommandSerializer(version: Int) : VersionSerializer(version) { + override fun write(kryo: Kryo, output: Output, command: CompositeCommand) { + output.writeInt(command.commands.size) + command.commands.forEach { cmd -> + kryo.writeClassAndObject(output, cmd) + } + } + + override fun read(kryo: Kryo, input: Input, type: Class): CompositeCommand = + super.handleVersions(this, kryo, input, type) + + override fun readCurrentVersion(kryo: Kryo, input: Input, type: Class): CompositeCommand { + val size = input.readInt() + return CompositeCommand().apply { + repeat(size) { + addCommand(kryo.readClassAndObject(input) as Command) + } + } + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/CropCommandSerializer.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/CropCommandSerializer.kt new file mode 100644 index 00000000..4ce7d19a --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/CropCommandSerializer.kt @@ -0,0 +1,50 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.command.serialization + +import com.esotericsoftware.kryo.Kryo +import com.esotericsoftware.kryo.io.Input +import com.esotericsoftware.kryo.io.Output +import org.catrobat.paintroid.command.implementation.CropCommand + +class CropCommandSerializer(version: Int) : VersionSerializer(version) { + override fun write(kryo: Kryo, output: Output, command: CropCommand) { + with(output) { + writeInt(command.resizeCoordinateXLeft) + writeInt(command.resizeCoordinateYTop) + writeInt(command.resizeCoordinateXRight) + writeInt(command.resizeCoordinateYBottom) + writeInt(command.maximumBitmapResolution) + } + } + + override fun read(kryo: Kryo, input: Input, type: Class): CropCommand = + super.handleVersions(this, kryo, input, type) + + override fun readCurrentVersion(kryo: Kryo, input: Input, type: Class): CropCommand { + return with(input) { + val coordinateXLeft = readInt() + val coordinateYTop = readInt() + val coordinateXRight = readInt() + val coordinateYBottom = readInt() + val maxResolution = readInt() + CropCommand(coordinateXLeft, coordinateYTop, coordinateXRight, coordinateYBottom, maxResolution) + } + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/CutCommandSerializer.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/CutCommandSerializer.kt new file mode 100644 index 00000000..1df7fea8 --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/CutCommandSerializer.kt @@ -0,0 +1,49 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.command.serialization + +import android.graphics.Point +import com.esotericsoftware.kryo.Kryo +import com.esotericsoftware.kryo.io.Input +import com.esotericsoftware.kryo.io.Output +import org.catrobat.paintroid.command.implementation.CutCommand + +class CutCommandSerializer(version: Int) : VersionSerializer(version) { + override fun write(kryo: Kryo, output: Output, command: CutCommand) { + kryo.writeObject(output, command.toolPosition) + with(output) { + writeFloat(command.boxWidth) + writeFloat(command.boxHeight) + writeFloat(command.boxRotation) + } + } + + override fun read(kryo: Kryo, input: Input, type: Class): CutCommand = + super.handleVersions(this, kryo, input, type) + + override fun readCurrentVersion(kryo: Kryo, input: Input, type: Class): CutCommand { + val position = kryo.readObject(input, Point::class.java) + return with(input) { + val width = readFloat() + val height = readFloat() + val rotation = readFloat() + CutCommand(position, width, height, rotation) + } + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/DataStructuresSerializer.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/DataStructuresSerializer.kt new file mode 100644 index 00000000..2886c436 --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/DataStructuresSerializer.kt @@ -0,0 +1,134 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.command.serialization + +import android.graphics.Point +import android.graphics.PointF +import android.graphics.RectF +import com.esotericsoftware.kryo.Kryo +import com.esotericsoftware.kryo.io.Input +import com.esotericsoftware.kryo.io.Output + +class DataStructuresSerializer { + class FloatArraySerializer(version: Int) : VersionSerializer(version) { + override fun write(kryo: Kryo, output: Output, array: FloatArray) { + with(output) { + writeInt(array.size) + array.forEach { floatValue -> + writeFloat(floatValue) + } + } + } + + override fun read(kryo: Kryo, input: Input, type: Class): FloatArray = + super.handleVersions(this, kryo, input, type) + + override fun readCurrentVersion(kryo: Kryo, input: Input, type: Class): FloatArray { + return with(input) { + val size = readInt() + val floatList = ArrayList() + repeat(size) { + floatList.add(readFloat()) + } + floatList.toFloatArray() + } + } + } + + class StringArraySerializer(version: Int) : VersionSerializer>(version) { + override fun write(kryo: Kryo, output: Output, array: Array) { + with(output) { + writeInt(array.size) + array.forEach { str -> + writeString(str) + } + } + } + + override fun read(kryo: Kryo, input: Input, type: Class>): Array = + super.handleVersions(this, kryo, input, type) + + override fun readCurrentVersion(kryo: Kryo, input: Input, type: Class>): Array { + return with(input) { + val size = readInt() + val strList = ArrayList() + repeat(size) { + strList.add(readString()) + } + strList.toTypedArray() + } + } + } + + class RectFSerializer(version: Int) : VersionSerializer(version) { + override fun write(kryo: Kryo, output: Output, rect: RectF) { + with(output) { + writeFloat(rect.left) + writeFloat(rect.top) + writeFloat(rect.right) + writeFloat(rect.bottom) + } + } + + override fun read(kryo: Kryo, input: Input, type: Class): RectF = + super.handleVersions(this, kryo, input, type) + + override fun readCurrentVersion(kryo: Kryo, input: Input, type: Class): RectF { + return with(input) { + RectF(readFloat(), readFloat(), readFloat(), readFloat()) + } + } + } + + class PointFSerializer(version: Int) : VersionSerializer(version) { + override fun write(kryo: Kryo, output: Output, rect: PointF) { + with(output) { + writeFloat(rect.x) + writeFloat(rect.y) + } + } + + override fun read(kryo: Kryo, input: Input, type: Class): PointF = + super.handleVersions(this, kryo, input, type) + + override fun readCurrentVersion(kryo: Kryo, input: Input, type: Class): PointF { + return with(input) { + PointF(readFloat(), readFloat()) + } + } + } + + class PointSerializer(version: Int) : VersionSerializer(version) { + override fun write(kryo: Kryo, output: Output, rect: Point) { + with(output) { + writeInt(rect.x) + writeInt(rect.y) + } + } + + override fun read(kryo: Kryo, input: Input, type: Class): Point = + super.handleVersions(this, kryo, input, type) + + override fun readCurrentVersion(kryo: Kryo, input: Input, type: Class): Point { + return with(input) { + Point(readInt(), readInt()) + } + } + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/FillCommandSerializer.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/FillCommandSerializer.kt new file mode 100644 index 00000000..42aa02b0 --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/FillCommandSerializer.kt @@ -0,0 +1,49 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.command.serialization + +import android.graphics.Paint +import android.graphics.Point +import com.esotericsoftware.kryo.Kryo +import com.esotericsoftware.kryo.io.Input +import com.esotericsoftware.kryo.io.Output +import org.catrobat.paintroid.command.implementation.FillCommand +import org.catrobat.paintroid.tools.helper.JavaFillAlgorithmFactory + +class FillCommandSerializer(version: Int) : VersionSerializer(version) { + override fun write(kryo: Kryo, output: Output, command: FillCommand) { + output.writeFloat(command.colorTolerance) + with(kryo) { + writeObject(output, command.clickedPixel) + writeObject(output, command.paint) + } + } + + override fun read(kryo: Kryo, input: Input, type: Class): FillCommand = + super.handleVersions(this, kryo, input, type) + + override fun readCurrentVersion(kryo: Kryo, input: Input, type: Class): FillCommand { + val tolerance = input.readFloat() + return with(kryo) { + val pixel = readObject(input, Point::class.java) + val paint = readObject(input, Paint::class.java) + FillCommand(JavaFillAlgorithmFactory(), pixel, paint, tolerance) + } + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/FlipCommandSerializer.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/FlipCommandSerializer.kt new file mode 100644 index 00000000..475e7152 --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/FlipCommandSerializer.kt @@ -0,0 +1,38 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.command.serialization + +import com.esotericsoftware.kryo.Kryo +import com.esotericsoftware.kryo.io.Input +import com.esotericsoftware.kryo.io.Output +import org.catrobat.paintroid.command.implementation.FlipCommand + +class FlipCommandSerializer(version: Int) : VersionSerializer(version) { + override fun write(kryo: Kryo, output: Output, command: FlipCommand) { + output.writeInt(command.flipDirection.ordinal) + } + + override fun read(kryo: Kryo, input: Input, type: Class): FlipCommand = + super.handleVersions(this, kryo, input, type) + + override fun readCurrentVersion(kryo: Kryo, input: Input, type: Class): FlipCommand { + val flipDirection = FlipCommand.FlipDirection.values()[input.readInt()] + return FlipCommand(flipDirection) + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/GeometricFillCommandSerializer.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/GeometricFillCommandSerializer.kt new file mode 100644 index 00000000..2f3334a5 --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/GeometricFillCommandSerializer.kt @@ -0,0 +1,111 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.command.serialization + +import android.graphics.Paint +import android.graphics.RectF +import com.esotericsoftware.kryo.Kryo +import com.esotericsoftware.kryo.io.Input +import com.esotericsoftware.kryo.io.Output +import org.catrobat.paintroid.command.implementation.GeometricFillCommand +import org.catrobat.paintroid.tools.drawable.HeartDrawable +import org.catrobat.paintroid.tools.drawable.OvalDrawable +import org.catrobat.paintroid.tools.drawable.RectangleDrawable +import org.catrobat.paintroid.tools.drawable.ShapeDrawable +import org.catrobat.paintroid.tools.drawable.StarDrawable + +class GeometricFillCommandSerializer(version: Int) : VersionSerializer(version) { + override fun write(kryo: Kryo, output: Output, command: GeometricFillCommand) { + with(kryo) { + with(output) { + writeClassAndObject(output, command.shapeDrawable) + writeInt(command.pointX) + writeInt(command.pointY) + writeObject(output, command.boxRect) + writeFloat(command.boxRotation) + writeObject(output, command.paint) + } + } + } + + override fun read(kryo: Kryo, input: Input, type: Class): GeometricFillCommand = + super.handleVersions(this, kryo, input, type) + + override fun readCurrentVersion(kryo: Kryo, input: Input, type: Class): GeometricFillCommand { + return with(kryo) { + with(input) { + val shape = readClassAndObject(input) as ShapeDrawable + val pointX = readInt() + val pointY = readInt() + val rect = readObject(input, RectF::class.java) + val rotation = readFloat() + val paint = readObject(input, Paint::class.java) + GeometricFillCommand(shape, pointX, pointY, rect, rotation, paint) + } + } + } + + class HeartDrawableSerializer(version: Int) : VersionSerializer(version) { + override fun write(kryo: Kryo, output: Output, command: HeartDrawable) { + // Has no member variables to save + } + + override fun read(kryo: Kryo, input: Input, type: Class): HeartDrawable = + super.handleVersions(this, kryo, input, type) + + override fun readCurrentVersion(kryo: Kryo, input: Input, type: Class): HeartDrawable = + HeartDrawable() + } + + class OvalDrawableSerializer(version: Int) : VersionSerializer(version) { + override fun write(kryo: Kryo, output: Output, command: OvalDrawable) { + // Has no member variables to save + } + + override fun read(kryo: Kryo, input: Input, type: Class): OvalDrawable = + super.handleVersions(this, kryo, input, type) + + override fun readCurrentVersion(kryo: Kryo, input: Input, type: Class): OvalDrawable = + OvalDrawable() + } + + class RectangleDrawableSerializer(version: Int) : VersionSerializer(version) { + override fun write(kryo: Kryo, output: Output, command: RectangleDrawable) { + // Has no member variables to save + } + + override fun read(kryo: Kryo, input: Input, type: Class): RectangleDrawable = + super.handleVersions(this, kryo, input, type) + + override fun readCurrentVersion(kryo: Kryo, input: Input, type: Class): RectangleDrawable = + RectangleDrawable() + } + + class StarDrawableSerializer(version: Int) : VersionSerializer(version) { + override fun write(kryo: Kryo, output: Output, command: StarDrawable) { + // Has no member variables to save + } + + override fun read(kryo: Kryo, input: Input, type: Class): StarDrawable = + super.handleVersions(this, kryo, input, type) + + override fun readCurrentVersion(kryo: Kryo, input: Input, type: Class): StarDrawable = + StarDrawable() + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/LayerOpacityCommandSerializer.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/LayerOpacityCommandSerializer.kt new file mode 100644 index 00000000..d28f4eb7 --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/LayerOpacityCommandSerializer.kt @@ -0,0 +1,44 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.command.serialization + +import com.esotericsoftware.kryo.Kryo +import com.esotericsoftware.kryo.io.Input +import com.esotericsoftware.kryo.io.Output +import org.catrobat.paintroid.command.implementation.LayerOpacityCommand + +class LayerOpacityCommandSerializer(version: Int) : VersionSerializer(version) { + override fun write(kryo: Kryo, output: Output, command: LayerOpacityCommand) { + with(output) { + writeInt(command.position) + writeInt(command.opacityPercentage) + } + } + + override fun read(kryo: Kryo, input: Input, type: Class): LayerOpacityCommand = + super.handleVersions(this, kryo, input, type) + + override fun readCurrentVersion(kryo: Kryo, input: Input, type: Class): LayerOpacityCommand { + return with(input) { + val position = readInt() + val opacityPercentage = readInt() + LayerOpacityCommand(position, opacityPercentage) + } + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/LoadCommandSerializer.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/LoadCommandSerializer.kt new file mode 100644 index 00000000..1ddba215 --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/LoadCommandSerializer.kt @@ -0,0 +1,45 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.command.serialization + +import android.graphics.Bitmap +import android.graphics.BitmapFactory +import com.esotericsoftware.kryo.Kryo +import com.esotericsoftware.kryo.io.Input +import com.esotericsoftware.kryo.io.Output +import org.catrobat.paintroid.command.implementation.LoadCommand + +class LoadCommandSerializer(version: Int) : VersionSerializer(version) { + + companion object { + private const val COMPRESSION_QUALITY = 100 + } + + override fun write(kryo: Kryo, output: Output, command: LoadCommand) { + command.loadedBitmap.compress(Bitmap.CompressFormat.PNG, COMPRESSION_QUALITY, output) + } + + override fun read(kryo: Kryo, input: Input, type: Class): LoadCommand = + super.handleVersions(this, kryo, input, type) + + override fun readCurrentVersion(kryo: Kryo, input: Input, type: Class): LoadCommand { + val bitmap = BitmapFactory.decodeStream(input) + return LoadCommand(bitmap) + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/LoadLayerListCommandSerializer.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/LoadLayerListCommandSerializer.kt new file mode 100644 index 00000000..5ce5be14 --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/LoadLayerListCommandSerializer.kt @@ -0,0 +1,69 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.command.serialization + +import android.graphics.Bitmap +import android.graphics.BitmapFactory +import com.esotericsoftware.kryo.Kryo +import com.esotericsoftware.kryo.io.Input +import com.esotericsoftware.kryo.io.Output +import org.catrobat.paintroid.command.implementation.LoadLayerListCommand +import org.catrobat.paintroid.model.Layer + +class LoadLayerListCommandSerializer(version: Int) : + VersionSerializer(version) { + + companion object { + private const val COMPRESSION_QUALITY = 100 + } + + override fun write(kryo: Kryo, output: Output, command: LoadLayerListCommand) { + output.writeInt(command.loadedLayers.size) + command.loadedLayers.forEach { layer -> + layer.bitmap.compress(Bitmap.CompressFormat.PNG, COMPRESSION_QUALITY, output) + output.writeInt(layer.opacityPercentage) + } + } + + override fun read( + kryo: Kryo, + input: Input, + type: Class + ): LoadLayerListCommand = + super.handleVersions(this, kryo, input, type) + + override fun readCurrentVersion( + kryo: Kryo, + input: Input, + type: Class + ): LoadLayerListCommand { + val size = input.readInt() + val layerList = ArrayList() + repeat(size) { + val bitmap = BitmapFactory.decodeStream(input) + val alpha = input.readInt() + val layer = Layer(bitmap).apply { + opacityPercentage = alpha + } + layerList.add(layer) + } + + return LoadLayerListCommand(layerList) + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/MergeLayersCommandSerializer.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/MergeLayersCommandSerializer.kt new file mode 100644 index 00000000..66b2514d --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/MergeLayersCommandSerializer.kt @@ -0,0 +1,44 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.command.serialization + +import com.esotericsoftware.kryo.Kryo +import com.esotericsoftware.kryo.io.Input +import com.esotericsoftware.kryo.io.Output +import org.catrobat.paintroid.command.implementation.MergeLayersCommand + +class MergeLayersCommandSerializer(version: Int) : VersionSerializer(version) { + override fun write(kryo: Kryo, output: Output, command: MergeLayersCommand) { + with(output) { + writeInt(command.position) + writeInt(command.mergeWith) + } + } + + override fun read(kryo: Kryo, input: Input, type: Class): MergeLayersCommand = + super.handleVersions(this, kryo, input, type) + + override fun readCurrentVersion(kryo: Kryo, input: Input, type: Class): MergeLayersCommand { + return with(input) { + val position = readInt() + val mergeWith = readInt() + MergeLayersCommand(position, mergeWith) + } + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/PaintSerializer.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/PaintSerializer.kt new file mode 100644 index 00000000..bf4e1e2d --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/PaintSerializer.kt @@ -0,0 +1,85 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.command.serialization + +import android.content.Context +import android.graphics.BlurMaskFilter +import android.graphics.Paint +import com.esotericsoftware.kryo.Kryo +import com.esotericsoftware.kryo.io.Input +import com.esotericsoftware.kryo.io.Output + +import org.catrobat.paintroid.tools.implementation.DefaultToolPaint +import org.catrobat.paintroid.tools.implementation.WatercolorTool +class PaintSerializer(version: Int, private val activityContext: Context) : VersionSerializer(version) { + override fun write(kryo: Kryo, output: Output, paint: Paint) { + with(output) { + writeInt(paint.color) + writeFloat(paint.strokeWidth) + writeInt(paint.strokeCap.ordinal) + writeBoolean(paint.isAntiAlias) + writeInt(paint.style.ordinal) + writeInt(paint.strokeJoin.ordinal) + writeBoolean(paint.maskFilter != null) + writeInt(paint.alpha) + } + } + + override fun read(kryo: Kryo, input: Input, type: Class): Paint = + super.handleVersions(this, kryo, input, type) + + override fun readV1(serializer: VersionSerializer, kryo: Kryo, input: Input, type: Class): Paint { + val toolPaint = DefaultToolPaint(activityContext).apply { + with(input) { + color = readInt() + strokeWidth = readFloat() + strokeCap = Paint.Cap.values()[readInt()] + } + } + + return toolPaint.paint.apply { + with(input) { + isAntiAlias = readBoolean() + style = Paint.Style.values()[readInt()] + strokeJoin = Paint.Join.values()[readInt()] + } + } + } + + override fun readCurrentVersion(kryo: Kryo, input: Input, type: Class): Paint { + val toolPaint = DefaultToolPaint(activityContext).apply { + with(input) { + color = readInt() + strokeWidth = readFloat() + strokeCap = Paint.Cap.values()[readInt()] + } + } + + return toolPaint.paint.apply { + with(input) { + isAntiAlias = readBoolean() + style = Paint.Style.values()[readInt()] + strokeJoin = Paint.Join.values()[readInt()] + val hadFilter: Boolean = input.readBoolean() + alpha = input.readInt() + if (hadFilter) maskFilter = BlurMaskFilter(WatercolorTool.calcRange(alpha), BlurMaskFilter.Blur.INNER) + } + } + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/PathCommandSerializer.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/PathCommandSerializer.kt new file mode 100644 index 00000000..9a5f8194 --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/PathCommandSerializer.kt @@ -0,0 +1,45 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.command.serialization + +import android.graphics.Paint +import com.esotericsoftware.kryo.Kryo +import com.esotericsoftware.kryo.io.Input +import com.esotericsoftware.kryo.io.Output +import org.catrobat.paintroid.command.implementation.PathCommand + +class PathCommandSerializer(version: Int) : VersionSerializer(version) { + override fun write(kryo: Kryo, output: Output, command: PathCommand) { + with(kryo) { + writeObject(output, command.paint) + writeObject(output, command.path as SerializablePath) + } + } + + override fun read(kryo: Kryo, input: Input, type: Class): PathCommand = + super.handleVersions(this, kryo, input, type) + + override fun readCurrentVersion(kryo: Kryo, input: Input, type: Class): PathCommand { + return with(kryo) { + val paint = readObject(input, Paint::class.java) + val path = readObject(input, SerializablePath::class.java) + PathCommand(Paint(paint), path) + } + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/PointCommandSerializer.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/PointCommandSerializer.kt new file mode 100644 index 00000000..3b06b23d --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/PointCommandSerializer.kt @@ -0,0 +1,46 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.command.serialization + +import android.graphics.Paint +import android.graphics.PointF +import com.esotericsoftware.kryo.Kryo +import com.esotericsoftware.kryo.io.Input +import com.esotericsoftware.kryo.io.Output +import org.catrobat.paintroid.command.implementation.PointCommand + +class PointCommandSerializer(version: Int) : VersionSerializer(version) { + override fun write(kryo: Kryo, output: Output, command: PointCommand) { + with(kryo) { + writeObject(output, command.point) + writeObject(output, command.paint) + } + } + + override fun read(kryo: Kryo, input: Input, type: Class): PointCommand = + super.handleVersions(this, kryo, input, type) + + override fun readCurrentVersion(kryo: Kryo, input: Input, type: Class): PointCommand { + return with(kryo) { + val point = readObject(input, PointF::class.java) + val paint = readObject(input, Paint::class.java) + PointCommand(paint, point) + } + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/RemoveLayerCommandSerializer.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/RemoveLayerCommandSerializer.kt new file mode 100644 index 00000000..75ebaecd --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/RemoveLayerCommandSerializer.kt @@ -0,0 +1,42 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.command.serialization + +import com.esotericsoftware.kryo.Kryo +import com.esotericsoftware.kryo.io.Input +import com.esotericsoftware.kryo.io.Output +import org.catrobat.paintroid.command.implementation.RemoveLayerCommand + +class RemoveLayerCommandSerializer(version: Int) : VersionSerializer(version) { + override fun write(kryo: Kryo, output: Output, command: RemoveLayerCommand) { + with(output) { + writeInt(command.position) + } + } + + override fun read(kryo: Kryo, input: Input, type: Class): RemoveLayerCommand = + super.handleVersions(this, kryo, input, type) + + override fun readCurrentVersion(kryo: Kryo, input: Input, type: Class): RemoveLayerCommand { + return with(input) { + val position = readInt() + RemoveLayerCommand(position) + } + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/ReorderLayersCommandSerializer.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/ReorderLayersCommandSerializer.kt new file mode 100644 index 00000000..64e0a719 --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/ReorderLayersCommandSerializer.kt @@ -0,0 +1,44 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.command.serialization + +import com.esotericsoftware.kryo.Kryo +import com.esotericsoftware.kryo.io.Input +import com.esotericsoftware.kryo.io.Output +import org.catrobat.paintroid.command.implementation.ReorderLayersCommand + +class ReorderLayersCommandSerializer(version: Int) : VersionSerializer(version) { + override fun write(kryo: Kryo, output: Output, command: ReorderLayersCommand) { + with(output) { + writeInt(command.position) + writeInt(command.destination) + } + } + + override fun read(kryo: Kryo, input: Input, type: Class): ReorderLayersCommand = + super.handleVersions(this, kryo, input, type) + + override fun readCurrentVersion(kryo: Kryo, input: Input, type: Class): ReorderLayersCommand { + return with(input) { + val position = readInt() + val destination = readInt() + ReorderLayersCommand(position, destination) + } + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/ResetCommandSerializer.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/ResetCommandSerializer.kt new file mode 100644 index 00000000..86cc8e58 --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/ResetCommandSerializer.kt @@ -0,0 +1,36 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.command.serialization + +import com.esotericsoftware.kryo.Kryo +import com.esotericsoftware.kryo.io.Input +import com.esotericsoftware.kryo.io.Output +import org.catrobat.paintroid.command.implementation.ResetCommand + +class ResetCommandSerializer(version: Int) : VersionSerializer(version) { + override fun write(kryo: Kryo, output: Output, command: ResetCommand) { + // Has no member variables to save + } + + override fun read(kryo: Kryo, input: Input, type: Class): ResetCommand = + super.handleVersions(this, kryo, input, type) + + override fun readCurrentVersion(kryo: Kryo, input: Input, type: Class): ResetCommand = + ResetCommand() +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/ResizeCommandSerializer.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/ResizeCommandSerializer.kt new file mode 100644 index 00000000..df7d04ba --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/ResizeCommandSerializer.kt @@ -0,0 +1,44 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.command.serialization + +import com.esotericsoftware.kryo.Kryo +import com.esotericsoftware.kryo.io.Input +import com.esotericsoftware.kryo.io.Output +import org.catrobat.paintroid.command.implementation.ResizeCommand + +class ResizeCommandSerializer(version: Int) : VersionSerializer(version) { + override fun write(kryo: Kryo, output: Output, command: ResizeCommand) { + with(output) { + writeInt(command.newWidth) + writeInt(command.newHeight) + } + } + + override fun read(kryo: Kryo, input: Input, type: Class): ResizeCommand = + super.handleVersions(this, kryo, input, type) + + override fun readCurrentVersion(kryo: Kryo, input: Input, type: Class): ResizeCommand { + return with(input) { + val width = readInt() + val height = readInt() + ResizeCommand(width, height) + } + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/RotateCommandSerializer.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/RotateCommandSerializer.kt new file mode 100644 index 00000000..8c80293b --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/RotateCommandSerializer.kt @@ -0,0 +1,38 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.command.serialization + +import com.esotericsoftware.kryo.Kryo +import com.esotericsoftware.kryo.io.Input +import com.esotericsoftware.kryo.io.Output +import org.catrobat.paintroid.command.implementation.RotateCommand + +class RotateCommandSerializer(version: Int) : VersionSerializer(version) { + override fun write(kryo: Kryo, output: Output, command: RotateCommand) { + output.writeInt(command.rotateDirection.ordinal) + } + + override fun read(kryo: Kryo, input: Input, type: Class): RotateCommand = + super.handleVersions(this, kryo, input, type) + + override fun readCurrentVersion(kryo: Kryo, input: Input, type: Class): RotateCommand { + val rotateDirection = RotateCommand.RotateDirection.values()[input.readInt()] + return RotateCommand(rotateDirection) + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/SelectLayerCommandSerializer.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/SelectLayerCommandSerializer.kt new file mode 100644 index 00000000..d443ce84 --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/SelectLayerCommandSerializer.kt @@ -0,0 +1,38 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.command.serialization + +import com.esotericsoftware.kryo.Kryo +import com.esotericsoftware.kryo.io.Input +import com.esotericsoftware.kryo.io.Output +import org.catrobat.paintroid.command.implementation.SelectLayerCommand + +class SelectLayerCommandSerializer(version: Int) : VersionSerializer(version) { + override fun write(kryo: Kryo, output: Output, command: SelectLayerCommand) { + output.writeInt(command.position) + } + + override fun read(kryo: Kryo, input: Input, type: Class): SelectLayerCommand = + super.handleVersions(this, kryo, input, type) + + override fun readCurrentVersion(kryo: Kryo, input: Input, type: Class): SelectLayerCommand { + val position = input.readInt() + return SelectLayerCommand(position) + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/SerializablePath.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/SerializablePath.kt new file mode 100644 index 00000000..64c86aff --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/SerializablePath.kt @@ -0,0 +1,210 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.command.serialization + +import android.graphics.Path +import com.esotericsoftware.kryo.Kryo +import com.esotericsoftware.kryo.io.Input +import com.esotericsoftware.kryo.io.Output + +open class SerializablePath : Path { + + var serializableActions = ArrayList() + + constructor() : super() + + constructor(actions: ArrayList) : super() { + actions.forEach { + it.perform(this) + } + } + + constructor(src: SerializablePath) : super(src) { + this.serializableActions.addAll(src.serializableActions) + } + + override fun moveTo(x: Float, y: Float) { + serializableActions.add(Move(x, y)) + super.moveTo(x, y) + } + + override fun lineTo(x: Float, y: Float) { + serializableActions.add(Line(x, y)) + super.lineTo(x, y) + } + + override fun quadTo(x1: Float, y1: Float, x2: Float, y2: Float) { + serializableActions.add(Quad(x1, y1, x2, y2)) + super.quadTo(x1, y1, x2, y2) + } + + override fun cubicTo(x1: Float, y1: Float, x2: Float, y2: Float, x3: Float, y3: Float) { + serializableActions.add(Cube(x1, y1, x2, y2, x3, y3)) + super.cubicTo(x1, y1, x2, y2, x3, y3) + } + + override fun rewind() { + serializableActions.clear() + super.rewind() + } + + interface SerializableAction { + fun perform(path: Path) + } + + class Move(val x: Float, val y: Float) : SerializableAction { + override fun perform(path: Path) { + path.moveTo(x, y) + } + } + + class Line(val x: Float, val y: Float) : SerializableAction { + override fun perform(path: Path) { + path.lineTo(x, y) + } + } + + class Quad(val x1: Float, val y1: Float, val x2: Float, val y2: Float) : SerializableAction { + override fun perform(path: Path) { + path.quadTo(x1, y1, x2, y2) + } + } + + class Cube(val x1: Float, val y1: Float, val x2: Float, val y2: Float, val x3: Float, val y3: Float) : SerializableAction { + override fun perform(path: Path) { + path.cubicTo(x1, y1, x2, y2, x3, y3) + } + } + + class Rewind : SerializableAction { + override fun perform(path: Path) { + path.rewind() + } + } + + class PathSerializer(version: Int) : VersionSerializer(version) { + override fun write(kryo: Kryo, output: Output, path: SerializablePath) { + output.writeInt(path.serializableActions.size) + path.serializableActions.forEach { action -> + kryo.writeClassAndObject(output, action) + } + } + + override fun read(kryo: Kryo, input: Input, type: Class): SerializablePath = + super.handleVersions(this, kryo, input, type) + + override fun readCurrentVersion(kryo: Kryo, input: Input, type: Class): SerializablePath { + val size = input.readInt() + val actionList = ArrayList() + repeat(size) { + actionList.add(kryo.readClassAndObject(input) as SerializableAction) + } + return SerializablePath(actionList) + } + } + + class PathActionMoveSerializer(version: Int) : VersionSerializer(version) { + override fun write(kryo: Kryo, output: Output, action: Move) { + with(output) { + writeFloat(action.x) + writeFloat(action.y) + } + } + + override fun read(kryo: Kryo, input: Input, type: Class): Move = + super.handleVersions(this, kryo, input, type) + + override fun readCurrentVersion(kryo: Kryo, input: Input, type: Class): Move { + return with(input) { + Move(readFloat(), readFloat()) + } + } + } + + class PathActionLineSerializer(version: Int) : VersionSerializer(version) { + override fun write(kryo: Kryo, output: Output, action: Line) { + with(output) { + writeFloat(action.x) + writeFloat(action.y) + } + } + + override fun read(kryo: Kryo, input: Input, type: Class): Line = + super.handleVersions(this, kryo, input, type) + + override fun readCurrentVersion(kryo: Kryo, input: Input, type: Class): Line { + return with(input) { + Line(readFloat(), readFloat()) + } + } + } + + class PathActionQuadSerializer(version: Int) : VersionSerializer(version) { + override fun write(kryo: Kryo, output: Output, action: Quad) { + with(output) { + writeFloat(action.x1) + writeFloat(action.y1) + writeFloat(action.x2) + writeFloat(action.y2) + } + } + + override fun read(kryo: Kryo, input: Input, type: Class): Quad = + super.handleVersions(this, kryo, input, type) + + override fun readCurrentVersion(kryo: Kryo, input: Input, type: Class): Quad { + return with(input) { + Quad(readFloat(), readFloat(), readFloat(), readFloat()) + } + } + } + + class PathActionCubeSerializer(version: Int) : VersionSerializer(version) { + override fun write(kryo: Kryo, output: Output, action: Cube) { + with(output) { + writeFloat(action.x1) + writeFloat(action.y1) + writeFloat(action.x2) + writeFloat(action.y2) + writeFloat(action.x3) + writeFloat(action.y3) + } + } + + override fun read(kryo: Kryo, input: Input, type: Class): Cube = + super.handleVersions(this, kryo, input, type) + + override fun readCurrentVersion(kryo: Kryo, input: Input, type: Class) = + with(input) { + Cube(readFloat(), readFloat(), readFloat(), readFloat(), readFloat(), readFloat()) + } + } + + class PathActionRewindSerializer(version: Int) : VersionSerializer(version) { + override fun write(kryo: Kryo, output: Output, action: Rewind) { + // Has no member variables to save + } + + override fun read(kryo: Kryo, input: Input, type: Class): Rewind = + super.handleVersions(this, kryo, input, type) + + override fun readCurrentVersion(kryo: Kryo, input: Input, type: Class): Rewind = + Rewind() + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/SerializableTypeface.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/SerializableTypeface.kt new file mode 100644 index 00000000..1aea05d5 --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/SerializableTypeface.kt @@ -0,0 +1,49 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.command.serialization + +import com.esotericsoftware.kryo.Kryo +import com.esotericsoftware.kryo.io.Input +import com.esotericsoftware.kryo.io.Output +import org.catrobat.paintroid.tools.FontType + +data class SerializableTypeface(val font: FontType, val bold: Boolean, val underline: Boolean, val italic: Boolean, val textSize: Float, val textSkewX: Float) { + + class TypefaceSerializer(version: Int) : VersionSerializer(version) { + override fun write(kryo: Kryo, output: Output, typeface: SerializableTypeface) { + with(output) { + writeString(typeface.font.name) + writeBoolean(typeface.bold) + writeBoolean(typeface.underline) + writeBoolean(typeface.italic) + writeFloat(typeface.textSize) + writeFloat(typeface.textSkewX) + } + } + + override fun read(kryo: Kryo, input: Input, type: Class): SerializableTypeface = + super.handleVersions(this, kryo, input, type) + + override fun readCurrentVersion(kryo: Kryo, input: Input, type: Class): SerializableTypeface { + return with(input) { + SerializableTypeface(FontType.valueOf(readString()), readBoolean(), readBoolean(), readBoolean(), readFloat(), readFloat()) + } + } + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/SetDimensionCommandSerializer.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/SetDimensionCommandSerializer.kt new file mode 100644 index 00000000..d967c4a2 --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/SetDimensionCommandSerializer.kt @@ -0,0 +1,44 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.command.serialization + +import com.esotericsoftware.kryo.Kryo +import com.esotericsoftware.kryo.io.Input +import com.esotericsoftware.kryo.io.Output +import org.catrobat.paintroid.command.implementation.SetDimensionCommand + +class SetDimensionCommandSerializer(version: Int) : VersionSerializer(version) { + override fun write(kryo: Kryo, output: Output, command: SetDimensionCommand) { + with(output) { + writeInt(command.width) + writeInt(command.height) + } + } + + override fun read(kryo: Kryo, input: Input, type: Class): SetDimensionCommand = + super.handleVersions(this, kryo, input, type) + + override fun readCurrentVersion(kryo: Kryo, input: Input, type: Class): SetDimensionCommand { + return with(input) { + val width = readInt() + val height = readInt() + SetDimensionCommand(width, height) + } + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/SmudgePathCommandSerializer.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/SmudgePathCommandSerializer.kt new file mode 100644 index 00000000..59cdb7d8 --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/SmudgePathCommandSerializer.kt @@ -0,0 +1,69 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.command.serialization + +import android.graphics.Bitmap +import android.graphics.BitmapFactory +import android.graphics.PointF +import com.esotericsoftware.kryo.Kryo +import com.esotericsoftware.kryo.io.Input +import com.esotericsoftware.kryo.io.Output +import org.catrobat.paintroid.command.implementation.SmudgePathCommand + +class SmudgePathCommandSerializer(version: Int) : VersionSerializer(version) { + + companion object { + private const val COMPRESSION_QUALITY = 100 + } + + override fun write(kryo: Kryo, output: Output, command: SmudgePathCommand) { + with(kryo) { + with(output) { + command.originalBitmap.compress(Bitmap.CompressFormat.PNG, COMPRESSION_QUALITY, output) + writeInt(command.pointPath.size) + command.pointPath.forEach { + writeObject(output, it) + } + writeFloat(command.maxPressure) + writeFloat(command.maxSize) + writeFloat(command.minSize) + } + } + } + + override fun read(kryo: Kryo, input: Input, type: Class): SmudgePathCommand = + super.handleVersions(this, kryo, input, type) + + override fun readCurrentVersion(kryo: Kryo, input: Input, type: Class): SmudgePathCommand { + return with(kryo) { + with(input) { + val originalBitmap = BitmapFactory.decodeStream(input) + val pointPath = mutableListOf() + val size = readInt() + repeat(size) { + pointPath.add(readObject(input, PointF::class.java)) + } + val maxPressure = readFloat() + val maxSize = readFloat() + val minSize = readFloat() + SmudgePathCommand(originalBitmap, pointPath, maxPressure, maxSize, minSize) + } + } + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/SprayCommandSerializer.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/SprayCommandSerializer.kt new file mode 100644 index 00000000..3d85125c --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/SprayCommandSerializer.kt @@ -0,0 +1,45 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.command.serialization + +import android.graphics.Paint +import com.esotericsoftware.kryo.Kryo +import com.esotericsoftware.kryo.io.Input +import com.esotericsoftware.kryo.io.Output +import org.catrobat.paintroid.command.implementation.SprayCommand + +class SprayCommandSerializer(version: Int) : VersionSerializer(version) { + override fun write(kryo: Kryo, output: Output, command: SprayCommand) { + with(kryo) { + writeObject(output, command.sprayedPoints) + writeObject(output, command.paint) + } + } + + override fun read(kryo: Kryo, input: Input, type: Class): SprayCommand = + super.handleVersions(this, kryo, input, type) + + override fun readCurrentVersion(kryo: Kryo, input: Input, type: Class): SprayCommand { + return with(kryo) { + val sprayPoints = kryo.readObject(input, FloatArray::class.java) + val paint = kryo.readObject(input, Paint::class.java) + SprayCommand(sprayPoints, paint) + } + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/TextToolCommandSerializer.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/TextToolCommandSerializer.kt new file mode 100644 index 00000000..466ca5b5 --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/TextToolCommandSerializer.kt @@ -0,0 +1,87 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.command.serialization + +import android.content.Context +import android.graphics.Paint +import android.graphics.PointF +import android.graphics.Typeface +import android.util.Log +import androidx.core.content.res.ResourcesCompat +import com.esotericsoftware.kryo.Kryo +import com.esotericsoftware.kryo.io.Input +import com.esotericsoftware.kryo.io.Output +import org.catrobat.paintroid.R +import org.catrobat.paintroid.command.implementation.TextToolCommand +import org.catrobat.paintroid.tools.FontType + +class TextToolCommandSerializer(version: Int, private val activityContext: Context) : VersionSerializer(version) { + override fun write(kryo: Kryo, output: Output, command: TextToolCommand) { + with(kryo) { + with(output) { + writeObject(output, command.multilineText) + writeObject(output, command.textPaint) + writeFloat(command.boxOffset) + writeFloat(command.boxWidth) + writeFloat(command.boxHeight) + writeObject(output, command.toolPosition) + writeFloat(command.rotationAngle) + writeObject(output, command.typeFaceInfo) + } + } + } + + override fun read(kryo: Kryo, input: Input, type: Class): TextToolCommand = + super.handleVersions(this, kryo, input, type) + + @Suppress("Detekt.TooGenericExceptionCaught") + override fun readCurrentVersion(kryo: Kryo, input: Input, type: Class): TextToolCommand { + return with(input) { + val text = kryo.readObject(input, Array::class.java) + val paint = kryo.readObject(input, Paint::class.java) + val offset = readFloat() + val width = readFloat() + val height = readFloat() + val position = kryo.readObject(input, PointF::class.java) + val rotation = readFloat() + val typeFaceInfo = kryo.readObject(input, SerializableTypeface::class.java) + + paint.apply { + isFakeBoldText = typeFaceInfo.bold + isUnderlineText = typeFaceInfo.underline + textSize = typeFaceInfo.textSize + textSkewX = typeFaceInfo.textSkewX + val style = if (typeFaceInfo.italic) Typeface.ITALIC else Typeface.NORMAL + typeface = try { + when (typeFaceInfo.font) { + FontType.SANS_SERIF -> Typeface.create(Typeface.SANS_SERIF, style) + FontType.SERIF -> Typeface.create(Typeface.SERIF, style) + FontType.MONOSPACE -> Typeface.create(Typeface.MONOSPACE, style) + FontType.STC -> ResourcesCompat.getFont(activityContext, R.font.stc_regular) + FontType.DUBAI -> ResourcesCompat.getFont(activityContext, R.font.dubai) + } + } catch (e: Exception) { + Log.e("LoadImageAsync", "Typeface not supported on this mobile phone") + Typeface.create(Typeface.SANS_SERIF, style) + } + } + TextToolCommand(text, paint, offset, width, height, position, rotation, typeFaceInfo) + } + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/VersionSerializer.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/VersionSerializer.kt new file mode 100644 index 00000000..fac0c552 --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/command/serialization/VersionSerializer.kt @@ -0,0 +1,56 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.command.serialization + +import com.esotericsoftware.kryo.Kryo +import com.esotericsoftware.kryo.KryoException +import com.esotericsoftware.kryo.Serializer +import com.esotericsoftware.kryo.io.Input + +abstract class VersionSerializer(val version: Int) : Serializer() { + + companion object { + private const val V1 = 1 + private const val V2 = 2 + private const val V3 = 3 + } + + protected fun handleVersions(serializer: VersionSerializer, kryo: Kryo, input: Input, type: Class): T { + return when (version) { + // Currently just here to see the intended pattern + V1 -> serializer.readV1(serializer, kryo, input, type) + V2 -> serializer.readV2(serializer, kryo, input, type) + V3 -> serializer.readV3(serializer, kryo, input, type) + // Enable when CURRENT_IMAGE_VERSION reached 4 + // CommandSerializationUtilities.CURRENT_IMAGE_VERSION -> serializer.readCurrentVersion(kryo, input, type) + else -> throw KryoException() + } + } + + protected open fun readV1(serializer: VersionSerializer, kryo: Kryo, input: Input, type: Class): T = + serializer.readV2(serializer, kryo, input, type) + + protected open fun readV2(serializer: VersionSerializer, kryo: Kryo, input: Input, type: Class): T = + serializer.readV3(serializer, kryo, input, type) + + protected open fun readV3(serializer: VersionSerializer, kryo: Kryo, input: Input, type: Class): T = + serializer.readCurrentVersion(kryo, input, type) + + abstract fun readCurrentVersion(kryo: Kryo, input: Input, type: Class): T +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/common/CommonBrushChangedListener.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/common/CommonBrushChangedListener.kt new file mode 100644 index 00000000..6cb26158 --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/common/CommonBrushChangedListener.kt @@ -0,0 +1,33 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.tools.common + +import android.graphics.Paint.Cap +import org.catrobat.paintroid.tools.Tool +import org.catrobat.paintroid.tools.options.BrushToolOptionsView.OnBrushChangedListener + +class CommonBrushChangedListener(private val tool: Tool) : OnBrushChangedListener { + override fun setCap(strokeCap: Cap) { + tool.changePaintStrokeCap(strokeCap) + } + + override fun setStrokeWidth(strokeWidth: Int) { + tool.changePaintStrokeWidth(strokeWidth) + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/common/CommonBrushPreviewListener.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/common/CommonBrushPreviewListener.kt new file mode 100644 index 00000000..4143945b --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/common/CommonBrushPreviewListener.kt @@ -0,0 +1,42 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.tools.common + +import android.graphics.MaskFilter +import android.graphics.Paint.Cap +import org.catrobat.paintroid.tools.ToolPaint +import org.catrobat.paintroid.tools.ToolType +import org.catrobat.paintroid.tools.options.BrushToolOptionsView.OnBrushPreviewListener + +class CommonBrushPreviewListener( + private val toolPaint: ToolPaint, + override val toolType: ToolType +) : OnBrushPreviewListener { + override val strokeWidth: Float + get() = toolPaint.strokeWidth + + override val strokeCap: Cap + get() = toolPaint.strokeCap + + override val color: Int + get() = toolPaint.color + + override val maskFilter: MaskFilter? + get() = toolPaint.paint.maskFilter +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/common/CommonFactory.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/common/CommonFactory.kt new file mode 100644 index 00000000..cf002e94 --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/common/CommonFactory.kt @@ -0,0 +1,45 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.common + +import android.graphics.Bitmap +import android.graphics.Canvas +import android.graphics.Paint +import android.graphics.Point +import android.graphics.PointF +import android.graphics.RectF +import org.catrobat.paintroid.command.serialization.SerializablePath + +open class CommonFactory { + open fun createCanvas() = Canvas() + + open fun createBitmap(width: Int, height: Int, config: Bitmap.Config): Bitmap = + Bitmap.createBitmap(width, height, config) + + fun createPaint(paint: Paint?) = Paint(paint) + + fun createPointF(point: PointF) = PointF(point.x, point.y) + + fun createPoint(x: Int, y: Int) = Point(x, y) + + open fun createSerializablePath(path: SerializablePath): SerializablePath = + SerializablePath(path) + + fun createRectF(rect: RectF?) = RectF(rect) +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/common/Constants.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/common/Constants.kt new file mode 100644 index 00000000..19b121b8 --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/common/Constants.kt @@ -0,0 +1,70 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.common + +import android.os.Environment +import java.io.File + +const val PAINTROID_PICTURE_PATH = "org.catrobat.extra.PAINTROID_PICTURE_PATH" +const val PAINTROID_PICTURE_NAME = "org.catrobat.extra.PAINTROID_PICTURE_NAME" +const val TEMP_PICTURE_NAME = "catroidTemp" +const val MEDIA_GALLEY_URL = "https://share.catrob.at/pocketcode/media-library/looks" +const val ABOUT_DIALOG_FRAGMENT_TAG = "aboutdialogfragment" +const val LIKE_US_DIALOG_FRAGMENT_TAG = "likeusdialogfragment" +const val RATE_US_DIALOG_FRAGMENT_TAG = "rateusdialogfragment" +const val FEEDBACK_DIALOG_FRAGMENT_TAG = "feedbackdialogfragment" +const val ZOOM_WINDOW_SETTINGS_DIALOG_FRAGMENT_TAG = "zoomwindowsettingsdialogfragment" +const val ADVANCED_SETTINGS_DIALOG_FRAGMENT_TAG = "advancedsettingsdialogfragment" +const val SAVE_DIALOG_FRAGMENT_TAG = "savedialogerror" +const val LOAD_DIALOG_FRAGMENT_TAG = "loadbitmapdialogerror" +const val COLOR_PICKER_DIALOG_TAG = "ColorPickerDialogTag" +const val SAVE_QUESTION_FRAGMENT_TAG = "savebeforequitfragment" +const val SAVE_INFORMATION_DIALOG_TAG = "saveinformationdialogfragment" +const val OVERWRITE_INFORMATION_DIALOG_TAG = "saveinformationdialogfragment" +const val PNG_INFORMATION_DIALOG_TAG = "pnginformationdialogfragment" +const val JPG_INFORMATION_DIALOG_TAG = "jpginformationdialogfragment" +const val ORA_INFORMATION_DIALOG_TAG = "orainformationdialogfragment" +const val CATROBAT_INFORMATION_DIALOG_TAG = "catrobatinformationdialogfragment" +const val CATROID_MEDIA_GALLERY_FRAGMENT_TAG = "catroidmediagalleryfragment" +const val PERMISSION_DIALOG_FRAGMENT_TAG = "permissiondialogfragment" +const val SHOW_LIKE_US_DIALOG_SHARED_PREFERENCES_TAG = "showlikeusdialog" +const val ZOOM_WINDOW_ENABLED_SHARED_PREFERENCES_TAG = "zoomwindowenabled" +const val ZOOM_WINDOW_ZOOM_PERCENTAGE_SHARED_PREFERENCES_TAG = "zoomwindowzoompercentage" +const val IMAGE_NUMBER_SHARED_PREFERENCES_TAG = "imagenumbertag" +const val SCALE_IMAGE_FRAGMENT_TAG = "showscaleimagedialog" +const val INDETERMINATE_PROGRESS_DIALOG_TAG = "indeterminateprogressdialogfragment" +const val INVALID_RESOURCE_ID = 0 +const val MAX_LAYERS = 100 +const val MEGABYTE_IN_BYTE = 1_048_576L +const val MINIMUM_HEAP_SPACE_FOR_NEW_LAYER = 40 +const val CATROBAT_IMAGE_ENDING = "catrobat-image" +const val TEMP_IMAGE_DIRECTORY_NAME = "TemporaryImages" +const val TEMP_IMAGE_NAME = "temporaryImage" +const val ITALIC_FONT_BOX_ADJUSTMENT = 1.2f +const val TEMP_IMAGE_PATH = "$TEMP_IMAGE_DIRECTORY_NAME/$TEMP_IMAGE_NAME.$CATROBAT_IMAGE_ENDING" +const val TEMP_IMAGE_TEMP_PATH = "$TEMP_IMAGE_DIRECTORY_NAME/${TEMP_IMAGE_NAME}1.$CATROBAT_IMAGE_ENDING" +const val SPECIFIC_FILETYPE_SHARED_PREFERENCES_NAME = "Ownfiletypepreferences" +const val ANIMATION_DURATION: Long = 250 + +object Constants { + @JvmField + val PICTURES_DIRECTORY = File(Environment.getExternalStorageDirectory(), Environment.DIRECTORY_PICTURES) + @JvmField + val DOWNLOADS_DIRECTORY = File(Environment.getExternalStorageDirectory(), Environment.DIRECTORY_DOWNLOADS) +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/contract/LayerContracts.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/contract/LayerContracts.kt new file mode 100644 index 00000000..99a2b86e --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/contract/LayerContracts.kt @@ -0,0 +1,155 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.contract + +import android.graphics.Bitmap +import android.view.View +import android.widget.LinearLayout +import androidx.annotation.StringRes + + +interface LayerContracts { + /* interface Adapter { + fun notifyDataSetChanged() + + fun getViewHolderAt(position: Int): LayerViewHolder? + } + + interface Presenter { + val layerCount: Int + val presenter: Presenter + + fun onSelectedLayerInvisible() + + fun onSelectedLayerVisible() + + fun getListItemDragHandler(): ListItemDragHandler + + fun refreshLayerMenuViewHolder() + + fun disableVisibilityAndOpacityButtons() + + fun getLayerItem(position: Int): Layer? + + fun getLayerItemId(position: Int): Long + + fun addLayer() + + fun removeLayer() + + fun changeLayerOpacity(position: Int, opacityPercentage: Int) + + fun setLayerVisibility(position: Int, isVisible: Boolean) + + fun refreshDrawingSurface() + + fun setAdapter(layerAdapter: Adapter) + + fun setDrawingSurface(drawingSurface: DrawingSurface) + + fun invalidate() + + fun setDefaultToolController(defaultToolController: DefaultToolController) + + fun setBottomNavigationViewHolder(bottomNavigationViewHolder: BottomNavigationViewHolder) + + fun isShown(): Boolean + + fun onStartDragging(position: Int, view: View) + + fun onStopDragging() + + fun setLayerSelected(position: Int) + + fun getSelectedLayer(): Layer? + } + + interface LayerViewHolder { + val bitmap: Bitmap? + val view: View + + fun setSelected(isSelected: Boolean) + + fun updateImageView(layer: Layer) + + fun setMergable() + + fun isSelected(): Boolean + + fun getViewLayout(): LinearLayout + + fun bindView() + + fun setLayerVisibilityCheckbox(setTo: Boolean) + } + + interface LayerMenuViewHolder { + fun disableAddLayerButton() + + fun enableAddLayerButton() + + fun disableRemoveLayerButton() + + fun enableRemoveLayerButton() + + fun disableLayerOpacityButton() + + fun disableLayerVisibilityButton() + + fun isShown(): Boolean + } +*/ + interface Layer { + var bitmap: Bitmap + var isVisible: Boolean + var opacityPercentage: Int + + fun getValueForOpacityPercentage(): Int + } + + interface Model { + val layers: List + var currentLayer: Layer? + var width: Int + var height: Int + val layerCount: Int + + fun reset() + + fun getLayerAt(index: Int): Layer? + + fun getLayerIndexOf(layer: Layer): Int + + fun addLayerAt(index: Int, layer: Layer): Boolean + + fun listIterator(index: Int): ListIterator + + fun setLayerAt(position: Int, layer: Layer) + + fun removeLayerAt(position: Int): Boolean + + fun getBitmapOfAllLayers(): Bitmap? + + fun getBitmapListOfAllLayers(): List + } + + interface Navigator { + fun showToast(@StringRes id: Int, length: Int) + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/model/CommandManagerModel.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/model/CommandManagerModel.kt new file mode 100644 index 00000000..b19c23b8 --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/model/CommandManagerModel.kt @@ -0,0 +1,5 @@ +package org.catrobat.paintroid.model + +import org.catrobat.paintroid.command.Command + +data class CommandManagerModel(val initialCommand: Command, val commands: MutableList) diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/model/Layer.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/model/Layer.kt new file mode 100644 index 00000000..acb8c356 --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/model/Layer.kt @@ -0,0 +1,32 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.model + +import android.graphics.Bitmap +import org.catrobat.paintroid.contract.LayerContracts + +const val MAX_LAYER_OPACITY_PERCENTAGE = 100 +const val MAX_LAYER_OPACITY_VALUE = 255 + +open class Layer(override var bitmap: Bitmap) : LayerContracts.Layer { + override var isVisible: Boolean = true + override var opacityPercentage: Int = MAX_LAYER_OPACITY_PERCENTAGE + + override fun getValueForOpacityPercentage(): Int = (opacityPercentage.toFloat() / MAX_LAYER_OPACITY_PERCENTAGE * MAX_LAYER_OPACITY_VALUE).toInt() +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/tools/ContextCallback.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/tools/ContextCallback.kt new file mode 100644 index 00000000..bd5172ef --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/tools/ContextCallback.kt @@ -0,0 +1,55 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.tools + +import android.graphics.Shader +import android.graphics.Typeface +import android.graphics.drawable.Drawable +import android.util.DisplayMetrics +import androidx.annotation.ColorInt +import androidx.annotation.ColorRes +import androidx.annotation.DrawableRes +import androidx.annotation.FontRes +import androidx.annotation.StringRes + +interface ContextCallback { + val scrollTolerance: Int + val orientation: ScreenOrientation? + val displayMetrics: DisplayMetrics + val checkeredBitmapShader: Shader? + + fun showNotification(@StringRes resId: Int) + + fun showNotificationWithDuration(@StringRes resId: Int, duration: NotificationDuration) + + fun getFont(@FontRes id: Int): Typeface? + + @ColorInt + fun getColor(@ColorRes id: Int): Int + + fun getDrawable(@DrawableRes resource: Int): Drawable? + + enum class ScreenOrientation { + PORTRAIT, LANDSCAPE + } + + enum class NotificationDuration { + SHORT, LONG + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/tools/FontType.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/tools/FontType.kt new file mode 100644 index 00000000..dbaeea08 --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/tools/FontType.kt @@ -0,0 +1,12 @@ +package org.catrobat.paintroid.tools + +import androidx.annotation.StringRes +import org.catrobat.paintroid.R + +enum class FontType(@StringRes val nameResource: Int) { + SANS_SERIF(R.string.text_tool_dialog_font_sans_serif), + SERIF(R.string.text_tool_dialog_font_serif), + MONOSPACE(R.string.text_tool_dialog_font_monospace), + STC(R.string.text_tool_dialog_font_arabic_stc), + DUBAI(R.string.text_tool_dialog_font_dubai); +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/tools/Tool.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/tools/Tool.kt new file mode 100644 index 00000000..f9014481 --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/tools/Tool.kt @@ -0,0 +1,71 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.tools + +import android.graphics.Canvas +import android.graphics.Paint +import android.graphics.Paint.Cap +import android.graphics.Point +import android.graphics.PointF +import android.os.Bundle + +interface Tool { + val toolType: ToolType + + val drawPaint: Paint + + var drawTime: Long + + fun handToolMode(): Boolean + + fun handleDown(coordinate: PointF?): Boolean + + fun handleMove(coordinate: PointF?, shouldAnimate: Boolean = false): Boolean + + fun handleUp(coordinate: PointF?): Boolean + + fun changePaintColor(color: Int, invalidate: Boolean = true) + + fun changePaintStrokeWidth(strokeWidth: Int) + + fun changePaintStrokeCap(cap: Cap) + + fun draw(canvas: Canvas) + + fun resetInternalState(stateChange: StateChange) + + fun getAutoScrollDirection( + pointX: Float, + pointY: Float, + screenWidth: Int, + screenHeight: Int + ): Point + + fun handleUpAnimations(coordinate: PointF?) + fun handleDownAnimations(coordinate: PointF?) + fun onSaveInstanceState(bundle: Bundle?) + + fun onRestoreInstanceState(bundle: Bundle?) + + enum class StateChange { + ALL, RESET_INTERNAL_STATE, NEW_IMAGE_LOADED, MOVE_CANCELED + } + + fun toolPositionCoordinates(coordinate: PointF): PointF +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/tools/ToolPaint.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/tools/ToolPaint.kt new file mode 100644 index 00000000..9d98c5fd --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/tools/ToolPaint.kt @@ -0,0 +1,37 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.tools + +import android.graphics.Paint +import android.graphics.Paint.Cap +import android.graphics.PorterDuffXfermode +import android.graphics.Shader + +interface ToolPaint { + var paint: Paint + val previewPaint: Paint + var color: Int + val eraseXfermode: PorterDuffXfermode + val previewColor: Int + var strokeWidth: Float + var strokeCap: Cap + val checkeredShader: Shader? + + fun setAntialiasing() +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/tools/ToolType.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/tools/ToolType.kt new file mode 100644 index 00000000..f5b649fa --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/tools/ToolType.kt @@ -0,0 +1,226 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.tools + +import androidx.annotation.DrawableRes +import androidx.annotation.IdRes +import androidx.annotation.StringRes +import org.catrobat.paintroid.R +import org.catrobat.paintroid.common.INVALID_RESOURCE_ID +import org.catrobat.paintroid.tools.Tool.StateChange +import java.util.EnumSet + +enum class ToolType( + @get:StringRes val nameResource: Int, + @get:StringRes val helpTextResource: Int, + @get:DrawableRes val drawableResource: Int, + private val stateChangeBehaviour: EnumSet, + @get:IdRes val toolButtonID: Int, + @get:DrawableRes val overlayDrawableResource: Int, + private val hasOptions: Boolean +) { + // TODO MAY CAUSE PROBLEMS + PIPETTE( + R.string.button_pipette, + R.string.help_content_eyedropper, + R.drawable.ic_pocketpaint_tool_pipette, + EnumSet.of(StateChange.ALL), + 1, + INVALID_RESOURCE_ID, + false + ), + BRUSH( + R.string.button_brush, + R.string.help_content_brush, + R.drawable.ic_pocketpaint_tool_brush, + EnumSet.of(StateChange.ALL), + 2, + INVALID_RESOURCE_ID, + true + ), + UNDO( + R.string.button_undo, + R.string.help_content_undo, + R.drawable.ic_pocketpaint_undo, + EnumSet.of(StateChange.ALL), + 3, + INVALID_RESOURCE_ID, + false + ), + REDO( + R.string.button_redo, + R.string.help_content_redo, + R.drawable.ic_pocketpaint_redo, + EnumSet.of(StateChange.ALL), + 4, + INVALID_RESOURCE_ID, + false + ), + FILL( + R.string.button_fill, + R.string.help_content_fill, + R.drawable.ic_pocketpaint_tool_fill, + EnumSet.of(StateChange.ALL), + 5, + INVALID_RESOURCE_ID, + true + ), + CLIPBOARD( + R.string.button_clipboard, + R.string.help_content_clipboard, + R.drawable.ic_pocketpaint_tool_clipboard, + EnumSet.of(StateChange.ALL), + 6, + INVALID_RESOURCE_ID, + true + ), + LINE( + R.string.button_line, + R.string.help_content_line, + R.drawable.ic_pocketpaint_tool_line, + EnumSet.of(StateChange.ALL), + 7, + INVALID_RESOURCE_ID, + true + ), + CURSOR( + R.string.button_cursor, + R.string.help_content_cursor, + R.drawable.ic_pocketpaint_tool_cursor, + EnumSet.of(StateChange.ALL), + 8, + INVALID_RESOURCE_ID, + true + ), + IMPORTPNG( + R.string.button_import_image, + R.string.help_content_import_png, + R.drawable.ic_pocketpaint_tool_import, + EnumSet.of(StateChange.ALL), + 9, + INVALID_RESOURCE_ID, + false + ), + TRANSFORM( + R.string.button_transform, + R.string.help_content_transform, + R.drawable.ic_pocketpaint_tool_transform, + EnumSet.of(StateChange.RESET_INTERNAL_STATE, StateChange.NEW_IMAGE_LOADED), + 10, + INVALID_RESOURCE_ID, + true + ), + ERASER( + R.string.button_eraser, + R.string.help_content_eraser, + R.drawable.ic_pocketpaint_tool_eraser, + EnumSet.of(StateChange.ALL), + 11, + INVALID_RESOURCE_ID, + true + ), + SHAPE( + R.string.button_shape, + R.string.help_content_shape, + R.drawable.ic_pocketpaint_tool_rectangle, + EnumSet.of(StateChange.ALL), + 12, + INVALID_RESOURCE_ID, + true + ), + TEXT( + R.string.button_text, + R.string.help_content_text, + R.drawable.ic_pocketpaint_tool_text, + EnumSet.of(StateChange.ALL), + 13, + INVALID_RESOURCE_ID, + true + ), + LAYER( + R.string.layers_title, + R.string.help_content_layer, + R.drawable.ic_pocketpaint_layers, + EnumSet.of(StateChange.ALL), + INVALID_RESOURCE_ID, + INVALID_RESOURCE_ID, + false + ), + COLORCHOOSER( + 4386, + R.string.help_content_color_chooser, + 746, + EnumSet.of(StateChange.ALL), + INVALID_RESOURCE_ID, + INVALID_RESOURCE_ID, + false + ), + HAND( + R.string.button_hand, + R.string.help_content_hand, + R.drawable.ic_pocketpaint_tool_hand, + EnumSet.of(StateChange.ALL), + 14, + INVALID_RESOURCE_ID, + false + ), + SPRAY( + R.string.button_spray_can, + R.string.help_content_spray_can, + R.drawable.ic_pocketpaint_tool_spray_can, + EnumSet.of(StateChange.ALL), + 15, + INVALID_RESOURCE_ID, + true + ), + WATERCOLOR( + R.string.button_watercolor, + R.string.help_content_watercolor, + R.drawable.ic_pocketpaint_tool_watercolor, + EnumSet.of(StateChange.ALL), + 16, + INVALID_RESOURCE_ID, + true + ), + SMUDGE( + R.string.button_smudge, + R.string.help_content_smudge, + R.drawable.ic_pocketpaint_tool_smudge, + EnumSet.of(StateChange.ALL), + 17, + INVALID_RESOURCE_ID, + true + ), + CLIP( + R.string.button_clip, + R.string.help_content_clip, + R.drawable.ic_pocketpaint_tool_clipping, + EnumSet.of(StateChange.RESET_INTERNAL_STATE), + 18, + INVALID_RESOURCE_ID, + true + ); + + fun shouldReactToStateChange(stateChange: StateChange): Boolean = + stateChangeBehaviour.contains(StateChange.ALL) || stateChangeBehaviour.contains( + stateChange + ) + + fun hasOptions(): Boolean = hasOptions +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/tools/Workspace.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/tools/Workspace.kt new file mode 100644 index 00000000..d3b7a95d --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/tools/Workspace.kt @@ -0,0 +1,52 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.tools + +import android.graphics.Bitmap +import android.graphics.PointF +import android.graphics.RectF +import org.catrobat.paintroid.contract.LayerContracts +import org.catrobat.paintroid.ui.Perspective + +interface Workspace { + val height: Int + val width: Int + val surfaceWidth: Int + val surfaceHeight: Int + val bitmapOfAllLayers: Bitmap? + val bitmapListOfAllLayers: List + var bitmapOfCurrentLayer: Bitmap? + val currentLayerIndex: Int + val scaleForCenterBitmap: Float + var scale: Float + var perspective: Perspective + val layerModel: LayerContracts.Model + + fun contains(point: PointF): Boolean + + fun intersectsWith(rectF: RectF): Boolean + + fun resetPerspective() + + fun getSurfacePointFromCanvasPoint(coordinate: PointF): PointF + + fun getCanvasPointFromSurfacePoint(surfacePoint: PointF): PointF + + fun invalidate() +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/tools/common/Constants.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/tools/common/Constants.kt new file mode 100644 index 00000000..57c4d1cb --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/tools/common/Constants.kt @@ -0,0 +1,22 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.tools.common + +const val SCROLL_TOLERANCE_PERCENTAGE = 0.1f +const val MOVE_TOLERANCE = 5f diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/tools/common/PointScrollBehavior.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/tools/common/PointScrollBehavior.kt new file mode 100644 index 00000000..08f1eba9 --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/tools/common/PointScrollBehavior.kt @@ -0,0 +1,46 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.tools.common + +import android.graphics.Point + +class PointScrollBehavior(private val scrollTolerance: Int) : ScrollBehavior { + override fun getScrollDirection( + pointX: Float, + pointY: Float, + viewWidth: Int, + viewHeight: Int + ): Point { + var deltaX = 0 + var deltaY = 0 + if (pointX < scrollTolerance) { + deltaX = 1 + } + if (pointX > viewWidth - scrollTolerance) { + deltaX = -1 + } + if (pointY < scrollTolerance) { + deltaY = 1 + } + if (pointY > viewHeight - scrollTolerance) { + deltaY = -1 + } + return Point(deltaX, deltaY) + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/tools/common/ScrollBehavior.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/tools/common/ScrollBehavior.kt new file mode 100644 index 00000000..7edd6311 --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/tools/common/ScrollBehavior.kt @@ -0,0 +1,25 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.tools.common + +import android.graphics.Point + +interface ScrollBehavior { + fun getScrollDirection(pointX: Float, pointY: Float, viewWidth: Int, viewHeight: Int): Point +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/tools/drawable/DrawableShape.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/tools/drawable/DrawableShape.kt new file mode 100644 index 00000000..cc76fae8 --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/tools/drawable/DrawableShape.kt @@ -0,0 +1,23 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.tools.drawable + +enum class DrawableShape { + RECTANGLE, OVAL, HEART, STAR +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/tools/drawable/DrawableStyle.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/tools/drawable/DrawableStyle.kt new file mode 100644 index 00000000..4f37c0e6 --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/tools/drawable/DrawableStyle.kt @@ -0,0 +1,23 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.tools.drawable + +enum class DrawableStyle { + STROKE, FILL +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/tools/drawable/HeartDrawable.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/tools/drawable/HeartDrawable.kt new file mode 100644 index 00000000..0bc6a4ab --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/tools/drawable/HeartDrawable.kt @@ -0,0 +1,66 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.tools.drawable + +import android.graphics.Canvas +import android.graphics.Paint +import android.graphics.Path +import android.graphics.RectF + +private const val CONSTANT_1 = 8f +private const val CONSTANT_2 = 0.2f +private const val CONSTANT_3 = 0.8f +private const val CONSTANT_4 = 1.2f +private const val CONSTANT_5 = 1.5f +private const val CONSTANT_6 = 4.5f +private const val CONSTANT_7 = 7.2f + +class HeartDrawable : ShapeDrawable { + private val path = Path() + private val paint = Paint() + override fun draw(canvas: Canvas, shapeRect: RectF, drawPaint: Paint) { + paint.set(drawPaint) + val midWidth = shapeRect.width() / 2 + val height = shapeRect.height() + val width = shapeRect.width() + path.run { + reset() + moveTo(midWidth, height) + var x1 = -CONSTANT_2 * width + var x2 = CONSTANT_3 * width / CONSTANT_1 + val y1 = CONSTANT_6 * height / CONSTANT_1 + val y2 = -CONSTANT_5 * height / CONSTANT_1 + cubicTo( + x1, y1, + x2, y2, + midWidth, -y2 + ) + x1 = CONSTANT_7 * width / CONSTANT_1 + x2 = CONSTANT_4 * width + cubicTo( + x1, y2, + x2, y1, + midWidth, height + ) + close() + offset(shapeRect.left, shapeRect.top) + } + canvas.drawPath(path, paint) + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/tools/drawable/OvalDrawable.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/tools/drawable/OvalDrawable.kt new file mode 100644 index 00000000..b24dded6 --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/tools/drawable/OvalDrawable.kt @@ -0,0 +1,29 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.tools.drawable + +import android.graphics.Canvas +import android.graphics.Paint +import android.graphics.RectF + +class OvalDrawable : ShapeDrawable { + override fun draw(canvas: Canvas, shapeRect: RectF, drawPaint: Paint) { + canvas.drawOval(shapeRect, drawPaint) + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/tools/drawable/RectangleDrawable.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/tools/drawable/RectangleDrawable.kt new file mode 100644 index 00000000..789b5e34 --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/tools/drawable/RectangleDrawable.kt @@ -0,0 +1,29 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.tools.drawable + +import android.graphics.Canvas +import android.graphics.Paint +import android.graphics.RectF + +class RectangleDrawable : ShapeDrawable { + override fun draw(canvas: Canvas, shapeRect: RectF, drawPaint: Paint) { + canvas.drawRect(shapeRect, drawPaint) + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/tools/drawable/ShapeDrawable.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/tools/drawable/ShapeDrawable.kt new file mode 100644 index 00000000..10d85c80 --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/tools/drawable/ShapeDrawable.kt @@ -0,0 +1,27 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.tools.drawable + +import android.graphics.Canvas +import android.graphics.Paint +import android.graphics.RectF + +interface ShapeDrawable { + fun draw(canvas: Canvas, shapeRect: RectF, drawPaint: Paint) +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/tools/drawable/StarDrawable.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/tools/drawable/StarDrawable.kt new file mode 100644 index 00000000..554888d1 --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/tools/drawable/StarDrawable.kt @@ -0,0 +1,58 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.tools.drawable + +import android.graphics.Canvas +import android.graphics.Paint +import android.graphics.Path +import android.graphics.RectF + +private const val CONSTANT_1 = 8f +private const val CONSTANT_2 = 1.8f +private const val CONSTANT_3 = 3f +private const val CONSTANT_4 = 2f + +class StarDrawable : ShapeDrawable { + private val path = Path() + private val paint = Paint() + override fun draw(canvas: Canvas, shapeRect: RectF, drawPaint: Paint) { + paint.set(drawPaint) + val midWidth = shapeRect.width() / 2 + val midHeight = shapeRect.height() / 2 + val height = shapeRect.height() + val width = shapeRect.width() + path.run { + reset() + moveTo(midWidth, 0f) + lineTo(midWidth + width / CONSTANT_1, midHeight - height / CONSTANT_1) + lineTo(width, midHeight - height / CONSTANT_1) + lineTo(midWidth + CONSTANT_2 * width / CONSTANT_1, midHeight + height / CONSTANT_1) + lineTo(midWidth + CONSTANT_3 * width / CONSTANT_1, height) + lineTo(midWidth, midHeight + CONSTANT_4 * height / CONSTANT_1) + lineTo(midWidth - CONSTANT_3 * width / CONSTANT_1, height) + lineTo(midWidth - CONSTANT_2 * width / CONSTANT_1, midHeight + height / CONSTANT_1) + lineTo(0f, midHeight - height / CONSTANT_1) + lineTo(midWidth - width / CONSTANT_1, midHeight - height / CONSTANT_1) + lineTo(midWidth, 0f) + close() + offset(shapeRect.left, shapeRect.top) + } + canvas.drawPath(path, paint) + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/tools/helper/AdvancedSettingsAlgorithms.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/tools/helper/AdvancedSettingsAlgorithms.kt new file mode 100644 index 00000000..4da88117 --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/tools/helper/AdvancedSettingsAlgorithms.kt @@ -0,0 +1,82 @@ +package org.catrobat.paintroid.tools.helper + +import android.graphics.PointF +import org.catrobat.paintroid.command.serialization.SerializablePath + +// MAYBE CAN A STUB WORK +object AdvancedSettingsAlgorithms { + @kotlin.jvm.JvmField + var smoothing = true + + const val threshold = 0.2 + const val divider = 3 + + @JvmStatic + fun smoothingAlgorithm(pointArray: List): SerializablePath { + + val diffPointArray = mutableListOf() + if (pointArray.size > 1) { + for (i in pointArray.indices) { + if (i >= 0) { + val point: PointF = pointArray[i] + when (i) { + 0 -> { + val next: PointF = pointArray[i + 1] + val differenceX = next.x - point.x + val differenceY = next.y - point.y + val newPoint = PointF(differenceX / divider, differenceY / divider) + diffPointArray.add(newPoint) + } + pointArray.size - 1 -> { + val prev: PointF = pointArray[i - 1] + val differenceX = point.x - prev.x + val differenceY = point.y - prev.y + val newPoint = PointF(differenceX / divider, differenceY / divider) + diffPointArray.add(newPoint) + } + else -> { + val next: PointF = pointArray[i + 1] + val prev: PointF = pointArray[i - 1] + val differenceX = next.x - prev.x + val differenceY = next.y - prev.y + val newPoint = PointF(differenceX / divider, differenceY / divider) + diffPointArray.add(newPoint) + } + } + } + } + } + + val trueList = mutableListOf() + trueList.add(PointF(pointArray[0].x, pointArray[0].y)) + for (i in 1 until pointArray.size) { + + val point: PointF = pointArray[i] + val diff: PointF = diffPointArray[i] + + val erg1 = point.x + diff.x + val erg2 = point.y + diff.y + + trueList.add(PointF(erg1, erg2)) + } + + val pathNew = SerializablePath() + pathNew.incReserve(1) + pathNew.moveTo(trueList[0].x, trueList[0].y) + + for (i in 1 until trueList.size - 1 step 2) { + val point: PointF = trueList[i] + + val prev: PointF = trueList[i - 1] + val next: PointF = trueList[i + 1] + + pathNew.cubicTo(prev.x, prev.y, point.x, point.y, next.x, next.y) + pathNew.incReserve(1) + } + + trueList.clear() + diffPointArray.clear() + + return pathNew + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/tools/helper/FillAlgorithm.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/tools/helper/FillAlgorithm.kt new file mode 100644 index 00000000..43f2ac16 --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/tools/helper/FillAlgorithm.kt @@ -0,0 +1,34 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.tools.helper + +import android.graphics.Bitmap +import android.graphics.Point + +interface FillAlgorithm { + fun setParameters( + bitmap: Bitmap, + clickedPixel: Point, + targetColor: Int, + replacementColor: Int, + colorToleranceThreshold: Float + ) + + fun performFilling() +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/tools/helper/FillAlgorithmFactory.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/tools/helper/FillAlgorithmFactory.kt new file mode 100644 index 00000000..809cadf9 --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/tools/helper/FillAlgorithmFactory.kt @@ -0,0 +1,23 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.tools.helper + +interface FillAlgorithmFactory { + fun createFillAlgorithm(): FillAlgorithm +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/tools/helper/JavaFillAlgorithm.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/tools/helper/JavaFillAlgorithm.kt new file mode 100644 index 00000000..9b461ba1 --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/tools/helper/JavaFillAlgorithm.kt @@ -0,0 +1,179 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.tools.helper + +import android.graphics.Bitmap +import android.graphics.Color +import android.graphics.Point +import androidx.annotation.VisibleForTesting +import java.util.LinkedList +import java.util.Queue + +private const val UP = true +private const val DOWN = false + +class JavaFillAlgorithm : FillAlgorithm { + @VisibleForTesting + lateinit var pixels: Array + + @VisibleForTesting + lateinit var clickedPixel: Point + + @VisibleForTesting + val ranges: Queue = LinkedList() + + @VisibleForTesting + var targetColor = 0 + + @VisibleForTesting + var colorToBeReplaced = 0 + + @VisibleForTesting + var colorToleranceThresholdSquared = 0 + + private lateinit var filledPixels: Array + private lateinit var bitmap: Bitmap + private var considerTolerance = false + private var width = 0 + private var height = 0 + + override fun setParameters( + bitmap: Bitmap, + clickedPixel: Point, + targetColor: Int, + replacementColor: Int, + colorToleranceThreshold: Float + ) { + this.bitmap = bitmap + this.width = bitmap.width + this.height = bitmap.height + pixels = Array(bitmap.height) { IntArray(bitmap.width) } + for (i in 0 until height) { + this.bitmap.getPixels(pixels[i], 0, width, 0, i, width, 1) + } + filledPixels = Array(bitmap.height) { BooleanArray(bitmap.width) } + this.clickedPixel = clickedPixel + this.targetColor = targetColor + this.colorToBeReplaced = replacementColor + colorToleranceThresholdSquared = square(colorToleranceThreshold.toInt()) + considerTolerance = colorToleranceThreshold > 0 + } + + override fun performFilling() { + var range = generateRangeAndReplaceColor( + clickedPixel.y, clickedPixel.x, UP + ) + ranges.add(range) + ranges.add(Range(range.line, range.start, range.end, DOWN)) + var row: Int + while (!ranges.isEmpty()) { + range = ranges.poll() ?: break + val direction = range.direction + val diff = if (direction == UP) -1 else 1 + row = range.line + diff + if (row in 0 until height) { + checkRangeAndGenerateNewRanges(range, row, direction) + } + } + } + + private fun square(x: Int) = x * x + + private fun shouldCellBeFilled(row: Int, col: Int): Boolean = + !filledPixels[row][col] && ( + pixels[row][col] == colorToBeReplaced || considerTolerance && isPixelWithinColorTolerance( + pixels[row][col], + colorToBeReplaced + ) + ) + + private fun validateAndAssign(row: Int, col: Int): Boolean = if (shouldCellBeFilled(row, col)) { + pixels[row][col] = targetColor + filledPixels[row][col] = true + true + } else false + + private fun getStartIndex(row: Int, col: Int): Int { + val start = (col downTo 0).find { !validateAndAssign(row, it) } ?: -1 + return start + 1 + } + + private fun getEndIndex(row: Int, col: Int): Int { + val end = (col until width).find { !validateAndAssign(row, it) } ?: width + return end - 1 + } + + private fun generateRangeAndReplaceColor(row: Int, col: Int, direction: Boolean): Range { + val range = Range() + pixels[row][col] = targetColor + filledPixels[row][col] = true + + val start = getStartIndex(row, col - 1) + val end = getEndIndex(row, col + 1) + range.apply { + line = row + this.end = end + this.start = start + this.direction = direction + } + bitmap.setPixels(pixels[row], start, width, start, row, end - start + 1, 1) + return range + } + + private fun checkRangeAndGenerateNewRanges(range: Range, row: Int, directionUp: Boolean) { + var newRange: Range + var col = range.start + while (col <= range.end) { + if (shouldCellBeFilled(row, col)) { + newRange = generateRangeAndReplaceColor(row, col, directionUp) + ranges.add(newRange) + if (newRange.start <= range.start - 2) { + ranges.add(Range(row, newRange.start, range.start - 2, !directionUp)) + } + if (newRange.end >= range.end + 2) { + ranges.add(Range(row, range.end + 2, newRange.end, !directionUp)) + } + col = if (newRange.end >= range.end - 1) { + break + } else { + newRange.end + 1 + } + } + col++ + } + } + + private fun isPixelWithinColorTolerance(pixel: Int, referenceColor: Int): Boolean { + val redDiff = Color.red(pixel) - Color.red(referenceColor) + val greenDiff = Color.green(pixel) - Color.green(referenceColor) + val blueDiff = Color.blue(pixel) - Color.blue(referenceColor) + val alphaDiff = Color.alpha(pixel) - Color.alpha(referenceColor) + return ( + square(redDiff) + square(greenDiff) + square(blueDiff) + square(alphaDiff) + <= colorToleranceThresholdSquared + ) + } + + data class Range( + var line: Int = 0, + var start: Int = 0, + var end: Int = 0, + var direction: Boolean = false + ) +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/tools/helper/JavaFillAlgorithmFactory.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/tools/helper/JavaFillAlgorithmFactory.kt new file mode 100644 index 00000000..8301aaa7 --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/tools/helper/JavaFillAlgorithmFactory.kt @@ -0,0 +1,23 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.tools.helper + +class JavaFillAlgorithmFactory : FillAlgorithmFactory { + override fun createFillAlgorithm(): FillAlgorithm = JavaFillAlgorithm() +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/tools/implementation/BaseTool.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/tools/implementation/BaseTool.kt new file mode 100644 index 00000000..c5093d16 --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/tools/implementation/BaseTool.kt @@ -0,0 +1,124 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.tools.implementation + +import android.graphics.Canvas +import android.graphics.Paint +import android.graphics.Paint.Cap +import android.graphics.Point +import android.graphics.PointF +import android.os.Bundle +import androidx.annotation.ColorInt +import androidx.test.espresso.idling.CountingIdlingResource +import org.catrobat.paintroid.command.CommandManager +import org.catrobat.paintroid.tools.ContextCallback +import org.catrobat.paintroid.tools.Tool +import org.catrobat.paintroid.tools.Tool.StateChange +import org.catrobat.paintroid.tools.ToolPaint +import org.catrobat.paintroid.tools.Workspace +import org.catrobat.paintroid.tools.common.PointScrollBehavior +import org.catrobat.paintroid.tools.common.ScrollBehavior +import org.catrobat.paintroid.tools.options.ToolOptionsViewController +import org.catrobat.paintroid.command.CommandFactory + +// TODO MAY CAUSE CRASH, as we set some stubs +abstract class BaseTool( + open var contextCallback: ContextCallback, + @JvmField var toolOptionsViewController: ToolOptionsViewController, + @JvmField + protected var toolPaint: ToolPaint, + @JvmField + protected var workspace: Workspace, + @JvmField + protected var idlingResource: CountingIdlingResource, + @JvmField + protected var commandManager: CommandManager +) : Tool { + @JvmField + protected val movedDistance: PointF + + @JvmField + protected var scrollBehavior: ScrollBehavior + + @JvmField + var previousEventCoordinate: PointF? + + protected lateinit var commandFactory: CommandFactory + + init { + val scrollTolerance = contextCallback.scrollTolerance + scrollBehavior = PointScrollBehavior(scrollTolerance) + movedDistance = PointF(0f, 0f) + previousEventCoordinate = PointF(0f, 0f) + if (toolPaint != null && toolPaint.paint != null && toolPaint.paint.pathEffect != null) { + toolPaint.paint.pathEffect = null + } + } + + override fun onSaveInstanceState(bundle: Bundle?) = Unit + + override fun onRestoreInstanceState(bundle: Bundle?) = Unit + + override fun changePaintColor(@ColorInt color: Int, invalidate: Boolean) { + toolPaint.color = color + } + + override fun changePaintStrokeWidth(strokeWidth: Int) { + toolPaint.strokeWidth = strokeWidth.toFloat() + } + + override fun changePaintStrokeCap(cap: Cap) { + toolPaint.strokeCap = cap + } + + override fun handleDown(coordinate: PointF?): Boolean = true + + override fun handleUp(coordinate: PointF?): Boolean { + toolOptionsViewController.animateBottomAndTopNavigation(false) + return true + } + + override fun handleMove(coordinate: PointF?, shouldAnimate: Boolean): Boolean { + if (shouldAnimate) { + toolOptionsViewController.animateBottomAndTopNavigation(true) + } + return true + } + override val drawPaint + get() = Paint(toolPaint.paint) + + abstract override fun draw(canvas: Canvas) + + protected open fun resetInternalState() {} + + override fun resetInternalState(stateChange: StateChange) { + if (toolType.shouldReactToStateChange(stateChange)) { + resetInternalState() + } + } + + override fun getAutoScrollDirection( + pointX: Float, + pointY: Float, + screenWidth: Int, + screenHeight: Int + ): Point = scrollBehavior.getScrollDirection(pointX, pointY, screenWidth, screenHeight) + + override fun handToolMode(): Boolean = false +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/tools/implementation/BrushTool.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/tools/implementation/BrushTool.kt new file mode 100644 index 00000000..dfcdd5f3 --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/tools/implementation/BrushTool.kt @@ -0,0 +1,269 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.tools.implementation + +import android.graphics.Canvas +import android.graphics.Paint +import android.graphics.PointF +import android.view.View +import androidx.test.espresso.idling.CountingIdlingResource +import org.catrobat.paintroid.command.CommandManager +import org.catrobat.paintroid.command.serialization.SerializablePath +import org.catrobat.paintroid.tools.ContextCallback +import org.catrobat.paintroid.tools.Tool.StateChange +import org.catrobat.paintroid.tools.ToolPaint +import org.catrobat.paintroid.tools.ToolType +import org.catrobat.paintroid.tools.Workspace +import org.catrobat.paintroid.tools.common.CommonBrushChangedListener +import org.catrobat.paintroid.tools.common.CommonBrushPreviewListener +import org.catrobat.paintroid.tools.common.MOVE_TOLERANCE +import org.catrobat.paintroid.tools.helper.AdvancedSettingsAlgorithms +import org.catrobat.paintroid.tools.helper.AdvancedSettingsAlgorithms.smoothing +import org.catrobat.paintroid.tools.helper.AdvancedSettingsAlgorithms.threshold +import org.catrobat.paintroid.tools.options.BrushToolOptionsView +import org.catrobat.paintroid.tools.options.ToolOptionsViewController +import kotlin.math.abs +import kotlin.math.max +import kotlin.math.sqrt + +open class BrushTool( + val brushToolOptionsView: BrushToolOptionsView, + contextCallback: ContextCallback, + toolOptionsViewController: ToolOptionsViewController, + toolPaint: ToolPaint, + workspace: Workspace, + idlingResource: CountingIdlingResource, + commandManager: CommandManager, + override var drawTime: Long +) : BaseTool(contextCallback, toolOptionsViewController, toolPaint, workspace, idlingResource, commandManager) { + protected open val previewPaint: Paint + get() = toolPaint.previewPaint + + protected open val bitmapPaint: Paint + get() = toolPaint.paint + + override val toolType: ToolType + get() = ToolType.BRUSH + + @JvmField + var pathToDraw: SerializablePath = SerializablePath() + var initialEventCoordinate: PointF? = null + private var pathInsideBitmap = false + private val drawToolMovedDistance = PointF(0f, 0f) + + val pointArray = mutableListOf() + + init { + toolOptionsViewController.enable() + pathToDraw.incReserve(1) + brushToolOptionsView.setBrushChangedListener(CommonBrushChangedListener(this)) + brushToolOptionsView.setBrushPreviewListener( + CommonBrushPreviewListener( + toolPaint, + toolType + ) + ) + brushToolOptionsView.setCurrentPaint(toolPaint.paint) + brushToolOptionsView.setStrokeCapButtonChecked(toolPaint.strokeCap) + } + + override fun draw(canvas: Canvas) { + canvas.run { + save() + clipRect(0, 0, workspace.width, workspace.height) + drawPath(pathToDraw, previewPaint) + restore() + } + } + + private fun hideBrushSpecificLayoutOnHandleDown() { + if (toolOptionsViewController.isVisible) { + if (brushToolOptionsView.getTopToolOptions().visibility == View.VISIBLE) { + toolOptionsViewController.slideUp( + brushToolOptionsView.getTopToolOptions(), + willHide = true, + showOptionsView = false + ) + } + + if (brushToolOptionsView.getBottomToolOptions().visibility == View.VISIBLE) { + toolOptionsViewController.slideDown( + brushToolOptionsView.getBottomToolOptions(), + willHide = true, + showOptionsView = false + ) + } + } + } + + private fun showBrushSpecificLayoutOnHandleUp() { + if (!toolOptionsViewController.isVisible) { + if (brushToolOptionsView.getBottomToolOptions().visibility == View.INVISIBLE) { + toolOptionsViewController.slideDown( + brushToolOptionsView.getTopToolOptions(), + willHide = false, + showOptionsView = true + ) + } + + if (brushToolOptionsView.getBottomToolOptions().visibility == View.INVISIBLE) { + toolOptionsViewController.slideUp( + brushToolOptionsView.getBottomToolOptions(), + willHide = false, + showOptionsView = true + ) + } + } + } + + override fun handleDown(coordinate: PointF?): Boolean { + coordinate ?: return false + super.handleDown(coordinate) + initialEventCoordinate = PointF(coordinate.x, coordinate.y) + previousEventCoordinate = PointF(coordinate.x, coordinate.y) + pathToDraw.moveTo(coordinate.x, coordinate.y) + drawToolMovedDistance.set(0f, 0f) + pointArray.add(PointF(coordinate.x, coordinate.y)) + pathInsideBitmap = workspace.contains(coordinate) + return true + } + + override fun handleDownAnimations(coordinate: PointF?) { + hideBrushSpecificLayoutOnHandleDown() + } + + override fun handleUpAnimations(coordinate: PointF?) { + showBrushSpecificLayoutOnHandleUp() + super.handleUp(coordinate) + } + + override fun handleMove(coordinate: PointF?, shouldAnimate: Boolean): Boolean { + if (eventCoordinatesAreNull() || coordinate == null) { + return false + } + super.handleMove(coordinate, shouldAnimate) + if (shouldAnimate) { + hideBrushSpecificLayoutOnHandleDown() + } + previousEventCoordinate?.let { + pathToDraw.quadTo(it.x, it.y, coordinate.x, coordinate.y) + pathToDraw.incReserve(1) + drawToolMovedDistance.set( + drawToolMovedDistance.x + abs(coordinate.x - it.x), + drawToolMovedDistance.y + abs(coordinate.y - it.y) + ) + pointArray.add(PointF(coordinate.x, coordinate.y)) + it.set(coordinate.x, coordinate.y) + } + if (!pathInsideBitmap && workspace.contains(coordinate)) { + pathInsideBitmap = true + } + return true + } + + override fun handleUp(coordinate: PointF?): Boolean { + if (eventCoordinatesAreNull() || coordinate == null) { + return false + } + showBrushSpecificLayoutOnHandleUp() + super.handleUp(coordinate) + + if (!pathInsideBitmap && workspace.contains(coordinate)) { + pathInsideBitmap = true + } + + previousEventCoordinate?.let { + drawToolMovedDistance.set( + drawToolMovedDistance.x + abs(coordinate.x - it.x), + drawToolMovedDistance.y + abs(coordinate.y - it.y) + ) + } + + return if (MOVE_TOLERANCE < max(drawToolMovedDistance.x, drawToolMovedDistance.y)) { + addPathCommand(coordinate) + } else { + initialEventCoordinate?.let { + return addPointCommand(it) + } + false + } + } + + override fun toolPositionCoordinates(coordinate: PointF): PointF = coordinate + + override fun resetInternalState() { + pathToDraw.rewind() + pointArray.clear() + initialEventCoordinate = null + previousEventCoordinate = null + } + + override fun changePaintColor(color: Int, invalidate: Boolean) { + super.changePaintColor(color, invalidate) + if (invalidate) brushToolOptionsView.invalidate() + } + + private fun eventCoordinatesAreNull(): Boolean = + initialEventCoordinate == null || previousEventCoordinate == null + + private fun addPathCommand(coordinate: PointF): Boolean { + pathToDraw.lineTo(coordinate.x, coordinate.y) + + if (!pathInsideBitmap) { + resetInternalState(StateChange.RESET_INTERNAL_STATE) + return false + } + + if (toolType == ToolType.ERASER) { + val command = commandFactory.createPathCommand(bitmapPaint, pathToDraw) + commandManager.addCommand(command) + } else { + var distance: Double? = null + initialEventCoordinate?.apply { + distance = + sqrt(((coordinate.x - x) * (coordinate.x - x) + (coordinate.y - y) * (coordinate.y - y)).toDouble()) + } + val speed = distance?.div(drawTime) + + if (!smoothing || speed != null && speed < threshold) { + val command = commandFactory.createPathCommand(bitmapPaint, pathToDraw) + commandManager.addCommand(command) + } else { + val pathNew = AdvancedSettingsAlgorithms.smoothingAlgorithm(pointArray) + val command = commandFactory.createPathCommand(bitmapPaint, pathNew) + commandManager.addCommand(command) + } + } + + pointArray.clear() + return true + } + + private fun addPointCommand(coordinate: PointF): Boolean { + if (!pathInsideBitmap) { + resetInternalState(StateChange.RESET_INTERNAL_STATE) + return false + } + + pointArray.clear() + val command = commandFactory.createPointCommand(bitmapPaint, coordinate) + commandManager.addCommand(command) + return true + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/tools/implementation/DefaultToolPaint.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/tools/implementation/DefaultToolPaint.kt new file mode 100644 index 00000000..230792c5 --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/tools/implementation/DefaultToolPaint.kt @@ -0,0 +1,119 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.tools.implementation + +import android.content.Context +import android.graphics.BitmapFactory +import android.graphics.BitmapShader +import android.graphics.Color +import android.graphics.Paint +import android.graphics.Paint.Cap +import android.graphics.PorterDuff +import android.graphics.PorterDuffXfermode +import android.graphics.Shader +import org.catrobat.paintroid.R +import org.catrobat.paintroid.tools.ToolPaint + +const val STROKE_25 = 25f +const val STROKE_10 = 10f + +class DefaultToolPaint(private val context: Context) : ToolPaint { + private val bitmapPaint = Paint().apply { + reset() + isAntiAlias = antialiasing + color = Color.BLACK + style = Paint.Style.STROKE + strokeJoin = Paint.Join.ROUND + strokeCap = Cap.ROUND + strokeWidth = STROKE_25 + } + + override val checkeredShader: Shader? + get() { + val checkerboard = + BitmapFactory.decodeResource(context.resources, R.drawable.pocketpaint_checkeredbg) + if (checkerboard != null) { + return BitmapShader(checkerboard, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT) + } + return null + } + + override val previewPaint: Paint + get() = Paint().apply { set(bitmapPaint) } + + override val previewColor: Int + get() = previewPaint.color + + override var color: Int + get() = bitmapPaint.color + set(color) { + bitmapPaint.color = color + previewPaint.set(bitmapPaint) + previewPaint.xfermode = null + if (Color.alpha(color) == 0) { + previewPaint.shader = checkeredShader + previewPaint.color = Color.BLACK + bitmapPaint.xfermode = eraseXfermode + bitmapPaint.alpha = 0 + } else { + bitmapPaint.xfermode = null + } + } + + override var strokeWidth: Float + get() = bitmapPaint.strokeWidth + set(strokeWidth) { + bitmapPaint.strokeWidth = strokeWidth + previewPaint.strokeWidth = strokeWidth + var antiAliasing = antialiasing + if (strokeWidth <= 1) { + antiAliasing = false + } + bitmapPaint.isAntiAlias = antiAliasing + previewPaint.isAntiAlias = antiAliasing + } + + override var strokeCap: Cap + get() = bitmapPaint.strokeCap + set(strokeCap) { + bitmapPaint.strokeCap = strokeCap + previewPaint.strokeCap = strokeCap + } + + override var paint: Paint + get() = bitmapPaint + set(paint) { + bitmapPaint.set(paint) + previewPaint.set(paint) + } + + override val eraseXfermode: PorterDuffXfermode + get() = PorterDuffXfermode(PorterDuff.Mode.CLEAR) + + companion object { + var antialiasing = true + fun arePaintEquals(paint1: Paint, paint2: Paint): Boolean = + paint1.color == paint2.color && paint1.strokeCap == paint2.strokeCap && paint1.isAntiAlias == paint2.isAntiAlias && paint1.strokeJoin == paint2.strokeJoin && paint1.style == paint2.style + } + + override fun setAntialiasing() { + bitmapPaint.isAntiAlias = antialiasing + previewPaint.isAntiAlias = antialiasing + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/tools/implementation/SmudgeTool.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/tools/implementation/SmudgeTool.kt new file mode 100644 index 00000000..1603447d --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/tools/implementation/SmudgeTool.kt @@ -0,0 +1,317 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.tools.implementation + +import android.graphics.Bitmap +import android.graphics.Canvas +import android.graphics.ColorMatrix +import android.graphics.ColorMatrixColorFilter +import android.graphics.Paint +import android.graphics.Path +import android.graphics.PointF +import android.graphics.RectF +import androidx.annotation.VisibleForTesting +import androidx.test.espresso.idling.CountingIdlingResource +import org.catrobat.paintroid.command.CommandManager +import org.catrobat.paintroid.tools.ContextCallback +import org.catrobat.paintroid.tools.ToolPaint +import org.catrobat.paintroid.tools.ToolType +import org.catrobat.paintroid.tools.Workspace +import org.catrobat.paintroid.tools.common.CommonBrushChangedListener +import org.catrobat.paintroid.tools.common.CommonBrushPreviewListener +import org.catrobat.paintroid.tools.options.SmudgeToolOptionsView +import org.catrobat.paintroid.tools.options.ToolOptionsViewController +import kotlin.math.sqrt + +const val PERCENT_100 = 100f +const val BITMAP_ROTATION_FACTOR = -0.0f +const val DEFAULT_PRESSURE_IN_PERCENT = 50 +const val MAX_PRESSURE = 1f +const val MIN_PRESSURE = 0.85f +const val DEFAULT_DRAG_IN_PERCENT = 50 +const val DISTANCE_SMOOTHING = 3f +const val DRAW_THRESHOLD = 0.8f +const val PRESSURE_UPDATE_STEP = 0.004f + +class SmudgeTool( + smudgeToolOptionsView: SmudgeToolOptionsView, + contextCallback: ContextCallback, + toolOptionsViewController: ToolOptionsViewController, + toolPaint: ToolPaint, + workspace: Workspace, + idlingResource: CountingIdlingResource, + commandManager: CommandManager +) : BaseTool(contextCallback, toolOptionsViewController, toolPaint, workspace, idlingResource, commandManager) { + + override var drawTime: Long = 0 + override fun handleUpAnimations(coordinate: PointF?) { + super.handleUp(coordinate) + } + + override fun handleDownAnimations(coordinate: PointF?) { + super.handleDown(coordinate) + } + + private var currentBitmap: Bitmap? = null + private var prevPoint: PointF? = null + private var numOfPointsOnPath = -1 + + @VisibleForTesting + var maxPressure = 0f + + @VisibleForTesting + var pressure = maxPressure + + @VisibleForTesting + var maxSmudgeSize = toolPaint.strokeWidth + + @VisibleForTesting + var minSmudgeSize = 0f + + @VisibleForTesting + val pointArray = mutableListOf() + + override val toolType: ToolType + get() = ToolType.SMUDGE + + init { + smudgeToolOptionsView.setBrushChangedListener(CommonBrushChangedListener(this)) + smudgeToolOptionsView.setBrushPreviewListener( + CommonBrushPreviewListener( + toolPaint, + toolType + ) + ) + smudgeToolOptionsView.setCurrentPaint(toolPaint.paint) + smudgeToolOptionsView.setStrokeCapButtonChecked(toolPaint.strokeCap) + smudgeToolOptionsView.setCallback(object : SmudgeToolOptionsView.Callback { + override fun onPressureChanged(pressure: Int) { + updatePressure(pressure) + } + + override fun onDragChanged(drag: Int) { + updateDrag(drag) + } + }) + + updatePressure(DEFAULT_PRESSURE_IN_PERCENT) + updateDrag(DEFAULT_DRAG_IN_PERCENT) + } + + fun updatePressure(pressureInPercent: Int) { + val onePercent = (MAX_PRESSURE - MIN_PRESSURE) / PERCENT_100 + maxPressure = MIN_PRESSURE + onePercent * pressureInPercent + pressure = maxPressure + } + + fun updateDrag(dragInPercent: Int) { + val onePercent = maxSmudgeSize / PERCENT_100 + minSmudgeSize = onePercent * dragInPercent + } + + override fun handleDown(coordinate: PointF?): Boolean { + coordinate ?: return false + + if (maxSmudgeSize != toolPaint.strokeWidth) { + val ratio = minSmudgeSize / maxSmudgeSize + maxSmudgeSize = toolPaint.strokeWidth + minSmudgeSize = maxSmudgeSize * ratio + } + + val layerBitmap = workspace.bitmapOfCurrentLayer + currentBitmap = Bitmap.createBitmap( + maxSmudgeSize.toInt(), + maxSmudgeSize.toInt(), + Bitmap.Config.ARGB_8888 + ) + currentBitmap?.let { + Canvas(it).apply { + translate(-coordinate.x + maxSmudgeSize / 2f, -coordinate.y + maxSmudgeSize / 2f) + rotate(BITMAP_ROTATION_FACTOR, coordinate.x, coordinate.y) + layerBitmap?.let { bitmap -> + drawBitmap(bitmap, 0f, 0f, null) + } + } + + if (toolPaint.strokeCap == Paint.Cap.ROUND) { + currentBitmap = getBitmapClippedCircle(it) + } + } + + if (!currentBitmapHasColor()) { + currentBitmap?.recycle() + currentBitmap = null + return false + } + + prevPoint = PointF(coordinate.x, coordinate.y) + prevPoint?.apply { + pointArray.add(PointF(x, y)) + } + + return true + } + + override fun handleMove(coordinate: PointF?, shouldAnimate: Boolean): Boolean { + coordinate ?: return false + + if (currentBitmap != null) { + if (pressure < DRAW_THRESHOLD) { // Needed to stop drawing preview when bitmap becomes too transparent. Has no effect on final drawing. + return false + } + + prevPoint?.apply { + val x1 = coordinate.x - x + val y1 = coordinate.y - y + + val distance = (sqrt(x1 * x1 + y1 * y1) / DISTANCE_SMOOTHING).toInt() + val xInterval = x1 / distance + val yInterval = y1 / distance + + repeat(distance) { + x += xInterval + y += yInterval + + pressure -= PRESSURE_UPDATE_STEP + + pointArray.add(PointF(x, y)) + } + } + return true + } else { + return false + } + } + + private fun getBitmapClippedCircle(bitmap: Bitmap): Bitmap { + val width = bitmap.width + val height = bitmap.height + val outputBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888) + val path = Path() + path.addCircle( + (width / 2).toFloat(), + (height / 2).toFloat(), + kotlin.math.min(width, height / 2).toFloat(), + Path.Direction.CCW + ) + val canvas = Canvas(outputBitmap) + canvas.clipPath(path) + canvas.drawBitmap(bitmap, 0f, 0f, null) + return outputBitmap + } + + private fun currentBitmapHasColor(): Boolean { + currentBitmap?.apply { + for (x in 0 until width) { + for (y in 0 until height) { + if (getPixel(x, y) != 0) { + return true + } + } + } + } + return false + } + + override fun handleUp(coordinate: PointF?): Boolean { + coordinate ?: return false + + if (pointArray.isNotEmpty() && currentBitmap != null) { + currentBitmap?.let { + val command = commandFactory.createSmudgePathCommand( + it, + pointArray, + maxPressure, + maxSmudgeSize, + minSmudgeSize + ) + commandManager.addCommand(command) + } + + numOfPointsOnPath = if (numOfPointsOnPath < 0) { + pointArray.size + } else { + (numOfPointsOnPath + pointArray.size) / 2 + } + + pressure = maxPressure + pointArray.clear() + currentBitmap?.recycle() + currentBitmap = null + return true + } else { + return false + } + } + + override fun toolPositionCoordinates(coordinate: PointF): PointF = coordinate + + override fun draw(canvas: Canvas) { + if (pointArray.isNotEmpty()) { + val pointPath = pointArray.toMutableList() + + val step = if (numOfPointsOnPath < 0) { + (maxSmudgeSize - minSmudgeSize) / pointPath.size + } else { + (maxSmudgeSize - minSmudgeSize) / numOfPointsOnPath + } + + var size = maxSmudgeSize + var pressure = maxPressure + val colorMatrix = ColorMatrix() + val paint = Paint() + var bitmap = currentBitmap?.copy(Bitmap.Config.ARGB_8888, false) + + pointPath.forEach { + colorMatrix.setScale(1f, 1f, 1f, pressure) + paint.colorFilter = ColorMatrixColorFilter(colorMatrix) + + val newBitmap = Bitmap.createBitmap( + maxSmudgeSize.toInt(), + maxSmudgeSize.toInt(), + Bitmap.Config.ARGB_8888 + ) + + Canvas(newBitmap).apply { + bitmap?.let { currentBitmap -> + drawBitmap(currentBitmap, 0f, 0f, paint) + } + } + + bitmap?.recycle() + bitmap = newBitmap + + val rect = RectF(-size / 2f, -size / 2f, size / 2f, size / 2f) + with(canvas) { + save() + clipRect(0, 0, workspace.width, workspace.height) + translate(it.x, it.y) + bitmap?.let { currentBitmap -> + drawBitmap(currentBitmap, null, rect, Paint(Paint.DITHER_FLAG)) + } + restore() + } + size -= step + pressure -= PRESSURE_UPDATE_STEP + } + + bitmap?.recycle() + } + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/tools/implementation/WatercolorTool.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/tools/implementation/WatercolorTool.kt new file mode 100644 index 00000000..764eba62 --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/tools/implementation/WatercolorTool.kt @@ -0,0 +1,79 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.tools.implementation + +import android.graphics.BlurMaskFilter +import androidx.test.espresso.idling.CountingIdlingResource +import org.catrobat.paintroid.command.CommandManager +import org.catrobat.paintroid.tools.ContextCallback +import org.catrobat.paintroid.tools.ToolPaint +import org.catrobat.paintroid.tools.ToolType +import org.catrobat.paintroid.tools.Workspace +import org.catrobat.paintroid.tools.options.BrushToolOptionsView +import org.catrobat.paintroid.tools.options.ToolOptionsViewController + +const val MAX_ALPHA_VALUE = 255 +const val MAX_NEW_RANGE = 150 +const val MIN_NEW_RANGE = 20 + +class WatercolorTool( + brushToolOptionsView: BrushToolOptionsView, + contextCallback: ContextCallback, + toolOptionsViewController: ToolOptionsViewController, + toolPaint: ToolPaint, + workspace: Workspace, + idlingResource: CountingIdlingResource, + commandManager: CommandManager, + drawTime: Long +) : BrushTool( + brushToolOptionsView, + contextCallback, + toolOptionsViewController, + toolPaint, + workspace, + idlingResource, + commandManager, + drawTime +) { + override val toolType: ToolType + get() = ToolType.WATERCOLOR + + init { + bitmapPaint.maskFilter = BlurMaskFilter(calcRange(bitmapPaint.alpha), BlurMaskFilter.Blur.INNER) + previewPaint.maskFilter = BlurMaskFilter(calcRange(previewPaint.alpha), BlurMaskFilter.Blur.INNER) + } + + override fun changePaintColor(color: Int, invalidate: Boolean) { + super.changePaintColor(color, invalidate) + + if (invalidate) brushToolOptionsView.invalidate() + bitmapPaint.maskFilter = BlurMaskFilter(calcRange(bitmapPaint.alpha), BlurMaskFilter.Blur.INNER) + previewPaint.maskFilter = BlurMaskFilter(calcRange(previewPaint.alpha), BlurMaskFilter.Blur.INNER) + } + companion object { + fun calcRange(value: Int): Float { + val oldRange = MAX_ALPHA_VALUE + val newRange = MAX_NEW_RANGE - MIN_NEW_RANGE + var newValue = value * newRange / oldRange + MIN_NEW_RANGE + + newValue = MAX_NEW_RANGE - newValue + MIN_NEW_RANGE + return newValue.toFloat() + } + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/tools/options/BrushToolOptionsView.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/tools/options/BrushToolOptionsView.kt new file mode 100644 index 00000000..c86679ed --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/tools/options/BrushToolOptionsView.kt @@ -0,0 +1,57 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.tools.options + +import android.graphics.MaskFilter +import android.graphics.Paint +import android.graphics.Paint.Cap +import android.view.View +import org.catrobat.paintroid.tools.ToolType + +interface BrushToolOptionsView { + fun invalidate() + + fun setCurrentPaint(paint: Paint) + + fun setStrokeCapButtonChecked(strokeCap: Cap) + + fun setBrushChangedListener(onBrushChangedListener: OnBrushChangedListener) + + fun setBrushPreviewListener(onBrushPreviewListener: OnBrushPreviewListener) + + fun getTopToolOptions(): View + + fun getBottomToolOptions(): View + + fun hideCaps() + + interface OnBrushChangedListener { + fun setCap(strokeCap: Cap) + + fun setStrokeWidth(strokeWidth: Int) + } + + interface OnBrushPreviewListener { + val strokeWidth: Float + val strokeCap: Cap + val color: Int + val toolType: ToolType + val maskFilter: MaskFilter? + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/tools/options/BrushToolPreview.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/tools/options/BrushToolPreview.kt new file mode 100644 index 00000000..2cf4572b --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/tools/options/BrushToolPreview.kt @@ -0,0 +1,27 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.tools.options + +import org.catrobat.paintroid.tools.options.BrushToolOptionsView.OnBrushPreviewListener + +interface BrushToolPreview { + fun setListener(callback: OnBrushPreviewListener) + + fun invalidate() +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/tools/options/ClipboardToolOptionsView.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/tools/options/ClipboardToolOptionsView.kt new file mode 100644 index 00000000..77a6255d --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/tools/options/ClipboardToolOptionsView.kt @@ -0,0 +1,41 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.tools.options + +import android.view.View + +interface ClipboardToolOptionsView { + fun setCallback(callback: Callback) + + fun enablePaste(enable: Boolean) + + fun setShapeSizeText(shapeSize: String) + + fun toggleShapeSizeVisibility(isVisible: Boolean) + + fun getClipboardToolOptionsLayout(): View + + interface Callback { + fun copyClicked() + + fun cutClicked() + + fun pasteClicked() + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/tools/options/FillToolOptionsView.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/tools/options/FillToolOptionsView.kt new file mode 100644 index 00000000..ddbe1558 --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/tools/options/FillToolOptionsView.kt @@ -0,0 +1,27 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.tools.options + +interface FillToolOptionsView { + fun setCallback(callback: Callback) + + interface Callback { + fun onColorToleranceChanged(colorTolerance: Int) + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/tools/options/ImportToolOptionsView.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/tools/options/ImportToolOptionsView.kt new file mode 100644 index 00000000..1f5b372e --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/tools/options/ImportToolOptionsView.kt @@ -0,0 +1,27 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2024 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.tools.options + +interface ImportToolOptionsView { + fun setShapeSizeText(shapeSize: String) + + fun setShapeSizeInvisble() + + fun toggleShapeSizeVisibility(isVisible: Boolean) +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/tools/options/ShapeToolOptionsView.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/tools/options/ShapeToolOptionsView.kt new file mode 100644 index 00000000..9e7cb562 --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/tools/options/ShapeToolOptionsView.kt @@ -0,0 +1,47 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.tools.options + +import android.view.View +import org.catrobat.paintroid.tools.drawable.DrawableShape +import org.catrobat.paintroid.tools.drawable.DrawableStyle + +interface ShapeToolOptionsView { + fun setShapeActivated(shape: DrawableShape) + + fun setDrawTypeActivated(drawType: DrawableStyle) + + fun setShapeOutlineWidth(outlineWidth: Int) + + fun setCallback(callback: Callback) + + fun setShapeSizeText(shapeSize: String) + + fun toggleShapeSizeVisibility(isVisible: Boolean) + + fun getShapeToolOptionsLayout(): View + + interface Callback { + fun setToolType(shape: DrawableShape) + + fun setDrawType(drawType: DrawableStyle) + + fun setOutlineWidth(outlineWidth: Int) + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/tools/options/SmudgeToolOptionsView.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/tools/options/SmudgeToolOptionsView.kt new file mode 100644 index 00000000..8ef47759 --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/tools/options/SmudgeToolOptionsView.kt @@ -0,0 +1,29 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.tools.options + +interface SmudgeToolOptionsView : BrushToolOptionsView { + fun setCallback(callback: Callback) + + interface Callback { + fun onPressureChanged(pressure: Int) + + fun onDragChanged(drag: Int) + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/tools/options/SprayToolOptionsView.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/tools/options/SprayToolOptionsView.kt new file mode 100644 index 00000000..5095c960 --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/tools/options/SprayToolOptionsView.kt @@ -0,0 +1,35 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.tools.options + +import android.graphics.Paint + +interface SprayToolOptionsView { + fun setCallback(callback: Callback?) + + fun setRadius(radius: Int) + + fun setCurrentPaint(paint: Paint) + + fun getRadius(): Float + + interface Callback { + fun radiusChanged(radius: Int) + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/tools/options/TextToolOptionsView.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/tools/options/TextToolOptionsView.kt new file mode 100644 index 00000000..948d36d1 --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/tools/options/TextToolOptionsView.kt @@ -0,0 +1,63 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.tools.options + +import android.view.View +import org.catrobat.paintroid.tools.FontType + +interface TextToolOptionsView { + fun setState( + bold: Boolean, + italic: Boolean, + underlined: Boolean, + text: String, + textSize: Int, + fontType: FontType + ) + + fun setCallback(listener: Callback) + + fun hideKeyboard() + + fun showKeyboard() + + fun getTopLayout(): View + + fun getBottomLayout(): View + + fun setShapeSizeText(shapeSize: String) + + fun toggleShapeSizeVisibility(isVisible: Boolean) + + interface Callback { + fun setText(text: String) + + fun setFont(fontType: FontType) + + fun setUnderlined(underlined: Boolean) + + fun setItalic(italic: Boolean) + + fun setBold(bold: Boolean) + + fun setTextSize(size: Int) + + fun hideToolOptions() + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/tools/options/ToolOptionsViewController.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/tools/options/ToolOptionsViewController.kt new file mode 100644 index 00000000..93f049d7 --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/tools/options/ToolOptionsViewController.kt @@ -0,0 +1,48 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.tools.options + +import android.view.View +import android.view.ViewGroup + +interface ToolOptionsViewController : ToolOptionsVisibilityController { + val toolSpecificOptionsLayout: ViewGroup + + fun disable() + + fun enable() + + fun disableHide() + + fun enableHide() + + fun resetToOrigin() + + fun removeToolViews() + + fun showCheckmark() + + fun hideCheckmark() + + fun slideUp(view: View, willHide: Boolean, showOptionsView: Boolean, setViewGone: Boolean = false) + + fun slideDown(view: View, willHide: Boolean, showOptionsView: Boolean, setViewGone: Boolean = false) + + fun animateBottomAndTopNavigation(hide: Boolean) +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/tools/options/ToolOptionsVisibilityController.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/tools/options/ToolOptionsVisibilityController.kt new file mode 100644 index 00000000..c379c138 --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/tools/options/ToolOptionsVisibilityController.kt @@ -0,0 +1,37 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.tools.options + +interface ToolOptionsVisibilityController { + val isVisible: Boolean + + fun hide() + + fun setCallback(callback: Callback) + + fun show(isFullScreen: Boolean = false) + + fun showDelayed() + + interface Callback { + fun onHide() + + fun onShow() + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/tools/options/TransformToolOptionsView.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/tools/options/TransformToolOptionsView.kt new file mode 100644 index 00000000..68b367b0 --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/tools/options/TransformToolOptionsView.kt @@ -0,0 +1,55 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.tools.options + +import org.catrobat.paintroid.ui.tools.NumberRangeFilter + +interface TransformToolOptionsView { + fun setWidthFilter(numberRangeFilter: NumberRangeFilter) + + fun setHeightFilter(numberRangeFilter: NumberRangeFilter) + + fun setCallback(callback: Callback) + + fun setWidth(width: Int) + + fun setHeight(height: Int) + + interface Callback { + fun autoCropClicked() + + fun setCenterClicked() + + fun rotateCounterClockwiseClicked() + + fun rotateClockwiseClicked() + + fun flipHorizontalClicked() + + fun flipVerticalClicked() + + fun applyResizeClicked(resizePercentage: Int) + + fun setBoxWidth(boxWidth: Float) + + fun setBoxHeight(boxHeight: Float) + + fun hideToolOptions() + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/ui/Perspective.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/ui/Perspective.kt new file mode 100644 index 00000000..a04254d4 --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/ui/Perspective.kt @@ -0,0 +1,223 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.ui + +import android.graphics.Canvas +import android.graphics.PointF +import android.graphics.Rect +import androidx.annotation.VisibleForTesting +import org.catrobat.paintroid.MainActivity +import kotlin.jvm.Synchronized +import kotlin.math.max +import kotlin.math.min + +const val MIN_SCALE = 0.1f +const val MAX_SCALE = 100f +private const val SCROLL_BORDER = 50f + +open class Perspective(private var bitmapWidth: Int, private var bitmapHeight: Int) { + @JvmField + var surfaceWidth = 0 + + @JvmField + var surfaceHeight = 0 + + @VisibleForTesting + @JvmField + var surfaceCenterX = 0f + + @VisibleForTesting + @JvmField + var surfaceCenterY = 0f + + @VisibleForTesting + var surfaceScale = 1f + + @JvmField + var surfaceTranslationX = 0f + + @JvmField + var surfaceTranslationY = 0f + + @VisibleForTesting + var initialTranslationY = 0f + + @set:Synchronized + var scale: Float + get() = surfaceScale + set(scale) { + surfaceScale = max(MIN_SCALE, min(MAX_SCALE, scale)) + } + + val scaleForCenterBitmap: Float + get() { + var ratioDependentScale = 0f + val displayHeight: Int? = mainActivity?.resources?.displayMetrics?.heightPixels + var screenSizeRatio = 0f + + if (displayHeight != null) { + screenSizeRatio = surfaceWidth.toFloat() / displayHeight.toFloat() + } + + val bitmapSizeRatio = bitmapWidth.toFloat() / bitmapHeight + if (screenSizeRatio > bitmapSizeRatio) { + if (displayHeight != null) { + ratioDependentScale = displayHeight.toFloat() / bitmapHeight.toFloat() + } + } else { + ratioDependentScale = surfaceWidth.toFloat() / bitmapWidth.toFloat() + } + + ratioDependentScale = min(ratioDependentScale, 1f) + ratioDependentScale = max(ratioDependentScale, MIN_SCALE) + + return ratioDependentScale + } + + // counts to 2 at the start of the app. makes it so that the reset method will + // be called at the start of the app in Drawingsurface.kt surfaceChanged. + var callResetScaleAndTransformationOnStartUp = 0 + private var initialTranslationX = 0f + var oldHeight = 0f + var mainActivity: MainActivity? = null + + @Synchronized + fun setSurfaceFrame(surfaceFrame: Rect) { + if (surfaceHeight == 0) oldHeight = surfaceFrame.bottom.toFloat() + surfaceFrame.apply { + surfaceWidth = right + surfaceCenterX = exactCenterX() + surfaceHeight = bottom + surfaceCenterY = getExactCenterYIgnoreWindowResize(surfaceFrame.exactCenterY()) + } + } + + @Synchronized + fun setBitmapDimensions(width: Int, height: Int) { + bitmapWidth = width + bitmapHeight = height + } + + @Synchronized + fun resetScaleAndTranslation() { + surfaceScale = 1f + if (surfaceWidth == 0 || surfaceHeight == 0) { + surfaceTranslationX = 0f + surfaceTranslationY = 0f + } else { + surfaceTranslationX = surfaceWidth / 2f - bitmapWidth / 2f + initialTranslationX = surfaceTranslationX + surfaceTranslationY = surfaceHeight / 2f - bitmapHeight / 2f + initialTranslationY = surfaceTranslationY + } + val zoomFactor = calculateZoomFactor() + + surfaceScale = scaleForCenterBitmap * zoomFactor + } + + @Synchronized + fun calculateZoomFactor(): Float { + val displayHeight: Int? = mainActivity?.resources?.displayMetrics?.heightPixels + if (bitmapHeight > bitmapWidth) { + if (bitmapHeight > surfaceHeight) { + return 1.0f + } else { + if (displayHeight != null) { + return displayHeight.toFloat() / bitmapHeight.toFloat() + } else { + return surfaceHeight.toFloat() / bitmapHeight.toFloat() + } + } + } else { + if (bitmapWidth >= surfaceWidth) { + return 1.0f + } else { + return surfaceWidth.toFloat() / bitmapWidth.toFloat() + } + } + } + + @Synchronized + fun multiplyScale(factor: Float) { + scale = surfaceScale * factor + } + + @Synchronized + fun translate(dx: Float, dy: Float) { + surfaceTranslationX += dx / surfaceScale + surfaceTranslationY += dy / surfaceScale + val xmax = bitmapWidth / 2f + (surfaceWidth / 2f - SCROLL_BORDER) / surfaceScale + if (surfaceTranslationX > xmax + initialTranslationX) { + surfaceTranslationX = xmax + initialTranslationX + } else if (surfaceTranslationX < -xmax + initialTranslationX) { + surfaceTranslationX = -xmax + initialTranslationX + } + val ymax = bitmapHeight / 2f + (surfaceHeight / 2f - SCROLL_BORDER) / surfaceScale + if (surfaceTranslationY > ymax + initialTranslationY) { + surfaceTranslationY = ymax + initialTranslationY + } else if (surfaceTranslationY < -ymax + initialTranslationY) { + surfaceTranslationY = -ymax + initialTranslationY + } + } + + @Synchronized + fun convertToCanvasFromSurface(surfacePoint: PointF) { + surfacePoint.x = + (surfacePoint.x - surfaceCenterX) / surfaceScale + surfaceCenterX - surfaceTranslationX + surfacePoint.y = + (surfacePoint.y - surfaceCenterY) / surfaceScale + surfaceCenterY - surfaceTranslationY + } + + @Synchronized + fun convertToSurfaceFromCanvas(canvasPoint: PointF) { + canvasPoint.x = + (canvasPoint.x + surfaceTranslationX - surfaceCenterX) * surfaceScale + surfaceCenterX + canvasPoint.y = + (canvasPoint.y + surfaceTranslationY - surfaceCenterY) * surfaceScale + surfaceCenterY + } + + @Synchronized + fun getCanvasPointFromSurfacePoint(surfacePoint: PointF): PointF { + val canvasPoint = PointF(surfacePoint.x, surfacePoint.y) + convertToCanvasFromSurface(canvasPoint) + return canvasPoint + } + + @Synchronized + fun getSurfacePointFromCanvasPoint(canvasPoint: PointF): PointF { + val surfacePoint = PointF(canvasPoint.x, canvasPoint.y) + convertToSurfaceFromCanvas(surfacePoint) + return surfacePoint + } + + @Synchronized + fun applyToCanvas(canvas: Canvas) { + canvas.scale(surfaceScale, surfaceScale, surfaceCenterX, surfaceCenterY) + canvas.translate(surfaceTranslationX, surfaceTranslationY) + } + + private fun getExactCenterYIgnoreWindowResize(actualExactCenterY: Float): Float { + var exactCenterYIgnoreWindowResize = if (surfaceCenterY != 0.0f && surfaceCenterY > actualExactCenterY) { + surfaceCenterY + } else { + actualExactCenterY + } + return exactCenterYIgnoreWindowResize + } +} diff --git a/android/app/src/main/kotlin/org/catrobat/paintroid/ui/tools/NumberRangeFilter.kt b/android/app/src/main/kotlin/org/catrobat/paintroid/ui/tools/NumberRangeFilter.kt new file mode 100644 index 00000000..9aa81e41 --- /dev/null +++ b/android/app/src/main/kotlin/org/catrobat/paintroid/ui/tools/NumberRangeFilter.kt @@ -0,0 +1,35 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.catrobat.paintroid.ui.tools + +import android.text.InputFilter +import android.text.Spanned + +interface NumberRangeFilter : InputFilter { + var max: Int + + override fun filter( + source: CharSequence, + start: Int, + end: Int, + dest: Spanned, + dstart: Int, + dend: Int + ): CharSequence? +} diff --git a/android/app/src/main/res/drawable/ic_pocketpaint_layers.xml b/android/app/src/main/res/drawable/ic_pocketpaint_layers.xml new file mode 100644 index 00000000..7173d0ba --- /dev/null +++ b/android/app/src/main/res/drawable/ic_pocketpaint_layers.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/android/app/src/main/res/drawable/ic_pocketpaint_redo.xml b/android/app/src/main/res/drawable/ic_pocketpaint_redo.xml new file mode 100644 index 00000000..8125a34f --- /dev/null +++ b/android/app/src/main/res/drawable/ic_pocketpaint_redo.xml @@ -0,0 +1,9 @@ + + + diff --git a/android/app/src/main/res/drawable/ic_pocketpaint_tool_brush.xml b/android/app/src/main/res/drawable/ic_pocketpaint_tool_brush.xml new file mode 100644 index 00000000..eae56253 --- /dev/null +++ b/android/app/src/main/res/drawable/ic_pocketpaint_tool_brush.xml @@ -0,0 +1,9 @@ + + + diff --git a/android/app/src/main/res/drawable/ic_pocketpaint_tool_center_focus_strong.xml b/android/app/src/main/res/drawable/ic_pocketpaint_tool_center_focus_strong.xml new file mode 100644 index 00000000..04da4972 --- /dev/null +++ b/android/app/src/main/res/drawable/ic_pocketpaint_tool_center_focus_strong.xml @@ -0,0 +1,12 @@ + + + + diff --git a/android/app/src/main/res/drawable/ic_pocketpaint_tool_circle.png b/android/app/src/main/res/drawable/ic_pocketpaint_tool_circle.png new file mode 100644 index 00000000..020878ad Binary files /dev/null and b/android/app/src/main/res/drawable/ic_pocketpaint_tool_circle.png differ diff --git a/android/app/src/main/res/drawable/ic_pocketpaint_tool_clipboard.xml b/android/app/src/main/res/drawable/ic_pocketpaint_tool_clipboard.xml new file mode 100644 index 00000000..3cf755a3 --- /dev/null +++ b/android/app/src/main/res/drawable/ic_pocketpaint_tool_clipboard.xml @@ -0,0 +1,9 @@ + + + \ No newline at end of file diff --git a/android/app/src/main/res/drawable/ic_pocketpaint_tool_clipping.xml b/android/app/src/main/res/drawable/ic_pocketpaint_tool_clipping.xml new file mode 100644 index 00000000..4778f274 --- /dev/null +++ b/android/app/src/main/res/drawable/ic_pocketpaint_tool_clipping.xml @@ -0,0 +1,7 @@ + + + \ No newline at end of file diff --git a/android/app/src/main/res/drawable/ic_pocketpaint_tool_cursor.xml b/android/app/src/main/res/drawable/ic_pocketpaint_tool_cursor.xml new file mode 100644 index 00000000..9abf694b --- /dev/null +++ b/android/app/src/main/res/drawable/ic_pocketpaint_tool_cursor.xml @@ -0,0 +1,31 @@ + + + + + + + diff --git a/android/app/src/main/res/drawable/ic_pocketpaint_tool_eraser.xml b/android/app/src/main/res/drawable/ic_pocketpaint_tool_eraser.xml new file mode 100644 index 00000000..66c9e1ff --- /dev/null +++ b/android/app/src/main/res/drawable/ic_pocketpaint_tool_eraser.xml @@ -0,0 +1,9 @@ + + + diff --git a/android/app/src/main/res/drawable/ic_pocketpaint_tool_fill.xml b/android/app/src/main/res/drawable/ic_pocketpaint_tool_fill.xml new file mode 100644 index 00000000..566b535b --- /dev/null +++ b/android/app/src/main/res/drawable/ic_pocketpaint_tool_fill.xml @@ -0,0 +1,9 @@ + + + diff --git a/android/app/src/main/res/drawable/ic_pocketpaint_tool_flip_horizontal.xml b/android/app/src/main/res/drawable/ic_pocketpaint_tool_flip_horizontal.xml new file mode 100644 index 00000000..43b4a196 --- /dev/null +++ b/android/app/src/main/res/drawable/ic_pocketpaint_tool_flip_horizontal.xml @@ -0,0 +1,9 @@ + + + diff --git a/android/app/src/main/res/drawable/ic_pocketpaint_tool_flip_vertical.xml b/android/app/src/main/res/drawable/ic_pocketpaint_tool_flip_vertical.xml new file mode 100644 index 00000000..8bf61990 --- /dev/null +++ b/android/app/src/main/res/drawable/ic_pocketpaint_tool_flip_vertical.xml @@ -0,0 +1,9 @@ + + + diff --git a/android/app/src/main/res/drawable/ic_pocketpaint_tool_hand.xml b/android/app/src/main/res/drawable/ic_pocketpaint_tool_hand.xml new file mode 100644 index 00000000..829d3e9b --- /dev/null +++ b/android/app/src/main/res/drawable/ic_pocketpaint_tool_hand.xml @@ -0,0 +1,12 @@ + + + + diff --git a/android/app/src/main/res/drawable/ic_pocketpaint_tool_import.xml b/android/app/src/main/res/drawable/ic_pocketpaint_tool_import.xml new file mode 100644 index 00000000..804484a0 --- /dev/null +++ b/android/app/src/main/res/drawable/ic_pocketpaint_tool_import.xml @@ -0,0 +1,9 @@ + + + diff --git a/android/app/src/main/res/drawable/ic_pocketpaint_tool_line.xml b/android/app/src/main/res/drawable/ic_pocketpaint_tool_line.xml new file mode 100644 index 00000000..691c964e --- /dev/null +++ b/android/app/src/main/res/drawable/ic_pocketpaint_tool_line.xml @@ -0,0 +1,12 @@ + + + diff --git a/android/app/src/main/res/drawable/ic_pocketpaint_tool_pipette.xml b/android/app/src/main/res/drawable/ic_pocketpaint_tool_pipette.xml new file mode 100644 index 00000000..9b254c71 --- /dev/null +++ b/android/app/src/main/res/drawable/ic_pocketpaint_tool_pipette.xml @@ -0,0 +1,9 @@ + + + diff --git a/android/app/src/main/res/drawable/ic_pocketpaint_tool_rectangle.xml b/android/app/src/main/res/drawable/ic_pocketpaint_tool_rectangle.xml new file mode 100644 index 00000000..4939b243 --- /dev/null +++ b/android/app/src/main/res/drawable/ic_pocketpaint_tool_rectangle.xml @@ -0,0 +1,14 @@ + + + + diff --git a/android/app/src/main/res/drawable/ic_pocketpaint_tool_resize_adjust.xml b/android/app/src/main/res/drawable/ic_pocketpaint_tool_resize_adjust.xml new file mode 100644 index 00000000..6ec0ddbc --- /dev/null +++ b/android/app/src/main/res/drawable/ic_pocketpaint_tool_resize_adjust.xml @@ -0,0 +1,9 @@ + + + diff --git a/android/app/src/main/res/drawable/ic_pocketpaint_tool_rotate_left.xml b/android/app/src/main/res/drawable/ic_pocketpaint_tool_rotate_left.xml new file mode 100644 index 00000000..19ab4aa6 --- /dev/null +++ b/android/app/src/main/res/drawable/ic_pocketpaint_tool_rotate_left.xml @@ -0,0 +1,24 @@ + + + + + + + + diff --git a/android/app/src/main/res/drawable/ic_pocketpaint_tool_smudge.xml b/android/app/src/main/res/drawable/ic_pocketpaint_tool_smudge.xml new file mode 100644 index 00000000..4f772765 --- /dev/null +++ b/android/app/src/main/res/drawable/ic_pocketpaint_tool_smudge.xml @@ -0,0 +1,12 @@ + + + \ No newline at end of file diff --git a/android/app/src/main/res/drawable/ic_pocketpaint_tool_spray_can.xml b/android/app/src/main/res/drawable/ic_pocketpaint_tool_spray_can.xml new file mode 100644 index 00000000..597e942c --- /dev/null +++ b/android/app/src/main/res/drawable/ic_pocketpaint_tool_spray_can.xml @@ -0,0 +1,44 @@ + + + + + + + diff --git a/android/app/src/main/res/drawable/ic_pocketpaint_tool_square.png b/android/app/src/main/res/drawable/ic_pocketpaint_tool_square.png new file mode 100644 index 00000000..c7c0a45a Binary files /dev/null and b/android/app/src/main/res/drawable/ic_pocketpaint_tool_square.png differ diff --git a/android/app/src/main/res/drawable/ic_pocketpaint_tool_text.xml b/android/app/src/main/res/drawable/ic_pocketpaint_tool_text.xml new file mode 100644 index 00000000..751b78c9 --- /dev/null +++ b/android/app/src/main/res/drawable/ic_pocketpaint_tool_text.xml @@ -0,0 +1,9 @@ + + + diff --git a/android/app/src/main/res/drawable/ic_pocketpaint_tool_transform.xml b/android/app/src/main/res/drawable/ic_pocketpaint_tool_transform.xml new file mode 100644 index 00000000..abf0ce8a --- /dev/null +++ b/android/app/src/main/res/drawable/ic_pocketpaint_tool_transform.xml @@ -0,0 +1,9 @@ + + + diff --git a/android/app/src/main/res/drawable/ic_pocketpaint_tool_watercolor.xml b/android/app/src/main/res/drawable/ic_pocketpaint_tool_watercolor.xml new file mode 100644 index 00000000..17042928 --- /dev/null +++ b/android/app/src/main/res/drawable/ic_pocketpaint_tool_watercolor.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + diff --git a/android/app/src/main/res/drawable/ic_pocketpaint_undo.xml b/android/app/src/main/res/drawable/ic_pocketpaint_undo.xml new file mode 100644 index 00000000..c7e9eddc --- /dev/null +++ b/android/app/src/main/res/drawable/ic_pocketpaint_undo.xml @@ -0,0 +1,9 @@ + + + diff --git a/android/app/src/main/res/drawable/pocketpaint_checkeredbg.png b/android/app/src/main/res/drawable/pocketpaint_checkeredbg.png new file mode 100644 index 00000000..d31b4a8f Binary files /dev/null and b/android/app/src/main/res/drawable/pocketpaint_checkeredbg.png differ diff --git a/android/app/src/main/res/font/dubai.ttf b/android/app/src/main/res/font/dubai.ttf new file mode 100644 index 00000000..9d9cea18 Binary files /dev/null and b/android/app/src/main/res/font/dubai.ttf differ diff --git a/android/app/src/main/res/font/stc_regular.otf b/android/app/src/main/res/font/stc_regular.otf new file mode 100644 index 00000000..efb515fd Binary files /dev/null and b/android/app/src/main/res/font/stc_regular.otf differ diff --git a/android/app/src/main/res/values/colors.xml b/android/app/src/main/res/values/colors.xml index e4f6f88a..d58b68f2 100644 --- a/android/app/src/main/res/values/colors.xml +++ b/android/app/src/main/res/values/colors.xml @@ -1,4 +1,31 @@ #bff8fb + + #138293 + #0D6775 + #157DA2 + #99157DA2 + #BD5800 + + + #c8e8ff + #007A99 + #00000000 + #80000000 + #D3D3D3 + #99555555 + #9933B5E5 + #99E56B33 + #555555 + + + #ffffffff + #64ffffff + + #DFDADA + + #33ac86 + + #33B5E5 \ No newline at end of file diff --git a/android/app/src/main/res/values/string.xml b/android/app/src/main/res/values/string.xml new file mode 100644 index 00000000..280f901d --- /dev/null +++ b/android/app/src/main/res/values/string.xml @@ -0,0 +1,253 @@ + + + + Pocket Paint + Brush + Cursor + Pipette + Undo + Redo + Information + Clipboard + Import image + Eraser + Transform + Fill + Line + Text + Shapes + Hand + Watercolor + Spray can + Smudge + Clip area + Apply + Checkmark + Done + + Gallery + + Stickers not available, check your internet connection. + + Paste + Copy + Cut + + Stickers + @string/button_import_image + @string/menu_save_image + Tools + Error load/save file + Check image or SD-card! + Stroke width + Save changes? + Help + Remove parts of the image like with an eraser. + Tap on the symbols on the bottom bar to change the color or the brush size. + Similar to the brush tool with a watercolor effect. However you can also change the strength of the brush with the slider in the color menu. + Tap on the image to select a color. + Tap to undo your previous action. + Tap to redo an undone action. + Tap on the image to fill an area with the selected color. + Position the cursor where you want to draw. Tap to activate the cursor. Move your finger to draw. Tap again to deactivate. + Use to transform the image. + Move and resize the rectangle to cover the area you want to stamp. Tap on copy or cut to select the area. Move it, then tap on paste to stamp. + Import an image from the gallery to the clipboard. + Draw a straight line. + Write text and format it. Resize the text box afterwards. Tap on the checkmark to insert the text on the image. + Choose a shape and tap on the checkmark to insert the selected shape. + Create new layers or modify existing ones. + Select or adjust a color. + Move your finger to move the canvas. + Move your finger on the image to create a spray can pattern. + Move your finger on the image on different drawings to smudge them. + Mark area which should not be erased. + Quit + Save changes? + New image + Discard image + You are only able to merge or reorder if all layers are visible + No tools are available on hidden layer + Load image + Replace image + Add to current layer + Save image + Save copy + Hide buttons + Share image + Send image via + @string/pocketpaint_about_title + Rate us! + Export + Feedback + Advanced settings + Zoom window settings + Image saved to\n + Image saved + Copy saved to\n + Copy saved + Quit + Save + Discard + Cancel + Overwrite + + nothing to resize + cannot resize to this size + max image resolution reached + + U + I + B + Tap here to write + Monospace + Serif + Sans Serif + Dubai + STC + + Rectangle + Ellipse + Star + Heart + Fill + Outline + + Color tolerance + + Pressure + Drag + + rotate left + rotate right + flip vertical + flip horizontal + resize + crop/enlarge + Width + Height + Auto + Set center + px + + Tap on copy to copy content + + Layers + New layer + Delete layer + Too many layers + Layers merged + Layer background + Layer preview + + Error on loading image + Not a valid image + Settings + + Image name + Image format + + Antialiasing + Smoothing + + Enabled + 100 + 300 + 100 + + Quality: + + jpg + Takes up minimal storage space. No transparency is remembered. + png + Lossless compression. Transparency is preserved. + Ora + This format remembers layers. It can be opened by apps that support the Openraster format. + catrobat-image + Pocket Paint\'s native image format. This format remembers commands and layers. + + + This app needs the requested permission to function properly. In order to save images to the local memory, the app needs read and write access to it. + This app needs the requested permission to function properly. In order to save images to the local memory, the app needs read and write access to it. + As you have denied permission with do not ask again, please go to your phone settings and grant the required permissions if you wish to use the associated functions. + + Tap the screen to define the new center position. + Drag edges to their new position, tap on the checkmark to enlarge or crop the image area. + Pan to position, then tap to start painting. + Pan to draw, then tap again to stop painting. + Welcome To Pocket Paint + With Pocket Paint there are no limits to your creativity. If you are new, start the intro, or skip it if you are already familiar with Pocket Paint. + Tap on a tool to get more information + More possibilities + Use the top bar to open the overflow menu and to undo or redo changes + Landscape + Pocket Paint also supports drawing in landscape mode to give you the best painting experience. + You are all set. Enjoy Pocket Paint. + Get started and create a new masterpiece. + Let\'s go + Next + Skip + + About + Version %s + GNU Affero General Public License, v3 + Pocket Paint is a picture editing library that is part of the Catrobat project.\n\nCatrobat is a visual programming language and set of creativity tools for smartphones.\n\nThe source code of Pocket Paint is mainly licensed under the %s.\nFor precise details of the license see the link below. + Pocket Paint source code license + About Catrobat + <a href=\"https://catrob.at/licenses\">%s</a> + <a href=\"https://catrobat.org/\">%s</a> + + Intro + + Intro does not support split screen. + + Overwrite File? + You are about to overwrite an existing image (hint: To save it as a new image, use the \"%s\" option). Save anyway? + + Do you like Pocket Paint? + Would you like to rate Pocket Paint? + We are sorry to hear that. If you want to share your experience with us, please write to contact@catrobat.org + Yes + No + @android:string/ok + Cancel + Not now + Rate Pocket Paint + + Tools + Current + Color + Layers + Item for bottom navigation + + Switch to the tool you want to use. + Shows the currently used tool and opens its options. + Shows the currently used color and opens the color picker. + Opens the layer menu and lets you manage your layers. + + Current tool icon + + Image is too big to load + The image is too big to load. Tap OK to scale down the image automatically. + + Used to display a zoomed in part of the drawing surface + + diff --git a/lib/core/commands/command_implementation/graphic/path_command.dart b/lib/core/commands/command_implementation/graphic/path_command.dart index a26fff85..57dd899a 100644 --- a/lib/core/commands/command_implementation/graphic/path_command.dart +++ b/lib/core/commands/command_implementation/graphic/path_command.dart @@ -41,7 +41,7 @@ class PathCommand extends GraphicCommand { Map toJson() => _$PathCommandToJson(this); factory PathCommand.fromJson(Map json) { - int version = json['version'] as int; + int version = json['version'] as int; // TODO switch (version) { case Version.v1: diff --git a/lib/core/json_serialization/converter/paint_converter.dart b/lib/core/json_serialization/converter/paint_converter.dart index 5a41ef4f..220b63c3 100644 --- a/lib/core/json_serialization/converter/paint_converter.dart +++ b/lib/core/json_serialization/converter/paint_converter.dart @@ -21,8 +21,12 @@ class PaintConverter implements JsonConverter> { paint.strokeJoin = StrokeJoin.values[json['strokeJoin']]; paint.blendMode = BlendMode.values[json['blendMode']]; } - if (version >= Version.v2) { + if (version == Version.v2) { // paint.newAttribute = json['newAttribute']; + } + if ( version == Version.v3) + { + } return paint; } diff --git a/lib/core/models/catrobat_image.dart b/lib/core/models/catrobat_image.dart index 4d0a3232..02350c3e 100644 --- a/lib/core/models/catrobat_image.dart +++ b/lib/core/models/catrobat_image.dart @@ -25,7 +25,7 @@ class CatrobatImage { this.backgroundImage, { int? version, this.magicValue = 'CATROBAT', - }) : version = version ?? + }) : version = version ?? // here should be 3 VersionStrategyManager.strategy.getCatrobatImageVersion(); Uint8List toBytes() { diff --git a/lib/core/providers/object/load_image_from_file_manager.dart b/lib/core/providers/object/load_image_from_file_manager.dart index 849224ad..14b4f5f8 100644 --- a/lib/core/providers/object/load_image_from_file_manager.dart +++ b/lib/core/providers/object/load_image_from_file_manager.dart @@ -3,6 +3,7 @@ import 'dart:io'; import 'dart:typed_data'; import 'dart:ui'; +import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:oxidized/oxidized.dart'; @@ -16,6 +17,8 @@ import 'package:paintroid/core/utils/failure.dart'; import 'package:paintroid/core/utils/load_image_failure.dart'; import 'package:paintroid/core/utils/save_image_failure.dart'; +import 'native_catrobat_service.dart'; + extension on File { String? get extension { final list = path.split('.'); @@ -28,16 +31,18 @@ class LoadImageFromFileManager with LoggableMixin { final IFileService fileService; final IImageService imageService; final IPermissionService permissionService; + final INativeCatrobatService nativeCatrobatService; LoadImageFromFileManager( - this.fileService, this.imageService, this.permissionService); + this.fileService, this.imageService, this.permissionService, this.nativeCatrobatService); static final provider = Provider((ref) { final imageService = ref.watch(IImageService.provider); final fileService = ref.watch(IFileService.provider); final permissionService = ref.watch(IPermissionService.provider); + final nativeService = ref.watch(INativeCatrobatService.provider); return LoadImageFromFileManager( - fileService, imageService, permissionService); + fileService, imageService, permissionService,nativeService); }); Future> call( @@ -60,6 +65,16 @@ class LoadImageFromFileManager with LoggableMixin { .map((img) => ImageFromFile.rasterImage(img)); case 'catrobat-image': Uint8List bytes = await file.readAsBytes(); + // check for json + // if json + // file.path + var fileValidity = checkJson(bytes); + if(!fileValidity) + { + final ByteData result = await nativeCatrobatService.getNativeClassData(file.uri.path); + var t = 10; + return const Result.err(LoadImageFailure.invalidImage); + } CatrobatImage catrobatImage = CatrobatImage.fromBytes(bytes); Image? backgroundImage = await rebuildBackgroundImage(catrobatImage); @@ -79,6 +94,16 @@ class LoadImageFromFileManager with LoggableMixin { } }); } + bool checkJson(Uint8List bytes) + { + try { + String jsonString = utf8.decode(bytes); + Map jsonMap = json.decode(jsonString); + return true; + } catch (e) { + return false; + } + } Future rebuildBackgroundImage(CatrobatImage catrobatImage) async { if (catrobatImage.backgroundImage.isNotEmpty) { diff --git a/lib/core/providers/object/native_catrobat_service.dart b/lib/core/providers/object/native_catrobat_service.dart new file mode 100644 index 00000000..46bad400 --- /dev/null +++ b/lib/core/providers/object/native_catrobat_service.dart @@ -0,0 +1,44 @@ +import 'dart:io'; +import 'package:flutter/services.dart'; + +import 'package:flutter_riverpod/flutter_riverpod.dart'; + + +abstract class INativeCatrobatService{ + Future getNativeClassData(String parameter); + + static final provider = Provider((ref) + { + const channel= MethodChannel('org.catrobat.paintroid/native'); + return NativeCatrobatService(channel); + }); +} + + + + + +class NativeCatrobatService implements INativeCatrobatService { + NativeCatrobatService(this._methodChannel); + + final MethodChannel _methodChannel; + @override + Future getNativeClassData(String parameter) async { + if(Platform.isAndroid) { + try { + final Uint8List bytes = await _methodChannel.invokeMethod('getNativeClassData', {'path': parameter}); + final ByteData data = ByteData.view(bytes.buffer); + return data; + } catch (e) { + print('Failed to get native class data: $e'); + throw e; // Re-throw to allow caller to handle the exception. + } + } + throw UnimplementedError(); + } + +} + + + +