blob: a63302866d29d3f95949d2dbffb14f53fbe8ffce [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
/**
* @author Viskov Nikolay
*
*/
#include "pngencoder.h"
#include <stdio.h>
/*
* Class: org_apache_harmony_awt_gl_image_PngEncoder
* Method: initIDs
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_org_apache_harmony_x_imageio_plugins_png_PNGImageWriter_initIDs
(JNIEnv *env, jclass cls, jclass iosClass) {
//-- ImageOutputStream.write(byte[], int, int)
img_IOSwriteID = (*env)->GetMethodID(env, iosClass, "write", "([BII)V");
}
void gl_write_data(png_structp png_ptr, png_bytep data, png_size_t length) {
png_encoder_info_ptr encoderInfo = png_get_io_ptr(png_ptr);
jbyteArray java_buffer;
unsigned char *native_buffer;
JNIEnv *env = encoderInfo->env;
png_size_t bufferLength = IO_BUFFER_SIZE - encoderInfo->freeBytesInIOBuffer;
//printf("%u", length);
if (encoderInfo->freeBytesInIOBuffer < length) {
if (IO_BUFFER_SIZE < length) {
java_buffer = (*env)->NewByteArray(env, (jsize) (length + bufferLength));
native_buffer = (unsigned char *)(*env)->GetPrimitiveArrayCritical(env, java_buffer, NULL);
memcpy(native_buffer, encoderInfo->ioBuffer, bufferLength);
memcpy(native_buffer + bufferLength, data, length);
(*env)->ReleasePrimitiveArrayCritical(env, java_buffer, native_buffer, 0);
(*env)->CallVoidMethod(env, encoderInfo->ios, img_IOSwriteID, java_buffer, 0, length + bufferLength);
encoderInfo->freeBytesInIOBuffer = IO_BUFFER_SIZE;
} else {
gl_flush_data(encoderInfo->png_ptr);
memcpy(encoderInfo->ioBuffer, data, length);
encoderInfo->freeBytesInIOBuffer -= length;
}
} else {
memcpy(encoderInfo->ioBuffer + bufferLength, data, length);
encoderInfo->freeBytesInIOBuffer -= length;
}
//printf("passed\n");
}
void gl_flush_data(png_structp png_ptr) {
png_encoder_info_ptr encoderInfo = png_get_io_ptr(png_ptr);
jbyteArray java_buffer;
unsigned char *native_buffer;
png_size_t bufferLength = IO_BUFFER_SIZE - encoderInfo->freeBytesInIOBuffer;
JNIEnv *env = encoderInfo->env;
//printf("flush ");
java_buffer = (*env)->NewByteArray(env, (jsize) bufferLength);
native_buffer = (unsigned char *)(*env)->GetPrimitiveArrayCritical(env, java_buffer, NULL);
memcpy(native_buffer, encoderInfo->ioBuffer, bufferLength);
(*env)->ReleasePrimitiveArrayCritical(env, java_buffer, native_buffer, 0);
(*env)->CallVoidMethod(env, encoderInfo->ios, img_IOSwriteID, java_buffer, 0, bufferLength);
encoderInfo->freeBytesInIOBuffer = IO_BUFFER_SIZE;
//printf("passed\n");
}
/*
* Class: org_apache_harmony_awt_gl_image_PngEncoder
* Method: decode
* Signature: ([BILjava/lang/Object;IIII[IIZ)I
*/
JNIEXPORT jint JNICALL
Java_org_apache_harmony_x_imageio_plugins_png_PNGImageWriter_encode
(JNIEnv *env,
jobject obj,
jbyteArray jInput,
jint bytesInBuffer,
jint bytePixelSize,
jobject iosObj,
jint imageWidth,
jint imageHeight,
jint bitDipth,
jint colorType,
jintArray palette,
jint paletteSize,
jboolean isInterlace) {
png_encoder_info_ptr encoderInfo;
int i;
if(!(encoderInfo = initPng())) {
throwNewExceptionByName(env, "java/lang/RuntimeException",
"Can't create native PNG encoder");
return 2; // NULL
}
if(setjmp(encoderInfo->jmpBuf)) { // Only way to deal with errors in libpng
destroyPng(&encoderInfo);
return 2;
}
png_set_write_fn(
encoderInfo->png_ptr,
encoderInfo,
gl_write_data,
gl_flush_data
);
encoderInfo->ios = iosObj;
// Update JNI-related fields
encoderInfo->env = env;
encoderInfo->obj = obj;
encoderInfo->jInputData = jInput;
encoderInfo->jPalette = palette;
png_set_IHDR(encoderInfo->png_ptr, encoderInfo->info_ptr, imageWidth, imageHeight,
bitDipth, colorType, isInterlace ? PNG_INTERLACE_ADAM7 : PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
if (colorType == PNG_COLOR_TYPE_PALETTE) {
jint *tmpPalette = (jint *) (*env)->GetPrimitiveArrayCritical(env, palette, 0);
png_colorp pngPalette = (png_colorp) malloc(paletteSize * sizeof(png_color));
//png_bytep alpha = (png_bytep) malloc(paletteSize * sizeof(png_byte));
for (i = 0; i < paletteSize; i ++) {
//printf("n%u = %u\n", i, (tmpPalette[i] & 0xff000000));
(pngPalette + i)->red =
(png_byte) ((tmpPalette[i] & 0x00ff0000) >> 16);
(pngPalette + i)->green =
(png_byte) ((tmpPalette[i] & 0x0000ff00) >> 8);
(pngPalette + i)->blue = (png_byte) tmpPalette[i] & 0x000000ff;
//alpha[i] = 256 - (tmpPalette[i] & 0xff000000);
}
png_set_PLTE(encoderInfo->png_ptr, encoderInfo->info_ptr, pngPalette, paletteSize);
//png_set_tRNS(encoderInfo->png_ptr, encoderInfo->info_ptr, alpha, paletteSize, NULL);
//free(alpha);
free(pngPalette);
(*env)->ReleasePrimitiveArrayCritical(env, palette, tmpPalette, 0);
}
encoderInfo->inputBuffer = (*env)->GetPrimitiveArrayCritical(env, jInput, 0);
encoderInfo->tmpBuffer = (png_bytepp) malloc(imageHeight * sizeof(png_bytep));
for (i = 0; i < imageHeight; i ++) {
encoderInfo->tmpBuffer[i] = encoderInfo->inputBuffer + (i * (imageWidth * bytePixelSize));
}
png_set_rows(encoderInfo->png_ptr, encoderInfo->info_ptr, encoderInfo->tmpBuffer);
png_write_png(encoderInfo->png_ptr, encoderInfo->info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
free(encoderInfo->tmpBuffer);
encoderInfo->tmpBuffer = NULL;
(*env)->ReleasePrimitiveArrayCritical(env, jInput, encoderInfo->inputBuffer, 0);
encoderInfo->inputBuffer = NULL;
png_write_end(encoderInfo->png_ptr, encoderInfo->info_ptr);
png_write_flush(encoderInfo->png_ptr);
destroyPng(&encoderInfo);
return 0;
}
void gl_error_fn(png_structp png_ptr, png_const_charp error_msg) {
png_encoder_info_ptr encoderInfo = png_get_error_ptr(png_ptr);
throwNewExceptionByName(encoderInfo->env, "java/lang/RuntimeException",
error_msg);
if(encoderInfo) { // Else there's no way to terminate correctly
longjmp(encoderInfo->jmpBuf, 1);
}
}
void gl_warning_fn(png_structp png_ptr, png_const_charp warning_msg) {
}
void destroyPng(png_encoder_info_ptr *encoderInfoP) {
png_encoder_info_ptr encoderInfo = *encoderInfoP;
JNIEnv *env = encoderInfo->env;
//printf("now = %u\n",(encoderInfo->inputBuffer));
// Release arrays
if(encoderInfo->jInputData && encoderInfo->inputBuffer) {
(*env)->ReleasePrimitiveArrayCritical(
env, encoderInfo->jInputData, encoderInfo->inputBuffer, 0
);
encoderInfo->inputBuffer = NULL;
}
if (encoderInfo->tmpBuffer) {
free(encoderInfo->tmpBuffer);
}
encoderInfo->tmpBuffer = NULL;
if(encoderInfo->png_ptr && encoderInfo->info_ptr)
png_destroy_write_struct(&encoderInfo->png_ptr, &encoderInfo->info_ptr);
free(encoderInfo);
encoderInfo = NULL;
}
png_encoder_info_ptr initPng() {
png_encoder_info_ptr encoderInfo = calloc(sizeof(png_encoder_info), 1);
if(!encoderInfo) {
return NULL;
}
encoderInfo->png_ptr = png_create_write_struct(
PNG_LIBPNG_VER_STRING,
encoderInfo,
gl_error_fn,
gl_warning_fn
);
if(!encoderInfo->png_ptr) {
return NULL;
}
encoderInfo->info_ptr = png_create_info_struct(encoderInfo->png_ptr);
if (!encoderInfo->info_ptr) {
png_destroy_write_struct(&encoderInfo->png_ptr, png_infopp_NULL);
return NULL;
}
encoderInfo->freeBytesInIOBuffer = IO_BUFFER_SIZE;
return encoderInfo;
}