Skip to content

Commit 4387232

Browse files
committed
Fix building for coil
1 parent cb212b7 commit 4387232

File tree

9 files changed

+186
-32
lines changed

9 files changed

+186
-32
lines changed

app/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ android {
4343

4444
dependencies {
4545
implementation project(":avif-coder")
46+
implementation project(":avif-coder-coil")
4647
implementation 'androidx.core:core-ktx:1.10.1'
4748
implementation 'androidx.appcompat:appcompat:1.6.1'
4849
implementation 'com.google.android.material:material:1.9.0'

app/src/main/AndroidManifest.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
33
xmlns:tools="http://schemas.android.com/tools">
44

5+
<uses-permission android:name="android.permission.INTERNET"/>
6+
57
<application
68
android:allowBackup="true"
79
android:dataExtractionRules="@xml/data_extraction_rules"

app/src/main/java/com/radzivon/bartoshyk/avif/MainActivity.kt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ import android.util.Log
1111
import android.util.Size
1212
import androidx.core.graphics.scale
1313
import androidx.lifecycle.lifecycleScope
14+
import coil.ImageLoader
15+
import coil.load
16+
import com.github.awxkee.avifcoil.HeifDecoder
1417
import com.radzivon.bartoshyk.avif.coder.HeifCoder
1518
import com.radzivon.bartoshyk.avif.databinding.ActivityMainBinding
1619
import kotlinx.coroutines.Dispatchers
@@ -32,8 +35,8 @@ class MainActivity : AppCompatActivity() {
3235
setContentView(binding.root)
3336

3437
// Example of a call to a native method
35-
36-
val buffer = this.assets.open("bt_2020_pq.avif").source().buffer().readByteArray()
38+
//
39+
val buffer = this.assets.open("hato-wide-gamut.avif").source().buffer().readByteArray()
3740
// assert(HeifCoder().isAvif(buffer))
3841
val size = HeifCoder().getSize(buffer)!!
3942
val bitmap = HeifCoder().decodeSampled(buffer, size.width / 3, size.height / 3)
@@ -45,6 +48,7 @@ class MainActivity : AppCompatActivity() {
4548
val encoded = HeifCoder().encodeAvif(bitmap)
4649
val decodedSample = HeifCoder().decode(encoded)
4750
binding.imageView.setImageBitmap(decodedSample)
51+
4852
// binding.imageView.setImageBitmap(bitmap)
4953
// binding.imageView.setImageBitmap(cc16)
5054
// val avif12DepthBuffer =

avif-coder-coil/src/main/java/com/github/awxkee/avifcoil/HeifDecoder.kt

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,22 +15,23 @@ import coil.size.Scale
1515
import coil.size.pxOrElse
1616
import com.radzivon.bartoshyk.avif.coder.HeifCoder
1717
import kotlinx.coroutines.runInterruptible
18+
import okio.ByteString
1819

1920
class HeifDecoder(
2021
private val source: SourceResult,
2122
private val options: Options,
22-
private val imageLoader: ImageLoader
23+
private val imageLoader: ImageLoader,
24+
private val sourceData: ByteArray,
2325
) : Decoder {
2426

2527
override suspend fun decode(): DecodeResult? = runInterruptible {
26-
val byteArray = source.source.source().readByteArray()
27-
val originalSize = HeifCoder().getSize(byteArray) ?: return@runInterruptible null
28+
val originalSize = HeifCoder().getSize(sourceData) ?: return@runInterruptible null
2829
val dstWidth = options.size.width.pxOrElse { 0 }
2930
val dstHeight = options.size.height.pxOrElse { 0 }
3031
if (options.scale == Scale.FIT) {
3132
val scaledSize = aspectScale(originalSize, Size(dstWidth, dstHeight))
3233
val originalImage =
33-
HeifCoder().decodeSampled(byteArray, scaledSize.width, scaledSize.height)
34+
HeifCoder().decodeSampled(sourceData, scaledSize.width, scaledSize.height)
3435
return@runInterruptible DecodeResult(
3536
BitmapDrawable(
3637
options.context.resources,
@@ -51,7 +52,7 @@ class HeifDecoder(
5152
)) else originalSize
5253
val aspectSize = aspectScale(originalSize, targetSizeNotAspect)
5354
val originalImage =
54-
HeifCoder().decodeSampled(byteArray, aspectSize.width, aspectSize.height)
55+
HeifCoder().decodeSampled(sourceData, aspectSize.width, aspectSize.height)
5556
val resizedBitmap = resizeAspectFill(originalImage, Size(dstWidth, dstHeight))
5657
originalImage.recycle()
5758
return@runInterruptible DecodeResult(
@@ -113,9 +114,12 @@ class HeifDecoder(
113114
result: SourceResult,
114115
options: Options,
115116
imageLoader: ImageLoader
116-
) = if (HeifCoder().isHeif(result.source.source().readByteArray())) {
117-
HeifDecoder(result, options, imageLoader)
118-
} else null
117+
): Decoder? {
118+
val originalBytes = result.source.source().readByteArray()
119+
if (HeifCoder().isSupportedImage(originalBytes)) {
120+
return HeifDecoder(result, options, imageLoader, originalBytes)
121+
} else return null
122+
}
119123
}
120124

121125
}

avif-coder/src/main/cpp/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ add_library( # Sets the name of the library.
1717
coder.cpp jni_exception.cpp scaler.cpp icc/cmsalpha.c icc/cmscam02.c icc/cmscgats.c icc/cmscnvrt.c icc/cmserr.c icc/cmsgamma.c
1818
icc/cmsgmt.c icc/cmshalf.c icc/cmsintrp.c icc/cmsio0.c icc/cmsio1.c icc/cmslut.c icc/cmsmd5.c icc/cmsmtrx.c icc/cmsnamed.c
1919
icc/cmsopt.c icc/cmspack.c icc/cmspcs.c icc/cmsplugin.c icc/cmsps2.c icc/cmssamp.c icc/cmssm.c icc/cmstypes.c icc/cmsvirt.c
20-
icc/cmswtpnt.c icc/cmsxform.c rgba_to_bgra_neon.cpp rgba_to_bgra.cpp colorspace.cpp)
20+
icc/cmswtpnt.c icc/cmsxform.c rgba_to_bgra_neon.cpp rgba_to_bgra.cpp colorspace.cpp halfFloats.cpp)
2121

2222
add_library(libaom STATIC IMPORTED)
2323
add_library(libx265 STATIC IMPORTED)

avif-coder/src/main/cpp/coder.cpp

Lines changed: 40 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include <math.h>
1919
#include <limits>
2020
#include "attenuate_alpha.h"
21+
#include "halfFloats.h"
2122

2223
struct AvifMemEncoder {
2324
std::vector<char> buffer;
@@ -83,12 +84,17 @@ jbyteArray encodeBitmap(JNIEnv *env, jobject thiz,
8384
return static_cast<jbyteArray>(nullptr);
8485
}
8586

87+
8688
void *addr;
8789
if (AndroidBitmap_lockPixels(env, bitmap, &addr) != 0) {
8890
throwPixelsException(env);
8991
return static_cast<jbyteArray>(nullptr);
9092
}
9193

94+
std::vector<uint8_t> sourceData(info.height * info.stride);
95+
std::copy(reinterpret_cast<uint8_t *>(addr),
96+
reinterpret_cast<uint8_t *>(addr) + info.height * info.stride, sourceData.data());
97+
9298
heif_image *image;
9399
heif_chroma chroma = heif_chroma_interleaved_RGBA;
94100
if (info.format == ANDROID_BITMAP_FORMAT_RGBA_F16 &&
@@ -124,10 +130,10 @@ jbyteArray encodeBitmap(JNIEnv *env, jobject thiz,
124130
int stride;
125131
uint8_t *imgData = heif_image_get_plane(image, heif_channel_interleaved, &stride);
126132
if (info.format == ANDROID_BITMAP_FORMAT_RGBA_8888) {
127-
libyuv::ARGBCopy(reinterpret_cast<const uint8_t *>(addr), (int) info.stride, imgData,
133+
libyuv::ARGBCopy(sourceData.data(), (int) info.stride, imgData,
128134
stride, (int) info.width, (int) info.height);
129135
} else if (info.format == ANDROID_BITMAP_FORMAT_RGB_565) {
130-
libyuv::RGB565ToARGB(reinterpret_cast<const uint8_t *>(addr), (int) info.stride, imgData,
136+
libyuv::RGB565ToARGB(sourceData.data(), (int) info.stride, imgData,
131137
stride, (int) info.width, (int) info.height);
132138
for (int i = 0; i < stride / 4 * info.height; i += 4) {
133139
imgData[i] = imgData[i + 1]; // R
@@ -138,14 +144,14 @@ jbyteArray encodeBitmap(JNIEnv *env, jobject thiz,
138144
} else if (info.format == ANDROID_BITMAP_FORMAT_RGBA_1010102) {
139145
if (heifCompressionFormat == heif_compression_HEVC) {
140146
auto dstY = (char *) imgData;
141-
auto srcY = (char *) addr;
147+
auto srcY = (char *) sourceData.data();
142148
for (int y = 0; y < info.height; ++y) {
143149
memcpy(dstY, srcY, info.width * 4 * sizeof(uint32_t));
144150
srcY += info.width * sizeof(uint64_t);
145151
dstY += stride;
146152
}
147153
} else {
148-
libyuv::AR30ToARGB(static_cast<const uint8_t *>(addr), (int) info.stride, imgData,
154+
libyuv::AR30ToARGB(sourceData.data(), (int) info.stride, imgData,
149155
stride, (int) info.width,
150156
(int) info.height);
151157
for (int i = 0; i < stride / 4 * info.height; i += 4) {
@@ -160,39 +166,53 @@ jbyteArray encodeBitmap(JNIEnv *env, jobject thiz,
160166
std::shared_ptr<char> dstARGB(
161167
static_cast<char *>(malloc(info.width * info.height * 4 * sizeof(uint16_t))),
162168
[](char *f) { free(f); });
163-
auto *srcData = static_cast<float16_t *>(addr);
169+
auto *srcData = reinterpret_cast<uint8_t *>(sourceData.data());
164170
uint16_t tmpR;
165171
uint16_t tmpG;
166172
uint16_t tmpB;
167173
uint16_t tmpA;
168-
auto *data64Ptr = reinterpret_cast<uint64_t *>(dstARGB.get());
169-
const float maxColors = (float) pow(2.0, bitDepth) - 1;
170-
for (int i = 0, k = 0; i < std::min(info.stride * info.height,
171-
info.width * info.height * 4); i += 4, k += 1) {
172-
tmpR = (uint16_t) (srcData[i] * maxColors);
173-
tmpG = (uint16_t) (srcData[i + 1] * maxColors);
174-
tmpB = (uint16_t) (srcData[i + 2] * maxColors);
175-
tmpA = (uint16_t) (srcData[i + 3] * maxColors);
176-
uint64_t color =
177-
((uint64_t) tmpA & 0xffff) << 48 | ((uint64_t) tmpB & 0xffff) << 32 |
178-
((uint64_t) tmpG & 0xffff) << 16 | ((uint64_t) tmpR & 0xffff);
179-
data64Ptr[k] = color;
174+
auto data64Ptr = reinterpret_cast<uint8_t *>(dstARGB.get());
175+
const float scale = 1.0f / float((1 << bitDepth) - 1);
176+
int dstStride = (int) info.width * 4 * (int) sizeof(uint16_t);
177+
178+
for (int y = 0; y < info.height; ++y) {
179+
180+
auto srcPtr = reinterpret_cast<uint16_t *>(srcData);
181+
auto dstPtr = reinterpret_cast<uint64_t *>(data64Ptr);
182+
183+
for (int x = 0; x < info.width; ++x) {
184+
auto alpha = half_to_float(srcPtr[3]);
185+
tmpR = (uint16_t) (half_to_float(srcPtr[0]) / scale / (alpha != 0 ? alpha : 1));
186+
tmpG = (uint16_t) (half_to_float(srcPtr[1]) / scale / (alpha != 0 ? alpha : 1));
187+
tmpB = (uint16_t) (half_to_float(srcPtr[2]) / scale / (alpha != 0 ? alpha : 1));
188+
tmpA = (uint16_t) (alpha / scale);
189+
uint64_t color =
190+
((uint64_t) tmpA & 0xffff) << 48 | ((uint64_t) tmpB & 0xffff) << 32 |
191+
((uint64_t) tmpG & 0xffff) << 16 | ((uint64_t) tmpR & 0xffff);
192+
dstPtr[0] = color;
193+
194+
srcPtr += 4;
195+
dstPtr += 1;
196+
}
197+
198+
srcData += info.stride;
199+
data64Ptr += dstStride;
180200
}
181-
auto *dataPtr = reinterpret_cast<void *>(dstARGB.get());
201+
auto dataPtr = reinterpret_cast<void *>(dstARGB.get());
182202
auto srcY = (char *) dataPtr;
183203
auto dstY = (char *) imgData;
184204
const auto sourceStride = info.width * 4 * sizeof(uint16_t);
185205
for (int y = 0; y < info.height; ++y) {
186206
memcpy(dstY, srcY, sourceStride);
187-
srcY += sourceStride;
207+
srcY += dstStride;
188208
dstY += stride;
189209
}
190210
dstARGB.reset();
191211
} else {
192212
std::shared_ptr<char> dstARGB(
193213
static_cast<char *>(malloc(info.width * info.height * 4 * sizeof(uint8_t))),
194214
[](char *f) { free(f); });
195-
auto *srcData = static_cast<float16_t *>(addr);
215+
auto *srcData = reinterpret_cast<float16_t *>(sourceData.data());
196216
char tmpR;
197217
char tmpG;
198218
char tmpB;

avif-coder/src/main/cpp/colorspace.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ void convertUseDefinedColorSpace(std::shared_ptr<char> &vector, int stride, int
126126
ptrTransform.get(),
127127
vector.get(),
128128
iccARGB.data(),
129-
width,
129+
width * 4,
130130
height,
131131
stride,
132132
mStride,
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
//
2+
// Created by Radzivon Bartoshyk on 04/09/2023.
3+
//
4+
5+
#include "halfFloats.h"
6+
#include <cstdint>
7+
#include <__threading_support>
8+
9+
uint as_uint(const float x) {
10+
return *(uint *) &x;
11+
}
12+
13+
float as_float(const uint x) {
14+
return *(float *) &x;
15+
}
16+
17+
uint16_t float_to_half(
18+
const float x) { // IEEE-754 16-bit floating-point format (without infinity): 1-5-10, exp-15, +-131008.0, +-6.1035156E-5, +-5.9604645E-8, 3.311 digits
19+
const uint b =
20+
as_uint(x) + 0x00001000; // round-to-nearest-even: add last bit after truncated mantissa
21+
const uint e = (b & 0x7F800000) >> 23; // exponent
22+
const uint m = b &
23+
0x007FFFFF; // mantissa; in line below: 0x007FF000 = 0x00800000-0x00001000 = decimal indicator flag - initial rounding
24+
return (b & 0x80000000) >> 16 | (e > 112) * ((((e - 112) << 10) & 0x7C00) | m >> 13) |
25+
((e < 113) & (e > 101)) * ((((0x007FF000 + m) >> (125 - e)) + 1) >> 1) |
26+
(e > 143) * 0x7FFF; // sign : normalized : denormalized : saturate
27+
}
28+
29+
float half_to_float(
30+
const uint16_t x) { // IEEE-754 16-bit floating-point format (without infinity): 1-5-10, exp-15, +-131008.0, +-6.1035156E-5, +-5.9604645E-8, 3.311 digits
31+
const uint e = (x & 0x7C00) >> 10; // exponent
32+
const uint m = (x & 0x03FF) << 13; // mantissa
33+
const uint v = as_uint((float) m)
34+
>> 23; // evil log2 bit hack to count leading zeros in denormalized format
35+
return as_float((x & 0x8000) << 16 | (e != 0) * ((e + 112) << 23 | m) | ((e == 0) & (m != 0)) *
36+
((v - 37) << 23 |
37+
((m << (150 - v)) &
38+
0x007FE000))); // sign : normalized : denormalized
39+
}
40+
41+
#if HAVE_NEON
42+
43+
#include <arm_neon.h>
44+
45+
void RGBAfloat32_to_float16_NEON(const float *src, int srcStride, uint16_t *dst, int dstStride,
46+
int width, int height) {
47+
48+
auto dstData = reinterpret_cast<uint8_t *>(dst);
49+
auto srcData = reinterpret_cast<const uint8_t *>(src);
50+
51+
for (int y = 0; y < height; ++y) {
52+
int x;
53+
54+
auto srcPixels = reinterpret_cast<const float *>(srcData);
55+
auto dstPixels = reinterpret_cast<uint16_t *>(dstData);
56+
57+
for (x = 0; x < width; x += 2) {
58+
// Load eight float32 values into a Neon register
59+
float32x4_t in0 = vld1q_f32(reinterpret_cast<const float *>(srcPixels));
60+
float32x4_t in1 = vld1q_f32(srcPixels + 4);
61+
62+
// Convert the float32 values to float16 values
63+
float16x4_t out0 = vcvt_f16_f32(in0);
64+
float16x4_t out1 = vcvt_f16_f32(in1);
65+
66+
// Store the float16 values into memory
67+
vst1_u16(dstPixels, vreinterpret_u16_f16(out0));
68+
vst1_u16(dstPixels + 4, vreinterpret_u16_f16(out1));
69+
70+
srcPixels += 8;
71+
dstPixels += 8;
72+
}
73+
74+
for (; x < width; ++x) {
75+
dstPixels[0] = float_to_half(srcPixels[0]);
76+
dstPixels[1] = float_to_half(srcPixels[1]);
77+
dstPixels[2] = float_to_half(srcPixels[2]);
78+
dstPixels[3] = float_to_half(srcPixels[3]);
79+
80+
dstPixels += 4;
81+
srcPixels += 4;
82+
}
83+
84+
dstData += dstStride;
85+
srcData += srcStride;
86+
}
87+
88+
}
89+
90+
#endif
91+
92+
void RGBAfloat32_to_float16(const float *src, uint16_t *dst, int numElements) {
93+
auto srcPixels = src;
94+
auto dstPixels = dst;
95+
for (int i = 0; i < numElements; i += 4) {
96+
dstPixels[0] = float_to_half(srcPixels[0]);
97+
dstPixels[1] = float_to_half(srcPixels[1]);
98+
dstPixels[2] = float_to_half(srcPixels[2]);
99+
dstPixels[3] = float_to_half(srcPixels[3]);
100+
101+
srcPixels += 4;
102+
dstPixels += 4;
103+
}
104+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//
2+
// Created by Radzivon Bartoshyk on 04/09/2023.
3+
//
4+
5+
#ifndef JXLCODER_HALFFLOATS_H
6+
#define JXLCODER_HALFFLOATS_H
7+
8+
#include <cstdint>
9+
10+
#if HAVE_NEON
11+
void RGBAfloat32_to_float16_NEON(const float *src, int srcStride, uint16_t *dst, int dstStride,
12+
int width, int height) ;
13+
#endif
14+
15+
void RGBAfloat32_to_float16(const float * src, uint16_t* dst, int numElements);
16+
float half_to_float(const uint16_t x);
17+
uint16_t float_to_half(const float x);
18+
19+
#endif //JXLCODER_HALFFLOATS_H

0 commit comments

Comments
 (0)