diff --git a/src/main/c/VipsImage.c b/src/main/c/VipsImage.c index d9f1def9..de0361b7 100644 --- a/src/main/c/VipsImage.c +++ b/src/main/c/VipsImage.c @@ -229,6 +229,42 @@ Java_com_criteo_vips_VipsImage_thumbnailImageNative(JNIEnv *env, jobject obj, ji g_object_unref(im); } +JNIEXPORT void JNICALL +Java_com_criteo_vips_VipsImage_thumbnailImageWithOptionsNative(JNIEnv *env, jobject obj, jint width, jint height, jobject options) +{ + VipsImage *im = (VipsImage *) (*env)->GetLongField(env, obj, handle_fid); + VipsImage *out = NULL; + + jclass optionsCls = (*env)->GetObjectClass(env, options); + jfieldID sizeFid = (*env)->GetFieldID(env, optionsCls, "size", "I"); + jfieldID noRotateFid = (*env)->GetFieldID(env, optionsCls, "noRotate", "Z"); + jfieldID cropFid = (*env)->GetFieldID(env, optionsCls, "crop", "I"); + jfieldID linearFid = (*env)->GetFieldID(env, optionsCls, "linear", "Z"); + jfieldID importProfileFid = (*env)->GetFieldID(env, optionsCls, "importProfile", "Ljava/lang/String;"); + jfieldID exportProfileFid = (*env)->GetFieldID(env, optionsCls, "exportProfile", "Ljava/lang/String;"); + jfieldID intentFid = (*env)->GetFieldID(env, optionsCls, "intent", "I"); + + jint size = (*env)->GetIntField(env, options, sizeFid); + jboolean noRotate = (*env)->GetBooleanField(env, options, noRotateFid); + jint crop = (*env)->GetIntField(env, options, cropFid); + jboolean linear = (*env)->GetBooleanField(env, options, linearFid); + jstring importProfile = (jstring) (*env)->GetObjectField(env, options, importProfileFid); // TODO how to pass if set, as there is no default + jstring exportProfile = (jstring) (*env)->GetObjectField(env, options, exportProfileFid); // TODO how to pass if set, as there is no default + jint intent = (*env)->GetIntField(env, options, intentFid); + + VipsSize vipsSize = size != -1 ? size : VIPS_SIZE_BOTH; + VipsInteresting vipsCrop = crop != -1 ? crop : VIPS_INTERESTING_NONE; + VipsIntent vipsIntent = intent != -1 ? intent : VIPS_INTENT_RELATIVE; + + if (vips_thumbnail_image(im, &out, width, "height", height, "size", vipsSize, "no-rotate", noRotate, "crop", vipsCrop, "linear", linear, "intent", intent, NULL)) + { + throwVipsException(env, "Unable to make thumbnail image"); + return; + } + (*env)->SetLongField(env, obj, handle_fid, (jlong) out); + g_object_unref(im); +} + JNIEXPORT jobject JNICALL Java_com_criteo_vips_VipsImage_thumbnailNative(JNIEnv *env, jclass cls, jstring filename, jint width, jint height, jboolean scale) { @@ -244,6 +280,41 @@ Java_com_criteo_vips_VipsImage_thumbnailNative(JNIEnv *env, jclass cls, jstring return (*env)->NewObject(env, cls, ctor_mid, (jlong) out); } +JNIEXPORT jobject JNICALL +Java_com_criteo_vips_VipsImage_thumbnailWithImageNative(JNIEnv *env, jclass cls, jstring filename, jint width, jint height, jobject options) +{ + VipsImage *out = NULL; + const char *name = (*env)->GetStringUTFChars(env, filename, NULL); + + jclass optionsCls = (*env)->GetObjectClass(env, options); + jfieldID sizeFid = (*env)->GetFieldID(env, optionsCls, "size", "I"); + jfieldID noRotateFid = (*env)->GetFieldID(env, optionsCls, "noRotate", "Z"); + jfieldID cropFid = (*env)->GetFieldID(env, optionsCls, "crop", "I"); + jfieldID linearFid = (*env)->GetFieldID(env, optionsCls, "linear", "Z"); + jfieldID importProfileFid = (*env)->GetFieldID(env, optionsCls, "importProfile", "Ljava/lang/String;"); + jfieldID exportProfileFid = (*env)->GetFieldID(env, optionsCls, "exportProfile", "Ljava/lang/String;"); + jfieldID intentFid = (*env)->GetFieldID(env, optionsCls, "intent", "I"); + + jint size = (*env)->GetIntField(env, options, sizeFid); + jboolean noRotate = (*env)->GetBooleanField(env, options, noRotateFid); + jint crop = (*env)->GetIntField(env, options, cropFid); + jboolean linear = (*env)->GetBooleanField(env, options, linearFid); + jstring importProfile = (jstring) (*env)->GetObjectField(env, options, importProfileFid); // TODO how to pass if set, as there is no default + jstring exportProfile = (jstring) (*env)->GetObjectField(env, options, exportProfileFid); // TODO how to pass if set, as there is no default + jint intent = (*env)->GetIntField(env, options, intentFid); + + VipsSize vipsSize = size != -1 ? size : VIPS_SIZE_BOTH; + VipsInteresting vipsCrop = crop != -1 ? crop : VIPS_INTERESTING_NONE; + VipsIntent vipsIntent = intent != -1 ? intent : VIPS_INTENT_RELATIVE; + + if (vips_thumbnail(name, &out, width, "height", height, "size", vipsSize, "no-rotate", noRotate, "crop", vipsCrop, "linear", linear, "intent", intent, NULL)) + { + throwVipsException(env, "Unable to make thumbnail"); + } + (*env)->ReleaseStringUTFChars(env, filename, name); + return (*env)->NewObject(env, cls, ctor_mid, (jlong) out); +} + JNIEXPORT void JNICALL Java_com_criteo_vips_VipsImage_resizeNative(JNIEnv *env, jobject obj, jdouble hscale, jdouble vscale, jint kernel) { diff --git a/src/main/c/VipsImage.h b/src/main/c/VipsImage.h index a953fa91..32c091bc 100644 --- a/src/main/c/VipsImage.h +++ b/src/main/c/VipsImage.h @@ -135,6 +135,14 @@ JNIEXPORT void JNICALL Java_com_criteo_vips_VipsImage_histFindNdimNative JNIEXPORT void JNICALL Java_com_criteo_vips_VipsImage_thumbnailImageNative (JNIEnv *, jobject, jint, jint, jboolean); +/* + * Class: com_criteo_vips_VipsImage + * Method: thumbnailImageWithOptionsNative + * Signature: (IILcom/criteo/vips/options/ThumbnailOptions;)V + */ +JNIEXPORT void JNICALL Java_com_criteo_vips_VipsImage_thumbnailImageWithOptionsNative + (JNIEnv *, jobject, jint, jint, jobject); + /* * Class: com_criteo_vips_VipsImage * Method: thumbnailNative @@ -143,6 +151,14 @@ JNIEXPORT void JNICALL Java_com_criteo_vips_VipsImage_thumbnailImageNative JNIEXPORT jobject JNICALL Java_com_criteo_vips_VipsImage_thumbnailNative (JNIEnv *, jclass, jstring, jint, jint, jboolean); +/* + * Class: com_criteo_vips_VipsImage + * Method: thumbnailWithOptionsNative + * Signature: (Ljava/lang/String;IILcom/criteo/vips/options/ThumbnailOptions;)Lcom/criteo/vips/VipsImage; + */ +JNIEXPORT jobject JNICALL Java_com_criteo_vips_VipsImage_thumbnailWithOptionsNative + (JNIEnv *, jclass, jstring, jint, jint, jobject); + /* * Class: com_criteo_vips_VipsImage * Method: resizeNative diff --git a/src/main/java/com/criteo/vips/CMakeLists.txt b/src/main/java/com/criteo/vips/CMakeLists.txt index 75b66565..d44f978b 100644 --- a/src/main/java/com/criteo/vips/CMakeLists.txt +++ b/src/main/java/com/criteo/vips/CMakeLists.txt @@ -11,8 +11,9 @@ set(JAVA_SOURCE_FILES "${JAVA_SOURCE_DIRECTORY}/Image.java" "${JAVA_SOURCE_DIRECTORY}/VipsImage.java") file(GLOB JAVA_ENUM_SOURCE_FILES "${JAVA_SOURCE_DIRECTORY}/enums/*.java") +file(GLOB JAVA_OPTIONS_SOURCE_FILES "${JAVA_SOURCE_DIRECTORY}/options/*.java") -add_jar(JVipsWrapper ${JAVA_ENUM_SOURCE_FILES} ${JAVA_SOURCE_FILES}) +add_jar(JVipsWrapper ${JAVA_ENUM_SOURCE_FILES} ${JAVA_OPTIONS_SOURCE_FILES} ${JAVA_SOURCE_FILES}) file(REMOVE ${JNI_HEADER_DIRECTORY}/Vips.h ${JNI_HEADER_DIRECTORY}/VipsContext.h diff --git a/src/main/java/com/criteo/vips/Image.java b/src/main/java/com/criteo/vips/Image.java index 1e76a775..f0a7d4db 100644 --- a/src/main/java/com/criteo/vips/Image.java +++ b/src/main/java/com/criteo/vips/Image.java @@ -17,6 +17,7 @@ package com.criteo.vips; import com.criteo.vips.enums.*; +import com.criteo.vips.options.ThumbnailOptions; import java.awt.*; @@ -138,6 +139,8 @@ public interface Image extends AutoCloseable { */ void thumbnailImage(Dimension dimension, boolean scale) throws VipsException; + void thumbnailImage(Dimension dimension, ThumbnailOptions options) throws VipsException; + /** * Make a thumbnail of this VipsImage with new target dimension * @@ -148,6 +151,8 @@ public interface Image extends AutoCloseable { */ void thumbnailImage(int width, int height, boolean scale) throws VipsException; + void thumbnailImage(int width, int height, ThumbnailOptions options) throws VipsException; + /** * Make a thumbnail of this VipsImage with new target dimension * diff --git a/src/main/java/com/criteo/vips/VipsImage.java b/src/main/java/com/criteo/vips/VipsImage.java index 984a5c88..d0c6c508 100644 --- a/src/main/java/com/criteo/vips/VipsImage.java +++ b/src/main/java/com/criteo/vips/VipsImage.java @@ -17,6 +17,7 @@ package com.criteo.vips; import com.criteo.vips.enums.*; +import com.criteo.vips.options.ThumbnailOptions; import java.awt.*; import java.nio.ByteBuffer; @@ -152,14 +153,27 @@ public void thumbnailImage(Dimension dimension, boolean scale) throws VipsExcept thumbnailImageNative(dimension.width, dimension.height, scale); } + public void thumbnailImage(Dimension dimension, ThumbnailOptions options) throws VipsException { + thumbnailImageWithOptionsNative(dimension.width, dimension.height, options); + } + public void thumbnailImage(int width, int height, boolean scale) throws VipsException { thumbnailImageNative(width, height, scale); } + @Override + public void thumbnailImage(int width, int height, ThumbnailOptions options) throws VipsException { + thumbnailImageWithOptionsNative(width, height, options); + } + public static VipsImage thumbnail(String filename, Dimension dimension, boolean scale) throws VipsException { return thumbnailNative(filename, dimension.width, dimension.height, scale); } + public static VipsImage thumbnail(String filename, Dimension dimension, ThumbnailOptions options) throws VipsException { + return thumbnailWithOptionsNative(filename, dimension.width, dimension.height, options); + } + /** * Make a thumbnail from a file with new target dimension * @@ -173,6 +187,10 @@ public static VipsImage thumbnail(String filename, int width, int height, boolea return thumbnailNative(filename, width, height, scale); } + public static VipsImage thumbnail(String filename, int width, int height, ThumbnailOptions options) throws VipsException { + return thumbnailWithOptionsNative(filename, width, height, options); + } + /** * @deprecated Use {@link #thumbnailImage(Dimension, boolean)} instead. */ @@ -191,8 +209,12 @@ public void resize(int width, int height, boolean scale) throws VipsException { private native void thumbnailImageNative(int width, int height, boolean scale) throws VipsException; + private native void thumbnailImageWithOptionsNative(int width, int height, ThumbnailOptions options) throws VipsException; + private static native VipsImage thumbnailNative(String filename, int width, int height, boolean scale) throws VipsException; + private static native VipsImage thumbnailWithOptionsNative(String filename, int width, int height, ThumbnailOptions options) throws VipsException; + public void resize(double hscale, double vscale, VipsKernel kernel) throws VipsException { resizeNative(hscale, vscale, kernel.getValue()); } diff --git a/src/main/java/com/criteo/vips/options/ThumbnailOptions.java b/src/main/java/com/criteo/vips/options/ThumbnailOptions.java new file mode 100644 index 00000000..e364bcd5 --- /dev/null +++ b/src/main/java/com/criteo/vips/options/ThumbnailOptions.java @@ -0,0 +1,148 @@ +/* + Copyright (c) 2021 Criteo + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package com.criteo.vips.options; + +import com.criteo.vips.enums.VipsIntent; +import com.criteo.vips.enums.VipsInteresting; +import com.criteo.vips.enums.VipsSize; + +public class ThumbnailOptions { + + private int size = 1; + private boolean noRotate; + private int crop = -1; + private boolean linear; + private String importProfile; + private String exportProfile; + private int intent = -1; + + public VipsSize getSize() { + if (size != -1) { + return VipsSize.valueOf(size); + } else { + return null; + } + } + + public void setSize(VipsSize size) { + if (size != null) { + this.size = size.getValue(); + } else { + this.size = -1; + } + } + + public ThumbnailOptions size(VipsSize size) { + setSize(size); + return this; + } + + public boolean isNoRotate() { + return noRotate; + } + + public void setNoRotate(boolean noRotate) { + this.noRotate = noRotate; + } + + public ThumbnailOptions noRotate(boolean noRotate) { + setNoRotate(noRotate); + return this; + } + + public VipsInteresting getCrop() { + if (crop != -1) { + return VipsInteresting.valueOf(crop); + } else { + return null; + } + } + + public void setCrop(VipsInteresting crop) { + if (crop != null) { + this.crop = crop.getValue(); + } else { + this.crop = -1; + } + } + + public ThumbnailOptions crop(VipsInteresting crop) { + setCrop(crop); + return this; + } + + public boolean isLinear() { + return linear; + } + + public void setLinear(boolean linear) { + this.linear = linear; + } + + public ThumbnailOptions linear(boolean linear) { + setLinear(linear); + return this; + } + + public String getImportProfile() { + return importProfile; + } + + public void setImportProfile(String importProfile) { + this.importProfile = importProfile; + } + + public ThumbnailOptions importProfile(String importProfile) { + setImportProfile(importProfile); + return this; + } + + public String getExportProfile() { + return exportProfile; + } + + public void setExportProfile(String exportProfile) { + this.exportProfile = exportProfile; + } + + public ThumbnailOptions exportProfile(String exportProfile) { + setExportProfile(exportProfile); + return this; + } + + public VipsIntent getIntent() { + if (intent != -1) { + return VipsIntent.valueOf(intent); + } else { + return null; + } + } + + public void setIntent(VipsIntent intent) { + if (intent != null) { + this.intent = intent.getValue(); + } else { + this.intent = -1; + } + } + + public ThumbnailOptions intent(VipsIntent intent) { + setIntent(intent); + return this; + } + +} diff --git a/src/test/java/com/criteo/vips/VipsImageTest.java b/src/test/java/com/criteo/vips/VipsImageTest.java index f0f5e1d6..ef227230 100644 --- a/src/test/java/com/criteo/vips/VipsImageTest.java +++ b/src/test/java/com/criteo/vips/VipsImageTest.java @@ -17,6 +17,8 @@ package com.criteo.vips; import com.criteo.vips.enums.*; +import com.criteo.vips.options.ThumbnailOptions; + import org.junit.Assert; import org.junit.Assume; import org.junit.BeforeClass; @@ -341,6 +343,26 @@ public void TestShouldRenderThumbnailImageWithExactDimension() throws IOExceptio } } + @Test + public void TestShouldRenderThumbnailImageWithExactDimensionUsingOptions() throws IOException, VipsException { + ByteBuffer buffer = VipsTestUtils.getDirectByteBuffer("in_vips.jpg"); + try (VipsImage img = new VipsImage(buffer, buffer.capacity())) { + img.thumbnailImage(new Dimension(800, 800), new ThumbnailOptions().size(VipsSize.Force)); + assertEquals(800, img.getWidth()); + assertEquals(800, img.getHeight()); + } + } + + @Test + public void TestShouldRenderThumbnailImageWithExactDimensionUsingCrop() throws IOException, VipsException { + ByteBuffer buffer = VipsTestUtils.getDirectByteBuffer("in_vips.jpg"); + try (VipsImage img = new VipsImage(buffer, buffer.capacity())) { + img.thumbnailImage(new Dimension(800, 800), new ThumbnailOptions().crop(VipsInteresting.Attention)); + assertEquals(800, img.getWidth()); + assertEquals(800, img.getHeight()); + } + } + @Test public void TestShouldRenderThumbnailWithExactDimension() throws IOException, VipsException { String filename = VipsTestUtils.getRessourcePath("in_vips.jpg");