Skip to content

Commit

Permalink
Codec (closes #95)
Browse files Browse the repository at this point in the history
  • Loading branch information
tonsky committed Mar 12, 2021
1 parent 9c45b5b commit ccf303e
Show file tree
Hide file tree
Showing 30 changed files with 869 additions and 28 deletions.
38 changes: 19 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,24 +97,25 @@ APIs:
```
Bitmap ▓▓▓▓▓▓▓▓▓▓ Paint ▓▓▓▓▓▓▓▓▓▓
Canvas ▓▓▓▓▓▓▓▓░░ Path ▓▓▓▓▓▓▓▓▓▓
Color ▓░░░░░░░░░ PathEffects ▓▓▓▓▓▓▓▓▓▓
ColorFilter ▓▓▓▓▓▓▓▓▓▓ PathMeasure ▓▓▓▓▓▓▓▓▓▓
ColorInfo ▓▓▓▓▓▓▓▓▓▓ PaintFilterCanvas ▓▓▓▓▓▓▓▓▓▓
ColorSpace ▓▓▓▓░░░░░░ Picture ▓▓▓▓▓▓▓▓▓░
Data ▓▓▓▓▓▓▓▓▓░ PictureRecorder ▓▓▓▓▓▓▓▓▓▓
Drawable ▓▓▓▓▓▓▓▓░░ PixelRef ▓▓▓▓▓▓▓▓▓▓
Flattenable ░░░░░░░░░░ Pixmap ░░░░░░░░░░
Font ▓▓▓▓▓▓▓▓▓▓ Region ▓▓▓▓▓▓▓▓▓▓
FontData ░░░░░░░░░░ ScalerContext ░░░░░░░░░░
FontManager ▓▓▓▓▓▓▓▓▓░ Shader ▓▓▓▓▓▓▓▓▓▓
FontStyle ▓▓▓▓▓▓▓▓▓▓ ShadowUtils ▓▓▓▓▓▓▓▓▓▓
FontStyleSet ▓▓▓▓▓▓▓▓▓▓ Stream ░░░░░░░░░░
Image ▓▓░░░░░░░░ String ▓░░░░░░░░░
ImageFilters ▓▓▓▓▓▓▓▓▓▓ Surface ▓░░░░░░░░░
ImageInfo ▓▓▓▓▓▓▓▓▓▓ TextBlob ▓▓▓▓▓▓▓▓▓▓
MaskFilter ▓▓▓▓▓▓▓▓▓▓ TextBlobBuilder ▓▓▓▓▓▓▓▓▓▓
Matrix33 ▓▓▓░░░░░░░ Typeface ▓▓▓▓▓▓▓▓░░
Matrix44 ▓▓▓░░░░░░░ WStream ▓▓░░░░░░░░
Codec ▓▓▓▓░░░░░░ PathEffects ▓▓▓▓▓▓▓▓▓▓
Color ▓░░░░░░░░░ PathMeasure ▓▓▓▓▓▓▓▓▓▓
ColorFilter ▓▓▓▓▓▓▓▓▓▓ PaintFilterCanvas ▓▓▓▓▓▓▓▓▓▓
ColorInfo ▓▓▓▓▓▓▓▓▓▓ Picture ▓▓▓▓▓▓▓▓▓░
ColorSpace ▓▓▓▓░░░░░░ PictureRecorder ▓▓▓▓▓▓▓▓▓▓
Data ▓▓▓▓▓▓▓▓▓░ PixelRef ▓▓▓▓▓▓▓▓▓▓
Drawable ▓▓▓▓▓▓▓▓░░ Pixmap ░░░░░░░░░░
Flattenable ░░░░░░░░░░ Region ▓▓▓▓▓▓▓▓▓▓
Font ▓▓▓▓▓▓▓▓▓▓ ScalerContext ░░░░░░░░░░
FontData ░░░░░░░░░░ Shader ▓▓▓▓▓▓▓▓▓▓
FontManager ▓▓▓▓▓▓▓▓▓░ ShadowUtils ▓▓▓▓▓▓▓▓▓▓
FontStyle ▓▓▓▓▓▓▓▓▓▓ Stream ░░░░░░░░░░
FontStyleSet ▓▓▓▓▓▓▓▓▓▓ String ▓░░░░░░░░░
Image ▓▓░░░░░░░░ Surface ▓░░░░░░░░░
ImageFilters ▓▓▓▓▓▓▓▓▓▓ TextBlob ▓▓▓▓▓▓▓▓▓▓
ImageInfo ▓▓▓▓▓▓▓▓▓▓ TextBlobBuilder ▓▓▓▓▓▓▓▓▓▓
MaskFilter ▓▓▓▓▓▓▓▓▓▓ Typeface ▓▓▓▓▓▓▓▓░░
Matrix33 ▓▓▓░░░░░░░ WStream ▓▓░░░░░░░░
Matrix44 ▓▓▓░░░░░░░
Shaper: Paragraph:
Expand All @@ -134,7 +135,6 @@ SVG:
SVGDOM ▓▓▓▓▓▓▓▓░░
SVGCanvas ▓▓▓▓▓▓▓▓▓▓
```

## Using Skija
Expand Down
Binary file added examples/scenes/images/codecs/animated.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/scenes/images/codecs/animated.webp
Binary file not shown.
Binary file added examples/scenes/images/codecs/dotpeek.ico
Binary file not shown.
Binary file removed examples/scenes/images/codecs/heic_loseless.heic
Binary file not shown.
Binary file removed examples/scenes/images/codecs/heic_lossy.heic
Binary file not shown.
Binary file removed examples/scenes/images/codecs/jpeg2000_loseless.jp2
Binary file not shown.
Binary file removed examples/scenes/images/codecs/jpeg2000_lossy.jp2
Binary file not shown.
Binary file added examples/scenes/images/codecs/orient_bl.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/scenes/images/codecs/orient_br.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/scenes/images/codecs/orient_lb.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/scenes/images/codecs/orient_lt.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/scenes/images/codecs/orient_rb.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/scenes/images/codecs/orient_rt.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/scenes/images/codecs/orient_tl.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/scenes/images/codecs/orient_tr.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed examples/scenes/images/codecs/tiff.tiff
Binary file not shown.
137 changes: 137 additions & 0 deletions examples/scenes/src/CodecScene.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
package org.jetbrains.skija.examples.scenes;

import java.io.*;
import java.util.*;
import java.util.stream.*;
import org.jetbrains.skija.*;

public class CodecScene extends Scene {
Paint stroke = new Paint().setColor(0x80CC3333).setMode(PaintMode.STROKE).setStrokeWidth(1);
List<Pair<String, Bitmap>> formats = new ArrayList<>();
List<Pair<String, Codec>> orientations = new ArrayList<>();

static class Animation {
Codec codec;
Bitmap bitmap;
int prevFrame = -1;
int[] durations;
long totalDuration;
}

List<Pair<String, Animation>> animations = new ArrayList<>();

float x, y;
float rowH = 100;
float columnW = 100;

public CodecScene() {
for (var file: new String[] {"bmp.bmp", "gif.gif", "favicon.ico", "dotpeek.ico", "jpeg.jpg", "png.png", "webp_lossy.webp", "webp_loseless.webp"}) {
try (var codec = Codec.makeFromData(Data.makeFromFileName(file("images/codecs/" + file)))) {
formats.add(new Pair(file + "\n" + codec.getEncodedImageFormat(), codec.readPixels()));
} catch (Exception e) {
formats.add(new Pair(file + "\n" + e.getMessage(), null));
}
}

for (var file: new String[] {"orient_tl.jpg", "orient_tr.jpg", "orient_br.jpg", "orient_bl.jpg", "orient_lt.jpg", "orient_lb.jpg", "orient_rb.jpg", "orient_rt.jpg",}) {
orientations.add(new Pair(file, Codec.makeFromData(Data.makeFromFileName(file("images/codecs/" + file)))));
}

for (var file: new String[] {"animated.gif", "animated.webp"}) {
var animation = new Animation();
animation.codec = Codec.makeFromData(Data.makeFromFileName(file("images/codecs/" + file)));
animation.bitmap = new Bitmap();
animation.bitmap.allocPixels(animation.codec.getImageInfo());
animation.durations = Arrays.stream(animation.codec.getFramesInfo()).mapToInt(AnimationFrameInfo::getDuration).toArray();
animation.totalDuration = Arrays.stream(animation.durations).sum();
animations.add(new Pair(file, animation));
}
}

public void drawOne(Canvas canvas, int width, String s, Runnable draw) {
if (x + columnW >= width) {
x = 20;
y += rowH + 60;
}
canvas.save();
canvas.translate(x, y);
draw.run();

var lines = s.lines().toArray();
for (int i = 0; i < lines.length; ++i)
canvas.drawString((String) lines[i], 0, rowH + 20 + i * 20, inter13, blackFill);
canvas.restore();
x += columnW + 20;
}

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

for (var pair: formats) {
var label = pair.getFirst();
var bitmap = pair.getSecond();
if (bitmap == null) {
drawOne(canvas, width, label, () -> {
canvas.drawRect(Rect.makeXYWH(0, 0, columnW, rowH), stroke);
canvas.drawLine(0, 0, columnW, rowH, stroke);
canvas.drawLine(0, rowH, columnW, 0, stroke);
});
} else {
drawOne(canvas, width, label, () -> { canvas.drawBitmapRect(bitmap, Rect.makeXYWH(0, 0, columnW, rowH)); });
}
}

x = 20;
y += rowH + 60;
for (var pair: orientations) {
var label = pair.getFirst();
var codec = pair.getSecond();
var origin = codec.getEncodedOrigin();
try (var bitmap = codec.readPixels()) {
int bitmapWidth = origin.swapsWidthHeight() ? codec.getHeight() : codec.getWidth();
int bitmapHeight = origin.swapsWidthHeight() ? codec.getWidth() : codec.getHeight();
drawOne(canvas, width, label + "\n" + codec.getEncodedImageFormat(), () -> {
canvas.save();
canvas.concat(origin.toMatrix(bitmapWidth, bitmapHeight));
canvas.drawBitmapRect(bitmap, Rect.makeXYWH(0, 0, codec.getWidth(), codec.getHeight()));
canvas.restore();
canvas.drawRect(Rect.makeXYWH(0, 0, bitmapWidth, bitmapHeight), stroke);
});
}
}

x = 20;
y += rowH + 60;

for (var pair: animations) {
var label = pair.getFirst();
var animation = pair.getSecond();
var codec = animation.codec;

int duration = 0;
int frame = 0;
long now = System.currentTimeMillis() % animation.totalDuration;
for (; frame < animation.durations.length; ++frame) {
duration += animation.durations[frame];
if (duration >= now)
break;
}
int finalFrame = frame;

drawOne(canvas, width, label + "\n" + codec.getEncodedImageFormat(), () -> {
try (var bitmap = new Bitmap()) {
bitmap.allocPixels(codec.getImageInfo());
codec.readPixels(bitmap, finalFrame);
canvas.drawBitmapRect(bitmap, Rect.makeXYWH(0, 0, columnW, rowH));
}
});

drawOne(canvas, width, label + "\n" + codec.getEncodedImageFormat() + " + priorFrame", () -> {
codec.readPixels(animation.bitmap, finalFrame, animation.prevFrame);
canvas.drawBitmapRect(animation.bitmap, Rect.makeXYWH(0, 0, columnW, rowH));
});
}
}
}
6 changes: 1 addition & 5 deletions examples/scenes/src/ImageCodecsScene.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,10 @@ public void loadImage(String file) {
public ImageCodecsScene() {
loadImage("bmp.bmp");
loadImage("gif.gif");
loadImage("heic_lossy.heic");
loadImage("heic_loseless.heic");
loadImage("favicon.ico");
loadImage("dotpeek.ico");
loadImage("jpeg.jpg");
loadImage("jpeg2000_lossy.jp2");
loadImage("jpeg2000_loseless.jp2");
loadImage("png.png");
loadImage("tiff.tiff");
loadImage("webp_lossy.webp");
loadImage("webp_loseless.webp");
}
Expand Down
3 changes: 2 additions & 1 deletion examples/scenes/src/Scenes.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

public class Scenes {
public static TreeMap<String, Scene> scenes;
public static String currentScene = "Image Codecs";
public static String currentScene = "Codec";
public static HUD hud = new HUD();
public static boolean vsync = true;
public static boolean stats = true;
Expand All @@ -16,6 +16,7 @@ public class Scenes {
scenes.put("Bitmap", null);
scenes.put("Bitmap Image", null);
scenes.put("Blends", null);
scenes.put("Codec", null);
scenes.put("Color Filters", null);
scenes.put("Decorations Bench", null);
scenes.put("Drawable", null);
Expand Down
90 changes: 90 additions & 0 deletions native/src/Codec.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
#include <iostream>
#include <jni.h>
#include "SkBitmap.h"
#include "SkCodec.h"
#include "SkData.h"
#include "interop.hh"

static void deleteCodec(SkCodec* instance) {
delete instance;
}

extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skija_Codec__1nGetFinalizer(JNIEnv* env, jclass jclass) {
return static_cast<jlong>(reinterpret_cast<uintptr_t>(&deleteCodec));
}

extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skija_Codec__1nMakeFromData
(JNIEnv* env, jclass jclass, jlong dataPtr) {
SkData* data = reinterpret_cast<SkData*>(static_cast<uintptr_t>(dataPtr));
std::unique_ptr<SkCodec> instance = SkCodec::MakeFromData(sk_ref_sp(data));
return reinterpret_cast<jlong>(instance.release());
}

extern "C" JNIEXPORT jobject JNICALL Java_org_jetbrains_skija_Codec__1nGetImageInfo
(JNIEnv* env, jclass jclass, jlong ptr) {
SkCodec* instance = reinterpret_cast<SkCodec*>(static_cast<uintptr_t>(ptr));
return skija::ImageInfo::toJava(env, instance->getInfo());
}

extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skija_Codec__1nGetSize
(JNIEnv* env, jclass jclass, jlong ptr) {
SkCodec* instance = reinterpret_cast<SkCodec*>(static_cast<uintptr_t>(ptr));
return packISize(instance->dimensions());
}

extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skija_Codec__1nGetEncodedOrigin
(JNIEnv* env, jclass jclass, jlong ptr) {
SkCodec* instance = reinterpret_cast<SkCodec*>(static_cast<uintptr_t>(ptr));
return static_cast<jint>(instance->getOrigin());
}

extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skija_Codec__1nGetEncodedImageFormat
(JNIEnv* env, jclass jclass, jlong ptr) {
SkCodec* instance = reinterpret_cast<SkCodec*>(static_cast<uintptr_t>(ptr));
return static_cast<jint>(instance->getEncodedFormat());
}

extern "C" JNIEXPORT jint JNICALL Java_org_jetbrains_skija_Codec__1nReadPixels
(JNIEnv* env, jclass jclass, jlong ptr, jlong bitmapPtr, jint frame, jint priorFrame) {
SkCodec* instance = reinterpret_cast<SkCodec*>(static_cast<uintptr_t>(ptr));
SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(static_cast<uintptr_t>(bitmapPtr));
SkCodec::Options opts;
opts.fFrameIndex = frame;
opts.fPriorFrame = priorFrame;
SkCodec::Result result = instance->getPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), &opts);
return static_cast<jint>(result);
}

extern "C" JNIEXPORT jint JNICALL Java_org_jetbrains_skija_Codec__1nGetFrameCount
(JNIEnv* env, jclass jclass, jlong ptr) {
SkCodec* instance = reinterpret_cast<SkCodec*>(static_cast<uintptr_t>(ptr));
return instance->getFrameCount();
}

extern "C" JNIEXPORT jobject JNICALL Java_org_jetbrains_skija_Codec__1nGetFrameInfo
(JNIEnv* env, jclass jclass, jlong ptr, jint frame) {
SkCodec* instance = reinterpret_cast<SkCodec*>(static_cast<uintptr_t>(ptr));
SkCodec::FrameInfo info;
instance->getFrameInfo(frame, &info);
return skija::AnimationFrameInfo::toJava(env, info);
}

extern "C" JNIEXPORT jobject JNICALL Java_org_jetbrains_skija_Codec__1nGetFramesInfo
(JNIEnv* env, jclass jclass, jlong ptr, jint frame) {
SkCodec* instance = reinterpret_cast<SkCodec*>(static_cast<uintptr_t>(ptr));
SkCodec::FrameInfo info;
std::vector<SkCodec::FrameInfo> frames = instance->getFrameInfo();
jobjectArray res = env->NewObjectArray(frames.size(), skija::AnimationFrameInfo::cls, nullptr);
for (int i = 0; i < frames.size(); ++i) {
jobject infoObj = skija::AnimationFrameInfo::toJava(env, frames[i]);
env->SetObjectArrayElement(res, i, infoObj);
env->DeleteLocalRef(infoObj);
}
return res;
}

extern "C" JNIEXPORT jint JNICALL Java_org_jetbrains_skija_Codec__1nGetRepetitionCount
(JNIEnv* env, jclass jclass, jlong ptr) {
SkCodec* instance = reinterpret_cast<SkCodec*>(static_cast<uintptr_t>(ptr));
return instance->getRepetitionCount();
}
40 changes: 39 additions & 1 deletion native/src/interop.cc
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,42 @@ namespace java {
}

namespace skija {
namespace AnimationFrameInfo {
jclass cls;
jmethodID ctor;

void onLoad(JNIEnv* env) {
jclass local = env->FindClass("org/jetbrains/skija/AnimationFrameInfo");
cls = static_cast<jclass>(env->NewGlobalRef(local));
ctor = env->GetMethodID(cls, "<init>", "(IIZIZIILorg/jetbrains/skija/IRect;)V");
}

void onUnload(JNIEnv* env) {
env->DeleteGlobalRef(cls);
}

jobject toJava(JNIEnv* env, const SkCodec::FrameInfo& i) {
SkBlendMode blend;
switch (i.fBlend) {
case SkCodecAnimation::Blend::kSrcOver:
blend = SkBlendMode::kSrcOver;
break;
case SkCodecAnimation::Blend::kSrc:
blend = SkBlendMode::kSrc;
break;
}
return env->NewObject(cls, ctor,
i.fRequiredFrame,
i.fDuration,
i.fFullyReceived,
static_cast<jint>(i.fAlphaType),
i.fHasAlphaWithinBounds,
static_cast<jint>(i.fDisposalMethod),
static_cast<jint>(blend),
IRect::fromSkIRect(env, i.fFrameRect));
}
}

namespace Color4f {
jclass cls;
jmethodID ctor;
Expand Down Expand Up @@ -684,6 +720,7 @@ namespace skija {
}

void onLoad(JNIEnv* env) {
AnimationFrameInfo::onLoad(env);
Color4f::onLoad(env);
Drawable::onLoad(env);
FontFamilyName::onLoad(env);
Expand Down Expand Up @@ -724,6 +761,7 @@ namespace skija {
FontFamilyName::onUnload(env);
Drawable::onUnload(env);
Color4f::onUnload(env);
AnimationFrameInfo::onUnload(env);
}
}
std::unique_ptr<SkMatrix> skMatrix(JNIEnv* env, jfloatArray matrixArray) {
Expand Down Expand Up @@ -891,7 +929,7 @@ jlong packIPoint(SkIPoint p) {
return packTwoInts(p.fX, p.fY);
}

jlong packTwoInt32(SkISize p) {
jlong packISize(SkISize p) {
return packTwoInts(p.fWidth, p.fHeight);
}

Expand Down
Loading

0 comments on commit ccf303e

Please sign in to comment.