Skip to content

Commit 3fda7b5

Browse files
committed
Bitmap.makeFromImage, Image.makeRaster, .makeFromBitmap, .getImageInfo, .readPixels (closes #72, #95, #98)
1 parent 247b1fb commit 3fda7b5

File tree

13 files changed

+529
-142
lines changed

13 files changed

+529
-142
lines changed
85.4 KB
Loading
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
package org.jetbrains.skija.examples.scenes;
2+
3+
import java.io.*;
4+
import java.nio.*;
5+
import java.nio.file.*;
6+
import java.nio.file.Path;
7+
import java.util.*;
8+
import java.util.function.*;
9+
import org.jetbrains.skija.*;
10+
11+
public class BitmapImageScene extends Scene {
12+
public final Image image;
13+
public int x, y;
14+
15+
public BitmapImageScene() {
16+
try {
17+
image = Image.makeFromEncoded(Files.readAllBytes(Path.of(file("images/IMG_7098.jpeg"))));
18+
} catch (IOException e) {
19+
throw new RuntimeException(e);
20+
}
21+
}
22+
23+
public void advance(Canvas canvas, int width) {
24+
canvas.restore();
25+
x += 220;
26+
if (x + 220 >= width) {
27+
x = 20;
28+
y += 240;
29+
}
30+
canvas.save();
31+
canvas.translate(x, y);
32+
}
33+
34+
@Override
35+
public void draw(Canvas canvas, int width, int height, float dpi, int xpos, int ypos) {
36+
canvas.save();
37+
canvas.translate(20, 20);
38+
x = 20;
39+
y = 20;
40+
41+
// Image
42+
canvas.drawImageRect(image, Rect.makeXYWH(0, 0, 200, 200));
43+
canvas.drawString("Image", 0, 220, inter13, blackFill);
44+
advance(canvas, width);
45+
46+
// Bitmap + Image.readPixels
47+
var bitmap = new Bitmap();
48+
bitmap.allocPixels(ImageInfo.makeS32(400, 400, ColorAlphaType.OPAQUE));
49+
image.readPixels(bitmap);
50+
canvas.drawBitmapRect(bitmap, Rect.makeXYWH(0, 0, 200, 200));
51+
canvas.drawString("Image.readPixels", 0, 220, inter13, blackFill);
52+
advance(canvas, width);
53+
54+
// Bitmap + Image.readPixels(50, 50)
55+
var partialBitmap = new Bitmap();
56+
partialBitmap.allocPixels(new ImageInfo(300, 300, ColorType.GRAY_8, ColorAlphaType.OPAQUE));
57+
image.readPixels(partialBitmap, 50, 50);
58+
canvas.drawBitmapRect(partialBitmap, Rect.makeXYWH(25, 25, 150, 150));
59+
canvas.drawString("Image.readPixels(50, 50)", 0, 220, inter13, blackFill);
60+
advance(canvas, width);
61+
62+
// Bitmap.makeFromImage
63+
var bitmapFromImage = Bitmap.makeFromImage(image);
64+
canvas.drawBitmapRect(bitmapFromImage, Rect.makeXYWH(0, 0, 200, 200));
65+
canvas.drawString("Bitmap.makeFromImage", 0, 220, inter13, blackFill);
66+
advance(canvas, width);
67+
68+
// Image.makeFromBitmap
69+
var imageFromBitmap = Image.makeFromBitmap(bitmap);
70+
canvas.drawImageRect(imageFromBitmap, Rect.makeXYWH(0, 0, 200, 200));
71+
canvas.drawString("Image.makeFromBitmap", 0, 220, inter13, blackFill);
72+
advance(canvas, width);
73+
74+
// Bitmap readPixels/installPixels
75+
var info = bitmapFromImage.getImageInfo();
76+
var threshold = 100 + phase() * 100;
77+
byte[] pixels = bitmapFromImage.readPixels();
78+
ByteBuffer buffer = ByteBuffer.wrap(pixels); // Assume RGBA_8888
79+
Function<Integer, Integer> luminocity = color -> Color.getR(color) + Color.getG(color) + Color.getB(color);
80+
Comparator<Integer> cmp = (a, b) -> Integer.compare(luminocity.apply(a), luminocity.apply(b));
81+
for (int x = 0; x < info.getWidth(); ++x) {
82+
// read pixels
83+
Integer column[] = new Integer[info.getHeight()];
84+
for (int y = 0; y < info.getHeight(); ++y)
85+
column[y] = buffer.getInt((y * info.getWidth() + x) * info.getBytesPerPixel());
86+
87+
// sort pixels
88+
var lastIdx = 0;
89+
for (int y = 0; y < info.getHeight() - 1; ++y) {
90+
if (Math.abs(luminocity.apply(column[y]) - luminocity.apply(column[y + 1])) > threshold) {
91+
Arrays.parallelSort(column, lastIdx, y, cmp);
92+
lastIdx = y;
93+
}
94+
}
95+
Arrays.parallelSort(column, lastIdx, info.getHeight(), cmp);
96+
97+
// write pixels
98+
for (int y = 0; y < info.getHeight(); ++y)
99+
buffer.putInt((y * info.getWidth() + x) * info.getBytesPerPixel(), column[y]);
100+
}
101+
bitmapFromImage.installPixels(pixels);
102+
canvas.drawBitmapRect(bitmapFromImage, Rect.makeXYWH(0, 0, 200, 200));
103+
canvas.drawString("Bitmap.readPixels/installPixels", 0, 220, inter13, blackFill);
104+
advance(canvas, width);
105+
106+
// Image.makeRaster
107+
var imageFromPixels = Image.makeRaster(info, pixels, info.getMinRowBytes());
108+
canvas.drawImageRect(imageFromPixels, Rect.makeXYWH(0, 0, 200, 200));
109+
canvas.drawString("Image.makeRaster", 0, 220, inter13, blackFill);
110+
advance(canvas, width);
111+
112+
// Image.makeRaster + Data
113+
var imageFromData = Image.makeRaster(info, Data.makeFromBytes(pixels), info.getMinRowBytes());
114+
canvas.drawImageRect(imageFromPixels, Rect.makeXYWH(0, 0, 200, 200));
115+
canvas.drawString("Image.makeRaster + Data", 0, 220, inter13, blackFill);
116+
advance(canvas, width);
117+
118+
bitmap.close();
119+
partialBitmap.close();
120+
bitmapFromImage.close();
121+
imageFromBitmap.close();
122+
imageFromPixels.close();
123+
imageFromData.close();
124+
}
125+
}

examples/scenes/src/ColorFiltersScene.java

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,6 @@ public ColorFiltersScene() {
2020
public void draw(Canvas canvas, int width, int height, float dpi, int xpos, int ypos) {
2121
canvas.translate(30, 30);
2222

23-
float percent = Math.abs((System.currentTimeMillis() % 3000) / 10f - 150f) - 25f;
24-
percent = Math.round(Math.max(0f, Math.min(100f, percent)));
25-
float ratio = percent / 100f;
26-
2723
byte[] tablePosterize = new byte[256];
2824
for (int i = 0; i < 256; ++i)
2925
tablePosterize[i] = (byte) (i & 0x80);
@@ -48,12 +44,12 @@ public void draw(Canvas canvas, int width, int height, float dpi, int xpos, int
4844
0, 0, 0, 1, 0
4945
)),
5046
ColorFilter.makeHSLAMatrix(new ColorMatrix(
51-
0, 0, 0, 0, ratio,
47+
0, 0, 0, 0, phase(),
5248
0, 1, 0, 0, 0,
5349
0, 0, 1, 0, 0,
5450
0, 0, 0, 1, 0
5551
)),
56-
ColorFilter.makeLerp(ColorFilter.makeBlend(0x80CC3333, BlendMode.SRC_OVER), ColorFilter.makeBlend(0x803333CC, BlendMode.SRC_OVER), ratio),
52+
ColorFilter.makeLerp(ColorFilter.makeBlend(0x80CC3333, BlendMode.SRC_OVER), ColorFilter.makeBlend(0x803333CC, BlendMode.SRC_OVER), phase()),
5753
ColorFilter.makeLighting(0x80CC3333, 0x803333CC),
5854
},
5955

examples/scenes/src/Scene.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,4 +50,11 @@ public static void drawStringLeft(Canvas canvas, String text, Rect outer, Font f
5050
outer.getTop() + (outer.getHeight() - innerHeight) / 2f - metrics.getAscent(),
5151
font, paint);
5252
}
53+
54+
public static float phase() {
55+
var angle = (System.currentTimeMillis() % 5000) / 5000.0 * Math.PI * 2.0;
56+
var phase = Math.sin(angle) * 1.2;
57+
phase = Math.min(1.0, Math.max(-1.0, phase));
58+
return (float) (phase + 1) / 2f;
59+
}
5360
}

examples/scenes/src/Scenes.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,15 @@
66

77
public class Scenes {
88
public static TreeMap<String, Scene> scenes;
9-
public static String currentScene = "Text Line Decorations";
9+
public static String currentScene = "Bitmap Image";
1010
public static HUD hud = new HUD();
1111
public static boolean vsync = true;
1212
public static boolean stats = true;
1313

1414
static {
1515
scenes = new TreeMap<>();
1616
scenes.put("Bitmap", null);
17+
scenes.put("Bitmap Image", null);
1718
scenes.put("Blends", null);
1819
scenes.put("Color Filters", null);
1920
scenes.put("Decorations Bench", null);

examples/scenes/src/TextBlobScene.java

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ public void drawPos(Canvas canvas) {
4848
float distance = 0;
4949

5050
Point[] pos = new Point[glyphs.length];
51-
float offset = System.currentTimeMillis() % 1000 / 1000f * 2 * (float) Math.PI;
51+
float offset = phase() * 2 * (float) Math.PI;
5252

5353
for (int i = 0; i < pos.length; ++i) {
5454
pos[i] = new Point(distance, (float) Math.sin(distance + offset) * 3);
@@ -68,16 +68,14 @@ public void drawRSXform(Canvas canvas) {
6868
RSXform[] xforms = new RSXform[glyphs.length];
6969

7070
float radius = 50;
71-
int period = 3000;
7271

7372
try (Path path = new Path().addCircle(0, 0, radius);
7473
PathMeasure measure = new PathMeasure(path);
7574
Paint fill = new Paint().setColor(0xffffba08);
7675
Paint stroke = new Paint().setColor(0xff3a86ff).setMode(PaintMode.STROKE).setStrokeWidth(1f);)
7776
{
7877
float length = measure.getLength();
79-
float relativeOffset = System.currentTimeMillis() % period / (float) period;
80-
float distance = relativeOffset * length;
78+
float distance = phase() * length;
8179

8280
for (int i=0; i < xforms.length; ++i) {
8381
float w = widths[i];
@@ -134,9 +132,7 @@ public void drawBuilder(Canvas canvas) {
134132
}
135133

136134
public void drawBounds(Canvas canvas) {
137-
float percent = Math.abs((System.currentTimeMillis() % 10000) / 33f - 150f) - 25f;
138-
percent = Math.round(Math.max(0f, Math.min(100f, percent)));
139-
float width = 100 + percent * 3;
135+
float width = 100 + phase() * 300;
140136
int color = 0xFF454A6F;
141137

142138
try (var shaper = Shaper.make();

native/src/Bitmap.cc

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,7 @@ extern "C" JNIEXPORT void JNICALL Java_org_jetbrains_skija_Bitmap__1nSwap
3535
extern "C" JNIEXPORT jobject JNICALL Java_org_jetbrains_skija_Bitmap__1nGetImageInfo
3636
(JNIEnv* env, jclass jclass, jlong ptr) {
3737
SkBitmap* instance = reinterpret_cast<SkBitmap*>(static_cast<uintptr_t>(ptr));
38-
const SkImageInfo& info = instance->info();
39-
return env->NewObject(skija::ImageInfo::cls, skija::ImageInfo::ctor,
40-
info.width(),
41-
info.height(),
42-
static_cast<jint>(info.colorType()),
43-
static_cast<jint>(info.alphaType()),
44-
reinterpret_cast<jlong>(info.refColorSpace().release()));
38+
return skija::ImageInfo::toJava(env, instance->info());
4539
}
4640

4741
extern "C" JNIEXPORT jint JNICALL Java_org_jetbrains_skija_Bitmap__1nGetRowBytesAsPixels

native/src/Image.cc

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,40 @@
44
#include "SkImage.h"
55
#include "interop.hh"
66

7+
extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skija_Image__1nMakeRaster
8+
(JNIEnv* env, jclass jclass, jint width, jint height, jint colorType, jint alphaType, jlong colorSpacePtr, jbyteArray bytesArr, jlong rowBytes) {
9+
SkColorSpace* colorSpace = reinterpret_cast<SkColorSpace*>(static_cast<uintptr_t>(colorSpacePtr));
10+
SkImageInfo imageInfo = SkImageInfo::Make(width,
11+
height,
12+
static_cast<SkColorType>(colorType),
13+
static_cast<SkAlphaType>(alphaType),
14+
sk_ref_sp<SkColorSpace>(colorSpace));
15+
void* bytes = env->GetPrimitiveArrayCritical(bytesArr, 0);
16+
sk_sp<SkImage> image = SkImage::MakeRasterCopy(SkPixmap(imageInfo, bytes, rowBytes));
17+
env->ReleasePrimitiveArrayCritical(bytesArr, bytes, 0);
18+
return reinterpret_cast<jlong>(image.release());
19+
}
20+
21+
extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skija_Image__1nMakeRasterData
22+
(JNIEnv* env, jclass jclass, jint width, jint height, jint colorType, jint alphaType, jlong colorSpacePtr, jlong dataPtr, jlong rowBytes) {
23+
SkColorSpace* colorSpace = reinterpret_cast<SkColorSpace*>(static_cast<uintptr_t>(colorSpacePtr));
24+
SkImageInfo imageInfo = SkImageInfo::Make(width,
25+
height,
26+
static_cast<SkColorType>(colorType),
27+
static_cast<SkAlphaType>(alphaType),
28+
sk_ref_sp<SkColorSpace>(colorSpace));
29+
SkData* data = reinterpret_cast<SkData*>(static_cast<uintptr_t>(dataPtr));
30+
sk_sp<SkImage> image = SkImage::MakeRasterData(imageInfo, sk_ref_sp(data), rowBytes);
31+
return reinterpret_cast<jlong>(image.release());
32+
}
33+
34+
extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skija_Image__1nMakeFromBitmap
35+
(JNIEnv* env, jclass jclass, jlong bitmapPtr) {
36+
SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(static_cast<uintptr_t>(bitmapPtr));
37+
sk_sp<SkImage> image = SkImage::MakeFromBitmap(*bitmap);
38+
return reinterpret_cast<jlong>(image.release());
39+
}
40+
741
extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skija_Image__1nMakeFromEncoded
842
(JNIEnv* env, jclass jclass, jbyteArray encodedArray) {
943
jsize encodedLen = env->GetArrayLength(encodedArray);
@@ -16,10 +50,10 @@ extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skija_Image__1nMakeFromEnc
1650
return reinterpret_cast<jlong>(image.release());
1751
}
1852

19-
extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skija_Image__1nGetDimensions
53+
extern "C" JNIEXPORT jobject JNICALL Java_org_jetbrains_skija_Image__1nGetImageInfo
2054
(JNIEnv* env, jclass jclass, jlong ptr) {
2155
SkImage* instance = reinterpret_cast<SkImage*>(static_cast<uintptr_t>(ptr));
22-
return packTwoInts(instance->width(), instance->height());
56+
return skija::ImageInfo::toJava(env, instance->imageInfo());
2357
}
2458

2559
extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skija_Image__1nEncodeToData
@@ -46,3 +80,12 @@ extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skija_Image__1nMakeShaderC
4680
sk_sp<SkShader> shader = instance->makeShader(static_cast<SkTileMode>(tmx), static_cast<SkTileMode>(tmy), sampling, localMatrix.get());
4781
return reinterpret_cast<jlong>(shader.release());
4882
}
83+
84+
extern "C" JNIEXPORT jboolean JNICALL Java_org_jetbrains_skija_Image__1nReadPixelsBitmap
85+
(JNIEnv* env, jclass jclass, jlong ptr, jlong contextPtr, jlong bitmapPtr, jint srcX, jint srcY, jboolean cache) {
86+
SkImage* instance = reinterpret_cast<SkImage*>(static_cast<uintptr_t>(ptr));
87+
GrDirectContext* context = reinterpret_cast<GrDirectContext*>(static_cast<uintptr_t>(contextPtr));
88+
SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(static_cast<uintptr_t>(bitmapPtr));
89+
auto cachingHint = cache ? SkImage::CachingHint::kAllow_CachingHint : SkImage::CachingHint::kDisallow_CachingHint;
90+
return instance->readPixels(context, bitmap->info(), bitmap->getPixels(), bitmap->pixmap().rowBytes(), srcX, srcY, cachingHint);
91+
}

native/src/interop.cc

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,15 @@ namespace skija {
302302
void onUnload(JNIEnv* env) {
303303
env->DeleteGlobalRef(cls);
304304
}
305+
306+
jobject toJava(JNIEnv* env, const SkImageInfo& info) {
307+
return env->NewObject(cls, ctor,
308+
info.width(),
309+
info.height(),
310+
static_cast<jint>(info.colorType()),
311+
static_cast<jint>(info.alphaType()),
312+
reinterpret_cast<jlong>(info.refColorSpace().release()));
313+
}
305314
}
306315

307316
namespace IPoint {

native/src/interop.hh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include <vector>
55
#include "SkFontMetrics.h"
66
#include "SkFontStyle.h"
7+
#include "SkImageInfo.h"
78
#include "SkMatrix.h"
89
#include "SkM44.h"
910
#include "SkPaint.h"
@@ -138,6 +139,7 @@ namespace skija {
138139
extern jmethodID ctor;
139140
void onLoad(JNIEnv* env);
140141
void onUnload(JNIEnv* env);
142+
jobject toJava(JNIEnv* env, const SkImageInfo& imageInfo);
141143
}
142144

143145
namespace IPoint {

0 commit comments

Comments
 (0)