Initial stupid migrate to ImageDecoder
This commit is contained in:
1 parent
7ad67a3dbd
commit
76459c5dd2
14 files changed
+41
-2067
No files matched your search
@@ -44,7 +44,6 @@ class VersionPreference @JvmOverloads constructor(
|
||||
val info = NativeLibInfo().addLib(Native.getlibarchiveVersion())
|
||||
.addLib("lzma(xz-utils)", Native.getliblzmaVersion())
|
||||
.addLib("zlib", Native.getzlibVersion())
|
||||
.addLib("libjpeg-turbo", Native.getlibjpeg_turboVersion())
|
||||
.addLib(Native.getnettleVersion())
|
||||
builder.setMessage(info.getMessage())
|
||||
}
|
||||
|
||||
@@ -28,8 +28,6 @@ public class Native {
|
||||
|
||||
public static native String getliblzmaVersion();
|
||||
|
||||
public static native String getlibjpeg_turboVersion();
|
||||
|
||||
public static native String getzlibVersion();
|
||||
|
||||
public static native String getnettleVersion();
|
||||
|
||||
@@ -16,6 +16,3 @@ cmake_minimum_required(VERSION 3.4.1)
|
||||
|
||||
add_subdirectory(image)
|
||||
add_subdirectory(stream)
|
||||
add_subdirectory(libjpeg-turbo)
|
||||
add_subdirectory(libpng)
|
||||
add_subdirectory(giflib)
|
||||
@@ -19,9 +19,6 @@ set(IMAGE_SOURCES
|
||||
image.c
|
||||
image_utils.c
|
||||
image_plain.c
|
||||
image_jpeg.c
|
||||
image_png.c
|
||||
image_gif.c
|
||||
filter/clahe.c
|
||||
filter/gray.c
|
||||
java_wrapper.c
|
||||
@@ -34,9 +31,6 @@ set(IMAGE_LIBRARIES
|
||||
jnigraphics
|
||||
GLESv2
|
||||
stream
|
||||
jpeg
|
||||
png
|
||||
gif
|
||||
)
|
||||
|
||||
add_library(image STATIC ${IMAGE_SOURCES})
|
||||
|
||||
@@ -18,424 +18,97 @@
|
||||
// Created by Hippo on 12/27/2015.
|
||||
//
|
||||
|
||||
#include <android/imagedecoder.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "image.h"
|
||||
#include "image_plain.h"
|
||||
#include "image_jpeg.h"
|
||||
#include "image_png.h"
|
||||
#include "image_gif.h"
|
||||
#include "filter/clahe.h"
|
||||
#include "filter/gray.h"
|
||||
#include "../log.h"
|
||||
|
||||
static int get_format(JNIEnv* env, InputStream* stream)
|
||||
{
|
||||
unsigned char temp[2];
|
||||
int read = read_input_stream(env, stream, temp, 0, 2);
|
||||
|
||||
if (read == 2) {
|
||||
#ifdef IMAGE_SUPPORT_JPEG
|
||||
if (temp[0] == IMAGE_JPEG_MAGIC_NUMBER_0 && temp[1] == IMAGE_JPEG_MAGIC_NUMBER_1) {
|
||||
return IMAGE_FORMAT_JPEG;
|
||||
}
|
||||
#endif
|
||||
#ifdef IMAGE_SUPPORT_PNG
|
||||
if (temp[0] == IMAGE_PNG_MAGIC_NUMBER_0 && temp[1] == IMAGE_PNG_MAGIC_NUMBER_1) {
|
||||
return IMAGE_FORMAT_PNG;
|
||||
}
|
||||
#endif
|
||||
#ifdef IMAGE_SUPPORT_GIF
|
||||
if (temp[0] == IMAGE_GIF_MAGIC_NUMBER_0 && temp[1] == IMAGE_GIF_MAGIC_NUMBER_1) {
|
||||
return IMAGE_FORMAT_GIF;
|
||||
}
|
||||
#endif
|
||||
LOGE(MSG("Can't recognize the two magic number: %d, %d"), temp[0], temp[1]);
|
||||
} else {
|
||||
LOGE(MSG("Can't read two magic number from stream"));
|
||||
}
|
||||
|
||||
return IMAGE_FORMAT_UNKNOWN;
|
||||
}
|
||||
|
||||
void* decode(JNIEnv* env, InputStream* stream, bool partially, int* format)
|
||||
{
|
||||
unsigned char magic_numbers[2];
|
||||
PatchHeadInputStream* patch_head_input_stream;
|
||||
|
||||
*format = get_format(env, stream);
|
||||
|
||||
switch (*format) {
|
||||
#ifdef IMAGE_SUPPORT_JPEG
|
||||
case IMAGE_FORMAT_JPEG:
|
||||
magic_numbers[0] = IMAGE_JPEG_MAGIC_NUMBER_0;
|
||||
magic_numbers[1] = IMAGE_JPEG_MAGIC_NUMBER_1;
|
||||
break;
|
||||
#endif
|
||||
#ifdef IMAGE_SUPPORT_PNG
|
||||
case IMAGE_FORMAT_PNG:
|
||||
magic_numbers[0] = IMAGE_PNG_MAGIC_NUMBER_0;
|
||||
magic_numbers[1] = IMAGE_PNG_MAGIC_NUMBER_1;
|
||||
break;
|
||||
#endif
|
||||
#ifdef IMAGE_SUPPORT_GIF
|
||||
case IMAGE_FORMAT_GIF:
|
||||
magic_numbers[0] = IMAGE_GIF_MAGIC_NUMBER_0;
|
||||
magic_numbers[1] = IMAGE_GIF_MAGIC_NUMBER_1;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
LOGE(MSG("Can't detect format %d"), *format);
|
||||
destroy_input_stream(env, &stream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
patch_head_input_stream = create_patch_head_input_stream(stream, magic_numbers, 2);
|
||||
if (patch_head_input_stream == NULL){
|
||||
WTF_OM;
|
||||
AImageDecoder *decoder;
|
||||
size_t len = 1024 * 1024 * 4;
|
||||
void* buffer = calloc(len, sizeof(char));
|
||||
len = read_input_stream(env, stream, buffer, 0, len);
|
||||
AImageDecoder_createFromBuffer(buffer, len, &decoder);
|
||||
free(buffer);
|
||||
AImageDecoderHeaderInfo *headerInfo = AImageDecoder_getHeaderInfo(decoder);
|
||||
IMAGE *image = malloc(sizeof(IMAGE));
|
||||
image->height = AImageDecoderHeaderInfo_getHeight(headerInfo);
|
||||
image->width = AImageDecoderHeaderInfo_getWidth(headerInfo);
|
||||
ssize_t bufferLen = 1024 * 1024 * 64;
|
||||
image->buffer = malloc(bufferLen);
|
||||
AImageDecoder_decodeImage(decoder, image->buffer, AImageDecoder_getMinimumStride(decoder), bufferLen);
|
||||
AImageDecoder_delete(decoder);
|
||||
destroy_input_stream(env, &stream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
switch (*format) {
|
||||
#ifdef IMAGE_SUPPORT_JPEG
|
||||
case IMAGE_FORMAT_JPEG:
|
||||
return JPEG_decode(env, patch_head_input_stream, partially);
|
||||
#endif
|
||||
#ifdef IMAGE_SUPPORT_PNG
|
||||
case IMAGE_FORMAT_PNG:
|
||||
return PNG_decode(env, patch_head_input_stream, partially);
|
||||
#endif
|
||||
#ifdef IMAGE_SUPPORT_GIF
|
||||
case IMAGE_FORMAT_GIF:
|
||||
return GIF_decode(env, patch_head_input_stream, partially);
|
||||
#endif
|
||||
default:
|
||||
LOGE(MSG("Can't detect format %d"), *format);
|
||||
close_patch_head_input_stream(env, patch_head_input_stream);
|
||||
destroy_patch_head_input_stream(env, &patch_head_input_stream);
|
||||
return NULL;
|
||||
}
|
||||
*format = IMAGE_FORMAT_JPEG;
|
||||
return image;
|
||||
}
|
||||
|
||||
void* create(unsigned int width, unsigned int height, const void* data)
|
||||
{
|
||||
#ifdef IMAGE_SUPPORT_PLAIN
|
||||
return PLAIN_create(width, height, data);
|
||||
#else
|
||||
return NULL;
|
||||
#endif
|
||||
return PLAIN_create(width, height, data);
|
||||
}
|
||||
|
||||
bool complete(JNIEnv* env, void* image, int format)
|
||||
{
|
||||
switch (format) {
|
||||
#ifdef IMAGE_SUPPORT_PLAIN
|
||||
case IMAGE_FORMAT_PLAIN:
|
||||
return PLAIN_complete((PLAIN*) image);
|
||||
#endif
|
||||
#ifdef IMAGE_SUPPORT_JPEG
|
||||
case IMAGE_FORMAT_JPEG:
|
||||
return JPEG_complete((JPEG*) image);
|
||||
#endif
|
||||
#ifdef IMAGE_SUPPORT_PNG
|
||||
case IMAGE_FORMAT_PNG:
|
||||
return PNG_complete(env, (PNG*) image);
|
||||
#endif
|
||||
#ifdef IMAGE_SUPPORT_GIF
|
||||
case IMAGE_FORMAT_GIF:
|
||||
return GIF_complete(env, (GIF*) image);
|
||||
#endif
|
||||
default:
|
||||
LOGE(MSG("Can't detect format %d"), format);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool is_completed(void* image, int format)
|
||||
{
|
||||
switch (format) {
|
||||
#ifdef IMAGE_SUPPORT_PLAIN
|
||||
case IMAGE_FORMAT_PLAIN:
|
||||
return PLAIN_is_completed((PLAIN*) image);
|
||||
#endif
|
||||
#ifdef IMAGE_SUPPORT_JPEG
|
||||
case IMAGE_FORMAT_JPEG:
|
||||
return JPEG_is_completed((JPEG*) image);
|
||||
#endif
|
||||
#ifdef IMAGE_SUPPORT_PNG
|
||||
case IMAGE_FORMAT_PNG:
|
||||
return PNG_is_completed((PNG*) image);
|
||||
#endif
|
||||
#ifdef IMAGE_SUPPORT_GIF
|
||||
case IMAGE_FORMAT_GIF:
|
||||
return GIF_is_completed((GIF*) image);
|
||||
#endif
|
||||
default:
|
||||
LOGE(MSG("Can't detect format %d"), format);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int get_width(void* image, int format)
|
||||
{
|
||||
switch (format) {
|
||||
#ifdef IMAGE_SUPPORT_PLAIN
|
||||
case IMAGE_FORMAT_PLAIN:
|
||||
return PLAIN_get_width((PLAIN*) image);
|
||||
#endif
|
||||
#ifdef IMAGE_SUPPORT_JPEG
|
||||
case IMAGE_FORMAT_JPEG:
|
||||
return JPEG_get_width((JPEG*) image);
|
||||
#endif
|
||||
#ifdef IMAGE_SUPPORT_PNG
|
||||
case IMAGE_FORMAT_PNG:
|
||||
return PNG_get_width((PNG*) image);
|
||||
#endif
|
||||
#ifdef IMAGE_SUPPORT_GIF
|
||||
case IMAGE_FORMAT_GIF:
|
||||
return GIF_get_width((GIF*) image);
|
||||
#endif
|
||||
default:
|
||||
LOGE(MSG("Can't detect format %d"), format);
|
||||
return -1;
|
||||
}
|
||||
return ((IMAGE*)image)->width;
|
||||
}
|
||||
|
||||
int get_height(void* image, int format)
|
||||
{
|
||||
switch (format) {
|
||||
#ifdef IMAGE_SUPPORT_PLAIN
|
||||
case IMAGE_FORMAT_PLAIN:
|
||||
return PLAIN_get_height((PLAIN*) image);
|
||||
#endif
|
||||
#ifdef IMAGE_SUPPORT_JPEG
|
||||
case IMAGE_FORMAT_JPEG:
|
||||
return JPEG_get_height((JPEG*) image);
|
||||
#endif
|
||||
#ifdef IMAGE_SUPPORT_PNG
|
||||
case IMAGE_FORMAT_PNG:
|
||||
return PNG_get_height((PNG*) image);
|
||||
#endif
|
||||
#ifdef IMAGE_SUPPORT_GIF
|
||||
case IMAGE_FORMAT_GIF:
|
||||
return GIF_get_height((GIF*) image);
|
||||
#endif
|
||||
default:
|
||||
LOGE(MSG("Can't detect format %d"), format);
|
||||
return -1;
|
||||
}
|
||||
return ((IMAGE*)image)->height;
|
||||
}
|
||||
|
||||
int get_byte_count(void* image, int format)
|
||||
{
|
||||
switch (format) {
|
||||
#ifdef IMAGE_SUPPORT_PLAIN
|
||||
case IMAGE_FORMAT_PLAIN:
|
||||
return PLAIN_get_byte_count((PLAIN*) image);
|
||||
#endif
|
||||
#ifdef IMAGE_SUPPORT_JPEG
|
||||
case IMAGE_FORMAT_JPEG:
|
||||
return JPEG_get_byte_count((JPEG*) image);
|
||||
#endif
|
||||
#ifdef IMAGE_SUPPORT_PNG
|
||||
case IMAGE_FORMAT_PNG:
|
||||
return PNG_get_byte_count((PNG*) image);
|
||||
#endif
|
||||
#ifdef IMAGE_SUPPORT_GIF
|
||||
case IMAGE_FORMAT_GIF:
|
||||
return GIF_get_byte_count((GIF*) image);
|
||||
#endif
|
||||
default:
|
||||
LOGE(MSG("Can't detect format %d"), format);
|
||||
return -1;
|
||||
}
|
||||
return ((IMAGE*)image)->width * ((IMAGE*)image)->height * 4;
|
||||
}
|
||||
|
||||
void render(void* image, int format, int src_x, int src_y,
|
||||
void* dst, int dst_w, int dst_h, int dst_x, int dst_y,
|
||||
int width, int height, bool fill_blank, int default_color)
|
||||
{
|
||||
switch (format) {
|
||||
#ifdef IMAGE_SUPPORT_PLAIN
|
||||
case IMAGE_FORMAT_PLAIN:
|
||||
PLAIN_render((PLAIN*) image, src_x, src_y,
|
||||
dst, dst_w, dst_h, dst_x, dst_y,
|
||||
width, height, fill_blank, default_color);
|
||||
break;
|
||||
#endif
|
||||
#ifdef IMAGE_SUPPORT_JPEG
|
||||
case IMAGE_FORMAT_JPEG:
|
||||
JPEG_render((JPEG*) image, src_x, src_y,
|
||||
dst, dst_w, dst_h, dst_x, dst_y,
|
||||
width, height, fill_blank, default_color);
|
||||
break;
|
||||
#endif
|
||||
#ifdef IMAGE_SUPPORT_PNG
|
||||
case IMAGE_FORMAT_PNG:
|
||||
PNG_render((PNG*) image, src_x, src_y,
|
||||
dst, dst_w, dst_h, dst_x, dst_y,
|
||||
width, height, fill_blank, default_color);
|
||||
break;
|
||||
#endif
|
||||
#ifdef IMAGE_SUPPORT_GIF
|
||||
case IMAGE_FORMAT_GIF:
|
||||
GIF_render((GIF*) image, src_x, src_y,
|
||||
dst, dst_w, dst_h, dst_x, dst_y,
|
||||
width, height, fill_blank, default_color);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
LOGE(MSG("Can't detect format %d"), format);
|
||||
break;
|
||||
}
|
||||
PLAIN_render((IMAGE *) image, src_x, src_y, dst, dst_w, dst_h, dst_x, dst_y, width, height, fill_blank, default_color);
|
||||
}
|
||||
|
||||
void advance(void* image, int format)
|
||||
{
|
||||
switch (format) {
|
||||
#ifdef IMAGE_SUPPORT_PLAIN
|
||||
case IMAGE_FORMAT_PLAIN:
|
||||
PLAIN_advance((PLAIN*) image);
|
||||
break;
|
||||
#endif
|
||||
#ifdef IMAGE_SUPPORT_JPEG
|
||||
case IMAGE_FORMAT_JPEG:
|
||||
JPEG_advance((JPEG*) image);
|
||||
break;
|
||||
#endif
|
||||
#ifdef IMAGE_SUPPORT_PNG
|
||||
case IMAGE_FORMAT_PNG:
|
||||
PNG_advance((PNG*) image);
|
||||
break;
|
||||
#endif
|
||||
#ifdef IMAGE_SUPPORT_GIF
|
||||
case IMAGE_FORMAT_GIF:
|
||||
GIF_advance((GIF*) image);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
LOGE(MSG("Can't detect format %d"), format);
|
||||
}
|
||||
}
|
||||
|
||||
int get_delay(void* image, int format)
|
||||
{
|
||||
switch (format) {
|
||||
#ifdef IMAGE_SUPPORT_PLAIN
|
||||
case IMAGE_FORMAT_PLAIN:
|
||||
return PLAIN_get_delay((PLAIN*) image);
|
||||
#endif
|
||||
#ifdef IMAGE_SUPPORT_JPEG
|
||||
case IMAGE_FORMAT_JPEG:
|
||||
return JPEG_get_delay((JPEG*) image);
|
||||
#endif
|
||||
#ifdef IMAGE_SUPPORT_PNG
|
||||
case IMAGE_FORMAT_PNG:
|
||||
return PNG_get_delay((PNG*) image);
|
||||
#endif
|
||||
#ifdef IMAGE_SUPPORT_GIF
|
||||
case IMAGE_FORMAT_GIF:
|
||||
return GIF_get_delay((GIF*) image);
|
||||
#endif
|
||||
default:
|
||||
LOGE(MSG("Can't detect format %d"), format);
|
||||
return false;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int get_frame_count(void* image, int format)
|
||||
{
|
||||
switch (format) {
|
||||
#ifdef IMAGE_SUPPORT_PLAIN
|
||||
case IMAGE_FORMAT_PLAIN:
|
||||
return PLAIN_get_frame_count((PLAIN*) image);
|
||||
#endif
|
||||
#ifdef IMAGE_SUPPORT_JPEG
|
||||
case IMAGE_FORMAT_JPEG:
|
||||
return JPEG_get_frame_count((JPEG*) image);
|
||||
#endif
|
||||
#ifdef IMAGE_SUPPORT_PNG
|
||||
case IMAGE_FORMAT_PNG:
|
||||
return PNG_get_frame_count((PNG*) image);
|
||||
#endif
|
||||
#ifdef IMAGE_SUPPORT_GIF
|
||||
case IMAGE_FORMAT_GIF:
|
||||
return GIF_get_frame_count((GIF*) image);
|
||||
#endif
|
||||
default:
|
||||
LOGE(MSG("Can't detect format %d"), format);
|
||||
return false;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool is_opaque(void* image, int format)
|
||||
{
|
||||
switch (format) {
|
||||
#ifdef IMAGE_SUPPORT_PLAIN
|
||||
case IMAGE_FORMAT_PLAIN:
|
||||
return PLAIN_is_opaque((PLAIN*) image);
|
||||
#endif
|
||||
#ifdef IMAGE_SUPPORT_JPEG
|
||||
case IMAGE_FORMAT_JPEG:
|
||||
return JPEG_is_opaque((JPEG*) image);
|
||||
#endif
|
||||
#ifdef IMAGE_SUPPORT_PNG
|
||||
case IMAGE_FORMAT_PNG:
|
||||
return PNG_is_opaque((PNG*) image);
|
||||
#endif
|
||||
#ifdef IMAGE_SUPPORT_GIF
|
||||
case IMAGE_FORMAT_GIF:
|
||||
return GIF_is_opaque((GIF*) image);
|
||||
#endif
|
||||
default:
|
||||
LOGE(MSG("Can't detect format %d"), format);
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void get_image_data(void* image, int format, void** pixel, int* width, int* height) {
|
||||
switch (format) {
|
||||
#ifdef IMAGE_SUPPORT_PLAIN
|
||||
case IMAGE_FORMAT_PLAIN: {
|
||||
PLAIN* plain = (PLAIN*) image;
|
||||
*pixel = PLAIN_get_pixels(plain);
|
||||
*width = PLAIN_get_width(plain);
|
||||
*height = PLAIN_get_height(plain);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
#ifdef IMAGE_SUPPORT_JPEG
|
||||
case IMAGE_FORMAT_JPEG: {
|
||||
JPEG* jpeg = (JPEG*) image;
|
||||
*pixel = JPEG_get_pixels(jpeg);
|
||||
*width = JPEG_get_width(jpeg);
|
||||
*height = JPEG_get_height(jpeg);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
#ifdef IMAGE_SUPPORT_PNG
|
||||
case IMAGE_FORMAT_PNG: {
|
||||
PNG* png = (PNG*) image;
|
||||
*pixel = PNG_get_pixels(png);
|
||||
*width = PNG_get_width(png);
|
||||
*height = PNG_get_height(png);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
#ifdef IMAGE_SUPPORT_GIF
|
||||
case IMAGE_FORMAT_GIF: {
|
||||
GIF* gif = (GIF*) image;
|
||||
*pixel = GIF_get_pixels(gif);
|
||||
*width = GIF_get_width(gif);
|
||||
*height = GIF_get_height(gif);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
*pixel = NULL;
|
||||
*width = 0;
|
||||
*height = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool is_gray(void* image, int format, int error)
|
||||
@@ -470,30 +143,7 @@ void clahe(void* image, int format, bool to_gray)
|
||||
|
||||
void recycle(JNIEnv *env, void* image, int format)
|
||||
{
|
||||
switch (format) {
|
||||
#ifdef IMAGE_SUPPORT_PLAIN
|
||||
case IMAGE_FORMAT_PLAIN:
|
||||
PLAIN_recycle((PLAIN*) image);
|
||||
break;
|
||||
#endif
|
||||
#ifdef IMAGE_SUPPORT_JPEG
|
||||
case IMAGE_FORMAT_JPEG:
|
||||
JPEG_recycle((JPEG*) image);
|
||||
break;
|
||||
#endif
|
||||
#ifdef IMAGE_SUPPORT_PNG
|
||||
case IMAGE_FORMAT_PNG:
|
||||
PNG_recycle(env, (PNG*) image);
|
||||
break;
|
||||
#endif
|
||||
#ifdef IMAGE_SUPPORT_GIF
|
||||
case IMAGE_FORMAT_GIF:
|
||||
GIF_recycle(env, (GIF*) image);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
LOGE(MSG("Can't detect format %d"), format);
|
||||
}
|
||||
PLAIN_recycle((PLAIN*) image);
|
||||
}
|
||||
|
||||
int get_supported_formats(int *formats)
|
||||
@@ -513,20 +163,5 @@ int get_supported_formats(int *formats)
|
||||
|
||||
const char *get_decoder_description(int format)
|
||||
{
|
||||
switch (format) {
|
||||
#ifdef IMAGE_SUPPORT_JPEG
|
||||
case IMAGE_FORMAT_JPEG:
|
||||
return IMAGE_JPEG_DECODER_DESCRIPTION;
|
||||
#endif
|
||||
#ifdef IMAGE_SUPPORT_PNG
|
||||
case IMAGE_FORMAT_PNG:
|
||||
return IMAGE_PNG_DECODER_DESCRIPTION;
|
||||
#endif
|
||||
#ifdef IMAGE_SUPPORT_GIF
|
||||
case IMAGE_FORMAT_GIF:
|
||||
return IMAGE_GIF_DECODER_DESCRIPTION;
|
||||
#endif
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
@@ -42,6 +42,13 @@
|
||||
|
||||
#define IMAGE_MAX_SUPPORTED_FORMAT_COUNT 3
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned int width;
|
||||
unsigned int height;
|
||||
void* buffer;
|
||||
} IMAGE;
|
||||
|
||||
void* decode(JNIEnv* env, InputStream* stream, bool partially, int* format);
|
||||
void* create(unsigned int width, unsigned int height, const void* data);
|
||||
bool complete(JNIEnv* env, void* image, int format);
|
||||
|
||||
@@ -1,558 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015 Hippo Seven
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
//
|
||||
// Created by Hippo on 12/27/2015.
|
||||
//
|
||||
|
||||
#include "config.h"
|
||||
#ifdef IMAGE_SUPPORT_GIF
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "image_gif.h"
|
||||
#include "image_utils.h"
|
||||
#include "java_wrapper.h"
|
||||
#include "../log.h"
|
||||
#include "../utils.h"
|
||||
|
||||
static int error_code = 0;
|
||||
|
||||
typedef struct {
|
||||
unsigned char red;
|
||||
unsigned char green;
|
||||
unsigned char blue;
|
||||
unsigned char alpha;
|
||||
} RGBA;
|
||||
|
||||
static int custom_read_fun(GifFileType* gif, GifByteType* bytes, int size) {
|
||||
bool attach;
|
||||
int read;
|
||||
PatchHeadInputStream* patch_head_input_stream = gif->UserData;
|
||||
JNIEnv *env = obtain_env(&attach);
|
||||
|
||||
if (env == NULL) {
|
||||
LOGE(MSG("Can't get JNIEnv"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
read = read_patch_head_input_stream(env, patch_head_input_stream, bytes, 0, (size_t) size);
|
||||
|
||||
if (attach) {
|
||||
release_env();
|
||||
}
|
||||
|
||||
return read;
|
||||
}
|
||||
|
||||
static void generate_prepare(GIF_FRAME_INFO* frame_info_array, int count)
|
||||
{
|
||||
int disposal = 0xff;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < count; i++){
|
||||
GIF_FRAME_INFO* frame_info = frame_info_array + i;
|
||||
|
||||
switch (disposal) {
|
||||
case DISPOSAL_UNSPECIFIED:
|
||||
case DISPOSE_DO_NOT:
|
||||
frame_info->prepare = IMAGE_GIF_PREPARE_NONE;
|
||||
break;
|
||||
case DISPOSE_BACKGROUND:
|
||||
frame_info->prepare = IMAGE_GIF_PREPARE_BACKGROUND;
|
||||
break;
|
||||
case DISPOSE_PREVIOUS:
|
||||
frame_info->prepare = IMAGE_GIF_PREPARE_USE_BACKUP;
|
||||
break;
|
||||
default:
|
||||
frame_info->prepare = IMAGE_GIF_PREPARE_BACKGROUND;
|
||||
break;
|
||||
}
|
||||
|
||||
disposal = frame_info->disposal;
|
||||
}
|
||||
}
|
||||
|
||||
static void read_gcb(GifFileType* gif_file, int index, GIF_FRAME_INFO* frame_info)
|
||||
{
|
||||
GraphicsControlBlock gcb;
|
||||
if (DGifSavedExtensionToGCB(gif_file, index, &gcb) == GIF_OK) {
|
||||
frame_info->tran = gcb.TransparentColor;
|
||||
frame_info->delay = gcb.DelayTime * 10;
|
||||
frame_info->disposal = gcb.DisposalMode;
|
||||
frame_info->prepare = IMAGE_GIF_PREPARE_UNKNOWN;
|
||||
} else {
|
||||
frame_info->tran = -1;
|
||||
frame_info->delay = 0;
|
||||
frame_info->disposal = DISPOSE_DO_NOT;
|
||||
frame_info->prepare = IMAGE_GIF_PREPARE_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
static void fix_gif_file(GifFileType* gif_file) {
|
||||
if (gif_file->ImageCount == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
SavedImage last_image = gif_file->SavedImages[gif_file->ImageCount - 1];
|
||||
// If RasterBits is NULL, discard this frame
|
||||
if (last_image.RasterBits == NULL) {
|
||||
gif_file->ImageCount--;
|
||||
// Free last image
|
||||
if (last_image.ImageDesc.ColorMap != NULL) {
|
||||
GifFreeMapObject(last_image.ImageDesc.ColorMap);
|
||||
last_image.ImageDesc.ColorMap = NULL;
|
||||
}
|
||||
GifFreeExtensions(&last_image.ExtensionBlockCount, &last_image.ExtensionBlocks);
|
||||
}
|
||||
}
|
||||
|
||||
void* GIF_decode(JNIEnv* env, PatchHeadInputStream* patch_head_input_stream, bool partially)
|
||||
{
|
||||
GIF* gif = NULL;
|
||||
GifFileType* gif_file = NULL;
|
||||
void* buffer = NULL;
|
||||
void*shown_buffer = NULL;
|
||||
GIF_FRAME_INFO* frame_info_array = NULL;
|
||||
int i;
|
||||
|
||||
gif = (GIF*) malloc(sizeof(GIF));
|
||||
if (gif == NULL) {
|
||||
WTF_OM;
|
||||
close_patch_head_input_stream(env, patch_head_input_stream);
|
||||
destroy_patch_head_input_stream(env, &patch_head_input_stream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Open
|
||||
gif_file = DGifOpen(patch_head_input_stream, &custom_read_fun, &error_code);
|
||||
if (gif_file == NULL) {
|
||||
WTF_OM;
|
||||
free(gif);
|
||||
close_patch_head_input_stream(env, patch_head_input_stream);
|
||||
destroy_patch_head_input_stream(env, &patch_head_input_stream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Buffer
|
||||
buffer = malloc(gif_file->SWidth * gif_file->SHeight * sizeof(RGBA));
|
||||
shown_buffer = malloc(gif_file->SWidth * gif_file->SHeight * sizeof(RGBA));
|
||||
if (buffer == NULL || shown_buffer == NULL) {
|
||||
WTF_OM;
|
||||
free(buffer);
|
||||
free(shown_buffer);
|
||||
DGifCloseFile(gif_file, &error_code);
|
||||
free(gif);
|
||||
close_patch_head_input_stream(env, patch_head_input_stream);
|
||||
destroy_patch_head_input_stream(env, &patch_head_input_stream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (partially) {
|
||||
// Glance
|
||||
if (DGifGlance(gif_file) != GIF_OK) {
|
||||
LOGE(MSG("GIF error code %d"), error_code);
|
||||
DGifCloseFile(gif_file, &error_code);
|
||||
free(buffer);
|
||||
free(gif);
|
||||
close_patch_head_input_stream(env, patch_head_input_stream);
|
||||
destroy_patch_head_input_stream(env, &patch_head_input_stream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Frame info
|
||||
frame_info_array = (GIF_FRAME_INFO*) malloc(sizeof(GIF_FRAME_INFO));
|
||||
if (frame_info_array == NULL) {
|
||||
WTF_OM;
|
||||
DGifCloseFile(gif_file, &error_code);
|
||||
free(buffer);
|
||||
free(gif);
|
||||
close_patch_head_input_stream(env, patch_head_input_stream);
|
||||
destroy_patch_head_input_stream(env, &patch_head_input_stream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Read gcb
|
||||
read_gcb(gif_file, 0, frame_info_array);
|
||||
|
||||
gif->partially = true;
|
||||
gif->patch_head_input_stream = patch_head_input_stream;
|
||||
} else {
|
||||
// Slurp
|
||||
if (DGifSlurp(gif_file) == GIF_ERROR) {
|
||||
fix_gif_file(gif_file);
|
||||
}
|
||||
if (gif_file->ImageCount <= 0) {
|
||||
LOGE(MSG("No frame"));
|
||||
DGifCloseFile(gif_file, &error_code);
|
||||
free(buffer);
|
||||
free(gif);
|
||||
close_patch_head_input_stream(env, patch_head_input_stream);
|
||||
destroy_patch_head_input_stream(env, &patch_head_input_stream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Frame info
|
||||
frame_info_array = (GIF_FRAME_INFO*) malloc(gif_file->ImageCount * sizeof(GIF_FRAME_INFO));
|
||||
if (frame_info_array == NULL) {
|
||||
WTF_OM;
|
||||
DGifCloseFile(gif_file, &error_code);
|
||||
free(buffer);
|
||||
free(gif);
|
||||
close_patch_head_input_stream(env, patch_head_input_stream);
|
||||
destroy_patch_head_input_stream(env, &patch_head_input_stream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Read gcb
|
||||
for (i = 0; i < gif_file->ImageCount; i++) {
|
||||
read_gcb(gif_file, i, frame_info_array + i);
|
||||
}
|
||||
|
||||
// Generate prepare
|
||||
generate_prepare(frame_info_array, gif_file->ImageCount);
|
||||
|
||||
// Close input stream
|
||||
close_patch_head_input_stream(env, patch_head_input_stream);
|
||||
destroy_patch_head_input_stream(env, &patch_head_input_stream);
|
||||
|
||||
gif->partially = false;
|
||||
gif->patch_head_input_stream = NULL;
|
||||
}
|
||||
|
||||
gif->gif_file = gif_file;
|
||||
gif->frame_info_array = frame_info_array;
|
||||
gif->buffer = buffer;
|
||||
gif->buffer_index = -1;
|
||||
gif->backup = NULL;
|
||||
gif->shown_buffer = shown_buffer;
|
||||
|
||||
GIF_advance(gif);
|
||||
|
||||
return gif;
|
||||
}
|
||||
|
||||
bool GIF_complete(JNIEnv* env, GIF* gif)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!gif->partially) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (gif->gif_file == NULL || gif->patch_head_input_stream == NULL) {
|
||||
LOGE(MSG("Some stuff is NULL"));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (DGifSlurp(gif->gif_file) == GIF_ERROR) {
|
||||
fix_gif_file(gif->gif_file);
|
||||
}
|
||||
|
||||
// Close input stream
|
||||
close_patch_head_input_stream(env, gif->patch_head_input_stream);
|
||||
destroy_patch_head_input_stream(env, &gif->patch_head_input_stream);
|
||||
gif->patch_head_input_stream = NULL;
|
||||
|
||||
gif->partially = false;
|
||||
|
||||
gif->frame_info_array = (GIF_FRAME_INFO*) realloc(gif->frame_info_array,
|
||||
gif->gif_file->ImageCount * sizeof(GIF_FRAME_INFO));
|
||||
if (gif->frame_info_array == NULL) {
|
||||
WTF_OM;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read gcb
|
||||
for (i = 0; i < gif->gif_file->ImageCount; i++) {
|
||||
read_gcb(gif->gif_file, i, gif->frame_info_array + i);
|
||||
}
|
||||
|
||||
// Generate prepare
|
||||
generate_prepare(gif->frame_info_array, gif->gif_file->ImageCount);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GIF_is_completed(GIF* gif)
|
||||
{
|
||||
return !gif->partially;
|
||||
}
|
||||
|
||||
void* GIF_get_pixels(GIF* gif)
|
||||
{
|
||||
if (!gif->partially && GIF_get_frame_count(gif) == 1) {
|
||||
return gif->buffer;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int GIF_get_width(GIF* gif)
|
||||
{
|
||||
return gif->gif_file->SWidth;
|
||||
}
|
||||
|
||||
int GIF_get_height(GIF* gif)
|
||||
{
|
||||
return gif->gif_file->SHeight;
|
||||
}
|
||||
|
||||
int GIF_get_byte_count(GIF* gif)
|
||||
{
|
||||
int i;
|
||||
SavedImage saved_image;
|
||||
int size = 0;
|
||||
// Add buffer size
|
||||
if (gif->buffer != NULL) {
|
||||
size += gif->gif_file->SWidth * gif->gif_file->SHeight * 4;
|
||||
}
|
||||
// Add backup size
|
||||
if (gif->backup != NULL) {
|
||||
size += gif->gif_file->SWidth * gif->gif_file->SHeight * 4;
|
||||
}
|
||||
// Add shown buffer size
|
||||
if (gif->shown_buffer != NULL) {
|
||||
size += gif->gif_file->SWidth * gif->gif_file->SHeight * 4;
|
||||
}
|
||||
// Add frames size
|
||||
if (gif->gif_file->SavedImages != NULL) {
|
||||
for (i = 0; i < gif->gif_file->ImageCount; i++) {
|
||||
saved_image = gif->gif_file->SavedImages[i];
|
||||
if (saved_image.RasterBits != NULL) {
|
||||
size += saved_image.ImageDesc.Width * saved_image.ImageDesc.Height * 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
void GIF_render(GIF* gif, int src_x, int src_y,
|
||||
void* dst, int dst_w, int dst_h, int dst_x, int dst_y,
|
||||
int width, int height, bool fill_blank, int default_color)
|
||||
{
|
||||
copy_pixels(gif->shown_buffer, gif->gif_file->SWidth, gif->gif_file->SHeight, src_x, src_y,
|
||||
dst, dst_w, dst_h, dst_x, dst_y,
|
||||
width, height, fill_blank, default_color);
|
||||
}
|
||||
|
||||
static void backup(GIF* gif)
|
||||
{
|
||||
if (gif->backup == NULL) {
|
||||
gif->backup = (unsigned char*) malloc((size_t) (gif->gif_file->SWidth * gif->gif_file->SHeight * 4));
|
||||
if (gif->backup == NULL) {
|
||||
WTF_OM;
|
||||
return;
|
||||
}
|
||||
}
|
||||
memcpy(gif->backup, gif->buffer, (size_t) (gif->gif_file->SWidth * gif->gif_file->SHeight * 4));
|
||||
}
|
||||
|
||||
static void restore(GIF* gif)
|
||||
{
|
||||
if (gif->backup == NULL) {
|
||||
LOGE(MSG("Backup is NULL"));
|
||||
} else {
|
||||
memcpy(gif->buffer, gif->backup, (size_t) (gif->gif_file->SWidth * gif->gif_file->SHeight * 4));
|
||||
}
|
||||
}
|
||||
|
||||
static void switch_buffer_backup(GIF* gif)
|
||||
{
|
||||
unsigned char* temp;
|
||||
|
||||
if (gif->backup == NULL) {
|
||||
backup(gif);
|
||||
} else {
|
||||
temp = gif->buffer;
|
||||
gif->buffer = gif->backup;
|
||||
gif->backup = temp;
|
||||
}
|
||||
}
|
||||
|
||||
static void clear(RGBA* pixels, int num, RGBA color) {
|
||||
RGBA* ptr = pixels;
|
||||
int i;
|
||||
for (i = 0; i < num; i++) {
|
||||
*(ptr++) = color;
|
||||
}
|
||||
}
|
||||
|
||||
static bool get_color_from_table(const ColorMapObject* cmap, int index,
|
||||
RGBA* color) {
|
||||
if (cmap == NULL || index < 0 || index >= cmap->ColorCount) {
|
||||
return false;
|
||||
} else {
|
||||
GifColorType gct = cmap->Colors[index];
|
||||
color->red = gct.Red;
|
||||
color->green = gct.Green;
|
||||
color->blue = gct.Blue;
|
||||
color->alpha = 0xff;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static void clear_bg(GifFileType* gif_file, void* pixels)
|
||||
{
|
||||
RGBA color;
|
||||
if (!get_color_from_table(gif_file->SColorMap, gif_file->SBackGroundColor, &color)) {
|
||||
color.red = 0x00;
|
||||
color.green = 0x00;
|
||||
color.blue = 0x00;
|
||||
color.alpha = 0x00;
|
||||
}
|
||||
clear(pixels, gif_file->SWidth * gif_file->SHeight, color);
|
||||
}
|
||||
|
||||
static void copy_line(GifByteType* src, RGBA* dst,
|
||||
const ColorMapObject* cmap, int tran, int len)
|
||||
{
|
||||
for (; len > 0; len--, src++, dst++) {
|
||||
int index = *src;
|
||||
if (tran == -1 || index != tran) {
|
||||
get_color_from_table(cmap, index, dst);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void blend(GifFileType* gif_file, int index, void* pixels, int tran)
|
||||
{
|
||||
int width = gif_file->SWidth;
|
||||
int height = gif_file->SHeight;
|
||||
SavedImage cur = gif_file->SavedImages[index];
|
||||
GifImageDesc desc = cur.ImageDesc;
|
||||
int copy_width = MIN(width - desc.Left, desc.Width);
|
||||
int copy_height = MIN(height - desc.Top, desc.Height);
|
||||
ColorMapObject *cmap = desc.ColorMap;
|
||||
GifByteType* src = cur.RasterBits;
|
||||
RGBA* dst = pixels;
|
||||
GifByteType* src_ptr;
|
||||
RGBA* dst_ptr;
|
||||
size_t len;
|
||||
int i;
|
||||
|
||||
if (cmap == NULL) {
|
||||
cmap = gif_file->SColorMap;
|
||||
}
|
||||
if (cmap != NULL) {
|
||||
for (i = 0; i < copy_height; i++) {
|
||||
src_ptr = src + (i * desc.Width);
|
||||
dst_ptr = dst + ((desc.Top + i) * width + desc.Left);
|
||||
len = (size_t) copy_width;
|
||||
copy_line(src_ptr, dst_ptr, cmap, tran, len);
|
||||
}
|
||||
} else {
|
||||
LOGW(MSG("Can't find color map"));
|
||||
}
|
||||
}
|
||||
|
||||
void GIF_advance(GIF* gif)
|
||||
{
|
||||
int index;
|
||||
GIF_FRAME_INFO frame_info;
|
||||
|
||||
index = (gif->buffer_index + 1) % gif->gif_file->ImageCount;
|
||||
if (index != 0 && gif->partially) {
|
||||
LOGE(MSG("The png is only decoded partially. Only the first frame can be shown."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (gif->frame_info_array == NULL) {
|
||||
frame_info.tran = -1;
|
||||
frame_info.disposal = DISPOSE_DO_NOT;
|
||||
frame_info.delay = 0;
|
||||
frame_info.prepare = IMAGE_GIF_PREPARE_BACKGROUND;
|
||||
} else {
|
||||
memcpy(&frame_info, gif->frame_info_array + index, sizeof(GIF_FRAME_INFO));
|
||||
}
|
||||
|
||||
if (frame_info.disposal == DISPOSE_PREVIOUS && frame_info.prepare == IMAGE_GIF_PREPARE_USE_BACKUP) {
|
||||
switch_buffer_backup(gif);
|
||||
} else {
|
||||
// Backup
|
||||
if (frame_info.disposal == DISPOSE_PREVIOUS) {
|
||||
backup(gif);
|
||||
}
|
||||
|
||||
// Prepare
|
||||
switch (frame_info.prepare) {
|
||||
case IMAGE_GIF_PREPARE_NONE:
|
||||
// Do nothing
|
||||
break;
|
||||
default:
|
||||
case IMAGE_GIF_PREPARE_BACKGROUND:
|
||||
// Set bg
|
||||
clear_bg(gif->gif_file, gif->buffer);
|
||||
break;
|
||||
case IMAGE_GIF_PREPARE_USE_BACKUP:
|
||||
restore(gif);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
blend(gif->gif_file, index, gif->buffer, frame_info.tran);
|
||||
|
||||
// Copy to shown buffer
|
||||
memcpy(gif->shown_buffer, gif->buffer, (size_t) (gif->gif_file->SWidth * gif->gif_file->SHeight * 4));
|
||||
|
||||
gif->buffer_index = index;
|
||||
}
|
||||
|
||||
int GIF_get_delay(GIF* gif)
|
||||
{
|
||||
if (gif->frame_info_array == NULL) {
|
||||
return 0;
|
||||
} else {
|
||||
return gif->frame_info_array[gif->buffer_index].delay;
|
||||
}
|
||||
}
|
||||
|
||||
int GIF_get_frame_count(GIF* gif)
|
||||
{
|
||||
return gif->gif_file->ImageCount;
|
||||
}
|
||||
|
||||
bool GIF_is_opaque(GIF* gif)
|
||||
{
|
||||
return gif->frame_info_array->tran < 0;
|
||||
}
|
||||
|
||||
void GIF_recycle(JNIEnv* env, GIF* gif)
|
||||
{
|
||||
DGifCloseFile(gif->gif_file, &error_code);
|
||||
gif->gif_file = NULL;
|
||||
|
||||
free(gif->frame_info_array);
|
||||
gif->frame_info_array = NULL;
|
||||
|
||||
free(gif->buffer);
|
||||
gif->buffer = NULL;
|
||||
|
||||
free(gif->backup);
|
||||
gif->backup = NULL;
|
||||
|
||||
free(gif->shown_buffer);
|
||||
gif->shown_buffer = NULL;
|
||||
|
||||
if (gif->patch_head_input_stream != NULL) {
|
||||
close_patch_head_input_stream(env, gif->patch_head_input_stream);
|
||||
destroy_patch_head_input_stream(env, &gif->patch_head_input_stream);
|
||||
gif->patch_head_input_stream = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // IMAGE_SUPPORT_GIF
|
||||
@@ -1,84 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015 Hippo Seven
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
//
|
||||
// Created by Hippo on 12/27/2015.
|
||||
//
|
||||
|
||||
#ifndef IMAGE_IMAGE_GIF_H
|
||||
#define IMAGE_IMAGE_GIF_H
|
||||
|
||||
#include "config.h"
|
||||
#ifdef IMAGE_SUPPORT_GIF
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "patch_head_input_stream.h"
|
||||
#include "gif_lib.h"
|
||||
#include "../utils.h"
|
||||
|
||||
#define IMAGE_GIF_DECODER_DESCRIPTION ("giflib " MAKESTRING(STRINGIZE, GIFLIB_MAJOR) "." \
|
||||
MAKESTRING(STRINGIZE, GIFLIB_MINOR) "." \
|
||||
MAKESTRING(STRINGIZE, GIFLIB_RELEASE))
|
||||
|
||||
#define IMAGE_GIF_MAGIC_NUMBER_0 0x47
|
||||
#define IMAGE_GIF_MAGIC_NUMBER_1 0x49
|
||||
|
||||
#define IMAGE_GIF_PREPARE_UNKNOWN 0x00
|
||||
#define IMAGE_GIF_PREPARE_NONE 0x01
|
||||
#define IMAGE_GIF_PREPARE_BACKGROUND 0x02
|
||||
#define IMAGE_GIF_PREPARE_USE_BACKUP 0x03
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int tran;
|
||||
int disposal;
|
||||
int delay;
|
||||
int prepare;
|
||||
} GIF_FRAME_INFO;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GifFileType* gif_file;
|
||||
GIF_FRAME_INFO* frame_info_array;
|
||||
void* buffer;
|
||||
int buffer_index;
|
||||
void* backup;
|
||||
bool partially;
|
||||
// Use extra buffer to avoid blink
|
||||
void* shown_buffer;
|
||||
PatchHeadInputStream* patch_head_input_stream;
|
||||
} GIF;
|
||||
|
||||
void* GIF_decode(JNIEnv* env, PatchHeadInputStream* patch_head_input_stream, bool partially);
|
||||
bool GIF_complete(JNIEnv* env, GIF* gif);
|
||||
bool GIF_is_completed(GIF* gif);
|
||||
void* GIF_get_pixels(GIF* gif);
|
||||
int GIF_get_width(GIF* gif);
|
||||
int GIF_get_height(GIF* gif);
|
||||
int GIF_get_byte_count(GIF* gif);
|
||||
void GIF_render(GIF* gif, int src_x, int src_y,
|
||||
void* dst, int dst_w, int dst_h, int dst_x, int dst_y,
|
||||
int width, int height, bool fill_blank, int default_color);
|
||||
void GIF_advance(GIF* gif);
|
||||
int GIF_get_delay(GIF* gif);
|
||||
int GIF_get_frame_count(GIF* gif);
|
||||
bool GIF_is_opaque(GIF* gif);
|
||||
void GIF_recycle(JNIEnv* env, GIF* gif);
|
||||
|
||||
#endif // IMAGE_SUPPORT_GIF
|
||||
|
||||
#endif // IMAGE_IMAGE_GIF_H
|
||||
@@ -1,210 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 Hippo Seven
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
//
|
||||
// Created by Hippo on 1/3/2016.
|
||||
//
|
||||
|
||||
#include "config.h"
|
||||
#ifdef IMAGE_SUPPORT_JPEG
|
||||
|
||||
#include <setjmp.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "image_jpeg.h"
|
||||
#include "image_utils.h"
|
||||
#include "java_wrapper.h"
|
||||
#include "../log.h"
|
||||
|
||||
struct my_error_mgr {
|
||||
struct jpeg_error_mgr pub;
|
||||
jmp_buf setjmp_buffer;
|
||||
};
|
||||
|
||||
typedef struct my_error_mgr * my_error_ptr;
|
||||
|
||||
static char emsg[JMSG_LENGTH_MAX];
|
||||
|
||||
static void my_error_exit(j_common_ptr cinfo)
|
||||
{
|
||||
my_error_ptr myerr = (my_error_ptr) cinfo->err;
|
||||
(*cinfo->err->format_message)(cinfo, emsg);
|
||||
longjmp(myerr->setjmp_buffer, 1);
|
||||
}
|
||||
|
||||
static size_t custom_read(void * custom_stuff, unsigned char * buffer, size_t size)
|
||||
{
|
||||
bool attach;
|
||||
size_t read;
|
||||
PatchHeadInputStream* patch_head_input_stream = custom_stuff;
|
||||
JNIEnv *env = obtain_env(&attach);
|
||||
|
||||
if (env == NULL) {
|
||||
LOGE(MSG("Can't get JNIEnv"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
read = read_patch_head_input_stream(env, patch_head_input_stream, buffer, 0, size);
|
||||
|
||||
if (attach) {
|
||||
release_env();
|
||||
}
|
||||
|
||||
return read;
|
||||
}
|
||||
|
||||
void* JPEG_decode(JNIEnv* env, PatchHeadInputStream* patch_head_input_stream, bool partially)
|
||||
{
|
||||
JPEG* jpeg = NULL;
|
||||
struct jpeg_decompress_struct cinfo;
|
||||
struct my_error_mgr jerr;
|
||||
unsigned char* buffer = NULL;
|
||||
size_t stride;
|
||||
unsigned char* line_buffer_array[3];
|
||||
int read_lines;
|
||||
|
||||
jpeg = (JPEG*) malloc(sizeof(JPEG));
|
||||
if (jpeg == NULL) {
|
||||
WTF_OM;
|
||||
close_patch_head_input_stream(env, patch_head_input_stream);
|
||||
destroy_patch_head_input_stream(env, &patch_head_input_stream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Init
|
||||
cinfo.err = jpeg_std_error(&jerr.pub);
|
||||
jerr.pub.error_exit = my_error_exit;
|
||||
if (setjmp(jerr.setjmp_buffer)) {
|
||||
LOGE(MSG("%s"), emsg);
|
||||
free(jpeg);
|
||||
free(buffer);
|
||||
jpeg_destroy_decompress(&cinfo);
|
||||
close_patch_head_input_stream(env, patch_head_input_stream);
|
||||
destroy_patch_head_input_stream(env, &patch_head_input_stream);
|
||||
return NULL;
|
||||
}
|
||||
jpeg_create_decompress(&cinfo);
|
||||
jpeg_custom_src(&cinfo, &custom_read, patch_head_input_stream);
|
||||
jpeg_read_header(&cinfo, TRUE);
|
||||
|
||||
// Start decompress
|
||||
cinfo.out_color_space = JCS_EXT_RGBA;
|
||||
jpeg_start_decompress(&cinfo);
|
||||
|
||||
stride = cinfo.output_components * cinfo.output_width;
|
||||
buffer = malloc(stride * cinfo.output_height);
|
||||
if (buffer == NULL) {
|
||||
free(jpeg);
|
||||
jpeg_destroy_decompress(&cinfo);
|
||||
close_patch_head_input_stream(env, patch_head_input_stream);
|
||||
destroy_patch_head_input_stream(env, &patch_head_input_stream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Copy buffer
|
||||
line_buffer_array[0] = buffer;
|
||||
line_buffer_array[1] = line_buffer_array[0] + stride;
|
||||
line_buffer_array[2] = line_buffer_array[1] + stride;
|
||||
while (cinfo.output_scanline < cinfo.output_height) {
|
||||
read_lines = jpeg_read_scanlines(&cinfo, line_buffer_array, 3);
|
||||
line_buffer_array[0] += stride * read_lines;
|
||||
line_buffer_array[1] = line_buffer_array[0] + stride;
|
||||
line_buffer_array[2] = line_buffer_array[1] + stride;
|
||||
}
|
||||
|
||||
// Finish decompress
|
||||
jpeg_finish_decompress(&cinfo);
|
||||
jpeg_destroy_decompress(&cinfo);
|
||||
|
||||
// Close stream
|
||||
close_patch_head_input_stream(env, patch_head_input_stream);
|
||||
destroy_patch_head_input_stream(env, &patch_head_input_stream);
|
||||
|
||||
// Fill jpeg
|
||||
jpeg->width = cinfo.output_width;
|
||||
jpeg->height = cinfo.output_height;
|
||||
jpeg->buffer = buffer;
|
||||
|
||||
return jpeg;
|
||||
}
|
||||
|
||||
bool JPEG_complete(JPEG* jpeg)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool JPEG_is_completed(JPEG* jpeg)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void* JPEG_get_pixels(JPEG* jpeg)
|
||||
{
|
||||
return jpeg->buffer;
|
||||
}
|
||||
|
||||
int JPEG_get_width(JPEG* jpeg)
|
||||
{
|
||||
return jpeg->width;
|
||||
}
|
||||
|
||||
int JPEG_get_height(JPEG* jpeg)
|
||||
{
|
||||
return jpeg->height;
|
||||
}
|
||||
|
||||
int JPEG_get_byte_count(JPEG* jpeg)
|
||||
{
|
||||
return jpeg->width * jpeg->height * 4;
|
||||
}
|
||||
|
||||
void JPEG_render(JPEG* jpeg, int src_x, int src_y,
|
||||
void* dst, int dst_w, int dst_h, int dst_x, int dst_y,
|
||||
int width, int height, bool fill_blank, int default_color)
|
||||
{
|
||||
copy_pixels(jpeg->buffer, jpeg->width, jpeg->height, src_x, src_y,
|
||||
dst, dst_w, dst_h, dst_x, dst_y,
|
||||
width, height, fill_blank, default_color);
|
||||
}
|
||||
|
||||
void JPEG_advance(JPEG* jpeg)
|
||||
{
|
||||
}
|
||||
|
||||
int JPEG_get_delay(JPEG* jpeg)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int JPEG_get_frame_count(JPEG* jpeg)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool JPEG_is_opaque(JPEG* jpeg)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void JPEG_recycle(JPEG* jpeg)
|
||||
{
|
||||
free(jpeg->buffer);
|
||||
jpeg->buffer = NULL;
|
||||
|
||||
free(jpeg);
|
||||
}
|
||||
|
||||
#endif // IMAGE_SUPPORT_JPEG
|
||||
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 Hippo Seven
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
//
|
||||
// Created by Hippo on 1/3/2016.
|
||||
//
|
||||
|
||||
#ifndef IMAGE_IMAGE_JPEG_H
|
||||
#define IMAGE_IMAGE_JPEG_H
|
||||
|
||||
#include "config.h"
|
||||
#ifdef IMAGE_SUPPORT_JPEG
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "jpeglib.h"
|
||||
#include "patch_head_input_stream.h"
|
||||
#include "../utils.h"
|
||||
|
||||
#define IMAGE_JPEG_DECODER_DESCRIPTION ("libjpeg-turbo " MAKESTRING(STRINGIZE, LIBJPEG_TURBO_VERSION))
|
||||
|
||||
#define IMAGE_JPEG_MAGIC_NUMBER_0 0xFF
|
||||
#define IMAGE_JPEG_MAGIC_NUMBER_1 0xD8
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned int width;
|
||||
unsigned int height;
|
||||
void* buffer;
|
||||
} JPEG;
|
||||
|
||||
void* JPEG_decode(JNIEnv* env, PatchHeadInputStream* patch_head_input_stream, bool partially);
|
||||
bool JPEG_complete(JPEG* jpeg);
|
||||
bool JPEG_is_completed(JPEG* jpeg);
|
||||
void* JPEG_get_pixels(JPEG* jpeg);
|
||||
int JPEG_get_width(JPEG* jpeg);
|
||||
int JPEG_get_height(JPEG* jpeg);
|
||||
int JPEG_get_byte_count(JPEG* jpeg);
|
||||
void JPEG_render(JPEG* jpeg, int src_x, int src_y,
|
||||
void* dst, int dst_w, int dst_h, int dst_x, int dst_y,
|
||||
int width, int height, bool fill_blank, int default_color);
|
||||
void JPEG_advance(JPEG* jpeg);
|
||||
int JPEG_get_delay(JPEG* jpeg);
|
||||
int JPEG_get_frame_count(JPEG* jpeg);
|
||||
bool JPEG_is_opaque(JPEG* jpeg);
|
||||
void JPEG_recycle(JPEG* jpeg);
|
||||
|
||||
#endif // IMAGE_SUPPORT_JPEG
|
||||
|
||||
#endif // IMAGE_IMAGE_JPEG_H
|
||||
@@ -28,12 +28,9 @@
|
||||
|
||||
#include "patch_head_input_stream.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned int width;
|
||||
unsigned int height;
|
||||
void* buffer;
|
||||
} PLAIN;
|
||||
#include "image.h"
|
||||
|
||||
typedef IMAGE PLAIN;
|
||||
|
||||
void* PLAIN_create(unsigned int width, unsigned int height, const void* data);
|
||||
bool PLAIN_complete(PLAIN* plain);
|
||||
|
||||
@@ -1,642 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015 Hippo Seven
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
//
|
||||
// Created by Hippo on 12/27/2015.
|
||||
//
|
||||
|
||||
#include "config.h"
|
||||
#ifdef IMAGE_SUPPORT_PNG
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "../log.h"
|
||||
#include "../utils.h"
|
||||
#include "image_png.h"
|
||||
#include "image_utils.h"
|
||||
#include "java_wrapper.h"
|
||||
|
||||
static void user_read_fn(png_structp png_ptr,
|
||||
png_bytep data, png_size_t length)
|
||||
{
|
||||
bool attach;
|
||||
PatchHeadInputStream* patch_head_input_stream = png_get_io_ptr(png_ptr);
|
||||
JNIEnv *env = obtain_env(&attach);
|
||||
|
||||
if (env == NULL) {
|
||||
LOGE(MSG("Can't get JNIEnv"));
|
||||
}
|
||||
|
||||
read_patch_head_input_stream(env, patch_head_input_stream, data, 0, length);
|
||||
|
||||
if (attach) {
|
||||
release_env();
|
||||
}
|
||||
}
|
||||
|
||||
static void user_error_fn(png_structp png_ptr,
|
||||
png_const_charp error_msg)
|
||||
{
|
||||
LOGE(MSG("%s"), error_msg);
|
||||
}
|
||||
|
||||
static void user_warn_fn(png_structp png_ptr,
|
||||
png_const_charp error_msg)
|
||||
{
|
||||
LOGW(MSG("%s"), error_msg);
|
||||
}
|
||||
|
||||
static void free_frame_info_array(PNG_FRAME_INFO* frame_info_array, unsigned int count)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (frame_info_array == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
PNG_FRAME_INFO* frame_info = frame_info_array + i;
|
||||
free(frame_info->buffer);
|
||||
frame_info->buffer = NULL;
|
||||
}
|
||||
free(frame_info_array);
|
||||
}
|
||||
|
||||
static bool read_image(png_structp png_ptr, unsigned char* buffer, unsigned int width, unsigned int height)
|
||||
{
|
||||
int i;
|
||||
unsigned char** line_buffer_ptr_array = (unsigned char**) malloc(height * sizeof(unsigned char*));
|
||||
if (line_buffer_ptr_array == NULL) {
|
||||
WTF_OM;
|
||||
return false;
|
||||
}
|
||||
for (i = 0; i < height; i++) {
|
||||
line_buffer_ptr_array[i] = buffer + (width * i * 4);
|
||||
}
|
||||
png_read_image(png_ptr, line_buffer_ptr_array);
|
||||
free(line_buffer_ptr_array);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void read_frame(png_structp png_ptr, png_infop info_ptr, PNG_FRAME_INFO* frame_info)
|
||||
{
|
||||
unsigned int width;
|
||||
unsigned int height;
|
||||
unsigned int offset_x;
|
||||
unsigned int offset_y;
|
||||
unsigned short delay_num;
|
||||
unsigned short delay_den;
|
||||
unsigned char dop;
|
||||
unsigned char bop;
|
||||
unsigned int delay;
|
||||
unsigned char* buffer = NULL;
|
||||
|
||||
png_read_frame_head(png_ptr, info_ptr);
|
||||
png_get_next_frame_fcTL(png_ptr, info_ptr, &width, &height, &offset_x, &offset_y, &delay_num, &delay_den, &dop, &bop);
|
||||
|
||||
delay = 1000u * delay_num / delay_den;
|
||||
|
||||
frame_info->width = width;
|
||||
frame_info->height = height;
|
||||
frame_info->offset_x = offset_x;
|
||||
frame_info->offset_y = offset_y;
|
||||
frame_info->delay = delay;
|
||||
frame_info->dop = dop;
|
||||
frame_info->bop = bop;
|
||||
frame_info->pop = IMAGE_PNG_PREPARE_UNKNOWN;
|
||||
|
||||
buffer = (unsigned char*) malloc(width * height * 4);
|
||||
if (buffer == NULL) {
|
||||
frame_info->buffer = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
read_image(png_ptr, buffer, width, height);
|
||||
frame_info->buffer = buffer;
|
||||
}
|
||||
|
||||
static void generate_pop(PNG_FRAME_INFO* frame_info_array, int count)
|
||||
{
|
||||
unsigned char dop = 0xff;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < count; i++){
|
||||
PNG_FRAME_INFO* frame_info = frame_info_array + i;
|
||||
|
||||
switch (dop) {
|
||||
case PNG_DISPOSE_OP_NONE:
|
||||
frame_info->pop = IMAGE_PNG_PREPARE_NONE;
|
||||
break;
|
||||
case PNG_DISPOSE_OP_BACKGROUND:
|
||||
frame_info->pop = IMAGE_PNG_PREPARE_BACKGROUND;
|
||||
break;
|
||||
case PNG_DISPOSE_OP_PREVIOUS:
|
||||
frame_info->pop = IMAGE_PNG_PREPARE_USE_BACKUP;
|
||||
break;
|
||||
default:
|
||||
frame_info->pop = IMAGE_PNG_PREPARE_BACKGROUND;
|
||||
break;
|
||||
}
|
||||
|
||||
dop = frame_info->dop;
|
||||
}
|
||||
}
|
||||
|
||||
void* PNG_decode(JNIEnv* env, PatchHeadInputStream* patch_head_input_stream, bool partially)
|
||||
{
|
||||
PNG *png = NULL;
|
||||
png_structp png_ptr = NULL;
|
||||
png_infop info_ptr = NULL;
|
||||
bool apng;
|
||||
unsigned int width;
|
||||
unsigned int height;
|
||||
int color_type;
|
||||
int bit_depth;
|
||||
bool is_opaque;
|
||||
unsigned char* buffer = NULL;
|
||||
unsigned int frame_count = 0;
|
||||
bool hide_first_frame = false;
|
||||
PNG_FRAME_INFO* frame_info_array = NULL;
|
||||
int i;
|
||||
|
||||
png = (PNG *) malloc(sizeof(PNG));
|
||||
if (png == NULL) {
|
||||
WTF_OM;
|
||||
close_patch_head_input_stream(env, patch_head_input_stream);
|
||||
destroy_patch_head_input_stream(env, &patch_head_input_stream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, &user_error_fn, &user_warn_fn);
|
||||
if (png_ptr == NULL) {
|
||||
free(png);
|
||||
png = NULL;
|
||||
close_patch_head_input_stream(env, patch_head_input_stream);
|
||||
destroy_patch_head_input_stream(env, &patch_head_input_stream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
info_ptr = png_create_info_struct(png_ptr);
|
||||
if (info_ptr == NULL) {
|
||||
png_destroy_read_struct(&png_ptr, NULL, NULL);
|
||||
free(png);
|
||||
png = NULL;
|
||||
close_patch_head_input_stream(env, patch_head_input_stream);
|
||||
destroy_patch_head_input_stream(env, &patch_head_input_stream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (setjmp(png_jmpbuf(png_ptr))) {
|
||||
LOGE(MSG("Error in png decode"));
|
||||
free_frame_info_array(frame_info_array, frame_count);
|
||||
frame_info_array = NULL;
|
||||
free(buffer);
|
||||
buffer = NULL;
|
||||
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
|
||||
free(png);
|
||||
png = NULL;
|
||||
close_patch_head_input_stream(env, patch_head_input_stream);
|
||||
destroy_patch_head_input_stream(env, &patch_head_input_stream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Set custom read function
|
||||
png_set_read_fn(png_ptr, patch_head_input_stream, &user_read_fn);
|
||||
|
||||
// Get png info
|
||||
png_read_info(png_ptr, info_ptr);
|
||||
|
||||
// Check apng
|
||||
if (png_get_valid(png_ptr, info_ptr, PNG_INFO_acTL)) {
|
||||
apng = true;
|
||||
} else {
|
||||
apng = false;
|
||||
}
|
||||
|
||||
// PNG info
|
||||
width = png_get_image_width(png_ptr, info_ptr);
|
||||
height = png_get_image_height(png_ptr, info_ptr);
|
||||
color_type = png_get_color_type(png_ptr, info_ptr);
|
||||
bit_depth = png_get_bit_depth(png_ptr, info_ptr);
|
||||
|
||||
// Create buffer
|
||||
buffer = (unsigned char*) malloc(width * height * 4);
|
||||
if (buffer == NULL) {
|
||||
WTF_OM;
|
||||
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
|
||||
free(png);
|
||||
png = NULL;
|
||||
close_patch_head_input_stream(env, patch_head_input_stream);
|
||||
destroy_patch_head_input_stream(env, &patch_head_input_stream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (apng) {
|
||||
// Get frame count
|
||||
frame_count = png_get_num_frames(png_ptr, info_ptr);
|
||||
hide_first_frame = png_get_first_frame_is_hidden(png_ptr, info_ptr);
|
||||
if (hide_first_frame) {
|
||||
frame_count--;
|
||||
}
|
||||
|
||||
// Create frame info array
|
||||
frame_info_array = (PNG_FRAME_INFO*) calloc(frame_count, sizeof(PNG_FRAME_INFO));
|
||||
if (frame_info_array == NULL) {
|
||||
WTF_OM;
|
||||
free(buffer);
|
||||
buffer = NULL;
|
||||
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
|
||||
free(png);
|
||||
png = NULL;
|
||||
close_patch_head_input_stream(env, patch_head_input_stream);
|
||||
destroy_patch_head_input_stream(env, &patch_head_input_stream);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Configure to ARGB
|
||||
png_set_expand(png_ptr);
|
||||
if (bit_depth == 16) {
|
||||
png_set_scale_16(png_ptr);
|
||||
}
|
||||
if (color_type == PNG_COLOR_TYPE_GRAY ||
|
||||
color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
|
||||
png_set_gray_to_rgb(png_ptr);
|
||||
}
|
||||
if (!(color_type & PNG_COLOR_MASK_ALPHA)) {
|
||||
is_opaque = true;
|
||||
png_set_add_alpha(png_ptr, 0xff, PNG_FILLER_AFTER);
|
||||
} else {
|
||||
is_opaque = false;
|
||||
}
|
||||
|
||||
if (apng) {
|
||||
if (hide_first_frame) {
|
||||
// Skip first frame
|
||||
read_image(png_ptr, buffer, width, height);
|
||||
}
|
||||
|
||||
// Read first frame
|
||||
read_frame(png_ptr, info_ptr, frame_info_array);
|
||||
// Fix dop
|
||||
if (frame_info_array->dop == PNG_DISPOSE_OP_PREVIOUS) {
|
||||
frame_info_array->dop = PNG_DISPOSE_OP_BACKGROUND;
|
||||
}
|
||||
|
||||
if (!partially || frame_count == 1) {
|
||||
// Read all frame
|
||||
for (i = 1; i < frame_count; read_frame(png_ptr, info_ptr, frame_info_array + i++));
|
||||
|
||||
// Generate pop
|
||||
generate_pop(frame_info_array, frame_count);
|
||||
|
||||
// End read
|
||||
png_read_end(png_ptr, info_ptr);
|
||||
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
|
||||
|
||||
// Close input stream
|
||||
close_patch_head_input_stream(env, patch_head_input_stream);
|
||||
destroy_patch_head_input_stream(env, &patch_head_input_stream);
|
||||
|
||||
png->partially = false;
|
||||
png->png_ptr = NULL;
|
||||
png->info_ptr = NULL;
|
||||
png->patch_head_input_stream = NULL;
|
||||
} else {
|
||||
png->partially = true;
|
||||
png->png_ptr = png_ptr;
|
||||
png->info_ptr = info_ptr;
|
||||
png->patch_head_input_stream = patch_head_input_stream;
|
||||
}
|
||||
|
||||
// Fill PNG
|
||||
png->width = width;
|
||||
png->height = height;
|
||||
png->is_opaque = is_opaque;
|
||||
png->buffer = buffer;
|
||||
png->apng = true;
|
||||
png->buffer_index = -1;
|
||||
png->frame_info_array = frame_info_array;
|
||||
png->frame_count = frame_count;
|
||||
png->backup = NULL;
|
||||
|
||||
// Render first frame
|
||||
PNG_advance(png);
|
||||
} else {
|
||||
read_image(png_ptr, buffer, width, height);
|
||||
|
||||
// End read
|
||||
png_read_end(png_ptr, info_ptr);
|
||||
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
|
||||
|
||||
// Close input stream
|
||||
close_patch_head_input_stream(env, patch_head_input_stream);
|
||||
destroy_patch_head_input_stream(env, &patch_head_input_stream);
|
||||
|
||||
// Fill PNG
|
||||
png->width = width;
|
||||
png->height = height;
|
||||
png->buffer = buffer;
|
||||
png->apng = false;
|
||||
png->buffer_index = 0;
|
||||
png->frame_info_array = NULL;
|
||||
png->frame_count = 0;
|
||||
png->backup = NULL;
|
||||
png->partially = false;
|
||||
png->png_ptr = NULL;
|
||||
png->info_ptr = NULL;
|
||||
png->patch_head_input_stream = NULL;
|
||||
}
|
||||
|
||||
return png;
|
||||
}
|
||||
|
||||
bool PNG_complete(JNIEnv *env, PNG* png)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!png->partially) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (png->png_ptr == NULL || png->info_ptr == NULL || png->patch_head_input_stream == NULL) {
|
||||
LOGE(MSG("Some stuff is NULL"));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read left frames
|
||||
for (i = 1; i < png->frame_count; read_frame(png->png_ptr, png->info_ptr, png->frame_info_array + i++));
|
||||
|
||||
// Generate pop
|
||||
generate_pop(png->frame_info_array, png->frame_count);
|
||||
|
||||
// End read
|
||||
png_read_end(png->png_ptr, png->info_ptr);
|
||||
png_destroy_read_struct(&png->png_ptr, &png->info_ptr, NULL);
|
||||
|
||||
// Close input stream
|
||||
close_patch_head_input_stream(env, png->patch_head_input_stream);
|
||||
destroy_patch_head_input_stream(env, &png->patch_head_input_stream);
|
||||
|
||||
// Clean up
|
||||
png->partially = false;
|
||||
png->png_ptr = NULL;
|
||||
png->info_ptr = NULL;
|
||||
png->patch_head_input_stream = NULL;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PNG_is_completed(PNG* png)
|
||||
{
|
||||
return !png->partially;
|
||||
}
|
||||
|
||||
void* PNG_get_pixels(PNG* png)
|
||||
{
|
||||
if (!png->partially && PNG_get_frame_count(png) == 1) {
|
||||
return png->buffer;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int PNG_get_width(PNG* png)
|
||||
{
|
||||
return png->width;
|
||||
}
|
||||
|
||||
int PNG_get_height(PNG* png)
|
||||
{
|
||||
return png->height;
|
||||
}
|
||||
|
||||
int PNG_get_byte_count(PNG* png)
|
||||
{
|
||||
int i;
|
||||
PNG_FRAME_INFO* frame_info;
|
||||
int size = 0;
|
||||
// Add buffer size
|
||||
if (png->buffer != NULL) {
|
||||
size += png->width * png->height * 4;
|
||||
}
|
||||
// Add backup size
|
||||
if (png->backup != NULL) {
|
||||
size += png->width * png->height * 4;
|
||||
}
|
||||
// Add frames size
|
||||
if (png->frame_info_array != NULL) {
|
||||
for (i = 0; i < png->frame_count; i++) {
|
||||
frame_info = png->frame_info_array + i;
|
||||
// Add frame buffer size
|
||||
if (frame_info->buffer != NULL) {
|
||||
size += frame_info->width * frame_info->height * 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
void PNG_render(PNG* png, int src_x, int src_y,
|
||||
void* dst, int dst_w, int dst_h, int dst_x, int dst_y,
|
||||
int width, int height, bool fill_blank, int default_color)
|
||||
{
|
||||
copy_pixels(png->buffer, png->width, png->height, src_x, src_y,
|
||||
dst, dst_w, dst_h, dst_x, dst_y,
|
||||
width, height, fill_blank, default_color);
|
||||
}
|
||||
|
||||
static void backup(PNG* png)
|
||||
{
|
||||
if (png->backup == NULL) {
|
||||
png->backup = (unsigned char*) malloc(png->width * png->height * 4);
|
||||
if (png->backup == NULL) {
|
||||
WTF_OM;
|
||||
return;
|
||||
}
|
||||
}
|
||||
memcpy(png->backup, png->buffer, png->width * png->height * 4);
|
||||
}
|
||||
|
||||
static void restore(PNG* png)
|
||||
{
|
||||
if (png->backup == NULL) {
|
||||
LOGE(MSG("Backup is NULL"));
|
||||
} else {
|
||||
memcpy(png->buffer, png->backup, png->width * png->height * 4);
|
||||
}
|
||||
}
|
||||
|
||||
static void switch_buffer_backup(PNG* png)
|
||||
{
|
||||
unsigned char* temp;
|
||||
|
||||
if (png->backup == NULL) {
|
||||
backup(png);
|
||||
} else {
|
||||
temp = png->buffer;
|
||||
png->buffer = png->backup;
|
||||
png->backup = temp;
|
||||
}
|
||||
}
|
||||
|
||||
static void blend_over(unsigned char* dp, const unsigned char* sp, size_t len)
|
||||
{
|
||||
unsigned int i;
|
||||
int u, v, al;
|
||||
|
||||
for (i = 0; i < len; i += 4, sp += 4, dp += 4) {
|
||||
if (sp[3] == 255) {
|
||||
memcpy(dp, sp, 4);
|
||||
} else if (sp[3] != 0) {
|
||||
if (dp[3] != 0) {
|
||||
u = sp[3] * 255;
|
||||
v = (255 - sp[3]) * dp[3];
|
||||
al = u + v;
|
||||
dp[0] = (unsigned char) ((sp[0] * u + dp[0] * v) / al);
|
||||
dp[1] = (unsigned char) ((sp[1] * u + dp[1] * v) / al);
|
||||
dp[2] = (unsigned char) ((sp[2] * u + dp[2] * v) / al);
|
||||
dp[3] = (unsigned char) (al / 255);
|
||||
} else {
|
||||
memcpy(dp, sp, 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void blend(unsigned char* src, int src_width, int src_height, int offset_x, int offset_y,
|
||||
unsigned char* dst, int dst_width, int dst_height, bool blend_op_over)
|
||||
{
|
||||
int i;
|
||||
unsigned char* src_ptr;
|
||||
unsigned char* dst_ptr;
|
||||
size_t len;
|
||||
int copy_width = MIN(dst_width - offset_x, src_width);
|
||||
int copy_height = MIN(dst_height - offset_y, src_height);
|
||||
|
||||
for (i = 0; i < copy_height; i++) {
|
||||
src_ptr = src + (i * src_width * 4);
|
||||
dst_ptr = dst + (((offset_y + i) * dst_width + offset_x) * 4);
|
||||
len = (size_t) (copy_width * 4);
|
||||
|
||||
if (blend_op_over) {
|
||||
blend_over(dst_ptr, src_ptr, len);
|
||||
} else {
|
||||
memcpy(dst_ptr, src_ptr, len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PNG_advance(PNG* png)
|
||||
{
|
||||
int index;
|
||||
|
||||
if (!png->apng) {
|
||||
LOGW(MSG("It is not apng, no need to advance"));
|
||||
return;
|
||||
}
|
||||
|
||||
index = (png->buffer_index + 1) % png->frame_count;
|
||||
if (index != 0 && png->partially) {
|
||||
LOGE(MSG("The png is only decoded partially. Only the first frame can be shown."));
|
||||
return;
|
||||
}
|
||||
|
||||
PNG_FRAME_INFO* frame_info = png->frame_info_array + index;
|
||||
|
||||
if (frame_info->dop == PNG_DISPOSE_OP_PREVIOUS && frame_info->pop == IMAGE_PNG_PREPARE_USE_BACKUP) {
|
||||
switch_buffer_backup(png);
|
||||
} else {
|
||||
// Backup
|
||||
if (frame_info->dop == PNG_DISPOSE_OP_PREVIOUS) {
|
||||
backup(png);
|
||||
}
|
||||
|
||||
// Prepare
|
||||
switch (frame_info->pop) {
|
||||
case IMAGE_PNG_PREPARE_NONE:
|
||||
// Do nothing
|
||||
break;
|
||||
default:
|
||||
case IMAGE_PNG_PREPARE_BACKGROUND:
|
||||
// Set transparent
|
||||
memset(png->buffer, '\0', png->width * png->height * 4);
|
||||
break;
|
||||
case IMAGE_PNG_PREPARE_USE_BACKUP:
|
||||
restore(png);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
blend(frame_info->buffer, frame_info->width, frame_info->height, frame_info->offset_x, frame_info->offset_y,
|
||||
png->buffer, png->width, png->height, frame_info->bop == PNG_BLEND_OP_OVER);
|
||||
|
||||
png->buffer_index = index;
|
||||
}
|
||||
|
||||
int PNG_get_delay(PNG* png)
|
||||
{
|
||||
if (png->apng) {
|
||||
return png->frame_info_array[png->buffer_index].delay;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int PNG_get_frame_count(PNG* png)
|
||||
{
|
||||
if (png->apng) {
|
||||
return png->frame_count;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
bool PNG_is_opaque(PNG* png)
|
||||
{
|
||||
return png->is_opaque;
|
||||
}
|
||||
|
||||
void PNG_recycle(JNIEnv *env, PNG* png)
|
||||
{
|
||||
if (png == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
free(png->buffer);
|
||||
png->buffer = NULL;
|
||||
|
||||
free_frame_info_array(png->frame_info_array, png->frame_count);
|
||||
png->frame_info_array = NULL;
|
||||
|
||||
free(png->backup);
|
||||
png->backup = NULL;
|
||||
|
||||
if (png->png_ptr != NULL && png->info_ptr != NULL) {
|
||||
png_destroy_read_struct(&png->png_ptr, &png->info_ptr, NULL);
|
||||
}
|
||||
png->png_ptr = NULL;
|
||||
png->info_ptr = NULL;
|
||||
|
||||
if (png->patch_head_input_stream != NULL) {
|
||||
close_patch_head_input_stream(env, png->patch_head_input_stream);
|
||||
destroy_patch_head_input_stream(env, &png->patch_head_input_stream);
|
||||
png->patch_head_input_stream = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // IMAGE_SUPPORT_PNG
|
||||
@@ -1,89 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015 Hippo Seven
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
//
|
||||
// Created by Hippo on 12/27/2015.
|
||||
//
|
||||
|
||||
#ifndef IMAGE_IMAGE_PNG_H
|
||||
#define IMAGE_IMAGE_PNG_H
|
||||
|
||||
#include "config.h"
|
||||
#ifdef IMAGE_SUPPORT_PNG
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "png.h"
|
||||
#include "patch_head_input_stream.h"
|
||||
|
||||
#define IMAGE_PNG_DECODER_DESCRIPTION ("libpng " PNG_LIBPNG_VER_STRING)
|
||||
|
||||
#define IMAGE_PNG_MAGIC_NUMBER_0 0x89
|
||||
#define IMAGE_PNG_MAGIC_NUMBER_1 0x50
|
||||
|
||||
#define IMAGE_PNG_PREPARE_UNKNOWN 0x00
|
||||
#define IMAGE_PNG_PREPARE_NONE 0x01
|
||||
#define IMAGE_PNG_PREPARE_BACKGROUND 0x02
|
||||
#define IMAGE_PNG_PREPARE_USE_BACKUP 0x03
|
||||
|
||||
typedef struct {
|
||||
unsigned char* buffer;
|
||||
unsigned int width;
|
||||
unsigned int height;
|
||||
unsigned int offset_x;
|
||||
unsigned int offset_y;
|
||||
unsigned int delay; // ms
|
||||
unsigned char dop;
|
||||
unsigned char bop;
|
||||
unsigned char pop;
|
||||
} PNG_FRAME_INFO;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned int width;
|
||||
unsigned int height;
|
||||
bool is_opaque;
|
||||
unsigned char* buffer;
|
||||
bool apng;
|
||||
int buffer_index;
|
||||
PNG_FRAME_INFO* frame_info_array;
|
||||
unsigned int frame_count;
|
||||
unsigned char* backup;
|
||||
bool partially;
|
||||
png_structp png_ptr;
|
||||
png_infop info_ptr;
|
||||
PatchHeadInputStream* patch_head_input_stream;
|
||||
} PNG;
|
||||
|
||||
void* PNG_decode(JNIEnv* env, PatchHeadInputStream* patch_head_input_stream, bool partially);
|
||||
bool PNG_complete(JNIEnv* env, PNG* png);
|
||||
bool PNG_is_completed(PNG* png);
|
||||
void* PNG_get_pixels(PNG* png);
|
||||
int PNG_get_width(PNG* png);
|
||||
int PNG_get_height(PNG* png);
|
||||
int PNG_get_byte_count(PNG* png);
|
||||
void PNG_render(PNG* png, int src_x, int src_y,
|
||||
void* dst, int dst_w, int dst_h, int dst_x, int dst_y,
|
||||
int width, int height, bool fill_blank, int default_color);
|
||||
void PNG_advance(PNG* png);
|
||||
int PNG_get_delay(PNG* png);
|
||||
int PNG_get_frame_count(PNG* png);
|
||||
bool PNG_is_opaque(PNG* png);
|
||||
void PNG_recycle(JNIEnv* env, PNG* png);
|
||||
|
||||
#endif // IMAGE_SUPPORT_PNG
|
||||
|
||||
#endif // IMAGE_IMAGE_PNG_H
|
||||
@@ -21,7 +21,6 @@
|
||||
#include <archive.h>
|
||||
#include <lzma.h>
|
||||
#include <zlib.h>
|
||||
#include "image/libjpeg-turbo/jconfigint.h"
|
||||
#include <nettle/config.h>
|
||||
|
||||
#include "java_wrapper.h"
|
||||
@@ -50,11 +49,6 @@ Java_com_hippo_Native_getliblzmaVersion(JNIEnv *env, jclass clazz) {
|
||||
return (*env)->NewStringUTF(env, lzma_version_string());
|
||||
}
|
||||
|
||||
JNIEXPORT jstring JNICALL
|
||||
Java_com_hippo_Native_getlibjpeg_1turboVersion(JNIEnv *env, jclass clazz) {
|
||||
return (*env)->NewStringUTF(env, VERSION);
|
||||
}
|
||||
|
||||
JNIEXPORT jstring JNICALL
|
||||
Java_com_hippo_Native_getzlibVersion(JNIEnv *env, jclass clazz) {
|
||||
return (*env)->NewStringUTF(env, ZLIB_VERSION);
|
||||
|
||||
Reference in new issue
Block a user