[androidkit] Image shader support
Tangential updates: - new encoded input stream Image factory - Paint setters now return the object to support a fluent workflow - cube demo updated to store face paints instead of colors Change-Id: I6142a229b18165112ef1fe76acae38bc4b27480d Reviewed-on: https://skia-review.googlesource.com/c/skia/+/410789 Commit-Queue: Florin Malita <fmalita@google.com> Reviewed-by: Jorge Betancourt <jmbetancourt@google.com>
This commit is contained in:
parent
8017581ec0
commit
50cc5d4147
1
BUILD.gn
1
BUILD.gn
@ -2505,6 +2505,7 @@ if (skia_enable_tools) {
|
||||
"modules/androidkit/src/Surface.h",
|
||||
"modules/androidkit/src/SurfaceThread.cpp",
|
||||
"modules/androidkit/src/SurfaceThread.h",
|
||||
"modules/androidkit/src/Utils.cpp",
|
||||
]
|
||||
libs = [
|
||||
"android",
|
||||
|
@ -8,24 +8,12 @@
|
||||
#include "include/core/SkCanvas.h"
|
||||
#include "include/core/SkColor.h"
|
||||
#include "include/core/SkPaint.h"
|
||||
#include "modules/androidkit/src/Utils.h"
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
namespace {
|
||||
|
||||
SkSamplingOptions sampling_opts(jint desc, jfloat coeffB, jfloat coeffC) {
|
||||
if (desc & 0x01) {
|
||||
return SkSamplingOptions(SkCubicResampler{coeffB, coeffC});
|
||||
}
|
||||
|
||||
const auto fm = static_cast<SkFilterMode>((desc >> 1) & 0x01);
|
||||
SkASSERT(fm <= SkFilterMode::kLast);
|
||||
const auto mm = static_cast<SkMipmapMode>((desc >> 2) & 0x03);
|
||||
SkASSERT(mm <= SkMipmapMode::kLast);
|
||||
|
||||
return SkSamplingOptions(fm, mm);
|
||||
}
|
||||
|
||||
jint Canvas_GetWidth(JNIEnv* env, jobject, jlong native_instance) {
|
||||
const auto* canvas = reinterpret_cast<const SkCanvas*>(native_instance);
|
||||
return canvas ? canvas->imageInfo().width() : 0;
|
||||
@ -99,7 +87,8 @@ void Canvas_DrawImage(JNIEnv* env, jobject, jlong native_instance, jlong native_
|
||||
auto* image = reinterpret_cast<SkImage *>(native_image);
|
||||
|
||||
if (canvas && image) {
|
||||
canvas->drawImage(image, x, y, sampling_opts(sampling_desc, sampling_b, sampling_c));
|
||||
canvas->drawImage(image, x, y,
|
||||
androidkit::utils::SamplingOptions(sampling_desc, sampling_b, sampling_c));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,8 @@
|
||||
#include <jni.h>
|
||||
|
||||
#include "include/core/SkImage.h"
|
||||
#include "include/core/SkTileMode.h"
|
||||
#include "modules/androidkit/src/Utils.h"
|
||||
|
||||
namespace {
|
||||
|
||||
@ -35,15 +37,37 @@ jint Image_GetHeight(JNIEnv*, jobject, jlong native_instance) {
|
||||
return image ? image->height() : 0;
|
||||
}
|
||||
|
||||
jlong Image_MakeShader(JNIEnv*, jobject, jlong native_instance, jint jtmx, jint jtmy,
|
||||
jint sampling_desc, jfloat sampling_b, jfloat sampling_c,
|
||||
jlong native_matrix) {
|
||||
sk_sp<SkShader> shader;
|
||||
|
||||
if (const auto* image = reinterpret_cast<const SkImage*>(native_instance)) {
|
||||
const auto tmx = androidkit::utils::TileMode(jtmx),
|
||||
tmy = androidkit::utils::TileMode(jtmy);
|
||||
const auto sampling = androidkit::utils::SamplingOptions(sampling_desc,
|
||||
sampling_b, sampling_c);
|
||||
|
||||
const auto* lm = reinterpret_cast<const SkM44*>(native_matrix);
|
||||
shader = lm
|
||||
? image->makeShader(tmx, tmy, sampling, lm->asM33())
|
||||
: image->makeShader(tmx, tmy, sampling);
|
||||
}
|
||||
|
||||
return reinterpret_cast<jlong>(shader.release());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int register_androidkit_Image(JNIEnv* env) {
|
||||
static const JNINativeMethod methods[] = {
|
||||
{"nCreate" , "([B)J", reinterpret_cast<void*>(Image_Create) },
|
||||
{"nRelease" , "(J)V" , reinterpret_cast<void*>(Image_Release) },
|
||||
{"nCreate" , "([B)J" , reinterpret_cast<void*>(Image_Create) },
|
||||
{"nRelease" , "(J)V" , reinterpret_cast<void*>(Image_Release) },
|
||||
|
||||
{"nGetWidth" , "(J)I" , reinterpret_cast<void*>(Image_GetWidth) },
|
||||
{"nGetHeight", "(J)I" , reinterpret_cast<void*>(Image_GetHeight)},
|
||||
{"nGetWidth" , "(J)I" , reinterpret_cast<void*>(Image_GetWidth) },
|
||||
{"nGetHeight" , "(J)I" , reinterpret_cast<void*>(Image_GetHeight)},
|
||||
|
||||
{"nMakeShader", "(JIIIFFJ)J" , reinterpret_cast<void*>(Image_MakeShader)},
|
||||
};
|
||||
|
||||
const auto clazz = env->FindClass("org/skia/androidkit/Image");
|
||||
|
37
modules/androidkit/src/Utils.cpp
Normal file
37
modules/androidkit/src/Utils.cpp
Normal file
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2021 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "modules/androidkit/src/Utils.h"
|
||||
|
||||
namespace androidkit {
|
||||
namespace utils {
|
||||
|
||||
SkSamplingOptions SamplingOptions(jint desc, jfloat coeffB, jfloat coeffC) {
|
||||
if (desc & 0x01) {
|
||||
return SkSamplingOptions(SkCubicResampler{coeffB, coeffC});
|
||||
}
|
||||
|
||||
const auto fm = static_cast<SkFilterMode>((desc >> 1) & 0x01);
|
||||
SkASSERT(fm <= SkFilterMode::kLast);
|
||||
const auto mm = static_cast<SkMipmapMode>((desc >> 2) & 0x03);
|
||||
SkASSERT(mm <= SkMipmapMode::kLast);
|
||||
|
||||
return SkSamplingOptions(fm, mm);
|
||||
}
|
||||
|
||||
SkTileMode TileMode(jint tm) {
|
||||
// to catch Skia API changes
|
||||
static_assert(static_cast<int>(SkTileMode::kClamp ) == 0);
|
||||
static_assert(static_cast<int>(SkTileMode::kRepeat) == 1);
|
||||
static_assert(static_cast<int>(SkTileMode::kMirror) == 2);
|
||||
static_assert(static_cast<int>(SkTileMode::kDecal ) == 3);
|
||||
|
||||
return static_cast<SkTileMode>(tm);
|
||||
}
|
||||
|
||||
} // namespace utils
|
||||
} // namespace androidkit
|
@ -7,6 +7,9 @@
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#include "include/core/SkSamplingOptions.h"
|
||||
#include "include/core/SkTileMode.h"
|
||||
|
||||
namespace androidkit {
|
||||
namespace utils {
|
||||
|
||||
@ -37,5 +40,8 @@ private:
|
||||
CString& operator=(const CString&) = delete;
|
||||
};
|
||||
|
||||
SkSamplingOptions SamplingOptions(jint, jfloat, jfloat);
|
||||
SkTileMode TileMode(jint);
|
||||
|
||||
} // namespace utils
|
||||
} // namespace androidkit
|
||||
|
@ -7,6 +7,13 @@
|
||||
|
||||
package org.skia.androidkit;
|
||||
|
||||
import android.support.annotation.Nullable;
|
||||
import java.io.InputStream;
|
||||
import org.skia.androidkit.Matrix;
|
||||
import org.skia.androidkit.SamplingOptions;
|
||||
import org.skia.androidkit.Shader;
|
||||
import org.skia.androidkit.TileMode;
|
||||
|
||||
public class Image {
|
||||
private long mNativeInstance;
|
||||
|
||||
@ -22,6 +29,18 @@ public class Image {
|
||||
: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct an Image from an encoded data stream.
|
||||
*
|
||||
* Returns null for unsupported formats or invalid stream.
|
||||
*/
|
||||
public static Image fromStream(InputStream encodedStream) throws java.io.IOException {
|
||||
byte[] encodedData = new byte[encodedStream.available()];
|
||||
encodedStream.read(encodedData);
|
||||
|
||||
return fromEncoded(encodedData);
|
||||
}
|
||||
|
||||
public int getWidth() {
|
||||
return nGetWidth(mNativeInstance);
|
||||
}
|
||||
@ -30,6 +49,19 @@ public class Image {
|
||||
return nGetHeight(mNativeInstance);
|
||||
}
|
||||
|
||||
public Shader makeShader(TileMode tmx, TileMode tmy, SamplingOptions sampling) {
|
||||
return makeShader(tmx, tmy, sampling, null);
|
||||
}
|
||||
|
||||
public Shader makeShader(TileMode tmx, TileMode tmy, SamplingOptions sampling,
|
||||
@Nullable Matrix localMatrix) {
|
||||
long nativeMatrix = localMatrix != null ? localMatrix.getNativeInstance() : 0;
|
||||
return new Shader(nMakeShader(mNativeInstance, tmx.ordinal(), tmy.ordinal(),
|
||||
sampling.getNativeDesc(),
|
||||
sampling.getCubicCoeffB(), sampling.getCubicCoeffC(),
|
||||
nativeMatrix));
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases any resources associated with this Paint.
|
||||
*/
|
||||
@ -56,4 +88,8 @@ public class Image {
|
||||
|
||||
private static native int nGetWidth(long nativeInstance);
|
||||
private static native int nGetHeight(long nativeInstance);
|
||||
|
||||
private static native long nMakeShader(long nativeInstance, int tmx, int tmy, int samplingDesc,
|
||||
float samplingCoeffB, float samplingCoeffC,
|
||||
long nativeMatrix);
|
||||
}
|
||||
|
@ -17,20 +17,24 @@ public class Paint {
|
||||
mNativeInstance = nCreate();
|
||||
}
|
||||
|
||||
public void setColor(Color c) {
|
||||
public Paint setColor(Color c) {
|
||||
nSetColor(mNativeInstance, c.r(), c.g(), c.b(), c.a());
|
||||
return this;
|
||||
}
|
||||
|
||||
public void setShader(Shader shader) {
|
||||
public Paint setShader(Shader shader) {
|
||||
nSetShader(mNativeInstance, shader != null ? shader.getNativeInstance() : 0);
|
||||
return this;
|
||||
}
|
||||
|
||||
public void setStroke(boolean stroke) {
|
||||
public Paint setStroke(boolean stroke) {
|
||||
nSetStroke(mNativeInstance, stroke);
|
||||
return this;
|
||||
}
|
||||
|
||||
public void setStrokeWidth(float w) {
|
||||
public Paint setStrokeWidth(float w) {
|
||||
nSetStrokeWidth(mNativeInstance, w);
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Releases any resources associated with this Paint.
|
||||
|
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2021 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
package org.skia.androidkit;
|
||||
|
||||
public enum TileMode {
|
||||
/**
|
||||
* Replicate the edge color if the shader draws outside of its
|
||||
* original bounds.
|
||||
*/
|
||||
CLAMP,
|
||||
|
||||
/**
|
||||
* Repeat the shader's image horizontally and vertically.
|
||||
*/
|
||||
REPEAT,
|
||||
|
||||
/**
|
||||
* Repeat the shader's image horizontally and vertically, alternating
|
||||
* mirror images so that adjacent images always seam.
|
||||
*/
|
||||
MIRROR,
|
||||
|
||||
|
||||
/**
|
||||
* Only draw within the original domain, return transparent-black everywhere else.
|
||||
*/
|
||||
DECAL,
|
||||
}
|
@ -8,6 +8,7 @@
|
||||
package org.skia.androidkitdemo1;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.res.Resources;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.view.SurfaceHolder;
|
||||
@ -15,9 +16,13 @@ import android.view.SurfaceView;
|
||||
|
||||
import org.skia.androidkit.Canvas;
|
||||
import org.skia.androidkit.Color;
|
||||
import org.skia.androidkit.Image;
|
||||
import org.skia.androidkit.Matrix;
|
||||
import org.skia.androidkit.Paint;
|
||||
import org.skia.androidkit.SamplingOptions;
|
||||
import org.skia.androidkit.Shader;
|
||||
import org.skia.androidkit.Surface;
|
||||
import org.skia.androidkit.TileMode;
|
||||
import org.skia.androidkit.util.SurfaceRenderer;
|
||||
|
||||
import static java.lang.Math.tan;
|
||||
@ -25,12 +30,12 @@ import static java.lang.Math.tan;
|
||||
class Face {
|
||||
private float rotX;
|
||||
private float rotY;
|
||||
public Color color;
|
||||
public Paint paint;
|
||||
|
||||
Face(float rotX, float rotY, Color color) {
|
||||
Face(float rotX, float rotY, Paint paint) {
|
||||
this.rotX = rotX;
|
||||
this.rotY = rotY;
|
||||
this.color = color;
|
||||
this.paint = paint;
|
||||
}
|
||||
|
||||
Matrix asMatrix(float scale) {
|
||||
@ -51,20 +56,28 @@ class CubeRenderer extends SurfaceRenderer {
|
||||
private Matrix perspective = Matrix.makePerspective(0.05f, 4, fAngle);
|
||||
private Matrix viewport;
|
||||
|
||||
private Paint mPaint = new Paint();
|
||||
|
||||
private final float rot = (float) Math.PI;
|
||||
private Face[] faces = {new Face(0, 0, new Color(1, 0, 0, 1)),
|
||||
new Face(0, rot, new Color(0, 1, 0, 1)),
|
||||
new Face(rot/2, 0, new Color(0, 0, 1, 1)),
|
||||
new Face(-rot/2, 0, new Color(1, 1, 0, 1)),
|
||||
new Face(0, rot/2, new Color(0, 1, 1, 1)),
|
||||
new Face(0, -rot/2, new Color(0, 0, 0, 1))};
|
||||
private Face[] faces;
|
||||
|
||||
public CubeRenderer() {
|
||||
mPaint.setColor(new Color(0, 1, 1, 1));
|
||||
mPaint.setStroke(false);
|
||||
mPaint.setStrokeWidth(10);
|
||||
public CubeRenderer(Resources res) {
|
||||
Paint brickPaint = new Paint();
|
||||
|
||||
try {
|
||||
Image image = Image.fromStream(res.openRawResource(R.raw.brickwork_texture));
|
||||
Shader shader =
|
||||
image.makeShader(TileMode.REPEAT, TileMode.REPEAT,
|
||||
new SamplingOptions(SamplingOptions.FilterMode.LINEAR));
|
||||
brickPaint.setShader(shader);
|
||||
} catch (Exception e) {}
|
||||
|
||||
faces = new Face[] {
|
||||
new Face(0, -rot/2, brickPaint),
|
||||
new Face(0, 0 , new Paint().setColor(new Color(1, 0, 0, 1))),
|
||||
new Face(0, rot , new Paint().setColor(new Color(0, 1, 0, 1))),
|
||||
new Face(rot/2, 0 , new Paint().setColor(new Color(0, 0, 1, 1))),
|
||||
new Face(-rot/2, 0, new Paint().setColor(new Color(1, 1, 0, 1))),
|
||||
new Face(0, rot/2 , new Paint().setColor(new Color(0, 1, 1, 1))),
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -100,8 +113,7 @@ class CubeRenderer extends SurfaceRenderer {
|
||||
canvas.concat(localToWorld);
|
||||
|
||||
if (front(canvas.getLocalToDevice())) {
|
||||
mPaint.setColor(f.color);
|
||||
canvas.drawRect(0, 0, mCubeSideLength, mCubeSideLength, mPaint);
|
||||
canvas.drawRect(0, 0, mCubeSideLength, mCubeSideLength, f.paint);
|
||||
}
|
||||
canvas.restore();
|
||||
}
|
||||
@ -130,6 +142,6 @@ public class CubeActivity extends Activity {
|
||||
setContentView(R.layout.activity_cube);
|
||||
|
||||
SurfaceView sv = findViewById(R.id.surfaceView);
|
||||
sv.getHolder().addCallback(new CubeRenderer());
|
||||
sv.getHolder().addCallback(new CubeRenderer(getResources()));
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,6 @@ import android.util.Log;
|
||||
import android.view.SurfaceHolder;
|
||||
import android.view.SurfaceView;
|
||||
import android.widget.ImageView;
|
||||
import java.io.InputStream;
|
||||
import org.skia.androidkit.*;
|
||||
|
||||
public class MainActivity extends Activity implements SurfaceHolder.Callback {
|
||||
@ -55,11 +54,7 @@ public class MainActivity extends Activity implements SurfaceHolder.Callback {
|
||||
canvas.drawImage(snapshot, 0, 200);
|
||||
|
||||
try {
|
||||
InputStream is = getResources().openRawResource(R.raw.brickwork_texture);
|
||||
byte[] data = new byte[is.available()];
|
||||
is.read(data);
|
||||
|
||||
Image image = Image.fromEncoded(data);
|
||||
Image image = Image.fromStream(getResources().openRawResource(R.raw.brickwork_texture));
|
||||
// TODO: Canvas.scale
|
||||
canvas.concat(new Matrix().scale(10, 10));
|
||||
canvas.drawImage(image, 20, 0, SamplingOptions.CATMULLROM());
|
||||
|
Loading…
Reference in New Issue
Block a user