Skip to content

Commit

Permalink
Bitmap.makeFromImage, Image.makeRaster, .makeFromBitmap, .getImageInf…
Browse files Browse the repository at this point in the history
…o, .readPixels (closes #72, #95, #98)
  • Loading branch information
tonsky committed Mar 5, 2021
1 parent 247b1fb commit 3df8fa6
Show file tree
Hide file tree
Showing 13 changed files with 529 additions and 142 deletions.
Binary file added examples/scenes/images/IMG_7098.jpeg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
125 changes: 125 additions & 0 deletions examples/scenes/src/BitmapImageScene.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package org.jetbrains.skija.examples.scenes;

import java.io.*;
import java.nio.*;
import java.nio.file.*;
import java.nio.file.Path;
import java.util.*;
import java.util.function.*;
import org.jetbrains.skija.*;

public class BitmapImageScene extends Scene {
public final Image image;
public int x, y;

public BitmapImageScene() {
try {
image = Image.makeFromEncoded(Files.readAllBytes(Path.of(file("images/IMG_7098.jpeg"))));
} catch (IOException e) {
throw new RuntimeException(e);
}
}

public void advance(Canvas canvas, int width) {
canvas.restore();
x += 220;
if (x + 220 >= width) {
x = 20;
y += 240;
}
canvas.save();
canvas.translate(x, y);
}

@Override
public void draw(Canvas canvas, int width, int height, float dpi, int xpos, int ypos) {
canvas.save();
canvas.translate(20, 20);
x = 20;
y = 20;

// Image
canvas.drawImageRect(image, Rect.makeXYWH(0, 0, 200, 200));
canvas.drawString("Image", 0, 220, inter13, blackFill);
advance(canvas, width);

// Bitmap + Image.readPixels
var bitmap = new Bitmap();
bitmap.allocPixels(ImageInfo.makeS32(400, 400, ColorAlphaType.OPAQUE));
image.readPixels(bitmap);
canvas.drawBitmapRect(bitmap, Rect.makeXYWH(0, 0, 200, 200));
canvas.drawString("Image.readPixels", 0, 220, inter13, blackFill);
advance(canvas, width);

// Bitmap + Image.readPixels(50, 50)
var partialBitmap = new Bitmap();
partialBitmap.allocPixels(new ImageInfo(300, 300, ColorType.GRAY_8, ColorAlphaType.OPAQUE));
image.readPixels(partialBitmap, 50, 50);
canvas.drawBitmapRect(partialBitmap, Rect.makeXYWH(25, 25, 150, 150));
canvas.drawString("Image.readPixels(50, 50)", 0, 220, inter13, blackFill);
advance(canvas, width);

// Bitmap.makeFromImage
var bitmapFromImage = Bitmap.makeFromImage(image);
canvas.drawBitmapRect(bitmapFromImage, Rect.makeXYWH(0, 0, 200, 200));
canvas.drawString("Bitmap.makeFromImage", 0, 220, inter13, blackFill);
advance(canvas, width);

// Image.makeFromBitmap
var imageFromBitmap = Image.makeFromBitmap(bitmap);
canvas.drawImageRect(imageFromBitmap, Rect.makeXYWH(0, 0, 200, 200));
canvas.drawString("Image.makeFromBitmap", 0, 220, inter13, blackFill);
advance(canvas, width);

// Bitmap readPixels/installPixels
var info = bitmapFromImage.getImageInfo();
var threshold = 100 + phase() * 100;
byte[] pixels = bitmapFromImage.readPixels();
ByteBuffer buffer = ByteBuffer.wrap(pixels); // Assume RGBA_8888
Function<Integer, Integer> luminocity = color -> Color.getR(color) + Color.getG(color) + Color.getB(color);
Comparator<Integer> cmp = (a, b) -> Integer.compare(luminocity.apply(a), luminocity.apply(b));
for (int x = 0; x < info.getWidth(); ++x) {
// read pixels
Integer column[] = new Integer[info.getHeight()];
for (int y = 0; y < info.getHeight(); ++y)
column[y] = buffer.getInt((y * info.getWidth() + x) * info.getBytesPerPixel());

// sort pixels
var lastIdx = 0;
for (int y = 0; y < info.getHeight() - 1; ++y) {
if (Math.abs(luminocity.apply(column[y]) - luminocity.apply(column[y + 1])) > threshold) {
Arrays.parallelSort(column, lastIdx, y, cmp);
lastIdx = y;
}
}
Arrays.parallelSort(column, lastIdx, info.getHeight(), cmp);

// write pixels
for (int y = 0; y < info.getHeight(); ++y)
buffer.putInt((y * info.getWidth() + x) * info.getBytesPerPixel(), column[y]);
}
bitmapFromImage.installPixels(pixels);
canvas.drawBitmapRect(bitmapFromImage, Rect.makeXYWH(0, 0, 200, 200));
canvas.drawString("Bitmap.readPixels/installPixels", 0, 220, inter13, blackFill);
advance(canvas, width);

// Image.makeRaster
var imageFromPixels = Image.makeRaster(info, pixels, info.getMinRowBytes());
canvas.drawImageRect(imageFromPixels, Rect.makeXYWH(0, 0, 200, 200));
canvas.drawString("Image.makeRaster", 0, 220, inter13, blackFill);
advance(canvas, width);

// Image.makeRaster + Data
var imageFromData = Image.makeRaster(info, Data.makeFromBytes(pixels), info.getMinRowBytes());
canvas.drawImageRect(imageFromPixels, Rect.makeXYWH(0, 0, 200, 200));
canvas.drawString("Image.makeRaster + Data", 0, 220, inter13, blackFill);
advance(canvas, width);

bitmap.close();
partialBitmap.close();
bitmapFromImage.close();
imageFromBitmap.close();
imageFromPixels.close();
imageFromData.close();
}
}
8 changes: 2 additions & 6 deletions examples/scenes/src/ColorFiltersScene.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,6 @@ public ColorFiltersScene() {
public void draw(Canvas canvas, int width, int height, float dpi, int xpos, int ypos) {
canvas.translate(30, 30);

float percent = Math.abs((System.currentTimeMillis() % 3000) / 10f - 150f) - 25f;
percent = Math.round(Math.max(0f, Math.min(100f, percent)));
float ratio = percent / 100f;

byte[] tablePosterize = new byte[256];
for (int i = 0; i < 256; ++i)
tablePosterize[i] = (byte) (i & 0x80);
Expand All @@ -48,12 +44,12 @@ public void draw(Canvas canvas, int width, int height, float dpi, int xpos, int
0, 0, 0, 1, 0
)),
ColorFilter.makeHSLAMatrix(new ColorMatrix(
0, 0, 0, 0, ratio,
0, 0, 0, 0, phase(),
0, 1, 0, 0, 0,
0, 0, 1, 0, 0,
0, 0, 0, 1, 0
)),
ColorFilter.makeLerp(ColorFilter.makeBlend(0x80CC3333, BlendMode.SRC_OVER), ColorFilter.makeBlend(0x803333CC, BlendMode.SRC_OVER), ratio),
ColorFilter.makeLerp(ColorFilter.makeBlend(0x80CC3333, BlendMode.SRC_OVER), ColorFilter.makeBlend(0x803333CC, BlendMode.SRC_OVER), phase()),
ColorFilter.makeLighting(0x80CC3333, 0x803333CC),
},

Expand Down
7 changes: 7 additions & 0 deletions examples/scenes/src/Scene.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,11 @@ public static void drawStringLeft(Canvas canvas, String text, Rect outer, Font f
outer.getTop() + (outer.getHeight() - innerHeight) / 2f - metrics.getAscent(),
font, paint);
}

public static float phase() {
var angle = (System.currentTimeMillis() % 5000) / 5000.0 * Math.PI * 2.0;
var phase = Math.sin(angle) * 1.2;
phase = Math.min(1.0, Math.max(-1.0, phase));
return (float) (phase + 1) / 2f;
}
}
3 changes: 2 additions & 1 deletion examples/scenes/src/Scenes.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@

public class Scenes {
public static TreeMap<String, Scene> scenes;
public static String currentScene = "Text Line Decorations";
public static String currentScene = "Bitmap Image";
public static HUD hud = new HUD();
public static boolean vsync = true;
public static boolean stats = true;

static {
scenes = new TreeMap<>();
scenes.put("Bitmap", null);
scenes.put("Bitmap Image", null);
scenes.put("Blends", null);
scenes.put("Color Filters", null);
scenes.put("Decorations Bench", null);
Expand Down
10 changes: 3 additions & 7 deletions examples/scenes/src/TextBlobScene.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public void drawPos(Canvas canvas) {
float distance = 0;

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

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

float radius = 50;
int period = 3000;

try (Path path = new Path().addCircle(0, 0, radius);
PathMeasure measure = new PathMeasure(path);
Paint fill = new Paint().setColor(0xffffba08);
Paint stroke = new Paint().setColor(0xff3a86ff).setMode(PaintMode.STROKE).setStrokeWidth(1f);)
{
float length = measure.getLength();
float relativeOffset = System.currentTimeMillis() % period / (float) period;
float distance = relativeOffset * length;
float distance = phase() * length;

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

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

try (var shaper = Shaper.make();
Expand Down
8 changes: 1 addition & 7 deletions native/src/Bitmap.cc
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,7 @@ extern "C" JNIEXPORT void JNICALL Java_org_jetbrains_skija_Bitmap__1nSwap
extern "C" JNIEXPORT jobject JNICALL Java_org_jetbrains_skija_Bitmap__1nGetImageInfo
(JNIEnv* env, jclass jclass, jlong ptr) {
SkBitmap* instance = reinterpret_cast<SkBitmap*>(static_cast<uintptr_t>(ptr));
const SkImageInfo& info = instance->info();
return env->NewObject(skija::ImageInfo::cls, skija::ImageInfo::ctor,
info.width(),
info.height(),
static_cast<jint>(info.colorType()),
static_cast<jint>(info.alphaType()),
reinterpret_cast<jlong>(info.refColorSpace().release()));
return skija::ImageInfo::toJava(env, instance->info());
}

extern "C" JNIEXPORT jint JNICALL Java_org_jetbrains_skija_Bitmap__1nGetRowBytesAsPixels
Expand Down
47 changes: 45 additions & 2 deletions native/src/Image.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,40 @@
#include "SkImage.h"
#include "interop.hh"

extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skija_Image__1nMakeRaster
(JNIEnv* env, jclass jclass, jint width, jint height, jint colorType, jint alphaType, jlong colorSpacePtr, jbyteArray bytesArr, jlong rowBytes) {
SkColorSpace* colorSpace = reinterpret_cast<SkColorSpace*>(static_cast<uintptr_t>(colorSpacePtr));
SkImageInfo imageInfo = SkImageInfo::Make(width,
height,
static_cast<SkColorType>(colorType),
static_cast<SkAlphaType>(alphaType),
sk_ref_sp<SkColorSpace>(colorSpace));
void* bytes = env->GetPrimitiveArrayCritical(bytesArr, 0);
sk_sp<SkImage> image = SkImage::MakeRasterCopy(SkPixmap(imageInfo, bytes, rowBytes));
env->ReleasePrimitiveArrayCritical(bytesArr, bytes, 0);
return reinterpret_cast<jlong>(image.release());
}

extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skija_Image__1nMakeRasterData
(JNIEnv* env, jclass jclass, jint width, jint height, jint colorType, jint alphaType, jlong colorSpacePtr, jlong dataPtr, jlong rowBytes) {
SkColorSpace* colorSpace = reinterpret_cast<SkColorSpace*>(static_cast<uintptr_t>(colorSpacePtr));
SkImageInfo imageInfo = SkImageInfo::Make(width,
height,
static_cast<SkColorType>(colorType),
static_cast<SkAlphaType>(alphaType),
sk_ref_sp<SkColorSpace>(colorSpace));
SkData* data = reinterpret_cast<SkData*>(static_cast<uintptr_t>(dataPtr));
sk_sp<SkImage> image = SkImage::MakeRasterData(imageInfo, sk_ref_sp(data), rowBytes);
return reinterpret_cast<jlong>(image.release());
}

extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skija_Image__1nMakeFromBitmap
(JNIEnv* env, jclass jclass, jlong bitmapPtr) {
SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(static_cast<uintptr_t>(bitmapPtr));
sk_sp<SkImage> image = SkImage::MakeFromBitmap(*bitmap);
return reinterpret_cast<jlong>(image.release());
}

extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skija_Image__1nMakeFromEncoded
(JNIEnv* env, jclass jclass, jbyteArray encodedArray) {
jsize encodedLen = env->GetArrayLength(encodedArray);
Expand All @@ -16,10 +50,10 @@ extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skija_Image__1nMakeFromEnc
return reinterpret_cast<jlong>(image.release());
}

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

extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skija_Image__1nEncodeToData
Expand All @@ -46,3 +80,12 @@ extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skija_Image__1nMakeShaderC
sk_sp<SkShader> shader = instance->makeShader(static_cast<SkTileMode>(tmx), static_cast<SkTileMode>(tmy), sampling, localMatrix.get());
return reinterpret_cast<jlong>(shader.release());
}

extern "C" JNIEXPORT jboolean JNICALL Java_org_jetbrains_skija_Image__1nReadPixelsBitmap
(JNIEnv* env, jclass jclass, jlong ptr, jlong contextPtr, jlong bitmapPtr, jint srcX, jint srcY, jboolean cache) {
SkImage* instance = reinterpret_cast<SkImage*>(static_cast<uintptr_t>(ptr));
GrDirectContext* context = reinterpret_cast<GrDirectContext*>(static_cast<uintptr_t>(contextPtr));
SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(static_cast<uintptr_t>(bitmapPtr));
auto cachingHint = cache ? SkImage::CachingHint::kAllow_CachingHint : SkImage::CachingHint::kDisallow_CachingHint;
return instance->readPixels(context, bitmap->info(), bitmap->getPixels(), bitmap->pixmap().rowBytes(), srcX, srcY, cachingHint);
}
9 changes: 9 additions & 0 deletions native/src/interop.cc
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,15 @@ namespace skija {
void onUnload(JNIEnv* env) {
env->DeleteGlobalRef(cls);
}

jobject toJava(JNIEnv* env, const SkImageInfo& info) {
return env->NewObject(cls, ctor,
info.width(),
info.height(),
static_cast<jint>(info.colorType()),
static_cast<jint>(info.alphaType()),
reinterpret_cast<jlong>(info.refColorSpace().release()));
}
}

namespace IPoint {
Expand Down
2 changes: 2 additions & 0 deletions native/src/interop.hh
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <vector>
#include "SkFontMetrics.h"
#include "SkFontStyle.h"
#include "SkImageInfo.h"
#include "SkMatrix.h"
#include "SkM44.h"
#include "SkPaint.h"
Expand Down Expand Up @@ -138,6 +139,7 @@ namespace skija {
extern jmethodID ctor;
void onLoad(JNIEnv* env);
void onUnload(JNIEnv* env);
jobject toJava(JNIEnv* env, const SkImageInfo& imageInfo);
}

namespace IPoint {
Expand Down
Loading

0 comments on commit 3df8fa6

Please sign in to comment.